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::diagnostic::{DiagnosticCode, DiagnosticReport};
7use crate::lexer::{Lexer, Location, Span, Token, TokenWithSpan};
8use crate::parse_limits::{nesting_limit_error, ParseLimits, DEFAULT_PARSE_LIMITS};
9use std::collections::HashMap;
10
11pub use crate::diagnostic::ParseError;
12
13/// ACL Parser
14pub struct Parser<'a> {
15    tokens: Vec<TokenWithSpan>,
16    pos: usize,
17    limits: ParseLimits,
18    initial_error: Option<ParseError>,
19    block_depth: usize,
20    _marker: std::marker::PhantomData<&'a ()>,
21}
22
23impl<'a> Parser<'a> {
24    pub fn new(input: &'a str) -> Self {
25        Self::with_limits(input, DEFAULT_PARSE_LIMITS)
26    }
27
28    /// Construct a parser with explicit resource limits.
29    pub fn with_limits(input: &'a str, limits: ParseLimits) -> Self {
30        if input.len() > limits.max_document_bytes {
31            return Self {
32                tokens: vec![TokenWithSpan::new(
33                    Token::Eof,
34                    Default::default(),
35                    Default::default(),
36                )],
37                pos: 0,
38                limits,
39                initial_error: Some(ParseError::at(
40                    DiagnosticCode::DocumentBytesLimit,
41                    format!(
42                        "ACL parse limit exceeded: document is larger than {} bytes",
43                        limits.max_document_bytes
44                    ),
45                    Location {
46                        line: 1,
47                        column: 1,
48                        offset: 0,
49                    },
50                )),
51                block_depth: 0,
52                _marker: std::marker::PhantomData,
53            };
54        }
55
56        let mut lexer = Lexer::with_max_token_bytes(input, limits.max_token_bytes);
57        let tokens = lexer.tokenize();
58        let initial_error = lexer.token_limit_span().map(|span| {
59            ParseError::new(
60                DiagnosticCode::TokenBytesLimit,
61                format!(
62                    "ACL parse limit exceeded: token is longer than {} bytes",
63                    limits.max_token_bytes
64                ),
65                span,
66            )
67        });
68        let initial_error =
69            initial_error.or_else(|| nesting_limit_error(&tokens, limits.max_nesting_depth));
70
71        Self {
72            tokens,
73            pos: 0,
74            limits,
75            initial_error,
76            block_depth: 0,
77            _marker: std::marker::PhantomData,
78        }
79    }
80
81    fn current(&self) -> Option<&TokenWithSpan> {
82        self.tokens.get(self.pos)
83    }
84
85    fn peek(&self, offset: usize) -> Option<&TokenWithSpan> {
86        self.tokens.get(self.pos + offset)
87    }
88
89    fn eof_span(&self) -> Span {
90        self.tokens
91            .last()
92            .map(|token| token.span)
93            .unwrap_or_else(|| {
94                Span::point(Location {
95                    line: 1,
96                    column: 1,
97                    offset: 0,
98                })
99            })
100    }
101
102    fn advance(&mut self) {
103        if self.pos < self.tokens.len() - 1 {
104            self.pos += 1;
105        }
106    }
107
108    fn skip_newlines(&mut self) {
109        while let Some(t) = self.current() {
110            if matches!(t.token, Token::Newline | Token::Comment) {
111                self.advance();
112            } else {
113                break;
114            }
115        }
116    }
117
118    /// Parse the entire input into a Document
119    pub fn parse(&mut self) -> Result<Document, ParseError> {
120        if let Some(error) = &self.initial_error {
121            return Err(error.clone());
122        }
123
124        let mut doc = Document::default();
125        self.skip_newlines();
126
127        while !self.is_eof() {
128            self.parse_document_item(&mut doc)?;
129            self.skip_newlines();
130        }
131
132        Ok(doc)
133    }
134
135    /// Collect parse diagnostics in source order with bounded line recovery.
136    pub fn collect_diagnostics(&mut self) -> DiagnosticReport {
137        let mut report = DiagnosticReport::default();
138        if let Some(error) = &self.initial_error {
139            Self::record_diagnostic(&mut report, error.clone(), self.limits.max_diagnostics);
140            return report;
141        }
142
143        let mut doc = Document::default();
144        self.skip_newlines();
145
146        while !self.is_eof() {
147            let item_start = self.pos;
148            match self.parse_document_item(&mut doc) {
149                Ok(()) => self.skip_newlines(),
150                Err(error) => {
151                    let error_line = error.span.start.line;
152                    let is_limit = error.code.is_limit();
153                    if !Self::record_diagnostic(&mut report, error, self.limits.max_diagnostics) {
154                        break;
155                    }
156                    if is_limit {
157                        break;
158                    }
159                    self.recover_after_error(item_start, error_line);
160                }
161            }
162        }
163
164        report
165    }
166
167    fn is_eof(&self) -> bool {
168        self.current()
169            .map(|token| matches!(token.token, Token::Eof))
170            .unwrap_or(true)
171    }
172
173    fn parse_document_item(&mut self, doc: &mut Document) -> Result<(), ParseError> {
174        let token = self.current().cloned().unwrap_or_else(|| {
175            TokenWithSpan::new(Token::Eof, self.eof_span().start, self.eof_span().end)
176        });
177        let span = token.span;
178        match token.token {
179            Token::Ident(name) => {
180                self.ensure_collection_capacity(doc.blocks.len(), span)?;
181                let is_bare_attr = self
182                    .peek(1)
183                    .map(|next| matches!(next.token, Token::Equal | Token::Colon))
184                    .unwrap_or(false);
185
186                if is_bare_attr {
187                    self.advance();
188                    let attr = self.parse_attribute(name.clone())?;
189                    doc.blocks.push(Block {
190                        name,
191                        labels: vec![],
192                        blocks: vec![],
193                        attributes: vec![attr].into_iter().collect(),
194                    });
195                } else {
196                    doc.blocks.push(self.parse_block()?);
197                }
198                Ok(())
199            }
200            _ => Err(ParseError::new(
201                DiagnosticCode::UnexpectedToken,
202                format!("Unexpected token: {}", token.token.kind_name()),
203                span,
204            )),
205        }
206    }
207
208    fn record_diagnostic(
209        report: &mut DiagnosticReport,
210        error: ParseError,
211        max_diagnostics: usize,
212    ) -> bool {
213        if report.diagnostics.len() >= max_diagnostics {
214            report.truncated = true;
215            return false;
216        }
217        report.diagnostics.push(error);
218        true
219    }
220
221    fn recover_after_error(&mut self, item_start: usize, error_line: usize) {
222        if self.pos == item_start && !self.is_eof() {
223            self.advance();
224        }
225        while let Some(token) = self.current() {
226            if matches!(token.token, Token::Eof) || token.span.start.line > error_line {
227                break;
228            }
229            self.advance();
230        }
231        self.block_depth = 0;
232        self.skip_newlines();
233    }
234
235    fn ensure_collection_capacity(&self, current_len: usize, span: Span) -> Result<(), ParseError> {
236        if current_len >= self.limits.max_collection_items {
237            return Err(ParseError::new(
238                DiagnosticCode::CollectionItemsLimit,
239                format!(
240                    "ACL parse limit exceeded: collection has more than {} items",
241                    self.limits.max_collection_items
242                ),
243                span,
244            ));
245        }
246        Ok(())
247    }
248
249    /// Parse a single block
250    fn parse_block(&mut self) -> Result<Block, ParseError> {
251        let span = self.current().map(|token| token.span).unwrap_or_default();
252        if self.block_depth >= self.limits.max_nesting_depth {
253            return Err(ParseError::new(
254                DiagnosticCode::NestingDepthLimit,
255                format!(
256                    "ACL parse limit exceeded: nesting depth is greater than {}",
257                    self.limits.max_nesting_depth
258                ),
259                span,
260            ));
261        }
262
263        self.block_depth += 1;
264        let result = self.parse_block_inner();
265        self.block_depth -= 1;
266        result
267    }
268
269    fn parse_block_inner(&mut self) -> Result<Block, ParseError> {
270        let ident = match self.current() {
271            Some(t) if matches!(t.token, Token::Ident(_)) => {
272                let name = match &t.token {
273                    Token::Ident(s) => s.clone(),
274                    _ => unreachable!(),
275                };
276                self.advance();
277                name
278            }
279            Some(t) => {
280                return Err(ParseError::new(
281                    DiagnosticCode::ExpectedToken,
282                    format!("Expected block name, found {}", t.token.kind_name()),
283                    t.span,
284                ));
285            }
286            None => {
287                return Err(ParseError::new(
288                    DiagnosticCode::UnexpectedEof,
289                    "Unexpected end of input",
290                    self.eof_span(),
291                ));
292            }
293        };
294
295        // Parse optional labels (e.g., "openai" in providers "openai" { })
296        let mut labels = Vec::new();
297        while let Some(t) = self.current() {
298            match &t.token {
299                Token::String(s) => {
300                    let span = t.span;
301                    let label = s.clone();
302                    self.ensure_collection_capacity(labels.len(), span)?;
303                    labels.push(label);
304                    self.advance();
305                }
306                Token::Ident(s)
307                    if self
308                        .peek(1)
309                        .map(|p| matches!(p.token, Token::LeftBrace))
310                        .unwrap_or(false) =>
311                {
312                    // This is a block without labels, break
313                    break;
314                }
315                _ => break,
316            }
317        }
318
319        self.skip_newlines();
320
321        // Parse block body
322        let (blocks, attributes) = self.parse_block_body()?;
323
324        Ok(Block {
325            name: ident,
326            labels,
327            blocks,
328            attributes,
329        })
330    }
331
332    /// Parse the body of a block (attributes and nested blocks)
333    fn parse_block_body(&mut self) -> Result<(Vec<Block>, HashMap<String, Value>), ParseError> {
334        let mut blocks = Vec::new();
335        let mut attributes = HashMap::new();
336        let mut item_count = 0;
337
338        self.skip_newlines();
339
340        // Handle blocks without braces (implicit blocks)
341        while let Some(t) = self.current() {
342            match &t.token {
343                Token::RightBrace => {
344                    self.advance();
345                    break;
346                }
347                Token::LeftBrace => {
348                    // This shouldn't happen normally
349                    self.advance();
350                }
351                Token::Ident(name) => {
352                    // Check if this is a nested block or attribute
353                    let span = t.span;
354                    let name = name.clone();
355                    let after_ident = self.peek(1);
356                    let after_after = self.peek(2);
357
358                    if let Some(next) = after_ident {
359                        match &next.token {
360                            Token::Equal | Token::Colon => {
361                                // It's an attribute: name = value or name : value
362                                self.ensure_collection_capacity(item_count, span)?;
363                                self.advance(); // consume the identifier
364                                let attr = self.parse_attribute(name)?;
365                                attributes.insert(attr.0, attr.1);
366                                item_count += 1;
367                            }
368                            Token::String(_) => {
369                                // It's a block with a string label, parse as nested block
370                                self.ensure_collection_capacity(item_count, span)?;
371                                let block = self.parse_block()?;
372                                blocks.push(block);
373                                item_count += 1;
374                            }
375                            Token::LeftBrace => {
376                                // Nested block with no labels
377                                self.ensure_collection_capacity(item_count, span)?;
378                                self.advance(); // consume the block type name
379                                let block = self.parse_nested_block(name.clone())?;
380                                blocks.push(block);
381                                item_count += 1;
382                            }
383                            Token::Ident(_) => {
384                                // This could be a nested block type
385                                // Check if next is a label or another ident
386                                if let Some(after) = after_after {
387                                    match &after.token {
388                                        Token::LeftBrace | Token::String(_) => {
389                                            // It's a nested block
390                                            self.ensure_collection_capacity(item_count, span)?;
391                                            let block = self.parse_block()?;
392                                            blocks.push(block);
393                                            item_count += 1;
394                                        }
395                                        Token::Equal | Token::Colon => {
396                                            // It's an attribute
397                                            self.ensure_collection_capacity(item_count, span)?;
398                                            self.advance(); // consume the identifier
399                                            let attr = self.parse_attribute(name)?;
400                                            attributes.insert(attr.0, attr.1);
401                                            item_count += 1;
402                                        }
403                                        _ => {
404                                            // Try as nested block
405                                            self.ensure_collection_capacity(item_count, span)?;
406                                            let block = self.parse_block()?;
407                                            blocks.push(block);
408                                            item_count += 1;
409                                        }
410                                    }
411                                } else {
412                                    // Just an identifier, skip
413                                    self.advance();
414                                }
415                            }
416                            _ => {
417                                self.advance();
418                            }
419                        }
420                    } else {
421                        self.advance();
422                    }
423                }
424                Token::Newline | Token::Comment => {
425                    self.advance();
426                }
427                Token::Eof => break,
428                _ => {
429                    // Skip unexpected tokens
430                    self.advance();
431                }
432            }
433            self.skip_newlines();
434        }
435
436        Ok((blocks, attributes))
437    }
438
439    /// Parse a nested block after we've already consumed the type name
440    fn parse_nested_block(&mut self, name: String) -> Result<Block, ParseError> {
441        // Check for labels
442        let mut labels = Vec::new();
443        while let Some(t) = self.current() {
444            match &t.token {
445                Token::String(s) => {
446                    let span = t.span;
447                    let label = s.clone();
448                    self.ensure_collection_capacity(labels.len(), span)?;
449                    labels.push(label);
450                    self.advance();
451                }
452                _ => break,
453            }
454        }
455
456        self.skip_newlines();
457
458        let (blocks, attributes) = self.parse_block_body()?;
459
460        Ok(Block {
461            name,
462            labels,
463            blocks,
464            attributes,
465        })
466    }
467
468    /// Parse an attribute assignment: name = value
469    fn parse_attribute(&mut self, name: String) -> Result<(String, Value), ParseError> {
470        // Already consumed the attribute name, now expecting = or :
471        let token = self.current().cloned();
472        if let Some(t) = token {
473            if !matches!(t.token, Token::Equal | Token::Colon) {
474                return Err(ParseError::new(
475                    DiagnosticCode::ExpectedToken,
476                    format!("Expected '=' or ':', found {}", t.token.kind_name()),
477                    t.span,
478                ));
479            }
480            self.advance();
481        }
482
483        self.skip_newlines();
484        let value = self.parse_value()?;
485
486        Ok((name, value))
487    }
488
489    /// Parse a value
490    fn parse_value(&mut self) -> Result<Value, ParseError> {
491        let eof_span = self.eof_span();
492        let token = self.current().ok_or_else(|| {
493            ParseError::new(
494                DiagnosticCode::UnexpectedEof,
495                "Unexpected end of input",
496                eof_span,
497            )
498        })?;
499
500        match token.token.clone() {
501            Token::String(s) => {
502                self.advance();
503                Ok(Value::String(s))
504            }
505            Token::Number(n) => {
506                self.advance();
507                Ok(Value::Number(n))
508            }
509            Token::True => {
510                self.advance();
511                Ok(Value::Bool(true))
512            }
513            Token::False => {
514                self.advance();
515                Ok(Value::Bool(false))
516            }
517            Token::Null => {
518                self.advance();
519                Ok(Value::Null)
520            }
521            Token::LeftBracket => {
522                self.advance();
523                self.parse_list()
524            }
525            Token::LeftBrace => {
526                self.advance();
527                let (_blocks, attrs) = self.parse_block_body()?;
528                Ok(Value::Object(attrs.into_iter().collect()))
529            }
530            Token::Ident(name) => {
531                let name = name.clone();
532                // Could be an identifier, a nested block, or a function call
533                self.advance();
534                self.skip_newlines();
535
536                // Check if this is a function call: name(args)
537                if let Some(next) = self.current() {
538                    match &next.token {
539                        Token::LeftParen => {
540                            // It's a function call
541                            self.advance(); // consume '('
542                            let args = self.parse_call_args()?;
543                            return Ok(Value::Call(name, args));
544                        }
545                        Token::LeftBrace => {
546                            // It's a nested block, but we already consumed the name
547                            let block = self.parse_nested_block(name)?;
548                            return Ok(Value::Object(vec![(
549                                "_block".to_string(),
550                                Value::Object(vec![
551                                    ("name".to_string(), Value::String(block.name)),
552                                    (
553                                        "labels".to_string(),
554                                        Value::List(
555                                            block
556                                                .labels
557                                                .iter()
558                                                .map(|s| Value::String(s.clone()))
559                                                .collect(),
560                                        ),
561                                    ),
562                                    (
563                                        "attributes".to_string(),
564                                        Value::Object(block.attributes.into_iter().collect()),
565                                    ),
566                                ]),
567                            )]));
568                        }
569                        _ => {}
570                    }
571                }
572
573                Ok(Value::String(name))
574            }
575            Token::Eof => Err(ParseError::new(
576                DiagnosticCode::UnexpectedEof,
577                "Unexpected end of input",
578                token.span,
579            )),
580            _ => Err(ParseError::new(
581                DiagnosticCode::UnexpectedToken,
582                format!(
583                    "Unexpected token in value position: {}",
584                    token.token.kind_name()
585                ),
586                token.span,
587            )),
588        }
589    }
590
591    /// Parse a list: [1, 2, 3]
592    fn parse_list(&mut self) -> Result<Value, ParseError> {
593        let mut items = Vec::new();
594        self.skip_newlines();
595
596        while let Some(t) = self.current() {
597            match &t.token {
598                Token::RightBracket => {
599                    self.advance();
600                    break;
601                }
602                Token::Comma => {
603                    self.advance();
604                    self.skip_newlines();
605                }
606                Token::Newline | Token::Comment => {
607                    self.advance();
608                }
609                _ => {
610                    let span = t.span;
611                    self.ensure_collection_capacity(items.len(), span)?;
612                    let value = self.parse_value()?;
613                    items.push(value);
614                    self.skip_newlines();
615
616                    // Check for comma or end of list
617                    if let Some(next) = self.current() {
618                        match &next.token {
619                            Token::Comma => {
620                                self.advance(); // consume comma
621                                self.skip_newlines();
622                            }
623                            Token::RightBracket => {
624                                // end of list
625                            }
626                            _ => {}
627                        }
628                    }
629                }
630            }
631        }
632
633        Ok(Value::List(items))
634    }
635
636    /// Parse function call arguments: arg1, arg2, ...)
637    fn parse_call_args(&mut self) -> Result<Vec<Value>, ParseError> {
638        let mut args = Vec::new();
639        self.skip_newlines();
640
641        // Handle empty args
642        if let Some(t) = self.current() {
643            if matches!(t.token, Token::RightParen) {
644                self.advance();
645                return Ok(args);
646            }
647        }
648
649        loop {
650            let span = self.current().map(|token| token.span).unwrap_or_default();
651            self.ensure_collection_capacity(args.len(), span)?;
652            let value = self.parse_value()?;
653            args.push(value);
654            self.skip_newlines();
655
656            match self.current() {
657                Some(t) if matches!(t.token, Token::Comma) => {
658                    self.advance(); // consume comma
659                    self.skip_newlines();
660                }
661                Some(t) if matches!(t.token, Token::RightParen) => {
662                    self.advance(); // consume ')'
663                    break;
664                }
665                Some(t) => {
666                    return Err(ParseError::new(
667                        DiagnosticCode::ExpectedToken,
668                        format!("Expected ',' or ')', found {}", t.token.kind_name()),
669                        t.span,
670                    ));
671                }
672                None => {
673                    return Err(ParseError::new(
674                        DiagnosticCode::UnexpectedEof,
675                        "Unexpected end of input in function call",
676                        self.eof_span(),
677                    ));
678                }
679            }
680        }
681
682        Ok(args)
683    }
684}
685
686/// Parse ACL text into a Document
687pub fn parse(input: &str) -> Result<Document, ParseError> {
688    parse_with_limits(input, DEFAULT_PARSE_LIMITS)
689}
690
691/// Parse ACL text into a Document with explicit resource limits.
692pub fn parse_with_limits(input: &str, limits: ParseLimits) -> Result<Document, ParseError> {
693    Parser::with_limits(input, limits).parse()
694}
695
696/// Collect bounded parse diagnostics using the default resource limits.
697pub fn collect_diagnostics(input: &str) -> DiagnosticReport {
698    collect_diagnostics_with_limits(input, DEFAULT_PARSE_LIMITS)
699}
700
701/// Collect bounded parse diagnostics using explicit resource limits.
702pub fn collect_diagnostics_with_limits(input: &str, limits: ParseLimits) -> DiagnosticReport {
703    Parser::with_limits(input, limits).collect_diagnostics()
704}
705
706#[cfg(test)]
707mod tests {
708    use super::*;
709
710    #[test]
711    fn test_parse_simple() {
712        let input = r#"
713            name = "test"
714            count = 42
715        "#;
716        let doc = parse(input).unwrap();
717        assert_eq!(doc.blocks.len(), 2);
718        assert_eq!(doc.blocks[0].name, "name");
719        assert_eq!(doc.blocks[1].name, "count");
720    }
721
722    #[test]
723    fn test_parse_block() {
724        let input = r#"
725            providers "openai" {
726                name = "openai"
727                api_key = "sk-test"
728            }
729        "#;
730        let doc = parse(input).unwrap();
731        assert_eq!(doc.blocks.len(), 1);
732        let block = &doc.blocks[0];
733        assert_eq!(block.name, "providers");
734        assert_eq!(block.labels, vec!["openai"]);
735        assert_eq!(
736            block.attributes.get("name").map(|v| v.to_string()).unwrap(),
737            "openai"
738        );
739    }
740
741    #[test]
742    fn test_parse_nested_block() {
743        let input = r#"
744            providers {
745                openai "gpt-4" {
746                    model = "gpt-4"
747                }
748            }
749        "#;
750        let doc = parse(input).unwrap();
751        assert_eq!(doc.blocks.len(), 1);
752        let block = &doc.blocks[0];
753        assert_eq!(block.name, "providers");
754        assert!(block.blocks.len() >= 1);
755    }
756
757    #[test]
758    fn test_parse_list() {
759        let input = r#"
760            numbers = [1, 2, 3]
761        "#;
762        let doc = parse(input).unwrap();
763        assert_eq!(doc.blocks.len(), 1);
764        let attr = doc.blocks[0].attributes.get("numbers").unwrap();
765        match attr {
766            Value::List(items) => assert_eq!(items.len(), 3),
767            _ => panic!("Expected list"),
768        }
769    }
770
771    #[test]
772    fn test_parse_string_list() {
773        // Test list with single element
774        let input = r#"
775            names = ["alice"]
776        "#;
777        let doc = parse(input).unwrap();
778        let attr = doc.blocks[0].attributes.get("names").unwrap();
779        match attr {
780            Value::List(items) => assert_eq!(items.len(), 1),
781            _ => panic!("Expected list"),
782        }
783    }
784
785    #[test]
786    fn test_parse_boolean_true() {
787        let input = r#"
788            enabled = true
789        "#;
790        let doc = parse(input).unwrap();
791        let attr = doc.blocks[0].attributes.get("enabled").unwrap();
792        assert_eq!(attr, &Value::Bool(true));
793    }
794
795    #[test]
796    fn test_parse_boolean_false() {
797        let input = r#"
798            enabled = false
799        "#;
800        let doc = parse(input).unwrap();
801        let attr = doc.blocks[0].attributes.get("enabled").unwrap();
802        assert_eq!(attr, &Value::Bool(false));
803    }
804
805    #[test]
806    fn test_parse_null() {
807        let input = r#"
808            value = null
809        "#;
810        let doc = parse(input).unwrap();
811        let attr = doc.blocks[0].attributes.get("value").unwrap();
812        assert_eq!(attr, &Value::Null);
813    }
814
815    #[test]
816    fn test_parse_number_integer() {
817        let input = r#"
818            count = 42
819        "#;
820        let doc = parse(input).unwrap();
821        let attr = doc.blocks[0].attributes.get("count").unwrap();
822        match attr {
823            Value::Number(n) => assert_eq!(*n, 42.0),
824            _ => panic!("Expected number"),
825        }
826    }
827
828    #[test]
829    fn test_parse_number_float() {
830        let input = r#"
831            pi = 3.14
832        "#;
833        let doc = parse(input).unwrap();
834        let attr = doc.blocks[0].attributes.get("pi").unwrap();
835        match attr {
836            Value::Number(n) => assert!((*n - 3.14).abs() < 0.001),
837            _ => panic!("Expected number"),
838        }
839    }
840
841    #[test]
842    fn test_parse_negative_number() {
843        let input = r#"
844            temp = -10
845        "#;
846        let doc = parse(input).unwrap();
847        let attr = doc.blocks[0].attributes.get("temp").unwrap();
848        match attr {
849            Value::Number(n) => assert_eq!(*n, -10.0),
850            _ => panic!("Expected number"),
851        }
852    }
853
854    #[test]
855    fn test_parse_string_value() {
856        let input = r#"
857            name = "hello world"
858        "#;
859        let doc = parse(input).unwrap();
860        let attr = doc.blocks[0].attributes.get("name").unwrap();
861        match attr {
862            Value::String(s) => assert_eq!(s, "hello world"),
863            _ => panic!("Expected string"),
864        }
865    }
866
867    #[test]
868    fn test_parse_colon_separator() {
869        let input = r#"
870            config {
871                key: "value"
872            }
873        "#;
874        let doc = parse(input).unwrap();
875        assert_eq!(doc.blocks.len(), 1);
876        assert_eq!(doc.blocks[0].name, "config");
877    }
878
879    #[test]
880    fn test_parse_error_display() {
881        let err = ParseError::new(
882            DiagnosticCode::UnexpectedToken,
883            "test error",
884            Span::point(Location {
885                line: 10,
886                column: 5,
887                offset: 42,
888            }),
889        );
890        let display = format!("{}", err);
891        assert!(display.contains("line 10"));
892        assert!(display.contains("column 5"));
893        assert!(display.contains("test error"));
894    }
895
896    #[test]
897    fn test_parse_error_trait() {
898        let err = ParseError::new(
899            DiagnosticCode::UnexpectedToken,
900            "test",
901            Span::point(Location {
902                line: 1,
903                column: 1,
904                offset: 0,
905            }),
906        );
907        let _ = err.clone();
908    }
909
910    #[test]
911    fn test_parse_empty_document() {
912        let input = "";
913        let doc = parse(input).unwrap();
914        assert!(doc.blocks.is_empty());
915    }
916
917    #[test]
918    fn test_parse_only_newlines() {
919        let input = "\n\n\n";
920        let doc = parse(input).unwrap();
921        assert!(doc.blocks.is_empty());
922    }
923
924    #[test]
925    fn test_parse_comments() {
926        let input = r#"
927            # this is a comment
928            name = "test"
929            // another comment
930            count = 42
931        "#;
932        let doc = parse(input).unwrap();
933        assert_eq!(doc.blocks.len(), 2);
934    }
935
936    #[test]
937    fn test_parse_block_no_braces() {
938        let input = r#"
939            config
940                name = "test"
941        "#;
942        let doc = parse(input).unwrap();
943        assert_eq!(doc.blocks.len(), 1);
944        assert_eq!(doc.blocks[0].name, "config");
945    }
946
947    #[test]
948    fn test_parse_multiple_blocks() {
949        let input = r#"
950            block1 "label1" {}
951            block2 "label2" {}
952        "#;
953        let doc = parse(input).unwrap();
954        assert_eq!(doc.blocks.len(), 2);
955        assert_eq!(doc.blocks[0].name, "block1");
956        assert_eq!(doc.blocks[1].name, "block2");
957    }
958
959    #[test]
960    fn test_parse_identifier_value() {
961        let input = r#"
962            ref = other_name
963        "#;
964        let doc = parse(input).unwrap();
965        let attr = doc.blocks[0].attributes.get("ref").unwrap();
966        match attr {
967            Value::String(s) => assert_eq!(s, "other_name"),
968            _ => panic!("Expected string"),
969        }
970    }
971
972    #[test]
973    fn test_parse_block_with_block_nested_inside() {
974        let input = r#"
975            outer {
976                inner "label" {
977                    key = "value"
978                }
979            }
980        "#;
981        let doc = parse(input).unwrap();
982        assert_eq!(doc.blocks.len(), 1);
983        assert_eq!(doc.blocks[0].name, "outer");
984    }
985
986    #[test]
987    fn test_parse_implicit_block_with_labels() {
988        let input = r#"
989            item "first" "second" {
990                name = "test"
991            }
992        "#;
993        let doc = parse(input).unwrap();
994        assert_eq!(doc.blocks[0].labels.len(), 2);
995    }
996
997    #[test]
998    fn test_parse_error_unexpected_token() {
999        let input = r#"
1000            [invalid]
1001        "#;
1002        let result = parse(input);
1003        assert!(result.is_err());
1004    }
1005
1006    #[test]
1007    fn test_parse_error_eof_in_block() {
1008        // Test with unexpected end of input in attribute value
1009        let input = r#"block key = "#;
1010        let result = parse(input);
1011        assert!(result.is_err());
1012    }
1013
1014    #[test]
1015    fn test_parse_block_no_labels() {
1016        let input = r#"
1017            block {
1018                key = "value"
1019            }
1020        "#;
1021        let doc = parse(input).unwrap();
1022        assert_eq!(doc.blocks.len(), 1);
1023        assert_eq!(doc.blocks[0].name, "block");
1024        assert!(doc.blocks[0].labels.is_empty());
1025    }
1026
1027    #[test]
1028    fn test_parse_multiple_attributes() {
1029        let input = r#"
1030            config {
1031                a = 1
1032                b = 2
1033                c = 3
1034            }
1035        "#;
1036        let doc = parse(input).unwrap();
1037        assert_eq!(doc.blocks[0].attributes.len(), 3);
1038    }
1039
1040    #[test]
1041    fn test_parse_list_of_numbers() {
1042        let input = r#"
1043            nums = [1, 2, 3]
1044        "#;
1045        let doc = parse(input).unwrap();
1046        let attr = doc.blocks[0].attributes.get("nums").unwrap();
1047        match attr {
1048            Value::List(items) => {
1049                assert_eq!(items.len(), 3);
1050                assert_eq!(items[0], Value::Number(1.0));
1051                assert_eq!(items[1], Value::Number(2.0));
1052                assert_eq!(items[2], Value::Number(3.0));
1053            }
1054            _ => panic!("Expected list"),
1055        }
1056    }
1057
1058    #[test]
1059    fn test_parse_list_of_mixed() {
1060        let input = r#"
1061            mixed = ["string", 42, true]
1062        "#;
1063        let doc = parse(input).unwrap();
1064        let attr = doc.blocks[0].attributes.get("mixed").unwrap();
1065        match attr {
1066            Value::List(items) => assert_eq!(items.len(), 3),
1067            _ => panic!("Expected list"),
1068        }
1069    }
1070
1071    #[test]
1072    fn test_parse_empty_list() {
1073        let input = r#"items = []"#;
1074        let doc = parse(input).unwrap();
1075        let attr = doc.blocks[0].attributes.get("items").unwrap();
1076        match attr {
1077            Value::List(items) => assert!(items.is_empty()),
1078            _ => panic!("Expected list"),
1079        }
1080    }
1081
1082    #[test]
1083    fn test_parse_nested_block_with_labels() {
1084        let input = r#"
1085            parent {
1086                child "label" {
1087                    key = "value"
1088                }
1089            }
1090        "#;
1091        let doc = parse(input).unwrap();
1092        assert!(!doc.blocks[0].blocks.is_empty());
1093    }
1094
1095    #[test]
1096    fn test_parse_left_brace_after_ident() {
1097        // Test case: ident followed by left brace (implicit block without labels)
1098        let input = r#"
1099            myblock {
1100                key = "value"
1101            }
1102        "#;
1103        let doc = parse(input).unwrap();
1104        assert_eq!(doc.blocks.len(), 1);
1105        assert_eq!(doc.blocks[0].name, "myblock");
1106    }
1107
1108    #[test]
1109    fn test_parse_left_brace_after_string() {
1110        // Test case: ident followed by string label then left brace
1111        let input = r#"
1112            myblock "label" {
1113                key = "value"
1114            }
1115        "#;
1116        let doc = parse(input).unwrap();
1117        assert_eq!(doc.blocks[0].labels, vec!["label"]);
1118    }
1119
1120    #[test]
1121    fn test_parse_ident_after_ident_with_left_brace() {
1122        // Test case: ident followed by another ident and then left brace
1123        let input = r#"
1124            type "name" {
1125                key = "value"
1126            }
1127        "#;
1128        let doc = parse(input).unwrap();
1129        assert_eq!(doc.blocks[0].name, "type");
1130        assert_eq!(doc.blocks[0].labels, vec!["name"]);
1131    }
1132
1133    #[test]
1134    fn test_parse_block_sparse_labels() {
1135        let input = r#"
1136            block "a" "b" "c" {}
1137        "#;
1138        let doc = parse(input).unwrap();
1139        assert_eq!(doc.blocks[0].labels, vec!["a", "b", "c"]);
1140    }
1141
1142    #[test]
1143    fn test_parse_value_number_negative() {
1144        let input = r#"val = -123"#;
1145        let doc = parse(input).unwrap();
1146        let attr = doc.blocks[0].attributes.get("val").unwrap();
1147        assert_eq!(attr, &Value::Number(-123.0));
1148    }
1149
1150    #[test]
1151    fn test_parse_value_number_float() {
1152        let input = r#"val = 0.5"#;
1153        let doc = parse(input).unwrap();
1154        let attr = doc.blocks[0].attributes.get("val").unwrap();
1155        match attr {
1156            Value::Number(n) => assert!((*n - 0.5).abs() < 0.001),
1157            _ => panic!("Expected number"),
1158        }
1159    }
1160
1161    #[test]
1162    fn test_parse_whitespace_only() {
1163        let input = "   \t\t  \n\n\n   ";
1164        let doc = parse(input).unwrap();
1165        assert!(doc.blocks.is_empty());
1166    }
1167
1168    #[test]
1169    fn test_parse_only_comments() {
1170        let input = "# comment\n// another\n# third";
1171        let doc = parse(input).unwrap();
1172        assert!(doc.blocks.is_empty());
1173    }
1174
1175    #[test]
1176    fn test_parse_comment_between_statements() {
1177        let input = r#"
1178            a = 1
1179            # comment
1180            b = 2
1181        "#;
1182        let doc = parse(input).unwrap();
1183        assert_eq!(doc.blocks.len(), 2);
1184    }
1185
1186    #[test]
1187    fn test_parse_skip_unexpected_token() {
1188        // Test that unexpected tokens in block body are skipped
1189        let input = r#"
1190            block {
1191                key = "value"
1192                unexpected_token
1193                other = "data"
1194            }
1195        "#;
1196        let doc = parse(input).unwrap();
1197        // The parser should skip unexpected tokens and continue
1198        assert_eq!(doc.blocks.len(), 1);
1199    }
1200
1201    #[test]
1202    fn test_parse_only_newline_in_list() {
1203        // Test list with newlines between items
1204        let input = r#"
1205            nums = [
1206                1,
1207                2,
1208                3
1209            ]
1210        "#;
1211        let doc = parse(input).unwrap();
1212        let attr = doc.blocks[0].attributes.get("nums").unwrap();
1213        match attr {
1214            Value::List(items) => assert_eq!(items.len(), 3),
1215            _ => panic!("Expected list"),
1216        }
1217    }
1218
1219    #[test]
1220    fn test_parse_list_trailing_comma() {
1221        let input = r#"nums = [1, 2, 3,]"#;
1222        let result = parse(input);
1223        // May or may not parse depending on parser rules
1224        // Just ensure no crash
1225        assert!(result.is_ok() || result.is_err());
1226    }
1227
1228    #[test]
1229    fn test_parse_block_with_comment_before_attr() {
1230        let input = r#"
1231            block {
1232                # this is a comment
1233                key = "value"
1234            }
1235        "#;
1236        let doc = parse(input).unwrap();
1237        assert_eq!(
1238            doc.blocks[0]
1239                .attributes
1240                .get("key")
1241                .map(|v| v.to_string())
1242                .unwrap(),
1243            "value"
1244        );
1245    }
1246
1247    #[test]
1248    fn test_parse_float_scientific() {
1249        let input = r#"val = 1e-5"#;
1250        let doc = parse(input).unwrap();
1251        let attr = doc.blocks[0].attributes.get("val").unwrap();
1252        match attr {
1253            Value::Number(n) => assert!((*n - 0.00001).abs() < 0.000001),
1254            _ => panic!("Expected number"),
1255        }
1256    }
1257
1258    #[test]
1259    fn test_parse_block_without_body() {
1260        // Block with just name, no braces
1261        let input = r#"empty_block"#;
1262        let doc = parse(input).unwrap();
1263        assert_eq!(doc.blocks.len(), 1);
1264        assert_eq!(doc.blocks[0].name, "empty_block");
1265    }
1266
1267    #[test]
1268    fn test_parse_nested_block_type_ident() {
1269        // Test nested block where type is an identifier (not string)
1270        let input = r#"
1271            outer {
1272                inner "label" {}
1273            }
1274        "#;
1275        let doc = parse(input).unwrap();
1276        assert!(!doc.blocks[0].blocks.is_empty());
1277    }
1278
1279    #[test]
1280    fn test_parse_block_with_plus_equal() {
1281        // Test += operator (though it's not fully supported)
1282        let input = r#"key += value"#;
1283        let result = parse(input);
1284        // Should either parse or error gracefully
1285        assert!(result.is_ok() || result.is_err());
1286    }
1287
1288    #[test]
1289    fn test_parse_attr_with_colon() {
1290        // Test attribute with colon separator
1291        let input = r#"key: "value""#;
1292        let doc = parse(input).unwrap();
1293        assert_eq!(
1294            doc.blocks[0]
1295                .attributes
1296                .get("key")
1297                .map(|v| v.to_string())
1298                .unwrap(),
1299            "value"
1300        );
1301    }
1302
1303    #[test]
1304    fn test_parse_block_empty_labels() {
1305        let input = r#"block "" {}"#;
1306        let doc = parse(input).unwrap();
1307        assert_eq!(doc.blocks[0].labels, vec![""]);
1308    }
1309
1310    #[test]
1311    fn test_parse_eof_after_block() {
1312        let input = r#"block {}"#;
1313        let doc = parse(input).unwrap();
1314        assert_eq!(doc.blocks.len(), 1);
1315    }
1316
1317    #[test]
1318    fn test_parse_block_single_quoted_label() {
1319        let input = r#"block 'label' {}"#;
1320        let doc = parse(input).unwrap();
1321        assert_eq!(doc.blocks[0].labels, vec!["label"]);
1322    }
1323
1324    #[test]
1325    fn test_parse_string_with_equals() {
1326        let input = r#"key = "a=b""#;
1327        let doc = parse(input).unwrap();
1328        assert_eq!(
1329            doc.blocks[0]
1330                .attributes
1331                .get("key")
1332                .map(|v| v.to_string())
1333                .unwrap(),
1334            "a=b"
1335        );
1336    }
1337
1338    #[test]
1339    fn test_parse_string_with_braces() {
1340        let input = r#"key = "a{b}c""#;
1341        let doc = parse(input).unwrap();
1342        assert_eq!(
1343            doc.blocks[0]
1344                .attributes
1345                .get("key")
1346                .map(|v| v.to_string())
1347                .unwrap(),
1348            "a{b}c"
1349        );
1350    }
1351
1352    #[test]
1353    fn test_parse_string_with_brackets() {
1354        let input = r#"key = "a[b]c""#;
1355        let doc = parse(input).unwrap();
1356        assert_eq!(
1357            doc.blocks[0]
1358                .attributes
1359                .get("key")
1360                .map(|v| v.to_string())
1361                .unwrap(),
1362            "a[b]c"
1363        );
1364    }
1365
1366    #[test]
1367    fn test_parse_string_with_hash() {
1368        let input = r#"key = "a#b""#;
1369        let doc = parse(input).unwrap();
1370        assert_eq!(
1371            doc.blocks[0]
1372                .attributes
1373                .get("key")
1374                .map(|v| v.to_string())
1375                .unwrap(),
1376            "a#b"
1377        );
1378    }
1379
1380    #[test]
1381    fn test_parse_mixed_block_and_attrs() {
1382        let input = r#"
1383            outer {
1384                attr1 = 1
1385                inner "label" {
1386                    attr2 = 2
1387                }
1388                attr3 = 3
1389            }
1390        "#;
1391        let doc = parse(input).unwrap();
1392        let outer = &doc.blocks[0];
1393        assert_eq!(outer.attributes.len(), 2); // attr1 and attr3
1394        assert!(!outer.blocks.is_empty()); // inner block
1395    }
1396
1397    #[test]
1398    fn test_parse_block_starting_with_dot() {
1399        // String starting with dot needs quoting
1400        let input = r#"key = ".hidden""#;
1401        let doc = parse(input).unwrap();
1402        assert_eq!(
1403            doc.blocks[0]
1404                .attributes
1405                .get("key")
1406                .map(|v| v.to_string())
1407                .unwrap(),
1408            ".hidden"
1409        );
1410    }
1411
1412    #[test]
1413    fn test_parse_block_with_number_value() {
1414        let input = r#"key = 0"#;
1415        let doc = parse(input).unwrap();
1416        assert_eq!(
1417            doc.blocks[0]
1418                .attributes
1419                .get("key")
1420                .map(|v| v.to_string())
1421                .unwrap(),
1422            "0"
1423        );
1424    }
1425
1426    #[test]
1427    fn test_parse_bool_true_as_value() {
1428        let input = r#"key = true"#;
1429        let doc = parse(input).unwrap();
1430        assert_eq!(
1431            doc.blocks[0]
1432                .attributes
1433                .get("key")
1434                .map(|v| v.to_string())
1435                .unwrap(),
1436            "true"
1437        );
1438    }
1439
1440    #[test]
1441    fn test_parse_bool_false_as_value() {
1442        let input = r#"key = false"#;
1443        let doc = parse(input).unwrap();
1444        assert_eq!(
1445            doc.blocks[0]
1446                .attributes
1447                .get("key")
1448                .map(|v| v.to_string())
1449                .unwrap(),
1450            "false"
1451        );
1452    }
1453
1454    #[test]
1455    fn test_parse_null_as_value() {
1456        let input = r#"key = null"#;
1457        let doc = parse(input).unwrap();
1458        assert_eq!(
1459            doc.blocks[0]
1460                .attributes
1461                .get("key")
1462                .map(|v| v.to_string())
1463                .unwrap(),
1464            "null"
1465        );
1466    }
1467
1468    #[test]
1469    fn test_parse_string_with_unicode() {
1470        let input = r#"key = "hello 世界""#;
1471        let doc = parse(input).unwrap();
1472        assert_eq!(
1473            doc.blocks[0]
1474                .attributes
1475                .get("key")
1476                .map(|v| v.to_string())
1477                .unwrap(),
1478            "hello 世界"
1479        );
1480    }
1481
1482    #[test]
1483    fn test_parse_multiple_labels_different_types() {
1484        let input = r#"block "a" 'b' "c" {}"#;
1485        let doc = parse(input).unwrap();
1486        assert_eq!(doc.blocks[0].labels, vec!["a", "b", "c"]);
1487    }
1488
1489    #[test]
1490    fn test_parse_ident_value_followed_by_block() {
1491        // Test that identifier value followed by block parses correctly
1492        let input = r#"block {
1493            type container
1494            name = "test"
1495        }"#;
1496        let doc = parse(input).unwrap();
1497        assert_eq!(doc.blocks[0].name, "block");
1498    }
1499
1500    #[test]
1501    fn test_parse_value_with_equals_in_string() {
1502        // Test string containing = character
1503        let input = r#"key = "a=b=c""#;
1504        let doc = parse(input).unwrap();
1505        assert_eq!(
1506            doc.blocks[0]
1507                .attributes
1508                .get("key")
1509                .map(|v| v.to_string())
1510                .unwrap(),
1511            "a=b=c"
1512        );
1513    }
1514
1515    #[test]
1516    fn test_parse_list_with_single_number() {
1517        let input = r#"nums = [42]"#;
1518        let doc = parse(input).unwrap();
1519        let attr = doc.blocks[0].attributes.get("nums").unwrap();
1520        match attr {
1521            Value::List(items) => assert_eq!(items.len(), 1),
1522            _ => panic!("Expected list"),
1523        }
1524    }
1525
1526    #[test]
1527    fn test_parse_list_with_trailing_newline() {
1528        let input = r#"
1529            nums = [
1530                1,
1531                2,
1532            ]
1533        "#;
1534        let doc = parse(input).unwrap();
1535        let attr = doc.blocks[0].attributes.get("nums").unwrap();
1536        match attr {
1537            Value::List(items) => assert_eq!(items.len(), 2),
1538            _ => panic!("Expected list"),
1539        }
1540    }
1541
1542    #[test]
1543    fn test_parse_attr_with_number_starting_with_dot() {
1544        // String starting with dot needs quotes
1545        let input = r#"key = ".5""#;
1546        let doc = parse(input).unwrap();
1547        assert_eq!(
1548            doc.blocks[0]
1549                .attributes
1550                .get("key")
1551                .map(|v| v.to_string())
1552                .unwrap(),
1553            ".5"
1554        );
1555    }
1556
1557    #[test]
1558    fn test_parse_eof_in_nested_block() {
1559        // Test EOF reached while parsing - unclosed string
1560        let input = r#"key = "unclosed"#;
1561        let result = parse(input);
1562        // This might not error depending on how lexer handles it
1563        assert!(result.is_ok() || result.is_err());
1564    }
1565
1566    #[test]
1567    fn test_parse_block_with_comment_after_name() {
1568        let input = r#"
1569            block # comment
1570                key = "value"
1571        "#;
1572        let doc = parse(input).unwrap();
1573        assert_eq!(doc.blocks[0].name, "block");
1574    }
1575
1576    #[test]
1577    fn test_parse_float_with_leading_dot() {
1578        let input = r#"val = .123"#;
1579        let doc = parse(input).unwrap();
1580        let attr = doc.blocks[0].attributes.get("val").unwrap();
1581        match attr {
1582            Value::Number(n) => assert!((*n - 0.123).abs() < 0.001),
1583            _ => panic!("Expected number"),
1584        }
1585    }
1586
1587    #[test]
1588    fn test_parse_error_block_name_eof() {
1589        // EOF during block name parsing triggers line 142-148
1590        let input = r#"block";
1591        let result = parse(input);
1592        assert!(result.is_err());
1593    }
1594
1595    #[test]
1596    fn test_parse_error_eof_in_block() {
1597        // EOF after opening brace in block body
1598        let input = r#"block { attr"#;
1599        let result = parse(input);
1600        assert!(result.is_err());
1601    }
1602
1603    #[test]
1604    fn test_parse_eof_in_nested_block_graceful() {
1605        // EOF in nested block context is handled gracefully
1606        let input = r#"outer { inner"#;
1607        let result = parse(input);
1608        // Parser handles this gracefully by treating inner as a block
1609        assert!(result.is_ok());
1610    }
1611
1612    #[test]
1613    fn test_parse_error_invalid_list_token() {
1614        // Unknown token where value is expected
1615        let input = r#"val = [}"]"#;
1616        let result = parse(input);
1617        assert!(result.is_err());
1618    }
1619
1620    #[test]
1621    fn test_parse_ident_at_eof() {
1622        // Identifier at end of input (triggers peek returning None)
1623        let input = r#"name"#;
1624        let doc = parse(input).unwrap();
1625        // Should parse as a block with no attributes
1626        assert_eq!(doc.blocks.len(), 1);
1627        assert_eq!(doc.blocks[0].name, "name");
1628    }
1629
1630    #[test]
1631    fn test_parse_block_with_only_newline_in_body() {
1632        // Block followed by newline in body
1633        let input = r#"block {
1634        }"#;
1635        let doc = parse(input).unwrap();
1636        assert_eq!(doc.blocks.len(), 1);
1637    }
1638
1639    #[test]
1640    fn test_parse_error_number_as_block_name() {
1641        // Number where block name expected - triggers line 135-139
1642        let input = r#"123"#;
1643        let result = parse(input);
1644        assert!(result.is_err());
1645        let err = result.unwrap_err();
1646        // Parser returns error for unexpected token
1647        assert!(
1648            err.message.contains("Unexpected token") || err.message.contains("Expected block name")
1649        );
1650    }
1651
1652    #[test]
1653    fn test_parse_label_then_nested() {
1654        // Block with string label followed by attribute
1655        let input = r#"block "label" {
1656            key = "value"
1657        }"#;
1658        let doc = parse(input).unwrap();
1659        assert_eq!(doc.blocks.len(), 1);
1660        assert_eq!(doc.blocks[0].labels, vec!["label"]);
1661    }
1662
1663    #[test]
1664    fn test_parse_block_empty_string_label() {
1665        // Block with empty string label
1666        let input = r#"block "" {
1667        }"#;
1668        let doc = parse(input).unwrap();
1669        assert_eq!(doc.blocks.len(), 1);
1670        assert_eq!(doc.blocks[0].labels, vec![""]);
1671    }
1672
1673    #[test]
1674    fn test_parse_multiple_string_labels() {
1675        // Block with multiple string labels
1676        let input = r#"block "label1" "label2" {
1677        }"#;
1678        let doc = parse(input).unwrap();
1679        assert_eq!(doc.blocks.len(), 1);
1680        assert_eq!(doc.blocks[0].labels, vec!["label1", "label2"]);
1681    }
1682
1683    #[test]
1684    fn test_parse_string_then_brace_as_block() {
1685        // String followed by left brace is parsed as block
1686        let input = r#"provider "openai" {
1687            key = "value"
1688        }"#;
1689        let doc = parse(input).unwrap();
1690        assert_eq!(doc.blocks.len(), 1);
1691        assert_eq!(doc.blocks[0].name, "provider");
1692    }
1693
1694    #[test]
1695    fn test_parse_attribute_with_plus_equal() {
1696        // Attribute with += operator
1697        let input = r#"vals += 1"#;
1698        let doc = parse(input).unwrap();
1699        assert_eq!(doc.blocks.len(), 1);
1700    }
1701
1702    #[test]
1703    fn test_parse_function_call_env() {
1704        // env() function call
1705        let input = r#"api_key = env("API_KEY")"#;
1706        let doc = parse(input).unwrap();
1707        let attr = doc.blocks[0].attributes.get("api_key").unwrap();
1708        match attr {
1709            Value::Call(name, args) => {
1710                assert_eq!(name, "env");
1711                assert_eq!(args.len(), 1);
1712                assert_eq!(args[0], Value::String("API_KEY".to_string()));
1713            }
1714            _ => panic!("Expected Call value"),
1715        }
1716    }
1717
1718    #[test]
1719    fn test_parse_function_call_with_multiple_args() {
1720        // concat() function call with multiple args
1721        let input = r#"url = concat("postgres://", host, ":", port)"#;
1722        let doc = parse(input).unwrap();
1723        let attr = doc.blocks[0].attributes.get("url").unwrap();
1724        match attr {
1725            Value::Call(name, args) => {
1726                assert_eq!(name, "concat");
1727                assert_eq!(args.len(), 4);
1728            }
1729            _ => panic!("Expected Call value"),
1730        }
1731    }
1732
1733    #[test]
1734    fn test_parse_function_call_with_nested() {
1735        // Nested function call
1736        let input = r#"path = concat(env("HOME"), "/", "file")"#;
1737        let doc = parse(input).unwrap();
1738        let attr = doc.blocks[0].attributes.get("path").unwrap();
1739        match attr {
1740            Value::Call(name, args) => {
1741                assert_eq!(name, "concat");
1742                assert_eq!(args.len(), 3);
1743            }
1744            _ => panic!("Expected Call value"),
1745        }
1746    }
1747
1748    #[test]
1749    fn test_parse_function_call_empty_args() {
1750        // Function call with no args
1751        let input = r#"result = getenv()"#;
1752        let doc = parse(input).unwrap();
1753        let attr = doc.blocks[0].attributes.get("result").unwrap();
1754        match attr {
1755            Value::Call(name, args) => {
1756                assert_eq!(name, "getenv");
1757                assert!(args.is_empty());
1758            }
1759            _ => panic!("Expected Call value"),
1760        }
1761    }
1762
1763    #[test]
1764    fn test_parse_function_call_in_list() {
1765        // Function call as list item
1766        let input = r#"paths = [env("PATH1"), env("PATH2")]"#;
1767        let doc = parse(input).unwrap();
1768        let attr = doc.blocks[0].attributes.get("paths").unwrap();
1769        match attr {
1770            Value::List(items) => {
1771                assert_eq!(items.len(), 2);
1772            }
1773            _ => panic!("Expected List value"),
1774        }
1775    }
1776
1777    #[test]
1778    fn test_parse_function_call_missing_paren() {
1779        // Function call without closing paren - should error
1780        let input = r#"val = env("API_KEY"#;
1781        let result = parse(input);
1782        assert!(result.is_err());
1783    }
1784
1785    #[test]
1786    fn test_parse_list_of_objects() {
1787        let input = r#"
1788            knowledge_bases = [
1789                { id = "WThXBKfN21eAxJOl3n1PA", name = "个人知识" },
1790                { id = "another_id", name = "another_name" }
1791            ]
1792        "#;
1793        let doc = parse(input).unwrap();
1794        let attr = doc.blocks[0].attributes.get("knowledge_bases").unwrap();
1795        match attr {
1796            Value::List(items) => {
1797                assert_eq!(items.len(), 2);
1798                match &items[0] {
1799                    Value::Object(pairs) => {
1800                        let map: HashMap<_, _> = pairs.iter().cloned().collect();
1801                        assert_eq!(
1802                            map.get("id"),
1803                            Some(&Value::String("WThXBKfN21eAxJOl3n1PA".to_string()))
1804                        );
1805                        assert_eq!(
1806                            map.get("name"),
1807                            Some(&Value::String("个人知识".to_string()))
1808                        );
1809                    }
1810                    _ => panic!("Expected Object value"),
1811                }
1812            }
1813            _ => panic!("Expected List value"),
1814        }
1815    }
1816
1817    #[test]
1818    fn test_parse_list_of_single_object() {
1819        let input = r#"items = [{ id = "1", name = "test" }]"#;
1820        let doc = parse(input).unwrap();
1821        let attr = doc.blocks[0].attributes.get("items").unwrap();
1822        match attr {
1823            Value::List(items) => assert_eq!(items.len(), 1),
1824            _ => panic!("Expected List value"),
1825        }
1826    }
1827}