Skip to main content

a3s_acl/
parser.rs

1// ============================================================================
2// ACL Parser - Parser for Agent Configuration Language
3// ============================================================================
4
5use crate::ast::{Block, Document, Value};
6use crate::lexer::{Lexer, Token, TokenWithSpan};
7use std::collections::HashMap;
8
9/// Parse error
10#[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
29/// ACL Parser
30pub 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    /// Parse the entire input into a Document
73    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                    // Check if this is a bare attribute (name = value) or a block
85                    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(); // consume the identifier
96                                        // Parse as a single-attribute block (name = value)
97                        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                        // Parse as a block
107                        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    /// Parse a single block
126    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        // Parse optional labels (e.g., "openai" in providers "openai" { })
153        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                    // This is a block without labels, break
167                    break;
168                }
169                _ => break,
170            }
171        }
172
173        self.skip_newlines();
174
175        // Parse block body
176        let (blocks, attributes) = self.parse_block_body()?;
177
178        Ok(Block {
179            name: ident,
180            labels,
181            blocks,
182            attributes,
183        })
184    }
185
186    /// Parse the body of a block (attributes and nested blocks)
187    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        // Handle blocks without braces (implicit blocks)
194        while let Some(t) = self.current() {
195            match &t.token {
196                Token::RightBrace => {
197                    self.advance();
198                    break;
199                }
200                Token::LeftBrace => {
201                    // This shouldn't happen normally
202                    self.advance();
203                }
204                Token::Ident(name) => {
205                    // Check if this is a nested block or attribute
206                    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                                // It's an attribute: name = value or name : value
214                                self.advance(); // consume the identifier
215                                let attr = self.parse_attribute(name)?;
216                                attributes.insert(attr.0, attr.1);
217                            }
218                            Token::String(_) => {
219                                // It's a block with a string label, parse as nested block
220                                let block = self.parse_block()?;
221                                blocks.push(block);
222                            }
223                            Token::LeftBrace => {
224                                // Nested block with no labels
225                                self.advance(); // consume the block type name
226                                let block = self.parse_nested_block(name.clone())?;
227                                blocks.push(block);
228                            }
229                            Token::Ident(_) => {
230                                // This could be a nested block type
231                                // Check if next is a label or another ident
232                                if let Some(after) = after_after {
233                                    match &after.token {
234                                        Token::LeftBrace | Token::String(_) => {
235                                            // It's a nested block
236                                            let block = self.parse_block()?;
237                                            blocks.push(block);
238                                        }
239                                        Token::Equal | Token::Colon => {
240                                            // It's an attribute
241                                            self.advance(); // consume the identifier
242                                            let attr = self.parse_attribute(name)?;
243                                            attributes.insert(attr.0, attr.1);
244                                        }
245                                        _ => {
246                                            // Try as nested block
247                                            let block = self.parse_block()?;
248                                            blocks.push(block);
249                                        }
250                                    }
251                                } else {
252                                    // Just an identifier, skip
253                                    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                    // Skip unexpected tokens
270                    self.advance();
271                }
272            }
273            self.skip_newlines();
274        }
275
276        Ok((blocks, attributes))
277    }
278
279    /// Parse a nested block after we've already consumed the type name
280    fn parse_nested_block(&mut self, name: String) -> Result<Block, ParseError> {
281        // Check for labels
282        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    /// Parse an attribute assignment: name = value
306    fn parse_attribute(&mut self, name: String) -> Result<(String, Value), ParseError> {
307        // Already consumed the attribute name, now expecting = or :
308        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    /// Parse a value
327    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                // Could be an identifier, a nested block, or a function call
367                self.advance();
368                self.skip_newlines();
369
370                // Check if this is a function call: name(args)
371                if let Some(next) = self.current() {
372                    match &next.token {
373                        Token::LeftParen => {
374                            // It's a function call
375                            self.advance(); // consume '('
376                            let args = self.parse_call_args()?;
377                            return Ok(Value::Call(name, args));
378                        }
379                        Token::LeftBrace => {
380                            // It's a nested block, but we already consumed the name
381                            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    /// Parse a list: [1, 2, 3]
418    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                    // Check for comma or end of list
441                    if let Some(next) = self.current() {
442                        match &next.token {
443                            Token::Comma => {
444                                self.advance(); // consume comma
445                                self.skip_newlines();
446                            }
447                            Token::RightBracket => {
448                                // end of list
449                            }
450                            _ => {}
451                        }
452                    }
453                }
454            }
455        }
456
457        Ok(Value::List(items))
458    }
459
460    /// Parse function call arguments: arg1, arg2, ...)
461    fn parse_call_args(&mut self) -> Result<Vec<Value>, ParseError> {
462        let mut args = Vec::new();
463        self.skip_newlines();
464
465        // Handle empty args
466        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(); // consume comma
481                    self.skip_newlines();
482                }
483                Some(t) if matches!(t.token, Token::RightParen) => {
484                    self.advance(); // consume ')'
485                    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
508/// Parse ACL text into a Document
509pub 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        // Test list with single element
581        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        // Test with unexpected end of input in attribute value
808        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        // Test case: ident followed by left brace (implicit block without labels)
897        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        // Test case: ident followed by string label then left brace
910        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        // Test case: ident followed by another ident and then left brace
922        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        // Test that unexpected tokens in block body are skipped
988        let input = r#"
989            block {
990                key = "value"
991                unexpected_token
992                other = "data"
993            }
994        "#;
995        let doc = parse(input).unwrap();
996        // The parser should skip unexpected tokens and continue
997        assert_eq!(doc.blocks.len(), 1);
998    }
999
1000    #[test]
1001    fn test_parse_only_newline_in_list() {
1002        // Test list with newlines between items
1003        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        // May or may not parse depending on parser rules
1023        // Just ensure no crash
1024        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        // Block with just name, no braces
1060        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        // Test nested block where type is an identifier (not string)
1069        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        // Test += operator (though it's not fully supported)
1081        let input = r#"key += value"#;
1082        let result = parse(input);
1083        // Should either parse or error gracefully
1084        assert!(result.is_ok() || result.is_err());
1085    }
1086
1087    #[test]
1088    fn test_parse_attr_with_colon() {
1089        // Test attribute with colon separator
1090        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); // attr1 and attr3
1193        assert!(!outer.blocks.is_empty()); // inner block
1194    }
1195
1196    #[test]
1197    fn test_parse_block_starting_with_dot() {
1198        // String starting with dot needs quoting
1199        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        // Test that identifier value followed by block parses correctly
1291        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        // Test string containing = character
1302        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        // String starting with dot needs quotes
1344        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        // Test EOF reached while parsing - unclosed string
1359        let input = r#"key = "unclosed"#;
1360        let result = parse(input);
1361        // This might not error depending on how lexer handles it
1362        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        // EOF during block name parsing triggers line 142-148
1389        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        // EOF in nested block context is handled gracefully
1405        let input = r#"outer { inner"#;
1406        let result = parse(input);
1407        // Parser handles this gracefully by treating inner as a block
1408        assert!(result.is_ok());
1409    }
1410
1411    #[test]
1412    fn test_parse_error_invalid_list_token() {
1413        // Unknown token where value is expected
1414        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        // Identifier at end of input (triggers peek returning None)
1422        let input = r#"name"#;
1423        let doc = parse(input).unwrap();
1424        // Should parse as a block with no attributes
1425        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        // Block followed by newline in body
1432        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        // Number where block name expected - triggers line 135-139
1441        let input = r#"123"#;
1442        let result = parse(input);
1443        assert!(result.is_err());
1444        let err = result.unwrap_err();
1445        // Parser returns error for unexpected token
1446        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        // Block with string label followed by attribute
1454        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        // Block with empty string label
1465        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        // Block with multiple string labels
1475        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        // String followed by left brace is parsed as block
1485        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        // Attribute with += operator
1496        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        // env() function call
1504        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        // concat() function call with multiple args
1520        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        // Nested function call
1535        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        // Function call with no args
1550        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        // Function call as list item
1565        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        // Function call without closing paren - should error
1579        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}