1use crate::ast::{Include, Program, Statement, WordDef};
12use crate::types::{Effect, StackType, Type};
13
14pub struct Parser {
15 tokens: Vec<String>,
16 pos: usize,
17 next_quotation_id: usize,
20}
21
22impl Parser {
23 pub fn new(source: &str) -> Self {
24 let tokens = tokenize(source);
25 Parser {
26 tokens,
27 pos: 0,
28 next_quotation_id: 0,
29 }
30 }
31
32 pub fn parse(&mut self) -> Result<Program, String> {
33 let mut program = Program::new();
34
35 if self.tokens.iter().any(|t| t == "<<<UNCLOSED_STRING>>>") {
37 return Err("Unclosed string literal - missing closing quote".to_string());
38 }
39
40 while !self.is_at_end() {
41 self.skip_comments();
42 if self.is_at_end() {
43 break;
44 }
45
46 if self.check("include") {
48 let include = self.parse_include()?;
49 program.includes.push(include);
50 continue;
51 }
52
53 let word = self.parse_word_def()?;
54 program.words.push(word);
55 }
56
57 Ok(program)
58 }
59
60 fn parse_include(&mut self) -> Result<Include, String> {
64 self.consume("include");
65
66 let token = self
67 .advance()
68 .ok_or("Expected module name after 'include'")?
69 .clone();
70
71 if token == "std" {
73 if !self.consume(":") {
75 return Err("Expected ':' after 'std' in include statement".to_string());
76 }
77 let name = self
79 .advance()
80 .ok_or("Expected module name after 'std:'")?
81 .clone();
82 return Ok(Include::Std(name));
83 }
84
85 if token.starts_with('"') && token.ends_with('"') {
87 let path = token.trim_start_matches('"').trim_end_matches('"');
88 return Ok(Include::Relative(path.to_string()));
89 }
90
91 Err(format!(
92 "Invalid include syntax '{}'. Use 'include std:name' or 'include \"path\"'",
93 token
94 ))
95 }
96
97 fn parse_word_def(&mut self) -> Result<WordDef, String> {
98 if !self.consume(":") {
100 return Err(format!(
101 "Expected ':' to start word definition, got '{}'",
102 self.current()
103 ));
104 }
105
106 let name = self
108 .advance()
109 .ok_or("Expected word name after ':'")?
110 .clone();
111
112 let effect = if self.check("(") {
114 Some(self.parse_stack_effect()?)
115 } else {
116 None
117 };
118
119 let mut body = Vec::new();
121 while !self.check(";") {
122 if self.is_at_end() {
123 return Err(format!("Unexpected end of file in word '{}'", name));
124 }
125
126 self.skip_comments();
128 if self.check(";") {
129 break;
130 }
131
132 body.push(self.parse_statement()?);
133 }
134
135 self.consume(";");
137
138 Ok(WordDef {
139 name,
140 effect,
141 body,
142 source: None,
143 })
144 }
145
146 fn parse_statement(&mut self) -> Result<Statement, String> {
147 let token = self.advance().ok_or("Unexpected end of file")?.clone();
148
149 if let Some(f) = is_float_literal(&token)
152 .then(|| token.parse::<f64>().ok())
153 .flatten()
154 {
155 return Ok(Statement::FloatLiteral(f));
156 }
157
158 if let Ok(n) = token.parse::<i64>() {
160 return Ok(Statement::IntLiteral(n));
161 }
162
163 if token == "true" {
165 return Ok(Statement::BoolLiteral(true));
166 }
167 if token == "false" {
168 return Ok(Statement::BoolLiteral(false));
169 }
170
171 if token.starts_with('"') {
173 let raw = token.trim_start_matches('"').trim_end_matches('"');
174 let unescaped = unescape_string(raw)?;
175 return Ok(Statement::StringLiteral(unescaped));
176 }
177
178 if token == "if" {
180 return self.parse_if();
181 }
182
183 if token == "[" {
185 return self.parse_quotation();
186 }
187
188 Ok(Statement::WordCall(token))
190 }
191
192 fn parse_if(&mut self) -> Result<Statement, String> {
193 let mut then_branch = Vec::new();
194
195 loop {
197 if self.is_at_end() {
198 return Err("Unexpected end of file in 'if' statement".to_string());
199 }
200
201 self.skip_comments();
203
204 if self.check("else") {
205 self.advance();
206 break;
208 }
209
210 if self.check("then") {
211 self.advance();
212 return Ok(Statement::If {
214 then_branch,
215 else_branch: None,
216 });
217 }
218
219 then_branch.push(self.parse_statement()?);
220 }
221
222 let mut else_branch = Vec::new();
224 loop {
225 if self.is_at_end() {
226 return Err("Unexpected end of file in 'else' branch".to_string());
227 }
228
229 self.skip_comments();
231
232 if self.check("then") {
233 self.advance();
234 return Ok(Statement::If {
235 then_branch,
236 else_branch: Some(else_branch),
237 });
238 }
239
240 else_branch.push(self.parse_statement()?);
241 }
242 }
243
244 fn parse_quotation(&mut self) -> Result<Statement, String> {
245 let mut body = Vec::new();
246
247 loop {
249 if self.is_at_end() {
250 return Err("Unexpected end of file in quotation".to_string());
251 }
252
253 self.skip_comments();
255
256 if self.check("]") {
257 self.advance();
258 let id = self.next_quotation_id;
259 self.next_quotation_id += 1;
260 return Ok(Statement::Quotation { id, body });
261 }
262
263 body.push(self.parse_statement()?);
264 }
265 }
266
267 fn parse_stack_effect(&mut self) -> Result<Effect, String> {
269 if !self.consume("(") {
271 return Err("Expected '(' to start stack effect".to_string());
272 }
273
274 let (input_row_var, input_types) =
276 self.parse_type_list_until(&["--", ")"], "stack effect inputs", 0)?;
277
278 if !self.consume("--") {
280 return Err("Expected '--' separator in stack effect".to_string());
281 }
282
283 let (output_row_var, output_types) =
285 self.parse_type_list_until(&[")"], "stack effect outputs", 0)?;
286
287 if !self.consume(")") {
289 return Err("Expected ')' to end stack effect".to_string());
290 }
291
292 let inputs = self.build_stack_type(input_row_var, input_types);
294 let outputs = self.build_stack_type(output_row_var, output_types);
295
296 Ok(Effect::new(inputs, outputs))
297 }
298
299 fn parse_type(&self, token: &str) -> Result<Type, String> {
301 match token {
302 "Int" => Ok(Type::Int),
303 "Float" => Ok(Type::Float),
304 "Bool" => Ok(Type::Bool),
305 "String" => Ok(Type::String),
306 _ => {
307 if let Some(first_char) = token.chars().next() {
309 if first_char.is_uppercase() {
310 Ok(Type::Var(token.to_string()))
311 } else {
312 Err(format!(
313 "Unknown type: '{}'. Expected Int, Bool, String, Closure, or a type variable (uppercase)",
314 token
315 ))
316 }
317 } else {
318 Err(format!("Invalid type: '{}'", token))
319 }
320 }
321 }
322 }
323
324 fn validate_row_var_name(&self, name: &str) -> Result<(), String> {
327 if name.is_empty() {
328 return Err("Row variable must have a name after '..'".to_string());
329 }
330
331 let first_char = name.chars().next().unwrap();
333 if !first_char.is_ascii_lowercase() {
334 return Err(format!(
335 "Row variable '..{}' must start with a lowercase letter (a-z)",
336 name
337 ));
338 }
339
340 for ch in name.chars() {
342 if !ch.is_alphanumeric() && ch != '_' {
343 return Err(format!(
344 "Row variable '..{}' can only contain letters, numbers, and underscores",
345 name
346 ));
347 }
348 }
349
350 match name {
352 "Int" | "Bool" | "String" => {
353 return Err(format!(
354 "Row variable '..{}' cannot use type name as identifier",
355 name
356 ));
357 }
358 _ => {}
359 }
360
361 Ok(())
362 }
363
364 fn parse_type_list_until(
370 &mut self,
371 terminators: &[&str],
372 context: &str,
373 depth: usize,
374 ) -> Result<(Option<String>, Vec<Type>), String> {
375 const MAX_QUOTATION_DEPTH: usize = 32;
376
377 if depth > MAX_QUOTATION_DEPTH {
378 return Err(format!(
379 "Quotation type nesting exceeds maximum depth of {} (possible deeply nested types or DOS attack)",
380 MAX_QUOTATION_DEPTH
381 ));
382 }
383
384 let mut types = Vec::new();
385 let mut row_var = None;
386
387 while !terminators.iter().any(|t| self.check(t)) {
388 if self.is_at_end() {
389 return Err(format!(
390 "Unexpected end while parsing {} - expected one of: {}",
391 context,
392 terminators.join(", ")
393 ));
394 }
395
396 let token = self
397 .advance()
398 .ok_or_else(|| format!("Unexpected end in {}", context))?
399 .clone();
400
401 if token.starts_with("..") {
403 let var_name = token.trim_start_matches("..").to_string();
404 self.validate_row_var_name(&var_name)?;
405 row_var = Some(var_name);
406 } else if token == "Closure" {
407 if !self.consume("[") {
409 return Err("Expected '[' after 'Closure' in type signature".to_string());
410 }
411 let effect_type = self.parse_quotation_type(depth)?;
412 match effect_type {
413 Type::Quotation(effect) => {
414 types.push(Type::Closure {
415 effect,
416 captures: Vec::new(), });
418 }
419 _ => unreachable!("parse_quotation_type should return Quotation"),
420 }
421 } else if token == "[" {
422 types.push(self.parse_quotation_type(depth)?);
424 } else {
425 types.push(self.parse_type(&token)?);
427 }
428 }
429
430 Ok((row_var, types))
431 }
432
433 fn parse_quotation_type(&mut self, depth: usize) -> Result<Type, String> {
438 let (input_row_var, input_types) =
440 self.parse_type_list_until(&["--", "]"], "quotation type inputs", depth + 1)?;
441
442 if !self.consume("--") {
444 if self.check("]") {
446 return Err(
447 "Quotation types require '--' separator. Did you mean '[Int -- ]' or '[ -- Int]'?"
448 .to_string(),
449 );
450 }
451 return Err("Expected '--' separator in quotation type".to_string());
452 }
453
454 let (output_row_var, output_types) =
456 self.parse_type_list_until(&["]"], "quotation type outputs", depth + 1)?;
457
458 if !self.consume("]") {
460 return Err("Expected ']' to end quotation type".to_string());
461 }
462
463 let inputs = self.build_stack_type(input_row_var, input_types);
465 let outputs = self.build_stack_type(output_row_var, output_types);
466
467 Ok(Type::Quotation(Box::new(Effect::new(inputs, outputs))))
468 }
469
470 fn build_stack_type(&self, row_var: Option<String>, types: Vec<Type>) -> StackType {
480 let base = match row_var {
482 Some(name) => StackType::RowVar(name),
483 None => StackType::RowVar("rest".to_string()),
484 };
485
486 types.into_iter().fold(base, |stack, ty| stack.push(ty))
488 }
489
490 fn skip_comments(&mut self) {
491 loop {
492 if self.check("#") {
493 while !self.is_at_end() && self.current() != "\n" {
495 self.advance();
496 }
497 if !self.is_at_end() {
498 self.advance(); }
500 } else if self.check("\n") {
501 self.advance();
503 } else {
504 break;
505 }
506 }
507 }
508
509 fn check(&self, expected: &str) -> bool {
510 if self.is_at_end() {
511 return false;
512 }
513 self.current() == expected
514 }
515
516 fn consume(&mut self, expected: &str) -> bool {
517 if self.check(expected) {
518 self.advance();
519 true
520 } else {
521 false
522 }
523 }
524
525 fn current(&self) -> &str {
526 if self.is_at_end() {
527 ""
528 } else {
529 &self.tokens[self.pos]
530 }
531 }
532
533 fn advance(&mut self) -> Option<&String> {
534 if self.is_at_end() {
535 None
536 } else {
537 let token = &self.tokens[self.pos];
538 self.pos += 1;
539 Some(token)
540 }
541 }
542
543 fn is_at_end(&self) -> bool {
544 self.pos >= self.tokens.len()
545 }
546}
547
548fn is_float_literal(token: &str) -> bool {
557 let s = token.strip_prefix('-').unwrap_or(token);
559
560 if s.is_empty() {
562 return false;
563 }
564
565 s.contains('.') || s.contains('e') || s.contains('E')
567}
568
569fn unescape_string(s: &str) -> Result<String, String> {
581 let mut result = String::new();
582 let mut chars = s.chars();
583
584 while let Some(ch) = chars.next() {
585 if ch == '\\' {
586 match chars.next() {
587 Some('"') => result.push('"'),
588 Some('\\') => result.push('\\'),
589 Some('n') => result.push('\n'),
590 Some('r') => result.push('\r'),
591 Some('t') => result.push('\t'),
592 Some(c) => {
593 return Err(format!(
594 "Unknown escape sequence '\\{}' in string literal. \
595 Supported: \\\" \\\\ \\n \\r \\t",
596 c
597 ));
598 }
599 None => {
600 return Err("String ends with incomplete escape sequence '\\'".to_string());
601 }
602 }
603 } else {
604 result.push(ch);
605 }
606 }
607
608 Ok(result)
609}
610
611fn tokenize(source: &str) -> Vec<String> {
612 let mut tokens = Vec::new();
613 let mut current = String::new();
614 let mut in_string = false;
615 let mut prev_was_backslash = false;
616
617 for ch in source.chars() {
618 if in_string {
619 current.push(ch);
620 if ch == '"' && !prev_was_backslash {
621 in_string = false;
623 tokens.push(current.clone());
624 current.clear();
625 prev_was_backslash = false;
626 } else if ch == '\\' && !prev_was_backslash {
627 prev_was_backslash = true;
629 } else {
630 prev_was_backslash = false;
632 }
633 } else if ch == '"' {
634 if !current.is_empty() {
635 tokens.push(current.clone());
636 current.clear();
637 }
638 in_string = true;
639 current.push(ch);
640 prev_was_backslash = false;
641 } else if ch.is_whitespace() {
642 if !current.is_empty() {
643 tokens.push(current.clone());
644 current.clear();
645 }
646 if ch == '\n' {
648 tokens.push("\n".to_string());
649 }
650 } else if "():;[]".contains(ch) {
651 if !current.is_empty() {
652 tokens.push(current.clone());
653 current.clear();
654 }
655 tokens.push(ch.to_string());
656 } else {
657 current.push(ch);
658 }
659 }
660
661 if in_string {
663 tokens.push("<<<UNCLOSED_STRING>>>".to_string());
666 } else if !current.is_empty() {
667 tokens.push(current);
668 }
669
670 tokens
671}
672
673#[cfg(test)]
674mod tests {
675 use super::*;
676
677 #[test]
678 fn test_parse_hello_world() {
679 let source = r#"
680: main ( -- )
681 "Hello, World!" write_line ;
682"#;
683
684 let mut parser = Parser::new(source);
685 let program = parser.parse().unwrap();
686
687 assert_eq!(program.words.len(), 1);
688 assert_eq!(program.words[0].name, "main");
689 assert_eq!(program.words[0].body.len(), 2);
690
691 match &program.words[0].body[0] {
692 Statement::StringLiteral(s) => assert_eq!(s, "Hello, World!"),
693 _ => panic!("Expected StringLiteral"),
694 }
695
696 match &program.words[0].body[1] {
697 Statement::WordCall(name) => assert_eq!(name, "write_line"),
698 _ => panic!("Expected WordCall"),
699 }
700 }
701
702 #[test]
703 fn test_parse_with_numbers() {
704 let source = ": add-example ( -- ) 2 3 add ;";
705
706 let mut parser = Parser::new(source);
707 let program = parser.parse().unwrap();
708
709 assert_eq!(program.words[0].body.len(), 3);
710 assert_eq!(program.words[0].body[0], Statement::IntLiteral(2));
711 assert_eq!(program.words[0].body[1], Statement::IntLiteral(3));
712 assert_eq!(
713 program.words[0].body[2],
714 Statement::WordCall("add".to_string())
715 );
716 }
717
718 #[test]
719 fn test_parse_escaped_quotes() {
720 let source = r#": main ( -- ) "Say \"hello\" there" write_line ;"#;
721
722 let mut parser = Parser::new(source);
723 let program = parser.parse().unwrap();
724
725 assert_eq!(program.words.len(), 1);
726 assert_eq!(program.words[0].body.len(), 2);
727
728 match &program.words[0].body[0] {
729 Statement::StringLiteral(s) => assert_eq!(s, "Say \"hello\" there"),
731 _ => panic!("Expected StringLiteral with escaped quotes"),
732 }
733 }
734
735 #[test]
736 fn test_escape_sequences() {
737 let source = r#": main ( -- ) "Line 1\nLine 2\tTabbed" write_line ;"#;
738
739 let mut parser = Parser::new(source);
740 let program = parser.parse().unwrap();
741
742 match &program.words[0].body[0] {
743 Statement::StringLiteral(s) => assert_eq!(s, "Line 1\nLine 2\tTabbed"),
744 _ => panic!("Expected StringLiteral"),
745 }
746 }
747
748 #[test]
749 fn test_unknown_escape_sequence() {
750 let source = r#": main ( -- ) "Bad \x sequence" write_line ;"#;
751
752 let mut parser = Parser::new(source);
753 let result = parser.parse();
754
755 assert!(result.is_err());
756 assert!(result.unwrap_err().contains("Unknown escape sequence"));
757 }
758
759 #[test]
760 fn test_unclosed_string_literal() {
761 let source = r#": main ( -- ) "unclosed string ;"#;
762
763 let mut parser = Parser::new(source);
764 let result = parser.parse();
765
766 assert!(result.is_err());
767 assert!(result.unwrap_err().contains("Unclosed string literal"));
768 }
769
770 #[test]
771 fn test_multiple_word_definitions() {
772 let source = r#"
773: double ( Int -- Int )
774 2 multiply ;
775
776: quadruple ( Int -- Int )
777 double double ;
778"#;
779
780 let mut parser = Parser::new(source);
781 let program = parser.parse().unwrap();
782
783 assert_eq!(program.words.len(), 2);
784 assert_eq!(program.words[0].name, "double");
785 assert_eq!(program.words[1].name, "quadruple");
786
787 assert!(program.words[0].effect.is_some());
789 assert!(program.words[1].effect.is_some());
790 }
791
792 #[test]
793 fn test_user_word_calling_user_word() {
794 let source = r#"
795: helper ( -- )
796 "helper called" write_line ;
797
798: main ( -- )
799 helper ;
800"#;
801
802 let mut parser = Parser::new(source);
803 let program = parser.parse().unwrap();
804
805 assert_eq!(program.words.len(), 2);
806
807 match &program.words[1].body[0] {
809 Statement::WordCall(name) => assert_eq!(name, "helper"),
810 _ => panic!("Expected WordCall to helper"),
811 }
812 }
813
814 #[test]
815 fn test_parse_simple_stack_effect() {
816 let source = ": test ( Int -- Bool ) 1 ;";
819 let mut parser = Parser::new(source);
820 let program = parser.parse().unwrap();
821
822 assert_eq!(program.words.len(), 1);
823 let word = &program.words[0];
824 assert!(word.effect.is_some());
825
826 let effect = word.effect.as_ref().unwrap();
827
828 assert_eq!(
830 effect.inputs,
831 StackType::Cons {
832 rest: Box::new(StackType::RowVar("rest".to_string())),
833 top: Type::Int
834 }
835 );
836
837 assert_eq!(
839 effect.outputs,
840 StackType::Cons {
841 rest: Box::new(StackType::RowVar("rest".to_string())),
842 top: Type::Bool
843 }
844 );
845 }
846
847 #[test]
848 fn test_parse_row_polymorphic_stack_effect() {
849 let source = ": test ( ..a Int -- ..a Bool ) 1 ;";
851 let mut parser = Parser::new(source);
852 let program = parser.parse().unwrap();
853
854 assert_eq!(program.words.len(), 1);
855 let word = &program.words[0];
856 assert!(word.effect.is_some());
857
858 let effect = word.effect.as_ref().unwrap();
859
860 assert_eq!(
862 effect.inputs,
863 StackType::Cons {
864 rest: Box::new(StackType::RowVar("a".to_string())),
865 top: Type::Int
866 }
867 );
868
869 assert_eq!(
871 effect.outputs,
872 StackType::Cons {
873 rest: Box::new(StackType::RowVar("a".to_string())),
874 top: Type::Bool
875 }
876 );
877 }
878
879 #[test]
880 fn test_parse_invalid_row_var_starts_with_digit() {
881 let source = ": test ( ..123 Int -- ) ;";
883 let mut parser = Parser::new(source);
884 let result = parser.parse();
885
886 assert!(result.is_err());
887 let err_msg = result.unwrap_err();
888 assert!(
889 err_msg.contains("lowercase letter"),
890 "Expected error about lowercase letter, got: {}",
891 err_msg
892 );
893 }
894
895 #[test]
896 fn test_parse_invalid_row_var_starts_with_uppercase() {
897 let source = ": test ( ..Int Int -- ) ;";
899 let mut parser = Parser::new(source);
900 let result = parser.parse();
901
902 assert!(result.is_err());
903 let err_msg = result.unwrap_err();
904 assert!(
905 err_msg.contains("lowercase letter") || err_msg.contains("type name"),
906 "Expected error about lowercase letter or type name, got: {}",
907 err_msg
908 );
909 }
910
911 #[test]
912 fn test_parse_invalid_row_var_with_special_chars() {
913 let source = ": test ( ..a-b Int -- ) ;";
915 let mut parser = Parser::new(source);
916 let result = parser.parse();
917
918 assert!(result.is_err());
919 let err_msg = result.unwrap_err();
920 assert!(
921 err_msg.contains("letters, numbers, and underscores")
922 || err_msg.contains("Unknown type"),
923 "Expected error about valid characters, got: {}",
924 err_msg
925 );
926 }
927
928 #[test]
929 fn test_parse_valid_row_var_with_underscore() {
930 let source = ": test ( ..my_row Int -- ..my_row Bool ) ;";
932 let mut parser = Parser::new(source);
933 let result = parser.parse();
934
935 assert!(result.is_ok(), "Should accept row variable with underscore");
936 }
937
938 #[test]
939 fn test_parse_multiple_types_stack_effect() {
940 let source = ": test ( Int String -- Bool ) 1 ;";
943 let mut parser = Parser::new(source);
944 let program = parser.parse().unwrap();
945
946 let effect = program.words[0].effect.as_ref().unwrap();
947
948 let (rest, top) = effect.inputs.clone().pop().unwrap();
950 assert_eq!(top, Type::String);
951 let (rest2, top2) = rest.pop().unwrap();
952 assert_eq!(top2, Type::Int);
953 assert_eq!(rest2, StackType::RowVar("rest".to_string()));
954
955 assert_eq!(
957 effect.outputs,
958 StackType::Cons {
959 rest: Box::new(StackType::RowVar("rest".to_string())),
960 top: Type::Bool
961 }
962 );
963 }
964
965 #[test]
966 fn test_parse_type_variable() {
967 let source = ": dup ( ..a T -- ..a T T ) ;";
969 let mut parser = Parser::new(source);
970 let program = parser.parse().unwrap();
971
972 let effect = program.words[0].effect.as_ref().unwrap();
973
974 assert_eq!(
976 effect.inputs,
977 StackType::Cons {
978 rest: Box::new(StackType::RowVar("a".to_string())),
979 top: Type::Var("T".to_string())
980 }
981 );
982
983 let (rest, top) = effect.outputs.clone().pop().unwrap();
985 assert_eq!(top, Type::Var("T".to_string()));
986 let (rest2, top2) = rest.pop().unwrap();
987 assert_eq!(top2, Type::Var("T".to_string()));
988 assert_eq!(rest2, StackType::RowVar("a".to_string()));
989 }
990
991 #[test]
992 fn test_parse_empty_stack_effect() {
993 let source = ": test ( -- ) ;";
997 let mut parser = Parser::new(source);
998 let program = parser.parse().unwrap();
999
1000 let effect = program.words[0].effect.as_ref().unwrap();
1001
1002 assert_eq!(effect.inputs, StackType::RowVar("rest".to_string()));
1004 assert_eq!(effect.outputs, StackType::RowVar("rest".to_string()));
1005 }
1006
1007 #[test]
1008 fn test_parse_invalid_type() {
1009 let source = ": test ( invalid -- Bool ) ;";
1011 let mut parser = Parser::new(source);
1012 let result = parser.parse();
1013
1014 assert!(result.is_err());
1015 assert!(result.unwrap_err().contains("Unknown type"));
1016 }
1017
1018 #[test]
1019 fn test_parse_unclosed_stack_effect() {
1020 let source = ": test ( Int -- Bool body ;";
1023 let mut parser = Parser::new(source);
1024 let result = parser.parse();
1025
1026 assert!(result.is_err());
1027 let err_msg = result.unwrap_err();
1028 assert!(err_msg.contains("Unknown type"));
1030 }
1031
1032 #[test]
1033 fn test_parse_simple_quotation_type() {
1034 let source = ": apply ( [Int -- Int] -- ) ;";
1036 let mut parser = Parser::new(source);
1037 let program = parser.parse().unwrap();
1038
1039 let effect = program.words[0].effect.as_ref().unwrap();
1040
1041 let (rest, top) = effect.inputs.clone().pop().unwrap();
1043 match top {
1044 Type::Quotation(quot_effect) => {
1045 assert_eq!(
1047 quot_effect.inputs,
1048 StackType::Cons {
1049 rest: Box::new(StackType::RowVar("rest".to_string())),
1050 top: Type::Int
1051 }
1052 );
1053 assert_eq!(
1055 quot_effect.outputs,
1056 StackType::Cons {
1057 rest: Box::new(StackType::RowVar("rest".to_string())),
1058 top: Type::Int
1059 }
1060 );
1061 }
1062 _ => panic!("Expected Quotation type, got {:?}", top),
1063 }
1064 assert_eq!(rest, StackType::RowVar("rest".to_string()));
1065 }
1066
1067 #[test]
1068 fn test_parse_quotation_type_with_row_vars() {
1069 let source = ": test ( ..a [..a T -- ..a Bool] -- ..a ) ;";
1071 let mut parser = Parser::new(source);
1072 let program = parser.parse().unwrap();
1073
1074 let effect = program.words[0].effect.as_ref().unwrap();
1075
1076 let (rest, top) = effect.inputs.clone().pop().unwrap();
1078 match top {
1079 Type::Quotation(quot_effect) => {
1080 let (q_in_rest, q_in_top) = quot_effect.inputs.clone().pop().unwrap();
1082 assert_eq!(q_in_top, Type::Var("T".to_string()));
1083 assert_eq!(q_in_rest, StackType::RowVar("a".to_string()));
1084
1085 let (q_out_rest, q_out_top) = quot_effect.outputs.clone().pop().unwrap();
1087 assert_eq!(q_out_top, Type::Bool);
1088 assert_eq!(q_out_rest, StackType::RowVar("a".to_string()));
1089 }
1090 _ => panic!("Expected Quotation type, got {:?}", top),
1091 }
1092 assert_eq!(rest, StackType::RowVar("a".to_string()));
1093 }
1094
1095 #[test]
1096 fn test_parse_nested_quotation_type() {
1097 let source = ": nested ( [[Int -- Int] -- Bool] -- ) ;";
1099 let mut parser = Parser::new(source);
1100 let program = parser.parse().unwrap();
1101
1102 let effect = program.words[0].effect.as_ref().unwrap();
1103
1104 let (_, top) = effect.inputs.clone().pop().unwrap();
1106 match top {
1107 Type::Quotation(outer_effect) => {
1108 let (_, outer_in_top) = outer_effect.inputs.clone().pop().unwrap();
1110 match outer_in_top {
1111 Type::Quotation(inner_effect) => {
1112 assert!(matches!(
1114 inner_effect.inputs.clone().pop().unwrap().1,
1115 Type::Int
1116 ));
1117 assert!(matches!(
1118 inner_effect.outputs.clone().pop().unwrap().1,
1119 Type::Int
1120 ));
1121 }
1122 _ => panic!("Expected nested Quotation type"),
1123 }
1124
1125 let (_, outer_out_top) = outer_effect.outputs.clone().pop().unwrap();
1127 assert_eq!(outer_out_top, Type::Bool);
1128 }
1129 _ => panic!("Expected Quotation type"),
1130 }
1131 }
1132
1133 #[test]
1134 fn test_parse_deeply_nested_quotation_type_exceeds_limit() {
1135 let mut source = String::from(": deep ( ");
1138
1139 for _ in 0..35 {
1141 source.push_str("[ -- ");
1142 }
1143
1144 source.push_str("Int");
1145
1146 for _ in 0..35 {
1148 source.push_str(" ]");
1149 }
1150
1151 source.push_str(" -- ) ;");
1152
1153 let mut parser = Parser::new(&source);
1154 let result = parser.parse();
1155
1156 assert!(result.is_err());
1158 let err_msg = result.unwrap_err();
1159 assert!(
1160 err_msg.contains("depth") || err_msg.contains("32"),
1161 "Expected depth limit error, got: {}",
1162 err_msg
1163 );
1164 }
1165
1166 #[test]
1167 fn test_parse_empty_quotation_type() {
1168 let source = ": empty-quot ( [ -- ] -- ) ;";
1171 let mut parser = Parser::new(source);
1172 let program = parser.parse().unwrap();
1173
1174 let effect = program.words[0].effect.as_ref().unwrap();
1175
1176 let (_, top) = effect.inputs.clone().pop().unwrap();
1177 match top {
1178 Type::Quotation(quot_effect) => {
1179 assert_eq!(quot_effect.inputs, StackType::RowVar("rest".to_string()));
1181 assert_eq!(quot_effect.outputs, StackType::RowVar("rest".to_string()));
1182 }
1183 _ => panic!("Expected Quotation type"),
1184 }
1185 }
1186
1187 #[test]
1188 fn test_parse_quotation_type_in_output() {
1189 let source = ": maker ( -- [Int -- Int] ) ;";
1191 let mut parser = Parser::new(source);
1192 let program = parser.parse().unwrap();
1193
1194 let effect = program.words[0].effect.as_ref().unwrap();
1195
1196 let (_, top) = effect.outputs.clone().pop().unwrap();
1198 match top {
1199 Type::Quotation(quot_effect) => {
1200 assert!(matches!(
1201 quot_effect.inputs.clone().pop().unwrap().1,
1202 Type::Int
1203 ));
1204 assert!(matches!(
1205 quot_effect.outputs.clone().pop().unwrap().1,
1206 Type::Int
1207 ));
1208 }
1209 _ => panic!("Expected Quotation type"),
1210 }
1211 }
1212
1213 #[test]
1214 fn test_parse_unclosed_quotation_type() {
1215 let source = ": broken ( [Int -- Int -- ) ;";
1217 let mut parser = Parser::new(source);
1218 let result = parser.parse();
1219
1220 assert!(result.is_err());
1221 let err_msg = result.unwrap_err();
1222 assert!(
1225 err_msg.contains("Unclosed")
1226 || err_msg.contains("Expected")
1227 || err_msg.contains("Unexpected"),
1228 "Got error: {}",
1229 err_msg
1230 );
1231 }
1232
1233 #[test]
1234 fn test_parse_multiple_quotation_types() {
1235 let source = ": multi ( [Int -- Int] [String -- Bool] -- ) ;";
1237 let mut parser = Parser::new(source);
1238 let program = parser.parse().unwrap();
1239
1240 let effect = program.words[0].effect.as_ref().unwrap();
1241
1242 let (rest, top) = effect.inputs.clone().pop().unwrap();
1244 match top {
1245 Type::Quotation(quot_effect) => {
1246 assert!(matches!(
1247 quot_effect.inputs.clone().pop().unwrap().1,
1248 Type::String
1249 ));
1250 assert!(matches!(
1251 quot_effect.outputs.clone().pop().unwrap().1,
1252 Type::Bool
1253 ));
1254 }
1255 _ => panic!("Expected Quotation type"),
1256 }
1257
1258 let (_, top2) = rest.pop().unwrap();
1260 match top2 {
1261 Type::Quotation(quot_effect) => {
1262 assert!(matches!(
1263 quot_effect.inputs.clone().pop().unwrap().1,
1264 Type::Int
1265 ));
1266 assert!(matches!(
1267 quot_effect.outputs.clone().pop().unwrap().1,
1268 Type::Int
1269 ));
1270 }
1271 _ => panic!("Expected Quotation type"),
1272 }
1273 }
1274
1275 #[test]
1276 fn test_parse_quotation_type_without_separator() {
1277 let source = ": consumer ( [Int] -- ) ;";
1288 let mut parser = Parser::new(source);
1289 let result = parser.parse();
1290
1291 assert!(result.is_err());
1293 let err_msg = result.unwrap_err();
1294 assert!(
1295 err_msg.contains("require") && err_msg.contains("--"),
1296 "Expected error about missing '--' separator, got: {}",
1297 err_msg
1298 );
1299 }
1300
1301 #[test]
1302 fn test_parse_no_stack_effect() {
1303 let source = ": test 1 2 add ;";
1305 let mut parser = Parser::new(source);
1306 let program = parser.parse().unwrap();
1307
1308 assert_eq!(program.words.len(), 1);
1309 assert!(program.words[0].effect.is_none());
1310 }
1311
1312 #[test]
1313 fn test_parse_simple_quotation() {
1314 let source = r#"
1315: test ( -- Quot )
1316 [ 1 add ] ;
1317"#;
1318
1319 let mut parser = Parser::new(source);
1320 let program = parser.parse().unwrap();
1321
1322 assert_eq!(program.words.len(), 1);
1323 assert_eq!(program.words[0].name, "test");
1324 assert_eq!(program.words[0].body.len(), 1);
1325
1326 match &program.words[0].body[0] {
1327 Statement::Quotation { body, .. } => {
1328 assert_eq!(body.len(), 2);
1329 assert_eq!(body[0], Statement::IntLiteral(1));
1330 assert_eq!(body[1], Statement::WordCall("add".to_string()));
1331 }
1332 _ => panic!("Expected Quotation statement"),
1333 }
1334 }
1335
1336 #[test]
1337 fn test_parse_empty_quotation() {
1338 let source = ": test [ ] ;";
1339
1340 let mut parser = Parser::new(source);
1341 let program = parser.parse().unwrap();
1342
1343 assert_eq!(program.words.len(), 1);
1344
1345 match &program.words[0].body[0] {
1346 Statement::Quotation { body, .. } => {
1347 assert_eq!(body.len(), 0);
1348 }
1349 _ => panic!("Expected Quotation statement"),
1350 }
1351 }
1352
1353 #[test]
1354 fn test_parse_quotation_with_call() {
1355 let source = r#"
1356: test ( -- )
1357 5 [ 1 add ] call ;
1358"#;
1359
1360 let mut parser = Parser::new(source);
1361 let program = parser.parse().unwrap();
1362
1363 assert_eq!(program.words.len(), 1);
1364 assert_eq!(program.words[0].body.len(), 3);
1365
1366 assert_eq!(program.words[0].body[0], Statement::IntLiteral(5));
1367
1368 match &program.words[0].body[1] {
1369 Statement::Quotation { body, .. } => {
1370 assert_eq!(body.len(), 2);
1371 }
1372 _ => panic!("Expected Quotation"),
1373 }
1374
1375 assert_eq!(
1376 program.words[0].body[2],
1377 Statement::WordCall("call".to_string())
1378 );
1379 }
1380
1381 #[test]
1382 fn test_parse_nested_quotation() {
1383 let source = ": test [ [ 1 add ] call ] ;";
1384
1385 let mut parser = Parser::new(source);
1386 let program = parser.parse().unwrap();
1387
1388 assert_eq!(program.words.len(), 1);
1389
1390 match &program.words[0].body[0] {
1391 Statement::Quotation {
1392 body: outer_body, ..
1393 } => {
1394 assert_eq!(outer_body.len(), 2);
1395
1396 match &outer_body[0] {
1397 Statement::Quotation {
1398 body: inner_body, ..
1399 } => {
1400 assert_eq!(inner_body.len(), 2);
1401 assert_eq!(inner_body[0], Statement::IntLiteral(1));
1402 assert_eq!(inner_body[1], Statement::WordCall("add".to_string()));
1403 }
1404 _ => panic!("Expected nested Quotation"),
1405 }
1406
1407 assert_eq!(outer_body[1], Statement::WordCall("call".to_string()));
1408 }
1409 _ => panic!("Expected Quotation"),
1410 }
1411 }
1412
1413 #[test]
1414 fn test_parse_while_with_quotations() {
1415 let source = r#"
1416: countdown ( Int -- )
1417 [ dup 0 > ] [ 1 subtract ] while drop ;
1418"#;
1419
1420 let mut parser = Parser::new(source);
1421 let program = parser.parse().unwrap();
1422
1423 assert_eq!(program.words.len(), 1);
1424 assert_eq!(program.words[0].body.len(), 4);
1425
1426 match &program.words[0].body[0] {
1428 Statement::Quotation { body: pred, .. } => {
1429 assert_eq!(pred.len(), 3);
1430 assert_eq!(pred[0], Statement::WordCall("dup".to_string()));
1431 assert_eq!(pred[1], Statement::IntLiteral(0));
1432 assert_eq!(pred[2], Statement::WordCall(">".to_string()));
1433 }
1434 _ => panic!("Expected predicate quotation"),
1435 }
1436
1437 match &program.words[0].body[1] {
1439 Statement::Quotation { body, .. } => {
1440 assert_eq!(body.len(), 2);
1441 assert_eq!(body[0], Statement::IntLiteral(1));
1442 assert_eq!(body[1], Statement::WordCall("subtract".to_string()));
1443 }
1444 _ => panic!("Expected body quotation"),
1445 }
1446
1447 assert_eq!(
1449 program.words[0].body[2],
1450 Statement::WordCall("while".to_string())
1451 );
1452
1453 assert_eq!(
1455 program.words[0].body[3],
1456 Statement::WordCall("drop".to_string())
1457 );
1458 }
1459
1460 #[test]
1461 fn test_parse_simple_closure_type() {
1462 let source = ": make-adder ( Int -- Closure[Int -- Int] ) ;";
1464 let mut parser = Parser::new(source);
1465 let program = parser.parse().unwrap();
1466
1467 assert_eq!(program.words.len(), 1);
1468 let word = &program.words[0];
1469 assert!(word.effect.is_some());
1470
1471 let effect = word.effect.as_ref().unwrap();
1472
1473 let (input_rest, input_top) = effect.inputs.clone().pop().unwrap();
1475 assert_eq!(input_top, Type::Int);
1476 assert_eq!(input_rest, StackType::RowVar("rest".to_string()));
1477
1478 let (output_rest, output_top) = effect.outputs.clone().pop().unwrap();
1480 match output_top {
1481 Type::Closure { effect, captures } => {
1482 assert_eq!(
1484 effect.inputs,
1485 StackType::Cons {
1486 rest: Box::new(StackType::RowVar("rest".to_string())),
1487 top: Type::Int
1488 }
1489 );
1490 assert_eq!(
1491 effect.outputs,
1492 StackType::Cons {
1493 rest: Box::new(StackType::RowVar("rest".to_string())),
1494 top: Type::Int
1495 }
1496 );
1497 assert_eq!(captures.len(), 0);
1499 }
1500 _ => panic!("Expected Closure type, got {:?}", output_top),
1501 }
1502 assert_eq!(output_rest, StackType::RowVar("rest".to_string()));
1503 }
1504
1505 #[test]
1506 fn test_parse_closure_type_with_row_vars() {
1507 let source = ": make-handler ( ..a Config -- ..a Closure[Request -- Response] ) ;";
1509 let mut parser = Parser::new(source);
1510 let program = parser.parse().unwrap();
1511
1512 let effect = program.words[0].effect.as_ref().unwrap();
1513
1514 let (rest, top) = effect.outputs.clone().pop().unwrap();
1516 match top {
1517 Type::Closure { effect, .. } => {
1518 let (_, in_top) = effect.inputs.clone().pop().unwrap();
1520 assert_eq!(in_top, Type::Var("Request".to_string()));
1521 let (_, out_top) = effect.outputs.clone().pop().unwrap();
1522 assert_eq!(out_top, Type::Var("Response".to_string()));
1523 }
1524 _ => panic!("Expected Closure type"),
1525 }
1526 assert_eq!(rest, StackType::RowVar("a".to_string()));
1527 }
1528
1529 #[test]
1530 fn test_parse_closure_type_missing_bracket() {
1531 let source = ": broken ( Int -- Closure ) ;";
1533 let mut parser = Parser::new(source);
1534 let result = parser.parse();
1535
1536 assert!(result.is_err());
1537 let err_msg = result.unwrap_err();
1538 assert!(
1539 err_msg.contains("[") && err_msg.contains("Closure"),
1540 "Expected error about missing '[' after Closure, got: {}",
1541 err_msg
1542 );
1543 }
1544
1545 #[test]
1546 fn test_parse_closure_type_in_input() {
1547 let source = ": apply-closure ( Closure[Int -- Int] -- ) ;";
1549 let mut parser = Parser::new(source);
1550 let program = parser.parse().unwrap();
1551
1552 let effect = program.words[0].effect.as_ref().unwrap();
1553
1554 let (_, top) = effect.inputs.clone().pop().unwrap();
1556 match top {
1557 Type::Closure { effect, .. } => {
1558 assert!(matches!(effect.inputs.clone().pop().unwrap().1, Type::Int));
1560 assert!(matches!(effect.outputs.clone().pop().unwrap().1, Type::Int));
1561 }
1562 _ => panic!("Expected Closure type in input"),
1563 }
1564 }
1565}