fspl/analyzer/tree.go
2024-02-08 03:51:21 -05:00

126 lines
3.7 KiB
Go

package analyzer
import "git.tebibyte.media/sashakoshka/fspl/errors"
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.Position, 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.Position,
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.Position, name)
if err != nil { return err }
this.rawMethods[name] = method
}
}
return nil
}
func (this *Tree) topLevelNameAvailable (currentPos errors.Position, name string) error {
if _, isPrimitive := primitiveTypes[name]; isPrimitive {
return errors.Errorf (
currentPos, "cannot shadow primitive %s",
name)
}
if _, isBuiltin := builtinTypes[name]; isBuiltin {
return errors.Errorf (
currentPos, "cannot shadow builtin %s",
name)
}
if ty, isType := this.rawTypes[name]; isType {
return errors.Errorf (
currentPos, "%s already declared at %v",
name, ty.Position)
}
if function, isFunction := this.rawFunctions[name]; isFunction {
return errors.Errorf (
currentPos, "%s already declared at %v",
name, function.Position)
}
if method, isMethod := this.rawMethods[name]; isMethod {
return errors.Errorf (
currentPos, "%s already declared at %v",
name, method.Position)
}
return nil
}
func (this *Tree) analyzeDeclarations () error {
for name, rawType := range this.rawTypes {
ty, err := this.analyzeTypedef(rawType.Position, name, false)
if err != nil { return err }
this.Types[name] = ty
}
for name, rawFunction := range this.rawFunctions {
_, err := this.analyzeFunction(rawFunction.Position, name)
if err != nil { return err }
}
for _, rawMethod := range this.rawMethods {
_, err := this.analyzeMethod (
rawMethod.Position,
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 {
this.Types[name] = &entity.Typedef {
Acc: entity.AccessPublic,
Name: name,
Type: ty,
}
}
}