From 6869b88564b7b8a72c82562ea47c807d2e241c79 Mon Sep 17 00:00:00 2001 From: LastLeaf Date: Mon, 24 Jun 2024 20:32:11 +0800 Subject: [PATCH 1/2] fix: ternary operator detection while leading dot in true branch (#167) --- .../src/parse/expr.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/glass-easel-template-compiler/src/parse/expr.rs b/glass-easel-template-compiler/src/parse/expr.rs index 959181e..a3eb828 100644 --- a/glass-easel-template-compiler/src/parse/expr.rs +++ b/glass-easel-template-compiler/src/parse/expr.rs @@ -1115,9 +1115,24 @@ define_operator!(logic_and, "&&", ["="]); define_operator!(logic_or, "||", ["="]); define_operator!(nullish_coalescing, "??", ["="]); -define_operator!(condition, "?", ["?", "."]); define_operator!(condition_end, ":", []); +impl ParseOperator { + fn condition(ps: &mut ParseState) -> Option> { + ps + .consume_str_except_followed("?", ["?", "."]) + .or_else(|| { + let [p0, p1, p2] = ps.peek_n::<3>()?; + if p0 == '?' && p1 == '.' && ('0'..='9').contains(&p2) { + // `?.1` should be treated as `?` and `.1` + ps.consume_str("?") + } else { + None + } + }) + } +} + // `=` `+=` `-=` `**=` `*=` `/=` `%=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `&&=` `||=` `??=` are not allowed fn is_ident_char(ch: char) -> bool { @@ -1955,6 +1970,13 @@ mod test { ParseErrorKind::UnexpectedExpressionCharacter, 5..5 ); + case!( + "{{ a ?. 1 : 2 }}", + "", + ParseErrorKind::UnexpectedExpressionCharacter, + 5..5 + ); + case!("{{ a ?.1 : 2 }}", "{{a?0.1:2}}"); case!("{{ a ? b : c }}", "{{a?b:c}}"); } From 57b440f187f08be666c1f6fffe29bbd3d183cdd6 Mon Sep 17 00:00:00 2001 From: LastLeaf Date: Tue, 25 Jun 2024 12:05:53 +0800 Subject: [PATCH 2/2] feat(template-compiler): support bitwise-shift ops (#166) --- .../src/parse/expr.rs | 101 +++++++++++++++--- .../src/proc_gen/expr.rs | 56 +++++++++- .../src/stringify/expr.rs | 67 +++++++++--- glass-easel/tests/tmpl/expression.test.ts | 20 ++++ 4 files changed, 205 insertions(+), 39 deletions(-) diff --git a/glass-easel-template-compiler/src/parse/expr.rs b/glass-easel-template-compiler/src/parse/expr.rs index a3eb828..0d1aee1 100644 --- a/glass-easel-template-compiler/src/parse/expr.rs +++ b/glass-easel-template-compiler/src/parse/expr.rs @@ -120,6 +120,22 @@ pub enum Expression { location: Range, }, + LeftShift { + left: Box, + right: Box, + location: Range, + }, + RightShift { + left: Box, + right: Box, + location: Range, + }, + UnsignedRightShift { + left: Box, + right: Box, + location: Range, + }, + Lt { left: Box, right: Box, @@ -267,6 +283,9 @@ impl TemplateStructure for Expression { Self::Remainer { left, .. } => left.location_start(), Self::Plus { left, .. } => left.location_start(), Self::Minus { left, .. } => left.location_start(), + Self::LeftShift { left, .. } => left.location_start(), + Self::RightShift { left, .. } => left.location_start(), + Self::UnsignedRightShift { left, .. } => left.location_start(), Self::Lt { left, .. } => left.location_start(), Self::Gt { left, .. } => left.location_start(), Self::Lte { left, .. } => left.location_start(), @@ -315,6 +334,9 @@ impl TemplateStructure for Expression { Self::Remainer { right, .. } => right.location_end(), Self::Plus { right, .. } => right.location_end(), Self::Minus { right, .. } => right.location_end(), + Self::LeftShift { right, .. } => right.location_end(), + Self::RightShift { right, .. } => right.location_end(), + Self::UnsignedRightShift { right, .. } => right.location_end(), Self::Lt { right, .. } => right.location_end(), Self::Gt { right, .. } => right.location_end(), Self::Lte { right, .. } => right.location_end(), @@ -1036,7 +1058,8 @@ macro_rules! parse_left_to_right { parse_left_to_right!(parse_multiply, parse_reverse, multiply => Multiply, divide => Divide, remainer => Remainer); parse_left_to_right!(parse_plus, parse_multiply, plus => Plus, minus => Minus); -parse_left_to_right!(parse_cmp, parse_plus, lt => Lt, gt => Gt, lte => Lte, gte => Gte, instanceof => InstanceOf); +parse_left_to_right!(parse_shift, parse_plus, left_shift => LeftShift, right_shift => RightShift, unsigned_right_shift => UnsignedRightShift); +parse_left_to_right!(parse_cmp, parse_shift, lt => Lt, gt => Gt, lte => Lte, gte => Gte, instanceof => InstanceOf); parse_left_to_right!(parse_eq, parse_cmp, eq => Eq, ne => Ne, eq_full => EqFull, ne_full => NeFull); parse_left_to_right!(parse_bit_and, parse_eq, bit_and => BitAnd); parse_left_to_right!(parse_bit_xor, parse_bit_and, bit_xor => BitXor); @@ -1090,7 +1113,9 @@ define_operator!(remainer, "%", ["="]); define_operator!(plus, "+", ["+", "="]); define_operator!(minus, "-", ["-", "="]); -// `<<` `>>` `>>>` are not supported +define_operator!(left_shift, "<<", ["="]); +define_operator!(right_shift, ">>", [">", "="]); +define_operator!(unsigned_right_shift, ">>>", ["="]); define_operator!(lt, "<", ["<", "="]); define_operator!(lte, "<=", []); @@ -1324,6 +1349,9 @@ macro_rules! iter_sub_expr { | Expression::Remainer { left, right, .. } | Expression::Plus { left, right, .. } | Expression::Minus { left, right, .. } + | Expression::LeftShift { left, right, .. } + | Expression::RightShift { left, right, .. } + | Expression::UnsignedRightShift { left, right, .. } | Expression::Lt { left, right, .. } | Expression::Gt { left, right, .. } | Expression::Lte { left, right, .. } @@ -1731,48 +1759,87 @@ mod test { } #[test] - fn lt() { + fn left_shift() { case!( - "{{ a < }}", + "{{ a << }}", "", ParseErrorKind::UnexpectedExpressionCharacter, - 7..7 + 8..8 ); case!( - "{{ a << b }}", + "{{ a <<= b }}", "", ParseErrorKind::UnexpectedExpressionCharacter, 5..5 ); - case!("{{ a < b + c }}", "{{a> }}", "", ParseErrorKind::UnexpectedExpressionCharacter, 8..8 ); - case!("{{ a <= b + c }}", "{{a<=b+c}}"); + case!( + "{{ a >>= b }}", + "", + ParseErrorKind::UnexpectedExpressionCharacter, + 5..5 + ); + case!("{{ a >> b + c }}", "{{a>>b+c}}"); } #[test] - fn gt() { + fn unsigned_right_shift() { case!( - "{{ a > }}", + "{{ a >>> }}", "", ParseErrorKind::UnexpectedExpressionCharacter, - 7..7 + 9..9 ); case!( - "{{ a >> b }}", + "{{ a >>>= b }}", "", ParseErrorKind::UnexpectedExpressionCharacter, 5..5 ); - case!("{{ a > b + c }}", "{{a>b+c}}"); + case!("{{ a >>> b + c }}", "{{a>>>b+c}}"); + } + + #[test] + fn lt() { + case!( + "{{ a < }}", + "", + ParseErrorKind::UnexpectedExpressionCharacter, + 7..7 + ); + case!("{{ a < b << c }}", "{{a }}", + "", + ParseErrorKind::UnexpectedExpressionCharacter, + 7..7 + ); + case!("{{ a > b << c }}", "{{a>b<= b + c }}", "{{a>=b+c}}"); + case!("{{ a >= b << c }}", "{{a>=b<> c }}", "{{a instanceof b>>c}}"); } #[test] diff --git a/glass-easel-template-compiler/src/proc_gen/expr.rs b/glass-easel-template-compiler/src/proc_gen/expr.rs index e0bbc09..8842e6f 100644 --- a/glass-easel-template-compiler/src/proc_gen/expr.rs +++ b/glass-easel-template-compiler/src/proc_gen/expr.rs @@ -801,6 +801,49 @@ impl Expression { PathAnalysisState::NotInPath } + Expression::LeftShift { + left: x, right: y, .. + } => { + x.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; + write!(value, "<<")?; + y.to_proc_gen_rec_and_end_path( + w, + scopes, + ExpressionLevel::Plus, + path_calc, + value, + )?; + PathAnalysisState::NotInPath + } + Expression::RightShift { + left: x, right: y, .. + } => { + x.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; + write!(value, ">>")?; + y.to_proc_gen_rec_and_end_path( + w, + scopes, + ExpressionLevel::Plus, + path_calc, + value, + )?; + PathAnalysisState::NotInPath + } + Expression::UnsignedRightShift { + left: x, right: y, .. + } => { + x.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; + write!(value, ">>>")?; + y.to_proc_gen_rec_and_end_path( + w, + scopes, + ExpressionLevel::Plus, + path_calc, + value, + )?; + PathAnalysisState::NotInPath + } + Expression::Lt { left: x, right: y, .. } => { @@ -812,7 +855,7 @@ impl Expression { value, )?; write!(value, "<")?; - y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Plus, path_calc, value)?; + y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; PathAnalysisState::NotInPath } Expression::Gt { @@ -826,7 +869,7 @@ impl Expression { value, )?; write!(value, ">")?; - y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Plus, path_calc, value)?; + y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; PathAnalysisState::NotInPath } Expression::Lte { @@ -840,7 +883,7 @@ impl Expression { value, )?; write!(value, "<=")?; - y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Plus, path_calc, value)?; + y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; PathAnalysisState::NotInPath } Expression::Gte { @@ -854,7 +897,7 @@ impl Expression { value, )?; write!(value, ">=")?; - y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Plus, path_calc, value)?; + y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; PathAnalysisState::NotInPath } Expression::InstanceOf { @@ -868,7 +911,7 @@ impl Expression { value, )?; write!(value, " instanceof ")?; - y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Plus, path_calc, value)?; + y.to_proc_gen_rec_and_end_path(w, scopes, ExpressionLevel::Shift, path_calc, value)?; PathAnalysisState::NotInPath } Expression::Eq { @@ -1202,6 +1245,9 @@ fn proc_gen_expression_level(expr: &Expression) -> ExpressionLevel { Expression::Remainer { .. } => ExpressionLevel::Multiply, Expression::Plus { .. } => ExpressionLevel::Plus, Expression::Minus { .. } => ExpressionLevel::Plus, + Expression::LeftShift { .. } => ExpressionLevel::Shift, + Expression::RightShift { .. } => ExpressionLevel::Shift, + Expression::UnsignedRightShift { .. } => ExpressionLevel::Shift, Expression::Lt { .. } => ExpressionLevel::Comparison, Expression::Gt { .. } => ExpressionLevel::Comparison, Expression::Lte { .. } => ExpressionLevel::Comparison, diff --git a/glass-easel-template-compiler/src/stringify/expr.rs b/glass-easel-template-compiler/src/stringify/expr.rs index a96faaa..3df4b46 100644 --- a/glass-easel-template-compiler/src/stringify/expr.rs +++ b/glass-easel-template-compiler/src/stringify/expr.rs @@ -6,21 +6,23 @@ use crate::{ parse::expr::{ArrayFieldKind, Expression, ObjectFieldKind}, }; +#[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub(crate) enum ExpressionLevel { Lit = 0, - Member = 1, - Unary = 2, - Multiply = 3, - Plus = 4, - Comparison = 5, - Eq = 6, - BitAnd = 7, - BitXor = 8, - BitOr = 9, - LogicAnd = 10, - LogicOr = 11, - Cond = 12, + Member, + Unary, + Multiply, + Plus, + Shift, + Comparison, + Eq, + BitAnd, + BitXor, + BitOr, + LogicAnd, + LogicOr, + Cond, } impl ExpressionLevel { @@ -51,6 +53,9 @@ impl ExpressionLevel { Expression::Remainer { .. } => ExpressionLevel::Multiply, Expression::Plus { .. } => ExpressionLevel::Plus, Expression::Minus { .. } => ExpressionLevel::Plus, + Expression::LeftShift { .. } => ExpressionLevel::Shift, + Expression::RightShift { .. } => ExpressionLevel::Shift, + Expression::UnsignedRightShift { .. } => ExpressionLevel::Shift, Expression::Lt { .. } => ExpressionLevel::Comparison, Expression::Gt { .. } => ExpressionLevel::Comparison, Expression::Lte { .. } => ExpressionLevel::Comparison, @@ -292,6 +297,34 @@ fn expression_strigify_write<'s, W: FmtWrite>( expression_strigify_write(&right, stringifier, ExpressionLevel::Multiply)?; } + Expression::LeftShift { + left, + right, + location, + } => { + expression_strigify_write(&left, stringifier, ExpressionLevel::Shift)?; + stringifier.write_token("<<", "<<", location)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + } + Expression::RightShift { + left, + right, + location, + } => { + expression_strigify_write(&left, stringifier, ExpressionLevel::Shift)?; + stringifier.write_token(">>", ">>", location)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + } + Expression::UnsignedRightShift { + left, + right, + location, + } => { + expression_strigify_write(&left, stringifier, ExpressionLevel::Shift)?; + stringifier.write_token(">>>", ">>>", location)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + } + Expression::Lt { left, right, @@ -299,7 +332,7 @@ fn expression_strigify_write<'s, W: FmtWrite>( } => { expression_strigify_write(&left, stringifier, ExpressionLevel::Comparison)?; stringifier.write_token("<", "<", location)?; - expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Shift)?; } Expression::Lte { left, @@ -308,7 +341,7 @@ fn expression_strigify_write<'s, W: FmtWrite>( } => { expression_strigify_write(&left, stringifier, ExpressionLevel::Comparison)?; stringifier.write_token("<=", "<=", location)?; - expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Shift)?; } Expression::Gt { left, @@ -317,7 +350,7 @@ fn expression_strigify_write<'s, W: FmtWrite>( } => { expression_strigify_write(&left, stringifier, ExpressionLevel::Comparison)?; stringifier.write_token(">", ">", location)?; - expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Shift)?; } Expression::Gte { left, @@ -326,7 +359,7 @@ fn expression_strigify_write<'s, W: FmtWrite>( } => { expression_strigify_write(&left, stringifier, ExpressionLevel::Comparison)?; stringifier.write_token(">=", ">=", location)?; - expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Shift)?; } Expression::InstanceOf { left, @@ -335,7 +368,7 @@ fn expression_strigify_write<'s, W: FmtWrite>( } => { expression_strigify_write(&left, stringifier, ExpressionLevel::Comparison)?; stringifier.write_token(" instanceof ", " instanceof ", location)?; - expression_strigify_write(&right, stringifier, ExpressionLevel::Plus)?; + expression_strigify_write(&right, stringifier, ExpressionLevel::Shift)?; } Expression::Eq { diff --git a/glass-easel/tests/tmpl/expression.test.ts b/glass-easel/tests/tmpl/expression.test.ts index 67fca1d..81b1b35 100644 --- a/glass-easel/tests/tmpl/expression.test.ts +++ b/glass-easel/tests/tmpl/expression.test.ts @@ -101,6 +101,26 @@ describe('binding expression', () => { expect(n4.dataset.a).toEqual(-789) }) + test('shift operators', () => { + const def = glassEasel.registerElement({ + template: tmpl(` +
+
+ `), + data: { + a: 0x10, + }, + }) + const elem = glassEasel.Component.createWithContext('root', def.general(), domBackend) + const n1 = elem.getShadowRoot()!.getElementById('n1')! + const n2 = elem.getShadowRoot()!.getElementById('n2')! + expect(n1.dataset.a).toEqual(0x20) + expect(n2.dataset.a).toEqual(0x02) + elem.setData({ a: -0x10 }) + expect(n1.dataset.a).toEqual(-0x20) + expect(n2.dataset.a).toEqual(0x1ffffffe) + }) + test('comparison operators', () => { const def = glassEasel.registerElement({ template: tmpl(`