@@ -15,6 +15,7 @@ use rustpython_parser::{ast, parser};
15
15
16
16
type BasicOutputStream = PeepholeOptimizer < CodeObjectStream > ;
17
17
18
+ /// Main structure holding the state of compilation.
18
19
struct Compiler < O : OutputStream = BasicOutputStream > {
19
20
output_stack : Vec < O > ,
20
21
scope_stack : Vec < SymbolScope > ,
@@ -107,12 +108,6 @@ pub enum Mode {
107
108
Single ,
108
109
}
109
110
110
- #[ derive( Clone , Copy ) ]
111
- enum EvalContext {
112
- Statement ,
113
- Expression ,
114
- }
115
-
116
111
pub ( crate ) type Label = usize ;
117
112
118
113
impl < O > Default for Compiler < O >
@@ -350,14 +345,14 @@ impl<O: OutputStream> Compiler<O> {
350
345
match orelse {
351
346
None => {
352
347
// Only if:
353
- self . compile_test ( test, None , Some ( end_label) , EvalContext :: Statement ) ?;
348
+ self . compile_jump_if ( test, false , end_label) ?;
354
349
self . compile_statements ( body) ?;
355
350
self . set_label ( end_label) ;
356
351
}
357
352
Some ( statements) => {
358
353
// if - else:
359
354
let else_label = self . new_label ( ) ;
360
- self . compile_test ( test, None , Some ( else_label) , EvalContext :: Statement ) ?;
355
+ self . compile_jump_if ( test, false , else_label) ?;
361
356
self . compile_statements ( body) ?;
362
357
self . emit ( Instruction :: Jump { target : end_label } ) ;
363
358
@@ -459,7 +454,7 @@ impl<O: OutputStream> Compiler<O> {
459
454
// if some flag, ignore all assert statements!
460
455
if self . optimize == 0 {
461
456
let end_label = self . new_label ( ) ;
462
- self . compile_test ( test, Some ( end_label ) , None , EvalContext :: Statement ) ?;
457
+ self . compile_jump_if ( test, true , end_label ) ?;
463
458
self . emit ( Instruction :: LoadName {
464
459
name : String :: from ( "AssertionError" ) ,
465
460
scope : bytecode:: NameScope :: Local ,
@@ -1006,7 +1001,7 @@ impl<O: OutputStream> Compiler<O> {
1006
1001
1007
1002
self . set_label ( start_label) ;
1008
1003
1009
- self . compile_test ( test, None , Some ( else_label) , EvalContext :: Statement ) ?;
1004
+ self . compile_jump_if ( test, false , else_label) ?;
1010
1005
1011
1006
let was_in_loop = self . in_loop ;
1012
1007
self . in_loop = true ;
@@ -1118,12 +1113,9 @@ impl<O: OutputStream> Compiler<O> {
1118
1113
} ) ;
1119
1114
1120
1115
// if comparison result is false, we break with this value; if true, try the next one.
1121
- // (CPython compresses these three opcodes into JUMP_IF_FALSE_OR_POP)
1122
- self . emit ( Instruction :: Duplicate ) ;
1123
- self . emit ( Instruction :: JumpIfFalse {
1116
+ self . emit ( Instruction :: JumpIfFalseOrPop {
1124
1117
target : break_label,
1125
1118
} ) ;
1126
- self . emit ( Instruction :: Pop ) ;
1127
1119
}
1128
1120
1129
1121
// handle the last comparison
@@ -1256,66 +1248,120 @@ impl<O: OutputStream> Compiler<O> {
1256
1248
self . emit ( Instruction :: BinaryOperation { op : i, inplace } ) ;
1257
1249
}
1258
1250
1259
- fn compile_test (
1251
+ /// Implement boolean short circuit evaluation logic.
1252
+ /// https://en.wikipedia.org/wiki/Short-circuit_evaluation
1253
+ ///
1254
+ /// This means, in a boolean statement 'x and y' the variable y will
1255
+ /// not be evaluated when x is false.
1256
+ ///
1257
+ /// The idea is to jump to a label if the expression is either true or false
1258
+ /// (indicated by the condition parameter).
1259
+ fn compile_jump_if (
1260
1260
& mut self ,
1261
1261
expression : & ast:: Expression ,
1262
- true_label : Option < Label > ,
1263
- false_label : Option < Label > ,
1264
- context : EvalContext ,
1262
+ condition : bool ,
1263
+ target_label : Label ,
1265
1264
) -> Result < ( ) , CompileError > {
1266
1265
// Compile expression for test, and jump to label if false
1267
1266
match & expression. node {
1268
- ast:: ExpressionType :: BoolOp { a, op, b } => match op {
1269
- ast:: BooleanOperator :: And => {
1270
- let f = false_label. unwrap_or_else ( || self . new_label ( ) ) ;
1271
- self . compile_test ( a, None , Some ( f) , context) ?;
1272
- self . compile_test ( b, true_label, false_label, context) ?;
1273
- if false_label. is_none ( ) {
1274
- self . set_label ( f) ;
1267
+ ast:: ExpressionType :: BoolOp { op, values } => {
1268
+ match op {
1269
+ ast:: BooleanOperator :: And => {
1270
+ if condition {
1271
+ // If all values are true.
1272
+ let end_label = self . new_label ( ) ;
1273
+ let ( last_value, values) = values. split_last ( ) . unwrap ( ) ;
1274
+
1275
+ // If any of the values is false, we can short-circuit.
1276
+ for value in values {
1277
+ self . compile_jump_if ( value, false , end_label) ?;
1278
+ }
1279
+
1280
+ // It depends upon the last value now: will it be true?
1281
+ self . compile_jump_if ( last_value, true , target_label) ?;
1282
+ self . set_label ( end_label) ;
1283
+ } else {
1284
+ // If any value is false, the whole condition is false.
1285
+ for value in values {
1286
+ self . compile_jump_if ( value, false , target_label) ?;
1287
+ }
1288
+ }
1275
1289
}
1276
- }
1277
- ast:: BooleanOperator :: Or => {
1278
- let t = true_label. unwrap_or_else ( || self . new_label ( ) ) ;
1279
- self . compile_test ( a, Some ( t) , None , context) ?;
1280
- self . compile_test ( b, true_label, false_label, context) ?;
1281
- if true_label. is_none ( ) {
1282
- self . set_label ( t) ;
1290
+ ast:: BooleanOperator :: Or => {
1291
+ if condition {
1292
+ // If any of the values is true.
1293
+ for value in values {
1294
+ self . compile_jump_if ( value, true , target_label) ?;
1295
+ }
1296
+ } else {
1297
+ // If all of the values are false.
1298
+ let end_label = self . new_label ( ) ;
1299
+ let ( last_value, values) = values. split_last ( ) . unwrap ( ) ;
1300
+
1301
+ // If any value is true, we can short-circuit:
1302
+ for value in values {
1303
+ self . compile_jump_if ( value, true , end_label) ?;
1304
+ }
1305
+
1306
+ // It all depends upon the last value now!
1307
+ self . compile_jump_if ( last_value, false , target_label) ?;
1308
+ self . set_label ( end_label) ;
1309
+ }
1283
1310
}
1284
1311
}
1285
- } ,
1312
+ }
1313
+ ast:: ExpressionType :: Unop {
1314
+ op : ast:: UnaryOperator :: Not ,
1315
+ a,
1316
+ } => {
1317
+ self . compile_jump_if ( a, !condition, target_label) ?;
1318
+ }
1286
1319
_ => {
1320
+ // Fall back case which always will work!
1287
1321
self . compile_expression ( expression) ?;
1288
- match context {
1289
- EvalContext :: Statement => {
1290
- if let Some ( true_label) = true_label {
1291
- self . emit ( Instruction :: JumpIf { target : true_label } ) ;
1292
- }
1293
- if let Some ( false_label) = false_label {
1294
- self . emit ( Instruction :: JumpIfFalse {
1295
- target : false_label,
1296
- } ) ;
1297
- }
1298
- }
1299
- EvalContext :: Expression => {
1300
- if let Some ( true_label) = true_label {
1301
- self . emit ( Instruction :: Duplicate ) ;
1302
- self . emit ( Instruction :: JumpIf { target : true_label } ) ;
1303
- self . emit ( Instruction :: Pop ) ;
1304
- }
1305
- if let Some ( false_label) = false_label {
1306
- self . emit ( Instruction :: Duplicate ) ;
1307
- self . emit ( Instruction :: JumpIfFalse {
1308
- target : false_label,
1309
- } ) ;
1310
- self . emit ( Instruction :: Pop ) ;
1311
- }
1312
- }
1322
+ if condition {
1323
+ self . emit ( Instruction :: JumpIfTrue {
1324
+ target : target_label,
1325
+ } ) ;
1326
+ } else {
1327
+ self . emit ( Instruction :: JumpIfFalse {
1328
+ target : target_label,
1329
+ } ) ;
1313
1330
}
1314
1331
}
1315
1332
}
1316
1333
Ok ( ( ) )
1317
1334
}
1318
1335
1336
+ /// Compile a boolean operation as an expression.
1337
+ /// This means, that the last value remains on the stack.
1338
+ fn compile_bool_op (
1339
+ & mut self ,
1340
+ op : & ast:: BooleanOperator ,
1341
+ values : & [ ast:: Expression ] ,
1342
+ ) -> Result < ( ) , CompileError > {
1343
+ let end_label = self . new_label ( ) ;
1344
+
1345
+ let ( last_value, values) = values. split_last ( ) . unwrap ( ) ;
1346
+ for value in values {
1347
+ self . compile_expression ( value) ?;
1348
+
1349
+ match op {
1350
+ ast:: BooleanOperator :: And => {
1351
+ self . emit ( Instruction :: JumpIfFalseOrPop { target : end_label } ) ;
1352
+ }
1353
+ ast:: BooleanOperator :: Or => {
1354
+ self . emit ( Instruction :: JumpIfTrueOrPop { target : end_label } ) ;
1355
+ }
1356
+ }
1357
+ }
1358
+
1359
+ // If all values did not qualify, take the value of the last value:
1360
+ self . compile_expression ( last_value) ?;
1361
+ self . set_label ( end_label) ;
1362
+ Ok ( ( ) )
1363
+ }
1364
+
1319
1365
fn compile_expression ( & mut self , expression : & ast:: Expression ) -> Result < ( ) , CompileError > {
1320
1366
trace ! ( "Compiling {:?}" , expression) ;
1321
1367
self . set_source_location ( & expression. location ) ;
@@ -1327,12 +1373,7 @@ impl<O: OutputStream> Compiler<O> {
1327
1373
args,
1328
1374
keywords,
1329
1375
} => self . compile_call ( function, args, keywords) ?,
1330
- BoolOp { .. } => self . compile_test (
1331
- expression,
1332
- Option :: None ,
1333
- Option :: None ,
1334
- EvalContext :: Expression ,
1335
- ) ?,
1376
+ BoolOp { op, values } => self . compile_bool_op ( op, values) ?,
1336
1377
Binop { a, op, b } => {
1337
1378
self . compile_expression ( a) ?;
1338
1379
self . compile_expression ( b) ?;
@@ -1527,8 +1568,7 @@ impl<O: OutputStream> Compiler<O> {
1527
1568
IfExpression { test, body, orelse } => {
1528
1569
let no_label = self . new_label ( ) ;
1529
1570
let end_label = self . new_label ( ) ;
1530
- self . compile_test ( test, Option :: None , Option :: None , EvalContext :: Expression ) ?;
1531
- self . emit ( Instruction :: JumpIfFalse { target : no_label } ) ;
1571
+ self . compile_jump_if ( test, false , no_label) ?;
1532
1572
// True case
1533
1573
self . compile_expression ( body) ?;
1534
1574
self . emit ( Instruction :: Jump { target : end_label } ) ;
@@ -1745,12 +1785,7 @@ impl<O: OutputStream> Compiler<O> {
1745
1785
1746
1786
// Now evaluate the ifs:
1747
1787
for if_condition in & generator. ifs {
1748
- self . compile_test (
1749
- if_condition,
1750
- None ,
1751
- Some ( start_label) ,
1752
- EvalContext :: Statement ,
1753
- ) ?
1788
+ self . compile_jump_if ( if_condition, false , start_label) ?
1754
1789
}
1755
1790
}
1756
1791
@@ -1988,11 +2023,11 @@ mod tests {
1988
2023
LoadConst {
1989
2024
value: Boolean { value: true }
1990
2025
} ,
1991
- JumpIf { target: 1 } ,
2026
+ JumpIfTrue { target: 1 } ,
1992
2027
LoadConst {
1993
2028
value: Boolean { value: false }
1994
2029
} ,
1995
- JumpIf { target: 1 } ,
2030
+ JumpIfTrue { target: 1 } ,
1996
2031
LoadConst {
1997
2032
value: Boolean { value: false }
1998
2033
} ,
@@ -2042,7 +2077,7 @@ mod tests {
2042
2077
LoadConst {
2043
2078
value: Boolean { value: false }
2044
2079
} ,
2045
- JumpIf { target: 1 } ,
2080
+ JumpIfTrue { target: 1 } ,
2046
2081
LoadConst {
2047
2082
value: Boolean { value: false }
2048
2083
} ,
0 commit comments