[Python-Dev] Adding C ternary select (a?b:c) to Python?
Guido van Rossum
guido@CNRI.Reston.VA.US
Sun, 30 Jan 2000 08:32:18 -0500
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