Skip to content

Commit

Permalink
Add recursion limit
Browse files Browse the repository at this point in the history
  • Loading branch information
Aloso committed Jun 18, 2022
1 parent 330b353 commit 60aa2dc
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 13 deletions.
3 changes: 3 additions & 0 deletions rulex-lib/src/error/parse_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ pub(crate) enum ParseErrorKind {
#[error(transparent)]
Unsupported(UnsupportedError),

#[error("Recursion limit reached. Try a less nested expression")]
RecursionLimit,

#[error("Unknown error: {:?}", .0)]
Nom(nom::error::ErrorKind),
#[error("Incomplete parse")]
Expand Down
2 changes: 1 addition & 1 deletion rulex-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<'i> Rulex<'i> {
/// The parsed `Rulex` can be displayed with `Debug` if the `dbg` feature is
/// enabled.
pub fn parse(input: &'i str, options: ParseOptions) -> Result<Self, ParseError> {
let rule = parse::parse(input)?;
let rule = parse::parse(input, 256)?;
rule.validate(&options)?;
Ok(Rulex(rule))
}
Expand Down
27 changes: 22 additions & 5 deletions rulex-lib/src/parse/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ use super::token::Token;
pub(crate) struct Input<'i, 'b> {
source: &'i str,
tokens: &'b [(Token, Span)],
recursion: u16,
}

impl<'i, 'b> Input<'i, 'b> {
pub(super) fn from(source: &'i str, tokens: &'b [(Token, Span)]) -> Result<Self, ParseError> {
pub(super) fn from(
source: &'i str,
tokens: &'b [(Token, Span)],
recursion: u16,
) -> Result<Self, ParseError> {
let error = tokens.iter().find_map(|&(t, span)| match t {
Token::Error => Some((span, None)),
Token::ErrorMsg(m) => Some((span, Some(m))),
Expand All @@ -29,7 +34,19 @@ impl<'i, 'b> Input<'i, 'b> {
};
}

Ok(Input { source, tokens })
Ok(Input { source, tokens, recursion })
}

pub(super) fn recursion_start(&mut self) -> Result<(), ParseError> {
self.recursion = self
.recursion
.checked_sub(1)
.ok_or_else(|| ParseErrorKind::RecursionLimit.at(self.span()))?;
Ok(())
}

pub(super) fn recursion_end(&mut self) {
self.recursion += 1;
}

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

fn iter_elements(&self) -> Self::IterElem {
Input { source: self.source, tokens: self.tokens }
Input { ..*self }
}

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

Input { source: self.source, tokens }
Input { tokens, ..*self }
}

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

(Input { source: self.source, tokens: left }, Input { source: self.source, tokens: right })
(Input { tokens: left, ..*self }, Input { tokens: right, ..*self })
}
}
33 changes: 26 additions & 7 deletions rulex-lib/src/parse/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ use super::{Input, Token};

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

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

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

fn recurse<'i, 'b, O>(
mut parser: impl Parser<Input<'i, 'b>, O, ParseError>,
) -> impl FnMut(Input<'i, 'b>) -> PResult<'i, 'b, O> {
move |mut input| {
input.recursion_start().map_err(nom::Err::Failure)?;

match parser.parse(input) {
Ok((mut input, output)) => {
input.recursion_end();
Ok((input, output))
}
Err(e) => Err(e),
}
}
}

pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<'i>> {
enum ModifierKind {
Enable,
Expand Down Expand Up @@ -78,15 +94,15 @@ pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru
"let",
cut(Token::Identifier),
cut(Token::Equals),
cut(parse_or),
cut(recurse(parse_or)),
cut(Token::Semicolon),
)),
|((_, span_start), (name, name_span), _, rule, (_, span_end))| {
(Stmt::Let(Let::new(name, rule, name_span)), span_start.join(span_end))
},
),
))),
parse_or,
recurse(parse_or),
),
|(stmts, mut rule): (Vec<(Stmt, Span)>, _)| {
if stmts.len() > 1 {
Expand Down Expand Up @@ -137,7 +153,7 @@ pub(super) fn parse_sequence<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru
pub(super) fn parse_fixes<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<'i>> {
alt((
try_map(
pair(Token::Not, opt(parse_fixes)),
pair(Token::Not, opt(recurse(parse_fixes))),
|(_, rule)| {
if let Some(mut rule) = rule {
rule.negate()?;
Expand All @@ -148,7 +164,7 @@ pub(super) fn parse_fixes<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<
},
nom::Err::Failure,
),
map(pair(parse_lookaround, parse_modified), |((kind, span), rule)| {
map(pair(parse_lookaround, recurse(parse_modified)), |((kind, span), rule)| {
let span = span.join(rule.span());
Rule::Lookaround(Box::new(Lookaround::new(rule, kind, span)))
}),
Expand Down Expand Up @@ -282,7 +298,10 @@ pub(super) fn parse_group<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<
}

map(
pair(opt(parse_capture), tuple((Token::OpenParen, parse_modified, cut(Token::CloseParen)))),
pair(
opt(parse_capture),
tuple((Token::OpenParen, recurse(parse_modified), cut(Token::CloseParen))),
),
|(capture, (_, rule, (_, close_paren)))| match (capture, rule) {
(None, rule) => rule,
(Some((capture, c_span)), Rule::Group(mut g)) if !g.is_capturing() => {
Expand Down

0 comments on commit 60aa2dc

Please # to comment.