sim_codec/implementation/
lowering.rs1use sim_kernel::Expr;
4
5pub fn lower_operator_nodes(expr: Expr) -> Expr {
12 match expr {
13 Expr::Infix {
14 operator,
15 left,
16 right,
17 } => Expr::Call {
18 operator: Box::new(Expr::Symbol(operator)),
19 args: vec![lower_operator_nodes(*left), lower_operator_nodes(*right)],
20 },
21 Expr::Prefix { operator, arg } | Expr::Postfix { operator, arg } => Expr::Call {
22 operator: Box::new(Expr::Symbol(operator)),
23 args: vec![lower_operator_nodes(*arg)],
24 },
25 Expr::Call { operator, args } => Expr::Call {
26 operator: Box::new(lower_operator_nodes(*operator)),
27 args: args.into_iter().map(lower_operator_nodes).collect(),
28 },
29 Expr::List(items) => Expr::List(items.into_iter().map(lower_operator_nodes).collect()),
30 Expr::Vector(items) => Expr::Vector(items.into_iter().map(lower_operator_nodes).collect()),
31 Expr::Map(entries) => Expr::Map(
32 entries
33 .into_iter()
34 .map(|(key, value)| (lower_operator_nodes(key), lower_operator_nodes(value)))
35 .collect(),
36 ),
37 Expr::Set(items) => Expr::Set(items.into_iter().map(lower_operator_nodes).collect()),
38 Expr::Block(items) => Expr::Block(items.into_iter().map(lower_operator_nodes).collect()),
39 Expr::Annotated { expr, annotations } => Expr::Annotated {
40 expr: Box::new(lower_operator_nodes(*expr)),
41 annotations,
42 },
43 other => other,
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use sim_kernel::{Expr, QuoteMode, Symbol};
50
51 use super::lower_operator_nodes;
52
53 fn sym(name: &str) -> Symbol {
54 Symbol::new(name)
55 }
56
57 fn sym_expr(name: &str) -> Expr {
58 Expr::Symbol(sym(name))
59 }
60
61 fn infix(operator: &str, left: Expr, right: Expr) -> Expr {
62 Expr::Infix {
63 operator: sym(operator),
64 left: Box::new(left),
65 right: Box::new(right),
66 }
67 }
68
69 fn prefix(operator: &str, arg: Expr) -> Expr {
70 Expr::Prefix {
71 operator: sym(operator),
72 arg: Box::new(arg),
73 }
74 }
75
76 fn postfix(operator: &str, arg: Expr) -> Expr {
77 Expr::Postfix {
78 operator: sym(operator),
79 arg: Box::new(arg),
80 }
81 }
82
83 fn call(operator: &str, args: Vec<Expr>) -> Expr {
84 Expr::Call {
85 operator: Box::new(sym_expr(operator)),
86 args,
87 }
88 }
89
90 #[test]
91 fn lowers_nested_operators_inside_calls() {
92 let expr = Expr::Call {
93 operator: Box::new(prefix("resolve", sym_expr("f"))),
94 args: vec![
95 infix("+", sym_expr("a"), postfix("!", sym_expr("b"))),
96 prefix("-", infix("*", sym_expr("c"), sym_expr("d"))),
97 ],
98 };
99
100 let expected = Expr::Call {
101 operator: Box::new(call("resolve", vec![sym_expr("f")])),
102 args: vec![
103 call("+", vec![sym_expr("a"), call("!", vec![sym_expr("b")])]),
104 call("-", vec![call("*", vec![sym_expr("c"), sym_expr("d")])]),
105 ],
106 };
107
108 assert_eq!(lower_operator_nodes(expr), expected);
109 }
110
111 #[test]
112 fn preserves_container_shapes_while_lowering_children() {
113 let expr = Expr::Block(vec![
114 Expr::List(vec![infix("+", sym_expr("a"), sym_expr("b"))]),
115 Expr::Vector(vec![prefix("-", sym_expr("x"))]),
116 Expr::Map(vec![(
117 infix("*", sym_expr("k1"), sym_expr("k2")),
118 postfix("?", sym_expr("v")),
119 )]),
120 Expr::Set(vec![infix("/", sym_expr("n"), sym_expr("d"))]),
121 ]);
122
123 let expected = Expr::Block(vec![
124 Expr::List(vec![call("+", vec![sym_expr("a"), sym_expr("b")])]),
125 Expr::Vector(vec![call("-", vec![sym_expr("x")])]),
126 Expr::Map(vec![(
127 call("*", vec![sym_expr("k1"), sym_expr("k2")]),
128 call("?", vec![sym_expr("v")]),
129 )]),
130 Expr::Set(vec![call("/", vec![sym_expr("n"), sym_expr("d")])]),
131 ]);
132
133 assert_eq!(lower_operator_nodes(expr), expected);
134 }
135
136 #[test]
137 fn leaves_quoted_operator_payloads_unchanged() {
138 let quoted = Expr::Quote {
139 mode: QuoteMode::Quote,
140 expr: Box::new(infix("+", sym_expr("a"), sym_expr("b"))),
141 };
142
143 assert_eq!(lower_operator_nodes(quoted.clone()), quoted);
144 }
145}