aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/ast/error.rs2
-rw-r--r--compiler/src/ast/mod.rs21
-rw-r--r--compiler/src/ast/parser.rs2
-rw-r--r--compiler/src/ast/tests.rs747
-rw-r--r--compiler/src/span.rs2
5 files changed, 767 insertions, 7 deletions
diff --git a/compiler/src/ast/error.rs b/compiler/src/ast/error.rs
index 21102ea..c3283f4 100644
--- a/compiler/src/ast/error.rs
+++ b/compiler/src/ast/error.rs
@@ -4,7 +4,7 @@ use std::{
4 rc::Rc, 4 rc::Rc,
5}; 5};
6 6
7#[derive(Debug)] 7#[derive(Clone, Debug, PartialEq, Eq)]
8pub enum Error { 8pub enum Error {
9 // Number 9 // Number
10 InvalidFloatLiteral(ParseFloatError), 10 InvalidFloatLiteral(ParseFloatError),
diff --git a/compiler/src/ast/mod.rs b/compiler/src/ast/mod.rs
index 4587ea7..8e35baf 100644
--- a/compiler/src/ast/mod.rs
+++ b/compiler/src/ast/mod.rs
@@ -7,7 +7,10 @@ use crate::span::Spanned;
7pub use error::Error; 7pub use error::Error;
8pub use parser::Parser; 8pub use parser::Parser;
9 9
10#[derive(Clone, Debug)] 10#[cfg(test)]
11mod tests;
12
13#[derive(Clone, Debug, PartialEq)]
11pub enum Atom { 14pub enum Atom {
12 Float(f64), 15 Float(f64),
13 Integer(i64), 16 Integer(i64),
@@ -17,11 +20,21 @@ pub enum Atom {
17 Nil, 20 Nil,
18} 21}
19 22
20#[derive(Clone, Debug)] 23#[derive(Clone, Debug, PartialEq)]
21pub enum Expr { 24pub enum Expr {
22 Atom(Atom), 25 Atom(Atom),
23 List(Vec<Spanned<Expr>>), 26 List(Vec<Spanned<Expr>>),
24} 27}
25 28
26#[derive(Clone, Debug)] 29#[derive(Clone, Debug, PartialEq)]
27pub struct Program(pub Vec<Spanned<Expr>>); 30pub struct Program(Vec<Spanned<Expr>>);
31
32impl Program {
33 pub fn inner(&self) -> &[Spanned<Expr>] {
34 &self.0
35 }
36
37 pub fn into_inner(self) -> Vec<Spanned<Expr>> {
38 self.0
39 }
40}
diff --git a/compiler/src/ast/parser.rs b/compiler/src/ast/parser.rs
index 8e476ad..83e48b8 100644
--- a/compiler/src/ast/parser.rs
+++ b/compiler/src/ast/parser.rs
@@ -6,7 +6,7 @@ use crate::{
6 span::{Pos, Span, Spanned}, 6 span::{Pos, Span, Spanned},
7}; 7};
8 8
9const MAX_DEPTH: usize = 256; // TODO: make it a compile flag 9pub(super) const MAX_DEPTH: usize = 256; // TODO: make it a compile flag
10 10
11fn parse_number(number: &str) -> Result<Atom, Error> { 11fn parse_number(number: &str) -> Result<Atom, Error> {
12 let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')) 12 let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E'))
diff --git a/compiler/src/ast/tests.rs b/compiler/src/ast/tests.rs
new file mode 100644
index 0000000..708e788
--- /dev/null
+++ b/compiler/src/ast/tests.rs
@@ -0,0 +1,747 @@
1use std::f64;
2use std::fmt::Debug;
3use std::iter::repeat_n;
4use std::rc::Rc;
5
6use self::E::*;
7use super::{Error, Parser, parser::MAX_DEPTH};
8use crate::ast::{Atom, Expr};
9use crate::lexer::Token;
10use crate::lexer::Token::*;
11use crate::span::{Pos, Span, Spanned};
12
13#[derive(Debug, PartialEq)]
14enum E {
15 Flt(f64),
16 Int(i64),
17 Str(&'static str),
18 Sym(&'static str),
19 Bool(bool),
20 Nil,
21 List(Vec<E>),
22}
23
24impl From<Expr> for E {
25 fn from(expr: Expr) -> Self {
26 match expr {
27 Expr::Atom(atom) => match atom {
28 Atom::Float(f) => Flt(f),
29 Atom::Integer(i) => Int(i),
30 Atom::String(s) => Str(Box::leak(s.into())),
31 Atom::Symbol(s) => Sym(Box::leak(s.into())),
32 Atom::Bool(b) => Bool(b),
33 Atom::Nil => Nil,
34 },
35 Expr::List(l) => List(l.into_iter().map(|e| e.inner.into()).collect()),
36 }
37 }
38}
39
40fn dummy_tokens(tokens: Vec<Token<'static>>) -> impl Iterator<Item = Spanned<Token<'static>>> {
41 let span = Span::new(Pos::new(0, 0, 0), Pos::new(0, 0, 0));
42 tokens.into_iter().map(move |t| Spanned::new(t, span))
43}
44
45fn parse(tokens: Vec<Token<'static>>) -> Vec<E> {
46 Parser::new(dummy_tokens(tokens))
47 .parse()
48 .unwrap()
49 .into_inner()
50 .into_iter()
51 .map(|e| e.inner.into())
52 .collect()
53}
54
55#[test]
56fn test_empty() {
57 assert_eq!(parse(vec![]), vec![], "input: empty");
58}
59
60#[test]
61fn test_integers() {
62 let cases = vec![
63 (vec![Number("0")], vec![Int(0)]),
64 (vec![Number("42")], vec![Int(42)]),
65 (vec![Number("-7")], vec![Int(-7)]),
66 (vec![Number("+5")], vec![Int(5)]),
67 (vec![Number("9223372036854775807")], vec![Int(i64::MAX)]),
68 (vec![Number("-9223372036854775808")], vec![Int(i64::MIN)]),
69 ];
70 for (tokens, ast) in cases {
71 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
72 }
73}
74
75#[test]
76fn test_floats() {
77 let cases = vec![
78 (vec![Number("2.71")], vec![Flt(2.71)]),
79 (vec![Number("-2.5")], vec![Flt(-2.5)]),
80 (vec![Number("+0.0")], vec![Flt(0.0)]),
81 (vec![Number(".5")], vec![Flt(0.5)]),
82 (vec![Number("-.5")], vec![Flt(-0.5)]),
83 (vec![Number("+.5")], vec![Flt(0.5)]),
84 (vec![Number("1e10")], vec![Flt(1e10)]),
85 (vec![Number("1E10")], vec![Flt(1e10)]),
86 (vec![Number("1.5e-3")], vec![Flt(1.5e-3)]),
87 (vec![Number("-1.5E+3")], vec![Flt(-1.5e3)]),
88 (vec![Number("inf")], vec![Flt(f64::INFINITY)]),
89 (vec![Number("-inf")], vec![Flt(f64::NEG_INFINITY)]),
90 (vec![Number("1e9999")], vec![Flt(f64::INFINITY)]),
91 (vec![Number("-1e9999")], vec![Flt(f64::NEG_INFINITY)]),
92 ];
93 for (tokens, ast) in cases {
94 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
95 }
96}
97
98#[test]
99fn test_float_nan() {
100 let tokens = vec![Number("nan")];
101 let expr = &parse(tokens.clone())[0];
102 assert!(
103 matches!(expr, Flt(f) if f.is_nan()),
104 "input: {tokens:?}, got: {expr:?}"
105 );
106}
107
108#[test]
109fn test_keywords() {
110 let cases = vec![
111 (vec![Symbol("true")], vec![Bool(true)]),
112 (vec![Symbol("false")], vec![Bool(false)]),
113 (vec![Symbol("nil")], vec![Nil]),
114 (vec![Symbol("inf")], vec![Flt(f64::INFINITY)]),
115 (vec![Symbol("+inf")], vec![Flt(f64::INFINITY)]),
116 (vec![Symbol("-inf")], vec![Flt(f64::NEG_INFINITY)]),
117 (vec![LeftPar, RightPar], vec![Nil]),
118 ];
119 for (tokens, ast) in cases {
120 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
121 }
122}
123
124#[test]
125fn test_keyword_nan() {
126 let tokens = vec![Symbol("nan")];
127 let expr = &parse(tokens.clone())[0];
128 assert!(
129 matches!(expr, Flt(f) if f.is_nan()),
130 "input: {tokens:?}, got: {expr:?}"
131 );
132}
133
134#[test]
135fn test_keywords_case_sensitive() {
136 let cases = vec![
137 (vec![Symbol("True")], vec![Sym("True")]),
138 (vec![Symbol("FALSE")], vec![Sym("FALSE")]),
139 (vec![Symbol("NIL")], vec![Sym("NIL")]),
140 (vec![Symbol("Nil")], vec![Sym("Nil")]),
141 (vec![Symbol("Inf")], vec![Sym("Inf")]),
142 (vec![Symbol("INF")], vec![Sym("INF")]),
143 (vec![Symbol("NaN")], vec![Sym("NaN")]),
144 (vec![Symbol("NAN")], vec![Sym("NAN")]),
145 ];
146 for (tokens, ast) in cases {
147 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
148 }
149}
150
151#[test]
152fn test_symbols() {
153 let cases = vec![
154 (vec![Symbol("foo")], vec![Sym("foo")]),
155 (vec![Symbol("foo-bar")], vec![Sym("foo-bar")]),
156 (vec![Symbol("set!")], vec![Sym("set!")]),
157 (vec![Symbol("empty?")], vec![Sym("empty?")]),
158 (vec![Symbol("+")], vec![Sym("+")]),
159 (vec![Symbol("-")], vec![Sym("-")]),
160 (vec![Symbol("<=")], vec![Sym("<=")]),
161 (vec![Symbol(",")], vec![Sym(",")]),
162 ];
163 for (tokens, ast) in cases {
164 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
165 }
166}
167
168#[test]
169fn test_strings() {
170 let cases = vec![
171 (vec![String("")], vec![Str("")]),
172 (vec![String("hello")], vec![Str("hello")]),
173 (vec![String("(not a list)")], vec![Str("(not a list)")]),
174 (
175 vec![String("; not a comment")],
176 vec![Str("; not a comment")],
177 ),
178 ];
179 for (tokens, ast) in cases {
180 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
181 }
182}
183
184#[test]
185fn test_string_escapes() {
186 let cases = vec![
187 (vec![String(r"a\nb")], vec![Str("a\nb")]),
188 (vec![String(r#"a\"b"#)], vec![Str("a\"b")]),
189 (vec![String("a\\\nb")], vec![Str("ab")]),
190 ];
191 for (tokens, ast) in cases {
192 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
193 }
194}
195
196#[test]
197fn test_lists() {
198 let cases = vec![
199 // (1)
200 (
201 vec![LeftPar, Number("1"), RightPar],
202 vec![List(vec![Int(1)])],
203 ),
204 // (1 2 3)
205 (
206 vec![LeftPar, Number("1"), Number("2"), Number("3"), RightPar],
207 vec![List(vec![Int(1), Int(2), Int(3)])],
208 ),
209 // (()) -> (nil)
210 (
211 vec![LeftPar, LeftPar, RightPar, RightPar],
212 vec![List(vec![Nil])],
213 ),
214 // (a (b c) d)
215 (
216 vec![
217 LeftPar,
218 Symbol("a"),
219 LeftPar,
220 Symbol("b"),
221 Symbol("c"),
222 RightPar,
223 Symbol("d"),
224 RightPar,
225 ],
226 vec![List(vec![
227 Sym("a"),
228 List(vec![Sym("b"), Sym("c")]),
229 Sym("d"),
230 ])],
231 ),
232 // (define x 42)
233 (
234 vec![
235 LeftPar,
236 Symbol("define"),
237 Symbol("x"),
238 Number("42"),
239 RightPar,
240 ],
241 vec![List(vec![Sym("define"), Sym("x"), Int(42)])],
242 ),
243 // (1 2.5 "s" foo true nil)
244 (
245 vec![
246 LeftPar,
247 Number("1"),
248 Number("2.5"),
249 String("s"),
250 Symbol("foo"),
251 Symbol("true"),
252 Symbol("nil"),
253 RightPar,
254 ],
255 vec![List(vec![
256 Int(1),
257 Flt(2.5),
258 Str("s"),
259 Sym("foo"),
260 Bool(true),
261 Nil,
262 ])],
263 ),
264 ];
265 for (tokens, ast) in cases {
266 assert_eq!(parse(tokens.clone()), ast, "input: {tokens:?}");
267 }
268}
269
270fn quote(expr: E) -> E {
271 List(vec![Sym("quote"), expr])
272}
273
274#[test]
275fn test_quote() {
276 let cases = vec![
277 // 'x -> (quote x)
278 (vec![Quote, Symbol("x")], vec![quote(Sym("x"))]),
279 // '42 -> (quote 42)
280 (vec![Quote, Number("42")], vec![quote(Int(42))]),
281 // '() -> (quote nil)
282 (vec![Quote, LeftPar, RightPar], vec![quote(Nil)]),
283 // ''x -> (quote (quote x))
284 (
285 vec![Quote, Quote, Symbol("x")],
286 vec![quote(quote(Sym("x")))],
287 ),
288 // '''x -> (quote (quote (quote x)))
289 (
290 vec![Quote, Quote, Quote, Symbol("x")],
291 vec![quote(quote(quote(Sym("x"))))],
292 ),
293 // '(1 2) -> (quote (1 2))
294 (
295 vec![Quote, LeftPar, Number("1"), Number("2"), RightPar],
296 vec![quote(List(vec![Int(1), Int(2)]))],
297 ),
298 // (list 'a 'b) -> (list (quote a) (quote b))
299 (
300 vec![
301 LeftPar,
302 Symbol("list"),
303 Quote,
304 Symbol("a"),
305 Quote,
306 Symbol("b"),
307 RightPar,
308 ],
309 vec![List(vec![Sym("list"), quote(Sym("a")), quote(Sym("b"))])],
310 ),
311 ];
312 for (tokens, expected) in cases {
313 assert_eq!(parse(tokens.clone()), expected, "input: {tokens:?}");
314 }
315}
316
317#[test]
318fn test_top_level() {
319 let cases = vec![
320 (
321 vec![Number("1"), Number("2"), Number("3")],
322 vec![Int(1), Int(2), Int(3)],
323 ),
324 (
325 vec![
326 LeftPar,
327 Symbol("a"),
328 RightPar,
329 LeftPar,
330 Symbol("b"),
331 RightPar,
332 ],
333 vec![List(vec![Sym("a")]), List(vec![Sym("b")])],
334 ),
335 ];
336 for (tokens, expected) in cases {
337 assert_eq!(parse(tokens.clone()), expected, "input: {tokens:?}");
338 }
339}
340
341fn parse_err(tokens: Vec<Token<'static>>) -> Error {
342 Parser::new(dummy_tokens(tokens)).parse().unwrap_err().inner
343}
344
345#[test]
346fn test_unexpected_right_par() {
347 let cases = vec![
348 vec![RightPar],
349 vec![Number("1"), RightPar],
350 vec![LeftPar, Symbol("a"), RightPar, RightPar],
351 vec![Quote, RightPar],
352 vec![LeftPar, Quote, Quote, RightPar],
353 ];
354 for tokens in cases {
355 assert_eq!(
356 parse_err(tokens.clone()),
357 Error::UnexpectedRightPar,
358 "input: {tokens:?}"
359 );
360 }
361}
362
363#[test]
364fn test_unclosed_left_par() {
365 let cases = vec![
366 vec![LeftPar],
367 vec![LeftPar, Number("1")],
368 vec![LeftPar, LeftPar, Symbol("a"), RightPar],
369 ];
370 for tokens in cases {
371 assert_eq!(
372 parse_err(tokens.clone()),
373 Error::UnclosedLeftPar,
374 "input: {tokens:?}"
375 );
376 }
377}
378
379#[test]
380fn test_unexpected_eof_after_quote() {
381 let cases = vec![
382 vec![Quote],
383 vec![Quote, Quote],
384 vec![Symbol("a"), Quote],
385 vec![LeftPar, Quote],
386 vec![Quote, LeftPar, Quote],
387 ];
388 for tokens in cases {
389 assert_eq!(
390 parse_err(tokens.clone()),
391 Error::UnexpectedEof,
392 "input: {tokens:?}"
393 );
394 }
395}
396
397#[test]
398fn test_invalid_integer() {
399 let cases = vec![
400 "99999999999999999999",
401 "-99999999999999999999",
402 "+",
403 "-",
404 "",
405 "1_000",
406 "0x10",
407 "1 2",
408 "++1",
409 "--1",
410 "1,5",
411 "10,40",
412 ];
413 for number in cases {
414 let tokens = vec![Number(number)];
415 let error = number.parse::<i64>().unwrap_err();
416 assert_eq!(
417 parse_err(tokens.clone()),
418 Error::InvalidIntegerLiteral(error),
419 "input: {tokens:?}",
420 );
421 }
422}
423
424#[test]
425fn test_invalid_float() {
426 let cases = vec![
427 "12somE0txt",
428 "12som.0txt",
429 "12.3txt",
430 "1.2.3",
431 "1.2.3.4",
432 ".",
433 "+.",
434 "-.",
435 "1e",
436 "1e+",
437 "1e-",
438 "1.e",
439 ".e5",
440 "1ee5",
441 "1e1.5",
442 ];
443 for number in cases {
444 let tokens = vec![Number(number)];
445 let error = number.parse::<f64>().unwrap_err();
446 assert_eq!(
447 parse_err(tokens.clone()),
448 Error::InvalidFloatLiteral(error),
449 "input: {tokens:?}",
450 );
451 }
452}
453
454#[test]
455fn test_unclosed_string() {
456 let cases = vec![
457 (
458 vec![UnclosedString("oops")],
459 Error::UnclosedString("oops".into()),
460 ),
461 (
462 vec![String("oops\\")],
463 Error::UnclosedString("oops\\".into()),
464 ),
465 ];
466 for (tokens, error) in cases {
467 assert_eq!(parse_err(tokens.clone()), error, "input: {tokens:?}");
468 }
469}
470
471#[test]
472fn test_unexpected_escape_char_propagates() {
473 let tokens = vec![String(r"a\q")];
474 assert_eq!(
475 parse_err(tokens.clone()),
476 Error::UnexpectedEscapeChar('q'),
477 "input: {tokens:?}"
478 );
479}
480
481#[test]
482fn test_recursion_limit() {
483 let mut tokens = Vec::with_capacity(MAX_DEPTH * 2);
484 tokens.extend(repeat_n(LeftPar, MAX_DEPTH));
485 tokens.extend(repeat_n(RightPar, MAX_DEPTH));
486 assert_eq!(
487 parse_err(tokens),
488 Error::RecursionLimit,
489 "input: {} LeftPar then {} RightPar",
490 MAX_DEPTH,
491 MAX_DEPTH,
492 );
493}
494
495fn p(line: usize, column: usize, offset: usize) -> Pos {
496 Pos::new(line, column, offset)
497}
498
499fn sp(s: (usize, usize, usize), e: (usize, usize, usize)) -> Span {
500 Span::new(p(s.0, s.1, s.2), p(e.0, e.1, e.2))
501}
502
503fn tsp(t: Token<'static>, span: Span) -> Spanned<Token<'static>> {
504 Spanned::new(t, span)
505}
506
507fn parse_sp(tokens: Vec<Spanned<Token<'static>>>) -> Vec<Spanned<Expr>> {
508 Parser::new(tokens.into_iter())
509 .parse()
510 .unwrap()
511 .into_inner()
512 .into_iter()
513 .collect()
514}
515
516fn parse_sp_err(tokens: Vec<Spanned<Token<'static>>>) -> Spanned<Error> {
517 Parser::new(tokens.into_iter()).parse().unwrap_err()
518}
519
520#[test]
521fn test_span_atom() {
522 let s = sp((1, 0, 0), (1, 2, 2));
523 let tokens = vec![tsp(Number("42"), s)];
524 let prog = parse_sp(tokens.clone());
525
526 assert_eq!(prog[0].span, s, "input: {tokens:?}");
527}
528
529#[test]
530fn test_span_list_covers_parens() {
531 // (foo)
532 let lp = sp((1, 0, 0), (1, 1, 1));
533 let foo = sp((1, 1, 1), (1, 4, 4));
534 let rp = sp((1, 4, 4), (1, 5, 5));
535 let tokens = vec![tsp(LeftPar, lp), tsp(Symbol("foo"), foo), tsp(RightPar, rp)];
536 let prog = parse_sp(tokens.clone());
537
538 assert_eq!(prog[0].span, sp((1, 0, 0), (1, 5, 5)), "input: {tokens:?}");
539
540 if let Expr::List(items) = &prog[0].inner {
541 assert_eq!(items[0].span, foo, "input: {tokens:?}");
542 } else {
543 panic!("expected list, input: {tokens:?}");
544 }
545}
546
547#[test]
548fn test_span_empty_list_covers_parens() {
549 // () -> Atom::Nil, span [0..2]
550 let lp = sp((1, 0, 0), (1, 1, 1));
551 let rp = sp((1, 1, 1), (1, 2, 2));
552 let tokens = vec![tsp(LeftPar, lp), tsp(RightPar, rp)];
553 let prog = parse_sp(tokens.clone());
554
555 assert_eq!(prog[0].inner, Expr::Atom(Atom::Nil), "input: {tokens:?}");
556 assert_eq!(prog[0].span, sp((1, 0, 0), (1, 2, 2)), "input: {tokens:?}");
557}
558
559#[test]
560fn test_span_quote_atom() {
561 // 'x -> outer = [0..2]; Sym(quote) = [0..1]; x = [1..2]
562 let q = sp((1, 0, 0), (1, 1, 1));
563 let x = sp((1, 1, 1), (1, 2, 2));
564 let tokens = vec![tsp(Quote, q), tsp(Symbol("x"), x)];
565 let prog = parse_sp(tokens.clone());
566
567 assert_eq!(prog[0].span, sp((1, 0, 0), (1, 2, 2)), "input: {tokens:?}");
568
569 if let Expr::List(items) = &prog[0].inner {
570 assert_eq!(
571 items[0].inner,
572 Expr::Atom(Atom::Symbol(Rc::from("quote"))),
573 "input: {tokens:?}",
574 );
575 assert_eq!(items[0].span, q, "input: {tokens:?}");
576 assert_eq!(items[1].span, x, "input: {tokens:?}");
577 } else {
578 panic!("expected list, input: {tokens:?}");
579 }
580}
581
582#[test]
583fn test_span_quote_of_list() {
584 // '(quote x)
585 // 0 1 2..7 8 9 10
586 let q = sp((1, 0, 0), (1, 1, 1));
587 let lp = sp((1, 1, 1), (1, 2, 2));
588 let q_sym = sp((1, 2, 2), (1, 7, 7));
589 let x_sym = sp((1, 8, 8), (1, 9, 9));
590 let rp = sp((1, 9, 9), (1, 10, 10));
591
592 let tokens = vec![
593 tsp(Quote, q),
594 tsp(LeftPar, lp),
595 tsp(Symbol("quote"), q_sym),
596 tsp(Symbol("x"), x_sym),
597 tsp(RightPar, rp),
598 ];
599 let prog = parse_sp(tokens.clone());
600
601 let outer = &prog[0]; // (quote (quote x))
602 assert_eq!(outer.span, sp((1, 0, 0), (1, 10, 10)), "input: {tokens:?}");
603
604 if let Expr::List(items) = &outer.inner {
605 assert_eq!(items[0].span, q, "input: {tokens:?}");
606 assert_eq!(
607 items[1].span,
608 sp((1, 1, 1), (1, 10, 10)),
609 "input: {tokens:?}"
610 );
611 } else {
612 panic!("expected list, input: {tokens:?}");
613 }
614}
615
616#[test]
617fn test_error_span_unexpected_right_par() {
618 let s = sp((1, 5, 5), (1, 6, 6));
619 let tokens = vec![tsp(RightPar, s)];
620 let err = parse_sp_err(tokens.clone());
621
622 assert_eq!(err.inner, Error::UnexpectedRightPar, "input: {tokens:?}");
623 assert_eq!(err.span, s, "input: {tokens:?}");
624}
625
626#[test]
627fn test_error_span_invalid_integer() {
628 let s = sp((1, 0, 0), (1, 20, 20));
629 let tokens = vec![tsp(Number("99999999999999999999"), s)];
630 let err = parse_sp_err(tokens.clone());
631
632 assert_eq!(err.span, s, "input: {tokens:?}");
633}
634
635#[test]
636fn test_error_span_invalid_float() {
637 let s = sp((1, 0, 0), (1, 5, 5));
638 let tokens = vec![tsp(Number("1.2.3"), s)];
639 let err = parse_sp_err(tokens.clone());
640
641 assert!(
642 matches!(err.inner, Error::InvalidFloatLiteral(_)),
643 "input: {tokens:?}, got: {:?}",
644 err.inner,
645 );
646 assert_eq!(err.span, s, "input: {tokens:?}");
647}
648
649#[test]
650fn test_error_span_unclosed_left_par() {
651 let lp = sp((1, 0, 0), (1, 1, 1));
652 let tokens = vec![tsp(LeftPar, lp)];
653 let err = parse_sp_err(tokens.clone());
654
655 assert_eq!(err.inner, Error::UnclosedLeftPar, "input: {tokens:?}");
656 assert_eq!(err.span, lp, "input: {tokens:?}");
657}
658
659#[test]
660fn test_error_span_unclosed_left_par_nested() {
661 let outer = sp((1, 0, 0), (1, 1, 1));
662 let inner = sp((1, 2, 2), (1, 3, 3));
663 let tokens = vec![tsp(LeftPar, outer), tsp(LeftPar, inner)];
664 let err = parse_sp_err(tokens.clone());
665
666 assert_eq!(err.inner, Error::UnclosedLeftPar, "input: {tokens:?}");
667 assert_eq!(err.span, inner, "input: {tokens:?}");
668}
669
670#[test]
671fn test_error_span_unexpected_eof_after_quote() {
672 let q = sp((1, 0, 0), (1, 1, 1));
673 let tokens = vec![tsp(Quote, q)];
674 let err = parse_sp_err(tokens.clone());
675
676 assert_eq!(err.inner, Error::UnexpectedEof, "input: {tokens:?}");
677 assert_eq!(err.span, q, "input: {tokens:?}");
678}
679
680#[test]
681fn test_error_span_unclosed_string() {
682 let s = sp((1, 0, 0), (1, 7, 7));
683 let tokens = vec![tsp(UnclosedString("oops"), s)];
684 let err = parse_sp_err(tokens.clone());
685
686 assert_eq!(
687 err.inner,
688 Error::UnclosedString("oops".into()),
689 "input: {tokens:?}",
690 );
691 assert_eq!(err.span, s, "input: {tokens:?}");
692}
693
694#[test]
695fn test_error_span_unexpected_escape_char() {
696 let s = sp((1, 0, 0), (1, 5, 5));
697 let tokens = vec![tsp(String(r"a\q"), s)];
698 let err = parse_sp_err(tokens.clone());
699
700 assert_eq!(
701 err.inner,
702 Error::UnexpectedEscapeChar('q'),
703 "input: {tokens:?}"
704 );
705 assert_eq!(err.span, s, "input: {tokens:?}");
706}
707
708#[test]
709fn test_error_span_deep_nested() {
710 // (a (b (c BAD)))
711 let bad_span = sp((1, 9, 9), (1, 29, 29));
712 let any = sp((1, 0, 0), (1, 1, 1));
713 let tokens = vec![
714 tsp(LeftPar, any),
715 tsp(Symbol("a"), any),
716 tsp(LeftPar, any),
717 tsp(Symbol("b"), any),
718 tsp(LeftPar, any),
719 tsp(Symbol("c"), any),
720 tsp(Number("99999999999999999999"), bad_span),
721 tsp(RightPar, any),
722 tsp(RightPar, any),
723 tsp(RightPar, any),
724 ];
725 let err = parse_sp_err(tokens.clone());
726
727 assert!(
728 matches!(err.inner, Error::InvalidIntegerLiteral(_)),
729 "input: {tokens:?}, got: {:?}",
730 err.inner,
731 );
732 assert_eq!(err.span, bad_span, "input: {tokens:?}");
733}
734
735#[test]
736fn test_error_span_recursion_limit() {
737 let mut tokens = Vec::with_capacity(MAX_DEPTH);
738 for i in 0..MAX_DEPTH {
739 let s = sp((1, i, i), (1, i + 1, i + 1));
740 tokens.push(tsp(LeftPar, s));
741 }
742 let trigger = sp((1, MAX_DEPTH - 1, MAX_DEPTH - 1), (1, MAX_DEPTH, MAX_DEPTH));
743 let err = parse_sp_err(tokens);
744
745 assert_eq!(err.inner, Error::RecursionLimit, "MAX_DEPTH={MAX_DEPTH}");
746 assert_eq!(err.span, trigger, "MAX_DEPTH={MAX_DEPTH}");
747}
diff --git a/compiler/src/span.rs b/compiler/src/span.rs
index 369c58c..8686f4f 100644
--- a/compiler/src/span.rs
+++ b/compiler/src/span.rs
@@ -27,7 +27,7 @@ impl Span {
27 } 27 }
28} 28}
29 29
30#[derive(Clone, Copy, Debug)] 30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub struct Spanned<T> { 31pub struct Spanned<T> {
32 pub inner: T, 32 pub inner: T,
33 pub span: Span, 33 pub span: Span,