Skip to content

Commit 745676b

Browse files
author
thomas.wouters
committed
PEP 308 implementation, including minor refdocs and some testcases. It breaks the parser module, because it adds the if/else construct as well as two new grammar rules for backward compatibility. If no one else fixes parsermodule, I guess I'll go ahead and fix it later this week. The TeX code was checked with texcheck.py, but not rendered. There is actually a slight incompatibility: >>> (x for x in lambda:0) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: iteration over non-sequence changes into >>> (x for x in lambda: 0) File "<stdin>", line 1 (x for x in lambda: 0) ^ SyntaxError: invalid syntax Since there's no way the former version can be useful, it's probably a bugfix ;) git-svn-id: http://svn.python.org/projects/python/trunk@42586 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 76623d8 commit 745676b

11 files changed

Lines changed: 803 additions & 567 deletions

File tree

Doc/ref/ref5.tex

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ \subsection{List displays\label{lists}}
155155

156156
\begin{productionlist}
157157
\production{test}
158-
{\token{and_test} ( "or" \token{and_test} )*
159-
| \token{lambda_form}}
158+
{\token{or_test} | \token{lambda_form}}
160159
\production{testlist}
161160
{\token{test} ( "," \token{test} )* [ "," ]}
162161
\production{list_display}
@@ -1017,7 +1016,8 @@ \section{Boolean operations\label{Booleans}}
10171016
10181017
\begin{productionlist}
10191018
\production{expression}
1020-
{\token{or_test} | \token{lambda_form}}
1019+
{\token{or_test} [\token{if} \token{or_test} \token{else}
1020+
\token{test}] | \token{lambda_form}}
10211021
\production{or_test}
10221022
{\token{and_test} | \token{or_test} "or" \token{and_test}}
10231023
\production{and_test}
@@ -1036,6 +1036,11 @@ \section{Boolean operations\label{Booleans}}
10361036
\code{False} otherwise.
10371037
\opindex{not}
10381038
1039+
The expression \code{\var{x} if \var{C} else \var{y}} first evaluates
1040+
\var{C} (\emph{not} \var{x}); if \var{C} is true, \var{x} is evaluated and
1041+
its value is returned; otherwise, \var{y} is evaluated and its value is
1042+
returned.
1043+
10391044
The expression \code{\var{x} and \var{y}} first evaluates \var{x}; if
10401045
\var{x} is false, its value is returned; otherwise, \var{y} is
10411046
evaluated and the resulting value is returned.

Grammar/Grammar

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,17 @@ try_stmt: ('try' ':' suite
8383
except_clause: 'except' [test [',' test]]
8484
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
8585

86-
test: and_test ('or' and_test)* | lambdef
86+
# Backward compatibility cruft to support:
87+
# [ x for x in lambda: True, lambda: False if x() ]
88+
# even while also allowing:
89+
# lambda x: 5 if x else 2
90+
# (But not a mix of the two)
91+
testlist_safe: old_test [(',' old_test)+ [',']]
92+
old_test: or_test | old_lambdef
93+
old_lambdef: 'lambda' [varargslist] ':' old_test
94+
95+
test: or_test ['if' or_test 'else' test] | lambdef
96+
or_test: and_test ('or' and_test)*
8797
and_test: not_test ('and' not_test)*
8898
not_test: 'not' not_test | comparison
8999
comparison: expr (comp_op expr)*
@@ -110,7 +120,6 @@ subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
110120
sliceop: ':' [test]
111121
exprlist: expr (',' expr)* [',']
112122
testlist: test (',' test)* [',']
113-
testlist_safe: test [(',' test)+ [',']]
114123
dictmaker: test ':' test (',' test ':' test)* [',']
115124

116125
classdef: 'class' NAME ['(' [testlist] ')'] ':' suite
@@ -123,7 +132,7 @@ list_for: 'for' exprlist 'in' testlist_safe [list_iter]
123132
list_if: 'if' test [list_iter]
124133

125134
gen_iter: gen_for | gen_if
126-
gen_for: 'for' exprlist 'in' test [gen_iter]
135+
gen_for: 'for' exprlist 'in' or_test [gen_iter]
127136
gen_if: 'if' test [gen_iter]
128137

129138
testlist1: test (',' test)*

Include/Python-ast.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,10 @@ struct _stmt {
175175

176176
struct _expr {
177177
enum { BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4,
178-
Dict_kind=5, ListComp_kind=6, GeneratorExp_kind=7, Yield_kind=8,
179-
Compare_kind=9, Call_kind=10, Repr_kind=11, Num_kind=12,
180-
Str_kind=13, Attribute_kind=14, Subscript_kind=15, Name_kind=16,
181-
List_kind=17, Tuple_kind=18 } kind;
178+
IfExp_kind=5, Dict_kind=6, ListComp_kind=7, GeneratorExp_kind=8,
179+
Yield_kind=9, Compare_kind=10, Call_kind=11, Repr_kind=12,
180+
Num_kind=13, Str_kind=14, Attribute_kind=15, Subscript_kind=16,
181+
Name_kind=17, List_kind=18, Tuple_kind=19 } kind;
182182
union {
183183
struct {
184184
boolop_ty op;
@@ -201,6 +201,12 @@ struct _expr {
201201
expr_ty body;
202202
} Lambda;
203203

204+
struct {
205+
expr_ty test;
206+
expr_ty body;
207+
expr_ty orelse;
208+
} IfExp;
209+
204210
struct {
205211
asdl_seq *keys;
206212
asdl_seq *values;
@@ -371,6 +377,8 @@ expr_ty BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, PyArena
371377
*arena);
372378
expr_ty UnaryOp(unaryop_ty op, expr_ty operand, int lineno, PyArena *arena);
373379
expr_ty Lambda(arguments_ty args, expr_ty body, int lineno, PyArena *arena);
380+
expr_ty IfExp(expr_ty test, expr_ty body, expr_ty orelse, int lineno, PyArena
381+
*arena);
374382
expr_ty Dict(asdl_seq * keys, asdl_seq * values, int lineno, PyArena *arena);
375383
expr_ty ListComp(expr_ty elt, asdl_seq * generators, int lineno, PyArena
376384
*arena);

Include/graminit.h

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,40 +40,43 @@
4040
#define try_stmt 295
4141
#define except_clause 296
4242
#define suite 297
43-
#define test 298
44-
#define and_test 299
45-
#define not_test 300
46-
#define comparison 301
47-
#define comp_op 302
48-
#define expr 303
49-
#define xor_expr 304
50-
#define and_expr 305
51-
#define shift_expr 306
52-
#define arith_expr 307
53-
#define term 308
54-
#define factor 309
55-
#define power 310
56-
#define atom 311
57-
#define listmaker 312
58-
#define testlist_gexp 313
59-
#define lambdef 314
60-
#define trailer 315
61-
#define subscriptlist 316
62-
#define subscript 317
63-
#define sliceop 318
64-
#define exprlist 319
65-
#define testlist 320
66-
#define testlist_safe 321
67-
#define dictmaker 322
68-
#define classdef 323
69-
#define arglist 324
70-
#define argument 325
71-
#define list_iter 326
72-
#define list_for 327
73-
#define list_if 328
74-
#define gen_iter 329
75-
#define gen_for 330
76-
#define gen_if 331
77-
#define testlist1 332
78-
#define encoding_decl 333
79-
#define yield_expr 334
43+
#define testlist_safe 298
44+
#define old_test 299
45+
#define old_lambdef 300
46+
#define test 301
47+
#define or_test 302
48+
#define and_test 303
49+
#define not_test 304
50+
#define comparison 305
51+
#define comp_op 306
52+
#define expr 307
53+
#define xor_expr 308
54+
#define and_expr 309
55+
#define shift_expr 310
56+
#define arith_expr 311
57+
#define term 312
58+
#define factor 313
59+
#define power 314
60+
#define atom 315
61+
#define listmaker 316
62+
#define testlist_gexp 317
63+
#define lambdef 318
64+
#define trailer 319
65+
#define subscriptlist 320
66+
#define subscript 321
67+
#define sliceop 322
68+
#define exprlist 323
69+
#define testlist 324
70+
#define dictmaker 325
71+
#define classdef 326
72+
#define arglist 327
73+
#define argument 328
74+
#define list_iter 329
75+
#define list_for 330
76+
#define list_if 331
77+
#define gen_iter 332
78+
#define gen_for 333
79+
#define gen_if 334
80+
#define testlist1 335
81+
#define encoding_decl 336
82+
#define yield_expr 337

Lib/test/test_grammar.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,3 +798,28 @@ def test_nested_front():
798798
x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x))
799799
x = 5; t = True;
800800
verify([(i,j) for i in range(10) for j in range(5)] == list(g))
801+
802+
# Test ifelse expressions in various cases
803+
def _checkeval(msg, ret):
804+
"helper to check that evaluation of expressions is done correctly"
805+
print x
806+
return ret
807+
808+
verify([ x() for x in lambda: True, lambda: False if x() ] == [True])
809+
verify([ x() for x in (lambda: True, lambda: False) if x() ] == [True])
810+
verify([ x(False) for x in (lambda x: False if x else True, lambda x: True if x else False) if x(False) ] == [True])
811+
verify((5 if 1 else _checkeval("check 1", 0)) == 5)
812+
verify((_checkeval("check 2", 0) if 0 else 5) == 5)
813+
verify((5 and 6 if 0 else 1) == 1)
814+
verify(((5 and 6) if 0 else 1) == 1)
815+
verify((5 and (6 if 1 else 1)) == 6)
816+
verify((0 or _checkeval("check 3", 2) if 0 else 3) == 3)
817+
verify((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)) == 1)
818+
verify((0 or 5 if 1 else _checkeval("check 6", 3)) == 5)
819+
verify((not 5 if 1 else 1) == False)
820+
verify((not 5 if 0 else 1) == 1)
821+
verify((6 + 1 if 1 else 2) == 7)
822+
verify((6 - 1 if 1 else 2) == 5)
823+
verify((6 * 2 if 1 else 4) == 12)
824+
verify((6 / 2 if 1 else 3) == 3)
825+
verify((6 < 4 if 0 else 2) == 2)

Parser/Python.asdl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ module Python
5252
| BinOp(expr left, operator op, expr right)
5353
| UnaryOp(unaryop op, expr operand)
5454
| Lambda(arguments args, expr body)
55+
| IfExp(expr test, expr body, expr orelse)
5556
| Dict(expr* keys, expr* values)
5657
| ListComp(expr elt, comprehension* generators)
5758
| GeneratorExp(expr elt, comprehension* generators)

Python/Python-ast.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ char *Lambda_fields[]={
151151
"args",
152152
"body",
153153
};
154+
PyTypeObject *IfExp_type;
155+
char *IfExp_fields[]={
156+
"test",
157+
"body",
158+
"orelse",
159+
};
154160
PyTypeObject *Dict_type;
155161
char *Dict_fields[]={
156162
"keys",
@@ -431,6 +437,7 @@ static int init_types(void)
431437
BinOp_type = make_type("BinOp", expr_type, BinOp_fields, 3);
432438
UnaryOp_type = make_type("UnaryOp", expr_type, UnaryOp_fields, 2);
433439
Lambda_type = make_type("Lambda", expr_type, Lambda_fields, 2);
440+
IfExp_type = make_type("IfExp", expr_type, IfExp_fields, 3);
434441
Dict_type = make_type("Dict", expr_type, Dict_fields, 2);
435442
ListComp_type = make_type("ListComp", expr_type, ListComp_fields, 2);
436443
GeneratorExp_type = make_type("GeneratorExp", expr_type,
@@ -1137,6 +1144,38 @@ Lambda(arguments_ty args, expr_ty body, int lineno, PyArena *arena)
11371144
return p;
11381145
}
11391146

1147+
expr_ty
1148+
IfExp(expr_ty test, expr_ty body, expr_ty orelse, int lineno, PyArena *arena)
1149+
{
1150+
expr_ty p;
1151+
if (!test) {
1152+
PyErr_SetString(PyExc_ValueError,
1153+
"field test is required for IfExp");
1154+
return NULL;
1155+
}
1156+
if (!body) {
1157+
PyErr_SetString(PyExc_ValueError,
1158+
"field body is required for IfExp");
1159+
return NULL;
1160+
}
1161+
if (!orelse) {
1162+
PyErr_SetString(PyExc_ValueError,
1163+
"field orelse is required for IfExp");
1164+
return NULL;
1165+
}
1166+
p = (expr_ty)PyArena_Malloc(arena, sizeof(*p));
1167+
if (!p) {
1168+
PyErr_NoMemory();
1169+
return NULL;
1170+
}
1171+
p->kind = IfExp_kind;
1172+
p->v.IfExp.test = test;
1173+
p->v.IfExp.body = body;
1174+
p->v.IfExp.orelse = orelse;
1175+
p->lineno = lineno;
1176+
return p;
1177+
}
1178+
11401179
expr_ty
11411180
Dict(asdl_seq * keys, asdl_seq * values, int lineno, PyArena *arena)
11421181
{
@@ -2077,6 +2116,25 @@ ast2obj_expr(void* _o)
20772116
goto failed;
20782117
Py_DECREF(value);
20792118
break;
2119+
case IfExp_kind:
2120+
result = PyType_GenericNew(IfExp_type, NULL, NULL);
2121+
if (!result) goto failed;
2122+
value = ast2obj_expr(o->v.IfExp.test);
2123+
if (!value) goto failed;
2124+
if (PyObject_SetAttrString(result, "test", value) == -1)
2125+
goto failed;
2126+
Py_DECREF(value);
2127+
value = ast2obj_expr(o->v.IfExp.body);
2128+
if (!value) goto failed;
2129+
if (PyObject_SetAttrString(result, "body", value) == -1)
2130+
goto failed;
2131+
Py_DECREF(value);
2132+
value = ast2obj_expr(o->v.IfExp.orelse);
2133+
if (!value) goto failed;
2134+
if (PyObject_SetAttrString(result, "orelse", value) == -1)
2135+
goto failed;
2136+
Py_DECREF(value);
2137+
break;
20802138
case Dict_kind:
20812139
result = PyType_GenericNew(Dict_type, NULL, NULL);
20822140
if (!result) goto failed;

Python/ast.c

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,25 @@ ast_for_lambdef(struct compiling *c, const node *n)
847847
return Lambda(args, expression, LINENO(n), c->c_arena);
848848
}
849849

850+
static expr_ty
851+
ast_for_ifexpr(struct compiling *c, const node *n)
852+
{
853+
/* test: or_test 'if' or_test 'else' test */
854+
expr_ty expression, body, orelse;
855+
856+
assert(NCH(n) >= 3);
857+
body = ast_for_expr(c, CHILD(n, 0));
858+
if (!body)
859+
return NULL;
860+
expression = ast_for_expr(c, CHILD(n, 2));
861+
if (!expression)
862+
return NULL;
863+
orelse = ast_for_expr(c, CHILD(n, 4));
864+
if (!orelse)
865+
return NULL;
866+
return IfExp(expression, body, orelse, LINENO(n), c->c_arena);
867+
}
868+
850869
/* Count the number of 'for' loop in a list comprehension.
851870
852871
Helper for ast_for_listcomp().
@@ -1456,7 +1475,8 @@ static expr_ty
14561475
ast_for_expr(struct compiling *c, const node *n)
14571476
{
14581477
/* handle the full range of simple expressions
1459-
test: and_test ('or' and_test)* | lambdef
1478+
test: or_test ['if' or_test 'else' test] | lambdef
1479+
or_test: and_test ('or' and_test)*
14601480
and_test: not_test ('and' not_test)*
14611481
not_test: 'not' not_test | comparison
14621482
comparison: expr (comp_op expr)*
@@ -1468,6 +1488,15 @@ ast_for_expr(struct compiling *c, const node *n)
14681488
term: factor (('*'|'/'|'%'|'//') factor)*
14691489
factor: ('+'|'-'|'~') factor | power
14701490
power: atom trailer* ('**' factor)*
1491+
1492+
As well as modified versions that exist for backward compatibility,
1493+
to explicitly allow:
1494+
[ x for x in lambda: 0, lambda: 1 ]
1495+
(which would be ambiguous without these extra rules)
1496+
1497+
old_test: or_test | old_lambdef
1498+
old_lambdef: 'lambda' [vararglist] ':' old_test
1499+
14711500
*/
14721501

14731502
asdl_seq *seq;
@@ -1476,9 +1505,14 @@ ast_for_expr(struct compiling *c, const node *n)
14761505
loop:
14771506
switch (TYPE(n)) {
14781507
case test:
1479-
if (TYPE(CHILD(n, 0)) == lambdef)
1508+
case old_test:
1509+
if (TYPE(CHILD(n, 0)) == lambdef ||
1510+
TYPE(CHILD(n, 0)) == old_lambdef)
14801511
return ast_for_lambdef(c, CHILD(n, 0));
1481-
/* Fall through to and_test */
1512+
else if (NCH(n) > 1)
1513+
return ast_for_ifexpr(c, n);
1514+
/* Fallthrough */
1515+
case or_test:
14821516
case and_test:
14831517
if (NCH(n) == 1) {
14841518
n = CHILD(n, 0);

0 commit comments

Comments
 (0)