1use crate::core::{
3 env::{intern, sym},
4 gc::Context,
5 object::{Object, Symbol},
6};
7use crate::fns;
8use num_bigint::BigInt;
9use rune_core::macros::list;
10use std::str;
11use std::{fmt, iter::Peekable, str::CharIndices};
12use std::{fmt::Display, str::FromStr};
13
14type Result<T> = std::result::Result<T, Error>;
15
16#[derive(PartialEq, Debug, Copy, Clone)]
18pub(crate) enum Error {
19 MissingCloseParen(usize),
20 MissingCloseBracket(usize),
21 MissingStringDel(usize),
22 MissingQuotedItem(usize),
23 ExtraItemInCdr(usize),
24 ExtraCloseParen(usize),
25 ExtraCloseBracket(usize),
26 UnexpectedChar(char, usize),
27 UnknownMacroCharacter(char, usize),
28 ParseInt(u8, usize),
29 MalformedUnicdoe(usize),
30 EmptyStream,
31}
32
33impl Display for Error {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 match self {
36 Error::MissingCloseParen(i) => write!(f, "Missing close paren: at {i}"),
37 Error::MissingCloseBracket(i) => write!(f, "Missing close bracket: at {i}"),
38 Error::MissingStringDel(i) => write!(f, "Missing closing string quote: at {i}"),
39 Error::ExtraCloseParen(i) => write!(f, "Extra Closing paren: at {i}"),
40 Error::ExtraCloseBracket(i) => write!(f, "Extra Closing brace: at {i}"),
41 Error::UnexpectedChar(chr, i) => write!(f, "Unexpected character {chr}: at {i}"),
42 Error::MalformedUnicdoe(i) => write!(f, "Malformed unicode: at {i}"),
43 Error::EmptyStream => write!(f, "Empty Stream"),
44 Error::ExtraItemInCdr(i) => write!(f, "Extra item in cdr: at {i}"),
45 Error::MissingQuotedItem(i) => write!(f, "Missing element after quote: at {i}"),
46 Error::ParseInt(radix, i) => {
47 write!(f, "invalid character for radix {radix}: at {i}")
48 }
49 Error::UnknownMacroCharacter(chr, i) => {
50 write!(f, "Unkown reader macro character {chr}: at {i}")
51 }
52 }
53 }
54}
55
56impl std::error::Error for Error {}
57
58impl Error {
59 fn mut_pos(&mut self) -> Option<&mut usize> {
60 match self {
61 Error::MissingCloseParen(i)
62 | Error::MissingCloseBracket(i)
63 | Error::MissingStringDel(i)
64 | Error::UnexpectedChar(_, i)
65 | Error::MalformedUnicdoe(i)
66 | Error::ExtraItemInCdr(i)
67 | Error::ExtraCloseParen(i)
68 | Error::ExtraCloseBracket(i)
69 | Error::MissingQuotedItem(i)
70 | Error::UnknownMacroCharacter(_, i)
71 | Error::ParseInt(_, i) => Some(i),
72 Error::EmptyStream => None,
73 }
74 }
75
76 pub(crate) fn update_pos(&mut self, offset: usize) {
77 if let Some(pos) = self.mut_pos() {
78 *pos += offset;
79 }
80 }
81}
82
83#[derive(PartialEq, Debug, Copy, Clone)]
84enum Token<'a> {
85 OpenParen(usize),
86 CloseParen(usize),
87 OpenBracket(usize),
88 CloseBracket(usize),
89 Quote(usize),
90 Backquote(usize),
91 Unquote(usize),
92 Splice(usize),
93 Sharp(usize),
94 QuestionMark(usize, char),
95 Ident(&'a str),
96 String(&'a str),
97}
98
99impl Display for Token<'_> {
100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 match self {
102 Token::OpenParen(_) => write!(f, "("),
103 Token::CloseParen(_) => write!(f, ")"),
104 Token::OpenBracket(_) => write!(f, "["),
105 Token::CloseBracket(_) => write!(f, "]"),
106 Token::Quote(_) => write!(f, "'"),
107 Token::Backquote(_) => write!(f, "`"),
108 Token::Unquote(_) => write!(f, ","),
109 Token::Splice(_) => write!(f, ",@"),
110 Token::Sharp(_) => write!(f, "#"),
111 Token::QuestionMark(_, chr) => write!(f, "?{chr}"),
112 Token::Ident(x) => write!(f, "{x}"),
113 Token::String(x) => write!(f, "\"{x}\""),
114 }
115 }
116}
117
118#[derive(Clone)]
119struct Tokenizer<'a> {
120 slice: &'a str,
121 iter: Peekable<CharIndices<'a>>,
122}
123
124impl<'a> Tokenizer<'a> {
125 fn new(slice: &'a str) -> Self {
126 Self { slice, iter: slice.char_indices().peekable() }
127 }
128
129 fn relative_pos(&self, token: Token<'a>) -> usize {
131 match token {
132 Token::OpenParen(x)
133 | Token::CloseParen(x)
134 | Token::OpenBracket(x)
135 | Token::CloseBracket(x)
136 | Token::Quote(x)
137 | Token::Backquote(x)
138 | Token::Unquote(x)
139 | Token::Splice(x)
140 | Token::Sharp(x)
141 | Token::QuestionMark(x, _) => x,
142 Token::Ident(slice) | Token::String(slice) => {
143 let beg = self.slice.as_ptr() as usize;
144 let end = slice.as_ptr() as usize;
145 end - beg
146 }
147 }
148 }
149
150 fn cur_pos(&mut self) -> usize {
153 match self.iter.peek() {
154 Some((idx, _)) => *idx,
155 None => self.slice.len(),
156 }
157 }
158
159 fn skip_till(&mut self, mut func: impl FnMut(char) -> bool) -> usize {
161 while self.iter.next_if(|x| !func(x.1)).is_some() {}
162 match self.iter.peek() {
163 Some((idx, _)) => *idx,
164 None => self.slice.len(),
165 }
166 }
167
168 fn skip_till_char(&mut self) {
170 let mut in_comment = false;
171 let valid_char = |chr: char| {
172 if in_comment {
173 if chr == '\n' {
174 in_comment = false;
175 }
176 false
177 } else if chr.is_ascii_whitespace() {
178 false
179 } else if chr == ';' {
180 in_comment = true;
181 false
182 } else {
183 true
184 }
185 };
186 self.skip_till(valid_char);
187 }
188
189 fn get_string(&mut self, open_delim_pos: usize) -> Result<Token<'a>> {
190 let mut skip = false;
191 let idx_chr = self.iter.find(|(_, chr)| !escaped(&mut skip, *chr) && *chr == '"');
192 match idx_chr {
193 Some((end, '"')) => Ok(Token::String(&self.slice[(open_delim_pos + 1)..end])),
194 _ => Err(Error::MissingStringDel(open_delim_pos)),
195 }
196 }
197
198 fn get_symbol(&mut self, beg: usize, chr: char) -> Token<'a> {
199 let mut skip = chr == '\\';
200 let end = self.skip_till(|c| !escaped(&mut skip, c) && !symbol_char(c));
201 Token::Ident(&self.slice[beg..end])
202 }
203
204 fn get_macro_char(&mut self, idx: usize) -> Token<'a> {
206 match self.iter.next_if(|(_, chr)| *chr == '@') {
207 Some(_) => Token::Splice(idx),
208 None => Token::Unquote(idx),
209 }
210 }
211
212 fn read_quoted_char(&mut self, idx: usize) -> Result<Token<'a>> {
213 match self.iter.next() {
214 Some((start, item)) => {
215 if item == '\\' {
216 let Token::Ident(tok) = self.get_symbol(start, item) else { unreachable!() };
217 let Some(chr) = tok.chars().nth(1) else {
218 return Err(Error::MissingQuotedItem(start));
219 };
220 if chr == 'u' || chr == 'x' {
221 match u32::from_str_radix(&tok[2..], 16) {
222 Ok(digits) => match char::from_u32(digits) {
223 Some(c) => Ok(Token::QuestionMark(start, c)),
224 None => Err(Error::MalformedUnicdoe(start)),
225 },
226 Err(_) => Err(Error::MalformedUnicdoe(start)),
227 }
228 } else if tok.chars().count() == 2 {
229 let new = match chr {
230 'a' => '\u{07}',
231 'b' => '\u{08}',
232 'd' => '\u{7F}',
233 'e' => '\u{1B}',
234 'f' => '\u{0C}',
235 'n' => '\n',
236 'r' => '\r',
237 's' => ' ',
238 't' => '\t',
239 'v' => '\u{0B}',
240 c => c,
241 };
242 Ok(Token::QuestionMark(start, new))
243 } else {
244 Ok(Token::QuestionMark(start, '\0'))
246 }
247 } else {
248 match self.iter.peek() {
249 Some((i, chr)) if symbol_char(*chr) && *chr != '?' => {
250 Err(Error::UnexpectedChar(*chr, *i)) }
252 _ => Ok(Token::QuestionMark(idx, item)), }
254 }
255 }
256 None => Err(Error::MissingQuotedItem(idx)),
257 }
258 }
259
260 fn read_char(&mut self) -> Option<char> {
261 self.iter.next().map(|x| x.1)
262 }
263}
264
265impl<'a> Iterator for Tokenizer<'a> {
266 type Item = Result<Token<'a>>;
267
268 fn next(&mut self) -> Option<Self::Item> {
269 self.skip_till_char();
270 let (idx, chr) = self.iter.next()?;
271 let token = match chr {
272 '(' => Ok(Token::OpenParen(idx)),
273 ')' => Ok(Token::CloseParen(idx)),
274 '[' => Ok(Token::OpenBracket(idx)),
275 ']' => Ok(Token::CloseBracket(idx)),
276 '\'' => Ok(Token::Quote(idx)),
277 ',' => Ok(self.get_macro_char(idx)),
278 '`' => Ok(Token::Backquote(idx)),
279 '#' => Ok(Token::Sharp(idx)),
280 '?' => self.read_quoted_char(idx),
281 '"' => self.get_string(idx),
282 other if symbol_char(other) => Ok(self.get_symbol(idx, other)),
283 unknown => Err(Error::UnexpectedChar(unknown, idx)),
284 };
285 Some(token)
286 }
287}
288
289fn intern_symbol<'ob>(symbol: &str, cx: &'ob Context) -> Symbol<'ob> {
290 let mut escaped = false;
291 let is_not_escape = |c: &char| {
292 if escaped {
293 escaped = false;
294 true
295 } else if *c == '\\' {
296 escaped = true;
297 false
298 } else {
299 true
300 }
301 };
302 if symbol.contains('\\') {
303 let escaped_slice: String = symbol.chars().filter(is_not_escape).collect();
304 intern(escaped_slice.as_str(), cx)
305 } else {
306 intern(symbol, cx)
307 }
308}
309
310fn parse_symbol<'a>(slice: &str, cx: &'a Context) -> Object<'a> {
313 if let Ok(num) = slice.parse::<i64>() {
314 return cx.add(num);
315 }
316 if let Ok(num) = BigInt::from_str(slice) {
317 return cx.add(num);
318 }
319 match slice.parse::<f64>() {
320 Ok(num) => cx.add(num),
321 Err(_) => cx.add(intern_symbol(slice, cx)),
322 }
323}
324
325fn unescape_string<'a>(string: &str, cx: &'a Context) -> Object<'a> {
328 let mut escaped = false;
329 let unescape = |c: char| {
330 if escaped {
331 escaped = false;
332 match c {
334 'n' => Some('\n'),
335 't' => Some('\t'),
336 'r' => Some('\r'),
337 '\n' | ' ' => None,
338 _ => Some(c),
339 }
340 } else if c == '\\' {
341 escaped = true;
342 None
343 } else {
344 Some(c)
345 }
346 };
347 let mut new = cx.string_with_capacity(string.len());
348 for c in string.chars().filter_map(unescape) {
349 new.push(c);
350 }
351 cx.add(new)
352}
353
354const fn symbol_char(chr: char) -> bool {
356 !matches!(chr, '\x00'..=' ' | '(' | ')' | '[' | ']' | '#' | ',' | '`' | ';' | '"' | '\'')
357}
358
359fn escaped(escaped: &mut bool, chr: char) -> bool {
360 if *escaped {
361 *escaped = false;
362 true
363 } else if chr == '\\' {
364 *escaped = true;
365 true
366 } else {
367 false
368 }
369}
370
371struct Reader<'a, 'ob> {
373 tokens: Tokenizer<'a>,
375 cx: &'ob Context<'ob>,
377}
378
379impl<'a, 'ob> Reader<'a, 'ob> {
380 fn read_cdr(&mut self, delim: usize) -> Result<Option<Object<'ob>>> {
386 match self.tokens.next() {
387 Some(Ok(Token::CloseParen(_))) => Ok(None),
388 Some(Ok(sexp)) => {
389 let obj = self.read_sexp(sexp)?;
390 match self.tokens.next() {
391 Some(Ok(Token::CloseParen(_))) => Ok(Some(obj)),
392 Some(Ok(token)) => Err(Error::ExtraItemInCdr(self.tokens.relative_pos(token))),
393 Some(Err(e)) => Err(e),
394 None => Err(Error::MissingCloseParen(delim)),
395 }
396 }
397 Some(Err(e)) => Err(e),
398 None => Err(Error::MissingCloseParen(delim)),
399 }
400 }
401
402 fn read_list(&mut self, delim: usize) -> Result<Object<'ob>> {
403 let mut objects = Vec::new();
404 while let Some(token) = self.tokens.next() {
405 match token? {
406 Token::CloseParen(_) => return Ok(fns::slice_into_list(&objects, None, self.cx)),
407 Token::Ident(".") => {
408 let cdr = self.read_cdr(delim)?;
409 if cdr.is_none() {
410 objects.push(parse_symbol(".", self.cx));
411 }
412 return Ok(fns::slice_into_list(&objects, cdr, self.cx));
413 }
414 tok => objects.push(self.read_sexp(tok)?),
415 }
416 }
417 Err(Error::MissingCloseParen(delim))
418 }
419
420 fn read_vec(&mut self, delim: usize) -> Result<Object<'ob>> {
421 let mut objects = self.cx.vec_new();
422 while let Some(token) = self.tokens.next() {
423 match token? {
424 Token::CloseBracket(_) => return Ok(self.cx.add(objects)),
425 tok => objects.push(self.read_sexp(tok)?),
426 }
427 }
428 Err(Error::MissingCloseBracket(delim))
429 }
430
431 fn quote_item(&mut self, pos: usize, symbol: Symbol) -> Result<Object<'ob>> {
433 match self.tokens.next() {
434 Some(token) => {
435 let obj = self.read_sexp(token?)?;
436 Ok(list!(symbol, obj; self.cx))
437 }
438 None => Err(Error::MissingQuotedItem(pos)),
439 }
440 }
441
442 fn read_radix(&mut self, pos: usize, radix: u8) -> Result<Object<'ob>> {
444 if !(2..=36).contains(&radix) {
445 return Err(Error::ParseInt(radix, pos));
446 }
447
448 match self.tokens.next() {
449 Some(Ok(Token::Ident(ident))) => match usize::from_str_radix(ident, radix.into()) {
450 Ok(x) => Ok(self.cx.add(x as i64)),
451 Err(_) => Err(Error::ParseInt(radix, pos)),
452 },
453 _ => Err(Error::ParseInt(radix, pos)),
454 }
455 }
456
457 fn read_sharp(&mut self, pos: usize) -> Result<Object<'ob>> {
460 match self.tokens.read_char() {
461 Some('\'') => match self.tokens.next() {
462 Some(Ok(Token::OpenParen(i))) => {
463 let list = self.read_list(i)?;
464 Ok(list!(sym::FUNCTION, list; self.cx))
465 }
466 Some(token) => {
467 let obj = self.read_sexp(token?)?;
468 Ok(list!(sym::FUNCTION, obj; self.cx))
469 }
470 None => Err(Error::MissingQuotedItem(pos)),
471 },
472 Some('b') => self.read_radix(pos, 2),
473 Some('o') => self.read_radix(pos, 8),
474 Some('x') => self.read_radix(pos, 16),
475 Some(chr) if chr.is_ascii_digit() => {
476 let mut radix = (chr as u8) - b'0';
477 loop {
478 match self.tokens.read_char() {
479 Some('r') => break,
480 Some(chr) if chr.is_ascii_digit() => {
481 match radix
482 .checked_mul(10)
483 .and_then(|r| r.checked_add(chr as u8 - b'0'))
484 {
485 Some(r) => radix = r,
486 None => return Err(Error::UnknownMacroCharacter(chr, pos)),
488 }
489 }
490 Some(chr) => return Err(Error::UnknownMacroCharacter(chr, pos)),
491 None => return Err(Error::MissingQuotedItem(pos)),
492 }
493 }
494 self.read_radix(pos, radix)
495 }
496 Some(chr) => Err(Error::UnknownMacroCharacter(chr, pos)),
497 None => Err(Error::MissingQuotedItem(pos)),
498 }
499 }
500
501 fn read_sexp(&mut self, token: Token<'a>) -> Result<Object<'ob>> {
502 match token {
503 Token::OpenParen(i) => self.read_list(i),
504 Token::CloseParen(i) => Err(Error::ExtraCloseParen(i)),
505 Token::OpenBracket(i) => self.read_vec(i),
506 Token::CloseBracket(i) => Err(Error::ExtraCloseBracket(i)),
507 Token::Quote(i) => self.quote_item(i, sym::QUOTE),
508 Token::Unquote(i) => self.quote_item(i, sym::UNQUOTE),
509 Token::Splice(i) => self.quote_item(i, sym::SPLICE),
510 Token::Backquote(i) => self.quote_item(i, sym::BACKQUOTE),
511 Token::Sharp(i) => self.read_sharp(i),
512 Token::QuestionMark(_, c) => Ok((c as i64).into()),
513 Token::Ident(x) => Ok(parse_symbol(x, self.cx)),
514 Token::String(x) => Ok(unescape_string(x, self.cx)),
515 }
516 }
517}
518
519pub(crate) fn read<'ob>(slice: &str, cx: &'ob Context) -> Result<(Object<'ob>, usize)> {
522 let mut reader = Reader { tokens: Tokenizer::new(slice), cx };
523 match reader.tokens.next() {
524 Some(Ok(t)) => reader.read_sexp(t).map(|x| (x, reader.tokens.cur_pos())),
525 Some(Err(e)) => Err(e),
526 None => Err(Error::EmptyStream),
527 }
528}
529
530#[cfg(test)]
531mod test {
532 use crate::core::{cons::Cons, gc::RootSet};
533
534 use super::*;
535
536 #[test]
537 fn tokens() {
538 let mut iter = Tokenizer::new("1 foo (\"bar\" . 1.3)");
539 assert_eq!(iter.next(), Some(Ok(Token::Ident("1"))));
540 assert_eq!(iter.next(), Some(Ok(Token::Ident("foo"))));
541 assert_eq!(iter.next(), Some(Ok(Token::OpenParen(6))));
542 assert_eq!(iter.next(), Some(Ok(Token::String("bar"))));
543 assert_eq!(iter.next(), Some(Ok(Token::Ident("."))));
544 assert_eq!(iter.next(), Some(Ok(Token::Ident("1.3"))));
545 assert_eq!(iter.next(), Some(Ok(Token::CloseParen(18))));
546 assert_eq!(iter.next(), None);
547 }
548
549 macro_rules! check_reader {
550 ($expect:expr, $compare:expr, $cx:expr) => {{
551 let obj = $cx.add($expect);
552 assert_eq!(obj, read($compare, $cx).unwrap().0)
553 }};
554 }
555
556 #[test]
557 fn test_read_number() {
558 let roots = &RootSet::default();
559 let cx = &Context::new(roots);
560 check_reader!(5, "5", cx);
561 check_reader!(49, "49", cx);
562 check_reader!(-105, "-105", cx);
563 check_reader!(1.5, "1.5", cx);
564 check_reader!(-3.0, "-3.0", cx);
565 check_reader!(1, "+1", cx);
566 check_reader!(1, "001", cx);
567 check_reader!(1, "#o001", cx);
568 check_reader!(8, "#o10", cx);
569 check_reader!(8, "#8r10", cx);
570 check_reader!(2385, "#o4521", cx);
571 check_reader!(2385, "#8r4521", cx);
572 check_reader!(0b1, "#b001", cx);
573 check_reader!(0b10, "#b10", cx);
574 check_reader!(0b10, "#2r10", cx);
575 check_reader!(0b10, "#00000002r10", cx);
576 check_reader!(0b101_1101, "#b1011101", cx);
577 check_reader!(0x1, "#x001", cx);
578 check_reader!(0x10, "#x10", cx);
579 check_reader!(0xdead_beef_i64, "#xDeAdBeEf", cx);
580 check_reader!(171, "#12r0123", cx);
581 check_reader!(49360, "#36r1234", cx);
582 assert_error("#37r1234", Error::ParseInt(37, 0), cx);
583 assert_error("#257r1234", Error::UnknownMacroCharacter('7', 0), cx);
584 assert_error("#123456r1234", Error::UnknownMacroCharacter('4', 0), cx);
585 }
586
587 #[test]
588 #[expect(clippy::non_ascii_literal)]
589 fn test_read_char() {
590 let roots = &RootSet::default();
591 let cx = &Context::new(roots);
592 check_reader!(97, "?a", cx);
593 check_reader!(21, "?", cx);
594 check_reader!(225, "?á", cx);
595 check_reader!(97, "?a?a", cx);
596 check_reader!(97, "?a#'foo ?a", cx);
597 assert_error("?aa", Error::UnexpectedChar('a', 2), cx);
598 assert_error("?", Error::MissingQuotedItem(0), cx);
599 }
600
601 #[test]
602 fn read_bool() {
603 let roots = &RootSet::default();
604 let cx = &Context::new(roots);
605 check_reader!(false, "nil", cx);
606 check_reader!(false, "()", cx);
607 check_reader!(true, "t", cx);
608 }
609
610 #[test]
611 fn test_read_symbol() {
612 let roots = &RootSet::default();
613 let cx = &Context::new(roots);
614 check_reader!(sym::IF, "if", cx);
615 check_reader!(intern("--1", cx), "--1", cx);
616 check_reader!(intern("1", cx), "\\1", cx);
617 check_reader!(intern("3.0.0", cx), "3.0.0", cx);
618 check_reader!(intern("1+", cx), "1+", cx);
619 check_reader!(intern("+1", cx), "\\+1", cx);
620 check_reader!(intern(" x", cx), "\\ x", cx);
621 check_reader!(intern("\\x", cx), "\\\\x", cx);
622 check_reader!(intern("x.y", cx), "x.y", cx);
623 check_reader!(intern("(* 1 2)", cx), "\\(*\\ 1\\ 2\\)", cx);
624 check_reader!(intern("+-*/_~!@$%^&=:<>{}", cx), "+-*/_~!@$%^&=:<>{}", cx);
625 }
626
627 #[test]
628 fn test_read_string() {
629 let roots = &RootSet::default();
630 let cx = &Context::new(roots);
631 check_reader!("foo", r#""foo""#, cx);
632 check_reader!("foo bar", r#""foo bar""#, cx);
633 check_reader!("foo\nbar\t\r", r#""foo\nbar\t\r""#, cx);
634 check_reader!(
635 "foobarbaz",
636 r#""foo\ bar\
637baz""#,
638 cx
639 );
640 }
641
642 #[test]
643 fn test_read_cons() {
644 let roots = &RootSet::default();
645 let cx = &Context::new(roots);
646 check_reader!(false, "()", cx);
647 check_reader!(Cons::new(1, 2, cx), "(1 . 2)", cx);
648 check_reader!(list!(1; cx), "(1)", cx);
649 check_reader!(list!("foo"; cx), "(\"foo\")", cx);
650 check_reader!(Cons::new(1, Cons::new(1.5, "foo", cx), cx), "(1 1.5 . \"foo\")", cx);
651 check_reader!(list!("foo", Cons::new(1, 1.5, cx); cx), "(\"foo\" (1 . 1.5))", cx);
652 check_reader!(list!(1, 1.5; cx), "(1 1.5)", cx);
653 check_reader!(list!(1, 1.5, -7; cx), "(1 1.5 -7)", cx);
654 check_reader!(list!(1, 1.5, intern(".", cx); cx), "(1 1.5 .)", cx);
655 check_reader!(list!(1, 1.5, intern("...", cx), 2; cx), "(1 1.5 ... 2)", cx);
656 }
657
658 #[test]
659 fn read_quote() {
660 let roots = &RootSet::default();
661 let cx = &Context::new(roots);
662 let quote = sym::QUOTE;
663 check_reader!(list!(quote, sym::IF; cx), "(quote if)", cx);
664 check_reader!(list!(quote, sym::IF; cx), "'if", cx);
665 check_reader!(list!(quote, list!(1, 2, 3; cx); cx), "'(1 2 3)", cx);
666 check_reader!(u32::from('a'), "?a", cx);
667 check_reader!(u32::from(' '), "?\\s", cx);
668 check_reader!(u32::from('\t'), "?\\t", cx);
669 check_reader!(u32::from('\u{AFD}'), "?\\uafd", cx);
670 check_reader!(0xabc_u32, "?\\xabc", cx);
671 }
672
673 #[test]
674 fn read_sharp() {
675 let roots = &RootSet::default();
676 let cx = &Context::new(roots);
677 let quote = sym::FUNCTION;
678 check_reader!(list!(quote, sym::IF; cx), "#'if", cx);
679 check_reader!(
680 list!(quote, list!(intern("lambda", cx), sym::IF, false, false; cx); cx),
681 "#'(lambda if () nil)",
682 cx
683 );
684 assert_error("#", Error::MissingQuotedItem(0), cx);
685 assert_error("#'", Error::MissingQuotedItem(0), cx);
686 assert_error("#a", Error::UnknownMacroCharacter('a', 0), cx);
687 }
688
689 #[test]
690 fn test_read_vec() {
691 let roots = &RootSet::default();
692 let cx = &Context::new(roots);
693 check_reader!(Vec::<Object>::new(), "[]", cx);
694 let vec: Vec<Object> = vec![1.into()];
695 check_reader!(vec, "[1]", cx);
696 let vec: Vec<Object> = vec![1.into(), 2.into()];
697 check_reader!(vec, "[1 2]", cx);
698 let vec: Vec<Object> = vec![1.into(), 2.into(), 3.into()];
699 check_reader!(vec, "[1 2 3]", cx);
700 }
701
702 fn assert_error(input: &str, error: Error, cx: &Context) {
703 let result = read(input, cx).err().unwrap();
704 assert_eq!(result, error);
705 }
706
707 #[test]
708 fn reader_error() {
709 let roots = &RootSet::default();
710 let cx = &Context::new(roots);
711 assert_error("", Error::EmptyStream, cx);
712 assert_error(" (1 2", Error::MissingCloseParen(1), cx);
713 assert_error(" (1 (2 3) 4", Error::MissingCloseParen(2), cx);
714 assert_error(" (1 (2 3 4", Error::MissingCloseParen(5), cx);
715 assert_error(" \"foo", Error::MissingStringDel(1), cx);
716 assert_error("(1 2 . 3 4)", Error::ExtraItemInCdr(9), cx);
717 assert_error("(1 3 \0)", Error::UnexpectedChar('\0', 5), cx);
718 assert_error(" '", Error::MissingQuotedItem(1), cx);
719 assert_error(" )", Error::ExtraCloseParen(1), cx);
720 assert_error("(1 . #o9 3)", Error::ParseInt(8, 5), cx);
721 }
722
723 #[test]
724 fn comments() {
725 let roots = &RootSet::default();
726 let cx = &Context::new(roots);
727 assert_error(" ; comment ", Error::EmptyStream, cx);
728 check_reader!(1, "; comment \n 1", cx);
729 }
730}