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!(
51 t.token,
52 Token::Ellipsis | Token::Section | Token::LineComment | Token::BlockComment
53 ) {
54 skip_newlines = matches!(t.token, Token::Ellipsis);
58 continue;
59 }
60 if skip_newlines && matches!(t.token, Token::Newline) {
61 continue;
62 }
63 skip_newlines = false;
64 tokens.push(TokenInfo {
65 token: t.token,
66 lexeme: t.lexeme,
67 position: t.start,
68 end: t.end,
69 });
70 }
71
72 let mut parser = Parser {
73 tokens,
74 pos: 0,
75 input: input.to_string(),
76 options,
77 in_matrix_expr: false,
78 current_classdef_name: None,
79 };
80 parser.parse_program()
81}
82
83impl Parser {
84 fn parse_program(&mut self) -> Result<Program, SyntaxError> {
85 let mut body = Vec::new();
86 while self.pos < self.tokens.len() {
87 if self.consume(&Token::Semicolon)
88 || self.consume(&Token::Comma)
89 || self.consume(&Token::Newline)
90 {
91 continue;
92 }
93 body.push(self.parse_stmt_with_semicolon()?);
94 }
95 Ok(Program { body })
96 }
97
98 fn finalize_stmt(&self, stmt: Stmt, is_semicolon_terminated: bool) -> Stmt {
99 match stmt {
100 Stmt::ExprStmt(expr, _, span) => Stmt::ExprStmt(expr, is_semicolon_terminated, span),
101 Stmt::Assign(name, expr, _, span) => {
102 Stmt::Assign(name, expr, is_semicolon_terminated, span)
103 }
104 Stmt::MultiAssign(names, expr, _, span) => {
105 Stmt::MultiAssign(names, expr, is_semicolon_terminated, span)
106 }
107 Stmt::AssignLValue(lv, expr, _, span) => {
108 Stmt::AssignLValue(lv, expr, is_semicolon_terminated, span)
109 }
110 other => other,
111 }
112 }
113}