226 lines
8 KiB
Go
226 lines
8 KiB
Go
package lang
|
|
|
|
import "strings"
|
|
|
|
const PackageMetadataVersion = 1
|
|
|
|
type PackageMetadata struct {
|
|
Version int `json:"version"`
|
|
ImportPath string `json:"importPath"`
|
|
PackageName string `json:"packageName"`
|
|
Classes []ClassMetadata `json:"classes,omitempty"`
|
|
Enums []EnumMetadata `json:"enums,omitempty"`
|
|
Functions []FunctionMetadata `json:"functions,omitempty"`
|
|
}
|
|
|
|
type ClassMetadata struct {
|
|
Name string `json:"name"`
|
|
TypeParams []string `json:"typeParams,omitempty"`
|
|
Data bool `json:"data,omitempty"`
|
|
Fields []FieldMetadata `json:"fields,omitempty"`
|
|
Methods []FunctionMetadata `json:"methods,omitempty"`
|
|
}
|
|
|
|
type FieldMetadata struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Mutable bool `json:"mutable,omitempty"`
|
|
}
|
|
|
|
type FunctionMetadata struct {
|
|
Name string `json:"name"`
|
|
TypeParams []string `json:"typeParams,omitempty"`
|
|
Params []ParamMetadata `json:"params,omitempty"`
|
|
Result string `json:"result"`
|
|
Effects Effect `json:"effects,omitempty"`
|
|
}
|
|
|
|
type ParamMetadata struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
type EnumMetadata struct {
|
|
Name string `json:"name"`
|
|
Variants []EnumVariantMetadata `json:"variants"`
|
|
}
|
|
|
|
type EnumVariantMetadata struct {
|
|
Name string `json:"name"`
|
|
PayloadTypes []string `json:"payloadTypes,omitempty"`
|
|
StringValue string `json:"stringValue,omitempty"`
|
|
}
|
|
|
|
func BuildPackageMetadata(program *Program, importPath string) (*PackageMetadata, error) {
|
|
semantic, err := Analyze(program)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
metadata := &PackageMetadata{Version: PackageMetadataVersion, ImportPath: importPath, PackageName: goPackageName(program.PackagePath)}
|
|
for index := range program.Classes {
|
|
class := &program.Classes[index]
|
|
item := ClassMetadata{Name: class.Name, TypeParams: class.TypeParams, Data: class.Data}
|
|
for _, field := range class.Fields {
|
|
item.Fields = append(item.Fields, FieldMetadata{Name: field.Name, Type: field.Type, Mutable: field.Mutable})
|
|
}
|
|
for methodIndex := range class.Methods {
|
|
method := &class.Methods[methodIndex]
|
|
result := Type(nil)
|
|
if symbol := semantic.ClassInfo[class.Name].Method(method.Name); symbol != nil {
|
|
if signature, ok := symbol.Type.(FunctionType); ok {
|
|
result = signature.Result
|
|
}
|
|
}
|
|
item.Methods = append(item.Methods, metadataFunction(method, semantic.FunctionEffects[class.Name+"."+method.Name], result))
|
|
}
|
|
metadata.Classes = append(metadata.Classes, item)
|
|
}
|
|
for _, enum := range program.Enums {
|
|
item := EnumMetadata{Name: enum.Name}
|
|
for _, variant := range enum.Variants {
|
|
item.Variants = append(item.Variants, EnumVariantMetadata{Name: variant.Name, PayloadTypes: variant.PayloadTypes, StringValue: variant.StringValue})
|
|
}
|
|
metadata.Enums = append(metadata.Enums, item)
|
|
}
|
|
for index := range program.Functions {
|
|
function := &program.Functions[index]
|
|
result := Type(nil)
|
|
if symbol, ok := semantic.Global.Lookup(function.Name); ok {
|
|
if signature, ok := symbol.Type.(FunctionType); ok {
|
|
result = signature.Result
|
|
}
|
|
}
|
|
metadata.Functions = append(metadata.Functions, metadataFunction(function, semantic.FunctionEffects[function.Name], result))
|
|
}
|
|
return metadata, nil
|
|
}
|
|
|
|
func metadataFunction(function *FunctionDecl, effects Effect, result Type) FunctionMetadata {
|
|
resultName := function.ReturnType
|
|
if result != nil && !isUnknownType(result) {
|
|
resultName = result.String()
|
|
}
|
|
item := FunctionMetadata{Name: function.Name, TypeParams: function.TypeParams, Result: resultName, Effects: effects}
|
|
for _, param := range function.Params {
|
|
item.Params = append(item.Params, ParamMetadata{Name: param.Name, Type: param.Type})
|
|
}
|
|
return item
|
|
}
|
|
|
|
func metadataForImport(imported ImportDecl, metadata []*PackageMetadata) *PackageMetadata {
|
|
path := strings.Trim(imported.Path, `"`)
|
|
if !strings.Contains(path, "/") {
|
|
path = importPathToGoPath(path)
|
|
}
|
|
for _, candidate := range metadata {
|
|
if candidate != nil && candidate.ImportPath == path {
|
|
return candidate
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func packageSymbolFromMetadata(alias string, metadata *PackageMetadata) *PackageSymbol {
|
|
pack := &PackageSymbol{Alias: alias, Metadata: metadata, Classes: map[string]*ClassSymbol{}, Functions: map[string]*Symbol{}, Enums: map[string]EnumDecl{}}
|
|
for _, item := range metadata.Classes {
|
|
decl := &ClassDecl{Name: item.Name, TypeParams: item.TypeParams, Data: item.Data}
|
|
class := &ClassSymbol{Name: item.Name, TypeParams: item.TypeParams, Decl: decl, Fields: map[string]*Symbol{}, Methods: map[string]*Symbol{}}
|
|
pack.Classes[item.Name] = class
|
|
}
|
|
for _, item := range metadata.Classes {
|
|
class := pack.Classes[item.Name]
|
|
for _, field := range item.Fields {
|
|
typ := resolvePackageType(field.Type, alias, pack, item.TypeParams)
|
|
decl := FieldDecl{Name: field.Name, Type: field.Type, Mutable: field.Mutable}
|
|
class.Decl.Fields = append(class.Decl.Fields, decl)
|
|
class.Fields[field.Name] = &Symbol{Name: field.Name, Kind: VariableSymbol, Type: typ, Mutable: field.Mutable, Decl: &decl}
|
|
}
|
|
for _, method := range item.Methods {
|
|
typ := metadataFunctionType(method, alias, pack, item.TypeParams)
|
|
class.Methods[method.Name] = &Symbol{Name: method.Name, Kind: FunctionSymbolKind, Type: typ, Effects: method.Effects}
|
|
}
|
|
}
|
|
for _, item := range metadata.Functions {
|
|
typ := metadataFunctionType(item, alias, pack, nil)
|
|
pack.Functions[item.Name] = &Symbol{Name: item.Name, Kind: FunctionSymbolKind, Type: typ, Effects: item.Effects}
|
|
}
|
|
for _, item := range metadata.Enums {
|
|
decl := EnumDecl{Name: item.Name}
|
|
for _, variant := range item.Variants {
|
|
decl.Variants = append(decl.Variants, EnumVariant{Name: variant.Name, PayloadTypes: variant.PayloadTypes, StringValue: variant.StringValue})
|
|
}
|
|
pack.Enums[item.Name] = decl
|
|
}
|
|
return pack
|
|
}
|
|
|
|
func (pack *PackageSymbol) Function(name string) *Symbol {
|
|
if function := pack.Functions[name]; function != nil {
|
|
return function
|
|
}
|
|
return pack.Functions[exportedGoName(name)]
|
|
}
|
|
|
|
func (class *ClassSymbol) Method(name string) *Symbol {
|
|
if method := class.Methods[name]; method != nil {
|
|
return method
|
|
}
|
|
return class.Methods[exportedGoName(name)]
|
|
}
|
|
|
|
func (class *ClassSymbol) Field(name string) *Symbol {
|
|
if field := class.Fields[name]; field != nil {
|
|
return field
|
|
}
|
|
return class.Fields[exportedGoName(name)]
|
|
}
|
|
|
|
func metadataFunctionType(function FunctionMetadata, alias string, pack *PackageSymbol, enclosing []string) FunctionType {
|
|
params := append(append([]string{}, enclosing...), function.TypeParams...)
|
|
values := make([]Type, len(function.Params))
|
|
for index, param := range function.Params {
|
|
values[index] = resolvePackageType(param.Type, alias, pack, params)
|
|
}
|
|
return FunctionType{TypeParams: function.TypeParams, Params: values, Result: resolvePackageType(function.Result, alias, pack, params), Effects: function.Effects}
|
|
}
|
|
|
|
func resolvePackageType(text, alias string, pack *PackageSymbol, params []string) Type {
|
|
typ, err := ParseType(text)
|
|
if err != nil {
|
|
return UnknownType{}
|
|
}
|
|
paramSet := map[string]bool{}
|
|
for _, param := range params {
|
|
paramSet[param] = true
|
|
}
|
|
typ = resolveTypeParameters(typ, paramSet)
|
|
var resolve func(Type) Type
|
|
resolve = func(value Type) Type {
|
|
switch item := value.(type) {
|
|
case NamedType:
|
|
if class := pack.Classes[item.Name]; class != nil {
|
|
return ImportedClassType{Package: alias, Class: class}
|
|
}
|
|
return item
|
|
case NullableType:
|
|
return NullableType{Element: resolve(item.Element)}
|
|
case GoPointerType:
|
|
return GoPointerType{Element: resolve(item.Element)}
|
|
case GenericType:
|
|
args := make([]Type, len(item.Args))
|
|
for index, arg := range item.Args {
|
|
args[index] = resolve(arg)
|
|
}
|
|
return GenericType{Base: resolve(item.Base), Args: args}
|
|
case FunctionType:
|
|
args := make([]Type, len(item.Params))
|
|
for index, arg := range item.Params {
|
|
args[index] = resolve(arg)
|
|
}
|
|
return FunctionType{TypeParams: item.TypeParams, Params: args, Result: resolve(item.Result), Effects: item.Effects}
|
|
default:
|
|
return value
|
|
}
|
|
}
|
|
return resolve(typ)
|
|
}
|