| 211 | } |
| 212 | |
| 213 | fn step(&mut self, mut ip: InstPtr, mut at: InputAt) -> bool { |
| 214 | use prog::Inst::*; |
| 215 | loop { |
| 216 | // This loop is an optimization to avoid constantly pushing/popping |
| 217 | // from the stack. Namely, if we're pushing a job only to run it |
| 218 | // next, avoid the push and just mutate `ip` (and possibly `at`) |
| 219 | // in place. |
| 220 | if self.has_visited(ip, at) { |
| 221 | return false; |
| 222 | } |
| 223 | match self.prog[ip] { |
| 224 | Match(slot) => { |
| 225 | if slot < self.matches.len() { |
| 226 | self.matches[slot] = true; |
| 227 | } |
| 228 | return true; |
| 229 | } |
| 230 | Save(ref inst) => { |
| 231 | if let Some(&old_pos) = self.slots.get(inst.slot) { |
| 232 | // If this path doesn't work out, then we save the old |
| 233 | // capture index (if one exists) in an alternate |
| 234 | // job. If the next path fails, then the alternate |
| 235 | // job is popped and the old capture index is restored. |
| 236 | self.m.jobs.push(Job::SaveRestore { |
| 237 | slot: inst.slot, |
| 238 | old_pos: old_pos, |
| 239 | }); |
| 240 | self.slots[inst.slot] = Some(at.pos()); |
| 241 | } |
| 242 | ip = inst.goto; |
| 243 | } |
| 244 | Split(ref inst) => { |
| 245 | self.m.jobs.push(Job::Inst { ip: inst.goto2, at: at }); |
| 246 | ip = inst.goto1; |
| 247 | } |
| 248 | EmptyLook(ref inst) => { |
| 249 | if self.input.is_empty_match(at, inst) { |
| 250 | ip = inst.goto; |
| 251 | } else { |
| 252 | return false; |
| 253 | } |
| 254 | } |
| 255 | Char(ref inst) => { |
| 256 | if inst.c == at.char() { |
| 257 | ip = inst.goto; |
| 258 | at = self.input.at(at.next_pos()); |
| 259 | } else { |
| 260 | return false; |
| 261 | } |
| 262 | } |
| 263 | Ranges(ref inst) => { |
| 264 | if inst.matches(at.char()) { |
| 265 | ip = inst.goto; |
| 266 | at = self.input.at(at.next_pos()); |
| 267 | } else { |
| 268 | return false; |
| 269 | } |
| 270 | } |