1 //! Simulates parser execution, for a single token of input, without incurring 2 //! any side effects. 3 //! 4 //! This is basically a copy of the parser.rs source code with calls to 5 //! generated_parser::reduce, and stack bookkeeping, omitted. 6 7 use crate::parser::Parser; 8 use ast::SourceLocation; 9 use generated_parser::{ 10 noop_actions, ParseError, ParserTrait, Result, StackValue, Term, TermValue, TerminalId, Token, 11 TABLES, 12 }; 13 14 /// The Simulator is used to check whether we can shift one token, either to 15 /// check what might be accepted, or to check whether we can End parsing now. 16 /// This is used by the REPL to verify whether or not we can end the input. 17 pub struct Simulator<'alloc, 'parser> { 18 /// Define the top of the immutable stack. 19 sp: usize, 20 /// Immutable state stack coming from the forked parser. 21 state_stack: &'parser [usize], 22 /// Immuatable term stack coming from the forked parser. 23 node_stack: &'parser [TermValue<StackValue<'alloc>>], 24 /// Mutable state stack used by the simulator on top of the immutable 25 /// parser's state stack. 26 sim_state_stack: Vec<usize>, 27 /// Mutable term stack used by the simulator on top of the immutable 28 /// parser's term stack. 29 sim_node_stack: Vec<TermValue<()>>, 30 /// Mutable term stack used by the simulator for replaying terms when reducing non-terminals are replaying lookahead terminals. 31 replay_stack: Vec<TermValue<()>>, 32 } 33 34 impl<'alloc, 'parser> ParserTrait<'alloc, ()> for Simulator<'alloc, 'parser> { shift(&mut self, tv: TermValue<()>) -> Result<'alloc, bool>35 fn shift(&mut self, tv: TermValue<()>) -> Result<'alloc, bool> { 36 // Shift the new terminal/nonterminal and its associated value. 37 let mut state = self.state(); 38 assert!(state < TABLES.shift_count); 39 let mut tv = tv; 40 loop { 41 let term_index: usize = tv.term.into(); 42 assert!(term_index < TABLES.shift_width); 43 let index = state * TABLES.shift_width + term_index; 44 let goto = TABLES.shift_table[index]; 45 if goto < 0 { 46 // Error handling is in charge of shifting an ErrorSymbol from the 47 // current state. 48 self.try_error_handling(tv)?; 49 tv = self.replay_stack.pop().unwrap(); 50 continue; 51 } 52 state = goto as usize; 53 self.sim_state_stack.push(state); 54 self.sim_node_stack.push(tv); 55 // Execute any actions, such as reduce actions. 56 if state >= TABLES.shift_count { 57 assert!(state < TABLES.action_count + TABLES.shift_count); 58 if noop_actions(self, state)? { 59 return Ok(true); 60 } 61 state = self.state(); 62 } 63 assert!(state < TABLES.shift_count); 64 if let Some(tv_temp) = self.replay_stack.pop() { 65 tv = tv_temp; 66 } else { 67 break; 68 } 69 } 70 Ok(false) 71 } unshift(&mut self)72 fn unshift(&mut self) { 73 let tv = self.pop(); 74 self.replay(tv) 75 } pop(&mut self) -> TermValue<()>76 fn pop(&mut self) -> TermValue<()> { 77 if let Some(s) = self.sim_node_stack.pop() { 78 self.sim_state_stack.pop(); 79 return s; 80 } 81 let t = self.node_stack[self.sp - 1].term; 82 self.sp -= 1; 83 TermValue { term: t, value: () } 84 } replay(&mut self, tv: TermValue<()>)85 fn replay(&mut self, tv: TermValue<()>) { 86 self.replay_stack.push(tv) 87 } epsilon(&mut self, state: usize)88 fn epsilon(&mut self, state: usize) { 89 if self.sim_state_stack.is_empty() { 90 self.sim_state_stack.push(self.state_stack[self.sp]); 91 self.sim_node_stack.push(TermValue { 92 term: self.node_stack[self.sp - 1].term, 93 value: (), 94 }); 95 self.sp -= 1; 96 } 97 *self.sim_state_stack.last_mut().unwrap() = state; 98 } check_not_on_new_line(&mut self, _peek: usize) -> Result<'alloc, bool>99 fn check_not_on_new_line(&mut self, _peek: usize) -> Result<'alloc, bool> { 100 Ok(true) 101 } 102 } 103 104 impl<'alloc, 'parser> Simulator<'alloc, 'parser> { new( state_stack: &'parser [usize], node_stack: &'parser [TermValue<StackValue<'alloc>>], ) -> Simulator<'alloc, 'parser>105 pub fn new( 106 state_stack: &'parser [usize], 107 node_stack: &'parser [TermValue<StackValue<'alloc>>], 108 ) -> Simulator<'alloc, 'parser> { 109 let sp = state_stack.len() - 1; 110 assert_eq!(state_stack.len(), node_stack.len() + 1); 111 Simulator { 112 sp, 113 state_stack, 114 node_stack, 115 sim_state_stack: vec![], 116 sim_node_stack: vec![], 117 replay_stack: vec![], 118 } 119 } 120 state(&self) -> usize121 fn state(&self) -> usize { 122 if let Some(res) = self.sim_state_stack.last() { 123 *res 124 } else { 125 self.state_stack[self.sp] 126 } 127 } 128 write_token(&mut self, t: TerminalId) -> Result<'alloc, ()>129 pub fn write_token(&mut self, t: TerminalId) -> Result<'alloc, ()> { 130 // Shift the token with the associated StackValue. 131 let accept = self.shift(TermValue { 132 term: Term::Terminal(t), 133 value: (), 134 })?; 135 // JavaScript grammar accepts empty inputs, therefore we can never 136 // accept any program before receiving a TerminalId::End. 137 assert!(!accept); 138 Ok(()) 139 } 140 close(&mut self, _position: usize) -> Result<'alloc, ()>141 pub fn close(&mut self, _position: usize) -> Result<'alloc, ()> { 142 // Shift the End terminal with the associated StackValue. 143 let accept = self.shift(TermValue { 144 term: Term::Terminal(TerminalId::End), 145 value: (), 146 })?; 147 // Adding a TerminalId::End would either lead to a parse error, or to 148 // accepting the current input. In which case we return matching node 149 // value. 150 assert!(accept); 151 152 // We can either reduce a Script/Module, or a Script/Module followed by 153 // an <End> terminal. 154 assert!(self.sp + self.sim_node_stack.len() >= 1); 155 Ok(()) 156 } 157 158 // Simulate the action of Parser::try_error_handling. try_error_handling(&mut self, t: TermValue<()>) -> Result<'alloc, bool>159 fn try_error_handling(&mut self, t: TermValue<()>) -> Result<'alloc, bool> { 160 if let Term::Terminal(term) = t.term { 161 let bogus_loc = SourceLocation::new(0, 0); 162 let token = &Token::basic_token(term, bogus_loc); 163 164 // Error tokens might them-self cause more errors to be reported. 165 // This happens due to the fact that the ErrorToken can be replayed, 166 // and while the ErrorToken might be in the lookahead rules, it 167 // might not be in the shifted terms coming after the reduced 168 // nonterminal. 169 if t.term == Term::Terminal(TerminalId::ErrorToken) { 170 return Err(Parser::parse_error(token).into()); 171 } 172 173 // Otherwise, check if the current rule accept an Automatic 174 // Semi-Colon insertion (ASI). 175 let state = self.state(); 176 assert!(state < TABLES.shift_count); 177 let error_code = TABLES.error_codes[state]; 178 if let Some(error_code) = error_code { 179 Parser::recover(token, error_code)?; 180 self.replay(t); 181 self.replay(TermValue { 182 term: Term::Terminal(TerminalId::ErrorToken), 183 value: (), 184 }); 185 return Ok(false); 186 } 187 return Err(Parser::parse_error(token).into()); 188 } 189 // On error, don't attempt error handling again. 190 Err(ParseError::ParserCannotUnpackToken.into()) 191 } 192 } 193