diff --git a/compiler/can/src/operator.rs b/compiler/can/src/operator.rs index 7ec20f9a09..bb24c55b37 100644 --- a/compiler/can/src/operator.rs +++ b/compiler/can/src/operator.rs @@ -10,26 +10,6 @@ use roc_region::all::{Located, Region}; // BinOp precedence logic adapted from Gluon by Markus Westerlind, MIT licensed // https://github.com/gluon-lang/gluon // Thank you, Markus! -fn new_op_expr<'a>( - arena: &'a Bump, - left: Located>, - op: Located, - right: Located>, -) -> Located> { - let new_region = Region { - start_line: left.region.start_line, - start_col: left.region.start_col, - - end_line: right.region.end_line, - end_col: right.region.end_col, - }; - let new_expr = Expr::BinOp(arena.alloc((left, op, right))); - - Located { - value: new_expr, - region: new_region, - } -} fn new_op_call_expr<'a>( arena: &'a Bump, @@ -724,265 +704,3 @@ fn binop_step<'a>( } } } - -fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located>) -> &'a Located> { - use roc_module::operator::Associativity::*; - use std::cmp::Ordering; - - let mut infixes = Infixes::new(loc_expr); - let mut arg_stack: Vec<&'a Located> = Vec::new_in(arena); - let mut op_stack: Vec> = Vec::new_in(arena); - - while let Some(token) = infixes.next() { - match token { - InfixToken::Arg(next_expr) => arg_stack.push(next_expr), - InfixToken::Op(next_op) => { - match op_stack.pop() { - Some(stack_op) => { - match next_op.value.cmp(&stack_op.value) { - Ordering::Less => { - // Inline - let right = arg_stack.pop().unwrap(); - let left = arg_stack.pop().unwrap(); - - infixes.next_op = Some(next_op); - arg_stack.push(arena.alloc(new_op_expr( - arena, - Located { - value: Nested(&left.value), - region: left.region, - }, - stack_op, - Located { - value: Nested(&right.value), - region: right.region, - }, - ))); - } - - Ordering::Greater => { - // Swap - op_stack.push(stack_op); - op_stack.push(next_op); - } - - Ordering::Equal => { - match ( - next_op.value.associativity(), - stack_op.value.associativity(), - ) { - (LeftAssociative, LeftAssociative) => { - // Inline - let right = arg_stack.pop().unwrap(); - let left = arg_stack.pop().unwrap(); - - infixes.next_op = Some(next_op); - arg_stack.push(arena.alloc(new_op_expr( - arena, - Located { - value: Nested(&left.value), - region: left.region, - }, - stack_op, - Located { - value: Nested(&right.value), - region: right.region, - }, - ))); - } - - (RightAssociative, RightAssociative) => { - // Swap - op_stack.push(stack_op); - op_stack.push(next_op); - } - - (NonAssociative, NonAssociative) => { - // Both operators were non-associative, e.g. (True == False == False). - // We should tell the author to disambiguate by grouping them with parens. - let bad_op = next_op; - let right = arg_stack.pop().unwrap(); - let left = arg_stack.pop().unwrap(); - let broken_expr = new_op_expr( - arena, - Located { - value: Nested(&left.value), - region: left.region, - }, - next_op, - Located { - value: Nested(&right.value), - region: right.region, - }, - ); - let region = broken_expr.region; - let data = roc_parse::ast::PrecedenceConflict { - whole_region: loc_expr.region, - binop1_position: stack_op.region.start(), - binop1: stack_op.value, - binop2_position: bad_op.region.start(), - binop2: bad_op.value, - expr: arena.alloc(broken_expr), - }; - let value = Expr::PrecedenceConflict(arena.alloc(data)); - - return arena.alloc(Located { region, value }); - } - - _ => { - // The operators had the same precedence but different associativity. - // - // In many languages, this case can happen due to (for example) <| and |> having the same - // precedence but different associativity. Languages which support custom operators with - // (e.g. Haskell) can potentially have arbitrarily many of these cases. - // - // By design, Roc neither allows custom operators nor has any built-in operators with - // the same precedence and different associativity, so this should never happen! - panic!("BinOps had the same associativity, but different precedence. This should never happen!"); - } - } - } - } - } - None => op_stack.push(next_op), - }; - } - } - } - - for loc_op in op_stack.into_iter().rev() { - let right = desugar_expr(arena, arg_stack.pop().unwrap()); - let left = desugar_expr(arena, arg_stack.pop().unwrap()); - - let region = Region::span_across(&left.region, &right.region); - let value = match loc_op.value { - Pizza => { - // Rewrite the Pizza operator into an Apply - - match &right.value { - Apply(function, arguments, _called_via) => { - let mut args = Vec::with_capacity_in(1 + arguments.len(), arena); - - args.push(left); - - for arg in arguments.iter() { - args.push(arg); - } - - let args = args.into_bump_slice(); - - Apply(function, args, CalledVia::BinOp(Pizza)) - } - expr => { - // e.g. `1 |> (if b then (\a -> a) else (\c -> c))` - let mut args = Vec::with_capacity_in(1, arena); - - args.push(left); - - let function = arena.alloc(Located { - value: Nested(expr), - region: right.region, - }); - - let args = args.into_bump_slice(); - - Apply(function, args, CalledVia::BinOp(Pizza)) - } - } - } - binop => { - // This is a normal binary operator like (+), so desugar it - // into the appropriate function call. - let (module_name, ident) = binop_to_function(binop); - let mut args = Vec::with_capacity_in(2, arena); - - args.push(left); - args.push(right); - - let loc_expr = arena.alloc(Located { - value: Expr::Var { module_name, ident }, - region: loc_op.region, - }); - - let args = args.into_bump_slice(); - - Apply(loc_expr, args, CalledVia::BinOp(binop)) - } - }; - - arg_stack.push(arena.alloc(Located { region, value })); - } - - assert_eq!(arg_stack.len(), 1); - - arg_stack.pop().unwrap() -} - -#[derive(Debug, Clone, PartialEq)] -enum InfixToken<'a> { - Arg(&'a Located>), - Op(Located), -} - -/// An iterator that takes an expression that has had its operators grouped -/// with _right associativity_, and yeilds a sequence of `InfixToken`s. This -/// is useful for reparsing the operators with their correct associativies -/// and precedences. -/// -/// For example, the expression: -/// -/// ```text -/// (1 + (2 ^ (4 * (6 - 8)))) -/// ``` -/// -/// Will result in the following iterations: -/// -/// ```text -/// Arg: 1 -/// Op: + -/// Arg: 2 -/// Op: ^ -/// Arg: 4 -/// Op: * -/// Arg: 6 -/// Op: - -/// Arg: 8 -/// ``` -struct Infixes<'a> { - /// The next part of the expression that we need to flatten - remaining_expr: Option<&'a Located>>, - /// Cached operator from a previous iteration - next_op: Option>, -} - -impl<'a> Infixes<'a> { - fn new(expr: &'a Located>) -> Infixes<'a> { - Infixes { - remaining_expr: Some(expr), - next_op: None, - } - } -} - -impl<'a> Iterator for Infixes<'a> { - type Item = InfixToken<'a>; - - fn next(&mut self) -> Option> { - match self.next_op.take() { - Some(op) => Some(InfixToken::Op(op)), - None => self - .remaining_expr - .take() - .map(|loc_expr| match loc_expr.value { - Expr::BinOp((left, loc_op, right)) - | Expr::Nested(Expr::BinOp((left, loc_op, right))) => { - self.remaining_expr = Some(right); - self.next_op = Some(*loc_op); - - InfixToken::Arg(left) - } - _ => InfixToken::Arg(loc_expr), - }), - } - } -}