Skip to content

Commit

Permalink
Change: improved performance on infix by changing to state approach
Browse files Browse the repository at this point in the history
Instead of assignment left everytime we just assign it on an unfinished
statement. Additionally the check upfront is removed by using states.
  • Loading branch information
nichtsfrei committed Jan 11, 2024
1 parent 1b83a2d commit 5f11b38
Showing 1 changed file with 47 additions and 30 deletions.
77 changes: 47 additions & 30 deletions rust/nasl-syntax/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
postfix_extension::Postfix,
prefix_extension::Prefix,
token::{Category, Token, Tokenizer},
unexpected_statement, unexpected_token, Statement, StatementKind, AssignOrder, unexpected_end,
unexpected_end, unexpected_statement, unexpected_token, AssignOrder, Statement, StatementKind,
};

/// Is used to parse Token to Statement
Expand Down Expand Up @@ -103,7 +103,12 @@ fn first_element_as_named_parameter(mut params: Vec<Statement>) -> Result<Statem
params.reverse();
Ok(Statement::without_token(StatementKind::Parameter(params)))
}

enum InFixState {
NoInfix,
ReturnContinue(Statement),
ReturnEnd(Token, Statement),
Unfinished(Statement),
}
impl<'a> Lexer<'a> {
/// Creates a Lexer
pub fn new(tokenizer: Tokenizer<'a>) -> Lexer<'a> {
Expand Down Expand Up @@ -267,16 +272,40 @@ impl<'a> Lexer<'a> {
})
}

fn needs_infix(&self, op: &Operation, min_bp: u8) -> Option<bool> {
let (l_bp, _) = infix_binding_power(op)?;
if l_bp < min_bp {
Some(false)
} else {
Some(true)
}
/// Returns an infix state
///
/// On NoInfix the operation is not infix based.
/// On ReturnContinue the operation does not have the required binding power.
/// On ReturnEnd the statement is finished.
/// On Unfinished the upper loop should continue while caching the statement.
fn handle_infix(
&mut self,
op: &Operation,
min_bp: u8,
token: Token,
left: Statement,
abort: &impl Fn(&Category) -> bool,
) -> Result<InFixState, SyntaxError> {
// returns three states 1. not handled, 2. return continue, 3. return done 4. continue
// loop

Ok(match infix_binding_power(op) {
None => InFixState::NoInfix,
Some((x, _)) if x < min_bp => InFixState::ReturnContinue(left),
Some(_) => {
self.token();
let (end, nl) = self.infix_statement(op.clone(), token, left, abort)?;
match end {
End::Done(cat) => {
self.depth = 0;
InFixState::ReturnEnd(cat, nl)
}
End::Continue => InFixState::Unfinished(nl),
}
}
})
}


/// Returns the next expression.
///
/// It uses a prefix_extension to verify if a token is prefix relevant and if parsing should continue
Expand Down Expand Up @@ -348,25 +377,14 @@ impl<'a> Lexer<'a> {
}
continue;
}

if let Some(min_bp_reached) = self.needs_infix(&op, min_binding_power) {
if !min_bp_reached {
// TODO could be changed to unexpected statement so that it doesn't need to be done in the iterator
return cont(left);
match self.handle_infix(&op, min_binding_power, token.clone(), left, abort)? {
InFixState::NoInfix => return Err(unexpected_token!(token)),
InFixState::ReturnContinue(left) => return cont(left),
InFixState::ReturnEnd(cat, left) => return done(cat, left),
InFixState::Unfinished(nl) => {
left = nl;
}
self.token();
let (end, nl) = self.infix_statement(op, token, left, abort)?;
left = nl;
if let End::Done(cat) = end {
self.depth = 0;
return done(cat, left);
} else {
// jump to the next without handling it as an error
continue;
}
}
// Due to peeking it can end up in an endless loop
return Err(unexpected_token!(token));
};
}

Ok((End::Continue, left))
Expand Down Expand Up @@ -411,8 +429,7 @@ mod infix {
use super::*;

use crate::token::Category::*;



use StatementKind::*;

// simplified resolve method to verify a calculate with a given statement
Expand Down

0 comments on commit 5f11b38

Please sign in to comment.