forked from mirrors/jj
templater: parse negative integer as unary operator and literal
Follows up 9702a425e5
"Allow negative numbers in the template grammar."
Since we've added parsing rules for operator expressions, it makes sense to
parse unary '-' as operator.
This commit is contained in:
parent
5b517b542e
commit
e943a5b092
4 changed files with 33 additions and 7 deletions
|
@ -25,8 +25,8 @@ raw_literal = @{ literal_char+ }
|
|||
literal = { "\"" ~ (raw_literal | escape)* ~ "\"" }
|
||||
|
||||
integer_literal = {
|
||||
"-"? ~ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*
|
||||
| "-"? ~ "0"
|
||||
ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*
|
||||
| "0"
|
||||
}
|
||||
|
||||
identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
|
@ -35,7 +35,8 @@ concat_op = { "++" }
|
|||
logical_or_op = { "||" }
|
||||
logical_and_op = { "&&" }
|
||||
logical_not_op = { "!" }
|
||||
prefix_ops = _{ logical_not_op }
|
||||
negate_op = { "-" }
|
||||
prefix_ops = _{ logical_not_op | negate_op }
|
||||
infix_ops = _{ logical_or_op | logical_and_op }
|
||||
|
||||
function = { identifier ~ "(" ~ whitespace* ~ function_arguments ~ whitespace* ~ ")" }
|
||||
|
|
|
@ -283,6 +283,11 @@ fn build_unary_operation<'a, L: TemplateLanguage<'a>>(
|
|||
let arg = expect_boolean_expression(language, build_ctx, arg_node)?;
|
||||
language.wrap_boolean(TemplateFunction::new(arg, |v| !v))
|
||||
}
|
||||
UnaryOp::Negate => {
|
||||
let arg = expect_integer_expression(language, build_ctx, arg_node)?;
|
||||
// TODO: propagate error on overflow?
|
||||
language.wrap_integer(TemplateFunction::new(arg, |v| v.saturating_neg()))
|
||||
}
|
||||
};
|
||||
Ok(Expression::unlabeled(property))
|
||||
}
|
||||
|
@ -1147,12 +1152,12 @@ mod tests {
|
|||
= Method "foo" doesn't exist for type "Integer"
|
||||
"###);
|
||||
insta::assert_snapshot!(env.parse_err(r#"(-empty)"#), @r###"
|
||||
--> 1:2
|
||||
--> 1:3
|
||||
|
|
||||
1 | (-empty)
|
||||
| ^---
|
||||
| ^---^
|
||||
|
|
||||
= expected <template>
|
||||
= Expected expression of type "Integer"
|
||||
"###);
|
||||
|
||||
insta::assert_snapshot!(env.parse_err(r#"("foo" ++ "bar").baz()"#), @r###"
|
||||
|
@ -1279,6 +1284,21 @@ mod tests {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic_operation() {
|
||||
let mut env = TestTemplateEnv::default();
|
||||
env.add_keyword("i64_min", |language| {
|
||||
language.wrap_integer(Literal(i64::MIN))
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(env.render_ok(r#"-1"#), @"-1");
|
||||
insta::assert_snapshot!(env.render_ok(r#"--2"#), @"2");
|
||||
insta::assert_snapshot!(env.render_ok(r#"-(3)"#), @"-3");
|
||||
|
||||
// No panic on integer overflow. Runtime error might be better.
|
||||
insta::assert_snapshot!(env.render_ok(r#"-i64_min"#), @"9223372036854775807");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logical_operation() {
|
||||
let env = TestTemplateEnv::default();
|
||||
|
|
|
@ -43,6 +43,7 @@ impl Rule {
|
|||
Rule::logical_or_op => Some("||"),
|
||||
Rule::logical_and_op => Some("&&"),
|
||||
Rule::logical_not_op => Some("!"),
|
||||
Rule::negate_op => Some("-"),
|
||||
Rule::prefix_ops => None,
|
||||
Rule::infix_ops => None,
|
||||
Rule::function => None,
|
||||
|
@ -253,6 +254,8 @@ pub enum ExpressionKind<'i> {
|
|||
pub enum UnaryOp {
|
||||
/// `!`
|
||||
LogicalNot,
|
||||
/// `-`
|
||||
Negate,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
|
@ -429,13 +432,14 @@ fn parse_expression_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode
|
|||
PrattParser::new()
|
||||
.op(Op::infix(Rule::logical_or_op, Assoc::Left))
|
||||
.op(Op::infix(Rule::logical_and_op, Assoc::Left))
|
||||
.op(Op::prefix(Rule::logical_not_op))
|
||||
.op(Op::prefix(Rule::logical_not_op) | Op::prefix(Rule::negate_op))
|
||||
});
|
||||
PRATT
|
||||
.map_primary(parse_term_node)
|
||||
.map_prefix(|op, rhs| {
|
||||
let op_kind = match op.as_rule() {
|
||||
Rule::logical_not_op => UnaryOp::LogicalNot,
|
||||
Rule::negate_op => UnaryOp::Negate,
|
||||
r => panic!("unexpected prefix operator rule {r:?}"),
|
||||
};
|
||||
let rhs = Box::new(rhs?);
|
||||
|
|
|
@ -57,6 +57,7 @@ The following keywords can be used in `jj op log` templates.
|
|||
The following operators are supported.
|
||||
|
||||
* `x.f()`: Method call.
|
||||
* `-x`: Negate integer value.
|
||||
* `!x`: Logical not.
|
||||
* `x && y`: Logical and.
|
||||
* `x || y`: Logical or.
|
||||
|
|
Loading…
Reference in a new issue