Merge pull request #1090 from rtfeldman/binop-improvements

Binop improvements
This commit is contained in:
Richard Feldman 2021-03-20 21:59:43 -04:00 committed by GitHub
commit e238c2e202
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 490 additions and 493 deletions

View File

@ -121,7 +121,6 @@ fn jit_to_ast_help<'a>(
}
Layout::PhantomEmptyStruct => Ok(run_jit_function!(lib, main_fn_name, &u8, |_| {
Expr::Record {
update: None,
fields: &[],
final_comments: env.arena.alloc([]),
}
@ -474,7 +473,6 @@ fn struct_to_ast<'a>(
let output = env.arena.alloc([loc_field]);
Expr::Record {
update: None,
fields: output,
final_comments: &[],
}
@ -510,7 +508,6 @@ fn struct_to_ast<'a>(
let output = output.into_bump_slice();
Expr::Record {
update: None,
fields: output,
final_comments: &[],
}
@ -554,7 +551,6 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
};
Expr::Record {
update: None,
fields: arena.alloc([loc_assigned_field]),
final_comments: arena.alloc([]),
}
@ -667,7 +663,6 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
};
Expr::Record {
update: None,
fields: arena.alloc([loc_assigned_field]),
final_comments: &[],
}
@ -783,7 +778,6 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
};
Expr::Record {
update: None,
fields: arena.alloc([loc_assigned_field]),
final_comments: arena.alloc([]),
}

View File

@ -207,7 +207,37 @@ pub fn canonicalize_expr<'a>(
}
ast::Expr::Record {
fields,
update: Some(loc_update),
final_comments: _,
} => {
if fields.is_empty() {
(EmptyRecord, Output::default())
} else {
match canonicalize_fields(env, var_store, scope, region, fields) {
Ok((can_fields, output)) => (
Record {
record_var: var_store.fresh(),
fields: can_fields,
},
output,
),
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region,
}) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
field_name,
field_region,
record_region,
}),
Output::default(),
),
}
}
}
ast::Expr::RecordUpdate {
fields,
update: loc_update,
final_comments: _,
} => {
let (can_update, update_out) =
@ -253,37 +283,6 @@ pub fn canonicalize_expr<'a>(
(answer, Output::default())
}
}
ast::Expr::Record {
fields,
update: None,
final_comments: _,
} => {
if fields.is_empty() {
(EmptyRecord, Output::default())
} else {
match canonicalize_fields(env, var_store, scope, region, fields) {
Ok((can_fields, output)) => (
Record {
record_var: var_store.fresh(),
fields: can_fields,
},
output,
),
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region,
}) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
field_name,
field_region,
record_region,
}),
Output::default(),
),
}
}
}
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
ast::Expr::List {
items: loc_elems, ..
@ -720,10 +719,34 @@ pub fn canonicalize_expr<'a>(
)
}
ast::Expr::PrecedenceConflict(whole_region, binop1, binop2, _expr) => {
ast::Expr::PrecedenceConflict(ast::PrecedenceConflict {
whole_region,
binop1_position,
binop2_position,
binop1,
binop2,
expr: _,
}) => {
use roc_problem::can::RuntimeError::*;
let problem = PrecedenceProblem::BothNonAssociative(*whole_region, *binop1, *binop2);
let region1 = Region::new(
binop1_position.row,
binop1_position.row,
binop1_position.col,
binop1_position.col + binop1.width(),
);
let loc_binop1 = Located::at(region1, *binop1);
let region2 = Region::new(
binop2_position.row,
binop2_position.row,
binop2_position.col,
binop2_position.col + binop2.width(),
);
let loc_binop2 = Located::at(region2, *binop2);
let problem =
PrecedenceProblem::BothNonAssociative(*whole_region, loc_binop1, loc_binop2);
env.problem(Problem::PrecedenceProblem(problem.clone()));
@ -780,9 +803,9 @@ pub fn canonicalize_expr<'a>(
bad_expr
);
}
bad_expr @ ast::Expr::BinOp(_) => {
bad_expr @ ast::Expr::BinOps { .. } => {
panic!(
"A binary operator did not get desugared somehow: {:#?}",
"A binary operator chain did not get desugared somehow: {:#?}",
bad_expr
);
}

View File

@ -10,25 +10,53 @@ 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>(
fn new_op_call_expr<'a>(
arena: &'a Bump,
left: Located<Expr<'a>>,
op: Located<BinOp>,
right: Located<Expr<'a>>,
left: &'a Located<Expr<'a>>,
loc_op: Located<BinOp>,
right: &'a Located<Expr<'a>>,
) -> Located<Expr<'a>> {
let new_region = Region {
start_line: left.region.start_line,
start_col: left.region.start_col,
let region = Region::span_across(&left.region, &right.region);
end_line: right.region.end_line,
end_col: right.region.end_col,
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);
args.extend(arguments.iter());
let args = args.into_bump_slice();
Apply(function, args, CalledVia::BinOp(Pizza))
}
_ => {
// e.g. `1 |> (if b then (\a -> a) else (\c -> c))`
Apply(right, arena.alloc([left]), 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 args = arena.alloc([left, right]);
let loc_expr = arena.alloc(Located {
value: Expr::Var { module_name, ident },
region: loc_op.region,
});
Apply(loc_expr, args, CalledVia::BinOp(binop))
}
};
let new_expr = Expr::BinOp(arena.alloc((left, op, right)));
Located {
value: new_expr,
region: new_region,
}
Located { value, region }
}
fn desugar_defs<'a>(
@ -117,8 +145,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
| Nested(MalformedIdent(_, _))
| MalformedClosure
| Nested(MalformedClosure)
| PrecedenceConflict(_, _, _, _)
| Nested(PrecedenceConflict(_, _, _, _))
| PrecedenceConflict { .. }
| Nested(PrecedenceConflict { .. })
| GlobalTag(_)
| Nested(GlobalTag(_))
| PrivateTag(_)
@ -160,12 +188,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
}
Record {
fields,
update,
final_comments,
}
| Nested(Record {
fields,
update,
final_comments,
}) => {
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
@ -184,6 +210,39 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
arena.alloc(Located {
region: loc_expr.region,
value: Record {
fields: new_fields,
final_comments,
},
})
}
RecordUpdate {
fields,
update,
final_comments,
}
| Nested(RecordUpdate {
fields,
update,
final_comments,
}) => {
// NOTE the `update` field is always a `Var { .. }` and does not need to be desugared
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
for field in fields.iter() {
let value = desugar_field(arena, &field.value);
new_fields.push(Located {
value,
region: field.region,
});
}
let new_fields = new_fields.into_bump_slice();
arena.alloc(Located {
region: loc_expr.region,
value: RecordUpdate {
update: *update,
fields: new_fields,
final_comments,
@ -205,12 +264,12 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
// first desugar the body, because it may contain |>
let desugared_body = desugar_expr(arena, loc_body);
let desugared_ret = desugar_expr(arena, loc_ret);
let closure = Expr::Closure(loc_patterns, desugared_ret);
let loc_closure = Located::at_zero(closure);
match &desugared_body.value {
Expr::Apply(function, arguments, called_via) => {
let desugared_ret = desugar_expr(arena, loc_ret);
let closure = Expr::Closure(loc_patterns, desugared_ret);
let loc_closure = Located::at_zero(closure);
let mut new_arguments: Vec<'a, &'a Located<Expr<'a>>> =
Vec::with_capacity_in(arguments.len() + 1, arena);
new_arguments.extend(arguments.iter());
@ -221,10 +280,22 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
arena.alloc(loc_call)
}
_ => panic!(),
_ => {
// e.g. `x <- (if b then (\a -> a) else (\c -> c))`
let call = Expr::Apply(
desugared_body,
arena.alloc([&*arena.alloc(loc_closure)]),
CalledVia::Space,
);
let loc_call = Located::at(loc_expr.region, call);
arena.alloc(loc_call)
}
}
}
BinOp(_) | Nested(BinOp(_)) => desugar_bin_op(arena, loc_expr),
BinOps(lefts, right) | Nested(BinOps(lefts, right)) => {
desugar_bin_ops(arena, loc_expr.region, lefts, right)
}
Defs(defs, loc_ret) | Nested(Defs(defs, loc_ret)) => {
desugar_defs(arena, loc_expr.region, *defs, loc_ret)
}
@ -301,9 +372,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
},
};
let loc_fn_var = arena.alloc(Located { region, value });
let desugared_args = bumpalo::vec![in arena; desugar_expr(arena, loc_arg)];
let desugared_args = desugared_args.into_bump_slice();
let desugared_args = arena.alloc([desugar_expr(arena, loc_arg)]);
arena.alloc(Located {
value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)),
@ -426,261 +495,149 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
}
}
fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a Located<Expr<'a>> {
fn desugar_bin_ops<'a>(
arena: &'a Bump,
whole_region: Region,
lefts: &'a [(Located<Expr<'_>>, Located<BinOp>)],
right: &'a Located<Expr<'_>>,
) -> &'a Located<Expr<'a>> {
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::with_capacity_in(lefts.len() + 1, arena);
let mut op_stack: Vec<Located<BinOp>> = Vec::with_capacity_in(lefts.len(), arena);
for (loc_expr, loc_op) in lefts {
arg_stack.push(desugar_expr(arena, loc_expr));
match run_binop_step(arena, whole_region, &mut arg_stack, &mut op_stack, *loc_op) {
Err(problem) => return problem,
Ok(()) => continue,
}
}
let mut expr = desugar_expr(arena, right);
for (left, loc_op) in arg_stack.into_iter().zip(op_stack.into_iter()).rev() {
expr = arena.alloc(new_op_call_expr(arena, left, loc_op, expr));
}
expr
}
enum Step<'a> {
Error(&'a Located<Expr<'a>>),
Push(Located<BinOp>),
Skip,
}
fn run_binop_step<'a>(
arena: &'a Bump,
whole_region: Region,
arg_stack: &mut Vec<&'a Located<Expr<'a>>>,
op_stack: &mut Vec<Located<BinOp>>,
next_op: Located<BinOp>,
) -> Result<(), &'a Located<Expr<'a>>> {
use Step::*;
match binop_step(arena, whole_region, arg_stack, op_stack, next_op) {
Error(problem) => Err(problem),
Push(loc_op) => run_binop_step(arena, whole_region, arg_stack, op_stack, loc_op),
Skip => Ok(()),
}
}
fn binop_step<'a>(
arena: &'a Bump,
whole_region: Region,
arg_stack: &mut Vec<&'a Located<Expr<'a>>>,
op_stack: &mut Vec<Located<BinOp>>,
next_op: Located<BinOp>,
) -> Step<'a> {
use roc_module::operator::Associativity::*;
use std::cmp::Ordering;
let mut infixes = Infixes::new(loc_expr);
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
let mut op_stack: Vec<Located<BinOp>> = Vec::new_in(arena);
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();
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();
arg_stack.push(arena.alloc(new_op_call_expr(arena, left, stack_op, right)));
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,
},
)));
}
Step::Push(next_op)
}
Ordering::Greater => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
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();
Step::Skip
}
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::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();
(RightAssociative, RightAssociative) => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
arg_stack
.push(arena.alloc(new_op_call_expr(arena, left, stack_op, right)));
(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 value = Expr::PrecedenceConflict(
loc_expr.region,
stack_op,
bad_op,
arena.alloc(broken_expr),
);
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);
Step::Push(next_op)
}
let args = args.into_bump_slice();
(RightAssociative, RightAssociative) => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
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);
Step::Skip
}
args.push(left);
(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 =
arena.alloc(new_op_call_expr(arena, left, stack_op, right));
let region = broken_expr.region;
let data = roc_parse::ast::PrecedenceConflict {
whole_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));
let function = arena.alloc(Located {
value: Nested(expr),
region: right.region,
});
Step::Error(arena.alloc(Located { region, value }))
}
let args = args.into_bump_slice();
Apply(function, args, CalledVia::BinOp(Pizza))
_ => {
// 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!");
}
}
}
}
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<Expr<'a>>),
Op(Located<BinOp>),
}
/// 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<Expr<'a>>>,
/// Cached operator from a previous iteration
next_op: Option<Located<BinOp>>,
}
impl<'a> Infixes<'a> {
fn new(expr: &'a Located<Expr<'a>>) -> 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<InfixToken<'a>> {
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),
}),
}
None => {
op_stack.push(next_op);
Step::Skip
}
}
}

View File

@ -65,18 +65,15 @@ impl<'a> Formattable<'a> for Expr<'a> {
.any(|(c, t)| c.is_multiline() || t.is_multiline())
}
BinOp((loc_left, _, loc_right)) => {
let next_is_multiline_bin_op: bool = match &loc_right.value {
Expr::BinOp((_, _, nested_loc_right)) => nested_loc_right.is_multiline(),
_ => false,
};
next_is_multiline_bin_op || loc_left.is_multiline() || loc_right.is_multiline()
BinOps(lefts, loc_right) => {
lefts.iter().any(|(loc_expr, _)| loc_expr.is_multiline())
|| loc_right.is_multiline()
}
UnaryOp(loc_subexpr, _) | PrecedenceConflict(_, _, _, loc_subexpr) => {
loc_subexpr.is_multiline()
}
UnaryOp(loc_subexpr, _)
| PrecedenceConflict(roc_parse::ast::PrecedenceConflict {
expr: loc_subexpr, ..
}) => loc_subexpr.is_multiline(),
ParensAround(subexpr) | Nested(subexpr) => subexpr.is_multiline(),
@ -97,6 +94,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
}
Record { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
RecordUpdate { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
}
}
@ -240,11 +238,17 @@ impl<'a> Formattable<'a> for Expr<'a> {
buf.push_str(string);
}
Record {
fields,
final_comments,
} => {
fmt_record(buf, None, fields, final_comments, indent);
}
RecordUpdate {
fields,
update,
final_comments,
} => {
fmt_record(buf, *update, fields, final_comments, indent);
fmt_record(buf, Some(*update), fields, final_comments, indent);
}
Closure(loc_patterns, loc_ret) => {
fmt_closure(buf, loc_patterns, loc_ret, indent);
@ -281,15 +285,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
} => {
fmt_list(buf, &items, final_comments, indent);
}
BinOp((loc_left_side, bin_op, loc_right_side)) => fmt_bin_op(
buf,
loc_left_side,
bin_op,
loc_right_side,
false,
parens,
indent,
),
BinOps(lefts, right) => fmt_bin_ops(buf, lefts, right, false, parens, indent),
UnaryOp(sub_expr, unary_op) => {
match &unary_op.value {
operator::UnaryOp::Negate => {
@ -316,7 +312,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
}
MalformedIdent(_, _) => {}
MalformedClosure => {}
PrecedenceConflict(_, _, _, _) => {}
PrecedenceConflict { .. } => {}
}
}
}
@ -351,28 +347,8 @@ fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut String<'a>, indent: u1
}
}
fn fmt_bin_op<'a>(
buf: &mut String<'a>,
loc_left_side: &'a Located<Expr<'a>>,
loc_bin_op: &'a Located<BinOp>,
loc_right_side: &'a Located<Expr<'a>>,
part_of_multi_line_bin_ops: bool,
apply_needs_parens: Parens,
indent: u16,
) {
loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, indent);
let is_multiline = (&loc_right_side.value).is_multiline()
|| (&loc_left_side.value).is_multiline()
|| part_of_multi_line_bin_ops;
if is_multiline {
newline(buf, indent + INDENT)
} else {
buf.push(' ');
}
match &loc_bin_op.value {
fn push_op(buf: &mut String, op: BinOp) {
match op {
operator::BinOp::Caret => buf.push('^'),
operator::BinOp::Star => buf.push('*'),
operator::BinOp::Slash => buf.push('/'),
@ -394,26 +370,35 @@ fn fmt_bin_op<'a>(
operator::BinOp::HasType => unreachable!(),
operator::BinOp::Backpassing => unreachable!(),
}
}
buf.push(' ');
fn fmt_bin_ops<'a>(
buf: &mut String<'a>,
lefts: &'a [(Located<Expr<'a>>, Located<BinOp>)],
loc_right_side: &'a Located<Expr<'a>>,
part_of_multi_line_bin_ops: bool,
apply_needs_parens: Parens,
indent: u16,
) {
let is_multiline = part_of_multi_line_bin_ops
|| (&loc_right_side.value).is_multiline()
|| lefts.iter().any(|(expr, _)| expr.value.is_multiline());
match &loc_right_side.value {
Expr::BinOp((nested_left_side, nested_bin_op, nested_right_side)) => {
fmt_bin_op(
buf,
nested_left_side,
nested_bin_op,
nested_right_side,
is_multiline,
apply_needs_parens,
indent,
);
for (loc_left_side, loc_bin_op) in lefts {
loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, indent);
if is_multiline {
newline(buf, indent + INDENT)
} else {
buf.push(' ');
}
_ => {
loc_right_side.format_with_options(buf, apply_needs_parens, Newlines::Yes, indent);
}
push_op(buf, loc_bin_op.value);
buf.push(' ');
}
loc_right_side.format_with_options(buf, apply_needs_parens, Newlines::Yes, indent);
}
fn fmt_list<'a>(

View File

@ -47,6 +47,18 @@ pub enum BinOp {
Backpassing,
}
impl BinOp {
/// how wide this operator is when typed out
pub fn width(self) -> u16 {
match self {
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
DoubleSlash | DoublePercent | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq
| And | Or | Pizza => 2,
Assignment | HasType | Backpassing => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ArgSide {
Left,

View File

@ -3,7 +3,7 @@ use crate::ident::Ident;
use bumpalo::collections::String;
use bumpalo::Bump;
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Region};
use roc_region::all::{Loc, Position, Region};
#[derive(Clone, Debug, PartialEq)]
pub enum Module<'a> {
@ -98,8 +98,13 @@ pub enum Expr<'a> {
final_comments: &'a [CommentOrNewline<'a>],
},
RecordUpdate {
update: &'a Loc<Expr<'a>>,
fields: &'a [Loc<AssignedField<'a, Expr<'a>>>],
final_comments: &'a &'a [CommentOrNewline<'a>],
},
Record {
update: Option<&'a Loc<Expr<'a>>>,
fields: &'a [Loc<AssignedField<'a, Expr<'a>>>],
final_comments: &'a [CommentOrNewline<'a>],
},
@ -124,7 +129,7 @@ pub enum Expr<'a> {
/// To apply by name, do Apply(Var(...), ...)
/// To apply a tag by name, do Apply(Tag(...), ...)
Apply(&'a Loc<Expr<'a>>, &'a [&'a Loc<Expr<'a>>], CalledVia),
BinOp(&'a (Loc<Expr<'a>>, Loc<BinOp>, Loc<Expr<'a>>)),
BinOps(&'a [(Loc<Expr<'a>>, Loc<BinOp>)], &'a Loc<Expr<'a>>),
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
// Conditionals
@ -155,7 +160,17 @@ pub enum Expr<'a> {
MalformedClosure,
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(Region, Loc<BinOp>, Loc<BinOp>, &'a Loc<Expr<'a>>),
PrecedenceConflict(&'a PrecedenceConflict<'a>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct PrecedenceConflict<'a> {
pub whole_region: Region,
pub binop1_position: Position,
pub binop2_position: Position,
pub binop1: BinOp,
pub binop2: BinOp,
pub expr: &'a Loc<Expr<'a>>,
}
#[derive(Debug, Clone, PartialEq)]

View File

@ -416,15 +416,18 @@ fn parse_expr_final<'a>(
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let mut expr = to_call(arena, expr_state.arguments, expr_state.expr);
let right_arg = to_call(arena, expr_state.arguments, expr_state.expr);
for (left_arg, op) in expr_state.operators.into_iter().rev() {
let region = Region::span_across(&left_arg.region, &expr.region);
let new = Expr::BinOp(arena.alloc((left_arg, op, expr)));
expr = Located::at(region, new);
}
let expr = if expr_state.operators.is_empty() {
right_arg.value
} else {
Expr::BinOps(
expr_state.operators.into_bump_slice(),
arena.alloc(right_arg),
)
};
Ok((MadeProgress, expr.value, state))
Ok((MadeProgress, expr, state))
}
fn to_call<'a>(
@ -1257,21 +1260,7 @@ fn parse_expr_end<'a>(
// roll back space parsing
let state = expr_state.initial;
if expr_state.operators.is_empty() {
let expr = to_call(arena, expr_state.arguments, expr_state.expr);
Ok((MadeProgress, expr.value, state))
} else {
let mut expr = to_call(arena, expr_state.arguments, expr_state.expr);
for (left_arg, op) in expr_state.operators.into_iter().rev() {
let region = Region::span_across(&left_arg.region, &expr.region);
let new = Expr::BinOp(arena.alloc((left_arg, op, expr)));
expr = Located::at(region, new);
}
Ok((MadeProgress, expr.value, state))
}
parse_expr_final(expr_state, arena, state)
}
}
}
@ -1352,7 +1341,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
Expr::Record {
fields,
update: None,
final_comments: _,
} => {
let mut loc_patterns = Vec::with_capacity_in(fields.len(), arena);
@ -1384,15 +1372,13 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::List { .. }
| Expr::Closure(_, _)
| Expr::Backpassing(_, _, _)
| Expr::BinOp(_)
| Expr::BinOps { .. }
| Expr::Defs(_, _)
| Expr::If(_, _)
| Expr::When(_, _)
| Expr::MalformedClosure
| Expr::PrecedenceConflict(_, _, _, _)
| Expr::Record {
update: Some(_), ..
}
| Expr::PrecedenceConflict { .. }
| Expr::RecordUpdate { .. }
| Expr::UnaryOp(_, _) => Err(()),
Expr::Str(string) => Ok(Pattern::StrLiteral(string.clone())),
@ -2085,10 +2071,16 @@ fn record_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'
let (opt_update, loc_assigned_fields_with_comments) = loc_record.value;
// This is a record literal, not a destructure.
let mut value = Expr::Record {
update: opt_update.map(|loc_expr| &*arena.alloc(loc_expr)),
fields: loc_assigned_fields_with_comments.value.0.into_bump_slice(),
final_comments: loc_assigned_fields_with_comments.value.1,
let mut value = match opt_update {
Some(update) => Expr::RecordUpdate {
update: &*arena.alloc(update),
fields: loc_assigned_fields_with_comments.value.0.into_bump_slice(),
final_comments: arena.alloc(loc_assigned_fields_with_comments.value.1),
},
None => Expr::Record {
fields: loc_assigned_fields_with_comments.value.0.into_bump_slice(),
final_comments: loc_assigned_fields_with_comments.value.1,
},
};
// there can be field access, e.g. `{ x : 4 }.x`

View File

@ -82,6 +82,19 @@ mod test_parse {
}
}
fn single_binop<'a>(
arena: &'a Bump,
args: (
Located<Expr<'a>>,
Located<roc_module::operator::BinOp>,
Located<Expr<'a>>,
),
) -> Expr<'a> {
let (left, op, right) = args;
Expr::BinOps(arena.alloc([(left, op)]), arena.alloc(right))
}
// STRING LITERALS
fn expect_parsed_str(input: &str, expected: &str) {
@ -414,7 +427,6 @@ mod test_parse {
let arena = Bump::new();
let expected = Record {
fields: &[],
update: None,
final_comments: &[],
};
let actual = parse_expr_with(&arena, "{}");
@ -435,7 +447,7 @@ mod test_parse {
&[],
arena.alloc(Located::new(0, 0, 25, 26, Num("0"))),
);
let fields = &[
let fields: &[_] = &[
Located::new(0, 0, 16, 20, label1),
Located::new(0, 0, 22, 26, label2),
];
@ -444,10 +456,10 @@ mod test_parse {
ident: "baz",
};
let update_target = Located::new(0, 0, 2, 13, var);
let expected = Record {
update: Some(&*arena.alloc(update_target)),
let expected = RecordUpdate {
update: &*arena.alloc(update_target),
fields,
final_comments: &[],
final_comments: &(&[] as &[_]),
};
let actual = parse_expr_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }");
@ -480,7 +492,6 @@ mod test_parse {
Located::new(0, 0, 28, 32, label2),
];
let expected = Record {
update: None,
fields,
final_comments: &[],
};
@ -494,12 +505,12 @@ mod test_parse {
#[test]
fn one_plus_two() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, Num("1")),
Located::new(0, 0, 1, 2, Plus),
Located::new(0, 0, 2, 3, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "1+2");
assert_eq!(Ok(expected), actual);
@ -508,12 +519,12 @@ mod test_parse {
#[test]
fn one_minus_two() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, Num("1")),
Located::new(0, 0, 1, 2, Minus),
Located::new(0, 0, 2, 3, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "1-2");
assert_eq!(Ok(expected), actual);
@ -522,7 +533,7 @@ mod test_parse {
#[test]
fn var_minus_two() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(
0,
0,
@ -535,8 +546,8 @@ mod test_parse {
),
Located::new(0, 0, 1, 2, Minus),
Located::new(0, 0, 2, 3, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x-2");
assert_eq!(Ok(expected), actual);
@ -545,12 +556,12 @@ mod test_parse {
#[test]
fn add_with_spaces() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, Num("1")),
Located::new(0, 0, 3, 4, Plus),
Located::new(0, 0, 7, 8, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "1 + 2");
assert_eq!(Ok(expected), actual);
@ -559,12 +570,12 @@ mod test_parse {
#[test]
fn sub_with_spaces() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, Num("1")),
Located::new(0, 0, 3, 4, Minus),
Located::new(0, 0, 7, 8, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "1 - 2");
assert_eq!(Ok(expected), actual);
@ -582,12 +593,12 @@ mod test_parse {
module_name: "",
ident: "x",
};
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, var),
Located::new(0, 0, 2, 3, Plus),
Located::new(0, 0, 4, 5, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x + 2");
assert_eq!(Ok(expected), actual);
@ -604,12 +615,12 @@ mod test_parse {
module_name: "",
ident: "x",
};
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, var),
Located::new(0, 0, 2, 3, Minus),
Located::new(0, 0, 4, 5, Num("2")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x - 2");
assert_eq!(Ok(expected), actual);
@ -619,12 +630,12 @@ mod test_parse {
fn newline_before_add() {
let arena = Bump::new();
let spaced_int = Expr::SpaceAfter(arena.alloc(Num("3")), &[Newline]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Plus),
Located::new(1, 1, 2, 3, Num("4")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 \n+ 4");
assert_eq!(Ok(expected), actual);
@ -634,12 +645,12 @@ mod test_parse {
fn newline_before_sub() {
let arena = Bump::new();
let spaced_int = Expr::SpaceAfter(arena.alloc(Num("3")), &[Newline]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Minus),
Located::new(1, 1, 2, 3, Num("4")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 \n- 4");
assert_eq!(Ok(expected), actual);
@ -649,12 +660,12 @@ mod test_parse {
fn newline_after_mul() {
let arena = Bump::new();
let spaced_int = arena.alloc(Num("4")).before(&[Newline]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, Num("3")),
Located::new(0, 0, 3, 4, Star),
Located::new(1, 1, 2, 3, spaced_int),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 *\n 4");
assert_eq!(Ok(expected), actual);
@ -664,12 +675,12 @@ mod test_parse {
fn newline_after_sub() {
let arena = Bump::new();
let spaced_int = arena.alloc(Num("4")).before(&[Newline]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, Num("3")),
Located::new(0, 0, 3, 4, Minus),
Located::new(1, 1, 2, 3, spaced_int),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 -\n 4");
assert_eq!(Ok(expected), actual);
@ -679,16 +690,16 @@ mod test_parse {
fn newline_and_spaces_before_less_than() {
let arena = Bump::new();
let spaced_int = arena.alloc(Num("1")).after(&[Newline]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 4, 5, spaced_int),
Located::new(1, 1, 4, 5, LessThan),
Located::new(1, 1, 6, 7, Num("2")),
));
);
let newlines = bumpalo::vec![in &arena; Newline, Newline];
let def = Def::Body(
arena.alloc(Located::new(0, 0, 0, 1, Identifier("x"))),
arena.alloc(Located::new(0, 1, 4, 7, BinOp(tuple))),
arena.alloc(Located::new(0, 1, 4, 7, single_binop(&arena, tuple))),
);
let loc_def = &*arena.alloc(Located::new(0, 1, 0, 7, def));
let defs = &[loc_def];
@ -696,7 +707,7 @@ mod test_parse {
let loc_ret = Located::new(3, 3, 0, 2, ret);
let expected = Defs(defs, arena.alloc(loc_ret));
// let expected = BinOp(tuple);
// let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x = 1\n < 2\n\n42");
assert_eq!(Ok(expected), actual);
@ -706,12 +717,12 @@ mod test_parse {
fn comment_with_non_ascii() {
let arena = Bump::new();
let spaced_int = arena.alloc(Num("3")).after(&[LineComment(" 2 × 2")]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Plus),
Located::new(1, 1, 2, 3, Num("4")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 # 2 × 2\n+ 4");
assert_eq!(Ok(expected), actual);
@ -721,12 +732,12 @@ mod test_parse {
fn comment_before_op() {
let arena = Bump::new();
let spaced_int = arena.alloc(Num("3")).after(&[LineComment(" test!")]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, spaced_int),
Located::new(1, 1, 0, 1, Plus),
Located::new(1, 1, 2, 3, Num("4")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 # test!\n+ 4");
assert_eq!(Ok(expected), actual);
@ -736,12 +747,12 @@ mod test_parse {
fn comment_after_op() {
let arena = Bump::new();
let spaced_int = arena.alloc(Num("92")).before(&[LineComment(" test!")]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 2, Num("12")),
Located::new(0, 0, 4, 5, Star),
Located::new(1, 1, 1, 3, spaced_int),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "12 * # test!\n 92");
assert_eq!(Ok(expected), actual);
@ -752,12 +763,12 @@ mod test_parse {
let arena = Bump::new();
let spaced_int1 = arena.alloc(Num("3")).after(&[Newline]);
let spaced_int2 = arena.alloc(Num("4")).before(&[Newline, Newline]);
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, spaced_int1),
Located::new(1, 1, 0, 1, Plus),
Located::new(3, 3, 2, 3, spaced_int2),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "3 \n+ \n\n 4");
assert_eq!(Ok(expected), actual);
@ -776,12 +787,12 @@ mod test_parse {
module_name: "",
ident: "y",
};
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, var1),
Located::new(0, 0, 1, 2, Minus),
Located::new(0, 0, 3, 4, var2),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x- y");
assert_eq!(Ok(expected), actual);
@ -790,12 +801,12 @@ mod test_parse {
#[test]
fn minus_twelve_minus_five() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 3, Num("-12")),
Located::new(0, 0, 3, 4, Minus),
Located::new(0, 0, 4, 5, Num("5")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "-12-5");
assert_eq!(Ok(expected), actual);
@ -804,12 +815,12 @@ mod test_parse {
#[test]
fn ten_times_eleven() {
let arena = Bump::new();
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 2, Num("10")),
Located::new(0, 0, 2, 3, Star),
Located::new(0, 0, 3, 5, Num("11")),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "10*11");
assert_eq!(Ok(expected), actual);
@ -818,17 +829,20 @@ mod test_parse {
#[test]
fn multiple_operators() {
let arena = Bump::new();
let inner = arena.alloc((
Located::new(0, 0, 3, 5, Num("42")),
Located::new(0, 0, 5, 6, Plus),
Located::new(0, 0, 6, 9, Num("534")),
));
let outer = arena.alloc((
Located::new(0, 0, 0, 2, Num("31")),
Located::new(0, 0, 2, 3, Star),
Located::new(0, 0, 3, 9, BinOp(inner)),
));
let expected = BinOp(outer);
let lefts = [
(
Located::new(0, 0, 0, 2, Num("31")),
Located::new(0, 0, 2, 3, Star),
),
(
Located::new(0, 0, 3, 5, Num("42")),
Located::new(0, 0, 5, 6, Plus),
),
];
let right = Located::new(0, 0, 6, 9, Num("534"));
let expected = BinOps(&lefts, &right);
let actual = parse_expr_with(&arena, "31*42+534");
assert_eq!(Ok(expected), actual);
@ -845,12 +859,12 @@ mod test_parse {
module_name: "",
ident: "y",
};
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, var1),
Located::new(0, 0, 1, 3, Equals),
Located::new(0, 0, 3, 4, var2),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x==y");
assert_eq!(Ok(expected), actual);
@ -867,12 +881,12 @@ mod test_parse {
module_name: "",
ident: "y",
};
let tuple = arena.alloc((
let tuple = (
Located::new(0, 0, 0, 1, var1),
Located::new(0, 0, 2, 4, Equals),
Located::new(0, 0, 5, 6, var2),
));
let expected = BinOp(tuple);
);
let expected = single_binop(&arena, tuple);
let actual = parse_expr_with(&arena, "x == y");
assert_eq!(Ok(expected), actual);
@ -1860,7 +1874,6 @@ mod test_parse {
let identifier_z = Located::new(2, 2, 0, 1, Identifier("z"));
let empty_record = Record {
update: None,
fields: &[],
final_comments: &[],
};
@ -1956,11 +1969,14 @@ mod test_parse {
let loc_closure = Located::new(0, 0, 8, 23, apply);
let binop = Expr::BinOp(arena.alloc((
loc_var_x,
Located::new(2, 2, 2, 3, roc_module::operator::BinOp::Plus),
loc_var_y,
)));
let binop = single_binop(
&arena,
(
loc_var_x,
Located::new(2, 2, 2, 3, roc_module::operator::BinOp::Plus),
loc_var_y,
),
);
let spaced_binop = Expr::SpaceBefore(arena.alloc(binop), &[Newline, Newline]);
@ -3363,6 +3379,11 @@ mod test_parse {
assert!(actual.is_ok());
}
#[test]
fn parse_expr_size() {
assert_eq!(std::mem::size_of::<roc_parse::ast::Expr>(), 40);
}
// PARSE ERROR
// TODO this should be parse error, but isn't!

View File

@ -65,7 +65,6 @@ mod test_reporting {
problems: can_problems,
..
} = can_expr(arena, expr_src)?;
dbg!(&loc_expr);
let mut subs = Subs::new(var_store.into());
for (var, name) in output.introduced_variables.name_by_var {

View File

@ -379,9 +379,9 @@ pub fn to_expr2<'a>(
)
}
Record {
RecordUpdate {
fields,
update: Some(loc_update),
update: loc_update,
final_comments: _,
} => {
let (can_update, update_out) =
@ -435,7 +435,6 @@ pub fn to_expr2<'a>(
Record {
fields,
update: None,
final_comments: _,
} => {
if fields.is_empty() {
@ -779,7 +778,7 @@ pub fn to_expr2<'a>(
(expr, output)
}
PrecedenceConflict(_whole_region, _binop1, _binop2, _expr) => {
PrecedenceConflict { .. } => {
// use roc_problem::can::RuntimeError::*;
//
// let problem = PrecedenceProblem::BothNonAssociative(
@ -833,9 +832,9 @@ pub fn to_expr2<'a>(
bad_expr
);
}
bad_expr @ BinOp(_) => {
bad_expr @ BinOps { .. } => {
panic!(
"A binary operator did not get desugared somehow: {:#?}",
"A binary operator chain did not get desugared somehow: {:#?}",
bad_expr
);
}