Skip to main content

runmat_parser/parser/
mod.rs

1mod 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}
27
28pub fn parse(input: &str) -> Result<Program, SyntaxError> {
29    parse_with_options(input, ParserOptions::default())
30}
31
32pub fn parse_with_options(input: &str, options: ParserOptions) -> Result<Program, SyntaxError> {
33    use runmat_lexer::tokenize_detailed;
34
35    let toks = tokenize_detailed(input);
36    let mut tokens = Vec::new();
37
38    for t in toks {
39        if matches!(t.token, Token::Error) {
40            return Err(SyntaxError {
41                message: format!("Invalid token: '{}'", t.lexeme),
42                position: t.start,
43                found_token: Some(t.lexeme),
44                expected: None,
45            });
46        }
47        // Skip layout-only tokens from lexing.
48        if matches!(t.token, Token::Ellipsis | Token::Section) {
49            continue;
50        }
51        tokens.push(TokenInfo {
52            token: t.token,
53            lexeme: t.lexeme,
54            position: t.start,
55            end: t.end,
56        });
57    }
58
59    let mut parser = Parser {
60        tokens,
61        pos: 0,
62        input: input.to_string(),
63        options,
64        in_matrix_expr: false,
65    };
66    parser.parse_program()
67}
68
69impl Parser {
70    fn parse_program(&mut self) -> Result<Program, SyntaxError> {
71        let mut body = Vec::new();
72        while self.pos < self.tokens.len() {
73            if self.consume(&Token::Semicolon)
74                || self.consume(&Token::Comma)
75                || self.consume(&Token::Newline)
76            {
77                continue;
78            }
79            body.push(self.parse_stmt_with_semicolon()?);
80        }
81        Ok(Program { body })
82    }
83
84    fn finalize_stmt(&self, stmt: Stmt, is_semicolon_terminated: bool) -> Stmt {
85        match stmt {
86            Stmt::ExprStmt(expr, _, span) => Stmt::ExprStmt(expr, is_semicolon_terminated, span),
87            Stmt::Assign(name, expr, _, span) => {
88                Stmt::Assign(name, expr, is_semicolon_terminated, span)
89            }
90            Stmt::MultiAssign(names, expr, _, span) => {
91                Stmt::MultiAssign(names, expr, is_semicolon_terminated, span)
92            }
93            Stmt::AssignLValue(lv, expr, _, span) => {
94                Stmt::AssignLValue(lv, expr, is_semicolon_terminated, span)
95            }
96            other => other,
97        }
98    }
99}