Preserve Gotlin semantics across packages
This commit is contained in:
parent
5b30e49486
commit
acb42a5702
10 changed files with 525 additions and 34 deletions
210
internal/lang/metadata.go
Normal file
210
internal/lang/metadata.go
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue