fspl/analyzer/tree.go

188 lines
5.2 KiB
Go

package analyzer
import "github.com/google/uuid"
import "git.tebibyte.media/fspl/fspl/errors"
import "git.tebibyte.media/fspl/fspl/entity"
import "git.tebibyte.media/fspl/fspl/parser/fspl"
// Tree is a semantic tree. It contains the same constructs as the syntax tree,
// but with their semantic information filled in.
type Tree struct {
Types map[entity.Key] *entity.Typedef
Functions map[entity.Key] *entity.Function
unit uuid.UUID
ast fsplParser.Tree
nicknames map[string] uuid.UUID
rawTypes map[entity.Key] *entity.Typedef
rawFunctions map[entity.Key] *entity.Function
rawMethods map[entity.Key] *entity.Method
scopeContextManager
incompleteTypes map[entity.Key] *entity.Typedef
}
// 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 (
unit uuid.UUID,
nicknames map[string] uuid.UUID,
ast fsplParser.Tree,
) error {
this.ensure()
this.resetRawMaps()
this.ast = ast
this.unit = unit
this.nicknames = nicknames
err := this.assembleRawMaps()
if err != nil { return err }
return this.analyzeDeclarations()
}
func (this *Tree) assembleRawMaps () error {
for _, declaration := range this.ast.Declarations {
switch declaration := declaration.(type) {
case *entity.Typedef:
key := entity.Key {
Unit: this.unit,
Name: declaration.Name,
}
err := this.topLevelNameAvailable(declaration.Position(), key)
if err != nil { return err }
declaration.Methods = make(map[string] *entity.Method)
this.rawTypes[key] = declaration
case *entity.Function:
key := entity.Key {
Unit: this.unit,
Name: declaration.Signature.Name,
}
err := this.topLevelNameAvailable(declaration.Position(), key)
if err != nil { return err }
this.rawFunctions[key] = declaration
case *entity.Method:
key := entity.Key {
Unit: this.unit,
Name: declaration.TypeName,
Method: declaration.Signature.Name,
}
err := this.topLevelNameAvailable(declaration.Position(), key)
if err != nil { return err }
this.rawMethods[key] = declaration
}
}
return nil
}
func (this *Tree) topLevelNameAvailable (currentPos errors.Position, key entity.Key) error {
if fsplParser.IsReserved(key.Name) {
return errors.Errorf (
currentPos, "cannot shadow reserved identifier %s",
key.Name)
}
if _, isPrimitive := primitiveTypes[key.Name]; isPrimitive {
return errors.Errorf (
currentPos, "cannot shadow primitive %s",
key.Name)
}
if _, isBuiltin := builtinTypes[key.Name]; isBuiltin {
return errors.Errorf (
currentPos, "cannot shadow builtin %s",
key.Name)
}
if ty, isType := this.rawTypes[key]; isType {
return errors.Errorf (
currentPos, "%s already declared at %v",
key.Name, ty.Position())
}
if function, isFunction := this.rawFunctions[key]; isFunction {
return errors.Errorf (
currentPos, "%s already declared at %v",
key.Name, function.Position())
}
if method, isMethod := this.rawMethods[key]; isMethod {
return errors.Errorf (
currentPos, "%s.%s already declared at %v",
key.Name, key.Method, method.Position())
}
return nil
}
func (this *Tree) analyzeDeclarations () error {
for key, rawType := range this.rawTypes {
ty, err := this.analyzeTypedef(rawType.Position(), key, false)
if err != nil { return err }
this.Types[key] = ty
}
for key, rawFunction := range this.rawFunctions {
_, err := this.analyzeFunction(rawFunction.Position(), key)
if err != nil { return err }
}
for _, rawMethod := range this.rawMethods {
_, err := this.analyzeMethod (
rawMethod.Position(),
entity.Key {
Unit: this.unit,
Name: rawMethod.TypeName,
Method: rawMethod.Signature.Name,
})
if err != nil { return err }
}
return nil
}
func (this *Tree) resolveNickname (pos errors.Position, nickname string) (uuid.UUID, error) {
if nickname == "" { return this.unit, nil }
fail := func () (uuid.UUID, error) {
return uuid.UUID { }, errors.Errorf (
pos, "no unit named %s",
nickname)
}
if this.nicknames == nil { return fail() }
unit, ok := this.nicknames[nickname]
if !ok { return fail() }
return unit, nil
}
func (this *Tree) ensure () {
if this.rawTypes != nil { return }
this.resetRawMaps()
this.incompleteTypes = make(map[entity.Key] *entity.Typedef)
this.Types = make(map[entity.Key] *entity.Typedef)
this.Functions = make(map[entity.Key] *entity.Function)
for name, ty := range builtinTypes {
// builtin types have a zero UUID in their key
this.Types[entity.Key { Name: name }] = &entity.Typedef {
Acc: entity.AccessPublic,
Name: name,
Type: ty,
}
}
}
func (this *Tree) resetRawMaps () {
if this.rawTypes != nil &&
this.rawFunctions != nil &&
this.rawMethods != nil {
if len(this.rawTypes) > 0 {
this.rawTypes = make(map[entity.Key] *entity.Typedef)
}
if len(this.rawFunctions) > 0 {
this.rawFunctions = make(map[entity.Key] *entity.Function)
}
if len(this.rawMethods) > 0 {
this.rawMethods = make(map[entity.Key] *entity.Method)
}
} else {
this.rawTypes = make(map[entity.Key] *entity.Typedef)
this.rawFunctions = make(map[entity.Key] *entity.Function)
this.rawMethods = make(map[entity.Key] *entity.Method)
}
}