hngnggg i forgor to commit
- implemented scope management - finished function signature analyzing - added method analyzing - had to restructure type analysis slightly to do this
This commit is contained in:
parent
8b4244e3cb
commit
decefce142
|
@ -23,13 +23,19 @@ func (this *Tree) analyzeFunction (
|
|||
if !exists {
|
||||
return nil, participle.Errorf(pos, "no function named %s", name)
|
||||
}
|
||||
|
||||
// create a new scope context for this function
|
||||
this.PushScopeContext()
|
||||
this.PushScope(function)
|
||||
defer this.PopScopeContext()
|
||||
defer this.PopScope()
|
||||
|
||||
// analyze signature
|
||||
// analyze signature and add arguments to root scope of function
|
||||
function.Signature, err = this.assembleSignatureMap(function.Signature)
|
||||
if err != nil { return function, err }
|
||||
for name, argument := range function.Signature.ArgumentMap {
|
||||
argument.Type, err = this.analyzeType(argument.Type, false)
|
||||
// TODO add argument to scope
|
||||
this.AddVariable(argument)
|
||||
function.Signature.ArgumentMap[name] = argument
|
||||
if err != nil { return function, err }
|
||||
}
|
||||
|
@ -37,6 +43,11 @@ func (this *Tree) analyzeFunction (
|
|||
this.analyzeType(function.Signature.Return, false)
|
||||
if err != nil { return function, err }
|
||||
|
||||
// add incomplete function to complete functions because there is enough
|
||||
// information for it to be complete from the point of view of other
|
||||
// parts of the code
|
||||
this.Functions[name] = function
|
||||
|
||||
// TODO: analyze function body
|
||||
return function, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package analyzer
|
||||
|
||||
import "github.com/alecthomas/participle/v2"
|
||||
import "github.com/alecthomas/participle/v2/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
func (this *Tree) analyzeMethod (
|
||||
pos lexer.Position,
|
||||
typeName string,
|
||||
name string,
|
||||
) (
|
||||
*entity.Method,
|
||||
error,
|
||||
) {
|
||||
// get parent typedef
|
||||
owner, err := this.analyzeTypedef(pos, typeName, false)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
|
||||
// return if exists already
|
||||
if method, exists := owner.Methods[name]; exists {
|
||||
return method, nil
|
||||
}
|
||||
|
||||
// error if method is missing
|
||||
method, exists := this.rawMethods[name]
|
||||
if !exists {
|
||||
return nil, participle.Errorf (
|
||||
pos, "no method named %s.%s",
|
||||
typeName, name)
|
||||
}
|
||||
|
||||
// create a new scope context for this function
|
||||
this.PushScopeContext()
|
||||
this.PushScope(method)
|
||||
defer this.PopScopeContext()
|
||||
defer this.PopScope()
|
||||
|
||||
// analyze signature and add arguments to root scope of function
|
||||
method.Signature, err = this.assembleSignatureMap(method.Signature)
|
||||
if err != nil { return method, err }
|
||||
for name, argument := range method.Signature.ArgumentMap {
|
||||
argument.Type, err = this.analyzeType(argument.Type, false)
|
||||
this.AddVariable(argument)
|
||||
method.Signature.ArgumentMap[name] = argument
|
||||
if err != nil { return method, err }
|
||||
}
|
||||
method.Signature.Return, err =
|
||||
this.analyzeType(method.Signature.Return, false)
|
||||
if err != nil { return method, err }
|
||||
|
||||
// add incomplete method to complete methods because there is enough
|
||||
// information for it to be complete from the point of view of other
|
||||
// parts of the code
|
||||
owner.Methods[name] = method
|
||||
|
||||
// TODO: analyze method body
|
||||
return method, nil
|
||||
}
|
|
@ -1,7 +1,82 @@
|
|||
package analyzer
|
||||
|
||||
// type ScopeManager struct {
|
||||
// stacks []
|
||||
// }
|
||||
//
|
||||
// type scopeStack []entity.Scoped
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
// scopeContextManager is a stack of scopeContexts allowing multiple stacks of
|
||||
// scopes to be managed at the same time in case the analysis of one scoped
|
||||
// entity causes the analysis of another.
|
||||
type scopeContextManager []scopeContext
|
||||
|
||||
func (this *scopeContextManager) PushScopeContext () {
|
||||
*this = append(*this, nil)
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) PopScopeContext () {
|
||||
this.assertPopulated()
|
||||
*this = (*this)[:len(*this) - 1]
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) PushScope (scope entity.Scoped) {
|
||||
this.assertPopulated()
|
||||
(*this)[len(*this) - 1].PushScope(scope)
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) PopScope () {
|
||||
this.assertPopulated()
|
||||
(*this)[len(*this) - 1].PopScope()
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) Variable (name string) *entity.Declaration {
|
||||
this.assertPopulated()
|
||||
return (*this)[len(*this) - 1].TopScope().Variable(name)
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) AddVariable (declaration *entity.Declaration) {
|
||||
this.assertPopulated()
|
||||
(*this)[len(*this) - 1].TopScope().AddVariable(declaration)
|
||||
}
|
||||
|
||||
func (this *scopeContextManager) assertPopulated () {
|
||||
if len(*this) < 1 {
|
||||
panic("scopeContextManager must have at least 1 scope context")
|
||||
}
|
||||
}
|
||||
|
||||
// scopeContext is a stack of scopes used when analyzing scoped entity.
|
||||
type scopeContext []entity.Scoped
|
||||
|
||||
func (this *scopeContext) PushScope (scope entity.Scoped) {
|
||||
*this = append(*this, scope)
|
||||
}
|
||||
|
||||
func (this *scopeContext) PopScope () {
|
||||
this.assertPopulated()
|
||||
*this = (*this)[:len(*this) - 1]
|
||||
}
|
||||
|
||||
func (this *scopeContext) TopScope () entity.Scoped {
|
||||
this.assertPopulated()
|
||||
return (*this)[len(*this) - 1]
|
||||
}
|
||||
|
||||
func (this *scopeContext) Variable (name string) *entity.Declaration {
|
||||
this.assertPopulated()
|
||||
for index := len(*this) - 1; index >= 0; index -- {
|
||||
variable := (*this)[index].Variable(name)
|
||||
if variable != nil {
|
||||
return variable
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *scopeContext) AddVariable (declaration *entity.Declaration) {
|
||||
this.assertPopulated()
|
||||
(*this)[len(*this) - 1].AddVariable(declaration)
|
||||
}
|
||||
|
||||
func (this *scopeContext) assertPopulated () {
|
||||
if len(*this) < 1 {
|
||||
panic("scopeContext must have at least 1 scope")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,16 @@ 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
|
||||
|
||||
incompleteTypes map[string] entity.Type
|
||||
|
||||
Types map[string] entity.Type
|
||||
Types map[string] *entity.Typedef
|
||||
Functions map[string] *entity.Function
|
||||
}
|
||||
|
||||
|
@ -97,11 +98,16 @@ func (this *Tree) analyzeDeclarations () error {
|
|||
this.Types[name] = ty
|
||||
}
|
||||
for name, rawFunction := range this.rawFunctions {
|
||||
function, err := this.analyzeFunction(rawFunction.Pos, name)
|
||||
_, err := this.analyzeFunction(rawFunction.Pos, name)
|
||||
if err != nil { return err }
|
||||
this.Functions[name] = function
|
||||
}
|
||||
// TODO methods
|
||||
// for _, rawMethod := range this.rawMethods {
|
||||
// _, err := this.analyzeMethod (
|
||||
// rawMethod.Pos,
|
||||
// rawMethod.TypeName,
|
||||
// rawMethod.Signature.Name)
|
||||
// if err != nil { return err }
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -110,11 +116,15 @@ func (this *Tree) ensure () {
|
|||
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.Type)
|
||||
this.Types = make(map[string] entity.Type)
|
||||
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] = ty
|
||||
this.Types[name] = &entity.Typedef {
|
||||
Public: true,
|
||||
Name: name,
|
||||
Type: ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,18 +10,18 @@ func (this *Tree) analyzeTypedef (
|
|||
name string,
|
||||
acceptIncomplete bool,
|
||||
) (
|
||||
entity.Type,
|
||||
*entity.Typedef,
|
||||
error,
|
||||
) {
|
||||
// return if exists already
|
||||
if ty, exists := this.Types[name]; exists {
|
||||
return ty, nil
|
||||
if definition, exists := this.Types[name]; exists {
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
// deal with incomplete types
|
||||
if ty, incomplete := this. incompleteTypes[name]; incomplete {
|
||||
if definition, incomplete := this. incompleteTypes[name]; incomplete {
|
||||
if acceptIncomplete {
|
||||
return ty, nil
|
||||
return definition, nil
|
||||
} else {
|
||||
return nil, participle.Errorf (
|
||||
pos, "type %s cannot be used in this context",
|
||||
|
@ -34,8 +34,11 @@ func (this *Tree) analyzeTypedef (
|
|||
if !exists {
|
||||
return nil, participle.Errorf(pos, "no type named %s", name)
|
||||
}
|
||||
|
||||
return this.analyzeTypeInternal(definition.Type, name, false)
|
||||
|
||||
var err error
|
||||
definition.Type, err = this.analyzeTypeInternal (
|
||||
definition.Type, definition, false)
|
||||
return definition, err
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeType (
|
||||
|
@ -45,12 +48,12 @@ func (this *Tree) analyzeType (
|
|||
entity.Type,
|
||||
error,
|
||||
) {
|
||||
return this.analyzeTypeInternal (ty, "", acceptIncomplete)
|
||||
return this.analyzeTypeInternal (ty, nil, acceptIncomplete)
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeTypeInternal (
|
||||
ty entity.Type,
|
||||
name string,
|
||||
root *entity.Typedef,
|
||||
acceptIncomplete bool,
|
||||
) (
|
||||
entity.Type,
|
||||
|
@ -61,14 +64,16 @@ func (this *Tree) analyzeTypeInternal (
|
|||
if ty == nil { return entity.Void(), nil }
|
||||
var err error
|
||||
|
||||
// manage incomplete type information
|
||||
// if we are analyzing a typedef, we will need to update incomplete type
|
||||
// information as we go in order for recursive type definitions to work
|
||||
updateIncompleteInfo := func () {
|
||||
if name != "" { this.incompleteTypes[name] = ty }
|
||||
if root != nil { this.incompleteTypes[root.Name] = root }
|
||||
}
|
||||
updatePseudoCompleteInfo := func () {
|
||||
if name != "" { this.Types[name] = ty }
|
||||
if root != nil { this.Types[root.Name] = root }
|
||||
}
|
||||
if name != "" { defer delete(this.incompleteTypes, name) }
|
||||
if root != nil { defer delete(this.incompleteTypes, root.Name) }
|
||||
// ---------
|
||||
|
||||
switch ty.(type) {
|
||||
// named type
|
||||
|
@ -78,7 +83,11 @@ func (this *Tree) analyzeTypeInternal (
|
|||
if primitive, isPrimitive := primitiveTypes[ty.Name]; isPrimitive {
|
||||
return primitive, nil
|
||||
}
|
||||
ty.Type, err = this.analyzeTypedef(ty.Pos, ty.Name, acceptIncomplete)
|
||||
var def *entity.Typedef
|
||||
def, err = this.analyzeTypedef(ty.Pos, ty.Name, acceptIncomplete)
|
||||
if err == nil {
|
||||
ty.Type = def.Type
|
||||
}
|
||||
return ty, err
|
||||
|
||||
// pointer type
|
||||
|
|
|
@ -43,14 +43,6 @@ world: (x:Int y:Int)
|
|||
`)
|
||||
}
|
||||
|
||||
func TestTypeNamedErrMissing (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"no type named Missing", 2, 10,
|
||||
`
|
||||
present: Missing
|
||||
`)
|
||||
}
|
||||
|
||||
func TestTypedefRecursiveErr (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"type List cannot be used in this context", 4, 9,
|
||||
|
@ -70,6 +62,14 @@ List: (
|
|||
`)
|
||||
}
|
||||
|
||||
func TestTypeNamedErrMissing (test *testing.T) {
|
||||
testStringErr (test,
|
||||
"no type named Missing", 2, 10,
|
||||
`
|
||||
present: Missing
|
||||
`)
|
||||
}
|
||||
|
||||
func TestTypeNamed (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
|
|
|
@ -8,7 +8,7 @@ type Scoped interface {
|
|||
Variable (name string) *Declaration
|
||||
|
||||
// AddVariable adds a variable to this entity's scope.
|
||||
AddVariable (name string, declaration *Declaration)
|
||||
AddVariable (declaration *Declaration)
|
||||
}
|
||||
|
||||
// Scope implements a scope.
|
||||
|
@ -23,9 +23,9 @@ func (this *Scope) Variable (name string) *Declaration {
|
|||
return this.variables[name]
|
||||
}
|
||||
|
||||
func (this *Scope) AddVariable (name string, declaration *Declaration) {
|
||||
func (this *Scope) AddVariable (declaration *Declaration) {
|
||||
if this.variables == nil {
|
||||
this.variables = make(map[string] *Declaration)
|
||||
}
|
||||
this.variables[name] = declaration
|
||||
this.variables[declaration.Name] = declaration
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ type Typedef struct {
|
|||
Type Type `parser:" ':' @@ "`
|
||||
|
||||
// Semantics
|
||||
Methods map[string] Method
|
||||
Methods map[string] *Method
|
||||
}
|
||||
func (*Typedef) topLevel(){}
|
||||
func (this *Typedef) String () string {
|
||||
|
@ -68,6 +68,7 @@ type Method struct {
|
|||
|
||||
// Semantics
|
||||
Scope
|
||||
Type Type
|
||||
}
|
||||
func (*Method) topLevel(){}
|
||||
func (this *Method) String () string {
|
||||
|
|
Loading…
Reference in New Issue