sea_core/parser/
ast.rs

1use crate::graph::Graph;
2use crate::parser::error::{ParseError, ParseResult};
3use crate::parser::{ParseOptions, Rule, SeaParser};
4use crate::patterns::Pattern;
5use crate::policy::{
6    AggregateFunction, BinaryOp, Expression, Policy, PolicyKind as CorePolicyKind,
7    PolicyModality as CorePolicyModality, Quantifier as PolicyQuantifier, UnaryOp, WindowSpec,
8};
9use crate::primitives::{ConceptChange, Entity, Flow, RelationType, Resource, Role, Severity};
10use crate::units::unit_from_string;
11use crate::SemanticVersion;
12use chrono::Duration;
13use pest::iterators::{Pair, Pairs};
14use pest::{Parser, Span};
15use rust_decimal::prelude::ToPrimitive;
16use rust_decimal::Decimal;
17use serde::{Deserialize, Serialize};
18use serde_json::json;
19use serde_json::Value as JsonValue;
20use std::collections::HashMap;
21
22/// File-level metadata from header annotations
23#[derive(Debug, Clone, PartialEq, Default)]
24pub struct FileMetadata {
25    pub namespace: Option<String>,
26    pub version: Option<String>,
27    pub owner: Option<String>,
28    pub profile: Option<String>,
29    pub imports: Vec<ImportDecl>,
30}
31
32/// Import declaration for a module file
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct ImportDecl {
35    pub specifier: ImportSpecifier,
36    pub from_module: String,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub enum ImportSpecifier {
41    Named(Vec<ImportItem>),
42    Wildcard(String),
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct ImportItem {
47    pub name: String,
48    pub alias: Option<String>,
49}
50
51/// Policy metadata
52#[derive(Debug, Clone, PartialEq)]
53pub struct PolicyMetadata {
54    pub kind: Option<PolicyKind>,
55    pub modality: Option<PolicyModality>,
56    pub priority: Option<i32>,
57    pub rationale: Option<String>,
58    pub tags: Vec<String>,
59}
60
61#[derive(Debug, Clone, PartialEq)]
62pub enum PolicyKind {
63    Constraint,
64    Derivation,
65    Obligation,
66}
67
68impl std::fmt::Display for PolicyKind {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        match self {
71            PolicyKind::Constraint => write!(f, "Constraint"),
72            PolicyKind::Derivation => write!(f, "Derivation"),
73            PolicyKind::Obligation => write!(f, "Obligation"),
74        }
75    }
76}
77
78#[derive(Debug, Clone, PartialEq)]
79pub enum PolicyModality {
80    Obligation,
81    Prohibition,
82    Permission,
83}
84
85impl std::fmt::Display for PolicyModality {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match self {
88            PolicyModality::Obligation => write!(f, "Obligation"),
89            PolicyModality::Prohibition => write!(f, "Prohibition"),
90            PolicyModality::Permission => write!(f, "Permission"),
91        }
92    }
93}
94
95/// Metric declaration AST node
96#[derive(Debug, Clone, PartialEq)]
97pub struct MetricMetadata {
98    pub refresh_interval: Option<Duration>,
99    pub unit: Option<String>,
100    pub threshold: Option<Decimal>,
101    pub severity: Option<Severity>,
102    pub target: Option<Decimal>,
103    pub window: Option<Duration>,
104}
105
106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107pub enum TargetFormat {
108    Calm,
109    Kg,
110    Sbvr,
111    Protobuf,
112}
113
114impl std::fmt::Display for TargetFormat {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        match self {
117            TargetFormat::Calm => write!(f, "CALM"),
118            TargetFormat::Kg => write!(f, "KG"),
119            TargetFormat::Sbvr => write!(f, "SBVR"),
120            TargetFormat::Protobuf => write!(f, "Protobuf"),
121        }
122    }
123}
124
125#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub struct MappingRule {
127    pub primitive_type: String,
128    pub primitive_name: String,
129    pub target_type: String,
130    pub fields: HashMap<String, JsonValue>,
131}
132
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134pub struct ProjectionOverride {
135    pub primitive_type: String,
136    pub primitive_name: String,
137    pub fields: HashMap<String, JsonValue>,
138}
139
140/// Abstract Syntax Tree for SEA DSL
141/// Abstract Syntax Tree for SEA DSL
142#[derive(Debug, Clone, PartialEq)]
143pub struct Spanned<T> {
144    pub node: T,
145    pub line: usize,
146    pub column: usize,
147}
148
149#[derive(Debug, Clone, PartialEq)]
150pub struct Ast {
151    pub metadata: FileMetadata,
152    pub declarations: Vec<Spanned<AstNode>>,
153}
154
155/// AST Node types
156#[derive(Debug, Clone, PartialEq)]
157pub enum AstNode {
158    Export(Box<Spanned<AstNode>>),
159    Entity {
160        name: String,
161        version: Option<String>,
162        annotations: HashMap<String, JsonValue>,
163        domain: Option<String>,
164    },
165    Resource {
166        name: String,
167        annotations: HashMap<String, JsonValue>,
168        unit_name: Option<String>,
169        domain: Option<String>,
170    },
171    Flow {
172        resource_name: String,
173        annotations: HashMap<String, JsonValue>,
174        from_entity: String,
175        to_entity: String,
176        quantity: Option<i32>,
177    },
178    Pattern {
179        name: String,
180        regex: String,
181    },
182    Role {
183        name: String,
184        domain: Option<String>,
185    },
186    Relation {
187        name: String,
188        subject_role: String,
189        predicate: String,
190        object_role: String,
191        via_flow: Option<String>,
192    },
193    Dimension {
194        name: String,
195    },
196    UnitDeclaration {
197        symbol: String,
198        dimension: String,
199        factor: Decimal,
200        base_unit: String,
201    },
202    Policy {
203        name: String,
204        version: Option<String>,
205        metadata: PolicyMetadata,
206        expression: Expression,
207    },
208    Instance {
209        name: String,
210        entity_type: String,
211        fields: HashMap<String, Expression>,
212    },
213    ConceptChange {
214        name: String,
215        from_version: String,
216        to_version: String,
217        migration_policy: String,
218        breaking_change: bool,
219    },
220    Metric {
221        name: String,
222        expression: Expression,
223        metadata: MetricMetadata,
224    },
225    MappingDecl {
226        name: String,
227        target: TargetFormat,
228        rules: Vec<MappingRule>,
229    },
230    ProjectionDecl {
231        name: String,
232        target: TargetFormat,
233        overrides: Vec<ProjectionOverride>,
234    },
235}
236
237/// Parse source code into an AST
238pub fn parse_source(source: &str) -> ParseResult<Ast> {
239    let pairs = SeaParser::parse(Rule::program, source)?;
240    build_ast(pairs)
241}
242
243/// Build AST from pest pairs
244fn build_ast(pairs: Pairs<Rule>) -> ParseResult<Ast> {
245    let mut metadata = FileMetadata::default();
246    let mut declarations = Vec::new();
247
248    for pair in pairs {
249        match pair.as_rule() {
250            Rule::program => {
251                for inner in pair.into_inner() {
252                    match inner.as_rule() {
253                        Rule::file_header => {
254                            metadata = parse_file_header(inner)?;
255                        }
256                        Rule::declaration => {
257                            for decl in inner.into_inner() {
258                                let node = parse_declaration(decl)?;
259                                declarations.push(node);
260                            }
261                        }
262                        Rule::EOI => {}
263                        _ => {}
264                    }
265                }
266            }
267            Rule::EOI => {}
268            _ => {}
269        }
270    }
271
272    Ok(Ast {
273        metadata,
274        declarations,
275    })
276}
277
278/// Parse file header annotations
279fn parse_file_header(pair: Pair<Rule>) -> ParseResult<FileMetadata> {
280    let mut metadata = FileMetadata::default();
281
282    for annotation in pair.into_inner() {
283        match annotation.as_rule() {
284            Rule::annotation => {
285                let mut inner = annotation.into_inner();
286                let name = inner.next().ok_or_else(|| {
287                    ParseError::GrammarError("Expected annotation name".to_string())
288                })?;
289                let value = inner.next().ok_or_else(|| {
290                    ParseError::GrammarError("Expected annotation value".to_string())
291                })?;
292
293                let name_str = name.as_str().to_lowercase();
294                let value_str = parse_string_literal(value)?;
295
296                match name_str.as_str() {
297                    "namespace" => metadata.namespace = Some(value_str),
298                    "version" => metadata.version = Some(value_str),
299                    "owner" => metadata.owner = Some(value_str),
300                    "profile" => metadata.profile = Some(value_str),
301                    _ => {
302                        return Err(ParseError::GrammarError(format!(
303                            "Unknown annotation: {}",
304                            name_str
305                        )))
306                    }
307                }
308            }
309            Rule::import_decl => {
310                metadata.imports.push(parse_import_decl(annotation)?);
311            }
312            _ => {}
313        }
314    }
315
316    Ok(metadata)
317}
318
319/// Parse a single declaration
320fn parse_declaration(pair: Pair<Rule>) -> ParseResult<Spanned<AstNode>> {
321    let (line, column) = pair.line_col();
322    let node = match pair.as_rule() {
323        Rule::export_decl => {
324            let mut inner = pair.into_inner();
325            let wrapped = inner.next().ok_or_else(|| {
326                ParseError::GrammarError("Expected declaration after export".to_string())
327            })?;
328            let node = parse_declaration(wrapped)?;
329            Ok(AstNode::Export(Box::new(node)))
330        }
331        Rule::declaration_inner => {
332            let inner = pair
333                .into_inner()
334                .next()
335                .ok_or_else(|| ParseError::GrammarError("Empty declaration".to_string()))?;
336            // Recursively call parse_declaration, but we need to unwrap the result if we want to avoid double spanning?
337            // Actually declaration_inner is just a wrapper. The inner parse_declaration returns Spanned<AstNode>.
338            // We should just return that directly.
339            return parse_declaration(inner);
340        }
341        Rule::dimension_decl => parse_dimension(pair),
342        Rule::unit_decl => parse_unit_declaration(pair),
343        Rule::entity_decl => parse_entity(pair),
344        Rule::resource_decl => parse_resource(pair),
345        Rule::flow_decl => parse_flow(pair),
346        Rule::pattern_decl => parse_pattern(pair),
347        Rule::role_decl => parse_role(pair),
348        Rule::relation_decl => parse_relation(pair),
349        Rule::instance_decl => parse_instance(pair),
350        Rule::policy_decl => parse_policy(pair),
351        Rule::concept_change_decl => parse_concept_change(pair),
352        Rule::metric_decl => parse_metric(pair),
353        Rule::mapping_decl => parse_mapping(pair),
354        Rule::projection_decl => parse_projection(pair),
355        _ => Err(ParseError::GrammarError(format!(
356            "Unexpected rule: {:?}",
357            pair.as_rule()
358        ))),
359    }?;
360
361    Ok(Spanned { node, line, column })
362}
363
364fn parse_import_decl(pair: Pair<Rule>) -> ParseResult<ImportDecl> {
365    let mut inner = pair.into_inner();
366    let specifier_pair = inner
367        .next()
368        .ok_or_else(|| ParseError::GrammarError("Missing import specifier".to_string()))?;
369    let from_pair = inner
370        .next()
371        .ok_or_else(|| ParseError::GrammarError("Missing import source".to_string()))?;
372
373    let specifier = match specifier_pair.as_rule() {
374        Rule::import_specifier | Rule::import_named | Rule::import_wildcard => {
375            parse_import_specifier(specifier_pair)?
376        }
377        _ => {
378            return Err(ParseError::GrammarError(format!(
379                "Unexpected import specifier: {:?}",
380                specifier_pair.as_rule()
381            )))
382        }
383    };
384
385    Ok(ImportDecl {
386        specifier,
387        from_module: parse_string_literal(from_pair)?,
388    })
389}
390
391fn parse_import_specifier(pair: Pair<Rule>) -> ParseResult<ImportSpecifier> {
392    match pair.as_rule() {
393        Rule::import_wildcard => {
394            let mut inner = pair.into_inner();
395            let alias = parse_identifier(inner.next().ok_or_else(|| {
396                ParseError::GrammarError("Expected alias for wildcard import".to_string())
397            })?)?;
398            Ok(ImportSpecifier::Wildcard(alias))
399        }
400        Rule::import_specifier | Rule::import_named => {
401            let mut items = Vec::new();
402            for item in pair.into_inner() {
403                if item.as_rule() == Rule::import_item {
404                    items.push(parse_import_item(item)?);
405                }
406            }
407            Ok(ImportSpecifier::Named(items))
408        }
409        _ => Err(ParseError::GrammarError(
410            "Invalid import specifier".to_string(),
411        )),
412    }
413}
414
415fn parse_import_item(pair: Pair<Rule>) -> ParseResult<ImportItem> {
416    let mut inner = pair.into_inner();
417    let name_pair = inner
418        .next()
419        .ok_or_else(|| ParseError::GrammarError("Expected import item name".to_string()))?;
420    let alias = inner.next().map(parse_identifier).transpose()?;
421
422    Ok(ImportItem {
423        name: parse_identifier(name_pair)?,
424        alias,
425    })
426}
427
428/// Parse dimension declaration
429fn parse_dimension(pair: Pair<Rule>) -> ParseResult<AstNode> {
430    let mut inner = pair.into_inner();
431    let name = parse_string_literal(
432        inner
433            .next()
434            .ok_or_else(|| ParseError::GrammarError("Expected dimension name".to_string()))?,
435    )?;
436
437    Ok(AstNode::Dimension { name })
438}
439
440/// Parse unit declaration
441fn parse_unit_declaration(pair: Pair<Rule>) -> ParseResult<AstNode> {
442    let mut inner = pair.into_inner();
443
444    let symbol = parse_string_literal(
445        inner
446            .next()
447            .ok_or_else(|| ParseError::GrammarError("Expected unit symbol".to_string()))?,
448    )?;
449
450    let dimension = parse_string_literal(
451        inner
452            .next()
453            .ok_or_else(|| ParseError::GrammarError("Expected dimension name".to_string()))?,
454    )?;
455
456    let factor_pair = inner
457        .next()
458        .ok_or_else(|| ParseError::GrammarError("Expected factor".to_string()))?;
459    let factor = parse_decimal(factor_pair)?;
460
461    let base_unit = parse_string_literal(
462        inner
463            .next()
464            .ok_or_else(|| ParseError::GrammarError("Expected base unit".to_string()))?,
465    )?;
466
467    Ok(AstNode::UnitDeclaration {
468        symbol,
469        dimension,
470        factor,
471        base_unit,
472    })
473}
474
475/// Parse entity declaration
476fn parse_entity(pair: Pair<Rule>) -> ParseResult<AstNode> {
477    let mut inner = pair.into_inner();
478
479    let name = parse_name(
480        inner
481            .next()
482            .ok_or_else(|| ParseError::GrammarError("Expected entity name".to_string()))?,
483    )?;
484
485    let mut version = None;
486    let mut annotations = HashMap::new();
487    let mut domain = None;
488
489    for part in inner {
490        match part.as_rule() {
491            Rule::version => {
492                version = Some(part.as_str().to_string());
493            }
494            Rule::entity_annotation => {
495                let mut annotation_inner = part.into_inner();
496                let key_pair = annotation_inner
497                    .next()
498                    .ok_or_else(|| ParseError::GrammarError("Empty annotation".to_string()))?;
499
500                match key_pair.as_rule() {
501                    Rule::ea_replaces => {
502                        let target_name =
503                            parse_name(annotation_inner.next().ok_or_else(|| {
504                                ParseError::GrammarError("Expected name in replaces".to_string())
505                            })?)?;
506                        // Check for optional version
507                        let mut target_version = None;
508                        if let Some(next) = annotation_inner.next() {
509                            if next.as_rule() == Rule::version {
510                                target_version = Some(next.as_str().to_string());
511                            }
512                        }
513
514                        let value = if let Some(v) = target_version {
515                            format!("{} v{}", target_name, v)
516                        } else {
517                            target_name
518                        };
519                        annotations.insert("replaces".to_string(), JsonValue::String(value));
520                    }
521                    Rule::ea_changes => {
522                        let array_pair = annotation_inner.next().ok_or_else(|| {
523                            ParseError::GrammarError("Expected string array in changes".to_string())
524                        })?;
525                        let mut changes = Vec::new();
526                        for item in array_pair.into_inner() {
527                            changes.push(parse_string_literal(item)?);
528                        }
529                        annotations.insert(
530                            "changes".to_string(),
531                            JsonValue::Array(changes.into_iter().map(JsonValue::String).collect()),
532                        );
533                    }
534                    Rule::identifier => {
535                        let key = parse_identifier(key_pair)?.to_lowercase();
536                        let value_pair = annotation_inner.next().ok_or_else(|| {
537                            ParseError::GrammarError("Expected annotation value".to_string())
538                        })?;
539                        let value = parse_annotation_value(value_pair)?;
540                        println!("debug insert annotation {} = {:?}", key, value);
541                        annotations.insert(key, value);
542                    }
543                    _ => {
544                        return Err(ParseError::GrammarError(format!(
545                            "Unexpected flow annotation: {}",
546                            key_pair.as_str()
547                        )));
548                    }
549                }
550            }
551            Rule::in_keyword => {
552                // Skip "in"
553            }
554            Rule::identifier => {
555                // This must be the domain
556                domain = Some(parse_identifier(part)?);
557            }
558            _ => {}
559        }
560    }
561
562    Ok(AstNode::Entity {
563        name,
564        version,
565        annotations,
566        domain,
567    })
568}
569
570/// Parse concept change declaration
571fn parse_concept_change(pair: Pair<Rule>) -> ParseResult<AstNode> {
572    let mut inner = pair.into_inner();
573
574    let name =
575        parse_name(inner.next().ok_or_else(|| {
576            ParseError::GrammarError("Expected concept change name".to_string())
577        })?)?;
578
579    let mut from_version = String::new();
580    let mut to_version = String::new();
581    let mut migration_policy = String::new();
582    let mut breaking_change = false;
583
584    for part in inner {
585        if part.as_rule() == Rule::concept_change_annotation {
586            let mut annotation_inner = part.into_inner();
587
588            let key_pair = annotation_inner
589                .next()
590                .ok_or_else(|| ParseError::GrammarError("Expected annotation key".to_string()))?;
591            let value_pair = annotation_inner
592                .next()
593                .ok_or_else(|| ParseError::GrammarError("Expected annotation value".to_string()))?;
594
595            match key_pair.as_rule() {
596                Rule::cc_from_version => from_version = parse_version(value_pair)?,
597                Rule::cc_to_version => to_version = parse_version(value_pair)?,
598                Rule::cc_migration_policy => migration_policy = parse_identifier(value_pair)?,
599                Rule::cc_breaking_change => {
600                    breaking_change = value_pair.as_str() == "true";
601                }
602                _ => {}
603            }
604        }
605    }
606
607    if from_version.is_empty() {
608        return Err(ParseError::GrammarError(
609            "Missing cc_from_version annotation".to_string(),
610        ));
611    }
612
613    if to_version.is_empty() {
614        return Err(ParseError::GrammarError(
615            "Missing cc_to_version annotation".to_string(),
616        ));
617    }
618
619    if migration_policy.is_empty() {
620        return Err(ParseError::GrammarError(
621            "Missing cc_migration_policy annotation".to_string(),
622        ));
623    }
624
625    Ok(AstNode::ConceptChange {
626        name,
627        from_version,
628        to_version,
629        migration_policy,
630        breaking_change,
631    })
632}
633
634/// Parse resource declaration
635fn parse_resource(pair: Pair<Rule>) -> ParseResult<AstNode> {
636    let mut inner = pair.into_inner();
637
638    let name = parse_name(
639        inner
640            .next()
641            .ok_or_else(|| ParseError::GrammarError("Expected resource name".to_string()))?,
642    )?;
643
644    let mut annotations = HashMap::new();
645    let mut unit_name = None;
646    let mut domain = None;
647    let mut saw_in_keyword = false;
648
649    for part in inner {
650        match part.as_rule() {
651            Rule::resource_annotation => {
652                let mut annotation_inner = part.into_inner();
653                let key_pair = annotation_inner
654                    .next()
655                    .ok_or_else(|| ParseError::GrammarError("Empty annotation".to_string()))?;
656
657                match key_pair.as_rule() {
658                    Rule::ea_replaces => {
659                        let target_name =
660                            parse_name(annotation_inner.next().ok_or_else(|| {
661                                ParseError::GrammarError("Expected name in replaces".to_string())
662                            })?)?;
663                        // Check for optional version
664                        let mut target_version = None;
665                        if let Some(next) = annotation_inner.next() {
666                            if next.as_rule() == Rule::version {
667                                target_version = Some(next.as_str().to_string());
668                            }
669                        }
670
671                        let value = if let Some(v) = target_version {
672                            format!("{} v{}", target_name, v)
673                        } else {
674                            target_name
675                        };
676                        annotations.insert("replaces".to_string(), JsonValue::String(value));
677                    }
678                    Rule::ea_changes => {
679                        let array_pair = annotation_inner.next().ok_or_else(|| {
680                            ParseError::GrammarError("Expected string array in changes".to_string())
681                        })?;
682                        let mut changes = Vec::new();
683                        for item in array_pair.into_inner() {
684                            changes.push(parse_string_literal(item)?);
685                        }
686                        annotations.insert(
687                            "changes".to_string(),
688                            JsonValue::Array(changes.into_iter().map(JsonValue::String).collect()),
689                        );
690                    }
691                    Rule::identifier => {
692                        let key = parse_identifier(key_pair)?.to_lowercase();
693                        let value_pair = annotation_inner.next().ok_or_else(|| {
694                            ParseError::GrammarError("Expected annotation value".to_string())
695                        })?;
696                        let value = parse_annotation_value(value_pair)?;
697                        annotations.insert(key, value);
698                    }
699                    _ => {
700                        return Err(ParseError::GrammarError(format!(
701                            "Unexpected flow annotation: {}",
702                            key_pair.as_str()
703                        )));
704                    }
705                }
706            }
707            Rule::in_keyword => {
708                // Mark that we've seen 'in', next identifier is domain
709                saw_in_keyword = true;
710            }
711            Rule::identifier => {
712                // If we've seen 'in' keyword, this is domain; otherwise it's unit
713                if saw_in_keyword {
714                    domain = Some(parse_identifier(part)?);
715                    saw_in_keyword = false; // Reset for safety
716                } else {
717                    unit_name = Some(parse_identifier(part)?);
718                }
719            }
720            _ => {}
721        }
722    }
723
724    Ok(AstNode::Resource {
725        name,
726        annotations,
727        unit_name,
728        domain,
729    })
730}
731
732/// Parse flow declaration
733fn parse_flow(pair: Pair<Rule>) -> ParseResult<AstNode> {
734    let mut inner = pair.into_inner();
735
736    let resource_name = parse_string_literal(
737        inner
738            .next()
739            .ok_or_else(|| ParseError::GrammarError("Expected resource name".to_string()))?,
740    )?;
741
742    let mut annotations = HashMap::new();
743    let mut from_entity = None;
744    let mut to_entity = None;
745    let mut quantity = None;
746    let mut pending_annotation_key: Option<String> = None;
747
748    for part in inner {
749        match part.as_rule() {
750            Rule::flow_annotation => {
751                let mut annotation_inner = part.into_inner();
752                let key_pair = annotation_inner
753                    .next()
754                    .ok_or_else(|| ParseError::GrammarError("Empty annotation".to_string()))?;
755
756                match key_pair.as_rule() {
757                    Rule::ea_replaces => {
758                        let target_name =
759                            parse_name(annotation_inner.next().ok_or_else(|| {
760                                ParseError::GrammarError("Expected name in replaces".to_string())
761                            })?)?;
762                        // Check for optional version
763                        let mut target_version = None;
764                        if let Some(next) = annotation_inner.next() {
765                            if next.as_rule() == Rule::version {
766                                target_version = Some(next.as_str().to_string());
767                            }
768                        }
769
770                        let value = if let Some(v) = target_version {
771                            format!("{} v{}", target_name, v)
772                        } else {
773                            target_name
774                        };
775                        annotations.insert("replaces".to_string(), JsonValue::String(value));
776                    }
777                    Rule::ea_changes => {
778                        let array_pair = annotation_inner.next().ok_or_else(|| {
779                            ParseError::GrammarError("Expected string array in changes".to_string())
780                        })?;
781                        let mut changes = Vec::new();
782                        for item in array_pair.into_inner() {
783                            changes.push(parse_string_literal(item)?);
784                        }
785                        annotations.insert(
786                            "changes".to_string(),
787                            JsonValue::Array(changes.into_iter().map(JsonValue::String).collect()),
788                        );
789                    }
790                    Rule::identifier => {
791                        let key = parse_identifier(key_pair)?.to_lowercase();
792                        let value_pair = annotation_inner.next().ok_or_else(|| {
793                            ParseError::GrammarError("Expected annotation value".to_string())
794                        })?;
795                        let value = parse_annotation_value(value_pair)?;
796                        annotations.insert(key, value);
797                    }
798                    _ => {
799                        return Err(ParseError::GrammarError(format!(
800                            "Unexpected flow annotation: {}",
801                            key_pair.as_str()
802                        )));
803                    }
804                }
805            }
806            Rule::identifier => {
807                if pending_annotation_key.is_some() {
808                    return Err(ParseError::GrammarError(
809                        "Unexpected annotation key without value".to_string(),
810                    ));
811                }
812                pending_annotation_key = Some(parse_identifier(part)?.to_lowercase());
813            }
814            Rule::annotation_value
815            | Rule::object_literal
816            | Rule::string_array
817            | Rule::boolean
818            | Rule::number
819            | Rule::string_literal => {
820                if let Some(key) = pending_annotation_key.take() {
821                    let value = parse_annotation_value(part)?;
822                    annotations.insert(key, value);
823                    continue;
824                }
825
826                if part.as_rule() == Rule::string_literal {
827                    // These are from_entity and to_entity in order
828                    let parsed = parse_string_literal(part)?;
829                    if from_entity.is_none() {
830                        from_entity = Some(parsed);
831                    } else if to_entity.is_none() {
832                        to_entity = Some(parsed);
833                    }
834                } else if part.as_rule() == Rule::number {
835                    quantity = Some(parse_number(part)?);
836                }
837            }
838            _ => {}
839        }
840    }
841
842    Ok(AstNode::Flow {
843        resource_name,
844        annotations,
845        from_entity: from_entity
846            .ok_or_else(|| ParseError::GrammarError("Expected from entity".to_string()))?,
847        to_entity: to_entity
848            .ok_or_else(|| ParseError::GrammarError("Expected to entity".to_string()))?,
849        quantity,
850    })
851}
852
853/// Parse pattern declaration
854fn parse_pattern(pair: Pair<Rule>) -> ParseResult<AstNode> {
855    let mut inner = pair.into_inner();
856
857    let name = parse_name(
858        inner
859            .next()
860            .ok_or_else(|| ParseError::GrammarError("Expected pattern name".to_string()))?,
861    )?;
862
863    let regex_literal = inner
864        .next()
865        .ok_or_else(|| ParseError::GrammarError("Expected regex for pattern".to_string()))?;
866    let regex = parse_string_literal(regex_literal)?;
867
868    Ok(AstNode::Pattern { name, regex })
869}
870
871/// Parse role declaration
872fn parse_role(pair: Pair<Rule>) -> ParseResult<AstNode> {
873    let mut inner = pair.into_inner();
874
875    let name = parse_name(
876        inner
877            .next()
878            .ok_or_else(|| ParseError::GrammarError("Expected role name".to_string()))?,
879    )?;
880
881    let domain = if let Some(domain_pair) = inner.next() {
882        Some(parse_identifier(domain_pair)?)
883    } else {
884        None
885    };
886
887    Ok(AstNode::Role { name, domain })
888}
889
890/// Parse relation declaration
891fn parse_relation(pair: Pair<Rule>) -> ParseResult<AstNode> {
892    let mut inner = pair.into_inner();
893
894    let name = parse_name(
895        inner
896            .next()
897            .ok_or_else(|| ParseError::GrammarError("Expected relation name".to_string()))?,
898    )?;
899
900    let subject_literal = inner
901        .next()
902        .ok_or_else(|| ParseError::GrammarError("Expected subject role in relation".to_string()))?;
903    let subject_role = parse_string_literal(subject_literal)?;
904
905    let predicate_literal = inner
906        .next()
907        .ok_or_else(|| ParseError::GrammarError("Expected predicate in relation".to_string()))?;
908    let predicate = parse_string_literal(predicate_literal)?;
909
910    let object_literal = inner
911        .next()
912        .ok_or_else(|| ParseError::GrammarError("Expected object role in relation".to_string()))?;
913    let object_role = parse_string_literal(object_literal)?;
914
915    let via_flow = if let Some(via_pair) = inner.next() {
916        match via_pair.as_rule() {
917            Rule::string_literal => Some(parse_string_literal(via_pair)?),
918            _ => {
919                let mut via_inner = via_pair.into_inner();
920                let flow_literal = via_inner.next().ok_or_else(|| {
921                    ParseError::GrammarError("Expected flow name after via".to_string())
922                })?;
923                Some(parse_string_literal(flow_literal)?)
924            }
925        }
926    } else {
927        None
928    };
929
930    Ok(AstNode::Relation {
931        name,
932        subject_role,
933        predicate,
934        object_role,
935        via_flow,
936    })
937}
938
939/// Parse instance declaration
940fn parse_instance(pair: Pair<Rule>) -> ParseResult<AstNode> {
941    let mut inner = pair.into_inner();
942
943    let name = parse_identifier(
944        inner
945            .next()
946            .ok_or_else(|| ParseError::GrammarError("Expected instance name".to_string()))?,
947    )?;
948
949    let entity_type = parse_string_literal(
950        inner
951            .next()
952            .ok_or_else(|| ParseError::GrammarError("Expected entity type".to_string()))?,
953    )?;
954
955    let mut fields = HashMap::new();
956
957    // Parse optional instance body
958    if let Some(body_pair) = inner.next() {
959        if body_pair.as_rule() == Rule::instance_body {
960            for field_pair in body_pair.into_inner() {
961                if field_pair.as_rule() == Rule::instance_field {
962                    let span = field_pair.as_span();
963                    let mut field_inner = field_pair.into_inner();
964
965                    let field_name = parse_identifier(field_inner.next().ok_or_else(|| {
966                        ParseError::GrammarError("Expected field name".to_string())
967                    })?)?;
968
969                    if fields.contains_key(&field_name) {
970                        let (line, column) = span.start_pos().line_col();
971                        return Err(ParseError::GrammarError(format!(
972                            "Duplicate field name '{}' at line {}, column {}",
973                            field_name, line, column
974                        )));
975                    }
976
977                    let field_value = parse_expression(field_inner.next().ok_or_else(|| {
978                        ParseError::GrammarError("Expected field value".to_string())
979                    })?)?;
980
981                    fields.insert(field_name, field_value);
982                }
983            }
984        }
985    }
986
987    Ok(AstNode::Instance {
988        name,
989        entity_type,
990        fields,
991    })
992}
993
994/// Parse policy declaration
995fn parse_policy(pair: Pair<Rule>) -> ParseResult<AstNode> {
996    let mut inner = pair.into_inner();
997
998    let name = parse_identifier(
999        inner
1000            .next()
1001            .ok_or_else(|| ParseError::GrammarError("Expected policy name".to_string()))?,
1002    )?;
1003
1004    let mut metadata = PolicyMetadata {
1005        kind: None,
1006        modality: None,
1007        priority: None,
1008        rationale: None,
1009        tags: Vec::new(),
1010    };
1011    let mut version: Option<String> = None;
1012
1013    for next_pair in inner {
1014        match next_pair.as_rule() {
1015            Rule::policy_kind => {
1016                metadata.kind = Some(match next_pair.as_str().to_lowercase().as_str() {
1017                    "constraint" => PolicyKind::Constraint,
1018                    "derivation" => PolicyKind::Derivation,
1019                    "obligation" => PolicyKind::Obligation,
1020                    _ => {
1021                        return Err(ParseError::GrammarError(format!(
1022                            "Unknown policy kind: {}",
1023                            next_pair.as_str()
1024                        )))
1025                    }
1026                });
1027            }
1028            Rule::policy_modality => {
1029                metadata.modality = Some(match next_pair.as_str().to_lowercase().as_str() {
1030                    "obligation" => PolicyModality::Obligation,
1031                    "prohibition" => PolicyModality::Prohibition,
1032                    "permission" => PolicyModality::Permission,
1033                    _ => {
1034                        return Err(ParseError::GrammarError(format!(
1035                            "Unknown policy modality: {}",
1036                            next_pair.as_str()
1037                        )))
1038                    }
1039                });
1040            }
1041            Rule::number => {
1042                metadata.priority = Some(parse_number(next_pair)?);
1043            }
1044            Rule::policy_annotation => {
1045                parse_policy_annotation(next_pair, &mut metadata)?;
1046            }
1047            Rule::version => {
1048                version = Some(next_pair.as_str().to_string());
1049            }
1050            Rule::expression => {
1051                let expression = parse_expression(next_pair)?;
1052                return Ok(AstNode::Policy {
1053                    name,
1054                    version,
1055                    metadata,
1056                    expression,
1057                });
1058            }
1059            _ => {}
1060        }
1061    }
1062
1063    Err(ParseError::GrammarError(
1064        "Policy missing expression".to_string(),
1065    ))
1066}
1067
1068fn parse_policy_annotation(pair: Pair<Rule>, metadata: &mut PolicyMetadata) -> ParseResult<()> {
1069    let mut inner = pair.into_inner();
1070
1071    let name = inner
1072        .next()
1073        .ok_or_else(|| ParseError::GrammarError("Expected annotation name".to_string()))?;
1074    let value = inner
1075        .next()
1076        .ok_or_else(|| ParseError::GrammarError("Expected annotation value".to_string()))?;
1077
1078    let name_str = name.as_str().to_lowercase();
1079
1080    match name_str.as_str() {
1081        "rationale" => {
1082            metadata.rationale = Some(parse_string_literal(value)?);
1083        }
1084        "tags" => {
1085            if value.as_rule() == Rule::string_array {
1086                for tag_pair in value.into_inner() {
1087                    if tag_pair.as_rule() == Rule::string_literal {
1088                        metadata.tags.push(parse_string_literal(tag_pair)?);
1089                    }
1090                }
1091            } else {
1092                metadata.tags.push(parse_string_literal(value)?);
1093            }
1094        }
1095        _ => {
1096            return Err(ParseError::GrammarError(format!(
1097                "Unknown policy annotation: {}",
1098                name_str
1099            )))
1100        }
1101    }
1102
1103    Ok(())
1104}
1105
1106/// Parse expression
1107fn parse_expression(pair: Pair<Rule>) -> ParseResult<Expression> {
1108    match pair.as_rule() {
1109        Rule::expression => {
1110            let inner = pair
1111                .into_inner()
1112                .next()
1113                .ok_or_else(|| ParseError::GrammarError("Empty expression".to_string()))?;
1114            parse_expression(inner)
1115        }
1116        Rule::or_expr => parse_or_expr(pair),
1117        Rule::and_expr => parse_and_expr(pair),
1118        Rule::not_expr => parse_not_expr(pair),
1119        Rule::comparison_expr => parse_comparison_expr(pair),
1120        Rule::additive_expr => parse_additive_expr(pair),
1121        Rule::multiplicative_expr => parse_multiplicative_expr(pair),
1122        Rule::unary_expr => parse_unary_expr(pair),
1123        Rule::cast_expr => parse_cast_expr(pair),
1124        Rule::primary_expr => parse_primary_expr(pair),
1125        _ => Err(ParseError::InvalidExpression(format!(
1126            "Unexpected expression rule: {:?}",
1127            pair.as_rule()
1128        ))),
1129    }
1130}
1131
1132/// Parse a single expression string into an Expression AST node.
1133pub fn parse_expression_from_str(source: &str) -> ParseResult<Expression> {
1134    let pairs = SeaParser::parse(Rule::expression, source)
1135        .map_err(|e| ParseError::GrammarError(format!("Parse error: {}", e)))?;
1136    let parsed_pairs: Vec<_> = pairs.collect();
1137
1138    if parsed_pairs.is_empty() {
1139        return Err(ParseError::GrammarError("Empty expression".to_string()));
1140    }
1141    if parsed_pairs.len() > 1 {
1142        return Err(ParseError::GrammarError(
1143            "Trailing input detected after expression".to_string(),
1144        ));
1145    }
1146
1147    let pair = parsed_pairs.into_iter().next().unwrap();
1148
1149    // Check that the entire input was consumed (no trailing characters)
1150    let consumed = pair.as_span().end();
1151    let trimmed_source = source.trim_end();
1152    if consumed < trimmed_source.len() {
1153        return Err(ParseError::GrammarError(format!(
1154            "Trailing input after expression: '{}'",
1155            &source[consumed..]
1156        )));
1157    }
1158
1159    parse_expression(pair)
1160}
1161
1162/// Parse OR expression
1163fn parse_or_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1164    let mut inner = pair.into_inner();
1165    let mut left =
1166        parse_expression(inner.next().ok_or_else(|| {
1167            ParseError::GrammarError("Expected left expression in OR".to_string())
1168        })?)?;
1169
1170    for right_pair in inner {
1171        let right = parse_expression(right_pair)?;
1172        left = Expression::Binary {
1173            left: Box::new(left),
1174            op: BinaryOp::Or,
1175            right: Box::new(right),
1176        };
1177    }
1178
1179    Ok(left)
1180}
1181
1182/// Parse AND expression
1183fn parse_and_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1184    let mut inner = pair.into_inner();
1185    let mut left =
1186        parse_expression(inner.next().ok_or_else(|| {
1187            ParseError::GrammarError("Expected left expression in AND".to_string())
1188        })?)?;
1189
1190    for right_pair in inner {
1191        let right = parse_expression(right_pair)?;
1192        left = Expression::Binary {
1193            left: Box::new(left),
1194            op: BinaryOp::And,
1195            right: Box::new(right),
1196        };
1197    }
1198
1199    Ok(left)
1200}
1201
1202/// Parse NOT expression
1203fn parse_not_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1204    let mut inner = pair.into_inner();
1205    let first = inner
1206        .next()
1207        .ok_or_else(|| ParseError::GrammarError("Expected expression in NOT".to_string()))?;
1208
1209    // Check if this is actually a NOT expression or just a comparison_expr
1210    if first.as_rule() == Rule::not_expr {
1211        // This is a recursive NOT, parse it
1212        let expr = parse_expression(first)?;
1213        Ok(Expression::Unary {
1214            op: UnaryOp::Not,
1215            operand: Box::new(expr),
1216        })
1217    } else {
1218        // This is just a comparison_expr, parse it directly
1219        parse_expression(first)
1220    }
1221}
1222
1223/// Parse comparison expression
1224fn parse_comparison_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1225    let mut inner = pair.into_inner();
1226    let mut left = parse_expression(inner.next().ok_or_else(|| {
1227        ParseError::GrammarError("Expected left expression in comparison".to_string())
1228    })?)?;
1229
1230    if let Some(op_pair) = inner.next() {
1231        let op = parse_comparison_op(op_pair)?;
1232        let right = parse_expression(inner.next().ok_or_else(|| {
1233            ParseError::GrammarError("Expected right expression in comparison".to_string())
1234        })?)?;
1235        left = Expression::Binary {
1236            left: Box::new(left),
1237            op,
1238            right: Box::new(right),
1239        };
1240    }
1241
1242    Ok(left)
1243}
1244
1245/// Parse comparison operator
1246fn parse_comparison_op(pair: Pair<Rule>) -> ParseResult<BinaryOp> {
1247    let op_str = pair.as_str();
1248    match op_str {
1249        "=" => Ok(BinaryOp::Equal),
1250        "!=" => Ok(BinaryOp::NotEqual),
1251        ">" => Ok(BinaryOp::GreaterThan),
1252        "<" => Ok(BinaryOp::LessThan),
1253        ">=" => Ok(BinaryOp::GreaterThanOrEqual),
1254        "<=" => Ok(BinaryOp::LessThanOrEqual),
1255        _ if op_str.eq_ignore_ascii_case("contains") => Ok(BinaryOp::Contains),
1256        _ if op_str.eq_ignore_ascii_case("startswith") => Ok(BinaryOp::StartsWith),
1257        _ if op_str.eq_ignore_ascii_case("endswith") => Ok(BinaryOp::EndsWith),
1258        _ if op_str.eq_ignore_ascii_case("matches") => Ok(BinaryOp::Matches),
1259        _ if op_str.eq_ignore_ascii_case("before") => Ok(BinaryOp::Before),
1260        _ if op_str.eq_ignore_ascii_case("after") => Ok(BinaryOp::After),
1261        _ if op_str.eq_ignore_ascii_case("during") => Ok(BinaryOp::During),
1262        _ if op_str.eq_ignore_ascii_case("has_role") => Ok(BinaryOp::HasRole),
1263        _ => Err(ParseError::InvalidExpression(format!(
1264            "Unknown comparison operator: {}",
1265            op_str
1266        ))),
1267    }
1268}
1269
1270/// Parse additive expression
1271fn parse_additive_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1272    let mut inner = pair.into_inner();
1273    let mut left = parse_expression(inner.next().ok_or_else(|| {
1274        ParseError::GrammarError("Expected left expression in additive".to_string())
1275    })?)?;
1276
1277    while let Some(op_pair) = inner.next() {
1278        let op = match op_pair.as_str() {
1279            "+" => BinaryOp::Plus,
1280            "-" => BinaryOp::Minus,
1281            _ => {
1282                return Err(ParseError::InvalidExpression(
1283                    "Invalid additive operator".to_string(),
1284                ))
1285            }
1286        };
1287        let right = parse_expression(inner.next().ok_or_else(|| {
1288            ParseError::GrammarError("Expected right expression in additive".to_string())
1289        })?)?;
1290        left = Expression::Binary {
1291            left: Box::new(left),
1292            op,
1293            right: Box::new(right),
1294        };
1295    }
1296
1297    Ok(left)
1298}
1299
1300/// Parse multiplicative expression
1301fn parse_multiplicative_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1302    let mut inner = pair.into_inner();
1303    let mut left = parse_expression(inner.next().ok_or_else(|| {
1304        ParseError::GrammarError("Expected left expression in multiplicative".to_string())
1305    })?)?;
1306
1307    while let Some(op_pair) = inner.next() {
1308        let op = match op_pair.as_str() {
1309            "*" => BinaryOp::Multiply,
1310            "/" => BinaryOp::Divide,
1311            _ => {
1312                return Err(ParseError::InvalidExpression(
1313                    "Invalid multiplicative operator".to_string(),
1314                ))
1315            }
1316        };
1317        let right = parse_expression(inner.next().ok_or_else(|| {
1318            ParseError::GrammarError("Expected right expression in multiplicative".to_string())
1319        })?)?;
1320        left = Expression::Binary {
1321            left: Box::new(left),
1322            op,
1323            right: Box::new(right),
1324        };
1325    }
1326
1327    Ok(left)
1328}
1329
1330/// Parse cast expression
1331fn parse_cast_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1332    let mut inner = pair.into_inner();
1333    let primary = parse_expression(inner.next().ok_or_else(|| {
1334        ParseError::GrammarError("Expected primary expression in cast".to_string())
1335    })?)?;
1336
1337    if let Some(as_pair) = inner.next() {
1338        let target_type = parse_string_literal(as_pair)?;
1339        Ok(Expression::cast(primary, target_type))
1340    } else {
1341        Ok(primary)
1342    }
1343}
1344
1345/// Parse unary expression
1346fn parse_unary_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1347    let mut inner = pair.into_inner();
1348    let first = inner
1349        .next()
1350        .ok_or_else(|| ParseError::GrammarError("Expected expression in unary".to_string()))?;
1351
1352    if first.as_str() == "-" {
1353        // Unary minus
1354        let expr = parse_expression(inner.next().ok_or_else(|| {
1355            ParseError::GrammarError("Expected expression after unary minus".to_string())
1356        })?)?;
1357        Ok(Expression::Unary {
1358            op: UnaryOp::Negate,
1359            operand: Box::new(expr),
1360        })
1361    } else {
1362        parse_expression(first)
1363    }
1364}
1365
1366/// Parse primary expression
1367fn parse_primary_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1368    let inner = pair.into_inner().next().ok_or_else(|| {
1369        ParseError::GrammarError("Expected inner expression in primary".to_string())
1370    })?;
1371
1372    match inner.as_rule() {
1373        Rule::expression => parse_expression(inner),
1374        Rule::group_by_expr => parse_group_by_expr(inner),
1375        Rule::aggregation_expr => parse_aggregation_expr(inner),
1376        Rule::quantified_expr => parse_quantified_expr(inner),
1377        Rule::member_access => parse_member_access(inner),
1378        Rule::literal => parse_literal_expr(inner),
1379        Rule::identifier => {
1380            let name = parse_identifier(inner)?;
1381            Ok(Expression::Variable(name))
1382        }
1383        _ => Err(ParseError::InvalidExpression(format!(
1384            "Unexpected primary expression: {:?}",
1385            inner.as_rule()
1386        ))),
1387    }
1388}
1389
1390/// Parse aggregation expression
1391fn parse_aggregation_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1392    let mut inner = pair.into_inner();
1393
1394    let function_pair = inner.next().ok_or_else(|| {
1395        ParseError::GrammarError(
1396            "Expected aggregate function in aggregation expression".to_string(),
1397        )
1398    })?;
1399    let function = parse_aggregate_fn(function_pair)?;
1400
1401    let collection_pair = inner.next().ok_or_else(|| {
1402        ParseError::GrammarError("Expected collection in aggregation expression".to_string())
1403    })?;
1404
1405    // Aggregation can be a simple form (e.g., sum(flows), sum(flows.quantity))
1406    // or the comprehension form (e.g., sum(f in flows where f.resource = "Money": f.quantity as "USD")).
1407    if collection_pair.as_rule() == Rule::aggregation_comprehension {
1408        // Parse aggregation comprehension
1409        let mut comp_inner = collection_pair.into_inner();
1410        let variable_pair = comp_inner.next().ok_or_else(|| {
1411            ParseError::GrammarError("Expected variable in aggregation comprehension".to_string())
1412        })?;
1413        let variable = parse_identifier(variable_pair)?;
1414        // Next token should be collection
1415        let collection_token = comp_inner.next().ok_or_else(|| {
1416            ParseError::GrammarError("Expected collection in aggregation comprehension".to_string())
1417        })?;
1418        let collection_name = parse_collection(collection_token)?;
1419        let collection = Box::new(Expression::Variable(collection_name));
1420
1421        let mut next_pair = comp_inner.next().ok_or_else(|| {
1422            ParseError::GrammarError(
1423                "Expected predicate expression, projection, or window in aggregation comprehension"
1424                    .to_string(),
1425            )
1426        })?;
1427
1428        let mut window = None;
1429        if next_pair.as_rule() == Rule::window_clause {
1430            window = Some(parse_window_clause(next_pair)?);
1431            next_pair = comp_inner.next().ok_or_else(|| {
1432                ParseError::GrammarError(
1433                    "Expected predicate or projection expression in aggregation comprehension"
1434                        .to_string(),
1435                )
1436            })?;
1437        }
1438
1439        let remaining_pairs: Vec<Pair<Rule>> =
1440            std::iter::once(next_pair).chain(comp_inner).collect();
1441
1442        let mut expr_pairs: Vec<Pair<Rule>> = Vec::new();
1443        let mut target_unit: Option<String> = None;
1444        for pair in remaining_pairs {
1445            match pair.as_rule() {
1446                Rule::expression => expr_pairs.push(pair),
1447                Rule::string_literal => {
1448                    target_unit = Some(parse_string_literal(pair)?);
1449                }
1450                Rule::identifier if pair.as_str().eq_ignore_ascii_case("as") => {
1451                    // skip explicit AS token
1452                }
1453                other => {
1454                    return Err(ParseError::GrammarError(format!(
1455                        "Unexpected token {:?} in aggregation comprehension",
1456                        other
1457                    )))
1458                }
1459            }
1460        }
1461
1462        let (predicate, projection) = match expr_pairs.len() {
1463            2 => {
1464                let mut expr_iter = expr_pairs.into_iter();
1465                let predicate_expr = parse_expression(expr_iter.next().ok_or_else(|| {
1466                    ParseError::GrammarError(
1467                        "Expected predicate expression in aggregation comprehension".to_string(),
1468                    )
1469                })?)?;
1470                let projection_expr = parse_expression(expr_iter.next().ok_or_else(|| {
1471                    ParseError::GrammarError(
1472                        "Expected projection expression in aggregation comprehension".to_string(),
1473                    )
1474                })?)?;
1475                (predicate_expr, projection_expr)
1476            }
1477            1 => {
1478                let projection_expr =
1479                    parse_expression(expr_pairs.into_iter().next().ok_or_else(|| {
1480                        ParseError::GrammarError(
1481                            "Expected projection expression in aggregation comprehension"
1482                                .to_string(),
1483                        )
1484                    })?)?;
1485                (Expression::Literal(JsonValue::Bool(true)), projection_expr)
1486            }
1487            other => {
1488                return Err(ParseError::GrammarError(format!(
1489                    "Unexpected number of expressions in aggregation comprehension: {}",
1490                    other
1491                )))
1492            }
1493        };
1494
1495        return Ok(Expression::AggregationComprehension {
1496            function,
1497            variable,
1498            collection,
1499            window,
1500            predicate: Box::new(predicate),
1501            projection: Box::new(projection),
1502            target_unit,
1503        });
1504    }
1505
1506    // Parse aggregation_simple
1507    let mut simple_inner = collection_pair.into_inner();
1508
1509    // First item is either collection or identifier
1510    let first_pair = simple_inner.next().ok_or_else(|| {
1511        ParseError::GrammarError(
1512            "Expected collection or identifier in aggregation_simple".to_string(),
1513        )
1514    })?;
1515
1516    let collection = match first_pair.as_rule() {
1517        Rule::collection => parse_collection(first_pair)?,
1518        Rule::identifier => parse_identifier(first_pair)?,
1519        _ => {
1520            return Err(ParseError::GrammarError(format!(
1521                "Expected collection or identifier, got {:?}",
1522                first_pair.as_rule()
1523            )))
1524        }
1525    };
1526
1527    let mut field: Option<String> = None;
1528    let mut filter: Option<Expression> = None;
1529
1530    // Parse optional field and filter
1531    for item in simple_inner {
1532        match item.as_rule() {
1533            Rule::identifier => {
1534                field = Some(parse_identifier(item)?);
1535            }
1536            Rule::expression => {
1537                filter = Some(parse_expression(item)?);
1538            }
1539            _ => {}
1540        }
1541    }
1542
1543    Ok(Expression::aggregation(
1544        function,
1545        Expression::Variable(collection),
1546        field,
1547        filter,
1548    ))
1549}
1550
1551/// Parse aggregate function
1552fn parse_aggregate_fn(pair: Pair<Rule>) -> ParseResult<AggregateFunction> {
1553    let fn_str = pair.as_str();
1554    match fn_str.to_lowercase().as_str() {
1555        "count" => Ok(AggregateFunction::Count),
1556        "sum" => Ok(AggregateFunction::Sum),
1557        "min" => Ok(AggregateFunction::Min),
1558        "max" => Ok(AggregateFunction::Max),
1559        "avg" => Ok(AggregateFunction::Avg),
1560        _ => Err(ParseError::InvalidExpression(format!(
1561            "Unknown aggregate function: {}",
1562            fn_str
1563        ))),
1564    }
1565}
1566
1567/// Parse quantified expression
1568fn parse_quantified_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1569    let mut inner = pair.into_inner();
1570
1571    let quantifier = parse_quantifier(inner.next().ok_or_else(|| {
1572        ParseError::GrammarError("Expected quantifier in quantified expression".to_string())
1573    })?)?;
1574    let variable = parse_identifier(inner.next().ok_or_else(|| {
1575        ParseError::GrammarError(
1576            "Expected variable identifier in quantified expression".to_string(),
1577        )
1578    })?)?;
1579    let collection = parse_collection(inner.next().ok_or_else(|| {
1580        ParseError::GrammarError(
1581            "Expected collection identifier in quantified expression".to_string(),
1582        )
1583    })?)?;
1584    let condition = parse_expression(inner.next().ok_or_else(|| {
1585        ParseError::GrammarError("Expected quantified condition expression".to_string())
1586    })?)?;
1587
1588    Ok(Expression::Quantifier {
1589        quantifier,
1590        variable,
1591        collection: Box::new(Expression::Variable(collection)),
1592        condition: Box::new(condition),
1593    })
1594}
1595
1596/// Parse quantifier
1597fn parse_quantifier(pair: Pair<Rule>) -> ParseResult<PolicyQuantifier> {
1598    let q_str = pair.as_str();
1599    match q_str.to_lowercase().as_str() {
1600        "forall" => Ok(PolicyQuantifier::ForAll),
1601        "exists" => Ok(PolicyQuantifier::Exists),
1602        "exists_unique" => Ok(PolicyQuantifier::ExistsUnique),
1603        _ => Err(ParseError::InvalidExpression(format!(
1604            "Unknown quantifier: {}",
1605            q_str
1606        ))),
1607    }
1608}
1609
1610/// Parse collection type
1611fn parse_collection(pair: Pair<Rule>) -> ParseResult<String> {
1612    Ok(pair.as_str().to_lowercase())
1613}
1614
1615/// Parse member access
1616fn parse_member_access(pair: Pair<Rule>) -> ParseResult<Expression> {
1617    let mut inner = pair.into_inner();
1618    let object = parse_identifier(inner.next().ok_or_else(|| {
1619        ParseError::GrammarError("Expected object identifier in member access".to_string())
1620    })?)?;
1621    let member = parse_identifier(inner.next().ok_or_else(|| {
1622        ParseError::GrammarError("Expected member identifier in member access".to_string())
1623    })?)?;
1624
1625    Ok(Expression::MemberAccess { object, member })
1626}
1627
1628/// Parse literal expression
1629fn parse_literal_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1630    let inner = pair
1631        .into_inner()
1632        .next()
1633        .ok_or_else(|| ParseError::GrammarError("Expected literal content".to_string()))?;
1634
1635    match inner.as_rule() {
1636        Rule::string_literal => {
1637            let s = parse_string_literal(inner)?;
1638            Ok(Expression::Literal(JsonValue::String(s)))
1639        }
1640        Rule::multiline_string => {
1641            let s = parse_multiline_string(inner)?;
1642            Ok(Expression::Literal(JsonValue::String(s)))
1643        }
1644        Rule::quantity_literal => {
1645            let mut parts = inner.into_inner();
1646            let number_part = parts.next().ok_or_else(|| {
1647                ParseError::GrammarError("Expected number in quantity literal".to_string())
1648            })?;
1649            let unit_part = parts.next().ok_or_else(|| {
1650                ParseError::GrammarError("Expected unit string in quantity literal".to_string())
1651            })?;
1652            let value = parse_decimal(number_part)?;
1653            let unit = parse_string_literal(unit_part)?;
1654            Ok(Expression::QuantityLiteral { value, unit })
1655        }
1656        Rule::time_literal => {
1657            // Parse ISO 8601 timestamp (already includes quotes in grammar)
1658            let timestamp = inner.as_str();
1659            // Remove surrounding quotes
1660            let timestamp = timestamp.trim_start_matches('"').trim_end_matches('"');
1661            Ok(Expression::TimeLiteral(timestamp.to_string()))
1662        }
1663        Rule::interval_literal => {
1664            // Parse interval("start", "end")
1665            let mut parts = inner.into_inner();
1666            let start_part = parts.next().ok_or_else(|| {
1667                ParseError::GrammarError("Expected start time in interval literal".to_string())
1668            })?;
1669            let end_part = parts.next().ok_or_else(|| {
1670                ParseError::GrammarError("Expected end time in interval literal".to_string())
1671            })?;
1672            let start = parse_string_literal(start_part)?;
1673            let end = parse_string_literal(end_part)?;
1674            Ok(Expression::IntervalLiteral { start, end })
1675        }
1676        Rule::number => {
1677            let n = parse_decimal(inner)?;
1678            // Convert Decimal to f64 for JSON Number representation
1679            let f = n.to_f64().ok_or_else(|| {
1680                ParseError::InvalidQuantity(format!(
1681                    "Decimal value {} cannot be represented as f64",
1682                    n
1683                ))
1684            })?;
1685            // Ensure the value is finite
1686            if !f.is_finite() {
1687                return Err(ParseError::InvalidQuantity(format!(
1688                    "Decimal value {} converts to non-finite f64: {}",
1689                    n, f
1690                )));
1691            }
1692            let num = serde_json::Number::from_f64(f).ok_or_else(|| {
1693                ParseError::InvalidQuantity(format!(
1694                    "Cannot create JSON Number from f64 value: {}",
1695                    f
1696                ))
1697            })?;
1698            Ok(Expression::Literal(JsonValue::Number(num)))
1699        }
1700        Rule::boolean => {
1701            let b = inner.as_str().eq_ignore_ascii_case("true");
1702            Ok(Expression::Literal(JsonValue::Bool(b)))
1703        }
1704        _ => Err(ParseError::InvalidExpression(format!(
1705            "Unknown literal type: {:?}",
1706            inner.as_rule()
1707        ))),
1708    }
1709}
1710
1711/// Parse name (handles both string_literal and multiline_string)
1712fn parse_name(pair: Pair<Rule>) -> ParseResult<String> {
1713    let inner = pair.into_inner().next().ok_or_else(|| {
1714        ParseError::GrammarError("Expected inner token for name but got empty pair".to_string())
1715    })?;
1716    match inner.as_rule() {
1717        Rule::string_literal => parse_string_literal(inner),
1718        Rule::multiline_string => parse_multiline_string(inner),
1719        _ => Err(ParseError::GrammarError(format!(
1720            "Expected string or multiline string for name, got {:?}",
1721            inner.as_rule()
1722        ))),
1723    }
1724}
1725
1726/// Parse string literal (handles escape sequences)
1727fn parse_string_literal(pair: Pair<Rule>) -> ParseResult<String> {
1728    let s = pair.as_str();
1729    if s.len() < 2 || !s.starts_with('"') || !s.ends_with('"') {
1730        return Err(ParseError::GrammarError(format!(
1731            "Invalid string literal: {}",
1732            s
1733        )));
1734    }
1735
1736    // Use serde_json to properly parse and unescape the string
1737    // The string already has quotes, so pass it directly
1738    match serde_json::from_str(s) {
1739        Ok(unescaped) => Ok(unescaped),
1740        Err(e) => Err(ParseError::GrammarError(format!(
1741            "Invalid string literal escape sequences: {} - {}",
1742            s, e
1743        ))),
1744    }
1745}
1746
1747/// Parse multiline string (removes triple quotes and handles escape sequences)
1748fn parse_multiline_string(pair: Pair<Rule>) -> ParseResult<String> {
1749    let s = pair.as_str();
1750    if s.len() < 6 || !s.starts_with("\"\"\"") || !s.ends_with("\"\"\"") {
1751        return Err(ParseError::GrammarError(format!(
1752            "Invalid multiline string: {}",
1753            s
1754        )));
1755    }
1756
1757    let content = &s[3..s.len() - 3];
1758
1759    // Escape special characters for JSON compatibility
1760    let escaped = content
1761        .replace('\\', "\\\\") // Backslash must be first
1762        .replace('"', "\\\"") // Double quotes
1763        .replace('\n', "\\n") // Newlines
1764        .replace('\r', "\\r") // Carriage returns
1765        .replace('\t', "\\t"); // Tabs
1766
1767    // Create a JSON string and parse it to handle escape sequences
1768    let json_string = format!("\"{}\"", escaped);
1769    match serde_json::from_str(&json_string) {
1770        Ok(unescaped) => Ok(unescaped),
1771        Err(e) => Err(ParseError::GrammarError(format!(
1772            "Invalid multiline string escape sequences in '{}': {}",
1773            s, e
1774        ))),
1775    }
1776}
1777
1778/// Parse identifier
1779fn parse_identifier(pair: Pair<Rule>) -> ParseResult<String> {
1780    Ok(pair.as_str().to_string())
1781}
1782
1783/// Parse semantic version (validated by grammar)
1784fn parse_version(pair: Pair<Rule>) -> ParseResult<String> {
1785    Ok(pair.as_str().to_string())
1786}
1787
1788/// Parse number as i32
1789fn parse_number(pair: Pair<Rule>) -> ParseResult<i32> {
1790    pair.as_str()
1791        .parse()
1792        .map_err(|_| ParseError::InvalidQuantity(format!("Invalid number: {}", pair.as_str())))
1793}
1794
1795/// Parse number as Decimal
1796fn parse_decimal(pair: Pair<Rule>) -> ParseResult<Decimal> {
1797    pair.as_str()
1798        .parse()
1799        .map_err(|_| ParseError::InvalidQuantity(format!("Invalid decimal: {}", pair.as_str())))
1800}
1801
1802/// Parse group_by expression
1803fn parse_group_by_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1804    let mut inner = pair.into_inner();
1805
1806    let variable_pair = inner
1807        .next()
1808        .ok_or_else(|| ParseError::GrammarError("Expected variable in group_by".to_string()))?;
1809    let variable = parse_identifier(variable_pair)?;
1810
1811    let collection_pair = inner
1812        .next()
1813        .ok_or_else(|| ParseError::GrammarError("Expected collection in group_by".to_string()))?;
1814    let collection_name = parse_collection(collection_pair)?;
1815    let collection = Box::new(Expression::Variable(collection_name));
1816
1817    let next_pair = inner.next().ok_or_else(|| {
1818        ParseError::GrammarError("Expected key or where clause in group_by".to_string())
1819    })?;
1820
1821    // Collect remaining pairs to determine structure without cloning
1822    let remaining: Vec<Pair<Rule>> = std::iter::once(next_pair).chain(inner).collect();
1823
1824    let (filter_expr, key_expr, condition_expr) = match remaining.len() {
1825        3 => {
1826            let mut iter = remaining.into_iter();
1827            let filter = parse_expression(iter.next().ok_or_else(|| {
1828                ParseError::GrammarError("Expected filter expression in group_by".to_string())
1829            })?)?;
1830            let key = parse_expression(iter.next().ok_or_else(|| {
1831                ParseError::GrammarError("Expected key expression in group_by".to_string())
1832            })?)?;
1833            let condition = parse_expression(iter.next().ok_or_else(|| {
1834                ParseError::GrammarError("Expected condition expression in group_by".to_string())
1835            })?)?;
1836            (Some(filter), key, condition)
1837        }
1838        2 => {
1839            let mut iter = remaining.into_iter();
1840            let key = parse_expression(iter.next().ok_or_else(|| {
1841                ParseError::GrammarError("Expected key expression in group_by".to_string())
1842            })?)?;
1843            let condition = parse_expression(iter.next().ok_or_else(|| {
1844                ParseError::GrammarError("Expected condition expression in group_by".to_string())
1845            })?)?;
1846            (None, key, condition)
1847        }
1848        other => {
1849            return Err(ParseError::GrammarError(format!(
1850                "Unexpected number of expressions in group_by: {}",
1851                other
1852            )))
1853        }
1854    };
1855
1856    Ok(Expression::GroupBy {
1857        variable,
1858        collection,
1859        filter: filter_expr.map(Box::new),
1860        key: Box::new(key_expr),
1861        condition: Box::new(condition_expr),
1862    })
1863}
1864
1865/// Parse window clause
1866fn parse_window_clause(pair: Pair<Rule>) -> ParseResult<WindowSpec> {
1867    let mut inner = pair.into_inner();
1868
1869    let duration_pair = inner.next().ok_or_else(|| {
1870        ParseError::GrammarError("Expected duration in window clause".to_string())
1871    })?;
1872    let duration_i32 = parse_number(duration_pair)?;
1873    if duration_i32 < 0 {
1874        return Err(ParseError::InvalidQuantity(
1875            "Window duration must be non-negative".to_string(),
1876        ));
1877    }
1878    let duration = duration_i32 as u64;
1879
1880    let unit_pair = inner
1881        .next()
1882        .ok_or_else(|| ParseError::GrammarError("Expected unit in window clause".to_string()))?;
1883    let unit = parse_string_literal(unit_pair)?;
1884
1885    Ok(WindowSpec { duration, unit })
1886}
1887
1888fn expression_kind(expr: &Expression) -> &'static str {
1889    match expr {
1890        Expression::Literal(_) => "literal",
1891        Expression::QuantityLiteral { .. } => "quantity_literal",
1892        Expression::TimeLiteral(_) => "time_literal",
1893        Expression::IntervalLiteral { .. } => "interval_literal",
1894        Expression::Variable(_) => "variable",
1895        Expression::GroupBy { .. } => "group_by",
1896        Expression::Binary { .. } => "binary",
1897        Expression::Unary { .. } => "unary",
1898        Expression::Cast { .. } => "cast",
1899        Expression::Quantifier { .. } => "quantifier",
1900        Expression::MemberAccess { .. } => "member_access",
1901        Expression::Aggregation { .. } => "aggregation",
1902        Expression::AggregationComprehension { .. } => "aggregation_comprehension",
1903    }
1904}
1905
1906/// Convert an Expression to a JSON Value for instance fields
1907fn expression_to_json(expr: &Expression) -> ParseResult<JsonValue> {
1908    match expr {
1909        Expression::Literal(v) => Ok(v.clone()),
1910        Expression::Variable(name) => Ok(JsonValue::String(name.clone())),
1911        Expression::QuantityLiteral { value, unit } => Ok(json!({
1912            "value": value.to_string(),
1913            "unit": unit
1914        })),
1915        Expression::TimeLiteral(timestamp) => Ok(JsonValue::String(timestamp.clone())),
1916        _ => Err(ParseError::UnsupportedExpression {
1917            kind: expression_kind(expr).to_string(),
1918            span: None,
1919        }),
1920    }
1921}
1922
1923/// Parse mapping declaration
1924fn parse_mapping(pair: Pair<Rule>) -> ParseResult<AstNode> {
1925    let mut inner = pair.into_inner();
1926
1927    let name = parse_string_literal(
1928        inner
1929            .next()
1930            .ok_or_else(|| ParseError::GrammarError("Expected mapping name".to_string()))?,
1931    )?;
1932
1933    let target_pair = inner
1934        .next()
1935        .ok_or_else(|| ParseError::GrammarError("Expected target format".to_string()))?;
1936    let target = parse_target_format(target_pair)?;
1937
1938    let mut rules = Vec::new();
1939
1940    for rule_pair in inner {
1941        if rule_pair.as_rule() == Rule::mapping_rule {
1942            rules.push(parse_mapping_rule(rule_pair)?);
1943        }
1944    }
1945
1946    Ok(AstNode::MappingDecl {
1947        name,
1948        target,
1949        rules,
1950    })
1951}
1952
1953fn parse_target_format(pair: Pair<Rule>) -> ParseResult<TargetFormat> {
1954    match pair.as_str().to_lowercase().as_str() {
1955        "calm" => Ok(TargetFormat::Calm),
1956        "kg" => Ok(TargetFormat::Kg),
1957        "sbvr" => Ok(TargetFormat::Sbvr),
1958        "protobuf" | "proto" => Ok(TargetFormat::Protobuf),
1959        _ => Err(ParseError::GrammarError(format!(
1960            "Unknown target format: {}",
1961            pair.as_str()
1962        ))),
1963    }
1964}
1965
1966fn parse_mapping_rule(pair: Pair<Rule>) -> ParseResult<MappingRule> {
1967    let mut inner = pair.into_inner();
1968
1969    let primitive_type = inner
1970        .next()
1971        .ok_or_else(|| ParseError::GrammarError("Expected primitive type".to_string()))?
1972        .as_str()
1973        .to_string();
1974
1975    let primitive_name = parse_string_literal(
1976        inner
1977            .next()
1978            .ok_or_else(|| ParseError::GrammarError("Expected primitive name".to_string()))?,
1979    )?;
1980
1981    let target_structure = inner
1982        .next()
1983        .ok_or_else(|| ParseError::GrammarError("Expected target structure".to_string()))?;
1984
1985    let mut target_inner = target_structure.into_inner();
1986    let target_type = parse_identifier(
1987        target_inner
1988            .next()
1989            .ok_or_else(|| ParseError::GrammarError("Expected target type".to_string()))?,
1990    )?;
1991
1992    let mut fields = HashMap::new();
1993    for field_pair in target_inner {
1994        if field_pair.as_rule() == Rule::mapping_field {
1995            let mut field_inner = field_pair.into_inner();
1996            let key = parse_identifier(
1997                field_inner
1998                    .next()
1999                    .ok_or_else(|| ParseError::GrammarError("Expected field key".to_string()))?,
2000            )?;
2001            let value_pair = field_inner
2002                .next()
2003                .ok_or_else(|| ParseError::GrammarError("Expected field value".to_string()))?;
2004
2005            let value = match value_pair.as_rule() {
2006                Rule::string_literal => JsonValue::String(parse_string_literal(value_pair)?),
2007                Rule::boolean => JsonValue::Bool(value_pair.as_str().eq_ignore_ascii_case("true")),
2008                Rule::object_literal => parse_object_literal(value_pair)?,
2009                _ => {
2010                    return Err(ParseError::GrammarError(
2011                        "Unexpected mapping field value".to_string(),
2012                    ))
2013                }
2014            };
2015            fields.insert(key, value);
2016        }
2017    }
2018
2019    Ok(MappingRule {
2020        primitive_type,
2021        primitive_name,
2022        target_type,
2023        fields,
2024    })
2025}
2026
2027fn parse_object_literal(pair: Pair<Rule>) -> ParseResult<JsonValue> {
2028    let mut map = serde_json::Map::new();
2029    let mut inner = pair.into_inner();
2030    while let Some(key_pair) = inner.next() {
2031        let key = parse_string_literal(key_pair)?;
2032        let value_pair = inner.next().ok_or_else(|| {
2033            ParseError::GrammarError("Expected value in object literal".to_string())
2034        })?;
2035        let value = parse_annotation_value(value_pair)?;
2036        map.insert(key, value);
2037    }
2038    Ok(JsonValue::Object(map))
2039}
2040
2041fn parse_annotation_value(pair: Pair<Rule>) -> ParseResult<JsonValue> {
2042    match pair.as_rule() {
2043        Rule::annotation_value => {
2044            let mut inner = pair.into_inner();
2045            let value_pair = inner
2046                .next()
2047                .ok_or_else(|| ParseError::GrammarError("Expected annotation value".to_string()))?;
2048            parse_annotation_value(value_pair)
2049        }
2050        Rule::string_literal => Ok(JsonValue::String(parse_string_literal(pair)?)),
2051        Rule::string_array => {
2052            let mut values = Vec::new();
2053            for item in pair.into_inner() {
2054                values.push(JsonValue::String(parse_string_literal(item)?));
2055            }
2056            Ok(JsonValue::Array(values))
2057        }
2058        Rule::boolean => Ok(JsonValue::Bool(pair.as_str().eq_ignore_ascii_case("true"))),
2059        Rule::number => {
2060            let d = parse_decimal(pair)?;
2061            let f = d.to_f64().ok_or_else(|| {
2062                ParseError::InvalidQuantity(format!(
2063                    "Decimal value {} cannot be represented as f64",
2064                    d
2065                ))
2066            })?;
2067            if !f.is_finite() {
2068                return Err(ParseError::InvalidQuantity(format!(
2069                    "Decimal value {} converts to non-finite f64",
2070                    d
2071                )));
2072            }
2073            let num = serde_json::Number::from_f64(f).ok_or_else(|| {
2074                ParseError::InvalidQuantity(format!("Cannot create JSON Number from decimal {}", d))
2075            })?;
2076            Ok(JsonValue::Number(num))
2077        }
2078        Rule::object_literal => parse_object_literal(pair),
2079        _ => Err(ParseError::GrammarError(format!(
2080            "Unexpected annotation value: {}",
2081            pair.as_str()
2082        ))),
2083    }
2084}
2085
2086/// Parse projection declaration
2087fn parse_projection(pair: Pair<Rule>) -> ParseResult<AstNode> {
2088    let mut inner = pair.into_inner();
2089
2090    let name = parse_string_literal(
2091        inner
2092            .next()
2093            .ok_or_else(|| ParseError::GrammarError("Expected projection name".to_string()))?,
2094    )?;
2095
2096    let target_pair = inner
2097        .next()
2098        .ok_or_else(|| ParseError::GrammarError("Expected target format".to_string()))?;
2099    let target = parse_target_format(target_pair)?;
2100
2101    let mut overrides = Vec::new();
2102
2103    for rule_pair in inner {
2104        if rule_pair.as_rule() == Rule::projection_rule {
2105            overrides.push(parse_projection_rule(rule_pair)?);
2106        }
2107    }
2108
2109    Ok(AstNode::ProjectionDecl {
2110        name,
2111        target,
2112        overrides,
2113    })
2114}
2115
2116fn parse_projection_rule(pair: Pair<Rule>) -> ParseResult<ProjectionOverride> {
2117    let mut inner = pair.into_inner();
2118
2119    let primitive_type = inner
2120        .next()
2121        .ok_or_else(|| ParseError::GrammarError("Expected primitive type".to_string()))?
2122        .as_str()
2123        .to_string();
2124
2125    let primitive_name = parse_string_literal(
2126        inner
2127            .next()
2128            .ok_or_else(|| ParseError::GrammarError("Expected primitive name".to_string()))?,
2129    )?;
2130
2131    let mut fields = HashMap::new();
2132    for field_pair in inner {
2133        if field_pair.as_rule() == Rule::projection_field {
2134            let mut field_inner = field_pair.into_inner();
2135            let key = parse_identifier(
2136                field_inner
2137                    .next()
2138                    .ok_or_else(|| ParseError::GrammarError("Expected field key".to_string()))?,
2139            )?;
2140            let value_pair = field_inner
2141                .next()
2142                .ok_or_else(|| ParseError::GrammarError("Expected field value".to_string()))?;
2143
2144            let value = match value_pair.as_rule() {
2145                Rule::string_literal => JsonValue::String(parse_string_literal(value_pair)?),
2146                Rule::property_mapping => parse_property_mapping(value_pair)?,
2147                _ => {
2148                    return Err(ParseError::GrammarError(
2149                        "Unexpected projection field value".to_string(),
2150                    ))
2151                }
2152            };
2153            fields.insert(key, value);
2154        }
2155    }
2156
2157    Ok(ProjectionOverride {
2158        primitive_type,
2159        primitive_name,
2160        fields,
2161    })
2162}
2163
2164fn parse_property_mapping(pair: Pair<Rule>) -> ParseResult<JsonValue> {
2165    let mut map = serde_json::Map::new();
2166    let mut inner = pair.into_inner();
2167    while let Some(key_pair) = inner.next() {
2168        let key = parse_string_literal(key_pair)?;
2169        let value_pair = inner.next().ok_or_else(|| {
2170            ParseError::GrammarError("Expected value in property mapping".to_string())
2171        })?;
2172        let value = parse_string_literal(value_pair)?;
2173        map.insert(key, JsonValue::String(value));
2174    }
2175    Ok(JsonValue::Object(map))
2176}
2177
2178fn unwrap_export(spanned: &Spanned<AstNode>) -> &AstNode {
2179    match &spanned.node {
2180        AstNode::Export(inner) => &inner.node,
2181        other => other,
2182    }
2183}
2184
2185/// Convert AST to Graph
2186pub fn ast_to_graph(ast: Ast) -> ParseResult<Graph> {
2187    ast_to_graph_with_options(ast, &ParseOptions::default())
2188}
2189
2190pub fn ast_to_graph_with_options(mut ast: Ast, options: &ParseOptions) -> ParseResult<Graph> {
2191    use crate::parser::profiles::ProfileRegistry;
2192    let registry = ProfileRegistry::global();
2193    let active_profile = ast
2194        .metadata
2195        .profile
2196        .clone()
2197        .or_else(|| options.active_profile.clone())
2198        .unwrap_or_else(|| "default".to_string());
2199    ast.metadata.profile.get_or_insert(active_profile.clone());
2200
2201    if registry.get(&active_profile).is_none() {
2202        let available = registry.list_names().join(", ");
2203        let message = format!(
2204            "Unknown profile: '{}'. Available profiles: {}",
2205            active_profile, available
2206        );
2207        if options.tolerate_profile_warnings {
2208            log::warn!("{}", message);
2209        } else {
2210            return Err(ParseError::Validation(message));
2211        }
2212    }
2213
2214    let mut graph = Graph::new();
2215    let mut entity_map = HashMap::new();
2216    let mut role_map = HashMap::new();
2217    let mut resource_map = HashMap::new();
2218    let mut relation_map = HashMap::new();
2219
2220    let default_namespace = ast
2221        .metadata
2222        .namespace
2223        .clone()
2224        .or_else(|| options.default_namespace.clone())
2225        .unwrap_or_else(|| "default".to_string());
2226
2227    // First pass: Register dimensions and units
2228    {
2229        use crate::units::{Dimension, Unit, UnitError, UnitRegistry};
2230        let registry = UnitRegistry::global();
2231        let mut registry = registry.write().map_err(|e| {
2232            ParseError::GrammarError(format!("Failed to lock unit registry: {}", e))
2233        })?;
2234
2235        for node in &ast.declarations {
2236            let node = unwrap_export(node);
2237            match node {
2238                AstNode::Dimension { name } => {
2239                    let dim = Dimension::parse(name);
2240                    registry.register_dimension(dim);
2241                }
2242                AstNode::UnitDeclaration {
2243                    symbol,
2244                    dimension,
2245                    factor,
2246                    base_unit,
2247                } => {
2248                    let dim = Dimension::parse(dimension);
2249                    let unit = Unit::new(
2250                        symbol.clone(),
2251                        symbol.clone(),
2252                        dim,
2253                        *factor,
2254                        base_unit.clone(),
2255                    );
2256                    match registry.get_unit(symbol) {
2257                        Ok(existing) => {
2258                            if existing != &unit {
2259                                return Err(ParseError::GrammarError(format!(
2260                                    "Conflicting unit '{}' already registered (existing: dimension={}, base_factor={}, base_unit={}; new: dimension={}, base_factor={}, base_unit={})",
2261                                    symbol,
2262                                    existing.dimension(),
2263                                    existing.base_factor(),
2264                                    existing.base_unit(),
2265                                    unit.dimension(),
2266                                    unit.base_factor(),
2267                                    unit.base_unit(),
2268                                )));
2269                            }
2270                        }
2271                        Err(UnitError::UnitNotFound(_)) => {
2272                            registry.register(unit).map_err(|e| {
2273                                ParseError::GrammarError(format!("Failed to register unit: {}", e))
2274                            })?;
2275                        }
2276                        Err(err) => {
2277                            return Err(ParseError::GrammarError(format!(
2278                                "Failed to inspect unit '{}': {}",
2279                                symbol, err
2280                            )));
2281                        }
2282                    }
2283                }
2284                _ => {}
2285            }
2286        }
2287    }
2288
2289    // Register patterns with eager regex validation
2290    for node in &ast.declarations {
2291        let node = unwrap_export(node);
2292        if let AstNode::Pattern { name, regex } = node {
2293            let namespace = default_namespace.clone();
2294            let pattern = Pattern::new(name.clone(), namespace, regex.clone())
2295                .map_err(ParseError::GrammarError)?;
2296
2297            graph
2298                .add_pattern(pattern)
2299                .map_err(ParseError::GrammarError)?;
2300        }
2301    }
2302
2303    // Register concept changes
2304    for node in &ast.declarations {
2305        let node = unwrap_export(node);
2306        if let AstNode::ConceptChange {
2307            name,
2308            from_version,
2309            to_version,
2310            migration_policy,
2311            breaking_change,
2312        } = node
2313        {
2314            let change = ConceptChange::new(
2315                name.clone(),
2316                from_version.clone(),
2317                to_version.clone(),
2318                migration_policy.clone(),
2319                *breaking_change,
2320            );
2321            graph.add_concept_change(change).map_err(|e| {
2322                ParseError::GrammarError(format!("Failed to add concept change: {}", e))
2323            })?;
2324        }
2325    }
2326
2327    // Second pass: Add roles, entities, and resources
2328    for node in &ast.declarations {
2329        let node = unwrap_export(node);
2330        match node {
2331            AstNode::Role { name, domain } => {
2332                if role_map.contains_key(name) {
2333                    return Err(ParseError::duplicate_declaration_no_loc(format!(
2334                        "Role '{}' already declared",
2335                        name
2336                    )));
2337                }
2338
2339                let namespace = domain.as_ref().unwrap_or(&default_namespace).clone();
2340                let role = Role::new_with_namespace(name.clone(), namespace);
2341                let role_id = role.id().clone();
2342                graph
2343                    .add_role(role)
2344                    .map_err(|e| ParseError::GrammarError(format!("Failed to add role: {}", e)))?;
2345                role_map.insert(name.clone(), role_id);
2346            }
2347            AstNode::Entity {
2348                name,
2349                domain,
2350                version,
2351                annotations,
2352            } => {
2353                if entity_map.contains_key(name) {
2354                    return Err(ParseError::duplicate_declaration_no_loc(format!(
2355                        "Entity '{}' already declared",
2356                        name
2357                    )));
2358                }
2359
2360                let namespace = domain.as_ref().unwrap_or(&default_namespace).clone();
2361                let mut entity = Entity::new_with_namespace(name.clone(), namespace);
2362
2363                if let Some(v_str) = version {
2364                    let sem_ver = SemanticVersion::parse(v_str).map_err(|e| {
2365                        ParseError::GrammarError(format!(
2366                            "Invalid entity version '{}': {}",
2367                            v_str, e
2368                        ))
2369                    })?;
2370                    entity = entity.with_version(sem_ver);
2371                }
2372
2373                if let Some(replaces_val) = annotations.get("replaces") {
2374                    if let Some(replaces_str) = replaces_val.as_str() {
2375                        entity = entity.with_replaces(replaces_str.to_string());
2376                    }
2377                }
2378
2379                if let Some(changes_val) = annotations.get("changes") {
2380                    if let Some(changes_arr) = changes_val.as_array() {
2381                        let changes: Vec<String> = changes_arr
2382                            .iter()
2383                            .filter_map(|v| v.as_str().map(|s| s.to_string()))
2384                            .collect();
2385                        entity = entity.with_changes(changes);
2386                    }
2387                }
2388
2389                let entity_id = entity.id().clone();
2390                graph.add_entity(entity).map_err(|e| {
2391                    ParseError::GrammarError(format!("Failed to add entity: {}", e))
2392                })?;
2393                entity_map.insert(name.clone(), entity_id);
2394            }
2395            AstNode::Resource {
2396                name,
2397                unit_name,
2398                domain,
2399                ..
2400            } => {
2401                if resource_map.contains_key(name) {
2402                    return Err(ParseError::duplicate_declaration_no_loc(format!(
2403                        "Resource '{}' already declared",
2404                        name
2405                    )));
2406                }
2407
2408                let namespace = domain.as_ref().unwrap_or(&default_namespace).clone();
2409                let unit = unit_from_string(unit_name.as_deref().unwrap_or("units"));
2410                let resource = Resource::new_with_namespace(name.clone(), unit, namespace);
2411                let resource_id = resource.id().clone();
2412                graph.add_resource(resource).map_err(|e| {
2413                    ParseError::GrammarError(format!("Failed to add resource: {}", e))
2414                })?;
2415                resource_map.insert(name.clone(), resource_id);
2416            }
2417            _ => {}
2418        }
2419    }
2420
2421    // Third pass: Add flows
2422    for node in &ast.declarations {
2423        let node = unwrap_export(node);
2424        if let AstNode::Flow {
2425            resource_name,
2426            from_entity,
2427            to_entity,
2428            quantity,
2429            ..
2430        } = node
2431        {
2432            let from_id = entity_map
2433                .get(from_entity)
2434                .ok_or_else(|| ParseError::undefined_entity_no_loc(from_entity))?;
2435
2436            let to_id = entity_map
2437                .get(to_entity)
2438                .ok_or_else(|| ParseError::undefined_entity_no_loc(to_entity))?;
2439
2440            let resource_id = resource_map
2441                .get(resource_name)
2442                .ok_or_else(|| ParseError::undefined_resource_no_loc(resource_name))?;
2443
2444            let qty = quantity.map(Decimal::from).unwrap_or(Decimal::ZERO);
2445            let flow = Flow::new(resource_id.clone(), from_id.clone(), to_id.clone(), qty);
2446
2447            graph
2448                .add_flow(flow)
2449                .map_err(|e| ParseError::GrammarError(format!("Failed to add flow: {}", e)))?;
2450        }
2451    }
2452
2453    // Fourth pass: Add relations
2454    for node in &ast.declarations {
2455        let node = unwrap_export(node);
2456        if let AstNode::Relation {
2457            name,
2458            subject_role,
2459            predicate,
2460            object_role,
2461            via_flow,
2462        } = node
2463        {
2464            if relation_map.contains_key(name) {
2465                return Err(ParseError::duplicate_declaration_no_loc(format!(
2466                    "Relation '{}' already declared",
2467                    name
2468                )));
2469            }
2470
2471            let subject_id = role_map.get(subject_role).ok_or_else(|| {
2472                ParseError::GrammarError(format!("Undefined subject role '{}'", subject_role))
2473            })?;
2474
2475            let object_id = role_map.get(object_role).ok_or_else(|| {
2476                ParseError::GrammarError(format!("Undefined object role '{}'", object_role))
2477            })?;
2478
2479            let via_flow_id = if let Some(flow_name) = via_flow {
2480                Some(
2481                    resource_map
2482                        .get(flow_name)
2483                        .cloned()
2484                        .ok_or_else(|| ParseError::undefined_resource_no_loc(flow_name))?,
2485                )
2486            } else {
2487                None
2488            };
2489
2490            let relation = RelationType::new(
2491                name.clone(),
2492                default_namespace.clone(),
2493                subject_id.clone(),
2494                predicate.clone(),
2495                object_id.clone(),
2496                via_flow_id,
2497            );
2498
2499            let relation_id = relation.id().clone();
2500            graph.add_relation_type(relation).map_err(|e| {
2501                ParseError::GrammarError(format!("Failed to add relation '{}': {}", name, e))
2502            })?;
2503            relation_map.insert(name.clone(), relation_id);
2504        }
2505    }
2506
2507    // Instance pass: Add instances (after entities are created)
2508    for node in &ast.declarations {
2509        let node = unwrap_export(node);
2510        if let AstNode::Instance {
2511            name,
2512            entity_type,
2513            fields,
2514        } = node
2515        {
2516            let namespace = default_namespace.clone();
2517            let mut instance = crate::primitives::Instance::new_with_namespace(
2518                name.clone(),
2519                entity_type.clone(),
2520                namespace,
2521            );
2522
2523            // Evaluate and set fields
2524            for (field_name, field_expr) in fields {
2525                let value = expression_to_json(field_expr)?;
2526                instance.set_field(field_name.clone(), value);
2527            }
2528
2529            graph.add_entity_instance(instance).map_err(|e| {
2530                ParseError::GrammarError(format!("Failed to add entity instance '{}': {}", name, e))
2531            })?;
2532        }
2533    }
2534
2535    // Fifth pass: Add policies
2536    for node in &ast.declarations {
2537        let node = unwrap_export(node);
2538        if let AstNode::Policy {
2539            name,
2540            version,
2541            metadata,
2542            expression,
2543        } = node
2544        {
2545            let namespace = ast
2546                .metadata
2547                .namespace
2548                .as_ref()
2549                .cloned()
2550                .or_else(|| options.default_namespace.clone())
2551                .unwrap_or_else(|| "default".to_string());
2552
2553            let kind = metadata.kind.as_ref().map(|kind| match kind {
2554                PolicyKind::Constraint => CorePolicyKind::Constraint,
2555                PolicyKind::Derivation => CorePolicyKind::Derivation,
2556                PolicyKind::Obligation => CorePolicyKind::Obligation,
2557            });
2558
2559            let modality = metadata.modality.as_ref().map(|modality| match modality {
2560                PolicyModality::Obligation => CorePolicyModality::Obligation,
2561                PolicyModality::Prohibition => CorePolicyModality::Prohibition,
2562                PolicyModality::Permission => CorePolicyModality::Permission,
2563            });
2564
2565            let mut policy =
2566                Policy::new_with_namespace(name.clone(), namespace, expression.clone())
2567                    .with_metadata(
2568                        kind,
2569                        modality,
2570                        metadata.priority,
2571                        metadata.rationale.clone(),
2572                        metadata.tags.clone(),
2573                    );
2574
2575            let version_to_apply = version
2576                .as_ref()
2577                .cloned()
2578                .or_else(|| ast.metadata.version.clone());
2579
2580            if let Some(version_str) = version_to_apply {
2581                let semantic_version = SemanticVersion::parse(&version_str).map_err(|err| {
2582                    ParseError::GrammarError(format!(
2583                        "Invalid policy version '{}': {}",
2584                        version_str, err
2585                    ))
2586                })?;
2587                policy = policy.with_version(semantic_version);
2588            }
2589
2590            graph.add_policy(policy).map_err(|e| {
2591                ParseError::GrammarError(format!("Failed to add policy '{}': {}", name, e))
2592            })?;
2593        }
2594    }
2595
2596    // Sixth pass: Add metrics
2597    for node in &ast.declarations {
2598        let node = unwrap_export(node);
2599        if let AstNode::Metric {
2600            name,
2601            expression,
2602            metadata,
2603        } = node
2604        {
2605            let namespace = ast
2606                .metadata
2607                .namespace
2608                .as_ref()
2609                .cloned()
2610                .or_else(|| options.default_namespace.clone())
2611                .unwrap_or_else(|| "default".to_string());
2612
2613            let mut metric =
2614                crate::primitives::Metric::new(name.clone(), namespace, expression.clone());
2615
2616            if let Some(duration) = metadata.refresh_interval {
2617                metric = metric.with_refresh_interval(duration);
2618            }
2619
2620            if let Some(unit) = &metadata.unit {
2621                metric = metric.with_unit(unit.clone());
2622            }
2623
2624            if let Some(threshold) = metadata.threshold {
2625                metric = metric.with_threshold(threshold);
2626            }
2627
2628            if let Some(severity) = metadata.severity.clone() {
2629                metric = metric.with_severity(severity);
2630            }
2631
2632            if let Some(target) = metadata.target {
2633                metric = metric.with_target(target);
2634            }
2635
2636            if let Some(duration) = metadata.window {
2637                metric = metric.with_window(duration);
2638            }
2639
2640            graph.add_metric(metric).map_err(|e| {
2641                ParseError::GrammarError(format!("Failed to add metric '{}': {}", name, e))
2642            })?;
2643        }
2644    }
2645
2646    // Seventh pass: Add mappings
2647    for node in &ast.declarations {
2648        let node = unwrap_export(node);
2649        if let AstNode::MappingDecl {
2650            name,
2651            target,
2652            rules,
2653        } = node
2654        {
2655            let namespace = ast
2656                .metadata
2657                .namespace
2658                .clone()
2659                .or_else(|| options.default_namespace.clone())
2660                .unwrap_or_else(|| "default".to_string());
2661            let mapping = crate::primitives::MappingContract::new(
2662                crate::ConceptId::from_concept(&namespace, name),
2663                name.clone(),
2664                namespace,
2665                target.clone(),
2666                rules.clone(),
2667            );
2668            graph
2669                .add_mapping(mapping)
2670                .map_err(|e| ParseError::GrammarError(format!("Failed to add mapping: {}", e)))?;
2671        }
2672    }
2673
2674    // Eighth pass: Add projections
2675    for node in &ast.declarations {
2676        let node = unwrap_export(node);
2677        if let AstNode::ProjectionDecl {
2678            name,
2679            target,
2680            overrides,
2681        } = node
2682        {
2683            let namespace = ast
2684                .metadata
2685                .namespace
2686                .clone()
2687                .or_else(|| options.default_namespace.clone())
2688                .unwrap_or_else(|| "default".to_string());
2689            let projection = crate::primitives::ProjectionContract::new(
2690                crate::ConceptId::from_concept(&namespace, name),
2691                name.clone(),
2692                namespace,
2693                target.clone(),
2694                overrides.clone(),
2695            );
2696            graph.add_projection(projection).map_err(|e| {
2697                ParseError::GrammarError(format!("Failed to add projection: {}", e))
2698            })?;
2699        }
2700    }
2701
2702    Ok(graph)
2703}
2704
2705/// Parse metric declaration
2706fn parse_metric(pair: Pair<Rule>) -> ParseResult<AstNode> {
2707    let mut inner = pair.into_inner();
2708
2709    let name = parse_name(
2710        inner
2711            .next()
2712            .ok_or_else(|| ParseError::GrammarError("Expected metric name".to_string()))?,
2713    )?;
2714
2715    let mut metadata = MetricMetadata {
2716        refresh_interval: None,
2717        unit: None,
2718        threshold: None,
2719        severity: None,
2720        target: None,
2721        window: None,
2722    };
2723
2724    let mut expression_pair = None;
2725
2726    for part in inner {
2727        match part.as_rule() {
2728            Rule::metric_annotation => {
2729                let mut annotation_inner = part.into_inner();
2730                let key_pair = annotation_inner.next().ok_or_else(|| {
2731                    ParseError::GrammarError("Expected annotation key".to_string())
2732                })?;
2733
2734                match key_pair.as_rule() {
2735                    Rule::ma_refresh_interval => {
2736                        let value_pair = annotation_inner.next().ok_or_else(|| {
2737                            ParseError::GrammarError("Expected refresh interval value".to_string())
2738                        })?;
2739                        let value = parse_number_i64(value_pair)?;
2740                        let unit_pair = annotation_inner.next().ok_or_else(|| {
2741                            ParseError::GrammarError("Expected refresh interval unit".to_string())
2742                        })?;
2743                        let unit = parse_string_literal(unit_pair.clone())?;
2744                        let duration = parse_duration_with_unit(value, &unit, unit_pair.as_span())?;
2745                        metadata.refresh_interval = Some(duration);
2746                    }
2747                    Rule::ma_unit => {
2748                        let unit_pair = annotation_inner
2749                            .next()
2750                            .ok_or_else(|| ParseError::GrammarError("Expected unit".to_string()))?;
2751                        metadata.unit = Some(parse_string_literal(unit_pair)?);
2752                    }
2753                    Rule::ma_threshold => {
2754                        let value_pair = annotation_inner.next().ok_or_else(|| {
2755                            ParseError::GrammarError("Expected threshold value".to_string())
2756                        })?;
2757                        metadata.threshold = Some(parse_decimal(value_pair)?);
2758                    }
2759                    Rule::ma_severity => {
2760                        let severity_pair = annotation_inner.next().ok_or_else(|| {
2761                            ParseError::GrammarError("Expected severity".to_string())
2762                        })?;
2763                        let severity_str = parse_string_literal(severity_pair.clone())?;
2764                        let severity =
2765                            parse_severity_value(&severity_str, severity_pair.as_span())?;
2766                        metadata.severity = Some(severity);
2767                    }
2768                    Rule::ma_target => {
2769                        let value_pair = annotation_inner.next().ok_or_else(|| {
2770                            ParseError::GrammarError("Expected target value".to_string())
2771                        })?;
2772                        metadata.target = Some(parse_decimal(value_pair)?);
2773                    }
2774                    Rule::ma_window => {
2775                        let value_pair = annotation_inner.next().ok_or_else(|| {
2776                            ParseError::GrammarError("Expected window value".to_string())
2777                        })?;
2778                        let value = parse_number_i64(value_pair)?;
2779                        let unit_pair = annotation_inner.next().ok_or_else(|| {
2780                            ParseError::GrammarError("Expected window unit".to_string())
2781                        })?;
2782                        let unit = parse_string_literal(unit_pair.clone())?;
2783                        let duration = parse_duration_with_unit(value, &unit, unit_pair.as_span())?;
2784                        metadata.window = Some(duration);
2785                    }
2786                    _ => {
2787                        let (line, column) = key_pair.as_span().start_pos().line_col();
2788                        return Err(ParseError::GrammarError(format!(
2789                            "Unknown metric annotation '{}' at {}:{}",
2790                            key_pair.as_str(),
2791                            line,
2792                            column
2793                        )));
2794                    }
2795                }
2796            }
2797            Rule::expression => {
2798                expression_pair = Some(part);
2799            }
2800            _ => {}
2801        }
2802    }
2803
2804    let expression = parse_expression(
2805        expression_pair
2806            .ok_or_else(|| ParseError::GrammarError("Expected metric expression".to_string()))?,
2807    )?;
2808
2809    Ok(AstNode::Metric {
2810        name,
2811        expression,
2812        metadata,
2813    })
2814}
2815
2816fn parse_duration_with_unit(value: i64, unit: &str, span: Span<'_>) -> ParseResult<Duration> {
2817    let normalized_unit = unit.to_ascii_lowercase();
2818    let multiplier = match normalized_unit.as_str() {
2819        "second" => Some(1),
2820        "seconds" | "s" => Some(1),
2821        "minute" => Some(60),
2822        "minutes" | "m" => Some(60),
2823        "hour" => Some(60 * 60),
2824        "hours" | "h" => Some(60 * 60),
2825        "day" => Some(60 * 60 * 24),
2826        "days" | "d" => Some(60 * 60 * 24),
2827        _ => None,
2828    }
2829    .ok_or_else(|| {
2830        let (line, column) = span.start_pos().line_col();
2831        ParseError::GrammarError(format!(
2832            "Invalid duration unit '{}' at {}:{} (allowed: second(s)/s, minute(s)/m, hour(s)/h, day(s)/d)",
2833            unit, line, column
2834        ))
2835    })?;
2836
2837    let total_seconds = value.checked_mul(multiplier).ok_or_else(|| {
2838        let (line, column) = span.start_pos().line_col();
2839        ParseError::GrammarError(format!(
2840            "Duration overflow for value {} {} at {}:{}",
2841            value, unit, line, column
2842        ))
2843    })?;
2844
2845    Ok(Duration::seconds(total_seconds))
2846}
2847
2848fn parse_severity_value(value: &str, span: Span<'_>) -> ParseResult<Severity> {
2849    let normalized = value.to_ascii_lowercase();
2850    match normalized.as_str() {
2851        "info" => Ok(Severity::Info),
2852        "warning" => Ok(Severity::Warning),
2853        "error" => Ok(Severity::Error),
2854        "critical" => Ok(Severity::Critical),
2855        _ => {
2856            let (line, column) = span.start_pos().line_col();
2857            Err(ParseError::GrammarError(format!(
2858                "Unknown severity '{}' at {}:{} (expected one of: info, warning, error, critical)",
2859                value, line, column
2860            )))
2861        }
2862    }
2863}
2864
2865fn parse_number_i64(pair: Pair<Rule>) -> ParseResult<i64> {
2866    let s = pair.as_str();
2867    s.parse::<i64>()
2868        .map_err(|_| ParseError::GrammarError(format!("Invalid integer: {}", s)))
2869}