Skip to main content

ternlang_core/
parser.rs

1use crate::lexer::Token;
2use crate::ast::*;
3use logos::{Logos, Lexer};
4
5pub struct Parser<'a> {
6    lex: Lexer<'a, Token>,
7}
8
9#[derive(Debug)]
10pub enum ParseError {
11    UnexpectedToken(String),
12    ExpectedToken(String, String),
13    InvalidTrit(String),
14    NonExhaustiveMatch(String),
15}
16
17impl<'a> Parser<'a> {
18    pub fn new(input: &'a str) -> Self {
19        Self { lex: Token::lexer(input) }
20    }
21
22    pub fn parse_program(&mut self) -> Result<Program, ParseError> {
23        let mut structs = Vec::new();
24        let mut agents = Vec::new();
25        let mut functions = Vec::new();
26        while self.peek_token().is_ok() {
27            match self.peek_token()? {
28                Token::Struct => structs.push(self.parse_struct_def()?),
29                Token::Agent  => agents.push(self.parse_agent_def()?),
30                _             => functions.push(self.parse_function()?),
31            }
32        }
33        Ok(Program { structs, agents, functions })
34    }
35
36    fn parse_agent_def(&mut self) -> Result<AgentDef, ParseError> {
37        self.expect(Token::Agent)?;
38        let name = match self.next_token()? {
39            Token::Ident(n) => n,
40            t => return Err(ParseError::ExpectedToken("agent name".into(), format!("{:?}", t))),
41        };
42        self.expect(Token::LBrace)?;
43        let mut methods = Vec::new();
44        while self.peek_token()? != Token::RBrace {
45            methods.push(self.parse_function()?);
46        }
47        self.expect(Token::RBrace)?;
48        Ok(AgentDef { name, methods })
49    }
50
51    fn parse_struct_def(&mut self) -> Result<StructDef, ParseError> {
52        self.expect(Token::Struct)?;
53        let name = match self.next_token()? {
54            Token::Ident(n) => n,
55            t => return Err(ParseError::ExpectedToken("struct name".into(), format!("{:?}", t))),
56        };
57        self.expect(Token::LBrace)?;
58        let mut fields = Vec::new();
59        while self.peek_token()? != Token::RBrace {
60            let field_name = match self.next_token()? {
61                Token::Ident(n) => n,
62                t => return Err(ParseError::ExpectedToken("field name".into(), format!("{:?}", t))),
63            };
64            self.expect(Token::Colon)?;
65            let field_type = self.parse_type()?;
66            fields.push((field_name, field_type));
67            if let Ok(Token::Comma) = self.peek_token() { self.next_token()?; }
68        }
69        self.expect(Token::RBrace)?;
70        Ok(StructDef { name, fields })
71    }
72
73    pub fn parse_function(&mut self) -> Result<Function, ParseError> {
74        self.expect(Token::Fn)?;
75        let name = match self.next_token()? {
76            Token::Ident(n) => n,
77            t => return Err(ParseError::ExpectedToken("function name".into(), format!("{:?}", t))),
78        };
79
80        self.expect(Token::LParen)?;
81        let mut params = Vec::new();
82        if self.peek_token()? != Token::RParen {
83            loop {
84                let p_name = match self.next_token()? {
85                    Token::Ident(n) => n,
86                    t => return Err(ParseError::ExpectedToken("parameter name".into(), format!("{:?}", t))),
87                };
88                self.expect(Token::Colon)?;
89                let p_type = self.parse_type()?;
90                params.push((p_name, p_type));
91                if self.peek_token()? == Token::Comma { self.next_token()?; } else { break; }
92            }
93        }
94        self.expect(Token::RParen)?;
95        self.expect(Token::Arrow)?;
96        let return_type = self.parse_type()?;
97        let body = match self.parse_block()? {
98            Stmt::Block(stmts) => stmts,
99            _ => unreachable!(),
100        };
101        Ok(Function { name, params, return_type, body })
102    }
103
104    fn next_token(&mut self) -> Result<Token, ParseError> {
105        self.lex.next()
106            .map(|res| res.map_err(|_| ParseError::UnexpectedToken("Invalid token".into())))
107            .transpose()?
108            .ok_or(ParseError::UnexpectedToken("EOF".into()))
109    }
110
111    fn peek_token(&mut self) -> Result<Token, ParseError> {
112        let mut cloned = self.lex.clone();
113        cloned.next()
114            .map(|res| res.map_err(|_| ParseError::UnexpectedToken("Invalid token".into())))
115            .transpose()?
116            .ok_or(ParseError::UnexpectedToken("EOF".into()))
117    }
118
119    pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
120        self.parse_binary_expr(0)
121    }
122
123    fn parse_binary_expr(&mut self, min_prec: i8) -> Result<Expr, ParseError> {
124        let mut lhs = self.parse_unary_expr()?;
125        loop {
126            let Ok(op_token) = self.peek_token() else { break };
127            let prec = self.get_precedence(&op_token);
128            if prec < min_prec { break; }
129            self.next_token()?;
130            let rhs = self.parse_binary_expr(prec + 1)?;
131            lhs = Expr::BinaryOp {
132                op: self.token_to_binop(op_token),
133                lhs: Box::new(lhs),
134                rhs: Box::new(rhs),
135            };
136        }
137        Ok(lhs)
138    }
139
140    fn get_precedence(&self, token: &Token) -> i8 {
141        match token {
142            Token::Or                      => 0,
143            Token::And                     => 1,
144            Token::Equal | Token::NotEqual => 2,
145            Token::Plus  | Token::Minus    => 3,
146            Token::Star                    => 4,
147            _ => -1,
148        }
149    }
150
151    fn token_to_binop(&self, token: Token) -> BinOp {
152        match token {
153            Token::Plus     => BinOp::Add,
154            Token::Minus    => BinOp::Sub,
155            Token::Star     => BinOp::Mul,
156            Token::Equal    => BinOp::Equal,
157            Token::NotEqual => BinOp::NotEqual,
158            Token::And      => BinOp::And,
159            Token::Or       => BinOp::Or,
160            _ => unreachable!(),
161        }
162    }
163
164    /// Parse unary prefix expressions, then wrap with postfix (field access).
165    fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
166        let mut expr = self.parse_primary_expr()?;
167        // Postfix: `.field` chains
168        loop {
169            if let Ok(Token::Dot) = self.peek_token() {
170                self.next_token()?; // consume `.`
171                let field = match self.next_token()? {
172                    Token::Ident(n) => n,
173                    t => return Err(ParseError::ExpectedToken("field name".into(), format!("{:?}", t))),
174                };
175                expr = Expr::FieldAccess { object: Box::new(expr), field };
176            } else {
177                break;
178            }
179        }
180        Ok(expr)
181    }
182
183    fn parse_primary_expr(&mut self) -> Result<Expr, ParseError> {
184        let token = self.next_token()?;
185        match token {
186            // spawn AgentName           — local agent
187            // spawn remote "addr" Name  — remote agent (Phase 5.1)
188            Token::Spawn => {
189                let node_addr = if let Ok(Token::Remote) = self.peek_token() {
190                    self.next_token()?; // consume `remote`
191                    match self.next_token()? {
192                        Token::StringLit(addr) => Some(addr),
193                        t => return Err(ParseError::ExpectedToken("node address string".into(), format!("{:?}", t))),
194                    }
195                } else {
196                    None
197                };
198                let agent_name = match self.next_token()? {
199                    Token::Ident(n) => n,
200                    t => return Err(ParseError::ExpectedToken("agent name".into(), format!("{:?}", t))),
201                };
202                Ok(Expr::Spawn { agent_name, node_addr })
203            }
204            // await <agentref_expr> — receive from agent mailbox
205            Token::Await => {
206                let target = self.parse_unary_expr()?;
207                Ok(Expr::Await { target: Box::new(target) })
208            }
209            Token::NodeId => Ok(Expr::NodeId),
210            Token::Minus => {
211                let expr = self.parse_unary_expr()?;
212                Ok(Expr::UnaryOp { op: UnOp::Neg, expr: Box::new(expr) })
213            }
214            Token::TritLiteral => {
215                let slice = self.lex.slice();
216                let val = slice.parse::<i8>()
217                    .map_err(|_| ParseError::InvalidTrit(slice.to_string()))?;
218                Ok(Expr::TritLiteral(val))
219            }
220            Token::Int(val) => Ok(Expr::IntLiteral(val)),
221            Token::StringLit(s) => Ok(Expr::StringLiteral(s)),
222            Token::Ident(name) => {
223                // cast(expr) built-in: returns Cast node
224                if name == "cast" {
225                    if let Ok(Token::LParen) = self.peek_token() {
226                        self.next_token()?;
227                        let inner = self.parse_expr()?;
228                        self.expect(Token::RParen)?;
229                        // Type is resolved by context (the let binding ty)
230                        // Emit with placeholder Trit — semantic pass refines this
231                        return Ok(Expr::Cast { expr: Box::new(inner), ty: Type::Trit });
232                    }
233                }
234
235                if let Ok(Token::LParen) = self.peek_token() {
236                    // Function call
237                    self.next_token()?;
238                    let mut args = Vec::new();
239                    if self.peek_token()? != Token::RParen {
240                        loop {
241                            args.push(self.parse_expr()?);
242                            if self.peek_token()? == Token::Comma {
243                                self.next_token()?;
244                            } else {
245                                break;
246                            }
247                        }
248                    }
249                    self.expect(Token::RParen)?;
250                    Ok(Expr::Call { callee: name, args })
251                } else {
252                    Ok(Expr::Ident(name))
253                }
254            }
255            Token::LParen => {
256                let expr = self.parse_expr()?;
257                self.expect(Token::RParen)?;
258                Ok(expr)
259            }
260            _ => Err(ParseError::UnexpectedToken(format!("{:?}", token))),
261        }
262    }
263
264    pub fn parse_stmt(&mut self) -> Result<Stmt, ParseError> {
265        let token = self.peek_token()?;
266        match token {
267            Token::At => {
268                self.next_token()?;
269                let dir = match self.next_token()? {
270                    Token::SparseSkip  => "sparseskip".to_string(),
271                    Token::Ident(n)    => n,
272                    t => return Err(ParseError::ExpectedToken("directive".into(), format!("{:?}", t))),
273                };
274                let stmt = self.parse_stmt()?;
275                Ok(Stmt::Decorated { directive: dir, stmt: Box::new(stmt) })
276            }
277
278            Token::Use => {
279                self.next_token()?;
280                let mut path = Vec::new();
281                loop {
282                    // Accept both identifiers and reserved type keywords as path segments
283                    let segment = match self.next_token()? {
284                        Token::Ident(n)   => n,
285                        Token::TritType   => "trit".to_string(),
286                        Token::TritTensor => "trittensor".to_string(),
287                        t => return Err(ParseError::ExpectedToken("module path segment".into(), format!("{:?}", t))),
288                    };
289                    path.push(segment);
290                    if let Ok(Token::DoubleColon) = self.peek_token() {
291                        self.next_token()?;
292                    } else {
293                        break;
294                    }
295                }
296                self.expect(Token::Semicolon)?;
297                Ok(Stmt::Use { path })
298            }
299
300            Token::Let => {
301                self.next_token()?;
302                // optional mut
303                let _mutable = if let Ok(Token::Mut) = self.peek_token() {
304                    self.next_token()?; true
305                } else { false };
306
307                let name = match self.next_token()? {
308                    Token::Ident(n) => n,
309                    t => return Err(ParseError::ExpectedToken("identifier".into(), format!("{:?}", t))),
310                };
311                self.expect(Token::Colon)?;
312                let ty = self.parse_type()?;
313                let value = if let Ok(Token::Assign) = self.peek_token() {
314                    self.next_token()?;
315                    self.parse_expr()?
316                } else {
317                    Expr::TritLiteral(0)
318                };
319                self.expect(Token::Semicolon)?;
320                Ok(Stmt::Let { name, ty, value })
321            }
322
323            Token::If => {
324                self.next_token()?;
325                let condition = self.parse_expr()?;
326                self.expect(Token::UncertainBranch)?;
327                let on_pos  = Box::new(self.parse_block()?);
328                self.expect(Token::Else)?;
329                let on_zero = Box::new(self.parse_block()?);
330                self.expect(Token::Else)?;
331                let on_neg  = Box::new(self.parse_block()?);
332                Ok(Stmt::IfTernary { condition, on_pos, on_zero, on_neg })
333            }
334
335            Token::Match => {
336                self.next_token()?;
337                let condition = self.parse_expr()?;
338                self.expect(Token::LBrace)?;
339                let mut arms = Vec::new();
340                while self.peek_token()? != Token::RBrace {
341                    let val = match self.next_token()? {
342                        Token::TritLiteral => {
343                            let slice = self.lex.slice();
344                            slice.parse::<i8>().map_err(|_| ParseError::InvalidTrit(slice.to_string()))?
345                        }
346                        t => return Err(ParseError::ExpectedToken("trit literal".into(), format!("{:?}", t))),
347                    };
348                    self.expect(Token::FatArrow)?;
349                    let stmt = self.parse_stmt()?;
350                    arms.push((val, stmt));
351                }
352                self.expect(Token::RBrace)?;
353
354                // Enforce exhaustiveness: must have arms for -1, 0, and +1
355                let has_pos  = arms.iter().any(|(v, _)| *v ==  1);
356                let has_zero = arms.iter().any(|(v, _)| *v ==  0);
357                let has_neg  = arms.iter().any(|(v, _)| *v == -1);
358                if !has_pos || !has_zero || !has_neg {
359                    let missing: Vec<&str> = [
360                        if !has_pos  { Some("1 (truth)")    } else { None },
361                        if !has_zero { Some("0 (hold)")     } else { None },
362                        if !has_neg  { Some("-1 (conflict)") } else { None },
363                    ].iter().filter_map(|x| *x).collect();
364                    return Err(ParseError::NonExhaustiveMatch(
365                        format!("match missing arms: {}", missing.join(", "))
366                    ));
367                }
368
369                Ok(Stmt::Match { condition, arms })
370            }
371
372            // for <var> in <expr> { body }
373            Token::For => {
374                self.next_token()?;
375                let var = match self.next_token()? {
376                    Token::Ident(n) => n,
377                    t => return Err(ParseError::ExpectedToken("loop variable".into(), format!("{:?}", t))),
378                };
379                self.expect(Token::In)?;
380                let iter = self.parse_expr()?;
381                let body = Box::new(self.parse_block()?);
382                Ok(Stmt::ForIn { var, iter, body })
383            }
384
385            // while <condition> ? { on_pos } else { on_zero } else { on_neg }
386            Token::While => {
387                self.next_token()?;
388                let condition = self.parse_expr()?;
389                self.expect(Token::UncertainBranch)?;
390                let on_pos  = Box::new(self.parse_block()?);
391                self.expect(Token::Else)?;
392                let on_zero = Box::new(self.parse_block()?);
393                self.expect(Token::Else)?;
394                let on_neg  = Box::new(self.parse_block()?);
395                Ok(Stmt::WhileTernary { condition, on_pos, on_zero, on_neg })
396            }
397
398            // loop { body }
399            Token::Loop => {
400                self.next_token()?;
401                let body = Box::new(self.parse_block()?);
402                Ok(Stmt::Loop { body })
403            }
404
405            // send <target_expr> <message_expr>;
406            Token::Send => {
407                self.next_token()?;
408                let target = self.parse_expr()?;
409                let message = self.parse_expr()?;
410                self.expect(Token::Semicolon)?;
411                Ok(Stmt::Send { target, message })
412            }
413
414            Token::Break => {
415                self.next_token()?;
416                self.expect(Token::Semicolon)?;
417                Ok(Stmt::Break)
418            }
419
420            Token::Continue => {
421                self.next_token()?;
422                self.expect(Token::Semicolon)?;
423                Ok(Stmt::Continue)
424            }
425
426            Token::Return => {
427                self.next_token()?;
428                let expr = self.parse_expr()?;
429                self.expect(Token::Semicolon)?;
430                Ok(Stmt::Return(expr))
431            }
432
433            Token::LBrace => self.parse_block(),
434
435            _ => {
436                // Could be: expr; OR field assignment: ident.field = value;
437                let expr = self.parse_expr()?;
438
439                // Check for field assignment: expr was `ident.field`, next is `=`
440                if let Ok(Token::Assign) = self.peek_token() {
441                    if let Expr::FieldAccess { object, field } = expr {
442                        if let Expr::Ident(obj_name) = *object {
443                            self.next_token()?; // consume `=`
444                            let value = self.parse_expr()?;
445                            self.expect(Token::Semicolon)?;
446                            return Ok(Stmt::FieldSet { object: obj_name, field, value });
447                        }
448                    }
449                    return Err(ParseError::UnexpectedToken("invalid assignment target".into()));
450                }
451
452                self.expect(Token::Semicolon)?;
453                Ok(Stmt::Expr(expr))
454            }
455        }
456    }
457
458    fn parse_block(&mut self) -> Result<Stmt, ParseError> {
459        self.expect(Token::LBrace)?;
460        let mut stmts = Vec::new();
461        while self.peek_token()? != Token::RBrace {
462            stmts.push(self.parse_stmt()?);
463        }
464        self.expect(Token::RBrace)?;
465        Ok(Stmt::Block(stmts))
466    }
467
468    fn parse_type(&mut self) -> Result<Type, ParseError> {
469        let token = self.next_token()?;
470        match token {
471            Token::TritType   => Ok(Type::Trit),
472            Token::AgentRef   => Ok(Type::AgentRef),
473            Token::TritTensor => {
474                self.expect(Token::LAngle)?;
475                let mut dims = Vec::new();
476                loop {
477                    let d = match self.next_token()? {
478                        Token::Int(v) => v as usize,
479                        // TritLiteral matches "0" and "1" — accept them as dims
480                        Token::TritLiteral => {
481                            let s = self.lex.slice();
482                            s.parse::<i8>().unwrap_or(0).max(0) as usize
483                        }
484                        t => return Err(ParseError::ExpectedToken("dimension".into(), format!("{:?}", t))),
485                    };
486                    dims.push(d);
487                    if self.peek_token()? == Token::Ident("x".to_string()) {
488                        self.next_token()?;
489                    } else {
490                        break;
491                    }
492                }
493                self.expect(Token::RAngle)?;
494                Ok(Type::TritTensor { dims })
495            }
496            Token::Ident(ref name) => match name.as_str() {
497                "int"    => Ok(Type::Int),
498                "float"  => Ok(Type::Float),
499                "bool"   => Ok(Type::Bool),
500                "string" => Ok(Type::String),
501                // Named struct type
502                _        => Ok(Type::Named(name.clone())),
503            },
504            _ => Err(ParseError::UnexpectedToken(format!("{:?}", token))),
505        }
506    }
507
508    fn expect(&mut self, expected: Token) -> Result<(), ParseError> {
509        let token = self.next_token()?;
510        if token == expected {
511            Ok(())
512        } else {
513            Err(ParseError::ExpectedToken(format!("{:?}", expected), format!("{:?}", token)))
514        }
515    }
516}
517
518#[cfg(test)]
519mod tests {
520    use super::*;
521
522    #[test]
523    fn test_parse_function() {
524        let input = "fn invert(signal: trit) -> trit { return -signal; }";
525        let mut parser = Parser::new(input);
526        let func = parser.parse_function().unwrap();
527        assert_eq!(func.name, "invert");
528        assert_eq!(func.params[0].1, Type::Trit);
529        assert_eq!(func.return_type, Type::Trit);
530    }
531
532    #[test]
533    fn test_parse_match() {
534        let input = "match x { 1 => return 1; 0 => return 0; -1 => return -1; }";
535        let mut parser = Parser::new(input);
536        let stmt = parser.parse_stmt().unwrap();
537        if let Stmt::Match { arms, .. } = stmt {
538            assert_eq!(arms.len(), 3);
539            assert_eq!(arms[0].0, 1);
540            assert_eq!(arms[1].0, 0);
541            assert_eq!(arms[2].0, -1);
542        } else {
543            panic!("Expected Match");
544        }
545    }
546
547    #[test]
548    fn test_match_exhaustiveness_enforced() {
549        // Missing hold (0) arm — must fail
550        let input = "match x { 1 => return 1; -1 => return -1; }";
551        let mut parser = Parser::new(input);
552        let result = parser.parse_stmt();
553        assert!(matches!(result, Err(ParseError::NonExhaustiveMatch(_))),
554            "expected NonExhaustiveMatch error");
555    }
556
557    #[test]
558    fn test_parse_for_loop() {
559        let input = "for item in weights { return item; }";
560        let mut parser = Parser::new(input);
561        let stmt = parser.parse_stmt().unwrap();
562        assert!(matches!(stmt, Stmt::ForIn { .. }));
563    }
564
565    #[test]
566    fn test_parse_loop_break() {
567        let input = "loop { break; }";
568        let mut parser = Parser::new(input);
569        let stmt = parser.parse_stmt().unwrap();
570        assert!(matches!(stmt, Stmt::Loop { .. }));
571    }
572
573    #[test]
574    fn test_parse_use() {
575        let input = "use std::trit;";
576        let mut parser = Parser::new(input);
577        let stmt = parser.parse_stmt().unwrap();
578        if let Stmt::Use { path } = stmt {
579            assert_eq!(path, vec!["std", "trit"]);
580        } else {
581            panic!("Expected Use");
582        }
583    }
584
585    #[test]
586    fn test_parse_mut_let() {
587        let input = "let mut signal: trit = 1;";
588        let mut parser = Parser::new(input);
589        let stmt = parser.parse_stmt().unwrap();
590        assert!(matches!(stmt, Stmt::Let { .. }));
591    }
592
593    #[test]
594    fn test_parse_struct_def() {
595        let input = "struct Signal { value: trit, weight: trit }";
596        let mut parser = Parser::new(input);
597        let s = parser.parse_struct_def().unwrap();
598        assert_eq!(s.name, "Signal");
599        assert_eq!(s.fields.len(), 2);
600        assert_eq!(s.fields[0], ("value".to_string(), Type::Trit));
601        assert_eq!(s.fields[1], ("weight".to_string(), Type::Trit));
602    }
603
604    #[test]
605    fn test_parse_field_access() {
606        let input = "let v: trit = sig.value;";
607        let mut parser = Parser::new(input);
608        let stmt = parser.parse_stmt().unwrap();
609        if let Stmt::Let { value: Expr::FieldAccess { field, .. }, .. } = stmt {
610            assert_eq!(field, "value");
611        } else {
612            panic!("Expected FieldAccess in let binding");
613        }
614    }
615
616    #[test]
617    fn test_parse_field_set() {
618        let input = "sig.value = 1;";
619        let mut parser = Parser::new(input);
620        let stmt = parser.parse_stmt().unwrap();
621        assert!(matches!(stmt, Stmt::FieldSet { .. }));
622    }
623
624    #[test]
625    fn test_parse_cast() {
626        let input = "let t: trit = cast(flag);";
627        let mut parser = Parser::new(input);
628        let stmt = parser.parse_stmt().unwrap();
629        if let Stmt::Let { value: Expr::Cast { .. }, .. } = stmt {
630            // ok
631        } else {
632            panic!("Expected Cast in let binding");
633        }
634    }
635
636    #[test]
637    fn test_parse_named_type() {
638        let input = "let s: Signal;";
639        let mut parser = Parser::new(input);
640        let stmt = parser.parse_stmt().unwrap();
641        if let Stmt::Let { ty: Type::Named(name), .. } = stmt {
642            assert_eq!(name, "Signal");
643        } else {
644            panic!("Expected Named type");
645        }
646    }
647
648    #[test]
649    fn test_parse_agent_def() {
650        let input = r#"
651            agent Voter {
652                fn handle(msg: trit) -> trit {
653                    match msg {
654                         1 => { return  1; }
655                         0 => { return  0; }
656                        -1 => { return -1; }
657                    }
658                }
659            }
660        "#;
661        let mut parser = Parser::new(input);
662        let agent = parser.parse_agent_def().unwrap();
663        assert_eq!(agent.name, "Voter");
664        assert_eq!(agent.methods.len(), 1);
665        assert_eq!(agent.methods[0].name, "handle");
666    }
667
668    #[test]
669    fn test_parse_spawn() {
670        let input = "let v: agentref = spawn Voter;";
671        let mut parser = Parser::new(input);
672        let stmt = parser.parse_stmt().unwrap();
673        if let Stmt::Let { ty: Type::AgentRef, value: Expr::Spawn { agent_name, node_addr }, .. } = stmt {
674            assert_eq!(agent_name, "Voter");
675            assert_eq!(node_addr, None);
676        } else {
677            panic!("Expected spawn in let binding");
678        }
679    }
680
681    #[test]
682    fn test_parse_spawn_remote() {
683        let input = r#"let v: agentref = spawn remote "10.0.0.1:7373" Voter;"#;
684        let mut parser = Parser::new(input);
685        let stmt = parser.parse_stmt().unwrap();
686        if let Stmt::Let { ty: Type::AgentRef, value: Expr::Spawn { agent_name, node_addr }, .. } = stmt {
687            assert_eq!(agent_name, "Voter");
688            assert_eq!(node_addr, Some("10.0.0.1:7373".to_string()));
689        } else {
690            panic!("Expected remote spawn in let binding");
691        }
692    }
693
694    #[test]
695    fn test_parse_send() {
696        let input = "send v 1;";
697        let mut parser = Parser::new(input);
698        let stmt = parser.parse_stmt().unwrap();
699        assert!(matches!(stmt, Stmt::Send { .. }));
700    }
701
702    #[test]
703    fn test_parse_await() {
704        let input = "let reply: trit = await v;";
705        let mut parser = Parser::new(input);
706        let stmt = parser.parse_stmt().unwrap();
707        if let Stmt::Let { value: Expr::Await { .. }, .. } = stmt {
708            // ok
709        } else {
710            panic!("Expected await in let binding");
711        }
712    }
713}