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 ;)
This commit is contained in:
Thomas Wouters 2006-02-27 00:24:13 +00:00
parent d3a5f53a27
commit dca3b9c797
11 changed files with 878 additions and 642 deletions

View File

@ -155,8 +155,7 @@ square brackets:
\begin{productionlist} \begin{productionlist}
\production{test} \production{test}
{\token{and_test} ( "or" \token{and_test} )* {\token{or_test} | \token{lambda_form}}
| \token{lambda_form}}
\production{testlist} \production{testlist}
{\token{test} ( "," \token{test} )* [ "," ]} {\token{test} ( "," \token{test} )* [ "," ]}
\production{list_display} \production{list_display}
@ -1017,7 +1016,8 @@ Boolean operations have the lowest priority of all Python operations:
\begin{productionlist} \begin{productionlist}
\production{expression} \production{expression}
{\token{or_test} | \token{lambda_form}} {\token{or_test} [\token{if} \token{or_test} \token{else}
\token{test}] | \token{lambda_form}}
\production{or_test} \production{or_test}
{\token{and_test} | \token{or_test} "or" \token{and_test}} {\token{and_test} | \token{or_test} "or" \token{and_test}}
\production{and_test} \production{and_test}
@ -1036,6 +1036,11 @@ The operator \keyword{not} yields \code{True} if its argument is false,
\code{False} otherwise. \code{False} otherwise.
\opindex{not} \opindex{not}
The expression \code{\var{x} if \var{C} else \var{y}} first evaluates
\var{C} (\emph{not} \var{x}); if \var{C} is true, \var{x} is evaluated and
its value is returned; otherwise, \var{y} is evaluated and its value is
returned.
The expression \code{\var{x} and \var{y}} first evaluates \var{x}; if The expression \code{\var{x} and \var{y}} first evaluates \var{x}; if
\var{x} is false, its value is returned; otherwise, \var{y} is \var{x} is false, its value is returned; otherwise, \var{y} is
evaluated and the resulting value is returned. evaluated and the resulting value is returned.

View File

@ -83,7 +83,17 @@ try_stmt: ('try' ':' suite
except_clause: 'except' [test [',' test]] except_clause: 'except' [test [',' test]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
test: and_test ('or' and_test)* | lambdef # Backward compatibility cruft to support:
# [ x for x in lambda: True, lambda: False if x() ]
# even while also allowing:
# lambda x: 5 if x else 2
# (But not a mix of the two)
testlist_safe: old_test [(',' old_test)+ [',']]
old_test: or_test | old_lambdef
old_lambdef: 'lambda' [varargslist] ':' old_test
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)* and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)* comparison: expr (comp_op expr)*
@ -110,7 +120,6 @@ subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
sliceop: ':' [test] sliceop: ':' [test]
exprlist: expr (',' expr)* [','] exprlist: expr (',' expr)* [',']
testlist: test (',' test)* [','] testlist: test (',' test)* [',']
testlist_safe: test [(',' test)+ [',']]
dictmaker: test ':' test (',' test ':' test)* [','] dictmaker: test ':' test (',' test ':' test)* [',']
classdef: 'class' NAME ['(' [testlist] ')'] ':' suite classdef: 'class' NAME ['(' [testlist] ')'] ':' suite
@ -123,7 +132,7 @@ list_for: 'for' exprlist 'in' testlist_safe [list_iter]
list_if: 'if' test [list_iter] list_if: 'if' test [list_iter]
gen_iter: gen_for | gen_if gen_iter: gen_for | gen_if
gen_for: 'for' exprlist 'in' test [gen_iter] gen_for: 'for' exprlist 'in' or_test [gen_iter]
gen_if: 'if' test [gen_iter] gen_if: 'if' test [gen_iter]
testlist1: test (',' test)* testlist1: test (',' test)*

View File

@ -175,10 +175,10 @@ struct _stmt {
struct _expr { struct _expr {
enum { BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4, enum { BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4,
Dict_kind=5, ListComp_kind=6, GeneratorExp_kind=7, Yield_kind=8, IfExp_kind=5, Dict_kind=6, ListComp_kind=7, GeneratorExp_kind=8,
Compare_kind=9, Call_kind=10, Repr_kind=11, Num_kind=12, Yield_kind=9, Compare_kind=10, Call_kind=11, Repr_kind=12,
Str_kind=13, Attribute_kind=14, Subscript_kind=15, Name_kind=16, Num_kind=13, Str_kind=14, Attribute_kind=15, Subscript_kind=16,
List_kind=17, Tuple_kind=18 } kind; Name_kind=17, List_kind=18, Tuple_kind=19 } kind;
union { union {
struct { struct {
boolop_ty op; boolop_ty op;
@ -201,6 +201,12 @@ struct _expr {
expr_ty body; expr_ty body;
} Lambda; } Lambda;
struct {
expr_ty test;
expr_ty body;
expr_ty orelse;
} IfExp;
struct { struct {
asdl_seq *keys; asdl_seq *keys;
asdl_seq *values; asdl_seq *values;
@ -371,6 +377,8 @@ expr_ty BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, PyArena
*arena); *arena);
expr_ty UnaryOp(unaryop_ty op, expr_ty operand, int lineno, PyArena *arena); expr_ty UnaryOp(unaryop_ty op, expr_ty operand, int lineno, PyArena *arena);
expr_ty Lambda(arguments_ty args, expr_ty body, int lineno, PyArena *arena); expr_ty Lambda(arguments_ty args, expr_ty body, int lineno, PyArena *arena);
expr_ty IfExp(expr_ty test, expr_ty body, expr_ty orelse, int lineno, PyArena
*arena);
expr_ty Dict(asdl_seq * keys, asdl_seq * values, int lineno, PyArena *arena); expr_ty Dict(asdl_seq * keys, asdl_seq * values, int lineno, PyArena *arena);
expr_ty ListComp(expr_ty elt, asdl_seq * generators, int lineno, PyArena expr_ty ListComp(expr_ty elt, asdl_seq * generators, int lineno, PyArena
*arena); *arena);

View File

@ -40,40 +40,43 @@
#define try_stmt 295 #define try_stmt 295
#define except_clause 296 #define except_clause 296
#define suite 297 #define suite 297
#define test 298 #define testlist_safe 298
#define and_test 299 #define old_test 299
#define not_test 300 #define old_lambdef 300
#define comparison 301 #define test 301
#define comp_op 302 #define or_test 302
#define expr 303 #define and_test 303
#define xor_expr 304 #define not_test 304
#define and_expr 305 #define comparison 305
#define shift_expr 306 #define comp_op 306
#define arith_expr 307 #define expr 307
#define term 308 #define xor_expr 308
#define factor 309 #define and_expr 309
#define power 310 #define shift_expr 310
#define atom 311 #define arith_expr 311
#define listmaker 312 #define term 312
#define testlist_gexp 313 #define factor 313
#define lambdef 314 #define power 314
#define trailer 315 #define atom 315
#define subscriptlist 316 #define listmaker 316
#define subscript 317 #define testlist_gexp 317
#define sliceop 318 #define lambdef 318
#define exprlist 319 #define trailer 319
#define testlist 320 #define subscriptlist 320
#define testlist_safe 321 #define subscript 321
#define dictmaker 322 #define sliceop 322
#define classdef 323 #define exprlist 323
#define arglist 324 #define testlist 324
#define argument 325 #define dictmaker 325
#define list_iter 326 #define classdef 326
#define list_for 327 #define arglist 327
#define list_if 328 #define argument 328
#define gen_iter 329 #define list_iter 329
#define gen_for 330 #define list_for 330
#define gen_if 331 #define list_if 331
#define testlist1 332 #define gen_iter 332
#define encoding_decl 333 #define gen_for 333
#define yield_expr 334 #define gen_if 334
#define testlist1 335
#define encoding_decl 336
#define yield_expr 337

View File

@ -798,3 +798,28 @@ verify(len(list(g)) == 10)
x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x)) x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x))
x = 5; t = True; x = 5; t = True;
verify([(i,j) for i in range(10) for j in range(5)] == list(g)) verify([(i,j) for i in range(10) for j in range(5)] == list(g))
# Test ifelse expressions in various cases
def _checkeval(msg, ret):
"helper to check that evaluation of expressions is done correctly"
print x
return ret
verify([ x() for x in lambda: True, lambda: False if x() ] == [True])
verify([ x() for x in (lambda: True, lambda: False) if x() ] == [True])
verify([ x(False) for x in (lambda x: False if x else True, lambda x: True if x else False) if x(False) ] == [True])
verify((5 if 1 else _checkeval("check 1", 0)) == 5)
verify((_checkeval("check 2", 0) if 0 else 5) == 5)
verify((5 and 6 if 0 else 1) == 1)
verify(((5 and 6) if 0 else 1) == 1)
verify((5 and (6 if 1 else 1)) == 6)
verify((0 or _checkeval("check 3", 2) if 0 else 3) == 3)
verify((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)) == 1)
verify((0 or 5 if 1 else _checkeval("check 6", 3)) == 5)
verify((not 5 if 1 else 1) == False)
verify((not 5 if 0 else 1) == 1)
verify((6 + 1 if 1 else 2) == 7)
verify((6 - 1 if 1 else 2) == 5)
verify((6 * 2 if 1 else 4) == 12)
verify((6 / 2 if 1 else 3) == 3)
verify((6 < 4 if 0 else 2) == 2)

View File

@ -52,6 +52,7 @@ module Python
| BinOp(expr left, operator op, expr right) | BinOp(expr left, operator op, expr right)
| UnaryOp(unaryop op, expr operand) | UnaryOp(unaryop op, expr operand)
| Lambda(arguments args, expr body) | Lambda(arguments args, expr body)
| IfExp(expr test, expr body, expr orelse)
| Dict(expr* keys, expr* values) | Dict(expr* keys, expr* values)
| ListComp(expr elt, comprehension* generators) | ListComp(expr elt, comprehension* generators)
| GeneratorExp(expr elt, comprehension* generators) | GeneratorExp(expr elt, comprehension* generators)

View File

@ -151,6 +151,12 @@ char *Lambda_fields[]={
"args", "args",
"body", "body",
}; };
PyTypeObject *IfExp_type;
char *IfExp_fields[]={
"test",
"body",
"orelse",
};
PyTypeObject *Dict_type; PyTypeObject *Dict_type;
char *Dict_fields[]={ char *Dict_fields[]={
"keys", "keys",
@ -431,6 +437,7 @@ static int init_types(void)
BinOp_type = make_type("BinOp", expr_type, BinOp_fields, 3); BinOp_type = make_type("BinOp", expr_type, BinOp_fields, 3);
UnaryOp_type = make_type("UnaryOp", expr_type, UnaryOp_fields, 2); UnaryOp_type = make_type("UnaryOp", expr_type, UnaryOp_fields, 2);
Lambda_type = make_type("Lambda", expr_type, Lambda_fields, 2); Lambda_type = make_type("Lambda", expr_type, Lambda_fields, 2);
IfExp_type = make_type("IfExp", expr_type, IfExp_fields, 3);
Dict_type = make_type("Dict", expr_type, Dict_fields, 2); Dict_type = make_type("Dict", expr_type, Dict_fields, 2);
ListComp_type = make_type("ListComp", expr_type, ListComp_fields, 2); ListComp_type = make_type("ListComp", expr_type, ListComp_fields, 2);
GeneratorExp_type = make_type("GeneratorExp", expr_type, GeneratorExp_type = make_type("GeneratorExp", expr_type,
@ -1137,6 +1144,38 @@ Lambda(arguments_ty args, expr_ty body, int lineno, PyArena *arena)
return p; return p;
} }
expr_ty
IfExp(expr_ty test, expr_ty body, expr_ty orelse, int lineno, PyArena *arena)
{
expr_ty p;
if (!test) {
PyErr_SetString(PyExc_ValueError,
"field test is required for IfExp");
return NULL;
}
if (!body) {
PyErr_SetString(PyExc_ValueError,
"field body is required for IfExp");
return NULL;
}
if (!orelse) {
PyErr_SetString(PyExc_ValueError,
"field orelse is required for IfExp");
return NULL;
}
p = (expr_ty)PyArena_Malloc(arena, sizeof(*p));
if (!p) {
PyErr_NoMemory();
return NULL;
}
p->kind = IfExp_kind;
p->v.IfExp.test = test;
p->v.IfExp.body = body;
p->v.IfExp.orelse = orelse;
p->lineno = lineno;
return p;
}
expr_ty expr_ty
Dict(asdl_seq * keys, asdl_seq * values, int lineno, PyArena *arena) Dict(asdl_seq * keys, asdl_seq * values, int lineno, PyArena *arena)
{ {
@ -2077,6 +2116,25 @@ ast2obj_expr(void* _o)
goto failed; goto failed;
Py_DECREF(value); Py_DECREF(value);
break; break;
case IfExp_kind:
result = PyType_GenericNew(IfExp_type, NULL, NULL);
if (!result) goto failed;
value = ast2obj_expr(o->v.IfExp.test);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "test", value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_expr(o->v.IfExp.body);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "body", value) == -1)
goto failed;
Py_DECREF(value);
value = ast2obj_expr(o->v.IfExp.orelse);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "orelse", value) == -1)
goto failed;
Py_DECREF(value);
break;
case Dict_kind: case Dict_kind:
result = PyType_GenericNew(Dict_type, NULL, NULL); result = PyType_GenericNew(Dict_type, NULL, NULL);
if (!result) goto failed; if (!result) goto failed;

View File

@ -847,6 +847,25 @@ ast_for_lambdef(struct compiling *c, const node *n)
return Lambda(args, expression, LINENO(n), c->c_arena); return Lambda(args, expression, LINENO(n), c->c_arena);
} }
static expr_ty
ast_for_ifexpr(struct compiling *c, const node *n)
{
/* test: or_test 'if' or_test 'else' test */
expr_ty expression, body, orelse;
assert(NCH(n) >= 3);
body = ast_for_expr(c, CHILD(n, 0));
if (!body)
return NULL;
expression = ast_for_expr(c, CHILD(n, 2));
if (!expression)
return NULL;
orelse = ast_for_expr(c, CHILD(n, 4));
if (!orelse)
return NULL;
return IfExp(expression, body, orelse, LINENO(n), c->c_arena);
}
/* Count the number of 'for' loop in a list comprehension. /* Count the number of 'for' loop in a list comprehension.
Helper for ast_for_listcomp(). Helper for ast_for_listcomp().
@ -1456,7 +1475,8 @@ static expr_ty
ast_for_expr(struct compiling *c, const node *n) ast_for_expr(struct compiling *c, const node *n)
{ {
/* handle the full range of simple expressions /* handle the full range of simple expressions
test: and_test ('or' and_test)* | lambdef test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)* and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)* comparison: expr (comp_op expr)*
@ -1468,6 +1488,15 @@ ast_for_expr(struct compiling *c, const node *n)
term: factor (('*'|'/'|'%'|'//') factor)* term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power factor: ('+'|'-'|'~') factor | power
power: atom trailer* ('**' factor)* power: atom trailer* ('**' factor)*
As well as modified versions that exist for backward compatibility,
to explicitly allow:
[ x for x in lambda: 0, lambda: 1 ]
(which would be ambiguous without these extra rules)
old_test: or_test | old_lambdef
old_lambdef: 'lambda' [vararglist] ':' old_test
*/ */
asdl_seq *seq; asdl_seq *seq;
@ -1476,9 +1505,14 @@ ast_for_expr(struct compiling *c, const node *n)
loop: loop:
switch (TYPE(n)) { switch (TYPE(n)) {
case test: case test:
if (TYPE(CHILD(n, 0)) == lambdef) case old_test:
if (TYPE(CHILD(n, 0)) == lambdef ||
TYPE(CHILD(n, 0)) == old_lambdef)
return ast_for_lambdef(c, CHILD(n, 0)); return ast_for_lambdef(c, CHILD(n, 0));
/* Fall through to and_test */ else if (NCH(n) > 1)
return ast_for_ifexpr(c, n);
/* Fallthrough */
case or_test:
case and_test: case and_test:
if (NCH(n) == 1) { if (NCH(n) == 1) {
n = CHILD(n, 0); n = CHILD(n, 0);

View File

@ -2009,6 +2009,30 @@ compiler_class(struct compiler *c, stmt_ty s)
return 1; return 1;
} }
static int
compiler_ifexp(struct compiler *c, expr_ty e)
{
basicblock *end, *next;
assert(e->kind == IfExp_kind);
end = compiler_new_block(c);
if (end == NULL)
return 0;
next = compiler_new_block(c);
if (next == NULL)
return 0;
VISIT(c, expr, e->v.IfExp.test);
ADDOP_JREL(c, JUMP_IF_FALSE, next);
ADDOP(c, POP_TOP);
VISIT(c, expr, e->v.IfExp.body);
ADDOP_JREL(c, JUMP_FORWARD, end);
compiler_use_next_block(c, next);
ADDOP(c, POP_TOP);
VISIT(c, expr, e->v.IfExp.orelse);
compiler_use_next_block(c, end);
return 1;
}
static int static int
compiler_lambda(struct compiler *c, expr_ty e) compiler_lambda(struct compiler *c, expr_ty e)
{ {
@ -3290,6 +3314,8 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
break; break;
case Lambda_kind: case Lambda_kind:
return compiler_lambda(c, e); return compiler_lambda(c, e);
case IfExp_kind:
return compiler_ifexp(c, e);
case Dict_kind: case Dict_kind:
/* XXX get rid of arg? */ /* XXX get rid of arg? */
ADDOP_I(c, BUILD_MAP, 0); ADDOP_I(c, BUILD_MAP, 0);

File diff suppressed because it is too large Load Diff

View File

@ -1084,6 +1084,11 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
return 0; return 0;
break; break;
} }
case IfExp_kind:
VISIT(st, expr, e->v.IfExp.test);
VISIT(st, expr, e->v.IfExp.body);
VISIT(st, expr, e->v.IfExp.orelse);
break;
case Dict_kind: case Dict_kind:
VISIT_SEQ(st, expr, e->v.Dict.keys); VISIT_SEQ(st, expr, e->v.Dict.keys);
VISIT_SEQ(st, expr, e->v.Dict.values); VISIT_SEQ(st, expr, e->v.Dict.values);