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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/** Compiler symbolic scopes.
In Tokay code, this relates to any block.
Parselets introduce new variable scopes.
Loops introduce a new loop scope.
*/
use super::*;
use crate::builtin::Builtin;
use crate::error::Error;
use crate::reader::*;
use crate::value::{RefValue, Token};
use indexmap::IndexMap;
use std::cell::RefCell;
pub(super) enum ScopeLevel {
Parselet(ImlRefParselet), // parselet level (refers to currently constructed parselet)
Block, // block level (constants can be defined here)
Loop, // loop level (allows the use of break & continue)
}
pub(super) struct Scope<'compiler, 'parent> {
pub compiler: &'compiler Compiler, // reference to compiler
pub level: ScopeLevel, // Scope level
parent: Option<&'parent Scope<'compiler, 'parent>>, // Previous scope
pub constants: RefCell<IndexMap<String, ImlValue>>, // Symbol table of named constants
pub usages: RefCell<Vec<ImlValue>>, // Unresolved usages within scope
pub errors: RefCell<Vec<Error>>, // Errors raised
}
impl<'compiler, 'parent> Scope<'compiler, 'parent> {
/** Create a new scope in compiler, with scopelevel level, and optional parent.
Use self.shadow() for simpler scope creation. */
pub fn new(
compiler: &'compiler Compiler,
level: ScopeLevel,
parent: Option<&'parent Scope<'compiler, 'parent>>,
) -> Self {
let scope = Self {
compiler,
level,
parent,
constants: RefCell::new(IndexMap::new()),
usages: RefCell::new(Vec::new()),
errors: RefCell::new(Vec::new()),
};
// Register standard whitespace
if scope.parent.is_none() {
scope.define_constant(
"_",
RefValue::from(Token::builtin("Whitespaces").unwrap()).into(),
);
}
scope
}
/// Create a new scope with given level and shadow scope self until this newly created scope outlives
pub fn shadow(&'parent self, level: ScopeLevel) -> Self {
Self::new(self.compiler, level, Some(self))
}
/// Check if self is the global scope
pub fn is_global(&self) -> bool {
match self.level {
ScopeLevel::Parselet(_) => self.parent.is_none(),
_ => self.parent.as_ref().unwrap().is_global(),
}
}
/// Check if scope is a loop-scope.
pub fn is_loop(&self) -> bool {
match self.level {
ScopeLevel::Loop => true,
ScopeLevel::Parselet(_) => false,
_ => self.parent.as_ref().unwrap().is_loop(),
}
}
/// Retrieve current outer parselet; In global scope, this is the __main__ parselet.
pub fn parselet(&self) -> ImlRefParselet {
match &self.level {
ScopeLevel::Parselet(parselet) => parselet.clone(),
_ => self.parent.as_ref().unwrap().parselet(),
}
}
/// Register variable with name
pub fn register_variable(&self, name: &str) {
self.parselet().borrow().model.borrow_mut().var(name);
}
/// Define constant value to name in current scope.
pub fn define_constant(&self, name: &str, mut value: ImlValue) {
/*
Special meaning for whitespace constants names "_" and "__".
When set, the corresponding consumable Value becomes the following:
- `__ : Value+`
- `_ : __?`
This is always the case whenever "_" or "__" is set.
Fallback defaults to `Value : Whitespace`, handled in get_constant().
*/
let mut secondary = None;
if name == "_" || name == "__" {
// `__` becomes `Value+`
value = value.into_generic("Pos", Some(0), None).try_resolve(self);
secondary = Some(("__", value.clone()));
// ...and then in-place "_" is defined as `_ : __?`
value = value.into_generic("Opt", Some(0), None).try_resolve(self);
}
// Insert constant into current scope
let mut constants = self.constants.borrow_mut();
if let Some((name, value)) = secondary {
constants.insert(name.to_string(), value);
}
constants.insert(name.to_string(), value);
}
/// Resolve a name starting from the current scope.
pub fn resolve_name(&self, offset: Option<Offset>, name: &str) -> Option<ImlValue> {
let mut top = Some(self);
let mut top_parselet = true;
while let Some(scope) = top {
// Check constants of scope
if let Some(value) = scope.constants.borrow().get(name) {
return Some(value.clone());
}
if let ScopeLevel::Parselet(parselet) = &scope.level {
if top_parselet
&& (parselet.borrow().generics.get(name).is_some()
|| name == "Self"
|| name == "self")
{
return Some(ImlValue::Generic {
offset,
name: name.to_string(),
});
}
// Check for variable only in first or global scope
if scope.parent.is_none() || top_parselet {
let parselet = parselet.borrow();
if let Some(addr) = parselet.model.borrow().variables.get(name) {
return Some(ImlValue::Variable {
offset,
name: name.to_string(),
is_global: scope.parent.is_none(),
addr: *addr,
});
};
}
top_parselet = false;
}
top = scope.parent.as_deref();
}
// Check for a builtin function
if let Some(builtin) = Builtin::get(name) {
return Some(RefValue::from(builtin).into());
}
// Check for built-in token
if let Some(value) = Token::builtin(name) {
return Some(RefValue::from(value).into());
}
None
}
/// Resolve any open usages within the current scope.
pub fn resolve_usages(&self) {
let resolve: Vec<ImlValue> = self.usages.borrow_mut().drain(..).collect();
// Try to resolve open usages, keep then when they are still unresolved
for value in resolve.into_iter() {
value.try_resolve(self);
}
}
/// Push an Error to the scope's error log, with given offset and msg.
pub fn push_error(&self, offset: Option<Offset>, msg: String) {
self.errors.borrow_mut().push(Error::new(offset, msg))
}
}
impl<'compiler, 'parent> Drop for Scope<'compiler, 'parent> {
fn drop(&mut self) {
self.resolve_usages();
match &mut self.parent {
Some(parent) => {
parent
.usages
.borrow_mut()
.extend(self.usages.borrow_mut().drain(..));
parent
.errors
.borrow_mut()
.extend(self.errors.borrow_mut().drain(..));
}
None => return,
}
}
}