1use runmat_lexer::Token;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5#[serde(rename_all = "snake_case")]
6pub enum CompatMode {
7 #[default]
8 Matlab,
9 Strict,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ParserOptions {
14 #[serde(default)]
15 pub compat_mode: CompatMode,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
19pub struct Span {
20 pub start: usize,
21 pub end: usize,
22}
23
24impl Default for ParserOptions {
25 fn default() -> Self {
26 Self {
27 compat_mode: CompatMode::Matlab,
28 }
29 }
30}
31
32impl ParserOptions {
33 pub fn new(compat_mode: CompatMode) -> Self {
34 Self { compat_mode }
35 }
36}
37
38#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
39pub enum Expr {
40 Number(String, Span),
41 String(String, Span),
42 Ident(String, Span),
43 EndKeyword(Span), Unary(UnOp, Box<Expr>, Span),
45 Binary(Box<Expr>, BinOp, Box<Expr>, Span),
46 Tensor(Vec<Vec<Expr>>, Span),
47 Cell(Vec<Vec<Expr>>, Span),
48 Index(Box<Expr>, Vec<Expr>, Span),
49 IndexCell(Box<Expr>, Vec<Expr>, Span),
50 Range(Box<Expr>, Option<Box<Expr>>, Box<Expr>, Span),
51 Colon(Span),
52 FuncCall(String, Vec<Expr>, Span),
53 Member(Box<Expr>, String, Span),
54 MemberDynamic(Box<Expr>, Box<Expr>, Span),
56 DottedInvoke(Box<Expr>, String, Vec<Expr>, Span),
57 MethodCall(Box<Expr>, String, Vec<Expr>, Span),
58 AnonFunc {
59 params: Vec<String>,
60 body: Box<Expr>,
61 span: Span,
62 },
63 FuncHandle(String, Span),
64 MetaClass(String, Span),
65}
66
67impl Expr {
68 pub fn span(&self) -> Span {
69 match self {
70 Expr::Number(_, span)
71 | Expr::String(_, span)
72 | Expr::Ident(_, span)
73 | Expr::EndKeyword(span)
74 | Expr::Unary(_, _, span)
75 | Expr::Binary(_, _, _, span)
76 | Expr::Tensor(_, span)
77 | Expr::Cell(_, span)
78 | Expr::Index(_, _, span)
79 | Expr::IndexCell(_, _, span)
80 | Expr::Range(_, _, _, span)
81 | Expr::Colon(span)
82 | Expr::FuncCall(_, _, span)
83 | Expr::Member(_, _, span)
84 | Expr::MemberDynamic(_, _, span)
85 | Expr::DottedInvoke(_, _, _, span)
86 | Expr::MethodCall(_, _, _, span)
87 | Expr::FuncHandle(_, span)
88 | Expr::MetaClass(_, span) => *span,
89 Expr::AnonFunc { span, .. } => *span,
90 }
91 }
92
93 pub fn with_span(self, span: Span) -> Expr {
94 match self {
95 Expr::Number(value, _) => Expr::Number(value, span),
96 Expr::String(value, _) => Expr::String(value, span),
97 Expr::Ident(value, _) => Expr::Ident(value, span),
98 Expr::EndKeyword(_) => Expr::EndKeyword(span),
99 Expr::Unary(op, expr, _) => Expr::Unary(op, expr, span),
100 Expr::Binary(lhs, op, rhs, _) => Expr::Binary(lhs, op, rhs, span),
101 Expr::Tensor(rows, _) => Expr::Tensor(rows, span),
102 Expr::Cell(rows, _) => Expr::Cell(rows, span),
103 Expr::Index(base, indices, _) => Expr::Index(base, indices, span),
104 Expr::IndexCell(base, indices, _) => Expr::IndexCell(base, indices, span),
105 Expr::Range(start, step, end, _) => Expr::Range(start, step, end, span),
106 Expr::Colon(_) => Expr::Colon(span),
107 Expr::FuncCall(name, args, _) => Expr::FuncCall(name, args, span),
108 Expr::Member(base, name, _) => Expr::Member(base, name, span),
109 Expr::MemberDynamic(base, name, _) => Expr::MemberDynamic(base, name, span),
110 Expr::DottedInvoke(base, name, args, _) => Expr::DottedInvoke(base, name, args, span),
111 Expr::MethodCall(base, name, args, _) => Expr::MethodCall(base, name, args, span),
112 Expr::AnonFunc { params, body, .. } => Expr::AnonFunc { params, body, span },
113 Expr::FuncHandle(name, _) => Expr::FuncHandle(name, span),
114 Expr::MetaClass(name, _) => Expr::MetaClass(name, span),
115 }
116 }
117}
118
119#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
120pub enum BinOp {
121 Add,
122 Sub,
123 Mul,
124 RightDiv,
125 Pow,
126 LeftDiv,
127 Colon,
128 ElemMul, ElemDiv, ElemPow, ElemLeftDiv, AndAnd, OrOr, BitAnd, BitOr, Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual, }
146
147#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
148pub enum UnOp {
149 Plus,
150 Minus,
151 Transpose,
152 NonConjugateTranspose,
153 Not, }
155
156#[derive(Debug, PartialEq)]
157pub enum Stmt {
158 ExprStmt(Expr, bool, Span), Assign(String, Expr, bool, Span), MultiAssign(Vec<String>, Expr, bool, Span),
161 AssignLValue(LValue, Expr, bool, Span),
162 If {
163 cond: Expr,
164 then_body: Vec<Stmt>,
165 elseif_blocks: Vec<(Expr, Vec<Stmt>)>,
166 else_body: Option<Vec<Stmt>>,
167 span: Span,
168 },
169 While {
170 cond: Expr,
171 body: Vec<Stmt>,
172 span: Span,
173 },
174 For {
175 var: String,
176 expr: Expr,
177 body: Vec<Stmt>,
178 span: Span,
179 },
180 Switch {
181 expr: Expr,
182 cases: Vec<(Expr, Vec<Stmt>)>,
183 otherwise: Option<Vec<Stmt>>,
184 span: Span,
185 },
186 TryCatch {
187 try_body: Vec<Stmt>,
188 catch_var: Option<String>,
189 catch_body: Vec<Stmt>,
190 span: Span,
191 },
192 Global(Vec<String>, Span),
193 Persistent(Vec<String>, Span),
194 Break(Span),
195 Continue(Span),
196 Return(Span),
197 Function {
198 name: String,
199 params: Vec<String>,
200 outputs: Vec<String>,
201 body: Vec<Stmt>,
202 span: Span,
203 },
204 Import {
205 path: Vec<String>,
206 wildcard: bool,
207 span: Span,
208 },
209 ClassDef {
210 name: String,
211 super_class: Option<String>,
212 members: Vec<ClassMember>,
213 span: Span,
214 },
215}
216
217impl Stmt {
218 pub fn span(&self) -> Span {
219 match self {
220 Stmt::ExprStmt(_, _, span)
221 | Stmt::Assign(_, _, _, span)
222 | Stmt::MultiAssign(_, _, _, span)
223 | Stmt::AssignLValue(_, _, _, span)
224 | Stmt::Global(_, span)
225 | Stmt::Persistent(_, span)
226 | Stmt::Break(span)
227 | Stmt::Continue(span)
228 | Stmt::Return(span) => *span,
229 Stmt::If { span, .. }
230 | Stmt::While { span, .. }
231 | Stmt::For { span, .. }
232 | Stmt::Switch { span, .. }
233 | Stmt::TryCatch { span, .. }
234 | Stmt::Function { span, .. }
235 | Stmt::Import { span, .. }
236 | Stmt::ClassDef { span, .. } => *span,
237 }
238 }
239}
240
241#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
242pub enum LValue {
243 Var(String),
244 Member(Box<Expr>, String),
245 MemberDynamic(Box<Expr>, Box<Expr>),
246 Index(Box<Expr>, Vec<Expr>),
247 IndexCell(Box<Expr>, Vec<Expr>),
248}
249
250#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
251pub struct Attr {
252 pub name: String,
253 pub value: Option<String>,
254}
255
256#[derive(Debug, PartialEq)]
257pub enum ClassMember {
258 Properties {
259 attributes: Vec<Attr>,
260 names: Vec<String>,
261 },
262 Methods {
263 attributes: Vec<Attr>,
264 body: Vec<Stmt>,
265 },
266 Events {
267 attributes: Vec<Attr>,
268 names: Vec<String>,
269 },
270 Enumeration {
271 attributes: Vec<Attr>,
272 names: Vec<String>,
273 },
274 Arguments {
275 attributes: Vec<Attr>,
276 names: Vec<String>,
277 },
278}
279
280#[derive(Debug, PartialEq)]
281pub struct Program {
282 pub body: Vec<Stmt>,
283}
284
285#[derive(Clone)]
286struct TokenInfo {
287 token: Token,
288 lexeme: String,
289 position: usize,
290 end: usize,
291}
292
293#[derive(Debug)]
294pub struct SyntaxError {
295 pub message: String,
296 pub position: usize,
297 pub found_token: Option<String>,
298 pub expected: Option<String>,
299}
300
301pub fn parse(input: &str) -> Result<Program, SyntaxError> {
302 parse_with_options(input, ParserOptions::default())
303}
304
305pub fn parse_with_options(input: &str, options: ParserOptions) -> Result<Program, SyntaxError> {
306 use runmat_lexer::tokenize_detailed;
307
308 let toks = tokenize_detailed(input);
309 let mut tokens = Vec::new();
310
311 for t in toks {
312 if matches!(t.token, Token::Error) {
313 return Err(SyntaxError {
314 message: format!("Invalid token: '{}'", t.lexeme),
315 position: t.start,
316 found_token: Some(t.lexeme),
317 expected: None,
318 });
319 }
320 if matches!(t.token, Token::Ellipsis | Token::Section) {
322 continue;
323 }
324 tokens.push(TokenInfo {
325 token: t.token,
326 lexeme: t.lexeme,
327 position: t.start,
328 end: t.end,
329 });
330 }
331
332 let mut parser = Parser {
333 tokens,
334 pos: 0,
335 input: input.to_string(),
336 options,
337 in_matrix_expr: false,
338 };
339 parser.parse_program()
340}
341
342impl std::fmt::Display for SyntaxError {
343 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344 write!(
345 f,
346 "Syntax error at position {}: {}",
347 self.position, self.message
348 )?;
349 if let Some(found) = &self.found_token {
350 write!(f, " (found: '{found}')")?;
351 }
352 if let Some(expected) = &self.expected {
353 write!(f, " (expected: {expected})")?;
354 }
355 Ok(())
356 }
357}
358
359impl std::error::Error for SyntaxError {}
360
361impl From<String> for SyntaxError {
362 fn from(value: String) -> Self {
363 SyntaxError {
364 message: value,
365 position: 0,
366 found_token: None,
367 expected: None,
368 }
369 }
370}
371
372impl From<SyntaxError> for String {
373 fn from(error: SyntaxError) -> Self {
374 error.to_string()
375 }
376}
377
378struct Parser {
379 tokens: Vec<TokenInfo>,
380 pos: usize,
381 input: String,
382 options: ParserOptions,
383 in_matrix_expr: bool,
384}
385
386#[derive(Clone, Copy)]
387struct CommandVerb {
388 name: &'static str,
389 arg_kind: CommandArgKind,
390}
391
392#[derive(Clone, Copy)]
393enum CommandArgKind {
394 Keyword {
395 allowed: &'static [&'static str],
396 optional: bool,
397 },
398 Any,
399 StringifyWords,
400}
401
402const COMMAND_VERBS: &[CommandVerb] = &[
403 CommandVerb {
404 name: "hold",
405 arg_kind: CommandArgKind::Keyword {
406 allowed: &["on", "off", "all", "reset"],
407 optional: false,
408 },
409 },
410 CommandVerb {
411 name: "grid",
412 arg_kind: CommandArgKind::Keyword {
413 allowed: &["on", "off"],
414 optional: false,
415 },
416 },
417 CommandVerb {
418 name: "box",
419 arg_kind: CommandArgKind::Keyword {
420 allowed: &["on", "off"],
421 optional: false,
422 },
423 },
424 CommandVerb {
425 name: "axis",
426 arg_kind: CommandArgKind::Keyword {
427 allowed: &["auto", "manual", "tight", "equal", "ij", "xy"],
428 optional: false,
429 },
430 },
431 CommandVerb {
432 name: "shading",
433 arg_kind: CommandArgKind::Keyword {
434 allowed: &["flat", "interp", "faceted"],
435 optional: false,
436 },
437 },
438 CommandVerb {
439 name: "colormap",
440 arg_kind: CommandArgKind::Keyword {
441 allowed: &[
442 "parula", "jet", "hsv", "hot", "cool", "spring", "summer", "autumn", "winter",
443 "gray", "bone", "copper", "pink",
444 ],
445 optional: false,
446 },
447 },
448 CommandVerb {
449 name: "colorbar",
450 arg_kind: CommandArgKind::Keyword {
451 allowed: &["on", "off"],
452 optional: true,
453 },
454 },
455 CommandVerb {
456 name: "figure",
457 arg_kind: CommandArgKind::Any,
458 },
459 CommandVerb {
460 name: "subplot",
461 arg_kind: CommandArgKind::Any,
462 },
463 CommandVerb {
464 name: "clf",
465 arg_kind: CommandArgKind::Any,
466 },
467 CommandVerb {
468 name: "cla",
469 arg_kind: CommandArgKind::Any,
470 },
471 CommandVerb {
472 name: "close",
473 arg_kind: CommandArgKind::StringifyWords,
474 },
475 CommandVerb {
476 name: "clear",
477 arg_kind: CommandArgKind::StringifyWords,
478 },
479];
480
481impl Parser {
482 fn skip_newlines(&mut self) {
483 while self.consume(&Token::Newline) {}
484 }
485
486 fn tokens_adjacent(&self, left: usize, right: usize) -> bool {
487 match (self.tokens.get(left), self.tokens.get(right)) {
488 (Some(a), Some(b)) => a.end == b.position,
489 _ => false,
490 }
491 }
492
493 fn span_from(&self, start: usize, end: usize) -> Span {
494 Span { start, end }
495 }
496
497 fn span_between(&self, start: Span, end: Span) -> Span {
498 Span {
499 start: start.start,
500 end: end.end,
501 }
502 }
503
504 fn last_token_end(&self) -> usize {
505 self.tokens
506 .get(self.pos.saturating_sub(1))
507 .map(|t| t.end)
508 .unwrap_or(self.input.len())
509 }
510
511 fn make_binary(&self, left: Expr, op: BinOp, right: Expr) -> Expr {
512 let span = self.span_between(left.span(), right.span());
513 Expr::Binary(Box::new(left), op, Box::new(right), span)
514 }
515
516 fn make_unary(&self, op: UnOp, operand: Expr, op_start: usize) -> Expr {
517 let span = self.span_from(op_start, operand.span().end);
518 Expr::Unary(op, Box::new(operand), span)
519 }
520
521 fn is_simple_assignment_ahead(&self) -> bool {
522 self.peek_token() == Some(&Token::Ident) && self.peek_token_at(1) == Some(&Token::Assign)
524 }
525 fn parse_program(&mut self) -> Result<Program, SyntaxError> {
526 let mut body = Vec::new();
527 while self.pos < self.tokens.len() {
528 if self.consume(&Token::Semicolon)
529 || self.consume(&Token::Comma)
530 || self.consume(&Token::Newline)
531 {
532 continue;
533 }
534 body.push(self.parse_stmt_with_semicolon()?);
535 }
536 Ok(Program { body })
537 }
538
539 fn error(&self, message: &str) -> SyntaxError {
540 SyntaxError {
541 message: message.to_string(),
542 position: self.current_position(),
543 found_token: self.peek().map(|t| t.lexeme.clone()),
544 expected: None,
545 }
546 }
547
548 fn error_with_expected(&self, message: &str, expected: &str) -> SyntaxError {
549 SyntaxError {
550 message: message.to_string(),
551 position: self.current_position(),
552 found_token: self.peek().map(|t| t.lexeme.clone()),
553 expected: Some(expected.to_string()),
554 }
555 }
556
557 fn parse_stmt_with_semicolon(&mut self) -> Result<Stmt, SyntaxError> {
558 let stmt = self.parse_stmt()?;
559 let is_semicolon_terminated = self.consume(&Token::Semicolon);
560
561 match stmt {
564 Stmt::ExprStmt(expr, _, span) => {
565 Ok(Stmt::ExprStmt(expr, is_semicolon_terminated, span))
566 }
567 Stmt::Assign(name, expr, _, span) => {
568 Ok(Stmt::Assign(name, expr, is_semicolon_terminated, span))
569 }
570 Stmt::MultiAssign(names, expr, _, span) => Ok(Stmt::MultiAssign(
571 names,
572 expr,
573 is_semicolon_terminated,
574 span,
575 )),
576 Stmt::AssignLValue(lv, expr, _, span) => {
577 Ok(Stmt::AssignLValue(lv, expr, is_semicolon_terminated, span))
578 }
579 other => Ok(other),
580 }
581 }
582
583 fn parse_stmt(&mut self) -> Result<Stmt, SyntaxError> {
584 match self.peek_token() {
585 Some(Token::If) => self.parse_if().map_err(|e| e.into()),
586 Some(Token::For) => self.parse_for().map_err(|e| e.into()),
587 Some(Token::While) => self.parse_while().map_err(|e| e.into()),
588 Some(Token::Switch) => self.parse_switch().map_err(|e| e.into()),
589 Some(Token::Try) => self.parse_try_catch().map_err(|e| e.into()),
590 Some(Token::Import) => self.parse_import().map_err(|e| e.into()),
591 Some(Token::ClassDef) => self.parse_classdef().map_err(|e| e.into()),
592 Some(Token::Global) => self.parse_global().map_err(|e| e.into()),
593 Some(Token::Persistent) => self.parse_persistent().map_err(|e| e.into()),
594 Some(Token::Break) => {
595 let token = &self.tokens[self.pos];
596 self.pos += 1;
597 Ok(Stmt::Break(self.span_from(token.position, token.end)))
598 }
599 Some(Token::Continue) => {
600 let token = &self.tokens[self.pos];
601 self.pos += 1;
602 Ok(Stmt::Continue(self.span_from(token.position, token.end)))
603 }
604 Some(Token::Return) => {
605 let token = &self.tokens[self.pos];
606 self.pos += 1;
607 Ok(Stmt::Return(self.span_from(token.position, token.end)))
608 }
609 Some(Token::Function) => self.parse_function().map_err(|e| e.into()),
610 Some(Token::LBracket) => {
612 if matches!(self.peek_token_at(1), Some(Token::Ident | Token::Tilde)) {
613 match self.try_parse_multi_assign() {
614 Ok(stmt) => Ok(stmt),
615 Err(msg) => Err(self.error(&msg)),
616 }
617 } else {
618 let expr = self.parse_expr()?;
619 let span = expr.span();
620 Ok(Stmt::ExprStmt(expr, false, span))
621 }
622 }
623 _ => {
624 if self.peek_token() == Some(&Token::Ident)
625 && self.peek_token_at(1) == Some(&Token::Assign)
626 {
627 let name_token = self
628 .next()
629 .ok_or_else(|| self.error("expected identifier"))?;
630 if !self.consume(&Token::Assign) {
631 return Err(self.error_with_expected("expected assignment operator", "'='"));
632 }
633 let expr = self.parse_expr()?;
634 let span = self.span_from(name_token.position, expr.span().end);
635 Ok(Stmt::Assign(name_token.lexeme, expr, false, span))
636 } else if self.is_simple_assignment_ahead() {
637 let name = self.expect_ident().map_err(|e| self.error(&e))?;
639 let start = self.tokens[self.pos.saturating_sub(1)].position;
640 if !self.consume(&Token::Assign) {
641 return Err(self.error_with_expected("expected assignment operator", "'='"));
642 }
643 let expr = self.parse_expr()?;
644 let span = self.span_from(start, expr.span().end);
645 Ok(Stmt::Assign(name, expr, false, span))
646 } else if self.peek_token() == Some(&Token::Ident) {
647 if let Some(lv) = self.try_parse_lvalue_assign()? {
649 return Ok(lv);
650 }
651 if self.can_start_command_form() {
654 if self.options.compat_mode == CompatMode::Strict {
655 return Err(self.error(
656 "Command syntax is disabled in strict compatibility mode; call functions with parentheses.",
657 ));
658 }
659 let name_token = self.next().unwrap();
660 let mut args = self.parse_command_args();
661 if let Some(command) = self.lookup_command(&name_token.lexeme) {
662 self.normalize_command_args(command, &mut args[..])?;
663 }
664 let end = self.last_token_end();
665 let span = self.span_from(name_token.position, end);
666 Ok(Stmt::ExprStmt(
667 Expr::FuncCall(name_token.lexeme, args, span),
668 false,
669 span,
670 ))
671 } else {
672 if matches!(self.peek_token_at(1), Some(Token::Ident))
675 && matches!(
676 self.peek_token_at(2),
677 Some(
678 Token::LParen
679 | Token::Dot
680 | Token::LBracket
681 | Token::LBrace
682 | Token::Transpose
683 )
684 )
685 {
686 return Err(self.error(
687 "Unexpected adjacency: interpret as function call? Use parentheses (e.g., foo(b(1))).",
688 ));
689 }
690 let expr = self.parse_expr()?;
691 let span = expr.span();
692 Ok(Stmt::ExprStmt(expr, false, span))
693 }
694 } else {
695 let expr = self.parse_expr()?;
696 let span = expr.span();
697 Ok(Stmt::ExprStmt(expr, false, span))
698 }
699 }
700 }
701 }
702
703 fn can_start_command_form(&self) -> bool {
704 let Some(current) = self.tokens.get(self.pos) else {
706 return false;
707 };
708 let verb = current.lexeme.as_str();
709 let command = self.lookup_command(verb);
710 let zero_arg_allowed = matches!(
711 command,
712 Some(CommandVerb {
713 arg_kind: CommandArgKind::Any,
714 ..
715 })
716 ) || matches!(
717 command,
718 Some(CommandVerb {
719 arg_kind: CommandArgKind::Keyword { optional: true, .. },
720 ..
721 })
722 );
723
724 let mut i = 1;
725 let mut saw_arg = false;
726 while matches!(
727 self.peek_token_at(i),
728 Some(Token::Newline | Token::Ellipsis)
729 ) {
730 i += 1;
731 }
732 if !matches!(
734 self.peek_token_at(i),
735 Some(Token::Ident | Token::Integer | Token::Float | Token::Str | Token::End)
736 ) {
737 if !zero_arg_allowed {
738 return false;
739 }
740 } else {
741 saw_arg = true;
742 }
743 loop {
745 match self.peek_token_at(i) {
746 Some(Token::Ident | Token::Integer | Token::Float | Token::Str | Token::End) => {
747 saw_arg = true;
748 i += 1;
749 }
750 Some(Token::Newline | Token::Ellipsis) => {
751 i += 1;
752 }
753 _ => break,
754 }
755 }
756 if !saw_arg && !zero_arg_allowed {
757 return false;
758 }
759 match self.peek_token_at(i) {
761 Some(Token::LParen)
762 | Some(Token::Dot)
763 | Some(Token::LBracket)
764 | Some(Token::LBrace)
765 | Some(Token::Transpose) => false,
766 Some(Token::Assign) => false,
768 None | Some(Token::Semicolon) | Some(Token::Comma) | Some(Token::Newline) => true,
770 _ => true,
772 }
773 }
774
775 fn parse_command_args(&mut self) -> Vec<Expr> {
776 let mut args = Vec::new();
777 loop {
778 if self.consume(&Token::Newline) {
779 continue;
780 }
781 if self.consume(&Token::Ellipsis) {
782 continue;
783 }
784 match self.peek_token() {
785 Some(Token::Ident) => {
786 let token = self.next().unwrap();
787 let span = self.span_from(token.position, token.end);
788 args.push(Expr::Ident(token.lexeme, span));
789 }
790 Some(Token::End) => {
792 let token = &self.tokens[self.pos];
793 self.pos += 1;
794 let span = self.span_from(token.position, token.end);
795 args.push(Expr::Ident("end".to_string(), span));
796 }
797 Some(Token::Integer) | Some(Token::Float) => {
798 let token = self.next().unwrap();
799 let span = self.span_from(token.position, token.end);
800 args.push(Expr::Number(token.lexeme, span));
801 }
802 Some(Token::Str) => {
803 let token = self.next().unwrap();
804 let span = self.span_from(token.position, token.end);
805 args.push(Expr::String(token.lexeme, span));
806 }
807 Some(Token::Slash)
809 | Some(Token::Star)
810 | Some(Token::Backslash)
811 | Some(Token::Plus)
812 | Some(Token::Minus)
813 | Some(Token::LParen)
814 | Some(Token::Dot)
815 | Some(Token::LBracket)
816 | Some(Token::LBrace)
817 | Some(Token::Transpose) => break,
818 _ => break,
819 }
820 }
821 args
822 }
823
824 fn lookup_command(&self, name: &str) -> Option<&'static CommandVerb> {
825 COMMAND_VERBS
826 .iter()
827 .find(|cmd| cmd.name.eq_ignore_ascii_case(name))
828 }
829
830 fn normalize_command_args(
831 &self,
832 command: &CommandVerb,
833 args: &mut [Expr],
834 ) -> Result<(), SyntaxError> {
835 match command.arg_kind {
836 CommandArgKind::Keyword { allowed, optional } => {
837 if args.is_empty() {
838 if optional {
839 return Ok(());
840 }
841 return Err(self.error(&format!(
842 "'{}' command syntax requires an argument",
843 command.name
844 )));
845 }
846 if args.len() > 1 {
847 return Err(self.error(&format!(
848 "'{}' command syntax accepts only one argument",
849 command.name
850 )));
851 }
852 let keyword = extract_keyword(&args[0]).ok_or_else(|| {
853 self.error(&format!(
854 "'{}' command syntax expects a keyword argument",
855 command.name
856 ))
857 })?;
858 if allowed
859 .iter()
860 .any(|candidate| candidate.eq_ignore_ascii_case(&keyword))
861 {
862 let span = args[0].span();
863 args[0] = Expr::String(format!("\"{}\"", keyword), span);
864 } else {
865 return Err(self.error(&format!(
866 "'{}' command syntax does not support '{}'",
867 command.name, keyword
868 )));
869 }
870 }
871 CommandArgKind::Any => {
872 }
874 CommandArgKind::StringifyWords => {
875 for arg in args {
876 let span = arg.span();
877 match arg {
878 Expr::Ident(word, _) => {
879 *arg = Expr::String(format!("\"{}\"", word), span);
880 }
881 Expr::EndKeyword(_) => {
882 *arg = Expr::String("\"end\"".to_string(), span);
883 }
884 _ => {}
885 }
886 }
887 }
888 }
889 Ok(())
890 }
891
892 fn try_parse_lvalue_assign(&mut self) -> Result<Option<Stmt>, SyntaxError> {
893 let save = self.pos;
894 let lvalue = if self.peek_token() == Some(&Token::Ident) {
896 let base_token = self.next().unwrap();
898 let base_span = self.span_from(base_token.position, base_token.end);
899 let mut base = Expr::Ident(base_token.lexeme, base_span);
900 loop {
901 if self.consume(&Token::LParen) {
902 let mut args = Vec::new();
903 if !self.consume(&Token::RParen) {
904 args.push(self.parse_expr()?);
905 while self.consume(&Token::Comma) {
906 args.push(self.parse_expr()?);
907 }
908 if !self.consume(&Token::RParen) {
909 return Err(self.error_with_expected("expected ')' after indices", ")"));
910 }
911 }
912 let end = self.last_token_end();
913 let span = self.span_from(base.span().start, end);
914 base = Expr::Index(Box::new(base), args, span);
915 } else if self.consume(&Token::LBracket) {
916 let mut idxs = Vec::new();
917 idxs.push(self.parse_expr()?);
918 while self.consume(&Token::Comma) {
919 idxs.push(self.parse_expr()?);
920 }
921 if !self.consume(&Token::RBracket) {
922 return Err(self.error_with_expected("expected ']'", "]"));
923 }
924 let end = self.last_token_end();
925 let span = self.span_from(base.span().start, end);
926 base = Expr::Index(Box::new(base), idxs, span);
927 } else if self.consume(&Token::LBrace) {
928 let mut idxs = Vec::new();
929 idxs.push(self.parse_expr()?);
930 while self.consume(&Token::Comma) {
931 idxs.push(self.parse_expr()?);
932 }
933 if !self.consume(&Token::RBrace) {
934 return Err(self.error_with_expected("expected '}'", "}"));
935 }
936 let end = self.last_token_end();
937 let span = self.span_from(base.span().start, end);
938 base = Expr::IndexCell(Box::new(base), idxs, span);
939 } else if self.peek_token() == Some(&Token::Dot) {
940 if self.peek_token_at(1) == Some(&Token::Transpose) {
942 break;
943 }
944 if self.peek_token_at(1) == Some(&Token::Plus)
946 || self.peek_token_at(1) == Some(&Token::Minus)
947 {
948 break;
949 }
950 self.pos += 1; if self.consume(&Token::LParen) {
954 let name_expr = self.parse_expr()?;
955 if !self.consume(&Token::RParen) {
956 return Err(self.error_with_expected(
957 "expected ')' after dynamic field expression",
958 ")",
959 ));
960 }
961 let end = self.last_token_end();
962 let span = self.span_from(base.span().start, end);
963 base = Expr::MemberDynamic(Box::new(base), Box::new(name_expr), span);
964 } else {
965 let name = self.expect_ident()?;
966 let end = self.last_token_end();
967 let span = self.span_from(base.span().start, end);
968 base = Expr::Member(Box::new(base), name, span);
969 }
970 } else {
971 break;
972 }
973 }
974 base
975 } else {
976 self.pos = save;
977 return Ok(None);
978 };
979 if !self.consume(&Token::Assign) {
980 self.pos = save;
981 return Ok(None);
982 }
983 let rhs = self.parse_expr()?;
984 let stmt_span = self.span_between(lvalue.span(), rhs.span());
985 let stmt = match lvalue {
986 Expr::Member(b, name, _) => {
987 Stmt::AssignLValue(LValue::Member(b, name), rhs, false, stmt_span)
988 }
989 Expr::MemberDynamic(b, n, _) => {
990 Stmt::AssignLValue(LValue::MemberDynamic(b, n), rhs, false, stmt_span)
991 }
992 Expr::Index(b, idxs, _) => {
993 Stmt::AssignLValue(LValue::Index(b, idxs), rhs, false, stmt_span)
994 }
995 Expr::IndexCell(b, idxs, _) => {
996 Stmt::AssignLValue(LValue::IndexCell(b, idxs), rhs, false, stmt_span)
997 }
998 Expr::Ident(v, _) => Stmt::Assign(v, rhs, false, stmt_span),
999 _ => {
1000 self.pos = save;
1001 return Ok(None);
1002 }
1003 };
1004 Ok(Some(stmt))
1005 }
1006
1007 fn parse_expr(&mut self) -> Result<Expr, SyntaxError> {
1008 self.parse_logical_or()
1009 }
1010
1011 fn parse_logical_or(&mut self) -> Result<Expr, SyntaxError> {
1012 let mut node = self.parse_logical_and()?;
1013 while self.consume(&Token::OrOr) {
1014 let rhs = self.parse_logical_and()?;
1015 node = self.make_binary(node, BinOp::OrOr, rhs);
1016 }
1017 Ok(node)
1018 }
1019
1020 fn parse_logical_and(&mut self) -> Result<Expr, SyntaxError> {
1021 let mut node = self.parse_bitwise_or()?;
1022 while self.consume(&Token::AndAnd) {
1023 let rhs = self.parse_bitwise_or()?;
1024 node = self.make_binary(node, BinOp::AndAnd, rhs);
1025 }
1026 Ok(node)
1027 }
1028
1029 fn parse_bitwise_or(&mut self) -> Result<Expr, SyntaxError> {
1030 let mut node = self.parse_bitwise_and()?;
1031 while self.consume(&Token::Or) {
1032 let rhs = self.parse_bitwise_and()?;
1033 node = self.make_binary(node, BinOp::BitOr, rhs);
1034 }
1035 Ok(node)
1036 }
1037
1038 fn parse_bitwise_and(&mut self) -> Result<Expr, SyntaxError> {
1039 let mut node = self.parse_range()?;
1040 while self.consume(&Token::And) {
1041 let rhs = self.parse_range()?;
1042 node = self.make_binary(node, BinOp::BitAnd, rhs);
1043 }
1044 Ok(node)
1045 }
1046
1047 fn parse_range(&mut self) -> Result<Expr, SyntaxError> {
1048 let mut node = self.parse_comparison()?;
1049 if self.consume(&Token::Colon) {
1050 let mid = self.parse_comparison()?;
1051 if self.consume(&Token::Colon) {
1052 let end = self.parse_comparison()?;
1053 let span = self.span_between(node.span(), end.span());
1054 node = Expr::Range(Box::new(node), Some(Box::new(mid)), Box::new(end), span);
1055 } else {
1056 let span = self.span_between(node.span(), mid.span());
1057 node = Expr::Range(Box::new(node), None, Box::new(mid), span);
1058 }
1059 }
1060 Ok(node)
1061 }
1062
1063 fn parse_comparison(&mut self) -> Result<Expr, SyntaxError> {
1064 let mut node = self.parse_add_sub()?;
1065 loop {
1066 let op = match self.peek_token() {
1067 Some(Token::Equal) => BinOp::Equal,
1068 Some(Token::NotEqual) => BinOp::NotEqual,
1069 Some(Token::Less) => BinOp::Less,
1070 Some(Token::LessEqual) => BinOp::LessEqual,
1071 Some(Token::Greater) => BinOp::Greater,
1072 Some(Token::GreaterEqual) => BinOp::GreaterEqual,
1073 _ => break,
1074 };
1075 self.pos += 1; let rhs = self.parse_add_sub()?;
1077 node = self.make_binary(node, op, rhs);
1078 }
1079 Ok(node)
1080 }
1081
1082 fn parse_add_sub(&mut self) -> Result<Expr, String> {
1083 let mut node = self.parse_mul_div()?;
1084 loop {
1085 if self.in_matrix_expr
1090 && matches!(self.peek_token(), Some(Token::Plus | Token::Minus))
1091 && self.pos > 0
1092 && !self.tokens_adjacent(self.pos - 1, self.pos)
1093 && self.tokens_adjacent(self.pos, self.pos + 1)
1094 {
1095 let rhs_index = self.pos + 1;
1096 let rhs_is_imag_literal = self
1097 .tokens
1098 .get(rhs_index)
1099 .map(|info| matches!(info.token, Token::Integer | Token::Float))
1100 .unwrap_or(false)
1101 && self
1102 .tokens
1103 .get(rhs_index + 1)
1104 .map(|info| {
1105 matches!(info.token, Token::Ident)
1106 && (info.lexeme.eq_ignore_ascii_case("i")
1107 || info.lexeme.eq_ignore_ascii_case("j"))
1108 && self.tokens_adjacent(rhs_index, rhs_index + 1)
1109 })
1110 .unwrap_or(false);
1111 if !rhs_is_imag_literal {
1112 break;
1113 }
1114 }
1115 let op = if self.consume(&Token::Plus) {
1116 Some(BinOp::Add)
1117 } else if self.consume(&Token::Minus) {
1118 Some(BinOp::Sub)
1119 } else if self.peek_token() == Some(&Token::Dot)
1120 && (self.peek_token_at(1) == Some(&Token::Plus)
1121 || self.peek_token_at(1) == Some(&Token::Minus))
1122 {
1123 self.pos += 2;
1126 if self.tokens[self.pos - 1].token == Token::Plus {
1127 Some(BinOp::Add)
1128 } else {
1129 Some(BinOp::Sub)
1130 }
1131 } else {
1132 None
1133 };
1134 let Some(op) = op else { break };
1135 let rhs = self.parse_mul_div()?;
1136 node = self.make_binary(node, op, rhs);
1137 }
1138 Ok(node)
1139 }
1140
1141 fn parse_mul_div(&mut self) -> Result<Expr, String> {
1142 let mut node = self.parse_unary()?;
1143 loop {
1144 if self.peek_token() == Some(&Token::Ident) && self.pos > 0 {
1145 let prev = &self.tokens[self.pos - 1];
1146 let curr = &self.tokens[self.pos];
1147 let is_adjacent = self.tokens_adjacent(self.pos - 1, self.pos);
1148 let is_imag =
1149 curr.lexeme.eq_ignore_ascii_case("i") || curr.lexeme.eq_ignore_ascii_case("j");
1150 if is_adjacent && is_imag && matches!(prev.token, Token::Integer | Token::Float) {
1151 let token = self.next().unwrap();
1152 let rhs = Expr::Ident(token.lexeme, self.span_from(token.position, token.end));
1153 node = self.make_binary(node, BinOp::Mul, rhs);
1154 continue;
1155 }
1156 }
1157 let op = match self.peek_token() {
1158 Some(Token::Star) => BinOp::Mul,
1159 Some(Token::DotStar) => BinOp::ElemMul,
1160 Some(Token::Slash) => BinOp::RightDiv,
1161 Some(Token::DotSlash) => BinOp::ElemDiv,
1162 Some(Token::Backslash) => BinOp::LeftDiv,
1163 Some(Token::DotBackslash) => BinOp::ElemLeftDiv,
1164 _ => break,
1165 };
1166 self.pos += 1; let rhs = self.parse_unary()?;
1168 node = self.make_binary(node, op, rhs);
1169 }
1170 Ok(node)
1171 }
1172
1173 fn parse_pow(&mut self) -> Result<Expr, String> {
1174 let node = self.parse_postfix()?;
1175 if let Some(token) = self.peek_token() {
1176 let op = match token {
1177 Token::Caret => BinOp::Pow,
1178 Token::DotCaret => BinOp::ElemPow,
1179 _ => return Ok(node),
1180 };
1181 self.pos += 1; let rhs = self.parse_pow()?; Ok(self.make_binary(node, op, rhs))
1184 } else {
1185 Ok(node)
1186 }
1187 }
1188
1189 fn parse_postfix_with_base(&mut self, mut expr: Expr) -> Result<Expr, String> {
1190 loop {
1191 if self.consume(&Token::LParen) {
1192 let start = expr.span().start;
1193 let mut args = Vec::new();
1194 if !self.consume(&Token::RParen) {
1195 args.push(self.parse_expr()?);
1196 while self.consume(&Token::Comma) {
1197 args.push(self.parse_expr()?);
1198 }
1199 if !self.consume(&Token::RParen) {
1200 return Err("expected ')' after arguments".into());
1201 }
1202 }
1203 let end = self.last_token_end();
1204 let span = self.span_from(start, end);
1205 if let Expr::Ident(ref name, _) = expr {
1209 expr = Expr::FuncCall(name.clone(), args, span);
1210 } else {
1211 expr = Expr::Index(Box::new(expr), args, span);
1213 }
1214 } else if self.consume(&Token::LBracket) {
1215 let start = expr.span().start;
1217 let mut indices = Vec::new();
1218 indices.push(self.parse_expr()?);
1219 while self.consume(&Token::Comma) {
1220 indices.push(self.parse_expr()?);
1221 }
1222 if !self.consume(&Token::RBracket) {
1223 return Err("expected ']'".into());
1224 }
1225 let end = self.last_token_end();
1226 let span = self.span_from(start, end);
1227 expr = Expr::Index(Box::new(expr), indices, span);
1228 } else if self.consume(&Token::LBrace) {
1229 let start = expr.span().start;
1231 let mut indices = Vec::new();
1232 indices.push(self.parse_expr()?);
1233 while self.consume(&Token::Comma) {
1234 indices.push(self.parse_expr()?);
1235 }
1236 if !self.consume(&Token::RBrace) {
1237 return Err("expected '}'".into());
1238 }
1239 let end = self.last_token_end();
1240 let span = self.span_from(start, end);
1241 expr = Expr::IndexCell(Box::new(expr), indices, span);
1242 } else if self.peek_token() == Some(&Token::Dot) {
1243 if self.peek_token_at(1) == Some(&Token::Transpose) {
1245 self.pos += 2; let end = self.last_token_end();
1247 let span = self.span_from(expr.span().start, end);
1248 expr = Expr::Unary(UnOp::NonConjugateTranspose, Box::new(expr), span);
1249 continue;
1250 }
1251 if self.peek_token_at(1) == Some(&Token::Plus)
1252 || self.peek_token_at(1) == Some(&Token::Minus)
1253 {
1254 break;
1256 }
1257 self.pos += 1; let name_token = match self.next() {
1260 Some(TokenInfo {
1261 token: Token::Ident,
1262 lexeme,
1263 position,
1264 end,
1265 }) => (lexeme, position, end),
1266 _ => return Err("expected member name after '.'".into()),
1267 };
1268 if self.consume(&Token::LParen) {
1269 let mut args = Vec::new();
1270 if !self.consume(&Token::RParen) {
1271 args.push(self.parse_expr()?);
1272 while self.consume(&Token::Comma) {
1273 args.push(self.parse_expr()?);
1274 }
1275 if !self.consume(&Token::RParen) {
1276 return Err("expected ')' after method arguments".into());
1277 }
1278 }
1279 let end = self.last_token_end();
1280 let span = self.span_from(expr.span().start, end);
1281 if matches!(expr, Expr::MetaClass(_, _)) {
1282 expr = Expr::MethodCall(Box::new(expr), name_token.0, args, span);
1283 } else {
1284 expr = Expr::DottedInvoke(Box::new(expr), name_token.0, args, span);
1285 }
1286 } else {
1287 let span = self.span_from(expr.span().start, name_token.2);
1288 expr = Expr::Member(Box::new(expr), name_token.0, span);
1289 }
1290 } else if self.consume(&Token::Transpose) {
1291 let end = self.last_token_end();
1293 let span = self.span_from(expr.span().start, end);
1294 expr = Expr::Unary(UnOp::Transpose, Box::new(expr), span);
1295 } else {
1296 break;
1297 }
1298 }
1299 Ok(expr)
1300 }
1301
1302 fn parse_postfix(&mut self) -> Result<Expr, String> {
1303 let expr = self.parse_primary()?;
1304 self.parse_postfix_with_base(expr)
1305 }
1306
1307 fn parse_unary(&mut self) -> Result<Expr, String> {
1308 if self.peek_token() == Some(&Token::Plus) {
1309 let start = self.tokens[self.pos].position;
1310 self.pos += 1;
1311 let expr = self.parse_unary()?;
1312 Ok(self.make_unary(UnOp::Plus, expr, start))
1313 } else if self.peek_token() == Some(&Token::Minus) {
1314 let start = self.tokens[self.pos].position;
1315 self.pos += 1;
1316 let expr = self.parse_unary()?;
1317 Ok(self.make_unary(UnOp::Minus, expr, start))
1318 } else if self.peek_token() == Some(&Token::Tilde) {
1319 let start = self.tokens[self.pos].position;
1320 self.pos += 1;
1321 let expr = self.parse_unary()?;
1322 Ok(self.make_unary(UnOp::Not, expr, start))
1323 } else if self.peek_token() == Some(&Token::Question) {
1324 let start = self.tokens[self.pos].position;
1325 self.pos += 1;
1326 let mut parts: Vec<String> = Vec::new();
1329 let first = self.expect_ident()?;
1330 let class_consumed = first
1331 .chars()
1332 .next()
1333 .map(|c| c.is_uppercase())
1334 .unwrap_or(false);
1335 parts.push(first);
1336 while self.peek_token() == Some(&Token::Dot)
1337 && matches!(self.peek_token_at(1), Some(Token::Ident))
1338 {
1339 let next_lex = if let Some(ti) = self.tokens.get(self.pos + 1) {
1341 ti.lexeme.clone()
1342 } else {
1343 String::new()
1344 };
1345 let is_upper = next_lex
1346 .chars()
1347 .next()
1348 .map(|c| c.is_uppercase())
1349 .unwrap_or(false);
1350 if class_consumed {
1351 break;
1352 }
1353 self.pos += 1; let seg = self.expect_ident()?;
1356 parts.push(seg);
1357 if is_upper {
1358 break;
1359 }
1360 }
1361 let end = self.last_token_end();
1362 let span = self.span_from(start, end);
1363 let base = Expr::MetaClass(parts.join("."), span);
1364 self.parse_postfix_with_base(base)
1365 } else {
1366 self.parse_pow()
1367 }
1368 }
1369
1370 fn parse_primary(&mut self) -> Result<Expr, String> {
1371 match self.next() {
1372 Some(info) => match info.token {
1373 Token::Integer | Token::Float => {
1374 let span = self.span_from(info.position, info.end);
1375 Ok(Expr::Number(info.lexeme, span))
1376 }
1377 Token::Str => {
1378 let span = self.span_from(info.position, info.end);
1379 Ok(Expr::String(info.lexeme, span))
1380 }
1381 Token::True => {
1382 let span = self.span_from(info.position, info.end);
1383 Ok(Expr::Ident("true".into(), span))
1384 }
1385 Token::False => {
1386 let span = self.span_from(info.position, info.end);
1387 Ok(Expr::Ident("false".into(), span))
1388 }
1389 Token::Ident => {
1390 let span = self.span_from(info.position, info.end);
1391 Ok(Expr::Ident(info.lexeme, span))
1392 }
1393 Token::End => {
1395 let span = self.span_from(info.position, info.end);
1396 Ok(Expr::EndKeyword(span))
1397 }
1398 Token::At => {
1399 let start = info.position;
1400 if self.consume(&Token::LParen) {
1402 let mut params = Vec::new();
1403 if !self.consume(&Token::RParen) {
1404 params.push(self.expect_ident()?);
1405 while self.consume(&Token::Comma) {
1406 params.push(self.expect_ident()?);
1407 }
1408 if !self.consume(&Token::RParen) {
1409 return Err(
1410 "expected ')' after anonymous function parameters".into()
1411 );
1412 }
1413 }
1414 let body = self.parse_expr().map_err(|e| e.message)?;
1415 let span = self.span_from(start, body.span().end);
1416 Ok(Expr::AnonFunc {
1417 params,
1418 body: Box::new(body),
1419 span,
1420 })
1421 } else {
1422 let name = self.expect_ident()?;
1424 let end = self.last_token_end();
1425 let span = self.span_from(start, end);
1426 Ok(Expr::FuncHandle(name, span))
1427 }
1428 }
1429 Token::LParen => {
1430 let start = info.position;
1431 let expr = self.parse_expr()?;
1432 if !self.consume(&Token::RParen) {
1433 return Err("expected ')' to close parentheses".into());
1434 }
1435 let end = self.last_token_end();
1436 let span = self.span_from(start, end);
1437 Ok(expr.with_span(span))
1438 }
1439 Token::LBracket => {
1440 let start = info.position;
1441 let matrix = self.parse_matrix()?;
1442 if !self.consume(&Token::RBracket) {
1443 return Err("expected ']' to close matrix literal".into());
1444 }
1445 let end = self.last_token_end();
1446 let span = self.span_from(start, end);
1447 Ok(matrix.with_span(span))
1448 }
1449 Token::LBrace => {
1450 let start = info.position;
1451 let cell = self.parse_cell()?;
1452 if !self.consume(&Token::RBrace) {
1453 return Err("expected '}' to close cell literal".into());
1454 }
1455 let end = self.last_token_end();
1456 let span = self.span_from(start, end);
1457 Ok(cell.with_span(span))
1458 }
1459 Token::Colon => {
1460 let span = self.span_from(info.position, info.end);
1461 Ok(Expr::Colon(span))
1462 }
1463 _ => Err(format!("unexpected token: {:?}", info.token)),
1464 },
1465 None => Err("unexpected end of input".into()),
1466 }
1467 }
1468
1469 fn parse_matrix(&mut self) -> Result<Expr, String> {
1470 self.skip_newlines();
1471 let mut rows = Vec::new();
1472 if self.peek_token() == Some(&Token::RBracket) {
1473 return Ok(Expr::Tensor(rows, Span::default()));
1474 }
1475 loop {
1476 self.skip_newlines();
1477 if self.peek_token() == Some(&Token::RBracket) {
1478 break;
1479 }
1480 let mut row = Vec::new();
1481 row.push(self.parse_matrix_expr()?);
1483 loop {
1485 if self.consume(&Token::Newline) {
1486 continue;
1487 }
1488 if self.consume(&Token::Comma) {
1489 row.push(self.parse_matrix_expr()?);
1490 continue;
1491 }
1492 if matches!(
1494 self.peek_token(),
1495 Some(Token::Semicolon) | Some(Token::RBracket)
1496 ) {
1497 break;
1498 }
1499 match self.peek_token() {
1502 Some(
1503 Token::Ident
1504 | Token::Integer
1505 | Token::Float
1506 | Token::Str
1507 | Token::LParen
1508 | Token::LBracket
1509 | Token::LBrace
1510 | Token::At
1511 | Token::Plus
1512 | Token::Minus
1513 | Token::Colon
1514 | Token::True
1515 | Token::False,
1516 ) => {
1517 row.push(self.parse_matrix_expr()?);
1518 }
1519 _ => {
1520 break;
1521 }
1522 }
1523 }
1524 rows.push(row);
1525 if self.consume(&Token::Semicolon) {
1526 self.skip_newlines();
1527 continue;
1528 } else {
1529 break;
1530 }
1531 }
1532 self.skip_newlines();
1533 Ok(Expr::Tensor(rows, Span::default()))
1534 }
1535
1536 fn parse_matrix_expr(&mut self) -> Result<Expr, String> {
1537 let prior = self.in_matrix_expr;
1538 self.in_matrix_expr = true;
1539 let expr = self.parse_expr().map_err(|e| e.message);
1540 self.in_matrix_expr = prior;
1541 expr
1542 }
1543
1544 fn parse_if(&mut self) -> Result<Stmt, String> {
1545 let start = self.tokens[self.pos].position;
1546 self.consume(&Token::If);
1547 let cond = self.parse_expr()?;
1548 let then_body =
1549 self.parse_block(|t| matches!(t, Token::Else | Token::ElseIf | Token::End))?;
1550 let mut elseif_blocks = Vec::new();
1551 while self.consume(&Token::ElseIf) {
1552 let c = self.parse_expr()?;
1553 let body =
1554 self.parse_block(|t| matches!(t, Token::Else | Token::ElseIf | Token::End))?;
1555 elseif_blocks.push((c, body));
1556 }
1557 let else_body = if self.consume(&Token::Else) {
1558 Some(self.parse_block(|t| matches!(t, Token::End))?)
1559 } else {
1560 None
1561 };
1562 if !self.consume(&Token::End) {
1563 return Err("expected 'end'".into());
1564 }
1565 let end = self.last_token_end();
1566 Ok(Stmt::If {
1567 cond,
1568 then_body,
1569 elseif_blocks,
1570 else_body,
1571 span: self.span_from(start, end),
1572 })
1573 }
1574
1575 fn parse_while(&mut self) -> Result<Stmt, String> {
1576 let start = self.tokens[self.pos].position;
1577 self.consume(&Token::While);
1578 let cond = self.parse_expr()?;
1579 let body = self.parse_block(|t| matches!(t, Token::End))?;
1580 if !self.consume(&Token::End) {
1581 return Err("expected 'end'".into());
1582 }
1583 let end = self.last_token_end();
1584 Ok(Stmt::While {
1585 cond,
1586 body,
1587 span: self.span_from(start, end),
1588 })
1589 }
1590
1591 fn parse_for(&mut self) -> Result<Stmt, String> {
1592 let start = self.tokens[self.pos].position;
1593 self.consume(&Token::For);
1594 let var = self.expect_ident()?;
1595 if !self.consume(&Token::Assign) {
1596 return Err("expected '='".into());
1597 }
1598 let expr = self.parse_expr()?;
1599 let body = self.parse_block(|t| matches!(t, Token::End))?;
1600 if !self.consume(&Token::End) {
1601 return Err("expected 'end'".into());
1602 }
1603 let end = self.last_token_end();
1604 Ok(Stmt::For {
1605 var,
1606 expr,
1607 body,
1608 span: self.span_from(start, end),
1609 })
1610 }
1611
1612 fn parse_function(&mut self) -> Result<Stmt, String> {
1613 let start = self.tokens[self.pos].position;
1614 self.consume(&Token::Function);
1615 let mut outputs = Vec::new();
1616 if self.consume(&Token::LBracket) {
1617 outputs.push(self.expect_ident_or_tilde()?);
1618 while self.consume(&Token::Comma) {
1619 outputs.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 } else if self.peek_token() == Some(&Token::Ident)
1628 && self.peek_token_at(1) == Some(&Token::Assign)
1629 {
1630 outputs.push(self.next().unwrap().lexeme);
1631 self.consume(&Token::Assign);
1632 }
1633 let name = self.expect_ident()?;
1634 if !self.consume(&Token::LParen) {
1635 return Err("expected '('".into());
1636 }
1637 let mut params = Vec::new();
1638 if !self.consume(&Token::RParen) {
1639 params.push(self.expect_ident()?);
1640 while self.consume(&Token::Comma) {
1641 params.push(self.expect_ident()?);
1642 }
1643 if !self.consume(&Token::RParen) {
1644 return Err("expected ')'".into());
1645 }
1646 }
1647
1648 if let Some(idx) = params.iter().position(|p| p == "varargin") {
1651 if idx != params.len() - 1 {
1652 return Err("'varargin' must be the last input parameter".into());
1653 }
1654 if params.iter().filter(|p| p.as_str() == "varargin").count() > 1 {
1655 return Err("'varargin' cannot appear more than once".into());
1656 }
1657 }
1658 if let Some(idx) = outputs.iter().position(|o| o == "varargout") {
1660 if idx != outputs.len() - 1 {
1661 return Err("'varargout' must be the last output parameter".into());
1662 }
1663 if outputs.iter().filter(|o| o.as_str() == "varargout").count() > 1 {
1664 return Err("'varargout' cannot appear more than once".into());
1665 }
1666 }
1667
1668 if self.peek_token() == Some(&Token::Arguments) {
1671 self.pos += 1; loop {
1674 if self.consume(&Token::End) {
1675 break;
1676 }
1677 if self.consume(&Token::Semicolon) || self.consume(&Token::Comma) {
1678 continue;
1679 }
1680 if matches!(self.peek_token(), Some(Token::Ident)) {
1681 let _ = self.expect_ident()?;
1682 continue;
1683 }
1684 if self.peek_token().is_none() {
1686 break;
1687 }
1688 break;
1689 }
1690 }
1691
1692 let body = self.parse_block(|t| matches!(t, Token::End))?;
1693 if !self.consume(&Token::End) {
1694 return Err("expected 'end'".into());
1695 }
1696 let end = self.last_token_end();
1697 Ok(Stmt::Function {
1698 name,
1699 params,
1700 outputs,
1701 body,
1702 span: self.span_from(start, end),
1703 })
1704 }
1705
1706 fn parse_block<F>(&mut self, term: F) -> Result<Vec<Stmt>, String>
1707 where
1708 F: Fn(&Token) -> bool,
1709 {
1710 let mut body = Vec::new();
1711 while let Some(tok) = self.peek_token() {
1712 if term(tok) {
1713 break;
1714 }
1715 if self.consume(&Token::Semicolon)
1716 || self.consume(&Token::Comma)
1717 || self.consume(&Token::Newline)
1718 {
1719 continue;
1720 }
1721 let stmt = if self.peek_token() == Some(&Token::LBracket) {
1723 self.try_parse_multi_assign()?
1724 } else {
1725 self.parse_stmt().map_err(|e| e.message)?
1726 };
1727 let is_semicolon_terminated = self.consume(&Token::Semicolon);
1728
1729 let final_stmt = match stmt {
1730 Stmt::ExprStmt(expr, _, span) => {
1731 Stmt::ExprStmt(expr, is_semicolon_terminated, span)
1732 }
1733 Stmt::Assign(name, expr, _, span) => {
1734 Stmt::Assign(name, expr, is_semicolon_terminated, span)
1735 }
1736 Stmt::MultiAssign(names, expr, _, span) => {
1737 Stmt::MultiAssign(names, expr, is_semicolon_terminated, span)
1738 }
1739 Stmt::AssignLValue(lv, expr, _, span) => {
1740 Stmt::AssignLValue(lv, expr, is_semicolon_terminated, span)
1741 }
1742 other => other,
1743 };
1744 body.push(final_stmt);
1745 }
1746 Ok(body)
1747 }
1748
1749 fn parse_cell(&mut self) -> Result<Expr, String> {
1750 let mut rows = Vec::new();
1751 self.skip_newlines();
1752 if self.peek_token() == Some(&Token::RBrace) {
1753 return Ok(Expr::Cell(rows, Span::default()));
1754 }
1755 loop {
1756 self.skip_newlines();
1757 if self.peek_token() == Some(&Token::RBrace) {
1758 break;
1759 }
1760 let mut row = Vec::new();
1761 row.push(self.parse_expr()?);
1762 while self.consume(&Token::Comma) {
1763 row.push(self.parse_expr()?);
1764 }
1765 rows.push(row);
1766 if self.consume(&Token::Semicolon) {
1767 self.skip_newlines();
1768 continue;
1769 } else {
1770 break;
1771 }
1772 }
1773 self.skip_newlines();
1774 Ok(Expr::Cell(rows, Span::default()))
1775 }
1776
1777 fn parse_switch(&mut self) -> Result<Stmt, String> {
1778 let start = self.tokens[self.pos].position;
1779 self.consume(&Token::Switch);
1780 let control = self.parse_expr()?;
1781 let mut cases = Vec::new();
1782 let mut otherwise: Option<Vec<Stmt>> = None;
1783 loop {
1784 if self.consume(&Token::Newline) || self.consume(&Token::Semicolon) {
1785 continue;
1786 }
1787 if self.consume(&Token::Case) {
1788 let val = self.parse_expr()?;
1789 let body =
1790 self.parse_block(|t| matches!(t, Token::Case | Token::Otherwise | Token::End))?;
1791 cases.push((val, body));
1792 } else if self.consume(&Token::Otherwise) {
1793 let body = self.parse_block(|t| matches!(t, Token::End))?;
1794 otherwise = Some(body);
1795 } else if self.consume(&Token::Comma) {
1796 continue;
1797 } else {
1798 break;
1799 }
1800 }
1801 if !self.consume(&Token::End) {
1802 return Err("expected 'end' for switch".into());
1803 }
1804 let end = self.last_token_end();
1805 Ok(Stmt::Switch {
1806 expr: control,
1807 cases,
1808 otherwise,
1809 span: self.span_from(start, end),
1810 })
1811 }
1812
1813 fn parse_try_catch(&mut self) -> Result<Stmt, String> {
1814 let start = self.tokens[self.pos].position;
1815 self.consume(&Token::Try);
1816 let try_body = self.parse_block(|t| matches!(t, Token::Catch | Token::End))?;
1817 if !self.consume(&Token::Catch) {
1818 return Err("expected 'catch' after try".into());
1819 }
1820 let catch_var = if self.peek_token() == Some(&Token::Ident) {
1821 Some(self.expect_ident()?)
1822 } else {
1823 None
1824 };
1825 let catch_body = self.parse_block(|t| matches!(t, Token::End))?;
1826 if !self.consume(&Token::End) {
1827 return Err("expected 'end' after catch".into());
1828 }
1829 let end = self.last_token_end();
1830 Ok(Stmt::TryCatch {
1831 try_body,
1832 catch_var,
1833 catch_body,
1834 span: self.span_from(start, end),
1835 })
1836 }
1837
1838 fn parse_import(&mut self) -> Result<Stmt, String> {
1839 let start = self.tokens[self.pos].position;
1840 self.consume(&Token::Import);
1841 let mut path = Vec::new();
1843 path.push(self.expect_ident()?);
1844 let mut wildcard = false;
1845 loop {
1846 if self.consume(&Token::DotStar) {
1847 wildcard = true;
1848 break;
1849 }
1850 if self.consume(&Token::Dot) {
1851 if self.consume(&Token::Star) {
1852 wildcard = true;
1853 break;
1854 } else {
1855 path.push(self.expect_ident()?);
1856 continue;
1857 }
1858 }
1859 break;
1860 }
1861 let end = self.last_token_end();
1862 Ok(Stmt::Import {
1863 path,
1864 wildcard,
1865 span: self.span_from(start, end),
1866 })
1867 }
1868
1869 fn parse_classdef(&mut self) -> Result<Stmt, String> {
1870 let start = self.tokens[self.pos].position;
1871 self.consume(&Token::ClassDef);
1872 let name = self.parse_qualified_name()?;
1873 let mut super_class = None;
1874 if self.consume(&Token::Less) {
1875 super_class = Some(self.parse_qualified_name()?);
1876 }
1877 let mut members: Vec<ClassMember> = Vec::new();
1878 loop {
1879 if self.consume(&Token::Semicolon)
1881 || self.consume(&Token::Comma)
1882 || self.consume(&Token::Newline)
1883 {
1884 continue;
1885 }
1886 match self.peek_token() {
1887 Some(Token::Properties) => {
1888 self.pos += 1;
1889 let attrs = self.parse_optional_attr_list();
1890 let props = self.parse_properties_names_block()?;
1891 if !self.consume(&Token::End) {
1892 return Err("expected 'end' after properties".into());
1893 }
1894 members.push(ClassMember::Properties {
1895 attributes: attrs,
1896 names: props,
1897 });
1898 }
1899 Some(Token::Methods) => {
1900 self.pos += 1;
1901 let attrs = self.parse_optional_attr_list();
1902 let body = self.parse_block(|t| matches!(t, Token::End))?;
1903 if !self.consume(&Token::End) {
1904 return Err("expected 'end' after methods".into());
1905 }
1906 members.push(ClassMember::Methods {
1907 attributes: attrs,
1908 body,
1909 });
1910 }
1911 Some(Token::Events) => {
1912 self.pos += 1;
1913 let attrs = self.parse_optional_attr_list();
1914 let names = self.parse_name_block()?;
1915 if !self.consume(&Token::End) {
1916 return Err("expected 'end' after events".into());
1917 }
1918 members.push(ClassMember::Events {
1919 attributes: attrs,
1920 names,
1921 });
1922 }
1923 Some(Token::Enumeration) => {
1924 self.pos += 1;
1925 let attrs = self.parse_optional_attr_list();
1926 let names = self.parse_name_block()?;
1927 if !self.consume(&Token::End) {
1928 return Err("expected 'end' after enumeration".into());
1929 }
1930 members.push(ClassMember::Enumeration {
1931 attributes: attrs,
1932 names,
1933 });
1934 }
1935 Some(Token::Arguments) => {
1936 self.pos += 1;
1937 let attrs = self.parse_optional_attr_list();
1938 let names = self.parse_name_block()?;
1939 if !self.consume(&Token::End) {
1940 return Err("expected 'end' after arguments".into());
1941 }
1942 members.push(ClassMember::Arguments {
1943 attributes: attrs,
1944 names,
1945 });
1946 }
1947 Some(Token::End) => {
1948 self.pos += 1;
1949 break;
1950 }
1951 _ => break,
1952 }
1953 }
1954 let end = self.last_token_end();
1955 Ok(Stmt::ClassDef {
1956 name,
1957 super_class,
1958 members,
1959 span: self.span_from(start, end),
1960 })
1961 }
1962
1963 fn parse_name_block(&mut self) -> Result<Vec<String>, String> {
1964 let mut names = Vec::new();
1965 while let Some(tok) = self.peek_token() {
1966 if matches!(tok, Token::End) {
1967 break;
1968 }
1969 if self.consume(&Token::Semicolon)
1970 || self.consume(&Token::Comma)
1971 || self.consume(&Token::Newline)
1972 {
1973 continue;
1974 }
1975 if let Some(Token::Ident) = self.peek_token() {
1976 names.push(self.expect_ident()?);
1977 } else {
1978 break;
1979 }
1980 }
1981 Ok(names)
1982 }
1983
1984 fn parse_properties_names_block(&mut self) -> Result<Vec<String>, String> {
1985 let mut names = Vec::new();
1987 while let Some(tok) = self.peek_token() {
1988 if matches!(tok, Token::End) {
1989 break;
1990 }
1991 if self.consume(&Token::Semicolon)
1992 || self.consume(&Token::Comma)
1993 || self.consume(&Token::Newline)
1994 {
1995 continue;
1996 }
1997 if let Some(Token::Ident) = self.peek_token() {
1998 names.push(self.expect_ident()?);
1999 if self.consume(&Token::Assign) {
2001 let _ = self.parse_expr().map_err(|e| e.message)?;
2003 }
2004 } else {
2005 break;
2006 }
2007 }
2008 Ok(names)
2009 }
2010
2011 fn parse_optional_attr_list(&mut self) -> Vec<Attr> {
2012 let mut attrs: Vec<Attr> = Vec::new();
2014 if !self.consume(&Token::LParen) {
2015 return attrs;
2016 }
2017 loop {
2018 if self.consume(&Token::RParen) {
2019 break;
2020 }
2021 match self.peek_token() {
2022 Some(Token::Ident) => {
2023 let name = self.expect_ident().unwrap_or_else(|_| "".to_string());
2024 let mut value: Option<String> = None;
2025 if self.consume(&Token::Assign) {
2026 if let Some(tok) = self.next() {
2028 value = Some(tok.lexeme);
2029 }
2030 }
2031 attrs.push(Attr { name, value });
2032 let _ = self.consume(&Token::Comma);
2033 }
2034 Some(Token::Comma) => {
2035 self.pos += 1;
2036 }
2037 Some(Token::RParen) => {
2038 self.pos += 1;
2039 break;
2040 }
2041 Some(_) => {
2042 self.pos += 1;
2043 }
2044 None => {
2045 break;
2046 }
2047 }
2048 }
2049 attrs
2050 }
2051
2052 fn parse_global(&mut self) -> Result<Stmt, String> {
2053 let start = self.tokens[self.pos].position;
2054 self.consume(&Token::Global);
2055 let mut names = Vec::new();
2056 names.push(self.expect_ident()?);
2057 loop {
2058 if self.consume(&Token::Comma) {
2059 names.push(self.expect_ident()?);
2060 continue;
2061 }
2062 if self.peek_token() == Some(&Token::Ident) {
2063 names.push(self.expect_ident()?);
2064 continue;
2065 }
2066 break;
2067 }
2068 let end = self.last_token_end();
2069 Ok(Stmt::Global(names, self.span_from(start, end)))
2070 }
2071
2072 fn parse_persistent(&mut self) -> Result<Stmt, String> {
2073 let start = self.tokens[self.pos].position;
2074 self.consume(&Token::Persistent);
2075 let mut names = Vec::new();
2076 names.push(self.expect_ident()?);
2077 loop {
2078 if self.consume(&Token::Comma) {
2079 names.push(self.expect_ident()?);
2080 continue;
2081 }
2082 if self.peek_token() == Some(&Token::Ident) {
2083 names.push(self.expect_ident()?);
2084 continue;
2085 }
2086 break;
2087 }
2088 let end = self.last_token_end();
2089 Ok(Stmt::Persistent(names, self.span_from(start, end)))
2090 }
2091
2092 fn try_parse_multi_assign(&mut self) -> Result<Stmt, String> {
2093 if !self.consume(&Token::LBracket) {
2094 return Err("not a multi-assign".into());
2095 }
2096 let start = self.tokens[self.pos.saturating_sub(1)].position;
2097 let mut names = Vec::new();
2098 names.push(self.expect_ident_or_tilde()?);
2099 while self.consume(&Token::Comma) {
2100 names.push(self.expect_ident_or_tilde()?);
2101 }
2102 if !self.consume(&Token::RBracket) {
2103 return Err("expected ']'".into());
2104 }
2105 if !self.consume(&Token::Assign) {
2106 return Err("expected '='".into());
2107 }
2108 let rhs = self.parse_expr().map_err(|e| e.message)?;
2109 let span = self.span_from(start, rhs.span().end);
2110 Ok(Stmt::MultiAssign(names, rhs, false, span))
2111 }
2112
2113 fn parse_qualified_name(&mut self) -> Result<String, String> {
2114 let mut parts = Vec::new();
2115 parts.push(self.expect_ident()?);
2116 while self.consume(&Token::Dot) {
2117 parts.push(self.expect_ident()?);
2118 }
2119 Ok(parts.join("."))
2120 }
2121
2122 fn expect_ident(&mut self) -> Result<String, String> {
2123 match self.next() {
2124 Some(TokenInfo {
2125 token: Token::Ident,
2126 lexeme,
2127 ..
2128 }) => Ok(lexeme),
2129 _ => Err("expected identifier".into()),
2130 }
2131 }
2132
2133 fn expect_ident_or_tilde(&mut self) -> Result<String, String> {
2134 match self.next() {
2135 Some(TokenInfo {
2136 token: Token::Ident,
2137 lexeme,
2138 ..
2139 }) => Ok(lexeme),
2140 Some(TokenInfo {
2141 token: Token::Tilde,
2142 ..
2143 }) => Ok("~".to_string()),
2144 _ => Err("expected identifier or '~'".into()),
2145 }
2146 }
2147
2148 fn peek(&self) -> Option<&TokenInfo> {
2149 self.tokens.get(self.pos)
2150 }
2151
2152 fn current_position(&self) -> usize {
2153 self.peek()
2154 .map(|t| t.position)
2155 .unwrap_or_else(|| self.input.len())
2156 }
2157
2158 fn peek_token(&self) -> Option<&Token> {
2159 self.tokens.get(self.pos).map(|t| &t.token)
2160 }
2161
2162 fn peek_token_at(&self, offset: usize) -> Option<&Token> {
2163 self.tokens.get(self.pos + offset).map(|t| &t.token)
2164 }
2165
2166 fn next(&mut self) -> Option<TokenInfo> {
2167 if self.pos < self.tokens.len() {
2168 let info = self.tokens[self.pos].clone();
2169 self.pos += 1;
2170 Some(info)
2171 } else {
2172 None
2173 }
2174 }
2175
2176 fn consume(&mut self, t: &Token) -> bool {
2177 if self.peek_token() == Some(t) {
2178 self.pos += 1;
2179 true
2180 } else {
2181 false
2182 }
2183 }
2184}
2185
2186fn extract_keyword(expr: &Expr) -> Option<String> {
2187 match expr {
2188 Expr::Ident(s, _) => Some(s.clone()),
2189 Expr::String(s, _) => Some(s.trim_matches(&['"', '\''][..]).to_string()),
2190 _ => None,
2191 }
2192}