aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/lexer/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/lexer/tests.rs')
-rw-r--r--compiler/src/lexer/tests.rs372
1 files changed, 372 insertions, 0 deletions
diff --git a/compiler/src/lexer/tests.rs b/compiler/src/lexer/tests.rs
new file mode 100644
index 0000000..65dd2f2
--- /dev/null
+++ b/compiler/src/lexer/tests.rs
@@ -0,0 +1,372 @@
1use crate::span::Pos;
2
3use super::Token::*;
4use super::*;
5
6fn tokenize<'a>(input: &'a str) -> Vec<Token<'a>> {
7 Lexer::new(input).map(|s| s.into_inner().unwrap()).collect()
8}
9
10#[test]
11fn test_spaces() {
12 let cases = vec![
13 ("", vec![]),
14 (" ", vec![]),
15 ("\n", vec![]),
16 ("\t\n \r\n", vec![]),
17 ];
18 for (code, tokens) in cases {
19 assert_eq!(tokenize(code), tokens);
20 }
21}
22
23#[test]
24fn test_parens() {
25 let cases = vec![
26 ("()", vec![LeftPar, RightPar]),
27 ("( )", vec![LeftPar, RightPar]),
28 ("(())", vec![LeftPar, LeftPar, RightPar, RightPar]),
29 (
30 "((()))",
31 vec![LeftPar, LeftPar, LeftPar, RightPar, RightPar, RightPar],
32 ),
33 (")(", vec![RightPar, LeftPar]),
34 ];
35 for (code, tokens) in cases {
36 assert_eq!(tokenize(code), tokens);
37 }
38}
39
40#[test]
41fn test_quote() {
42 let cases = vec![
43 ("'", vec![Quote]),
44 ("'a", vec![Quote, Symbol("a")]),
45 ("''a", vec![Quote, Quote, Symbol("a")]),
46 ("'()", vec![Quote, LeftPar, RightPar]),
47 (
48 "'(1 2)",
49 vec![Quote, LeftPar, Number("1"), Number("2"), RightPar],
50 ),
51 ("(' )", vec![LeftPar, Quote, RightPar]),
52 ];
53 for (code, tokens) in cases {
54 assert_eq!(tokenize(code), tokens);
55 }
56}
57
58#[test]
59fn test_numbers() {
60 let cases = vec![
61 ("0", vec![Number("0")]),
62 ("42", vec![Number("42")]),
63 ("3.14", vec![Number("3.14")]),
64 ("-7", vec![Number("-7")]),
65 ("+5", vec![Number("+5")]),
66 ("-0.5", vec![Number("-0.5")]),
67 ("1e10", vec![Number("1e10")]),
68 ("1.5e-3", vec![Number("1.5e-3")]),
69 (".5", vec![Number(".5")]),
70 ];
71 for (code, tokens) in cases {
72 assert_eq!(tokenize(code), tokens);
73 }
74}
75
76#[test]
77fn test_strings() {
78 let cases = vec![
79 (r#""""#, vec![String("")]),
80 (r#""hello""#, vec![String("hello")]),
81 (r#""hello world""#, vec![String("hello world")]),
82 (r#""(not a list)""#, vec![String("(not a list)")]),
83 (r#""'not a quote""#, vec![String("'not a quote")]),
84 (r#""; not a comment""#, vec![String("; not a comment")]),
85 (r#"" spaces ""#, vec![String(" spaces ")]),
86 ];
87 for (code, tokens) in cases {
88 assert_eq!(tokenize(code), tokens);
89 }
90}
91
92#[test]
93fn test_string_escapes() {
94 let cases = vec![
95 (r#""line\nbreak""#, vec![String(r"line\nbreak")]),
96 (r#""with \"quotes\"""#, vec![String(r#"with \"quotes\""#)]),
97 (r#""\\""#, vec![String(r"\\")]),
98 ("\"single\\\nline\"", vec![String("single\\\nline")]),
99 ];
100 for (code, tokens) in cases {
101 assert_eq!(tokenize(code), tokens);
102 }
103}
104
105#[test]
106fn test_symbols() {
107 let cases = vec![
108 ("foo", vec![Symbol("foo")]),
109 ("foo-bar", vec![Symbol("foo-bar")]),
110 ("foo!", vec![Symbol("foo!")]),
111 ("empty?", vec![Symbol("empty?")]),
112 ("set!", vec![Symbol("set!")]),
113 ("->", vec![Symbol("->")]),
114 ("+", vec![Symbol("+")]),
115 ("-", vec![Symbol("-")]),
116 ("*", vec![Symbol("*")]),
117 ("/", vec![Symbol("/")]),
118 ("=", vec![Symbol("=")]),
119 ("<=", vec![Symbol("<=")]),
120 (">=", vec![Symbol(">=")]),
121 ("a1b2", vec![Symbol("a1b2")]),
122 ("x", vec![Symbol("x")]),
123 ];
124 for (code, tokens) in cases {
125 assert_eq!(tokenize(code), tokens);
126 }
127}
128
129#[test]
130fn test_ambiguous() {
131 let cases = vec![
132 ("-x", vec![Symbol("-x")]),
133 ("+foo", vec![Symbol("+foo")]),
134 ("...", vec![Symbol("...")]),
135 (".foo", vec![Symbol(".foo")]),
136 ];
137 for (code, tokens) in cases {
138 assert_eq!(tokenize(code), tokens);
139 }
140}
141
142#[test]
143fn test_no_separators() {
144 let cases = vec![
145 ("(foo)", vec![LeftPar, Symbol("foo"), RightPar]),
146 ("(1)", vec![LeftPar, Number("1"), RightPar]),
147 ("(a)b", vec![LeftPar, Symbol("a"), RightPar, Symbol("b")]),
148 ("'(a)", vec![Quote, LeftPar, Symbol("a"), RightPar]),
149 (r#"("s")"#, vec![LeftPar, String("s"), RightPar]),
150 ];
151 for (code, tokens) in cases {
152 assert_eq!(tokenize(code), tokens);
153 }
154}
155
156#[test]
157fn test_whitespace_separators() {
158 let cases = vec![
159 (
160 "(\n foo\n bar\n)",
161 vec![LeftPar, Symbol("foo"), Symbol("bar"), RightPar],
162 ),
163 (
164 "(\tfoo\tbar\t)",
165 vec![LeftPar, Symbol("foo"), Symbol("bar"), RightPar],
166 ),
167 ];
168 for (code, tokens) in cases {
169 assert_eq!(tokenize(code), tokens);
170 }
171}
172
173#[test]
174fn test_expressions() {
175 let cases = vec![
176 (
177 "(define x 42)",
178 vec![
179 LeftPar,
180 Symbol("define"),
181 Symbol("x"),
182 Number("42"),
183 RightPar,
184 ],
185 ),
186 (
187 "(+ 1 2)",
188 vec![LeftPar, Symbol("+"), Number("1"), Number("2"), RightPar],
189 ),
190 (
191 "(if (= x 0) 'zero 'nonzero)",
192 vec![
193 LeftPar,
194 Symbol("if"),
195 LeftPar,
196 Symbol("="),
197 Symbol("x"),
198 Number("0"),
199 RightPar,
200 Quote,
201 Symbol("zero"),
202 Quote,
203 Symbol("nonzero"),
204 RightPar,
205 ],
206 ),
207 (
208 r#"(print "hello, world")"#,
209 vec![LeftPar, Symbol("print"), String("hello, world"), RightPar],
210 ),
211 (
212 "(lambda (x) (* x x))",
213 vec![
214 LeftPar,
215 Symbol("lambda"),
216 LeftPar,
217 Symbol("x"),
218 RightPar,
219 LeftPar,
220 Symbol("*"),
221 Symbol("x"),
222 Symbol("x"),
223 RightPar,
224 RightPar,
225 ],
226 ),
227 (
228 "'(1 2 3)",
229 vec![
230 Quote,
231 LeftPar,
232 Number("1"),
233 Number("2"),
234 Number("3"),
235 RightPar,
236 ],
237 ),
238 ];
239 for (code, tokens) in cases {
240 assert_eq!(tokenize(code), tokens);
241 }
242}
243
244#[test]
245fn test_comments() {
246 let cases = vec![
247 (";", vec![]),
248 (";\n", vec![]),
249 ("; comment", vec![]),
250 ("; comment\n", vec![]),
251 ("; comment\n42", vec![Number("42")]),
252 ("42 ; comment", vec![Number("42")]),
253 ("42; comment", vec![Number("42")]),
254 (
255 "(+ 1 2) ; calc\n(- 3 4)",
256 vec![
257 LeftPar,
258 Symbol("+"),
259 Number("1"),
260 Number("2"),
261 RightPar,
262 LeftPar,
263 Symbol("-"),
264 Number("3"),
265 Number("4"),
266 RightPar,
267 ],
268 ),
269 ];
270 for (code, tokens) in cases {
271 assert_eq!(tokenize(code), tokens);
272 }
273}
274
275fn first_error(input: &str) -> Error {
276 Lexer::new(input)
277 .find_map(|s| s.into_inner().err())
278 .expect("error expected")
279}
280
281#[test]
282fn test_unclosed_string_at_eof() {
283 assert_eq!(first_error(r#""abc"#), Error::UnclosedString);
284 assert_eq!(first_error(r#"""#), Error::UnclosedString);
285}
286
287#[test]
288fn test_unclosed_string_with_trailing_escape() {
289 assert_eq!(first_error("\"abc\\"), Error::UnclosedString);
290}
291
292#[test]
293fn test_unclosed_string_with_newline() {
294 assert_eq!(first_error("\"abc\ndef\""), Error::UnclosedString);
295}
296
297#[test]
298fn test_lexer_stops_after_string_error() {
299 let mut lex = Lexer::new(r#""abc"#);
300 assert!(lex.next().unwrap().into_inner().is_err());
301 assert!(lex.next().is_none());
302}
303
304fn spans(input: &str) -> Vec<(Pos, Pos)> {
305 Lexer::new(input).map(|s| (s.start(), s.end())).collect()
306}
307
308#[test]
309fn test_span_single_char() {
310 let s = spans("(");
311 assert_eq!(s, vec![(Pos::new(1, 0, 0), Pos::new(1, 1, 1))]);
312}
313
314#[test]
315fn test_span_after_leading_whitespace() {
316 let s = spans(" (");
317 assert_eq!(s, vec![(Pos::new(1, 3, 3), Pos::new(1, 4, 4))]);
318}
319
320#[test]
321fn test_span_after_newline() {
322 let s = spans("\n(");
323 assert_eq!(s, vec![(Pos::new(2, 0, 1), Pos::new(2, 1, 2))]);
324}
325
326#[test]
327fn test_span_multi_char_() {
328 let s = spans("foo");
329 assert_eq!(s, vec![(Pos::new(1, 0, 0), Pos::new(1, 3, 3))]);
330}
331
332#[test]
333fn test_span_string() {
334 let s = spans(r#""hi""#);
335 assert_eq!(s, vec![(Pos::new(1, 0, 0), Pos::new(1, 4, 4))]);
336}
337
338#[test]
339fn test_span_sequence() {
340 // (foo 42)
341 // 012345678
342 let s = spans("(foo 42)");
343 assert_eq!(
344 s,
345 vec![
346 (Pos::new(1, 0, 0), Pos::new(1, 1, 1)), // (
347 (Pos::new(1, 1, 1), Pos::new(1, 4, 4)), // foo
348 (Pos::new(1, 5, 5), Pos::new(1, 7, 7)), // 42
349 (Pos::new(1, 7, 7), Pos::new(1, 8, 8)), // )
350 ],
351 );
352}
353
354#[test]
355fn test_span_lines() {
356 let s = spans("foo\nbar");
357 assert_eq!(
358 s,
359 vec![
360 (Pos::new(1, 0, 0), Pos::new(1, 3, 3)),
361 (Pos::new(2, 0, 4), Pos::new(2, 3, 7)),
362 ],
363 );
364}
365
366#[test]
367fn test_span_after_comment() {
368 // ; cm\nfoo
369 // 01234 5678
370 let s = spans("; cm\nfoo");
371 assert_eq!(s, vec![(Pos::new(2, 0, 5), Pos::new(2, 3, 8))]);
372}