Skip to main content

icl_core/parser/
mod.rs

1//! ICL Parser — tokenizer, AST types, and recursive descent parser
2//!
3//! Converts ICL text into an Abstract Syntax Tree (AST).
4//! Matches the BNF grammar defined in CORE-SPECIFICATION.md Section 1.
5//!
6//! # Pipeline
7//!
8//! `ICL text → Tokenizer → Token stream → Parser → ContractNode (AST)`
9//!
10//! # Guarantees
11//!
12//! - **Deterministic**: same input always produces identical AST
13//! - **Pure**: no side effects, no I/O, no randomness
14//! - **Complete errors**: line:column for every error
15
16pub mod ast;
17pub mod tokenizer;
18
19use crate::{Error, Result};
20use ast::*;
21use tokenizer::{Span, SpannedToken, Token, Tokenizer};
22
23// ── Public API ─────────────────────────────────────────────
24
25/// Parse ICL text into an AST ContractNode
26///
27/// Returns the raw parse tree with source positions (spans).
28/// For a semantic `Contract`, use [`parse_contract`] instead.
29///
30/// # Guarantees
31/// - Deterministic: same input always produces same AST
32/// - Error messages include line:column
33///
34/// # Errors
35/// Returns `ParseError` with line:column for syntax violations.
36pub fn parse(input: &str) -> Result<ContractNode> {
37    let mut tokenizer = Tokenizer::new(input);
38    let tokens = tokenizer.tokenize()?;
39    let mut parser = Parser::new(tokens);
40    parser.parse_contract_definition()
41}
42
43/// Parse ICL text into a semantic Contract (parse + lower)
44///
45/// Combines parsing (text → AST) with lowering (AST → semantic Contract).
46///
47/// # Errors
48/// Returns `ParseError` for syntax errors or `ValidationError` for
49/// semantic issues (e.g., confidence_level out of range).
50pub fn parse_contract(input: &str) -> Result<crate::Contract> {
51    let node = parse(input)?;
52    lower_contract(&node)
53}
54
55// ── Parser ─────────────────────────────────────────────────
56
57struct Parser {
58    tokens: Vec<SpannedToken>,
59    position: usize,
60}
61
62impl Parser {
63    fn new(tokens: Vec<SpannedToken>) -> Self {
64        Parser {
65            tokens,
66            position: 0,
67        }
68    }
69
70    // ── Token helpers ──────────────────────────────────
71
72    fn current_span(&self) -> Span {
73        if self.position < self.tokens.len() {
74            self.tokens[self.position].span.clone()
75        } else {
76            Span {
77                line: 0,
78                column: 0,
79                offset: 0,
80            }
81        }
82    }
83
84    fn peek(&self) -> &Token {
85        &self.tokens[self.position].token
86    }
87
88    fn advance(&mut self) -> SpannedToken {
89        let token = self.tokens[self.position].clone();
90        if self.position < self.tokens.len() - 1 {
91            self.position += 1;
92        }
93        token
94    }
95
96    /// Expect a specific token (exact match for keywords/symbols)
97    fn expect(&mut self, expected: Token) -> Result<SpannedToken> {
98        let current = self.tokens[self.position].clone();
99        if current.token == expected {
100            self.advance();
101            Ok(current)
102        } else {
103            Err(Error::ParseError(format!(
104                "Expected {:?}, found {:?} at {}",
105                expected, current.token, current.span
106            )))
107        }
108    }
109
110    /// Expect a string literal and return its value with span
111    fn expect_string_literal(&mut self) -> Result<SpannedValue<String>> {
112        let st = self.advance();
113        match st.token {
114            Token::StringLiteral(s) => Ok(SpannedValue::new(s, st.span)),
115            _ => Err(Error::ParseError(format!(
116                "Expected string literal, found {:?} at {}",
117                st.token, st.span
118            ))),
119        }
120    }
121
122    /// Expect an integer literal and return its value with span
123    fn expect_integer_literal(&mut self) -> Result<SpannedValue<i64>> {
124        let st = self.advance();
125        match st.token {
126            Token::IntegerLiteral(n) => Ok(SpannedValue::new(n, st.span)),
127            _ => Err(Error::ParseError(format!(
128                "Expected integer literal, found {:?} at {}",
129                st.token, st.span
130            ))),
131        }
132    }
133
134    /// Expect a float literal and return its value with span
135    fn expect_float_literal(&mut self) -> Result<SpannedValue<f64>> {
136        let st = self.advance();
137        match st.token {
138            Token::FloatLiteral(f) => Ok(SpannedValue::new(f, st.span)),
139            _ => Err(Error::ParseError(format!(
140                "Expected float literal, found {:?} at {}",
141                st.token, st.span
142            ))),
143        }
144    }
145
146    /// Expect a named field: `identifier ":"`
147    fn expect_field(&mut self, name: &str) -> Result<Span> {
148        let st = self.advance();
149        match &st.token {
150            Token::Identifier(id) if id == name => {}
151            _ => {
152                return Err(Error::ParseError(format!(
153                    "Expected field '{}', found {:?} at {}",
154                    name, st.token, st.span
155                )));
156            }
157        }
158        self.expect(Token::Colon)?;
159        Ok(st.span)
160    }
161
162    /// Consume a comma if present (for optional trailing commas)
163    fn optional_comma(&mut self) {
164        if matches!(self.peek(), Token::Comma) {
165            self.advance();
166        }
167    }
168
169    /// Peek at the current token and return identifier name without advancing
170    fn peek_identifier_name(&self) -> Result<String> {
171        match &self.tokens[self.position].token {
172            Token::Identifier(name) => Ok(name.clone()),
173            other => Err(Error::ParseError(format!(
174                "Expected field name identifier, found {:?} at {}",
175                other, self.tokens[self.position].span
176            ))),
177        }
178    }
179
180    // ── Top-level parsing ──────────────────────────────
181
182    /// Parse: `Contract { ... } [Extensions { ... }]`
183    fn parse_contract_definition(&mut self) -> Result<ContractNode> {
184        let span = self.current_span();
185        self.expect(Token::Contract)?;
186        self.expect(Token::LBrace)?;
187
188        let identity = self.parse_identity()?;
189        let purpose_statement = self.parse_purpose_statement()?;
190        let data_semantics = self.parse_data_semantics()?;
191        let behavioral_semantics = self.parse_behavioral_semantics()?;
192        let execution_constraints = self.parse_execution_constraints()?;
193        let human_machine_contract = self.parse_human_machine_contract()?;
194
195        self.expect(Token::RBrace)?;
196
197        // Optional Extensions block (outside Contract per BNF §5)
198        let extensions = if matches!(self.peek(), Token::Extensions) {
199            Some(self.parse_extensions()?)
200        } else {
201            None
202        };
203
204        Ok(ContractNode {
205            identity,
206            purpose_statement,
207            data_semantics,
208            behavioral_semantics,
209            execution_constraints,
210            human_machine_contract,
211            extensions,
212            span,
213        })
214    }
215
216    // ── Identity (§1.2) ───────────────────────────────
217
218    fn parse_identity(&mut self) -> Result<IdentityNode> {
219        let span = self.current_span();
220        self.expect(Token::Identity)?;
221        self.expect(Token::LBrace)?;
222
223        let mut stable_id: Option<SpannedValue<String>> = None;
224        let mut version: Option<SpannedValue<i64>> = None;
225        let mut created_timestamp: Option<SpannedValue<String>> = None;
226        let mut owner: Option<SpannedValue<String>> = None;
227        let mut semantic_hash: Option<SpannedValue<String>> = None;
228
229        while !matches!(self.peek(), Token::RBrace) {
230            let field_name = self.peek_identifier_name()?;
231            match field_name.as_str() {
232                "stable_id" => {
233                    self.expect_field("stable_id")?;
234                    stable_id = Some(self.expect_string_literal()?);
235                }
236                "version" => {
237                    self.expect_field("version")?;
238                    version = Some(self.expect_integer_literal()?);
239                }
240                "created_timestamp" => {
241                    self.expect_field("created_timestamp")?;
242                    created_timestamp = Some(self.expect_string_literal()?);
243                }
244                "owner" => {
245                    self.expect_field("owner")?;
246                    owner = Some(self.expect_string_literal()?);
247                }
248                "semantic_hash" => {
249                    self.expect_field("semantic_hash")?;
250                    semantic_hash = Some(self.expect_string_literal()?);
251                }
252                other => {
253                    return Err(Error::ParseError(format!(
254                        "Unknown field '{}' in Identity at {}",
255                        other,
256                        self.current_span()
257                    )));
258                }
259            }
260            self.optional_comma();
261        }
262
263        self.expect(Token::RBrace)?;
264
265        Ok(IdentityNode {
266            stable_id: stable_id.ok_or_else(|| {
267                Error::ParseError(format!(
268                    "Missing required field 'stable_id' in Identity at {}",
269                    span
270                ))
271            })?,
272            version: version.ok_or_else(|| {
273                Error::ParseError(format!(
274                    "Missing required field 'version' in Identity at {}",
275                    span
276                ))
277            })?,
278            created_timestamp: created_timestamp.ok_or_else(|| {
279                Error::ParseError(format!(
280                    "Missing required field 'created_timestamp' in Identity at {}",
281                    span
282                ))
283            })?,
284            owner: owner.ok_or_else(|| {
285                Error::ParseError(format!(
286                    "Missing required field 'owner' in Identity at {}",
287                    span
288                ))
289            })?,
290            semantic_hash: semantic_hash.ok_or_else(|| {
291                Error::ParseError(format!(
292                    "Missing required field 'semantic_hash' in Identity at {}",
293                    span
294                ))
295            })?,
296            span,
297        })
298    }
299
300    // ── PurposeStatement (§1.3) ───────────────────────
301
302    fn parse_purpose_statement(&mut self) -> Result<PurposeStatementNode> {
303        let span = self.current_span();
304        self.expect(Token::PurposeStatement)?;
305        self.expect(Token::LBrace)?;
306
307        let mut narrative: Option<SpannedValue<String>> = None;
308        let mut intent_source: Option<SpannedValue<String>> = None;
309        let mut confidence_level: Option<SpannedValue<f64>> = None;
310
311        while !matches!(self.peek(), Token::RBrace) {
312            let field_name = self.peek_identifier_name()?;
313            match field_name.as_str() {
314                "narrative" => {
315                    self.expect_field("narrative")?;
316                    narrative = Some(self.expect_string_literal()?);
317                }
318                "intent_source" => {
319                    self.expect_field("intent_source")?;
320                    intent_source = Some(self.expect_string_literal()?);
321                }
322                "confidence_level" => {
323                    self.expect_field("confidence_level")?;
324                    let cl = self.expect_float_literal()?;
325                    if cl.value < 0.0 || cl.value > 1.0 {
326                        return Err(Error::ValidationError(format!(
327                            "confidence_level must be in [0.0, 1.0], found {} at {}",
328                            cl.value, cl.span
329                        )));
330                    }
331                    confidence_level = Some(cl);
332                }
333                other => {
334                    return Err(Error::ParseError(format!(
335                        "Unknown field '{}' in PurposeStatement at {}",
336                        other,
337                        self.current_span()
338                    )));
339                }
340            }
341            self.optional_comma();
342        }
343
344        self.expect(Token::RBrace)?;
345
346        Ok(PurposeStatementNode {
347            narrative: narrative.ok_or_else(|| {
348                Error::ParseError(format!(
349                    "Missing required field 'narrative' in PurposeStatement at {}",
350                    span
351                ))
352            })?,
353            intent_source: intent_source.ok_or_else(|| {
354                Error::ParseError(format!(
355                    "Missing required field 'intent_source' in PurposeStatement at {}",
356                    span
357                ))
358            })?,
359            confidence_level: confidence_level.ok_or_else(|| {
360                Error::ParseError(format!(
361                    "Missing required field 'confidence_level' in PurposeStatement at {}",
362                    span
363                ))
364            })?,
365            span,
366        })
367    }
368
369    // ── DataSemantics (§1.4) ──────────────────────────
370
371    fn parse_data_semantics(&mut self) -> Result<DataSemanticsNode> {
372        let span = self.current_span();
373        self.expect(Token::DataSemantics)?;
374        self.expect(Token::LBrace)?;
375
376        let mut state: Option<Vec<StateFieldNode>> = None;
377        let mut invariants: Option<Vec<SpannedValue<String>>> = None;
378
379        while !matches!(self.peek(), Token::RBrace) {
380            let field_name = self.peek_identifier_name()?;
381            match field_name.as_str() {
382                "state" => {
383                    self.expect_field("state")?;
384                    self.expect(Token::LBrace)?;
385                    state = Some(self.parse_state_fields()?);
386                    self.expect(Token::RBrace)?;
387                }
388                "invariants" => {
389                    self.expect_field("invariants")?;
390                    invariants = Some(self.parse_string_list()?);
391                }
392                other => {
393                    return Err(Error::ParseError(format!(
394                        "Unknown field '{}' in DataSemantics at {}",
395                        other,
396                        self.current_span()
397                    )));
398                }
399            }
400            self.optional_comma();
401        }
402
403        self.expect(Token::RBrace)?;
404
405        Ok(DataSemanticsNode {
406            state: state.ok_or_else(|| {
407                Error::ParseError(format!(
408                    "Missing required field 'state' in DataSemantics at {}",
409                    span
410                ))
411            })?,
412            invariants: invariants.ok_or_else(|| {
413                Error::ParseError(format!(
414                    "Missing required field 'invariants' in DataSemantics at {}",
415                    span
416                ))
417            })?,
418            span,
419        })
420    }
421
422    /// Parse state field list: `field1: Type, field2: Type = default, ...`
423    fn parse_state_fields(&mut self) -> Result<Vec<StateFieldNode>> {
424        let mut fields = Vec::new();
425        while !matches!(self.peek(), Token::RBrace) {
426            fields.push(self.parse_state_field()?);
427            self.optional_comma();
428        }
429        Ok(fields)
430    }
431
432    /// Parse a single state field: `name: TypeExpression [= default]`
433    fn parse_state_field(&mut self) -> Result<StateFieldNode> {
434        let span = self.current_span();
435
436        let name_st = self.advance();
437        let name = match name_st.token {
438            Token::Identifier(s) => SpannedValue::new(s, name_st.span),
439            _ => {
440                return Err(Error::ParseError(format!(
441                    "Expected field name, found {:?} at {}",
442                    name_st.token, name_st.span
443                )));
444            }
445        };
446
447        self.expect(Token::Colon)?;
448        let type_expr = self.parse_type_expression()?;
449
450        let default_value = if matches!(self.peek(), Token::Equals) {
451            self.advance(); // consume =
452            Some(self.parse_literal_value()?)
453        } else {
454            None
455        };
456
457        Ok(StateFieldNode {
458            name,
459            type_expr,
460            default_value,
461            span,
462        })
463    }
464
465    // ── Type Expressions ──────────────────────────────
466
467    fn parse_type_expression(&mut self) -> Result<TypeExpression> {
468        let span = self.current_span();
469        match self.peek().clone() {
470            Token::IntegerType => {
471                self.advance();
472                Ok(TypeExpression::Primitive(PrimitiveType::Integer, span))
473            }
474            Token::FloatType => {
475                self.advance();
476                Ok(TypeExpression::Primitive(PrimitiveType::Float, span))
477            }
478            Token::StringType => {
479                self.advance();
480                Ok(TypeExpression::Primitive(PrimitiveType::String, span))
481            }
482            Token::BooleanType => {
483                self.advance();
484                Ok(TypeExpression::Primitive(PrimitiveType::Boolean, span))
485            }
486            Token::Iso8601Type => {
487                self.advance();
488                Ok(TypeExpression::Primitive(PrimitiveType::Iso8601, span))
489            }
490            Token::UuidType => {
491                self.advance();
492                Ok(TypeExpression::Primitive(PrimitiveType::Uuid, span))
493            }
494            Token::ArrayType => self.parse_array_type(span),
495            Token::MapType => self.parse_map_type(span),
496            Token::ObjectType => self.parse_object_type(span),
497            Token::EnumType => self.parse_enum_type(span),
498            _ => Err(Error::ParseError(format!(
499                "Expected type expression, found {:?} at {}",
500                self.peek(),
501                span
502            ))),
503        }
504    }
505
506    fn parse_array_type(&mut self, span: Span) -> Result<TypeExpression> {
507        self.advance(); // consume Array
508        self.expect(Token::LAngle)?;
509        let inner = self.parse_type_expression()?;
510        self.expect(Token::RAngle)?;
511        Ok(TypeExpression::Array(Box::new(inner), span))
512    }
513
514    fn parse_map_type(&mut self, span: Span) -> Result<TypeExpression> {
515        self.advance(); // consume Map
516        self.expect(Token::LAngle)?;
517        let key = self.parse_type_expression()?;
518        self.expect(Token::Comma)?;
519        let value = self.parse_type_expression()?;
520        self.expect(Token::RAngle)?;
521        Ok(TypeExpression::Map(Box::new(key), Box::new(value), span))
522    }
523
524    fn parse_object_type(&mut self, span: Span) -> Result<TypeExpression> {
525        self.advance(); // consume Object
526        self.expect(Token::LBrace)?;
527        let fields = self.parse_state_fields()?;
528        self.expect(Token::RBrace)?;
529        Ok(TypeExpression::Object(fields, span))
530    }
531
532    fn parse_enum_type(&mut self, span: Span) -> Result<TypeExpression> {
533        self.advance(); // consume Enum
534        self.expect(Token::LBracket)?;
535
536        let mut variants = Vec::new();
537        if !matches!(self.peek(), Token::RBracket) {
538            variants.push(self.expect_string_literal()?);
539            while matches!(self.peek(), Token::Comma) {
540                self.advance(); // consume comma
541                if matches!(self.peek(), Token::RBracket) {
542                    break; // trailing comma
543                }
544                variants.push(self.expect_string_literal()?);
545            }
546        }
547
548        self.expect(Token::RBracket)?;
549        Ok(TypeExpression::Enum(variants, span))
550    }
551
552    // ── Literal Values ────────────────────────────────
553
554    fn parse_literal_value(&mut self) -> Result<LiteralValue> {
555        let span = self.current_span();
556        match self.peek().clone() {
557            Token::StringLiteral(_) => {
558                let st = self.advance();
559                if let Token::StringLiteral(s) = st.token {
560                    Ok(LiteralValue::String(s, st.span))
561                } else {
562                    unreachable!()
563                }
564            }
565            Token::IntegerLiteral(_) => {
566                let st = self.advance();
567                if let Token::IntegerLiteral(n) = st.token {
568                    Ok(LiteralValue::Integer(n, st.span))
569                } else {
570                    unreachable!()
571                }
572            }
573            Token::FloatLiteral(_) => {
574                let st = self.advance();
575                if let Token::FloatLiteral(f) = st.token {
576                    Ok(LiteralValue::Float(f, st.span))
577                } else {
578                    unreachable!()
579                }
580            }
581            Token::BooleanLiteral(_) => {
582                let st = self.advance();
583                if let Token::BooleanLiteral(b) = st.token {
584                    Ok(LiteralValue::Boolean(b, st.span))
585                } else {
586                    unreachable!()
587                }
588            }
589            Token::LBracket => {
590                self.advance(); // consume [
591                let mut items = Vec::new();
592                if !matches!(self.peek(), Token::RBracket) {
593                    items.push(self.parse_literal_value()?);
594                    while matches!(self.peek(), Token::Comma) {
595                        self.advance(); // consume comma
596                        if matches!(self.peek(), Token::RBracket) {
597                            break; // trailing comma
598                        }
599                        items.push(self.parse_literal_value()?);
600                    }
601                }
602                self.expect(Token::RBracket)?;
603                Ok(LiteralValue::Array(items, span))
604            }
605            _ => Err(Error::ParseError(format!(
606                "Expected literal value, found {:?} at {}",
607                self.peek(),
608                span
609            ))),
610        }
611    }
612
613    // ── BehavioralSemantics (§1.5) ────────────────────
614
615    fn parse_behavioral_semantics(&mut self) -> Result<BehavioralSemanticsNode> {
616        let span = self.current_span();
617        self.expect(Token::BehavioralSemantics)?;
618        self.expect(Token::LBrace)?;
619
620        self.expect_field("operations")?;
621        self.expect(Token::LBracket)?;
622
623        let mut operations = Vec::new();
624        while !matches!(self.peek(), Token::RBracket) {
625            operations.push(self.parse_operation()?);
626            self.optional_comma();
627        }
628
629        self.expect(Token::RBracket)?;
630        self.optional_comma();
631
632        self.expect(Token::RBrace)?;
633
634        Ok(BehavioralSemanticsNode { operations, span })
635    }
636
637    fn parse_operation(&mut self) -> Result<OperationNode> {
638        let span = self.current_span();
639        self.expect(Token::LBrace)?;
640
641        let mut name: Option<SpannedValue<String>> = None;
642        let mut precondition: Option<SpannedValue<String>> = None;
643        let mut parameters: Option<Vec<StateFieldNode>> = None;
644        let mut postcondition: Option<SpannedValue<String>> = None;
645        let mut side_effects: Option<Vec<SpannedValue<String>>> = None;
646        let mut idempotence: Option<SpannedValue<String>> = None;
647
648        while !matches!(self.peek(), Token::RBrace) {
649            let field_name = self.peek_identifier_name()?;
650            match field_name.as_str() {
651                "name" => {
652                    self.expect_field("name")?;
653                    name = Some(self.expect_string_literal()?);
654                }
655                "precondition" => {
656                    self.expect_field("precondition")?;
657                    precondition = Some(self.expect_string_literal()?);
658                }
659                "parameters" => {
660                    self.expect_field("parameters")?;
661                    self.expect(Token::LBrace)?;
662                    parameters = Some(self.parse_state_fields()?);
663                    self.expect(Token::RBrace)?;
664                }
665                "postcondition" => {
666                    self.expect_field("postcondition")?;
667                    postcondition = Some(self.expect_string_literal()?);
668                }
669                "side_effects" => {
670                    self.expect_field("side_effects")?;
671                    side_effects = Some(self.parse_string_list()?);
672                }
673                "idempotence" => {
674                    self.expect_field("idempotence")?;
675                    idempotence = Some(self.expect_string_literal()?);
676                }
677                other => {
678                    return Err(Error::ParseError(format!(
679                        "Unknown field '{}' in operation at {}",
680                        other,
681                        self.current_span()
682                    )));
683                }
684            }
685            self.optional_comma();
686        }
687
688        self.expect(Token::RBrace)?;
689
690        Ok(OperationNode {
691            name: name.ok_or_else(|| {
692                Error::ParseError(format!(
693                    "Missing required field 'name' in operation at {}",
694                    span
695                ))
696            })?,
697            precondition: precondition.ok_or_else(|| {
698                Error::ParseError(format!(
699                    "Missing required field 'precondition' in operation at {}",
700                    span
701                ))
702            })?,
703            parameters: parameters.ok_or_else(|| {
704                Error::ParseError(format!(
705                    "Missing required field 'parameters' in operation at {}",
706                    span
707                ))
708            })?,
709            postcondition: postcondition.ok_or_else(|| {
710                Error::ParseError(format!(
711                    "Missing required field 'postcondition' in operation at {}",
712                    span
713                ))
714            })?,
715            side_effects: side_effects.ok_or_else(|| {
716                Error::ParseError(format!(
717                    "Missing required field 'side_effects' in operation at {}",
718                    span
719                ))
720            })?,
721            idempotence: idempotence.ok_or_else(|| {
722                Error::ParseError(format!(
723                    "Missing required field 'idempotence' in operation at {}",
724                    span
725                ))
726            })?,
727            span,
728        })
729    }
730
731    // ── ExecutionConstraints (§1.6) ───────────────────
732
733    fn parse_execution_constraints(&mut self) -> Result<ExecutionConstraintsNode> {
734        let span = self.current_span();
735        self.expect(Token::ExecutionConstraints)?;
736        self.expect(Token::LBrace)?;
737
738        let mut trigger_types: Option<Vec<SpannedValue<String>>> = None;
739        let mut resource_limits: Option<ResourceLimitsNode> = None;
740        let mut external_permissions: Option<Vec<SpannedValue<String>>> = None;
741        let mut sandbox_mode: Option<SpannedValue<String>> = None;
742
743        while !matches!(self.peek(), Token::RBrace) {
744            let field_name = self.peek_identifier_name()?;
745            match field_name.as_str() {
746                "trigger_types" => {
747                    self.expect_field("trigger_types")?;
748                    trigger_types = Some(self.parse_string_list()?);
749                }
750                "resource_limits" => {
751                    self.expect_field("resource_limits")?;
752                    resource_limits = Some(self.parse_resource_limits()?);
753                }
754                "external_permissions" => {
755                    self.expect_field("external_permissions")?;
756                    external_permissions = Some(self.parse_string_list()?);
757                }
758                "sandbox_mode" => {
759                    self.expect_field("sandbox_mode")?;
760                    sandbox_mode = Some(self.expect_string_literal()?);
761                }
762                other => {
763                    return Err(Error::ParseError(format!(
764                        "Unknown field '{}' in ExecutionConstraints at {}",
765                        other,
766                        self.current_span()
767                    )));
768                }
769            }
770            self.optional_comma();
771        }
772
773        self.expect(Token::RBrace)?;
774
775        Ok(ExecutionConstraintsNode {
776            trigger_types: trigger_types.ok_or_else(|| {
777                Error::ParseError(format!(
778                    "Missing required field 'trigger_types' in ExecutionConstraints at {}",
779                    span
780                ))
781            })?,
782            resource_limits: resource_limits.ok_or_else(|| {
783                Error::ParseError(format!(
784                    "Missing required field 'resource_limits' in ExecutionConstraints at {}",
785                    span
786                ))
787            })?,
788            external_permissions: external_permissions.ok_or_else(|| {
789                Error::ParseError(format!(
790                    "Missing required field 'external_permissions' in ExecutionConstraints at {}",
791                    span
792                ))
793            })?,
794            sandbox_mode: sandbox_mode.ok_or_else(|| {
795                Error::ParseError(format!(
796                    "Missing required field 'sandbox_mode' in ExecutionConstraints at {}",
797                    span
798                ))
799            })?,
800            span,
801        })
802    }
803
804    fn parse_resource_limits(&mut self) -> Result<ResourceLimitsNode> {
805        let span = self.current_span();
806        self.expect(Token::LBrace)?;
807
808        let mut max_memory_bytes: Option<SpannedValue<i64>> = None;
809        let mut computation_timeout_ms: Option<SpannedValue<i64>> = None;
810        let mut max_state_size_bytes: Option<SpannedValue<i64>> = None;
811
812        while !matches!(self.peek(), Token::RBrace) {
813            let field_name = self.peek_identifier_name()?;
814            match field_name.as_str() {
815                "max_memory_bytes" => {
816                    self.expect_field("max_memory_bytes")?;
817                    max_memory_bytes = Some(self.expect_integer_literal()?);
818                }
819                "computation_timeout_ms" => {
820                    self.expect_field("computation_timeout_ms")?;
821                    computation_timeout_ms = Some(self.expect_integer_literal()?);
822                }
823                "max_state_size_bytes" => {
824                    self.expect_field("max_state_size_bytes")?;
825                    max_state_size_bytes = Some(self.expect_integer_literal()?);
826                }
827                other => {
828                    return Err(Error::ParseError(format!(
829                        "Unknown field '{}' in resource_limits at {}",
830                        other,
831                        self.current_span()
832                    )));
833                }
834            }
835            self.optional_comma();
836        }
837
838        self.expect(Token::RBrace)?;
839
840        Ok(ResourceLimitsNode {
841            max_memory_bytes: max_memory_bytes.ok_or_else(|| {
842                Error::ParseError(format!(
843                    "Missing required field 'max_memory_bytes' in resource_limits at {}",
844                    span
845                ))
846            })?,
847            computation_timeout_ms: computation_timeout_ms.ok_or_else(|| {
848                Error::ParseError(format!(
849                    "Missing required field 'computation_timeout_ms' in resource_limits at {}",
850                    span
851                ))
852            })?,
853            max_state_size_bytes: max_state_size_bytes.ok_or_else(|| {
854                Error::ParseError(format!(
855                    "Missing required field 'max_state_size_bytes' in resource_limits at {}",
856                    span
857                ))
858            })?,
859            span,
860        })
861    }
862
863    // ── HumanMachineContract (§1.7) ───────────────────
864
865    fn parse_human_machine_contract(&mut self) -> Result<HumanMachineContractNode> {
866        let span = self.current_span();
867        self.expect(Token::HumanMachineContract)?;
868        self.expect(Token::LBrace)?;
869
870        let mut system_commitments: Option<Vec<SpannedValue<String>>> = None;
871        let mut system_refusals: Option<Vec<SpannedValue<String>>> = None;
872        let mut user_obligations: Option<Vec<SpannedValue<String>>> = None;
873
874        while !matches!(self.peek(), Token::RBrace) {
875            let field_name = self.peek_identifier_name()?;
876            match field_name.as_str() {
877                "system_commitments" => {
878                    self.expect_field("system_commitments")?;
879                    system_commitments = Some(self.parse_string_list()?);
880                }
881                "system_refusals" => {
882                    self.expect_field("system_refusals")?;
883                    system_refusals = Some(self.parse_string_list()?);
884                }
885                "user_obligations" => {
886                    self.expect_field("user_obligations")?;
887                    user_obligations = Some(self.parse_string_list()?);
888                }
889                other => {
890                    return Err(Error::ParseError(format!(
891                        "Unknown field '{}' in HumanMachineContract at {}",
892                        other,
893                        self.current_span()
894                    )));
895                }
896            }
897            self.optional_comma();
898        }
899
900        self.expect(Token::RBrace)?;
901
902        Ok(HumanMachineContractNode {
903            system_commitments: system_commitments.ok_or_else(|| {
904                Error::ParseError(format!(
905                    "Missing required field 'system_commitments' in HumanMachineContract at {}",
906                    span
907                ))
908            })?,
909            system_refusals: system_refusals.ok_or_else(|| {
910                Error::ParseError(format!(
911                    "Missing required field 'system_refusals' in HumanMachineContract at {}",
912                    span
913                ))
914            })?,
915            user_obligations: user_obligations.ok_or_else(|| {
916                Error::ParseError(format!(
917                    "Missing required field 'user_obligations' in HumanMachineContract at {}",
918                    span
919                ))
920            })?,
921            span,
922        })
923    }
924
925    // ── Extensions (§5) ───────────────────────────────
926
927    fn parse_extensions(&mut self) -> Result<ExtensionsNode> {
928        let span = self.current_span();
929        self.expect(Token::Extensions)?;
930        self.expect(Token::LBrace)?;
931
932        let mut systems = Vec::new();
933        while !matches!(self.peek(), Token::RBrace) {
934            systems.push(self.parse_system_extension()?);
935        }
936
937        self.expect(Token::RBrace)?;
938
939        Ok(ExtensionsNode { systems, span })
940    }
941
942    fn parse_system_extension(&mut self) -> Result<SystemExtensionNode> {
943        let span = self.current_span();
944
945        let name_st = self.advance();
946        let name = match name_st.token {
947            Token::Identifier(s) => SpannedValue::new(s, name_st.span),
948            _ => {
949                return Err(Error::ParseError(format!(
950                    "Expected system extension name, found {:?} at {}",
951                    name_st.token, name_st.span
952                )));
953            }
954        };
955
956        self.expect(Token::LBrace)?;
957
958        let mut fields = Vec::new();
959        while !matches!(self.peek(), Token::RBrace) {
960            fields.push(self.parse_custom_field()?);
961            self.optional_comma();
962        }
963
964        self.expect(Token::RBrace)?;
965
966        Ok(SystemExtensionNode { name, fields, span })
967    }
968
969    fn parse_custom_field(&mut self) -> Result<CustomFieldNode> {
970        let span = self.current_span();
971
972        let name_st = self.advance();
973        let name = match name_st.token {
974            Token::Identifier(s) => SpannedValue::new(s, name_st.span),
975            _ => {
976                return Err(Error::ParseError(format!(
977                    "Expected field name, found {:?} at {}",
978                    name_st.token, name_st.span
979                )));
980            }
981        };
982
983        self.expect(Token::Colon)?;
984        let value = self.parse_literal_value()?;
985
986        Ok(CustomFieldNode { name, value, span })
987    }
988
989    // ── Helpers ────────────────────────────────────────
990
991    /// Parse: `[ "str1", "str2", ... ]`
992    fn parse_string_list(&mut self) -> Result<Vec<SpannedValue<String>>> {
993        self.expect(Token::LBracket)?;
994
995        let mut items = Vec::new();
996        if !matches!(self.peek(), Token::RBracket) {
997            items.push(self.expect_string_literal()?);
998            while matches!(self.peek(), Token::Comma) {
999                self.advance(); // consume comma
1000                if matches!(self.peek(), Token::RBracket) {
1001                    break; // trailing comma
1002                }
1003                items.push(self.expect_string_literal()?);
1004            }
1005        }
1006
1007        self.expect(Token::RBracket)?;
1008        Ok(items)
1009    }
1010}
1011
1012// ── Lowering: AST → semantic Contract ──────────────────────
1013
1014/// Convert a parsed AST into a runtime Contract struct.
1015/// This is the bridge between the parser output and the executor input.
1016pub fn lower_contract(node: &ContractNode) -> Result<crate::Contract> {
1017    Ok(crate::Contract {
1018        identity: crate::Identity {
1019            stable_id: node.identity.stable_id.value.clone(),
1020            version: node.identity.version.value as u32,
1021            created_timestamp: node.identity.created_timestamp.value.clone(),
1022            owner: node.identity.owner.value.clone(),
1023            semantic_hash: node.identity.semantic_hash.value.clone(),
1024        },
1025        purpose_statement: crate::PurposeStatement {
1026            narrative: node.purpose_statement.narrative.value.clone(),
1027            intent_source: node.purpose_statement.intent_source.value.clone(),
1028            confidence_level: node.purpose_statement.confidence_level.value,
1029        },
1030        data_semantics: lower_data_semantics(&node.data_semantics),
1031        behavioral_semantics: lower_behavioral_semantics(&node.behavioral_semantics),
1032        execution_constraints: crate::ExecutionConstraints {
1033            trigger_types: node
1034                .execution_constraints
1035                .trigger_types
1036                .iter()
1037                .map(|s| s.value.clone())
1038                .collect(),
1039            resource_limits: crate::ResourceLimits {
1040                max_memory_bytes: node
1041                    .execution_constraints
1042                    .resource_limits
1043                    .max_memory_bytes
1044                    .value as u64,
1045                computation_timeout_ms: node
1046                    .execution_constraints
1047                    .resource_limits
1048                    .computation_timeout_ms
1049                    .value as u64,
1050                max_state_size_bytes: node
1051                    .execution_constraints
1052                    .resource_limits
1053                    .max_state_size_bytes
1054                    .value as u64,
1055            },
1056            external_permissions: node
1057                .execution_constraints
1058                .external_permissions
1059                .iter()
1060                .map(|s| s.value.clone())
1061                .collect(),
1062            sandbox_mode: node.execution_constraints.sandbox_mode.value.clone(),
1063        },
1064        human_machine_contract: crate::HumanMachineContract {
1065            system_commitments: node
1066                .human_machine_contract
1067                .system_commitments
1068                .iter()
1069                .map(|s| s.value.clone())
1070                .collect(),
1071            system_refusals: node
1072                .human_machine_contract
1073                .system_refusals
1074                .iter()
1075                .map(|s| s.value.clone())
1076                .collect(),
1077            user_obligations: node
1078                .human_machine_contract
1079                .user_obligations
1080                .iter()
1081                .map(|s| s.value.clone())
1082                .collect(),
1083        },
1084    })
1085}
1086
1087fn lower_data_semantics(node: &DataSemanticsNode) -> crate::DataSemantics {
1088    let mut state = serde_json::Map::new();
1089    for field in &node.state {
1090        let type_str = field.type_expr.to_string();
1091        let value = if let Some(ref default) = field.default_value {
1092            // Store as {"type": "...", "default": value} to preserve defaults
1093            let default_json = lower_literal(default);
1094            serde_json::json!({
1095                "type": type_str,
1096                "default": default_json
1097            })
1098        } else {
1099            serde_json::Value::String(type_str)
1100        };
1101        state.insert(field.name.value.clone(), value);
1102    }
1103    crate::DataSemantics {
1104        state: serde_json::Value::Object(state),
1105        invariants: node.invariants.iter().map(|s| s.value.clone()).collect(),
1106    }
1107}
1108
1109fn lower_literal(lit: &ast::LiteralValue) -> serde_json::Value {
1110    match lit {
1111        ast::LiteralValue::String(s, _) => serde_json::Value::String(s.clone()),
1112        ast::LiteralValue::Integer(i, _) => serde_json::json!(*i),
1113        ast::LiteralValue::Float(f, _) => serde_json::json!(*f),
1114        ast::LiteralValue::Boolean(b, _) => serde_json::Value::Bool(*b),
1115        ast::LiteralValue::Array(arr, _) => {
1116            serde_json::Value::Array(arr.iter().map(lower_literal).collect())
1117        }
1118    }
1119}
1120
1121fn lower_behavioral_semantics(node: &BehavioralSemanticsNode) -> crate::BehavioralSemantics {
1122    let operations = node
1123        .operations
1124        .iter()
1125        .map(|op| {
1126            let mut params = serde_json::Map::new();
1127            for p in &op.parameters {
1128                params.insert(
1129                    p.name.value.clone(),
1130                    serde_json::Value::String(p.type_expr.to_string()),
1131                );
1132            }
1133
1134            crate::Operation {
1135                name: op.name.value.clone(),
1136                precondition: op.precondition.value.clone(),
1137                parameters: serde_json::Value::Object(params),
1138                postcondition: op.postcondition.value.clone(),
1139                side_effects: op.side_effects.iter().map(|s| s.value.clone()).collect(),
1140                idempotence: op.idempotence.value.clone(),
1141            }
1142        })
1143        .collect();
1144
1145    crate::BehavioralSemantics { operations }
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150    use super::*;
1151    use std::fs;
1152    use std::path::Path;
1153
1154    // ── Helper ─────────────────────────────────────────
1155
1156    fn parse_valid(input: &str) -> ContractNode {
1157        parse(input).unwrap_or_else(|e| panic!("Expected successful parse, got: {}", e))
1158    }
1159
1160    fn parse_err(input: &str) -> String {
1161        parse(input).unwrap_err().to_string()
1162    }
1163
1164    // ── Minimal contract ───────────────────────────────
1165
1166    const MINIMAL_CONTRACT: &str = r#"Contract {
1167  Identity {
1168    stable_id: "ic-test-001",
1169    version: 1,
1170    created_timestamp: 2026-02-01T00:00:00Z,
1171    owner: "test",
1172    semantic_hash: "0000000000000000"
1173  }
1174
1175  PurposeStatement {
1176    narrative: "Minimal test contract",
1177    intent_source: "test",
1178    confidence_level: 1.0
1179  }
1180
1181  DataSemantics {
1182    state: {
1183      value: String
1184    },
1185    invariants: []
1186  }
1187
1188  BehavioralSemantics {
1189    operations: []
1190  }
1191
1192  ExecutionConstraints {
1193    trigger_types: ["manual"],
1194    resource_limits: {
1195      max_memory_bytes: 1048576,
1196      computation_timeout_ms: 100,
1197      max_state_size_bytes: 1048576
1198    },
1199    external_permissions: [],
1200    sandbox_mode: "full_isolation"
1201  }
1202
1203  HumanMachineContract {
1204    system_commitments: [],
1205    system_refusals: [],
1206    user_obligations: []
1207  }
1208}"#;
1209
1210    #[test]
1211    fn test_parse_minimal_contract() {
1212        let ast = parse_valid(MINIMAL_CONTRACT);
1213        assert_eq!(ast.identity.stable_id.value, "ic-test-001");
1214        assert_eq!(ast.identity.version.value, 1);
1215        assert_eq!(ast.identity.owner.value, "test");
1216        assert_eq!(ast.purpose_statement.confidence_level.value, 1.0);
1217        assert_eq!(ast.data_semantics.state.len(), 1);
1218        assert_eq!(ast.data_semantics.state[0].name.value, "value");
1219        assert_eq!(ast.behavioral_semantics.operations.len(), 0);
1220        assert!(ast.extensions.is_none());
1221    }
1222
1223    #[test]
1224    fn test_parse_contract_lowers_correctly() {
1225        let contract = parse_contract(MINIMAL_CONTRACT).unwrap();
1226        assert_eq!(contract.identity.stable_id, "ic-test-001");
1227        assert_eq!(contract.identity.version, 1);
1228        assert_eq!(contract.purpose_statement.confidence_level, 1.0);
1229        assert_eq!(
1230            contract.execution_constraints.sandbox_mode,
1231            "full_isolation"
1232        );
1233    }
1234
1235    // ── Type expressions ───────────────────────────────
1236
1237    #[test]
1238    fn test_parse_all_primitive_types() {
1239        let input = r#"Contract {
1240  Identity {
1241    stable_id: "ic-types-001",
1242    version: 1,
1243    created_timestamp: 2026-02-01T00:00:00Z,
1244    owner: "test",
1245    semantic_hash: "1111111111111111"
1246  }
1247  PurposeStatement {
1248    narrative: "Primitive types test",
1249    intent_source: "test",
1250    confidence_level: 0.95
1251  }
1252  DataSemantics {
1253    state: {
1254      count: Integer = 0,
1255      ratio: Float = 1.0,
1256      label: String,
1257      active: Boolean,
1258      created_at: ISO8601,
1259      user_id: UUID
1260    },
1261    invariants: ["count >= 0"]
1262  }
1263  BehavioralSemantics {
1264    operations: []
1265  }
1266  ExecutionConstraints {
1267    trigger_types: ["manual"],
1268    resource_limits: {
1269      max_memory_bytes: 1048576,
1270      computation_timeout_ms: 100,
1271      max_state_size_bytes: 1048576
1272    },
1273    external_permissions: [],
1274    sandbox_mode: "full_isolation"
1275  }
1276  HumanMachineContract {
1277    system_commitments: [],
1278    system_refusals: [],
1279    user_obligations: []
1280  }
1281}"#;
1282        let ast = parse_valid(input);
1283        assert_eq!(ast.data_semantics.state.len(), 6);
1284
1285        let state = &ast.data_semantics.state;
1286        assert_eq!(state[0].name.value, "count");
1287        assert!(matches!(
1288            state[0].type_expr,
1289            TypeExpression::Primitive(PrimitiveType::Integer, _)
1290        ));
1291        assert!(state[0].default_value.is_some());
1292
1293        assert_eq!(state[3].name.value, "active");
1294        assert!(matches!(
1295            state[3].type_expr,
1296            TypeExpression::Primitive(PrimitiveType::Boolean, _)
1297        ));
1298
1299        assert_eq!(state[4].name.value, "created_at");
1300        assert!(matches!(
1301            state[4].type_expr,
1302            TypeExpression::Primitive(PrimitiveType::Iso8601, _)
1303        ));
1304
1305        assert_eq!(state[5].name.value, "user_id");
1306        assert!(matches!(
1307            state[5].type_expr,
1308            TypeExpression::Primitive(PrimitiveType::Uuid, _)
1309        ));
1310    }
1311
1312    #[test]
1313    fn test_parse_composite_types() {
1314        let input = r#"Contract {
1315  Identity {
1316    stable_id: "ic-composite-001",
1317    version: 1,
1318    created_timestamp: 2026-02-01T00:00:00Z,
1319    owner: "test",
1320    semantic_hash: "2222222222222222"
1321  }
1322  PurposeStatement {
1323    narrative: "Composite types",
1324    intent_source: "test",
1325    confidence_level: 1.0
1326  }
1327  DataSemantics {
1328    state: {
1329      status: Enum["pending", "active", "completed"],
1330      metadata: Object {
1331        key: String,
1332        value: String
1333      },
1334      tags: Array<String>,
1335      scores: Map<String, Integer>
1336    },
1337    invariants: ["status is valid enum value"]
1338  }
1339  BehavioralSemantics {
1340    operations: []
1341  }
1342  ExecutionConstraints {
1343    trigger_types: ["manual"],
1344    resource_limits: {
1345      max_memory_bytes: 1048576,
1346      computation_timeout_ms: 100,
1347      max_state_size_bytes: 1048576
1348    },
1349    external_permissions: [],
1350    sandbox_mode: "full_isolation"
1351  }
1352  HumanMachineContract {
1353    system_commitments: [],
1354    system_refusals: [],
1355    user_obligations: []
1356  }
1357}"#;
1358        let ast = parse_valid(input);
1359        let state = &ast.data_semantics.state;
1360        assert_eq!(state.len(), 4);
1361
1362        // Enum
1363        if let TypeExpression::Enum(variants, _) = &state[0].type_expr {
1364            assert_eq!(variants.len(), 3);
1365            assert_eq!(variants[0].value, "pending");
1366            assert_eq!(variants[2].value, "completed");
1367        } else {
1368            panic!("Expected Enum type");
1369        }
1370
1371        // Object
1372        if let TypeExpression::Object(fields, _) = &state[1].type_expr {
1373            assert_eq!(fields.len(), 2);
1374            assert_eq!(fields[0].name.value, "key");
1375        } else {
1376            panic!("Expected Object type");
1377        }
1378
1379        // Array<String>
1380        assert!(matches!(&state[2].type_expr, TypeExpression::Array(_, _)));
1381
1382        // Map<String, Integer>
1383        assert!(matches!(&state[3].type_expr, TypeExpression::Map(_, _, _)));
1384    }
1385
1386    // ── Operations ─────────────────────────────────────
1387
1388    #[test]
1389    fn test_parse_multiple_operations() {
1390        let input = r#"Contract {
1391  Identity {
1392    stable_id: "ic-ops-001",
1393    version: 2,
1394    created_timestamp: 2026-02-01T00:00:00Z,
1395    owner: "test",
1396    semantic_hash: "4444444444444444"
1397  }
1398  PurposeStatement {
1399    narrative: "Multiple operations",
1400    intent_source: "test",
1401    confidence_level: 0.99
1402  }
1403  DataSemantics {
1404    state: {
1405      items: Array<String>,
1406      count: Integer = 0
1407    },
1408    invariants: ["count >= 0"]
1409  }
1410  BehavioralSemantics {
1411    operations: [
1412      {
1413        name: "add_item",
1414        precondition: "item_not_duplicate",
1415        parameters: {
1416          item: String
1417        },
1418        postcondition: "item_added",
1419        side_effects: ["log_addition"],
1420        idempotence: "not_idempotent"
1421      },
1422      {
1423        name: "clear_all",
1424        precondition: "items_not_empty",
1425        parameters: {},
1426        postcondition: "items_empty",
1427        side_effects: ["log_clear"],
1428        idempotence: "idempotent"
1429      }
1430    ]
1431  }
1432  ExecutionConstraints {
1433    trigger_types: ["manual", "event_based"],
1434    resource_limits: {
1435      max_memory_bytes: 2097152,
1436      computation_timeout_ms: 200,
1437      max_state_size_bytes: 1048576
1438    },
1439    external_permissions: [],
1440    sandbox_mode: "full_isolation"
1441  }
1442  HumanMachineContract {
1443    system_commitments: ["Items managed correctly"],
1444    system_refusals: ["No duplicate items"],
1445    user_obligations: ["May add or remove items"]
1446  }
1447}"#;
1448        let ast = parse_valid(input);
1449        assert_eq!(ast.behavioral_semantics.operations.len(), 2);
1450
1451        let op1 = &ast.behavioral_semantics.operations[0];
1452        assert_eq!(op1.name.value, "add_item");
1453        assert_eq!(op1.parameters.len(), 1);
1454        assert_eq!(op1.parameters[0].name.value, "item");
1455
1456        let op2 = &ast.behavioral_semantics.operations[1];
1457        assert_eq!(op2.name.value, "clear_all");
1458        assert_eq!(op2.parameters.len(), 0);
1459    }
1460
1461    // ── Extensions ─────────────────────────────────────
1462
1463    #[test]
1464    fn test_parse_with_extensions() {
1465        let input = format!("{}\n\nExtensions {{\n  custom_system {{\n    priority: \"high\",\n    tags: [\"experimental\", \"beta\"]\n  }}\n}}", MINIMAL_CONTRACT);
1466        let ast = parse_valid(&input);
1467
1468        let ext = ast.extensions.as_ref().expect("Expected extensions");
1469        assert_eq!(ext.systems.len(), 1);
1470        assert_eq!(ext.systems[0].name.value, "custom_system");
1471        assert_eq!(ext.systems[0].fields.len(), 2);
1472        assert_eq!(ext.systems[0].fields[0].name.value, "priority");
1473
1474        if let LiteralValue::Array(items, _) = &ext.systems[0].fields[1].value {
1475            assert_eq!(items.len(), 2);
1476        } else {
1477            panic!("Expected array value for tags");
1478        }
1479    }
1480
1481    // ── Invalid inputs ─────────────────────────────────
1482
1483    #[test]
1484    fn test_parse_missing_identity() {
1485        let input = r#"Contract {
1486  PurposeStatement {
1487    narrative: "No identity",
1488    intent_source: "test",
1489    confidence_level: 1.0
1490  }
1491}"#;
1492        let err = parse_err(input);
1493        assert!(err.contains("Expected Identity"), "Error: {}", err);
1494    }
1495
1496    #[test]
1497    fn test_parse_missing_closing_brace() {
1498        let input = r#"Contract {
1499  Identity {
1500    stable_id: "ic-test-001",
1501    version: 1,
1502    created_timestamp: 2026-02-01T00:00:00Z,
1503    owner: "test",
1504    semantic_hash: "0000000000000000"
1505  }
1506"#;
1507        let err = parse_err(input);
1508        // Should fail: missing closing brace means it expects PurposeStatement but hits Eof
1509        assert!(
1510            err.contains("Expected") || err.contains("found"),
1511            "Error: {}",
1512            err
1513        );
1514    }
1515
1516    #[test]
1517    fn test_parse_wrong_version_type() {
1518        let input = r#"Contract {
1519  Identity {
1520    stable_id: "ic-test-001",
1521    version: "not_an_integer",
1522    created_timestamp: 2026-02-01T00:00:00Z,
1523    owner: "test",
1524    semantic_hash: "0000000000000000"
1525  }
1526}"#;
1527        let err = parse_err(input);
1528        assert!(err.contains("Expected integer literal"), "Error: {}", err);
1529    }
1530
1531    #[test]
1532    fn test_parse_confidence_out_of_range() {
1533        let input = r#"Contract {
1534  Identity {
1535    stable_id: "ic-test-001",
1536    version: 1,
1537    created_timestamp: 2026-02-01T00:00:00Z,
1538    owner: "test",
1539    semantic_hash: "0000000000000000"
1540  }
1541  PurposeStatement {
1542    narrative: "Invalid confidence",
1543    intent_source: "test",
1544    confidence_level: 2.5
1545  }
1546}"#;
1547        let err = parse_err(input);
1548        assert!(err.contains("confidence_level"), "Error: {}", err);
1549    }
1550
1551    #[test]
1552    fn test_parse_unknown_section() {
1553        let input = r#"Contract {
1554  Identity {
1555    stable_id: "ic-test-001",
1556    version: 1,
1557    created_timestamp: 2026-02-01T00:00:00Z,
1558    owner: "test",
1559    semantic_hash: "0000000000000000"
1560  }
1561  FakeSection {
1562    something: "invalid"
1563  }
1564}"#;
1565        let err = parse_err(input);
1566        // Parser expects PurposeStatement, finds Identifier("FakeSection")
1567        assert!(
1568            err.contains("Expected") || err.contains("PurposeStatement"),
1569            "Error: {}",
1570            err
1571        );
1572    }
1573
1574    // ── Conformance fixtures (file-based) ──────────────
1575
1576    fn read_fixture(path: &str) -> String {
1577        let full = Path::new(env!("CARGO_MANIFEST_DIR"))
1578            .join("../../tests/fixtures")
1579            .join(path);
1580        fs::read_to_string(&full)
1581            .unwrap_or_else(|e| panic!("Failed to read {}: {}", full.display(), e))
1582    }
1583
1584    #[test]
1585    fn test_conformance_valid_minimal_contract() {
1586        let input = read_fixture("conformance/valid/minimal-contract.icl");
1587        let ast = parse_valid(&input);
1588        assert_eq!(ast.identity.stable_id.value, "ic-test-001");
1589    }
1590
1591    #[test]
1592    fn test_conformance_valid_all_primitive_types() {
1593        let input = read_fixture("conformance/valid/all-primitive-types.icl");
1594        let ast = parse_valid(&input);
1595        assert_eq!(ast.data_semantics.state.len(), 6);
1596    }
1597
1598    #[test]
1599    fn test_conformance_valid_composite_types() {
1600        let input = read_fixture("conformance/valid/composite-types.icl");
1601        let ast = parse_valid(&input);
1602        assert_eq!(ast.data_semantics.state.len(), 4);
1603    }
1604
1605    #[test]
1606    fn test_conformance_valid_multiple_operations() {
1607        let input = read_fixture("conformance/valid/multiple-operations.icl");
1608        let ast = parse_valid(&input);
1609        assert_eq!(ast.behavioral_semantics.operations.len(), 3);
1610    }
1611
1612    #[test]
1613    fn test_conformance_valid_with_extensions() {
1614        let input = read_fixture("conformance/valid/with-extensions.icl");
1615        let ast = parse_valid(&input);
1616        assert!(ast.extensions.is_some());
1617    }
1618
1619    #[test]
1620    fn test_conformance_invalid_missing_identity() {
1621        let input = read_fixture("conformance/invalid/missing-identity.icl");
1622        assert!(parse(&input).is_err());
1623    }
1624
1625    #[test]
1626    fn test_conformance_invalid_missing_closing_brace() {
1627        let input = read_fixture("conformance/invalid/missing-closing-brace.icl");
1628        assert!(parse(&input).is_err());
1629    }
1630
1631    #[test]
1632    fn test_conformance_invalid_wrong_version_type() {
1633        let input = read_fixture("conformance/invalid/wrong-version-type.icl");
1634        assert!(parse(&input).is_err());
1635    }
1636
1637    #[test]
1638    fn test_conformance_invalid_confidence_out_of_range() {
1639        let input = read_fixture("conformance/invalid/confidence-out-of-range.icl");
1640        assert!(parse(&input).is_err());
1641    }
1642
1643    #[test]
1644    fn test_conformance_invalid_unknown_section() {
1645        let input = read_fixture("conformance/invalid/unknown-section.icl");
1646        assert!(parse(&input).is_err());
1647    }
1648
1649    // ── Determinism proof ──────────────────────────────
1650
1651    #[test]
1652    fn test_parse_determinism_100_iterations() {
1653        let first = parse(MINIMAL_CONTRACT).unwrap();
1654
1655        for i in 0..100 {
1656            let result = parse(MINIMAL_CONTRACT).unwrap();
1657            assert_eq!(first, result, "Determinism failure at iteration {}", i);
1658        }
1659    }
1660
1661    #[test]
1662    fn test_parse_determinism_complex_contract() {
1663        let input = read_fixture("conformance/valid/all-primitive-types.icl");
1664        let first = parse(&input).unwrap();
1665
1666        for i in 0..100 {
1667            let result = parse(&input).unwrap();
1668            assert_eq!(first, result, "Determinism failure at iteration {}", i);
1669        }
1670    }
1671
1672    // ── Empty input / edge cases ───────────────────────
1673
1674    #[test]
1675    fn test_parse_empty_input() {
1676        assert!(parse("").is_err());
1677    }
1678
1679    #[test]
1680    fn test_parse_just_contract_keyword() {
1681        assert!(parse("Contract").is_err());
1682    }
1683
1684    #[test]
1685    fn test_parse_empty_state() {
1686        // Contract with empty state: {} should parse
1687        let input = r#"Contract {
1688  Identity {
1689    stable_id: "ic-test-001",
1690    version: 1,
1691    created_timestamp: 2026-02-01T00:00:00Z,
1692    owner: "test",
1693    semantic_hash: "0000000000000000"
1694  }
1695  PurposeStatement {
1696    narrative: "Empty state",
1697    intent_source: "test",
1698    confidence_level: 0.5
1699  }
1700  DataSemantics {
1701    state: {},
1702    invariants: []
1703  }
1704  BehavioralSemantics {
1705    operations: []
1706  }
1707  ExecutionConstraints {
1708    trigger_types: ["manual"],
1709    resource_limits: {
1710      max_memory_bytes: 1048576,
1711      computation_timeout_ms: 100,
1712      max_state_size_bytes: 1048576
1713    },
1714    external_permissions: [],
1715    sandbox_mode: "full_isolation"
1716  }
1717  HumanMachineContract {
1718    system_commitments: [],
1719    system_refusals: [],
1720    user_obligations: []
1721  }
1722}"#;
1723        let ast = parse_valid(input);
1724        assert_eq!(ast.data_semantics.state.len(), 0);
1725    }
1726}