1use crate::scheme::value::{Value, Unit};
40use std::fmt;
41
42#[derive(Debug, Clone, PartialEq)]
48pub enum Token {
49 Integer(i64),
51 Real(f64),
52 Quantity(f64, String), String(String),
54 Char(char),
55 Symbol(String),
56 Keyword(String),
57 Bool(bool),
58
59 LeftParen, RightParen, LeftBracket, RightBracket, Dot, Quote, Quasiquote, Unquote, UnquoteSplicing, VectorStart, Eof,
77}
78
79impl fmt::Display for Token {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 Token::Integer(n) => write!(f, "{}", n),
83 Token::Real(n) => write!(f, "{}", n),
84 Token::Quantity(magnitude, unit) => write!(f, "{}{}", magnitude, unit),
85 Token::String(s) => write!(f, "\"{}\"", s),
86 Token::Char(ch) => write!(f, "#\\{}", ch),
87 Token::Symbol(s) => write!(f, "{}", s),
88 Token::Keyword(s) => write!(f, "#:{}", s),
89 Token::Bool(b) => write!(f, "{}", if *b { "#t" } else { "#f" }),
90 Token::LeftParen => write!(f, "("),
91 Token::RightParen => write!(f, ")"),
92 Token::LeftBracket => write!(f, "["),
93 Token::RightBracket => write!(f, "]"),
94 Token::Dot => write!(f, "."),
95 Token::Quote => write!(f, "'"),
96 Token::Quasiquote => write!(f, "`"),
97 Token::Unquote => write!(f, ","),
98 Token::UnquoteSplicing => write!(f, ",@"),
99 Token::VectorStart => write!(f, "#("),
100 Token::Eof => write!(f, "<EOF>"),
101 }
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, gc::Trace, gc::Finalize)]
116pub struct Position {
117 pub line: usize,
118 pub column: usize,
119}
120
121impl Position {
122 pub fn new() -> Self {
123 Position { line: 1, column: 1 }
124 }
125}
126
127impl Default for Position {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl fmt::Display for Position {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 write!(f, "{}:{}", self.line, self.column)
136 }
137}
138
139#[derive(Debug, Clone)]
145pub struct ParseError {
146 pub message: String,
147 pub position: Position,
148 pub filename: Option<String>,
149}
150
151impl ParseError {
152 pub fn new(message: String, position: Position) -> Self {
153 ParseError { message, position, filename: None }
154 }
155
156 pub fn with_filename(message: String, position: Position, filename: String) -> Self {
157 ParseError { message, position, filename: Some(filename) }
158 }
159}
160
161impl fmt::Display for ParseError {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 if let Some(ref filename) = self.filename {
164 write!(f, "{}:{}:E: {}", filename, self.position, self.message)
165 } else {
166 write!(f, "Parse error at {}: {}", self.position, self.message)
167 }
168 }
169}
170
171impl std::error::Error for ParseError {}
172
173pub type ParseResult<T> = Result<T, ParseError>;
174
175pub struct Tokenizer {
195 input: Vec<char>,
197
198 pos: usize,
200
201 line: usize,
203
204 column: usize,
206
207 peeked: Option<Token>,
209
210 filename: Option<String>,
212}
213
214impl Tokenizer {
215 pub fn new(input: &str) -> Self {
217 Tokenizer {
218 input: input.chars().collect(),
219 pos: 0,
220 line: 1,
221 column: 1,
222 peeked: None,
223 filename: None,
224 }
225 }
226
227 pub fn new_with_filename(input: &str, filename: String) -> Self {
229 Tokenizer {
230 input: input.chars().collect(),
231 pos: 0,
232 line: 1,
233 column: 1,
234 peeked: None,
235 filename: Some(filename),
236 }
237 }
238
239 fn error(&self, message: String, position: Position) -> ParseError {
241 if let Some(ref filename) = self.filename {
242 ParseError::with_filename(message, position, filename.clone())
243 } else {
244 ParseError::new(message, position)
245 }
246 }
247
248 pub fn position(&self) -> Position {
250 Position {
251 line: self.line,
252 column: self.column,
253 }
254 }
255
256 fn peek_char(&self) -> Option<char> {
258 if self.pos < self.input.len() {
259 Some(self.input[self.pos])
260 } else {
261 None
262 }
263 }
264
265 fn peek_char_at(&self, offset: usize) -> Option<char> {
267 let index = self.pos + offset;
268 if index < self.input.len() {
269 Some(self.input[index])
270 } else {
271 None
272 }
273 }
274
275 fn next_char(&mut self) -> Option<char> {
277 if self.pos < self.input.len() {
278 let ch = self.input[self.pos];
279 self.pos += 1;
280
281 if ch == '\n' {
283 self.line += 1;
284 self.column = 1;
285 } else {
286 self.column += 1;
287 }
288
289 Some(ch)
290 } else {
291 None
292 }
293 }
294
295 fn skip_whitespace(&mut self) {
299 loop {
300 match self.peek_char() {
301 Some(ch) if ch.is_whitespace() => {
303 self.next_char();
304 }
305
306 Some(';') => {
308 self.next_char();
309 while let Some(ch) = self.peek_char() {
310 self.next_char();
311 if ch == '\n' {
312 break;
313 }
314 }
315 }
316
317 Some('#') if self.peek_char_at(1) == Some('|') => {
319 self.next_char(); self.next_char(); let mut depth = 1;
324 while depth > 0 {
325 match self.next_char() {
326 Some('|') if self.peek_char() == Some('#') => {
327 self.next_char(); depth -= 1;
329 }
330 Some('#') if self.peek_char() == Some('|') => {
331 self.next_char(); depth += 1; }
334 Some(_) => {} None => break, }
337 }
338 }
339
340 _ => break,
342 }
343 }
344 }
345
346 fn is_delimiter(ch: char) -> bool {
348 ch.is_whitespace()
349 || matches!(
350 ch,
351 '(' | ')' | '[' | ']' | '"' | ';' | ',' | '`' | '\''
352 )
353 }
354
355 fn peek_quantity_suffix(&self) -> usize {
358 if let Some(ch1) = self.peek_char_at(0) {
361 if let Some(ch2) = self.peek_char_at(1) {
362 match (ch1, ch2) {
363 ('p', 't') | ('p', 'i') | ('p', 'c') | ('p', 'x') |
364 ('i', 'n') | ('m', 'm') | ('c', 'm') | ('e', 'm') => return 2,
365 _ => {}
366 }
367 }
368 }
369 0
370 }
371
372 fn parse_number(&mut self, start_pos: Position) -> ParseResult<Token> {
374 let mut num_str = String::new();
375
376 while let Some(ch) = self.peek_char() {
378 if ch.is_ascii_digit() || matches!(ch, '.' | '+' | '-') {
379 num_str.push(ch);
380 self.next_char();
381 } else if matches!(ch, 'e' | 'E') {
382 if self.peek_char_at(1) == Some('m') {
385 break;
388 } else {
389 num_str.push(ch);
391 self.next_char();
392 }
393 } else if Self::is_delimiter(ch) {
394 break;
395 } else {
396 let suffix_len = self.peek_quantity_suffix();
399 if suffix_len > 0 {
400 let mut suffix = String::new();
402 for _ in 0..suffix_len {
403 if let Some(ch) = self.peek_char() {
404 suffix.push(ch);
405 }
406 self.next_char();
407 }
408 if let Ok(n) = num_str.parse::<f64>() {
411 return Ok(Token::Quantity(n, suffix));
412 } else if let Ok(n) = num_str.parse::<i64>() {
413 return Ok(Token::Quantity(n as f64, suffix));
414 }
415 return Err(self.error(
416 format!("Invalid quantity: {}{}", num_str, suffix),
417 start_pos,
418 ));
419 }
420
421 return Err(self.error(
423 format!("Invalid character in number: {}", ch),
424 start_pos,
425 ));
426 }
427 }
428
429 let suffix_len = self.peek_quantity_suffix();
431 if suffix_len > 0 {
432 let mut suffix = String::new();
434 for _ in 0..suffix_len {
435 if let Some(ch) = self.peek_char() {
436 suffix.push(ch);
437 }
438 self.next_char();
439 }
440 if let Ok(n) = num_str.parse::<f64>() {
442 return Ok(Token::Quantity(n, suffix));
443 } else if let Ok(n) = num_str.parse::<i64>() {
444 return Ok(Token::Quantity(n as f64, suffix));
445 }
446 return Err(self.error(
447 format!("Invalid quantity: {}{}", num_str, suffix),
448 start_pos,
449 ));
450 }
451
452 if let Ok(n) = num_str.parse::<i64>() {
454 return Ok(Token::Integer(n));
455 }
456
457 if let Ok(n) = num_str.parse::<f64>() {
459 return Ok(Token::Real(n));
460 }
461
462 Err(self.error(
463 format!("Invalid number: {}", num_str),
464 start_pos,
465 ))
466 }
467
468 fn parse_hex_number(&mut self, start_pos: Position) -> ParseResult<Token> {
470 let mut num_str = String::new();
471
472 while let Some(ch) = self.peek_char() {
473 if ch.is_ascii_hexdigit() {
474 num_str.push(ch);
475 self.next_char();
476 } else if Self::is_delimiter(ch) {
477 break;
478 } else {
479 return Err(self.error(
480 format!("Invalid character in hex number: {}", ch),
481 start_pos,
482 ));
483 }
484 }
485
486 if num_str.is_empty() {
487 return Err(self.error("Empty hex number".to_string(), start_pos));
488 }
489
490 i64::from_str_radix(&num_str, 16)
491 .map(Token::Integer)
492 .map_err(|_| self.error(format!("Invalid hex number: {}", num_str), start_pos))
493 }
494
495 fn parse_octal_number(&mut self, start_pos: Position) -> ParseResult<Token> {
497 let mut num_str = String::new();
498
499 while let Some(ch) = self.peek_char() {
500 if ch.is_digit(8) {
501 num_str.push(ch);
502 self.next_char();
503 } else if Self::is_delimiter(ch) {
504 break;
505 } else {
506 return Err(self.error(
507 format!("Invalid character in octal number: {}", ch),
508 start_pos,
509 ));
510 }
511 }
512
513 if num_str.is_empty() {
514 return Err(self.error("Empty octal number".to_string(), start_pos));
515 }
516
517 i64::from_str_radix(&num_str, 8)
518 .map(Token::Integer)
519 .map_err(|_| self.error(format!("Invalid octal number: {}", num_str), start_pos))
520 }
521
522 fn parse_binary_number(&mut self, start_pos: Position) -> ParseResult<Token> {
524 let mut num_str = String::new();
525
526 while let Some(ch) = self.peek_char() {
527 if matches!(ch, '0' | '1') {
528 num_str.push(ch);
529 self.next_char();
530 } else if Self::is_delimiter(ch) {
531 break;
532 } else {
533 return Err(self.error(
534 format!("Invalid character in binary number: {}", ch),
535 start_pos,
536 ));
537 }
538 }
539
540 if num_str.is_empty() {
541 return Err(self.error("Empty binary number".to_string(), start_pos));
542 }
543
544 i64::from_str_radix(&num_str, 2)
545 .map(Token::Integer)
546 .map_err(|_| self.error(format!("Invalid binary number: {}", num_str), start_pos))
547 }
548
549 fn parse_symbol(&mut self) -> String {
551 let mut sym = String::new();
552
553 while let Some(ch) = self.peek_char() {
554 if Self::is_delimiter(ch) {
555 break;
556 }
557 sym.push(ch);
558 self.next_char();
559 }
560
561 sym
562 }
563
564 fn parse_string(&mut self, start_pos: Position) -> ParseResult<String> {
566 self.next_char(); let mut result = String::new();
569
570 loop {
571 match self.next_char() {
572 Some('"') => {
573 let normalized = result.replace("\r\n", "\n");
577 return Ok(normalized);
578 }
579 Some('\\') => {
580 match self.next_char() {
582 Some('n') => result.push('\n'),
583 Some('t') => result.push('\t'),
584 Some('r') => result.push('\r'),
585 Some('\\') => result.push('\\'),
586 Some('"') => result.push('"'),
587 Some(ch) => result.push(ch), None => {
589 return Err(self.error(
590 "Unexpected EOF in string escape".to_string(),
591 start_pos,
592 ))
593 }
594 }
595 }
596 Some(ch) => {
597 result.push(ch);
598 }
599 None => {
600 return Err(self.error(
601 "Unexpected EOF in string".to_string(),
602 start_pos,
603 ))
604 }
605 }
606 }
607 }
608
609 fn parse_char(&mut self, start_pos: Position) -> ParseResult<char> {
612 if self.next_char() != Some('\\') {
614 return Err(self.error(
615 "Expected \\ after # in character literal".to_string(),
616 start_pos,
617 ));
618 }
619
620 let mut name = String::new();
622 while let Some(ch) = self.peek_char() {
623 if Self::is_delimiter(ch) {
624 break;
625 }
626 name.push(ch);
627 self.next_char();
628 }
629
630 if name.is_empty() {
634 if let Some(ch) = self.next_char() {
635 return Ok(ch);
636 } else {
637 return Err(self.error(
638 "Unexpected end of input in character literal".to_string(),
639 start_pos,
640 ));
641 }
642 }
643
644 match name.as_str() {
646 "space" => Ok(' '),
647 "newline" => Ok('\n'),
648 "tab" => Ok('\t'),
649 "return" => Ok('\r'),
650 s if s.starts_with("U-") => {
653 let hex_str = &s[2..]; u32::from_str_radix(hex_str, 16)
655 .ok()
656 .and_then(std::char::from_u32)
657 .ok_or_else(|| self.error(
658 format!("Invalid Unicode character literal: #\\{}", name),
659 start_pos,
660 ))
661 }
662 s if s.chars().count() == 1 => Ok(s.chars().next().unwrap()),
665 _ => Err(self.error(
666 format!("Invalid character literal: #\\{}", name),
667 start_pos,
668 )),
669 }
670 }
671
672 fn parse_cdata_string(&mut self, start_pos: Position) -> ParseResult<Token> {
676 for _ in 0..9 {
678 self.next_char();
679 }
680
681 let mut content = String::new();
682
683 loop {
685 match self.peek_char() {
686 None => {
687 return Err(self.error(
688 "Unclosed CDATA section: missing ]]>".to_string(),
689 start_pos,
690 ));
691 }
692 Some(']') => {
693 if self.pos + 2 < self.input.len()
695 && self.input[self.pos] == ']'
696 && self.input[self.pos + 1] == ']'
697 && self.input[self.pos + 2] == '>'
698 {
699 self.next_char(); self.next_char(); self.next_char(); break;
704 } else {
705 content.push(']');
706 self.next_char();
707 }
708 }
709 Some(ch) => {
710 content.push(ch);
711 self.next_char();
712 }
713 }
714 }
715
716 Ok(Token::String(content))
717 }
718
719 pub fn next_token(&mut self) -> ParseResult<Token> {
721 if let Some(tok) = self.peeked.take() {
723 return Ok(tok);
724 }
725
726 self.skip_whitespace();
728
729 let start_pos = self.position();
730
731 match self.peek_char() {
732 None => Ok(Token::Eof),
733
734 Some('(') => {
735 self.next_char();
736 Ok(Token::LeftParen)
737 }
738
739 Some(')') => {
740 self.next_char();
741 Ok(Token::RightParen)
742 }
743
744 Some('[') => {
745 self.next_char();
746 Ok(Token::LeftBracket)
747 }
748
749 Some(']') => {
750 self.next_char();
751 Ok(Token::RightBracket)
752 }
753
754 Some('\'') => {
755 self.next_char();
756 Ok(Token::Quote)
757 }
758
759 Some('`') => {
760 self.next_char();
761 Ok(Token::Quasiquote)
762 }
763
764 Some(',') => {
765 self.next_char();
766 if self.peek_char() == Some('@') {
768 self.next_char();
769 Ok(Token::UnquoteSplicing)
770 } else {
771 Ok(Token::Unquote)
772 }
773 }
774
775 Some('"') => {
776 let s = self.parse_string(start_pos)?;
777 Ok(Token::String(s))
778 }
779
780 Some('#') => {
781 self.next_char(); match self.peek_char() {
783 Some('t') => {
784 self.next_char();
785 Ok(Token::Bool(true))
786 }
787 Some('f') => {
788 self.next_char();
789 Ok(Token::Bool(false))
790 }
791 Some('(') => {
792 self.next_char();
793 Ok(Token::VectorStart)
794 }
795 Some('\\') => {
796 let ch = self.parse_char(start_pos)?;
797 Ok(Token::Char(ch))
798 }
799 Some(':') => {
800 self.next_char(); let name = self.parse_symbol();
802 Ok(Token::Keyword(name))
803 }
804 Some('x') | Some('X') => {
805 self.next_char(); self.parse_hex_number(start_pos)
807 }
808 Some('o') | Some('O') => {
809 self.next_char(); self.parse_octal_number(start_pos)
811 }
812 Some('b') | Some('B') => {
813 self.next_char(); self.parse_binary_number(start_pos)
815 }
816 Some('!') => {
817 self.next_char(); let name = self.parse_symbol();
821 Ok(Token::Symbol(format!("#!{}", name)))
822 }
823 _ => Err(self.error(
824 format!("Invalid # syntax: #{:?}", self.peek_char()),
825 start_pos,
826 )),
827 }
828 }
829
830 Some(ch) if ch.is_ascii_digit() => self.parse_number(start_pos),
831
832 Some('+') | Some('-') => {
833 if let Some(next) = self.peek_char_at(1) {
835 if next.is_ascii_digit() {
836 self.parse_number(start_pos)
837 } else {
838 let sym = self.parse_symbol();
839 Ok(Token::Symbol(sym))
840 }
841 } else {
842 let sym = self.parse_symbol();
843 Ok(Token::Symbol(sym))
844 }
845 }
846
847 Some('.') => {
848 if let Some(next) = self.peek_char_at(1) {
850 if next.is_ascii_digit() {
851 self.parse_number(start_pos)
852 } else {
853 self.next_char();
854 Ok(Token::Dot)
855 }
856 } else {
857 self.next_char();
858 Ok(Token::Dot)
859 }
860 }
861
862 Some('<') => {
863 let cdata_prefix = ['<', '!', '[', 'C', 'D', 'A', 'T', 'A', '['];
866 let is_cdata = self.pos + cdata_prefix.len() <= self.input.len()
867 && self.input[self.pos..self.pos + cdata_prefix.len()] == cdata_prefix;
868
869 if is_cdata {
870 self.parse_cdata_string(start_pos)
871 } else {
872 let sym = self.parse_symbol();
874 Ok(Token::Symbol(sym))
875 }
876 }
877
878 Some(_) => {
879 let sym = self.parse_symbol();
881
882 if sym.ends_with(':') {
884 let keyword_name = sym[..sym.len()-1].to_string();
885 Ok(Token::Keyword(keyword_name))
886 } else {
887 Ok(Token::Symbol(sym))
888 }
889 }
890 }
891 }
892
893 pub fn peek_token(&mut self) -> ParseResult<&Token> {
895 if self.peeked.is_none() {
896 let tok = self.next_token()?;
897 self.peeked = Some(tok);
898 }
899 Ok(self.peeked.as_ref().unwrap())
900 }
901}
902
903pub struct Parser {
918 tokenizer: Tokenizer,
919 filename: Option<String>,
920}
921
922impl Parser {
923 pub fn new(input: &str) -> Self {
925 Parser {
926 tokenizer: Tokenizer::new(input),
927 filename: None,
928 }
929 }
930
931 pub fn new_with_filename(input: &str, filename: String) -> Self {
933 Parser {
934 tokenizer: Tokenizer::new_with_filename(input, filename.clone()),
935 filename: Some(filename),
936 }
937 }
938
939 fn error(&self, message: String, position: Position) -> ParseError {
941 if let Some(ref filename) = self.filename {
942 ParseError::with_filename(message, position, filename.clone())
943 } else {
944 ParseError::new(message, position)
945 }
946 }
947
948 pub fn parse(&mut self) -> ParseResult<Value> {
952 self.parse_expr()
953 }
954
955 pub fn peek_token(&mut self) -> ParseResult<&Token> {
960 self.tokenizer.peek_token()
961 }
962
963 pub fn current_position(&self) -> Position {
968 self.tokenizer.position()
969 }
970
971 pub fn parse_all(&mut self) -> ParseResult<Vec<Value>> {
975 let mut exprs = Vec::new();
976
977 loop {
978 let tok = self.tokenizer.peek_token()?;
979 if *tok == Token::Eof {
980 break;
981 }
982 exprs.push(self.parse_expr()?);
983 }
984
985 Ok(exprs)
986 }
987
988 fn parse_expr(&mut self) -> ParseResult<Value> {
990 let start_pos = self.tokenizer.position();
991 let tok = self.tokenizer.next_token()?;
992
993 match tok {
994 Token::Integer(n) => Ok(Value::integer(n)),
996 Token::Real(n) => Ok(Value::real(n)),
997 Token::Quantity(magnitude, suffix) => {
998 if let Some(unit) = Unit::from_suffix(&suffix) {
1000 Ok(Value::Quantity { magnitude, unit })
1001 } else {
1002 Err(self.error(
1003 format!("Invalid quantity unit: {}", suffix),
1004 start_pos,
1005 ))
1006 }
1007 }
1008 Token::String(s) => Ok(Value::string(s)),
1009 Token::Char(ch) => Ok(Value::char(ch)),
1010 Token::Bool(b) => Ok(Value::bool(b)),
1011 Token::Symbol(s) => Ok(Value::symbol(&s)),
1012 Token::Keyword(s) => Ok(Value::keyword(&s)),
1013
1014 Token::LeftParen | Token::LeftBracket => self.parse_list(start_pos),
1016
1017 Token::VectorStart => self.parse_vector(start_pos),
1019
1020 Token::Quote => {
1022 let quoted = self.parse_expr()?;
1023 Ok(Value::cons_with_pos(Value::symbol("quote"), Value::cons(quoted, Value::Nil), start_pos))
1024 }
1025
1026 Token::Quasiquote => {
1028 let quoted = self.parse_expr()?;
1029 Ok(Value::cons_with_pos(
1030 Value::symbol("quasiquote"),
1031 Value::cons(quoted, Value::Nil),
1032 start_pos
1033 ))
1034 }
1035
1036 Token::Unquote => {
1038 let quoted = self.parse_expr()?;
1039 Ok(Value::cons_with_pos(
1040 Value::symbol("unquote"),
1041 Value::cons(quoted, Value::Nil),
1042 start_pos
1043 ))
1044 }
1045
1046 Token::UnquoteSplicing => {
1048 let quoted = self.parse_expr()?;
1049 Ok(Value::cons_with_pos(
1050 Value::symbol("unquote-splicing"),
1051 Value::cons(quoted, Value::Nil),
1052 start_pos
1053 ))
1054 }
1055
1056 Token::RightParen | Token::RightBracket => Err(self.error(
1058 format!("Unexpected closing delimiter: {}", tok),
1059 start_pos,
1060 )),
1061
1062 Token::Dot => Err(self.error(
1063 "Unexpected dot outside of list".to_string(),
1064 start_pos,
1065 )),
1066
1067 Token::Eof => Err(self.error(
1068 "Unexpected end of input".to_string(),
1069 start_pos,
1070 )),
1071 }
1072 }
1073
1074 fn parse_list(&mut self, start_pos: Position) -> ParseResult<Value> {
1076 let mut elements = Vec::new();
1077 let mut element_positions = Vec::new();
1078 let mut dotted_tail = None;
1079
1080 loop {
1081 let tok = self.tokenizer.peek_token()?;
1082
1083 match tok {
1084 Token::RightParen | Token::RightBracket => {
1085 self.tokenizer.next_token()?; break;
1087 }
1088
1089 Token::Dot => {
1090 if elements.is_empty() {
1094 let dot_pos = self.tokenizer.position();
1095 return Err(self.error(
1096 "Invalid syntax: dot cannot appear immediately after opening parenthesis\n\
1097 Note: Identifiers cannot start with '.' (dot character is reserved for dotted pairs)\n\
1098 Example of valid dotted pair: (a . b)\n\
1099 Example of invalid syntax: (.gitignore)".to_string(),
1100 dot_pos,
1101 ));
1102 }
1103
1104 self.tokenizer.next_token()?; dotted_tail = Some(self.parse_expr()?);
1108
1109 let tok = self.tokenizer.next_token()?;
1111 if !matches!(tok, Token::RightParen | Token::RightBracket) {
1112 return Err(self.error(
1113 format!("Expected ) after dotted tail, got {}", tok),
1114 start_pos,
1115 ));
1116 }
1117 break;
1118 }
1119
1120 Token::Eof => {
1121 return Err(self.error(
1122 "Unexpected EOF in list".to_string(),
1123 start_pos,
1124 ))
1125 }
1126
1127 _ => {
1128 let elem_pos = self.tokenizer.position();
1131 elements.push(self.parse_expr()?);
1132 element_positions.push(elem_pos);
1133 }
1134 }
1135 }
1136
1137 let mut result = dotted_tail.unwrap_or(Value::Nil);
1139 for (elem, elem_pos) in elements.into_iter().zip(element_positions.into_iter()).rev() {
1140 result = Value::cons_with_pos(elem, result, elem_pos);
1141 }
1142
1143 Ok(result)
1144 }
1145
1146 fn parse_vector(&mut self, start_pos: Position) -> ParseResult<Value> {
1148 let mut elements = Vec::new();
1149
1150 loop {
1151 let tok = self.tokenizer.peek_token()?;
1152
1153 match tok {
1154 Token::RightParen => {
1155 self.tokenizer.next_token()?; break;
1157 }
1158
1159 Token::Eof => {
1160 return Err(self.error(
1161 "Unexpected EOF in vector".to_string(),
1162 start_pos,
1163 ))
1164 }
1165
1166 _ => {
1167 elements.push(self.parse_expr()?);
1168 }
1169 }
1170 }
1171
1172 Ok(Value::vector(elements))
1173 }
1174}
1175
1176#[cfg(test)]
1181mod tests {
1182 use super::*;
1183
1184 #[test]
1185 fn test_tokenize_simple() {
1186 let mut tok = Tokenizer::new("(+ 1 2)");
1187 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1188 assert_eq!(tok.next_token().unwrap(), Token::Symbol("+".to_string()));
1189 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1190 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1191 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1192 assert_eq!(tok.next_token().unwrap(), Token::Eof);
1193 }
1194
1195 #[test]
1196 fn test_tokenize_whitespace_agnostic() {
1197 let input = r#"(let ((x 1)
1199 (y 2))
1200 (+ x y))"#;
1201 let mut tok = Tokenizer::new(input);
1202
1203 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1204 assert_eq!(tok.next_token().unwrap(), Token::Symbol("let".to_string()));
1205 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1206 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1207 assert_eq!(tok.next_token().unwrap(), Token::Symbol("x".to_string()));
1208 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1209 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1210 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1212 assert_eq!(tok.next_token().unwrap(), Token::Symbol("y".to_string()));
1213 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1214 }
1215
1216 #[test]
1217 fn test_tokenize_strings() {
1218 let mut tok = Tokenizer::new(r#""hello world""#);
1219 assert_eq!(
1220 tok.next_token().unwrap(),
1221 Token::String("hello world".to_string())
1222 );
1223
1224 let mut tok = Tokenizer::new(r#""with\nnewline""#);
1225 assert_eq!(
1226 tok.next_token().unwrap(),
1227 Token::String("with\nnewline".to_string())
1228 );
1229 }
1230
1231 #[test]
1232 fn test_tokenize_cdata() {
1233 let mut tok = Tokenizer::new(r#"<![CDATA[<!DOCTYPE HTML>]]>"#);
1235 assert_eq!(
1236 tok.next_token().unwrap(),
1237 Token::String("<!DOCTYPE HTML>".to_string())
1238 );
1239
1240 let mut tok = Tokenizer::new("<![CDATA[\nLine 1\nLine 2\n]]>");
1242 assert_eq!(
1243 tok.next_token().unwrap(),
1244 Token::String("\nLine 1\nLine 2\n".to_string())
1245 );
1246
1247 let mut tok = Tokenizer::new("(define x <![CDATA[test]]>)");
1249 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1250 assert_eq!(tok.next_token().unwrap(), Token::Symbol("define".to_string()));
1251 assert_eq!(tok.next_token().unwrap(), Token::Symbol("x".to_string()));
1252 assert_eq!(tok.next_token().unwrap(), Token::String("test".to_string()));
1253 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1254 }
1255
1256 #[test]
1257 fn test_tokenize_comments() {
1258 let mut tok = Tokenizer::new("(+ 1 ; comment\n 2)");
1259 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1260 assert_eq!(tok.next_token().unwrap(), Token::Symbol("+".to_string()));
1261 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1262 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1264 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1265 }
1266
1267 #[test]
1268 fn test_tokenize_block_comments() {
1269 let mut tok = Tokenizer::new("(+ 1 #| block comment |# 2)");
1270 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1271 assert_eq!(tok.next_token().unwrap(), Token::Symbol("+".to_string()));
1272 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1273 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1275 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1276 }
1277
1278 #[test]
1279 fn test_tokenize_booleans() {
1280 let mut tok = Tokenizer::new("#t #f");
1281 assert_eq!(tok.next_token().unwrap(), Token::Bool(true));
1282 assert_eq!(tok.next_token().unwrap(), Token::Bool(false));
1283 }
1284
1285 #[test]
1286 fn test_tokenize_characters() {
1287 let mut tok = Tokenizer::new(r#"#\a #\space #\newline"#);
1288 assert_eq!(tok.next_token().unwrap(), Token::Char('a'));
1289 assert_eq!(tok.next_token().unwrap(), Token::Char(' '));
1290 assert_eq!(tok.next_token().unwrap(), Token::Char('\n'));
1291 }
1292
1293 #[test]
1294 fn test_tokenize_hex_numbers() {
1295 let mut tok = Tokenizer::new("#xff");
1297 assert_eq!(tok.next_token().unwrap(), Token::Integer(255));
1298
1299 let mut tok = Tokenizer::new("#X10");
1301 assert_eq!(tok.next_token().unwrap(), Token::Integer(16));
1302
1303 let mut tok = Tokenizer::new("#xDEADBEEF");
1305 assert_eq!(tok.next_token().unwrap(), Token::Integer(0xDEADBEEF));
1306
1307 let mut tok = Tokenizer::new("#x0");
1309 assert_eq!(tok.next_token().unwrap(), Token::Integer(0));
1310 }
1311
1312 #[test]
1313 fn test_tokenize_octal_numbers() {
1314 let mut tok = Tokenizer::new("#o77");
1316 assert_eq!(tok.next_token().unwrap(), Token::Integer(63));
1317
1318 let mut tok = Tokenizer::new("#O10");
1320 assert_eq!(tok.next_token().unwrap(), Token::Integer(8));
1321
1322 let mut tok = Tokenizer::new("#o0");
1324 assert_eq!(tok.next_token().unwrap(), Token::Integer(0));
1325
1326 let mut tok = Tokenizer::new("#o777");
1328 assert_eq!(tok.next_token().unwrap(), Token::Integer(511));
1329 }
1330
1331 #[test]
1332 fn test_tokenize_binary_numbers() {
1333 let mut tok = Tokenizer::new("#b1010");
1335 assert_eq!(tok.next_token().unwrap(), Token::Integer(10));
1336
1337 let mut tok = Tokenizer::new("#B1111");
1339 assert_eq!(tok.next_token().unwrap(), Token::Integer(15));
1340
1341 let mut tok = Tokenizer::new("#b0");
1343 assert_eq!(tok.next_token().unwrap(), Token::Integer(0));
1344
1345 let mut tok = Tokenizer::new("#b11111111");
1347 assert_eq!(tok.next_token().unwrap(), Token::Integer(255));
1348 }
1349
1350 #[test]
1351 fn test_tokenize_quote() {
1352 let mut tok = Tokenizer::new("'(1 2)");
1353 assert_eq!(tok.next_token().unwrap(), Token::Quote);
1354 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1355 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1356 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1357 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1358 }
1359
1360 #[test]
1361 fn test_error_positions() {
1362 let mut tok = Tokenizer::new("(+ 1\n \"unclosed string");
1363 tok.next_token().unwrap(); tok.next_token().unwrap(); tok.next_token().unwrap(); let err = tok.next_token().unwrap_err();
1368 assert_eq!(err.position.line, 2); assert!(err.message.contains("EOF in string"));
1370 }
1371
1372 #[test]
1377 fn test_parse_integer() {
1378 let mut parser = Parser::new("42");
1379 let val = parser.parse().unwrap();
1380 assert!(val.is_integer());
1381 if let Value::Integer(n) = val {
1382 assert_eq!(n, 42);
1383 }
1384 }
1385
1386 #[test]
1387 fn test_parse_simple_list() {
1388 let mut parser = Parser::new("(+ 1 2)");
1389 let val = parser.parse().unwrap();
1390 assert!(val.is_list());
1391
1392 if let Value::Pair(ref p) = val {
1394 let pair = p.borrow();
1395 assert!(pair.car.is_symbol());
1396 }
1397 }
1398
1399 #[test]
1400 fn test_parse_nested_list() {
1401 let mut parser = Parser::new("(+ (* 2 3) 4)");
1402 let val = parser.parse().unwrap();
1403 assert!(val.is_list());
1404 }
1405
1406 #[test]
1407 fn test_parse_quoted() {
1408 let mut parser = Parser::new("'(1 2 3)");
1409 let val = parser.parse().unwrap();
1410
1411 if let Value::Pair(ref p) = val {
1413 let pair = p.borrow();
1414 if let Value::Symbol(s) = &pair.car {
1415 assert_eq!(&**s, "quote");
1416 } else {
1417 panic!("Expected symbol 'quote'");
1418 }
1419 } else {
1420 panic!("Expected pair");
1421 }
1422 }
1423
1424 #[test]
1425 fn test_parse_vector() {
1426 let mut parser = Parser::new("#(1 2 3)");
1427 let val = parser.parse().unwrap();
1428 assert!(val.is_vector());
1429
1430 if let Value::Vector(ref v) = val {
1431 let vec = v.borrow();
1432 assert_eq!(vec.len(), 3);
1433 }
1434 }
1435
1436 #[test]
1437 fn test_parse_dotted_list() {
1438 let mut parser = Parser::new("(1 . 2)");
1439 let val = parser.parse().unwrap();
1440
1441 if let Value::Pair(ref p) = val {
1442 let pair = p.borrow();
1443 assert!(matches!(pair.car, Value::Integer(1)));
1444 assert!(matches!(pair.cdr, Value::Integer(2)));
1445 } else {
1446 panic!("Expected pair");
1447 }
1448 }
1449
1450 #[test]
1451 fn test_parse_string() {
1452 let mut parser = Parser::new(r#""hello world""#);
1453 let val = parser.parse().unwrap();
1454 assert!(val.is_string());
1455 }
1456
1457 #[test]
1458 fn test_parse_bool() {
1459 let mut parser = Parser::new("#t");
1460 let val = parser.parse().unwrap();
1461 assert!(val.is_bool());
1462 assert!(val.is_true());
1463
1464 let mut parser = Parser::new("#f");
1465 let val = parser.parse().unwrap();
1466 assert!(val.is_bool());
1467 assert!(!val.is_true());
1468 }
1469
1470 #[test]
1471 fn test_parse_multiline_let() {
1472 let input = r#"
1474 (let ((x 1)
1475 (y 2))
1476 (+ x y))
1477 "#;
1478
1479 let mut parser = Parser::new(input);
1480 let val = parser.parse().unwrap();
1481 assert!(val.is_list());
1482
1483 }
1486
1487 #[test]
1488 fn test_parse_all() {
1489 let input = "(define x 1) (define y 2) (+ x y)";
1490 let mut parser = Parser::new(input);
1491 let exprs = parser.parse_all().unwrap();
1492 assert_eq!(exprs.len(), 3);
1493 }
1494
1495 #[test]
1496 fn test_parse_empty_list() {
1497 let mut parser = Parser::new("()");
1498 let val = parser.parse().unwrap();
1499 assert!(val.is_nil());
1500 }
1501
1502 #[test]
1503 fn test_parse_keyword() {
1504 let mut parser = Parser::new("#:foo");
1505 let val = parser.parse().unwrap();
1506 if let Value::Keyword(ref k) = val {
1507 assert_eq!(&**k, "foo");
1508 } else {
1509 panic!("Expected keyword");
1510 }
1511 }
1512
1513 #[test]
1514 fn test_parse_error_with_filename() {
1515 let mut parser = Parser::new_with_filename("(define x", "test.scm".to_string());
1517 let err = parser.parse().unwrap_err();
1518 let err_string = err.to_string();
1519
1520 assert!(err_string.contains("test.scm"), "Error should contain filename: {}", err_string);
1522 assert!(err_string.contains("1:1"), "Error should contain position: {}", err_string);
1524 assert!(err_string.contains("test.scm:1:1:E:"), "Error should use OpenJade format: {}", err_string);
1526 }
1527
1528 #[test]
1529 fn test_parse_error_without_filename() {
1530 let mut parser = Parser::new("(define x");
1532 let err = parser.parse().unwrap_err();
1533 let err_string = err.to_string();
1534
1535 assert!(!err_string.contains("test.scm"), "Error should not contain filename when not provided");
1537 assert!(err_string.contains("1:1"), "Error should still contain position: {}", err_string);
1539 }
1540
1541 #[test]
1542 fn test_parse_error_dot_after_open_paren() {
1543 let mut parser = Parser::new("(.gitignore)");
1546 let err = parser.parse().unwrap_err();
1547 let err_string = err.to_string();
1548
1549 assert!(err_string.contains("dot cannot appear immediately after opening parenthesis"),
1551 "Error should mention invalid dot position: {}", err_string);
1552 assert!(err_string.contains("Identifiers cannot start with '.'"),
1554 "Error should explain why: {}", err_string);
1555 }
1556
1557 #[test]
1558 fn test_parse_error_dot_function_definition() {
1559 let mut parser = Parser::new("(define (.gitignore) (list))");
1561 let err = parser.parse().unwrap_err();
1562 let err_string = err.to_string();
1563
1564 assert!(err_string.contains("dot cannot appear immediately after opening parenthesis"),
1566 "Error should mention invalid dot position: {}", err_string);
1567 }
1568
1569 #[test]
1570 fn test_parse_valid_dotted_pair_still_works() {
1571 let mut parser = Parser::new("(a . b)");
1573 let val = parser.parse().unwrap();
1574
1575 if let Value::Pair(ref p) = val {
1577 let pair = p.borrow();
1578 if let Value::Symbol(s) = &pair.car {
1579 assert_eq!(&**s, "a");
1580 } else {
1581 panic!("Expected symbol 'a'");
1582 }
1583 if let Value::Symbol(s) = &pair.cdr {
1584 assert_eq!(&**s, "b");
1585 } else {
1586 panic!("Expected symbol 'b'");
1587 }
1588 } else {
1589 panic!("Expected pair");
1590 }
1591 }
1592}
1593