diff --git a/Doc/library/token-list.inc b/Doc/library/token-list.inc
index 877d39a432ed70..d3c3a2b8b69e82 100644
--- a/Doc/library/token-list.inc
+++ b/Doc/library/token-list.inc
@@ -201,6 +201,10 @@
Token value for ``":="``.
+.. data:: QUESTION
+
+ Token value for ``"?"``.
+
.. data:: OP
.. data:: AWAIT
diff --git a/Grammar/Tokens b/Grammar/Tokens
index 9de2da5d15fc3b..dbfcd310de86d3 100644
--- a/Grammar/Tokens
+++ b/Grammar/Tokens
@@ -53,6 +53,7 @@ ATEQUAL '@='
RARROW '->'
ELLIPSIS '...'
COLONEQUAL ':='
+QUESTION '?'
OP
AWAIT
diff --git a/Grammar/python.gram b/Grammar/python.gram
index c5a5dbe1724f3e..43a0b1e474b31f 100644
--- a/Grammar/python.gram
+++ b/Grammar/python.gram
@@ -327,6 +327,7 @@ expressions[expr_ty]:
_Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) }
| a=expression ',' { _Py_Tuple(CHECK(_PyPegen_singleton_seq(p, a)), Load, EXTRA) }
| expression
+
expression[expr_ty] (memo):
| a=disjunction 'if' b=disjunction 'else' c=expression { _Py_IfExp(b, a, c, EXTRA) }
| disjunction
@@ -452,6 +453,7 @@ factor[expr_ty] (memo):
| '+' a=factor { _Py_UnaryOp(UAdd, a, EXTRA) }
| '-' a=factor { _Py_UnaryOp(USub, a, EXTRA) }
| '~' a=factor { _Py_UnaryOp(Invert, a, EXTRA) }
+ | a=factor '?' { _Py_UnaryOp(Question, a, EXTRA) }
| power
power[expr_ty]:
| a=await_primary '**' b=factor { _Py_BinOp(a, Pow, b, EXTRA) }
@@ -656,8 +658,8 @@ invalid_assignment:
RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) }
| (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") }
| a=star_expressions augassign (yield_expr | star_expressions) {
- RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
- a,
+ RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
+ a,
"'%s' is an illegal expression for augmented assignment",
_PyPegen_get_expr_name(a)
)}
diff --git a/Include/Python-ast.h b/Include/Python-ast.h
index e7afa1e6579e8d..258f79ef166de7 100644
--- a/Include/Python-ast.h
+++ b/Include/Python-ast.h
@@ -25,7 +25,8 @@ typedef enum _operator { Add=1, Sub=2, Mult=3, MatMult=4, Div=5, Mod=6, Pow=7,
LShift=8, RShift=9, BitOr=10, BitXor=11, BitAnd=12,
FloorDiv=13 } operator_ty;
-typedef enum _unaryop { Invert=1, Not=2, UAdd=3, USub=4 } unaryop_ty;
+typedef enum _unaryop { Invert=1, Not=2, UAdd=3, USub=4, Question=5 }
+ unaryop_ty;
typedef enum _cmpop { Eq=1, NotEq=2, Lt=3, LtE=4, Gt=5, GtE=6, Is=7, IsNot=8,
In=9, NotIn=10 } cmpop_ty;
@@ -223,7 +224,7 @@ struct _stmt {
int end_col_offset;
};
-enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, BinOp_kind=3, UnaryOp_kind=4,
+enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, UnaryOp_kind=3, BinOp_kind=4,
Lambda_kind=5, IfExp_kind=6, Dict_kind=7, Set_kind=8,
ListComp_kind=9, SetComp_kind=10, DictComp_kind=11,
GeneratorExp_kind=12, Await_kind=13, Yield_kind=14,
@@ -244,17 +245,17 @@ struct _expr {
expr_ty value;
} NamedExpr;
+ struct {
+ unaryop_ty op;
+ expr_ty operand;
+ } UnaryOp;
+
struct {
expr_ty left;
operator_ty op;
expr_ty right;
} BinOp;
- struct {
- unaryop_ty op;
- expr_ty operand;
- } UnaryOp;
-
struct {
arguments_ty args;
expr_ty body;
@@ -565,13 +566,13 @@ expr_ty _Py_BoolOp(boolop_ty op, asdl_seq * values, int lineno, int col_offset,
expr_ty _Py_NamedExpr(expr_ty target, expr_ty value, int lineno, int
col_offset, int end_lineno, int end_col_offset, PyArena
*arena);
+#define UnaryOp(a0, a1, a2, a3, a4, a5, a6) _Py_UnaryOp(a0, a1, a2, a3, a4, a5, a6)
+expr_ty _Py_UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset,
+ int end_lineno, int end_col_offset, PyArena *arena);
#define BinOp(a0, a1, a2, a3, a4, a5, a6, a7) _Py_BinOp(a0, a1, a2, a3, a4, a5, a6, a7)
expr_ty _Py_BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int
col_offset, int end_lineno, int end_col_offset, PyArena
*arena);
-#define UnaryOp(a0, a1, a2, a3, a4, a5, a6) _Py_UnaryOp(a0, a1, a2, a3, a4, a5, a6)
-expr_ty _Py_UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset,
- int end_lineno, int end_col_offset, PyArena *arena);
#define Lambda(a0, a1, a2, a3, a4, a5, a6) _Py_Lambda(a0, a1, a2, a3, a4, a5, a6)
expr_ty _Py_Lambda(arguments_ty args, expr_ty body, int lineno, int col_offset,
int end_lineno, int end_col_offset, PyArena *arena);
diff --git a/Include/Python.h b/Include/Python.h
index 57f71d41d8d477..b9f3cee218e2fd 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -122,6 +122,7 @@
#include "iterobject.h"
#include "genobject.h"
#include "descrobject.h"
+#include "optionalobject.h"
#include "genericaliasobject.h"
#include "warnings.h"
#include "weakrefobject.h"
diff --git a/Include/opcode.h b/Include/opcode.h
index 19944fac0b9f2b..300dbdc6385a96 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -17,6 +17,7 @@ extern "C" {
#define UNARY_POSITIVE 10
#define UNARY_NEGATIVE 11
#define UNARY_NOT 12
+#define UNARY_QUESTION 14
#define UNARY_INVERT 15
#define BINARY_MATRIX_MULTIPLY 16
#define INPLACE_MATRIX_MULTIPLY 17
diff --git a/Include/optionalobject.h b/Include/optionalobject.h
new file mode 100644
index 00000000000000..ffeeafcb234efb
--- /dev/null
+++ b/Include/optionalobject.h
@@ -0,0 +1,15 @@
+
+#ifndef Py_OPTIONALTYPEOBJECT_H
+#define Py_OPTIONALTYPEOBJECT_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ PyAPI_FUNC(PyObject *) Py_Optional(PyObject *);
+ PyAPI_DATA(PyTypeObject) Py_OptionalType;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_OPTIONALTYPEOBJECT_H */
diff --git a/Include/token.h b/Include/token.h
index 9b8a3aae074674..c4f30f75ca7ffb 100644
--- a/Include/token.h
+++ b/Include/token.h
@@ -64,13 +64,14 @@ extern "C" {
#define RARROW 51
#define ELLIPSIS 52
#define COLONEQUAL 53
-#define OP 54
-#define AWAIT 55
-#define ASYNC 56
-#define TYPE_IGNORE 57
-#define TYPE_COMMENT 58
-#define ERRORTOKEN 59
-#define N_TOKENS 63
+#define QUESTION 54
+#define OP 55
+#define AWAIT 56
+#define ASYNC 57
+#define TYPE_IGNORE 58
+#define TYPE_COMMENT 59
+#define ERRORTOKEN 60
+#define N_TOKENS 64
#define NT_OFFSET 256
/* Special definitions for cooperation with parser */
diff --git a/Lib/opcode.py b/Lib/opcode.py
index ac1aa535f66ff6..f0e88619c0794d 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -65,7 +65,7 @@ def jabs_op(name, op):
def_op('UNARY_POSITIVE', 10)
def_op('UNARY_NEGATIVE', 11)
def_op('UNARY_NOT', 12)
-
+def_op('UNARY_QUESTION', 14)
def_op('UNARY_INVERT', 15)
def_op('BINARY_MATRIX_MULTIPLY', 16)
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index ef7d1a15c7570d..99a0ab590ca794 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -407,6 +407,10 @@ def test_var_annot_simple_exec(self):
with self.assertRaises(KeyError):
gns['__annotations__']
+ def test_annot_optional(self):
+ x: int? = None
+ self.assertRaises(SyntaxError, eval, "?bar = 1")
+
def test_var_annot_custom_maps(self):
# tests with custom locals() and __annotations__
ns = {'__annotations__': CNS()}
diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py
index 53639e984e48a7..af5043c32faced 100644
--- a/Lib/test/test_isinstance.py
+++ b/Lib/test/test_isinstance.py
@@ -4,6 +4,7 @@
import unittest
import sys
+import typing
@@ -271,6 +272,13 @@ def __bases__(self):
self.assertEqual(True, issubclass(B(), int))
+ def test_optional_isinstance(self):
+ self.assertTrue(issubclass(int, int?))
+ self.assertTrue(issubclass(Child, Super?))
+ self.assertTrue(isinstance(Child(), Super?))
+ self.assertTrue(isinstance(None, Super?))
+
+
def blowstack(fxn, arg, compare_to):
# Make sure that calling isinstance with a deeply nested tuple for its
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index 49dc5bf40e3ed8..6a263574d938a0 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -7,6 +7,7 @@
import locale
import sys
import types
+import typing
import unittest.mock
import weakref
@@ -598,6 +599,9 @@ def test_method_descriptor_types(self):
self.assertIsInstance(int.from_bytes, types.BuiltinMethodType)
self.assertIsInstance(int.__new__, types.BuiltinMethodType)
+ def test_optional_operator_types(self):
+ self.assertEqual(int?, typing.Optional[int])
+ self.assertIsInstance(1, int?)
class MappingProxyTests(unittest.TestCase):
mappingproxy = types.MappingProxyType
@@ -803,7 +807,6 @@ def test_union(self):
self.assertDictEqual(mapping, {'a': 0, 'b': 1, 'c': 2})
self.assertDictEqual(other, {'c': 3, 'p': 0})
-
class ClassCreationTests(unittest.TestCase):
class Meta(type):
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index f429e883b59538..423f26e7bf5fe3 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -3585,6 +3585,17 @@ def foo(a: A) -> Optional[BaseException]:
assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt)
assert foo(None) is None
+ def test_new_optional(self):
+ assert int? == Optional[int]
+ assert int? != int
+
+ A = Type[BaseException]?
+ def foo(a: A) -> BaseException?:
+ if a is None:
+ return None
+ else:
+ return a()
+ assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt)
class NewTypeTests(BaseTestCase):
diff --git a/Lib/token.py b/Lib/token.py
index 493bf0426508fe..19de47c3cb8183 100644
--- a/Lib/token.py
+++ b/Lib/token.py
@@ -57,17 +57,18 @@
RARROW = 51
ELLIPSIS = 52
COLONEQUAL = 53
-OP = 54
-AWAIT = 55
-ASYNC = 56
-TYPE_IGNORE = 57
-TYPE_COMMENT = 58
+QUESTION = 54
+OP = 55
+AWAIT = 56
+ASYNC = 57
+TYPE_IGNORE = 58
+TYPE_COMMENT = 59
# These aren't used by the C tokenizer but are needed for tokenize.py
-ERRORTOKEN = 59
-COMMENT = 60
-NL = 61
-ENCODING = 62
-N_TOKENS = 63
+ERRORTOKEN = 60
+COMMENT = 61
+NL = 62
+ENCODING = 63
+N_TOKENS = 64
# Special definitions for cooperation with parser
NT_OFFSET = 256
@@ -113,6 +114,7 @@
'>=': GREATEREQUAL,
'>>': RIGHTSHIFT,
'>>=': RIGHTSHIFTEQUAL,
+ '?': QUESTION,
'@': AT,
'@=': ATEQUAL,
'[': LSQB,
diff --git a/Lib/tokenize.py b/Lib/tokenize.py
index 1aee21b5e18fa7..306d8821c981dd 100644
--- a/Lib/tokenize.py
+++ b/Lib/tokenize.py
@@ -600,7 +600,6 @@ def _tokenize(readline, encoding):
yield TokenInfo(ERRORTOKEN, line[pos],
(lnum, pos), (lnum, pos+1), line)
pos += 1
-
# Add an implicit NEWLINE if the input doesn't end in one
if last_line and last_line[-1] not in '\r\n':
yield TokenInfo(NEWLINE, '', (lnum - 1, len(last_line)), (lnum - 1, len(last_line) + 1), '')
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 3428b9842a5a0f..23b2eda28a61ef 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -429,6 +429,7 @@ OBJECT_OBJS= \
Objects/typeobject.o \
Objects/unicodeobject.o \
Objects/unicodectype.o \
+ Objects/optionalobject.o \
Objects/weakrefobject.o
##########################################################################
diff --git a/Objects/optionalobject.c b/Objects/optionalobject.c
new file mode 100644
index 00000000000000..ac62152cc884b2
--- /dev/null
+++ b/Objects/optionalobject.c
@@ -0,0 +1,283 @@
+// types.Optional -- used to represent typing.Optional[int] as ?int
+#include "Python.h"
+#include "pycore_object.h"
+#include "structmember.h"
+
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *args;
+} optionalobject;
+
+static void
+optionalobject_dealloc(PyObject *self)
+{
+ optionalobject *alias = (optionalobject *)self;
+
+ Py_XDECREF(alias->args);
+ self->ob_type->tp_free(self);
+}
+
+static Py_hash_t
+optional_hash(PyObject *self)
+{
+ optionalobject *alias = (optionalobject *)self;
+ Py_hash_t h1 = PyObject_Hash(alias->args);
+ if (h1 == -1) {
+ return -1;
+ }
+ return h1;
+}
+
+static PyMemberDef optional_members[] = {
+ {"__args__", T_OBJECT, offsetof(optionalobject, args), READONLY},
+ {0}
+};
+
+static PyObject *
+optional_getattro(PyObject *self, PyObject *name)
+{
+ return PyObject_GenericGetAttr(self, name);
+}
+
+static PyObject *
+optional_instancecheck(PyObject *self, PyObject *instance)
+{
+ optionalobject *alias = (optionalobject *)self;
+ PyObject *arg = alias->args;
+ if (PyType_Check(arg) && PyObject_IsInstance(instance, arg) != 0) {
+ Py_RETURN_TRUE;
+ }
+ if (instance == Py_None) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+optional_subclasscheck(PyObject *self, PyObject *instance)
+{
+ if (!PyType_Check(instance)) {
+ PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class");
+ return NULL;
+ }
+
+ optionalobject *alias = (optionalobject *)self;
+ PyObject *arg = alias->args;
+
+ if (PyType_Check(arg) && (PyType_IsSubtype((PyTypeObject *)instance, (PyTypeObject *)arg) != 0)) {
+ Py_RETURN_TRUE;
+ }
+
+ Py_RETURN_FALSE;
+}
+
+static PyMethodDef optional_methods[] = {
+ {"__instancecheck__", optional_instancecheck, METH_O},
+ {"__subclasscheck__", optional_subclasscheck, METH_O},
+ {0}};
+
+static int
+is_typing_module(PyObject *obj) {
+ PyObject *module = PyObject_GetAttrString(obj, "__module__");
+ if (module == NULL) {
+ return -1;
+ }
+ int is_typing = PyUnicode_Check(module) && _PyUnicode_EqualToASCIIString(module, "typing");
+ Py_DECREF(module);
+ return is_typing;
+}
+
+static int
+is_typing_name(PyObject *obj, char *name)
+{
+ PyTypeObject *type = Py_TYPE(obj);
+ if (strcmp(type->tp_name, name) != 0) {
+ return 0;
+ }
+ return is_typing_module(obj);
+}
+
+static PyObject *
+optional_richcompare(PyObject *a, PyObject *b, int op)
+{
+ if (op != Py_EQ && op != Py_NE) {
+ PyObject *result = Py_NotImplemented;
+ Py_INCREF(result);
+ return result;
+ }
+ optionalobject *aa = (optionalobject *)a;
+
+ int is_typing_union = is_typing_name(b, "_UnionGenericAlias");
+ if (is_typing_union < 0) {
+ return NULL;
+ }
+
+ PyTypeObject *type = Py_TYPE(b);
+ if (is_typing_union) {
+ // create a tuple of the optional arg + PyNone.
+ PyObject *a_tuple = PyTuple_Pack(2, aa->args, Py_TYPE(Py_None));
+ if (a_tuple == NULL) {
+ return NULL;
+ }
+ PyObject *a_set = PySet_New(a_tuple);
+ if (a_set == NULL) {
+ Py_DECREF(a_set);
+ return NULL;
+ }
+ Py_DECREF(a_tuple);
+
+ PyObject* b_args = PyObject_GetAttrString(b, "__args__");
+ if (b_args == NULL) {
+ Py_DECREF(a_set);
+ return NULL;
+ }
+ PyObject *b_set = PySet_New(b_args);
+ if (b_set == NULL) {
+ Py_DECREF(a_set);
+ Py_DECREF(b_args);
+ return NULL;
+ }
+ Py_DECREF(b_args);
+ PyObject *result = PyObject_RichCompare(a_set, b_set, op);
+ Py_DECREF(a_set);
+ Py_DECREF(b_set);
+ return result;
+ } else if (type == &Py_OptionalType) {
+ optionalobject *bb = (optionalobject *)b;
+ PyObject *result = PyObject_RichCompare(aa->args, bb->args, op);
+ return result;
+ }
+ if (op == Py_NE) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+
+static int
+optional_repr_item(_PyUnicodeWriter *writer, PyObject *p)
+{
+ _Py_IDENTIFIER(__module__);
+ _Py_IDENTIFIER(__qualname__);
+ _Py_IDENTIFIER(__origin__);
+ _Py_IDENTIFIER(__args__);
+ PyObject *qualname = NULL;
+ PyObject *module = NULL;
+ PyObject *r = NULL;
+ PyObject *tmp;
+ int err;
+
+ if (p == Py_Ellipsis) {
+ // The Ellipsis object
+ r = PyUnicode_FromString("...");
+ goto done;
+ }
+
+ if (_PyObject_LookupAttrId(p, &PyId___origin__, &tmp) < 0) {
+ goto done;
+ }
+ if (tmp != NULL) {
+ Py_DECREF(tmp);
+ if (_PyObject_LookupAttrId(p, &PyId___args__, &tmp) < 0) {
+ goto done;
+ }
+ if (tmp != NULL) {
+ Py_DECREF(tmp);
+ // It looks like a GenericAlias
+ goto use_repr;
+ }
+ }
+
+ if (_PyObject_LookupAttrId(p, &PyId___qualname__, &qualname) < 0) {
+ goto done;
+ }
+ if (qualname == NULL) {
+ goto use_repr;
+ }
+ if (_PyObject_LookupAttrId(p, &PyId___module__, &module) < 0) {
+ goto done;
+ }
+ if (module == NULL || module == Py_None) {
+ goto use_repr;
+ }
+
+ // Looks like a class
+ if (PyUnicode_Check(module) &&
+ _PyUnicode_EqualToASCIIString(module, "builtins"))
+ {
+ // builtins don't need a module name
+ r = PyObject_Str(qualname);
+ goto done;
+ }
+ else {
+ r = PyUnicode_FromFormat("%S.%S", module, qualname);
+ goto done;
+ }
+
+use_repr:
+ r = PyObject_Repr(p);
+
+done:
+ Py_XDECREF(qualname);
+ Py_XDECREF(module);
+ if (r == NULL) {
+ // error if any of the above PyObject_Repr/PyUnicode_From* fail
+ err = -1;
+ }
+ else {
+ err = _PyUnicodeWriter_WriteStr(writer, r);
+ Py_DECREF(r);
+ }
+ return err;
+}
+
+static PyObject *
+optional_repr(PyObject *self)
+{
+ optionalobject *alias = (optionalobject *)self;
+ _PyUnicodeWriter writer;
+ _PyUnicodeWriter_Init(&writer);
+ if (_PyUnicodeWriter_WriteASCIIString(&writer, "?", 1) < 0) {
+ goto error;
+ }
+ if (optional_repr_item(&writer, alias->args) < 0) {
+ goto error;
+ }
+ return _PyUnicodeWriter_Finish(&writer);
+
+error:
+ _PyUnicodeWriter_Dealloc(&writer);
+ return NULL;
+}
+
+PyTypeObject Py_OptionalType = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ .tp_name = "types.Optional",
+ .tp_doc = "Represent an Optional type\n"
+ "\n"
+ "E.g. ?int",
+ .tp_basicsize = sizeof(optionalobject),
+ .tp_dealloc = optionalobject_dealloc,
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_free = PyObject_Del,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_hash = optional_hash,
+ .tp_getattro = optional_getattro,
+ .tp_members = optional_members,
+ .tp_methods = optional_methods,
+ .tp_richcompare = optional_richcompare,
+ .tp_repr = optional_repr,
+};
+
+PyObject *
+Py_Optional(PyObject *args)
+{
+ optionalobject *alias = PyObject_New(optionalobject, &Py_OptionalType);
+ if (alias == NULL) {
+ return NULL;
+ }
+ Py_INCREF(args);
+ alias->args = args;
+ return (PyObject *) alias;
+}
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index db26e38911bc05..1282b1e2f7bc2e 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -391,6 +391,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 1f3883768c9a25..64cf6113f273dc 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -1178,10 +1178,13 @@
Objects
+
+ Objects
+
Resource Files
-
\ No newline at end of file
+
diff --git a/Parser/Python.asdl b/Parser/Python.asdl
index 889712b4b3d36e..4ff475e344f058 100644
--- a/Parser/Python.asdl
+++ b/Parser/Python.asdl
@@ -54,8 +54,8 @@ module Python
-- BoolOp() can use left & right?
expr = BoolOp(boolop op, expr* values)
| NamedExpr(expr target, expr value)
- | BinOp(expr left, operator op, expr right)
| UnaryOp(unaryop op, expr operand)
+ | BinOp(expr left, operator op, expr right)
| Lambda(arguments args, expr body)
| IfExp(expr test, expr body, expr orelse)
| Dict(expr* keys, expr* values)
@@ -97,7 +97,7 @@ module Python
operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
| RShift | BitOr | BitXor | BitAnd | FloorDiv
- unaryop = Invert | Not | UAdd | USub
+ unaryop = Invert | Not | UAdd | USub | Question
cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
diff --git a/Parser/parser.c b/Parser/parser.c
index 323cd0e0efae30..05ed85d8077dfa 100644
--- a/Parser/parser.c
+++ b/Parser/parser.c
@@ -166,7 +166,7 @@ static KeywordToken *reserved_keywords[] = {
#define shift_expr_type 1097 // Left-recursive
#define sum_type 1098 // Left-recursive
#define term_type 1099 // Left-recursive
-#define factor_type 1100
+#define factor_type 1100 // Left-recursive
#define power_type 1101
#define await_primary_type 1102
#define primary_type 1103 // Left-recursive
@@ -6861,7 +6861,11 @@ annotated_rhs_rule(Parser *p)
return _res;
}
-// expressions: expression ((',' expression))+ ','? | expression ',' | expression
+// expressions:
+// | expression ((',' expression))+ ','?
+// | expression ','
+// | expression '?'
+// | expression
static expr_ty
expressions_rule(Parser *p)
{
@@ -6957,6 +6961,42 @@ expressions_rule(Parser *p)
D(fprintf(stderr, "%*c%s expressions[%d-%d]: %s failed!\n", p->level, ' ',
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ','"));
}
+ { // expression '?'
+ if (p->error_indicator) {
+ D(p->level--);
+ return NULL;
+ }
+ D(fprintf(stderr, "%*c> expressions[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression '?'"));
+ expr_ty a;
+ Token * b;
+ if (
+ (a = expression_rule(p)) // expression
+ &&
+ (b = _PyPegen_expect_token(p, 54)) // token='?'
+ )
+ {
+ D(fprintf(stderr, "%*c+ expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression '?'"));
+ Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+ if (_token == NULL) {
+ D(p->level--);
+ return NULL;
+ }
+ int _end_lineno = _token->end_lineno;
+ UNUSED(_end_lineno); // Only used by EXTRA macro
+ int _end_col_offset = _token->end_col_offset;
+ UNUSED(_end_col_offset); // Only used by EXTRA macro
+ _res = _Py_UnaryOp ( Question , a , EXTRA );
+ if (_res == NULL && PyErr_Occurred()) {
+ p->error_indicator = 1;
+ D(p->level--);
+ return NULL;
+ }
+ goto done;
+ }
+ p->mark = _mark;
+ D(fprintf(stderr, "%*c%s expressions[%d-%d]: %s failed!\n", p->level, ' ',
+ p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression '?'"));
+ }
{ // expression
if (p->error_indicator) {
D(p->level--);
@@ -9928,21 +9968,47 @@ term_raw(Parser *p)
return _res;
}
-// factor: '+' factor | '-' factor | '~' factor | power
+// Left-recursive
+// factor: '+' factor | '-' factor | '~' factor | factor '?' | power
+static expr_ty factor_raw(Parser *);
static expr_ty
factor_rule(Parser *p)
{
D(p->level++);
- if (p->error_indicator) {
- D(p->level--);
- return NULL;
- }
expr_ty _res = NULL;
if (_PyPegen_is_memoized(p, factor_type, &_res)) {
D(p->level--);
return _res;
}
int _mark = p->mark;
+ int _resmark = p->mark;
+ while (1) {
+ int tmpvar_7 = _PyPegen_update_memo(p, _mark, factor_type, _res);
+ if (tmpvar_7) {
+ D(p->level--);
+ return _res;
+ }
+ p->mark = _mark;
+ void *_raw = factor_raw(p);
+ if (_raw == NULL || p->mark <= _resmark)
+ break;
+ _resmark = p->mark;
+ _res = _raw;
+ }
+ p->mark = _resmark;
+ D(p->level--);
+ return _res;
+}
+static expr_ty
+factor_raw(Parser *p)
+{
+ D(p->level++);
+ if (p->error_indicator) {
+ D(p->level--);
+ return NULL;
+ }
+ expr_ty _res = NULL;
+ int _mark = p->mark;
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
p->error_indicator = 1;
D(p->level--);
@@ -10060,6 +10126,42 @@ factor_rule(Parser *p)
D(fprintf(stderr, "%*c%s factor[%d-%d]: %s failed!\n", p->level, ' ',
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'~' factor"));
}
+ { // factor '?'
+ if (p->error_indicator) {
+ D(p->level--);
+ return NULL;
+ }
+ D(fprintf(stderr, "%*c> factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "factor '?'"));
+ Token * _literal;
+ expr_ty a;
+ if (
+ (a = factor_rule(p)) // factor
+ &&
+ (_literal = _PyPegen_expect_token(p, 54)) // token='?'
+ )
+ {
+ D(fprintf(stderr, "%*c+ factor[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "factor '?'"));
+ Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+ if (_token == NULL) {
+ D(p->level--);
+ return NULL;
+ }
+ int _end_lineno = _token->end_lineno;
+ UNUSED(_end_lineno); // Only used by EXTRA macro
+ int _end_col_offset = _token->end_col_offset;
+ UNUSED(_end_col_offset); // Only used by EXTRA macro
+ _res = _Py_UnaryOp ( Question , a , EXTRA );
+ if (_res == NULL && PyErr_Occurred()) {
+ p->error_indicator = 1;
+ D(p->level--);
+ return NULL;
+ }
+ goto done;
+ }
+ p->mark = _mark;
+ D(fprintf(stderr, "%*c%s factor[%d-%d]: %s failed!\n", p->level, ' ',
+ p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "factor '?'"));
+ }
{ // power
if (p->error_indicator) {
D(p->level--);
@@ -10081,7 +10183,6 @@ factor_rule(Parser *p)
}
_res = NULL;
done:
- _PyPegen_insert_memo(p, _mark, factor_type, _res);
D(p->level--);
return _res;
}
@@ -10276,8 +10377,8 @@ primary_rule(Parser *p)
int _mark = p->mark;
int _resmark = p->mark;
while (1) {
- int tmpvar_7 = _PyPegen_update_memo(p, _mark, primary_type, _res);
- if (tmpvar_7) {
+ int tmpvar_8 = _PyPegen_update_memo(p, _mark, primary_type, _res);
+ if (tmpvar_8) {
D(p->level--);
return _res;
}
@@ -13928,8 +14029,8 @@ t_primary_rule(Parser *p)
int _mark = p->mark;
int _resmark = p->mark;
while (1) {
- int tmpvar_8 = _PyPegen_update_memo(p, _mark, t_primary_type, _res);
- if (tmpvar_8) {
+ int tmpvar_9 = _PyPegen_update_memo(p, _mark, t_primary_type, _res);
+ if (tmpvar_9) {
D(p->level--);
return _res;
}
diff --git a/Parser/token.c b/Parser/token.c
index a489668900901d..1541f17a89dc82 100644
--- a/Parser/token.c
+++ b/Parser/token.c
@@ -60,6 +60,7 @@ const char * const _PyParser_TokenNames[] = {
"RARROW",
"ELLIPSIS",
"COLONEQUAL",
+ "QUESTION",
"OP",
"AWAIT",
"ASYNC",
@@ -93,6 +94,7 @@ PyToken_OneChar(int c1)
case '<': return LESS;
case '=': return EQUAL;
case '>': return GREATER;
+ case '?': return QUESTION;
case '@': return AT;
case '[': return LSQB;
case ']': return RSQB;
diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c
index f3c1d9b20ade11..6c4295b8d00c8f 100644
--- a/Parser/tokenizer.c
+++ b/Parser/tokenizer.c
@@ -1378,6 +1378,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end)
return tok->done == E_EOF ? ENDMARKER : ERRORTOKEN;
}
+ /* Check for Optional shorthand */
+ if (c == '?') {
+ return QUESTION;
+ }
+
/* Identifier (most frequent token!) */
nonascii = 0;
if (is_potential_identifier_start(c)) {
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index 694987dd077881..f29c1dda36b6bb 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -106,6 +106,8 @@ typedef struct {
PyObject *Pass_type;
PyObject *Pow_singleton;
PyObject *Pow_type;
+ PyObject *Question_singleton;
+ PyObject *Question_type;
PyObject *RShift_singleton;
PyObject *RShift_type;
PyObject *Raise_type;
@@ -326,6 +328,8 @@ static int astmodule_clear(PyObject *module)
Py_CLEAR(astmodulestate(module)->Pass_type);
Py_CLEAR(astmodulestate(module)->Pow_singleton);
Py_CLEAR(astmodulestate(module)->Pow_type);
+ Py_CLEAR(astmodulestate(module)->Question_singleton);
+ Py_CLEAR(astmodulestate(module)->Question_type);
Py_CLEAR(astmodulestate(module)->RShift_singleton);
Py_CLEAR(astmodulestate(module)->RShift_type);
Py_CLEAR(astmodulestate(module)->Raise_type);
@@ -545,6 +549,8 @@ static int astmodule_traverse(PyObject *module, visitproc visit, void* arg)
Py_VISIT(astmodulestate(module)->Pass_type);
Py_VISIT(astmodulestate(module)->Pow_singleton);
Py_VISIT(astmodulestate(module)->Pow_type);
+ Py_VISIT(astmodulestate(module)->Question_singleton);
+ Py_VISIT(astmodulestate(module)->Question_type);
Py_VISIT(astmodulestate(module)->RShift_singleton);
Py_VISIT(astmodulestate(module)->RShift_type);
Py_VISIT(astmodulestate(module)->Raise_type);
@@ -909,15 +915,15 @@ static const char * const NamedExpr_fields[]={
"target",
"value",
};
+static const char * const UnaryOp_fields[]={
+ "op",
+ "operand",
+};
static const char * const BinOp_fields[]={
"left",
"op",
"right",
};
-static const char * const UnaryOp_fields[]={
- "op",
- "operand",
-};
static const char * const Lambda_fields[]={
"args",
"body",
@@ -1597,8 +1603,8 @@ static int init_types(void)
state->expr_type = make_type("expr", state->AST_type, NULL, 0,
"expr = BoolOp(boolop op, expr* values)\n"
" | NamedExpr(expr target, expr value)\n"
- " | BinOp(expr left, operator op, expr right)\n"
" | UnaryOp(unaryop op, expr operand)\n"
+ " | BinOp(expr left, operator op, expr right)\n"
" | Lambda(arguments args, expr body)\n"
" | IfExp(expr test, expr body, expr orelse)\n"
" | Dict(expr* keys, expr* values)\n"
@@ -1636,13 +1642,13 @@ static int init_types(void)
NamedExpr_fields, 2,
"NamedExpr(expr target, expr value)");
if (!state->NamedExpr_type) return 0;
- state->BinOp_type = make_type("BinOp", state->expr_type, BinOp_fields, 3,
- "BinOp(expr left, operator op, expr right)");
- if (!state->BinOp_type) return 0;
state->UnaryOp_type = make_type("UnaryOp", state->expr_type,
UnaryOp_fields, 2,
"UnaryOp(unaryop op, expr operand)");
if (!state->UnaryOp_type) return 0;
+ state->BinOp_type = make_type("BinOp", state->expr_type, BinOp_fields, 3,
+ "BinOp(expr left, operator op, expr right)");
+ if (!state->BinOp_type) return 0;
state->Lambda_type = make_type("Lambda", state->expr_type, Lambda_fields, 2,
"Lambda(arguments args, expr body)");
if (!state->Lambda_type) return 0;
@@ -1868,7 +1874,7 @@ static int init_types(void)
NULL);
if (!state->FloorDiv_singleton) return 0;
state->unaryop_type = make_type("unaryop", state->AST_type, NULL, 0,
- "unaryop = Invert | Not | UAdd | USub");
+ "unaryop = Invert | Not | UAdd | USub | Question");
if (!state->unaryop_type) return 0;
if (!add_attributes(state->unaryop_type, NULL, 0)) return 0;
state->Invert_type = make_type("Invert", state->unaryop_type, NULL, 0,
@@ -1896,6 +1902,13 @@ static int init_types(void)
state->USub_singleton = PyType_GenericNew((PyTypeObject *)state->USub_type,
NULL, NULL);
if (!state->USub_singleton) return 0;
+ state->Question_type = make_type("Question", state->unaryop_type, NULL, 0,
+ "Question");
+ if (!state->Question_type) return 0;
+ state->Question_singleton = PyType_GenericNew((PyTypeObject
+ *)state->Question_type, NULL,
+ NULL);
+ if (!state->Question_singleton) return 0;
state->cmpop_type = make_type("cmpop", state->AST_type, NULL, 0,
"cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn");
if (!state->cmpop_type) return 0;
@@ -2747,32 +2760,26 @@ NamedExpr(expr_ty target, expr_ty value, int lineno, int col_offset, int
}
expr_ty
-BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset,
- int end_lineno, int end_col_offset, PyArena *arena)
+UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, int
+ end_lineno, int end_col_offset, PyArena *arena)
{
expr_ty p;
- if (!left) {
- PyErr_SetString(PyExc_ValueError,
- "field 'left' is required for BinOp");
- return NULL;
- }
if (!op) {
PyErr_SetString(PyExc_ValueError,
- "field 'op' is required for BinOp");
+ "field 'op' is required for UnaryOp");
return NULL;
}
- if (!right) {
+ if (!operand) {
PyErr_SetString(PyExc_ValueError,
- "field 'right' is required for BinOp");
+ "field 'operand' is required for UnaryOp");
return NULL;
}
p = (expr_ty)PyArena_Malloc(arena, sizeof(*p));
if (!p)
return NULL;
- p->kind = BinOp_kind;
- p->v.BinOp.left = left;
- p->v.BinOp.op = op;
- p->v.BinOp.right = right;
+ p->kind = UnaryOp_kind;
+ p->v.UnaryOp.op = op;
+ p->v.UnaryOp.operand = operand;
p->lineno = lineno;
p->col_offset = col_offset;
p->end_lineno = end_lineno;
@@ -2781,26 +2788,32 @@ BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset,
}
expr_ty
-UnaryOp(unaryop_ty op, expr_ty operand, int lineno, int col_offset, int
- end_lineno, int end_col_offset, PyArena *arena)
+BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno, int col_offset,
+ int end_lineno, int end_col_offset, PyArena *arena)
{
expr_ty p;
+ if (!left) {
+ PyErr_SetString(PyExc_ValueError,
+ "field 'left' is required for BinOp");
+ return NULL;
+ }
if (!op) {
PyErr_SetString(PyExc_ValueError,
- "field 'op' is required for UnaryOp");
+ "field 'op' is required for BinOp");
return NULL;
}
- if (!operand) {
+ if (!right) {
PyErr_SetString(PyExc_ValueError,
- "field 'operand' is required for UnaryOp");
+ "field 'right' is required for BinOp");
return NULL;
}
p = (expr_ty)PyArena_Malloc(arena, sizeof(*p));
if (!p)
return NULL;
- p->kind = UnaryOp_kind;
- p->v.UnaryOp.op = op;
- p->v.UnaryOp.operand = operand;
+ p->kind = BinOp_kind;
+ p->v.BinOp.left = left;
+ p->v.BinOp.op = op;
+ p->v.BinOp.right = right;
p->lineno = lineno;
p->col_offset = col_offset;
p->end_lineno = end_lineno;
@@ -4167,39 +4180,39 @@ ast2obj_expr(void* _o)
goto failed;
Py_DECREF(value);
break;
- case BinOp_kind:
- tp = (PyTypeObject *)astmodulestate_global->BinOp_type;
+ case UnaryOp_kind:
+ tp = (PyTypeObject *)astmodulestate_global->UnaryOp_type;
result = PyType_GenericNew(tp, NULL, NULL);
if (!result) goto failed;
- value = ast2obj_expr(o->v.BinOp.left);
- if (!value) goto failed;
- if (PyObject_SetAttr(result, astmodulestate_global->left, value) == -1)
- goto failed;
- Py_DECREF(value);
- value = ast2obj_operator(o->v.BinOp.op);
+ value = ast2obj_unaryop(o->v.UnaryOp.op);
if (!value) goto failed;
if (PyObject_SetAttr(result, astmodulestate_global->op, value) == -1)
goto failed;
Py_DECREF(value);
- value = ast2obj_expr(o->v.BinOp.right);
+ value = ast2obj_expr(o->v.UnaryOp.operand);
if (!value) goto failed;
- if (PyObject_SetAttr(result, astmodulestate_global->right, value) == -1)
+ if (PyObject_SetAttr(result, astmodulestate_global->operand, value) ==
+ -1)
goto failed;
Py_DECREF(value);
break;
- case UnaryOp_kind:
- tp = (PyTypeObject *)astmodulestate_global->UnaryOp_type;
+ case BinOp_kind:
+ tp = (PyTypeObject *)astmodulestate_global->BinOp_type;
result = PyType_GenericNew(tp, NULL, NULL);
if (!result) goto failed;
- value = ast2obj_unaryop(o->v.UnaryOp.op);
+ value = ast2obj_expr(o->v.BinOp.left);
+ if (!value) goto failed;
+ if (PyObject_SetAttr(result, astmodulestate_global->left, value) == -1)
+ goto failed;
+ Py_DECREF(value);
+ value = ast2obj_operator(o->v.BinOp.op);
if (!value) goto failed;
if (PyObject_SetAttr(result, astmodulestate_global->op, value) == -1)
goto failed;
Py_DECREF(value);
- value = ast2obj_expr(o->v.UnaryOp.operand);
+ value = ast2obj_expr(o->v.BinOp.right);
if (!value) goto failed;
- if (PyObject_SetAttr(result, astmodulestate_global->operand, value) ==
- -1)
+ if (PyObject_SetAttr(result, astmodulestate_global->right, value) == -1)
goto failed;
Py_DECREF(value);
break;
@@ -4697,6 +4710,9 @@ PyObject* ast2obj_unaryop(unaryop_ty o)
case USub:
Py_INCREF(astmodulestate_global->USub_singleton);
return astmodulestate_global->USub_singleton;
+ case Question:
+ Py_INCREF(astmodulestate_global->Question_singleton);
+ return astmodulestate_global->Question_singleton;
}
Py_UNREACHABLE();
}
@@ -7434,98 +7450,98 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
if (*out == NULL) goto failed;
return 0;
}
- tp = astmodulestate_global->BinOp_type;
+ tp = astmodulestate_global->UnaryOp_type;
isinstance = PyObject_IsInstance(obj, tp);
if (isinstance == -1) {
return 1;
}
if (isinstance) {
- expr_ty left;
- operator_ty op;
- expr_ty right;
+ unaryop_ty op;
+ expr_ty operand;
- if (_PyObject_LookupAttr(obj, astmodulestate_global->left, &tmp) < 0) {
- return 1;
- }
- if (tmp == NULL) {
- PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from BinOp");
- return 1;
- }
- else {
- int res;
- res = obj2ast_expr(tmp, &left, arena);
- if (res != 0) goto failed;
- Py_CLEAR(tmp);
- }
if (_PyObject_LookupAttr(obj, astmodulestate_global->op, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
- PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BinOp");
+ PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from UnaryOp");
return 1;
}
else {
int res;
- res = obj2ast_operator(tmp, &op, arena);
+ res = obj2ast_unaryop(tmp, &op, arena);
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
- if (_PyObject_LookupAttr(obj, astmodulestate_global->right, &tmp) < 0) {
+ if (_PyObject_LookupAttr(obj, astmodulestate_global->operand, &tmp) <
+ 0) {
return 1;
}
if (tmp == NULL) {
- PyErr_SetString(PyExc_TypeError, "required field \"right\" missing from BinOp");
+ PyErr_SetString(PyExc_TypeError, "required field \"operand\" missing from UnaryOp");
return 1;
}
else {
int res;
- res = obj2ast_expr(tmp, &right, arena);
+ res = obj2ast_expr(tmp, &operand, arena);
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
- *out = BinOp(left, op, right, lineno, col_offset, end_lineno,
- end_col_offset, arena);
+ *out = UnaryOp(op, operand, lineno, col_offset, end_lineno,
+ end_col_offset, arena);
if (*out == NULL) goto failed;
return 0;
}
- tp = astmodulestate_global->UnaryOp_type;
+ tp = astmodulestate_global->BinOp_type;
isinstance = PyObject_IsInstance(obj, tp);
if (isinstance == -1) {
return 1;
}
if (isinstance) {
- unaryop_ty op;
- expr_ty operand;
+ expr_ty left;
+ operator_ty op;
+ expr_ty right;
+ if (_PyObject_LookupAttr(obj, astmodulestate_global->left, &tmp) < 0) {
+ return 1;
+ }
+ if (tmp == NULL) {
+ PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from BinOp");
+ return 1;
+ }
+ else {
+ int res;
+ res = obj2ast_expr(tmp, &left, arena);
+ if (res != 0) goto failed;
+ Py_CLEAR(tmp);
+ }
if (_PyObject_LookupAttr(obj, astmodulestate_global->op, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
- PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from UnaryOp");
+ PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BinOp");
return 1;
}
else {
int res;
- res = obj2ast_unaryop(tmp, &op, arena);
+ res = obj2ast_operator(tmp, &op, arena);
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
- if (_PyObject_LookupAttr(obj, astmodulestate_global->operand, &tmp) <
- 0) {
+ if (_PyObject_LookupAttr(obj, astmodulestate_global->right, &tmp) < 0) {
return 1;
}
if (tmp == NULL) {
- PyErr_SetString(PyExc_TypeError, "required field \"operand\" missing from UnaryOp");
+ PyErr_SetString(PyExc_TypeError, "required field \"right\" missing from BinOp");
return 1;
}
else {
int res;
- res = obj2ast_expr(tmp, &operand, arena);
+ res = obj2ast_expr(tmp, &right, arena);
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
- *out = UnaryOp(op, operand, lineno, col_offset, end_lineno,
- end_col_offset, arena);
+ *out = BinOp(left, op, right, lineno, col_offset, end_lineno,
+ end_col_offset, arena);
if (*out == NULL) goto failed;
return 0;
}
@@ -8999,6 +9015,14 @@ obj2ast_unaryop(PyObject* obj, unaryop_ty* out, PyArena* arena)
*out = USub;
return 0;
}
+ isinstance = PyObject_IsInstance(obj, astmodulestate_global->Question_type);
+ if (isinstance == -1) {
+ return 1;
+ }
+ if (isinstance) {
+ *out = Question;
+ return 0;
+ }
PyErr_Format(PyExc_TypeError, "expected some sort of unaryop, but got %R", obj);
return 1;
@@ -10084,15 +10108,15 @@ PyInit__ast(void)
goto error;
}
Py_INCREF(astmodulestate(m)->NamedExpr_type);
- if (PyModule_AddObject(m, "BinOp", astmodulestate_global->BinOp_type) < 0) {
- goto error;
- }
- Py_INCREF(astmodulestate(m)->BinOp_type);
if (PyModule_AddObject(m, "UnaryOp", astmodulestate_global->UnaryOp_type) <
0) {
goto error;
}
Py_INCREF(astmodulestate(m)->UnaryOp_type);
+ if (PyModule_AddObject(m, "BinOp", astmodulestate_global->BinOp_type) < 0) {
+ goto error;
+ }
+ Py_INCREF(astmodulestate(m)->BinOp_type);
if (PyModule_AddObject(m, "Lambda", astmodulestate_global->Lambda_type) <
0) {
goto error;
@@ -10313,6 +10337,11 @@ PyInit__ast(void)
goto error;
}
Py_INCREF(astmodulestate(m)->USub_type);
+ if (PyModule_AddObject(m, "Question", astmodulestate_global->Question_type)
+ < 0) {
+ goto error;
+ }
+ Py_INCREF(astmodulestate(m)->Question_type);
if (PyModule_AddObject(m, "cmpop", astmodulestate_global->cmpop_type) < 0) {
goto error;
}
diff --git a/Python/ceval.c b/Python/ceval.c
index 0386929a5b2b37..1ebbad4d73f010 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1624,6 +1624,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
DISPATCH();
}
+ case TARGET(UNARY_QUESTION): {
+ PyObject *value = TOP();
+ PyObject *res = Py_Optional(value);
+ Py_DECREF(value);
+ SET_TOP(res);
+ if (res == NULL)
+ goto error;
+ DISPATCH();
+ }
+
case TARGET(BINARY_POWER): {
PyObject *exp = POP();
PyObject *base = TOP();
diff --git a/Python/compile.c b/Python/compile.c
index 8fe82f91559e09..cf03f7a31831c3 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -874,6 +874,7 @@ stack_effect(int opcode, int oparg, int jump)
return 2;
/* Unary operators */
+ case UNARY_QUESTION:
case UNARY_POSITIVE:
case UNARY_NEGATIVE:
case UNARY_NOT:
@@ -3450,6 +3451,8 @@ unaryop(unaryop_ty op)
return UNARY_POSITIVE;
case USub:
return UNARY_NEGATIVE;
+ case Question:
+ return UNARY_QUESTION;
default:
PyErr_Format(PyExc_SystemError,
"unary op %d should not be possible", op);
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 538fdbe3e0b5ae..9b8456809967ab 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -13,7 +13,7 @@ static void *opcode_targets[256] = {
&&TARGET_UNARY_NEGATIVE,
&&TARGET_UNARY_NOT,
&&_unknown_opcode,
- &&_unknown_opcode,
+ &&TARGET_UNARY_QUESTION,
&&TARGET_UNARY_INVERT,
&&TARGET_BINARY_MATRIX_MULTIPLY,
&&TARGET_INPLACE_MATRIX_MULTIPLY,