1use thiserror::Error;
5
6use crate::lexer::{LexError, Token, TokenKind, tokenize};
7use crate::node::{Node, NodeKind};
8use crate::span::Span;
9use crate::trivia::{Trivia, TriviaKind};
10
11#[derive(Debug, Error)]
12pub enum ParseError {
13 #[error("lexer: {0}")]
14 Lex(#[from] LexError),
15 #[error("unexpected token {kind:?} at {span}")]
16 Unexpected { kind: TokenKind, span: Span },
17 #[error("unexpected end of input")]
18 Eof,
19 #[error("unmatched ')' at {0}")]
20 UnmatchedClose(Span),
21 #[error("reader macro ({0}) without a following form at {1}")]
22 DanglingReader(&'static str, Span),
23}
24
25pub fn parse(src: &str) -> Result<Vec<Node>, ParseError> {
26 let tokens = tokenize(src)?;
27 let mut p = Parser {
28 tokens: &tokens,
29 pos: 0,
30 };
31 let mut out: Vec<Node> = Vec::new();
32 loop {
33 let mut leading = p.consume_trivia();
34
35 if let Some(prev_end) = out.last().map(|n: &Node| n.span.end as usize) {
48 let own_line = leading
49 .iter()
50 .position(|t| {
51 let start = t.span.start as usize;
52 start >= prev_end && src[prev_end..start].contains('\n')
53 })
54 .unwrap_or(leading.len());
55 if own_line > 0 {
56 let same_line: Vec<_> = leading.drain(..own_line).collect();
57 if let Some(last) = out.last_mut() {
58 last.after.extend(same_line);
59 }
60 }
61 }
62
63 if p.peek().is_none() {
64 if let Some(last) = out.last_mut() {
69 last.after.extend(leading);
70 }
71 break;
72 }
73 let mut node = p.node()?;
74 if node.leading.is_empty() {
75 node.leading = leading;
76 } else {
77 let mut combined = leading;
79 combined.extend(node.leading.drain(..));
80 node.leading = combined;
81 }
82 out.push(node);
83 }
84 Ok(out)
85}
86
87struct Parser<'a> {
88 tokens: &'a [Token],
89 pos: usize,
90}
91
92impl<'a> Parser<'a> {
93 fn peek(&self) -> Option<&'a Token> {
94 self.tokens.get(self.pos)
95 }
96
97 fn bump(&mut self) -> Option<&'a Token> {
98 let t = self.tokens.get(self.pos)?;
99 self.pos += 1;
100 Some(t)
101 }
102
103 fn consume_trivia(&mut self) -> Vec<Trivia> {
105 let mut out = Vec::new();
106 while let Some(tok) = self.peek() {
107 match &tok.kind {
108 TokenKind::Shebang(s) => {
109 out.push(Trivia {
110 kind: TriviaKind::Shebang(s.clone()),
111 span: tok.span,
112 });
113 self.pos += 1;
114 }
115 TokenKind::LineComment(s) => {
116 out.push(Trivia {
117 kind: TriviaKind::LineComment(s.clone()),
118 span: tok.span,
119 });
120 self.pos += 1;
121 }
122 TokenKind::Newlines(n) if *n >= 2 => {
123 out.push(Trivia {
124 kind: TriviaKind::BlankLine,
125 span: tok.span,
126 });
127 self.pos += 1;
128 }
129 TokenKind::Newlines(_) | TokenKind::Whitespace => {
130 self.pos += 1;
131 }
132 _ => break,
133 }
134 }
135 out
136 }
137
138 fn node(&mut self) -> Result<Node, ParseError> {
139 let tok = self.peek().ok_or(ParseError::Eof)?;
140 let span = tok.span;
141 match &tok.kind {
142 TokenKind::LParen => self.sequence(&TokenKind::RParen, NodeKind::List),
143 TokenKind::LBrace => self.sequence(&TokenKind::RBrace, NodeKind::Map),
144 TokenKind::LBracket => self.sequence(&TokenKind::RBracket, NodeKind::Vector),
145 TokenKind::RParen | TokenKind::RBrace | TokenKind::RBracket => {
146 Err(ParseError::UnmatchedClose(span))
147 }
148 TokenKind::Quote => self.reader_macro("quote", |n| NodeKind::Quote(Box::new(n))),
149 TokenKind::Quasiquote => {
150 self.reader_macro("quasiquote", |n| NodeKind::Quasiquote(Box::new(n)))
151 }
152 TokenKind::Unquote => self.reader_macro("unquote", |n| NodeKind::Unquote(Box::new(n))),
153 TokenKind::UnquoteSplice => {
154 self.reader_macro("unquote-splicing", |n| NodeKind::UnquoteSplice(Box::new(n)))
155 }
156 TokenKind::Str(s) => {
157 let s = s.clone();
158 self.pos += 1;
159 Ok(Node::new(NodeKind::Str(s), span))
160 }
161 TokenKind::Int(i) => {
162 let i = *i;
163 self.pos += 1;
164 Ok(Node::new(NodeKind::Int(i), span))
165 }
166 TokenKind::Float(f) => {
167 let f = *f;
168 self.pos += 1;
169 Ok(Node::new(NodeKind::Float(f), span))
170 }
171 TokenKind::Bool(b) => {
172 let b = *b;
173 self.pos += 1;
174 Ok(Node::new(NodeKind::Bool(b), span))
175 }
176 TokenKind::Nil => {
177 self.pos += 1;
178 Ok(Node::new(NodeKind::Nil, span))
179 }
180 TokenKind::Symbol(s) => {
181 let s = s.clone();
182 self.pos += 1;
183 Ok(Node::new(NodeKind::Symbol(s), span))
184 }
185 TokenKind::Keyword(s) => {
186 let s = s.clone();
187 self.pos += 1;
188 Ok(Node::new(NodeKind::Keyword(s), span))
189 }
190 kind => Err(ParseError::Unexpected {
191 kind: kind.clone(),
192 span,
193 }),
194 }
195 }
196
197 fn sequence(
204 &mut self,
205 close_kind: &TokenKind,
206 wrap: fn(Vec<Node>) -> NodeKind,
207 ) -> Result<Node, ParseError> {
208 let open = self.bump().expect("opening delimiter").span;
209 let mut items = Vec::new();
210 loop {
211 let leading = self.consume_trivia();
212 let next = self.peek();
213 match next {
214 None => return Err(ParseError::Eof),
215 Some(tok) if tok.kind == *close_kind => {
216 let close = self.bump().expect("closing delimiter").span;
217 let span = open.union(close);
218 let mut node = Node::new(wrap(items), span);
219 node.leading = Vec::new();
221 node.trailing = leading;
228 return Ok(node);
229 }
230 Some(_) => {
231 let mut child = self.node()?;
232 if child.leading.is_empty() {
233 child.leading = leading;
234 }
235 items.push(child);
236 }
237 }
238 }
239 }
240
241 fn reader_macro(
242 &mut self,
243 name: &'static str,
244 wrap: impl FnOnce(Node) -> NodeKind,
245 ) -> Result<Node, ParseError> {
246 let head = self.bump().expect("reader macro token").span;
247 self.consume_trivia();
248 let inner = self.peek().ok_or(ParseError::DanglingReader(name, head))?;
249 let _ = inner;
250 let target = self.node()?;
251 let span = head.union(target.span);
252 Ok(Node::new(wrap(target), span))
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn parse_atom() {
262 let nodes = parse("42").unwrap();
263 assert_eq!(nodes.len(), 1);
264 assert!(matches!(nodes[0].kind, NodeKind::Int(42)));
265 assert_eq!(nodes[0].span, Span::new(0, 2));
266 }
267
268 #[test]
269 fn parse_list() {
270 let nodes = parse("(a b c)").unwrap();
271 assert_eq!(nodes.len(), 1);
272 let Some(items) = nodes[0].kind.as_list() else {
273 panic!("expected list");
274 };
275 assert_eq!(items.len(), 3);
276 assert!(matches!(items[0].kind, NodeKind::Symbol(ref s) if s == "a"));
277 }
278
279 #[test]
283 fn parse_map_and_vector() {
284 let nodes =
285 parse(r#"(defcaixa demo :package { :name "d" } :workflows [ :a :b ])"#).unwrap();
286 let Some(items) = nodes[0].kind.as_list() else {
287 panic!("expected list")
288 };
289 assert_eq!(items.len(), 6, "got {items:#?}");
292
293 let NodeKind::Map(m) = &items[3].kind else {
294 panic!("expected map, got {:?}", items[3].kind)
295 };
296 assert_eq!(m.len(), 2);
297 assert!(matches!(&m[0].kind, NodeKind::Keyword(k) if k == "name"));
298
299 let NodeKind::Vector(v) = &items[5].kind else {
300 panic!("expected vector, got {:?}", items[5].kind)
301 };
302 assert_eq!(v.len(), 2);
303 assert!(matches!(&v[0].kind, NodeKind::Keyword(k) if k == "a"));
304 }
305
306 #[test]
309 fn delimiters_terminate_atoms_without_whitespace() {
310 let nodes = parse(r"{:a 1}").unwrap();
311 let NodeKind::Map(m) = &nodes[0].kind else {
312 panic!("expected map, got {:?}", nodes[0].kind)
313 };
314 assert_eq!(m.len(), 2);
315 assert!(matches!(&m[0].kind, NodeKind::Keyword(k) if k == "a"));
316 assert!(matches!(m[1].kind, NodeKind::Int(1)));
317
318 let nodes = parse(r"[a b]").unwrap();
319 let NodeKind::Vector(v) = &nodes[0].kind else {
320 panic!("expected vector")
321 };
322 assert_eq!(v.len(), 2);
323 assert!(matches!(&v[1].kind, NodeKind::Symbol(s) if s == "b"));
324 }
325
326 #[test]
327 fn unmatched_closing_delimiters_are_rejected() {
328 for src in ["}", "]", ")"] {
329 assert!(
330 matches!(parse(src), Err(ParseError::UnmatchedClose(_))),
331 "{src:?} must be an unmatched-close error"
332 );
333 }
334 for src in ["{", "[", "("] {
335 assert!(
336 matches!(parse(src), Err(ParseError::Eof)),
337 "{src:?} must be an EOF error"
338 );
339 }
340 }
341
342 #[test]
343 fn parse_kwargs() {
344 let nodes = parse(r#"(defcaixa :nome "demo" :versao "0.1.0")"#).unwrap();
345 assert_eq!(nodes[0].head_symbol(), Some("defcaixa"));
346 assert!(matches!(
347 nodes[0].kwarg("nome").map(|n| &n.kind),
348 Some(NodeKind::Str(s)) if s == "demo"
349 ));
350 }
351
352 #[test]
353 fn parse_nested_with_comments() {
354 let src = r#"
355;; leading doc
356(defcaixa
357 :nome "demo"
358 ;; inline note
359 :versao "0.1.0")
360"#;
361 let nodes = parse(src).unwrap();
362 assert_eq!(nodes.len(), 1);
363 assert!(!nodes[0].leading.is_empty());
364 }
366
367 #[test]
368 fn parse_reader_macros() {
369 let nodes = parse("`(a ,b ,@cs)").unwrap();
370 let NodeKind::Quasiquote(inner) = &nodes[0].kind else {
371 panic!("expected quasiquote");
372 };
373 let Some(items) = inner.kind.as_list() else {
374 panic!("expected list inside quasiquote");
375 };
376 assert_eq!(items.len(), 3);
377 assert!(matches!(items[1].kind, NodeKind::Unquote(_)));
378 assert!(matches!(items[2].kind, NodeKind::UnquoteSplice(_)));
379 }
380
381 #[test]
382 fn to_tatara_sexp_equivalence() {
383 use tatara_lisp::{Atom, Sexp};
384 let src = r#"(defcaixa :nome "demo" :kind Biblioteca)"#;
385 let nodes = parse(src).unwrap();
386 let lowered = nodes[0].to_tatara_sexp();
387 match lowered {
388 Sexp::List(items) => {
389 assert_eq!(items.len(), 5);
390 assert!(matches!(items[0], Sexp::Atom(Atom::Symbol(ref s)) if s == "defcaixa"));
391 assert!(matches!(items[1], Sexp::Atom(Atom::Keyword(ref s)) if s == "nome"));
392 assert!(matches!(items[2], Sexp::Atom(Atom::Str(ref s)) if s == "demo"));
393 assert!(matches!(items[3], Sexp::Atom(Atom::Keyword(ref s)) if s == "kind"));
394 assert!(matches!(items[4], Sexp::Atom(Atom::Symbol(ref s)) if s == "Biblioteca"));
395 }
396 other => panic!("expected List, got {other:?}"),
397 }
398 }
399}