215 lines
6 KiB
Go
215 lines
6 KiB
Go
package lang
|
|
|
|
import (
|
|
"fmt"
|
|
gotypes "go/types"
|
|
)
|
|
|
|
type SymbolKind int
|
|
|
|
const (
|
|
VariableSymbol SymbolKind = iota
|
|
FunctionSymbolKind
|
|
ClassSymbolKind
|
|
EnumSymbolKind
|
|
ImportSymbolKind
|
|
)
|
|
|
|
type Symbol struct {
|
|
Name string
|
|
Kind SymbolKind
|
|
Type Type
|
|
Mutable bool
|
|
Decl any
|
|
}
|
|
|
|
type Scope struct {
|
|
Parent *Scope
|
|
Symbols map[string]*Symbol
|
|
}
|
|
|
|
func NewScope(parent *Scope) *Scope { return &Scope{Parent: parent, Symbols: map[string]*Symbol{}} }
|
|
|
|
func (scope *Scope) Define(symbol *Symbol) error {
|
|
if _, exists := scope.Symbols[symbol.Name]; exists {
|
|
return fmt.Errorf("duplicate symbol %s", symbol.Name)
|
|
}
|
|
scope.Symbols[symbol.Name] = symbol
|
|
return nil
|
|
}
|
|
|
|
func (scope *Scope) Lookup(name string) (*Symbol, bool) {
|
|
for current := scope; current != nil; current = current.Parent {
|
|
if symbol, ok := current.Symbols[name]; ok {
|
|
return symbol, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
type ClassSymbol struct {
|
|
Name string
|
|
Decl *ClassDecl
|
|
Fields map[string]*Symbol
|
|
Methods map[string]*Symbol
|
|
}
|
|
|
|
type SemanticProgram struct {
|
|
Syntax *Program
|
|
Global *Scope
|
|
Classes map[string]ClassDecl
|
|
ClassInfo map[string]*ClassSymbol
|
|
Functions map[string]FunctionDecl
|
|
Enums map[string]EnumDecl
|
|
Imports map[string]bool
|
|
GoPackages map[string]*gotypes.Package
|
|
HIR *HIRProgram
|
|
Mappings *mappingState
|
|
}
|
|
|
|
func Analyze(program *Program) (*SemanticProgram, error) {
|
|
semantic := &SemanticProgram{
|
|
Syntax: program,
|
|
Global: NewScope(nil),
|
|
Classes: map[string]ClassDecl{},
|
|
ClassInfo: map[string]*ClassSymbol{},
|
|
Functions: map[string]FunctionDecl{},
|
|
Enums: map[string]EnumDecl{},
|
|
Imports: map[string]bool{},
|
|
GoPackages: map[string]*gotypes.Package{},
|
|
Mappings: &mappingState{functions: map[string]string{}},
|
|
}
|
|
for index := range program.Classes {
|
|
decl := &program.Classes[index]
|
|
class := &ClassSymbol{Name: decl.Name, Decl: decl, Fields: map[string]*Symbol{}, Methods: map[string]*Symbol{}}
|
|
semantic.Classes[decl.Name] = *decl
|
|
semantic.ClassInfo[decl.Name] = class
|
|
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: ClassSymbolKind, Type: ClassType{Class: class}, Decl: decl}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for index := range program.Enums {
|
|
decl := &program.Enums[index]
|
|
semantic.Enums[decl.Name] = *decl
|
|
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: EnumSymbolKind, Type: NamedType{Name: decl.Name}, Decl: decl}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for _, imported := range program.Imports {
|
|
name := imported.Alias
|
|
if name == "" {
|
|
name = defaultImportAlias(imported)
|
|
}
|
|
semantic.Imports[name] = true
|
|
if importedPackage, err := importGoPackage(imported); err == nil {
|
|
semantic.GoPackages[name] = importedPackage
|
|
}
|
|
if existing, ok := semantic.Global.Lookup(name); ok && existing.Kind == ImportSymbolKind {
|
|
continue
|
|
}
|
|
if err := semantic.Global.Define(&Symbol{Name: name, Kind: ImportSymbolKind, Type: NamedType{Name: name}, Decl: imported}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for index := range program.Functions {
|
|
decl := &program.Functions[index]
|
|
semantic.Functions[decl.Name] = *decl
|
|
typ, err := semantic.functionType(decl.Params, decl.ReturnType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("function %s: %w", decl.Name, err)
|
|
}
|
|
if err := semantic.Global.Define(&Symbol{Name: decl.Name, Kind: FunctionSymbolKind, Type: typ, Decl: decl}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for index := range program.Classes {
|
|
decl := &program.Classes[index]
|
|
class := semantic.ClassInfo[decl.Name]
|
|
for fieldIndex := range decl.Fields {
|
|
field := &decl.Fields[fieldIndex]
|
|
typ, err := semantic.ResolveType(field.Type)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("field %s.%s: %w", decl.Name, field.Name, err)
|
|
}
|
|
class.Fields[field.Name] = &Symbol{Name: field.Name, Kind: VariableSymbol, Type: typ, Mutable: field.Mutable, Decl: field}
|
|
}
|
|
for methodIndex := range decl.Methods {
|
|
method := &decl.Methods[methodIndex]
|
|
typ, err := semantic.functionType(method.Params, method.ReturnType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("method %s.%s: %w", decl.Name, method.Name, err)
|
|
}
|
|
class.Methods[method.Name] = &Symbol{Name: method.Name, Kind: FunctionSymbolKind, Type: typ, Decl: method}
|
|
}
|
|
}
|
|
if err := validateMutability(semantic); err != nil {
|
|
return nil, err
|
|
}
|
|
resolver := semanticResolver{program: semantic}
|
|
if err := resolver.resolve(); err != nil {
|
|
return nil, err
|
|
}
|
|
return semantic, nil
|
|
}
|
|
|
|
func (semantic *SemanticProgram) ResolveType(text string) (Type, error) {
|
|
typ, err := ParseType(text)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resolved := resolveClassTypes(typ, semantic.ClassInfo)
|
|
if err := validateNoClassPointer(resolved); err != nil {
|
|
return nil, err
|
|
}
|
|
return resolved, nil
|
|
}
|
|
|
|
func validateNoClassPointer(typ Type) error {
|
|
switch value := typ.(type) {
|
|
case GoPointerType:
|
|
if class, ok := value.Element.(ClassType); ok {
|
|
return fmt.Errorf("Gotlin class %s is already reference-valued; remove '*'", class.Class.Name)
|
|
}
|
|
return validateNoClassPointer(value.Element)
|
|
case NullableType:
|
|
return validateNoClassPointer(value.Element)
|
|
case GenericType:
|
|
for _, arg := range value.Args {
|
|
if err := validateNoClassPointer(arg); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case FunctionType:
|
|
for _, param := range value.Params {
|
|
if err := validateNoClassPointer(param); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return validateNoClassPointer(value.Result)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (semantic *SemanticProgram) functionType(params []Param, result string) (Type, error) {
|
|
paramTypes := make([]Type, len(params))
|
|
for index, param := range params {
|
|
typ, err := semantic.ResolveType(param.Type)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
paramTypes[index] = typ
|
|
}
|
|
resultType, err := semantic.ResolveType(result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return FunctionType{Params: paramTypes, Result: resultType}, nil
|
|
}
|
|
|
|
func (semantic *SemanticProgram) GoType(text string) string {
|
|
typ, err := semantic.ResolveType(text)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return renderGoType(typ)
|
|
}
|