Skip to content

Commit

Permalink
feat(interpreter): Allow named stack frames
Browse files Browse the repository at this point in the history
The goal of this is so that runtime includes of partials can set this
and then any error (usually the `trace`) that happens within that partial can be prefixed with
the name.  Eventually we also want these to include line numbers (see #247).

In the end, this will make the errors look more like what you'd expect
from a compiler error.
  • Loading branch information
epage committed Dec 22, 2018
1 parent f2860a5 commit 4c37817
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
13 changes: 13 additions & 0 deletions liquid-interpreter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ impl<'g> Context<'g> {
self.stack.pop_frame();
result
}

/// Sets up a new stack frame, executes the supplied function and then
/// tears the stack frame down before returning the function's result
/// to the caller.
pub fn run_in_named_scope<RvalT, S: Into<String>, FnT>(&mut self, name: S, f: FnT) -> RvalT
where
FnT: FnOnce(&mut Context) -> RvalT,
{
self.stack.push_named_frame(name);
let result = f(self);
self.stack.pop_frame();
result
}
}

impl<'g> Default for Context<'g> {
Expand Down
48 changes: 40 additions & 8 deletions liquid-interpreter/src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,30 @@ use liquid_value::{Object, PathRef, Scalar, Value};

use super::ValueStore;

#[derive(Clone, Default, Debug)]
struct Frame {
name: Option<String>,
data: Object,
}

impl Frame {
fn new() -> Self {
Default::default()
}

fn with_name<S: Into<String>>(name: S) -> Self {
Self {
name: Some(name.into()),
data: Object::new(),
}
}
}

/// Stack of variables.
#[derive(Debug, Clone)]
pub struct Stack<'g> {
globals: Option<&'g ValueStore>,
stack: Vec<Object>,
stack: Vec<Frame>,
// State of variables created through increment or decrement tags.
indexes: Object,
}
Expand All @@ -22,7 +41,7 @@ impl<'g> Stack<'g> {
globals: None,
indexes: Object::new(),
// Mutable frame for globals.
stack: vec![Object::new()],
stack: vec![Frame::new()],
}
}

Expand All @@ -35,7 +54,12 @@ impl<'g> Stack<'g> {

/// Creates a new variable scope chained to a parent scope.
pub(crate) fn push_frame(&mut self) {
self.stack.push(Object::new());
self.stack.push(Frame::new());
}

/// Creates a new variable scope chained to a parent scope.
pub(crate) fn push_named_frame<S: Into<String>>(&mut self, name: S) {
self.stack.push(Frame::with_name(name));
}

/// Removes the topmost stack frame from the local variable stack.
Expand All @@ -52,6 +76,14 @@ impl<'g> Stack<'g> {
};
}

/// The name of the currently active template.
pub fn frame_name(&self) -> Option<&str> {
self.stack
.iter()
.rev()
.find_map(|f| f.name.as_ref().map(|s| s.as_str()))
}

/// Recursively index into the stack.
pub fn try_get(&self, path: PathRef) -> Option<&Value> {
let frame = self.find_path_frame(path)?;
Expand Down Expand Up @@ -79,7 +111,7 @@ impl<'g> Stack<'g> {
fn globals(&self) -> Vec<&str> {
let mut globals = self.globals.map(|g| g.roots()).unwrap_or_default();
for frame in self.stack.iter() {
globals.extend(frame.roots());
globals.extend(frame.data.roots());
}
globals.sort();
globals.dedup();
Expand All @@ -94,8 +126,8 @@ impl<'g> Stack<'g> {

fn find_frame<'a>(&'a self, name: &str) -> Option<&'a ValueStore> {
for frame in self.stack.iter().rev() {
if frame.contains_root(name) {
return Some(frame);
if frame.data.contains_root(name) {
return Some(&frame.data);
}
}

Expand Down Expand Up @@ -148,14 +180,14 @@ impl<'g> Stack<'g> {

fn current_frame(&mut self) -> &mut Object {
match self.stack.last_mut() {
Some(frame) => frame,
Some(frame) => &mut frame.data,
None => panic!("Global frame removed."),
}
}

fn global_frame(&mut self) -> &mut Object {
match self.stack.first_mut() {
Some(frame) => frame,
Some(frame) => &mut frame.data,
None => panic!("Global frame removed."),
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/tags/include_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ struct Include {
}

impl Renderable for Include {
fn render_to(&self, writer: &mut Write, mut context: &mut Context) -> Result<()> {
self.partial
.render_to(writer, &mut context)
.trace_with(|| format!("{{% include {} %}}", self.name).into())?;
fn render_to(&self, writer: &mut Write, context: &mut Context) -> Result<()> {
context.run_in_named_scope(self.name.clone(), |mut scope| -> Result<()> {
self.partial
.render_to(writer, &mut scope)
.trace_with(|| format!("{{% include {} %}}", self.name).into())
})?;

Ok(())
}
Expand Down

0 comments on commit 4c37817

Please sign in to comment.