1use sim_codec::{Decoder, Encoder, Input, ReadCx, encode_string_literal};
6use sim_codec_lisp::{LispProcMacroDecoder, LispProcMacroEncoder};
7use sim_kernel::{
8 EncodeOptions, Expr, LocatedExprTree, NumberLiteral, Origin, PrattTable, Result, Symbol,
9 Trivia, WriteCx,
10};
11
12pub fn encode_algol(
43 expr: &Expr,
44 table: &PrattTable,
45 parent_bp: u16,
46 cx: &mut WriteCx<'_>,
47) -> Result<String> {
48 match expr {
49 Expr::Nil => Ok("nil".to_owned()),
50 Expr::Bool(true) => Ok("true".to_owned()),
51 Expr::Bool(false) => Ok("false".to_owned()),
52 Expr::Number(number) => encode_number_algol(number, table, cx),
53 Expr::Symbol(symbol) => encode_symbol_algol(symbol, table, cx),
54 Expr::String(value) => Ok(encode_string_literal(value)),
55 Expr::Infix {
56 operator,
57 left,
58 right,
59 } => {
60 let op = table.require_infix(operator)?;
61 let lhs = encode_algol(left, table, op.left_bp, cx)?;
62 let rhs = encode_algol(right, table, op.right_bp, cx)?;
63 let text = format!("{} {} {}", lhs, operator, rhs);
64 if op.left_bp < parent_bp {
65 Ok(format!("({})", text))
66 } else {
67 Ok(text)
68 }
69 }
70 Expr::Prefix { operator, arg } => {
71 let op = table.require_prefix(operator)?;
72 let inner = encode_algol(arg, table, op.right_bp, cx)?;
73 Ok(format!("{}{}", operator, inner))
74 }
75 Expr::Postfix { operator, arg } => {
76 let op = table.require_postfix(operator)?;
77 let inner = encode_algol(arg, table, op.left_bp, cx)?;
78 Ok(format!("{}{}", inner, operator))
79 }
80 Expr::Call { operator, args } => {
81 let op = encode_algol(operator, table, 110, cx)?;
82 let args = args
83 .iter()
84 .map(|arg| encode_algol(arg, table, 0, cx))
85 .collect::<Result<Vec<_>>>()?
86 .join(", ");
87 Ok(format!("{}({})", op, args))
88 }
89 other => encode_escape_algol(other, cx),
90 }
91}
92
93fn encode_number_algol(
94 number: &NumberLiteral,
95 table: &PrattTable,
96 cx: &mut WriteCx<'_>,
97) -> Result<String> {
98 let expr = Expr::Number(number.clone());
99 if algol_text_round_trips(&expr, &number.canonical, table, cx) {
100 return Ok(number.canonical.clone());
101 }
102 encode_escape_algol(&expr, cx)
103}
104
105fn encode_symbol_algol(
106 symbol: &Symbol,
107 table: &PrattTable,
108 cx: &mut WriteCx<'_>,
109) -> Result<String> {
110 let text = symbol.to_string();
111 let expr = Expr::Symbol(symbol.clone());
112 if algol_text_round_trips(&expr, &text, table, cx) {
113 return Ok(text);
114 }
115 encode_escape_algol(&expr, cx)
116}
117
118fn algol_text_round_trips(
119 expected: &Expr,
120 text: &str,
121 table: &PrattTable,
122 cx: &mut WriteCx<'_>,
123) -> bool {
124 crate::parse::parse_algol_expr_with_table(cx.cx, table.clone(), text)
125 .is_ok_and(|parsed| parsed.canonical_eq(expected))
126}
127
128pub(crate) fn encode_algol_tree(
129 tree: &LocatedExprTree,
130 table: &PrattTable,
131 parent_bp: u16,
132 cx: &mut WriteCx<'_>,
133) -> Result<String> {
134 let prefix = encode_trivia(&tree.origin);
135 let body = match &tree.expr {
136 Expr::Infix { operator, .. } if tree.children.len() == 2 => {
137 let op = table.require_infix(operator)?;
138 let lhs = encode_algol_tree(&tree.children[0], table, op.left_bp, cx)?;
139 let rhs = encode_algol_tree(&tree.children[1], table, op.right_bp, cx)?;
140 let text = format!("{} {} {}", lhs, operator, rhs);
141 if op.left_bp < parent_bp {
142 format!("({})", text)
143 } else {
144 text
145 }
146 }
147 Expr::Prefix { operator, .. } if tree.children.len() == 1 => {
148 let op = table.require_prefix(operator)?;
149 let inner = encode_algol_tree(&tree.children[0], table, op.right_bp, cx)?;
150 format!("{}{}", operator, inner)
151 }
152 Expr::Postfix { operator, .. } if tree.children.len() == 1 => {
153 let op = table.require_postfix(operator)?;
154 let inner = encode_algol_tree(&tree.children[0], table, op.left_bp, cx)?;
155 format!("{}{}", inner, operator)
156 }
157 Expr::Call { .. } if !tree.children.is_empty() => {
158 let operator = encode_algol_tree(&tree.children[0], table, 110, cx)?;
159 let args = tree.children[1..]
160 .iter()
161 .map(|arg| encode_algol_tree(arg, table, 0, cx))
162 .collect::<Result<Vec<_>>>()?
163 .join(", ");
164 format!("{}({})", operator, args)
165 }
166 _ => encode_algol(&tree.expr, table, parent_bp, cx)?,
167 };
168 Ok(format!("{prefix}{body}"))
169}
170
171fn encode_escape_algol(expr: &Expr, cx: &mut WriteCx<'_>) -> Result<String> {
172 let mut nested = WriteCx {
173 cx: &mut *cx.cx,
174 codec: cx.codec,
175 options: EncodeOptions::default(),
176 };
177 let text = LispProcMacroEncoder
178 .encode(&mut nested, expr)?
179 .into_text()?;
180 Ok(format!("expr.lisp({})", encode_string_literal(&text)))
181}
182
183fn encode_trivia(origin: &Option<Origin>) -> String {
184 origin
185 .as_ref()
186 .map(|origin| {
187 origin
188 .trivia
189 .iter()
190 .map(|item| match item {
191 Trivia::Whitespace(text)
192 | Trivia::LineComment(text)
193 | Trivia::BlockComment(text) => text.clone(),
194 })
195 .collect::<Vec<_>>()
196 .join("")
197 })
198 .unwrap_or_default()
199}
200
201pub(crate) fn decode_escape(cx: &mut ReadCx<'_>, expr: Expr) -> Result<Expr> {
202 match expr {
203 Expr::Call { operator, args } => {
204 if matches!(operator.as_ref(), Expr::Symbol(symbol) if symbol == &Symbol::qualified("expr", "lisp"))
205 {
206 let [Expr::String(text)] = args.as_slice() else {
207 return Err(sim_kernel::Error::Eval(
208 "expr.lisp expects one string arg".to_owned(),
209 ));
210 };
211 return LispProcMacroDecoder.decode(cx, Input::Text(text.clone()));
212 }
213
214 Ok(Expr::Call {
215 operator,
216 args: args
217 .into_iter()
218 .map(|arg| decode_escape(cx, arg))
219 .collect::<Result<Vec<_>>>()?,
220 })
221 }
222 Expr::Infix {
223 operator,
224 left,
225 right,
226 } => Ok(Expr::Infix {
227 operator,
228 left: Box::new(decode_escape(cx, *left)?),
229 right: Box::new(decode_escape(cx, *right)?),
230 }),
231 Expr::Prefix { operator, arg } => Ok(Expr::Prefix {
232 operator,
233 arg: Box::new(decode_escape(cx, *arg)?),
234 }),
235 Expr::Postfix { operator, arg } => Ok(Expr::Postfix {
236 operator,
237 arg: Box::new(decode_escape(cx, *arg)?),
238 }),
239 Expr::List(items) => Ok(Expr::List(
240 items
241 .into_iter()
242 .map(|item| decode_escape(cx, item))
243 .collect::<Result<Vec<_>>>()?,
244 )),
245 Expr::Vector(items) => Ok(Expr::Vector(
246 items
247 .into_iter()
248 .map(|item| decode_escape(cx, item))
249 .collect::<Result<Vec<_>>>()?,
250 )),
251 Expr::Map(entries) => Ok(Expr::Map(
252 entries
253 .into_iter()
254 .map(|(key, value)| Ok((decode_escape(cx, key)?, decode_escape(cx, value)?)))
255 .collect::<Result<Vec<_>>>()?,
256 )),
257 Expr::Set(items) => Ok(Expr::Set(
258 items
259 .into_iter()
260 .map(|item| decode_escape(cx, item))
261 .collect::<Result<Vec<_>>>()?,
262 )),
263 Expr::Block(items) => Ok(Expr::Block(
264 items
265 .into_iter()
266 .map(|item| decode_escape(cx, item))
267 .collect::<Result<Vec<_>>>()?,
268 )),
269 Expr::Quote { mode, expr } => Ok(Expr::Quote { mode, expr }),
270 Expr::Annotated { expr, annotations } => Ok(Expr::Annotated {
271 expr: Box::new(decode_escape(cx, *expr)?),
272 annotations: annotations
273 .into_iter()
274 .map(|(name, value)| Ok((name, decode_escape(cx, value)?)))
275 .collect::<Result<Vec<_>>>()?,
276 }),
277 Expr::Extension { tag, payload } => Ok(Expr::Extension {
278 tag,
279 payload: Box::new(decode_escape(cx, *payload)?),
280 }),
281 other => Ok(other),
282 }
283}