From dbe0c6ee89be7bac0b610ec60e0ba7a4a6391e22 Mon Sep 17 00:00:00 2001 From: user_undeclared <93453842+user-undeclared@users.noreply.github.com> Date: Tue, 31 Oct 2023 03:08:48 -0700 Subject: [PATCH 1/4] add parsing and lexing support for unary minus the unary minus was added by adding a new Expr and enum called UnOp (short for Unary Operator). `parse_binary_operator` was also renamed to `parse_impl` since it now parses both binary and unary operators --- src/command.rs | 7 ++++ src/engine/expr.rs | 89 +++++++++++++++++++++++++++++++++++++--------- src/engine/rule.rs | 2 ++ 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/command.rs b/src/command.rs index b9494fc..6d4e39d 100644 --- a/src/command.rs +++ b/src/command.rs @@ -65,6 +65,13 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } + }, + Expr::UnOp(op, expr) => { + write!(f, "{}", op)?; + match **expr { + Expr::Op(_, _, _) | Expr::UnOp(_, _) => write!(f, "({})", expr), + _ => write!(f, "{}", expr) + } } } } diff --git a/src/engine/expr.rs b/src/engine/expr.rs index 066a55e..ecc7812 100644 --- a/src/engine/expr.rs +++ b/src/engine/expr.rs @@ -37,7 +37,6 @@ macro_rules! expr { }; } -// TODO: unary minus #[derive(Debug, Copy, Clone, PartialEq)] pub enum Op { Add, @@ -90,12 +89,35 @@ impl fmt::Display for Op { } } +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum UnOp { + Neg, +} + +impl UnOp { + fn from_token_kind(kind: TokenKind) -> Option { + match kind { + TokenKind::Dash => Some(UnOp::Neg), + _ => None + } + } +} + +impl fmt::Display for UnOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + UnOp::Neg => write!(f, "-"), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Expr { Sym(Token), Var(Token), Fun(Box, Vec), Op(Op, Box, Box), + UnOp(UnOp, Box), } impl Expr { @@ -116,6 +138,10 @@ impl Expr { rhs.substitute(bindings); }, + Self::UnOp(_, expr) => { + expr.substitute(bindings); + }, + Self::Fun(head, args) => { head.substitute(bindings); for arg in args { @@ -150,6 +176,7 @@ impl Expr { Self::Var(_) => "a variable", Self::Fun(_, _) => "a functor", Self::Op(_, _, _) => "a binary operator", + Self::UnOp(_, _) => "a unary operator", } } @@ -192,6 +219,10 @@ impl Expr { Self::parse_ident(token) }, + TokenKind::Dash => { + Self::parse(lexer, diag)? + }, + _ => { diag.report(&token.loc, Severity::Error, &format!("Expected start of a primary expression. Primary expressions start with {} or {}.", TokenKind::Ident, TokenKind::OpenParen)); return None; @@ -205,32 +236,41 @@ impl Expr { Some(head) } - fn parse_binary_operator(lexer: &mut Lexer, current_precedence: usize, diag: &mut impl Diagnoster) -> Option { + fn parse_impl(lexer: &mut Lexer, current_precedence: usize, diag: &mut impl Diagnoster) -> Option { if current_precedence > Op::MAX_PRECEDENCE { return Self::parse_primary(lexer, diag) } - let mut result = Self::parse_binary_operator(lexer, current_precedence + 1, diag)?; + if let Some(un_op_token) = UnOp::from_token_kind(lexer.peek_token().kind) { + lexer.next_token(); - while let Some(op) = Op::from_token_kind(lexer.peek_token().kind) { - if current_precedence != op.precedence() { - break - } + Some(Expr::UnOp( + un_op_token, + Box::new(Self::parse_impl(lexer, current_precedence, diag)?) + )) + } else { + let mut result = Self::parse_impl(lexer, current_precedence + 1, diag)?; - lexer.next_token(); + while let Some(op) = Op::from_token_kind(lexer.peek_token().kind) { + if current_precedence != op.precedence() { + break + } - result = Expr::Op( - op, - Box::new(result), - Box::new(Self::parse_binary_operator(lexer, current_precedence, diag)?) - ); - } + lexer.next_token(); - Some(result) + result = Expr::Op( + op, + Box::new(result), + Box::new(Self::parse_impl(lexer, current_precedence, diag)?) + ); + } + + Some(result) + } } pub fn parse(lexer: &mut Lexer, diag: &mut impl Diagnoster) -> Option { - Self::parse_binary_operator(lexer, 0, diag) + Self::parse_impl(lexer, 0, diag) } pub fn pattern_match(&self, value: &Expr) -> Option { @@ -302,6 +342,7 @@ impl fmt::Display for Expr { } else { write!(f, "{}", lhs)? } + Expr::UnOp(_, _) => write!(f, "({})", lhs)?, _ => write!(f, "{}", lhs)? } if op.precedence() <= 1 { @@ -315,8 +356,16 @@ impl fmt::Display for Expr { } else { write!(f, "{}", rhs) } + Expr::UnOp(_, _) => write!(f, "({})", rhs), _ => write!(f, "{}", rhs) } + }, + Expr::UnOp(un_op, expr) => { + write!(f, "{}", un_op)?; + match **expr { + Expr::UnOp(_, _) | Expr::Op(_, _, _) => write!(f, "({})", expr), + _ => write!(f, "{}", expr) + } } } } @@ -346,6 +395,11 @@ pub fn matches_at_least_one<'a>(pattern: &'a Expr, expr: &'a Expr) -> bool { return true; } } + Expr::UnOp(_, expr) => { + if matches_at_least_one(pattern, expr) { + return true; + } + } Expr::Sym(_) | Expr::Var(_) => {}, } false @@ -370,6 +424,9 @@ pub fn find_all_subexprs<'a>(pattern: &'a Expr, expr: &'a Expr) -> Vec<&'a Expr> find_all_subexprs_impl(pattern, lhs, subexprs); find_all_subexprs_impl(pattern, rhs, subexprs); } + Expr::UnOp(_, expr) => { + find_all_subexprs_impl(pattern, expr, subexprs); + } Expr::Sym(_) | Expr::Var(_) => {} } } diff --git a/src/engine/rule.rs b/src/engine/rule.rs index c5c527b..edcd441 100644 --- a/src/engine/rule.rs +++ b/src/engine/rule.rs @@ -104,6 +104,7 @@ impl Rule { } } + //TODO: support for applying rules that have unary minus signs in them pub fn apply(&self, expr: &mut Expr, strategy: &Strategy, apply_command_loc: &Loc, diag: &mut impl Diagnoster) -> Option<()> { fn apply_to_subexprs(rule: &Rule, expr: &mut Expr, strategy: &Strategy, apply_command_loc: &Loc, match_count: &mut usize, diag: &mut impl Diagnoster) -> Option { match expr { @@ -114,6 +115,7 @@ impl Rule { } apply_impl(rule, rhs, strategy, apply_command_loc, match_count, diag) } + Expr::UnOp(_, _) => todo!(), Expr::Fun(head, args) => { if apply_impl(rule, head, strategy, apply_command_loc, match_count, diag)? { return Some(true); From f1f60094946e285d3cf6feab432dbfb067659366 Mon Sep 17 00:00:00 2001 From: user_undeclared <93453842+user-undeclared@users.noreply.github.com> Date: Tue, 31 Oct 2023 05:33:59 -0700 Subject: [PATCH 2/4] finish implementing unary minus adding unary operators to the pattern matching code was easier than i thought! thanks zozi for the gud cood the unary minus ended up being pretty intuitive to use, but there are some things that are both weird to use and hard to fix. for instance, by making it so -1^2 = -(1^2), it also makes it so -1 + 2 = -(1 + 2). i already tried to fix that but it turned into a mess --- src/command.rs | 14 +++++++++----- src/engine/expr.rs | 7 +++++-- src/engine/rule.rs | 5 +++-- src/new_repl.rs | 11 +++++++++++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/command.rs b/src/command.rs index 6d4e39d..fe5a06c 100644 --- a/src/command.rs +++ b/src/command.rs @@ -50,6 +50,7 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } else { write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } + Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: lhs, subexpr})?, _ => write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } if op.precedence() <= 1 { @@ -63,14 +64,17 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } else { write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } + Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}), _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } }, - Expr::UnOp(op, expr) => { - write!(f, "{}", op)?; - match **expr { - Expr::Op(_, _, _) | Expr::UnOp(_, _) => write!(f, "({})", expr), - _ => write!(f, "{}", expr) + Expr::UnOp(un_op, un_op_expr) => { + write!(f, "{}", un_op)?; + match **un_op_expr { + Expr::Op(_, _, _) | Expr::UnOp(_, _) => { + write!(f, "({})", HighlightedSubexpr{expr: un_op_expr, subexpr}) + } + _ => write!(f, "{}", HighlightedSubexpr{expr: un_op_expr, subexpr}) } } } diff --git a/src/engine/expr.rs b/src/engine/expr.rs index ecc7812..94bbe22 100644 --- a/src/engine/expr.rs +++ b/src/engine/expr.rs @@ -241,11 +241,11 @@ impl Expr { return Self::parse_primary(lexer, diag) } - if let Some(un_op_token) = UnOp::from_token_kind(lexer.peek_token().kind) { + if let Some(un_op) = UnOp::from_token_kind(lexer.peek_token().kind) { lexer.next_token(); Some(Expr::UnOp( - un_op_token, + un_op, Box::new(Self::parse_impl(lexer, current_precedence, diag)?) )) } else { @@ -293,6 +293,9 @@ impl Expr { (Op(op1, lhs1, rhs1), Op(op2, lhs2, rhs2)) => { *op1 == *op2 && pattern_match_impl(lhs1, lhs2, bindings) && pattern_match_impl(rhs1, rhs2, bindings) } + (UnOp(un_op1, un_op_expr1), UnOp(un_op2, un_op_expr2)) => { + *un_op1 == *un_op2 && pattern_match_impl(un_op_expr1, un_op_expr2, bindings) + } (Fun(name1, args1), Fun(name2, args2)) => { if pattern_match_impl(name1, name2, bindings) && args1.len() == args2.len() { for i in 0..args1.len() { diff --git a/src/engine/rule.rs b/src/engine/rule.rs index edcd441..6648168 100644 --- a/src/engine/rule.rs +++ b/src/engine/rule.rs @@ -104,7 +104,6 @@ impl Rule { } } - //TODO: support for applying rules that have unary minus signs in them pub fn apply(&self, expr: &mut Expr, strategy: &Strategy, apply_command_loc: &Loc, diag: &mut impl Diagnoster) -> Option<()> { fn apply_to_subexprs(rule: &Rule, expr: &mut Expr, strategy: &Strategy, apply_command_loc: &Loc, match_count: &mut usize, diag: &mut impl Diagnoster) -> Option { match expr { @@ -115,7 +114,9 @@ impl Rule { } apply_impl(rule, rhs, strategy, apply_command_loc, match_count, diag) } - Expr::UnOp(_, _) => todo!(), + Expr::UnOp(_, un_op_expr) => { + apply_impl(rule, un_op_expr, strategy, apply_command_loc, match_count, diag) + } Expr::Fun(head, args) => { if apply_impl(rule, head, strategy, apply_command_loc, match_count, diag)? { return Some(true); diff --git a/src/new_repl.rs b/src/new_repl.rs index 199d78a..8478479 100644 --- a/src/new_repl.rs +++ b/src/new_repl.rs @@ -151,6 +151,7 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } else { write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } + Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: lhs, subexpr})?, _ => write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } if op.precedence() <= 1 { @@ -164,9 +165,19 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } else { write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } + Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}), _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } } + Expr::UnOp(un_op, un_op_expr) => { + write!(f, "{}", un_op)?; + match **un_op_expr { + Expr::UnOp(_, _) | Expr::Op(_, _, _) => { + write!(f, "({})", HighlightedSubexpr{expr: un_op_expr, subexpr}), + } + _ => write!(f, "{}", HighlightedSubexpr{expr: un_op_expr, subexpr}) + } + } } } } From d0701b08ccbf35425c0faf7320738433b9a3a8dd Mon Sep 17 00:00:00 2001 From: user_undeclared <93453842+user-undeclared@users.noreply.github.com> Date: Tue, 31 Oct 2023 06:17:11 -0700 Subject: [PATCH 3/4] final cleanup for unary minus-related things --- src/engine/expr.rs | 74 ++++++++++++++++++++++----------------------- src/engine/lexer.rs | 2 +- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/engine/expr.rs b/src/engine/expr.rs index 94bbe22..6f94621 100644 --- a/src/engine/expr.rs +++ b/src/engine/expr.rs @@ -138,8 +138,8 @@ impl Expr { rhs.substitute(bindings); }, - Self::UnOp(_, expr) => { - expr.substitute(bindings); + Self::UnOp(_, un_op_expr) => { + un_op_expr.substitute(bindings); }, Self::Fun(head, args) => { @@ -236,41 +236,41 @@ impl Expr { Some(head) } - fn parse_impl(lexer: &mut Lexer, current_precedence: usize, diag: &mut impl Diagnoster) -> Option { - if current_precedence > Op::MAX_PRECEDENCE { - return Self::parse_primary(lexer, diag) - } + pub fn parse(lexer: &mut Lexer, diag: &mut impl Diagnoster) -> Option { + fn parse_impl(lexer: &mut Lexer, current_precedence: usize, diag: &mut impl Diagnoster) -> Option { + if current_precedence > Op::MAX_PRECEDENCE { + return Expr::parse_primary(lexer, diag) + } - if let Some(un_op) = UnOp::from_token_kind(lexer.peek_token().kind) { - lexer.next_token(); + if let Some(un_op) = UnOp::from_token_kind(lexer.peek_token().kind) { + lexer.next_token(); - Some(Expr::UnOp( - un_op, - Box::new(Self::parse_impl(lexer, current_precedence, diag)?) - )) - } else { - let mut result = Self::parse_impl(lexer, current_precedence + 1, diag)?; + Some(Expr::UnOp( + un_op, + Box::new(parse_impl(lexer, current_precedence, diag)?) + )) + } else { + let mut result = parse_impl(lexer, current_precedence + 1, diag)?; - while let Some(op) = Op::from_token_kind(lexer.peek_token().kind) { - if current_precedence != op.precedence() { - break - } + while let Some(op) = Op::from_token_kind(lexer.peek_token().kind) { + if current_precedence != op.precedence() { + break + } - lexer.next_token(); + lexer.next_token(); - result = Expr::Op( - op, - Box::new(result), - Box::new(Self::parse_impl(lexer, current_precedence, diag)?) - ); - } + result = Expr::Op( + op, + Box::new(result), + Box::new(parse_impl(lexer, current_precedence, diag)?) + ); + } - Some(result) + Some(result) + } } - } - pub fn parse(lexer: &mut Lexer, diag: &mut impl Diagnoster) -> Option { - Self::parse_impl(lexer, 0, diag) + parse_impl(lexer, 0, diag) } pub fn pattern_match(&self, value: &Expr) -> Option { @@ -363,11 +363,11 @@ impl fmt::Display for Expr { _ => write!(f, "{}", rhs) } }, - Expr::UnOp(un_op, expr) => { + Expr::UnOp(un_op, un_op_expr) => { write!(f, "{}", un_op)?; - match **expr { - Expr::UnOp(_, _) | Expr::Op(_, _, _) => write!(f, "({})", expr), - _ => write!(f, "{}", expr) + match **un_op_expr { + Expr::UnOp(_, _) | Expr::Op(_, _, _) => write!(f, "({})", un_op_expr), + _ => write!(f, "{}", un_op_expr) } } } @@ -398,8 +398,8 @@ pub fn matches_at_least_one<'a>(pattern: &'a Expr, expr: &'a Expr) -> bool { return true; } } - Expr::UnOp(_, expr) => { - if matches_at_least_one(pattern, expr) { + Expr::UnOp(_, un_op_expr) => { + if matches_at_least_one(pattern, un_op_expr) { return true; } } @@ -427,8 +427,8 @@ pub fn find_all_subexprs<'a>(pattern: &'a Expr, expr: &'a Expr) -> Vec<&'a Expr> find_all_subexprs_impl(pattern, lhs, subexprs); find_all_subexprs_impl(pattern, rhs, subexprs); } - Expr::UnOp(_, expr) => { - find_all_subexprs_impl(pattern, expr, subexprs); + Expr::UnOp(_, un_op_expr) => { + find_all_subexprs_impl(pattern, un_op_expr, subexprs); } Expr::Sym(_) | Expr::Var(_) => {} } diff --git a/src/engine/lexer.rs b/src/engine/lexer.rs index 3cf2cf1..1d19717 100644 --- a/src/engine/lexer.rs +++ b/src/engine/lexer.rs @@ -52,10 +52,10 @@ pub enum TokenKind { CloseCurly, Bar, Bang, + Dash, // Binary Operators Plus, - Dash, Asterisk, Slash, Caret, From b1d99fbab012d4c02fabd17966106290682b797b Mon Sep 17 00:00:00 2001 From: user_undeclared <93453842+user-undeclared@users.noreply.github.com> Date: Thu, 2 Nov 2023 01:50:17 -0700 Subject: [PATCH 4/4] fix unary minus and remove `UnOp` so it turns out that making the unary minus part of `Op` actually works better than putting it into a separate `UnOp` and makes it easier to give it the correct precedence --- src/command.rs | 22 ++++--- src/engine/expr.rs | 143 +++++++++++++++++++++++---------------------- src/engine/rule.rs | 11 ++-- src/new_repl.rs | 24 ++++---- 4 files changed, 100 insertions(+), 100 deletions(-) diff --git a/src/command.rs b/src/command.rs index fe5a06c..1ecb6ab 100644 --- a/src/command.rs +++ b/src/command.rs @@ -43,14 +43,14 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } write!(f, ")") }, - Expr::Op(op, lhs, rhs) => { + Expr::Op(op, Some(lhs), rhs) => { match **lhs { - Expr::Op(sub_op, _, _) => if sub_op.precedence() <= op.precedence() { + Expr::Op(sub_op, Some(_), _) => if sub_op.precedence() <= op.precedence() { write!(f, "({})", HighlightedSubexpr{expr: lhs, subexpr})? } else { write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } - Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: lhs, subexpr})?, + Expr::Op(_, None, _) => write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})?, _ => write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } if op.precedence() <= 1 { @@ -59,22 +59,20 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { write!(f, "{}", op)?; } match **rhs { - Expr::Op(sub_op, _, _) => if sub_op.precedence() <= op.precedence() { + Expr::Op(sub_op, Some(_), _) => if sub_op.precedence() <= op.precedence() { write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}) } else { write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } - Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}), + Expr::Op(_, None, _) => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}), _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } }, - Expr::UnOp(un_op, un_op_expr) => { - write!(f, "{}", un_op)?; - match **un_op_expr { - Expr::Op(_, _, _) | Expr::UnOp(_, _) => { - write!(f, "({})", HighlightedSubexpr{expr: un_op_expr, subexpr}) - } - _ => write!(f, "{}", HighlightedSubexpr{expr: un_op_expr, subexpr}) + Expr::Op(op, None, rhs) => { + write!(f, "{}", op)?; + match **rhs { + Expr::Op(_, _, _) => write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}), + _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } } } diff --git a/src/engine/expr.rs b/src/engine/expr.rs index 6f94621..8900f31 100644 --- a/src/engine/expr.rs +++ b/src/engine/expr.rs @@ -46,10 +46,11 @@ pub enum Op { Pow, Mod, Eql, + Neg, } impl Op { - fn from_token_kind(kind: TokenKind) -> Option { + fn binary_op_from_token_kind(kind: TokenKind) -> Option { match kind { TokenKind::Plus => Some(Op::Add), TokenKind::Dash => Some(Op::Sub), @@ -62,17 +63,25 @@ impl Op { } } + fn unary_op_from_token_kind(kind: TokenKind) -> Option { + match kind { + TokenKind::Dash => Some(Op::Neg), + _ => None + } + } + pub fn precedence(&self) -> usize { use Op::*; match self { Eql => 0, Add | Sub => 1, Mul | Div | Mod => 2, - Pow => 3, + Neg => 3, + Pow => 4, } } - const MAX_PRECEDENCE: usize = 3; + const MAX_PRECEDENCE: usize = 4; } impl fmt::Display for Op { @@ -85,39 +94,16 @@ impl fmt::Display for Op { Op::Div => write!(f, "/"), Op::Mod => write!(f, "%"), Op::Pow => write!(f, "^"), + Op::Neg => write!(f, "-"), } } } - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum UnOp { - Neg, -} - -impl UnOp { - fn from_token_kind(kind: TokenKind) -> Option { - match kind { - TokenKind::Dash => Some(UnOp::Neg), - _ => None - } - } -} - -impl fmt::Display for UnOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - UnOp::Neg => write!(f, "-"), - } - } -} - #[derive(Debug, Clone, PartialEq)] pub enum Expr { Sym(Token), Var(Token), Fun(Box, Vec), - Op(Op, Box, Box), - UnOp(UnOp, Box), + Op(Op, Option>, Box), } impl Expr { @@ -133,15 +119,13 @@ impl Expr { *self = value.clone() } - Self::Op(_, lhs, rhs) => { - lhs.substitute(bindings); + Self::Op(_, maybe_lhs, rhs) => { + if let Some(lhs) = maybe_lhs { + lhs.substitute(bindings); + } rhs.substitute(bindings); }, - Self::UnOp(_, un_op_expr) => { - un_op_expr.substitute(bindings); - }, - Self::Fun(head, args) => { head.substitute(bindings); for arg in args { @@ -175,8 +159,8 @@ impl Expr { Self::Sym(_) => "a symbol", Self::Var(_) => "a variable", Self::Fun(_, _) => "a functor", - Self::Op(_, _, _) => "a binary operator", - Self::UnOp(_, _) => "a unary operator", + Self::Op(_, Some(_), _) => "a binary operator", + Self::Op(_, None, _) => "a unary operator", } } @@ -242,17 +226,37 @@ impl Expr { return Expr::parse_primary(lexer, diag) } - if let Some(un_op) = UnOp::from_token_kind(lexer.peek_token().kind) { + if let Some(un_op) = Op::unary_op_from_token_kind(lexer.peek_token().kind) { lexer.next_token(); - Some(Expr::UnOp( + let token_after_un_op_is_open_paren = lexer.peek_token().kind == TokenKind::OpenParen; + let sub_expr = parse_impl(lexer, current_precedence, diag)?; + + if !token_after_un_op_is_open_paren { + if let Expr::Op(op, Some(lhs), rhs) = &sub_expr { + if op.precedence() < un_op.precedence() { + return Some(Expr::Op( + *op, + Some(Box::new(Expr::Op( + un_op, + None, + lhs.clone() + ))), + rhs.clone() + )) + } + } + } + + Some(Expr::Op( un_op, - Box::new(parse_impl(lexer, current_precedence, diag)?) + None, + Box::new(sub_expr) )) } else { let mut result = parse_impl(lexer, current_precedence + 1, diag)?; - while let Some(op) = Op::from_token_kind(lexer.peek_token().kind) { + while let Some(op) = Op::binary_op_from_token_kind(lexer.peek_token().kind) { if current_precedence != op.precedence() { break } @@ -261,7 +265,7 @@ impl Expr { result = Expr::Op( op, - Box::new(result), + Some(Box::new(result)), Box::new(parse_impl(lexer, current_precedence, diag)?) ); } @@ -290,11 +294,16 @@ impl Expr { true } } - (Op(op1, lhs1, rhs1), Op(op2, lhs2, rhs2)) => { - *op1 == *op2 && pattern_match_impl(lhs1, lhs2, bindings) && pattern_match_impl(rhs1, rhs2, bindings) - } - (UnOp(un_op1, un_op_expr1), UnOp(un_op2, un_op_expr2)) => { - *un_op1 == *un_op2 && pattern_match_impl(un_op_expr1, un_op_expr2, bindings) + (Op(op1, maybe_lhs1, rhs1), Op(op2, maybe_lhs2, rhs2)) => { + if *op1 != *op2 || !pattern_match_impl(rhs1, rhs2, bindings) { + false + } else { + match (maybe_lhs1, maybe_lhs2) { + (Some(lhs1), Some(lhs2)) => pattern_match_impl(lhs1, lhs2, bindings), + (None, None) => true, + _ => false + } + } } (Fun(name1, args1), Fun(name2, args2)) => { if pattern_match_impl(name1, name2, bindings) && args1.len() == args2.len() { @@ -338,14 +347,14 @@ impl fmt::Display for Expr { } write!(f, ")") }, - Expr::Op(op, lhs, rhs) => { + Expr::Op(op, Some(lhs), rhs) => { match **lhs { - Expr::Op(sub_op, _, _) => if sub_op.precedence() <= op.precedence() { + Expr::Op(sub_op, Some(_), _) => if sub_op.precedence() <= op.precedence() { write!(f, "({})", lhs)? } else { write!(f, "{}", lhs)? } - Expr::UnOp(_, _) => write!(f, "({})", lhs)?, + Expr::Op(_, None, _) => write!(f, "{}", lhs)?, _ => write!(f, "{}", lhs)? } if op.precedence() <= 1 { @@ -354,20 +363,20 @@ impl fmt::Display for Expr { write!(f, "{}", op)?; } match **rhs { - Expr::Op(sub_op, _, _) => if sub_op.precedence() <= op.precedence() { + Expr::Op(sub_op, Some(_), _) => if sub_op.precedence() <= op.precedence() { write!(f, "({})", rhs) } else { write!(f, "{}", rhs) } - Expr::UnOp(_, _) => write!(f, "({})", rhs), + Expr::Op(_, None, _) => write!(f, "{}", rhs), _ => write!(f, "{}", rhs) } }, - Expr::UnOp(un_op, un_op_expr) => { - write!(f, "{}", un_op)?; - match **un_op_expr { - Expr::UnOp(_, _) | Expr::Op(_, _, _) => write!(f, "({})", un_op_expr), - _ => write!(f, "{}", un_op_expr) + Expr::Op(op, None, rhs) => { + write!(f, "{}", op)?; + match **rhs { + Expr::Op(_, _, _) => write!(f, "({})", rhs), + _ => write!(f, "{}", rhs) } } } @@ -390,19 +399,16 @@ pub fn matches_at_least_one<'a>(pattern: &'a Expr, expr: &'a Expr) -> bool { } } } - Expr::Op(_, lhs, rhs) => { - if matches_at_least_one(pattern, lhs) { - return true; + Expr::Op(_, maybe_lhs, rhs) => { + if let Some(lhs) = maybe_lhs { + if matches_at_least_one(pattern, lhs) { + return true; + } } if matches_at_least_one(pattern, rhs) { return true; } } - Expr::UnOp(_, un_op_expr) => { - if matches_at_least_one(pattern, un_op_expr) { - return true; - } - } Expr::Sym(_) | Expr::Var(_) => {}, } false @@ -423,13 +429,12 @@ pub fn find_all_subexprs<'a>(pattern: &'a Expr, expr: &'a Expr) -> Vec<&'a Expr> find_all_subexprs_impl(pattern, arg, subexprs); } } - Expr::Op(_, lhs, rhs) => { - find_all_subexprs_impl(pattern, lhs, subexprs); + Expr::Op(_, maybe_lhs, rhs) => { + if let Some(lhs) = maybe_lhs { + find_all_subexprs_impl(pattern, lhs, subexprs); + } find_all_subexprs_impl(pattern, rhs, subexprs); } - Expr::UnOp(_, un_op_expr) => { - find_all_subexprs_impl(pattern, un_op_expr, subexprs); - } Expr::Sym(_) | Expr::Var(_) => {} } } diff --git a/src/engine/rule.rs b/src/engine/rule.rs index 6648168..34aba11 100644 --- a/src/engine/rule.rs +++ b/src/engine/rule.rs @@ -108,15 +108,14 @@ impl Rule { fn apply_to_subexprs(rule: &Rule, expr: &mut Expr, strategy: &Strategy, apply_command_loc: &Loc, match_count: &mut usize, diag: &mut impl Diagnoster) -> Option { match expr { Expr::Sym(_) | Expr::Var(_) => Some(false), - Expr::Op(_, lhs, rhs) => { - if apply_impl(rule, lhs, strategy, apply_command_loc, match_count, diag)? { - return Some(true) + Expr::Op(_, maybe_lhs, rhs) => { + if let Some(lhs) = maybe_lhs { + if apply_impl(rule, lhs, strategy, apply_command_loc, match_count, diag)? { + return Some(true) + } } apply_impl(rule, rhs, strategy, apply_command_loc, match_count, diag) } - Expr::UnOp(_, un_op_expr) => { - apply_impl(rule, un_op_expr, strategy, apply_command_loc, match_count, diag) - } Expr::Fun(head, args) => { if apply_impl(rule, head, strategy, apply_command_loc, match_count, diag)? { return Some(true); diff --git a/src/new_repl.rs b/src/new_repl.rs index 8478479..c22b53d 100644 --- a/src/new_repl.rs +++ b/src/new_repl.rs @@ -144,14 +144,14 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { } write!(f, ")") }, - Expr::Op(op, lhs, rhs) => { + Expr::Op(op, Some(lhs), rhs) => { match **lhs { - Expr::Op(sub_op, _, _) => if sub_op.precedence() <= op.precedence() { + Expr::Op(sub_op, Some(_), _) => if sub_op.precedence() <= op.precedence() { write!(f, "({})", HighlightedSubexpr{expr: lhs, subexpr})? } else { write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } - Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: lhs, subexpr})?, + Expr::Op(_, None, _) => write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})?, _ => write!(f, "{}", HighlightedSubexpr{expr: lhs, subexpr})? } if op.precedence() <= 1 { @@ -160,22 +160,20 @@ impl<'a> fmt::Display for HighlightedSubexpr<'a> { write!(f, "{}", op)?; } match **rhs { - Expr::Op(sub_op, _, _) => if sub_op.precedence() <= op.precedence() { + Expr::Op(sub_op, Some(_), _) => if sub_op.precedence() <= op.precedence() { write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}) } else { write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } - Expr::UnOp(_, _) => write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}), + Expr::Op(_, None, _) => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}), _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } - } - Expr::UnOp(un_op, un_op_expr) => { - write!(f, "{}", un_op)?; - match **un_op_expr { - Expr::UnOp(_, _) | Expr::Op(_, _, _) => { - write!(f, "({})", HighlightedSubexpr{expr: un_op_expr, subexpr}), - } - _ => write!(f, "{}", HighlightedSubexpr{expr: un_op_expr, subexpr}) + }, + Expr::Op(op, None, rhs) => { + write!(f, "{}", op)?; + match **rhs { + Expr::Op(_, _, _) => write!(f, "({})", HighlightedSubexpr{expr: rhs, subexpr}), + _ => write!(f, "{}", HighlightedSubexpr{expr: rhs, subexpr}) } } }