1use crate::scheme::value::Value;
40use std::fmt;
41
42#[derive(Debug, Clone, PartialEq)]
48pub enum Token {
49 Integer(i64),
51 Real(f64),
52 String(String),
53 Char(char),
54 Symbol(String),
55 Keyword(String),
56 Bool(bool),
57
58 LeftParen, RightParen, LeftBracket, RightBracket, Dot, Quote, Quasiquote, Unquote, UnquoteSplicing, VectorStart, Eof,
76}
77
78impl fmt::Display for Token {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 Token::Integer(n) => write!(f, "{}", n),
82 Token::Real(n) => write!(f, "{}", n),
83 Token::String(s) => write!(f, "\"{}\"", s),
84 Token::Char(ch) => write!(f, "#\\{}", ch),
85 Token::Symbol(s) => write!(f, "{}", s),
86 Token::Keyword(s) => write!(f, "#:{}", s),
87 Token::Bool(b) => write!(f, "{}", if *b { "#t" } else { "#f" }),
88 Token::LeftParen => write!(f, "("),
89 Token::RightParen => write!(f, ")"),
90 Token::LeftBracket => write!(f, "["),
91 Token::RightBracket => write!(f, "]"),
92 Token::Dot => write!(f, "."),
93 Token::Quote => write!(f, "'"),
94 Token::Quasiquote => write!(f, "`"),
95 Token::Unquote => write!(f, ","),
96 Token::UnquoteSplicing => write!(f, ",@"),
97 Token::VectorStart => write!(f, "#("),
98 Token::Eof => write!(f, "<EOF>"),
99 }
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, gc::Trace, gc::Finalize)]
114pub struct Position {
115 pub line: usize,
116 pub column: usize,
117}
118
119impl Position {
120 pub fn new() -> Self {
121 Position { line: 1, column: 1 }
122 }
123}
124
125impl Default for Position {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131impl fmt::Display for Position {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(f, "{}:{}", self.line, self.column)
134 }
135}
136
137#[derive(Debug, Clone)]
143pub struct ParseError {
144 pub message: String,
145 pub position: Position,
146 pub filename: Option<String>,
147}
148
149impl ParseError {
150 pub fn new(message: String, position: Position) -> Self {
151 ParseError { message, position, filename: None }
152 }
153
154 pub fn with_filename(message: String, position: Position, filename: String) -> Self {
155 ParseError { message, position, filename: Some(filename) }
156 }
157}
158
159impl fmt::Display for ParseError {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 if let Some(ref filename) = self.filename {
162 write!(f, "{}:{}:E: {}", filename, self.position, self.message)
163 } else {
164 write!(f, "Parse error at {}: {}", self.position, self.message)
165 }
166 }
167}
168
169impl std::error::Error for ParseError {}
170
171pub type ParseResult<T> = Result<T, ParseError>;
172
173pub struct Tokenizer {
193 input: Vec<char>,
195
196 pos: usize,
198
199 line: usize,
201
202 column: usize,
204
205 peeked: Option<Token>,
207
208 filename: Option<String>,
210}
211
212impl Tokenizer {
213 pub fn new(input: &str) -> Self {
215 Tokenizer {
216 input: input.chars().collect(),
217 pos: 0,
218 line: 1,
219 column: 1,
220 peeked: None,
221 filename: None,
222 }
223 }
224
225 pub fn new_with_filename(input: &str, filename: String) -> Self {
227 Tokenizer {
228 input: input.chars().collect(),
229 pos: 0,
230 line: 1,
231 column: 1,
232 peeked: None,
233 filename: Some(filename),
234 }
235 }
236
237 fn error(&self, message: String, position: Position) -> ParseError {
239 if let Some(ref filename) = self.filename {
240 ParseError::with_filename(message, position, filename.clone())
241 } else {
242 ParseError::new(message, position)
243 }
244 }
245
246 pub fn position(&self) -> Position {
248 Position {
249 line: self.line,
250 column: self.column,
251 }
252 }
253
254 fn peek_char(&self) -> Option<char> {
256 if self.pos < self.input.len() {
257 Some(self.input[self.pos])
258 } else {
259 None
260 }
261 }
262
263 fn peek_char_at(&self, offset: usize) -> Option<char> {
265 let index = self.pos + offset;
266 if index < self.input.len() {
267 Some(self.input[index])
268 } else {
269 None
270 }
271 }
272
273 fn next_char(&mut self) -> Option<char> {
275 if self.pos < self.input.len() {
276 let ch = self.input[self.pos];
277 self.pos += 1;
278
279 if ch == '\n' {
281 self.line += 1;
282 self.column = 1;
283 } else {
284 self.column += 1;
285 }
286
287 Some(ch)
288 } else {
289 None
290 }
291 }
292
293 fn skip_whitespace(&mut self) {
297 loop {
298 match self.peek_char() {
299 Some(ch) if ch.is_whitespace() => {
301 self.next_char();
302 }
303
304 Some(';') => {
306 self.next_char();
307 while let Some(ch) = self.peek_char() {
308 self.next_char();
309 if ch == '\n' {
310 break;
311 }
312 }
313 }
314
315 Some('#') if self.peek_char_at(1) == Some('|') => {
317 self.next_char(); self.next_char(); let mut depth = 1;
322 while depth > 0 {
323 match self.next_char() {
324 Some('|') if self.peek_char() == Some('#') => {
325 self.next_char(); depth -= 1;
327 }
328 Some('#') if self.peek_char() == Some('|') => {
329 self.next_char(); depth += 1; }
332 Some(_) => {} None => break, }
335 }
336 }
337
338 _ => break,
340 }
341 }
342 }
343
344 fn is_delimiter(ch: char) -> bool {
346 ch.is_whitespace()
347 || matches!(
348 ch,
349 '(' | ')' | '[' | ']' | '"' | ';' | ',' | '`' | '\''
350 )
351 }
352
353 fn parse_number(&mut self, start_pos: Position) -> ParseResult<Token> {
355 let mut num_str = String::new();
356
357 while let Some(ch) = self.peek_char() {
359 if ch.is_ascii_digit() || matches!(ch, '.' | 'e' | 'E' | '+' | '-') {
360 num_str.push(ch);
361 self.next_char();
362 } else if Self::is_delimiter(ch) {
363 break;
364 } else {
365 return Err(self.error(
367 format!("Invalid character in number: {}", ch),
368 start_pos,
369 ));
370 }
371 }
372
373 if let Ok(n) = num_str.parse::<i64>() {
375 return Ok(Token::Integer(n));
376 }
377
378 if let Ok(n) = num_str.parse::<f64>() {
380 return Ok(Token::Real(n));
381 }
382
383 Err(self.error(
384 format!("Invalid number: {}", num_str),
385 start_pos,
386 ))
387 }
388
389 fn parse_hex_number(&mut self, start_pos: Position) -> ParseResult<Token> {
391 let mut num_str = String::new();
392
393 while let Some(ch) = self.peek_char() {
394 if ch.is_ascii_hexdigit() {
395 num_str.push(ch);
396 self.next_char();
397 } else if Self::is_delimiter(ch) {
398 break;
399 } else {
400 return Err(self.error(
401 format!("Invalid character in hex number: {}", ch),
402 start_pos,
403 ));
404 }
405 }
406
407 if num_str.is_empty() {
408 return Err(self.error("Empty hex number".to_string(), start_pos));
409 }
410
411 i64::from_str_radix(&num_str, 16)
412 .map(Token::Integer)
413 .map_err(|_| self.error(format!("Invalid hex number: {}", num_str), start_pos))
414 }
415
416 fn parse_octal_number(&mut self, start_pos: Position) -> ParseResult<Token> {
418 let mut num_str = String::new();
419
420 while let Some(ch) = self.peek_char() {
421 if ch.is_digit(8) {
422 num_str.push(ch);
423 self.next_char();
424 } else if Self::is_delimiter(ch) {
425 break;
426 } else {
427 return Err(self.error(
428 format!("Invalid character in octal number: {}", ch),
429 start_pos,
430 ));
431 }
432 }
433
434 if num_str.is_empty() {
435 return Err(self.error("Empty octal number".to_string(), start_pos));
436 }
437
438 i64::from_str_radix(&num_str, 8)
439 .map(Token::Integer)
440 .map_err(|_| self.error(format!("Invalid octal number: {}", num_str), start_pos))
441 }
442
443 fn parse_binary_number(&mut self, start_pos: Position) -> ParseResult<Token> {
445 let mut num_str = String::new();
446
447 while let Some(ch) = self.peek_char() {
448 if matches!(ch, '0' | '1') {
449 num_str.push(ch);
450 self.next_char();
451 } else if Self::is_delimiter(ch) {
452 break;
453 } else {
454 return Err(self.error(
455 format!("Invalid character in binary number: {}", ch),
456 start_pos,
457 ));
458 }
459 }
460
461 if num_str.is_empty() {
462 return Err(self.error("Empty binary number".to_string(), start_pos));
463 }
464
465 i64::from_str_radix(&num_str, 2)
466 .map(Token::Integer)
467 .map_err(|_| self.error(format!("Invalid binary number: {}", num_str), start_pos))
468 }
469
470 fn parse_symbol(&mut self) -> String {
472 let mut sym = String::new();
473
474 while let Some(ch) = self.peek_char() {
475 if Self::is_delimiter(ch) {
476 break;
477 }
478 sym.push(ch);
479 self.next_char();
480 }
481
482 sym
483 }
484
485 fn parse_string(&mut self, start_pos: Position) -> ParseResult<String> {
487 self.next_char(); let mut result = String::new();
490
491 loop {
492 match self.next_char() {
493 Some('"') => {
494 let normalized = result.replace("\r\n", "\n");
498 return Ok(normalized);
499 }
500 Some('\\') => {
501 match self.next_char() {
503 Some('n') => result.push('\n'),
504 Some('t') => result.push('\t'),
505 Some('r') => result.push('\r'),
506 Some('\\') => result.push('\\'),
507 Some('"') => result.push('"'),
508 Some(ch) => result.push(ch), None => {
510 return Err(self.error(
511 "Unexpected EOF in string escape".to_string(),
512 start_pos,
513 ))
514 }
515 }
516 }
517 Some(ch) => {
518 result.push(ch);
519 }
520 None => {
521 return Err(self.error(
522 "Unexpected EOF in string".to_string(),
523 start_pos,
524 ))
525 }
526 }
527 }
528 }
529
530 fn parse_char(&mut self, start_pos: Position) -> ParseResult<char> {
533 if self.next_char() != Some('\\') {
535 return Err(self.error(
536 "Expected \\ after # in character literal".to_string(),
537 start_pos,
538 ));
539 }
540
541 let mut name = String::new();
543 while let Some(ch) = self.peek_char() {
544 if Self::is_delimiter(ch) {
545 break;
546 }
547 name.push(ch);
548 self.next_char();
549 }
550
551 if name.is_empty() {
552 return Err(self.error(
553 "Empty character literal".to_string(),
554 start_pos,
555 ));
556 }
557
558 match name.as_str() {
560 "space" => Ok(' '),
561 "newline" => Ok('\n'),
562 "tab" => Ok('\t'),
563 "return" => Ok('\r'),
564 s if s.starts_with("U-") => {
567 let hex_str = &s[2..]; u32::from_str_radix(hex_str, 16)
569 .ok()
570 .and_then(std::char::from_u32)
571 .ok_or_else(|| self.error(
572 format!("Invalid Unicode character literal: #\\{}", name),
573 start_pos,
574 ))
575 }
576 s if s.chars().count() == 1 => Ok(s.chars().next().unwrap()),
579 _ => Err(self.error(
580 format!("Invalid character literal: #\\{}", name),
581 start_pos,
582 )),
583 }
584 }
585
586 fn parse_cdata_string(&mut self, start_pos: Position) -> ParseResult<Token> {
590 for _ in 0..9 {
592 self.next_char();
593 }
594
595 let mut content = String::new();
596
597 loop {
599 match self.peek_char() {
600 None => {
601 return Err(self.error(
602 "Unclosed CDATA section: missing ]]>".to_string(),
603 start_pos,
604 ));
605 }
606 Some(']') => {
607 if self.pos + 2 < self.input.len()
609 && self.input[self.pos] == ']'
610 && self.input[self.pos + 1] == ']'
611 && self.input[self.pos + 2] == '>'
612 {
613 self.next_char(); self.next_char(); self.next_char(); break;
618 } else {
619 content.push(']');
620 self.next_char();
621 }
622 }
623 Some(ch) => {
624 content.push(ch);
625 self.next_char();
626 }
627 }
628 }
629
630 Ok(Token::String(content))
631 }
632
633 pub fn next_token(&mut self) -> ParseResult<Token> {
635 if let Some(tok) = self.peeked.take() {
637 return Ok(tok);
638 }
639
640 self.skip_whitespace();
642
643 let start_pos = self.position();
644
645 match self.peek_char() {
646 None => Ok(Token::Eof),
647
648 Some('(') => {
649 self.next_char();
650 Ok(Token::LeftParen)
651 }
652
653 Some(')') => {
654 self.next_char();
655 Ok(Token::RightParen)
656 }
657
658 Some('[') => {
659 self.next_char();
660 Ok(Token::LeftBracket)
661 }
662
663 Some(']') => {
664 self.next_char();
665 Ok(Token::RightBracket)
666 }
667
668 Some('\'') => {
669 self.next_char();
670 Ok(Token::Quote)
671 }
672
673 Some('`') => {
674 self.next_char();
675 Ok(Token::Quasiquote)
676 }
677
678 Some(',') => {
679 self.next_char();
680 if self.peek_char() == Some('@') {
682 self.next_char();
683 Ok(Token::UnquoteSplicing)
684 } else {
685 Ok(Token::Unquote)
686 }
687 }
688
689 Some('"') => {
690 let s = self.parse_string(start_pos)?;
691 Ok(Token::String(s))
692 }
693
694 Some('#') => {
695 self.next_char(); match self.peek_char() {
697 Some('t') => {
698 self.next_char();
699 Ok(Token::Bool(true))
700 }
701 Some('f') => {
702 self.next_char();
703 Ok(Token::Bool(false))
704 }
705 Some('(') => {
706 self.next_char();
707 Ok(Token::VectorStart)
708 }
709 Some('\\') => {
710 let ch = self.parse_char(start_pos)?;
711 Ok(Token::Char(ch))
712 }
713 Some(':') => {
714 self.next_char(); let name = self.parse_symbol();
716 Ok(Token::Keyword(name))
717 }
718 Some('x') | Some('X') => {
719 self.next_char(); self.parse_hex_number(start_pos)
721 }
722 Some('o') | Some('O') => {
723 self.next_char(); self.parse_octal_number(start_pos)
725 }
726 Some('b') | Some('B') => {
727 self.next_char(); self.parse_binary_number(start_pos)
729 }
730 _ => Err(self.error(
731 format!("Invalid # syntax: #{:?}", self.peek_char()),
732 start_pos,
733 )),
734 }
735 }
736
737 Some(ch) if ch.is_ascii_digit() => self.parse_number(start_pos),
738
739 Some('+') | Some('-') => {
740 if let Some(next) = self.peek_char_at(1) {
742 if next.is_ascii_digit() {
743 self.parse_number(start_pos)
744 } else {
745 let sym = self.parse_symbol();
746 Ok(Token::Symbol(sym))
747 }
748 } else {
749 let sym = self.parse_symbol();
750 Ok(Token::Symbol(sym))
751 }
752 }
753
754 Some('.') => {
755 if let Some(next) = self.peek_char_at(1) {
757 if next.is_ascii_digit() {
758 self.parse_number(start_pos)
759 } else {
760 self.next_char();
761 Ok(Token::Dot)
762 }
763 } else {
764 self.next_char();
765 Ok(Token::Dot)
766 }
767 }
768
769 Some('<') => {
770 let cdata_prefix = ['<', '!', '[', 'C', 'D', 'A', 'T', 'A', '['];
773 let is_cdata = self.pos + cdata_prefix.len() <= self.input.len()
774 && self.input[self.pos..self.pos + cdata_prefix.len()] == cdata_prefix;
775
776 if is_cdata {
777 self.parse_cdata_string(start_pos)
778 } else {
779 let sym = self.parse_symbol();
781 Ok(Token::Symbol(sym))
782 }
783 }
784
785 Some(_) => {
786 let sym = self.parse_symbol();
788
789 if sym.ends_with(':') {
791 let keyword_name = sym[..sym.len()-1].to_string();
792 Ok(Token::Keyword(keyword_name))
793 } else {
794 Ok(Token::Symbol(sym))
795 }
796 }
797 }
798 }
799
800 pub fn peek_token(&mut self) -> ParseResult<&Token> {
802 if self.peeked.is_none() {
803 let tok = self.next_token()?;
804 self.peeked = Some(tok);
805 }
806 Ok(self.peeked.as_ref().unwrap())
807 }
808}
809
810pub struct Parser {
825 tokenizer: Tokenizer,
826 filename: Option<String>,
827}
828
829impl Parser {
830 pub fn new(input: &str) -> Self {
832 Parser {
833 tokenizer: Tokenizer::new(input),
834 filename: None,
835 }
836 }
837
838 pub fn new_with_filename(input: &str, filename: String) -> Self {
840 Parser {
841 tokenizer: Tokenizer::new_with_filename(input, filename.clone()),
842 filename: Some(filename),
843 }
844 }
845
846 fn error(&self, message: String, position: Position) -> ParseError {
848 if let Some(ref filename) = self.filename {
849 ParseError::with_filename(message, position, filename.clone())
850 } else {
851 ParseError::new(message, position)
852 }
853 }
854
855 pub fn parse(&mut self) -> ParseResult<Value> {
859 self.parse_expr()
860 }
861
862 pub fn peek_token(&mut self) -> ParseResult<&Token> {
867 self.tokenizer.peek_token()
868 }
869
870 pub fn current_position(&self) -> Position {
875 self.tokenizer.position()
876 }
877
878 pub fn parse_all(&mut self) -> ParseResult<Vec<Value>> {
882 let mut exprs = Vec::new();
883
884 loop {
885 let tok = self.tokenizer.peek_token()?;
886 if *tok == Token::Eof {
887 break;
888 }
889 exprs.push(self.parse_expr()?);
890 }
891
892 Ok(exprs)
893 }
894
895 fn parse_expr(&mut self) -> ParseResult<Value> {
897 let start_pos = self.tokenizer.position();
898 let tok = self.tokenizer.next_token()?;
899
900 match tok {
901 Token::Integer(n) => Ok(Value::integer(n)),
903 Token::Real(n) => Ok(Value::real(n)),
904 Token::String(s) => Ok(Value::string(s)),
905 Token::Char(ch) => Ok(Value::char(ch)),
906 Token::Bool(b) => Ok(Value::bool(b)),
907 Token::Symbol(s) => Ok(Value::symbol(&s)),
908 Token::Keyword(s) => Ok(Value::keyword(&s)),
909
910 Token::LeftParen | Token::LeftBracket => self.parse_list(start_pos),
912
913 Token::VectorStart => self.parse_vector(start_pos),
915
916 Token::Quote => {
918 let quoted = self.parse_expr()?;
919 Ok(Value::cons_with_pos(Value::symbol("quote"), Value::cons(quoted, Value::Nil), start_pos))
920 }
921
922 Token::Quasiquote => {
924 let quoted = self.parse_expr()?;
925 Ok(Value::cons_with_pos(
926 Value::symbol("quasiquote"),
927 Value::cons(quoted, Value::Nil),
928 start_pos
929 ))
930 }
931
932 Token::Unquote => {
934 let quoted = self.parse_expr()?;
935 Ok(Value::cons_with_pos(
936 Value::symbol("unquote"),
937 Value::cons(quoted, Value::Nil),
938 start_pos
939 ))
940 }
941
942 Token::UnquoteSplicing => {
944 let quoted = self.parse_expr()?;
945 Ok(Value::cons_with_pos(
946 Value::symbol("unquote-splicing"),
947 Value::cons(quoted, Value::Nil),
948 start_pos
949 ))
950 }
951
952 Token::RightParen | Token::RightBracket => Err(self.error(
954 format!("Unexpected closing delimiter: {}", tok),
955 start_pos,
956 )),
957
958 Token::Dot => Err(self.error(
959 "Unexpected dot outside of list".to_string(),
960 start_pos,
961 )),
962
963 Token::Eof => Err(self.error(
964 "Unexpected end of input".to_string(),
965 start_pos,
966 )),
967 }
968 }
969
970 fn parse_list(&mut self, start_pos: Position) -> ParseResult<Value> {
972 let mut elements = Vec::new();
973 let mut element_positions = Vec::new();
974 let mut dotted_tail = None;
975
976 loop {
977 let tok = self.tokenizer.peek_token()?;
978
979 match tok {
980 Token::RightParen | Token::RightBracket => {
981 self.tokenizer.next_token()?; break;
983 }
984
985 Token::Dot => {
986 if elements.is_empty() {
990 let dot_pos = self.tokenizer.position();
991 return Err(self.error(
992 "Invalid syntax: dot cannot appear immediately after opening parenthesis\n\
993 Note: Identifiers cannot start with '.' (dot character is reserved for dotted pairs)\n\
994 Example of valid dotted pair: (a . b)\n\
995 Example of invalid syntax: (.gitignore)".to_string(),
996 dot_pos,
997 ));
998 }
999
1000 self.tokenizer.next_token()?; dotted_tail = Some(self.parse_expr()?);
1004
1005 let tok = self.tokenizer.next_token()?;
1007 if !matches!(tok, Token::RightParen | Token::RightBracket) {
1008 return Err(self.error(
1009 format!("Expected ) after dotted tail, got {}", tok),
1010 start_pos,
1011 ));
1012 }
1013 break;
1014 }
1015
1016 Token::Eof => {
1017 return Err(self.error(
1018 "Unexpected EOF in list".to_string(),
1019 start_pos,
1020 ))
1021 }
1022
1023 _ => {
1024 let elem_pos = self.tokenizer.position();
1027 elements.push(self.parse_expr()?);
1028 element_positions.push(elem_pos);
1029 }
1030 }
1031 }
1032
1033 let mut result = dotted_tail.unwrap_or(Value::Nil);
1035 for (elem, elem_pos) in elements.into_iter().zip(element_positions.into_iter()).rev() {
1036 result = Value::cons_with_pos(elem, result, elem_pos);
1037 }
1038
1039 Ok(result)
1040 }
1041
1042 fn parse_vector(&mut self, start_pos: Position) -> ParseResult<Value> {
1044 let mut elements = Vec::new();
1045
1046 loop {
1047 let tok = self.tokenizer.peek_token()?;
1048
1049 match tok {
1050 Token::RightParen => {
1051 self.tokenizer.next_token()?; break;
1053 }
1054
1055 Token::Eof => {
1056 return Err(self.error(
1057 "Unexpected EOF in vector".to_string(),
1058 start_pos,
1059 ))
1060 }
1061
1062 _ => {
1063 elements.push(self.parse_expr()?);
1064 }
1065 }
1066 }
1067
1068 Ok(Value::vector(elements))
1069 }
1070}
1071
1072#[cfg(test)]
1077mod tests {
1078 use super::*;
1079
1080 #[test]
1081 fn test_tokenize_simple() {
1082 let mut tok = Tokenizer::new("(+ 1 2)");
1083 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1084 assert_eq!(tok.next_token().unwrap(), Token::Symbol("+".to_string()));
1085 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1086 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1087 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1088 assert_eq!(tok.next_token().unwrap(), Token::Eof);
1089 }
1090
1091 #[test]
1092 fn test_tokenize_whitespace_agnostic() {
1093 let input = r#"(let ((x 1)
1095 (y 2))
1096 (+ x y))"#;
1097 let mut tok = Tokenizer::new(input);
1098
1099 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1100 assert_eq!(tok.next_token().unwrap(), Token::Symbol("let".to_string()));
1101 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1102 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1103 assert_eq!(tok.next_token().unwrap(), Token::Symbol("x".to_string()));
1104 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1105 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1106 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1108 assert_eq!(tok.next_token().unwrap(), Token::Symbol("y".to_string()));
1109 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1110 }
1111
1112 #[test]
1113 fn test_tokenize_strings() {
1114 let mut tok = Tokenizer::new(r#""hello world""#);
1115 assert_eq!(
1116 tok.next_token().unwrap(),
1117 Token::String("hello world".to_string())
1118 );
1119
1120 let mut tok = Tokenizer::new(r#""with\nnewline""#);
1121 assert_eq!(
1122 tok.next_token().unwrap(),
1123 Token::String("with\nnewline".to_string())
1124 );
1125 }
1126
1127 #[test]
1128 fn test_tokenize_cdata() {
1129 let mut tok = Tokenizer::new(r#"<![CDATA[<!DOCTYPE HTML>]]>"#);
1131 assert_eq!(
1132 tok.next_token().unwrap(),
1133 Token::String("<!DOCTYPE HTML>".to_string())
1134 );
1135
1136 let mut tok = Tokenizer::new("<![CDATA[\nLine 1\nLine 2\n]]>");
1138 assert_eq!(
1139 tok.next_token().unwrap(),
1140 Token::String("\nLine 1\nLine 2\n".to_string())
1141 );
1142
1143 let mut tok = Tokenizer::new("(define x <![CDATA[test]]>)");
1145 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1146 assert_eq!(tok.next_token().unwrap(), Token::Symbol("define".to_string()));
1147 assert_eq!(tok.next_token().unwrap(), Token::Symbol("x".to_string()));
1148 assert_eq!(tok.next_token().unwrap(), Token::String("test".to_string()));
1149 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1150 }
1151
1152 #[test]
1153 fn test_tokenize_comments() {
1154 let mut tok = Tokenizer::new("(+ 1 ; comment\n 2)");
1155 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1156 assert_eq!(tok.next_token().unwrap(), Token::Symbol("+".to_string()));
1157 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1158 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1160 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1161 }
1162
1163 #[test]
1164 fn test_tokenize_block_comments() {
1165 let mut tok = Tokenizer::new("(+ 1 #| block comment |# 2)");
1166 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1167 assert_eq!(tok.next_token().unwrap(), Token::Symbol("+".to_string()));
1168 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1169 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1171 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1172 }
1173
1174 #[test]
1175 fn test_tokenize_booleans() {
1176 let mut tok = Tokenizer::new("#t #f");
1177 assert_eq!(tok.next_token().unwrap(), Token::Bool(true));
1178 assert_eq!(tok.next_token().unwrap(), Token::Bool(false));
1179 }
1180
1181 #[test]
1182 fn test_tokenize_characters() {
1183 let mut tok = Tokenizer::new(r#"#\a #\space #\newline"#);
1184 assert_eq!(tok.next_token().unwrap(), Token::Char('a'));
1185 assert_eq!(tok.next_token().unwrap(), Token::Char(' '));
1186 assert_eq!(tok.next_token().unwrap(), Token::Char('\n'));
1187 }
1188
1189 #[test]
1190 fn test_tokenize_hex_numbers() {
1191 let mut tok = Tokenizer::new("#xff");
1193 assert_eq!(tok.next_token().unwrap(), Token::Integer(255));
1194
1195 let mut tok = Tokenizer::new("#X10");
1197 assert_eq!(tok.next_token().unwrap(), Token::Integer(16));
1198
1199 let mut tok = Tokenizer::new("#xDEADBEEF");
1201 assert_eq!(tok.next_token().unwrap(), Token::Integer(0xDEADBEEF));
1202
1203 let mut tok = Tokenizer::new("#x0");
1205 assert_eq!(tok.next_token().unwrap(), Token::Integer(0));
1206 }
1207
1208 #[test]
1209 fn test_tokenize_octal_numbers() {
1210 let mut tok = Tokenizer::new("#o77");
1212 assert_eq!(tok.next_token().unwrap(), Token::Integer(63));
1213
1214 let mut tok = Tokenizer::new("#O10");
1216 assert_eq!(tok.next_token().unwrap(), Token::Integer(8));
1217
1218 let mut tok = Tokenizer::new("#o0");
1220 assert_eq!(tok.next_token().unwrap(), Token::Integer(0));
1221
1222 let mut tok = Tokenizer::new("#o777");
1224 assert_eq!(tok.next_token().unwrap(), Token::Integer(511));
1225 }
1226
1227 #[test]
1228 fn test_tokenize_binary_numbers() {
1229 let mut tok = Tokenizer::new("#b1010");
1231 assert_eq!(tok.next_token().unwrap(), Token::Integer(10));
1232
1233 let mut tok = Tokenizer::new("#B1111");
1235 assert_eq!(tok.next_token().unwrap(), Token::Integer(15));
1236
1237 let mut tok = Tokenizer::new("#b0");
1239 assert_eq!(tok.next_token().unwrap(), Token::Integer(0));
1240
1241 let mut tok = Tokenizer::new("#b11111111");
1243 assert_eq!(tok.next_token().unwrap(), Token::Integer(255));
1244 }
1245
1246 #[test]
1247 fn test_tokenize_quote() {
1248 let mut tok = Tokenizer::new("'(1 2)");
1249 assert_eq!(tok.next_token().unwrap(), Token::Quote);
1250 assert_eq!(tok.next_token().unwrap(), Token::LeftParen);
1251 assert_eq!(tok.next_token().unwrap(), Token::Integer(1));
1252 assert_eq!(tok.next_token().unwrap(), Token::Integer(2));
1253 assert_eq!(tok.next_token().unwrap(), Token::RightParen);
1254 }
1255
1256 #[test]
1257 fn test_error_positions() {
1258 let mut tok = Tokenizer::new("(+ 1\n \"unclosed string");
1259 tok.next_token().unwrap(); tok.next_token().unwrap(); tok.next_token().unwrap(); let err = tok.next_token().unwrap_err();
1264 assert_eq!(err.position.line, 2); assert!(err.message.contains("EOF in string"));
1266 }
1267
1268 #[test]
1273 fn test_parse_integer() {
1274 let mut parser = Parser::new("42");
1275 let val = parser.parse().unwrap();
1276 assert!(val.is_integer());
1277 if let Value::Integer(n) = val {
1278 assert_eq!(n, 42);
1279 }
1280 }
1281
1282 #[test]
1283 fn test_parse_simple_list() {
1284 let mut parser = Parser::new("(+ 1 2)");
1285 let val = parser.parse().unwrap();
1286 assert!(val.is_list());
1287
1288 if let Value::Pair(ref p) = val {
1290 let pair = p.borrow();
1291 assert!(pair.car.is_symbol());
1292 }
1293 }
1294
1295 #[test]
1296 fn test_parse_nested_list() {
1297 let mut parser = Parser::new("(+ (* 2 3) 4)");
1298 let val = parser.parse().unwrap();
1299 assert!(val.is_list());
1300 }
1301
1302 #[test]
1303 fn test_parse_quoted() {
1304 let mut parser = Parser::new("'(1 2 3)");
1305 let val = parser.parse().unwrap();
1306
1307 if let Value::Pair(ref p) = val {
1309 let pair = p.borrow();
1310 if let Value::Symbol(s) = &pair.car {
1311 assert_eq!(&**s, "quote");
1312 } else {
1313 panic!("Expected symbol 'quote'");
1314 }
1315 } else {
1316 panic!("Expected pair");
1317 }
1318 }
1319
1320 #[test]
1321 fn test_parse_vector() {
1322 let mut parser = Parser::new("#(1 2 3)");
1323 let val = parser.parse().unwrap();
1324 assert!(val.is_vector());
1325
1326 if let Value::Vector(ref v) = val {
1327 let vec = v.borrow();
1328 assert_eq!(vec.len(), 3);
1329 }
1330 }
1331
1332 #[test]
1333 fn test_parse_dotted_list() {
1334 let mut parser = Parser::new("(1 . 2)");
1335 let val = parser.parse().unwrap();
1336
1337 if let Value::Pair(ref p) = val {
1338 let pair = p.borrow();
1339 assert!(matches!(pair.car, Value::Integer(1)));
1340 assert!(matches!(pair.cdr, Value::Integer(2)));
1341 } else {
1342 panic!("Expected pair");
1343 }
1344 }
1345
1346 #[test]
1347 fn test_parse_string() {
1348 let mut parser = Parser::new(r#""hello world""#);
1349 let val = parser.parse().unwrap();
1350 assert!(val.is_string());
1351 }
1352
1353 #[test]
1354 fn test_parse_bool() {
1355 let mut parser = Parser::new("#t");
1356 let val = parser.parse().unwrap();
1357 assert!(val.is_bool());
1358 assert!(val.is_true());
1359
1360 let mut parser = Parser::new("#f");
1361 let val = parser.parse().unwrap();
1362 assert!(val.is_bool());
1363 assert!(!val.is_true());
1364 }
1365
1366 #[test]
1367 fn test_parse_multiline_let() {
1368 let input = r#"
1370 (let ((x 1)
1371 (y 2))
1372 (+ x y))
1373 "#;
1374
1375 let mut parser = Parser::new(input);
1376 let val = parser.parse().unwrap();
1377 assert!(val.is_list());
1378
1379 }
1382
1383 #[test]
1384 fn test_parse_all() {
1385 let input = "(define x 1) (define y 2) (+ x y)";
1386 let mut parser = Parser::new(input);
1387 let exprs = parser.parse_all().unwrap();
1388 assert_eq!(exprs.len(), 3);
1389 }
1390
1391 #[test]
1392 fn test_parse_empty_list() {
1393 let mut parser = Parser::new("()");
1394 let val = parser.parse().unwrap();
1395 assert!(val.is_nil());
1396 }
1397
1398 #[test]
1399 fn test_parse_keyword() {
1400 let mut parser = Parser::new("#:foo");
1401 let val = parser.parse().unwrap();
1402 if let Value::Keyword(ref k) = val {
1403 assert_eq!(&**k, "foo");
1404 } else {
1405 panic!("Expected keyword");
1406 }
1407 }
1408
1409 #[test]
1410 fn test_parse_error_with_filename() {
1411 let mut parser = Parser::new_with_filename("(define x", "test.scm".to_string());
1413 let err = parser.parse().unwrap_err();
1414 let err_string = err.to_string();
1415
1416 assert!(err_string.contains("test.scm"), "Error should contain filename: {}", err_string);
1418 assert!(err_string.contains("1:1"), "Error should contain position: {}", err_string);
1420 assert!(err_string.contains("test.scm:1:1:E:"), "Error should use OpenJade format: {}", err_string);
1422 }
1423
1424 #[test]
1425 fn test_parse_error_without_filename() {
1426 let mut parser = Parser::new("(define x");
1428 let err = parser.parse().unwrap_err();
1429 let err_string = err.to_string();
1430
1431 assert!(!err_string.contains("test.scm"), "Error should not contain filename when not provided");
1433 assert!(err_string.contains("1:1"), "Error should still contain position: {}", err_string);
1435 }
1436
1437 #[test]
1438 fn test_parse_error_dot_after_open_paren() {
1439 let mut parser = Parser::new("(.gitignore)");
1442 let err = parser.parse().unwrap_err();
1443 let err_string = err.to_string();
1444
1445 assert!(err_string.contains("dot cannot appear immediately after opening parenthesis"),
1447 "Error should mention invalid dot position: {}", err_string);
1448 assert!(err_string.contains("Identifiers cannot start with '.'"),
1450 "Error should explain why: {}", err_string);
1451 }
1452
1453 #[test]
1454 fn test_parse_error_dot_function_definition() {
1455 let mut parser = Parser::new("(define (.gitignore) (list))");
1457 let err = parser.parse().unwrap_err();
1458 let err_string = err.to_string();
1459
1460 assert!(err_string.contains("dot cannot appear immediately after opening parenthesis"),
1462 "Error should mention invalid dot position: {}", err_string);
1463 }
1464
1465 #[test]
1466 fn test_parse_valid_dotted_pair_still_works() {
1467 let mut parser = Parser::new("(a . b)");
1469 let val = parser.parse().unwrap();
1470
1471 if let Value::Pair(ref p) = val {
1473 let pair = p.borrow();
1474 if let Value::Symbol(s) = &pair.car {
1475 assert_eq!(&**s, "a");
1476 } else {
1477 panic!("Expected symbol 'a'");
1478 }
1479 if let Value::Symbol(s) = &pair.cdr {
1480 assert_eq!(&**s, "b");
1481 } else {
1482 panic!("Expected symbol 'b'");
1483 }
1484 } else {
1485 panic!("Expected pair");
1486 }
1487 }
1488}
1489