1use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree};
6use sim_codec::{DecodeBudget, Decoder, Input, LocatedDecoder, ReadCx, TreeDecoder};
7use sim_kernel::{
8 Error, Expr, LocatedExpr, LocatedExprTree, QuoteMode, Result, SourceId, Symbol, Value,
9 read_construct_capability, read_eval_capability,
10};
11
12use super::forms::{
13 decode_data_expr, lower_eval_surface, may_be_number_literal, parse_byte_string_literal,
14 parse_logic_var, parse_string_literal, parse_symbol, read_escape_form, read_explicit_quote,
15};
16use super::lex::{
17 lex_lisp_tokens, lex_lisp_tokens_without_trivia, origin_from_lisp_source,
18 strip_lisp_line_comments_preserve_layout,
19};
20use super::tree::LispTreeReader;
21
22pub fn token_stream_type_name() -> &'static str {
25 core::any::type_name::<proc_macro2::TokenStream>()
26}
27
28pub struct LispProcMacroDecoder;
35
36impl Decoder for LispProcMacroDecoder {
37 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
38 let source = input.into_string()?;
39 let mut budget = DecodeBudget::new(cx.limits);
40 budget.check_input_bytes(cx.codec, source.len())?;
41 let tokens = lex_lisp_tokens_without_trivia(cx.codec, &source, &mut budget)?;
42 budget.check_tokens(cx.codec, tokens.len())?;
43 let mut reader = LispTreeReader::new(
44 cx,
45 SourceId("<lisp-memory>".to_owned()),
46 &source,
47 tokens,
48 &mut budget,
49 );
50 let expr = reader.read_one(0)?.expr;
51 if !reader.is_empty() {
52 return Err(Error::CodecError {
53 codec: cx.codec,
54 message: "expected exactly one top-level expression".to_owned(),
55 });
56 }
57 Ok(expr)
58 }
59}
60
61impl LocatedDecoder for LispProcMacroDecoder {
62 fn decode_located(
63 &self,
64 cx: &mut ReadCx<'_>,
65 input: Input,
66 source_id: String,
67 ) -> Result<LocatedExpr> {
68 decode_lisp_located(cx, source_id, input)
69 }
70}
71
72impl TreeDecoder for LispProcMacroDecoder {
73 fn decode_tree(
74 &self,
75 cx: &mut ReadCx<'_>,
76 input: Input,
77 source_id: String,
78 ) -> Result<LocatedExprTree> {
79 decode_lisp_tree(cx, source_id, input)
80 }
81}
82
83pub fn decode_lisp_located(
86 cx: &mut ReadCx<'_>,
87 source_id: impl Into<String>,
88 input: Input,
89) -> Result<LocatedExpr> {
90 let source = input.into_string()?;
91 let mut budget = DecodeBudget::new(cx.limits);
92 budget.check_input_bytes(cx.codec, source.len())?;
93 let source_id = SourceId(source_id.into());
94 cx.cx.sources_mut().intern_text(source_id.clone(), &source);
95 let normalized = strip_lisp_line_comments_preserve_layout(&source);
96 let stream = normalized
97 .parse::<TokenStream>()
98 .map_err(|err| Error::CodecError {
99 codec: cx.codec,
100 message: err.to_string(),
101 })?;
102 let tokens = stream.into_iter().collect::<Vec<_>>();
103 budget.check_tokens(cx.codec, tokens.len())?;
104 let mut reader = LispReader::new(cx, tokens, &mut budget);
105 let expr = reader.read_one(0)?;
106 if !reader.is_empty() {
107 return Err(Error::CodecError {
108 codec: cx.codec,
109 message: "expected exactly one top-level expression".to_owned(),
110 });
111 }
112
113 Ok(LocatedExpr {
114 expr,
115 origin: Some(origin_from_lisp_source(cx.codec, source_id, &source)),
116 })
117}
118
119pub fn decode_lisp_tree(
122 cx: &mut ReadCx<'_>,
123 source_id: impl Into<String>,
124 input: Input,
125) -> Result<LocatedExprTree> {
126 let source = input.into_string()?;
127 let mut budget = DecodeBudget::new(cx.limits);
128 budget.check_input_bytes(cx.codec, source.len())?;
129 let source_id = SourceId(source_id.into());
130 cx.cx.sources_mut().intern_text(source_id.clone(), &source);
131 let tokens = lex_lisp_tokens(cx.codec, &source, &mut budget)?;
132 budget.check_tokens(cx.codec, tokens.len())?;
133 let mut reader = LispTreeReader::new(cx, source_id.clone(), &source, tokens, &mut budget);
134 let mut tree = reader.read_one(0)?;
135 if !reader.is_empty() {
136 return Err(Error::CodecError {
137 codec: cx.codec,
138 message: "expected exactly one top-level expression".to_owned(),
139 });
140 }
141 tree.origin = Some(origin_from_lisp_source(cx.codec, source_id, &source));
142 Ok(tree)
143}
144
145struct LispReader<'a, 'cx, 'b> {
146 cx: &'a mut ReadCx<'cx>,
147 budget: &'b mut sim_codec::DecodeBudget,
148 tokens: Vec<TokenTree>,
149 index: usize,
150}
151
152impl<'a, 'cx, 'b> LispReader<'a, 'cx, 'b> {
153 fn new(
154 cx: &'a mut ReadCx<'cx>,
155 tokens: Vec<TokenTree>,
156 budget: &'b mut sim_codec::DecodeBudget,
157 ) -> Self {
158 Self {
159 cx,
160 budget,
161 tokens,
162 index: 0,
163 }
164 }
165
166 fn is_empty(&self) -> bool {
167 self.index >= self.tokens.len()
168 }
169
170 fn peek(&self) -> Option<&TokenTree> {
171 self.tokens.get(self.index)
172 }
173
174 fn next(&mut self) -> Result<TokenTree> {
175 let token = self
176 .tokens
177 .get(self.index)
178 .cloned()
179 .ok_or(Error::CodecError {
180 codec: self.cx.codec,
181 message: "unexpected end of input".to_owned(),
182 })?;
183 self.index += 1;
184 Ok(token)
185 }
186
187 fn read_one(&mut self, depth: usize) -> Result<Expr> {
188 let token = self.next()?;
189 self.read_token(token, depth)
190 }
191
192 fn read_token(&mut self, token: TokenTree, depth: usize) -> Result<Expr> {
193 match token {
194 TokenTree::Group(group) => self.read_group(group, depth),
195 TokenTree::Literal(literal) => self.read_literal(literal, depth),
196 TokenTree::Punct(punct) if punct.as_char() == '\'' => {
197 self.budget.enter_node(self.cx.codec, depth)?;
198 let expr = self.read_one(depth + 1)?;
199 Ok(Expr::Quote {
200 mode: QuoteMode::Quote,
201 expr: Box::new(expr),
202 })
203 }
204 TokenTree::Punct(punct) if punct.as_char() == '#' => self.read_dispatch(depth),
205 token => self.read_symbolish(token, depth),
206 }
207 }
208
209 fn read_group(&mut self, group: Group, depth: usize) -> Result<Expr> {
210 let inner = group.stream().into_iter().collect::<Vec<_>>();
211 let mut nested = LispReader::new(self.cx, inner, self.budget);
212 let mut items: Vec<Expr> = Vec::new();
213 while !nested.is_empty() {
214 nested
215 .budget
216 .check_collection_len(nested.cx.codec, items.len() + 1)?;
217 items.push(nested.read_one(depth + 1)?);
218 }
219 self.budget.enter_node(self.cx.codec, depth)?;
220
221 match group.delimiter() {
222 Delimiter::Parenthesis => {
223 if let Some(quoted) = read_explicit_quote(&items) {
224 Ok(quoted)
225 } else if let Some(expr) = read_escape_form(&items)? {
226 Ok(expr)
227 } else {
228 Ok(Expr::List(items))
229 }
230 }
231 Delimiter::Bracket => Ok(Expr::Vector(items)),
232 Delimiter::Brace | Delimiter::None => Ok(Expr::Block(items)),
233 }
234 }
235
236 fn read_literal(&mut self, literal: Literal, depth: usize) -> Result<Expr> {
237 self.budget.enter_node(self.cx.codec, depth)?;
238 let raw = literal.to_string();
239 if raw.starts_with('"') {
240 let value = parse_string_literal(self.cx.codec, &raw)?;
241 self.budget.check_string_bytes(self.cx.codec, value.len())?;
242 return Ok(Expr::String(value));
243 }
244 if raw.starts_with("b\"") {
245 let value = parse_byte_string_literal(&raw)?;
246 self.budget.check_blob_bytes(self.cx.codec, value.len())?;
247 return Ok(Expr::Bytes(value));
248 }
249 if raw == "true" {
250 return Ok(Expr::Bool(true));
251 }
252 if raw == "false" {
253 return Ok(Expr::Bool(false));
254 }
255 let mut candidate = raw.clone();
256 if may_be_number_literal(&candidate) {
257 while let Some(next) = self.peek() {
258 if !continues_number_literal(&candidate, next) {
259 break;
260 }
261 let fragment = token_to_symbol_fragment(next);
262 let joined = format!("{candidate}{fragment}");
263 self.next()?;
264 candidate = joined;
265 }
266 }
267 if may_be_number_literal(&candidate)
268 && let Some(number) = self.cx.cx.parse_number_literal(&candidate)?
269 {
270 return Ok(Expr::Number(number));
271 }
272 Ok(Expr::Symbol(Symbol::new(candidate)))
273 }
274
275 fn read_symbolish(&mut self, first: TokenTree, depth: usize) -> Result<Expr> {
276 self.budget.enter_node(self.cx.codec, depth)?;
277 let mut text = token_to_symbol_fragment(&first);
278 while let Some(next) = self.peek() {
279 if !continues_symbol(text.as_str(), next) {
280 break;
281 }
282 text.push_str(&token_to_symbol_fragment(&self.next()?));
283 }
284
285 match text.as_str() {
286 "nil" => Ok(Expr::Nil),
287 "true" => Ok(Expr::Bool(true)),
288 "false" => Ok(Expr::Bool(false)),
289 _ if parse_logic_var(&text).is_some() => Ok(parse_logic_var(&text).unwrap()),
290 _ => Ok(Expr::Symbol(parse_symbol(&text))),
291 }
292 }
293
294 fn read_dispatch(&mut self, depth: usize) -> Result<Expr> {
295 self.budget.enter_node(self.cx.codec, depth)?;
296 let token = self.next()?;
297 match token {
298 TokenTree::Group(group) if group.delimiter() == Delimiter::Parenthesis => {
299 self.read_construct(group, depth + 1)
300 }
301 TokenTree::Ident(ident) if ident == "eval" => {
302 let token = self.next()?;
303 let TokenTree::Group(group) = token else {
304 return Err(self.error("expected #eval(...)"));
305 };
306 self.read_eval(group, depth + 1)
307 }
308 TokenTree::Punct(punct) if punct.as_char() == '.' => {
309 self.cx.read_policy.require(&read_eval_capability())?;
313 let expr = self.read_one(depth + 1)?;
314 self.eval_read_expr(expr)
315 }
316 other => Err(self.error(format!("unknown dispatch token {other}"))),
317 }
318 }
319
320 fn read_construct(&mut self, group: Group, depth: usize) -> Result<Expr> {
321 self.cx.read_policy.require(&read_construct_capability())?;
322
323 let form = self.read_group(group, depth)?;
324 let Expr::List(items) = form else {
325 return Err(self.error("read constructor must be a list"));
326 };
327 let Some((head, tail)) = items.split_first() else {
328 return Err(self.error("empty read constructor"));
329 };
330 let Expr::Symbol(class_symbol) = head else {
331 return Err(self.error("read constructor head must be a class symbol"));
332 };
333
334 let args = tail
335 .iter()
336 .cloned()
337 .map(|expr| self.decode_read_construct_arg(expr))
338 .collect::<Result<Vec<_>>>()?;
339 let value = self.cx.cx.read_construct(class_symbol, args)?;
340 value.object().as_expr(self.cx.cx)
341 }
342
343 fn read_eval(&mut self, group: Group, depth: usize) -> Result<Expr> {
344 self.cx.read_policy.require(&read_eval_capability())?;
345 let inner = group.stream().into_iter().collect::<Vec<_>>();
346 let mut nested = LispReader::new(self.cx, inner, self.budget);
347 let mut items: Vec<Expr> = Vec::new();
348 while !nested.is_empty() {
349 nested
350 .budget
351 .check_collection_len(nested.cx.codec, items.len() + 1)?;
352 items.push(nested.read_one(depth)?);
353 }
354 let expr = match items.as_slice() {
355 [] => return Err(self.error("empty #eval group")),
356 [one] => one.clone(),
357 _ => lower_eval_surface(Expr::List(items)),
358 };
359 self.eval_read_expr(expr)
360 }
361
362 fn eval_read_expr(&mut self, expr: Expr) -> Result<Expr> {
363 let value = self.cx.cx.eval_expr(lower_eval_surface(expr))?;
364 value.object().as_expr(self.cx.cx)
365 }
366
367 fn decode_read_construct_arg(&mut self, expr: Expr) -> Result<Value> {
368 decode_data_expr(self.cx, expr)
369 }
370
371 fn error(&self, message: impl Into<String>) -> Error {
372 Error::CodecError {
373 codec: self.cx.codec,
374 message: message.into(),
375 }
376 }
377}
378
379fn continues_symbol(current: &str, next: &TokenTree) -> bool {
380 match next {
381 TokenTree::Ident(_) => current.ends_with(['/', ':', '?', '!', '-', '+', '.']),
382 TokenTree::Punct(punct) => {
383 matches!(punct.as_char(), '/' | ':' | '?' | '!' | '-' | '+' | '.')
384 }
385 _ => false,
386 }
387}
388
389fn continues_number_literal(current: &str, next: &TokenTree) -> bool {
390 match next {
391 TokenTree::Punct(punct) => {
392 let joined = format!("{}{}", current, punct.as_char());
393 matches!(punct.as_char(), '+' | '-' | '/' | '.') && may_be_number_literal(&joined)
394 }
395 TokenTree::Ident(ident) => {
396 let joined = format!("{current}{ident}");
397 current.chars().any(|ch| ch.is_ascii_digit()) && may_be_number_literal(&joined)
398 }
399 TokenTree::Literal(literal) => {
400 if !current.ends_with(['+', '-', '/', '.']) {
401 return false;
402 }
403 let joined = format!("{current}{literal}");
404 may_be_number_literal(&joined)
405 }
406 TokenTree::Group(_) => false,
407 }
408}
409
410fn token_to_symbol_fragment(token: &TokenTree) -> String {
411 match token {
412 TokenTree::Group(group) => group.to_string(),
413 TokenTree::Ident(ident) => ident.to_string(),
414 TokenTree::Literal(literal) => literal.to_string(),
415 TokenTree::Punct(punct) => punct.as_char().to_string(),
416 }
417}