1use runmat_lexer::Token;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
5pub enum Expr {
6 Number(String),
7 String(String),
8 Ident(String),
9 EndKeyword, Unary(UnOp, Box<Expr>),
11 Binary(Box<Expr>, BinOp, Box<Expr>),
12 Tensor(Vec<Vec<Expr>>),
13 Cell(Vec<Vec<Expr>>),
14 Index(Box<Expr>, Vec<Expr>),
15 IndexCell(Box<Expr>, Vec<Expr>),
16 Range(Box<Expr>, Option<Box<Expr>>, Box<Expr>),
17 Colon,
18 FuncCall(String, Vec<Expr>),
19 Member(Box<Expr>, String),
20 MemberDynamic(Box<Expr>, Box<Expr>),
22 MethodCall(Box<Expr>, String, Vec<Expr>),
23 AnonFunc {
24 params: Vec<String>,
25 body: Box<Expr>,
26 },
27 FuncHandle(String),
28 MetaClass(String),
29}
30
31#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
32pub enum BinOp {
33 Add,
34 Sub,
35 Mul,
36 Div,
37 Pow,
38 LeftDiv,
39 Colon,
40 ElemMul, ElemDiv, ElemPow, ElemLeftDiv, AndAnd, OrOr, BitAnd, BitOr, Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual, }
58
59#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
60pub enum UnOp {
61 Plus,
62 Minus,
63 Transpose,
64 NonConjugateTranspose,
65 Not, }
67
68#[derive(Debug, PartialEq)]
69pub enum Stmt {
70 ExprStmt(Expr, bool), Assign(String, Expr, bool), MultiAssign(Vec<String>, Expr, bool),
73 AssignLValue(LValue, Expr, bool),
74 If {
75 cond: Expr,
76 then_body: Vec<Stmt>,
77 elseif_blocks: Vec<(Expr, Vec<Stmt>)>,
78 else_body: Option<Vec<Stmt>>,
79 },
80 While {
81 cond: Expr,
82 body: Vec<Stmt>,
83 },
84 For {
85 var: String,
86 expr: Expr,
87 body: Vec<Stmt>,
88 },
89 Switch {
90 expr: Expr,
91 cases: Vec<(Expr, Vec<Stmt>)>,
92 otherwise: Option<Vec<Stmt>>,
93 },
94 TryCatch {
95 try_body: Vec<Stmt>,
96 catch_var: Option<String>,
97 catch_body: Vec<Stmt>,
98 },
99 Global(Vec<String>),
100 Persistent(Vec<String>),
101 Break,
102 Continue,
103 Return,
104 Function {
105 name: String,
106 params: Vec<String>,
107 outputs: Vec<String>,
108 body: Vec<Stmt>,
109 },
110 Import {
111 path: Vec<String>,
112 wildcard: bool,
113 },
114 ClassDef {
115 name: String,
116 super_class: Option<String>,
117 members: Vec<ClassMember>,
118 },
119}
120
121#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
122pub enum LValue {
123 Var(String),
124 Member(Box<Expr>, String),
125 MemberDynamic(Box<Expr>, Box<Expr>),
126 Index(Box<Expr>, Vec<Expr>),
127 IndexCell(Box<Expr>, Vec<Expr>),
128}
129
130#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
131pub struct Attr {
132 pub name: String,
133 pub value: Option<String>,
134}
135
136#[derive(Debug, PartialEq)]
137pub enum ClassMember {
138 Properties {
139 attributes: Vec<Attr>,
140 names: Vec<String>,
141 },
142 Methods {
143 attributes: Vec<Attr>,
144 body: Vec<Stmt>,
145 },
146 Events {
147 attributes: Vec<Attr>,
148 names: Vec<String>,
149 },
150 Enumeration {
151 attributes: Vec<Attr>,
152 names: Vec<String>,
153 },
154 Arguments {
155 attributes: Vec<Attr>,
156 names: Vec<String>,
157 },
158}
159
160#[derive(Debug, PartialEq)]
161pub struct Program {
162 pub body: Vec<Stmt>,
163}
164
165#[derive(Clone)]
166struct TokenInfo {
167 token: Token,
168 lexeme: String,
169 position: usize,
170}
171
172#[derive(Debug)]
173pub struct ParseError {
174 pub message: String,
175 pub position: usize,
176 pub found_token: Option<String>,
177 pub expected: Option<String>,
178}
179
180pub fn parse(input: &str) -> Result<Program, ParseError> {
181 use runmat_lexer::tokenize_detailed;
182
183 let toks = tokenize_detailed(input);
184 let mut tokens = Vec::new();
185
186 for t in toks {
187 if matches!(t.token, Token::Error) {
188 return Err(ParseError {
189 message: format!("Invalid token: '{}'", t.lexeme),
190 position: t.start,
191 found_token: Some(t.lexeme),
192 expected: None,
193 });
194 }
195 if matches!(t.token, Token::Ellipsis | Token::Section) {
197 continue;
198 }
199 tokens.push(TokenInfo {
200 token: t.token,
201 lexeme: t.lexeme,
202 position: t.start,
203 });
204 }
205
206 let mut parser = Parser {
207 tokens,
208 pos: 0,
209 input: input.to_string(),
210 };
211 parser.parse_program()
212}
213
214pub fn parse_simple(input: &str) -> Result<Program, String> {
216 parse(input).map_err(|e| format!("{e}"))
217}
218
219impl std::fmt::Display for ParseError {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(
222 f,
223 "Parse error at position {}: {}",
224 self.position, self.message
225 )?;
226 if let Some(found) = &self.found_token {
227 write!(f, " (found: '{found}')")?;
228 }
229 if let Some(expected) = &self.expected {
230 write!(f, " (expected: {expected})")?;
231 }
232 Ok(())
233 }
234}
235
236impl std::error::Error for ParseError {}
237
238impl From<String> for ParseError {
239 fn from(message: String) -> Self {
240 ParseError {
241 message,
242 position: 0,
243 found_token: None,
244 expected: None,
245 }
246 }
247}
248
249impl From<ParseError> for String {
250 fn from(error: ParseError) -> Self {
251 format!("{error}")
252 }
253}
254
255struct Parser {
256 tokens: Vec<TokenInfo>,
257 pos: usize,
258 input: String,
259}
260
261impl Parser {
262 fn skip_newlines(&mut self) {
263 while self.consume(&Token::Newline) {}
264 }
265
266 fn is_simple_assignment_ahead(&self) -> bool {
267 self.peek_token() == Some(&Token::Ident) && self.peek_token_at(1) == Some(&Token::Assign)
269 }
270 fn parse_program(&mut self) -> Result<Program, ParseError> {
271 let mut body = Vec::new();
272 while self.pos < self.tokens.len() {
273 if self.consume(&Token::Semicolon)
274 || self.consume(&Token::Comma)
275 || self.consume(&Token::Newline)
276 {
277 continue;
278 }
279 body.push(self.parse_stmt_with_semicolon()?);
280 }
281 Ok(Program { body })
282 }
283
284 fn error(&self, message: &str) -> ParseError {
285 let (position, found_token) = if let Some(token_info) = self.tokens.get(self.pos) {
286 (token_info.position, Some(token_info.lexeme.clone()))
287 } else {
288 (self.input.len(), None)
289 };
290
291 ParseError {
292 message: message.to_string(),
293 position,
294 found_token,
295 expected: None,
296 }
297 }
298
299 fn error_with_expected(&self, message: &str, expected: &str) -> ParseError {
300 let (position, found_token) = if let Some(token_info) = self.tokens.get(self.pos) {
301 (token_info.position, Some(token_info.lexeme.clone()))
302 } else {
303 (self.input.len(), None)
304 };
305
306 ParseError {
307 message: message.to_string(),
308 position,
309 found_token,
310 expected: Some(expected.to_string()),
311 }
312 }
313
314 fn parse_stmt_with_semicolon(&mut self) -> Result<Stmt, ParseError> {
315 let stmt = self.parse_stmt()?;
316 let is_semicolon_terminated = self.consume(&Token::Semicolon);
317
318 match stmt {
322 Stmt::ExprStmt(expr, _) => Ok(Stmt::ExprStmt(expr, is_semicolon_terminated)),
323 Stmt::Assign(name, expr, _) => {
324 let has_more_toplevel_tokens = self.pos < self.tokens.len();
325 Ok(Stmt::Assign(
326 name,
327 expr,
328 is_semicolon_terminated && has_more_toplevel_tokens,
329 ))
330 }
331 Stmt::MultiAssign(names, expr, _) => {
332 let has_more_toplevel_tokens = self.pos < self.tokens.len();
333 Ok(Stmt::MultiAssign(
334 names,
335 expr,
336 is_semicolon_terminated && has_more_toplevel_tokens,
337 ))
338 }
339 Stmt::AssignLValue(lv, expr, _) => {
340 let has_more_toplevel_tokens = self.pos < self.tokens.len();
341 Ok(Stmt::AssignLValue(
342 lv,
343 expr,
344 is_semicolon_terminated && has_more_toplevel_tokens,
345 ))
346 }
347 other => Ok(other),
348 }
349 }
350
351 fn parse_stmt(&mut self) -> Result<Stmt, ParseError> {
352 match self.peek_token() {
353 Some(Token::If) => self.parse_if().map_err(|e| e.into()),
354 Some(Token::For) => self.parse_for().map_err(|e| e.into()),
355 Some(Token::While) => self.parse_while().map_err(|e| e.into()),
356 Some(Token::Switch) => self.parse_switch().map_err(|e| e.into()),
357 Some(Token::Try) => self.parse_try_catch().map_err(|e| e.into()),
358 Some(Token::Import) => self.parse_import().map_err(|e| e.into()),
359 Some(Token::ClassDef) => self.parse_classdef().map_err(|e| e.into()),
360 Some(Token::Global) => self.parse_global().map_err(|e| e.into()),
361 Some(Token::Persistent) => self.parse_persistent().map_err(|e| e.into()),
362 Some(Token::Break) => {
363 self.pos += 1;
364 Ok(Stmt::Break)
365 }
366 Some(Token::Continue) => {
367 self.pos += 1;
368 Ok(Stmt::Continue)
369 }
370 Some(Token::Return) => {
371 self.pos += 1;
372 Ok(Stmt::Return)
373 }
374 Some(Token::Function) => self.parse_function().map_err(|e| e.into()),
375 Some(Token::LBracket) => {
377 if matches!(self.peek_token_at(1), Some(Token::Ident | Token::Tilde)) {
378 match self.try_parse_multi_assign() {
379 Ok(stmt) => Ok(stmt),
380 Err(msg) => Err(self.error(&msg)),
381 }
382 } else {
383 let expr = self.parse_expr()?;
384 Ok(Stmt::ExprStmt(expr, false))
385 }
386 }
387 _ => {
388 if self.peek_token() == Some(&Token::Ident)
389 && self.peek_token_at(1) == Some(&Token::Assign)
390 {
391 let name = self
392 .next()
393 .ok_or_else(|| self.error("expected identifier"))?
394 .lexeme;
395 if !self.consume(&Token::Assign) {
396 return Err(self.error_with_expected("expected assignment operator", "'='"));
397 }
398 let expr = self.parse_expr()?;
399 Ok(Stmt::Assign(name, expr, false)) } else if self.is_simple_assignment_ahead() {
401 let name = self.expect_ident().map_err(|e| self.error(&e))?;
403 if !self.consume(&Token::Assign) {
404 return Err(self.error_with_expected("expected assignment operator", "'='"));
405 }
406 let expr = self.parse_expr()?;
407 Ok(Stmt::Assign(name, expr, false))
408 } else if self.peek_token() == Some(&Token::Ident) {
409 if let Some(lv) = self.try_parse_lvalue_assign()? {
411 return Ok(lv);
412 }
413 if self.can_start_command_form() {
416 let name = self.next().unwrap().lexeme;
417 let args = self.parse_command_args();
418 Ok(Stmt::ExprStmt(Expr::FuncCall(name, args), false))
419 } else {
420 if matches!(self.peek_token_at(1), Some(Token::Ident))
423 && matches!(
424 self.peek_token_at(2),
425 Some(
426 Token::LParen
427 | Token::Dot
428 | Token::LBracket
429 | Token::LBrace
430 | Token::Transpose
431 )
432 )
433 {
434 return Err(self.error(
435 "ambiguous command-form near identifier; use function syntax foo(b(...)) or quote argument",
436 ));
437 }
438 let expr = self.parse_expr()?;
440 Ok(Stmt::ExprStmt(expr, false))
441 }
442 } else if let Some(lv) = self.try_parse_lvalue_assign()? {
443 Ok(lv)
444 } else {
445 let expr = self.parse_expr()?;
446 Ok(Stmt::ExprStmt(expr, false))
450 }
451 }
452 }
453 }
454
455 fn can_start_command_form(&self) -> bool {
456 let mut i = 1;
458 while matches!(
459 self.peek_token_at(i),
460 Some(Token::Newline | Token::Ellipsis)
461 ) {
462 i += 1;
463 }
464 if !matches!(
466 self.peek_token_at(i),
467 Some(Token::Ident | Token::Integer | Token::Float | Token::Str | Token::End)
468 ) {
469 return false;
470 }
471 loop {
473 match self.peek_token_at(i) {
474 Some(Token::Ident | Token::Integer | Token::Float | Token::Str | Token::End) => {
475 i += 1;
476 }
477 Some(Token::Newline | Token::Ellipsis) => {
478 i += 1;
479 }
480 _ => break,
481 }
482 }
483 match self.peek_token_at(i) {
485 Some(Token::LParen)
486 | Some(Token::Dot)
487 | Some(Token::LBracket)
488 | Some(Token::LBrace)
489 | Some(Token::Transpose) => false,
490 Some(Token::Assign) => false,
492 None | Some(Token::Semicolon) | Some(Token::Comma) | Some(Token::Newline) => true,
494 _ => true,
496 }
497 }
498
499 fn parse_command_args(&mut self) -> Vec<Expr> {
500 let mut args = Vec::new();
501 loop {
502 if self.consume(&Token::Newline) {
503 continue;
504 }
505 if self.consume(&Token::Ellipsis) {
506 continue;
507 }
508 match self.peek_token() {
509 Some(Token::Ident) => {
510 let ident = self.next().unwrap().lexeme;
511 args.push(Expr::Ident(ident));
512 }
513 Some(Token::End) => {
515 self.pos += 1;
516 args.push(Expr::Ident("end".to_string()));
517 }
518 Some(Token::Integer) | Some(Token::Float) => {
519 let num = self.next().unwrap().lexeme;
520 args.push(Expr::Number(num));
521 }
522 Some(Token::Str) => {
523 let s = self.next().unwrap().lexeme;
524 args.push(Expr::String(s));
525 }
526 Some(Token::Slash)
528 | Some(Token::Star)
529 | Some(Token::Backslash)
530 | Some(Token::Plus)
531 | Some(Token::Minus)
532 | Some(Token::LParen)
533 | Some(Token::Dot)
534 | Some(Token::LBracket)
535 | Some(Token::LBrace)
536 | Some(Token::Transpose) => break,
537 _ => break,
538 }
539 }
540 args
541 }
542
543 fn try_parse_lvalue_assign(&mut self) -> Result<Option<Stmt>, ParseError> {
544 let save = self.pos;
545 let lvalue = if self.peek_token() == Some(&Token::Ident) {
547 let base_ident = self.next().unwrap().lexeme;
549 let mut base = Expr::Ident(base_ident);
550 loop {
551 if self.consume(&Token::LParen) {
552 let mut args = Vec::new();
553 if !self.consume(&Token::RParen) {
554 args.push(self.parse_expr()?);
555 while self.consume(&Token::Comma) {
556 args.push(self.parse_expr()?);
557 }
558 if !self.consume(&Token::RParen) {
559 return Err(self.error_with_expected("expected ')' after indices", ")"));
560 }
561 }
562 base = Expr::Index(Box::new(base), args);
563 } else if self.consume(&Token::LBracket) {
564 let mut idxs = Vec::new();
565 idxs.push(self.parse_expr()?);
566 while self.consume(&Token::Comma) {
567 idxs.push(self.parse_expr()?);
568 }
569 if !self.consume(&Token::RBracket) {
570 return Err(self.error_with_expected("expected ']'", "]"));
571 }
572 base = Expr::Index(Box::new(base), idxs);
573 } else if self.consume(&Token::LBrace) {
574 let mut idxs = Vec::new();
575 idxs.push(self.parse_expr()?);
576 while self.consume(&Token::Comma) {
577 idxs.push(self.parse_expr()?);
578 }
579 if !self.consume(&Token::RBrace) {
580 return Err(self.error_with_expected("expected '}'", "}"));
581 }
582 base = Expr::IndexCell(Box::new(base), idxs);
583 } else if self.peek_token() == Some(&Token::Dot) {
584 if self.peek_token_at(1) == Some(&Token::Transpose) {
586 break;
587 }
588 if self.peek_token_at(1) == Some(&Token::Plus)
590 || self.peek_token_at(1) == Some(&Token::Minus)
591 {
592 break;
593 }
594 self.pos += 1; if self.consume(&Token::LParen) {
598 let name_expr = self.parse_expr()?;
599 if !self.consume(&Token::RParen) {
600 return Err(self.error_with_expected(
601 "expected ')' after dynamic field expression",
602 ")",
603 ));
604 }
605 base = Expr::MemberDynamic(Box::new(base), Box::new(name_expr));
606 } else {
607 let name = self.expect_ident()?;
608 base = Expr::Member(Box::new(base), name);
609 }
610 } else {
611 break;
612 }
613 }
614 base
615 } else {
616 self.pos = save;
617 return Ok(None);
618 };
619 if !self.consume(&Token::Assign) {
620 self.pos = save;
621 return Ok(None);
622 }
623 let rhs = self.parse_expr()?;
624 let stmt = match lvalue {
625 Expr::Member(b, name) => Stmt::AssignLValue(LValue::Member(b, name), rhs, false),
626 Expr::MemberDynamic(b, n) => {
627 Stmt::AssignLValue(LValue::MemberDynamic(b, n), rhs, false)
628 }
629 Expr::Index(b, idxs) => Stmt::AssignLValue(LValue::Index(b, idxs), rhs, false),
630 Expr::IndexCell(b, idxs) => Stmt::AssignLValue(LValue::IndexCell(b, idxs), rhs, false),
631 Expr::Ident(v) => Stmt::Assign(v, rhs, false),
632 _ => {
633 self.pos = save;
634 return Ok(None);
635 }
636 };
637 Ok(Some(stmt))
638 }
639
640 fn parse_expr(&mut self) -> Result<Expr, ParseError> {
641 self.parse_logical_or()
642 }
643
644 fn parse_logical_or(&mut self) -> Result<Expr, ParseError> {
645 let mut node = self.parse_logical_and()?;
646 while self.consume(&Token::OrOr) {
647 let rhs = self.parse_logical_and()?;
648 node = Expr::Binary(Box::new(node), BinOp::OrOr, Box::new(rhs));
649 }
650 Ok(node)
651 }
652
653 fn parse_logical_and(&mut self) -> Result<Expr, ParseError> {
654 let mut node = self.parse_bitwise_or()?;
655 while self.consume(&Token::AndAnd) {
656 let rhs = self.parse_bitwise_or()?;
657 node = Expr::Binary(Box::new(node), BinOp::AndAnd, Box::new(rhs));
658 }
659 Ok(node)
660 }
661
662 fn parse_bitwise_or(&mut self) -> Result<Expr, ParseError> {
663 let mut node = self.parse_bitwise_and()?;
664 while self.consume(&Token::Or) {
665 let rhs = self.parse_bitwise_and()?;
666 node = Expr::Binary(Box::new(node), BinOp::BitOr, Box::new(rhs));
667 }
668 Ok(node)
669 }
670
671 fn parse_bitwise_and(&mut self) -> Result<Expr, ParseError> {
672 let mut node = self.parse_range()?;
673 while self.consume(&Token::And) {
674 let rhs = self.parse_range()?;
675 node = Expr::Binary(Box::new(node), BinOp::BitAnd, Box::new(rhs));
676 }
677 Ok(node)
678 }
679
680 fn parse_range(&mut self) -> Result<Expr, ParseError> {
681 let mut node = self.parse_comparison()?;
682 if self.consume(&Token::Colon) {
683 let mid = self.parse_comparison()?;
684 if self.consume(&Token::Colon) {
685 let end = self.parse_comparison()?;
686 node = Expr::Range(Box::new(node), Some(Box::new(mid)), Box::new(end));
687 } else {
688 node = Expr::Range(Box::new(node), None, Box::new(mid));
689 }
690 }
691 Ok(node)
692 }
693
694 fn parse_comparison(&mut self) -> Result<Expr, ParseError> {
695 let mut node = self.parse_add_sub()?;
696 loop {
697 let op = match self.peek_token() {
698 Some(Token::Equal) => BinOp::Equal,
699 Some(Token::NotEqual) => BinOp::NotEqual,
700 Some(Token::Less) => BinOp::Less,
701 Some(Token::LessEqual) => BinOp::LessEqual,
702 Some(Token::Greater) => BinOp::Greater,
703 Some(Token::GreaterEqual) => BinOp::GreaterEqual,
704 _ => break,
705 };
706 self.pos += 1; let rhs = self.parse_add_sub()?;
708 node = Expr::Binary(Box::new(node), op, Box::new(rhs));
709 }
710 Ok(node)
711 }
712
713 fn parse_add_sub(&mut self) -> Result<Expr, String> {
714 let mut node = self.parse_mul_div()?;
715 loop {
716 let op = if self.consume(&Token::Plus) {
717 Some(BinOp::Add)
718 } else if self.consume(&Token::Minus) {
719 Some(BinOp::Sub)
720 } else if self.peek_token() == Some(&Token::Dot)
721 && (self.peek_token_at(1) == Some(&Token::Plus)
722 || self.peek_token_at(1) == Some(&Token::Minus))
723 {
724 self.pos += 2;
727 if self.tokens[self.pos - 1].token == Token::Plus {
728 Some(BinOp::Add)
729 } else {
730 Some(BinOp::Sub)
731 }
732 } else {
733 None
734 };
735 let Some(op) = op else { break };
736 let rhs = self.parse_mul_div()?;
737 node = Expr::Binary(Box::new(node), op, Box::new(rhs));
738 }
739 Ok(node)
740 }
741
742 fn parse_mul_div(&mut self) -> Result<Expr, String> {
743 let mut node = self.parse_unary()?;
744 loop {
745 let op = match self.peek_token() {
746 Some(Token::Star) => BinOp::Mul,
747 Some(Token::DotStar) => BinOp::ElemMul,
748 Some(Token::Slash) => BinOp::Div,
749 Some(Token::DotSlash) => BinOp::ElemDiv,
750 Some(Token::Backslash) => BinOp::LeftDiv,
751 Some(Token::DotBackslash) => BinOp::ElemLeftDiv,
752 _ => break,
753 };
754 self.pos += 1; let rhs = self.parse_unary()?;
756 node = Expr::Binary(Box::new(node), op, Box::new(rhs));
757 }
758 Ok(node)
759 }
760
761 fn parse_pow(&mut self) -> Result<Expr, String> {
762 let node = self.parse_postfix()?;
763 if let Some(token) = self.peek_token() {
764 let op = match token {
765 Token::Caret => BinOp::Pow,
766 Token::DotCaret => BinOp::ElemPow,
767 _ => return Ok(node),
768 };
769 self.pos += 1; let rhs = self.parse_pow()?; Ok(Expr::Binary(Box::new(node), op, Box::new(rhs)))
772 } else {
773 Ok(node)
774 }
775 }
776
777 fn parse_postfix_with_base(&mut self, mut expr: Expr) -> Result<Expr, String> {
778 loop {
779 if self.consume(&Token::LParen) {
780 let mut args = Vec::new();
781 if !self.consume(&Token::RParen) {
782 args.push(self.parse_expr()?);
783 while self.consume(&Token::Comma) {
784 args.push(self.parse_expr()?);
785 }
786 if !self.consume(&Token::RParen) {
787 return Err("expected ')' after arguments".into());
788 }
789 }
790 if let Expr::Ident(ref name) = expr {
794 expr = Expr::FuncCall(name.clone(), args);
795 } else {
796 expr = Expr::Index(Box::new(expr), args);
798 }
799 } else if self.consume(&Token::LBracket) {
800 let mut indices = Vec::new();
802 indices.push(self.parse_expr()?);
803 while self.consume(&Token::Comma) {
804 indices.push(self.parse_expr()?);
805 }
806 if !self.consume(&Token::RBracket) {
807 return Err("expected ']'".into());
808 }
809 expr = Expr::Index(Box::new(expr), indices);
810 } else if self.consume(&Token::LBrace) {
811 let mut indices = Vec::new();
813 indices.push(self.parse_expr()?);
814 while self.consume(&Token::Comma) {
815 indices.push(self.parse_expr()?);
816 }
817 if !self.consume(&Token::RBrace) {
818 return Err("expected '}'".into());
819 }
820 expr = Expr::IndexCell(Box::new(expr), indices);
821 } else if self.peek_token() == Some(&Token::Dot) {
822 if self.peek_token_at(1) == Some(&Token::Transpose) {
824 self.pos += 2; expr = Expr::Unary(UnOp::NonConjugateTranspose, Box::new(expr));
826 continue;
827 }
828 if self.peek_token_at(1) == Some(&Token::Plus)
829 || self.peek_token_at(1) == Some(&Token::Minus)
830 {
831 break;
833 }
834 self.pos += 1; let name = match self.next() {
837 Some(TokenInfo {
838 token: Token::Ident,
839 lexeme,
840 ..
841 }) => lexeme,
842 _ => return Err("expected member name after '.'".into()),
843 };
844 if self.consume(&Token::LParen) {
845 let mut args = Vec::new();
846 if !self.consume(&Token::RParen) {
847 args.push(self.parse_expr()?);
848 while self.consume(&Token::Comma) {
849 args.push(self.parse_expr()?);
850 }
851 if !self.consume(&Token::RParen) {
852 return Err("expected ')' after method arguments".into());
853 }
854 }
855 expr = Expr::MethodCall(Box::new(expr), name, args);
856 } else {
857 expr = Expr::Member(Box::new(expr), name);
858 }
859 } else if self.consume(&Token::Transpose) {
860 expr = Expr::Unary(UnOp::Transpose, Box::new(expr));
862 } else {
863 break;
864 }
865 }
866 Ok(expr)
867 }
868
869 fn parse_postfix(&mut self) -> Result<Expr, String> {
870 let expr = self.parse_primary()?;
871 self.parse_postfix_with_base(expr)
872 }
873
874 fn parse_unary(&mut self) -> Result<Expr, String> {
875 if self.consume(&Token::Plus) {
876 Ok(Expr::Unary(UnOp::Plus, Box::new(self.parse_unary()?)))
877 } else if self.consume(&Token::Minus) {
878 Ok(Expr::Unary(UnOp::Minus, Box::new(self.parse_unary()?)))
879 } else if self.consume(&Token::Tilde) {
880 Ok(Expr::Unary(UnOp::Not, Box::new(self.parse_unary()?)))
881 } else if self.consume(&Token::Question) {
882 let mut parts: Vec<String> = Vec::new();
885 let first = self.expect_ident()?;
886 let class_consumed = first
887 .chars()
888 .next()
889 .map(|c| c.is_uppercase())
890 .unwrap_or(false);
891 parts.push(first);
892 while self.peek_token() == Some(&Token::Dot)
893 && matches!(self.peek_token_at(1), Some(Token::Ident))
894 {
895 let next_lex = if let Some(ti) = self.tokens.get(self.pos + 1) {
897 ti.lexeme.clone()
898 } else {
899 String::new()
900 };
901 let is_upper = next_lex
902 .chars()
903 .next()
904 .map(|c| c.is_uppercase())
905 .unwrap_or(false);
906 if class_consumed {
907 break;
908 }
909 self.pos += 1; let seg = self.expect_ident()?;
912 parts.push(seg);
913 if is_upper {
914 break;
915 }
916 }
917 let base = Expr::MetaClass(parts.join("."));
918 self.parse_postfix_with_base(base)
919 } else {
920 self.parse_pow()
921 }
922 }
923
924 fn parse_primary(&mut self) -> Result<Expr, String> {
925 match self.next() {
926 Some(info) => match info.token {
927 Token::Integer | Token::Float => Ok(Expr::Number(info.lexeme)),
928 Token::Str => Ok(Expr::String(info.lexeme)),
929 Token::True => Ok(Expr::Ident("true".into())),
930 Token::False => Ok(Expr::Ident("false".into())),
931 Token::Ident => Ok(Expr::Ident(info.lexeme)),
932 Token::End => Ok(Expr::EndKeyword),
934 Token::At => {
935 if self.consume(&Token::LParen) {
937 let mut params = Vec::new();
938 if !self.consume(&Token::RParen) {
939 params.push(self.expect_ident()?);
940 while self.consume(&Token::Comma) {
941 params.push(self.expect_ident()?);
942 }
943 if !self.consume(&Token::RParen) {
944 return Err(
945 "expected ')' after anonymous function parameters".into()
946 );
947 }
948 }
949 let body = self.parse_expr().map_err(|e| e.message)?;
950 Ok(Expr::AnonFunc {
951 params,
952 body: Box::new(body),
953 })
954 } else {
955 let name = self.expect_ident()?;
957 Ok(Expr::FuncHandle(name))
958 }
959 }
960 Token::LParen => {
961 let expr = self.parse_expr()?;
962 if !self.consume(&Token::RParen) {
963 return Err("expected ')' to close parentheses".into());
964 }
965 Ok(expr)
966 }
967 Token::LBracket => {
968 let matrix = self.parse_matrix()?;
969 if !self.consume(&Token::RBracket) {
970 return Err("expected ']' to close matrix literal".into());
971 }
972 Ok(matrix)
973 }
974 Token::LBrace => {
975 let cell = self.parse_cell()?;
976 if !self.consume(&Token::RBrace) {
977 return Err("expected '}' to close cell literal".into());
978 }
979 Ok(cell)
980 }
981 Token::Colon => Ok(Expr::Colon),
982 Token::ClassDef => {
983 self.pos -= 1;
985 Err("classdef in expression context".into())
986 }
987 _ => {
988 let token_desc = match info.token {
990 Token::Semicolon => "semicolon ';' (statement separator)",
991 Token::Comma => "comma ',' (list separator)",
992 Token::RParen => {
993 "closing parenthesis ')' (no matching opening parenthesis)"
994 }
995 Token::RBracket => "closing bracket ']' (no matching opening bracket)",
996 Token::If => "keyword 'if' (expected in statement context)",
997 Token::For => "keyword 'for' (expected in statement context)",
998 Token::While => "keyword 'while' (expected in statement context)",
999 Token::Function => "keyword 'function' (expected in statement context)",
1000 Token::End => "keyword 'end' (no matching control structure)",
1001 Token::Equal => "equality operator '==' (expected in comparison context)",
1002 Token::Assign => "assignment operator '=' (expected in assignment context)",
1003 Token::Error => "invalid character or symbol",
1004 _ => {
1005 return Err(format!(
1006 "unexpected token '{}' in expression context",
1007 info.lexeme
1008 ))
1009 }
1010 };
1011 Err(format!("unexpected {token_desc} in expression context"))
1012 }
1013 },
1014 None => Err("unexpected end of input, expected expression".into()),
1015 }
1016 }
1017
1018 fn parse_matrix(&mut self) -> Result<Expr, String> {
1019 self.skip_newlines();
1020 let mut rows = Vec::new();
1021 if self.peek_token() == Some(&Token::RBracket) {
1022 return Ok(Expr::Tensor(rows));
1023 }
1024 loop {
1025 self.skip_newlines();
1026 if self.peek_token() == Some(&Token::RBracket) {
1027 break;
1028 }
1029 let mut row = Vec::new();
1030 row.push(self.parse_expr()?);
1032 loop {
1034 if self.consume(&Token::Newline) {
1035 continue;
1036 }
1037 if self.consume(&Token::Comma) {
1038 row.push(self.parse_expr()?);
1039 continue;
1040 }
1041 if matches!(
1043 self.peek_token(),
1044 Some(Token::Semicolon) | Some(Token::RBracket)
1045 ) {
1046 break;
1047 }
1048 match self.peek_token() {
1051 Some(
1052 Token::Ident
1053 | Token::Integer
1054 | Token::Float
1055 | Token::Str
1056 | Token::LParen
1057 | Token::LBracket
1058 | Token::LBrace
1059 | Token::At
1060 | Token::Plus
1061 | Token::Minus
1062 | Token::Colon
1063 | Token::True
1064 | Token::False,
1065 ) => {
1066 row.push(self.parse_expr()?);
1067 }
1068 _ => {
1069 break;
1070 }
1071 }
1072 }
1073 rows.push(row);
1074 if self.consume(&Token::Semicolon) {
1075 self.skip_newlines();
1076 continue;
1077 } else {
1078 break;
1079 }
1080 }
1081 self.skip_newlines();
1082 Ok(Expr::Tensor(rows))
1083 }
1084
1085 fn parse_if(&mut self) -> Result<Stmt, String> {
1086 self.consume(&Token::If);
1087 let cond = self.parse_expr()?;
1088 let then_body =
1089 self.parse_block(|t| matches!(t, Token::Else | Token::ElseIf | Token::End))?;
1090 let mut elseif_blocks = Vec::new();
1091 while self.consume(&Token::ElseIf) {
1092 let c = self.parse_expr()?;
1093 let body =
1094 self.parse_block(|t| matches!(t, Token::Else | Token::ElseIf | Token::End))?;
1095 elseif_blocks.push((c, body));
1096 }
1097 let else_body = if self.consume(&Token::Else) {
1098 Some(self.parse_block(|t| matches!(t, Token::End))?)
1099 } else {
1100 None
1101 };
1102 if !self.consume(&Token::End) {
1103 return Err("expected 'end'".into());
1104 }
1105 Ok(Stmt::If {
1106 cond,
1107 then_body,
1108 elseif_blocks,
1109 else_body,
1110 })
1111 }
1112
1113 fn parse_while(&mut self) -> Result<Stmt, String> {
1114 self.consume(&Token::While);
1115 let cond = self.parse_expr()?;
1116 let body = self.parse_block(|t| matches!(t, Token::End))?;
1117 if !self.consume(&Token::End) {
1118 return Err("expected 'end'".into());
1119 }
1120 Ok(Stmt::While { cond, body })
1121 }
1122
1123 fn parse_for(&mut self) -> Result<Stmt, String> {
1124 self.consume(&Token::For);
1125 let var = self.expect_ident()?;
1126 if !self.consume(&Token::Assign) {
1127 return Err("expected '='".into());
1128 }
1129 let expr = self.parse_expr()?;
1130 let body = self.parse_block(|t| matches!(t, Token::End))?;
1131 if !self.consume(&Token::End) {
1132 return Err("expected 'end'".into());
1133 }
1134 Ok(Stmt::For { var, expr, body })
1135 }
1136
1137 fn parse_function(&mut self) -> Result<Stmt, String> {
1138 self.consume(&Token::Function);
1139 let mut outputs = Vec::new();
1140 if self.consume(&Token::LBracket) {
1141 outputs.push(self.expect_ident_or_tilde()?);
1142 while self.consume(&Token::Comma) {
1143 outputs.push(self.expect_ident_or_tilde()?);
1144 }
1145 if !self.consume(&Token::RBracket) {
1146 return Err("expected ']'".into());
1147 }
1148 if !self.consume(&Token::Assign) {
1149 return Err("expected '='".into());
1150 }
1151 } else if self.peek_token() == Some(&Token::Ident)
1152 && self.peek_token_at(1) == Some(&Token::Assign)
1153 {
1154 outputs.push(self.next().unwrap().lexeme);
1155 self.consume(&Token::Assign);
1156 }
1157 let name = self.expect_ident()?;
1158 if !self.consume(&Token::LParen) {
1159 return Err("expected '('".into());
1160 }
1161 let mut params = Vec::new();
1162 if !self.consume(&Token::RParen) {
1163 params.push(self.expect_ident()?);
1164 while self.consume(&Token::Comma) {
1165 params.push(self.expect_ident()?);
1166 }
1167 if !self.consume(&Token::RParen) {
1168 return Err("expected ')'".into());
1169 }
1170 }
1171
1172 if let Some(idx) = params.iter().position(|p| p == "varargin") {
1175 if idx != params.len() - 1 {
1176 return Err("'varargin' must be the last input parameter".into());
1177 }
1178 if params.iter().filter(|p| p.as_str() == "varargin").count() > 1 {
1179 return Err("'varargin' cannot appear more than once".into());
1180 }
1181 }
1182 if let Some(idx) = outputs.iter().position(|o| o == "varargout") {
1184 if idx != outputs.len() - 1 {
1185 return Err("'varargout' must be the last output parameter".into());
1186 }
1187 if outputs.iter().filter(|o| o.as_str() == "varargout").count() > 1 {
1188 return Err("'varargout' cannot appear more than once".into());
1189 }
1190 }
1191
1192 if self.peek_token() == Some(&Token::Arguments) {
1195 self.pos += 1; loop {
1198 if self.consume(&Token::End) {
1199 break;
1200 }
1201 if self.consume(&Token::Semicolon) || self.consume(&Token::Comma) {
1202 continue;
1203 }
1204 if matches!(self.peek_token(), Some(Token::Ident)) {
1205 let _ = self.expect_ident()?;
1206 continue;
1207 }
1208 if self.peek_token().is_none() {
1210 break;
1211 }
1212 break;
1213 }
1214 }
1215
1216 let body = self.parse_block(|t| matches!(t, Token::End))?;
1217 if !self.consume(&Token::End) {
1218 return Err("expected 'end'".into());
1219 }
1220 Ok(Stmt::Function {
1221 name,
1222 params,
1223 outputs,
1224 body,
1225 })
1226 }
1227
1228 fn parse_block<F>(&mut self, term: F) -> Result<Vec<Stmt>, String>
1229 where
1230 F: Fn(&Token) -> bool,
1231 {
1232 let mut body = Vec::new();
1233 while let Some(tok) = self.peek_token() {
1234 if term(tok) {
1235 break;
1236 }
1237 if self.consume(&Token::Semicolon)
1238 || self.consume(&Token::Comma)
1239 || self.consume(&Token::Newline)
1240 {
1241 continue;
1242 }
1243 let stmt = if self.peek_token() == Some(&Token::LBracket) {
1245 self.try_parse_multi_assign()?
1246 } else {
1247 self.parse_stmt().map_err(|e| e.message)?
1248 };
1249 let is_semicolon_terminated = self.consume(&Token::Semicolon);
1250
1251 let final_stmt = match stmt {
1253 Stmt::ExprStmt(expr, _) => Stmt::ExprStmt(expr, is_semicolon_terminated),
1254 Stmt::Assign(name, expr, _) => Stmt::Assign(name, expr, false),
1255 Stmt::MultiAssign(names, expr, _) => Stmt::MultiAssign(names, expr, false),
1256 Stmt::AssignLValue(lv, expr, _) => Stmt::AssignLValue(lv, expr, false),
1257 other => other,
1258 };
1259 body.push(final_stmt);
1260 }
1261 Ok(body)
1262 }
1263
1264 fn parse_cell(&mut self) -> Result<Expr, String> {
1265 let mut rows = Vec::new();
1266 self.skip_newlines();
1267 if self.peek_token() == Some(&Token::RBrace) {
1268 return Ok(Expr::Cell(rows));
1269 }
1270 loop {
1271 self.skip_newlines();
1272 if self.peek_token() == Some(&Token::RBrace) {
1273 break;
1274 }
1275 let mut row = Vec::new();
1276 row.push(self.parse_expr()?);
1277 while self.consume(&Token::Comma) {
1278 row.push(self.parse_expr()?);
1279 }
1280 rows.push(row);
1281 if self.consume(&Token::Semicolon) {
1282 self.skip_newlines();
1283 continue;
1284 } else {
1285 break;
1286 }
1287 }
1288 self.skip_newlines();
1289 Ok(Expr::Cell(rows))
1290 }
1291
1292 fn parse_switch(&mut self) -> Result<Stmt, String> {
1293 self.consume(&Token::Switch);
1294 let control = self.parse_expr()?;
1295 let mut cases = Vec::new();
1296 let mut otherwise: Option<Vec<Stmt>> = None;
1297 loop {
1298 if self.consume(&Token::Newline) || self.consume(&Token::Semicolon) {
1299 continue;
1300 }
1301 if self.consume(&Token::Case) {
1302 let val = self.parse_expr()?;
1303 let body =
1304 self.parse_block(|t| matches!(t, Token::Case | Token::Otherwise | Token::End))?;
1305 cases.push((val, body));
1306 } else if self.consume(&Token::Otherwise) {
1307 let body = self.parse_block(|t| matches!(t, Token::End))?;
1308 otherwise = Some(body);
1309 } else if self.consume(&Token::Comma) {
1310 continue;
1311 } else {
1312 break;
1313 }
1314 }
1315 if !self.consume(&Token::End) {
1316 return Err("expected 'end' for switch".into());
1317 }
1318 Ok(Stmt::Switch {
1319 expr: control,
1320 cases,
1321 otherwise,
1322 })
1323 }
1324
1325 fn parse_try_catch(&mut self) -> Result<Stmt, String> {
1326 self.consume(&Token::Try);
1327 let try_body = self.parse_block(|t| matches!(t, Token::Catch | Token::End))?;
1328 let (catch_var, catch_body) = if self.consume(&Token::Catch) {
1329 let maybe_ident = if let Some(Token::Ident) = self.peek_token() {
1330 Some(self.expect_ident()?)
1331 } else {
1332 None
1333 };
1334 let body = self.parse_block(|t| matches!(t, Token::End))?;
1335 (maybe_ident, body)
1336 } else {
1337 (None, Vec::new())
1338 };
1339 if !self.consume(&Token::End) {
1340 return Err("expected 'end' after try/catch".into());
1341 }
1342 Ok(Stmt::TryCatch {
1343 try_body,
1344 catch_var,
1345 catch_body,
1346 })
1347 }
1348
1349 fn parse_import(&mut self) -> Result<Stmt, String> {
1350 self.consume(&Token::Import);
1351 let mut path = Vec::new();
1353 path.push(self.expect_ident()?);
1354 let mut wildcard = false;
1355 loop {
1356 if self.consume(&Token::DotStar) {
1357 wildcard = true;
1358 break;
1359 }
1360 if self.consume(&Token::Dot) {
1361 if self.consume(&Token::Star) {
1362 wildcard = true;
1363 break;
1364 } else {
1365 path.push(self.expect_ident()?);
1366 continue;
1367 }
1368 }
1369 break;
1370 }
1371 Ok(Stmt::Import { path, wildcard })
1372 }
1373
1374 fn parse_classdef(&mut self) -> Result<Stmt, String> {
1375 self.consume(&Token::ClassDef);
1376 let name = self.parse_qualified_name()?;
1377 let mut super_class = None;
1378 if self.consume(&Token::Less) {
1379 super_class = Some(self.parse_qualified_name()?);
1380 }
1381 let mut members: Vec<ClassMember> = Vec::new();
1382 loop {
1383 if self.consume(&Token::Semicolon)
1385 || self.consume(&Token::Comma)
1386 || self.consume(&Token::Newline)
1387 {
1388 continue;
1389 }
1390 match self.peek_token() {
1391 Some(Token::Properties) => {
1392 self.pos += 1;
1393 let attrs = self.parse_optional_attr_list();
1394 let props = self.parse_properties_names_block()?;
1395 if !self.consume(&Token::End) {
1396 return Err("expected 'end' after properties".into());
1397 }
1398 members.push(ClassMember::Properties {
1399 attributes: attrs,
1400 names: props,
1401 });
1402 }
1403 Some(Token::Methods) => {
1404 self.pos += 1;
1405 let attrs = self.parse_optional_attr_list();
1406 let body = self.parse_block(|t| matches!(t, Token::End))?;
1407 if !self.consume(&Token::End) {
1408 return Err("expected 'end' after methods".into());
1409 }
1410 members.push(ClassMember::Methods {
1411 attributes: attrs,
1412 body,
1413 });
1414 }
1415 Some(Token::Events) => {
1416 self.pos += 1;
1417 let attrs = self.parse_optional_attr_list();
1418 let names = self.parse_name_block()?;
1419 if !self.consume(&Token::End) {
1420 return Err("expected 'end' after events".into());
1421 }
1422 members.push(ClassMember::Events {
1423 attributes: attrs,
1424 names,
1425 });
1426 }
1427 Some(Token::Enumeration) => {
1428 self.pos += 1;
1429 let attrs = self.parse_optional_attr_list();
1430 let names = self.parse_name_block()?;
1431 if !self.consume(&Token::End) {
1432 return Err("expected 'end' after enumeration".into());
1433 }
1434 members.push(ClassMember::Enumeration {
1435 attributes: attrs,
1436 names,
1437 });
1438 }
1439 Some(Token::Arguments) => {
1440 self.pos += 1;
1441 let attrs = self.parse_optional_attr_list();
1442 let names = self.parse_name_block()?;
1443 if !self.consume(&Token::End) {
1444 return Err("expected 'end' after arguments".into());
1445 }
1446 members.push(ClassMember::Arguments {
1447 attributes: attrs,
1448 names,
1449 });
1450 }
1451 Some(Token::End) => {
1452 self.pos += 1;
1453 break;
1454 }
1455 _ => break,
1456 }
1457 }
1458 Ok(Stmt::ClassDef {
1459 name,
1460 super_class,
1461 members,
1462 })
1463 }
1464
1465 fn parse_name_block(&mut self) -> Result<Vec<String>, String> {
1466 let mut names = Vec::new();
1467 while let Some(tok) = self.peek_token() {
1468 if matches!(tok, Token::End) {
1469 break;
1470 }
1471 if self.consume(&Token::Semicolon)
1472 || self.consume(&Token::Comma)
1473 || self.consume(&Token::Newline)
1474 {
1475 continue;
1476 }
1477 if let Some(Token::Ident) = self.peek_token() {
1478 names.push(self.expect_ident()?);
1479 } else {
1480 break;
1481 }
1482 }
1483 Ok(names)
1484 }
1485
1486 fn parse_properties_names_block(&mut self) -> Result<Vec<String>, String> {
1487 let mut names = Vec::new();
1489 while let Some(tok) = self.peek_token() {
1490 if matches!(tok, Token::End) {
1491 break;
1492 }
1493 if self.consume(&Token::Semicolon)
1494 || self.consume(&Token::Comma)
1495 || self.consume(&Token::Newline)
1496 {
1497 continue;
1498 }
1499 if let Some(Token::Ident) = self.peek_token() {
1500 names.push(self.expect_ident()?);
1501 if self.consume(&Token::Assign) {
1503 let _ = self.parse_expr().map_err(|e| e.message)?;
1505 }
1506 } else {
1507 break;
1508 }
1509 }
1510 Ok(names)
1511 }
1512
1513 fn parse_optional_attr_list(&mut self) -> Vec<Attr> {
1514 let mut attrs: Vec<Attr> = Vec::new();
1516 if !self.consume(&Token::LParen) {
1517 return attrs;
1518 }
1519 loop {
1520 if self.consume(&Token::RParen) {
1521 break;
1522 }
1523 match self.peek_token() {
1524 Some(Token::Ident) => {
1525 let name = self.expect_ident().unwrap_or_else(|_| "".to_string());
1526 let mut value: Option<String> = None;
1527 if self.consume(&Token::Assign) {
1528 if let Some(tok) = self.next() {
1530 value = Some(tok.lexeme);
1531 }
1532 }
1533 attrs.push(Attr { name, value });
1534 let _ = self.consume(&Token::Comma);
1535 }
1536 Some(Token::Comma) => {
1537 self.pos += 1;
1538 }
1539 Some(Token::RParen) => {
1540 self.pos += 1;
1541 break;
1542 }
1543 Some(_) => {
1544 self.pos += 1;
1545 }
1546 None => {
1547 break;
1548 }
1549 }
1550 }
1551 attrs
1552 }
1553
1554 fn parse_global(&mut self) -> Result<Stmt, String> {
1555 self.consume(&Token::Global);
1556 let mut names = Vec::new();
1557 names.push(self.expect_ident()?);
1558 loop {
1559 if self.consume(&Token::Comma) {
1560 names.push(self.expect_ident()?);
1561 continue;
1562 }
1563 if self.peek_token() == Some(&Token::Ident) {
1564 names.push(self.expect_ident()?);
1565 continue;
1566 }
1567 break;
1568 }
1569 Ok(Stmt::Global(names))
1570 }
1571
1572 fn parse_persistent(&mut self) -> Result<Stmt, String> {
1573 self.consume(&Token::Persistent);
1574 let mut names = Vec::new();
1575 names.push(self.expect_ident()?);
1576 loop {
1577 if self.consume(&Token::Comma) {
1578 names.push(self.expect_ident()?);
1579 continue;
1580 }
1581 if self.peek_token() == Some(&Token::Ident) {
1582 names.push(self.expect_ident()?);
1583 continue;
1584 }
1585 break;
1586 }
1587 Ok(Stmt::Persistent(names))
1588 }
1589
1590 fn try_parse_multi_assign(&mut self) -> Result<Stmt, String> {
1591 if !self.consume(&Token::LBracket) {
1592 return Err("not a multi-assign".into());
1593 }
1594 let mut names = Vec::new();
1595 names.push(self.expect_ident_or_tilde()?);
1596 while self.consume(&Token::Comma) {
1597 names.push(self.expect_ident_or_tilde()?);
1598 }
1599 if !self.consume(&Token::RBracket) {
1600 return Err("expected ']'".into());
1601 }
1602 if !self.consume(&Token::Assign) {
1603 return Err("expected '='".into());
1604 }
1605 let rhs = self.parse_expr().map_err(|e| e.message)?;
1606 Ok(Stmt::MultiAssign(names, rhs, false))
1607 }
1608
1609 fn parse_qualified_name(&mut self) -> Result<String, String> {
1610 let mut parts = Vec::new();
1611 parts.push(self.expect_ident()?);
1612 while self.consume(&Token::Dot) {
1613 parts.push(self.expect_ident()?);
1614 }
1615 Ok(parts.join("."))
1616 }
1617
1618 fn expect_ident(&mut self) -> Result<String, String> {
1619 match self.next() {
1620 Some(TokenInfo {
1621 token: Token::Ident,
1622 lexeme,
1623 ..
1624 }) => Ok(lexeme),
1625 _ => Err("expected identifier".into()),
1626 }
1627 }
1628
1629 fn expect_ident_or_tilde(&mut self) -> Result<String, String> {
1630 match self.next() {
1631 Some(TokenInfo {
1632 token: Token::Ident,
1633 lexeme,
1634 ..
1635 }) => Ok(lexeme),
1636 Some(TokenInfo {
1637 token: Token::Tilde,
1638 ..
1639 }) => Ok("~".to_string()),
1640 _ => Err("expected identifier or '~'".into()),
1641 }
1642 }
1643
1644 fn peek_token(&self) -> Option<&Token> {
1645 self.tokens.get(self.pos).map(|t| &t.token)
1646 }
1647
1648 fn peek_token_at(&self, offset: usize) -> Option<&Token> {
1649 self.tokens.get(self.pos + offset).map(|t| &t.token)
1650 }
1651
1652 fn next(&mut self) -> Option<TokenInfo> {
1653 if self.pos < self.tokens.len() {
1654 let info = self.tokens[self.pos].clone();
1655 self.pos += 1;
1656 Some(info)
1657 } else {
1658 None
1659 }
1660 }
1661
1662 fn consume(&mut self, t: &Token) -> bool {
1663 if self.peek_token() == Some(t) {
1664 self.pos += 1;
1665 true
1666 } else {
1667 false
1668 }
1669 }
1670}