//! Simulates parser execution, for a single token of input, without incurring //! any side effects. //! //! This is basically a copy of the parser.rs source code with calls to //! generated_parser::reduce, and stack bookkeeping, omitted.
usecrate::parser::Parser; use arrayvec::ArrayVec; use ast::SourceLocation; use generated_parser::{
noop_actions, ParseError, ParserTrait, Result, StackValue, TermValue, TerminalId, Token, TABLES,
};
/// The Simulator is used to check whether we can shift one token, either to /// check what might be accepted, or to check whether we can End parsing now. /// This is used by the REPL to verify whether or not we can end the input. pubstruct Simulator<'alloc, 'parser> { /// Define the top of the immutable stack.
sp: usize, /// Immutable state stack coming from the forked parser.
state_stack: &'parser [usize], /// Immuatable term stack coming from the forked parser.
node_stack: &'parser [TermValue<StackValue<'alloc>>], /// Mutable state stack used by the simulator on top of the immutable /// parser's state stack. /// /// Uses a fixed-size array as the number of lookahead is bounded to a lower /// value, panics otherwise.
sim_state_stack: ArrayVec<usize, 4>, /// Mutable term stack used by the simulator on top of the immutable /// parser's term stack. /// /// Uses a fixed-size array as the number of lookahead is bounded to a lower /// value, panics otherwise.
sim_node_stack: ArrayVec<TermValue<()>, 4>, /// Mutable term stack used by the simulator for replaying terms when /// reducing non-terminals are replaying lookahead terminals. /// /// Uses a fixed-size array as the number of lookahead is bounded to a lower /// value, panics otherwise.
replay_stack: ArrayVec<TermValue<()>, 4>,
}
impl<'alloc, 'parser> ParserTrait<'alloc, ()> for Simulator<'alloc, 'parser> { fn shift(&mutself, tv: TermValue<()>) -> Result<'alloc, bool> { // Shift the new terminal/nonterminal and its associated value. letmut state = self.state();
assert!(state < TABLES.shift_count); letmut tv = tv; loop { let term_index: usize = tv.term.into();
assert!(term_index < TABLES.shift_width); let index = state * TABLES.shift_width + term_index; let goto = TABLES.shift_table[index]; if goto < 0 { // Error handling is in charge of shifting an ErrorSymbol from the // current state. self.try_error_handling(tv)?;
tv = self.replay_stack.pop().unwrap(); continue;
}
state = goto as usize; self.sim_state_stack.push(state); self.sim_node_stack.push(tv); // Execute any actions, such as reduce actions. if state >= TABLES.shift_count {
assert!(state < TABLES.action_count + TABLES.shift_count); if noop_actions(self, state)? { return Ok(true);
}
state = self.state();
}
assert!(state < TABLES.shift_count); iflet Some(tv_temp) = self.replay_stack.pop() {
tv = tv_temp;
} else { break;
}
}
Ok(false)
} fn shift_replayed(&mutself, state: usize) { let tv = self.replay_stack.pop().unwrap(); self.sim_state_stack.push(state); self.sim_node_stack.push(tv);
} fn unshift(&mutself) { let tv = self.pop(); self.replay(tv)
} fn pop(&mutself) -> TermValue<()> { iflet Some(s) = self.sim_node_stack.pop() { self.sim_state_stack.pop(); return s;
} let t = self.node_stack[self.sp - 1].term; self.sp -= 1;
TermValue { term: t, value: () }
} fn replay(&mutself, tv: TermValue<()>) { self.replay_stack.push(tv)
} fn epsilon(&mutself, state: usize) { ifself.sim_state_stack.is_empty() { self.sim_state_stack.push(self.state_stack[self.sp]); self.sim_node_stack.push(TermValue {
term: self.node_stack[self.sp - 1].term,
value: (),
}); self.sp -= 1;
}
*self.sim_state_stack.last_mut().unwrap() = state;
} fn top_state(&self) -> usize { self.state()
} fn check_not_on_new_line(&mutself, _peek: usize) -> Result<'alloc, bool> {
Ok(true)
}
}
pubfn write_token(&mutself, t: TerminalId) -> Result<'alloc, ()> { // Shift the token with the associated StackValue. let accept = self.shift(TermValue {
term: t.into(),
value: (),
})?; // JavaScript grammar accepts empty inputs, therefore we can never // accept any program before receiving a TerminalId::End.
assert!(!accept);
Ok(())
}
pubfn close(&mutself, _position: usize) -> Result<'alloc, ()> { // Shift the End terminal with the associated StackValue. let accept = self.shift(TermValue {
term: TerminalId::End.into(),
value: (),
})?; // Adding a TerminalId::End would either lead to a parse error, or to // accepting the current input. In which case we return matching node // value.
assert!(accept);
// We can either reduce a Script/Module, or a Script/Module followed by // an <End> terminal.
assert!(self.sp + self.sim_node_stack.len() >= 1);
Ok(())
}
// Simulate the action of Parser::try_error_handling. fn try_error_handling(&mutself, t: TermValue<()>) -> Result<'alloc, bool> { if t.term.is_terminal() { let term = t.term.to_terminal(); let bogus_loc = SourceLocation::new(0, 0); let token = &Token::basic_token(term, bogus_loc);
// Error tokens might them-self cause more errors to be reported. // This happens due to the fact that the ErrorToken can be replayed, // and while the ErrorToken might be in the lookahead rules, it // might not be in the shifted terms coming after the reduced // nonterminal. if term == TerminalId::ErrorToken { return Err(Parser::parse_error(token).into());
}
// Otherwise, check if the current rule accept an Automatic // Semi-Colon insertion (ASI). let state = self.state();
assert!(state < TABLES.shift_count); let error_code = TABLES.error_codes[state]; iflet Some(error_code) = error_code {
Parser::recover(token, error_code)?; self.replay(t); self.replay(TermValue {
term: TerminalId::ErrorToken.into(),
value: (),
}); return Ok(false);
} return Err(Parser::parse_error(token).into());
} // On error, don't attempt error handling again.
Err(ParseError::ParserCannotUnpackToken.into())
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.0 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.