-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathScopeModule.grace
More file actions
195 lines (161 loc) · 6.22 KB
/
Copy pathScopeModule.grace
File metadata and controls
195 lines (161 loc) · 6.22 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
dialect "none"
import "standardGrace" as sg
import "ast" as ast
import "xmodule" as xmodule
import "io" as io
import "SharedTypes" as share
inherit sg.methods
type MethodType = share.MethodType
type GenericType = share.GenericType
type ObjectType = share.ObjectType
// The cached public type assignments.
def cache: Dictionary is readable = emptyDictionary
// cache holding confidential types for inheritance
def allCache: Dictionary is readable = emptyDictionary
// Holds dictionaries for names in scope
// Will be used for defs/vars, methods, and types
type StackOfKind⟦V⟧ = {
stack → List⟦Dictionary⟧
// push <name,value> into most recent level of stack
at (name : String) put (value:V) → Done
// add <name,value> to least recent level of stack
addToGlobalAt(name : String) put (value : V) → Done
// Starting from the most recent level of the scope, find name & return its
// value. If it is not there perform action in bl.
find (name : String) butIfMissing (bl: Function0⟦V⟧) → V
findOuter (levels : Number) butIfMissing (bl: Function0⟦V⟧) → V
exists (name: String) -> Boolean
// Starting from the least recent level of the scope, find name & return its
// value. If it is not there perform action in bl.
findFromLeastRecent (name : String) butIfMissing (bl: Function0⟦V⟧) → V
}
// class creating stack of given kind (e.g., types) holding values of type V
class stackOfKind⟦V⟧(kind : String) → StackOfKind⟦V⟧{
// represented as stack of dictionaries
def stack: List⟦Dictionary⟧ is public = list[emptyDictionary]
// add <name,value> to most recent level of the scope
method at (name : String) put (value:V) → Done {
stack.last.at(name) put(value)
}
// adds to the least recent level of the scope
method addToGlobalAt(name : String) put (value:V) → Done {
stack.first.at(name) put(value)
}
// Starting from the most recent level of the scope, find name & return its
// value. If it is not there perform action in bl.
method find (name : String) butIfMissing (bl: Function0⟦V⟧) → V {
var i: Number := stack.size
while { i > 0 } do {
var found: Boolean := true
def val = stack.at(i).at(name) ifAbsent {
found := false
}
if(found) then {
return val
}
i := i - 1
}
return bl.apply
}
// Starting from the current scope, go outside "level"
// number of nested objects and return the type of self there.
// If it is not there perform action in bl before returning.
method findOuter (levels : Number) butIfMissing (bl: Function0⟦V⟧) → V {
var i: Number := stack.size
for (1..levels) do {current: Number →
var found: Boolean := false
while { (i > 0) && !found} do {
if (stack.at(i).containsKey("outer")) then {
found := true
}
i := i - 1
}
if (!found) then {
return bl.apply
}
}
def outerType: V = stack.at(i+1).at("outer")
outerType
}
method exists (name : String) -> Boolean {
var i: Number := stack.size
while { i > 0 } do {
var found: Boolean := stack.at(i).containsKey(name)
if(found) then { return true }
i := i - 1
}
return false
}
// Starting from the least recent level of the scope, find name & return its
// value. If it is not there perform action in bl.
method findFromLeastRecent(name: String) butIfMissing(bl: Function0⟦V⟧) → V {
var i: Number := 1
while { i <= stack.size } do {
var found: Boolean := true
def val = stack.at(i).at(name) ifAbsent {
found := false
}
if(found) then {
return val
}
i := i + 1
}
return bl.apply
}
// Return string representing contents of each dictionary in the stack
method asString → String is override {
var out: String := ""
for(stack) do { dict:Dictionary⟦String, Object⟧ →
out := "{out}\ndict⟬"
dict.keysAndValuesDo { key:String, value:Object →
out := "{out}\n {key}::{value}"
}
out := "{out}\n⟭"
}
out
}
}
// Data structure keeping track of all names currently accessible in program
type Scope = {
// return stack representing current scope of each kind of object
variables → StackOfKind⟦ObjectType⟧
methods → StackOfKind⟦MethodType⟧
types → StackOfKind⟦ObjectType⟧
generics → StackOfKind⟦GenericType⟧
// number of levels on each stack (all the same)
size → Number
// Enter new scope to execute block bl and then delete scope afterwards
// returns value of bl
enter⟦V⟧(bl: Function0⟦V⟧) → V
}
// scope consists of stacks of scopes for each of variables, methods, & types
def scope: Scope is public = object {
// keep track of each kind of expression separately
def variables is public = stackOfKind⟦ObjectType⟧ ("variable")
def methods is public = stackOfKind ⟦MethodType⟧("method")
def types is public = stackOfKind ⟦ObjectType⟧("type")
def generics is public = stackOfKind ⟦GenericType⟧("generic")
// number of items on stack
method size → Number {
variables.stack.size
}
// Enter new scope to execute block bl and then delete afterwards
// returns value of bl
method enter⟦V⟧ (bl:Function0⟦V⟧) → V {
// create new empty environment
variables.stack.push (emptyDictionary)
methods.stack.push (emptyDictionary)
types.stack.push (emptyDictionary)
// execute bl in the new environment
def result: V = bl.apply
// release new environment as going out of scope
variables.stack.pop
methods.stack.pop
types.stack.pop
result
}
// Returns string including number of levels in scope
method asString → String is override {
"scope<{size}>"
}
}