mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge branch 'trunk' into double-typecheck
This commit is contained in:
commit
651e4563f7
7 changed files with 286 additions and 214 deletions
|
@ -59,7 +59,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
for loc_def in loc_defs {
|
for loc_def in loc_defs {
|
||||||
buf.push_back(canonicalize_def(
|
buf.push_back(canonicalize_def(
|
||||||
arena,
|
arena,
|
||||||
loc_def.value,
|
&loc_def.value,
|
||||||
loc_def.region,
|
loc_def.region,
|
||||||
home.clone(),
|
home.clone(),
|
||||||
scope,
|
scope,
|
||||||
|
@ -72,7 +72,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
|
|
||||||
fn canonicalize_def<'a>(
|
fn canonicalize_def<'a>(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
def: Def<'a>,
|
def: &'a Def<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
home: Box<str>,
|
home: Box<str>,
|
||||||
scope: &mut ImMap<Box<str>, (Symbol, Region)>,
|
scope: &mut ImMap<Box<str>, (Symbol, Region)>,
|
||||||
|
@ -150,8 +150,7 @@ fn canonicalize_def<'a>(
|
||||||
|
|
||||||
// Ignore spaces
|
// Ignore spaces
|
||||||
Def::SpaceBefore(def, _) | Def::SpaceAfter(def, _) => {
|
Def::SpaceBefore(def, _) | Def::SpaceAfter(def, _) => {
|
||||||
// TODO FIXME performance disaster!!!
|
canonicalize_def(arena, def, region, home, scope, var_store)
|
||||||
canonicalize_def(arena, def.clone(), region, home, scope, var_store)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -860,6 +859,12 @@ fn canonicalize_expr(
|
||||||
local_successors(&References::new(), &env.closures)
|
local_successors(&References::new(), &env.closures)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
ast::Expr::Nested(sub_expr) => {
|
||||||
|
let (answer, output) =
|
||||||
|
canonicalize_expr(rigids, env, var_store, scope, region, sub_expr, expected);
|
||||||
|
|
||||||
|
(answer.value, output)
|
||||||
|
}
|
||||||
ast::Expr::BinaryInt(string) => {
|
ast::Expr::BinaryInt(string) => {
|
||||||
let (constraint, answer) =
|
let (constraint, answer) =
|
||||||
int_expr_from_result(var_store, finish_parsing_bin(string), env, expected, region);
|
int_expr_from_result(var_store, finish_parsing_bin(string), env, expected, region);
|
||||||
|
@ -903,7 +908,7 @@ fn canonicalize_expr(
|
||||||
}
|
}
|
||||||
ast::Expr::UnaryOp(_, loc_op) => {
|
ast::Expr::UnaryOp(_, loc_op) => {
|
||||||
panic!(
|
panic!(
|
||||||
"A binary operator did not get desugared somehow: {:?}",
|
"A unary operator did not get desugared somehow: {:?}",
|
||||||
loc_op
|
loc_op
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1258,7 +1263,7 @@ fn add_idents_from_pattern<'a>(
|
||||||
RecordField(_, _) => {
|
RecordField(_, _) => {
|
||||||
panic!("TODO implement RecordField pattern in add_idents_from_pattern.");
|
panic!("TODO implement RecordField pattern in add_idents_from_pattern.");
|
||||||
}
|
}
|
||||||
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => {
|
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
|
||||||
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
||||||
add_idents_from_pattern(region, pattern, scope, answer)
|
add_idents_from_pattern(region, pattern, scope, answer)
|
||||||
}
|
}
|
||||||
|
@ -1300,7 +1305,7 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
|
||||||
RecordField(_, _) => {
|
RecordField(_, _) => {
|
||||||
panic!("TODO implement RecordField pattern in remove_idents.");
|
panic!("TODO implement RecordField pattern in remove_idents.");
|
||||||
}
|
}
|
||||||
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => {
|
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
|
||||||
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
||||||
remove_idents(pattern, idents)
|
remove_idents(pattern, idents)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,30 +34,41 @@ fn new_op_expr<'a>(
|
||||||
/// Reorder the expression tree based on operator precedence and associativity rules,
|
/// Reorder the expression tree based on operator precedence and associativity rules,
|
||||||
/// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
|
/// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
|
||||||
pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
|
pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
|
||||||
use crate::operator::Associativity::*;
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
match &loc_expr.value {
|
match &loc_expr.value {
|
||||||
Float(_)
|
Float(_)
|
||||||
|
| Nested(Float(_))
|
||||||
| Int(_)
|
| Int(_)
|
||||||
|
| Nested(Int(_))
|
||||||
| HexInt(_)
|
| HexInt(_)
|
||||||
|
| Nested(HexInt(_))
|
||||||
| OctalInt(_)
|
| OctalInt(_)
|
||||||
|
| Nested(OctalInt(_))
|
||||||
| BinaryInt(_)
|
| BinaryInt(_)
|
||||||
|
| Nested(BinaryInt(_))
|
||||||
| Str(_)
|
| Str(_)
|
||||||
|
| Nested(Str(_))
|
||||||
| BlockStr(_)
|
| BlockStr(_)
|
||||||
|
| Nested(BlockStr(_))
|
||||||
| QualifiedField(_, _)
|
| QualifiedField(_, _)
|
||||||
|
| Nested(QualifiedField(_, _))
|
||||||
| AccessorFunction(_)
|
| AccessorFunction(_)
|
||||||
|
| Nested(AccessorFunction(_))
|
||||||
| Var(_, _)
|
| Var(_, _)
|
||||||
|
| Nested(Var(_, _))
|
||||||
| MalformedIdent(_)
|
| MalformedIdent(_)
|
||||||
|
| Nested(MalformedIdent(_))
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
|
| Nested(MalformedClosure)
|
||||||
| PrecedenceConflict(_, _, _)
|
| PrecedenceConflict(_, _, _)
|
||||||
| Variant(_, _) => loc_expr,
|
| Nested(PrecedenceConflict(_, _, _))
|
||||||
|
| Variant(_, _)
|
||||||
|
| Nested(Variant(_, _)) => loc_expr,
|
||||||
|
|
||||||
Field(sub_expr, paths) => arena.alloc(Located {
|
Field(sub_expr, paths) | Nested(Field(sub_expr, paths)) => arena.alloc(Located {
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
value: Field(desugar(arena, sub_expr), paths.clone()),
|
value: Field(desugar(arena, sub_expr), paths.clone()),
|
||||||
}),
|
}),
|
||||||
List(elems) => {
|
List(elems) | Nested(List(elems)) => {
|
||||||
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
||||||
|
|
||||||
for elem in elems {
|
for elem in elems {
|
||||||
|
@ -70,7 +81,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Record(fields) => {
|
Record(fields) | Nested(Record(fields)) => {
|
||||||
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
|
let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||||
|
|
||||||
for field in fields {
|
for field in fields {
|
||||||
|
@ -87,171 +98,14 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
value: Record(new_fields),
|
value: Record(new_fields),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Closure(loc_patterns, loc_ret) => arena.alloc(Located {
|
Closure(loc_patterns, loc_ret) | Nested(Closure(loc_patterns, loc_ret)) => {
|
||||||
region: loc_expr.region,
|
arena.alloc(Located {
|
||||||
value: Closure(loc_patterns, desugar(arena, loc_ret)),
|
region: loc_expr.region,
|
||||||
}),
|
value: Closure(loc_patterns, desugar(arena, loc_ret)),
|
||||||
BinOp(_) => {
|
})
|
||||||
let mut infixes = Infixes::new(arena.alloc(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);
|
|
||||||
|
|
||||||
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,
|
|
||||||
left.clone(),
|
|
||||||
stack_op,
|
|
||||||
right.clone(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
left.clone(),
|
|
||||||
stack_op,
|
|
||||||
right.clone(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
(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.clone();
|
|
||||||
let right = arg_stack.pop().unwrap();
|
|
||||||
let left = arg_stack.pop().unwrap();
|
|
||||||
let broken_expr = new_op_expr(
|
|
||||||
arena,
|
|
||||||
left.clone(),
|
|
||||||
next_op,
|
|
||||||
right.clone(),
|
|
||||||
);
|
|
||||||
let region = broken_expr.region;
|
|
||||||
let value = Expr::PrecedenceConflict(
|
|
||||||
bad_op,
|
|
||||||
stack_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(arena, arg_stack.pop().unwrap());
|
|
||||||
let left = desugar(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 {
|
|
||||||
args.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(*arena.alloc(left));
|
|
||||||
|
|
||||||
let function = arena.alloc(Located {
|
|
||||||
value: expr.clone(),
|
|
||||||
region: right.region,
|
|
||||||
});
|
|
||||||
|
|
||||||
Apply(function, args, CalledVia::BinOp(Pizza))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binop => {
|
|
||||||
// This is a normal binary operator like (+), so desugar it
|
|
||||||
// into the appropriate function call.
|
|
||||||
let (module_parts, name) = desugar_binop(binop, arena);
|
|
||||||
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_parts, name),
|
|
||||||
region: loc_op.region,
|
|
||||||
});
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
Defs(defs, loc_ret) => {
|
BinOp(_) | Nested(BinOp(_)) => desugar_bin_op(arena, loc_expr),
|
||||||
|
Defs(defs, loc_ret) | Nested(Defs(defs, loc_ret)) => {
|
||||||
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena);
|
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena);
|
||||||
|
|
||||||
for loc_def in defs.into_iter() {
|
for loc_def in defs.into_iter() {
|
||||||
|
@ -265,7 +119,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Apply(loc_fn, loc_args, called_via) => {
|
Apply(loc_fn, loc_args, called_via) | Nested(Apply(loc_fn, loc_args, called_via)) => {
|
||||||
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
|
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||||
|
|
||||||
for loc_arg in loc_args {
|
for loc_arg in loc_args {
|
||||||
|
@ -277,15 +131,22 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Case(loc_cond_expr, branches) => {
|
Case(loc_cond_expr, branches) | Nested(Case(loc_cond_expr, branches)) => {
|
||||||
let loc_desugared_cond = &*arena.alloc(desugar(arena, &loc_cond_expr));
|
let loc_desugared_cond = &*arena.alloc(desugar(arena, &loc_cond_expr));
|
||||||
let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena);
|
let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena);
|
||||||
|
|
||||||
for (loc_pattern, loc_branch_expr) in branches.into_iter() {
|
for (loc_pattern, loc_branch_expr) in branches.into_iter() {
|
||||||
// TODO FIXME cloning performance disaster
|
let desugared = desugar(arena, &loc_branch_expr);
|
||||||
|
|
||||||
desugared_branches.push(&*arena.alloc((
|
desugared_branches.push(&*arena.alloc((
|
||||||
loc_pattern.clone(),
|
Located {
|
||||||
desugar(arena, &loc_branch_expr).clone(),
|
region: loc_pattern.region,
|
||||||
|
value: Pattern::Nested(&loc_pattern.value),
|
||||||
|
},
|
||||||
|
Located {
|
||||||
|
region: desugared.region,
|
||||||
|
value: Nested(&desugared.value),
|
||||||
|
},
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +155,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
UnaryOp(loc_arg, loc_op) => {
|
UnaryOp(loc_arg, loc_op) | Nested(UnaryOp(loc_arg, loc_op)) => {
|
||||||
use crate::operator::UnaryOp::*;
|
use crate::operator::UnaryOp::*;
|
||||||
|
|
||||||
let region = loc_op.region;
|
let region = loc_op.region;
|
||||||
|
@ -317,25 +178,25 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => {
|
SpaceBefore(expr, _)
|
||||||
|
| Nested(SpaceBefore(expr, _))
|
||||||
|
| SpaceAfter(expr, _)
|
||||||
|
| Nested(SpaceAfter(expr, _))
|
||||||
|
| ParensAround(expr)
|
||||||
|
| Nested(ParensAround(expr))
|
||||||
|
| Nested(Nested(expr)) => {
|
||||||
// Since we've already begun canonicalization, spaces and parens
|
// Since we've already begun canonicalization, spaces and parens
|
||||||
// are no longer needed and should be dropped.
|
// are no longer needed and should be dropped.
|
||||||
desugar(
|
desugar(
|
||||||
arena,
|
arena,
|
||||||
arena.alloc(Located {
|
arena.alloc(Located {
|
||||||
// TODO FIXME performance disaster!!! Must remove this clone!
|
value: Nested(expr),
|
||||||
//
|
|
||||||
// This won't be easy because:
|
|
||||||
//
|
|
||||||
// * If this function takes an &'a Expr, then Infixes hits a problem.
|
|
||||||
// * If SpaceBefore holds a Loc<&'a Expr>, then Spaceable hits a problem.
|
|
||||||
// * If all the existing &'a Loc<Expr> values become Loc<&'a Expr>...who knows?
|
|
||||||
value: (*expr).clone(),
|
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
If((condition, then_branch, else_branch)) => {
|
If((condition, then_branch, else_branch))
|
||||||
|
| Nested(If((condition, then_branch, else_branch))) => {
|
||||||
// desugar if into case, meaning that
|
// desugar if into case, meaning that
|
||||||
//
|
//
|
||||||
// if b then x else y
|
// if b then x else y
|
||||||
|
@ -358,13 +219,15 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
// no type errors will occur here so using this region should be fine
|
// no type errors will occur here so using this region should be fine
|
||||||
let pattern_region = condition.region;
|
let pattern_region = condition.region;
|
||||||
|
|
||||||
// TODO make False qualified
|
|
||||||
branches.push(&*arena.alloc((
|
branches.push(&*arena.alloc((
|
||||||
Located {
|
Located {
|
||||||
value: Pattern::Variant(&[], "False"),
|
value: Pattern::Variant(&[], "False"),
|
||||||
region: pattern_region,
|
region: pattern_region,
|
||||||
},
|
},
|
||||||
else_branch.clone(),
|
Located {
|
||||||
|
value: Nested(&else_branch.value),
|
||||||
|
region: else_branch.region,
|
||||||
|
},
|
||||||
)));
|
)));
|
||||||
|
|
||||||
branches.push(&*arena.alloc((
|
branches.push(&*arena.alloc((
|
||||||
|
@ -372,7 +235,10 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
value: Pattern::Underscore,
|
value: Pattern::Underscore,
|
||||||
region: pattern_region,
|
region: pattern_region,
|
||||||
},
|
},
|
||||||
then_branch.clone(),
|
Located {
|
||||||
|
value: Nested(&then_branch.value),
|
||||||
|
region: then_branch.region,
|
||||||
|
},
|
||||||
)));
|
)));
|
||||||
|
|
||||||
desugar(
|
desugar(
|
||||||
|
@ -417,23 +283,27 @@ fn desugar_field<'a>(
|
||||||
use crate::parse::ast::AssignedField::*;
|
use crate::parse::ast::AssignedField::*;
|
||||||
|
|
||||||
match field {
|
match field {
|
||||||
LabeledValue(ref loc_str, spaces, loc_expr) => {
|
LabeledValue(loc_str, spaces, loc_expr) => AssignedField::LabeledValue(
|
||||||
AssignedField::LabeledValue(loc_str.clone(), spaces, desugar(arena, loc_expr))
|
Located {
|
||||||
}
|
value: loc_str.value,
|
||||||
LabelOnly(ref loc_str) => LabelOnly(loc_str.clone()),
|
region: loc_str.region,
|
||||||
SpaceBefore(ref field, spaces) => {
|
},
|
||||||
SpaceBefore(arena.alloc(desugar_field(arena, field)), spaces)
|
spaces,
|
||||||
}
|
desugar(arena, loc_expr),
|
||||||
SpaceAfter(ref field, spaces) => {
|
),
|
||||||
SpaceAfter(arena.alloc(desugar_field(arena, field)), spaces)
|
LabelOnly(loc_str) => LabelOnly(Located {
|
||||||
}
|
value: loc_str.value,
|
||||||
|
region: loc_str.region,
|
||||||
|
}),
|
||||||
|
SpaceBefore(field, spaces) => SpaceBefore(arena.alloc(desugar_field(arena, field)), spaces),
|
||||||
|
SpaceAfter(field, spaces) => SpaceAfter(arena.alloc(desugar_field(arena, field)), spaces),
|
||||||
|
|
||||||
Malformed(string) => Malformed(string),
|
Malformed(string) => Malformed(string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn desugar_binop(binop: BinOp, arena: &Bump) -> (&[&str], &str) {
|
fn binop_to_function(binop: BinOp, arena: &Bump) -> (&[&str], &str) {
|
||||||
use self::BinOp::*;
|
use self::BinOp::*;
|
||||||
|
|
||||||
match binop {
|
match binop {
|
||||||
|
@ -505,6 +375,189 @@ fn desugar_binop(binop: BinOp, arena: &Bump) -> (&[&str], &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a Located<Expr<'a>> {
|
||||||
|
use crate::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);
|
||||||
|
|
||||||
|
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.clone();
|
||||||
|
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(
|
||||||
|
bad_op,
|
||||||
|
stack_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(arena, arg_stack.pop().unwrap());
|
||||||
|
let left = desugar(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 {
|
||||||
|
args.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
Apply(function, args, CalledVia::BinOp(Pizza))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binop => {
|
||||||
|
// This is a normal binary operator like (+), so desugar it
|
||||||
|
// into the appropriate function call.
|
||||||
|
let (module_parts, name) = binop_to_function(binop, arena);
|
||||||
|
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_parts, name),
|
||||||
|
region: loc_op.region,
|
||||||
|
});
|
||||||
|
|
||||||
|
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
enum InfixToken<'a> {
|
enum InfixToken<'a> {
|
||||||
Arg(&'a Located<Expr<'a>>),
|
Arg(&'a Located<Expr<'a>>),
|
||||||
|
@ -561,9 +614,10 @@ impl<'a> Iterator for Infixes<'a> {
|
||||||
.remaining_expr
|
.remaining_expr
|
||||||
.take()
|
.take()
|
||||||
.map(|loc_expr| match loc_expr.value {
|
.map(|loc_expr| match loc_expr.value {
|
||||||
Expr::BinOp((left, op, right)) => {
|
Expr::BinOp((left, loc_op, right))
|
||||||
|
| Expr::Nested(Expr::BinOp((left, loc_op, right))) => {
|
||||||
self.remaining_expr = Some(right);
|
self.remaining_expr = Some(right);
|
||||||
self.next_op = Some(op.clone());
|
self.next_op = Some(loc_op.clone());
|
||||||
|
|
||||||
InfixToken::Arg(left)
|
InfixToken::Arg(left)
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,7 +258,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
},
|
},
|
||||||
|
|
||||||
// &EmptyRecordLiteral => Pattern::EmptyRecordLiteral,
|
// &EmptyRecordLiteral => Pattern::EmptyRecordLiteral,
|
||||||
&SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => {
|
&SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) | Nested(sub_pattern) => {
|
||||||
return canonicalize_pattern(
|
return canonicalize_pattern(
|
||||||
env,
|
env,
|
||||||
state,
|
state,
|
||||||
|
@ -271,6 +271,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => panic!("TODO finish restoring can_pattern branch for {:?}", pattern),
|
_ => panic!("TODO finish restoring can_pattern branch for {:?}", pattern),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,7 +357,7 @@ fn add_constraints<'a>(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => {
|
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
|
||||||
add_constraints(pattern, scope, region, expected, state)
|
add_constraints(pattern, scope, region, expected, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,7 @@ pub fn is_multiline_expr<'a>(expr: &'a Expr<'a>) -> bool {
|
||||||
is_multiline_expr(&loc_subexpr.value)
|
is_multiline_expr(&loc_subexpr.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParensAround(subexpr) => is_multiline_expr(&subexpr),
|
ParensAround(subexpr) | Nested(subexpr) => is_multiline_expr(&subexpr),
|
||||||
|
|
||||||
Closure(loc_patterns, loc_body) => {
|
Closure(loc_patterns, loc_body) => {
|
||||||
// check the body first because it's more likely to be multiline
|
// check the body first because it's more likely to be multiline
|
||||||
|
|
|
@ -84,6 +84,10 @@ pub fn fmt_pattern<'a>(
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Nested(sub_pattern) => {
|
||||||
|
fmt_pattern(buf, sub_pattern, indent, apply_needs_parens);
|
||||||
|
}
|
||||||
|
|
||||||
// Malformed
|
// Malformed
|
||||||
Malformed(string) => buf.push_str(string),
|
Malformed(string) => buf.push_str(string),
|
||||||
QualifiedIdentifier(maybe_qualified) => {
|
QualifiedIdentifier(maybe_qualified) => {
|
||||||
|
|
|
@ -168,6 +168,10 @@ pub enum Expr<'a> {
|
||||||
SpaceAfter(&'a Expr<'a>, &'a [CommentOrNewline<'a>]),
|
SpaceAfter(&'a Expr<'a>, &'a [CommentOrNewline<'a>]),
|
||||||
ParensAround(&'a Expr<'a>),
|
ParensAround(&'a Expr<'a>),
|
||||||
|
|
||||||
|
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
|
||||||
|
/// It lets us take an (&Expr) and create a plain (Expr) from it.
|
||||||
|
Nested(&'a Expr<'a>),
|
||||||
|
|
||||||
// Problems
|
// Problems
|
||||||
MalformedIdent(&'a str),
|
MalformedIdent(&'a str),
|
||||||
MalformedClosure,
|
MalformedClosure,
|
||||||
|
@ -279,6 +283,10 @@ pub enum Pattern<'a> {
|
||||||
/// can only occur inside of a RecordDestructure
|
/// can only occur inside of a RecordDestructure
|
||||||
RecordField(&'a str, &'a Loc<Pattern<'a>>),
|
RecordField(&'a str, &'a Loc<Pattern<'a>>),
|
||||||
|
|
||||||
|
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
|
||||||
|
/// It lets us take an (&Expr) and create a plain (Expr) from it.
|
||||||
|
Nested(&'a Pattern<'a>),
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
IntLiteral(&'a str),
|
IntLiteral(&'a str),
|
||||||
HexIntLiteral(&'a str),
|
HexIntLiteral(&'a str),
|
||||||
|
|
|
@ -272,7 +272,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
||||||
spaces,
|
spaces,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
Expr::ParensAround(sub_expr) => expr_to_pattern(arena, sub_expr),
|
Expr::ParensAround(sub_expr) | Expr::Nested(sub_expr) => expr_to_pattern(arena, sub_expr),
|
||||||
|
|
||||||
Expr::Record(loc_assigned_fields) => {
|
Expr::Record(loc_assigned_fields) => {
|
||||||
let mut loc_patterns = Vec::with_capacity_in(loc_assigned_fields.len(), arena);
|
let mut loc_patterns = Vec::with_capacity_in(loc_assigned_fields.len(), arena);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue