(&mut self)
| 329 | /* while: cond + body + back-edge; optional else when cond falsifies. */ |
| 330 | |
| 331 | pub(super) fn while_stmt(&mut self) { |
| 332 | self.advance(); |
| 333 | self.enter_block(); |
| 334 | |
| 335 | let loop_start = self.chunk.instructions.len() as u16; |
| 336 | self.loop_starts.push(loop_start); |
| 337 | self.loop_breaks.push(vec![]); |
| 338 | self.loop_kinds.push(false); |
| 339 | self.loop_cleanup_base.push(self.cleanup_count); |
| 340 | |
| 341 | self.expr(); |
| 342 | let jf = self.emit_jump(OpCode::JumpIfFalse); |
| 343 | |
| 344 | self.eat(TokenType::Colon); |
| 345 | self.compile_block(); |
| 346 | |
| 347 | self.chunk.emit(OpCode::Jump, loop_start); |
| 348 | self.patch(jf); |
| 349 | |
| 350 | // Pop loop state before the else so its break/continue target the enclosing loop. |
| 351 | self.loop_starts.pop(); |
| 352 | self.loop_kinds.pop(); |
| 353 | self.loop_cleanup_base.pop(); |
| 354 | let breaks = self.loop_breaks.pop().unwrap_or_default(); |
| 355 | |
| 356 | if self.eat_if(TokenType::Else) { |
| 357 | self.eat(TokenType::Colon); |
| 358 | self.compile_block(); |
| 359 | } |
| 360 | |
| 361 | // Loop breaks land past the else clause. |
| 362 | for pos in breaks { self.patch(pos); } |
| 363 | |
| 364 | self.commit_block(); |
| 365 | } |
| 366 | |
| 367 | /* for / async for, with optional tuple/star unpacking. */ |
| 368 |
no test coverage detected