use crate::ast::{Expr, Literal, MatchArm, MatchPattern, RecordField};
use crate::interner::{ExprNodeId, Symbol, ToSymbol, TypeNodeId};
use crate::pattern::{Pattern, TypedId, TypedPattern};
use crate::types::Type;
pub fn translate(expr: ExprNodeId) -> ExprNodeId {
translate_stage0(expr)
}
fn strip_code_type(ty: TypeNodeId) -> TypeNodeId {
use crate::types::{PType, RecordTypeField};
match ty.to_type() {
Type::Code(_) => crate::numeric!(),
Type::Function { arg, ret } => {
let new_arg = strip_code_type(arg);
let new_ret = strip_code_type(ret);
if new_arg == arg && new_ret == ret {
ty
} else {
Type::Function {
arg: new_arg,
ret: new_ret,
}
.into_id()
}
}
Type::Tuple(elems) => {
let new_elems: Vec<TypeNodeId> = elems.iter().map(|e| strip_code_type(*e)).collect();
if new_elems == elems {
ty
} else {
Type::Tuple(new_elems).into_id()
}
}
Type::Record(fields) => {
let new_fields: Vec<RecordTypeField> = fields
.iter()
.map(|f| RecordTypeField {
key: f.key,
ty: strip_code_type(f.ty),
has_default: f.has_default,
})
.collect();
Type::Record(new_fields).into_id()
}
Type::Array(inner) => {
let new_inner = strip_code_type(inner);
if new_inner == inner {
ty
} else {
Type::Array(new_inner).into_id()
}
}
Type::Ref(inner) => {
let new_inner = strip_code_type(inner);
if new_inner == inner {
ty
} else {
Type::Ref(new_inner).into_id()
}
}
_ => ty,
}
}
fn strip_code_typed_id(id: TypedId) -> TypedId {
let new_ty = strip_code_type(id.ty);
if new_ty == id.ty {
id
} else {
TypedId { ty: new_ty, ..id }
}
}
fn strip_code_typed_pattern(tp: TypedPattern) -> TypedPattern {
let new_ty = strip_code_type(tp.ty);
if new_ty == tp.ty {
tp
} else {
TypedPattern { ty: new_ty, ..tp }
}
}
fn translate_stage0(expr: ExprNodeId) -> ExprNodeId {
match expr.to_expr() {
Expr::Bracket(inner) => translate_code(inner),
Expr::Escape(_) => {
log::warn!("translate_staging: unexpected Escape at stage 0");
expr
}
Expr::Let(tp, val, then) => {
let new_tp = strip_code_typed_pattern(tp);
let new_val = translate_stage0(val);
let new_then = then.map(translate_stage0);
Expr::Let(new_tp, new_val, new_then).into_id_without_span()
}
Expr::LetRec(id, val, then) => {
let new_id = strip_code_typed_id(id);
let new_val = translate_stage0(val);
let new_then = then.map(translate_stage0);
Expr::LetRec(new_id, new_val, new_then).into_id_without_span()
}
Expr::Lambda(params, rtype, body) => {
let new_params: Vec<TypedId> = params.into_iter().map(strip_code_typed_id).collect();
let new_rtype = rtype.map(strip_code_type);
let new_body = translate_stage0(body);
Expr::Lambda(new_params, new_rtype, new_body).into_id_without_span()
}
Expr::Apply(f, args) => {
let new_f = translate_stage0(f);
let new_args = args.into_iter().map(translate_stage0).collect();
Expr::Apply(new_f, new_args).into_id_without_span()
}
Expr::If(cond, then, else_opt) => {
let new_cond = translate_stage0(cond);
let new_then = translate_stage0(then);
let new_else = else_opt.map(translate_stage0);
Expr::If(new_cond, new_then, new_else).into_id_without_span()
}
Expr::Then(e1, e2) => {
let new_e1 = translate_stage0(e1);
let new_e2 = e2.map(translate_stage0);
Expr::Then(new_e1, new_e2).into_id_without_span()
}
Expr::Block(inner) => {
let new_inner = inner.map(translate_stage0);
Expr::Block(new_inner).into_id_without_span()
}
Expr::Tuple(elems) => {
let new_elems = elems.into_iter().map(translate_stage0).collect();
Expr::Tuple(new_elems).into_id_without_span()
}
Expr::ArrayLiteral(elems) => {
let new_elems = elems.into_iter().map(translate_stage0).collect();
Expr::ArrayLiteral(new_elems).into_id_without_span()
}
Expr::RecordLiteral(fields) => {
let new_fields = fields
.into_iter()
.map(|f| RecordField {
name: f.name,
expr: translate_stage0(f.expr),
})
.collect();
Expr::RecordLiteral(new_fields).into_id_without_span()
}
Expr::Proj(inner, idx) => {
let new_inner = translate_stage0(inner);
Expr::Proj(new_inner, idx).into_id_without_span()
}
Expr::ArrayAccess(arr, idx) => {
let new_arr = translate_stage0(arr);
let new_idx = translate_stage0(idx);
Expr::ArrayAccess(new_arr, new_idx).into_id_without_span()
}
Expr::FieldAccess(inner, name) => {
let new_inner = translate_stage0(inner);
Expr::FieldAccess(new_inner, name).into_id_without_span()
}
Expr::Assign(lhs, rhs) => {
let new_lhs = translate_stage0(lhs);
let new_rhs = translate_stage0(rhs);
Expr::Assign(new_lhs, new_rhs).into_id_without_span()
}
Expr::Feed(id, body) => {
let new_body = translate_stage0(body);
Expr::Feed(id, new_body).into_id_without_span()
}
Expr::Match(scrutinee, arms) => {
let new_scrutinee = translate_stage0(scrutinee);
let new_arms = arms
.into_iter()
.map(|arm| MatchArm {
pattern: arm.pattern,
body: translate_stage0(arm.body),
})
.collect();
Expr::Match(new_scrutinee, new_arms).into_id_without_span()
}
Expr::Paren(inner) => {
let new_inner = translate_stage0(inner);
Expr::Paren(new_inner).into_id_without_span()
}
Expr::Literal(_) | Expr::Var(_) | Expr::QualifiedVar(_) | Expr::Error => expr,
Expr::BinOp(..) | Expr::UniOp(..) | Expr::MacroExpand(..) => {
log::warn!("translate_staging: unexpected desugared-only node at stage 0");
expr
}
Expr::ImcompleteRecord(fields) => {
let new_fields = fields
.into_iter()
.map(|f| RecordField {
name: f.name,
expr: translate_stage0(f.expr),
})
.collect();
Expr::ImcompleteRecord(new_fields).into_id_without_span()
}
Expr::RecordUpdate(record, fields) => {
let new_record = translate_stage0(record);
let new_fields = fields
.into_iter()
.map(|f| RecordField {
name: f.name,
expr: translate_stage0(f.expr),
})
.collect();
Expr::RecordUpdate(new_record, new_fields).into_id_without_span()
}
}
}
fn translate_code(expr: ExprNodeId) -> ExprNodeId {
match expr.to_expr() {
Expr::Escape(inner) => translate_stage0(inner),
Expr::Bracket(inner) => {
log::warn!("translate_staging: nested Bracket in translate_code (multi-stage)");
make_apply1("code_block", translate_code(inner))
}
Expr::Literal(lit) => match lit {
Literal::Float(sym) => make_apply1(
"code_lit_f",
Expr::Literal(Literal::Float(sym)).into_id_without_span(),
),
Literal::Int(i) => make_apply1(
"code_lit_i",
Expr::Literal(Literal::Int(i)).into_id_without_span(),
),
Literal::String(sym) => make_apply1(
"code_lit_s",
Expr::Literal(Literal::String(sym)).into_id_without_span(),
),
Literal::SelfLit => make_apply0("code_self"),
Literal::Now => make_apply0("code_now"),
Literal::SampleRate => make_apply0("code_samplerate"),
Literal::PlaceHolder => {
log::warn!("translate_staging: PlaceHolder literal in translate_code");
expr
}
},
Expr::Var(name) => make_apply_str("code_var", name),
Expr::Apply(f, args) => {
let translated_f = translate_code(f);
match args.len() {
0 => {
let empty_arr = Expr::ArrayLiteral(vec![]).into_id_without_span();
make_apply("code_app", vec![translated_f, empty_arr])
}
1 => {
let translated_arg = translate_code(args.into_iter().next().unwrap());
make_apply("code_app1", vec![translated_f, translated_arg])
}
2 => {
let mut iter = args.into_iter();
let a1 = translate_code(iter.next().unwrap());
let a2 = translate_code(iter.next().unwrap());
make_apply("code_app2", vec![translated_f, a1, a2])
}
_ => {
let translated_args: Vec<ExprNodeId> =
args.into_iter().map(translate_code).collect();
let arr = Expr::ArrayLiteral(translated_args).into_id_without_span();
make_apply("code_app", vec![translated_f, arr])
}
}
}
Expr::Lambda(params, _rtype, body) => {
let translated_body = translate_code(body);
match params.len() {
1 => {
let name_lit = sym_to_string_literal(params[0].id);
make_apply("code_lam1_finish", vec![name_lit, translated_body])
}
_ => {
let name_lits: Vec<ExprNodeId> =
params.iter().map(|p| sym_to_string_literal(p.id)).collect();
let names_arr = Expr::ArrayLiteral(name_lits).into_id_without_span();
make_apply("code_lam_finish", vec![names_arr, translated_body])
}
}
}
Expr::Let(tp, val, then) => {
let translated_val = translate_code(val);
let translated_body = then
.map(translate_code)
.unwrap_or_else(|| pattern_to_var_code(&tp.pat));
translate_let_pattern(&tp.pat, translated_val, translated_body)
}
Expr::LetRec(id, val, then) => {
let name_lit = sym_to_string_literal(id.id);
let translated_val = translate_code(val);
match then {
Some(body) => {
let translated_body = translate_code(body);
make_apply(
"code_letrec",
vec![name_lit, translated_val, translated_body],
)
}
None => {
let var_ref = make_apply_str("code_var", id.id);
make_apply("code_letrec", vec![name_lit, translated_val, var_ref])
}
}
}
Expr::If(cond, then, else_opt) => {
let translated_cond = translate_code(cond);
let translated_then = translate_code(then);
let translated_else = else_opt
.map(translate_code)
.unwrap_or_else(|| {
let zero =
Expr::Literal(Literal::Float("0.0".to_symbol())).into_id_without_span();
make_apply1("code_lit_f", zero)
});
make_apply(
"code_if",
vec![translated_cond, translated_then, translated_else],
)
}
Expr::Then(e1, e2) => {
let translated_e1 = translate_code(e1);
match e2 {
Some(e) => {
let translated_e2 = translate_code(e);
make_apply("code_then", vec![translated_e1, translated_e2])
}
None => translated_e1,
}
}
Expr::Assign(lhs, rhs) => {
let translated_lhs = translate_code(lhs);
let translated_rhs = translate_code(rhs);
make_apply("code_assign", vec![translated_lhs, translated_rhs])
}
Expr::Tuple(elems) => {
let translated: Vec<ExprNodeId> = elems.into_iter().map(translate_code).collect();
let arr = Expr::ArrayLiteral(translated).into_id_without_span();
make_apply1("code_tuple", arr)
}
Expr::Proj(inner, idx) => {
let translated = translate_code(inner);
let idx_lit = Expr::Literal(Literal::Int(idx)).into_id_without_span();
make_apply("code_proj", vec![translated, idx_lit])
}
Expr::ArrayLiteral(elems) => {
let translated: Vec<ExprNodeId> = elems.into_iter().map(translate_code).collect();
let arr = Expr::ArrayLiteral(translated).into_id_without_span();
make_apply1("code_array", arr)
}
Expr::ArrayAccess(arr, idx) => {
let translated_arr = translate_code(arr);
let translated_idx = translate_code(idx);
make_apply("code_array_access", vec![translated_arr, translated_idx])
}
Expr::RecordLiteral(fields) => {
let names: Vec<ExprNodeId> = fields
.iter()
.map(|f| sym_to_string_literal(f.name))
.collect();
let vals: Vec<ExprNodeId> =
fields.into_iter().map(|f| translate_code(f.expr)).collect();
let names_arr = Expr::ArrayLiteral(names).into_id_without_span();
let vals_arr = Expr::ArrayLiteral(vals).into_id_without_span();
make_apply("code_record", vec![names_arr, vals_arr])
}
Expr::FieldAccess(inner, name) => {
let translated = translate_code(inner);
let name_lit = sym_to_string_literal(name);
make_apply("code_field_access", vec![translated, name_lit])
}
Expr::Feed(id, body) => {
let name_lit = sym_to_string_literal(id);
let translated_body = translate_code(body);
make_apply("code_feed", vec![name_lit, translated_body])
}
Expr::Block(inner) => match inner {
Some(e) => {
let translated = translate_code(e);
make_apply1("code_block", translated)
}
None => {
let zero = Expr::Literal(Literal::Float("0.0".to_symbol())).into_id_without_span();
make_apply1("code_lit_f", zero)
}
},
Expr::Match(scrutinee, arms) => translate_code_match(scrutinee, arms),
Expr::Paren(inner) => translate_code(inner),
Expr::QualifiedVar(_) => {
log::warn!("translate_staging: QualifiedVar in translate_code");
expr
}
Expr::BinOp(..) | Expr::UniOp(..) | Expr::MacroExpand(..) => {
log::warn!("translate_staging: desugared-only node in translate_code");
expr
}
Expr::ImcompleteRecord(fields) => {
let names: Vec<ExprNodeId> = fields
.iter()
.map(|f| sym_to_string_literal(f.name))
.collect();
let vals: Vec<ExprNodeId> =
fields.into_iter().map(|f| translate_code(f.expr)).collect();
let names_arr = Expr::ArrayLiteral(names).into_id_without_span();
let vals_arr = Expr::ArrayLiteral(vals).into_id_without_span();
make_apply("code_imcomplete_record", vec![names_arr, vals_arr])
}
Expr::RecordUpdate(record, fields) => {
let translated_record = translate_code(record);
let names: Vec<ExprNodeId> = fields
.iter()
.map(|f| sym_to_string_literal(f.name))
.collect();
let vals: Vec<ExprNodeId> =
fields.into_iter().map(|f| translate_code(f.expr)).collect();
let names_arr = Expr::ArrayLiteral(names).into_id_without_span();
let vals_arr = Expr::ArrayLiteral(vals).into_id_without_span();
make_apply(
"code_record_update",
vec![translated_record, names_arr, vals_arr],
)
}
Expr::Error => expr,
}
}
fn translate_code_match(scrutinee: ExprNodeId, arms: Vec<MatchArm>) -> ExprNodeId {
let translated_scrutinee = translate_code(scrutinee);
let translated_arm_bodies: Vec<ExprNodeId> =
arms.iter().map(|arm| translate_code(arm.body)).collect();
let pattern_tags: Vec<ExprNodeId> = arms
.iter()
.map(|arm| encode_match_pattern(&arm.pattern))
.collect();
let scrutinee_arg = translated_scrutinee;
let bodies_arr = Expr::ArrayLiteral(translated_arm_bodies).into_id_without_span();
let patterns_arr = Expr::ArrayLiteral(pattern_tags).into_id_without_span();
make_apply("code_match", vec![scrutinee_arg, patterns_arr, bodies_arr])
}
fn encode_match_pattern(pat: &MatchPattern) -> ExprNodeId {
match pat {
MatchPattern::Wildcard => Expr::Literal(Literal::Int(0)).into_id_without_span(),
MatchPattern::Variable(name) => {
Expr::Literal(Literal::String(*name)).into_id_without_span()
}
MatchPattern::Literal(lit) => Expr::Literal(lit.clone()).into_id_without_span(),
MatchPattern::Constructor(name, inner) => {
let tag = Expr::Literal(Literal::Int(1)).into_id_without_span();
let name_lit = Expr::Literal(Literal::String(*name)).into_id_without_span();
let mut elems = vec![tag, name_lit];
if let Some(inner_pat) = inner {
elems.push(encode_match_pattern(inner_pat));
}
Expr::Tuple(elems).into_id_without_span()
}
MatchPattern::Tuple(pats) => {
let tag = Expr::Literal(Literal::Int(2)).into_id_without_span();
let mut elems = vec![tag];
elems.extend(pats.iter().map(encode_match_pattern));
Expr::Tuple(elems).into_id_without_span()
}
}
}
fn make_apply(name: &str, args: Vec<ExprNodeId>) -> ExprNodeId {
let f = Expr::Var(name.to_symbol()).into_id_without_span();
Expr::Apply(f, args).into_id_without_span()
}
fn make_apply1(name: &str, arg: ExprNodeId) -> ExprNodeId {
make_apply(name, vec![arg])
}
fn make_apply0(name: &str) -> ExprNodeId {
make_apply(name, vec![])
}
fn make_apply_str(name: &str, sym: Symbol) -> ExprNodeId {
let lit = sym_to_string_literal(sym);
make_apply1(name, lit)
}
fn sym_to_string_literal(sym: Symbol) -> ExprNodeId {
Expr::Literal(Literal::String(sym)).into_id_without_span()
}
fn pattern_to_string_literal(pat: &Pattern) -> ExprNodeId {
sym_to_string_literal(pattern_to_symbol(pat))
}
fn pattern_to_symbol(pat: &Pattern) -> Symbol {
match pat {
Pattern::Single(name) => *name,
Pattern::Placeholder => "_".to_symbol(),
Pattern::Tuple(pats) => pats
.first()
.map(pattern_to_symbol)
.unwrap_or_else(|| "_".to_symbol()),
Pattern::Record(fields) => fields
.first()
.map(|(name, _)| *name)
.unwrap_or_else(|| "_".to_symbol()),
Pattern::Error => "_".to_symbol(),
}
}
fn translate_let_pattern(
pat: &Pattern,
translated_val: ExprNodeId,
translated_body: ExprNodeId,
) -> ExprNodeId {
match pat {
Pattern::Single(name) => {
let name_lit = sym_to_string_literal(*name);
make_apply("code_let", vec![name_lit, translated_val, translated_body])
}
Pattern::Placeholder => {
let name_lit = sym_to_string_literal("_".to_symbol());
make_apply("code_let", vec![name_lit, translated_val, translated_body])
}
Pattern::Tuple(pats) => translate_let_tuple_pattern(pats, translated_val, translated_body),
Pattern::Record(_) | Pattern::Error => {
let name_lit = pattern_to_string_literal(pat);
make_apply("code_let", vec![name_lit, translated_val, translated_body])
}
}
}
thread_local! {
static DESUGAR_COUNTER: std::cell::Cell<u32> = const { std::cell::Cell::new(0) };
}
fn fresh_desugar_name() -> Symbol {
DESUGAR_COUNTER.with(|c| {
let n = c.get();
c.set(n + 1);
format!("__dt{n}").to_symbol()
})
}
fn translate_let_tuple_pattern(
pats: &[Pattern],
translated_val: ExprNodeId,
translated_body: ExprNodeId,
) -> ExprNodeId {
let mut top_names: Vec<ExprNodeId> = Vec::with_capacity(pats.len());
let mut nested: Vec<(usize, &[Pattern], Symbol)> = Vec::new();
for (i, pat) in pats.iter().enumerate() {
match pat {
Pattern::Single(name) => {
top_names.push(sym_to_string_literal(*name));
}
Pattern::Placeholder => {
top_names.push(sym_to_string_literal("_".to_symbol()));
}
Pattern::Tuple(sub_pats) => {
let tmp = fresh_desugar_name();
top_names.push(sym_to_string_literal(tmp));
nested.push((i, sub_pats.as_slice(), tmp));
}
Pattern::Record(_) | Pattern::Error => {
let name = pattern_to_symbol(pat);
top_names.push(sym_to_string_literal(name));
}
}
}
let mut body = translated_body;
for (_i, sub_pats, tmp) in nested.into_iter().rev() {
let tmp_var = make_apply_str("code_var", tmp);
body = translate_let_tuple_pattern(sub_pats, tmp_var, body);
}
let names_arr = Expr::ArrayLiteral(top_names).into_id_without_span();
make_apply("code_let_tuple", vec![names_arr, translated_val, body])
}
fn pattern_to_var_code(pat: &Pattern) -> ExprNodeId {
match pat {
Pattern::Single(name) => make_apply_str("code_var", *name),
Pattern::Placeholder => {
let zero = Expr::Literal(Literal::Float("0.0".to_symbol())).into_id_without_span();
make_apply1("code_lit_f", zero)
}
Pattern::Tuple(pats) => {
let var_refs: Vec<ExprNodeId> = pats.iter().map(pattern_to_var_code).collect();
let arr = Expr::ArrayLiteral(var_refs).into_id_without_span();
make_apply1("code_tuple", arr)
}
Pattern::Record(fields) => {
let names: Vec<ExprNodeId> = fields
.iter()
.map(|(name, _)| sym_to_string_literal(*name))
.collect();
let vals: Vec<ExprNodeId> = fields
.iter()
.map(|(_, pat)| pattern_to_var_code(pat))
.collect();
let names_arr = Expr::ArrayLiteral(names).into_id_without_span();
let vals_arr = Expr::ArrayLiteral(vals).into_id_without_span();
make_apply("code_record", vec![names_arr, vals_arr])
}
Pattern::Error => {
let zero = Expr::Literal(Literal::Float("0.0".to_symbol())).into_id_without_span();
make_apply1("code_lit_f", zero)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::interner::ToSymbol;
use crate::pattern::TypedId;
use crate::types::Type;
use crate::utils::metadata::Location;
fn default_loc() -> Location {
Location::default()
}
fn unknown_ty() -> crate::interner::TypeNodeId {
Type::Unknown.into_id_with_location(default_loc())
}
#[allow(dead_code)]
fn translate_and_get(expr: ExprNodeId) -> Expr {
translate(expr).to_expr()
}
#[test]
fn simple_literal_in_bracket() {
let lit = Expr::Literal(Literal::Float("42.0".to_symbol())).into_id_without_span();
let bracket = Expr::Bracket(lit).into_id_without_span();
let result = translate(bracket);
match result.to_expr() {
Expr::Apply(f, args) => {
assert!(matches!(f.to_expr(), Expr::Var(name) if name.as_str() == "code_lit_f"));
assert_eq!(args.len(), 1);
assert!(matches!(
args[0].to_expr(),
Expr::Literal(Literal::Float(_))
));
}
other => panic!("expected Apply, got {:?}", other),
}
}
#[test]
fn variable_in_bracket() {
let var = Expr::Var("x".to_symbol()).into_id_without_span();
let bracket = Expr::Bracket(var).into_id_without_span();
let result = translate(bracket);
match result.to_expr() {
Expr::Apply(f, args) => {
assert!(matches!(f.to_expr(), Expr::Var(name) if name.as_str() == "code_var"));
assert_eq!(args.len(), 1);
match args[0].to_expr() {
Expr::Literal(Literal::String(s)) => assert_eq!(s.as_str(), "x"),
other => panic!("expected String literal, got {:?}", other),
}
}
other => panic!("expected Apply, got {:?}", other),
}
}
#[test]
fn escape_in_bracket() {
let var = Expr::Var("x".to_symbol()).into_id_without_span();
let escape = Expr::Escape(var).into_id_without_span();
let bracket = Expr::Bracket(escape).into_id_without_span();
let result = translate(bracket);
match result.to_expr() {
Expr::Var(name) => assert_eq!(name.as_str(), "x"),
other => panic!("expected Var, got {:?}", other),
}
}
#[test]
fn apply_in_bracket() {
let f = Expr::Var("f".to_symbol()).into_id_without_span();
let x = Expr::Var("x".to_symbol()).into_id_without_span();
let apply = Expr::Apply(f, vec![x]).into_id_without_span();
let bracket = Expr::Bracket(apply).into_id_without_span();
let result = translate(bracket);
match result.to_expr() {
Expr::Apply(func, args) => {
assert!(matches!(func.to_expr(), Expr::Var(name) if name.as_str() == "code_app1"));
assert_eq!(args.len(), 2);
}
other => panic!("expected Apply(code_app1, ...), got {other:?}"),
}
}
#[test]
fn lambda_in_bracket() {
let x_id = TypedId::new("x".to_symbol(), unknown_ty());
let body = Expr::Var("x".to_symbol()).into_id_without_span();
let lam = Expr::Lambda(vec![x_id], None, body).into_id_without_span();
let bracket = Expr::Bracket(lam).into_id_without_span();
let result = translate(bracket);
match result.to_expr() {
Expr::Apply(func, args) => {
assert!(
matches!(func.to_expr(), Expr::Var(name) if name.as_str() == "code_lam1_finish")
);
assert_eq!(args.len(), 2);
match args[0].to_expr() {
Expr::Literal(Literal::String(s)) => assert_eq!(s.as_str(), "x"),
other => panic!("expected String literal, got {other:?}"),
}
}
other => panic!("expected Apply(code_lam1_finish, ...), got {other:?}"),
}
}
#[test]
fn macro_pattern_bracket_escape_bracket() {
let lit_42 = Expr::Literal(Literal::Float("42.0".to_symbol())).into_id_without_span();
let bracket_inner = Expr::Bracket(lit_42).into_id_without_span();
let lambda = Expr::Lambda(vec![], None, bracket_inner).into_id_without_span();
let dsp_var = Expr::Var("dsp".to_symbol()).into_id_without_span();
let bracket_main = Expr::Bracket(dsp_var).into_id_without_span();
let make_42_id = TypedId::new("make_42".to_symbol(), unknown_ty());
let letrec = Expr::LetRec(make_42_id, lambda, Some(bracket_main)).into_id_without_span();
let escape = Expr::Escape(letrec).into_id_without_span();
let outer_bracket = Expr::Bracket(escape).into_id_without_span();
let result = translate(outer_bracket);
match result.to_expr() {
Expr::LetRec(id, val, then) => {
assert_eq!(id.id.as_str(), "make_42");
match val.to_expr() {
Expr::Lambda(params, _, body) => {
assert_eq!(params.len(), 0);
match body.to_expr() {
Expr::Apply(f, _) => {
assert!(
matches!(f.to_expr(), Expr::Var(name) if name.as_str() == "code_lit_f")
);
}
other => panic!("expected Apply(code_lit_f, ...), got {other:?}"),
}
}
other => panic!("expected Lambda, got {other:?}"),
}
let then = then.expect("expected Some(then)");
match then.to_expr() {
Expr::Apply(f, args) => {
assert!(
matches!(f.to_expr(), Expr::Var(name) if name.as_str() == "code_var")
);
assert_eq!(args.len(), 1);
}
other => panic!("expected Apply(code_var, ...), got {other:?}"),
}
}
other => panic!("expected LetRec, got {other:?}"),
}
}
}