diff --git a/liquid-compiler/src/parser.rs b/liquid-compiler/src/parser.rs index 5b5079c68..7dc5fa23d 100644 --- a/liquid-compiler/src/parser.rs +++ b/liquid-compiler/src/parser.rs @@ -57,11 +57,14 @@ pub fn unexpected_token_error_string(expected: &str, actual: Option) -> // creates an expression, which wraps everything that gets rendered fn parse_expression(tokens: &[Token], options: &LiquidOptions) -> Result> { match tokens.get(0) { - Some(&Token::Identifier(ref x)) + Some(&Token::Identifier(_)) if tokens.len() > 1 && (tokens[1] == Token::Dot || tokens[1] == Token::OpenSquare) => { + let mut result = tokens[0] + .to_arg()? + .into_variable() + .expect("identifiers must be variables"); let indexes = parse_indexes(&tokens[1..])?; - let mut result = Variable::with_literal(x.clone()); result.extend(indexes); Ok(Box::new(result)) } @@ -344,6 +347,7 @@ mod test_parse_expression { use super::*; use liquid_interpreter::Context; + use liquid_value::Array; use liquid_value::Object; use liquid_value::Value; @@ -401,6 +405,32 @@ mod test_parse_expression { let result = result.render(&mut context).unwrap(); assert_eq!("42", result); } + + #[test] + fn array_index_access() { + let tokens = granularize("post[0]").unwrap(); + let mut context = Context::new(); + let mut post = Array::new(); + post.push(Value::scalar(42i32)); + context.stack_mut().set_global("post", Value::Array(post)); + + let result = parse_expression(&tokens, &null_options()).unwrap(); + let result = result.render(&mut context).unwrap(); + assert_eq!("42", result); + } + + #[test] + fn mixed_access() { + let tokens = granularize("post.child[0]").unwrap(); + let mut context = Context::new(); + let mut post = Object::new(); + post.insert("child".into(), Value::Array(vec![Value::scalar(42i32)])); + context.stack_mut().set_global("post", Value::Object(post)); + + let result = parse_expression(&tokens, &null_options()).unwrap(); + let result = result.render(&mut context).unwrap(); + assert_eq!("42", result); + } } #[cfg(test)] diff --git a/liquid-error/src/error.rs b/liquid-error/src/error.rs index 90cef2607..910ceb9ef 100644 --- a/liquid-error/src/error.rs +++ b/liquid-error/src/error.rs @@ -176,9 +176,6 @@ const ERROR_DESCRIPTION: &str = "liquid"; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "{}: {}", ERROR_DESCRIPTION, self.inner.msg)?; - if let Some(ref cause) = self.inner.cause { - writeln!(f, "cause: {}", cause)?; - } for trace in &self.inner.user_backtrace { if let Some(trace) = trace.get_trace() { writeln!(f, "from: {}", trace)?; diff --git a/liquid-interpreter/src/context.rs b/liquid-interpreter/src/context.rs index dcd7db619..0292d382f 100644 --- a/liquid-interpreter/src/context.rs +++ b/liquid-interpreter/src/context.rs @@ -169,7 +169,7 @@ impl<'g> Stack<'g> { let mut indexes = path.iter(); let key = indexes .next() - .ok_or_else(|| Error::with_msg("No index provided"))?; + .ok_or_else(|| Error::with_msg("No variable provided"))?; let key = key.to_str(); let value = self.get_root(key.as_ref())?; @@ -177,7 +177,9 @@ impl<'g> Stack<'g> { let value = value?; let child = value.get(index); let child = child.ok_or_else(|| { - Error::with_msg("Invalid index").context("index", key.as_ref().to_owned()) + Error::with_msg("Unknown index") + .context("variable", format!("{}", path)) + .context("index", format!("{}", index)) })?; Ok(child) }) @@ -190,7 +192,7 @@ impl<'g> Stack<'g> { } } self.globals - .ok_or_else(|| Error::with_msg("Invalid index").context("index", name.to_owned())) + .ok_or_else(|| Error::with_msg("Unknown variable").context("variable", name.to_owned())) .and_then(|g| g.get(name)) .or_else(|err| self.get_index(name).ok_or_else(|| err)) } diff --git a/liquid-interpreter/src/expression.rs b/liquid-interpreter/src/expression.rs index dab0685d7..01dba4e6e 100644 --- a/liquid-interpreter/src/expression.rs +++ b/liquid-interpreter/src/expression.rs @@ -22,6 +22,22 @@ impl Expression { Expression::Literal(Value::scalar(literal)) } + /// Convert into a literal if possible. + pub fn into_literal(self) -> Option { + match self { + Expression::Literal(x) => Some(x), + Expression::Variable(_) => None, + } + } + + /// Convert into a variable, if possible. + pub fn into_variable(self) -> Option { + match self { + Expression::Literal(_) => None, + Expression::Variable(x) => Some(x), + } + } + /// Convert to a `Value`. pub fn evaluate(&self, context: &Context) -> Result { let val = match *self { diff --git a/liquid-interpreter/src/filter_chain.rs b/liquid-interpreter/src/filter_chain.rs index 739fe7f49..30a84f064 100644 --- a/liquid-interpreter/src/filter_chain.rs +++ b/liquid-interpreter/src/filter_chain.rs @@ -71,6 +71,7 @@ impl FilterChain { entry = f .filter(&entry, &*arguments) .chain("Filter error") + .context_with(|| ("filter".to_owned(), format!("{}", self))) .context_with(|| ("input".to_owned(), format!("{}", &entry))) .context_with(|| ("args".to_owned(), itertools::join(&arguments, ", ")))?; } diff --git a/liquid-interpreter/src/globals.rs b/liquid-interpreter/src/globals.rs index cdcc4dbe3..d4394b5ff 100644 --- a/liquid-interpreter/src/globals.rs +++ b/liquid-interpreter/src/globals.rs @@ -13,6 +13,6 @@ pub trait Globals: fmt::Debug { impl Globals for Object { fn get<'a>(&'a self, name: &str) -> Result<&'a Value> { self.get(name) - .ok_or_else(|| Error::with_msg("Invalid index").context("index", name.to_owned())) + .ok_or_else(|| Error::with_msg("Unknown variable").context("variable", name.to_owned())) } } diff --git a/src/filters/math.rs b/src/filters/math.rs index 4c54333b7..c74b0479b 100644 --- a/src/filters/math.rs +++ b/src/filters/math.rs @@ -12,7 +12,7 @@ pub fn abs(input: &Value, args: &[Value]) -> FilterResult { .to_integer() .map(|i| Value::scalar(i.abs())) .or_else(|| s.to_float().map(|i| Value::scalar(i.abs()))) - .ok_or_else(|| invalid_input("Numeric value expected")), + .ok_or_else(|| invalid_input("Numeric expected")), _ => Err(invalid_input("Number expected")), } }