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#[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#[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#[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#[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#[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#[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
237pub fn parse_source(source: &str) -> ParseResult<Ast> {
239 let pairs = SeaParser::parse(Rule::program, source)?;
240 build_ast(pairs)
241}
242
243fn 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
278fn 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
319fn 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 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
428fn 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
440fn 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
475fn 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 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 _ => {}
535 }
536 }
537 Rule::in_keyword => {
538 }
540 Rule::identifier => {
541 domain = Some(parse_identifier(part)?);
543 }
544 _ => {}
545 }
546 }
547
548 Ok(AstNode::Entity {
549 name,
550 version,
551 annotations,
552 domain,
553 })
554}
555
556fn parse_concept_change(pair: Pair<Rule>) -> ParseResult<AstNode> {
558 let mut inner = pair.into_inner();
559
560 let name =
561 parse_name(inner.next().ok_or_else(|| {
562 ParseError::GrammarError("Expected concept change name".to_string())
563 })?)?;
564
565 let mut from_version = String::new();
566 let mut to_version = String::new();
567 let mut migration_policy = String::new();
568 let mut breaking_change = false;
569
570 for part in inner {
571 if part.as_rule() == Rule::concept_change_annotation {
572 let mut annotation_inner = part.into_inner();
573
574 let key_pair = annotation_inner
575 .next()
576 .ok_or_else(|| ParseError::GrammarError("Expected annotation key".to_string()))?;
577 let value_pair = annotation_inner
578 .next()
579 .ok_or_else(|| ParseError::GrammarError("Expected annotation value".to_string()))?;
580
581 match key_pair.as_rule() {
582 Rule::cc_from_version => from_version = parse_version(value_pair)?,
583 Rule::cc_to_version => to_version = parse_version(value_pair)?,
584 Rule::cc_migration_policy => migration_policy = parse_identifier(value_pair)?,
585 Rule::cc_breaking_change => {
586 breaking_change = value_pair.as_str() == "true";
587 }
588 _ => {}
589 }
590 }
591 }
592
593 if from_version.is_empty() {
594 return Err(ParseError::GrammarError(
595 "Missing cc_from_version annotation".to_string(),
596 ));
597 }
598
599 if to_version.is_empty() {
600 return Err(ParseError::GrammarError(
601 "Missing cc_to_version annotation".to_string(),
602 ));
603 }
604
605 if migration_policy.is_empty() {
606 return Err(ParseError::GrammarError(
607 "Missing cc_migration_policy annotation".to_string(),
608 ));
609 }
610
611 Ok(AstNode::ConceptChange {
612 name,
613 from_version,
614 to_version,
615 migration_policy,
616 breaking_change,
617 })
618}
619
620fn parse_resource(pair: Pair<Rule>) -> ParseResult<AstNode> {
622 let mut inner = pair.into_inner();
623
624 let name = parse_name(
625 inner
626 .next()
627 .ok_or_else(|| ParseError::GrammarError("Expected resource name".to_string()))?,
628 )?;
629
630 let mut annotations = HashMap::new();
631 let mut unit_name = None;
632 let mut domain = None;
633 let mut saw_in_keyword = false;
634
635 for part in inner {
636 match part.as_rule() {
637 Rule::resource_annotation => {
638 let mut annotation_inner = part.into_inner();
639 let key_pair = annotation_inner
640 .next()
641 .ok_or_else(|| ParseError::GrammarError("Empty annotation".to_string()))?;
642
643 match key_pair.as_rule() {
644 Rule::ea_replaces => {
645 let target_name =
646 parse_name(annotation_inner.next().ok_or_else(|| {
647 ParseError::GrammarError("Expected name in replaces".to_string())
648 })?)?;
649 let mut target_version = None;
651 if let Some(next) = annotation_inner.next() {
652 if next.as_rule() == Rule::version {
653 target_version = Some(next.as_str().to_string());
654 }
655 }
656
657 let value = if let Some(v) = target_version {
658 format!("{} v{}", target_name, v)
659 } else {
660 target_name
661 };
662 annotations.insert("replaces".to_string(), JsonValue::String(value));
663 }
664 Rule::ea_changes => {
665 let array_pair = annotation_inner.next().ok_or_else(|| {
666 ParseError::GrammarError("Expected string array in changes".to_string())
667 })?;
668 let mut changes = Vec::new();
669 for item in array_pair.into_inner() {
670 changes.push(parse_string_literal(item)?);
671 }
672 annotations.insert(
673 "changes".to_string(),
674 JsonValue::Array(changes.into_iter().map(JsonValue::String).collect()),
675 );
676 }
677 _ => {}
678 }
679 }
680 Rule::in_keyword => {
681 saw_in_keyword = true;
683 }
684 Rule::identifier => {
685 if saw_in_keyword {
687 domain = Some(parse_identifier(part)?);
688 saw_in_keyword = false; } else {
690 unit_name = Some(parse_identifier(part)?);
691 }
692 }
693 _ => {}
694 }
695 }
696
697 Ok(AstNode::Resource {
698 name,
699 annotations,
700 unit_name,
701 domain,
702 })
703}
704
705fn parse_flow(pair: Pair<Rule>) -> ParseResult<AstNode> {
707 let mut inner = pair.into_inner();
708
709 let resource_name = parse_string_literal(
710 inner
711 .next()
712 .ok_or_else(|| ParseError::GrammarError("Expected resource name".to_string()))?,
713 )?;
714
715 let mut annotations = HashMap::new();
716 let mut from_entity = None;
717 let mut to_entity = None;
718 let mut quantity = None;
719
720 for part in inner {
721 match part.as_rule() {
722 Rule::flow_annotation => {
723 let mut annotation_inner = part.into_inner();
724 let key_pair = annotation_inner
725 .next()
726 .ok_or_else(|| ParseError::GrammarError("Empty annotation".to_string()))?;
727
728 match key_pair.as_rule() {
729 Rule::ea_replaces => {
730 let target_name =
731 parse_name(annotation_inner.next().ok_or_else(|| {
732 ParseError::GrammarError("Expected name in replaces".to_string())
733 })?)?;
734 let mut target_version = None;
736 if let Some(next) = annotation_inner.next() {
737 if next.as_rule() == Rule::version {
738 target_version = Some(next.as_str().to_string());
739 }
740 }
741
742 let value = if let Some(v) = target_version {
743 format!("{} v{}", target_name, v)
744 } else {
745 target_name
746 };
747 annotations.insert("replaces".to_string(), JsonValue::String(value));
748 }
749 Rule::ea_changes => {
750 let array_pair = annotation_inner.next().ok_or_else(|| {
751 ParseError::GrammarError("Expected string array in changes".to_string())
752 })?;
753 let mut changes = Vec::new();
754 for item in array_pair.into_inner() {
755 changes.push(parse_string_literal(item)?);
756 }
757 annotations.insert(
758 "changes".to_string(),
759 JsonValue::Array(changes.into_iter().map(JsonValue::String).collect()),
760 );
761 }
762 _ => {}
763 }
764 }
765 Rule::string_literal => {
766 let parsed = parse_string_literal(part)?;
768 if from_entity.is_none() {
769 from_entity = Some(parsed);
770 } else if to_entity.is_none() {
771 to_entity = Some(parsed);
772 }
773 }
774 Rule::number => {
775 quantity = Some(parse_number(part)?);
776 }
777 _ => {}
778 }
779 }
780
781 Ok(AstNode::Flow {
782 resource_name,
783 annotations,
784 from_entity: from_entity
785 .ok_or_else(|| ParseError::GrammarError("Expected from entity".to_string()))?,
786 to_entity: to_entity
787 .ok_or_else(|| ParseError::GrammarError("Expected to entity".to_string()))?,
788 quantity,
789 })
790}
791
792fn parse_pattern(pair: Pair<Rule>) -> ParseResult<AstNode> {
794 let mut inner = pair.into_inner();
795
796 let name = parse_name(
797 inner
798 .next()
799 .ok_or_else(|| ParseError::GrammarError("Expected pattern name".to_string()))?,
800 )?;
801
802 let regex_literal = inner
803 .next()
804 .ok_or_else(|| ParseError::GrammarError("Expected regex for pattern".to_string()))?;
805 let regex = parse_string_literal(regex_literal)?;
806
807 Ok(AstNode::Pattern { name, regex })
808}
809
810fn parse_role(pair: Pair<Rule>) -> ParseResult<AstNode> {
812 let mut inner = pair.into_inner();
813
814 let name = parse_name(
815 inner
816 .next()
817 .ok_or_else(|| ParseError::GrammarError("Expected role name".to_string()))?,
818 )?;
819
820 let domain = if let Some(domain_pair) = inner.next() {
821 Some(parse_identifier(domain_pair)?)
822 } else {
823 None
824 };
825
826 Ok(AstNode::Role { name, domain })
827}
828
829fn parse_relation(pair: Pair<Rule>) -> ParseResult<AstNode> {
831 let mut inner = pair.into_inner();
832
833 let name = parse_name(
834 inner
835 .next()
836 .ok_or_else(|| ParseError::GrammarError("Expected relation name".to_string()))?,
837 )?;
838
839 let subject_literal = inner
840 .next()
841 .ok_or_else(|| ParseError::GrammarError("Expected subject role in relation".to_string()))?;
842 let subject_role = parse_string_literal(subject_literal)?;
843
844 let predicate_literal = inner
845 .next()
846 .ok_or_else(|| ParseError::GrammarError("Expected predicate in relation".to_string()))?;
847 let predicate = parse_string_literal(predicate_literal)?;
848
849 let object_literal = inner
850 .next()
851 .ok_or_else(|| ParseError::GrammarError("Expected object role in relation".to_string()))?;
852 let object_role = parse_string_literal(object_literal)?;
853
854 let via_flow = if let Some(via_pair) = inner.next() {
855 match via_pair.as_rule() {
856 Rule::string_literal => Some(parse_string_literal(via_pair)?),
857 _ => {
858 let mut via_inner = via_pair.into_inner();
859 let flow_literal = via_inner.next().ok_or_else(|| {
860 ParseError::GrammarError("Expected flow name after via".to_string())
861 })?;
862 Some(parse_string_literal(flow_literal)?)
863 }
864 }
865 } else {
866 None
867 };
868
869 Ok(AstNode::Relation {
870 name,
871 subject_role,
872 predicate,
873 object_role,
874 via_flow,
875 })
876}
877
878fn parse_instance(pair: Pair<Rule>) -> ParseResult<AstNode> {
880 let mut inner = pair.into_inner();
881
882 let name = parse_identifier(
883 inner
884 .next()
885 .ok_or_else(|| ParseError::GrammarError("Expected instance name".to_string()))?,
886 )?;
887
888 let entity_type = parse_string_literal(
889 inner
890 .next()
891 .ok_or_else(|| ParseError::GrammarError("Expected entity type".to_string()))?,
892 )?;
893
894 let mut fields = HashMap::new();
895
896 if let Some(body_pair) = inner.next() {
898 if body_pair.as_rule() == Rule::instance_body {
899 for field_pair in body_pair.into_inner() {
900 if field_pair.as_rule() == Rule::instance_field {
901 let span = field_pair.as_span();
902 let mut field_inner = field_pair.into_inner();
903
904 let field_name = parse_identifier(field_inner.next().ok_or_else(|| {
905 ParseError::GrammarError("Expected field name".to_string())
906 })?)?;
907
908 if fields.contains_key(&field_name) {
909 let (line, column) = span.start_pos().line_col();
910 return Err(ParseError::GrammarError(format!(
911 "Duplicate field name '{}' at line {}, column {}",
912 field_name, line, column
913 )));
914 }
915
916 let field_value = parse_expression(field_inner.next().ok_or_else(|| {
917 ParseError::GrammarError("Expected field value".to_string())
918 })?)?;
919
920 fields.insert(field_name, field_value);
921 }
922 }
923 }
924 }
925
926 Ok(AstNode::Instance {
927 name,
928 entity_type,
929 fields,
930 })
931}
932
933fn parse_policy(pair: Pair<Rule>) -> ParseResult<AstNode> {
935 let mut inner = pair.into_inner();
936
937 let name = parse_identifier(
938 inner
939 .next()
940 .ok_or_else(|| ParseError::GrammarError("Expected policy name".to_string()))?,
941 )?;
942
943 let mut metadata = PolicyMetadata {
944 kind: None,
945 modality: None,
946 priority: None,
947 rationale: None,
948 tags: Vec::new(),
949 };
950 let mut version: Option<String> = None;
951
952 for next_pair in inner {
953 match next_pair.as_rule() {
954 Rule::policy_kind => {
955 metadata.kind = Some(match next_pair.as_str().to_lowercase().as_str() {
956 "constraint" => PolicyKind::Constraint,
957 "derivation" => PolicyKind::Derivation,
958 "obligation" => PolicyKind::Obligation,
959 _ => {
960 return Err(ParseError::GrammarError(format!(
961 "Unknown policy kind: {}",
962 next_pair.as_str()
963 )))
964 }
965 });
966 }
967 Rule::policy_modality => {
968 metadata.modality = Some(match next_pair.as_str().to_lowercase().as_str() {
969 "obligation" => PolicyModality::Obligation,
970 "prohibition" => PolicyModality::Prohibition,
971 "permission" => PolicyModality::Permission,
972 _ => {
973 return Err(ParseError::GrammarError(format!(
974 "Unknown policy modality: {}",
975 next_pair.as_str()
976 )))
977 }
978 });
979 }
980 Rule::number => {
981 metadata.priority = Some(parse_number(next_pair)?);
982 }
983 Rule::policy_annotation => {
984 parse_policy_annotation(next_pair, &mut metadata)?;
985 }
986 Rule::version => {
987 version = Some(next_pair.as_str().to_string());
988 }
989 Rule::expression => {
990 let expression = parse_expression(next_pair)?;
991 return Ok(AstNode::Policy {
992 name,
993 version,
994 metadata,
995 expression,
996 });
997 }
998 _ => {}
999 }
1000 }
1001
1002 Err(ParseError::GrammarError(
1003 "Policy missing expression".to_string(),
1004 ))
1005}
1006
1007fn parse_policy_annotation(pair: Pair<Rule>, metadata: &mut PolicyMetadata) -> ParseResult<()> {
1008 let mut inner = pair.into_inner();
1009
1010 let name = inner
1011 .next()
1012 .ok_or_else(|| ParseError::GrammarError("Expected annotation name".to_string()))?;
1013 let value = inner
1014 .next()
1015 .ok_or_else(|| ParseError::GrammarError("Expected annotation value".to_string()))?;
1016
1017 let name_str = name.as_str().to_lowercase();
1018
1019 match name_str.as_str() {
1020 "rationale" => {
1021 metadata.rationale = Some(parse_string_literal(value)?);
1022 }
1023 "tags" => {
1024 if value.as_rule() == Rule::string_array {
1025 for tag_pair in value.into_inner() {
1026 if tag_pair.as_rule() == Rule::string_literal {
1027 metadata.tags.push(parse_string_literal(tag_pair)?);
1028 }
1029 }
1030 } else {
1031 metadata.tags.push(parse_string_literal(value)?);
1032 }
1033 }
1034 _ => {
1035 return Err(ParseError::GrammarError(format!(
1036 "Unknown policy annotation: {}",
1037 name_str
1038 )))
1039 }
1040 }
1041
1042 Ok(())
1043}
1044
1045fn parse_expression(pair: Pair<Rule>) -> ParseResult<Expression> {
1047 match pair.as_rule() {
1048 Rule::expression => {
1049 let inner = pair
1050 .into_inner()
1051 .next()
1052 .ok_or_else(|| ParseError::GrammarError("Empty expression".to_string()))?;
1053 parse_expression(inner)
1054 }
1055 Rule::or_expr => parse_or_expr(pair),
1056 Rule::and_expr => parse_and_expr(pair),
1057 Rule::not_expr => parse_not_expr(pair),
1058 Rule::comparison_expr => parse_comparison_expr(pair),
1059 Rule::additive_expr => parse_additive_expr(pair),
1060 Rule::multiplicative_expr => parse_multiplicative_expr(pair),
1061 Rule::unary_expr => parse_unary_expr(pair),
1062 Rule::cast_expr => parse_cast_expr(pair),
1063 Rule::primary_expr => parse_primary_expr(pair),
1064 _ => Err(ParseError::InvalidExpression(format!(
1065 "Unexpected expression rule: {:?}",
1066 pair.as_rule()
1067 ))),
1068 }
1069}
1070
1071pub fn parse_expression_from_str(source: &str) -> ParseResult<Expression> {
1073 let pairs = SeaParser::parse(Rule::expression, source)
1074 .map_err(|e| ParseError::GrammarError(format!("Parse error: {}", e)))?;
1075 let parsed_pairs: Vec<_> = pairs.collect();
1076
1077 if parsed_pairs.is_empty() {
1078 return Err(ParseError::GrammarError("Empty expression".to_string()));
1079 }
1080 if parsed_pairs.len() > 1 {
1081 return Err(ParseError::GrammarError(
1082 "Trailing input detected after expression".to_string(),
1083 ));
1084 }
1085
1086 let pair = parsed_pairs.into_iter().next().unwrap();
1087
1088 let consumed = pair.as_span().end();
1090 let trimmed_source = source.trim_end();
1091 if consumed < trimmed_source.len() {
1092 return Err(ParseError::GrammarError(format!(
1093 "Trailing input after expression: '{}'",
1094 &source[consumed..]
1095 )));
1096 }
1097
1098 parse_expression(pair)
1099}
1100
1101fn parse_or_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1103 let mut inner = pair.into_inner();
1104 let mut left =
1105 parse_expression(inner.next().ok_or_else(|| {
1106 ParseError::GrammarError("Expected left expression in OR".to_string())
1107 })?)?;
1108
1109 for right_pair in inner {
1110 let right = parse_expression(right_pair)?;
1111 left = Expression::Binary {
1112 left: Box::new(left),
1113 op: BinaryOp::Or,
1114 right: Box::new(right),
1115 };
1116 }
1117
1118 Ok(left)
1119}
1120
1121fn parse_and_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1123 let mut inner = pair.into_inner();
1124 let mut left =
1125 parse_expression(inner.next().ok_or_else(|| {
1126 ParseError::GrammarError("Expected left expression in AND".to_string())
1127 })?)?;
1128
1129 for right_pair in inner {
1130 let right = parse_expression(right_pair)?;
1131 left = Expression::Binary {
1132 left: Box::new(left),
1133 op: BinaryOp::And,
1134 right: Box::new(right),
1135 };
1136 }
1137
1138 Ok(left)
1139}
1140
1141fn parse_not_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1143 let mut inner = pair.into_inner();
1144 let first = inner
1145 .next()
1146 .ok_or_else(|| ParseError::GrammarError("Expected expression in NOT".to_string()))?;
1147
1148 if first.as_rule() == Rule::not_expr {
1150 let expr = parse_expression(first)?;
1152 Ok(Expression::Unary {
1153 op: UnaryOp::Not,
1154 operand: Box::new(expr),
1155 })
1156 } else {
1157 parse_expression(first)
1159 }
1160}
1161
1162fn parse_comparison_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1164 let mut inner = pair.into_inner();
1165 let mut left = parse_expression(inner.next().ok_or_else(|| {
1166 ParseError::GrammarError("Expected left expression in comparison".to_string())
1167 })?)?;
1168
1169 if let Some(op_pair) = inner.next() {
1170 let op = parse_comparison_op(op_pair)?;
1171 let right = parse_expression(inner.next().ok_or_else(|| {
1172 ParseError::GrammarError("Expected right expression in comparison".to_string())
1173 })?)?;
1174 left = Expression::Binary {
1175 left: Box::new(left),
1176 op,
1177 right: Box::new(right),
1178 };
1179 }
1180
1181 Ok(left)
1182}
1183
1184fn parse_comparison_op(pair: Pair<Rule>) -> ParseResult<BinaryOp> {
1186 let op_str = pair.as_str();
1187 match op_str {
1188 "=" => Ok(BinaryOp::Equal),
1189 "!=" => Ok(BinaryOp::NotEqual),
1190 ">" => Ok(BinaryOp::GreaterThan),
1191 "<" => Ok(BinaryOp::LessThan),
1192 ">=" => Ok(BinaryOp::GreaterThanOrEqual),
1193 "<=" => Ok(BinaryOp::LessThanOrEqual),
1194 _ if op_str.eq_ignore_ascii_case("contains") => Ok(BinaryOp::Contains),
1195 _ if op_str.eq_ignore_ascii_case("startswith") => Ok(BinaryOp::StartsWith),
1196 _ if op_str.eq_ignore_ascii_case("endswith") => Ok(BinaryOp::EndsWith),
1197 _ if op_str.eq_ignore_ascii_case("matches") => Ok(BinaryOp::Matches),
1198 _ if op_str.eq_ignore_ascii_case("before") => Ok(BinaryOp::Before),
1199 _ if op_str.eq_ignore_ascii_case("after") => Ok(BinaryOp::After),
1200 _ if op_str.eq_ignore_ascii_case("during") => Ok(BinaryOp::During),
1201 _ if op_str.eq_ignore_ascii_case("has_role") => Ok(BinaryOp::HasRole),
1202 _ => Err(ParseError::InvalidExpression(format!(
1203 "Unknown comparison operator: {}",
1204 op_str
1205 ))),
1206 }
1207}
1208
1209fn parse_additive_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1211 let mut inner = pair.into_inner();
1212 let mut left = parse_expression(inner.next().ok_or_else(|| {
1213 ParseError::GrammarError("Expected left expression in additive".to_string())
1214 })?)?;
1215
1216 while let Some(op_pair) = inner.next() {
1217 let op = match op_pair.as_str() {
1218 "+" => BinaryOp::Plus,
1219 "-" => BinaryOp::Minus,
1220 _ => {
1221 return Err(ParseError::InvalidExpression(
1222 "Invalid additive operator".to_string(),
1223 ))
1224 }
1225 };
1226 let right = parse_expression(inner.next().ok_or_else(|| {
1227 ParseError::GrammarError("Expected right expression in additive".to_string())
1228 })?)?;
1229 left = Expression::Binary {
1230 left: Box::new(left),
1231 op,
1232 right: Box::new(right),
1233 };
1234 }
1235
1236 Ok(left)
1237}
1238
1239fn parse_multiplicative_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1241 let mut inner = pair.into_inner();
1242 let mut left = parse_expression(inner.next().ok_or_else(|| {
1243 ParseError::GrammarError("Expected left expression in multiplicative".to_string())
1244 })?)?;
1245
1246 while let Some(op_pair) = inner.next() {
1247 let op = match op_pair.as_str() {
1248 "*" => BinaryOp::Multiply,
1249 "/" => BinaryOp::Divide,
1250 _ => {
1251 return Err(ParseError::InvalidExpression(
1252 "Invalid multiplicative operator".to_string(),
1253 ))
1254 }
1255 };
1256 let right = parse_expression(inner.next().ok_or_else(|| {
1257 ParseError::GrammarError("Expected right expression in multiplicative".to_string())
1258 })?)?;
1259 left = Expression::Binary {
1260 left: Box::new(left),
1261 op,
1262 right: Box::new(right),
1263 };
1264 }
1265
1266 Ok(left)
1267}
1268
1269fn parse_cast_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1271 let mut inner = pair.into_inner();
1272 let primary = parse_expression(inner.next().ok_or_else(|| {
1273 ParseError::GrammarError("Expected primary expression in cast".to_string())
1274 })?)?;
1275
1276 if let Some(as_pair) = inner.next() {
1277 let target_type = parse_string_literal(as_pair)?;
1278 Ok(Expression::cast(primary, target_type))
1279 } else {
1280 Ok(primary)
1281 }
1282}
1283
1284fn parse_unary_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1286 let mut inner = pair.into_inner();
1287 let first = inner
1288 .next()
1289 .ok_or_else(|| ParseError::GrammarError("Expected expression in unary".to_string()))?;
1290
1291 if first.as_str() == "-" {
1292 let expr = parse_expression(inner.next().ok_or_else(|| {
1294 ParseError::GrammarError("Expected expression after unary minus".to_string())
1295 })?)?;
1296 Ok(Expression::Unary {
1297 op: UnaryOp::Negate,
1298 operand: Box::new(expr),
1299 })
1300 } else {
1301 parse_expression(first)
1302 }
1303}
1304
1305fn parse_primary_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1307 let inner = pair.into_inner().next().ok_or_else(|| {
1308 ParseError::GrammarError("Expected inner expression in primary".to_string())
1309 })?;
1310
1311 match inner.as_rule() {
1312 Rule::expression => parse_expression(inner),
1313 Rule::group_by_expr => parse_group_by_expr(inner),
1314 Rule::aggregation_expr => parse_aggregation_expr(inner),
1315 Rule::quantified_expr => parse_quantified_expr(inner),
1316 Rule::member_access => parse_member_access(inner),
1317 Rule::literal => parse_literal_expr(inner),
1318 Rule::identifier => {
1319 let name = parse_identifier(inner)?;
1320 Ok(Expression::Variable(name))
1321 }
1322 _ => Err(ParseError::InvalidExpression(format!(
1323 "Unexpected primary expression: {:?}",
1324 inner.as_rule()
1325 ))),
1326 }
1327}
1328
1329fn parse_aggregation_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1331 let mut inner = pair.into_inner();
1332
1333 let function_pair = inner.next().ok_or_else(|| {
1334 ParseError::GrammarError(
1335 "Expected aggregate function in aggregation expression".to_string(),
1336 )
1337 })?;
1338 let function = parse_aggregate_fn(function_pair)?;
1339
1340 let collection_pair = inner.next().ok_or_else(|| {
1341 ParseError::GrammarError("Expected collection in aggregation expression".to_string())
1342 })?;
1343
1344 if collection_pair.as_rule() == Rule::aggregation_comprehension {
1347 let mut comp_inner = collection_pair.into_inner();
1349 let variable_pair = comp_inner.next().ok_or_else(|| {
1350 ParseError::GrammarError("Expected variable in aggregation comprehension".to_string())
1351 })?;
1352 let variable = parse_identifier(variable_pair)?;
1353 let collection_token = comp_inner.next().ok_or_else(|| {
1355 ParseError::GrammarError("Expected collection in aggregation comprehension".to_string())
1356 })?;
1357 let collection_name = parse_collection(collection_token)?;
1358 let collection = Box::new(Expression::Variable(collection_name));
1359
1360 let mut next_pair = comp_inner.next().ok_or_else(|| {
1361 ParseError::GrammarError(
1362 "Expected predicate expression, projection, or window in aggregation comprehension"
1363 .to_string(),
1364 )
1365 })?;
1366
1367 let mut window = None;
1368 if next_pair.as_rule() == Rule::window_clause {
1369 window = Some(parse_window_clause(next_pair)?);
1370 next_pair = comp_inner.next().ok_or_else(|| {
1371 ParseError::GrammarError(
1372 "Expected predicate or projection expression in aggregation comprehension"
1373 .to_string(),
1374 )
1375 })?;
1376 }
1377
1378 let remaining_pairs: Vec<Pair<Rule>> =
1379 std::iter::once(next_pair).chain(comp_inner).collect();
1380
1381 let mut expr_pairs: Vec<Pair<Rule>> = Vec::new();
1382 let mut target_unit: Option<String> = None;
1383 for pair in remaining_pairs {
1384 match pair.as_rule() {
1385 Rule::expression => expr_pairs.push(pair),
1386 Rule::string_literal => {
1387 target_unit = Some(parse_string_literal(pair)?);
1388 }
1389 Rule::identifier if pair.as_str().eq_ignore_ascii_case("as") => {
1390 }
1392 other => {
1393 return Err(ParseError::GrammarError(format!(
1394 "Unexpected token {:?} in aggregation comprehension",
1395 other
1396 )))
1397 }
1398 }
1399 }
1400
1401 let (predicate, projection) = match expr_pairs.len() {
1402 2 => {
1403 let mut expr_iter = expr_pairs.into_iter();
1404 let predicate_expr = parse_expression(expr_iter.next().ok_or_else(|| {
1405 ParseError::GrammarError(
1406 "Expected predicate expression in aggregation comprehension".to_string(),
1407 )
1408 })?)?;
1409 let projection_expr = parse_expression(expr_iter.next().ok_or_else(|| {
1410 ParseError::GrammarError(
1411 "Expected projection expression in aggregation comprehension".to_string(),
1412 )
1413 })?)?;
1414 (predicate_expr, projection_expr)
1415 }
1416 1 => {
1417 let projection_expr =
1418 parse_expression(expr_pairs.into_iter().next().ok_or_else(|| {
1419 ParseError::GrammarError(
1420 "Expected projection expression in aggregation comprehension"
1421 .to_string(),
1422 )
1423 })?)?;
1424 (Expression::Literal(JsonValue::Bool(true)), projection_expr)
1425 }
1426 other => {
1427 return Err(ParseError::GrammarError(format!(
1428 "Unexpected number of expressions in aggregation comprehension: {}",
1429 other
1430 )))
1431 }
1432 };
1433
1434 return Ok(Expression::AggregationComprehension {
1435 function,
1436 variable,
1437 collection,
1438 window,
1439 predicate: Box::new(predicate),
1440 projection: Box::new(projection),
1441 target_unit,
1442 });
1443 }
1444
1445 let mut simple_inner = collection_pair.into_inner();
1447
1448 let first_pair = simple_inner.next().ok_or_else(|| {
1450 ParseError::GrammarError(
1451 "Expected collection or identifier in aggregation_simple".to_string(),
1452 )
1453 })?;
1454
1455 let collection = match first_pair.as_rule() {
1456 Rule::collection => parse_collection(first_pair)?,
1457 Rule::identifier => parse_identifier(first_pair)?,
1458 _ => {
1459 return Err(ParseError::GrammarError(format!(
1460 "Expected collection or identifier, got {:?}",
1461 first_pair.as_rule()
1462 )))
1463 }
1464 };
1465
1466 let mut field: Option<String> = None;
1467 let mut filter: Option<Expression> = None;
1468
1469 for item in simple_inner {
1471 match item.as_rule() {
1472 Rule::identifier => {
1473 field = Some(parse_identifier(item)?);
1474 }
1475 Rule::expression => {
1476 filter = Some(parse_expression(item)?);
1477 }
1478 _ => {}
1479 }
1480 }
1481
1482 Ok(Expression::aggregation(
1483 function,
1484 Expression::Variable(collection),
1485 field,
1486 filter,
1487 ))
1488}
1489
1490fn parse_aggregate_fn(pair: Pair<Rule>) -> ParseResult<AggregateFunction> {
1492 let fn_str = pair.as_str();
1493 match fn_str.to_lowercase().as_str() {
1494 "count" => Ok(AggregateFunction::Count),
1495 "sum" => Ok(AggregateFunction::Sum),
1496 "min" => Ok(AggregateFunction::Min),
1497 "max" => Ok(AggregateFunction::Max),
1498 "avg" => Ok(AggregateFunction::Avg),
1499 _ => Err(ParseError::InvalidExpression(format!(
1500 "Unknown aggregate function: {}",
1501 fn_str
1502 ))),
1503 }
1504}
1505
1506fn parse_quantified_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1508 let mut inner = pair.into_inner();
1509
1510 let quantifier = parse_quantifier(inner.next().ok_or_else(|| {
1511 ParseError::GrammarError("Expected quantifier in quantified expression".to_string())
1512 })?)?;
1513 let variable = parse_identifier(inner.next().ok_or_else(|| {
1514 ParseError::GrammarError(
1515 "Expected variable identifier in quantified expression".to_string(),
1516 )
1517 })?)?;
1518 let collection = parse_collection(inner.next().ok_or_else(|| {
1519 ParseError::GrammarError(
1520 "Expected collection identifier in quantified expression".to_string(),
1521 )
1522 })?)?;
1523 let condition = parse_expression(inner.next().ok_or_else(|| {
1524 ParseError::GrammarError("Expected quantified condition expression".to_string())
1525 })?)?;
1526
1527 Ok(Expression::Quantifier {
1528 quantifier,
1529 variable,
1530 collection: Box::new(Expression::Variable(collection)),
1531 condition: Box::new(condition),
1532 })
1533}
1534
1535fn parse_quantifier(pair: Pair<Rule>) -> ParseResult<PolicyQuantifier> {
1537 let q_str = pair.as_str();
1538 match q_str.to_lowercase().as_str() {
1539 "forall" => Ok(PolicyQuantifier::ForAll),
1540 "exists" => Ok(PolicyQuantifier::Exists),
1541 "exists_unique" => Ok(PolicyQuantifier::ExistsUnique),
1542 _ => Err(ParseError::InvalidExpression(format!(
1543 "Unknown quantifier: {}",
1544 q_str
1545 ))),
1546 }
1547}
1548
1549fn parse_collection(pair: Pair<Rule>) -> ParseResult<String> {
1551 Ok(pair.as_str().to_lowercase())
1552}
1553
1554fn parse_member_access(pair: Pair<Rule>) -> ParseResult<Expression> {
1556 let mut inner = pair.into_inner();
1557 let object = parse_identifier(inner.next().ok_or_else(|| {
1558 ParseError::GrammarError("Expected object identifier in member access".to_string())
1559 })?)?;
1560 let member = parse_identifier(inner.next().ok_or_else(|| {
1561 ParseError::GrammarError("Expected member identifier in member access".to_string())
1562 })?)?;
1563
1564 Ok(Expression::MemberAccess { object, member })
1565}
1566
1567fn parse_literal_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1569 let inner = pair
1570 .into_inner()
1571 .next()
1572 .ok_or_else(|| ParseError::GrammarError("Expected literal content".to_string()))?;
1573
1574 match inner.as_rule() {
1575 Rule::string_literal => {
1576 let s = parse_string_literal(inner)?;
1577 Ok(Expression::Literal(JsonValue::String(s)))
1578 }
1579 Rule::multiline_string => {
1580 let s = parse_multiline_string(inner)?;
1581 Ok(Expression::Literal(JsonValue::String(s)))
1582 }
1583 Rule::quantity_literal => {
1584 let mut parts = inner.into_inner();
1585 let number_part = parts.next().ok_or_else(|| {
1586 ParseError::GrammarError("Expected number in quantity literal".to_string())
1587 })?;
1588 let unit_part = parts.next().ok_or_else(|| {
1589 ParseError::GrammarError("Expected unit string in quantity literal".to_string())
1590 })?;
1591 let value = parse_decimal(number_part)?;
1592 let unit = parse_string_literal(unit_part)?;
1593 Ok(Expression::QuantityLiteral { value, unit })
1594 }
1595 Rule::time_literal => {
1596 let timestamp = inner.as_str();
1598 let timestamp = timestamp.trim_start_matches('"').trim_end_matches('"');
1600 Ok(Expression::TimeLiteral(timestamp.to_string()))
1601 }
1602 Rule::interval_literal => {
1603 let mut parts = inner.into_inner();
1605 let start_part = parts.next().ok_or_else(|| {
1606 ParseError::GrammarError("Expected start time in interval literal".to_string())
1607 })?;
1608 let end_part = parts.next().ok_or_else(|| {
1609 ParseError::GrammarError("Expected end time in interval literal".to_string())
1610 })?;
1611 let start = parse_string_literal(start_part)?;
1612 let end = parse_string_literal(end_part)?;
1613 Ok(Expression::IntervalLiteral { start, end })
1614 }
1615 Rule::number => {
1616 let n = parse_decimal(inner)?;
1617 let f = n.to_f64().ok_or_else(|| {
1619 ParseError::InvalidQuantity(format!(
1620 "Decimal value {} cannot be represented as f64",
1621 n
1622 ))
1623 })?;
1624 if !f.is_finite() {
1626 return Err(ParseError::InvalidQuantity(format!(
1627 "Decimal value {} converts to non-finite f64: {}",
1628 n, f
1629 )));
1630 }
1631 let num = serde_json::Number::from_f64(f).ok_or_else(|| {
1632 ParseError::InvalidQuantity(format!(
1633 "Cannot create JSON Number from f64 value: {}",
1634 f
1635 ))
1636 })?;
1637 Ok(Expression::Literal(JsonValue::Number(num)))
1638 }
1639 Rule::boolean => {
1640 let b = inner.as_str().eq_ignore_ascii_case("true");
1641 Ok(Expression::Literal(JsonValue::Bool(b)))
1642 }
1643 _ => Err(ParseError::InvalidExpression(format!(
1644 "Unknown literal type: {:?}",
1645 inner.as_rule()
1646 ))),
1647 }
1648}
1649
1650fn parse_name(pair: Pair<Rule>) -> ParseResult<String> {
1652 let inner = pair.into_inner().next().ok_or_else(|| {
1653 ParseError::GrammarError("Expected inner token for name but got empty pair".to_string())
1654 })?;
1655 match inner.as_rule() {
1656 Rule::string_literal => parse_string_literal(inner),
1657 Rule::multiline_string => parse_multiline_string(inner),
1658 _ => Err(ParseError::GrammarError(format!(
1659 "Expected string or multiline string for name, got {:?}",
1660 inner.as_rule()
1661 ))),
1662 }
1663}
1664
1665fn parse_string_literal(pair: Pair<Rule>) -> ParseResult<String> {
1667 let s = pair.as_str();
1668 if s.len() < 2 || !s.starts_with('"') || !s.ends_with('"') {
1669 return Err(ParseError::GrammarError(format!(
1670 "Invalid string literal: {}",
1671 s
1672 )));
1673 }
1674
1675 match serde_json::from_str(s) {
1678 Ok(unescaped) => Ok(unescaped),
1679 Err(e) => Err(ParseError::GrammarError(format!(
1680 "Invalid string literal escape sequences: {} - {}",
1681 s, e
1682 ))),
1683 }
1684}
1685
1686fn parse_multiline_string(pair: Pair<Rule>) -> ParseResult<String> {
1688 let s = pair.as_str();
1689 if s.len() < 6 || !s.starts_with("\"\"\"") || !s.ends_with("\"\"\"") {
1690 return Err(ParseError::GrammarError(format!(
1691 "Invalid multiline string: {}",
1692 s
1693 )));
1694 }
1695
1696 let content = &s[3..s.len() - 3];
1697
1698 let escaped = content
1700 .replace('\\', "\\\\") .replace('"', "\\\"") .replace('\n', "\\n") .replace('\r', "\\r") .replace('\t', "\\t"); let json_string = format!("\"{}\"", escaped);
1708 match serde_json::from_str(&json_string) {
1709 Ok(unescaped) => Ok(unescaped),
1710 Err(e) => Err(ParseError::GrammarError(format!(
1711 "Invalid multiline string escape sequences in '{}': {}",
1712 s, e
1713 ))),
1714 }
1715}
1716
1717fn parse_identifier(pair: Pair<Rule>) -> ParseResult<String> {
1719 Ok(pair.as_str().to_string())
1720}
1721
1722fn parse_version(pair: Pair<Rule>) -> ParseResult<String> {
1724 Ok(pair.as_str().to_string())
1725}
1726
1727fn parse_number(pair: Pair<Rule>) -> ParseResult<i32> {
1729 pair.as_str()
1730 .parse()
1731 .map_err(|_| ParseError::InvalidQuantity(format!("Invalid number: {}", pair.as_str())))
1732}
1733
1734fn parse_decimal(pair: Pair<Rule>) -> ParseResult<Decimal> {
1736 pair.as_str()
1737 .parse()
1738 .map_err(|_| ParseError::InvalidQuantity(format!("Invalid decimal: {}", pair.as_str())))
1739}
1740
1741fn parse_group_by_expr(pair: Pair<Rule>) -> ParseResult<Expression> {
1743 let mut inner = pair.into_inner();
1744
1745 let variable_pair = inner
1746 .next()
1747 .ok_or_else(|| ParseError::GrammarError("Expected variable in group_by".to_string()))?;
1748 let variable = parse_identifier(variable_pair)?;
1749
1750 let collection_pair = inner
1751 .next()
1752 .ok_or_else(|| ParseError::GrammarError("Expected collection in group_by".to_string()))?;
1753 let collection_name = parse_collection(collection_pair)?;
1754 let collection = Box::new(Expression::Variable(collection_name));
1755
1756 let next_pair = inner.next().ok_or_else(|| {
1757 ParseError::GrammarError("Expected key or where clause in group_by".to_string())
1758 })?;
1759
1760 let remaining: Vec<Pair<Rule>> = std::iter::once(next_pair).chain(inner).collect();
1762
1763 let (filter_expr, key_expr, condition_expr) = match remaining.len() {
1764 3 => {
1765 let mut iter = remaining.into_iter();
1766 let filter = parse_expression(iter.next().ok_or_else(|| {
1767 ParseError::GrammarError("Expected filter expression in group_by".to_string())
1768 })?)?;
1769 let key = parse_expression(iter.next().ok_or_else(|| {
1770 ParseError::GrammarError("Expected key expression in group_by".to_string())
1771 })?)?;
1772 let condition = parse_expression(iter.next().ok_or_else(|| {
1773 ParseError::GrammarError("Expected condition expression in group_by".to_string())
1774 })?)?;
1775 (Some(filter), key, condition)
1776 }
1777 2 => {
1778 let mut iter = remaining.into_iter();
1779 let key = parse_expression(iter.next().ok_or_else(|| {
1780 ParseError::GrammarError("Expected key expression in group_by".to_string())
1781 })?)?;
1782 let condition = parse_expression(iter.next().ok_or_else(|| {
1783 ParseError::GrammarError("Expected condition expression in group_by".to_string())
1784 })?)?;
1785 (None, key, condition)
1786 }
1787 other => {
1788 return Err(ParseError::GrammarError(format!(
1789 "Unexpected number of expressions in group_by: {}",
1790 other
1791 )))
1792 }
1793 };
1794
1795 Ok(Expression::GroupBy {
1796 variable,
1797 collection,
1798 filter: filter_expr.map(Box::new),
1799 key: Box::new(key_expr),
1800 condition: Box::new(condition_expr),
1801 })
1802}
1803
1804fn parse_window_clause(pair: Pair<Rule>) -> ParseResult<WindowSpec> {
1806 let mut inner = pair.into_inner();
1807
1808 let duration_pair = inner.next().ok_or_else(|| {
1809 ParseError::GrammarError("Expected duration in window clause".to_string())
1810 })?;
1811 let duration_i32 = parse_number(duration_pair)?;
1812 if duration_i32 < 0 {
1813 return Err(ParseError::InvalidQuantity(
1814 "Window duration must be non-negative".to_string(),
1815 ));
1816 }
1817 let duration = duration_i32 as u64;
1818
1819 let unit_pair = inner
1820 .next()
1821 .ok_or_else(|| ParseError::GrammarError("Expected unit in window clause".to_string()))?;
1822 let unit = parse_string_literal(unit_pair)?;
1823
1824 Ok(WindowSpec { duration, unit })
1825}
1826
1827fn expression_kind(expr: &Expression) -> &'static str {
1828 match expr {
1829 Expression::Literal(_) => "literal",
1830 Expression::QuantityLiteral { .. } => "quantity_literal",
1831 Expression::TimeLiteral(_) => "time_literal",
1832 Expression::IntervalLiteral { .. } => "interval_literal",
1833 Expression::Variable(_) => "variable",
1834 Expression::GroupBy { .. } => "group_by",
1835 Expression::Binary { .. } => "binary",
1836 Expression::Unary { .. } => "unary",
1837 Expression::Cast { .. } => "cast",
1838 Expression::Quantifier { .. } => "quantifier",
1839 Expression::MemberAccess { .. } => "member_access",
1840 Expression::Aggregation { .. } => "aggregation",
1841 Expression::AggregationComprehension { .. } => "aggregation_comprehension",
1842 }
1843}
1844
1845fn expression_to_json(expr: &Expression) -> ParseResult<JsonValue> {
1847 match expr {
1848 Expression::Literal(v) => Ok(v.clone()),
1849 Expression::Variable(name) => Ok(JsonValue::String(name.clone())),
1850 Expression::QuantityLiteral { value, unit } => Ok(json!({
1851 "value": value.to_string(),
1852 "unit": unit
1853 })),
1854 Expression::TimeLiteral(timestamp) => Ok(JsonValue::String(timestamp.clone())),
1855 _ => Err(ParseError::UnsupportedExpression {
1856 kind: expression_kind(expr).to_string(),
1857 span: None,
1858 }),
1859 }
1860}
1861
1862fn parse_mapping(pair: Pair<Rule>) -> ParseResult<AstNode> {
1864 let mut inner = pair.into_inner();
1865
1866 let name = parse_string_literal(
1867 inner
1868 .next()
1869 .ok_or_else(|| ParseError::GrammarError("Expected mapping name".to_string()))?,
1870 )?;
1871
1872 let target_pair = inner
1873 .next()
1874 .ok_or_else(|| ParseError::GrammarError("Expected target format".to_string()))?;
1875 let target = parse_target_format(target_pair)?;
1876
1877 let mut rules = Vec::new();
1878
1879 for rule_pair in inner {
1880 if rule_pair.as_rule() == Rule::mapping_rule {
1881 rules.push(parse_mapping_rule(rule_pair)?);
1882 }
1883 }
1884
1885 Ok(AstNode::MappingDecl {
1886 name,
1887 target,
1888 rules,
1889 })
1890}
1891
1892fn parse_target_format(pair: Pair<Rule>) -> ParseResult<TargetFormat> {
1893 match pair.as_str().to_lowercase().as_str() {
1894 "calm" => Ok(TargetFormat::Calm),
1895 "kg" => Ok(TargetFormat::Kg),
1896 "sbvr" => Ok(TargetFormat::Sbvr),
1897 "protobuf" | "proto" => Ok(TargetFormat::Protobuf),
1898 _ => Err(ParseError::GrammarError(format!(
1899 "Unknown target format: {}",
1900 pair.as_str()
1901 ))),
1902 }
1903}
1904
1905fn parse_mapping_rule(pair: Pair<Rule>) -> ParseResult<MappingRule> {
1906 let mut inner = pair.into_inner();
1907
1908 let primitive_type = inner
1909 .next()
1910 .ok_or_else(|| ParseError::GrammarError("Expected primitive type".to_string()))?
1911 .as_str()
1912 .to_string();
1913
1914 let primitive_name = parse_string_literal(
1915 inner
1916 .next()
1917 .ok_or_else(|| ParseError::GrammarError("Expected primitive name".to_string()))?,
1918 )?;
1919
1920 let target_structure = inner
1921 .next()
1922 .ok_or_else(|| ParseError::GrammarError("Expected target structure".to_string()))?;
1923
1924 let mut target_inner = target_structure.into_inner();
1925 let target_type = parse_identifier(
1926 target_inner
1927 .next()
1928 .ok_or_else(|| ParseError::GrammarError("Expected target type".to_string()))?,
1929 )?;
1930
1931 let mut fields = HashMap::new();
1932 for field_pair in target_inner {
1933 if field_pair.as_rule() == Rule::mapping_field {
1934 let mut field_inner = field_pair.into_inner();
1935 let key = parse_identifier(
1936 field_inner
1937 .next()
1938 .ok_or_else(|| ParseError::GrammarError("Expected field key".to_string()))?,
1939 )?;
1940 let value_pair = field_inner
1941 .next()
1942 .ok_or_else(|| ParseError::GrammarError("Expected field value".to_string()))?;
1943
1944 let value = match value_pair.as_rule() {
1945 Rule::string_literal => JsonValue::String(parse_string_literal(value_pair)?),
1946 Rule::boolean => JsonValue::Bool(value_pair.as_str().eq_ignore_ascii_case("true")),
1947 Rule::object_literal => parse_object_literal(value_pair)?,
1948 _ => {
1949 return Err(ParseError::GrammarError(
1950 "Unexpected mapping field value".to_string(),
1951 ))
1952 }
1953 };
1954 fields.insert(key, value);
1955 }
1956 }
1957
1958 Ok(MappingRule {
1959 primitive_type,
1960 primitive_name,
1961 target_type,
1962 fields,
1963 })
1964}
1965
1966fn parse_object_literal(pair: Pair<Rule>) -> ParseResult<JsonValue> {
1967 let mut map = serde_json::Map::new();
1968 let mut inner = pair.into_inner();
1969 while let Some(key_pair) = inner.next() {
1970 let key = parse_string_literal(key_pair)?;
1971 let value_pair = inner.next().ok_or_else(|| {
1972 ParseError::GrammarError("Expected value in object literal".to_string())
1973 })?;
1974 let value = match value_pair.as_rule() {
1975 Rule::string_literal => JsonValue::String(parse_string_literal(value_pair)?),
1976 Rule::boolean => JsonValue::Bool(value_pair.as_str().eq_ignore_ascii_case("true")),
1977 Rule::number => {
1978 let d = parse_decimal(value_pair)?;
1979 let f = d.to_f64().ok_or_else(|| {
1981 ParseError::InvalidQuantity(format!(
1982 "Decimal value {} cannot be represented as f64",
1983 d
1984 ))
1985 })?;
1986 if !f.is_finite() {
1987 return Err(ParseError::InvalidQuantity(format!(
1988 "Decimal value {} converts to non-finite f64",
1989 d
1990 )));
1991 }
1992 let num = serde_json::Number::from_f64(f).ok_or_else(|| {
1993 ParseError::InvalidQuantity(format!(
1994 "Cannot create JSON Number from decimal {}",
1995 d
1996 ))
1997 })?;
1998 JsonValue::Number(num)
1999 }
2000 _ => {
2001 return Err(ParseError::GrammarError(
2002 "Unexpected object field value".to_string(),
2003 ))
2004 }
2005 };
2006 map.insert(key, value);
2007 }
2008 Ok(JsonValue::Object(map))
2009}
2010
2011fn parse_projection(pair: Pair<Rule>) -> ParseResult<AstNode> {
2013 let mut inner = pair.into_inner();
2014
2015 let name = parse_string_literal(
2016 inner
2017 .next()
2018 .ok_or_else(|| ParseError::GrammarError("Expected projection name".to_string()))?,
2019 )?;
2020
2021 let target_pair = inner
2022 .next()
2023 .ok_or_else(|| ParseError::GrammarError("Expected target format".to_string()))?;
2024 let target = parse_target_format(target_pair)?;
2025
2026 let mut overrides = Vec::new();
2027
2028 for rule_pair in inner {
2029 if rule_pair.as_rule() == Rule::projection_rule {
2030 overrides.push(parse_projection_rule(rule_pair)?);
2031 }
2032 }
2033
2034 Ok(AstNode::ProjectionDecl {
2035 name,
2036 target,
2037 overrides,
2038 })
2039}
2040
2041fn parse_projection_rule(pair: Pair<Rule>) -> ParseResult<ProjectionOverride> {
2042 let mut inner = pair.into_inner();
2043
2044 let primitive_type = inner
2045 .next()
2046 .ok_or_else(|| ParseError::GrammarError("Expected primitive type".to_string()))?
2047 .as_str()
2048 .to_string();
2049
2050 let primitive_name = parse_string_literal(
2051 inner
2052 .next()
2053 .ok_or_else(|| ParseError::GrammarError("Expected primitive name".to_string()))?,
2054 )?;
2055
2056 let mut fields = HashMap::new();
2057 for field_pair in inner {
2058 if field_pair.as_rule() == Rule::projection_field {
2059 let mut field_inner = field_pair.into_inner();
2060 let key = parse_identifier(
2061 field_inner
2062 .next()
2063 .ok_or_else(|| ParseError::GrammarError("Expected field key".to_string()))?,
2064 )?;
2065 let value_pair = field_inner
2066 .next()
2067 .ok_or_else(|| ParseError::GrammarError("Expected field value".to_string()))?;
2068
2069 let value = match value_pair.as_rule() {
2070 Rule::string_literal => JsonValue::String(parse_string_literal(value_pair)?),
2071 Rule::property_mapping => parse_property_mapping(value_pair)?,
2072 _ => {
2073 return Err(ParseError::GrammarError(
2074 "Unexpected projection field value".to_string(),
2075 ))
2076 }
2077 };
2078 fields.insert(key, value);
2079 }
2080 }
2081
2082 Ok(ProjectionOverride {
2083 primitive_type,
2084 primitive_name,
2085 fields,
2086 })
2087}
2088
2089fn parse_property_mapping(pair: Pair<Rule>) -> ParseResult<JsonValue> {
2090 let mut map = serde_json::Map::new();
2091 let mut inner = pair.into_inner();
2092 while let Some(key_pair) = inner.next() {
2093 let key = parse_string_literal(key_pair)?;
2094 let value_pair = inner.next().ok_or_else(|| {
2095 ParseError::GrammarError("Expected value in property mapping".to_string())
2096 })?;
2097 let value = parse_string_literal(value_pair)?;
2098 map.insert(key, JsonValue::String(value));
2099 }
2100 Ok(JsonValue::Object(map))
2101}
2102
2103fn unwrap_export(spanned: &Spanned<AstNode>) -> &AstNode {
2104 match &spanned.node {
2105 AstNode::Export(inner) => &inner.node,
2106 other => other,
2107 }
2108}
2109
2110pub fn ast_to_graph(ast: Ast) -> ParseResult<Graph> {
2112 ast_to_graph_with_options(ast, &ParseOptions::default())
2113}
2114
2115pub fn ast_to_graph_with_options(mut ast: Ast, options: &ParseOptions) -> ParseResult<Graph> {
2116 use crate::parser::profiles::ProfileRegistry;
2117 let registry = ProfileRegistry::global();
2118 let active_profile = ast
2119 .metadata
2120 .profile
2121 .clone()
2122 .or_else(|| options.active_profile.clone())
2123 .unwrap_or_else(|| "default".to_string());
2124 ast.metadata.profile.get_or_insert(active_profile.clone());
2125
2126 if registry.get(&active_profile).is_none() {
2127 let available = registry.list_names().join(", ");
2128 let message = format!(
2129 "Unknown profile: '{}'. Available profiles: {}",
2130 active_profile, available
2131 );
2132 if options.tolerate_profile_warnings {
2133 log::warn!("{}", message);
2134 } else {
2135 return Err(ParseError::Validation(message));
2136 }
2137 }
2138
2139 let mut graph = Graph::new();
2140 let mut entity_map = HashMap::new();
2141 let mut role_map = HashMap::new();
2142 let mut resource_map = HashMap::new();
2143 let mut relation_map = HashMap::new();
2144
2145 let default_namespace = ast
2146 .metadata
2147 .namespace
2148 .clone()
2149 .or_else(|| options.default_namespace.clone())
2150 .unwrap_or_else(|| "default".to_string());
2151
2152 {
2154 use crate::units::{Dimension, Unit, UnitError, UnitRegistry};
2155 let registry = UnitRegistry::global();
2156 let mut registry = registry.write().map_err(|e| {
2157 ParseError::GrammarError(format!("Failed to lock unit registry: {}", e))
2158 })?;
2159
2160 for node in &ast.declarations {
2161 let node = unwrap_export(node);
2162 match node {
2163 AstNode::Dimension { name } => {
2164 let dim = Dimension::parse(name);
2165 registry.register_dimension(dim);
2166 }
2167 AstNode::UnitDeclaration {
2168 symbol,
2169 dimension,
2170 factor,
2171 base_unit,
2172 } => {
2173 let dim = Dimension::parse(dimension);
2174 let unit = Unit::new(
2175 symbol.clone(),
2176 symbol.clone(),
2177 dim,
2178 *factor,
2179 base_unit.clone(),
2180 );
2181 match registry.get_unit(symbol) {
2182 Ok(existing) => {
2183 if existing != &unit {
2184 return Err(ParseError::GrammarError(format!(
2185 "Conflicting unit '{}' already registered (existing: dimension={}, base_factor={}, base_unit={}; new: dimension={}, base_factor={}, base_unit={})",
2186 symbol,
2187 existing.dimension(),
2188 existing.base_factor(),
2189 existing.base_unit(),
2190 unit.dimension(),
2191 unit.base_factor(),
2192 unit.base_unit(),
2193 )));
2194 }
2195 }
2196 Err(UnitError::UnitNotFound(_)) => {
2197 registry.register(unit).map_err(|e| {
2198 ParseError::GrammarError(format!("Failed to register unit: {}", e))
2199 })?;
2200 }
2201 Err(err) => {
2202 return Err(ParseError::GrammarError(format!(
2203 "Failed to inspect unit '{}': {}",
2204 symbol, err
2205 )));
2206 }
2207 }
2208 }
2209 _ => {}
2210 }
2211 }
2212 }
2213
2214 for node in &ast.declarations {
2216 let node = unwrap_export(node);
2217 if let AstNode::Pattern { name, regex } = node {
2218 let namespace = default_namespace.clone();
2219 let pattern = Pattern::new(name.clone(), namespace, regex.clone())
2220 .map_err(ParseError::GrammarError)?;
2221
2222 graph
2223 .add_pattern(pattern)
2224 .map_err(ParseError::GrammarError)?;
2225 }
2226 }
2227
2228 for node in &ast.declarations {
2230 let node = unwrap_export(node);
2231 if let AstNode::ConceptChange {
2232 name,
2233 from_version,
2234 to_version,
2235 migration_policy,
2236 breaking_change,
2237 } = node
2238 {
2239 let change = ConceptChange::new(
2240 name.clone(),
2241 from_version.clone(),
2242 to_version.clone(),
2243 migration_policy.clone(),
2244 *breaking_change,
2245 );
2246 graph.add_concept_change(change).map_err(|e| {
2247 ParseError::GrammarError(format!("Failed to add concept change: {}", e))
2248 })?;
2249 }
2250 }
2251
2252 for node in &ast.declarations {
2254 let node = unwrap_export(node);
2255 match node {
2256 AstNode::Role { name, domain } => {
2257 if role_map.contains_key(name) {
2258 return Err(ParseError::duplicate_declaration_no_loc(format!(
2259 "Role '{}' already declared",
2260 name
2261 )));
2262 }
2263
2264 let namespace = domain.as_ref().unwrap_or(&default_namespace).clone();
2265 let role = Role::new_with_namespace(name.clone(), namespace);
2266 let role_id = role.id().clone();
2267 graph
2268 .add_role(role)
2269 .map_err(|e| ParseError::GrammarError(format!("Failed to add role: {}", e)))?;
2270 role_map.insert(name.clone(), role_id);
2271 }
2272 AstNode::Entity {
2273 name,
2274 domain,
2275 version,
2276 annotations,
2277 } => {
2278 if entity_map.contains_key(name) {
2279 return Err(ParseError::duplicate_declaration_no_loc(format!(
2280 "Entity '{}' already declared",
2281 name
2282 )));
2283 }
2284
2285 let namespace = domain.as_ref().unwrap_or(&default_namespace).clone();
2286 let mut entity = Entity::new_with_namespace(name.clone(), namespace);
2287
2288 if let Some(v_str) = version {
2289 let sem_ver = SemanticVersion::parse(v_str).map_err(|e| {
2290 ParseError::GrammarError(format!(
2291 "Invalid entity version '{}': {}",
2292 v_str, e
2293 ))
2294 })?;
2295 entity = entity.with_version(sem_ver);
2296 }
2297
2298 if let Some(replaces_val) = annotations.get("replaces") {
2299 if let Some(replaces_str) = replaces_val.as_str() {
2300 entity = entity.with_replaces(replaces_str.to_string());
2301 }
2302 }
2303
2304 if let Some(changes_val) = annotations.get("changes") {
2305 if let Some(changes_arr) = changes_val.as_array() {
2306 let changes: Vec<String> = changes_arr
2307 .iter()
2308 .filter_map(|v| v.as_str().map(|s| s.to_string()))
2309 .collect();
2310 entity = entity.with_changes(changes);
2311 }
2312 }
2313
2314 let entity_id = entity.id().clone();
2315 graph.add_entity(entity).map_err(|e| {
2316 ParseError::GrammarError(format!("Failed to add entity: {}", e))
2317 })?;
2318 entity_map.insert(name.clone(), entity_id);
2319 }
2320 AstNode::Resource {
2321 name,
2322 unit_name,
2323 domain,
2324 ..
2325 } => {
2326 if resource_map.contains_key(name) {
2327 return Err(ParseError::duplicate_declaration_no_loc(format!(
2328 "Resource '{}' already declared",
2329 name
2330 )));
2331 }
2332
2333 let namespace = domain.as_ref().unwrap_or(&default_namespace).clone();
2334 let unit = unit_from_string(unit_name.as_deref().unwrap_or("units"));
2335 let resource = Resource::new_with_namespace(name.clone(), unit, namespace);
2336 let resource_id = resource.id().clone();
2337 graph.add_resource(resource).map_err(|e| {
2338 ParseError::GrammarError(format!("Failed to add resource: {}", e))
2339 })?;
2340 resource_map.insert(name.clone(), resource_id);
2341 }
2342 _ => {}
2343 }
2344 }
2345
2346 for node in &ast.declarations {
2348 let node = unwrap_export(node);
2349 if let AstNode::Flow {
2350 resource_name,
2351 from_entity,
2352 to_entity,
2353 quantity,
2354 ..
2355 } = node
2356 {
2357 let from_id = entity_map
2358 .get(from_entity)
2359 .ok_or_else(|| ParseError::undefined_entity_no_loc(from_entity))?;
2360
2361 let to_id = entity_map
2362 .get(to_entity)
2363 .ok_or_else(|| ParseError::undefined_entity_no_loc(to_entity))?;
2364
2365 let resource_id = resource_map
2366 .get(resource_name)
2367 .ok_or_else(|| ParseError::undefined_resource_no_loc(resource_name))?;
2368
2369 let qty = quantity.map(Decimal::from).unwrap_or(Decimal::ZERO);
2370 let flow = Flow::new(resource_id.clone(), from_id.clone(), to_id.clone(), qty);
2371
2372 graph
2373 .add_flow(flow)
2374 .map_err(|e| ParseError::GrammarError(format!("Failed to add flow: {}", e)))?;
2375 }
2376 }
2377
2378 for node in &ast.declarations {
2380 let node = unwrap_export(node);
2381 if let AstNode::Relation {
2382 name,
2383 subject_role,
2384 predicate,
2385 object_role,
2386 via_flow,
2387 } = node
2388 {
2389 if relation_map.contains_key(name) {
2390 return Err(ParseError::duplicate_declaration_no_loc(format!(
2391 "Relation '{}' already declared",
2392 name
2393 )));
2394 }
2395
2396 let subject_id = role_map.get(subject_role).ok_or_else(|| {
2397 ParseError::GrammarError(format!("Undefined subject role '{}'", subject_role))
2398 })?;
2399
2400 let object_id = role_map.get(object_role).ok_or_else(|| {
2401 ParseError::GrammarError(format!("Undefined object role '{}'", object_role))
2402 })?;
2403
2404 let via_flow_id = if let Some(flow_name) = via_flow {
2405 Some(
2406 resource_map
2407 .get(flow_name)
2408 .cloned()
2409 .ok_or_else(|| ParseError::undefined_resource_no_loc(flow_name))?,
2410 )
2411 } else {
2412 None
2413 };
2414
2415 let relation = RelationType::new(
2416 name.clone(),
2417 default_namespace.clone(),
2418 subject_id.clone(),
2419 predicate.clone(),
2420 object_id.clone(),
2421 via_flow_id,
2422 );
2423
2424 let relation_id = relation.id().clone();
2425 graph.add_relation_type(relation).map_err(|e| {
2426 ParseError::GrammarError(format!("Failed to add relation '{}': {}", name, e))
2427 })?;
2428 relation_map.insert(name.clone(), relation_id);
2429 }
2430 }
2431
2432 for node in &ast.declarations {
2434 let node = unwrap_export(node);
2435 if let AstNode::Instance {
2436 name,
2437 entity_type,
2438 fields,
2439 } = node
2440 {
2441 let namespace = default_namespace.clone();
2442 let mut instance = crate::primitives::Instance::new_with_namespace(
2443 name.clone(),
2444 entity_type.clone(),
2445 namespace,
2446 );
2447
2448 for (field_name, field_expr) in fields {
2450 let value = expression_to_json(field_expr)?;
2451 instance.set_field(field_name.clone(), value);
2452 }
2453
2454 graph.add_entity_instance(instance).map_err(|e| {
2455 ParseError::GrammarError(format!("Failed to add entity instance '{}': {}", name, e))
2456 })?;
2457 }
2458 }
2459
2460 for node in &ast.declarations {
2462 let node = unwrap_export(node);
2463 if let AstNode::Policy {
2464 name,
2465 version,
2466 metadata,
2467 expression,
2468 } = node
2469 {
2470 let namespace = ast
2471 .metadata
2472 .namespace
2473 .as_ref()
2474 .cloned()
2475 .or_else(|| options.default_namespace.clone())
2476 .unwrap_or_else(|| "default".to_string());
2477
2478 let kind = metadata.kind.as_ref().map(|kind| match kind {
2479 PolicyKind::Constraint => CorePolicyKind::Constraint,
2480 PolicyKind::Derivation => CorePolicyKind::Derivation,
2481 PolicyKind::Obligation => CorePolicyKind::Obligation,
2482 });
2483
2484 let modality = metadata.modality.as_ref().map(|modality| match modality {
2485 PolicyModality::Obligation => CorePolicyModality::Obligation,
2486 PolicyModality::Prohibition => CorePolicyModality::Prohibition,
2487 PolicyModality::Permission => CorePolicyModality::Permission,
2488 });
2489
2490 let mut policy =
2491 Policy::new_with_namespace(name.clone(), namespace, expression.clone())
2492 .with_metadata(
2493 kind,
2494 modality,
2495 metadata.priority,
2496 metadata.rationale.clone(),
2497 metadata.tags.clone(),
2498 );
2499
2500 let version_to_apply = version
2501 .as_ref()
2502 .cloned()
2503 .or_else(|| ast.metadata.version.clone());
2504
2505 if let Some(version_str) = version_to_apply {
2506 let semantic_version = SemanticVersion::parse(&version_str).map_err(|err| {
2507 ParseError::GrammarError(format!(
2508 "Invalid policy version '{}': {}",
2509 version_str, err
2510 ))
2511 })?;
2512 policy = policy.with_version(semantic_version);
2513 }
2514
2515 graph.add_policy(policy).map_err(|e| {
2516 ParseError::GrammarError(format!("Failed to add policy '{}': {}", name, e))
2517 })?;
2518 }
2519 }
2520
2521 for node in &ast.declarations {
2523 let node = unwrap_export(node);
2524 if let AstNode::Metric {
2525 name,
2526 expression,
2527 metadata,
2528 } = node
2529 {
2530 let namespace = ast
2531 .metadata
2532 .namespace
2533 .as_ref()
2534 .cloned()
2535 .or_else(|| options.default_namespace.clone())
2536 .unwrap_or_else(|| "default".to_string());
2537
2538 let mut metric =
2539 crate::primitives::Metric::new(name.clone(), namespace, expression.clone());
2540
2541 if let Some(duration) = metadata.refresh_interval {
2542 metric = metric.with_refresh_interval(duration);
2543 }
2544
2545 if let Some(unit) = &metadata.unit {
2546 metric = metric.with_unit(unit.clone());
2547 }
2548
2549 if let Some(threshold) = metadata.threshold {
2550 metric = metric.with_threshold(threshold);
2551 }
2552
2553 if let Some(severity) = metadata.severity.clone() {
2554 metric = metric.with_severity(severity);
2555 }
2556
2557 if let Some(target) = metadata.target {
2558 metric = metric.with_target(target);
2559 }
2560
2561 if let Some(duration) = metadata.window {
2562 metric = metric.with_window(duration);
2563 }
2564
2565 graph.add_metric(metric).map_err(|e| {
2566 ParseError::GrammarError(format!("Failed to add metric '{}': {}", name, e))
2567 })?;
2568 }
2569 }
2570
2571 for node in &ast.declarations {
2573 let node = unwrap_export(node);
2574 if let AstNode::MappingDecl {
2575 name,
2576 target,
2577 rules,
2578 } = node
2579 {
2580 let namespace = ast
2581 .metadata
2582 .namespace
2583 .clone()
2584 .or_else(|| options.default_namespace.clone())
2585 .unwrap_or_else(|| "default".to_string());
2586 let mapping = crate::primitives::MappingContract::new(
2587 crate::ConceptId::from_concept(&namespace, name),
2588 name.clone(),
2589 namespace,
2590 target.clone(),
2591 rules.clone(),
2592 );
2593 graph
2594 .add_mapping(mapping)
2595 .map_err(|e| ParseError::GrammarError(format!("Failed to add mapping: {}", e)))?;
2596 }
2597 }
2598
2599 for node in &ast.declarations {
2601 let node = unwrap_export(node);
2602 if let AstNode::ProjectionDecl {
2603 name,
2604 target,
2605 overrides,
2606 } = node
2607 {
2608 let namespace = ast
2609 .metadata
2610 .namespace
2611 .clone()
2612 .or_else(|| options.default_namespace.clone())
2613 .unwrap_or_else(|| "default".to_string());
2614 let projection = crate::primitives::ProjectionContract::new(
2615 crate::ConceptId::from_concept(&namespace, name),
2616 name.clone(),
2617 namespace,
2618 target.clone(),
2619 overrides.clone(),
2620 );
2621 graph.add_projection(projection).map_err(|e| {
2622 ParseError::GrammarError(format!("Failed to add projection: {}", e))
2623 })?;
2624 }
2625 }
2626
2627 Ok(graph)
2628}
2629
2630fn parse_metric(pair: Pair<Rule>) -> ParseResult<AstNode> {
2632 let mut inner = pair.into_inner();
2633
2634 let name = parse_name(
2635 inner
2636 .next()
2637 .ok_or_else(|| ParseError::GrammarError("Expected metric name".to_string()))?,
2638 )?;
2639
2640 let mut metadata = MetricMetadata {
2641 refresh_interval: None,
2642 unit: None,
2643 threshold: None,
2644 severity: None,
2645 target: None,
2646 window: None,
2647 };
2648
2649 let mut expression_pair = None;
2650
2651 for part in inner {
2652 match part.as_rule() {
2653 Rule::metric_annotation => {
2654 let mut annotation_inner = part.into_inner();
2655 let key_pair = annotation_inner.next().ok_or_else(|| {
2656 ParseError::GrammarError("Expected annotation key".to_string())
2657 })?;
2658
2659 match key_pair.as_rule() {
2660 Rule::ma_refresh_interval => {
2661 let value_pair = annotation_inner.next().ok_or_else(|| {
2662 ParseError::GrammarError("Expected refresh interval value".to_string())
2663 })?;
2664 let value = parse_number_i64(value_pair)?;
2665 let unit_pair = annotation_inner.next().ok_or_else(|| {
2666 ParseError::GrammarError("Expected refresh interval unit".to_string())
2667 })?;
2668 let unit = parse_string_literal(unit_pair.clone())?;
2669 let duration = parse_duration_with_unit(value, &unit, unit_pair.as_span())?;
2670 metadata.refresh_interval = Some(duration);
2671 }
2672 Rule::ma_unit => {
2673 let unit_pair = annotation_inner
2674 .next()
2675 .ok_or_else(|| ParseError::GrammarError("Expected unit".to_string()))?;
2676 metadata.unit = Some(parse_string_literal(unit_pair)?);
2677 }
2678 Rule::ma_threshold => {
2679 let value_pair = annotation_inner.next().ok_or_else(|| {
2680 ParseError::GrammarError("Expected threshold value".to_string())
2681 })?;
2682 metadata.threshold = Some(parse_decimal(value_pair)?);
2683 }
2684 Rule::ma_severity => {
2685 let severity_pair = annotation_inner.next().ok_or_else(|| {
2686 ParseError::GrammarError("Expected severity".to_string())
2687 })?;
2688 let severity_str = parse_string_literal(severity_pair.clone())?;
2689 let severity =
2690 parse_severity_value(&severity_str, severity_pair.as_span())?;
2691 metadata.severity = Some(severity);
2692 }
2693 Rule::ma_target => {
2694 let value_pair = annotation_inner.next().ok_or_else(|| {
2695 ParseError::GrammarError("Expected target value".to_string())
2696 })?;
2697 metadata.target = Some(parse_decimal(value_pair)?);
2698 }
2699 Rule::ma_window => {
2700 let value_pair = annotation_inner.next().ok_or_else(|| {
2701 ParseError::GrammarError("Expected window value".to_string())
2702 })?;
2703 let value = parse_number_i64(value_pair)?;
2704 let unit_pair = annotation_inner.next().ok_or_else(|| {
2705 ParseError::GrammarError("Expected window unit".to_string())
2706 })?;
2707 let unit = parse_string_literal(unit_pair.clone())?;
2708 let duration = parse_duration_with_unit(value, &unit, unit_pair.as_span())?;
2709 metadata.window = Some(duration);
2710 }
2711 _ => {
2712 let (line, column) = key_pair.as_span().start_pos().line_col();
2713 return Err(ParseError::GrammarError(format!(
2714 "Unknown metric annotation '{}' at {}:{}",
2715 key_pair.as_str(),
2716 line,
2717 column
2718 )));
2719 }
2720 }
2721 }
2722 Rule::expression => {
2723 expression_pair = Some(part);
2724 }
2725 _ => {}
2726 }
2727 }
2728
2729 let expression = parse_expression(
2730 expression_pair
2731 .ok_or_else(|| ParseError::GrammarError("Expected metric expression".to_string()))?,
2732 )?;
2733
2734 Ok(AstNode::Metric {
2735 name,
2736 expression,
2737 metadata,
2738 })
2739}
2740
2741fn parse_duration_with_unit(value: i64, unit: &str, span: Span<'_>) -> ParseResult<Duration> {
2742 let normalized_unit = unit.to_ascii_lowercase();
2743 let multiplier = match normalized_unit.as_str() {
2744 "second" => Some(1),
2745 "seconds" | "s" => Some(1),
2746 "minute" => Some(60),
2747 "minutes" | "m" => Some(60),
2748 "hour" => Some(60 * 60),
2749 "hours" | "h" => Some(60 * 60),
2750 "day" => Some(60 * 60 * 24),
2751 "days" | "d" => Some(60 * 60 * 24),
2752 _ => None,
2753 }
2754 .ok_or_else(|| {
2755 let (line, column) = span.start_pos().line_col();
2756 ParseError::GrammarError(format!(
2757 "Invalid duration unit '{}' at {}:{} (allowed: second(s)/s, minute(s)/m, hour(s)/h, day(s)/d)",
2758 unit, line, column
2759 ))
2760 })?;
2761
2762 let total_seconds = value.checked_mul(multiplier).ok_or_else(|| {
2763 let (line, column) = span.start_pos().line_col();
2764 ParseError::GrammarError(format!(
2765 "Duration overflow for value {} {} at {}:{}",
2766 value, unit, line, column
2767 ))
2768 })?;
2769
2770 Ok(Duration::seconds(total_seconds))
2771}
2772
2773fn parse_severity_value(value: &str, span: Span<'_>) -> ParseResult<Severity> {
2774 let normalized = value.to_ascii_lowercase();
2775 match normalized.as_str() {
2776 "info" => Ok(Severity::Info),
2777 "warning" => Ok(Severity::Warning),
2778 "error" => Ok(Severity::Error),
2779 "critical" => Ok(Severity::Critical),
2780 _ => {
2781 let (line, column) = span.start_pos().line_col();
2782 Err(ParseError::GrammarError(format!(
2783 "Unknown severity '{}' at {}:{} (expected one of: info, warning, error, critical)",
2784 value, line, column
2785 )))
2786 }
2787 }
2788}
2789
2790fn parse_number_i64(pair: Pair<Rule>) -> ParseResult<i64> {
2791 let s = pair.as_str();
2792 s.parse::<i64>()
2793 .map_err(|_| ParseError::GrammarError(format!("Invalid integer: {}", s)))
2794}