fspl/analyzer/scope.go

133 lines
3.6 KiB
Go

package analyzer
import "git.tebibyte.media/fspl/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 (declaration entity.TopLevel) {
*this = append(*this, scopeContext { declaration: declaration })
}
func (this *scopeContextManager) popScopeContext () {
this.assertPopulated()
*this = (*this)[:len(*this) - 1]
}
func (this *scopeContextManager) pushLoop (loop entity.Breakable) {
this.assertPopulated()
(*this)[len(*this) - 1].pushLoop(loop)
}
func (this *scopeContextManager) popLoop () {
this.assertPopulated()
(*this)[len(*this) - 1].popLoop()
}
func (this *scopeContextManager) topLoop () (entity.Breakable, bool) {
this.assertPopulated()
return (*this)[len(*this) - 1].topLoop()
}
func (this *scopeContextManager) topDeclaration () (entity.TopLevel, bool) {
if len(*this) < 1 { return nil, false }
return (*this)[len(*this) - 1].declaration, true
}
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) topScope () (entity.Scoped, bool) {
this.assertPopulated()
return (*this)[len(*this) - 1].topScope()
}
func (this *scopeContextManager) variable (name string) *entity.Declaration {
this.assertPopulated()
return (*this)[len(*this) - 1].variable(name)
}
func (this *scopeContextManager) addVariable (declaration *entity.Declaration) {
this.assertPopulated()
(*this)[len(*this) - 1].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 and loops used when analyzing scoped
// entities.
type scopeContext struct {
scopes []entity.Scoped
loops []entity.Breakable
declaration entity.TopLevel
}
func (this *scopeContext) pushLoop (loop entity.Breakable) {
this.loops = append(this.loops, loop)
}
func (this *scopeContext) popLoop () {
this.assertLoopPopulated()
this.loops = this.loops[:len(this.loops) - 1]
}
func (this *scopeContext) topLoop () (entity.Breakable, bool) {
if len(this.loops) < 1 { return nil, false }
return this.loops[len(this.loops) - 1], true
}
func (this *scopeContext) pushScope (scope entity.Scoped) {
this.scopes = append(this.scopes, scope)
}
func (this *scopeContext) popScope () {
this.assertScopePopulated()
this.scopes = this.scopes[:len(this.scopes) - 1]
}
func (this *scopeContext) topScope () (entity.Scoped, bool) {
if len(this.scopes) < 1 { return nil, false }
return this.scopes[len(this.scopes) - 1], true
}
func (this *scopeContext) variable (name string) *entity.Declaration {
this.assertScopePopulated()
for index := len(this.scopes) - 1; index >= 0; index -- {
variable := this.scopes[index].Variable(name)
if variable != nil {
return variable
}
}
return nil
}
func (this *scopeContext) addVariable (declaration *entity.Declaration) {
this.assertScopePopulated()
this.scopes[len(this.scopes) - 1].AddVariable(declaration)
}
func (this *scopeContext) assertScopePopulated () {
if len(this.scopes) < 1 {
panic("scopeContext must have at least 1 scope")
}
}
func (this *scopeContext) assertLoopPopulated () {
if len(this.scopes) < 1 {
panic("scopeContext must have at least 1 loop")
}
}