diff options
| author | Tolmachev Igor <me@igorek.dev> | 2026-05-09 18:06:25 +0300 |
|---|---|---|
| committer | Tolmachev Igor <me@igorek.dev> | 2026-05-09 18:06:25 +0300 |
| commit | c628391e10c6db7ca8b979e20d5b09ac92251c96 (patch) | |
| tree | 508084a57b7d916b2140583bdb11c2b9526b682c /compiler/src/ast | |
| parent | bf89132011e906384591bc85dead16d64a150a02 (diff) | |
| download | crisp-c628391e10c6db7ca8b979e20d5b09ac92251c96.tar.gz crisp-c628391e10c6db7ca8b979e20d5b09ac92251c96.zip | |
Add RecursionLimit parser error
Returns error after 256 levels of nesting to avoid stack overflow. The limit is hardcoded now, but
will become a compiler flag later.
Diffstat (limited to 'compiler/src/ast')
| -rw-r--r-- | compiler/src/ast/error.rs | 2 | ||||
| -rw-r--r-- | compiler/src/ast/parser.rs | 10 |
2 files changed, 12 insertions, 0 deletions
diff --git a/compiler/src/ast/error.rs b/compiler/src/ast/error.rs index ced2231..21102ea 100644 --- a/compiler/src/ast/error.rs +++ b/compiler/src/ast/error.rs | |||
| @@ -19,6 +19,8 @@ pub enum Error { | |||
| 19 | UnclosedLeftPar, | 19 | UnclosedLeftPar, |
| 20 | 20 | ||
| 21 | UnexpectedEof, | 21 | UnexpectedEof, |
| 22 | |||
| 23 | RecursionLimit, | ||
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | impl From<ParseFloatError> for Error { | 26 | impl From<ParseFloatError> for Error { |
diff --git a/compiler/src/ast/parser.rs b/compiler/src/ast/parser.rs index 6b11cc6..8e476ad 100644 --- a/compiler/src/ast/parser.rs +++ b/compiler/src/ast/parser.rs | |||
| @@ -6,6 +6,8 @@ use crate::{ | |||
| 6 | span::{Pos, Span, Spanned}, | 6 | span::{Pos, Span, Spanned}, |
| 7 | }; | 7 | }; |
| 8 | 8 | ||
| 9 | const MAX_DEPTH: usize = 256; // TODO: make it a compile flag | ||
| 10 | |||
| 9 | fn parse_number(number: &str) -> Result<Atom, Error> { | 11 | fn parse_number(number: &str) -> Result<Atom, Error> { |
| 10 | let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')) | 12 | let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')) |
| 11 | || matches!(number, "inf" | "+inf" | "-inf" | "nan"); | 13 | || matches!(number, "inf" | "+inf" | "-inf" | "nan"); |
| @@ -66,6 +68,7 @@ where | |||
| 66 | { | 68 | { |
| 67 | tokens: Peekable<I>, | 69 | tokens: Peekable<I>, |
| 68 | last_token_span: Span, | 70 | last_token_span: Span, |
| 71 | depth: usize, | ||
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | impl<'a, I> Parser<'a, I> | 74 | impl<'a, I> Parser<'a, I> |
| @@ -76,6 +79,7 @@ where | |||
| 76 | Self { | 79 | Self { |
| 77 | tokens: tokens.peekable(), | 80 | tokens: tokens.peekable(), |
| 78 | last_token_span: Span::new(Pos::new(1, 0, 0), Pos::new(1, 0, 0)), | 81 | last_token_span: Span::new(Pos::new(1, 0, 0), Pos::new(1, 0, 0)), |
| 82 | depth: 0, | ||
| 79 | } | 83 | } |
| 80 | } | 84 | } |
| 81 | 85 | ||
| @@ -147,6 +151,11 @@ where | |||
| 147 | fn parse_list(&mut self, left_par_span: Span) -> Result<Vec<Spanned<Expr>>, Spanned<Error>> { | 151 | fn parse_list(&mut self, left_par_span: Span) -> Result<Vec<Spanned<Expr>>, Spanned<Error>> { |
| 148 | let mut list = Vec::new(); | 152 | let mut list = Vec::new(); |
| 149 | 153 | ||
| 154 | self.depth += 1; | ||
| 155 | if self.depth >= MAX_DEPTH { | ||
| 156 | return Err(Spanned::new(Error::RecursionLimit, self.last_token_span)); | ||
| 157 | } | ||
| 158 | |||
| 150 | while let Some(Spanned { inner: token, .. }) = self.peek() { | 159 | while let Some(Spanned { inner: token, .. }) = self.peek() { |
| 151 | match token { | 160 | match token { |
| 152 | Token::RightPar => { | 161 | Token::RightPar => { |
| @@ -156,6 +165,7 @@ where | |||
| 156 | _ => list.push(self.parse_expr()?), | 165 | _ => list.push(self.parse_expr()?), |
| 157 | } | 166 | } |
| 158 | } | 167 | } |
| 168 | self.depth -= 1; | ||
| 159 | 169 | ||
| 160 | Err(Spanned::new(Error::UnclosedLeftPar, left_par_span)) | 170 | Err(Spanned::new(Error::UnclosedLeftPar, left_par_span)) |
| 161 | } | 171 | } |
