fspl/analyzer/tree.go

128 lines
3.7 KiB
Go

package analyzer
import "github.com/alecthomas/participle/v2"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/parser"
// Tree is a semantic tree. It contains the same constructs as the syntax tree,
// but with their semantic information filled in.
type Tree struct {
scopeContextManager
rawTypes map[string] *entity.Typedef
rawFunctions map[string] *entity.Function
rawMethods map[string] *entity.Method
ast parser.Tree
incompleteTypes map[string] *entity.Typedef
Types map[string] *entity.Typedef
Functions map[string] *entity.Function
}
// Analyze takes in an AST and analyzes its contents within the context of this
// semantic tree. Analyzed entities will be added to the tree.
func (this *Tree) Analyze (ast parser.Tree) error {
this.ensure()
this.ast = ast
err := this.assembleRawMaps()
if err != nil { return err }
return this.analyzeDeclarations()
}
func (this *Tree) assembleRawMaps () error {
for _, declaration := range this.ast.Declarations {
switch declaration.(type) {
case *entity.Typedef:
ty := declaration.(*entity.Typedef)
err := this.topLevelNameAvailable(ty.Pos, ty.Name)
if err != nil { return err }
ty.Methods = make(map[string] *entity.Method)
this.rawTypes[ty.Name] = ty
case *entity.Function:
function := declaration.(*entity.Function)
err := this.topLevelNameAvailable (
function.Pos,
function.Signature.Name)
if err != nil { return err }
this.rawFunctions[function.Signature.Name] = function
case *entity.Method:
method := declaration.(*entity.Method)
name := method.TypeName + "." + method.Signature.Name
err := this.topLevelNameAvailable(method.Pos, name)
if err != nil { return err }
this.rawMethods[name] = method
}
}
return nil
}
func (this *Tree) topLevelNameAvailable (currentPos lexer.Position, name string) error {
if _, isPrimitive := primitiveTypes[name]; isPrimitive {
return participle.Errorf (
currentPos, "cannot shadow primitive %s",
name)
}
if _, isBuiltin := builtinTypes[name]; isBuiltin {
return participle.Errorf (
currentPos, "cannot shadow builtin %s",
name)
}
if ty, isType := this.rawTypes[name]; isType {
return participle.Errorf (
currentPos, "%s already declared at %v",
name, ty.Pos)
}
if function, isFunction := this.rawFunctions[name]; isFunction {
return participle.Errorf (
currentPos, "%s already declared at %v",
name, function.Pos)
}
if method, isMethod := this.rawMethods[name]; isMethod {
return participle.Errorf (
currentPos, "%s already declared at %v",
name, method.Pos)
}
return nil
}
func (this *Tree) analyzeDeclarations () error {
for name, rawType := range this.rawTypes {
ty, err := this.analyzeTypedef(rawType.Pos, name, false)
if err != nil { return err }
this.Types[name] = ty
}
for name, rawFunction := range this.rawFunctions {
_, err := this.analyzeFunction(rawFunction.Pos, name)
if err != nil { return err }
}
for _, rawMethod := range this.rawMethods {
_, err := this.analyzeMethod (
rawMethod.Pos,
rawMethod.TypeName,
rawMethod.Signature.Name)
if err != nil { return err }
}
return nil
}
func (this *Tree) ensure () {
if this.rawTypes != nil { return }
this.rawTypes = make(map[string] *entity.Typedef)
this.rawFunctions = make(map[string] *entity.Function)
this.rawMethods = make(map[string] *entity.Method)
this.incompleteTypes = make(map[string] *entity.Typedef)
this.Types = make(map[string] *entity.Typedef)
this.Functions = make(map[string] *entity.Function)
for name, ty := range builtinTypes {
access := entity.AccessPublic
this.Types[name] = &entity.Typedef {
Acc: &access,
Name: name,
Type: ty,
}
}
}