@@ -29,6 +29,43 @@ static bool printDocstringAndGlobals = false;
29
29
/* Use this to keep track of whether we need to print a class or module docstring */
30
30
static bool printClassDocstring = true ;
31
31
32
+ // shortcut for all top/pop calls
33
+ PycRef<ASTNode> StackPopTop (FastStack& stack)
34
+ {
35
+ const auto node{ stack.top () };
36
+ stack.pop ();
37
+ return node;
38
+ }
39
+
40
+ /* compiler generates very, VERY similar byte code for if/else statement block and if-expression
41
+ * statement
42
+ * if a: b = 1
43
+ * else: b = 2
44
+ * expression:
45
+ * b = 1 if a else 2
46
+ * (see for instance https://stackoverflow.com/a/52202007)
47
+ * here, try to guess if just finished else statement is part of if-expression (ternary operator)
48
+ * if it is, remove statements from the block and put a ternary node on top of stack
49
+ */
50
+ void CheckIfExpr (FastStack& stack, PycRef<ASTBlock> curblock)
51
+ {
52
+ if (stack.empty ())
53
+ return ;
54
+ if (curblock->nodes ().size () < 2 )
55
+ return ;
56
+ auto rit{ curblock->nodes ().crbegin () };
57
+ ++rit; // the last is "else" block, the one before should be "if" (could be "for", ...)
58
+ if ((*rit)->type () != ASTNode::NODE_BLOCK ||
59
+ (*rit).cast <ASTBlock>()->blktype () != ASTBlock::BLK_IF)
60
+ return ;
61
+ auto else_expr{ StackPopTop (stack) };
62
+ curblock->removeLast ();
63
+ auto if_block{ curblock->nodes ().back () };
64
+ auto if_expr{ StackPopTop (stack) };
65
+ curblock->removeLast ();
66
+ stack.push (new ASTTernary (std::move (if_block), std::move (if_expr), std::move (else_expr)));
67
+ }
68
+
32
69
PycRef<ASTNode> BuildFromCode (PycRef<PycCode> code, PycModule* mod)
33
70
{
34
71
PycBuffer source (code->code ()->value (), code->code ()->length ());
@@ -109,6 +146,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
109
146
curblock->append (prev.cast <ASTNode>());
110
147
111
148
prev = curblock;
149
+
150
+ CheckIfExpr (stack, curblock);
112
151
}
113
152
}
114
153
@@ -1363,19 +1402,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
1363
1402
break ;
1364
1403
}
1365
1404
1366
- if ((curblock->blktype () == ASTBlock::BLK_WHILE
1367
- && !curblock->inited ())
1368
- || (curblock->blktype () == ASTBlock::BLK_IF
1369
- && curblock->size () == 0 )) {
1370
- PycRef<PycObject> fakeint = new PycInt (1 );
1371
- PycRef<ASTNode> truthy = new ASTObject (fakeint);
1372
-
1373
- stack.push (truthy);
1374
- break ;
1375
- }
1376
-
1377
1405
if (!stack_hist.empty ()) {
1378
- stack = stack_hist.top ();
1406
+ if (stack.empty ()) // if it's part of if-expression, TOS at the moment is the result of "if" part
1407
+ stack = stack_hist.top ();
1379
1408
stack_hist.pop ();
1380
1409
}
1381
1410
@@ -3256,6 +3285,26 @@ void print_src(PycRef<ASTNode> node, PycModule* mod)
3256
3285
print_src (type, mod);
3257
3286
}
3258
3287
break ;
3288
+ case ASTNode::NODE_TERNARY:
3289
+ {
3290
+ /* parenthesis might not be needed,
3291
+ * but when if-expr is part of numerical expression, ternary has the LOWEST precedence
3292
+ * print(a + b if False else c)
3293
+ * output is c, not a+c (a+b is calculated first)
3294
+ */
3295
+ PycRef<ASTTernary> ternary = node.cast <ASTTernary>();
3296
+ fputs (" ( " , pyc_output);
3297
+ print_src (ternary->if_expr (), mod);
3298
+ const auto if_block = ternary->if_block ().require_cast <ASTCondBlock>();
3299
+ fputs (" if " , pyc_output);
3300
+ if (if_block->negative ())
3301
+ fputs (" not " , pyc_output);
3302
+ print_src (if_block->cond (), mod);
3303
+ fputs (" else " , pyc_output);
3304
+ print_src (ternary->else_expr (), mod);
3305
+ fputs (" )" , pyc_output);
3306
+ }
3307
+ break ;
3259
3308
default :
3260
3309
fprintf (pyc_output, " <NODE:%d>" , node->type ());
3261
3310
fprintf (stderr, " Unsupported Node type: %d\n " , node->type ());
0 commit comments