Skip to content

Commit

Permalink
Merge pull request #360 from andy128k/issue-308
Browse files Browse the repository at this point in the history
add support for 'is not' operator
  • Loading branch information
Keats authored Nov 26, 2018
2 parents 2104706 + 828df17 commit 180d8c7
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ impl Expr {
pub struct Test {
/// Which expression is evaluated
pub ident: String,
/// Is it using `not`?
pub negated: bool,
/// Name of the test
pub name: String,
/// Any optional arg given to the test
Expand Down
8 changes: 7 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ fn parse_test(pair: Pair<Rule>) -> Test {
};
}

Test { ident: ident.unwrap(), name: name.unwrap(), args }
Test { ident: ident.unwrap(), negated: false, name: name.unwrap(), args }
}

fn parse_string_concat(pair: Pair<Rule>) -> ExprVal {
Expand Down Expand Up @@ -238,6 +238,11 @@ fn parse_basic_expression(pair: Pair<Rule>) -> ExprVal {
_ => unreachable!(),
},
Rule::test => ExprVal::Test(parse_test(pair)),
Rule::test_not => {
let mut test = parse_test(pair);
test.negated = true;
ExprVal::Test(test)
},
Rule::fn_call => ExprVal::FunctionCall(parse_fn_call(pair)),
Rule::macro_call => ExprVal::MacroCall(parse_macro_call(pair)),
Rule::string => ExprVal::String(replace_string_markers(pair.as_str())),
Expand Down Expand Up @@ -899,6 +904,7 @@ pub fn parse(input: &str) -> TeraResult<Vec<Node>> {
Rule::op_modulo => "`%`".to_string(),
Rule::filter => "a filter".to_string(),
Rule::test => "a test".to_string(),
Rule::test_not => "a negated test".to_string(),
Rule::test_call => "a test call".to_string(),
Rule::test_arg => "a test argument (any expressions)".to_string(),
Rule::test_args => "a list of test arguments (any expressions)".to_string(),
Expand Down
3 changes: 2 additions & 1 deletion src/parser/tera.pest
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ string_concat = { (fn_call | float | int | string | dotted_square_bracket_ident)
/// We'll use precedence climbing on those in the parser phase

// boolean first so they are not caught as identifiers
basic_val = _{ boolean | string_concat | test | macro_call | fn_call | dotted_square_bracket_ident | float | int | string }
basic_val = _{ boolean | string_concat | test_not | test | macro_call | fn_call | dotted_square_bracket_ident | float | int | string }
basic_op = _{ op_plus | op_minus | op_times | op_slash | op_modulo }
basic_expr = { ("(" ~ basic_expr ~ ")" | basic_val) ~ (basic_op ~ basic_val)* }
basic_expr_filter = { basic_expr ~ filter* }
Expand Down Expand Up @@ -124,6 +124,7 @@ macro_call = { ident ~ "::" ~ ident ~ "(" ~ kwargs? ~ ")" }
test_arg = { logic_expr }
test_args = !{ test_arg ~ ("," ~ test_arg)* }
test_call = !{ ident ~ ("(" ~ test_args ~ ")")? }
test_not = { dotted_ident ~ "is" ~ "not" ~ test_call }
test = { dotted_ident ~ "is" ~ test_call }

// -------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/parser/tests/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ fn lex_logic_val() {
r#""hey""#,
"a is defined",
"a is defined(2)",
"a is not defined",
"1 + 1",
"1 + counts",
"1 + counts.first",
Expand Down
16 changes: 16 additions & 0 deletions src/parser/tests/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,21 @@ fn parse_variable_tag_simple_test() {
ast[0],
Node::VariableBlock(Expr::new(ExprVal::Test(Test {
ident: "id".to_string(),
negated: false,
name: "defined".to_string(),
args: vec![],
},)))
);
}

#[test]
fn parse_variable_tag_simple_negated_test() {
let ast = parse("{{ id is not defined }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(Expr::new(ExprVal::Test(Test {
ident: "id".to_string(),
negated: true,
name: "defined".to_string(),
args: vec![],
},)))
Expand All @@ -262,6 +277,7 @@ fn parse_variable_tag_test_as_expression() {
Node::VariableBlock(Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Test(Test {
ident: "user".to_string(),
negated: false,
name: "defined".to_string(),
args: vec![],
},))),
Expand Down
7 changes: 6 additions & 1 deletion src/renderer/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,12 @@ impl<'a> Processor<'a> {

let found = self.lookup_ident(&test.ident).map(|found| found.clone().into_owned()).ok();

Ok(tester_fn.test(found.as_ref(), &tester_args)?)
let result = tester_fn.test(found.as_ref(), &tester_args)?;
if test.negated {
Ok(!result)
} else {
Ok(result)
}
}

fn eval_tera_fn_call(self: &mut Self, function_call: &'a FunctionCall) -> Result<Val<'a>> {
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ fn render_variable_block_ident() {
("{{ name | length }}", "4"),
("{{ name is defined }}", "true"),
("{{ not name is defined }}", "false"),
("{{ name is not defined }}", "false"),
("{{ not name is not defined }}", "true"),
("{{ a is odd }}", "false"),
("{{ a is odd or b is odd }}", "true"),
("{{ range(start=1, end=4) }}", "[1, 2, 3]"),
Expand Down

0 comments on commit 180d8c7

Please sign in to comment.