
Dear developers, Eric Raymond has sent me the following patch, which adds conditional expressions to Python. I'd like to hear opinions on whether this is a good thing to add to Python, and whether this is the right syntax. I am a bit skeptical about whether this is sufficiently Pythonic, but on the other hand there have always been requests for such a feature, and the existing solutions are ugly: a and b or c only works when you know for sure that b will never be false, and (a and [b] or [c])[0] is dead ugly... --Guido van Rossum (home page: http://www.python.org/~guido/) Subject: Ternary select -- here it is. From: "Eric S. Raymond" <esr@thyrsus.com> To: Guido Van Rossum <guido@CNRI.Reston.Va.US> Date: Sat, 29 Jan 2000 17:40:31 -0500 X-Eric-Conspiracy: There is no conspiracy Dang, Guido, this was *fun*! This patch extends the Python interpreter to support the C ternary operator, and documents the extension in the Reference Manual. The implementation is dead simple and robust: it's adapted from the existing code for if statements, and adds or changes less than 70 lines of code all told. Diffs between last version checked in and current workfile(s): --- Grammar/Grammar 2000/01/28 17:10:18 1.1 +++ Grammar/Grammar 2000/01/29 22:14:05 @@ -61,7 +61,8 @@ except_clause: 'except' [test [',' test]] suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT -test: and_test ('or' and_test)* | lambdef +test: bool_test ['?' bool_test ':' bool_test] +bool_test: and_test ('or' and_test)* | lambdef and_test: not_test ('and' not_test)* not_test: 'not' not_test | comparison comparison: expr (comp_op expr)* --- Include/token.h 2000/01/28 17:38:55 1.1 +++ Include/token.h 2000/01/29 01:27:00 @@ -74,10 +74,11 @@ #define LEFTSHIFT 34 #define RIGHTSHIFT 35 #define DOUBLESTAR 36 +#define QUERY 37 /* Don't forget to update the table _PyParser_TokenNames in tokenizer.c! */ -#define OP 37 -#define ERRORTOKEN 38 -#define N_TOKENS 39 +#define OP 38 +#define ERRORTOKEN 39 +#define N_TOKENS 34 /* Special definitions for cooperation with parser */ --- Modules/parsermodule.c 2000/01/28 18:03:27 1.1 +++ Modules/parsermodule.c 2000/01/29 22:13:45 @@ -945,6 +945,7 @@ #define validate_star(ch) validate_terminal(ch, STAR, "*") #define validate_vbar(ch) validate_terminal(ch, VBAR, "|") #define validate_doublestar(ch) validate_terminal(ch, DOUBLESTAR, "**") +#define validate_query(ch) validate_terminal(ch, QUERY, "?") #define validate_dot(ch) validate_terminal(ch, DOT, ".") #define validate_name(ch, str) validate_terminal(ch, NAME, str) @@ -963,7 +964,8 @@ VALIDATER(exec_stmt); VALIDATER(compound_stmt); VALIDATER(while); VALIDATER(for); VALIDATER(try); VALIDATER(except_clause); -VALIDATER(test); VALIDATER(and_test); +VALIDATER(test); +VALIDATER(bool_test); VALIDATER(and_test); VALIDATER(not_test); VALIDATER(comparison); VALIDATER(comp_op); VALIDATER(expr); VALIDATER(xor_expr); VALIDATER(and_expr); @@ -1829,12 +1831,34 @@ } /* validate_except_clause() */ +/* bool_test ( | bool_test ? bool_test ) + * + */ static int validate_test(tree) node *tree; { + if (!validate_ntype(tree, test)) + return 0; + else if (NCH(tree) == 1) + return(validate_bool_test(CHILD(tree, 0))); + else if (validate_numnodes(tree, 5, "expr")) + { + return validate_bool_test(CHILD(tree, 0)) + && validate_query(CHILD(tree, 1)) + && validate_bool_test(CHILD(tree, 2)) + && validate_colon(CHILD(tree, 3)) + && validate_bool_test(CHILD(tree, 4)); + } +} /* validate_test() */ + + +static int +validate_bool_test(tree) + node *tree; +{ int nch = NCH(tree); - int res = validate_ntype(tree, test) && is_odd(nch); + int res = validate_ntype(tree, bool_test) && is_odd(nch); if (res && (TYPE(CHILD(tree, 0)) == lambdef)) res = ((nch == 1) @@ -1848,7 +1872,7 @@ } return (res); -} /* validate_test() */ +} /* validate_bool_test() */ static int --- Parser/tokenizer.c 2000/01/28 17:37:48 1.1 +++ Parser/tokenizer.c 2000/01/29 01:27:26 @@ -99,6 +99,7 @@ "LEFTSHIFT", "RIGHTSHIFT", "DOUBLESTAR", + "QUERY", /* This table must match the #defines in token.h! */ "OP", "<ERRORTOKEN>", @@ -384,6 +385,7 @@ case '}': return RBRACE; case '^': return CIRCUMFLEX; case '~': return TILDE; + case '?': return QUERY; default: return OP; } } --- Python/compile.c 2000/01/28 23:17:19 1.1 +++ Python/compile.c 2000/01/29 22:19:29 @@ -1698,11 +1698,11 @@ } static void -com_test(c, n) +com_bool_test(c, n) struct compiling *c; node *n; { - REQ(n, test); /* and_test ('or' and_test)* | lambdef */ + REQ(n, bool_test); /* and_test ('or' and_test)* | lambdef */ if (NCH(n) == 1 && TYPE(CHILD(n, 0)) == lambdef) { PyObject *v; int i; @@ -1738,6 +1738,32 @@ } static void +com_test(c, n) + struct compiling *c; + node *n; +{ + int op; + REQ(n, test); + com_bool_test(c, CHILD(n, 0)); + + /* is there a following ternary operator? */ + /* XXX optimize the compilation when the guard is a constant */ + if (NCH(n) == 5) + { + int anchor1 = 0, anchor2 = 0; + com_addfwref(c, JUMP_IF_FALSE, &anchor2); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + com_node(c, CHILD(n, 2)); + com_addfwref(c, JUMP_FORWARD, &anchor1); + com_backpatch(c, anchor2); + com_addbyte(c, POP_TOP); + com_node(c, CHILD(n, 4)); + com_backpatch(c, anchor1); + } +} + +static void com_list(c, n, toplevel) struct compiling *c; node *n; @@ -2931,6 +2957,9 @@ break; case test: com_test(c, n); + break; + case bool_test: + com_bool_test(c, n); break; case and_test: com_and_test(c, n); --- Doc/ref/ref5.tex 2000/01/29 21:28:13 1.1 +++ Doc/ref/ref5.tex 2000/01/29 22:00:02 @@ -764,7 +764,7 @@ \section{Boolean operations\label{Booleans}} \indexii{Boolean}{operation} -Boolean operations have the lowest priority of all Python operations: +Boolean operations have the lowest priority of all Python binary operations: \begin{verbatim} expression: or_test | lambda_form @@ -832,6 +832,24 @@ def make_incrementor(increment): return lambda x, n=increment: x+n \end{verbatim} + +\section{Select\label{select}} +\index{select} + +The select operator is a ternary operator with lower priority than +boolean operations (and thus lower priority than all other binary and +unary operators). + +\begin{verbatim} +select_expr: xor_expr | xor_expr "?" xor_expr ":" xor_expr +\end{verbatim} + +If its first operand is nonempty, the value of a select operation is +its second operand; otherwise the value is the third operand. + +(The semantics and precedence level of select are intended to be +unsurprising to programmers familiar with \C's ternary select +operator.) \section{Expression lists\label{exprlists}} \indexii{expression}{list} End of diffs. -- <a href="http://www.tuxedo.org/~esr">Eric S. Raymond</a> You know why there's a Second Amendment? In case the government fails to follow the first one. -- Rush Limbaugh, in a moment of unaccustomed profundity 17 Aug 1993