Introduce typed semantic analysis pipeline
This commit is contained in:
parent
117d188194
commit
f4cd4f4458
30 changed files with 2079 additions and 2381 deletions
765
internal/lang/hir.go
Normal file
765
internal/lang/hir.go
Normal file
|
|
@ -0,0 +1,765 @@
|
|||
package lang
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ExprMeaning int
|
||||
|
||||
const (
|
||||
UnresolvedExpr ExprMeaning = iota
|
||||
LiteralExpr
|
||||
LocalReferenceExpr
|
||||
FunctionReferenceExpr
|
||||
ClassReferenceExpr
|
||||
EnumReferenceExpr
|
||||
ImportReferenceExpr
|
||||
GotlinCallExpr
|
||||
GoCallExpr
|
||||
ClassConstructionExpr
|
||||
EnumConstructionExpr
|
||||
FieldAccessExpr
|
||||
MethodCallExpr
|
||||
PropagateResultExpr
|
||||
MatchValueExpr
|
||||
SQLExpression
|
||||
MappingExpression
|
||||
CoroutineExpression
|
||||
)
|
||||
|
||||
type ExprMeta struct{ Semantic *HIRExpr }
|
||||
|
||||
type HIRExpr struct {
|
||||
Type Type
|
||||
Meaning ExprMeaning
|
||||
Symbol *Symbol
|
||||
Node HIRNode
|
||||
}
|
||||
|
||||
type HIRNode interface{ hirNode() }
|
||||
|
||||
type HIRLiteral struct{}
|
||||
|
||||
func (HIRLiteral) hirNode() {}
|
||||
|
||||
type HIRReference struct{ Target *Symbol }
|
||||
|
||||
func (HIRReference) hirNode() {}
|
||||
|
||||
type HIRGoCall struct {
|
||||
Callee *HIRExpr
|
||||
Result Type
|
||||
}
|
||||
|
||||
func (HIRGoCall) hirNode() {}
|
||||
|
||||
type HIRGotlinCall struct {
|
||||
Target *Symbol
|
||||
Result Type
|
||||
}
|
||||
|
||||
func (HIRGotlinCall) hirNode() {}
|
||||
|
||||
type HIRClassConstruction struct{ Class *ClassSymbol }
|
||||
|
||||
func (HIRClassConstruction) hirNode() {}
|
||||
|
||||
type HIREnumConstruction struct {
|
||||
EnumName, VariantName string
|
||||
}
|
||||
|
||||
func (HIREnumConstruction) hirNode() {}
|
||||
|
||||
type HIRPropagateResult struct {
|
||||
Value *HIRExpr
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (HIRPropagateResult) hirNode() {}
|
||||
|
||||
type HIRMatch struct {
|
||||
Value *HIRExpr
|
||||
Cases []HIRMatchCase
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (HIRMatch) hirNode() {}
|
||||
|
||||
type HIRMatchCase struct {
|
||||
EnumName, VariantName string
|
||||
Bindings []string
|
||||
Value *HIRExpr
|
||||
}
|
||||
|
||||
type HIRCoroutine struct{ Operation string }
|
||||
|
||||
func (HIRCoroutine) hirNode() {}
|
||||
|
||||
type HIRSQL struct{ Type Type }
|
||||
|
||||
func (HIRSQL) hirNode() {}
|
||||
|
||||
type HIRMapping struct {
|
||||
Source Type
|
||||
Target Type
|
||||
}
|
||||
|
||||
func (HIRMapping) hirNode() {}
|
||||
|
||||
type HIRFunction struct {
|
||||
Symbol *Symbol
|
||||
Decl *FunctionDecl
|
||||
Scope *Scope
|
||||
}
|
||||
|
||||
type HIRProgram struct {
|
||||
Functions []*HIRFunction
|
||||
Methods []*HIRFunction
|
||||
}
|
||||
|
||||
func exprMeta(expr Expr) *HIRExpr {
|
||||
switch value := expr.(type) {
|
||||
case IdentExpr:
|
||||
return value.Meta.Semantic
|
||||
case IntExpr:
|
||||
return value.Meta.Semantic
|
||||
case FloatExpr:
|
||||
return value.Meta.Semantic
|
||||
case StringExpr:
|
||||
return value.Meta.Semantic
|
||||
case BoolExpr:
|
||||
return value.Meta.Semantic
|
||||
case NullExpr:
|
||||
return value.Meta.Semantic
|
||||
case UnaryExpr:
|
||||
return value.Meta.Semantic
|
||||
case BinaryExpr:
|
||||
return value.Meta.Semantic
|
||||
case CallExpr:
|
||||
return value.Meta.Semantic
|
||||
case SelectorExpr:
|
||||
return value.Meta.Semantic
|
||||
case SafeSelectorExpr:
|
||||
return value.Meta.Semantic
|
||||
case NonNullExpr:
|
||||
return value.Meta.Semantic
|
||||
case TryExpr:
|
||||
return value.Meta.Semantic
|
||||
case IndexExpr:
|
||||
return value.Meta.Semantic
|
||||
case EnumVariantExpr:
|
||||
return value.Meta.Semantic
|
||||
case MatchExpr:
|
||||
return value.Meta.Semantic
|
||||
case LambdaExpr:
|
||||
return value.Meta.Semantic
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ResolvedType(expr Expr) Type {
|
||||
if semantic := exprMeta(expr); semantic != nil {
|
||||
return semantic.Type
|
||||
}
|
||||
return UnknownType{}
|
||||
}
|
||||
|
||||
func ResolvedMeaning(expr Expr) ExprMeaning {
|
||||
if semantic := exprMeta(expr); semantic != nil {
|
||||
return semantic.Meaning
|
||||
}
|
||||
return UnresolvedExpr
|
||||
}
|
||||
|
||||
func withExprMeta(expr Expr, semantic *HIRExpr) Expr {
|
||||
switch value := expr.(type) {
|
||||
case IdentExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case IntExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case FloatExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case StringExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case BoolExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case NullExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case UnaryExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case BinaryExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case CallExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case SelectorExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case SafeSelectorExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case NonNullExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case TryExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case IndexExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case EnumVariantExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case MatchExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
case LambdaExpr:
|
||||
value.Meta.Semantic = semantic
|
||||
return value
|
||||
default:
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
type semanticResolver struct {
|
||||
program *SemanticProgram
|
||||
err error
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) resolve() error {
|
||||
resolver.program.HIR = &HIRProgram{}
|
||||
for index := range resolver.program.Syntax.Functions {
|
||||
decl := &resolver.program.Syntax.Functions[index]
|
||||
symbol, _ := resolver.program.Global.Lookup(decl.Name)
|
||||
scope := NewScope(resolver.program.Global)
|
||||
for _, param := range decl.Params {
|
||||
typ, _ := resolver.program.ResolveType(param.Type)
|
||||
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
result, _ := resolver.program.ResolveType(decl.ReturnType)
|
||||
resolver.resolveStmts(decl.Body, scope, nil, result)
|
||||
resolver.program.HIR.Functions = append(resolver.program.HIR.Functions, &HIRFunction{Symbol: symbol, Decl: decl, Scope: scope})
|
||||
}
|
||||
for classIndex := range resolver.program.Syntax.Classes {
|
||||
decl := &resolver.program.Syntax.Classes[classIndex]
|
||||
class := resolver.program.ClassInfo[decl.Name]
|
||||
for methodIndex := range decl.Methods {
|
||||
method := &decl.Methods[methodIndex]
|
||||
scope := NewScope(resolver.program.Global)
|
||||
_ = scope.Define(&Symbol{Name: "this", Kind: VariableSymbol, Type: ClassType{Class: class}})
|
||||
for _, param := range method.Params {
|
||||
typ, _ := resolver.program.ResolveType(param.Type)
|
||||
_ = scope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
result, _ := resolver.program.ResolveType(method.ReturnType)
|
||||
resolver.resolveStmts(method.Body, scope, class, result)
|
||||
resolver.program.HIR.Methods = append(resolver.program.HIR.Methods, &HIRFunction{Symbol: class.Methods[method.Name], Decl: method, Scope: scope})
|
||||
}
|
||||
}
|
||||
return resolver.err
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) resolveStmts(stmts []Stmt, scope *Scope, class *ClassSymbol, returnType Type) {
|
||||
for index, stmt := range stmts {
|
||||
switch value := stmt.(type) {
|
||||
case VarDecl:
|
||||
expected := Type(UnknownType{})
|
||||
if value.Type != "" {
|
||||
expected, _ = resolver.program.ResolveType(value.Type)
|
||||
}
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, expected)
|
||||
if isUnknownType(expected) {
|
||||
expected = exprMeta(value.Value).Type
|
||||
}
|
||||
_ = scope.Define(&Symbol{Name: value.Name, Kind: VariableSymbol, Type: expected, Mutable: value.Mutable, Decl: &value})
|
||||
stmts[index] = value
|
||||
case MultiVarDecl:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
valueTypes := make([]Type, len(value.Names))
|
||||
for index := range valueTypes {
|
||||
valueTypes[index] = UnknownType{}
|
||||
}
|
||||
if result, ok := ResolvedType(value.Value).(GenericType); ok && result.Base.String() == "Result" && len(result.Args) == 2 && len(valueTypes) == 2 {
|
||||
valueTypes[0] = result.Args[0]
|
||||
valueTypes[1] = NullableType{Element: result.Args[1]}
|
||||
}
|
||||
for index, name := range value.Names {
|
||||
_ = scope.Define(&Symbol{Name: name, Kind: VariableSymbol, Type: valueTypes[index], Mutable: value.Mutable})
|
||||
}
|
||||
stmts[index] = value
|
||||
case AssignStmt:
|
||||
expected := Type(UnknownType{})
|
||||
if symbol, ok := scope.Lookup(value.Name); ok {
|
||||
expected = symbol.Type
|
||||
}
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, expected)
|
||||
stmts[index] = value
|
||||
case AddAssignStmt:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
stmts[index] = value
|
||||
case MultiAssignStmt:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
stmts[index] = value
|
||||
case ReturnStmt:
|
||||
if value.Value != nil {
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, returnType)
|
||||
stmts[index] = value
|
||||
}
|
||||
case ThrowStmt:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, NamedType{Name: "Error"})
|
||||
stmts[index] = value
|
||||
case DeferStmt:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, NamedType{Name: "Unit"})
|
||||
stmts[index] = value
|
||||
case ExprStmt:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
stmts[index] = value
|
||||
case IfStmt:
|
||||
value.Cond, _ = resolver.resolveExpr(value.Cond, scope, class, NamedType{Name: "Boolean"})
|
||||
resolver.resolveStmts(value.Then, NewScope(scope), class, returnType)
|
||||
resolver.resolveStmts(value.Else, NewScope(scope), class, returnType)
|
||||
stmts[index] = value
|
||||
case WhileStmt:
|
||||
value.Cond, _ = resolver.resolveExpr(value.Cond, scope, class, NamedType{Name: "Boolean"})
|
||||
resolver.resolveStmts(value.Body, NewScope(scope), class, returnType)
|
||||
stmts[index] = value
|
||||
case ForEachStmt:
|
||||
value.Source, _ = resolver.resolveExpr(value.Source, scope, class, UnknownType{})
|
||||
bodyScope := NewScope(scope)
|
||||
element := collectionElement(exprMeta(value.Source).Type)
|
||||
_ = bodyScope.Define(&Symbol{Name: value.Name, Kind: VariableSymbol, Type: element})
|
||||
resolver.resolveStmts(value.Body, bodyScope, class, returnType)
|
||||
stmts[index] = value
|
||||
case MatchStmt:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
for caseIndex := range value.Cases {
|
||||
matchCase := &value.Cases[caseIndex]
|
||||
caseScope := NewScope(scope)
|
||||
resolver.defineMatchBindings(caseScope, matchCase.EnumName, matchCase.VariantName, matchCase.Bindings)
|
||||
resolver.resolveStmts(matchCase.Body, caseScope, class, returnType)
|
||||
}
|
||||
resolver.validateStatementMatch(value)
|
||||
stmts[index] = value
|
||||
case TryCatchStmt:
|
||||
resolver.resolveStmts(value.TryBody, NewScope(scope), class, returnType)
|
||||
catchScope := NewScope(scope)
|
||||
catchType, _ := resolver.program.ResolveType(value.CatchType)
|
||||
_ = catchScope.Define(&Symbol{Name: value.CatchName, Kind: VariableSymbol, Type: catchType})
|
||||
resolver.resolveStmts(value.CatchBody, catchScope, class, returnType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) resolveExpr(expr Expr, scope *Scope, class *ClassSymbol, expected Type) (Expr, Type) {
|
||||
environment := TypeEnvironment{Scope: scope, Class: class}
|
||||
switch value := expr.(type) {
|
||||
case UnaryExpr:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
expr = value
|
||||
case BinaryExpr:
|
||||
value.Left, _ = resolver.resolveExpr(value.Left, scope, class, UnknownType{})
|
||||
value.Right, _ = resolver.resolveExpr(value.Right, scope, class, UnknownType{})
|
||||
expr = value
|
||||
case CallExpr:
|
||||
value.Callee, _ = resolver.resolveExpr(value.Callee, scope, class, UnknownType{})
|
||||
for index := range value.Args {
|
||||
argumentExpected := Type(UnknownType{})
|
||||
if selector, ok := value.Callee.(SelectorExpr); ok {
|
||||
if receiver, ok := selector.Receiver.(IdentExpr); ok && receiver.Name == "Result" {
|
||||
if selector.Name == "Err" {
|
||||
argumentExpected = NamedType{Name: "Error"}
|
||||
} else if selector.Name == "Ok" {
|
||||
if result, ok := expected.(GenericType); ok && result.Base.String() == "Result" && len(result.Args) == 2 {
|
||||
argumentExpected = result.Args[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
value.Args[index], _ = resolver.resolveExpr(value.Args[index], scope, class, argumentExpected)
|
||||
}
|
||||
for index := range value.NamedArgs {
|
||||
value.NamedArgs[index].Value, _ = resolver.resolveExpr(value.NamedArgs[index].Value, scope, class, UnknownType{})
|
||||
}
|
||||
expr = value
|
||||
case SelectorExpr:
|
||||
value.Receiver, _ = resolver.resolveExpr(value.Receiver, scope, class, UnknownType{})
|
||||
expr = value
|
||||
case SafeSelectorExpr:
|
||||
value.Receiver, _ = resolver.resolveExpr(value.Receiver, scope, class, UnknownType{})
|
||||
expr = value
|
||||
case NonNullExpr:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
expr = value
|
||||
case TryExpr:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
expr = value
|
||||
case IndexExpr:
|
||||
value.Receiver, _ = resolver.resolveExpr(value.Receiver, scope, class, UnknownType{})
|
||||
value.Index, _ = resolver.resolveExpr(value.Index, scope, class, NamedType{Name: "Int"})
|
||||
expr = value
|
||||
case EnumVariantExpr:
|
||||
for index := range value.Values {
|
||||
value.Values[index], _ = resolver.resolveExpr(value.Values[index], scope, class, UnknownType{})
|
||||
}
|
||||
expr = value
|
||||
case MatchExpr:
|
||||
value.Value, _ = resolver.resolveExpr(value.Value, scope, class, UnknownType{})
|
||||
for index := range value.Cases {
|
||||
matchCase := &value.Cases[index]
|
||||
caseScope := NewScope(scope)
|
||||
resolver.defineMatchBindings(caseScope, matchCase.EnumName, matchCase.VariantName, matchCase.Bindings)
|
||||
matchCase.Value, _ = resolver.resolveExpr(matchCase.Value, caseScope, class, expected)
|
||||
}
|
||||
resolver.validateValueMatch(value)
|
||||
expr = value
|
||||
case LambdaExpr:
|
||||
lambdaScope := NewScope(scope)
|
||||
for _, param := range value.Params {
|
||||
typ, _ := resolver.program.ResolveType(param.Type)
|
||||
_ = lambdaScope.Define(&Symbol{Name: param.Name, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
resolver.resolveStmts(value.Body, lambdaScope, class, functionResult(expected))
|
||||
expr = value
|
||||
}
|
||||
typ := resolver.program.TypeOf(expr, environment)
|
||||
if isUnknownType(typ) && !isUnknownType(expected) {
|
||||
typ = expected
|
||||
}
|
||||
if binary, ok := expr.(BinaryExpr); ok {
|
||||
switch binary.Op {
|
||||
case "==", "!=", "<", "<=", ">", ">=", "&&", "||":
|
||||
typ = NamedType{Name: "Boolean"}
|
||||
default:
|
||||
if meta := exprMeta(binary.Left); meta != nil {
|
||||
typ = meta.Type
|
||||
}
|
||||
}
|
||||
}
|
||||
meaning, symbol := resolver.meaning(expr, scope)
|
||||
resolver.validateEnumExpression(expr, meaning)
|
||||
if meaning == GoCallExpr && isResultType(expected) {
|
||||
typ = expected
|
||||
}
|
||||
if nullable, ok := typ.(NullableType); ok && typeEqual(nullable.Element, expected) {
|
||||
typ = expected
|
||||
}
|
||||
semantic := &HIRExpr{Type: typ, Meaning: meaning, Symbol: symbol}
|
||||
semantic.Node = resolver.hirNode(expr, semantic)
|
||||
return withExprMeta(expr, semantic), typ
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) hirNode(expr Expr, semantic *HIRExpr) HIRNode {
|
||||
switch semantic.Meaning {
|
||||
case LiteralExpr:
|
||||
return HIRLiteral{}
|
||||
case LocalReferenceExpr, FunctionReferenceExpr, ClassReferenceExpr, EnumReferenceExpr, ImportReferenceExpr, FieldAccessExpr:
|
||||
return HIRReference{Target: semantic.Symbol}
|
||||
case GoCallExpr:
|
||||
if call, ok := expr.(CallExpr); ok {
|
||||
return HIRGoCall{Callee: exprMeta(call.Callee), Result: semantic.Type}
|
||||
}
|
||||
case GotlinCallExpr, MethodCallExpr:
|
||||
return HIRGotlinCall{Target: semantic.Symbol, Result: semantic.Type}
|
||||
case ClassConstructionExpr:
|
||||
if class := classTypeOf(semantic.Type); class != nil {
|
||||
return HIRClassConstruction{Class: class}
|
||||
}
|
||||
case EnumConstructionExpr:
|
||||
enumName, variantName := enumExpressionName(expr)
|
||||
return HIREnumConstruction{EnumName: enumName, VariantName: variantName}
|
||||
case PropagateResultExpr:
|
||||
if attempt, ok := expr.(TryExpr); ok {
|
||||
return HIRPropagateResult{Value: exprMeta(attempt.Value), Type: semantic.Type}
|
||||
}
|
||||
case MatchValueExpr:
|
||||
if match, ok := expr.(MatchExpr); ok {
|
||||
cases := make([]HIRMatchCase, len(match.Cases))
|
||||
for index, matchCase := range match.Cases {
|
||||
cases[index] = HIRMatchCase{EnumName: matchCase.EnumName, VariantName: matchCase.VariantName, Bindings: matchCase.Bindings, Value: exprMeta(matchCase.Value)}
|
||||
}
|
||||
return HIRMatch{Value: exprMeta(match.Value), Cases: cases, Type: semantic.Type}
|
||||
}
|
||||
case CoroutineExpression:
|
||||
if call, ok := expr.(CallExpr); ok {
|
||||
if ident, ok := call.Callee.(IdentExpr); ok {
|
||||
return HIRCoroutine{Operation: ident.Name}
|
||||
}
|
||||
}
|
||||
case SQLExpression:
|
||||
return HIRSQL{Type: semantic.Type}
|
||||
case MappingExpression:
|
||||
if call, ok := expr.(CallExpr); ok {
|
||||
if selector, ok := call.Callee.(SelectorExpr); ok {
|
||||
target := semantic.Type
|
||||
return HIRMapping{Source: ResolvedType(selector.Receiver), Target: target}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func enumExpressionName(expr Expr) (string, string) {
|
||||
switch value := expr.(type) {
|
||||
case CallExpr:
|
||||
if selector, ok := value.Callee.(SelectorExpr); ok {
|
||||
if receiver, ok := selector.Receiver.(IdentExpr); ok {
|
||||
return receiver.Name, selector.Name
|
||||
}
|
||||
}
|
||||
case SelectorExpr:
|
||||
if receiver, ok := value.Receiver.(IdentExpr); ok {
|
||||
return receiver.Name, value.Name
|
||||
}
|
||||
case EnumVariantExpr:
|
||||
return value.EnumName, value.VariantName
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) meaning(expr Expr, scope *Scope) (ExprMeaning, *Symbol) {
|
||||
switch value := expr.(type) {
|
||||
case IntExpr, FloatExpr, StringExpr, BoolExpr, NullExpr:
|
||||
return LiteralExpr, nil
|
||||
case IdentExpr:
|
||||
if symbol, ok := scope.Lookup(value.Name); ok {
|
||||
switch symbol.Kind {
|
||||
case FunctionSymbolKind:
|
||||
return FunctionReferenceExpr, symbol
|
||||
case ClassSymbolKind:
|
||||
return ClassReferenceExpr, symbol
|
||||
case EnumSymbolKind:
|
||||
return EnumReferenceExpr, symbol
|
||||
case ImportSymbolKind:
|
||||
return ImportReferenceExpr, symbol
|
||||
default:
|
||||
return LocalReferenceExpr, symbol
|
||||
}
|
||||
}
|
||||
case CallExpr:
|
||||
if _, _, _, ok := splitSQLChain(value); ok {
|
||||
return SQLExpression, nil
|
||||
}
|
||||
if _, ok := value.Callee.(SelectorExpr); ok {
|
||||
selector := value.Callee.(SelectorExpr)
|
||||
if receiver, ok := selector.Receiver.(IdentExpr); ok && receiver.Name == "json" && selector.Name == "decode" {
|
||||
return GotlinCallExpr, nil
|
||||
}
|
||||
if selector.Name == "mapTo" {
|
||||
return MappingExpression, nil
|
||||
}
|
||||
if receiver, ok := selector.Receiver.(IdentExpr); ok {
|
||||
if receiver.Name == "Result" {
|
||||
return EnumConstructionExpr, nil
|
||||
}
|
||||
if _, ok := resolver.program.Enums[receiver.Name]; ok {
|
||||
return EnumConstructionExpr, nil
|
||||
}
|
||||
}
|
||||
if root, ok := selectorRootAlias(selector); ok && resolver.program.Imports[root] {
|
||||
return GoCallExpr, nil
|
||||
}
|
||||
receiverType := ResolvedType(selector.Receiver)
|
||||
if !isUnknownType(resolver.program.goMethodType(receiverType, selector.Name)) {
|
||||
return GoCallExpr, nil
|
||||
}
|
||||
return MethodCallExpr, nil
|
||||
}
|
||||
if ident, ok := value.Callee.(IdentExpr); ok {
|
||||
if coroutineBuiltins[ident.Name] {
|
||||
return CoroutineExpression, nil
|
||||
}
|
||||
if symbol, ok := resolver.program.Global.Lookup(ident.Name); ok {
|
||||
if symbol.Kind == ClassSymbolKind {
|
||||
return ClassConstructionExpr, symbol
|
||||
}
|
||||
if symbol.Kind == FunctionSymbolKind {
|
||||
return GotlinCallExpr, symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
return GoCallExpr, nil
|
||||
case SelectorExpr:
|
||||
if receiver, ok := value.Receiver.(IdentExpr); ok {
|
||||
if _, ok := resolver.program.Enums[receiver.Name]; ok {
|
||||
return EnumReferenceExpr, nil
|
||||
}
|
||||
}
|
||||
return FieldAccessExpr, nil
|
||||
case SafeSelectorExpr:
|
||||
return FieldAccessExpr, nil
|
||||
case EnumVariantExpr:
|
||||
return EnumConstructionExpr, nil
|
||||
case TryExpr:
|
||||
return PropagateResultExpr, nil
|
||||
case MatchExpr:
|
||||
return MatchValueExpr, nil
|
||||
}
|
||||
return UnresolvedExpr, nil
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) defineMatchBindings(scope *Scope, enumName, variantName string, bindings []string) {
|
||||
decl, ok := resolver.program.Enums[enumName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
variant := enumVariant(decl, variantName)
|
||||
if variant == nil {
|
||||
return
|
||||
}
|
||||
for index, binding := range bindings {
|
||||
if index >= len(variant.PayloadTypes) {
|
||||
break
|
||||
}
|
||||
typ, _ := resolver.program.ResolveType(variant.PayloadTypes[index])
|
||||
_ = scope.Define(&Symbol{Name: binding, Kind: VariableSymbol, Type: typ})
|
||||
}
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) validateStatementMatch(match MatchStmt) {
|
||||
patterns := make([]matchPattern, len(match.Cases))
|
||||
for index, matchCase := range match.Cases {
|
||||
patterns[index] = matchPattern{enumName: matchCase.EnumName, variantName: matchCase.VariantName, bindings: matchCase.Bindings}
|
||||
}
|
||||
resolver.validateMatchPatterns(patterns)
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) validateValueMatch(match MatchExpr) {
|
||||
patterns := make([]matchPattern, len(match.Cases))
|
||||
var result Type = UnknownType{}
|
||||
for index, matchCase := range match.Cases {
|
||||
patterns[index] = matchPattern{enumName: matchCase.EnumName, variantName: matchCase.VariantName, bindings: matchCase.Bindings}
|
||||
armType := ResolvedType(matchCase.Value)
|
||||
if isUnknownType(result) {
|
||||
result = armType
|
||||
} else if !isUnknownType(armType) && !typeEqual(result, armType) {
|
||||
resolver.fail(fmt.Errorf("match expression arm %s.%s has type %s, expected %s", matchCase.EnumName, matchCase.VariantName, armType.String(), result.String()))
|
||||
}
|
||||
}
|
||||
if isUnknownType(result) {
|
||||
resolver.fail(fmt.Errorf("match expression result type cannot be inferred"))
|
||||
}
|
||||
resolver.validateMatchPatterns(patterns)
|
||||
}
|
||||
|
||||
type matchPattern struct {
|
||||
enumName, variantName string
|
||||
bindings []string
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) validateMatchPatterns(patterns []matchPattern) {
|
||||
if len(patterns) == 0 {
|
||||
resolver.fail(fmt.Errorf("match requires at least one case"))
|
||||
return
|
||||
}
|
||||
enumName := patterns[0].enumName
|
||||
decl, ok := resolver.program.Enums[enumName]
|
||||
if !ok {
|
||||
resolver.fail(fmt.Errorf("match value is not a known enum"))
|
||||
return
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, pattern := range patterns {
|
||||
if pattern.enumName != enumName {
|
||||
resolver.fail(fmt.Errorf("match case %s.%s does not match enum %s", pattern.enumName, pattern.variantName, enumName))
|
||||
return
|
||||
}
|
||||
if seen[pattern.variantName] {
|
||||
resolver.fail(fmt.Errorf("duplicate match case %s.%s", enumName, pattern.variantName))
|
||||
return
|
||||
}
|
||||
seen[pattern.variantName] = true
|
||||
variant := enumVariant(decl, pattern.variantName)
|
||||
if variant == nil {
|
||||
resolver.fail(fmt.Errorf("unknown variant %s.%s", enumName, pattern.variantName))
|
||||
return
|
||||
}
|
||||
if len(pattern.bindings) != len(variant.PayloadTypes) {
|
||||
resolver.fail(fmt.Errorf("match case %s.%s expects %d bindings", enumName, pattern.variantName, len(variant.PayloadTypes)))
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, variant := range decl.Variants {
|
||||
if !seen[variant.Name] {
|
||||
resolver.fail(fmt.Errorf("non-exhaustive match for %s: missing %s", enumName, variant.Name))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) fail(err error) {
|
||||
if resolver.err == nil {
|
||||
resolver.err = err
|
||||
}
|
||||
}
|
||||
|
||||
func (resolver *semanticResolver) validateEnumExpression(expr Expr, meaning ExprMeaning) {
|
||||
if meaning != EnumConstructionExpr {
|
||||
return
|
||||
}
|
||||
var enumName, variantName string
|
||||
valueCount := 0
|
||||
switch value := expr.(type) {
|
||||
case CallExpr:
|
||||
selector, ok := value.Callee.(SelectorExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
receiver, ok := selector.Receiver.(IdentExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
enumName, variantName, valueCount = receiver.Name, selector.Name, len(value.Args)
|
||||
case SelectorExpr:
|
||||
receiver, ok := value.Receiver.(IdentExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
enumName, variantName = receiver.Name, value.Name
|
||||
default:
|
||||
return
|
||||
}
|
||||
if enumName == "Result" {
|
||||
if valueCount != 1 {
|
||||
resolver.fail(fmt.Errorf("Result.%s expects one value", variantName))
|
||||
}
|
||||
return
|
||||
}
|
||||
decl, ok := resolver.program.Enums[enumName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
variant := enumVariant(decl, variantName)
|
||||
if variant == nil {
|
||||
resolver.fail(fmt.Errorf("unknown variant %s.%s", enumName, variantName))
|
||||
return
|
||||
}
|
||||
if len(variant.PayloadTypes) != valueCount {
|
||||
resolver.fail(fmt.Errorf("variant %s.%s expects %d values", enumName, variantName, len(variant.PayloadTypes)))
|
||||
}
|
||||
}
|
||||
|
||||
func collectionElement(typ Type) Type {
|
||||
if generic, ok := typ.(GenericType); ok && len(generic.Args) > 0 {
|
||||
return generic.Args[len(generic.Args)-1]
|
||||
}
|
||||
return UnknownType{}
|
||||
}
|
||||
|
||||
func functionResult(typ Type) Type {
|
||||
if function, ok := typ.(FunctionType); ok {
|
||||
return function.Result
|
||||
}
|
||||
return UnknownType{}
|
||||
}
|
||||
|
||||
func isResultType(typ Type) bool {
|
||||
result, ok := typ.(GenericType)
|
||||
return ok && result.Base.String() == "Result" && len(result.Args) == 2
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue