-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathsymbol_table.go
More file actions
138 lines (121 loc) · 2.93 KB
/
symbol_table.go
File metadata and controls
138 lines (121 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package plush
// SymbolTable represents a scope
type SymbolTable struct {
vars map[int]interface{}
parent *SymbolTable
// Interning system
localInterner *InternTable
globalInterner *InternTable
}
// NewScope creates a new scope with an optional parent
func NewScope(parent *SymbolTable) *SymbolTable {
if parent == nil {
global := NewInternTable()
local := NewInternTable()
return &SymbolTable{
vars: make(map[int]interface{}),
parent: nil,
globalInterner: global,
localInterner: local,
}
}
// Inherit interning from parent
return &SymbolTable{
vars: make(map[int]interface{}),
parent: parent,
globalInterner: parent.globalInterner,
localInterner: parent.localInterner,
}
}
// Declare adds or updates a variable in the current scope
func (s *SymbolTable) Declare(name string, value interface{}) {
if value == nil {
return
}
id := s.localInterner.Intern(name)
s.vars[id] = value
}
// Assign searches outer scopes and updates an existing variable
func (s *SymbolTable) Assign(name string, value interface{}) bool {
var id int
var ok bool
isLocal := false
// Try local interner first
if id, ok = s.localInterner.Lookup(name); !ok {
// Then try global interner
if id, ok = s.globalInterner.Lookup(name); !ok {
return false
}
} else {
isLocal = true
}
firstK := 0
for curr := s; curr != nil; curr = curr.parent {
//Skip if we know it's not in the first local scope
if !isLocal && firstK == 0 {
firstK += 1
continue
}
if _, exists := curr.vars[id]; exists {
curr.vars[id] = value
return true
}
}
return false
}
// Has finds the value of a variable
func (s *SymbolTable) Has(name string) bool {
var id int
var ok bool
isLocal := false
// Try local first
if id, ok = s.localInterner.Lookup(name); !ok {
// Try global if not found locally
if id, ok = s.globalInterner.Lookup(name); !ok {
return false
}
} else {
isLocal = true
}
firstK := 0
// Only one walk through the scope chain, using the ID we found
for curr := s; curr != nil; curr = curr.parent {
//Skip if we know it's not in the first local scope
if !isLocal && firstK == 0 {
firstK += 1
continue
}
if _, exists := curr.vars[id]; exists {
return true
}
}
return false
}
// Resolve finds the value of a variable
func (s *SymbolTable) Resolve(name string) (interface{}, bool) {
var id int
var ok bool
isLocal := false
// Try local first
if id, ok = s.localInterner.Lookup(name); !ok {
// Try global if not found locally
if id, ok = s.globalInterner.Lookup(name); !ok {
return nil, false
}
} else {
isLocal = true
}
firstK := 0
// Only one walk through the scope chain, using the ID we found
for curr := s; curr != nil; curr = curr.parent {
//Skip if we know it's not in the first local scope
if !isLocal && firstK == 0 {
firstK += 1
continue
}
if val, exists := curr.vars[id]; exists {
return val, true
}
}
return nil, false
}