Skip to main content

sage_parser/
parser.rs

1//! Parser implementation using chumsky.
2//!
3//! This module transforms a token stream into an AST.
4
5use crate::ast::{
6    AgentDecl, BeliefDecl, BinOp, Block, ChildSpec, ClosureParam, ConstDecl, EffectHandlerDecl,
7    ElseBranch, EnumDecl, EventKind, Expr, FieldInit, FnDecl, HandlerAssignment, HandlerConfig,
8    HandlerDecl, Literal, MapEntry, MatchArm, MockValue, ModDecl, Param, Pattern, Program,
9    ProtocolDecl, ProtocolRole, ProtocolStep, RecordDecl, RecordField, RestartPolicy, Stmt,
10    StringPart, StringTemplate, SupervisionStrategy, SupervisorDecl, TestDecl, ToolDecl,
11    ToolFnDecl, UnaryOp, UseDecl, UseKind,
12};
13use crate::{Ident, Span, TypeExpr};
14use crate::{Spanned, Token};
15use chumsky::prelude::*;
16use chumsky::BoxedParser;
17use std::ops::Range;
18use std::sync::Arc;
19
20/// Parse error type using byte range spans.
21pub type ParseError = Simple<Token>;
22
23/// Parse a sequence of tokens into a Program AST.
24///
25/// # Errors
26///
27/// Returns parse errors if the token stream doesn't form a valid program.
28#[must_use]
29#[allow(clippy::needless_pass_by_value)] // Arc<str> is cheap to clone and idiomatic here
30pub fn parse(tokens: &[Spanned], source: Arc<str>) -> (Option<Program>, Vec<ParseError>) {
31    let len = source.len();
32
33    // Convert our Spanned tokens to (Token, Range<usize>) for chumsky
34    let token_spans: Vec<(Token, Range<usize>)> = tokens
35        .iter()
36        .map(|s| (s.token.clone(), s.start..s.end))
37        .collect();
38
39    let stream = chumsky::Stream::from_iter(len..len, token_spans.into_iter());
40
41    let (ast, errors) = program_parser(Arc::clone(&source)).parse_recovery(stream);
42
43    (ast, errors)
44}
45
46// =============================================================================
47// Top-level parsers
48// =============================================================================
49
50/// Parser for a complete program.
51#[allow(clippy::needless_pass_by_value)]
52fn program_parser(source: Arc<str>) -> impl Parser<Token, Program, Error = ParseError> {
53    let src = source.clone();
54    let src2 = source.clone();
55
56    // Top-level declarations with recovery - skip to next keyword on error
57    let top_level = mod_parser(source.clone())
58        .or(use_parser(source.clone()))
59        .or(record_parser(source.clone()))
60        .or(enum_parser(source.clone()))
61        .or(const_parser(source.clone()))
62        .or(tool_parser(source.clone()))
63        .or(protocol_parser(source.clone()))
64        .or(effect_handler_parser(source.clone()))
65        .or(agent_parser(source.clone()))
66        .or(supervisor_parser(source.clone()))
67        .or(fn_parser(source.clone()))
68        .or(test_parser(source.clone()))
69        .recover_with(skip_then_retry_until([
70            Token::KwMod,
71            Token::KwUse,
72            Token::KwPub,
73            Token::KwRecord,
74            Token::KwEnum,
75            Token::KwConst,
76            Token::KwTool,
77            Token::KwProtocol,
78            Token::KwHandler,
79            Token::KwAgent,
80            Token::KwSupervisor,
81            Token::KwFn,
82            Token::KwRun,
83            Token::KwTest,
84        ]));
85
86    let run_stmt = just(Token::KwRun)
87        .ignore_then(ident_token_parser(src.clone()))
88        .then_ignore(just(Token::Semicolon))
89        .or_not();
90
91    top_level.repeated().then(run_stmt).map_with_span(
92        move |(items, run_agent), span: Range<usize>| {
93            let mut mod_decls = Vec::new();
94            let mut use_decls = Vec::new();
95            let mut records = Vec::new();
96            let mut enums = Vec::new();
97            let mut consts = Vec::new();
98            let mut tools = Vec::new();
99            let mut protocols = Vec::new();
100            let mut effect_handlers = Vec::new();
101            let mut agents = Vec::new();
102            let mut supervisors = Vec::new();
103            let mut functions = Vec::new();
104            let mut tests = Vec::new();
105
106            for item in items {
107                match item {
108                    TopLevel::Mod(m) => mod_decls.push(m),
109                    TopLevel::Use(u) => use_decls.push(u),
110                    TopLevel::Record(r) => records.push(r),
111                    TopLevel::Enum(e) => enums.push(e),
112                    TopLevel::Const(c) => consts.push(c),
113                    TopLevel::Tool(t) => tools.push(t),
114                    TopLevel::Protocol(p) => protocols.push(p),
115                    TopLevel::EffectHandler(h) => effect_handlers.push(h),
116                    TopLevel::Agent(a) => agents.push(a),
117                    TopLevel::Supervisor(s) => supervisors.push(s),
118                    TopLevel::Function(f) => functions.push(f),
119                    TopLevel::Test(t) => tests.push(t),
120                }
121            }
122
123            Program {
124                mod_decls,
125                use_decls,
126                records,
127                enums,
128                consts,
129                tools,
130                protocols,
131                effect_handlers,
132                agents,
133                supervisors,
134                functions,
135                tests,
136                run_agent,
137                span: make_span(&src2, span),
138            }
139        },
140    )
141}
142
143/// Helper enum for collecting top-level declarations.
144enum TopLevel {
145    Mod(ModDecl),
146    Use(UseDecl),
147    Record(RecordDecl),
148    Enum(EnumDecl),
149    Const(ConstDecl),
150    Tool(ToolDecl),
151    Protocol(ProtocolDecl),
152    EffectHandler(EffectHandlerDecl),
153    Agent(AgentDecl),
154    Supervisor(SupervisorDecl),
155    Function(FnDecl),
156    Test(TestDecl),
157}
158
159// =============================================================================
160// Module declaration parsers
161// =============================================================================
162
163/// Parser for a mod declaration: `mod foo` or `pub mod foo`
164#[allow(clippy::needless_pass_by_value)]
165fn mod_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
166    let src = source.clone();
167
168    just(Token::KwPub)
169        .or_not()
170        .then_ignore(just(Token::KwMod))
171        .then(ident_token_parser(src.clone()))
172        .then_ignore(just(Token::Semicolon))
173        .map_with_span(move |(is_pub, name), span: Range<usize>| {
174            TopLevel::Mod(ModDecl {
175                is_pub: is_pub.is_some(),
176                name,
177                span: make_span(&src, span),
178            })
179        })
180}
181
182/// Parser for a use declaration: `use path::to::Item` or `use path::{A, B}`
183#[allow(clippy::needless_pass_by_value)]
184fn use_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
185    let src = source.clone();
186    let src2 = source.clone();
187    let src3 = source.clone();
188    let src4 = source.clone();
189
190    // Simple use: `use a::b::C` or `use a::b::C as D`
191    let simple_use = just(Token::KwPub)
192        .or_not()
193        .then_ignore(just(Token::KwUse))
194        .then(
195            ident_token_parser(src.clone())
196                .separated_by(just(Token::ColonColon))
197                .at_least(1),
198        )
199        .then(
200            just(Token::KwAs)
201                .ignore_then(ident_token_parser(src.clone()))
202                .or_not(),
203        )
204        .then_ignore(just(Token::Semicolon))
205        .map_with_span(move |((is_pub, path), alias), span: Range<usize>| {
206            TopLevel::Use(UseDecl {
207                is_pub: is_pub.is_some(),
208                path,
209                kind: UseKind::Simple(alias),
210                span: make_span(&src, span),
211            })
212        });
213
214    // Group import item: `Name` or `Name as Alias`
215    let group_item = ident_token_parser(src2.clone()).then(
216        just(Token::KwAs)
217            .ignore_then(ident_token_parser(src2.clone()))
218            .or_not(),
219    );
220
221    // Group use: `use a::b::{C, D as E}`
222    let group_use = just(Token::KwPub)
223        .or_not()
224        .then_ignore(just(Token::KwUse))
225        .then(
226            ident_token_parser(src3.clone())
227                .then_ignore(just(Token::ColonColon))
228                .repeated()
229                .at_least(1),
230        )
231        .then(
232            group_item
233                .separated_by(just(Token::Comma))
234                .allow_trailing()
235                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
236        )
237        .then_ignore(just(Token::Semicolon))
238        .map_with_span(move |((is_pub, path), items), span: Range<usize>| {
239            TopLevel::Use(UseDecl {
240                is_pub: is_pub.is_some(),
241                path,
242                kind: UseKind::Group(items),
243                span: make_span(&src3, span),
244            })
245        });
246
247    // Glob use: `use a::b::*`
248    let glob_use = just(Token::KwPub)
249        .or_not()
250        .then_ignore(just(Token::KwUse))
251        .then(
252            ident_token_parser(src4.clone())
253                .then_ignore(just(Token::ColonColon))
254                .repeated()
255                .at_least(1),
256        )
257        .then_ignore(just(Token::Star))
258        .then_ignore(just(Token::Semicolon))
259        .map_with_span(move |(is_pub, path), span: Range<usize>| {
260            TopLevel::Use(UseDecl {
261                is_pub: is_pub.is_some(),
262                path,
263                kind: UseKind::Glob,
264                span: make_span(&src4, span),
265            })
266        });
267
268    // Try group/glob first (they need :: before { or *), then simple
269    group_use.or(glob_use).or(simple_use)
270}
271
272// =============================================================================
273// Record, Enum, Const parsers
274// =============================================================================
275
276/// Parser for a record declaration: `record Point { x: Int, y: Int }`
277#[allow(clippy::needless_pass_by_value)]
278/// Parser for type parameters: `<T>`, `<A, B>`, `<T, U, V>`, etc.
279/// Returns empty Vec if no type parameters present.
280fn type_params_parser(
281    source: Arc<str>,
282) -> impl Parser<Token, Vec<Ident>, Error = ParseError> + Clone {
283    ident_token_parser(source)
284        .separated_by(just(Token::Comma))
285        .allow_trailing()
286        .delimited_by(just(Token::Lt), just(Token::Gt))
287        .or_not()
288        .map(|params| params.unwrap_or_default())
289}
290
291fn record_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
292    let src = source.clone();
293    let src2 = source.clone();
294
295    // Record field: `name: Type`
296    let field = ident_token_parser(src.clone())
297        .then_ignore(just(Token::Colon))
298        .then(type_parser(src.clone()))
299        .map_with_span(move |(name, ty), span: Range<usize>| RecordField {
300            name,
301            ty,
302            span: make_span(&src, span),
303        });
304
305    just(Token::KwPub)
306        .or_not()
307        .then_ignore(just(Token::KwRecord))
308        .then(ident_token_parser(src2.clone()))
309        .then(type_params_parser(src2.clone()))
310        .then(
311            field
312                .separated_by(just(Token::Comma))
313                .allow_trailing()
314                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
315        )
316        .map_with_span(
317            move |(((is_pub, name), type_params), fields), span: Range<usize>| {
318                TopLevel::Record(RecordDecl {
319                    is_pub: is_pub.is_some(),
320                    name,
321                    type_params,
322                    fields,
323                    span: make_span(&src2, span),
324                })
325            },
326        )
327}
328
329/// Parser for an enum declaration: `enum Status { Active, Pending, Done }` or `enum Result { Ok(T), Err(E) }`
330#[allow(clippy::needless_pass_by_value)]
331fn enum_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
332    let src = source.clone();
333    let src2 = source.clone();
334    let src3 = source.clone();
335
336    // Enum variant with optional payload: `Ok(T)` or `None`
337    let variant = ident_token_parser(src.clone())
338        .then(
339            type_parser(src.clone())
340                .delimited_by(just(Token::LParen), just(Token::RParen))
341                .or_not(),
342        )
343        .map_with_span({
344            let src = src.clone();
345            move |(name, payload), span: Range<usize>| crate::ast::EnumVariant {
346                name,
347                payload,
348                span: make_span(&src, span),
349            }
350        });
351
352    just(Token::KwPub)
353        .or_not()
354        .then_ignore(just(Token::KwEnum))
355        .then(ident_token_parser(src3.clone()))
356        .then(type_params_parser(src3.clone()))
357        .then(
358            variant
359                .separated_by(just(Token::Comma))
360                .allow_trailing()
361                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
362        )
363        .map_with_span(
364            move |(((is_pub, name), type_params), variants), span: Range<usize>| {
365                TopLevel::Enum(EnumDecl {
366                    is_pub: is_pub.is_some(),
367                    name,
368                    type_params,
369                    variants,
370                    span: make_span(&src2, span),
371                })
372            },
373        )
374}
375
376/// Parser for a const declaration: `const MAX_RETRIES: Int = 3`
377#[allow(clippy::needless_pass_by_value)]
378fn const_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
379    let src = source.clone();
380    let src2 = source.clone();
381
382    just(Token::KwPub)
383        .or_not()
384        .then_ignore(just(Token::KwConst))
385        .then(ident_token_parser(src.clone()))
386        .then_ignore(just(Token::Colon))
387        .then(type_parser(src.clone()))
388        .then_ignore(just(Token::Eq))
389        .then(expr_parser(src.clone()))
390        .then_ignore(just(Token::Semicolon))
391        .map_with_span(move |(((is_pub, name), ty), value), span: Range<usize>| {
392            TopLevel::Const(ConstDecl {
393                is_pub: is_pub.is_some(),
394                name,
395                ty,
396                value,
397                span: make_span(&src2, span),
398            })
399        })
400}
401
402// =============================================================================
403// Tool parsers (RFC-0011)
404// =============================================================================
405
406/// Parser for a tool declaration: `tool Http { fn get(url: String) -> String }`
407#[allow(clippy::needless_pass_by_value)]
408fn tool_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
409    let src = source.clone();
410    let src2 = source.clone();
411    let src3 = source.clone();
412
413    // Tool function parameter: `name: Type`
414    let param = ident_token_parser(src.clone())
415        .then_ignore(just(Token::Colon))
416        .then(type_parser(src.clone()))
417        .map_with_span(move |(name, ty), span: Range<usize>| Param {
418            name,
419            ty,
420            span: make_span(&src, span),
421        });
422
423    let params = param
424        .separated_by(just(Token::Comma))
425        .allow_trailing()
426        .delimited_by(just(Token::LParen), just(Token::RParen));
427
428    // Tool function signature: `fn name(params) -> ReturnType`
429    let tool_fn = just(Token::KwFn)
430        .ignore_then(ident_token_parser(src2.clone()))
431        .then(params)
432        .then_ignore(just(Token::Arrow))
433        .then(type_parser(src2.clone()))
434        .map_with_span(
435            move |((name, params), return_ty), span: Range<usize>| ToolFnDecl {
436                name,
437                params,
438                return_ty,
439                span: make_span(&src2, span),
440            },
441        );
442
443    just(Token::KwPub)
444        .or_not()
445        .then_ignore(just(Token::KwTool))
446        .then(ident_token_parser(src3.clone()))
447        .then(
448            tool_fn
449                .repeated()
450                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
451        )
452        .map_with_span(move |((is_pub, name), functions), span: Range<usize>| {
453            TopLevel::Tool(ToolDecl {
454                is_pub: is_pub.is_some(),
455                name,
456                functions,
457                span: make_span(&src3, span),
458            })
459        })
460}
461
462// =============================================================================
463// Protocol parsers (Phase 3 session types)
464// =============================================================================
465
466/// Parser for a protocol declaration.
467///
468/// Syntax:
469/// ```sage
470/// protocol SchemaSync {
471///     DatabaseSteward -> APISteward: SchemaChanged
472///     APISteward -> DatabaseSteward: Acknowledged
473/// }
474/// ```
475#[allow(clippy::needless_pass_by_value)]
476fn protocol_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
477    let src = source.clone();
478    let src2 = source.clone();
479    let src3 = source.clone();
480
481    // Protocol step: Sender -> Receiver: MessageType
482    let protocol_step = ident_token_parser(src.clone())
483        .then_ignore(just(Token::Arrow))
484        .then(ident_token_parser(src.clone()))
485        .then_ignore(just(Token::Colon))
486        .then(type_parser(src.clone()))
487        .map_with_span({
488            let src = src.clone();
489            move |((sender, receiver), message_type), span: Range<usize>| ProtocolStep {
490                sender,
491                receiver,
492                message_type,
493                span: make_span(&src, span),
494            }
495        });
496
497    // Full protocol declaration
498    just(Token::KwPub)
499        .or_not()
500        .then_ignore(just(Token::KwProtocol))
501        .then(ident_token_parser(src2.clone()))
502        .then(
503            protocol_step
504                .repeated()
505                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
506        )
507        .map_with_span(move |((is_pub, name), steps), span: Range<usize>| {
508            TopLevel::Protocol(ProtocolDecl {
509                is_pub: is_pub.is_some(),
510                name,
511                steps,
512                span: make_span(&src3, span),
513            })
514        })
515}
516
517// =============================================================================
518// Effect handler parsers (Phase 3 algebraic effects)
519// =============================================================================
520
521/// Parser for an effect handler declaration.
522///
523/// Syntax:
524/// ```sage
525/// handler DefaultLLM handles Infer {
526///     model: "gpt-4o"
527///     temperature: 0.7
528///     max_tokens: 1024
529/// }
530/// ```
531#[allow(clippy::needless_pass_by_value)]
532fn effect_handler_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
533    let src = source.clone();
534    let src2 = source.clone();
535
536    // Handler config entry: key: value (value must be a literal)
537    let config_entry = ident_token_parser(src.clone())
538        .then_ignore(just(Token::Colon))
539        .then(literal_value_parser(src.clone()))
540        .map_with_span({
541            let src = src.clone();
542            move |(key, value), span: Range<usize>| HandlerConfig {
543                key,
544                value,
545                span: make_span(&src, span),
546            }
547        });
548
549    // Full handler declaration
550    just(Token::KwPub)
551        .or_not()
552        .then_ignore(just(Token::KwHandler))
553        .then(ident_token_parser(src2.clone()))
554        .then_ignore(just(Token::KwHandles))
555        .then(ident_token_parser(src2.clone()))
556        .then(
557            config_entry
558                .repeated()
559                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
560        )
561        .map_with_span(move |(((is_pub, name), effect), config), span: Range<usize>| {
562            TopLevel::EffectHandler(EffectHandlerDecl {
563                is_pub: is_pub.is_some(),
564                name,
565                effect,
566                config,
567                span: make_span(&src2, span),
568            })
569        })
570}
571
572/// Parser for literal values only (for handler config).
573#[allow(clippy::needless_pass_by_value)]
574fn literal_value_parser(source: Arc<str>) -> impl Parser<Token, Literal, Error = ParseError> {
575    let src = source.clone();
576
577    filter_map(move |span: Range<usize>, tok| match tok {
578        Token::IntLit => {
579            let s = &src[span.clone()];
580            let n = s.parse::<i64>().map_err(|_| {
581                Simple::custom(span, format!("invalid integer literal `{s}`"))
582            })?;
583            Ok(Literal::Int(n))
584        }
585        Token::FloatLit => {
586            let s = &src[span.clone()];
587            let n = s.parse::<f64>().map_err(|_| {
588                Simple::custom(span, format!("invalid float literal `{s}`"))
589            })?;
590            Ok(Literal::Float(n))
591        }
592        Token::StringLit => {
593            let s = &src[span.clone()];
594            // Remove quotes and handle escape sequences
595            let content = &s[1..s.len() - 1];
596            let unescaped = unescape_string(content);
597            Ok(Literal::String(unescaped))
598        }
599        Token::KwTrue => Ok(Literal::Bool(true)),
600        Token::KwFalse => Ok(Literal::Bool(false)),
601        _ => Err(Simple::expected_input_found(span, [], Some(tok))),
602    })
603}
604
605/// Helper to unescape string literals.
606fn unescape_string(s: &str) -> String {
607    let mut result = String::new();
608    let mut chars = s.chars().peekable();
609    while let Some(c) = chars.next() {
610        if c == '\\' {
611            match chars.next() {
612                Some('n') => result.push('\n'),
613                Some('t') => result.push('\t'),
614                Some('r') => result.push('\r'),
615                Some('\\') => result.push('\\'),
616                Some('"') => result.push('"'),
617                Some('\'') => result.push('\''),
618                Some(other) => {
619                    result.push('\\');
620                    result.push(other);
621                }
622                None => result.push('\\'),
623            }
624        } else {
625            result.push(c);
626        }
627    }
628    result
629}
630
631// =============================================================================
632// Test parsers (RFC-0012)
633// =============================================================================
634
635/// Parser for a test declaration: `test "name" { ... }` or `@serial test "name" { ... }`
636#[allow(clippy::needless_pass_by_value)]
637fn test_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
638    let src = source.clone();
639    let src2 = source.clone();
640
641    // Parse @serial annotation (@ followed by identifier "serial")
642    // For now, we'll use a simple approach: look for @ then ident
643    let serial_annotation = just(Token::At)
644        .then(filter(|t: &Token| matches!(t, Token::Ident)))
645        .or_not()
646        .map(|opt| opt.is_some());
647
648    // Parse test name (string literal)
649    let test_name = filter_map(|span: Range<usize>, tok: Token| match tok {
650        Token::StringLit => Ok(()),
651        _ => Err(Simple::expected_input_found(span, [], Some(tok))),
652    })
653    .map_with_span(move |_, span: Range<usize>| {
654        // Extract the string content without quotes (handles both " and ')
655        let s = &src[span.clone()];
656        s[1..s.len() - 1].to_string()
657    });
658
659    // Test body - use the statement parser
660    let body = block_parser(src2.clone());
661
662    serial_annotation
663        .then_ignore(just(Token::KwTest))
664        .then(test_name)
665        .then(body)
666        .map_with_span(move |((is_serial, name), body), span: Range<usize>| {
667            TopLevel::Test(TestDecl {
668                name,
669                is_serial,
670                body,
671                span: make_span(&src2, span),
672            })
673        })
674}
675
676// =============================================================================
677// Supervisor parsers (v2 supervision trees)
678// =============================================================================
679
680/// Parser for a supervisor declaration.
681///
682/// Syntax:
683/// ```sage
684/// supervisor Name {
685///     strategy: OneForOne
686///     children {
687///         AgentName { restart: Permanent, belief: value }
688///     }
689/// }
690/// ```
691#[allow(clippy::needless_pass_by_value)]
692fn supervisor_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
693    let src = source.clone();
694    let src2 = source.clone();
695    let src3 = source.clone();
696    let src4 = source.clone();
697
698    // Strategy parser: strategy: OneForOne | OneForAll | RestForOne
699    let strategy = just(Token::KwStrategy)
700        .ignore_then(just(Token::Colon))
701        .ignore_then(filter_map({
702            let src = src.clone();
703            move |span: Range<usize>, token| match token {
704                Token::Ident => {
705                    let text = &src[span.start..span.end];
706                    match text {
707                        "OneForOne" => Ok(SupervisionStrategy::OneForOne),
708                        "OneForAll" => Ok(SupervisionStrategy::OneForAll),
709                        "RestForOne" => Ok(SupervisionStrategy::RestForOne),
710                        _ => Err(Simple::custom(
711                            span,
712                            format!("unknown strategy `{text}`, expected OneForOne, OneForAll, or RestForOne"),
713                        )),
714                    }
715                }
716                _ => Err(Simple::expected_input_found(
717                    span,
718                    vec![Some(Token::Ident)],
719                    Some(token),
720                )),
721            }
722        }));
723
724    // Restart policy parser: restart: Permanent | Transient | Temporary
725    let restart_policy = just(Token::KwRestart)
726        .ignore_then(just(Token::Colon))
727        .ignore_then(filter_map({
728            let src = src2.clone();
729            move |span: Range<usize>, token| match token {
730                Token::Ident => {
731                    let text = &src[span.start..span.end];
732                    match text {
733                        "Permanent" => Ok(RestartPolicy::Permanent),
734                        "Transient" => Ok(RestartPolicy::Transient),
735                        "Temporary" => Ok(RestartPolicy::Temporary),
736                        _ => Err(Simple::custom(
737                            span,
738                            format!("unknown restart policy `{text}`, expected Permanent, Transient, or Temporary"),
739                        )),
740                    }
741                }
742                _ => Err(Simple::expected_input_found(
743                    span,
744                    vec![Some(Token::Ident)],
745                    Some(token),
746                )),
747            }
748        }));
749
750    // Handler assignment: handler Effect: HandlerName (Phase 3: Algebraic Effects)
751    let src_handler = src3.clone();
752    let handler_assignment = just(Token::KwHandler)
753        .ignore_then(ident_token_parser(src3.clone()))
754        .then_ignore(just(Token::Colon))
755        .then(ident_token_parser(src3.clone()))
756        .map_with_span(move |(effect, handler), span: Range<usize>| HandlerAssignment {
757            effect,
758            handler,
759            span: make_span(&src_handler, span),
760        });
761
762    // Field init for beliefs: name: value
763    let field_init = ident_token_parser(src3.clone())
764        .then_ignore(just(Token::Colon))
765        .then(expr_parser(src3.clone()))
766        .map_with_span({
767            let src = src3.clone();
768            move |(name, value), span: Range<usize>| FieldInit {
769                name,
770                value,
771                span: make_span(&src, span),
772            }
773        });
774
775    // Child spec: AgentName { restart: Permanent, handler Infer: DefaultLLM, belief1: value1, ... }
776    // Or just: AgentName { belief1: value1, ... } (defaults to Permanent)
777    // Commas between fields are optional for consistent multiline style.
778    let child_spec = ident_token_parser(src3.clone())
779        .then_ignore(just(Token::LBrace))
780        .then(restart_policy.then_ignore(just(Token::Comma).or_not()).or_not())
781        .then(
782            handler_assignment
783                .then_ignore(just(Token::Comma).or_not())
784                .repeated(),
785        )
786        .then(
787            field_init
788                .then_ignore(just(Token::Comma).or_not())
789                .repeated(),
790        )
791        .then_ignore(just(Token::RBrace))
792        .map_with_span({
793            let src = src3.clone();
794            move |(((agent_name, restart), handler_assignments), beliefs), span: Range<usize>| {
795                ChildSpec {
796                    agent_name,
797                    restart: restart.unwrap_or_default(),
798                    beliefs,
799                    handler_assignments,
800                    span: make_span(&src, span),
801                }
802            }
803        });
804
805    // Children block: children { child1 child2 ... }
806    let children = just(Token::KwChildren)
807        .ignore_then(
808            child_spec
809                .repeated()
810                .at_least(1)
811                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
812        );
813
814    // Full supervisor declaration
815    just(Token::KwPub)
816        .or_not()
817        .then_ignore(just(Token::KwSupervisor))
818        .then(ident_token_parser(src4.clone()))
819        .then_ignore(just(Token::LBrace))
820        .then(strategy)
821        .then(children)
822        .then_ignore(just(Token::RBrace))
823        .map_with_span(
824            move |(((is_pub, name), strategy), children), span: Range<usize>| {
825                TopLevel::Supervisor(SupervisorDecl {
826                    is_pub: is_pub.is_some(),
827                    name,
828                    strategy,
829                    children,
830                    span: make_span(&src4, span),
831                })
832            },
833        )
834}
835
836// =============================================================================
837// Agent parsers
838// =============================================================================
839
840/// Parser for an agent declaration.
841#[allow(clippy::needless_pass_by_value)]
842fn agent_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
843    let src2 = source.clone();
844    let src3 = source.clone();
845    let src4 = source.clone();
846    let src5 = source.clone();
847    let src6 = source.clone();
848
849    // Tool use clause: `use Http, Fs`
850    let tool_use = just(Token::KwUse)
851        .ignore_then(
852            ident_token_parser(src5.clone())
853                .separated_by(just(Token::Comma))
854                .at_least(1),
855        )
856        .or_not()
857        .map(|tools| tools.unwrap_or_default());
858
859    // Agent state fields: `name: Type` or `@persistent name: Type`
860    // We still call them "beliefs" internally for backwards compatibility
861    let src_annot = source.clone();
862    let persistent_annotation = just(Token::At)
863        .ignore_then(filter_map(move |span: Range<usize>, token| match token {
864            Token::Ident => {
865                let text = &src_annot[span.start..span.end];
866                if text == "persistent" {
867                    Ok(true)
868                } else {
869                    Err(Simple::custom(
870                        span,
871                        format!("unknown annotation @{}, expected @persistent", text),
872                    ))
873                }
874            }
875            _ => Err(Simple::expected_input_found(
876                span,
877                vec![Some(Token::Ident)],
878                Some(token),
879            )),
880        }))
881        .or_not();
882
883    let src_belief = source.clone();
884    let belief = persistent_annotation
885        .then(ident_token_parser(src_belief.clone()))
886        .then_ignore(just(Token::Colon))
887        .then(type_parser(src_belief.clone()))
888        .map_with_span(move |((is_persistent, name), ty), span: Range<usize>| BeliefDecl {
889            is_persistent: is_persistent.unwrap_or(false),
890            name,
891            ty,
892            span: make_span(&src_belief, span),
893        });
894
895    let handler = just(Token::KwOn)
896        .ignore_then(event_kind_parser(src2.clone()))
897        .then(block_parser(src2.clone()))
898        .map_with_span(move |(event, body), span: Range<usize>| HandlerDecl {
899            event,
900            body,
901            span: make_span(&src2, span),
902        });
903
904    // Optional `receives MsgType` clause
905    let receives_clause = just(Token::KwReceives)
906        .ignore_then(type_parser(src3.clone()))
907        .or_not();
908
909    // Optional `follows Protocol as Role` clause (Phase 3: Session Types)
910    // Syntax: follows Protocol as Role [, Protocol2 as Role2]*
911    let src_follows = src6.clone();
912    let single_follows = ident_token_parser(src6.clone())
913        .then_ignore(just(Token::KwAs))
914        .then(ident_token_parser(src6.clone()))
915        .map_with_span(move |(protocol, role), span: Range<usize>| ProtocolRole {
916            protocol,
917            role,
918            span: make_span(&src_follows, span),
919        });
920
921    let follows_clause = just(Token::KwFollows)
922        .ignore_then(single_follows.separated_by(just(Token::Comma)).at_least(1))
923        .or_not()
924        .map(|follows| follows.unwrap_or_default());
925
926    just(Token::KwPub)
927        .or_not()
928        .then_ignore(just(Token::KwAgent))
929        .then(ident_token_parser(src3.clone()))
930        .then(receives_clause)
931        .then(follows_clause)
932        .then_ignore(just(Token::LBrace))
933        .then(tool_use)
934        .then(belief.repeated())
935        .then(handler.repeated())
936        .then_ignore(just(Token::RBrace))
937        .map_with_span(
938            move |((((((is_pub, name), receives), follows), tool_uses), beliefs), handlers),
939                  span: Range<usize>| {
940                TopLevel::Agent(AgentDecl {
941                    is_pub: is_pub.is_some(),
942                    name,
943                    receives,
944                    follows,
945                    tool_uses,
946                    beliefs,
947                    handlers,
948                    span: make_span(&src4, span),
949                })
950            },
951        )
952}
953
954/// Parser for event kinds.
955#[allow(clippy::needless_pass_by_value)]
956fn event_kind_parser(source: Arc<str>) -> impl Parser<Token, EventKind, Error = ParseError> {
957    let src = source.clone();
958
959    // v2 lifecycle: waking runs before start
960    let waking = just(Token::KwWaking).to(EventKind::Waking);
961    let start = just(Token::KwStart).to(EventKind::Start);
962    // v2 lifecycle: pause/resume for graceful suspend
963    let pause = just(Token::KwPause).to(EventKind::Pause);
964    let resume = just(Token::KwResume).to(EventKind::Resume);
965    let stop = just(Token::KwStop).to(EventKind::Stop);
966    // v2 lifecycle: resting is an alias for stop
967    let resting = just(Token::KwResting).to(EventKind::Resting);
968
969    let message = just(Token::KwMessage)
970        .ignore_then(just(Token::LParen))
971        .ignore_then(ident_token_parser(src.clone()))
972        .then_ignore(just(Token::Colon))
973        .then(type_parser(src.clone()))
974        .then_ignore(just(Token::RParen))
975        .map(|(param_name, param_ty)| EventKind::Message {
976            param_name,
977            param_ty,
978        });
979
980    // RFC-0007: on error(e) handler
981    let error = just(Token::KwError)
982        .ignore_then(just(Token::LParen))
983        .ignore_then(ident_token_parser(src))
984        .then_ignore(just(Token::RParen))
985        .map(|param_name| EventKind::Error { param_name });
986
987    waking
988        .or(start)
989        .or(pause)
990        .or(resume)
991        .or(stop)
992        .or(resting)
993        .or(message)
994        .or(error)
995}
996
997// =============================================================================
998// Function parsers
999// =============================================================================
1000
1001/// Parser for a function declaration.
1002#[allow(clippy::needless_pass_by_value)]
1003fn fn_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
1004    let src = source.clone();
1005    let src2 = source.clone();
1006    let src3 = source.clone();
1007
1008    let param = ident_token_parser(src.clone())
1009        .then_ignore(just(Token::Colon))
1010        .then(type_parser(src.clone()))
1011        .map_with_span(move |(name, ty), span: Range<usize>| Param {
1012            name,
1013            ty,
1014            span: make_span(&src, span),
1015        });
1016
1017    let params = param
1018        .separated_by(just(Token::Comma))
1019        .allow_trailing()
1020        .delimited_by(just(Token::LParen), just(Token::RParen));
1021
1022    just(Token::KwPub)
1023        .or_not()
1024        .then_ignore(just(Token::KwFn))
1025        .then(ident_token_parser(src2.clone()))
1026        .then(type_params_parser(src2.clone()))
1027        .then(params)
1028        .then_ignore(just(Token::Arrow))
1029        .then(type_parser(src2.clone()))
1030        .then(just(Token::KwFails).or_not())
1031        .then(block_parser(src2))
1032        .map_with_span(
1033            move |((((((is_pub, name), type_params), params), return_ty), is_fallible), body),
1034                  span: Range<usize>| {
1035                TopLevel::Function(FnDecl {
1036                    is_pub: is_pub.is_some(),
1037                    name,
1038                    type_params,
1039                    params,
1040                    return_ty,
1041                    is_fallible: is_fallible.is_some(),
1042                    body,
1043                    span: make_span(&src3, span),
1044                })
1045            },
1046        )
1047}
1048
1049// =============================================================================
1050// Statement parsers
1051// =============================================================================
1052
1053/// Parser for a block of statements.
1054/// Uses `boxed()` to reduce type complexity and avoid macOS linker symbol length limits.
1055#[allow(clippy::needless_pass_by_value)]
1056fn block_parser(source: Arc<str>) -> BoxedParser<'static, Token, Block, ParseError> {
1057    let src = source.clone();
1058
1059    recursive(move |block: Recursive<Token, Block, ParseError>| {
1060        let src_inner = src.clone();
1061        stmt_parser(src.clone(), block)
1062            .repeated()
1063            .delimited_by(just(Token::LBrace), just(Token::RBrace))
1064            .recover_with(nested_delimiters(
1065                Token::LBrace,
1066                Token::RBrace,
1067                [
1068                    (Token::LParen, Token::RParen),
1069                    (Token::LBracket, Token::RBracket),
1070                ],
1071                |_span: Range<usize>| vec![],
1072            ))
1073            .map_with_span(move |stmts, span: Range<usize>| Block {
1074                stmts,
1075                span: make_span(&src_inner, span),
1076            })
1077    })
1078    .boxed()
1079}
1080
1081/// Parser for statements.
1082#[allow(clippy::needless_pass_by_value)]
1083fn stmt_parser(
1084    source: Arc<str>,
1085    block: impl Parser<Token, Block, Error = ParseError> + Clone + 'static,
1086) -> impl Parser<Token, Stmt, Error = ParseError> + Clone {
1087    let src = source.clone();
1088    let src2 = source.clone();
1089    let src3 = source.clone();
1090    let src4 = source.clone();
1091    let src5 = source.clone();
1092    let src6 = source.clone();
1093    let src7 = source.clone();
1094
1095    // Let tuple destructuring: let (a, b) = expr;
1096    let src10 = source.clone();
1097    let let_tuple_stmt = just(Token::KwLet)
1098        .ignore_then(
1099            ident_token_parser(src10.clone())
1100                .separated_by(just(Token::Comma))
1101                .at_least(2)
1102                .allow_trailing()
1103                .delimited_by(just(Token::LParen), just(Token::RParen)),
1104        )
1105        .then(
1106            just(Token::Colon)
1107                .ignore_then(type_parser(src10.clone()))
1108                .or_not(),
1109        )
1110        .then_ignore(just(Token::Eq))
1111        .then(expr_parser(src10.clone()))
1112        .then_ignore(just(Token::Semicolon))
1113        .map_with_span(
1114            move |((names, ty), value), span: Range<usize>| Stmt::LetTuple {
1115                names,
1116                ty,
1117                value,
1118                span: make_span(&src10, span),
1119            },
1120        );
1121
1122    let let_stmt = just(Token::KwLet)
1123        .ignore_then(ident_token_parser(src.clone()))
1124        .then(
1125            just(Token::Colon)
1126                .ignore_then(type_parser(src.clone()))
1127                .or_not(),
1128        )
1129        .then_ignore(just(Token::Eq))
1130        .then(expr_parser(src.clone()))
1131        .then_ignore(just(Token::Semicolon))
1132        .map_with_span(move |((name, ty), value), span: Range<usize>| Stmt::Let {
1133            name,
1134            ty,
1135            value,
1136            span: make_span(&src, span),
1137        });
1138
1139    let return_stmt = just(Token::KwReturn)
1140        .ignore_then(expr_parser(src2.clone()).or_not())
1141        .then_ignore(just(Token::Semicolon))
1142        .map_with_span(move |value, span: Range<usize>| Stmt::Return {
1143            value,
1144            span: make_span(&src2, span),
1145        });
1146
1147    let if_stmt = recursive(|if_stmt| {
1148        let src_if = src3.clone();
1149        let block_clone = block.clone();
1150
1151        just(Token::KwIf)
1152            .ignore_then(expr_parser(src3.clone()))
1153            .then(block_clone.clone())
1154            .then(
1155                just(Token::KwElse)
1156                    .ignore_then(
1157                        if_stmt
1158                            .map(|s| ElseBranch::ElseIf(Box::new(s)))
1159                            .or(block_clone.map(ElseBranch::Block)),
1160                    )
1161                    .or_not(),
1162            )
1163            .map_with_span(
1164                move |((condition, then_block), else_block), span: Range<usize>| Stmt::If {
1165                    condition,
1166                    then_block,
1167                    else_block,
1168                    span: make_span(&src_if, span),
1169                },
1170            )
1171    });
1172
1173    let for_stmt = just(Token::KwFor)
1174        .ignore_then(for_pattern_parser(src4.clone()))
1175        .then_ignore(just(Token::KwIn))
1176        .then(expr_parser(src4.clone()))
1177        .then(block.clone())
1178        .map_with_span(
1179            move |((pattern, iter), body), span: Range<usize>| Stmt::For {
1180                pattern,
1181                iter,
1182                body,
1183                span: make_span(&src4, span),
1184            },
1185        );
1186
1187    let while_stmt = just(Token::KwWhile)
1188        .ignore_then(expr_parser(src7.clone()))
1189        .then(block.clone())
1190        .map_with_span(move |(condition, body), span: Range<usize>| Stmt::While {
1191            condition,
1192            body,
1193            span: make_span(&src7, span),
1194        });
1195
1196    let src8 = source.clone();
1197    let loop_stmt = just(Token::KwLoop)
1198        .ignore_then(block.clone())
1199        .map_with_span(move |body, span: Range<usize>| Stmt::Loop {
1200            body,
1201            span: make_span(&src8, span),
1202        });
1203
1204    let src9 = source.clone();
1205    let break_stmt = just(Token::KwBreak)
1206        .then_ignore(just(Token::Semicolon))
1207        .map_with_span(move |_, span: Range<usize>| Stmt::Break {
1208            span: make_span(&src9, span),
1209        });
1210
1211    // RFC-0012: mock divine -> value; or mock divine -> fail("msg");
1212    // Also accepts: mock infer -> value; (alias for mock divine)
1213    let src12 = source.clone();
1214    let mock_divine_stmt = just(Token::KwMock)
1215        .ignore_then(just(Token::KwDivine).or(just(Token::KwInfer)))
1216        .ignore_then(just(Token::Arrow))
1217        .ignore_then(expr_parser(src12.clone()).map(|expr| {
1218            // Check if this is a fail() call expression and convert to MockValue::Fail
1219            if let Expr::Fail { error, .. } = expr {
1220                MockValue::Fail(*error)
1221            } else {
1222                MockValue::Value(expr)
1223            }
1224        }))
1225        .then_ignore(just(Token::Semicolon))
1226        .map_with_span(move |value, span: Range<usize>| Stmt::MockDivine {
1227            value,
1228            span: make_span(&src12, span),
1229        });
1230
1231    // mock tool ToolName.fn_name -> value; or mock tool ToolName.fn_name -> fail("msg");
1232    let src13 = source.clone();
1233    let src14 = source.clone();
1234    let src15 = source.clone();
1235    let mock_tool_stmt = just(Token::KwMock)
1236        .ignore_then(just(Token::KwTool))
1237        .ignore_then(ident_token_parser(src13.clone())) // Tool name
1238        .then_ignore(just(Token::Dot))
1239        .then(ident_token_parser(src14.clone())) // Function name
1240        .then_ignore(just(Token::Arrow))
1241        .then(expr_parser(src15.clone()).map(|expr| {
1242            // Check if this is a fail() call expression and convert to MockValue::Fail
1243            if let Expr::Fail { error, .. } = expr {
1244                MockValue::Fail(*error)
1245            } else {
1246                MockValue::Value(expr)
1247            }
1248        }))
1249        .then_ignore(just(Token::Semicolon))
1250        .map_with_span(
1251            move |((tool_name, fn_name), value), span: Range<usize>| Stmt::MockTool {
1252                tool_name,
1253                fn_name,
1254                value,
1255                span: make_span(&src15, span),
1256            },
1257        );
1258
1259    let assign_stmt = ident_token_parser(src5.clone())
1260        .then_ignore(just(Token::Eq))
1261        .then(expr_parser(src5.clone()))
1262        .then_ignore(just(Token::Semicolon))
1263        .map_with_span(move |(name, value), span: Range<usize>| Stmt::Assign {
1264            name,
1265            value,
1266            span: make_span(&src5, span),
1267        });
1268
1269    // span "name" { body } - timed observability block
1270    let src16 = source.clone();
1271    let span_block_stmt = just(Token::KwSpan)
1272        .ignore_then(expr_parser(src16.clone()))
1273        .then(block.clone())
1274        .map_with_span(move |(name, body), span: Range<usize>| Stmt::SpanBlock {
1275            name,
1276            body,
1277            span: make_span(&src16, span),
1278        });
1279
1280    // checkpoint(); - explicit persistence checkpoint
1281    let src17 = source.clone();
1282    let checkpoint_stmt = just(Token::KwCheckpoint)
1283        .then_ignore(just(Token::LParen))
1284        .then_ignore(just(Token::RParen))
1285        .then_ignore(just(Token::Semicolon))
1286        .map_with_span(move |_, span: Range<usize>| Stmt::Checkpoint {
1287            span: make_span(&src17, span),
1288        });
1289
1290    let expr_stmt = expr_parser(src6.clone())
1291        .then_ignore(just(Token::Semicolon))
1292        .map_with_span(move |expr, span: Range<usize>| Stmt::Expr {
1293            expr,
1294            span: make_span(&src6, span),
1295        });
1296
1297    let_tuple_stmt
1298        .or(let_stmt)
1299        .or(return_stmt)
1300        .or(if_stmt)
1301        .or(for_stmt)
1302        .or(while_stmt)
1303        .or(loop_stmt)
1304        .or(break_stmt)
1305        .or(span_block_stmt)
1306        .or(checkpoint_stmt)
1307        .or(mock_divine_stmt)
1308        .or(mock_tool_stmt)
1309        .or(assign_stmt)
1310        .or(expr_stmt)
1311}
1312
1313// =============================================================================
1314// Expression parsers
1315// =============================================================================
1316
1317/// Parser for expressions (with precedence climbing for binary ops).
1318/// Uses `boxed()` to reduce type complexity and avoid macOS linker symbol length limits.
1319#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
1320fn expr_parser(source: Arc<str>) -> BoxedParser<'static, Token, Expr, ParseError> {
1321    recursive(move |expr: Recursive<Token, Expr, ParseError>| {
1322        let src = source.clone();
1323
1324        let literal = literal_parser(src.clone());
1325        let var = var_parser(src.clone());
1326
1327        // Parenthesized expression or tuple literal
1328        // (expr) is a paren, (expr, expr, ...) is a tuple
1329        let paren_or_tuple = just(Token::LParen)
1330            .ignore_then(
1331                expr.clone()
1332                    .separated_by(just(Token::Comma))
1333                    .allow_trailing(),
1334            )
1335            .then_ignore(just(Token::RParen))
1336            .map_with_span({
1337                let src = src.clone();
1338                move |elements, span: Range<usize>| {
1339                    if elements.len() == 1 {
1340                        // Single element without trailing comma = parenthesized expression
1341                        Expr::Paren {
1342                            inner: Box::new(elements.into_iter().next().unwrap()),
1343                            span: make_span(&src, span),
1344                        }
1345                    } else {
1346                        // Multiple elements or empty = tuple
1347                        Expr::Tuple {
1348                            elements,
1349                            span: make_span(&src, span),
1350                        }
1351                    }
1352                }
1353            });
1354
1355        let list = expr
1356            .clone()
1357            .separated_by(just(Token::Comma))
1358            .allow_trailing()
1359            .delimited_by(just(Token::LBracket), just(Token::RBracket))
1360            .map_with_span({
1361                let src = src.clone();
1362                move |elements, span: Range<usize>| Expr::List {
1363                    elements,
1364                    span: make_span(&src, span),
1365                }
1366            });
1367
1368        // self.field or self.method(args)
1369        let self_access = just(Token::KwSelf)
1370            .ignore_then(just(Token::Dot))
1371            .ignore_then(ident_token_parser(src.clone()))
1372            .then(
1373                expr.clone()
1374                    .separated_by(just(Token::Comma))
1375                    .allow_trailing()
1376                    .delimited_by(just(Token::LParen), just(Token::RParen))
1377                    .or_not(),
1378            )
1379            .map_with_span({
1380                let src = src.clone();
1381                move |(field, args), span: Range<usize>| match args {
1382                    Some(args) => Expr::SelfMethodCall {
1383                        method: field,
1384                        args,
1385                        span: make_span(&src, span),
1386                    },
1387                    None => Expr::SelfField {
1388                        field,
1389                        span: make_span(&src, span),
1390                    },
1391                }
1392            });
1393
1394        // divine("template") or divine("template" -> Type)
1395        // Also accepts: infer("template") or infer("template" -> Type)
1396        let divine_expr = just(Token::KwDivine)
1397            .or(just(Token::KwInfer))
1398            .ignore_then(just(Token::LParen))
1399            .ignore_then(string_template_parser(src.clone()))
1400            .then(
1401                just(Token::Arrow)
1402                    .ignore_then(type_parser(src.clone()))
1403                    .or_not(),
1404            )
1405            .then_ignore(just(Token::RParen))
1406            .map_with_span({
1407                let src = src.clone();
1408                move |(template, result_ty), span: Range<usize>| Expr::Divine {
1409                    template,
1410                    result_ty,
1411                    span: make_span(&src, span),
1412                }
1413            });
1414
1415        // summon Agent { field: value, ... }
1416        let summon_field_init = ident_token_parser(src.clone())
1417            .then_ignore(just(Token::Colon))
1418            .then(expr.clone())
1419            .map_with_span({
1420                let src = src.clone();
1421                move |(name, value), span: Range<usize>| FieldInit {
1422                    name,
1423                    value,
1424                    span: make_span(&src, span),
1425                }
1426            });
1427
1428        let summon_expr = just(Token::KwSummon)
1429            .ignore_then(ident_token_parser(src.clone()))
1430            .then_ignore(just(Token::LBrace))
1431            .then(
1432                summon_field_init
1433                    .separated_by(just(Token::Comma))
1434                    .allow_trailing(),
1435            )
1436            .then_ignore(just(Token::RBrace))
1437            .map_with_span({
1438                let src = src.clone();
1439                move |(agent, fields), span: Range<usize>| Expr::Summon {
1440                    agent,
1441                    fields,
1442                    span: make_span(&src, span),
1443                }
1444            });
1445
1446        // await expr - we need to handle this carefully to avoid left recursion
1447        // Parse: await handle [timeout(ms)]
1448        let timeout_clause = just(Token::KwTimeout)
1449            .ignore_then(just(Token::LParen))
1450            .ignore_then(expr.clone())
1451            .then_ignore(just(Token::RParen));
1452
1453        let await_expr = just(Token::KwAwait)
1454            .ignore_then(ident_token_parser(src.clone()).map_with_span({
1455                let src = src.clone();
1456                move |name, span: Range<usize>| Expr::Var {
1457                    name,
1458                    span: make_span(&src, span),
1459                }
1460            }))
1461            .then(timeout_clause.or_not())
1462            .map_with_span({
1463                let src = src.clone();
1464                move |(handle, timeout), span: Range<usize>| Expr::Await {
1465                    handle: Box::new(handle),
1466                    timeout: timeout.map(Box::new),
1467                    span: make_span(&src, span),
1468                }
1469            });
1470
1471        // send(handle, message)
1472        let send_expr = just(Token::KwSend)
1473            .ignore_then(just(Token::LParen))
1474            .ignore_then(expr.clone())
1475            .then_ignore(just(Token::Comma))
1476            .then(expr.clone())
1477            .then_ignore(just(Token::RParen))
1478            .map_with_span({
1479                let src = src.clone();
1480                move |(handle, message), span: Range<usize>| Expr::Send {
1481                    handle: Box::new(handle),
1482                    message: Box::new(message),
1483                    span: make_span(&src, span),
1484                }
1485            });
1486
1487        // yield(value)
1488        let yield_expr = just(Token::KwYield)
1489            .ignore_then(just(Token::LParen))
1490            .ignore_then(expr.clone())
1491            .then_ignore(just(Token::RParen))
1492            .map_with_span({
1493                let src = src.clone();
1494                move |value, span: Range<usize>| Expr::Yield {
1495                    value: Box::new(value),
1496                    span: make_span(&src, span),
1497                }
1498            });
1499
1500        // reply(message) - Phase 3 session types
1501        let reply_expr = just(Token::KwReply)
1502            .ignore_then(just(Token::LParen))
1503            .ignore_then(expr.clone())
1504            .then_ignore(just(Token::RParen))
1505            .map_with_span({
1506                let src = src.clone();
1507                move |message, span: Range<usize>| Expr::Reply {
1508                    message: Box::new(message),
1509                    span: make_span(&src, span),
1510                }
1511            });
1512
1513        // Turbofish type arguments: ::<Int, String>
1514        let turbofish = just(Token::ColonColon)
1515            .ignore_then(
1516                type_parser(src.clone())
1517                    .separated_by(just(Token::Comma))
1518                    .allow_trailing()
1519                    .delimited_by(just(Token::Lt), just(Token::Gt)),
1520            )
1521            .or_not()
1522            .map(|args| args.unwrap_or_default());
1523
1524        // function call: name(args) or name::<T, U>(args)
1525        let call_expr = ident_token_parser(src.clone())
1526            .then(turbofish.clone())
1527            .then(
1528                expr.clone()
1529                    .separated_by(just(Token::Comma))
1530                    .allow_trailing()
1531                    .delimited_by(just(Token::LParen), just(Token::RParen)),
1532            )
1533            .map_with_span({
1534                let src = src.clone();
1535                move |((name, type_args), args), span: Range<usize>| Expr::Call {
1536                    name,
1537                    type_args,
1538                    args,
1539                    span: make_span(&src, span),
1540                }
1541            });
1542
1543        // Pattern for match arms
1544        let pattern = pattern_parser(src.clone());
1545
1546        // match expression: match expr { Pattern => expr, ... }
1547        let match_arm = pattern
1548            .then_ignore(just(Token::FatArrow))
1549            .then(expr.clone())
1550            .map_with_span({
1551                let src = src.clone();
1552                move |(pattern, body), span: Range<usize>| MatchArm {
1553                    pattern,
1554                    body,
1555                    span: make_span(&src, span),
1556                }
1557            });
1558
1559        let match_expr = just(Token::KwMatch)
1560            .ignore_then(expr.clone())
1561            .then(
1562                match_arm
1563                    .separated_by(just(Token::Comma))
1564                    .allow_trailing()
1565                    .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1566            )
1567            .map_with_span({
1568                let src = src.clone();
1569                move |(scrutinee, arms), span: Range<usize>| Expr::Match {
1570                    scrutinee: Box::new(scrutinee),
1571                    arms,
1572                    span: make_span(&src, span),
1573                }
1574            });
1575
1576        // receive() - receive message from mailbox
1577        let receive_expr = just(Token::KwReceive)
1578            .ignore_then(just(Token::LParen))
1579            .ignore_then(just(Token::RParen))
1580            .map_with_span({
1581                let src = src.clone();
1582                move |_, span: Range<usize>| Expr::Receive {
1583                    span: make_span(&src, span),
1584                }
1585            });
1586
1587        // trace(message) - emit a trace event
1588        let trace_expr = just(Token::KwTrace)
1589            .ignore_then(just(Token::LParen))
1590            .ignore_then(expr.clone())
1591            .then_ignore(just(Token::RParen))
1592            .map_with_span({
1593                let src = src.clone();
1594                move |message, span: Range<usize>| Expr::Trace {
1595                    message: Box::new(message),
1596                    span: make_span(&src, span),
1597                }
1598            });
1599
1600        // Record construction: RecordName { field: value, ... }
1601        // This is similar to summon but without the summon keyword
1602        // Must come before var to avoid conflict
1603        let record_field_init = ident_token_parser(src.clone())
1604            .then_ignore(just(Token::Colon))
1605            .then(expr.clone())
1606            .map_with_span({
1607                let src = src.clone();
1608                move |(name, value), span: Range<usize>| FieldInit {
1609                    name,
1610                    value,
1611                    span: make_span(&src, span),
1612                }
1613            });
1614
1615        // Turbofish for record construction: Pair::<Int, String> { ... }
1616        let record_turbofish = just(Token::ColonColon)
1617            .ignore_then(
1618                type_parser(src.clone())
1619                    .separated_by(just(Token::Comma))
1620                    .allow_trailing()
1621                    .delimited_by(just(Token::Lt), just(Token::Gt)),
1622            )
1623            .or_not()
1624            .map(|args| args.unwrap_or_default());
1625
1626        let record_construct = ident_token_parser(src.clone())
1627            .then(record_turbofish)
1628            .then_ignore(just(Token::LBrace))
1629            .then(
1630                record_field_init
1631                    .separated_by(just(Token::Comma))
1632                    .allow_trailing(),
1633            )
1634            .then_ignore(just(Token::RBrace))
1635            .map_with_span({
1636                let src = src.clone();
1637                move |((name, type_args), fields), span: Range<usize>| Expr::RecordConstruct {
1638                    name,
1639                    type_args,
1640                    fields,
1641                    span: make_span(&src, span),
1642                }
1643            });
1644
1645        // Closure parameter: `name` or `name: Type`
1646        let closure_param = ident_token_parser(src.clone())
1647            .then(
1648                just(Token::Colon)
1649                    .ignore_then(type_parser(src.clone()))
1650                    .or_not(),
1651            )
1652            .map_with_span({
1653                let src = src.clone();
1654                move |(name, ty), span: Range<usize>| ClosureParam {
1655                    name,
1656                    ty,
1657                    span: make_span(&src, span),
1658                }
1659            });
1660
1661        // Closure expression: |params| body
1662        // Handle both `|| expr` (empty params using Or token) and `|params| expr`
1663        let closure_empty = just(Token::Or).ignore_then(expr.clone()).map_with_span({
1664            let src = src.clone();
1665            move |body, span: Range<usize>| Expr::Closure {
1666                params: vec![],
1667                body: Box::new(body),
1668                span: make_span(&src, span),
1669            }
1670        });
1671
1672        let closure_with_params = just(Token::Pipe)
1673            .ignore_then(
1674                closure_param
1675                    .separated_by(just(Token::Comma))
1676                    .allow_trailing(),
1677            )
1678            .then_ignore(just(Token::Pipe))
1679            .then(expr.clone())
1680            .map_with_span({
1681                let src = src.clone();
1682                move |(params, body), span: Range<usize>| Expr::Closure {
1683                    params,
1684                    body: Box::new(body),
1685                    span: make_span(&src, span),
1686                }
1687            });
1688
1689        let closure = closure_with_params.or(closure_empty);
1690
1691        // Map literal: { key: value, ... } or {}
1692        // This must be distinguished from record construction which has an identifier before the brace
1693        let map_entry = expr
1694            .clone()
1695            .then_ignore(just(Token::Colon))
1696            .then(expr.clone())
1697            .map_with_span({
1698                let src = src.clone();
1699                move |(key, value), span: Range<usize>| MapEntry {
1700                    key,
1701                    value,
1702                    span: make_span(&src, span),
1703                }
1704            });
1705
1706        let map_literal = map_entry
1707            .separated_by(just(Token::Comma))
1708            .allow_trailing()
1709            .delimited_by(just(Token::LBrace), just(Token::RBrace))
1710            .map_with_span({
1711                let src = src.clone();
1712                move |entries, span: Range<usize>| Expr::Map {
1713                    entries,
1714                    span: make_span(&src, span),
1715                }
1716            });
1717
1718        // Enum variant construction: EnumName::Variant or Either::<L, R>::Left(payload)
1719        // Turbofish comes between enum name and ::Variant
1720        let variant_turbofish = just(Token::ColonColon)
1721            .ignore_then(
1722                type_parser(src.clone())
1723                    .separated_by(just(Token::Comma))
1724                    .allow_trailing()
1725                    .delimited_by(just(Token::Lt), just(Token::Gt)),
1726            )
1727            .or_not()
1728            .map(|args| args.unwrap_or_default());
1729
1730        // Parser for enum name: accepts identifiers OR builtin type keywords (Option, Result)
1731        // This allows Option::Some(...) and Result::Ok(...) syntax
1732        let enum_name_parser = {
1733            let src = src.clone();
1734            ident_token_parser(src.clone()).or(just(Token::TyOption)
1735                .or(just(Token::TyResult))
1736                .map_with_span({
1737                    let src = src.clone();
1738                    move |token, span: Range<usize>| {
1739                        let name = match token {
1740                            Token::TyOption => "Option",
1741                            Token::TyResult => "Result",
1742                            _ => unreachable!(),
1743                        };
1744                        Ident {
1745                            name: name.to_string(),
1746                            span: make_span(&src, span),
1747                        }
1748                    }
1749                }))
1750        };
1751
1752        let variant_construct = enum_name_parser
1753            .then(variant_turbofish)
1754            .then_ignore(just(Token::ColonColon))
1755            .then(ident_token_parser(src.clone()))
1756            .then(
1757                expr.clone()
1758                    .delimited_by(just(Token::LParen), just(Token::RParen))
1759                    .or_not(),
1760            )
1761            .map_with_span({
1762                let src = src.clone();
1763                move |(((enum_name, type_args), variant), payload), span: Range<usize>| {
1764                    Expr::VariantConstruct {
1765                        enum_name,
1766                        type_args,
1767                        variant,
1768                        payload: payload.map(Box::new),
1769                        span: make_span(&src, span),
1770                    }
1771                }
1772            });
1773
1774        // Atom: the base expression without binary ops
1775        // Box early to cut type complexity
1776        // Note: record_construct must come before call_expr and var to parse `Name { ... }` correctly
1777        // Note: receive_expr must come before call_expr to avoid being parsed as function call
1778        // Note: closure must come before other expressions to handle `|` tokens correctly
1779        // Note: map_literal must come after record_construct (record has name before brace)
1780        // Note: variant_construct must come before call_expr to parse `EnumName::Variant(...)` correctly
1781        let atom = closure
1782            .or(divine_expr)
1783            .or(summon_expr)
1784            .or(await_expr)
1785            .or(send_expr)
1786            .or(yield_expr)
1787            .or(reply_expr)
1788            .or(receive_expr)
1789            .or(trace_expr)
1790            .or(match_expr)
1791            .or(self_access)
1792            .or(record_construct)
1793            .or(variant_construct)
1794            .or(call_expr)
1795            .or(map_literal)
1796            .or(list)
1797            .or(paren_or_tuple)
1798            .or(literal)
1799            .or(var)
1800            .boxed();
1801
1802        // Postfix access: expr.field, expr.0 (tuple index), or expr.method(args) (tool call)
1803        // We need to distinguish between field access, tuple index, and method call
1804        enum PostfixOp {
1805            Field(Ident),
1806            TupleIndex(usize, Range<usize>),
1807            MethodCall(Ident, Vec<Expr>, Range<usize>), // method name, args, span of closing paren
1808        }
1809
1810        // Parse method call: .ident(args)
1811        let method_call = ident_token_parser(src.clone())
1812            .then(
1813                expr.clone()
1814                    .separated_by(just(Token::Comma))
1815                    .allow_trailing()
1816                    .delimited_by(just(Token::LParen), just(Token::RParen)),
1817            )
1818            .map_with_span(|(name, args), span: Range<usize>| {
1819                PostfixOp::MethodCall(name, args, span)
1820            });
1821
1822        let postfix_op = just(Token::Dot).ignore_then(
1823            // Try to parse a tuple index (integer literal)
1824            filter_map({
1825                let src = src.clone();
1826                move |span: Range<usize>, token| match token {
1827                    Token::IntLit => {
1828                        let text = &src[span.start..span.end];
1829                        text.parse::<usize>()
1830                            .map(|idx| PostfixOp::TupleIndex(idx, span.clone()))
1831                            .map_err(|_| Simple::custom(span, "invalid tuple index"))
1832                    }
1833                    _ => Err(Simple::expected_input_found(
1834                        span,
1835                        vec![Some(Token::IntLit)],
1836                        Some(token),
1837                    )),
1838                }
1839            })
1840            // Try method call first, then field access
1841            .or(method_call)
1842            .or(ident_token_parser(src.clone()).map(PostfixOp::Field)),
1843        );
1844
1845        let postfix = atom
1846            .then(postfix_op.repeated())
1847            .foldl({
1848                let src = src.clone();
1849                move |object, op| match op {
1850                    PostfixOp::Field(field) => {
1851                        let span = make_span(&src, object.span().start..field.span.end);
1852                        Expr::FieldAccess {
1853                            object: Box::new(object),
1854                            field,
1855                            span,
1856                        }
1857                    }
1858                    PostfixOp::TupleIndex(index, idx_span) => {
1859                        let span = make_span(&src, object.span().start..idx_span.end);
1860                        Expr::TupleIndex {
1861                            tuple: Box::new(object),
1862                            index,
1863                            span,
1864                        }
1865                    }
1866                    PostfixOp::MethodCall(method, args, call_span) => {
1867                        // If object is a Var, this might be a tool call
1868                        // Tool calls look like: Http.get(url)
1869                        if let Expr::Var { name: tool, .. } = &object {
1870                            let span = make_span(&src, object.span().start..call_span.end);
1871                            Expr::ToolCall {
1872                                tool: tool.clone(),
1873                                function: method,
1874                                args,
1875                                span,
1876                            }
1877                        } else {
1878                            // Not a tool call - this is a method call on a value.
1879                            // Produce Apply { callee: FieldAccess, args }
1880                            let span = make_span(&src, object.span().start..call_span.end);
1881                            let callee = Expr::FieldAccess {
1882                                object: Box::new(object),
1883                                field: method,
1884                                span: span.clone(),
1885                            };
1886                            Expr::Apply {
1887                                callee: Box::new(callee),
1888                                args,
1889                                span,
1890                            }
1891                        }
1892                    }
1893                }
1894            })
1895            .boxed();
1896
1897        // Unary expressions
1898        let unary = just(Token::Minus)
1899            .to(UnaryOp::Neg)
1900            .or(just(Token::Bang).to(UnaryOp::Not))
1901            .repeated()
1902            .then(postfix.clone())
1903            .foldr(|op, operand| {
1904                let span = operand.span().clone();
1905                Expr::Unary {
1906                    op,
1907                    operand: Box::new(operand),
1908                    span,
1909                }
1910            })
1911            .boxed();
1912
1913        // RFC-0007: try expression - propagates errors upward
1914        // try expr
1915        let try_expr = just(Token::KwTry)
1916            .ignore_then(postfix.clone())
1917            .map_with_span({
1918                let src = src.clone();
1919                move |inner, span: Range<usize>| Expr::Try {
1920                    expr: Box::new(inner),
1921                    span: make_span(&src, span),
1922                }
1923            })
1924            .boxed();
1925
1926        // fail expression - explicit error raising
1927        // fail "message" or fail Error { ... }
1928        let fail_expr = just(Token::KwFail)
1929            .ignore_then(postfix.clone())
1930            .map_with_span({
1931                let src = src.clone();
1932                move |error, span: Range<usize>| Expr::Fail {
1933                    error: Box::new(error),
1934                    span: make_span(&src, span),
1935                }
1936            })
1937            .boxed();
1938
1939        // retry expression - retry a fallible operation
1940        // retry(count) { body }
1941        // retry(count, delay: ms) { body }
1942        // retry(count, on: [ErrorKind.Network, ...]) { body }
1943        let retry_delay = just(Token::Comma)
1944            .ignore_then(just(Token::KwDelay))
1945            .ignore_then(just(Token::Colon))
1946            .ignore_then(postfix.clone());
1947
1948        let retry_on = just(Token::Comma)
1949            .ignore_then(just(Token::KwOn))
1950            .ignore_then(just(Token::Colon))
1951            .ignore_then(
1952                postfix
1953                    .clone()
1954                    .separated_by(just(Token::Comma))
1955                    .allow_trailing()
1956                    .delimited_by(just(Token::LBracket), just(Token::RBracket)),
1957            );
1958
1959        let retry_expr = just(Token::KwRetry)
1960            .ignore_then(just(Token::LParen))
1961            .ignore_then(postfix.clone())
1962            .then(retry_delay.or_not())
1963            .then(retry_on.or_not())
1964            .then_ignore(just(Token::RParen))
1965            .then(
1966                expr.clone()
1967                    .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1968            )
1969            .map_with_span({
1970                let src = src.clone();
1971                move |(((count, delay), on_errors), body), span: Range<usize>| Expr::Retry {
1972                    count: Box::new(count),
1973                    delay: delay.map(Box::new),
1974                    on_errors,
1975                    body: Box::new(body),
1976                    span: make_span(&src, span),
1977                }
1978            })
1979            .boxed();
1980
1981        // Combined unary (including try, fail, and retry)
1982        let unary = retry_expr.or(fail_expr).or(try_expr).or(unary).boxed();
1983
1984        // Binary operators with precedence levels
1985        // Level 7: * / %
1986        let mul_div_op = just(Token::Star)
1987            .to(BinOp::Mul)
1988            .or(just(Token::Slash).to(BinOp::Div))
1989            .or(just(Token::Percent).to(BinOp::Rem));
1990
1991        let mul_div = unary
1992            .clone()
1993            .then(mul_div_op.then(unary.clone()).repeated())
1994            .foldl({
1995                let src = src.clone();
1996                move |left, (op, right)| {
1997                    let span = make_span(&src, left.span().start..right.span().end);
1998                    Expr::Binary {
1999                        op,
2000                        left: Box::new(left),
2001                        right: Box::new(right),
2002                        span,
2003                    }
2004                }
2005            })
2006            .boxed();
2007
2008        // Level 6: + -
2009        let add_sub_op = just(Token::Plus)
2010            .to(BinOp::Add)
2011            .or(just(Token::Minus).to(BinOp::Sub));
2012
2013        let add_sub = mul_div
2014            .clone()
2015            .then(add_sub_op.then(mul_div).repeated())
2016            .foldl({
2017                let src = src.clone();
2018                move |left, (op, right)| {
2019                    let span = make_span(&src, left.span().start..right.span().end);
2020                    Expr::Binary {
2021                        op,
2022                        left: Box::new(left),
2023                        right: Box::new(right),
2024                        span,
2025                    }
2026                }
2027            })
2028            .boxed();
2029
2030        // Level 5: ++
2031        let concat_op = just(Token::PlusPlus).to(BinOp::Concat);
2032
2033        let concat = add_sub
2034            .clone()
2035            .then(concat_op.then(add_sub).repeated())
2036            .foldl({
2037                let src = src.clone();
2038                move |left, (op, right)| {
2039                    let span = make_span(&src, left.span().start..right.span().end);
2040                    Expr::Binary {
2041                        op,
2042                        left: Box::new(left),
2043                        right: Box::new(right),
2044                        span,
2045                    }
2046                }
2047            })
2048            .boxed();
2049
2050        // Level 4: < > <= >=
2051        let cmp_op = choice((
2052            just(Token::Le).to(BinOp::Le),
2053            just(Token::Ge).to(BinOp::Ge),
2054            just(Token::Lt).to(BinOp::Lt),
2055            just(Token::Gt).to(BinOp::Gt),
2056        ));
2057
2058        let comparison = concat
2059            .clone()
2060            .then(cmp_op.then(concat).repeated())
2061            .foldl({
2062                let src = src.clone();
2063                move |left, (op, right)| {
2064                    let span = make_span(&src, left.span().start..right.span().end);
2065                    Expr::Binary {
2066                        op,
2067                        left: Box::new(left),
2068                        right: Box::new(right),
2069                        span,
2070                    }
2071                }
2072            })
2073            .boxed();
2074
2075        // Level 3: == !=
2076        let eq_op = just(Token::EqEq)
2077            .to(BinOp::Eq)
2078            .or(just(Token::Ne).to(BinOp::Ne));
2079
2080        let equality = comparison
2081            .clone()
2082            .then(eq_op.then(comparison).repeated())
2083            .foldl({
2084                let src = src.clone();
2085                move |left, (op, right)| {
2086                    let span = make_span(&src, left.span().start..right.span().end);
2087                    Expr::Binary {
2088                        op,
2089                        left: Box::new(left),
2090                        right: Box::new(right),
2091                        span,
2092                    }
2093                }
2094            })
2095            .boxed();
2096
2097        // Level 2: &&
2098        let and_op = just(Token::And).to(BinOp::And);
2099
2100        let and = equality
2101            .clone()
2102            .then(and_op.then(equality).repeated())
2103            .foldl({
2104                let src = src.clone();
2105                move |left, (op, right)| {
2106                    let span = make_span(&src, left.span().start..right.span().end);
2107                    Expr::Binary {
2108                        op,
2109                        left: Box::new(left),
2110                        right: Box::new(right),
2111                        span,
2112                    }
2113                }
2114            })
2115            .boxed();
2116
2117        // Level 1: ||
2118        let or_op = just(Token::Or).to(BinOp::Or);
2119
2120        let or_expr = and.clone().then(or_op.then(and).repeated()).foldl({
2121            let src = src.clone();
2122            move |left, (op, right)| {
2123                let span = make_span(&src, left.span().start..right.span().end);
2124                Expr::Binary {
2125                    op,
2126                    left: Box::new(left),
2127                    right: Box::new(right),
2128                    span,
2129                }
2130            }
2131        });
2132
2133        // RFC-0007: catch expression (lowest precedence)
2134        // expr catch { recovery } OR expr catch(e) { recovery }
2135        let catch_recovery = just(Token::KwCatch)
2136            .ignore_then(
2137                ident_token_parser(src.clone())
2138                    .delimited_by(just(Token::LParen), just(Token::RParen))
2139                    .or_not(),
2140            )
2141            .then(
2142                expr.clone()
2143                    .delimited_by(just(Token::LBrace), just(Token::RBrace)),
2144            );
2145
2146        or_expr.then(catch_recovery.or_not()).map_with_span({
2147            let src = src.clone();
2148            move |(inner, catch_opt), span: Range<usize>| match catch_opt {
2149                Some((error_bind, recovery)) => Expr::Catch {
2150                    expr: Box::new(inner),
2151                    error_bind,
2152                    recovery: Box::new(recovery),
2153                    span: make_span(&src, span),
2154                },
2155                None => inner,
2156            }
2157        })
2158    })
2159    .boxed()
2160}
2161
2162// =============================================================================
2163// Primitive parsers
2164// =============================================================================
2165
2166/// Create a Span from a Range<usize>.
2167fn make_span(source: &Arc<str>, range: Range<usize>) -> Span {
2168    Span::new(range.start, range.end, Arc::clone(source))
2169}
2170
2171/// Parser for identifier tokens.
2172fn ident_token_parser(source: Arc<str>) -> impl Parser<Token, Ident, Error = ParseError> + Clone {
2173    filter_map(move |span: Range<usize>, token| match token {
2174        Token::Ident => {
2175            let text = &source[span.start..span.end];
2176            Ok(Ident::new(text.to_string(), make_span(&source, span)))
2177        }
2178        _ => Err(Simple::expected_input_found(
2179            span,
2180            vec![Some(Token::Ident)],
2181            Some(token),
2182        )),
2183    })
2184}
2185
2186/// Parser for variable references.
2187fn var_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
2188    ident_token_parser(source.clone()).map_with_span(move |name, span: Range<usize>| Expr::Var {
2189        name,
2190        span: make_span(&source, span),
2191    })
2192}
2193
2194/// Parser for type expressions.
2195fn type_parser(source: Arc<str>) -> impl Parser<Token, TypeExpr, Error = ParseError> + Clone {
2196    recursive(move |ty| {
2197        let src = source.clone();
2198
2199        let primitive = choice((
2200            just(Token::TyInt).to(TypeExpr::Int),
2201            just(Token::TyFloat).to(TypeExpr::Float),
2202            just(Token::TyBool).to(TypeExpr::Bool),
2203            just(Token::TyString).to(TypeExpr::String),
2204            just(Token::TyUnit).to(TypeExpr::Unit),
2205        ));
2206
2207        let list_ty = just(Token::TyList)
2208            .ignore_then(just(Token::Lt))
2209            .ignore_then(ty.clone())
2210            .then_ignore(just(Token::Gt))
2211            .map(|inner| TypeExpr::List(Box::new(inner)));
2212
2213        let option_ty = just(Token::TyOption)
2214            .ignore_then(just(Token::Lt))
2215            .ignore_then(ty.clone())
2216            .then_ignore(just(Token::Gt))
2217            .map(|inner| TypeExpr::Option(Box::new(inner)));
2218
2219        let oracle_ty = just(Token::TyOracle)
2220            .ignore_then(just(Token::Lt))
2221            .ignore_then(ty.clone())
2222            .then_ignore(just(Token::Gt))
2223            .map(|inner| TypeExpr::Oracle(Box::new(inner)));
2224
2225        let agent_ty = just(Token::TyAgent)
2226            .ignore_then(just(Token::Lt))
2227            .ignore_then(ident_token_parser(src.clone()))
2228            .then_ignore(just(Token::Gt))
2229            .map(TypeExpr::Agent);
2230
2231        // Named type with optional type arguments: MyRecord, Pair<Int, String>
2232        let named_ty = ident_token_parser(src.clone())
2233            .then(
2234                ty.clone()
2235                    .separated_by(just(Token::Comma))
2236                    .allow_trailing()
2237                    .delimited_by(just(Token::Lt), just(Token::Gt))
2238                    .or_not(),
2239            )
2240            .map(|(name, type_args)| TypeExpr::Named(name, type_args.unwrap_or_default()));
2241
2242        // Function type: Fn(A, B) -> C
2243        let fn_ty = just(Token::TyFn)
2244            .ignore_then(
2245                ty.clone()
2246                    .separated_by(just(Token::Comma))
2247                    .allow_trailing()
2248                    .delimited_by(just(Token::LParen), just(Token::RParen)),
2249            )
2250            .then_ignore(just(Token::Arrow))
2251            .then(ty.clone())
2252            .map(|(params, ret)| TypeExpr::Fn(params, Box::new(ret)));
2253
2254        // Map type: Map<K, V>
2255        let map_ty = just(Token::TyMap)
2256            .ignore_then(just(Token::Lt))
2257            .ignore_then(ty.clone())
2258            .then_ignore(just(Token::Comma))
2259            .then(ty.clone())
2260            .then_ignore(just(Token::Gt))
2261            .map(|(k, v)| TypeExpr::Map(Box::new(k), Box::new(v)));
2262
2263        // Result type: Result<T, E>
2264        let result_ty = just(Token::TyResult)
2265            .ignore_then(just(Token::Lt))
2266            .ignore_then(ty.clone())
2267            .then_ignore(just(Token::Comma))
2268            .then(ty.clone())
2269            .then_ignore(just(Token::Gt))
2270            .map(|(ok, err)| TypeExpr::Result(Box::new(ok), Box::new(err)));
2271
2272        // Tuple type: (A, B, C) - at least 2 elements
2273        let tuple_ty = ty
2274            .clone()
2275            .separated_by(just(Token::Comma))
2276            .at_least(2)
2277            .allow_trailing()
2278            .delimited_by(just(Token::LParen), just(Token::RParen))
2279            .map(TypeExpr::Tuple);
2280
2281        primitive
2282            .or(list_ty)
2283            .or(option_ty)
2284            .or(oracle_ty)
2285            .or(agent_ty)
2286            .or(fn_ty)
2287            .or(map_ty)
2288            .or(result_ty)
2289            .or(tuple_ty)
2290            .or(named_ty)
2291    })
2292}
2293
2294/// Parser for patterns in for loops.
2295/// Only supports simple bindings (`x`) and tuple patterns (`(k, v)`).
2296fn for_pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
2297    recursive(move |pattern| {
2298        let src = source.clone();
2299        let src2 = source.clone();
2300
2301        // Simple binding pattern: `x`
2302        let binding = ident_token_parser(src.clone()).map_with_span({
2303            let src = src.clone();
2304            move |name, span: Range<usize>| Pattern::Binding {
2305                name,
2306                span: make_span(&src, span),
2307            }
2308        });
2309
2310        // Tuple pattern: `(a, b)` - at least 2 elements
2311        let tuple_pattern = pattern
2312            .clone()
2313            .separated_by(just(Token::Comma))
2314            .at_least(2)
2315            .allow_trailing()
2316            .delimited_by(just(Token::LParen), just(Token::RParen))
2317            .map_with_span({
2318                let src = src2.clone();
2319                move |elements, span: Range<usize>| Pattern::Tuple {
2320                    elements,
2321                    span: make_span(&src, span),
2322                }
2323            });
2324
2325        tuple_pattern.or(binding)
2326    })
2327}
2328
2329/// Parser for patterns in match expressions.
2330fn pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
2331    recursive(move |pattern| {
2332        let src = source.clone();
2333        let src2 = source.clone();
2334        let src3 = source.clone();
2335        let src4 = source.clone();
2336        let src5 = source.clone();
2337
2338        // Wildcard pattern: `_`
2339        let wildcard = filter_map({
2340            let src = src.clone();
2341            move |span: Range<usize>, token| match &token {
2342                Token::Ident if src[span.start..span.end].eq("_") => Ok(()),
2343                _ => Err(Simple::expected_input_found(span, vec![], Some(token))),
2344            }
2345        })
2346        .map_with_span(move |_, span: Range<usize>| Pattern::Wildcard {
2347            span: make_span(&src2, span),
2348        });
2349
2350        // Literal patterns: 42, "hello", true, false
2351        let lit_int = filter_map({
2352            let src = src3.clone();
2353            move |span: Range<usize>, token| match token {
2354                Token::IntLit => {
2355                    let text = &src[span.start..span.end];
2356                    text.parse::<i64>()
2357                        .map(Literal::Int)
2358                        .map_err(|_| Simple::custom(span, "invalid integer literal"))
2359                }
2360                _ => Err(Simple::expected_input_found(
2361                    span,
2362                    vec![Some(Token::IntLit)],
2363                    Some(token),
2364                )),
2365            }
2366        })
2367        .map_with_span({
2368            let src = src3.clone();
2369            move |value, span: Range<usize>| Pattern::Literal {
2370                value,
2371                span: make_span(&src, span),
2372            }
2373        });
2374
2375        let lit_bool = just(Token::KwTrue)
2376            .to(Literal::Bool(true))
2377            .or(just(Token::KwFalse).to(Literal::Bool(false)))
2378            .map_with_span({
2379                let src = src3.clone();
2380                move |value, span: Range<usize>| Pattern::Literal {
2381                    value,
2382                    span: make_span(&src, span),
2383                }
2384            });
2385
2386        // Tuple pattern: (a, b, c) - at least 2 elements
2387        let tuple_pattern = pattern
2388            .clone()
2389            .separated_by(just(Token::Comma))
2390            .at_least(2)
2391            .allow_trailing()
2392            .delimited_by(just(Token::LParen), just(Token::RParen))
2393            .map_with_span({
2394                let src = src5.clone();
2395                move |elements, span: Range<usize>| Pattern::Tuple {
2396                    elements,
2397                    span: make_span(&src, span),
2398                }
2399            });
2400
2401        // Enum variant with optional payload: `Ok(x)` or `Status::Active`
2402        // Qualified with payload: EnumName::Variant(pattern)
2403        // Parser for enum name: accepts identifiers OR builtin type keywords (Option, Result)
2404        let pattern_enum_name_parser = {
2405            let src = src4.clone();
2406            ident_token_parser(src.clone()).or(just(Token::TyOption)
2407                .or(just(Token::TyResult))
2408                .map_with_span({
2409                    let src = src.clone();
2410                    move |token, span: Range<usize>| {
2411                        let name = match token {
2412                            Token::TyOption => "Option",
2413                            Token::TyResult => "Result",
2414                            _ => unreachable!(),
2415                        };
2416                        Ident {
2417                            name: name.to_string(),
2418                            span: make_span(&src, span),
2419                        }
2420                    }
2421                }))
2422        };
2423        let qualified_variant_with_payload = pattern_enum_name_parser
2424            .then_ignore(just(Token::ColonColon))
2425            .then(ident_token_parser(src4.clone()))
2426            .then(
2427                pattern
2428                    .clone()
2429                    .delimited_by(just(Token::LParen), just(Token::RParen))
2430                    .or_not(),
2431            )
2432            .map_with_span({
2433                let src = src4.clone();
2434                move |((enum_name, variant), payload), span: Range<usize>| Pattern::Variant {
2435                    enum_name: Some(enum_name),
2436                    variant,
2437                    payload: payload.map(Box::new),
2438                    span: make_span(&src, span),
2439                }
2440            });
2441
2442        // Unqualified variant with payload: `Ok(x)` or just `x`
2443        let unqualified_with_payload = ident_token_parser(src4.clone())
2444            .then(
2445                pattern
2446                    .clone()
2447                    .delimited_by(just(Token::LParen), just(Token::RParen))
2448                    .or_not(),
2449            )
2450            .map_with_span({
2451                let src = src4.clone();
2452                move |(name, payload), span: Range<usize>| {
2453                    // If it looks like a variant (starts with uppercase), treat as variant
2454                    // Otherwise treat as binding (only if no payload)
2455                    if name.name.chars().next().is_some_and(|c| c.is_uppercase())
2456                        || payload.is_some()
2457                    {
2458                        Pattern::Variant {
2459                            enum_name: None,
2460                            variant: name,
2461                            payload: payload.map(Box::new),
2462                            span: make_span(&src, span),
2463                        }
2464                    } else {
2465                        Pattern::Binding {
2466                            name,
2467                            span: make_span(&src, span),
2468                        }
2469                    }
2470                }
2471            });
2472
2473        // Order matters: try wildcard first, then tuple pattern, then qualified variant, then literals, then unqualified
2474        wildcard
2475            .or(tuple_pattern)
2476            .or(qualified_variant_with_payload)
2477            .or(lit_int)
2478            .or(lit_bool)
2479            .or(unqualified_with_payload)
2480    })
2481}
2482
2483/// Parser for literals.
2484fn literal_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
2485    let src = source.clone();
2486    let src2 = source.clone();
2487    let src3 = source.clone();
2488    let src4 = source.clone();
2489    let src5 = source.clone();
2490
2491    let int_lit = filter_map(move |span: Range<usize>, token| match token {
2492        Token::IntLit => {
2493            let text = &src[span.start..span.end];
2494            text.parse::<i64>()
2495                .map(Literal::Int)
2496                .map_err(|_| Simple::custom(span, "invalid integer literal"))
2497        }
2498        _ => Err(Simple::expected_input_found(
2499            span,
2500            vec![Some(Token::IntLit)],
2501            Some(token),
2502        )),
2503    })
2504    .map_with_span(move |value, span: Range<usize>| Expr::Literal {
2505        value,
2506        span: make_span(&src2, span),
2507    });
2508
2509    let float_lit = filter_map(move |span: Range<usize>, token| match token {
2510        Token::FloatLit => {
2511            let text = &src3[span.start..span.end];
2512            text.parse::<f64>()
2513                .map(Literal::Float)
2514                .map_err(|_| Simple::custom(span, "invalid float literal"))
2515        }
2516        _ => Err(Simple::expected_input_found(
2517            span,
2518            vec![Some(Token::FloatLit)],
2519            Some(token),
2520        )),
2521    })
2522    .map_with_span(move |value, span: Range<usize>| Expr::Literal {
2523        value,
2524        span: make_span(&src4, span),
2525    });
2526
2527    let src6 = source.clone();
2528    let string_lit = filter_map(move |span: Range<usize>, token| match token {
2529        Token::StringLit => {
2530            let text = &src5[span.start..span.end];
2531            let inner = &text[1..text.len() - 1];
2532            let parts = parse_string_template(inner, &make_span(&src5, span.clone()));
2533            Ok(parts)
2534        }
2535        _ => Err(Simple::expected_input_found(
2536            span,
2537            vec![Some(Token::StringLit)],
2538            Some(token),
2539        )),
2540    })
2541    .map_with_span(move |parts, span: Range<usize>| {
2542        let span = make_span(&src6, span);
2543        // If no interpolations, use a simple string literal
2544        if parts.len() == 1 {
2545            if let StringPart::Literal(s) = &parts[0] {
2546                return Expr::Literal {
2547                    value: Literal::String(s.clone()),
2548                    span,
2549                };
2550            }
2551        }
2552        // Otherwise, use StringInterp
2553        Expr::StringInterp {
2554            template: StringTemplate {
2555                parts,
2556                span: span.clone(),
2557            },
2558            span,
2559        }
2560    });
2561
2562    let bool_lit = just(Token::KwTrue)
2563        .to(Literal::Bool(true))
2564        .or(just(Token::KwFalse).to(Literal::Bool(false)))
2565        .map_with_span(move |value, _span: Range<usize>| Expr::Literal {
2566            value,
2567            span: Span::dummy(), // bool literals don't carry source
2568        });
2569
2570    int_lit.or(float_lit).or(string_lit).or(bool_lit)
2571}
2572
2573/// Parser for string templates (handles interpolation).
2574fn string_template_parser(
2575    source: Arc<str>,
2576) -> impl Parser<Token, StringTemplate, Error = ParseError> + Clone {
2577    filter_map(move |span: Range<usize>, token| match token {
2578        Token::StringLit => {
2579            let text = &source[span.start..span.end];
2580            let inner = &text[1..text.len() - 1];
2581            let parts = parse_string_template(inner, &make_span(&source, span.clone()));
2582            Ok(StringTemplate {
2583                parts,
2584                span: make_span(&source, span),
2585            })
2586        }
2587        _ => Err(Simple::expected_input_found(
2588            span,
2589            vec![Some(Token::StringLit)],
2590            Some(token),
2591        )),
2592    })
2593}
2594
2595/// Parse a string into template parts, handling `{expr}` interpolations.
2596/// Supports arbitrary expressions: `{name}`, `{a + b}`, `{foo(x, y)}`
2597fn parse_string_template(s: &str, span: &Span) -> Vec<StringPart> {
2598    let mut parts = Vec::new();
2599    let mut current = String::new();
2600    let mut chars = s.chars().peekable();
2601
2602    while let Some(ch) = chars.next() {
2603        if ch == '{' {
2604            if !current.is_empty() {
2605                parts.push(StringPart::Literal(std::mem::take(&mut current)));
2606            }
2607
2608            // Collect the full interpolation expression, handling nested braces
2609            let mut expr_str = String::new();
2610            let mut brace_depth = 1;
2611            let mut string_quote: Option<char> = None; // Track which quote type we're in
2612            let mut escape_next = false;
2613
2614            while let Some(&c) = chars.peek() {
2615                if escape_next {
2616                    expr_str.push(c);
2617                    chars.next();
2618                    escape_next = false;
2619                    continue;
2620                }
2621
2622                if c == '\\' && string_quote.is_some() {
2623                    escape_next = true;
2624                    expr_str.push(c);
2625                    chars.next();
2626                    continue;
2627                }
2628
2629                // Handle string delimiters (both " and ')
2630                if c == '"' || c == '\'' {
2631                    match string_quote {
2632                        None => string_quote = Some(c),           // Start of string
2633                        Some(q) if q == c => string_quote = None, // End of string
2634                        Some(_) => {} // Inside different type of string, ignore
2635                    }
2636                }
2637
2638                if string_quote.is_none() {
2639                    if c == '{' {
2640                        brace_depth += 1;
2641                    } else if c == '}' {
2642                        brace_depth -= 1;
2643                        if brace_depth == 0 {
2644                            chars.next();
2645                            break;
2646                        }
2647                    }
2648                }
2649
2650                expr_str.push(c);
2651                chars.next();
2652            }
2653
2654            if !expr_str.is_empty() {
2655                let expr = parse_interp_expr(&expr_str, span);
2656                parts.push(StringPart::Interpolation(Box::new(expr)));
2657            }
2658        } else if ch == '\\' {
2659            if let Some(escaped) = chars.next() {
2660                current.push(match escaped {
2661                    'n' => '\n',
2662                    't' => '\t',
2663                    'r' => '\r',
2664                    '\\' => '\\',
2665                    '"' => '"',
2666                    '{' => '{',
2667                    '}' => '}',
2668                    other => other,
2669                });
2670            }
2671        } else {
2672            current.push(ch);
2673        }
2674    }
2675
2676    if !current.is_empty() {
2677        parts.push(StringPart::Literal(current));
2678    }
2679
2680    if parts.is_empty() {
2681        parts.push(StringPart::Literal(String::new()));
2682    }
2683
2684    parts
2685}
2686
2687/// Parse an interpolation expression string into an Expr AST node.
2688/// Lexes and parses the substring using a mini recursive-descent parser.
2689fn parse_interp_expr(s: &str, span: &Span) -> Expr {
2690    let trimmed = s.trim();
2691    if trimmed.is_empty() {
2692        // Empty interpolation, return a placeholder
2693        return Expr::Literal {
2694            value: Literal::String(String::new()),
2695            span: span.clone(),
2696        };
2697    }
2698
2699    // Lex the expression substring
2700    let lex_result = crate::lex(trimmed);
2701    let (tokens, source) = match lex_result {
2702        Ok(result) => (result.tokens().to_vec(), trimmed.to_string()),
2703        Err(_) => {
2704            // Lexing failed, return the raw string as a variable name (fallback)
2705            return Expr::Var {
2706                name: Ident::new(trimmed.to_string(), span.clone()),
2707                span: span.clone(),
2708            };
2709        }
2710    };
2711
2712    if tokens.is_empty() {
2713        return Expr::Var {
2714            name: Ident::new(trimmed.to_string(), span.clone()),
2715            span: span.clone(),
2716        };
2717    }
2718
2719    // Parse with a simple recursive descent parser
2720    let mut parser = InterpExprParser::new(&tokens, &source, span.clone());
2721    parser.parse_expr()
2722}
2723
2724/// A simple recursive-descent parser for interpolation expressions.
2725/// Supports: variables, field access, tuple index, binary ops, function calls, parens
2726struct InterpExprParser<'a> {
2727    tokens: &'a [crate::Spanned],
2728    source: &'a str,
2729    pos: usize,
2730    span: Span,
2731}
2732
2733impl<'a> InterpExprParser<'a> {
2734    fn new(tokens: &'a [crate::Spanned], source: &'a str, span: Span) -> Self {
2735        Self {
2736            tokens,
2737            source,
2738            pos: 0,
2739            span,
2740        }
2741    }
2742
2743    fn current(&self) -> Option<&Token> {
2744        self.tokens.get(self.pos).map(|s| &s.token)
2745    }
2746
2747    fn current_text(&self) -> Option<&str> {
2748        self.tokens
2749            .get(self.pos)
2750            .map(|s| &self.source[s.start..s.end])
2751    }
2752
2753    fn advance(&mut self) {
2754        if self.pos < self.tokens.len() {
2755            self.pos += 1;
2756        }
2757    }
2758
2759    fn parse_expr(&mut self) -> Expr {
2760        self.parse_or()
2761    }
2762
2763    fn parse_or(&mut self) -> Expr {
2764        let mut left = self.parse_and();
2765        while matches!(self.current(), Some(Token::Or)) {
2766            self.advance();
2767            let right = self.parse_and();
2768            left = Expr::Binary {
2769                left: Box::new(left),
2770                op: BinOp::Or,
2771                right: Box::new(right),
2772                span: self.span.clone(),
2773            };
2774        }
2775        left
2776    }
2777
2778    fn parse_and(&mut self) -> Expr {
2779        let mut left = self.parse_comparison();
2780        while matches!(self.current(), Some(Token::And)) {
2781            self.advance();
2782            let right = self.parse_comparison();
2783            left = Expr::Binary {
2784                left: Box::new(left),
2785                op: BinOp::And,
2786                right: Box::new(right),
2787                span: self.span.clone(),
2788            };
2789        }
2790        left
2791    }
2792
2793    fn parse_comparison(&mut self) -> Expr {
2794        let mut left = self.parse_additive();
2795        loop {
2796            let op = match self.current() {
2797                Some(Token::EqEq) => BinOp::Eq,
2798                Some(Token::Ne) => BinOp::Ne,
2799                Some(Token::Lt) => BinOp::Lt,
2800                Some(Token::Le) => BinOp::Le,
2801                Some(Token::Gt) => BinOp::Gt,
2802                Some(Token::Ge) => BinOp::Ge,
2803                _ => break,
2804            };
2805            self.advance();
2806            let right = self.parse_additive();
2807            left = Expr::Binary {
2808                left: Box::new(left),
2809                op,
2810                right: Box::new(right),
2811                span: self.span.clone(),
2812            };
2813        }
2814        left
2815    }
2816
2817    fn parse_additive(&mut self) -> Expr {
2818        let mut left = self.parse_multiplicative();
2819        loop {
2820            let op = match self.current() {
2821                Some(Token::Plus) => BinOp::Add,
2822                Some(Token::Minus) => BinOp::Sub,
2823                Some(Token::PlusPlus) => BinOp::Concat,
2824                _ => break,
2825            };
2826            self.advance();
2827            let right = self.parse_multiplicative();
2828            left = Expr::Binary {
2829                left: Box::new(left),
2830                op,
2831                right: Box::new(right),
2832                span: self.span.clone(),
2833            };
2834        }
2835        left
2836    }
2837
2838    fn parse_multiplicative(&mut self) -> Expr {
2839        let mut left = self.parse_unary();
2840        loop {
2841            let op = match self.current() {
2842                Some(Token::Star) => BinOp::Mul,
2843                Some(Token::Slash) => BinOp::Div,
2844                Some(Token::Percent) => BinOp::Rem,
2845                _ => break,
2846            };
2847            self.advance();
2848            let right = self.parse_unary();
2849            left = Expr::Binary {
2850                left: Box::new(left),
2851                op,
2852                right: Box::new(right),
2853                span: self.span.clone(),
2854            };
2855        }
2856        left
2857    }
2858
2859    fn parse_unary(&mut self) -> Expr {
2860        match self.current() {
2861            Some(Token::Minus) => {
2862                self.advance();
2863                let operand = self.parse_unary();
2864                Expr::Unary {
2865                    op: UnaryOp::Neg,
2866                    operand: Box::new(operand),
2867                    span: self.span.clone(),
2868                }
2869            }
2870            Some(Token::Bang) => {
2871                self.advance();
2872                let operand = self.parse_unary();
2873                Expr::Unary {
2874                    op: UnaryOp::Not,
2875                    operand: Box::new(operand),
2876                    span: self.span.clone(),
2877                }
2878            }
2879            _ => self.parse_postfix(),
2880        }
2881    }
2882
2883    fn parse_postfix(&mut self) -> Expr {
2884        let mut expr = self.parse_primary();
2885
2886        loop {
2887            match self.current() {
2888                Some(Token::Dot) => {
2889                    self.advance();
2890                    match self.current() {
2891                        Some(Token::IntLit) => {
2892                            let text = self.current_text().unwrap_or("0");
2893                            let index = text.parse::<usize>().unwrap_or(0);
2894                            self.advance();
2895                            expr = Expr::TupleIndex {
2896                                tuple: Box::new(expr),
2897                                index,
2898                                span: self.span.clone(),
2899                            };
2900                        }
2901                        Some(Token::Ident) => {
2902                            let name = self.current_text().unwrap_or("").to_string();
2903                            let field = Ident::new(name, self.span.clone());
2904                            self.advance();
2905                            expr = Expr::FieldAccess {
2906                                object: Box::new(expr),
2907                                field,
2908                                span: self.span.clone(),
2909                            };
2910                        }
2911                        _ => break,
2912                    }
2913                }
2914                Some(Token::ColonColon) if matches!(expr, Expr::Var { .. }) => {
2915                    // Turbofish: name::<Type1, Type2>(args)
2916                    if let Expr::Var { name, .. } = expr {
2917                        self.advance(); // consume ::
2918                        let type_args = self.parse_turbofish();
2919                        // Now expect (
2920                        if matches!(self.current(), Some(Token::LParen)) {
2921                            self.advance();
2922                            let args = self.parse_args();
2923                            expr = Expr::Call {
2924                                name,
2925                                type_args,
2926                                args,
2927                                span: self.span.clone(),
2928                            };
2929                        } else {
2930                            // Turbofish without call - just treat as var with type args
2931                            // This shouldn't normally happen, but handle gracefully
2932                            expr = Expr::Call {
2933                                name,
2934                                type_args,
2935                                args: vec![],
2936                                span: self.span.clone(),
2937                            };
2938                        }
2939                    }
2940                }
2941                Some(Token::LParen) if matches!(expr, Expr::Var { .. }) => {
2942                    // Function call without turbofish
2943                    if let Expr::Var { name, .. } = expr {
2944                        self.advance();
2945                        let args = self.parse_args();
2946                        expr = Expr::Call {
2947                            name,
2948                            type_args: vec![],
2949                            args,
2950                            span: self.span.clone(),
2951                        };
2952                    }
2953                }
2954                _ => break,
2955            }
2956        }
2957
2958        expr
2959    }
2960
2961    fn parse_primary(&mut self) -> Expr {
2962        match self.current() {
2963            Some(Token::IntLit) => {
2964                let text = self.current_text().unwrap_or("0");
2965                let n = text.parse::<i64>().unwrap_or(0);
2966                self.advance();
2967                Expr::Literal {
2968                    value: Literal::Int(n),
2969                    span: self.span.clone(),
2970                }
2971            }
2972            Some(Token::FloatLit) => {
2973                let text = self.current_text().unwrap_or("0.0");
2974                let f = text.parse::<f64>().unwrap_or(0.0);
2975                self.advance();
2976                Expr::Literal {
2977                    value: Literal::Float(f),
2978                    span: self.span.clone(),
2979                }
2980            }
2981            Some(Token::StringLit) => {
2982                let text = self.current_text().unwrap_or("\"\"");
2983                // Remove surrounding quotes
2984                let s = if text.len() >= 2 {
2985                    text[1..text.len() - 1].to_string()
2986                } else {
2987                    String::new()
2988                };
2989                self.advance();
2990                Expr::Literal {
2991                    value: Literal::String(s),
2992                    span: self.span.clone(),
2993                }
2994            }
2995            Some(Token::KwTrue) => {
2996                self.advance();
2997                Expr::Literal {
2998                    value: Literal::Bool(true),
2999                    span: self.span.clone(),
3000                }
3001            }
3002            Some(Token::KwFalse) => {
3003                self.advance();
3004                Expr::Literal {
3005                    value: Literal::Bool(false),
3006                    span: self.span.clone(),
3007                }
3008            }
3009            Some(Token::Ident) => {
3010                let name = self.current_text().unwrap_or("").to_string();
3011                self.advance();
3012                Expr::Var {
3013                    name: Ident::new(name, self.span.clone()),
3014                    span: self.span.clone(),
3015                }
3016            }
3017            Some(Token::KwSelf) => {
3018                self.advance();
3019                // Handle self.field
3020                if matches!(self.current(), Some(Token::Dot)) {
3021                    self.advance();
3022                    if let Some(Token::Ident) = self.current() {
3023                        let field_name = self.current_text().unwrap_or("").to_string();
3024                        let field = Ident::new(field_name, self.span.clone());
3025                        self.advance();
3026                        return Expr::SelfField {
3027                            field,
3028                            span: self.span.clone(),
3029                        };
3030                    }
3031                }
3032                Expr::Var {
3033                    name: Ident::new("self".to_string(), self.span.clone()),
3034                    span: self.span.clone(),
3035                }
3036            }
3037            Some(Token::LParen) => {
3038                self.advance();
3039                let inner = self.parse_expr();
3040                if matches!(self.current(), Some(Token::RParen)) {
3041                    self.advance();
3042                }
3043                Expr::Paren {
3044                    inner: Box::new(inner),
3045                    span: self.span.clone(),
3046                }
3047            }
3048            Some(Token::LBracket) => {
3049                // List literal
3050                self.advance();
3051                let mut elements = Vec::new();
3052                while !matches!(self.current(), Some(Token::RBracket) | None) {
3053                    elements.push(self.parse_expr());
3054                    if matches!(self.current(), Some(Token::Comma)) {
3055                        self.advance();
3056                    } else {
3057                        break;
3058                    }
3059                }
3060                if matches!(self.current(), Some(Token::RBracket)) {
3061                    self.advance();
3062                }
3063                Expr::List {
3064                    elements,
3065                    span: self.span.clone(),
3066                }
3067            }
3068            _ => {
3069                // Unknown token, return empty string literal as fallback
3070                Expr::Literal {
3071                    value: Literal::String(String::new()),
3072                    span: self.span.clone(),
3073                }
3074            }
3075        }
3076    }
3077
3078    fn parse_args(&mut self) -> Vec<Expr> {
3079        let mut args = Vec::new();
3080        while !matches!(self.current(), Some(Token::RParen) | None) {
3081            args.push(self.parse_expr());
3082            if matches!(self.current(), Some(Token::Comma)) {
3083                self.advance();
3084            } else {
3085                break;
3086            }
3087        }
3088        if matches!(self.current(), Some(Token::RParen)) {
3089            self.advance();
3090        }
3091        args
3092    }
3093
3094    /// Parse turbofish type arguments: `<Type1, Type2>`
3095    /// Assumes `::` has already been consumed.
3096    fn parse_turbofish(&mut self) -> Vec<TypeExpr> {
3097        let mut type_args = Vec::new();
3098
3099        // Expect <
3100        if !matches!(self.current(), Some(Token::Lt)) {
3101            return type_args;
3102        }
3103        self.advance();
3104
3105        // Parse comma-separated types until >
3106        loop {
3107            if matches!(self.current(), Some(Token::Gt) | None) {
3108                break;
3109            }
3110
3111            if let Some(ty) = self.parse_type() {
3112                type_args.push(ty);
3113            }
3114
3115            if matches!(self.current(), Some(Token::Comma)) {
3116                self.advance();
3117            } else {
3118                break;
3119            }
3120        }
3121
3122        // Consume >
3123        if matches!(self.current(), Some(Token::Gt)) {
3124            self.advance();
3125        }
3126
3127        type_args
3128    }
3129
3130    /// Parse a type expression: `Int`, `String`, `List<T>`, `Map<K, V>`, etc.
3131    fn parse_type(&mut self) -> Option<TypeExpr> {
3132        let name = match self.current() {
3133            Some(Token::Ident) => self.current_text().unwrap_or("").to_string(),
3134            Some(Token::TyInt) => "Int".to_string(),
3135            Some(Token::TyFloat) => "Float".to_string(),
3136            Some(Token::TyBool) => "Bool".to_string(),
3137            Some(Token::TyString) => "String".to_string(),
3138            Some(Token::TyUnit) => "Unit".to_string(),
3139            _ => return None,
3140        };
3141        self.advance();
3142
3143        // Check for type parameters: <T, U>
3144        if matches!(self.current(), Some(Token::Lt)) {
3145            self.advance();
3146            let mut params = Vec::new();
3147
3148            loop {
3149                if matches!(self.current(), Some(Token::Gt) | None) {
3150                    break;
3151                }
3152
3153                if let Some(param) = self.parse_type() {
3154                    params.push(param);
3155                }
3156
3157                if matches!(self.current(), Some(Token::Comma)) {
3158                    self.advance();
3159                } else {
3160                    break;
3161                }
3162            }
3163
3164            if matches!(self.current(), Some(Token::Gt)) {
3165                self.advance();
3166            }
3167
3168            // Return generic type
3169            Some(match name.as_str() {
3170                "List" => {
3171                    if let Some(elem) = params.into_iter().next() {
3172                        TypeExpr::List(Box::new(elem))
3173                    } else {
3174                        TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3175                    }
3176                }
3177                "Map" => {
3178                    let mut iter = params.into_iter();
3179                    if let (Some(k), Some(v)) = (iter.next(), iter.next()) {
3180                        TypeExpr::Map(Box::new(k), Box::new(v))
3181                    } else {
3182                        TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3183                    }
3184                }
3185                "Option" => {
3186                    if let Some(inner) = params.into_iter().next() {
3187                        TypeExpr::Option(Box::new(inner))
3188                    } else {
3189                        TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3190                    }
3191                }
3192                "Result" => {
3193                    let mut iter = params.into_iter();
3194                    if let (Some(ok), Some(err)) = (iter.next(), iter.next()) {
3195                        TypeExpr::Result(Box::new(ok), Box::new(err))
3196                    } else {
3197                        TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3198                    }
3199                }
3200                _ => {
3201                    // Generic named type
3202                    TypeExpr::Named(Ident::new(name, self.span.clone()), params)
3203                }
3204            })
3205        } else {
3206            // Simple type
3207            Some(match name.as_str() {
3208                "Int" => TypeExpr::Int,
3209                "Float" => TypeExpr::Float,
3210                "Bool" => TypeExpr::Bool,
3211                "String" => TypeExpr::String,
3212                "Unit" => TypeExpr::Unit,
3213                _ => TypeExpr::Named(Ident::new(name, self.span.clone()), vec![]),
3214            })
3215        }
3216    }
3217}
3218
3219// =============================================================================
3220// Tests
3221// =============================================================================
3222
3223#[cfg(test)]
3224mod tests {
3225    use super::*;
3226    use crate::lex;
3227
3228    fn parse_str(source: &str) -> (Option<Program>, Vec<ParseError>) {
3229        let lex_result = lex(source).expect("lexing should succeed");
3230        let source_arc: Arc<str> = Arc::from(source);
3231        parse(lex_result.tokens(), source_arc)
3232    }
3233
3234    #[test]
3235    fn parse_minimal_program() {
3236        let source = r#"
3237            agent Main {
3238                on start {
3239                    yield(42);
3240                }
3241            }
3242            run Main;
3243        "#;
3244
3245        let (prog, errors) = parse_str(source);
3246        assert!(errors.is_empty(), "errors: {errors:?}");
3247        let prog = prog.expect("should parse");
3248
3249        assert_eq!(prog.agents.len(), 1);
3250        assert_eq!(prog.agents[0].name.name, "Main");
3251        assert_eq!(prog.run_agent.as_ref().unwrap().name, "Main");
3252    }
3253
3254    #[test]
3255    fn parse_agent_with_beliefs() {
3256        let source = r#"
3257            agent Researcher {
3258                topic: String
3259                max_words: Int
3260
3261                on start {
3262                    yield(self.topic);
3263                }
3264            }
3265            run Researcher;
3266        "#;
3267
3268        let (prog, errors) = parse_str(source);
3269        assert!(errors.is_empty(), "errors: {errors:?}");
3270        let prog = prog.expect("should parse");
3271
3272        assert_eq!(prog.agents[0].beliefs.len(), 2);
3273        assert_eq!(prog.agents[0].beliefs[0].name.name, "topic");
3274        assert_eq!(prog.agents[0].beliefs[1].name.name, "max_words");
3275    }
3276
3277    #[test]
3278    fn parse_multiple_handlers() {
3279        let source = r#"
3280            agent Worker {
3281                on start {
3282                    print("started");
3283                }
3284
3285                on message(msg: String) {
3286                    print(msg);
3287                }
3288
3289                on stop {
3290                    print("stopped");
3291                }
3292            }
3293            run Worker;
3294        "#;
3295
3296        let (prog, errors) = parse_str(source);
3297        assert!(errors.is_empty(), "errors: {errors:?}");
3298        let prog = prog.expect("should parse");
3299
3300        assert_eq!(prog.agents[0].handlers.len(), 3);
3301        assert_eq!(prog.agents[0].handlers[0].event, EventKind::Start);
3302        assert!(matches!(
3303            prog.agents[0].handlers[1].event,
3304            EventKind::Message { .. }
3305        ));
3306        assert_eq!(prog.agents[0].handlers[2].event, EventKind::Stop);
3307    }
3308
3309    #[test]
3310    fn parse_v2_lifecycle_hooks() {
3311        let source = r#"
3312            agent StatefulWorker {
3313                on waking {
3314                    // Load persisted state
3315                    trace("waking up");
3316                }
3317
3318                on start {
3319                    trace("started");
3320                }
3321
3322                on pause {
3323                    // Save state before pause
3324                    trace("pausing");
3325                }
3326
3327                on resume {
3328                    trace("resuming");
3329                }
3330
3331                on resting {
3332                    // Cleanup before shutdown
3333                    trace("resting");
3334                }
3335            }
3336            run StatefulWorker;
3337        "#;
3338
3339        let (prog, errors) = parse_str(source);
3340        assert!(errors.is_empty(), "errors: {errors:?}");
3341        let prog = prog.expect("should parse");
3342
3343        assert_eq!(prog.agents[0].handlers.len(), 5);
3344        assert_eq!(prog.agents[0].handlers[0].event, EventKind::Waking);
3345        assert_eq!(prog.agents[0].handlers[1].event, EventKind::Start);
3346        assert_eq!(prog.agents[0].handlers[2].event, EventKind::Pause);
3347        assert_eq!(prog.agents[0].handlers[3].event, EventKind::Resume);
3348        assert_eq!(prog.agents[0].handlers[4].event, EventKind::Resting);
3349    }
3350
3351    #[test]
3352    fn parse_persistent_beliefs() {
3353        let source = r#"
3354            agent DatabaseSteward {
3355                @persistent schema_version: Int
3356                @persistent migration_log: List<String>
3357                active_connections: Int
3358
3359                on start {
3360                    yield(0);
3361                }
3362            }
3363            run DatabaseSteward;
3364        "#;
3365
3366        let (prog, errors) = parse_str(source);
3367        assert!(errors.is_empty(), "errors: {errors:?}");
3368        let prog = prog.expect("should parse");
3369
3370        assert_eq!(prog.agents[0].beliefs.len(), 3);
3371        assert!(prog.agents[0].beliefs[0].is_persistent);
3372        assert_eq!(prog.agents[0].beliefs[0].name.name, "schema_version");
3373        assert!(prog.agents[0].beliefs[1].is_persistent);
3374        assert_eq!(prog.agents[0].beliefs[1].name.name, "migration_log");
3375        assert!(!prog.agents[0].beliefs[2].is_persistent);
3376        assert_eq!(prog.agents[0].beliefs[2].name.name, "active_connections");
3377    }
3378
3379    #[test]
3380    fn parse_function() {
3381        let source = r#"
3382            fn greet(name: String) -> String {
3383                return "Hello, " ++ name;
3384            }
3385
3386            agent Main {
3387                on start {
3388                    yield(greet("World"));
3389                }
3390            }
3391            run Main;
3392        "#;
3393
3394        let (prog, errors) = parse_str(source);
3395        assert!(errors.is_empty(), "errors: {errors:?}");
3396        let prog = prog.expect("should parse");
3397
3398        assert_eq!(prog.functions.len(), 1);
3399        assert_eq!(prog.functions[0].name.name, "greet");
3400        assert_eq!(prog.functions[0].params.len(), 1);
3401    }
3402
3403    #[test]
3404    fn parse_let_statement() {
3405        let source = r#"
3406            agent Main {
3407                on start {
3408                    let x: Int = 42;
3409                    let y = "hello";
3410                    yield(x);
3411                }
3412            }
3413            run Main;
3414        "#;
3415
3416        let (prog, errors) = parse_str(source);
3417        assert!(errors.is_empty(), "errors: {errors:?}");
3418        let prog = prog.expect("should parse");
3419
3420        let stmts = &prog.agents[0].handlers[0].body.stmts;
3421        assert!(matches!(stmts[0], Stmt::Let { .. }));
3422        assert!(matches!(stmts[1], Stmt::Let { .. }));
3423    }
3424
3425    #[test]
3426    fn parse_if_statement() {
3427        let source = r#"
3428            agent Main {
3429                on start {
3430                    if true {
3431                        yield(1);
3432                    } else {
3433                        yield(2);
3434                    }
3435                }
3436            }
3437            run Main;
3438        "#;
3439
3440        let (prog, errors) = parse_str(source);
3441        assert!(errors.is_empty(), "errors: {errors:?}");
3442        let prog = prog.expect("should parse");
3443
3444        let stmts = &prog.agents[0].handlers[0].body.stmts;
3445        assert!(matches!(stmts[0], Stmt::If { .. }));
3446    }
3447
3448    #[test]
3449    fn parse_for_loop() {
3450        let source = r#"
3451            agent Main {
3452                on start {
3453                    for x in [1, 2, 3] {
3454                        print(x);
3455                    }
3456                    yield(0);
3457                }
3458            }
3459            run Main;
3460        "#;
3461
3462        let (prog, errors) = parse_str(source);
3463        assert!(errors.is_empty(), "errors: {errors:?}");
3464        let prog = prog.expect("should parse");
3465
3466        let stmts = &prog.agents[0].handlers[0].body.stmts;
3467        assert!(matches!(stmts[0], Stmt::For { .. }));
3468    }
3469
3470    #[test]
3471    fn parse_spawn_await() {
3472        let source = r#"
3473            agent Worker {
3474                name: String
3475
3476                on start {
3477                    yield(self.name);
3478                }
3479            }
3480
3481            agent Main {
3482                on start {
3483                    let w = summon Worker { name: "test" };
3484                    let result = await w;
3485                    yield(result);
3486                }
3487            }
3488            run Main;
3489        "#;
3490
3491        let (prog, errors) = parse_str(source);
3492        assert!(errors.is_empty(), "errors: {errors:?}");
3493        prog.expect("should parse");
3494    }
3495
3496    #[test]
3497    fn parse_await_with_timeout() {
3498        let source = r#"
3499            agent Worker {
3500                on start {
3501                    yield("done");
3502                }
3503            }
3504
3505            agent Main {
3506                on start {
3507                    let w = summon Worker {};
3508                    let result = await w timeout(5000);
3509                    yield(result);
3510                }
3511            }
3512            run Main;
3513        "#;
3514
3515        let (prog, errors) = parse_str(source);
3516        assert!(errors.is_empty(), "errors: {errors:?}");
3517        let prog = prog.expect("should parse");
3518
3519        // Find the await statement in Main's on start handler
3520        let main = &prog.agents[1];
3521        let stmts = &main.handlers[0].body.stmts;
3522        // stmts[0] is the let w = spawn...
3523        // stmts[1] is the let result = await w timeout(5000)
3524        if let Stmt::Let { value, .. } = &stmts[1] {
3525            if let Expr::Await { timeout, .. } = value {
3526                assert!(timeout.is_some(), "timeout should be present");
3527            } else {
3528                panic!("expected Await expression");
3529            }
3530        } else {
3531            panic!("expected Let statement with value");
3532        }
3533    }
3534
3535    #[test]
3536    fn parse_divine() {
3537        let source = r#"
3538            agent Main {
3539                on start {
3540                    let result = divine("What is 2+2?");
3541                    yield(result);
3542                }
3543            }
3544            run Main;
3545        "#;
3546
3547        let (prog, errors) = parse_str(source);
3548        assert!(errors.is_empty(), "errors: {errors:?}");
3549        prog.expect("should parse");
3550    }
3551
3552    #[test]
3553    fn parse_binary_precedence() {
3554        let source = r#"
3555            agent Main {
3556                on start {
3557                    let x = 2 + 3 * 4;
3558                    yield(x);
3559                }
3560            }
3561            run Main;
3562        "#;
3563
3564        let (prog, errors) = parse_str(source);
3565        assert!(errors.is_empty(), "errors: {errors:?}");
3566        let prog = prog.expect("should parse");
3567
3568        let stmts = &prog.agents[0].handlers[0].body.stmts;
3569        if let Stmt::Let { value, .. } = &stmts[0] {
3570            if let Expr::Binary { op, .. } = value {
3571                assert_eq!(*op, BinOp::Add);
3572            } else {
3573                panic!("expected binary expression");
3574            }
3575        }
3576    }
3577
3578    #[test]
3579    fn parse_string_interpolation() {
3580        let source = r#"
3581            agent Main {
3582                on start {
3583                    let name = "World";
3584                    let msg = divine("Greet {name}");
3585                    yield(msg);
3586                }
3587            }
3588            run Main;
3589        "#;
3590
3591        let (prog, errors) = parse_str(source);
3592        assert!(errors.is_empty(), "errors: {errors:?}");
3593        let prog = prog.expect("should parse");
3594
3595        let stmts = &prog.agents[0].handlers[0].body.stmts;
3596        if let Stmt::Let { value, .. } = &stmts[1] {
3597            if let Expr::Divine { template, .. } = value {
3598                assert!(template.has_interpolations());
3599            } else {
3600                panic!("expected infer expression");
3601            }
3602        }
3603    }
3604
3605    #[test]
3606    fn parse_single_quoted_string() {
3607        let source = r#"
3608            agent Main {
3609                on start {
3610                    let x = 'hello';
3611                    yield(0);
3612                }
3613            }
3614            run Main;
3615        "#;
3616
3617        let (prog, errors) = parse_str(source);
3618        assert!(errors.is_empty(), "errors: {errors:?}");
3619        let prog = prog.expect("should parse");
3620
3621        let stmts = &prog.agents[0].handlers[0].body.stmts;
3622        if let Stmt::Let { value, .. } = &stmts[0] {
3623            if let Expr::Literal {
3624                value: Literal::String(s),
3625                ..
3626            } = value
3627            {
3628                assert_eq!(s, "hello");
3629            } else {
3630                panic!("expected string literal, got {:?}", value);
3631            }
3632        } else {
3633            panic!("expected let statement");
3634        }
3635    }
3636
3637    #[test]
3638    fn parse_single_quoted_string_in_interpolation() {
3639        // Use single quotes for strings inside interpolations to avoid
3640        // conflicts with the outer double-quoted string
3641        let source = r#"
3642            fn reverse(s: String) -> String {
3643                return s;
3644            }
3645            agent Main {
3646                on start {
3647                    print("Result: {reverse('hello')}");
3648                    print("Concat: {'abc' ++ 'def'}");
3649                    yield(0);
3650                }
3651            }
3652            run Main;
3653        "#;
3654
3655        let (prog, errors) = parse_str(source);
3656        assert!(errors.is_empty(), "errors: {errors:?}");
3657        let prog = prog.expect("should parse");
3658
3659        // Verify the string interpolation was parsed
3660        let stmts = &prog.agents[0].handlers[0].body.stmts;
3661        if let Stmt::Expr {
3662            expr: Expr::Call { args, .. },
3663            ..
3664        } = &stmts[0]
3665        {
3666            if let Expr::StringInterp { template, .. } = &args[0] {
3667                assert!(template.has_interpolations());
3668            } else {
3669                panic!("expected string interpolation");
3670            }
3671        } else {
3672            panic!("expected print call");
3673        }
3674    }
3675
3676    // =========================================================================
3677    // Error recovery tests
3678    // =========================================================================
3679
3680    #[test]
3681    fn recover_from_malformed_agent_continues_to_next() {
3682        // First agent has syntax error (missing type after colon), second is valid
3683        let source = r#"
3684            agent Broken {
3685                x:
3686            }
3687
3688            agent Main {
3689                on start {
3690                    yield(42);
3691                }
3692            }
3693            run Main;
3694        "#;
3695
3696        let (prog, errors) = parse_str(source);
3697        // Should have errors from the broken agent
3698        assert!(!errors.is_empty(), "should have parse errors");
3699        // But should still produce a program with the valid agent
3700        let prog = prog.expect("should produce partial AST");
3701        assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
3702    }
3703
3704    #[test]
3705    fn recover_from_mismatched_braces_in_block() {
3706        let source = r#"
3707            agent Main {
3708                on start {
3709                    let x = [1, 2, 3;
3710                    yield(42);
3711                }
3712            }
3713            run Main;
3714        "#;
3715
3716        let (prog, errors) = parse_str(source);
3717        // Should have errors but still produce an AST
3718        assert!(!errors.is_empty(), "should have parse errors");
3719        assert!(prog.is_some(), "should produce partial AST despite errors");
3720    }
3721
3722    #[test]
3723    fn parse_mod_declaration() {
3724        let source = r#"
3725            mod agents;
3726            pub mod utils;
3727
3728            agent Main {
3729                on start {
3730                    yield(42);
3731                }
3732            }
3733            run Main;
3734        "#;
3735
3736        let (prog, errors) = parse_str(source);
3737        assert!(errors.is_empty(), "errors: {errors:?}");
3738        let prog = prog.expect("should parse");
3739
3740        assert_eq!(prog.mod_decls.len(), 2);
3741        assert!(!prog.mod_decls[0].is_pub);
3742        assert_eq!(prog.mod_decls[0].name.name, "agents");
3743        assert!(prog.mod_decls[1].is_pub);
3744        assert_eq!(prog.mod_decls[1].name.name, "utils");
3745    }
3746
3747    #[test]
3748    fn parse_use_simple() {
3749        let source = r#"
3750            use agents::Researcher;
3751
3752            agent Main {
3753                on start {
3754                    yield(42);
3755                }
3756            }
3757            run Main;
3758        "#;
3759
3760        let (prog, errors) = parse_str(source);
3761        assert!(errors.is_empty(), "errors: {errors:?}");
3762        let prog = prog.expect("should parse");
3763
3764        assert_eq!(prog.use_decls.len(), 1);
3765        assert!(!prog.use_decls[0].is_pub);
3766        assert_eq!(prog.use_decls[0].path.len(), 2);
3767        assert_eq!(prog.use_decls[0].path[0].name, "agents");
3768        assert_eq!(prog.use_decls[0].path[1].name, "Researcher");
3769        assert!(matches!(prog.use_decls[0].kind, UseKind::Simple(None)));
3770    }
3771
3772    #[test]
3773    fn parse_use_with_alias() {
3774        let source = r#"
3775            use agents::Researcher as R;
3776
3777            agent Main {
3778                on start {
3779                    yield(42);
3780                }
3781            }
3782            run Main;
3783        "#;
3784
3785        let (prog, errors) = parse_str(source);
3786        assert!(errors.is_empty(), "errors: {errors:?}");
3787        let prog = prog.expect("should parse");
3788
3789        assert_eq!(prog.use_decls.len(), 1);
3790        if let UseKind::Simple(Some(alias)) = &prog.use_decls[0].kind {
3791            assert_eq!(alias.name, "R");
3792        } else {
3793            panic!("expected Simple with alias");
3794        }
3795    }
3796
3797    #[test]
3798    fn parse_pub_agent() {
3799        let source = r#"
3800            pub agent Worker {
3801                on start {
3802                    yield(42);
3803                }
3804            }
3805
3806            agent Main {
3807                on start {
3808                    yield(0);
3809                }
3810            }
3811            run Main;
3812        "#;
3813
3814        let (prog, errors) = parse_str(source);
3815        assert!(errors.is_empty(), "errors: {errors:?}");
3816        let prog = prog.expect("should parse");
3817
3818        assert_eq!(prog.agents.len(), 2);
3819        assert!(prog.agents[0].is_pub);
3820        assert_eq!(prog.agents[0].name.name, "Worker");
3821        assert!(!prog.agents[1].is_pub);
3822    }
3823
3824    #[test]
3825    fn parse_pub_function() {
3826        let source = r#"
3827            pub fn helper(x: Int) -> Int {
3828                return x;
3829            }
3830
3831            agent Main {
3832                on start {
3833                    yield(helper(42));
3834                }
3835            }
3836            run Main;
3837        "#;
3838
3839        let (prog, errors) = parse_str(source);
3840        assert!(errors.is_empty(), "errors: {errors:?}");
3841        let prog = prog.expect("should parse");
3842
3843        assert_eq!(prog.functions.len(), 1);
3844        assert!(prog.functions[0].is_pub);
3845        assert_eq!(prog.functions[0].name.name, "helper");
3846    }
3847
3848    #[test]
3849    fn parse_library_no_run() {
3850        // A library module has no `run` statement
3851        let source = r#"
3852            pub agent Worker {
3853                on start {
3854                    yield(42);
3855                }
3856            }
3857
3858            pub fn helper(x: Int) -> Int {
3859                return x;
3860            }
3861        "#;
3862
3863        let (prog, errors) = parse_str(source);
3864        assert!(errors.is_empty(), "errors: {errors:?}");
3865        let prog = prog.expect("should parse");
3866
3867        assert!(prog.run_agent.is_none());
3868        assert_eq!(prog.agents.len(), 1);
3869        assert_eq!(prog.functions.len(), 1);
3870    }
3871
3872    #[test]
3873    fn recover_multiple_errors_reported() {
3874        // Multiple errors in different places - incomplete field missing type
3875        let source = r#"
3876            agent A {
3877                x:
3878            }
3879
3880            agent Main {
3881                on start {
3882                    yield(42);
3883                }
3884            }
3885            run Main;
3886        "#;
3887
3888        let (prog, errors) = parse_str(source);
3889        // The malformed field is missing its type after `:` so should cause an error
3890        // However, with recovery the valid agent may still parse
3891        // Check that we either have errors or recovered successfully
3892        if errors.is_empty() {
3893            // Recovery succeeded - should have parsed Main agent
3894            let prog = prog.expect("should have AST with recovery");
3895            assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
3896        }
3897        // Either way, the test passes - we're testing recovery works
3898    }
3899
3900    #[test]
3901    fn parse_record_declaration() {
3902        let source = r#"
3903            record Point {
3904                x: Int,
3905                y: Int,
3906            }
3907
3908            agent Main {
3909                on start {
3910                    yield(0);
3911                }
3912            }
3913            run Main;
3914        "#;
3915
3916        let (prog, errors) = parse_str(source);
3917        assert!(errors.is_empty(), "errors: {errors:?}");
3918        let prog = prog.expect("should parse");
3919
3920        assert_eq!(prog.records.len(), 1);
3921        assert!(!prog.records[0].is_pub);
3922        assert_eq!(prog.records[0].name.name, "Point");
3923        assert_eq!(prog.records[0].fields.len(), 2);
3924        assert_eq!(prog.records[0].fields[0].name.name, "x");
3925        assert_eq!(prog.records[0].fields[1].name.name, "y");
3926    }
3927
3928    #[test]
3929    fn parse_pub_record() {
3930        let source = r#"
3931            pub record Config {
3932                host: String,
3933                port: Int,
3934            }
3935
3936            agent Main {
3937                on start { yield(0); }
3938            }
3939            run Main;
3940        "#;
3941
3942        let (prog, errors) = parse_str(source);
3943        assert!(errors.is_empty(), "errors: {errors:?}");
3944        let prog = prog.expect("should parse");
3945
3946        assert_eq!(prog.records.len(), 1);
3947        assert!(prog.records[0].is_pub);
3948        assert_eq!(prog.records[0].name.name, "Config");
3949    }
3950
3951    #[test]
3952    fn parse_enum_declaration() {
3953        let source = r#"
3954            enum Status {
3955                Active,
3956                Pending,
3957                Done,
3958            }
3959
3960            agent Main {
3961                on start {
3962                    yield(0);
3963                }
3964            }
3965            run Main;
3966        "#;
3967
3968        let (prog, errors) = parse_str(source);
3969        assert!(errors.is_empty(), "errors: {errors:?}");
3970        let prog = prog.expect("should parse");
3971
3972        assert_eq!(prog.enums.len(), 1);
3973        assert!(!prog.enums[0].is_pub);
3974        assert_eq!(prog.enums[0].name.name, "Status");
3975        assert_eq!(prog.enums[0].variants.len(), 3);
3976        assert_eq!(prog.enums[0].variants[0].name.name, "Active");
3977        assert_eq!(prog.enums[0].variants[1].name.name, "Pending");
3978        assert_eq!(prog.enums[0].variants[2].name.name, "Done");
3979    }
3980
3981    #[test]
3982    fn parse_pub_enum() {
3983        let source = r#"
3984            pub enum Priority { High, Medium, Low }
3985
3986            agent Main {
3987                on start { yield(0); }
3988            }
3989            run Main;
3990        "#;
3991
3992        let (prog, errors) = parse_str(source);
3993        assert!(errors.is_empty(), "errors: {errors:?}");
3994        let prog = prog.expect("should parse");
3995
3996        assert_eq!(prog.enums.len(), 1);
3997        assert!(prog.enums[0].is_pub);
3998        assert_eq!(prog.enums[0].name.name, "Priority");
3999    }
4000
4001    #[test]
4002    fn parse_const_declaration() {
4003        let source = r#"
4004            const MAX_RETRIES: Int = 3;
4005
4006            agent Main {
4007                on start {
4008                    yield(0);
4009                }
4010            }
4011            run Main;
4012        "#;
4013
4014        let (prog, errors) = parse_str(source);
4015        assert!(errors.is_empty(), "errors: {errors:?}");
4016        let prog = prog.expect("should parse");
4017
4018        assert_eq!(prog.consts.len(), 1);
4019        assert!(!prog.consts[0].is_pub);
4020        assert_eq!(prog.consts[0].name.name, "MAX_RETRIES");
4021        assert!(matches!(prog.consts[0].ty, TypeExpr::Int));
4022    }
4023
4024    #[test]
4025    fn parse_pub_const() {
4026        let source = r#"
4027            pub const API_URL: String = "https://api.example.com";
4028
4029            agent Main {
4030                on start { yield(0); }
4031            }
4032            run Main;
4033        "#;
4034
4035        let (prog, errors) = parse_str(source);
4036        assert!(errors.is_empty(), "errors: {errors:?}");
4037        let prog = prog.expect("should parse");
4038
4039        assert_eq!(prog.consts.len(), 1);
4040        assert!(prog.consts[0].is_pub);
4041        assert_eq!(prog.consts[0].name.name, "API_URL");
4042    }
4043
4044    #[test]
4045    fn parse_multiple_type_declarations() {
4046        let source = r#"
4047            record Point { x: Int, y: Int }
4048            enum Color { Red, Green, Blue }
4049            const ORIGIN_X: Int = 0;
4050
4051            agent Main {
4052                on start { yield(0); }
4053            }
4054            run Main;
4055        "#;
4056
4057        let (prog, errors) = parse_str(source);
4058        assert!(errors.is_empty(), "errors: {errors:?}");
4059        let prog = prog.expect("should parse");
4060
4061        assert_eq!(prog.records.len(), 1);
4062        assert_eq!(prog.enums.len(), 1);
4063        assert_eq!(prog.consts.len(), 1);
4064    }
4065
4066    #[test]
4067    fn parse_match_expression() {
4068        let source = r#"
4069            enum Status { Active, Pending, Done }
4070
4071            agent Main {
4072                on start {
4073                    let s: Int = match Active {
4074                        Active => 1,
4075                        Pending => 2,
4076                        Done => 3,
4077                    };
4078                    yield(s);
4079                }
4080            }
4081            run Main;
4082        "#;
4083
4084        let (prog, errors) = parse_str(source);
4085        assert!(errors.is_empty(), "errors: {errors:?}");
4086        let prog = prog.expect("should parse");
4087
4088        // Check the agent parsed
4089        assert_eq!(prog.agents.len(), 1);
4090        // Match is in the handler
4091        let handler = &prog.agents[0].handlers[0];
4092        let stmt = &handler.body.stmts[0];
4093        if let Stmt::Let { value, .. } = stmt {
4094            assert!(matches!(value, Expr::Match { .. }));
4095        } else {
4096            panic!("expected let statement with match");
4097        }
4098    }
4099
4100    #[test]
4101    fn parse_match_with_wildcard() {
4102        let source = r#"
4103            agent Main {
4104                on start {
4105                    let x = 5;
4106                    let result = match x {
4107                        1 => 10,
4108                        2 => 20,
4109                        _ => 0,
4110                    };
4111                    yield(result);
4112                }
4113            }
4114            run Main;
4115        "#;
4116
4117        let (prog, errors) = parse_str(source);
4118        assert!(errors.is_empty(), "errors: {errors:?}");
4119        let prog = prog.expect("should parse");
4120
4121        assert_eq!(prog.agents.len(), 1);
4122    }
4123
4124    #[test]
4125    fn parse_record_construction() {
4126        let source = r#"
4127            record Point { x: Int, y: Int }
4128
4129            agent Main {
4130                on start {
4131                    let p = Point { x: 10, y: 20 };
4132                    yield(0);
4133                }
4134            }
4135            run Main;
4136        "#;
4137
4138        let (prog, errors) = parse_str(source);
4139        assert!(errors.is_empty(), "errors: {errors:?}");
4140        let prog = prog.expect("should parse");
4141
4142        assert_eq!(prog.records.len(), 1);
4143        assert_eq!(prog.agents.len(), 1);
4144
4145        // Check the let statement has a record construction
4146        let handler = &prog.agents[0].handlers[0];
4147        let stmt = &handler.body.stmts[0];
4148        if let Stmt::Let { value, .. } = stmt {
4149            if let Expr::RecordConstruct { name, fields, .. } = value {
4150                assert_eq!(name.name, "Point");
4151                assert_eq!(fields.len(), 2);
4152                assert_eq!(fields[0].name.name, "x");
4153                assert_eq!(fields[1].name.name, "y");
4154            } else {
4155                panic!("expected RecordConstruct");
4156            }
4157        } else {
4158            panic!("expected let statement");
4159        }
4160    }
4161
4162    #[test]
4163    fn parse_match_with_qualified_variant() {
4164        let source = r#"
4165            enum Status { Active, Pending }
4166
4167            fn get_status() -> Int {
4168                return 1;
4169            }
4170
4171            agent Main {
4172                on start {
4173                    let s = get_status();
4174                    let result = match s {
4175                        Status::Active => 1,
4176                        Status::Pending => 0,
4177                    };
4178                    yield(result);
4179                }
4180            }
4181            run Main;
4182        "#;
4183
4184        let (prog, errors) = parse_str(source);
4185        assert!(errors.is_empty(), "errors: {errors:?}");
4186        let prog = prog.expect("should parse");
4187
4188        assert_eq!(prog.enums.len(), 1);
4189        assert_eq!(prog.agents.len(), 1);
4190    }
4191
4192    #[test]
4193    fn parse_field_access() {
4194        let source = r#"
4195            record Point { x: Int, y: Int }
4196
4197            agent Main {
4198                on start {
4199                    let p = Point { x: 10, y: 20 };
4200                    let x_val = p.x;
4201                    let y_val = p.y;
4202                    yield(x_val);
4203                }
4204            }
4205            run Main;
4206        "#;
4207
4208        let (prog, errors) = parse_str(source);
4209        assert!(errors.is_empty(), "errors: {errors:?}");
4210        let prog = prog.expect("should parse");
4211
4212        assert_eq!(prog.records.len(), 1);
4213        assert_eq!(prog.agents.len(), 1);
4214
4215        // Check the field access
4216        let handler = &prog.agents[0].handlers[0];
4217        let stmt = &handler.body.stmts[1]; // p.x assignment
4218        if let Stmt::Let { value, .. } = stmt {
4219            if let Expr::FieldAccess { field, .. } = value {
4220                assert_eq!(field.name, "x");
4221            } else {
4222                panic!("expected FieldAccess");
4223            }
4224        } else {
4225            panic!("expected let statement");
4226        }
4227    }
4228
4229    #[test]
4230    fn parse_chained_field_access() {
4231        let source = r#"
4232            record Inner { val: Int }
4233            record Outer { inner: Inner }
4234
4235            agent Main {
4236                on start {
4237                    let inner = Inner { val: 42 };
4238                    let outer = Outer { inner: inner };
4239                    let v = outer.inner.val;
4240                    yield(v);
4241                }
4242            }
4243            run Main;
4244        "#;
4245
4246        let (prog, errors) = parse_str(source);
4247        assert!(errors.is_empty(), "errors: {errors:?}");
4248        let prog = prog.expect("should parse");
4249
4250        assert_eq!(prog.records.len(), 2);
4251        assert_eq!(prog.agents.len(), 1);
4252
4253        // Check the chained field access: outer.inner.val
4254        let handler = &prog.agents[0].handlers[0];
4255        let stmt = &handler.body.stmts[2]; // outer.inner.val assignment
4256        if let Stmt::Let { value, .. } = stmt {
4257            if let Expr::FieldAccess {
4258                object, field: val, ..
4259            } = value
4260            {
4261                assert_eq!(val.name, "val");
4262                // object should be outer.inner
4263                if let Expr::FieldAccess { field: inner, .. } = object.as_ref() {
4264                    assert_eq!(inner.name, "inner");
4265                } else {
4266                    panic!("expected nested FieldAccess");
4267                }
4268            } else {
4269                panic!("expected FieldAccess");
4270            }
4271        } else {
4272            panic!("expected let statement");
4273        }
4274    }
4275
4276    // =========================================================================
4277    // RFC-0006: Message passing tests
4278    // =========================================================================
4279
4280    #[test]
4281    fn parse_loop_break() {
4282        let source = r#"
4283            agent Main {
4284                on start {
4285                    let count = 0;
4286                    loop {
4287                        count = count + 1;
4288                        if count > 5 {
4289                            break;
4290                        }
4291                    }
4292                    yield(count);
4293                }
4294            }
4295            run Main;
4296        "#;
4297
4298        let (prog, errors) = parse_str(source);
4299        assert!(errors.is_empty(), "errors: {errors:?}");
4300        let prog = prog.expect("should parse");
4301
4302        assert_eq!(prog.agents.len(), 1);
4303        let handler = &prog.agents[0].handlers[0];
4304        // Check loop statement exists
4305        let loop_stmt = &handler.body.stmts[1];
4306        assert!(matches!(loop_stmt, Stmt::Loop { .. }));
4307        // Check break is inside the loop
4308        if let Stmt::Loop { body, .. } = loop_stmt {
4309            let if_stmt = &body.stmts[1];
4310            if let Stmt::If { then_block, .. } = if_stmt {
4311                assert!(matches!(then_block.stmts[0], Stmt::Break { .. }));
4312            } else {
4313                panic!("expected if statement");
4314            }
4315        }
4316    }
4317
4318    #[test]
4319    fn parse_agent_receives() {
4320        let source = r#"
4321            enum WorkerMsg {
4322                Task,
4323                Shutdown,
4324            }
4325
4326            agent Worker receives WorkerMsg {
4327                id: Int
4328
4329                on start {
4330                    yield(0);
4331                }
4332            }
4333
4334            agent Main {
4335                on start {
4336                    yield(0);
4337                }
4338            }
4339            run Main;
4340        "#;
4341
4342        let (prog, errors) = parse_str(source);
4343        assert!(errors.is_empty(), "errors: {errors:?}");
4344        let prog = prog.expect("should parse");
4345
4346        assert_eq!(prog.agents.len(), 2);
4347
4348        // Worker should have receives clause
4349        let worker = &prog.agents[0];
4350        assert_eq!(worker.name.name, "Worker");
4351        assert!(worker.receives.is_some());
4352        if let Some(TypeExpr::Named(name, _)) = &worker.receives {
4353            assert_eq!(name.name, "WorkerMsg");
4354        } else {
4355            panic!("expected named type for receives");
4356        }
4357
4358        // Main should not have receives
4359        let main = &prog.agents[1];
4360        assert_eq!(main.name.name, "Main");
4361        assert!(main.receives.is_none());
4362    }
4363
4364    #[test]
4365    fn parse_receive_expression() {
4366        let source = r#"
4367            enum Msg { Ping }
4368
4369            agent Worker receives Msg {
4370                on start {
4371                    let msg = receive();
4372                    yield(0);
4373                }
4374            }
4375
4376            agent Main {
4377                on start { yield(0); }
4378            }
4379            run Main;
4380        "#;
4381
4382        let (prog, errors) = parse_str(source);
4383        assert!(errors.is_empty(), "errors: {errors:?}");
4384        let prog = prog.expect("should parse");
4385
4386        // Find Worker agent
4387        let worker = prog
4388            .agents
4389            .iter()
4390            .find(|a| a.name.name == "Worker")
4391            .unwrap();
4392        let handler = &worker.handlers[0];
4393        let stmt = &handler.body.stmts[0];
4394
4395        if let Stmt::Let { value, .. } = stmt {
4396            assert!(matches!(value, Expr::Receive { .. }));
4397        } else {
4398            panic!("expected let with receive");
4399        }
4400    }
4401
4402    #[test]
4403    fn parse_message_passing_full() {
4404        let source = r#"
4405            enum WorkerMsg {
4406                Task,
4407                Shutdown,
4408            }
4409
4410            agent Worker receives WorkerMsg {
4411                id: Int
4412
4413                on start {
4414                    let msg = receive();
4415                    let result = match msg {
4416                        Task => 1,
4417                        Shutdown => 0,
4418                    };
4419                    yield(result);
4420                }
4421            }
4422
4423            agent Main {
4424                on start {
4425                    let w = summon Worker { id: 1 };
4426                    send(w, Task);
4427                    send(w, Shutdown);
4428                    await w;
4429                    yield(0);
4430                }
4431            }
4432            run Main;
4433        "#;
4434
4435        let (prog, errors) = parse_str(source);
4436        assert!(errors.is_empty(), "errors: {errors:?}");
4437        let prog = prog.expect("should parse");
4438
4439        assert_eq!(prog.enums.len(), 1);
4440        assert_eq!(prog.agents.len(), 2);
4441
4442        // Check Worker has receives
4443        let worker = prog
4444            .agents
4445            .iter()
4446            .find(|a| a.name.name == "Worker")
4447            .unwrap();
4448        assert!(worker.receives.is_some());
4449    }
4450
4451    // =========================================================================
4452    // RFC-0007: Error handling tests
4453    // =========================================================================
4454
4455    #[test]
4456    fn parse_fallible_function() {
4457        let source = r#"
4458            fn get_data(url: String) -> String fails {
4459                return divine("Get data from {url}" -> String);
4460            }
4461
4462            agent Main {
4463                on start { yield(0); }
4464            }
4465            run Main;
4466        "#;
4467
4468        let (prog, errors) = parse_str(source);
4469        assert!(errors.is_empty(), "errors: {errors:?}");
4470        let prog = prog.expect("should parse");
4471
4472        assert_eq!(prog.functions.len(), 1);
4473        assert!(prog.functions[0].is_fallible);
4474    }
4475
4476    #[test]
4477    fn parse_try_expression() {
4478        let source = r#"
4479            fn fallible() -> Int fails { return 42; }
4480
4481            agent Main {
4482                on start {
4483                    let x = try fallible();
4484                    yield(x);
4485                }
4486            }
4487            run Main;
4488        "#;
4489
4490        let (prog, errors) = parse_str(source);
4491        assert!(errors.is_empty(), "errors: {errors:?}");
4492        let prog = prog.expect("should parse");
4493
4494        // Find the let statement and check it contains a Try expression
4495        let handler = &prog.agents[0].handlers[0];
4496        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4497            assert!(matches!(value, Expr::Try { .. }));
4498        } else {
4499            panic!("expected Let statement");
4500        }
4501    }
4502
4503    #[test]
4504    fn parse_catch_expression() {
4505        let source = r#"
4506            fn fallible() -> Int fails { return 42; }
4507
4508            agent Main {
4509                on start {
4510                    let x = fallible() catch { 0 };
4511                    yield(x);
4512                }
4513            }
4514            run Main;
4515        "#;
4516
4517        let (prog, errors) = parse_str(source);
4518        assert!(errors.is_empty(), "errors: {errors:?}");
4519        let prog = prog.expect("should parse");
4520
4521        // Find the let statement and check it contains a Catch expression
4522        let handler = &prog.agents[0].handlers[0];
4523        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4524            if let Expr::Catch { error_bind, .. } = value {
4525                assert!(error_bind.is_none());
4526            } else {
4527                panic!("expected Catch expression");
4528            }
4529        } else {
4530            panic!("expected Let statement");
4531        }
4532    }
4533
4534    #[test]
4535    fn parse_catch_with_error_binding() {
4536        let source = r#"
4537            fn fallible() -> Int fails { return 42; }
4538
4539            agent Main {
4540                on start {
4541                    let x = fallible() catch(e) { 0 };
4542                    yield(x);
4543                }
4544            }
4545            run Main;
4546        "#;
4547
4548        let (prog, errors) = parse_str(source);
4549        assert!(errors.is_empty(), "errors: {errors:?}");
4550        let prog = prog.expect("should parse");
4551
4552        // Find the let statement and check it contains a Catch expression with binding
4553        let handler = &prog.agents[0].handlers[0];
4554        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4555            if let Expr::Catch { error_bind, .. } = value {
4556                assert!(error_bind.is_some());
4557                assert_eq!(error_bind.as_ref().unwrap().name, "e");
4558            } else {
4559                panic!("expected Catch expression");
4560            }
4561        } else {
4562            panic!("expected Let statement");
4563        }
4564    }
4565
4566    #[test]
4567    fn parse_fail_expression() {
4568        let source = r#"
4569            agent Main {
4570                on start {
4571                    fail "something went wrong";
4572                }
4573                on error(e) {
4574                    yield(0);
4575                }
4576            }
4577            run Main;
4578        "#;
4579
4580        let (prog, errors) = parse_str(source);
4581        assert!(errors.is_empty(), "errors: {errors:?}");
4582        let prog = prog.expect("should parse");
4583
4584        // Find the fail statement
4585        let handler = &prog.agents[0].handlers[0];
4586        if let Stmt::Expr { expr, .. } = &handler.body.stmts[0] {
4587            if let Expr::Fail { error, .. } = expr {
4588                assert!(matches!(**error, Expr::Literal { .. }));
4589            } else {
4590                panic!("expected Fail expression, got {expr:?}");
4591            }
4592        } else {
4593            panic!("expected Expr statement");
4594        }
4595    }
4596
4597    #[test]
4598    fn parse_retry_expression() {
4599        let source = r#"
4600            agent Main {
4601                topic: String
4602
4603                on start {
4604                    let result = retry(3) {
4605                        try divine("Summarize: {self.topic}")
4606                    } catch { "fallback" };
4607                    yield(result);
4608                }
4609
4610                on error(e) {
4611                    yield("");
4612                }
4613            }
4614            run Main;
4615        "#;
4616
4617        let (prog, errors) = parse_str(source);
4618        assert!(errors.is_empty(), "errors: {errors:?}");
4619        let prog = prog.expect("should parse");
4620
4621        // Find the let statement with retry
4622        let handler = &prog.agents[0].handlers[0];
4623        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4624            // Should be Catch wrapping Retry
4625            if let Expr::Catch { expr, .. } = value {
4626                if let Expr::Retry { count, delay, .. } = expr.as_ref() {
4627                    assert!(matches!(**count, Expr::Literal { .. }));
4628                    assert!(delay.is_none());
4629                } else {
4630                    panic!("expected Retry expression");
4631                }
4632            } else {
4633                panic!("expected Catch expression");
4634            }
4635        } else {
4636            panic!("expected Let statement");
4637        }
4638    }
4639
4640    #[test]
4641    fn parse_retry_with_delay() {
4642        let source = r#"
4643            agent Main {
4644                on start {
4645                    let result = retry(3, delay: 1000) {
4646                        42
4647                    } catch { 0 };
4648                    yield(result);
4649                }
4650            }
4651            run Main;
4652        "#;
4653
4654        let (prog, errors) = parse_str(source);
4655        assert!(errors.is_empty(), "errors: {errors:?}");
4656        let prog = prog.expect("should parse");
4657
4658        // Find the let statement with retry
4659        let handler = &prog.agents[0].handlers[0];
4660        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4661            if let Expr::Catch { expr, .. } = value {
4662                if let Expr::Retry { delay, .. } = expr.as_ref() {
4663                    assert!(delay.is_some());
4664                } else {
4665                    panic!("expected Retry expression");
4666                }
4667            } else {
4668                panic!("expected Catch expression");
4669            }
4670        } else {
4671            panic!("expected Let statement");
4672        }
4673    }
4674
4675    #[test]
4676    fn parse_on_error_handler() {
4677        let source = r#"
4678            agent Main {
4679                on start {
4680                    yield(0);
4681                }
4682
4683                on error(e) {
4684                    yield(1);
4685                }
4686            }
4687            run Main;
4688        "#;
4689
4690        let (prog, errors) = parse_str(source);
4691        assert!(errors.is_empty(), "errors: {errors:?}");
4692        let prog = prog.expect("should parse");
4693
4694        assert_eq!(prog.agents.len(), 1);
4695        assert_eq!(prog.agents[0].handlers.len(), 2);
4696
4697        // Check the error handler
4698        let error_handler = prog.agents[0]
4699            .handlers
4700            .iter()
4701            .find(|h| matches!(h.event, EventKind::Error { .. }));
4702        assert!(error_handler.is_some());
4703
4704        if let EventKind::Error { param_name } = &error_handler.unwrap().event {
4705            assert_eq!(param_name.name, "e");
4706        } else {
4707            panic!("expected Error event kind");
4708        }
4709    }
4710
4711    // =========================================================================
4712    // RFC-0009: Closures and function types
4713    // =========================================================================
4714
4715    #[test]
4716    fn parse_fn_type() {
4717        let source = r#"
4718            fn apply(f: Fn(Int) -> Int, x: Int) -> Int {
4719                return f(x);
4720            }
4721
4722            agent Main {
4723                on start {
4724                    yield(0);
4725                }
4726            }
4727            run Main;
4728        "#;
4729
4730        let (prog, errors) = parse_str(source);
4731        assert!(errors.is_empty(), "errors: {errors:?}");
4732        let prog = prog.expect("should parse");
4733
4734        assert_eq!(prog.functions.len(), 1);
4735        let func = &prog.functions[0];
4736        assert_eq!(func.name.name, "apply");
4737        assert_eq!(func.params.len(), 2);
4738
4739        // Check first param is Fn(Int) -> Int
4740        if let TypeExpr::Fn(params, ret) = &func.params[0].ty {
4741            assert_eq!(params.len(), 1);
4742            assert!(matches!(params[0], TypeExpr::Int));
4743            assert!(matches!(ret.as_ref(), TypeExpr::Int));
4744        } else {
4745            panic!("expected Fn type for first param");
4746        }
4747    }
4748
4749    #[test]
4750    fn parse_closure_with_params() {
4751        let source = r#"
4752            agent Main {
4753                on start {
4754                    let f = |x: Int| x + 1;
4755                    yield(0);
4756                }
4757            }
4758            run Main;
4759        "#;
4760
4761        let (prog, errors) = parse_str(source);
4762        assert!(errors.is_empty(), "errors: {errors:?}");
4763        let prog = prog.expect("should parse");
4764
4765        // Find the let statement in the on start handler
4766        let handler = &prog.agents[0].handlers[0];
4767        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4768            if let Expr::Closure { params, body, .. } = value {
4769                assert_eq!(params.len(), 1);
4770                assert_eq!(params[0].name.name, "x");
4771                assert!(matches!(&params[0].ty, Some(TypeExpr::Int)));
4772
4773                // Body should be a binary expression
4774                assert!(matches!(body.as_ref(), Expr::Binary { .. }));
4775            } else {
4776                panic!("expected closure expression");
4777            }
4778        } else {
4779            panic!("expected let statement");
4780        }
4781    }
4782
4783    #[test]
4784    fn parse_closure_empty_params() {
4785        let source = r#"
4786            agent Main {
4787                on start {
4788                    let f = || 42;
4789                    yield(0);
4790                }
4791            }
4792            run Main;
4793        "#;
4794
4795        let (prog, errors) = parse_str(source);
4796        assert!(errors.is_empty(), "errors: {errors:?}");
4797        let prog = prog.expect("should parse");
4798
4799        // Find the let statement
4800        let handler = &prog.agents[0].handlers[0];
4801        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4802            if let Expr::Closure { params, body, .. } = value {
4803                assert!(params.is_empty());
4804
4805                // Body should be a literal
4806                assert!(matches!(body.as_ref(), Expr::Literal { .. }));
4807            } else {
4808                panic!("expected closure expression");
4809            }
4810        } else {
4811            panic!("expected let statement");
4812        }
4813    }
4814
4815    #[test]
4816    fn parse_closure_multiple_params() {
4817        let source = r#"
4818            agent Main {
4819                on start {
4820                    let add = |x: Int, y: Int| x + y;
4821                    yield(0);
4822                }
4823            }
4824            run Main;
4825        "#;
4826
4827        let (prog, errors) = parse_str(source);
4828        assert!(errors.is_empty(), "errors: {errors:?}");
4829        let prog = prog.expect("should parse");
4830
4831        let handler = &prog.agents[0].handlers[0];
4832        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4833            if let Expr::Closure { params, .. } = value {
4834                assert_eq!(params.len(), 2);
4835                assert_eq!(params[0].name.name, "x");
4836                assert_eq!(params[1].name.name, "y");
4837            } else {
4838                panic!("expected closure expression");
4839            }
4840        } else {
4841            panic!("expected let statement");
4842        }
4843    }
4844
4845    #[test]
4846    fn parse_fn_type_multiarg() {
4847        let source = r#"
4848            fn fold_left(f: Fn(Int, Int) -> Int, init: Int) -> Int {
4849                return init;
4850            }
4851
4852            agent Main {
4853                on start {
4854                    yield(0);
4855                }
4856            }
4857            run Main;
4858        "#;
4859
4860        let (prog, errors) = parse_str(source);
4861        assert!(errors.is_empty(), "errors: {errors:?}");
4862        let prog = prog.expect("should parse");
4863
4864        // Check Fn(Int, Int) -> Int
4865        if let TypeExpr::Fn(params, ret) = &prog.functions[0].params[0].ty {
4866            assert_eq!(params.len(), 2);
4867            assert!(matches!(params[0], TypeExpr::Int));
4868            assert!(matches!(params[1], TypeExpr::Int));
4869            assert!(matches!(ret.as_ref(), TypeExpr::Int));
4870        } else {
4871            panic!("expected Fn type");
4872        }
4873    }
4874
4875    #[test]
4876    fn parse_tuple_literal() {
4877        let source = r#"
4878            agent Main {
4879                on start {
4880                    let t = (1, 2);
4881                    yield(0);
4882                }
4883            }
4884            run Main;
4885        "#;
4886
4887        let (prog, errors) = parse_str(source);
4888        assert!(errors.is_empty(), "errors: {errors:?}");
4889        let prog = prog.expect("should parse");
4890
4891        let handler = &prog.agents[0].handlers[0];
4892        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4893            if let Expr::Tuple { elements, .. } = value {
4894                assert_eq!(elements.len(), 2);
4895            } else {
4896                panic!("expected tuple expression, got {:?}", value);
4897            }
4898        } else {
4899            panic!("expected let statement");
4900        }
4901    }
4902
4903    // =========================================================================
4904    // RFC-0011: Tool support tests
4905    // =========================================================================
4906
4907    #[test]
4908    fn parse_tool_declaration() {
4909        let source = r#"
4910            tool Http {
4911                fn get(url: String) -> Result<String, String>
4912                fn post(url: String, body: String) -> Result<String, String>
4913            }
4914            agent Main {
4915                on start { yield(0); }
4916            }
4917            run Main;
4918        "#;
4919
4920        let (prog, errors) = parse_str(source);
4921        assert!(errors.is_empty(), "errors: {errors:?}");
4922        let prog = prog.expect("should parse");
4923
4924        assert_eq!(prog.tools.len(), 1);
4925        assert_eq!(prog.tools[0].name.name, "Http");
4926        assert_eq!(prog.tools[0].functions.len(), 2);
4927        assert_eq!(prog.tools[0].functions[0].name.name, "get");
4928        assert_eq!(prog.tools[0].functions[1].name.name, "post");
4929    }
4930
4931    #[test]
4932    fn parse_pub_tool_declaration() {
4933        let source = r#"
4934            pub tool Database {
4935                fn query(sql: String) -> Result<List<String>, String>
4936            }
4937            agent Main {
4938                on start { yield(0); }
4939            }
4940            run Main;
4941        "#;
4942
4943        let (prog, errors) = parse_str(source);
4944        assert!(errors.is_empty(), "errors: {errors:?}");
4945        let prog = prog.expect("should parse");
4946
4947        assert!(prog.tools[0].is_pub);
4948        assert_eq!(prog.tools[0].name.name, "Database");
4949    }
4950
4951    #[test]
4952    fn parse_agent_with_tool_use() {
4953        let source = r#"
4954            agent Fetcher {
4955                use Http
4956
4957                url: String
4958
4959                on start {
4960                    yield(0);
4961                }
4962            }
4963            run Fetcher;
4964        "#;
4965
4966        let (prog, errors) = parse_str(source);
4967        assert!(errors.is_empty(), "errors: {errors:?}");
4968        let prog = prog.expect("should parse");
4969
4970        assert_eq!(prog.agents[0].tool_uses.len(), 1);
4971        assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
4972        assert_eq!(prog.agents[0].beliefs.len(), 1);
4973    }
4974
4975    #[test]
4976    fn parse_agent_with_multiple_tool_uses() {
4977        let source = r#"
4978            agent Pipeline {
4979                use Http, Fs
4980
4981                on start {
4982                    yield(0);
4983                }
4984            }
4985            run Pipeline;
4986        "#;
4987
4988        let (prog, errors) = parse_str(source);
4989        assert!(errors.is_empty(), "errors: {errors:?}");
4990        let prog = prog.expect("should parse");
4991
4992        assert_eq!(prog.agents[0].tool_uses.len(), 2);
4993        assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
4994        assert_eq!(prog.agents[0].tool_uses[1].name, "Fs");
4995    }
4996
4997    #[test]
4998    fn parse_tool_call_expression() {
4999        let source = r#"
5000            agent Fetcher {
5001                use Http
5002
5003                on start {
5004                    let response = Http.get("https://example.com");
5005                    yield(0);
5006                }
5007            }
5008            run Fetcher;
5009        "#;
5010
5011        let (prog, errors) = parse_str(source);
5012        assert!(errors.is_empty(), "errors: {errors:?}");
5013        let prog = prog.expect("should parse");
5014
5015        let handler = &prog.agents[0].handlers[0];
5016        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5017            if let Expr::ToolCall {
5018                tool,
5019                function,
5020                args,
5021                ..
5022            } = value
5023            {
5024                assert_eq!(tool.name, "Http");
5025                assert_eq!(function.name, "get");
5026                assert_eq!(args.len(), 1);
5027            } else {
5028                panic!("expected ToolCall expression, got {:?}", value);
5029            }
5030        } else {
5031            panic!("expected let statement");
5032        }
5033    }
5034
5035    #[test]
5036    fn parse_tool_call_with_multiple_args() {
5037        let source = r#"
5038            agent Writer {
5039                use Fs
5040
5041                on start {
5042                    let result = Fs.write("/tmp/test.txt", "hello world");
5043                    yield(0);
5044                }
5045            }
5046            run Writer;
5047        "#;
5048
5049        let (prog, errors) = parse_str(source);
5050        assert!(errors.is_empty(), "errors: {errors:?}");
5051        let prog = prog.expect("should parse");
5052
5053        let handler = &prog.agents[0].handlers[0];
5054        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5055            if let Expr::ToolCall { args, .. } = value {
5056                assert_eq!(args.len(), 2);
5057            } else {
5058                panic!("expected ToolCall expression, got {:?}", value);
5059            }
5060        } else {
5061            panic!("expected let statement");
5062        }
5063    }
5064
5065    #[test]
5066    fn parse_string_interp_with_field_access() {
5067        let source = r#"
5068            record Person { name: String }
5069            agent Main {
5070                on start {
5071                    let p = Person { name: "Alice" };
5072                    print("Hello, {p.name}!");
5073                    yield(0);
5074                }
5075            }
5076            run Main;
5077        "#;
5078
5079        let (prog, errors) = parse_str(source);
5080        assert!(errors.is_empty(), "errors: {errors:?}");
5081        let prog = prog.expect("should parse");
5082
5083        // Find the print statement with interpolation
5084        let handler = &prog.agents[0].handlers[0];
5085        if let Stmt::Expr { expr, .. } = &handler.body.stmts[1] {
5086            if let Expr::Call { args, .. } = expr {
5087                if let Expr::StringInterp { template, .. } = &args[0] {
5088                    assert!(template.has_interpolations());
5089                    let interps: Vec<_> = template.interpolations().collect();
5090                    assert_eq!(interps.len(), 1);
5091                    // Should be a field access: p.name
5092                    match interps[0] {
5093                        Expr::FieldAccess { object, field, .. } => {
5094                            if let Expr::Var { name, .. } = object.as_ref() {
5095                                assert_eq!(name.name, "p");
5096                            } else {
5097                                panic!("expected Var as base");
5098                            }
5099                            assert_eq!(field.name, "name");
5100                        }
5101                        _ => panic!("expected FieldAccess, got {:?}", interps[0]),
5102                    }
5103                } else {
5104                    panic!("expected StringInterp");
5105                }
5106            } else {
5107                panic!("expected Call");
5108            }
5109        } else {
5110            panic!("expected Expr statement");
5111        }
5112    }
5113
5114    #[test]
5115    fn parse_string_interp_with_tuple_index() {
5116        let source = r#"
5117            agent Main {
5118                on start {
5119                    let pair = (1, 2);
5120                    print("First: {pair.0}");
5121                    yield(0);
5122                }
5123            }
5124            run Main;
5125        "#;
5126
5127        let (prog, errors) = parse_str(source);
5128        assert!(errors.is_empty(), "errors: {errors:?}");
5129        let prog = prog.expect("should parse");
5130
5131        let handler = &prog.agents[0].handlers[0];
5132        if let Stmt::Expr { expr, .. } = &handler.body.stmts[1] {
5133            if let Expr::Call { args, .. } = expr {
5134                if let Expr::StringInterp { template, .. } = &args[0] {
5135                    let interps: Vec<_> = template.interpolations().collect();
5136                    assert_eq!(interps.len(), 1);
5137                    match interps[0] {
5138                        Expr::TupleIndex { tuple, index, .. } => {
5139                            if let Expr::Var { name, .. } = tuple.as_ref() {
5140                                assert_eq!(name.name, "pair");
5141                            } else {
5142                                panic!("expected Var as tuple base");
5143                            }
5144                            assert_eq!(*index, 0);
5145                        }
5146                        _ => panic!("expected TupleIndex, got {:?}", interps[0]),
5147                    }
5148                } else {
5149                    panic!("expected StringInterp");
5150                }
5151            } else {
5152                panic!("expected Call");
5153            }
5154        } else {
5155            panic!("expected Expr statement");
5156        }
5157    }
5158
5159    #[test]
5160    fn parse_mock_tool_with_fail() {
5161        let source = r#"
5162            test "mock tool fail" {
5163                mock tool Http.get -> fail("network error");
5164            }
5165        "#;
5166
5167        let (prog, errors) = parse_str(source);
5168        assert!(errors.is_empty(), "errors: {errors:?}");
5169        let prog = prog.expect("should parse");
5170
5171        let test = &prog.tests[0];
5172        assert_eq!(test.body.stmts.len(), 1);
5173
5174        if let Stmt::MockTool {
5175            tool_name,
5176            fn_name,
5177            value,
5178            ..
5179        } = &test.body.stmts[0]
5180        {
5181            assert_eq!(tool_name.name, "Http");
5182            assert_eq!(fn_name.name, "get");
5183            assert!(
5184                matches!(value, MockValue::Fail(_)),
5185                "expected MockValue::Fail, got {:?}",
5186                value
5187            );
5188        } else {
5189            panic!("expected MockTool statement, got {:?}", test.body.stmts[0]);
5190        }
5191    }
5192
5193    #[test]
5194    fn parse_mock_tool_with_value() {
5195        let source = r#"
5196            test "mock tool value" {
5197                mock tool Http.get -> "response";
5198            }
5199        "#;
5200
5201        let (prog, errors) = parse_str(source);
5202        assert!(errors.is_empty(), "errors: {errors:?}");
5203        let prog = prog.expect("should parse");
5204
5205        let test = &prog.tests[0];
5206        if let Stmt::MockTool { value, .. } = &test.body.stmts[0] {
5207            assert!(
5208                matches!(value, MockValue::Value(_)),
5209                "expected MockValue::Value, got {:?}",
5210                value
5211            );
5212        } else {
5213            panic!("expected MockTool statement");
5214        }
5215    }
5216
5217    // =========================================================================
5218    // RFC-0015: Generics tests
5219    // =========================================================================
5220
5221    #[test]
5222    fn parse_generic_function() {
5223        let source = r#"
5224            fn identity<T>(x: T) -> T {
5225                return x;
5226            }
5227
5228            agent Main {
5229                on start { yield(0); }
5230            }
5231            run Main;
5232        "#;
5233
5234        let (prog, errors) = parse_str(source);
5235        assert!(errors.is_empty(), "errors: {errors:?}");
5236        let prog = prog.expect("should parse");
5237
5238        assert_eq!(prog.functions.len(), 1);
5239        let func = &prog.functions[0];
5240        assert_eq!(func.name.name, "identity");
5241        assert_eq!(func.type_params.len(), 1);
5242        assert_eq!(func.type_params[0].name, "T");
5243    }
5244
5245    #[test]
5246    fn parse_generic_function_multiple_params() {
5247        let source = r#"
5248            fn map<T, U>(list: List<T>, f: Fn(T) -> U) -> List<U> {
5249                return [];
5250            }
5251
5252            agent Main {
5253                on start { yield(0); }
5254            }
5255            run Main;
5256        "#;
5257
5258        let (prog, errors) = parse_str(source);
5259        assert!(errors.is_empty(), "errors: {errors:?}");
5260        let prog = prog.expect("should parse");
5261
5262        let func = &prog.functions[0];
5263        assert_eq!(func.name.name, "map");
5264        assert_eq!(func.type_params.len(), 2);
5265        assert_eq!(func.type_params[0].name, "T");
5266        assert_eq!(func.type_params[1].name, "U");
5267    }
5268
5269    #[test]
5270    fn parse_generic_record() {
5271        let source = r#"
5272            record Pair<A, B> {
5273                first: A,
5274                second: B,
5275            }
5276
5277            agent Main {
5278                on start { yield(0); }
5279            }
5280            run Main;
5281        "#;
5282
5283        let (prog, errors) = parse_str(source);
5284        assert!(errors.is_empty(), "errors: {errors:?}");
5285        let prog = prog.expect("should parse");
5286
5287        assert_eq!(prog.records.len(), 1);
5288        let record = &prog.records[0];
5289        assert_eq!(record.name.name, "Pair");
5290        assert_eq!(record.type_params.len(), 2);
5291        assert_eq!(record.type_params[0].name, "A");
5292        assert_eq!(record.type_params[1].name, "B");
5293    }
5294
5295    #[test]
5296    fn parse_generic_enum() {
5297        let source = r#"
5298            enum Tree<T> {
5299                Leaf(T),
5300                Node(Tree<T>),
5301            }
5302
5303            agent Main {
5304                on start { yield(0); }
5305            }
5306            run Main;
5307        "#;
5308
5309        let (prog, errors) = parse_str(source);
5310        assert!(errors.is_empty(), "errors: {errors:?}");
5311        let prog = prog.expect("should parse");
5312
5313        assert_eq!(prog.enums.len(), 1);
5314        let enumm = &prog.enums[0];
5315        assert_eq!(enumm.name.name, "Tree");
5316        assert_eq!(enumm.type_params.len(), 1);
5317        assert_eq!(enumm.type_params[0].name, "T");
5318    }
5319
5320    #[test]
5321    fn parse_generic_type_argument() {
5322        let source = r#"
5323            record Wrapper<T> {
5324                value: T,
5325            }
5326
5327            fn make_wrapper<T>(value: T) -> Wrapper<T> {
5328                return Wrapper { value: value };
5329            }
5330
5331            agent Main {
5332                on start { yield(0); }
5333            }
5334            run Main;
5335        "#;
5336
5337        let (prog, errors) = parse_str(source);
5338        assert!(errors.is_empty(), "errors: {errors:?}");
5339        let prog = prog.expect("should parse");
5340
5341        let func = &prog.functions[0];
5342        // Check return type is Wrapper<T>
5343        if let TypeExpr::Named(name, type_args) = &func.return_ty {
5344            assert_eq!(name.name, "Wrapper");
5345            assert_eq!(type_args.len(), 1);
5346            if let TypeExpr::Named(inner_name, _) = &type_args[0] {
5347                assert_eq!(inner_name.name, "T");
5348            } else {
5349                panic!("expected Named type argument");
5350            }
5351        } else {
5352            panic!("expected Named return type");
5353        }
5354    }
5355
5356    #[test]
5357    fn parse_turbofish_function_call() {
5358        let source = r#"
5359            fn identity<T>(x: T) -> T {
5360                return x;
5361            }
5362
5363            agent Main {
5364                on start {
5365                    let result = identity::<Int>(42);
5366                    yield(0);
5367                }
5368            }
5369            run Main;
5370        "#;
5371
5372        let (prog, errors) = parse_str(source);
5373        assert!(errors.is_empty(), "errors: {errors:?}");
5374        let prog = prog.expect("should parse");
5375
5376        let handler = &prog.agents[0].handlers[0];
5377        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5378            if let Expr::Call {
5379                name, type_args, ..
5380            } = value
5381            {
5382                assert_eq!(name.name, "identity");
5383                assert_eq!(type_args.len(), 1);
5384                assert!(matches!(type_args[0], TypeExpr::Int));
5385            } else {
5386                panic!("expected Call expression");
5387            }
5388        } else {
5389            panic!("expected Let statement");
5390        }
5391    }
5392
5393    #[test]
5394    fn parse_turbofish_multiple_type_args() {
5395        let source = r#"
5396            fn make_pair<A, B>(a: A, b: B) -> (A, B) {
5397                return (a, b);
5398            }
5399
5400            agent Main {
5401                on start {
5402                    let pair = make_pair::<Int, String>(42, "hello");
5403                    yield(0);
5404                }
5405            }
5406            run Main;
5407        "#;
5408
5409        let (prog, errors) = parse_str(source);
5410        assert!(errors.is_empty(), "errors: {errors:?}");
5411        let prog = prog.expect("should parse");
5412
5413        let handler = &prog.agents[0].handlers[0];
5414        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5415            if let Expr::Call {
5416                name, type_args, ..
5417            } = value
5418            {
5419                assert_eq!(name.name, "make_pair");
5420                assert_eq!(type_args.len(), 2);
5421                assert!(matches!(type_args[0], TypeExpr::Int));
5422                assert!(matches!(type_args[1], TypeExpr::String));
5423            } else {
5424                panic!("expected Call expression");
5425            }
5426        } else {
5427            panic!("expected Let statement");
5428        }
5429    }
5430
5431    #[test]
5432    fn parse_turbofish_record_construction() {
5433        let source = r#"
5434            record Pair<A, B> {
5435                first: A,
5436                second: B,
5437            }
5438
5439            agent Main {
5440                on start {
5441                    let p = Pair::<Int, String> { first: 42, second: "hi" };
5442                    yield(0);
5443                }
5444            }
5445            run Main;
5446        "#;
5447
5448        let (prog, errors) = parse_str(source);
5449        assert!(errors.is_empty(), "errors: {errors:?}");
5450        let prog = prog.expect("should parse");
5451
5452        let handler = &prog.agents[0].handlers[0];
5453        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5454            if let Expr::RecordConstruct {
5455                name, type_args, ..
5456            } = value
5457            {
5458                assert_eq!(name.name, "Pair");
5459                assert_eq!(type_args.len(), 2);
5460                assert!(matches!(type_args[0], TypeExpr::Int));
5461                assert!(matches!(type_args[1], TypeExpr::String));
5462            } else {
5463                panic!("expected RecordConstruct expression");
5464            }
5465        } else {
5466            panic!("expected Let statement");
5467        }
5468    }
5469
5470    #[test]
5471    fn parse_turbofish_variant_construction() {
5472        let source = r#"
5473            enum Either<L, R> {
5474                Left(L),
5475                Right(R),
5476            }
5477
5478            agent Main {
5479                on start {
5480                    let e = Either::<String, Int>::Left("hello");
5481                    yield(0);
5482                }
5483            }
5484            run Main;
5485        "#;
5486
5487        let (prog, errors) = parse_str(source);
5488        assert!(errors.is_empty(), "errors: {errors:?}");
5489        let prog = prog.expect("should parse");
5490
5491        let handler = &prog.agents[0].handlers[0];
5492        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5493            if let Expr::VariantConstruct {
5494                enum_name,
5495                type_args,
5496                variant,
5497                ..
5498            } = value
5499            {
5500                assert_eq!(enum_name.name, "Either");
5501                assert_eq!(variant.name, "Left");
5502                assert_eq!(type_args.len(), 2);
5503                assert!(matches!(type_args[0], TypeExpr::String));
5504                assert!(matches!(type_args[1], TypeExpr::Int));
5505            } else {
5506                panic!("expected VariantConstruct expression");
5507            }
5508        } else {
5509            panic!("expected Let statement");
5510        }
5511    }
5512
5513    #[test]
5514    fn parse_generic_in_type_annotation() {
5515        let source = r#"
5516            record Page<T> {
5517                items: List<T>,
5518                count: Int,
5519            }
5520
5521            agent Main {
5522                on start {
5523                    let page: Page<String> = Page { items: [], count: 0 };
5524                    yield(0);
5525                }
5526            }
5527            run Main;
5528        "#;
5529
5530        let (prog, errors) = parse_str(source);
5531        assert!(errors.is_empty(), "errors: {errors:?}");
5532        let prog = prog.expect("should parse");
5533
5534        let handler = &prog.agents[0].handlers[0];
5535        if let Stmt::Let { ty: Some(ty), .. } = &handler.body.stmts[0] {
5536            if let TypeExpr::Named(name, type_args) = ty {
5537                assert_eq!(name.name, "Page");
5538                assert_eq!(type_args.len(), 1);
5539                assert!(matches!(type_args[0], TypeExpr::String));
5540            } else {
5541                panic!("expected Named type");
5542            }
5543        } else {
5544            panic!("expected Let statement with type annotation");
5545        }
5546    }
5547
5548    #[test]
5549    fn parse_nested_generic_types() {
5550        let source = r#"
5551            record Nested<T> {
5552                value: Option<List<T>>,
5553            }
5554
5555            agent Main {
5556                on start { yield(0); }
5557            }
5558            run Main;
5559        "#;
5560
5561        let (prog, errors) = parse_str(source);
5562        assert!(errors.is_empty(), "errors: {errors:?}");
5563        let prog = prog.expect("should parse");
5564
5565        let record = &prog.records[0];
5566        let field_ty = &record.fields[0].ty;
5567        // Should be Option<List<T>>
5568        if let TypeExpr::Option(inner) = field_ty {
5569            if let TypeExpr::List(elem) = inner.as_ref() {
5570                if let TypeExpr::Named(name, _) = elem.as_ref() {
5571                    assert_eq!(name.name, "T");
5572                } else {
5573                    panic!("expected Named type T");
5574                }
5575            } else {
5576                panic!("expected List type");
5577            }
5578        } else {
5579            panic!("expected Option type");
5580        }
5581    }
5582
5583    #[test]
5584    fn parse_span_block() {
5585        let source = r#"
5586            agent Main {
5587                on start {
5588                    span "fetch_data" {
5589                        let x = 1;
5590                        let y = 2;
5591                    }
5592                    yield(42);
5593                }
5594            }
5595            run Main;
5596        "#;
5597
5598        let (prog, errors) = parse_str(source);
5599        assert!(errors.is_empty(), "errors: {errors:?}");
5600        let prog = prog.expect("should parse");
5601
5602        let handler = &prog.agents[0].handlers[0];
5603        // First statement should be a SpanBlock
5604        if let Stmt::SpanBlock { name, body, .. } = &handler.body.stmts[0] {
5605            // Name should be a string literal
5606            if let Expr::Literal {
5607                value: Literal::String(s),
5608                ..
5609            } = name
5610            {
5611                assert_eq!(s, "fetch_data");
5612            } else {
5613                panic!("expected string literal for span name");
5614            }
5615            // Body should have 2 statements
5616            assert_eq!(body.stmts.len(), 2);
5617        } else {
5618            panic!("expected SpanBlock statement");
5619        }
5620    }
5621
5622    #[test]
5623    fn parse_turbofish_in_interpolation() {
5624        let source = r#"
5625            fn identity<T>(x: T) -> T { return x; }
5626
5627            agent Main {
5628                on start {
5629                    let msg = "result: {identity::<Int>(42)}";
5630                    yield(0);
5631                }
5632            }
5633            run Main;
5634        "#;
5635
5636        let (prog, errors) = parse_str(source);
5637        assert!(errors.is_empty(), "errors: {errors:?}");
5638        let prog = prog.expect("should parse");
5639
5640        let handler = &prog.agents[0].handlers[0];
5641        if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5642            if let Expr::StringInterp { template, .. } = value {
5643                // Should have 2 parts: "result: " and the interpolated expression
5644                assert_eq!(template.parts.len(), 2);
5645                if let StringPart::Interpolation(call_expr) = &template.parts[1] {
5646                    if let Expr::Call { name, type_args, args, .. } = call_expr.as_ref() {
5647                        assert_eq!(name.name, "identity");
5648                        assert_eq!(type_args.len(), 1);
5649                        assert!(matches!(type_args[0], TypeExpr::Int));
5650                        assert_eq!(args.len(), 1);
5651                    } else {
5652                        panic!("expected Call expression in interpolation, got {:?}", call_expr);
5653                    }
5654                } else {
5655                    panic!("expected Interpolation part in template");
5656                }
5657            } else {
5658                panic!("expected StringInterp expression");
5659            }
5660        } else {
5661            panic!("expected Let statement");
5662        }
5663    }
5664
5665    // =============================================================================
5666    // Phase 3: Session Types & Algebraic Effects
5667    // =============================================================================
5668
5669    #[test]
5670    fn parse_protocol_declaration() {
5671        let source = r#"
5672            protocol SchemaSync {
5673                DatabaseSteward -> APISteward: SchemaChanged
5674                APISteward -> DatabaseSteward: Acknowledged
5675            }
5676
5677            agent Main {
5678                on start {
5679                    yield(0);
5680                }
5681            }
5682            run Main;
5683        "#;
5684
5685        let (prog, errors) = parse_str(source);
5686        assert!(errors.is_empty(), "errors: {errors:?}");
5687        let prog = prog.expect("should parse");
5688
5689        assert_eq!(prog.protocols.len(), 1);
5690        let proto = &prog.protocols[0];
5691        assert_eq!(proto.name.name, "SchemaSync");
5692        assert!(!proto.is_pub);
5693        assert_eq!(proto.steps.len(), 2);
5694
5695        assert_eq!(proto.steps[0].sender.name, "DatabaseSteward");
5696        assert_eq!(proto.steps[0].receiver.name, "APISteward");
5697        if let TypeExpr::Named(name, _) = &proto.steps[0].message_type {
5698            assert_eq!(name.name, "SchemaChanged");
5699        } else {
5700            panic!("expected Named type");
5701        }
5702
5703        assert_eq!(proto.steps[1].sender.name, "APISteward");
5704        assert_eq!(proto.steps[1].receiver.name, "DatabaseSteward");
5705    }
5706
5707    #[test]
5708    fn parse_public_protocol() {
5709        let source = r#"
5710            pub protocol PingPong {
5711                Pinger -> Ponger: Ping
5712                Ponger -> Pinger: Pong
5713            }
5714
5715            agent Main { on start { yield(0); } }
5716            run Main;
5717        "#;
5718
5719        let (prog, errors) = parse_str(source);
5720        assert!(errors.is_empty(), "errors: {errors:?}");
5721        let prog = prog.expect("should parse");
5722
5723        assert_eq!(prog.protocols.len(), 1);
5724        assert!(prog.protocols[0].is_pub);
5725    }
5726
5727    #[test]
5728    fn parse_agent_follows_protocol() {
5729        let source = r#"
5730            protocol SchemaSync {
5731                A -> B: Msg
5732            }
5733
5734            agent APISteward follows SchemaSync as APISteward {
5735                on start {
5736                    yield(0);
5737                }
5738            }
5739            run APISteward;
5740        "#;
5741
5742        let (prog, errors) = parse_str(source);
5743        assert!(errors.is_empty(), "errors: {errors:?}");
5744        let prog = prog.expect("should parse");
5745
5746        assert_eq!(prog.agents[0].follows.len(), 1);
5747        assert_eq!(prog.agents[0].follows[0].protocol.name, "SchemaSync");
5748        assert_eq!(prog.agents[0].follows[0].role.name, "APISteward");
5749    }
5750
5751    #[test]
5752    fn parse_agent_follows_multiple_protocols() {
5753        let source = r#"
5754            agent MultiProtocolAgent follows Proto1 as RoleA, Proto2 as RoleB {
5755                on start {
5756                    yield(0);
5757                }
5758            }
5759            run MultiProtocolAgent;
5760        "#;
5761
5762        let (prog, errors) = parse_str(source);
5763        assert!(errors.is_empty(), "errors: {errors:?}");
5764        let prog = prog.expect("should parse");
5765
5766        assert_eq!(prog.agents[0].follows.len(), 2);
5767        assert_eq!(prog.agents[0].follows[0].protocol.name, "Proto1");
5768        assert_eq!(prog.agents[0].follows[0].role.name, "RoleA");
5769        assert_eq!(prog.agents[0].follows[1].protocol.name, "Proto2");
5770        assert_eq!(prog.agents[0].follows[1].role.name, "RoleB");
5771    }
5772
5773    #[test]
5774    fn parse_agent_with_receives_and_follows() {
5775        let source = r#"
5776            agent Worker receives Request follows WorkProto as Worker {
5777                on message(msg: Request) {
5778                    reply(Response {});
5779                }
5780            }
5781            run Worker;
5782        "#;
5783
5784        let (prog, errors) = parse_str(source);
5785        assert!(errors.is_empty(), "errors: {errors:?}");
5786        let prog = prog.expect("should parse");
5787
5788        assert!(prog.agents[0].receives.is_some());
5789        assert_eq!(prog.agents[0].follows.len(), 1);
5790    }
5791
5792    #[test]
5793    fn parse_reply_expression() {
5794        let source = r#"
5795            agent Responder {
5796                on message(msg: Request) {
5797                    reply(Response { code: 200 });
5798                }
5799            }
5800            run Responder;
5801        "#;
5802
5803        let (prog, errors) = parse_str(source);
5804        assert!(errors.is_empty(), "errors: {errors:?}");
5805        let prog = prog.expect("should parse");
5806
5807        let handler = &prog.agents[0].handlers[0];
5808        if let Stmt::Expr { expr: Expr::Reply { message, .. }, .. } = &handler.body.stmts[0] {
5809            assert!(matches!(message.as_ref(), Expr::RecordConstruct { .. }));
5810        } else {
5811            panic!("expected Reply expression, got {:?}", handler.body.stmts[0]);
5812        }
5813    }
5814
5815    #[test]
5816    fn parse_effect_handler_declaration() {
5817        let source = r#"
5818            handler DefaultLLM handles Infer {
5819                model: "gpt-4o"
5820                temperature: 0.7
5821                max_tokens: 1024
5822            }
5823
5824            agent Main { on start { yield(0); } }
5825            run Main;
5826        "#;
5827
5828        let (prog, errors) = parse_str(source);
5829        assert!(errors.is_empty(), "errors: {errors:?}");
5830        let prog = prog.expect("should parse");
5831
5832        assert_eq!(prog.effect_handlers.len(), 1);
5833        let handler = &prog.effect_handlers[0];
5834        assert_eq!(handler.name.name, "DefaultLLM");
5835        assert_eq!(handler.effect.name, "Infer");
5836        assert!(!handler.is_pub);
5837        assert_eq!(handler.config.len(), 3);
5838
5839        assert_eq!(handler.config[0].key.name, "model");
5840        assert!(matches!(handler.config[0].value, Literal::String(_)));
5841
5842        assert_eq!(handler.config[1].key.name, "temperature");
5843        assert!(matches!(handler.config[1].value, Literal::Float(_)));
5844
5845        assert_eq!(handler.config[2].key.name, "max_tokens");
5846        assert!(matches!(handler.config[2].value, Literal::Int(1024)));
5847    }
5848
5849    #[test]
5850    fn parse_public_effect_handler() {
5851        let source = r#"
5852            pub handler FastLLM handles Infer {
5853                model: "gpt-4o-mini"
5854            }
5855
5856            agent Main { on start { yield(0); } }
5857            run Main;
5858        "#;
5859
5860        let (prog, errors) = parse_str(source);
5861        assert!(errors.is_empty(), "errors: {errors:?}");
5862        let prog = prog.expect("should parse");
5863
5864        assert!(prog.effect_handlers[0].is_pub);
5865    }
5866
5867    #[test]
5868    fn parse_supervisor_with_handler_assignment() {
5869        let source = r#"
5870            handler DefaultLLM handles Infer {
5871                model: "gpt-4o"
5872            }
5873
5874            agent Worker {
5875                on start {
5876                    let thought = divine("Think!");
5877                    yield(thought);
5878                }
5879            }
5880
5881            supervisor AppSupervisor {
5882                strategy: OneForOne
5883                children {
5884                    Worker {
5885                        restart: Permanent
5886                        handler Infer: DefaultLLM
5887                    }
5888                }
5889            }
5890
5891            run AppSupervisor;
5892        "#;
5893
5894        let (prog, errors) = parse_str(source);
5895        assert!(errors.is_empty(), "errors: {errors:?}");
5896        let prog = prog.expect("should parse");
5897
5898        let child = &prog.supervisors[0].children[0];
5899        assert_eq!(child.agent_name.name, "Worker");
5900        assert_eq!(child.handler_assignments.len(), 1);
5901        assert_eq!(child.handler_assignments[0].effect.name, "Infer");
5902        assert_eq!(child.handler_assignments[0].handler.name, "DefaultLLM");
5903    }
5904
5905    #[test]
5906    fn parse_supervisor_with_multiple_handler_assignments() {
5907        let source = r#"
5908            agent Worker { on start { yield(0); } }
5909
5910            supervisor MultiHandlerSupervisor {
5911                strategy: OneForOne
5912                children {
5913                    Worker {
5914                        restart: Permanent
5915                        handler Infer: FastLLM
5916                        handler Logger: FileLogger
5917                    }
5918                }
5919            }
5920
5921            run MultiHandlerSupervisor;
5922        "#;
5923
5924        let (prog, errors) = parse_str(source);
5925        assert!(errors.is_empty(), "errors: {errors:?}");
5926        let prog = prog.expect("should parse");
5927
5928        let child = &prog.supervisors[0].children[0];
5929        assert_eq!(child.handler_assignments.len(), 2);
5930        assert_eq!(child.handler_assignments[0].effect.name, "Infer");
5931        assert_eq!(child.handler_assignments[0].handler.name, "FastLLM");
5932        assert_eq!(child.handler_assignments[1].effect.name, "Logger");
5933        assert_eq!(child.handler_assignments[1].handler.name, "FileLogger");
5934    }
5935}