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