1use super::ast::{AstExpression, BinaryOp, ExprKind, UnaryOp};
2use super::diagnostics::{Diagnostic, DiagnosticReport};
3use super::lexer::{Token, TokenKind, tokenize};
4use super::span::SourceSpan;
5
6const MAX_PARSE_RECURSION_DEPTH: usize = 256;
7const MAX_AST_SERIALIZED_DEPTH: usize = 127;
11const PARSE_RECURSION_DEPTH_EXCEEDED: &str = "parse recursion depth limit exceeded";
12const AST_DEPTH_EXCEEDED: &str = "AST depth limit exceeded";
13
14pub fn parse_expression(input: &str) -> Result<AstExpression, DiagnosticReport> {
15 let tokens = tokenize(input).map_err(DiagnosticReport::new)?;
16 Parser::new(tokens).parse()
17}
18
19struct ParsedExpression {
20 ast: AstExpression,
21 serialized_depth: usize,
22}
23
24impl ParsedExpression {
25 fn leaf(kind: ExprKind, span: SourceSpan) -> Self {
26 Self {
27 ast: AstExpression::new(kind, span),
28 serialized_depth: 2,
29 }
30 }
31
32 fn checked(
33 kind: ExprKind,
34 span: SourceSpan,
35 serialized_depth: usize,
36 ) -> Result<Self, DiagnosticReport> {
37 if serialized_depth > MAX_AST_SERIALIZED_DEPTH {
38 Err(DiagnosticReport::single(AST_DEPTH_EXCEEDED, span))
39 } else {
40 Ok(Self {
41 ast: AstExpression::new(kind, span),
42 serialized_depth,
43 })
44 }
45 }
46
47 fn span(&self) -> SourceSpan {
48 self.ast.span
49 }
50}
51
52#[derive(Default)]
53struct ParsedSequence {
54 expressions: Vec<AstExpression>,
55 max_serialized_depth: usize,
56}
57
58impl ParsedSequence {
59 fn push(&mut self, expression: ParsedExpression) {
60 self.max_serialized_depth = self.max_serialized_depth.max(expression.serialized_depth);
61 self.expressions.push(expression.ast);
62 }
63}
64
65struct Parser {
66 tokens: Vec<Token>,
67 position: usize,
68 recursion_depth: usize,
69}
70
71impl Parser {
72 fn new(tokens: Vec<Token>) -> Self {
73 Self {
74 tokens,
75 position: 0,
76 recursion_depth: 0,
77 }
78 }
79
80 fn parse(mut self) -> Result<AstExpression, DiagnosticReport> {
81 let expression = self.parse_or()?;
82 if !matches!(self.peek().kind, TokenKind::Eof) {
83 return Err(self.error_here("unexpected token after expression"));
84 }
85 Ok(expression.ast)
86 }
87
88 fn parse_or(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
89 let mut expression = self.parse_and()?;
90 while self
91 .consume_kind(|kind| matches!(kind, TokenKind::OrOr))
92 .is_some()
93 {
94 let right = self.parse_and()?;
95 expression = binary(expression, BinaryOp::Or, right)?;
96 }
97 Ok(expression)
98 }
99
100 fn parse_and(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
101 let mut expression = self.parse_equality()?;
102 while self
103 .consume_kind(|kind| matches!(kind, TokenKind::AndAnd))
104 .is_some()
105 {
106 let right = self.parse_equality()?;
107 expression = binary(expression, BinaryOp::And, right)?;
108 }
109 Ok(expression)
110 }
111
112 fn parse_equality(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
113 let mut expression = self.parse_comparison()?;
114 loop {
115 let op = if self
116 .consume_kind(|kind| matches!(kind, TokenKind::EqEq))
117 .is_some()
118 {
119 Some(BinaryOp::Eq)
120 } else if self
121 .consume_kind(|kind| matches!(kind, TokenKind::Ne))
122 .is_some()
123 {
124 Some(BinaryOp::Ne)
125 } else {
126 None
127 };
128 let Some(op) = op else {
129 break;
130 };
131 let right = self.parse_comparison()?;
132 expression = binary(expression, op, right)?;
133 }
134 Ok(expression)
135 }
136
137 fn parse_comparison(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
138 let mut expression = self.parse_additive()?;
139 loop {
140 let op = if self
141 .consume_kind(|kind| matches!(kind, TokenKind::Lt))
142 .is_some()
143 {
144 Some(BinaryOp::Lt)
145 } else if self
146 .consume_kind(|kind| matches!(kind, TokenKind::Le))
147 .is_some()
148 {
149 Some(BinaryOp::Le)
150 } else if self
151 .consume_kind(|kind| matches!(kind, TokenKind::Gt))
152 .is_some()
153 {
154 Some(BinaryOp::Gt)
155 } else if self
156 .consume_kind(|kind| matches!(kind, TokenKind::Ge))
157 .is_some()
158 {
159 Some(BinaryOp::Ge)
160 } else {
161 None
162 };
163 let Some(op) = op else {
164 break;
165 };
166 let right = self.parse_additive()?;
167 expression = binary(expression, op, right)?;
168 }
169 Ok(expression)
170 }
171
172 fn parse_additive(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
173 let mut expression = self.parse_multiplicative()?;
174 loop {
175 let op = if self
176 .consume_kind(|kind| matches!(kind, TokenKind::Plus))
177 .is_some()
178 {
179 Some(BinaryOp::Add)
180 } else if self
181 .consume_kind(|kind| matches!(kind, TokenKind::Minus))
182 .is_some()
183 {
184 Some(BinaryOp::Sub)
185 } else {
186 None
187 };
188 let Some(op) = op else {
189 break;
190 };
191 let right = self.parse_multiplicative()?;
192 expression = binary(expression, op, right)?;
193 }
194 Ok(expression)
195 }
196
197 fn parse_multiplicative(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
198 let mut expression = self.parse_unary()?;
199 loop {
200 let op = if self
201 .consume_kind(|kind| matches!(kind, TokenKind::Star))
202 .is_some()
203 {
204 Some(BinaryOp::Mul)
205 } else if self
206 .consume_kind(|kind| matches!(kind, TokenKind::Slash))
207 .is_some()
208 {
209 Some(BinaryOp::Div)
210 } else if self
211 .consume_kind(|kind| matches!(kind, TokenKind::Percent))
212 .is_some()
213 {
214 Some(BinaryOp::Rem)
215 } else {
216 None
217 };
218 let Some(op) = op else {
219 break;
220 };
221 let right = self.parse_unary()?;
222 expression = binary(expression, op, right)?;
223 }
224 Ok(expression)
225 }
226
227 fn parse_unary(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
228 if let Some(token) = self.consume_kind(|kind| matches!(kind, TokenKind::Bang)) {
229 let expr = self.parse_nested(token.span, |parser| parser.parse_unary())?;
230 let span = token.span.join(expr.span());
231 let serialized_depth = 2 + expr.serialized_depth;
232 return ParsedExpression::checked(
233 ExprKind::Unary {
234 op: UnaryOp::Not,
235 expr: Box::new(expr.ast),
236 },
237 span,
238 serialized_depth,
239 );
240 }
241
242 if let Some(token) = self.consume_kind(|kind| matches!(kind, TokenKind::Minus)) {
243 let expr = self.parse_nested(token.span, |parser| parser.parse_unary())?;
244 let span = token.span.join(expr.span());
245 let serialized_depth = 2 + expr.serialized_depth;
246 return ParsedExpression::checked(
247 ExprKind::Unary {
248 op: UnaryOp::Neg,
249 expr: Box::new(expr.ast),
250 },
251 span,
252 serialized_depth,
253 );
254 }
255
256 self.parse_postfix()
257 }
258
259 fn parse_postfix(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
260 let mut expression = self.parse_primary()?;
261 while self
262 .consume_kind(|kind| matches!(kind, TokenKind::Dot))
263 .is_some()
264 {
265 let name = self.expect_identifier()?;
266 if self
267 .consume_kind(|kind| matches!(kind, TokenKind::LParen))
268 .is_some()
269 {
270 let (args, end_span) = self.parse_call_args()?;
271 let span = expression.span().join(end_span);
272 let serialized_depth = (2 + expression.serialized_depth).max(3 + args.max_serialized_depth);
273 expression = ParsedExpression::checked(
274 ExprKind::MethodCall {
275 receiver: Box::new(expression.ast),
276 name,
277 args: args.expressions,
278 },
279 span,
280 serialized_depth,
281 )?;
282 } else {
283 let span = expression.span().join(self.previous_span());
284 let serialized_depth = 2 + expression.serialized_depth;
285 expression = ParsedExpression::checked(
286 ExprKind::Member {
287 receiver: Box::new(expression.ast),
288 name,
289 },
290 span,
291 serialized_depth,
292 )?;
293 }
294 }
295 Ok(expression)
296 }
297
298 fn parse_primary(&mut self) -> Result<ParsedExpression, DiagnosticReport> {
299 let token = self.advance().clone();
300 match token.kind {
301 TokenKind::True => Ok(ParsedExpression::leaf(
302 ExprKind::Bool { value: true },
303 token.span,
304 )),
305 TokenKind::False => Ok(ParsedExpression::leaf(
306 ExprKind::Bool { value: false },
307 token.span,
308 )),
309 TokenKind::Null => Ok(ParsedExpression::leaf(ExprKind::Null, token.span)),
310 TokenKind::Int(value) => Ok(ParsedExpression::leaf(ExprKind::Int { value }, token.span)),
311 TokenKind::Float(value) => Ok(ParsedExpression::leaf(
312 ExprKind::Float { value },
313 token.span,
314 )),
315 TokenKind::String(value) => Ok(ParsedExpression::leaf(
316 ExprKind::String { value },
317 token.span,
318 )),
319 TokenKind::Identifier(name) => {
320 validate_identifier(&name, token.span)?;
321 if self
322 .consume_kind(|kind| matches!(kind, TokenKind::LParen))
323 .is_some()
324 {
325 let (args, end_span) = self.parse_call_args()?;
326 let serialized_depth = 3 + args.max_serialized_depth;
327 ParsedExpression::checked(
328 ExprKind::FunctionCall {
329 name,
330 args: args.expressions,
331 },
332 token.span.join(end_span),
333 serialized_depth,
334 )
335 } else {
336 Ok(ParsedExpression::leaf(
337 ExprKind::Identifier { name },
338 token.span,
339 ))
340 }
341 }
342 TokenKind::LParen => {
343 let expression = self.parse_nested(token.span, |parser| parser.parse_or())?;
344 self.expect_kind("expected closing parenthesis", |kind| {
345 matches!(kind, TokenKind::RParen)
346 })?;
347 Ok(expression)
348 }
349 TokenKind::LBracket => self.parse_array(token.span),
350 _ => Err(DiagnosticReport::single("expected expression", token.span)),
351 }
352 }
353
354 fn parse_array(&mut self, start_span: SourceSpan) -> Result<ParsedExpression, DiagnosticReport> {
355 let mut items = ParsedSequence::default();
356 if let Some(end) = self.consume_kind(|kind| matches!(kind, TokenKind::RBracket)) {
357 return ParsedExpression::checked(
358 ExprKind::Array {
359 items: items.expressions,
360 },
361 start_span.join(end.span),
362 3,
363 );
364 }
365
366 loop {
367 items.push(self.parse_nested(start_span, |parser| parser.parse_or())?);
368 if let Some(end) = self.consume_kind(|kind| matches!(kind, TokenKind::RBracket)) {
369 let serialized_depth = 3 + items.max_serialized_depth;
370 return ParsedExpression::checked(
371 ExprKind::Array {
372 items: items.expressions,
373 },
374 start_span.join(end.span),
375 serialized_depth,
376 );
377 }
378 self.expect_kind("expected comma in array literal", |kind| {
379 matches!(kind, TokenKind::Comma)
380 })?;
381 }
382 }
383
384 fn parse_call_args(&mut self) -> Result<(ParsedSequence, SourceSpan), DiagnosticReport> {
385 let mut args = ParsedSequence::default();
386 if let Some(end) = self.consume_kind(|kind| matches!(kind, TokenKind::RParen)) {
387 return Ok((args, end.span));
388 }
389
390 loop {
391 let span = self.peek().span;
392 args.push(self.parse_nested(span, |parser| parser.parse_or())?);
393 if let Some(end) = self.consume_kind(|kind| matches!(kind, TokenKind::RParen)) {
394 return Ok((args, end.span));
395 }
396 self.expect_kind("expected comma in argument list", |kind| {
397 matches!(kind, TokenKind::Comma)
398 })?;
399 }
400 }
401
402 fn expect_identifier(&mut self) -> Result<String, DiagnosticReport> {
403 let token = self.advance().clone();
404 match token.kind {
405 TokenKind::Identifier(name) => {
406 validate_identifier(&name, token.span)?;
407 Ok(name)
408 }
409 _ => Err(DiagnosticReport::single("expected identifier", token.span)),
410 }
411 }
412
413 fn expect_kind(
414 &mut self,
415 message: &'static str,
416 predicate: impl FnOnce(&TokenKind) -> bool,
417 ) -> Result<Token, DiagnosticReport> {
418 let token = self.advance().clone();
419 if predicate(&token.kind) {
420 Ok(token)
421 } else {
422 Err(DiagnosticReport::single(message, token.span))
423 }
424 }
425
426 fn consume_kind(&mut self, predicate: impl FnOnce(&TokenKind) -> bool) -> Option<Token> {
427 if predicate(&self.peek().kind) {
428 let token = self.peek().clone();
429 self.position += 1;
430 Some(token)
431 } else {
432 None
433 }
434 }
435
436 fn advance(&mut self) -> &Token {
437 let index = self.position.min(self.tokens.len().saturating_sub(1));
438 if !matches!(self.tokens[index].kind, TokenKind::Eof) {
439 self.position += 1;
440 }
441 &self.tokens[index]
442 }
443
444 fn peek(&self) -> &Token {
445 self.tokens.get(self.position).unwrap_or_else(|| {
446 self
447 .tokens
448 .last()
449 .expect("parser requires lexer to append an EOF token")
450 })
451 }
452
453 fn previous_span(&self) -> SourceSpan {
454 self
455 .tokens
456 .get(self.position.saturating_sub(1))
457 .map(|token| token.span)
458 .unwrap_or_default()
459 }
460
461 fn error_here(&self, message: &'static str) -> DiagnosticReport {
462 DiagnosticReport::single(message, self.peek().span)
463 }
464
465 fn parse_nested<T>(
466 &mut self,
467 span: SourceSpan,
468 parse: impl FnOnce(&mut Self) -> Result<T, DiagnosticReport>,
469 ) -> Result<T, DiagnosticReport> {
470 if self.recursion_depth >= MAX_PARSE_RECURSION_DEPTH {
471 return Err(DiagnosticReport::single(
472 PARSE_RECURSION_DEPTH_EXCEEDED,
473 span,
474 ));
475 }
476 self.recursion_depth += 1;
477 let result = parse(self);
478 self.recursion_depth -= 1;
479 result
480 }
481}
482
483fn binary(
484 left: ParsedExpression,
485 op: BinaryOp,
486 right: ParsedExpression,
487) -> Result<ParsedExpression, DiagnosticReport> {
488 let span = left.span().join(right.span());
489 let serialized_depth = 2 + left.serialized_depth.max(right.serialized_depth);
490 ParsedExpression::checked(
491 ExprKind::Binary {
492 left: Box::new(left.ast),
493 op,
494 right: Box::new(right.ast),
495 },
496 span,
497 serialized_depth,
498 )
499}
500
501fn validate_identifier(identifier: &str, span: SourceSpan) -> Result<(), DiagnosticReport> {
502 if is_reserved_identifier(identifier) {
503 Err(DiagnosticReport::new(vec![Diagnostic::new(
504 format!("reserved identifier {identifier}"),
505 span,
506 )]))
507 } else {
508 Ok(())
509 }
510}
511
512fn is_reserved_identifier(identifier: &str) -> bool {
513 matches!(
514 identifier,
515 "if"
516 | "else"
517 | "for"
518 | "while"
519 | "do"
520 | "switch"
521 | "let"
522 | "const"
523 | "function"
524 | "import"
525 | "export"
526 | "new"
527 | "try"
528 | "catch"
529 | "throw"
530 | "await"
531 | "return"
532 | "true"
533 | "false"
534 | "null"
535 )
536}
537
538#[cfg(test)]
539mod tests {
540 use crate::format_expression;
541
542 use super::parse_expression;
543
544 #[test]
545 fn parses_precedence() {
546 let ast = parse_expression("1 + 2 * 3 == 7 || false").expect("expression should parse");
547 assert_eq!(format_expression(&ast), "1 + 2 * 3 == 7 || false");
548 }
549
550 #[test]
551 fn parses_calls_members_and_arrays() {
552 let ast = parse_expression("user.name.starts_with('pi') && len([1, 2]) == 2")
553 .expect("expression should parse");
554 assert_eq!(
555 format_expression(&ast),
556 "user.name.starts_with(\"pi\") && len([1, 2]) == 2"
557 );
558 }
559}