runmat_parser/parser/
mod.rs1mod assignment;
2mod classdef;
3mod command;
4mod cursor;
5mod expr;
6mod stmt;
7
8use runmat_lexer::Token;
9
10use crate::{ParserOptions, Program, Stmt, SyntaxError};
11
12#[derive(Clone)]
13struct TokenInfo {
14 token: Token,
15 lexeme: String,
16 position: usize,
17 end: usize,
18}
19
20struct Parser {
21 tokens: Vec<TokenInfo>,
22 pos: usize,
23 input: String,
24 options: ParserOptions,
25 in_matrix_expr: bool,
26 current_classdef_name: Option<String>,
27}
28
29pub fn parse(input: &str) -> Result<Program, SyntaxError> {
30 parse_with_options(input, ParserOptions::default())
31}
32
33pub fn parse_with_options(input: &str, options: ParserOptions) -> Result<Program, SyntaxError> {
34 use runmat_lexer::tokenize_detailed;
35
36 let toks = tokenize_detailed(input);
37 let mut tokens = Vec::new();
38 let mut skip_newlines = false;
39
40 for t in toks {
41 if matches!(t.token, Token::Error) {
42 return Err(SyntaxError {
43 message: format!("Invalid token: '{}'", t.lexeme),
44 position: t.start,
45 found_token: Some(t.lexeme),
46 expected: None,
47 });
48 }
49 if matches!(t.token, Token::Ellipsis | Token::Section) {
51 skip_newlines = matches!(t.token, Token::Ellipsis);
55 continue;
56 }
57 if skip_newlines && matches!(t.token, Token::Newline) {
58 continue;
59 }
60 skip_newlines = false;
61 tokens.push(TokenInfo {
62 token: t.token,
63 lexeme: t.lexeme,
64 position: t.start,
65 end: t.end,
66 });
67 }
68
69 let mut parser = Parser {
70 tokens,
71 pos: 0,
72 input: input.to_string(),
73 options,
74 in_matrix_expr: false,
75 current_classdef_name: None,
76 };
77 parser.parse_program()
78}
79
80impl Parser {
81 fn parse_program(&mut self) -> Result<Program, SyntaxError> {
82 let mut body = Vec::new();
83 while self.pos < self.tokens.len() {
84 if self.consume(&Token::Semicolon)
85 || self.consume(&Token::Comma)
86 || self.consume(&Token::Newline)
87 {
88 continue;
89 }
90 body.push(self.parse_stmt_with_semicolon()?);
91 }
92 Ok(Program { body })
93 }
94
95 fn finalize_stmt(&self, stmt: Stmt, is_semicolon_terminated: bool) -> Stmt {
96 match stmt {
97 Stmt::ExprStmt(expr, _, span) => Stmt::ExprStmt(expr, is_semicolon_terminated, span),
98 Stmt::Assign(name, expr, _, span) => {
99 Stmt::Assign(name, expr, is_semicolon_terminated, span)
100 }
101 Stmt::MultiAssign(names, expr, _, span) => {
102 Stmt::MultiAssign(names, expr, is_semicolon_terminated, span)
103 }
104 Stmt::AssignLValue(lv, expr, _, span) => {
105 Stmt::AssignLValue(lv, expr, is_semicolon_terminated, span)
106 }
107 other => other,
108 }
109 }
110}