diff --git a/src/compile.rs b/src/compile.rs index a13d361..a0f20c1 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -15,6 +15,7 @@ use rustpython_parser::{ast, parser}; type BasicOutputStream = PeepholeOptimizer; +/// Main structure holding the state of compilation. struct Compiler { output_stack: Vec, scope_stack: Vec, @@ -107,12 +108,6 @@ pub enum Mode { Single, } -#[derive(Clone, Copy)] -enum EvalContext { - Statement, - Expression, -} - pub(crate) type Label = usize; impl Default for Compiler @@ -350,14 +345,14 @@ impl Compiler { match orelse { None => { // Only if: - self.compile_test(test, None, Some(end_label), EvalContext::Statement)?; + self.compile_jump_if(test, false, end_label)?; self.compile_statements(body)?; self.set_label(end_label); } Some(statements) => { // if - else: let else_label = self.new_label(); - self.compile_test(test, None, Some(else_label), EvalContext::Statement)?; + self.compile_jump_if(test, false, else_label)?; self.compile_statements(body)?; self.emit(Instruction::Jump { target: end_label }); @@ -459,7 +454,7 @@ impl Compiler { // if some flag, ignore all assert statements! if self.optimize == 0 { let end_label = self.new_label(); - self.compile_test(test, Some(end_label), None, EvalContext::Statement)?; + self.compile_jump_if(test, true, end_label)?; self.emit(Instruction::LoadName { name: String::from("AssertionError"), scope: bytecode::NameScope::Local, @@ -1006,7 +1001,7 @@ impl Compiler { self.set_label(start_label); - self.compile_test(test, None, Some(else_label), EvalContext::Statement)?; + self.compile_jump_if(test, false, else_label)?; let was_in_loop = self.in_loop; self.in_loop = true; @@ -1118,12 +1113,9 @@ impl Compiler { }); // if comparison result is false, we break with this value; if true, try the next one. - // (CPython compresses these three opcodes into JUMP_IF_FALSE_OR_POP) - self.emit(Instruction::Duplicate); - self.emit(Instruction::JumpIfFalse { + self.emit(Instruction::JumpIfFalseOrPop { target: break_label, }); - self.emit(Instruction::Pop); } // handle the last comparison @@ -1256,66 +1248,120 @@ impl Compiler { self.emit(Instruction::BinaryOperation { op: i, inplace }); } - fn compile_test( + /// Implement boolean short circuit evaluation logic. + /// https://en.wikipedia.org/wiki/Short-circuit_evaluation + /// + /// This means, in a boolean statement 'x and y' the variable y will + /// not be evaluated when x is false. + /// + /// The idea is to jump to a label if the expression is either true or false + /// (indicated by the condition parameter). + fn compile_jump_if( &mut self, expression: &ast::Expression, - true_label: Option