gotlin/internal/lang/metadata.go

210 lines
7.5 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]
item.Methods = append(item.Methods, metadataFunction(method, semantic.FunctionEffects[class.Name+"."+method.Name]))
}
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]
metadata.Functions = append(metadata.Functions, metadataFunction(function, semantic.FunctionEffects[function.Name]))
}
return metadata, nil
}
func metadataFunction(function *FunctionDecl, effects Effect) FunctionMetadata {
item := FunctionMetadata{Name: function.Name, TypeParams: function.TypeParams, Result: function.ReturnType, 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)
}