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