Skip to content

Commit 60aa2dc

Browse files
committed
Add recursion limit
1 parent 330b353 commit 60aa2dc

4 files changed

Lines changed: 52 additions & 13 deletions

File tree

rulex-lib/src/error/parse_error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ pub(crate) enum ParseErrorKind {
9595
#[error(transparent)]
9696
Unsupported(UnsupportedError),
9797

98+
#[error("Recursion limit reached. Try a less nested expression")]
99+
RecursionLimit,
100+
98101
#[error("Unknown error: {:?}", .0)]
99102
Nom(nom::error::ErrorKind),
100103
#[error("Incomplete parse")]

rulex-lib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl<'i> Rulex<'i> {
8484
/// The parsed `Rulex` can be displayed with `Debug` if the `dbg` feature is
8585
/// enabled.
8686
pub fn parse(input: &'i str, options: ParseOptions) -> Result<Self, ParseError> {
87-
let rule = parse::parse(input)?;
87+
let rule = parse::parse(input, 256)?;
8888
rule.validate(&options)?;
8989
Ok(Rulex(rule))
9090
}

rulex-lib/src/parse/input.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ use super::token::Token;
1313
pub(crate) struct Input<'i, 'b> {
1414
source: &'i str,
1515
tokens: &'b [(Token, Span)],
16+
recursion: u16,
1617
}
1718

1819
impl<'i, 'b> Input<'i, 'b> {
19-
pub(super) fn from(source: &'i str, tokens: &'b [(Token, Span)]) -> Result<Self, ParseError> {
20+
pub(super) fn from(
21+
source: &'i str,
22+
tokens: &'b [(Token, Span)],
23+
recursion: u16,
24+
) -> Result<Self, ParseError> {
2025
let error = tokens.iter().find_map(|&(t, span)| match t {
2126
Token::Error => Some((span, None)),
2227
Token::ErrorMsg(m) => Some((span, Some(m))),
@@ -29,7 +34,19 @@ impl<'i, 'b> Input<'i, 'b> {
2934
};
3035
}
3136

32-
Ok(Input { source, tokens })
37+
Ok(Input { source, tokens, recursion })
38+
}
39+
40+
pub(super) fn recursion_start(&mut self) -> Result<(), ParseError> {
41+
self.recursion = self
42+
.recursion
43+
.checked_sub(1)
44+
.ok_or_else(|| ParseErrorKind::RecursionLimit.at(self.span()))?;
45+
Ok(())
46+
}
47+
48+
pub(super) fn recursion_end(&mut self) {
49+
self.recursion += 1;
3350
}
3451

3552
pub(super) fn is_empty(&self) -> bool {
@@ -101,7 +118,7 @@ impl<'i, 'b> InputIter for Input<'i, 'b> {
101118
}
102119

103120
fn iter_elements(&self) -> Self::IterElem {
104-
Input { source: self.source, tokens: self.tokens }
121+
Input { ..*self }
105122
}
106123

107124
fn position<P>(&self, predicate: P) -> Option<usize>
@@ -131,12 +148,12 @@ impl<'i, 'b> InputTake for Input<'i, 'b> {
131148
fn take(&self, count: usize) -> Self {
132149
let tokens = &self.tokens[..count];
133150

134-
Input { source: self.source, tokens }
151+
Input { tokens, ..*self }
135152
}
136153

137154
fn take_split(&self, count: usize) -> (Self, Self) {
138155
let (left, right) = self.tokens.split_at(count);
139156

140-
(Input { source: self.source, tokens: left }, Input { source: self.source, tokens: right })
157+
(Input { tokens: left, ..*self }, Input { tokens: right, ..*self })
141158
}
142159
}

rulex-lib/src/parse/parsers.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ use super::{Input, Token};
3535

3636
pub(super) type PResult<'i, 'b, T> = IResult<Input<'i, 'b>, T, ParseError>;
3737

38-
pub(crate) fn parse(source: &str) -> Result<Rule<'_>, ParseError> {
38+
pub(crate) fn parse(source: &str, recursion: u16) -> Result<Rule<'_>, ParseError> {
3939
let tokens = super::tokenize::tokenize(source);
40-
let input = Input::from(source, &tokens)?;
40+
let input = Input::from(source, &tokens, recursion)?;
4141

4242
let (rest, rules) = parse_modified(input)?;
4343
if rest.is_empty() {
@@ -47,6 +47,22 @@ pub(crate) fn parse(source: &str) -> Result<Rule<'_>, ParseError> {
4747
}
4848
}
4949

50+
fn recurse<'i, 'b, O>(
51+
mut parser: impl Parser<Input<'i, 'b>, O, ParseError>,
52+
) -> impl FnMut(Input<'i, 'b>) -> PResult<'i, 'b, O> {
53+
move |mut input| {
54+
input.recursion_start().map_err(nom::Err::Failure)?;
55+
56+
match parser.parse(input) {
57+
Ok((mut input, output)) => {
58+
input.recursion_end();
59+
Ok((input, output))
60+
}
61+
Err(e) => Err(e),
62+
}
63+
}
64+
}
65+
5066
pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<'i>> {
5167
enum ModifierKind {
5268
Enable,
@@ -78,15 +94,15 @@ pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru
7894
"let",
7995
cut(Token::Identifier),
8096
cut(Token::Equals),
81-
cut(parse_or),
97+
cut(recurse(parse_or)),
8298
cut(Token::Semicolon),
8399
)),
84100
|((_, span_start), (name, name_span), _, rule, (_, span_end))| {
85101
(Stmt::Let(Let::new(name, rule, name_span)), span_start.join(span_end))
86102
},
87103
),
88104
))),
89-
parse_or,
105+
recurse(parse_or),
90106
),
91107
|(stmts, mut rule): (Vec<(Stmt, Span)>, _)| {
92108
if stmts.len() > 1 {
@@ -137,7 +153,7 @@ pub(super) fn parse_sequence<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru
137153
pub(super) fn parse_fixes<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<'i>> {
138154
alt((
139155
try_map(
140-
pair(Token::Not, opt(parse_fixes)),
156+
pair(Token::Not, opt(recurse(parse_fixes))),
141157
|(_, rule)| {
142158
if let Some(mut rule) = rule {
143159
rule.negate()?;
@@ -148,7 +164,7 @@ pub(super) fn parse_fixes<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<
148164
},
149165
nom::Err::Failure,
150166
),
151-
map(pair(parse_lookaround, parse_modified), |((kind, span), rule)| {
167+
map(pair(parse_lookaround, recurse(parse_modified)), |((kind, span), rule)| {
152168
let span = span.join(rule.span());
153169
Rule::Lookaround(Box::new(Lookaround::new(rule, kind, span)))
154170
}),
@@ -282,7 +298,10 @@ pub(super) fn parse_group<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<
282298
}
283299

284300
map(
285-
pair(opt(parse_capture), tuple((Token::OpenParen, parse_modified, cut(Token::CloseParen)))),
301+
pair(
302+
opt(parse_capture),
303+
tuple((Token::OpenParen, recurse(parse_modified), cut(Token::CloseParen))),
304+
),
286305
|(capture, (_, rule, (_, close_paren)))| match (capture, rule) {
287306
(None, rule) => rule,
288307
(Some((capture, c_span)), Rule::Group(mut g)) if !g.is_capturing() => {

0 commit comments

Comments
 (0)