Skip to main content

trident/syntax/parser/
mod.rs

1mod expr;
2mod items;
3mod stmts;
4mod types;
5
6#[cfg(test)]
7mod tests;
8
9use crate::ast::*;
10use crate::diagnostic::Diagnostic;
11use crate::lexeme::Lexeme;
12use crate::span::{Span, Spanned};
13
14const MAX_NESTING_DEPTH: u32 = 256;
15
16pub(crate) struct Parser {
17    tokens: Vec<Spanned<Lexeme>>,
18    pos: usize,
19    diagnostics: Vec<Diagnostic>,
20    depth: u32,
21    /// Source bytes for newline detection (empty if unavailable).
22    source: Vec<u8>,
23}
24
25impl Parser {
26    pub(crate) fn new(tokens: Vec<Spanned<Lexeme>>) -> Self {
27        Self {
28            tokens,
29            pos: 0,
30            diagnostics: Vec::new(),
31            depth: 0,
32            source: Vec::new(),
33        }
34    }
35
36    pub(crate) fn new_with_source(tokens: Vec<Spanned<Lexeme>>, source: &str) -> Self {
37        Self {
38            tokens,
39            pos: 0,
40            diagnostics: Vec::new(),
41            depth: 0,
42            source: source.as_bytes().to_vec(),
43        }
44    }
45
46    /// Check if two spans are on the same line (no newline between them).
47    /// Returns true if source is unavailable (conservative: assume same line).
48    fn same_line(&self, a: Span, b: Span) -> bool {
49        if self.source.is_empty() {
50            return true;
51        }
52        let start = a.end as usize;
53        let end = (b.start as usize).min(self.source.len());
54        if start >= end {
55            return true;
56        }
57        !self.source[start..end].contains(&b'\n')
58    }
59
60    pub(crate) fn parse_file(mut self) -> Result<File, Vec<Diagnostic>> {
61        let file = if self.at(&Lexeme::Program) {
62            self.parse_program()
63        } else if self.at(&Lexeme::Module) {
64            self.parse_module()
65        } else {
66            self.error_with_help(
67                "expected 'program' or 'module' declaration at the start of file",
68                "every .tri file must begin with `program <name>` or `module <name>`",
69            );
70            return Err(self.diagnostics);
71        };
72
73        if !self.diagnostics.is_empty() {
74            return Err(self.diagnostics);
75        }
76        Ok(file)
77    }
78
79    fn enter_nesting(&mut self) -> bool {
80        self.depth += 1;
81        if self.depth > MAX_NESTING_DEPTH {
82            self.error_with_help(
83                "nesting depth exceeded (maximum 256 levels)",
84                "simplify your program by extracting deeply nested code into functions",
85            );
86            return false;
87        }
88        true
89    }
90
91    fn exit_nesting(&mut self) {
92        self.depth -= 1;
93    }
94
95    // --- Utility methods ---
96
97    fn peek(&self) -> &Lexeme {
98        &self.tokens[self.pos].node
99    }
100
101    fn current_span(&self) -> Span {
102        self.tokens[self.pos].span
103    }
104
105    fn prev_span(&self) -> Span {
106        if self.pos > 0 {
107            self.tokens[self.pos - 1].span
108        } else {
109            self.current_span()
110        }
111    }
112
113    fn advance(&mut self) -> &Spanned<Lexeme> {
114        let tok = &self.tokens[self.pos];
115        if self.pos < self.tokens.len() - 1 {
116            self.pos += 1;
117        }
118        tok
119    }
120
121    fn at(&self, token: &Lexeme) -> bool {
122        std::mem::discriminant(self.peek()) == std::mem::discriminant(token)
123    }
124
125    fn eat(&mut self, token: &Lexeme) -> bool {
126        if self.at(token) {
127            self.advance();
128            true
129        } else {
130            false
131        }
132    }
133
134    fn expect(&mut self, token: &Lexeme) -> Span {
135        if self.at(token) {
136            let span = self.current_span();
137            self.advance();
138            span
139        } else {
140            self.error_at_current(&format!(
141                "expected {}, found {}",
142                token.description(),
143                self.peek().description()
144            ));
145            self.current_span()
146        }
147    }
148
149    fn expect_ident(&mut self) -> Spanned<String> {
150        if let Lexeme::Ident(name) = self.peek().clone() {
151            let span = self.current_span();
152            self.advance();
153            Spanned::new(name, span)
154        } else {
155            self.error_at_current(&format!(
156                "expected identifier, found {}",
157                self.peek().description()
158            ));
159            Spanned::new("_error_".to_string(), self.current_span())
160        }
161    }
162
163    fn try_ident(&mut self) -> Option<Spanned<String>> {
164        if let Lexeme::Ident(name) = self.peek().clone() {
165            let span = self.current_span();
166            self.advance();
167            Some(Spanned::new(name, span))
168        } else {
169            None
170        }
171    }
172
173    fn expect_integer(&mut self) -> u64 {
174        if let Lexeme::Integer(n) = self.peek() {
175            let n = *n;
176            self.advance();
177            n
178        } else {
179            self.error_at_current(&format!(
180                "expected integer literal, found {}",
181                self.peek().description()
182            ));
183            0
184        }
185    }
186
187    fn error_at_current(&mut self, msg: &str) {
188        self.diagnostics
189            .push(Diagnostic::error(msg.to_string(), self.current_span()));
190    }
191
192    fn error_with_help(&mut self, msg: &str, help: &str) {
193        self.diagnostics.push(
194            Diagnostic::error(msg.to_string(), self.current_span()).with_help(help.to_string()),
195        );
196    }
197
198    fn parse_module_path(&mut self) -> ModulePath {
199        let first = self.expect_ident();
200        let mut parts = vec![first.node];
201        while self.eat(&Lexeme::Dot) {
202            if let Some(ident) = self.try_ident() {
203                parts.push(ident.node);
204            } else {
205                break;
206            }
207        }
208        ModulePath(parts)
209    }
210}