summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/v2/symbol_table.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo/v2/symbol_table.go')
-rw-r--r--vendor/github.com/d5/tengo/v2/symbol_table.go165
1 files changed, 165 insertions, 0 deletions
diff --git a/vendor/github.com/d5/tengo/v2/symbol_table.go b/vendor/github.com/d5/tengo/v2/symbol_table.go
new file mode 100644
index 00000000..6ae5d7d3
--- /dev/null
+++ b/vendor/github.com/d5/tengo/v2/symbol_table.go
@@ -0,0 +1,165 @@
+package tengo
+
+// SymbolScope represents a symbol scope.
+type SymbolScope string
+
+// List of symbol scopes
+const (
+ ScopeGlobal SymbolScope = "GLOBAL"
+ ScopeLocal SymbolScope = "LOCAL"
+ ScopeBuiltin SymbolScope = "BUILTIN"
+ ScopeFree SymbolScope = "FREE"
+)
+
+// Symbol represents a symbol in the symbol table.
+type Symbol struct {
+ Name string
+ Scope SymbolScope
+ Index int
+ LocalAssigned bool // if the local symbol is assigned at least once
+}
+
+// SymbolTable represents a symbol table.
+type SymbolTable struct {
+ parent *SymbolTable
+ block bool
+ store map[string]*Symbol
+ numDefinition int
+ maxDefinition int
+ freeSymbols []*Symbol
+ builtinSymbols []*Symbol
+}
+
+// NewSymbolTable creates a SymbolTable.
+func NewSymbolTable() *SymbolTable {
+ return &SymbolTable{
+ store: make(map[string]*Symbol),
+ }
+}
+
+// Define adds a new symbol in the current scope.
+func (t *SymbolTable) Define(name string) *Symbol {
+ symbol := &Symbol{Name: name, Index: t.nextIndex()}
+ t.numDefinition++
+
+ if t.Parent(true) == nil {
+ symbol.Scope = ScopeGlobal
+ } else {
+ symbol.Scope = ScopeLocal
+ }
+ t.store[name] = symbol
+ t.updateMaxDefs(symbol.Index + 1)
+ return symbol
+}
+
+// DefineBuiltin adds a symbol for builtin function.
+func (t *SymbolTable) DefineBuiltin(index int, name string) *Symbol {
+ if t.parent != nil {
+ return t.parent.DefineBuiltin(index, name)
+ }
+
+ symbol := &Symbol{
+ Name: name,
+ Index: index,
+ Scope: ScopeBuiltin,
+ }
+ t.store[name] = symbol
+ t.builtinSymbols = append(t.builtinSymbols, symbol)
+ return symbol
+}
+
+// Resolve resolves a symbol with a given name.
+func (t *SymbolTable) Resolve(
+ name string,
+) (symbol *Symbol, depth int, ok bool) {
+ symbol, ok = t.store[name]
+ if !ok && t.parent != nil {
+ symbol, depth, ok = t.parent.Resolve(name)
+ if !ok {
+ return
+ }
+ depth++
+
+ // if symbol is defined in parent table and if it's not global/builtin
+ // then it's free variable.
+ if !t.block && depth > 0 &&
+ symbol.Scope != ScopeGlobal &&
+ symbol.Scope != ScopeBuiltin {
+ return t.defineFree(symbol), depth, true
+ }
+ return
+ }
+ return
+}
+
+// Fork creates a new symbol table for a new scope.
+func (t *SymbolTable) Fork(block bool) *SymbolTable {
+ return &SymbolTable{
+ store: make(map[string]*Symbol),
+ parent: t,
+ block: block,
+ }
+}
+
+// Parent returns the outer scope of the current symbol table.
+func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
+ if skipBlock && t.block {
+ return t.parent.Parent(skipBlock)
+ }
+ return t.parent
+}
+
+// MaxSymbols returns the total number of symbols defined in the scope.
+func (t *SymbolTable) MaxSymbols() int {
+ return t.maxDefinition
+}
+
+// FreeSymbols returns free symbols for the scope.
+func (t *SymbolTable) FreeSymbols() []*Symbol {
+ return t.freeSymbols
+}
+
+// BuiltinSymbols returns builtin symbols for the scope.
+func (t *SymbolTable) BuiltinSymbols() []*Symbol {
+ if t.parent != nil {
+ return t.parent.BuiltinSymbols()
+ }
+ return t.builtinSymbols
+}
+
+// Names returns the name of all the symbols.
+func (t *SymbolTable) Names() []string {
+ var names []string
+ for name := range t.store {
+ names = append(names, name)
+ }
+ return names
+}
+
+func (t *SymbolTable) nextIndex() int {
+ if t.block {
+ return t.parent.nextIndex() + t.numDefinition
+ }
+ return t.numDefinition
+}
+
+func (t *SymbolTable) updateMaxDefs(numDefs int) {
+ if numDefs > t.maxDefinition {
+ t.maxDefinition = numDefs
+ }
+ if t.block {
+ t.parent.updateMaxDefs(numDefs)
+ }
+}
+
+func (t *SymbolTable) defineFree(original *Symbol) *Symbol {
+ // TODO: should we check duplicates?
+ t.freeSymbols = append(t.freeSymbols, original)
+ symbol := &Symbol{
+ Name: original.Name,
+ Index: len(t.freeSymbols) - 1,
+ Scope: ScopeFree,
+ }
+ t.store[original.Name] = symbol
+ return symbol
+}