1use crate::ast::{Block, Document, Value};
6use crate::lexer::{Lexer, Token, TokenWithSpan};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct ParseError {
12 pub message: String,
13 pub line: usize,
14 pub column: usize,
15}
16
17impl std::fmt::Display for ParseError {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 write!(
20 f,
21 "Parse error at line {}, column {}: {}",
22 self.line, self.column, self.message
23 )
24 }
25}
26
27impl std::error::Error for ParseError {}
28
29pub struct Parser<'a> {
31 tokens: Vec<TokenWithSpan>,
32 pos: usize,
33 _marker: std::marker::PhantomData<&'a ()>,
34}
35
36impl<'a> Parser<'a> {
37 pub fn new(input: &'a str) -> Self {
38 let mut lexer = Lexer::new(input);
39 let tokens = lexer.tokenize();
40
41 Self {
42 tokens,
43 pos: 0,
44 _marker: std::marker::PhantomData,
45 }
46 }
47
48 fn current(&self) -> Option<&TokenWithSpan> {
49 self.tokens.get(self.pos)
50 }
51
52 fn peek(&self, offset: usize) -> Option<&TokenWithSpan> {
53 self.tokens.get(self.pos + offset)
54 }
55
56 fn advance(&mut self) {
57 if self.pos < self.tokens.len() - 1 {
58 self.pos += 1;
59 }
60 }
61
62 fn skip_newlines(&mut self) {
63 while let Some(t) = self.current() {
64 if matches!(t.token, Token::Newline | Token::Comment) {
65 self.advance();
66 } else {
67 break;
68 }
69 }
70 }
71
72 pub fn parse(&mut self) -> Result<Document, ParseError> {
74 let mut doc = Document::default();
75 self.skip_newlines();
76
77 while let Some(t) = self.current() {
78 if matches!(t.token, Token::Eof) {
79 break;
80 }
81
82 match &t.token {
83 Token::Ident(name) => {
84 let name = name.clone();
86 let is_bare_attr = {
87 if let Some(next) = self.peek(1) {
88 matches!(&next.token, Token::Equal | Token::Colon)
89 } else {
90 false
91 }
92 };
93
94 if is_bare_attr {
95 self.advance(); let attr = self.parse_attribute(name.clone())?;
98 let block = Block {
99 name,
100 labels: vec![],
101 blocks: vec![],
102 attributes: vec![attr].into_iter().collect(),
103 };
104 doc.blocks.push(block);
105 } else {
106 let block = self.parse_block()?;
108 doc.blocks.push(block);
109 }
110 }
111 _ => {
112 return Err(ParseError {
113 message: format!("Unexpected token: {:?}", t.token),
114 line: t.span.start.line,
115 column: t.span.start.column,
116 });
117 }
118 }
119 self.skip_newlines();
120 }
121
122 Ok(doc)
123 }
124
125 fn parse_block(&mut self) -> Result<Block, ParseError> {
127 let ident = match self.current() {
128 Some(t) if matches!(t.token, Token::Ident(_)) => {
129 let name = match &t.token {
130 Token::Ident(s) => s.clone(),
131 _ => unreachable!(),
132 };
133 self.advance();
134 name
135 }
136 Some(t) => {
137 return Err(ParseError {
138 message: format!("Expected block name, found {:?}", t.token),
139 line: t.span.start.line,
140 column: t.span.start.column,
141 });
142 }
143 None => {
144 return Err(ParseError {
145 message: "Unexpected end of input".to_string(),
146 line: 0,
147 column: 0,
148 });
149 }
150 };
151
152 let mut labels = Vec::new();
154 while let Some(t) = self.current() {
155 match &t.token {
156 Token::String(s) => {
157 labels.push(s.clone());
158 self.advance();
159 }
160 Token::Ident(s)
161 if self
162 .peek(1)
163 .map(|p| matches!(p.token, Token::LeftBrace))
164 .unwrap_or(false) =>
165 {
166 break;
168 }
169 _ => break,
170 }
171 }
172
173 self.skip_newlines();
174
175 let (blocks, attributes) = self.parse_block_body()?;
177
178 Ok(Block {
179 name: ident,
180 labels,
181 blocks,
182 attributes,
183 })
184 }
185
186 fn parse_block_body(&mut self) -> Result<(Vec<Block>, HashMap<String, Value>), ParseError> {
188 let mut blocks = Vec::new();
189 let mut attributes = HashMap::new();
190
191 self.skip_newlines();
192
193 while let Some(t) = self.current() {
195 match &t.token {
196 Token::RightBrace => {
197 self.advance();
198 break;
199 }
200 Token::LeftBrace => {
201 self.advance();
203 }
204 Token::Ident(name) => {
205 let name = name.clone();
207 let after_ident = self.peek(1);
208 let after_after = self.peek(2);
209
210 if let Some(next) = after_ident {
211 match &next.token {
212 Token::Equal | Token::Colon => {
213 self.advance(); let attr = self.parse_attribute(name)?;
216 attributes.insert(attr.0, attr.1);
217 }
218 Token::String(_) => {
219 let block = self.parse_block()?;
221 blocks.push(block);
222 }
223 Token::LeftBrace => {
224 self.advance(); let block = self.parse_nested_block(name.clone())?;
227 blocks.push(block);
228 }
229 Token::Ident(_) => {
230 if let Some(after) = after_after {
233 match &after.token {
234 Token::LeftBrace | Token::String(_) => {
235 let block = self.parse_block()?;
237 blocks.push(block);
238 }
239 Token::Equal | Token::Colon => {
240 self.advance(); let attr = self.parse_attribute(name)?;
243 attributes.insert(attr.0, attr.1);
244 }
245 _ => {
246 let block = self.parse_block()?;
248 blocks.push(block);
249 }
250 }
251 } else {
252 self.advance();
254 }
255 }
256 _ => {
257 self.advance();
258 }
259 }
260 } else {
261 self.advance();
262 }
263 }
264 Token::Newline | Token::Comment => {
265 self.advance();
266 }
267 Token::Eof => break,
268 _ => {
269 self.advance();
271 }
272 }
273 self.skip_newlines();
274 }
275
276 Ok((blocks, attributes))
277 }
278
279 fn parse_nested_block(&mut self, name: String) -> Result<Block, ParseError> {
281 let mut labels = Vec::new();
283 while let Some(t) = self.current() {
284 match &t.token {
285 Token::String(s) => {
286 labels.push(s.clone());
287 self.advance();
288 }
289 _ => break,
290 }
291 }
292
293 self.skip_newlines();
294
295 let (blocks, attributes) = self.parse_block_body()?;
296
297 Ok(Block {
298 name,
299 labels,
300 blocks,
301 attributes,
302 })
303 }
304
305 fn parse_attribute(&mut self, name: String) -> Result<(String, Value), ParseError> {
307 let token = self.current().cloned();
309 if let Some(t) = token {
310 if !matches!(t.token, Token::Equal | Token::Colon) {
311 return Err(ParseError {
312 message: format!("Expected '=' or ':', found {:?}", t.token),
313 line: t.span.start.line,
314 column: t.span.start.column,
315 });
316 }
317 self.advance();
318 }
319
320 self.skip_newlines();
321 let value = self.parse_value()?;
322
323 Ok((name, value))
324 }
325
326 fn parse_value(&mut self) -> Result<Value, ParseError> {
328 let token = self.current().ok_or_else(|| ParseError {
329 message: "Unexpected end of input".to_string(),
330 line: 0,
331 column: 0,
332 })?;
333
334 match token.token.clone() {
335 Token::String(s) => {
336 self.advance();
337 Ok(Value::String(s))
338 }
339 Token::Number(n) => {
340 self.advance();
341 Ok(Value::Number(n))
342 }
343 Token::True => {
344 self.advance();
345 Ok(Value::Bool(true))
346 }
347 Token::False => {
348 self.advance();
349 Ok(Value::Bool(false))
350 }
351 Token::Null => {
352 self.advance();
353 Ok(Value::Null)
354 }
355 Token::LeftBracket => {
356 self.advance();
357 self.parse_list()
358 }
359 Token::LeftBrace => {
360 self.advance();
361 let (_blocks, attrs) = self.parse_block_body()?;
362 Ok(Value::Object(attrs.into_iter().collect()))
363 }
364 Token::Ident(name) => {
365 let name = name.clone();
366 self.advance();
368 self.skip_newlines();
369
370 if let Some(next) = self.current() {
372 match &next.token {
373 Token::LeftParen => {
374 self.advance(); let args = self.parse_call_args()?;
377 return Ok(Value::Call(name, args));
378 }
379 Token::LeftBrace => {
380 let block = self.parse_nested_block(name)?;
382 return Ok(Value::Object(vec![(
383 "_block".to_string(),
384 Value::Object(vec![
385 ("name".to_string(), Value::String(block.name)),
386 (
387 "labels".to_string(),
388 Value::List(
389 block
390 .labels
391 .iter()
392 .map(|s| Value::String(s.clone()))
393 .collect(),
394 ),
395 ),
396 (
397 "attributes".to_string(),
398 Value::Object(block.attributes.into_iter().collect()),
399 ),
400 ]),
401 )]));
402 }
403 _ => {}
404 }
405 }
406
407 Ok(Value::String(name))
408 }
409 _ => Err(ParseError {
410 message: format!("Unexpected token in value position: {:?}", token.token),
411 line: token.span.start.line,
412 column: token.span.start.column,
413 }),
414 }
415 }
416
417 fn parse_list(&mut self) -> Result<Value, ParseError> {
419 let mut items = Vec::new();
420 self.skip_newlines();
421
422 while let Some(t) = self.current() {
423 match &t.token {
424 Token::RightBracket => {
425 self.advance();
426 break;
427 }
428 Token::Comma => {
429 self.advance();
430 self.skip_newlines();
431 }
432 Token::Newline | Token::Comment => {
433 self.advance();
434 }
435 _ => {
436 let value = self.parse_value()?;
437 items.push(value);
438 self.skip_newlines();
439
440 if let Some(next) = self.current() {
442 match &next.token {
443 Token::Comma => {
444 self.advance(); self.skip_newlines();
446 }
447 Token::RightBracket => {
448 }
450 _ => {}
451 }
452 }
453 }
454 }
455 }
456
457 Ok(Value::List(items))
458 }
459
460 fn parse_call_args(&mut self) -> Result<Vec<Value>, ParseError> {
462 let mut args = Vec::new();
463 self.skip_newlines();
464
465 if let Some(t) = self.current() {
467 if matches!(t.token, Token::RightParen) {
468 self.advance();
469 return Ok(args);
470 }
471 }
472
473 loop {
474 let value = self.parse_value()?;
475 args.push(value);
476 self.skip_newlines();
477
478 match self.current() {
479 Some(t) if matches!(t.token, Token::Comma) => {
480 self.advance(); self.skip_newlines();
482 }
483 Some(t) if matches!(t.token, Token::RightParen) => {
484 self.advance(); break;
486 }
487 Some(t) => {
488 return Err(ParseError {
489 message: format!("Expected ',' or ')', found {:?}", t.token),
490 line: t.span.start.line,
491 column: t.span.start.column,
492 });
493 }
494 None => {
495 return Err(ParseError {
496 message: "Unexpected end of input in function call".to_string(),
497 line: 0,
498 column: 0,
499 });
500 }
501 }
502 }
503
504 Ok(args)
505 }
506}
507
508pub fn parse(input: &str) -> Result<Document, ParseError> {
510 Parser::new(input).parse()
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516
517 #[test]
518 fn test_parse_simple() {
519 let input = r#"
520 name = "test"
521 count = 42
522 "#;
523 let doc = parse(input).unwrap();
524 assert_eq!(doc.blocks.len(), 2);
525 assert_eq!(doc.blocks[0].name, "name");
526 assert_eq!(doc.blocks[1].name, "count");
527 }
528
529 #[test]
530 fn test_parse_block() {
531 let input = r#"
532 providers "openai" {
533 name = "openai"
534 api_key = "sk-test"
535 }
536 "#;
537 let doc = parse(input).unwrap();
538 assert_eq!(doc.blocks.len(), 1);
539 let block = &doc.blocks[0];
540 assert_eq!(block.name, "providers");
541 assert_eq!(block.labels, vec!["openai"]);
542 assert_eq!(
543 block.attributes.get("name").map(|v| v.to_string()).unwrap(),
544 "openai"
545 );
546 }
547
548 #[test]
549 fn test_parse_nested_block() {
550 let input = r#"
551 providers {
552 openai "gpt-4" {
553 model = "gpt-4"
554 }
555 }
556 "#;
557 let doc = parse(input).unwrap();
558 assert_eq!(doc.blocks.len(), 1);
559 let block = &doc.blocks[0];
560 assert_eq!(block.name, "providers");
561 assert!(block.blocks.len() >= 1);
562 }
563
564 #[test]
565 fn test_parse_list() {
566 let input = r#"
567 numbers = [1, 2, 3]
568 "#;
569 let doc = parse(input).unwrap();
570 assert_eq!(doc.blocks.len(), 1);
571 let attr = doc.blocks[0].attributes.get("numbers").unwrap();
572 match attr {
573 Value::List(items) => assert_eq!(items.len(), 3),
574 _ => panic!("Expected list"),
575 }
576 }
577
578 #[test]
579 fn test_parse_string_list() {
580 let input = r#"
582 names = ["alice"]
583 "#;
584 let doc = parse(input).unwrap();
585 let attr = doc.blocks[0].attributes.get("names").unwrap();
586 match attr {
587 Value::List(items) => assert_eq!(items.len(), 1),
588 _ => panic!("Expected list"),
589 }
590 }
591
592 #[test]
593 fn test_parse_boolean_true() {
594 let input = r#"
595 enabled = true
596 "#;
597 let doc = parse(input).unwrap();
598 let attr = doc.blocks[0].attributes.get("enabled").unwrap();
599 assert_eq!(attr, &Value::Bool(true));
600 }
601
602 #[test]
603 fn test_parse_boolean_false() {
604 let input = r#"
605 enabled = false
606 "#;
607 let doc = parse(input).unwrap();
608 let attr = doc.blocks[0].attributes.get("enabled").unwrap();
609 assert_eq!(attr, &Value::Bool(false));
610 }
611
612 #[test]
613 fn test_parse_null() {
614 let input = r#"
615 value = null
616 "#;
617 let doc = parse(input).unwrap();
618 let attr = doc.blocks[0].attributes.get("value").unwrap();
619 assert_eq!(attr, &Value::Null);
620 }
621
622 #[test]
623 fn test_parse_number_integer() {
624 let input = r#"
625 count = 42
626 "#;
627 let doc = parse(input).unwrap();
628 let attr = doc.blocks[0].attributes.get("count").unwrap();
629 match attr {
630 Value::Number(n) => assert_eq!(*n, 42.0),
631 _ => panic!("Expected number"),
632 }
633 }
634
635 #[test]
636 fn test_parse_number_float() {
637 let input = r#"
638 pi = 3.14
639 "#;
640 let doc = parse(input).unwrap();
641 let attr = doc.blocks[0].attributes.get("pi").unwrap();
642 match attr {
643 Value::Number(n) => assert!((*n - 3.14).abs() < 0.001),
644 _ => panic!("Expected number"),
645 }
646 }
647
648 #[test]
649 fn test_parse_negative_number() {
650 let input = r#"
651 temp = -10
652 "#;
653 let doc = parse(input).unwrap();
654 let attr = doc.blocks[0].attributes.get("temp").unwrap();
655 match attr {
656 Value::Number(n) => assert_eq!(*n, -10.0),
657 _ => panic!("Expected number"),
658 }
659 }
660
661 #[test]
662 fn test_parse_string_value() {
663 let input = r#"
664 name = "hello world"
665 "#;
666 let doc = parse(input).unwrap();
667 let attr = doc.blocks[0].attributes.get("name").unwrap();
668 match attr {
669 Value::String(s) => assert_eq!(s, "hello world"),
670 _ => panic!("Expected string"),
671 }
672 }
673
674 #[test]
675 fn test_parse_colon_separator() {
676 let input = r#"
677 config {
678 key: "value"
679 }
680 "#;
681 let doc = parse(input).unwrap();
682 assert_eq!(doc.blocks.len(), 1);
683 assert_eq!(doc.blocks[0].name, "config");
684 }
685
686 #[test]
687 fn test_parse_error_display() {
688 let err = ParseError {
689 message: "test error".to_string(),
690 line: 10,
691 column: 5,
692 };
693 let display = format!("{}", err);
694 assert!(display.contains("line 10"));
695 assert!(display.contains("column 5"));
696 assert!(display.contains("test error"));
697 }
698
699 #[test]
700 fn test_parse_error_trait() {
701 let err = ParseError {
702 message: "test".to_string(),
703 line: 1,
704 column: 1,
705 };
706 let _ = err.clone();
707 }
708
709 #[test]
710 fn test_parse_empty_document() {
711 let input = "";
712 let doc = parse(input).unwrap();
713 assert!(doc.blocks.is_empty());
714 }
715
716 #[test]
717 fn test_parse_only_newlines() {
718 let input = "\n\n\n";
719 let doc = parse(input).unwrap();
720 assert!(doc.blocks.is_empty());
721 }
722
723 #[test]
724 fn test_parse_comments() {
725 let input = r#"
726 # this is a comment
727 name = "test"
728 // another comment
729 count = 42
730 "#;
731 let doc = parse(input).unwrap();
732 assert_eq!(doc.blocks.len(), 2);
733 }
734
735 #[test]
736 fn test_parse_block_no_braces() {
737 let input = r#"
738 config
739 name = "test"
740 "#;
741 let doc = parse(input).unwrap();
742 assert_eq!(doc.blocks.len(), 1);
743 assert_eq!(doc.blocks[0].name, "config");
744 }
745
746 #[test]
747 fn test_parse_multiple_blocks() {
748 let input = r#"
749 block1 "label1" {}
750 block2 "label2" {}
751 "#;
752 let doc = parse(input).unwrap();
753 assert_eq!(doc.blocks.len(), 2);
754 assert_eq!(doc.blocks[0].name, "block1");
755 assert_eq!(doc.blocks[1].name, "block2");
756 }
757
758 #[test]
759 fn test_parse_identifier_value() {
760 let input = r#"
761 ref = other_name
762 "#;
763 let doc = parse(input).unwrap();
764 let attr = doc.blocks[0].attributes.get("ref").unwrap();
765 match attr {
766 Value::String(s) => assert_eq!(s, "other_name"),
767 _ => panic!("Expected string"),
768 }
769 }
770
771 #[test]
772 fn test_parse_block_with_block_nested_inside() {
773 let input = r#"
774 outer {
775 inner "label" {
776 key = "value"
777 }
778 }
779 "#;
780 let doc = parse(input).unwrap();
781 assert_eq!(doc.blocks.len(), 1);
782 assert_eq!(doc.blocks[0].name, "outer");
783 }
784
785 #[test]
786 fn test_parse_implicit_block_with_labels() {
787 let input = r#"
788 item "first" "second" {
789 name = "test"
790 }
791 "#;
792 let doc = parse(input).unwrap();
793 assert_eq!(doc.blocks[0].labels.len(), 2);
794 }
795
796 #[test]
797 fn test_parse_error_unexpected_token() {
798 let input = r#"
799 [invalid]
800 "#;
801 let result = parse(input);
802 assert!(result.is_err());
803 }
804
805 #[test]
806 fn test_parse_error_eof_in_block() {
807 let input = r#"block key = "#;
809 let result = parse(input);
810 assert!(result.is_err());
811 }
812
813 #[test]
814 fn test_parse_block_no_labels() {
815 let input = r#"
816 block {
817 key = "value"
818 }
819 "#;
820 let doc = parse(input).unwrap();
821 assert_eq!(doc.blocks.len(), 1);
822 assert_eq!(doc.blocks[0].name, "block");
823 assert!(doc.blocks[0].labels.is_empty());
824 }
825
826 #[test]
827 fn test_parse_multiple_attributes() {
828 let input = r#"
829 config {
830 a = 1
831 b = 2
832 c = 3
833 }
834 "#;
835 let doc = parse(input).unwrap();
836 assert_eq!(doc.blocks[0].attributes.len(), 3);
837 }
838
839 #[test]
840 fn test_parse_list_of_numbers() {
841 let input = r#"
842 nums = [1, 2, 3]
843 "#;
844 let doc = parse(input).unwrap();
845 let attr = doc.blocks[0].attributes.get("nums").unwrap();
846 match attr {
847 Value::List(items) => {
848 assert_eq!(items.len(), 3);
849 assert_eq!(items[0], Value::Number(1.0));
850 assert_eq!(items[1], Value::Number(2.0));
851 assert_eq!(items[2], Value::Number(3.0));
852 }
853 _ => panic!("Expected list"),
854 }
855 }
856
857 #[test]
858 fn test_parse_list_of_mixed() {
859 let input = r#"
860 mixed = ["string", 42, true]
861 "#;
862 let doc = parse(input).unwrap();
863 let attr = doc.blocks[0].attributes.get("mixed").unwrap();
864 match attr {
865 Value::List(items) => assert_eq!(items.len(), 3),
866 _ => panic!("Expected list"),
867 }
868 }
869
870 #[test]
871 fn test_parse_empty_list() {
872 let input = r#"items = []"#;
873 let doc = parse(input).unwrap();
874 let attr = doc.blocks[0].attributes.get("items").unwrap();
875 match attr {
876 Value::List(items) => assert!(items.is_empty()),
877 _ => panic!("Expected list"),
878 }
879 }
880
881 #[test]
882 fn test_parse_nested_block_with_labels() {
883 let input = r#"
884 parent {
885 child "label" {
886 key = "value"
887 }
888 }
889 "#;
890 let doc = parse(input).unwrap();
891 assert!(!doc.blocks[0].blocks.is_empty());
892 }
893
894 #[test]
895 fn test_parse_left_brace_after_ident() {
896 let input = r#"
898 myblock {
899 key = "value"
900 }
901 "#;
902 let doc = parse(input).unwrap();
903 assert_eq!(doc.blocks.len(), 1);
904 assert_eq!(doc.blocks[0].name, "myblock");
905 }
906
907 #[test]
908 fn test_parse_left_brace_after_string() {
909 let input = r#"
911 myblock "label" {
912 key = "value"
913 }
914 "#;
915 let doc = parse(input).unwrap();
916 assert_eq!(doc.blocks[0].labels, vec!["label"]);
917 }
918
919 #[test]
920 fn test_parse_ident_after_ident_with_left_brace() {
921 let input = r#"
923 type "name" {
924 key = "value"
925 }
926 "#;
927 let doc = parse(input).unwrap();
928 assert_eq!(doc.blocks[0].name, "type");
929 assert_eq!(doc.blocks[0].labels, vec!["name"]);
930 }
931
932 #[test]
933 fn test_parse_block_sparse_labels() {
934 let input = r#"
935 block "a" "b" "c" {}
936 "#;
937 let doc = parse(input).unwrap();
938 assert_eq!(doc.blocks[0].labels, vec!["a", "b", "c"]);
939 }
940
941 #[test]
942 fn test_parse_value_number_negative() {
943 let input = r#"val = -123"#;
944 let doc = parse(input).unwrap();
945 let attr = doc.blocks[0].attributes.get("val").unwrap();
946 assert_eq!(attr, &Value::Number(-123.0));
947 }
948
949 #[test]
950 fn test_parse_value_number_float() {
951 let input = r#"val = 0.5"#;
952 let doc = parse(input).unwrap();
953 let attr = doc.blocks[0].attributes.get("val").unwrap();
954 match attr {
955 Value::Number(n) => assert!((*n - 0.5).abs() < 0.001),
956 _ => panic!("Expected number"),
957 }
958 }
959
960 #[test]
961 fn test_parse_whitespace_only() {
962 let input = " \t\t \n\n\n ";
963 let doc = parse(input).unwrap();
964 assert!(doc.blocks.is_empty());
965 }
966
967 #[test]
968 fn test_parse_only_comments() {
969 let input = "# comment\n// another\n# third";
970 let doc = parse(input).unwrap();
971 assert!(doc.blocks.is_empty());
972 }
973
974 #[test]
975 fn test_parse_comment_between_statements() {
976 let input = r#"
977 a = 1
978 # comment
979 b = 2
980 "#;
981 let doc = parse(input).unwrap();
982 assert_eq!(doc.blocks.len(), 2);
983 }
984
985 #[test]
986 fn test_parse_skip_unexpected_token() {
987 let input = r#"
989 block {
990 key = "value"
991 unexpected_token
992 other = "data"
993 }
994 "#;
995 let doc = parse(input).unwrap();
996 assert_eq!(doc.blocks.len(), 1);
998 }
999
1000 #[test]
1001 fn test_parse_only_newline_in_list() {
1002 let input = r#"
1004 nums = [
1005 1,
1006 2,
1007 3
1008 ]
1009 "#;
1010 let doc = parse(input).unwrap();
1011 let attr = doc.blocks[0].attributes.get("nums").unwrap();
1012 match attr {
1013 Value::List(items) => assert_eq!(items.len(), 3),
1014 _ => panic!("Expected list"),
1015 }
1016 }
1017
1018 #[test]
1019 fn test_parse_list_trailing_comma() {
1020 let input = r#"nums = [1, 2, 3,]"#;
1021 let result = parse(input);
1022 assert!(result.is_ok() || result.is_err());
1025 }
1026
1027 #[test]
1028 fn test_parse_block_with_comment_before_attr() {
1029 let input = r#"
1030 block {
1031 # this is a comment
1032 key = "value"
1033 }
1034 "#;
1035 let doc = parse(input).unwrap();
1036 assert_eq!(
1037 doc.blocks[0]
1038 .attributes
1039 .get("key")
1040 .map(|v| v.to_string())
1041 .unwrap(),
1042 "value"
1043 );
1044 }
1045
1046 #[test]
1047 fn test_parse_float_scientific() {
1048 let input = r#"val = 1e-5"#;
1049 let doc = parse(input).unwrap();
1050 let attr = doc.blocks[0].attributes.get("val").unwrap();
1051 match attr {
1052 Value::Number(n) => assert!((*n - 0.00001).abs() < 0.000001),
1053 _ => panic!("Expected number"),
1054 }
1055 }
1056
1057 #[test]
1058 fn test_parse_block_without_body() {
1059 let input = r#"empty_block"#;
1061 let doc = parse(input).unwrap();
1062 assert_eq!(doc.blocks.len(), 1);
1063 assert_eq!(doc.blocks[0].name, "empty_block");
1064 }
1065
1066 #[test]
1067 fn test_parse_nested_block_type_ident() {
1068 let input = r#"
1070 outer {
1071 inner "label" {}
1072 }
1073 "#;
1074 let doc = parse(input).unwrap();
1075 assert!(!doc.blocks[0].blocks.is_empty());
1076 }
1077
1078 #[test]
1079 fn test_parse_block_with_plus_equal() {
1080 let input = r#"key += value"#;
1082 let result = parse(input);
1083 assert!(result.is_ok() || result.is_err());
1085 }
1086
1087 #[test]
1088 fn test_parse_attr_with_colon() {
1089 let input = r#"key: "value""#;
1091 let doc = parse(input).unwrap();
1092 assert_eq!(
1093 doc.blocks[0]
1094 .attributes
1095 .get("key")
1096 .map(|v| v.to_string())
1097 .unwrap(),
1098 "value"
1099 );
1100 }
1101
1102 #[test]
1103 fn test_parse_block_empty_labels() {
1104 let input = r#"block "" {}"#;
1105 let doc = parse(input).unwrap();
1106 assert_eq!(doc.blocks[0].labels, vec![""]);
1107 }
1108
1109 #[test]
1110 fn test_parse_eof_after_block() {
1111 let input = r#"block {}"#;
1112 let doc = parse(input).unwrap();
1113 assert_eq!(doc.blocks.len(), 1);
1114 }
1115
1116 #[test]
1117 fn test_parse_block_single_quoted_label() {
1118 let input = r#"block 'label' {}"#;
1119 let doc = parse(input).unwrap();
1120 assert_eq!(doc.blocks[0].labels, vec!["label"]);
1121 }
1122
1123 #[test]
1124 fn test_parse_string_with_equals() {
1125 let input = r#"key = "a=b""#;
1126 let doc = parse(input).unwrap();
1127 assert_eq!(
1128 doc.blocks[0]
1129 .attributes
1130 .get("key")
1131 .map(|v| v.to_string())
1132 .unwrap(),
1133 "a=b"
1134 );
1135 }
1136
1137 #[test]
1138 fn test_parse_string_with_braces() {
1139 let input = r#"key = "a{b}c""#;
1140 let doc = parse(input).unwrap();
1141 assert_eq!(
1142 doc.blocks[0]
1143 .attributes
1144 .get("key")
1145 .map(|v| v.to_string())
1146 .unwrap(),
1147 "a{b}c"
1148 );
1149 }
1150
1151 #[test]
1152 fn test_parse_string_with_brackets() {
1153 let input = r#"key = "a[b]c""#;
1154 let doc = parse(input).unwrap();
1155 assert_eq!(
1156 doc.blocks[0]
1157 .attributes
1158 .get("key")
1159 .map(|v| v.to_string())
1160 .unwrap(),
1161 "a[b]c"
1162 );
1163 }
1164
1165 #[test]
1166 fn test_parse_string_with_hash() {
1167 let input = r#"key = "a#b""#;
1168 let doc = parse(input).unwrap();
1169 assert_eq!(
1170 doc.blocks[0]
1171 .attributes
1172 .get("key")
1173 .map(|v| v.to_string())
1174 .unwrap(),
1175 "a#b"
1176 );
1177 }
1178
1179 #[test]
1180 fn test_parse_mixed_block_and_attrs() {
1181 let input = r#"
1182 outer {
1183 attr1 = 1
1184 inner "label" {
1185 attr2 = 2
1186 }
1187 attr3 = 3
1188 }
1189 "#;
1190 let doc = parse(input).unwrap();
1191 let outer = &doc.blocks[0];
1192 assert_eq!(outer.attributes.len(), 2); assert!(!outer.blocks.is_empty()); }
1195
1196 #[test]
1197 fn test_parse_block_starting_with_dot() {
1198 let input = r#"key = ".hidden""#;
1200 let doc = parse(input).unwrap();
1201 assert_eq!(
1202 doc.blocks[0]
1203 .attributes
1204 .get("key")
1205 .map(|v| v.to_string())
1206 .unwrap(),
1207 ".hidden"
1208 );
1209 }
1210
1211 #[test]
1212 fn test_parse_block_with_number_value() {
1213 let input = r#"key = 0"#;
1214 let doc = parse(input).unwrap();
1215 assert_eq!(
1216 doc.blocks[0]
1217 .attributes
1218 .get("key")
1219 .map(|v| v.to_string())
1220 .unwrap(),
1221 "0"
1222 );
1223 }
1224
1225 #[test]
1226 fn test_parse_bool_true_as_value() {
1227 let input = r#"key = true"#;
1228 let doc = parse(input).unwrap();
1229 assert_eq!(
1230 doc.blocks[0]
1231 .attributes
1232 .get("key")
1233 .map(|v| v.to_string())
1234 .unwrap(),
1235 "true"
1236 );
1237 }
1238
1239 #[test]
1240 fn test_parse_bool_false_as_value() {
1241 let input = r#"key = false"#;
1242 let doc = parse(input).unwrap();
1243 assert_eq!(
1244 doc.blocks[0]
1245 .attributes
1246 .get("key")
1247 .map(|v| v.to_string())
1248 .unwrap(),
1249 "false"
1250 );
1251 }
1252
1253 #[test]
1254 fn test_parse_null_as_value() {
1255 let input = r#"key = null"#;
1256 let doc = parse(input).unwrap();
1257 assert_eq!(
1258 doc.blocks[0]
1259 .attributes
1260 .get("key")
1261 .map(|v| v.to_string())
1262 .unwrap(),
1263 "null"
1264 );
1265 }
1266
1267 #[test]
1268 fn test_parse_string_with_unicode() {
1269 let input = r#"key = "hello 世界""#;
1270 let doc = parse(input).unwrap();
1271 assert_eq!(
1272 doc.blocks[0]
1273 .attributes
1274 .get("key")
1275 .map(|v| v.to_string())
1276 .unwrap(),
1277 "hello 世界"
1278 );
1279 }
1280
1281 #[test]
1282 fn test_parse_multiple_labels_different_types() {
1283 let input = r#"block "a" 'b' "c" {}"#;
1284 let doc = parse(input).unwrap();
1285 assert_eq!(doc.blocks[0].labels, vec!["a", "b", "c"]);
1286 }
1287
1288 #[test]
1289 fn test_parse_ident_value_followed_by_block() {
1290 let input = r#"block {
1292 type container
1293 name = "test"
1294 }"#;
1295 let doc = parse(input).unwrap();
1296 assert_eq!(doc.blocks[0].name, "block");
1297 }
1298
1299 #[test]
1300 fn test_parse_value_with_equals_in_string() {
1301 let input = r#"key = "a=b=c""#;
1303 let doc = parse(input).unwrap();
1304 assert_eq!(
1305 doc.blocks[0]
1306 .attributes
1307 .get("key")
1308 .map(|v| v.to_string())
1309 .unwrap(),
1310 "a=b=c"
1311 );
1312 }
1313
1314 #[test]
1315 fn test_parse_list_with_single_number() {
1316 let input = r#"nums = [42]"#;
1317 let doc = parse(input).unwrap();
1318 let attr = doc.blocks[0].attributes.get("nums").unwrap();
1319 match attr {
1320 Value::List(items) => assert_eq!(items.len(), 1),
1321 _ => panic!("Expected list"),
1322 }
1323 }
1324
1325 #[test]
1326 fn test_parse_list_with_trailing_newline() {
1327 let input = r#"
1328 nums = [
1329 1,
1330 2,
1331 ]
1332 "#;
1333 let doc = parse(input).unwrap();
1334 let attr = doc.blocks[0].attributes.get("nums").unwrap();
1335 match attr {
1336 Value::List(items) => assert_eq!(items.len(), 2),
1337 _ => panic!("Expected list"),
1338 }
1339 }
1340
1341 #[test]
1342 fn test_parse_attr_with_number_starting_with_dot() {
1343 let input = r#"key = ".5""#;
1345 let doc = parse(input).unwrap();
1346 assert_eq!(
1347 doc.blocks[0]
1348 .attributes
1349 .get("key")
1350 .map(|v| v.to_string())
1351 .unwrap(),
1352 ".5"
1353 );
1354 }
1355
1356 #[test]
1357 fn test_parse_eof_in_nested_block() {
1358 let input = r#"key = "unclosed"#;
1360 let result = parse(input);
1361 assert!(result.is_ok() || result.is_err());
1363 }
1364
1365 #[test]
1366 fn test_parse_block_with_comment_after_name() {
1367 let input = r#"
1368 block # comment
1369 key = "value"
1370 "#;
1371 let doc = parse(input).unwrap();
1372 assert_eq!(doc.blocks[0].name, "block");
1373 }
1374
1375 #[test]
1376 fn test_parse_float_with_leading_dot() {
1377 let input = r#"val = .123"#;
1378 let doc = parse(input).unwrap();
1379 let attr = doc.blocks[0].attributes.get("val").unwrap();
1380 match attr {
1381 Value::Number(n) => assert!((*n - 0.123).abs() < 0.001),
1382 _ => panic!("Expected number"),
1383 }
1384 }
1385
1386 #[test]
1387 fn test_parse_error_block_name_eof() {
1388 let input = r#"block";
1390 let result = parse(input);
1391 assert!(result.is_err());
1392 }
1393
1394 #[test]
1395 fn test_parse_error_eof_in_block() {
1396 // EOF after opening brace in block body
1397 let input = r#"block { attr"#;
1398 let result = parse(input);
1399 assert!(result.is_err());
1400 }
1401
1402 #[test]
1403 fn test_parse_eof_in_nested_block_graceful() {
1404 let input = r#"outer { inner"#;
1406 let result = parse(input);
1407 assert!(result.is_ok());
1409 }
1410
1411 #[test]
1412 fn test_parse_error_invalid_list_token() {
1413 let input = r#"val = [}"]"#;
1415 let result = parse(input);
1416 assert!(result.is_err());
1417 }
1418
1419 #[test]
1420 fn test_parse_ident_at_eof() {
1421 let input = r#"name"#;
1423 let doc = parse(input).unwrap();
1424 assert_eq!(doc.blocks.len(), 1);
1426 assert_eq!(doc.blocks[0].name, "name");
1427 }
1428
1429 #[test]
1430 fn test_parse_block_with_only_newline_in_body() {
1431 let input = r#"block {
1433 }"#;
1434 let doc = parse(input).unwrap();
1435 assert_eq!(doc.blocks.len(), 1);
1436 }
1437
1438 #[test]
1439 fn test_parse_error_number_as_block_name() {
1440 let input = r#"123"#;
1442 let result = parse(input);
1443 assert!(result.is_err());
1444 let err = result.unwrap_err();
1445 assert!(
1447 err.message.contains("Unexpected token") || err.message.contains("Expected block name")
1448 );
1449 }
1450
1451 #[test]
1452 fn test_parse_label_then_nested() {
1453 let input = r#"block "label" {
1455 key = "value"
1456 }"#;
1457 let doc = parse(input).unwrap();
1458 assert_eq!(doc.blocks.len(), 1);
1459 assert_eq!(doc.blocks[0].labels, vec!["label"]);
1460 }
1461
1462 #[test]
1463 fn test_parse_block_empty_string_label() {
1464 let input = r#"block "" {
1466 }"#;
1467 let doc = parse(input).unwrap();
1468 assert_eq!(doc.blocks.len(), 1);
1469 assert_eq!(doc.blocks[0].labels, vec![""]);
1470 }
1471
1472 #[test]
1473 fn test_parse_multiple_string_labels() {
1474 let input = r#"block "label1" "label2" {
1476 }"#;
1477 let doc = parse(input).unwrap();
1478 assert_eq!(doc.blocks.len(), 1);
1479 assert_eq!(doc.blocks[0].labels, vec!["label1", "label2"]);
1480 }
1481
1482 #[test]
1483 fn test_parse_string_then_brace_as_block() {
1484 let input = r#"provider "openai" {
1486 key = "value"
1487 }"#;
1488 let doc = parse(input).unwrap();
1489 assert_eq!(doc.blocks.len(), 1);
1490 assert_eq!(doc.blocks[0].name, "provider");
1491 }
1492
1493 #[test]
1494 fn test_parse_attribute_with_plus_equal() {
1495 let input = r#"vals += 1"#;
1497 let doc = parse(input).unwrap();
1498 assert_eq!(doc.blocks.len(), 1);
1499 }
1500
1501 #[test]
1502 fn test_parse_function_call_env() {
1503 let input = r#"api_key = env("API_KEY")"#;
1505 let doc = parse(input).unwrap();
1506 let attr = doc.blocks[0].attributes.get("api_key").unwrap();
1507 match attr {
1508 Value::Call(name, args) => {
1509 assert_eq!(name, "env");
1510 assert_eq!(args.len(), 1);
1511 assert_eq!(args[0], Value::String("API_KEY".to_string()));
1512 }
1513 _ => panic!("Expected Call value"),
1514 }
1515 }
1516
1517 #[test]
1518 fn test_parse_function_call_with_multiple_args() {
1519 let input = r#"url = concat("postgres://", host, ":", port)"#;
1521 let doc = parse(input).unwrap();
1522 let attr = doc.blocks[0].attributes.get("url").unwrap();
1523 match attr {
1524 Value::Call(name, args) => {
1525 assert_eq!(name, "concat");
1526 assert_eq!(args.len(), 4);
1527 }
1528 _ => panic!("Expected Call value"),
1529 }
1530 }
1531
1532 #[test]
1533 fn test_parse_function_call_with_nested() {
1534 let input = r#"path = concat(env("HOME"), "/", "file")"#;
1536 let doc = parse(input).unwrap();
1537 let attr = doc.blocks[0].attributes.get("path").unwrap();
1538 match attr {
1539 Value::Call(name, args) => {
1540 assert_eq!(name, "concat");
1541 assert_eq!(args.len(), 3);
1542 }
1543 _ => panic!("Expected Call value"),
1544 }
1545 }
1546
1547 #[test]
1548 fn test_parse_function_call_empty_args() {
1549 let input = r#"result = getenv()"#;
1551 let doc = parse(input).unwrap();
1552 let attr = doc.blocks[0].attributes.get("result").unwrap();
1553 match attr {
1554 Value::Call(name, args) => {
1555 assert_eq!(name, "getenv");
1556 assert!(args.is_empty());
1557 }
1558 _ => panic!("Expected Call value"),
1559 }
1560 }
1561
1562 #[test]
1563 fn test_parse_function_call_in_list() {
1564 let input = r#"paths = [env("PATH1"), env("PATH2")]"#;
1566 let doc = parse(input).unwrap();
1567 let attr = doc.blocks[0].attributes.get("paths").unwrap();
1568 match attr {
1569 Value::List(items) => {
1570 assert_eq!(items.len(), 2);
1571 }
1572 _ => panic!("Expected List value"),
1573 }
1574 }
1575
1576 #[test]
1577 fn test_parse_function_call_missing_paren() {
1578 let input = r#"val = env("API_KEY"#;
1580 let result = parse(input);
1581 assert!(result.is_err());
1582 }
1583
1584 #[test]
1585 fn test_parse_list_of_objects() {
1586 let input = r#"
1587 knowledge_bases = [
1588 { id = "WThXBKfN21eAxJOl3n1PA", name = "个人知识" },
1589 { id = "another_id", name = "another_name" }
1590 ]
1591 "#;
1592 let doc = parse(input).unwrap();
1593 let attr = doc.blocks[0].attributes.get("knowledge_bases").unwrap();
1594 match attr {
1595 Value::List(items) => {
1596 assert_eq!(items.len(), 2);
1597 match &items[0] {
1598 Value::Object(pairs) => {
1599 let map: HashMap<_, _> = pairs.iter().cloned().collect();
1600 assert_eq!(
1601 map.get("id"),
1602 Some(&Value::String("WThXBKfN21eAxJOl3n1PA".to_string()))
1603 );
1604 assert_eq!(
1605 map.get("name"),
1606 Some(&Value::String("个人知识".to_string()))
1607 );
1608 }
1609 _ => panic!("Expected Object value"),
1610 }
1611 }
1612 _ => panic!("Expected List value"),
1613 }
1614 }
1615
1616 #[test]
1617 fn test_parse_list_of_single_object() {
1618 let input = r#"items = [{ id = "1", name = "test" }]"#;
1619 let doc = parse(input).unwrap();
1620 let attr = doc.blocks[0].attributes.get("items").unwrap();
1621 match attr {
1622 Value::List(items) => assert_eq!(items.len(), 1),
1623 _ => panic!("Expected List value"),
1624 }
1625 }
1626}