mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Desugar Record Builder
This commit is contained in:
parent
71c80171d4
commit
dfa9c29147
2 changed files with 126 additions and 4 deletions
|
@ -1052,6 +1052,9 @@ pub fn canonicalize_expr<'a>(
|
|||
can_defs_with_return(env, var_store, inner_scope, env.arena.alloc(defs), loc_ret)
|
||||
})
|
||||
}
|
||||
ast::Expr::RecordBuilder(_) => {
|
||||
unreachable!("RecordBuilder should have been desugared by now")
|
||||
}
|
||||
ast::Expr::Backpassing(_, _, _) => {
|
||||
unreachable!("Backpassing should have been desugared by now")
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use roc_module::called_via::BinOp::Pizza;
|
|||
use roc_module::called_via::{BinOp, CalledVia};
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{AssignedField, ValueDef, WhenBranch};
|
||||
use roc_parse::ast::{AssignedField, Collection, RecordBuilderField, ValueDef, WhenBranch};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
// BinOp precedence logic adapted from Gluon by Markus Westerlind
|
||||
|
@ -252,6 +252,9 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
}
|
||||
}
|
||||
}
|
||||
RecordBuilder(_) => {
|
||||
todo!("Compiler error: Record builders must be applied to functions");
|
||||
}
|
||||
BinOps(lefts, right) => desugar_bin_ops(arena, loc_expr.region, lefts, right),
|
||||
Defs(defs, loc_ret) => {
|
||||
let mut defs = (*defs).clone();
|
||||
|
@ -263,17 +266,51 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
}
|
||||
Apply(loc_fn, loc_args, called_via) => {
|
||||
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
let mut builder_apply_exprs = None;
|
||||
|
||||
for loc_arg in loc_args.iter() {
|
||||
desugared_args.push(desugar_expr(arena, loc_arg));
|
||||
let arg = match loc_arg.value {
|
||||
RecordBuilder(fields) => {
|
||||
if builder_apply_exprs.is_some() {
|
||||
todo!("Compiler error: A function application can only be passed one record builder")
|
||||
}
|
||||
|
||||
let builder_arg = record_builder_arg(arena, loc_arg.region, fields);
|
||||
builder_apply_exprs = Some(builder_arg.apply_exprs);
|
||||
builder_arg.closure
|
||||
}
|
||||
_ => loc_arg,
|
||||
};
|
||||
|
||||
desugared_args.push(desugar_expr(arena, arg));
|
||||
}
|
||||
|
||||
let desugared_args = desugared_args.into_bump_slice();
|
||||
|
||||
arena.alloc(Loc {
|
||||
let mut apply: &Loc<Expr> = arena.alloc(Loc {
|
||||
value: Apply(desugar_expr(arena, loc_fn), desugared_args, *called_via),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
});
|
||||
|
||||
match builder_apply_exprs {
|
||||
None => {}
|
||||
|
||||
Some(apply_exprs) => {
|
||||
for expr in apply_exprs {
|
||||
let desugared_expr = desugar_expr(arena, expr);
|
||||
|
||||
let args = std::slice::from_ref(arena.alloc(apply));
|
||||
|
||||
apply = arena.alloc(Loc {
|
||||
// Agus TODO: new CalledVia?
|
||||
value: Apply(desugared_expr, args, *called_via),
|
||||
region: loc_expr.region,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply
|
||||
}
|
||||
When(loc_cond_expr, branches) => {
|
||||
let loc_desugared_cond = &*arena.alloc(desugar_expr(arena, loc_cond_expr));
|
||||
|
@ -430,6 +467,88 @@ fn desugar_field<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
struct RecordBuilderArg<'a> {
|
||||
closure: &'a Loc<Expr<'a>>,
|
||||
apply_exprs: Vec<'a, &'a Loc<Expr<'a>>>,
|
||||
}
|
||||
|
||||
fn record_builder_arg<'a>(
|
||||
arena: &'a Bump,
|
||||
region: Region,
|
||||
fields: Collection<'a, Loc<RecordBuilderField<'a>>>,
|
||||
) -> RecordBuilderArg<'a> {
|
||||
let mut record_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||
let mut apply_exprs = Vec::with_capacity_in(fields.len(), arena);
|
||||
let mut apply_field_names = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
// Build the record that the closure will return and gather apply expressions
|
||||
|
||||
for field in fields.iter() {
|
||||
let mut current = field.value;
|
||||
|
||||
let new_field = loop {
|
||||
match current {
|
||||
RecordBuilderField::Value(label, spaces, expr) => {
|
||||
break AssignedField::RequiredValue(label, spaces, expr)
|
||||
}
|
||||
RecordBuilderField::ApplyValue(label, _spaces, expr) => {
|
||||
apply_field_names.push(label);
|
||||
apply_exprs.push(expr);
|
||||
|
||||
break AssignedField::LabelOnly(label);
|
||||
}
|
||||
RecordBuilderField::LabelOnly(label) => break AssignedField::LabelOnly(label),
|
||||
RecordBuilderField::SpaceBefore(sub_field, _) => {
|
||||
current = *sub_field;
|
||||
}
|
||||
RecordBuilderField::SpaceAfter(sub_field, _) => {
|
||||
current = *sub_field;
|
||||
}
|
||||
RecordBuilderField::Malformed(malformed) => {
|
||||
break AssignedField::Malformed(malformed)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
record_fields.push(Loc {
|
||||
value: new_field,
|
||||
region: field.region,
|
||||
});
|
||||
}
|
||||
|
||||
let record_fields = fields.replace_items(record_fields.into_bump_slice());
|
||||
|
||||
let mut body = arena.alloc(Loc {
|
||||
value: Record(record_fields),
|
||||
region,
|
||||
});
|
||||
|
||||
// Construct the builder's closure
|
||||
//
|
||||
// { x, y, z: 3 }
|
||||
// \y -> { x, y, z: 3 }
|
||||
// \x -> \y -> { x, y, z: 3 }
|
||||
|
||||
for name in apply_field_names.iter().rev() {
|
||||
let ident = roc_parse::ast::Pattern::Identifier(name.value);
|
||||
|
||||
let arg_pattern = arena.alloc(Loc {
|
||||
value: ident,
|
||||
region: name.region,
|
||||
});
|
||||
|
||||
body = arena.alloc(Loc {
|
||||
value: Closure(std::slice::from_ref(arg_pattern), body),
|
||||
region,
|
||||
});
|
||||
}
|
||||
|
||||
RecordBuilderArg {
|
||||
closure: body,
|
||||
apply_exprs,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move this desugaring to canonicalization, so we can use Symbols instead of strings
|
||||
#[inline(always)]
|
||||
fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue