Skip to content

Commit 3f8e943

Browse files
committed
fix: Isolate Stack state
This is a part of an effort to modularize context so it will be easier to put in special logic for globals. BREAKING CHANGE: For tags, you need to use `context.stack()`.
1 parent 34dc950 commit 3f8e943

File tree

11 files changed

+170
-126
lines changed

11 files changed

+170
-126
lines changed

src/compiler/token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ mod test {
179179
#[test]
180180
fn evaluate_handles_identifiers() {
181181
let mut ctx = Context::new();
182-
ctx.set_global_val("var0", Value::scalar(42f64));
182+
ctx.stack_mut().set_global_val("var0", Value::scalar(42f64));
183183
assert_eq!(
184184
Token::Identifier("var0".to_owned())
185185
.to_arg()

src/interpreter/argument.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ impl Argument {
1616
pub fn evaluate(&self, context: &Context) -> Result<Value> {
1717
let val = match *self {
1818
Argument::Val(ref x) => x.clone(),
19-
Argument::Var(ref x) => context.get_val_by_index(x.indexes().iter())?.clone(),
19+
Argument::Var(ref x) => context.stack().get_val_by_index(x.indexes().iter())?.clone(),
2020
};
2121
Ok(val)
2222
}

src/interpreter/context.rs

Lines changed: 115 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -87,56 +87,27 @@ impl<'a> CycleState<'a> {
8787
}
8888
}
8989

90-
#[derive(Default)]
91-
pub struct Context {
92-
stack: Vec<Object>,
90+
#[derive(Debug, Clone, PartialEq, Eq)]
91+
pub struct Stack {
9392
globals: Object,
94-
95-
interrupt: InterruptState,
96-
cycles: CycleStateInner,
97-
98-
filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
93+
stack: Vec<Object>,
9994
}
10095

101-
impl Context {
102-
/// Creates a new, empty rendering context.
96+
impl Stack {
10397
pub fn new() -> Self {
104-
Default::default()
98+
Self {
99+
globals: Object::new(),
100+
// Mutable frame for globals.
101+
stack: vec![Object::new()],
102+
}
105103
}
106104

107-
pub fn with_values(mut self, values: Object) -> Self {
105+
pub fn with_globals(&mut self, values: Object) {
108106
self.globals = values;
109-
self
110-
}
111-
112-
pub fn with_filters(mut self, filters: &sync::Arc<HashMap<&'static str, BoxedValueFilter>>) -> Self {
113-
self.filters = sync::Arc::clone(filters);
114-
self
115-
}
116-
117-
pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b FilterValue> {
118-
self.filters.get(name).map(|f| {
119-
let f: &FilterValue = f;
120-
f
121-
})
122-
}
123-
124-
pub fn interrupt(&self) -> &InterruptState {
125-
&self.interrupt
126-
}
127-
128-
pub fn interrupt_mut(&mut self) -> &mut InterruptState {
129-
&mut self.interrupt
130-
}
131-
132-
pub fn cycles<'a>(&'a mut self) -> CycleState<'a> {
133-
CycleState {
134-
context: self,
135-
}
136107
}
137108

138109
/// Creates a new variable scope chained to a parent scope.
139-
fn push_scope(&mut self) {
110+
fn push_frame(&mut self) {
140111
self.stack.push(Object::new());
141112
}
142113

@@ -148,25 +119,12 @@ impl Context {
148119
/// empty stack. Given that a context is created with a top-level stack
149120
/// frame already in place, empyting the stack should never happen in a
150121
/// well-formed program.
151-
fn pop_scope(&mut self) {
122+
fn pop_frame(&mut self) {
152123
if self.stack.pop().is_none() {
153124
panic!("Pop leaves empty stack")
154125
};
155126
}
156127

157-
/// Sets up a new stack frame, executes the supplied function and then
158-
/// tears the stack frame down before returning the function's result
159-
/// to the caller.
160-
pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
161-
where
162-
FnT: FnOnce(&mut Context) -> RvalT,
163-
{
164-
self.push_scope();
165-
let result = f(self);
166-
self.pop_scope();
167-
result
168-
}
169-
170128
/// Gets a value from the rendering context.
171129
pub fn get_val<'a>(&'a self, name: &str) -> Option<&'a Value> {
172130
for frame in self.stack.iter().rev() {
@@ -204,7 +162,7 @@ impl Context {
204162
where
205163
S: Into<borrow::Cow<'static, str>>,
206164
{
207-
self.globals.insert(name.into(), val)
165+
self.global_frame().insert(name.into(), val)
208166
}
209167

210168
/// Sets a value to the rendering context.
@@ -219,11 +177,97 @@ impl Context {
219177
where
220178
S: Into<borrow::Cow<'static, str>>,
221179
{
180+
self.current_frame().insert(name.into(), val)
181+
}
182+
183+
fn current_frame(&mut self) -> &mut Object {
222184
match self.stack.last_mut() {
223-
Some(frame) => frame.insert(name.into(), val),
224-
None => panic!("Cannot insert into an empty stack"),
185+
Some(frame) => frame,
186+
None => panic!("Global frame removed."),
225187
}
226188
}
189+
190+
fn global_frame(&mut self) -> &mut Object {
191+
match self.stack.first_mut() {
192+
Some(frame) => frame,
193+
None => panic!("Global frame removed."),
194+
}
195+
}
196+
}
197+
198+
impl Default for Stack {
199+
fn default() -> Self {
200+
Self::new()
201+
}
202+
}
203+
204+
#[derive(Default)]
205+
pub struct Context {
206+
stack: Stack,
207+
208+
interrupt: InterruptState,
209+
cycles: CycleStateInner,
210+
211+
filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
212+
}
213+
214+
impl Context {
215+
/// Creates a new, empty rendering context.
216+
pub fn new() -> Self {
217+
Default::default()
218+
}
219+
220+
pub fn with_values(mut self, values: Object) -> Self {
221+
self.stack.with_globals(values);
222+
self
223+
}
224+
225+
pub fn with_filters(mut self, filters: &sync::Arc<HashMap<&'static str, BoxedValueFilter>>) -> Self {
226+
self.filters = sync::Arc::clone(filters);
227+
self
228+
}
229+
230+
pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b FilterValue> {
231+
self.filters.get(name).map(|f| {
232+
let f: &FilterValue = f;
233+
f
234+
})
235+
}
236+
237+
pub fn interrupt(&self) -> &InterruptState {
238+
&self.interrupt
239+
}
240+
241+
pub fn interrupt_mut(&mut self) -> &mut InterruptState {
242+
&mut self.interrupt
243+
}
244+
245+
pub fn cycles<'a>(&'a mut self) -> CycleState<'a> {
246+
CycleState {
247+
context: self,
248+
}
249+
}
250+
251+
pub fn stack(&self) -> &Stack {
252+
&self.stack
253+
}
254+
255+
pub fn stack_mut(&mut self) -> &mut Stack {
256+
&mut self.stack
257+
}
258+
259+
/// Sets up a new stack frame, executes the supplied function and then
260+
/// tears the stack frame down before returning the function's result
261+
/// to the caller.
262+
pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
263+
where
264+
FnT: FnOnce(&mut Context) -> RvalT,
265+
{
266+
self.stack.push_frame();
267+
let result = f(self);
268+
self.stack.pop_frame();
269+
result
270+
}
227271
}
228272

229273
#[cfg(test)]
@@ -233,52 +277,52 @@ mod test {
233277
#[test]
234278
fn get_val() {
235279
let mut ctx = Context::new();
236-
ctx.set_global_val("number", Value::scalar(42f64));
237-
assert_eq!(ctx.get_val("number").unwrap(), &Value::scalar(42f64));
280+
ctx.stack_mut().set_global_val("number", Value::scalar(42f64));
281+
assert_eq!(ctx.stack().get_val("number").unwrap(), &Value::scalar(42f64));
238282
}
239283

240284
#[test]
241285
fn get_val_failure() {
242286
let mut ctx = Context::new();
243287
let mut post = Object::new();
244288
post.insert("number".into(), Value::scalar(42f64));
245-
ctx.set_global_val("post", Value::Object(post));
246-
assert!(ctx.get_val("post.number").is_none());
289+
ctx.stack_mut().set_global_val("post", Value::Object(post));
290+
assert!(ctx.stack().get_val("post.number").is_none());
247291
}
248292

249293
#[test]
250294
fn get_val_by_index() {
251295
let mut ctx = Context::new();
252296
let mut post = Object::new();
253297
post.insert("number".into(), Value::scalar(42f64));
254-
ctx.set_global_val("post", Value::Object(post));
298+
ctx.stack_mut().set_global_val("post", Value::Object(post));
255299
let indexes = vec![Index::with_key("post"), Index::with_key("number")];
256300
assert_eq!(
257-
ctx.get_val_by_index(indexes.iter()).unwrap(),
301+
ctx.stack().get_val_by_index(indexes.iter()).unwrap(),
258302
&Value::scalar(42f64)
259303
);
260304
}
261305

262306
#[test]
263307
fn scoped_variables() {
264308
let mut ctx = Context::new();
265-
ctx.set_global_val("test", Value::scalar(42f64));
266-
assert_eq!(ctx.get_val("test").unwrap(), &Value::scalar(42f64));
309+
ctx.stack_mut().set_global_val("test", Value::scalar(42f64));
310+
assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64));
267311

268312
ctx.run_in_scope(|new_scope| {
269313
// assert that values are chained to the parent scope
270-
assert_eq!(new_scope.get_val("test").unwrap(), &Value::scalar(42f64));
314+
assert_eq!(new_scope.stack().get_val("test").unwrap(), &Value::scalar(42f64));
271315

272316
// set a new local value, and assert that it overrides the previous value
273-
new_scope.set_val("test", Value::scalar(3.14f64));
274-
assert_eq!(new_scope.get_val("test").unwrap(), &Value::scalar(3.14f64));
317+
new_scope.stack_mut().set_val("test", Value::scalar(3.14f64));
318+
assert_eq!(new_scope.stack().get_val("test").unwrap(), &Value::scalar(3.14f64));
275319

276320
// sat a new val that we will pick up outside the scope
277-
new_scope.set_global_val("global", Value::scalar("some value"));
321+
new_scope.stack_mut().set_global_val("global", Value::scalar("some value"));
278322
});
279323

280324
// assert that the value has reverted to the old one
281-
assert_eq!(ctx.get_val("test").unwrap(), &Value::scalar(42f64));
282-
assert_eq!(ctx.get_val("global").unwrap(), &Value::scalar("some value"));
325+
assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64));
326+
assert_eq!(ctx.stack().get_val("global").unwrap(), &Value::scalar("some value"));
283327
}
284328
}

src/interpreter/variable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl fmt::Display for Variable {
4040

4141
impl Renderable for Variable {
4242
fn render_to(&self, writer: &mut Write, context: &mut Context) -> Result<()> {
43-
let value = context.get_val_by_index(self.indexes.iter())?;
43+
let value = context.stack().get_val_by_index(self.indexes.iter())?;
4444
write!(writer, "{}", value).chain("Failed to render")?;
4545
Ok(())
4646
}

src/tags/assign_tag.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl Renderable for Assign {
2626
let value = self.src
2727
.apply_filters(context)
2828
.trace_with(|| self.trace().into())?;
29-
context.set_global_val(self.dst.to_owned(), value);
29+
context.stack_mut().set_global_val(self.dst.to_owned(), value);
3030
Ok(())
3131
}
3232
}
@@ -91,7 +91,7 @@ mod test {
9191
// test one: no matching value in `tags`
9292
{
9393
let mut context = Context::new();
94-
context.set_global_val(
94+
context.stack_mut().set_global_val(
9595
"tags",
9696
Value::Array(vec![
9797
Value::scalar("alpha"),
@@ -101,14 +101,14 @@ mod test {
101101
);
102102

103103
let output = template.render(&mut context).unwrap();
104-
assert_eq!(context.get_val("freestyle"), Some(&Value::scalar(false)));
104+
assert_eq!(context.stack().get_val("freestyle"), Some(&Value::scalar(false)));
105105
assert_eq!(output, "");
106106
}
107107

108108
// test two: matching value in `tags`
109109
{
110110
let mut context = Context::new();
111-
context.set_global_val(
111+
context.stack_mut().set_global_val(
112112
"tags",
113113
Value::Array(vec![
114114
Value::scalar("alpha"),
@@ -119,7 +119,7 @@ mod test {
119119
);
120120

121121
let output = template.render(&mut context).unwrap();
122-
assert_eq!(context.get_val("freestyle"), Some(&Value::scalar(true)));
122+
assert_eq!(context.stack().get_val("freestyle"), Some(&Value::scalar(true)));
123123
assert_eq!(output, "<p>Freestyle!</p>");
124124
}
125125
}

src/tags/capture_block.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl Renderable for Capture {
3030
.trace_with(|| self.trace().into())?;
3131

3232
let output = String::from_utf8(captured).expect("render only writes UTF-8");
33-
context.set_global_val(self.id.to_owned(), Value::scalar(output));
33+
context.stack_mut().set_global_val(self.id.to_owned(), Value::scalar(output));
3434
Ok(())
3535
}
3636
}
@@ -87,12 +87,12 @@ mod test {
8787
.unwrap();
8888

8989
let mut ctx = Context::new();
90-
ctx.set_global_val("item", Value::scalar("potato"));
91-
ctx.set_global_val("i", Value::scalar(42f64));
90+
ctx.stack_mut().set_global_val("item", Value::scalar("potato"));
91+
ctx.stack_mut().set_global_val("i", Value::scalar(42f64));
9292

9393
let output = template.render(&mut ctx).unwrap();
9494
assert_eq!(
95-
ctx.get_val("attribute_name"),
95+
ctx.stack().get_val("attribute_name"),
9696
Some(&Value::scalar("potato-42-color"))
9797
);
9898
assert_eq!(output, "");

0 commit comments

Comments
 (0)