Merge pull request #7373 from jakobandersen/cpp_alternate_ops

C++, alternative spellings of operators
This commit is contained in:
Jakob Lykke Andersen 2020-03-24 18:16:04 +01:00 committed by GitHub
commit 0aedc353d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 115 additions and 51 deletions

View File

@ -18,6 +18,7 @@ Bugs fixed
* #7364: autosummary: crashed when :confval:`autosummary_generate` is False
* #7370: autosummary: raises UnboundLocalError when unknown module given
* #7367: C++, alternate operator spellings are now supported.
Testing
--------

View File

@ -306,6 +306,7 @@ _operator_re = re.compile(r'''(?x)
| ->\*? | \,
| (<<|>>)=? | && | \|\|
| [!<>=/*%+|&^~-]=?
| (\b(and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|xor|xor_eq)\b)
''')
_fold_operator_re = re.compile(r'''(?x)
->\* | \.\* | \,
@ -464,37 +465,37 @@ _id_operator_v2 = {
# '-(unary)' : 'ng',
# '&(unary)' : 'ad',
# '*(unary)' : 'de',
'~': 'co',
'~': 'co', 'compl': 'co',
'+': 'pl',
'-': 'mi',
'*': 'ml',
'/': 'dv',
'%': 'rm',
'&': 'an',
'|': 'or',
'^': 'eo',
'&': 'an', 'bitand': 'an',
'|': 'or', 'bitor': 'or',
'^': 'eo', 'xor': 'eo',
'=': 'aS',
'+=': 'pL',
'-=': 'mI',
'*=': 'mL',
'/=': 'dV',
'%=': 'rM',
'&=': 'aN',
'|=': 'oR',
'^=': 'eO',
'&=': 'aN', 'and_eq': 'aN',
'|=': 'oR', 'or_eq': 'oR',
'^=': 'eO', 'xor_eq': 'eO',
'<<': 'ls',
'>>': 'rs',
'<<=': 'lS',
'>>=': 'rS',
'==': 'eq',
'!=': 'ne',
'!=': 'ne', 'not_eq': 'ne',
'<': 'lt',
'>': 'gt',
'<=': 'le',
'>=': 'ge',
'!': 'nt',
'&&': 'aa',
'||': 'oo',
'!': 'nt', 'not': 'nt',
'&&': 'aa', 'and': 'aa',
'||': 'oo', 'or': 'oo',
'++': 'pp',
'--': 'mm',
',': 'cm',
@ -511,8 +512,8 @@ _id_operator_unary_v2 = {
'&': 'ad',
'+': 'ps',
'-': 'ng',
'!': 'nt',
'~': 'co'
'!': 'nt', 'not': 'nt',
'~': 'co', 'compl': 'co'
}
_id_char_from_prefix = {
None: 'c', 'u8': 'c',
@ -520,21 +521,21 @@ _id_char_from_prefix = {
} # type: Dict[Any, str]
# these are ordered by preceedence
_expression_bin_ops = [
['||'],
['&&'],
['|'],
['^'],
['&'],
['==', '!='],
['||', 'or'],
['&&', 'and'],
['|', 'bitor'],
['^', 'xor'],
['&', 'bitand'],
['==', '!=', 'not_eq'],
['<=', '>=', '<', '>'],
['<<', '>>'],
['+', '-'],
['*', '/', '%'],
['.*', '->*']
]
_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "~"]
_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "not", "~", "compl"]
_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=",
">>=", "<<=", "&=", "^=", "|="]
">>=", "<<=", "&=", "and_eq", "^=", "|=", "xor_eq", "or_eq"]
_id_explicit_cast = {
'dynamic_cast': 'dc',
'static_cast': 'sc',
@ -1260,7 +1261,10 @@ class ASTUnaryOpExpr(ASTExpression):
self.expr = expr
def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.op) + transform(self.expr)
if self.op[0] in 'cn':
return transform(self.op) + " " + transform(self.expr)
else:
return transform(self.op) + transform(self.expr)
def get_id(self, version: int) -> str:
return _id_operator_unary_v2[self.op] + self.expr.get_id(version)
@ -1268,6 +1272,8 @@ class ASTUnaryOpExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text(self.op))
if self.op[0] in 'cn':
signode.append(nodes.Text(' '))
self.expr.describe_signature(signode, mode, env, symbol)
@ -1584,6 +1590,8 @@ class ASTOperatorBuildIn(ASTOperator):
def get_id(self, version: int) -> str:
if version == 1:
ids = _id_operator_v1
if self.op not in ids:
raise NoOldIdError()
else:
ids = _id_operator_v2
if self.op not in ids:
@ -1592,7 +1600,7 @@ class ASTOperatorBuildIn(ASTOperator):
return ids[self.op]
def _stringify(self, transform: StringifyTransform) -> str:
if self.op in ('new', 'new[]', 'delete', 'delete[]'):
if self.op in ('new', 'new[]', 'delete', 'delete[]') or self.op[0] in "abcnox":
return 'operator ' + self.op
else:
return 'operator' + self.op
@ -5016,7 +5024,11 @@ class DefinitionParser(BaseParser):
self.skip_ws()
for op in _expression_unary_ops:
# TODO: hmm, should we be able to backtrack here?
if self.skip_string(op):
if op[0] in 'cn':
res = self.skip_word(op)
else:
res = self.skip_string(op)
if res:
expr = self._parse_cast_expression()
return ASTUnaryOpExpr(op, expr)
if self.skip_word_and_ws('sizeof'):
@ -5144,8 +5156,12 @@ class DefinitionParser(BaseParser):
pos = self.pos
oneMore = False
for op in _expression_bin_ops[opId]:
if not self.skip_string(op):
continue
if op[0] in 'abcnox':
if not self.skip_word(op):
continue
else:
if not self.skip_string(op):
continue
if op == '&' and self.current_char == '&':
# don't split the && 'token'
self.pos -= 1
@ -5187,8 +5203,12 @@ class DefinitionParser(BaseParser):
oneMore = False
self.skip_ws()
for op in _expression_assignment_ops:
if not self.skip_string(op):
continue
if op[0] in 'anox':
if not self.skip_word(op):
continue
else:
if not self.skip_string(op):
continue
expr = self._parse_logical_or_expression(False)
exprs.append(expr)
ops.append(op)

View File

@ -197,7 +197,9 @@ def test_expressions():
exprCheck('+5', 'psL5E')
exprCheck('-5', 'ngL5E')
exprCheck('!5', 'ntL5E')
exprCheck('not 5', 'ntL5E')
exprCheck('~5', 'coL5E')
exprCheck('compl 5', 'coL5E')
exprCheck('sizeof...(a)', 'sZ1a')
exprCheck('sizeof(T)', 'st1T')
exprCheck('sizeof -42', 'szngL42E')
@ -221,13 +223,19 @@ def test_expressions():
exprCheck('(int)2', 'cviL2E')
# binary op
exprCheck('5 || 42', 'ooL5EL42E')
exprCheck('5 or 42', 'ooL5EL42E')
exprCheck('5 && 42', 'aaL5EL42E')
exprCheck('5 and 42', 'aaL5EL42E')
exprCheck('5 | 42', 'orL5EL42E')
exprCheck('5 bitor 42', 'orL5EL42E')
exprCheck('5 ^ 42', 'eoL5EL42E')
exprCheck('5 xor 42', 'eoL5EL42E')
exprCheck('5 & 42', 'anL5EL42E')
exprCheck('5 bitand 42', 'anL5EL42E')
# ['==', '!=']
exprCheck('5 == 42', 'eqL5EL42E')
exprCheck('5 != 42', 'neL5EL42E')
exprCheck('5 not_eq 42', 'neL5EL42E')
# ['<=', '>=', '<', '>']
exprCheck('5 <= 42', 'leL5EL42E')
exprCheck('A <= 42', 'le1AL42E')
@ -261,8 +269,11 @@ def test_expressions():
exprCheck('a >>= 5', 'rS1aL5E')
exprCheck('a <<= 5', 'lS1aL5E')
exprCheck('a &= 5', 'aN1aL5E')
exprCheck('a and_eq 5', 'aN1aL5E')
exprCheck('a ^= 5', 'eO1aL5E')
exprCheck('a xor_eq 5', 'eO1aL5E')
exprCheck('a |= 5', 'oR1aL5E')
exprCheck('a or_eq 5', 'oR1aL5E')
# Additional tests
# a < expression that starts with something that could be a template
@ -531,30 +542,62 @@ def test_function_definitions():
def test_operators():
check('function', 'void operator new [ ] ()',
{1: "new-array-operator", 2: "nav"}, output='void operator new[]()')
check('function', 'void operator delete ()',
{1: "delete-operator", 2: "dlv"}, output='void operator delete()')
check('function', 'operator bool() const',
{1: "castto-b-operatorC", 2: "NKcvbEv"}, output='operator bool() const')
check('function', 'void operator new()', {1: "new-operator", 2: "nwv"})
check('function', 'void operator new[]()', {1: "new-array-operator", 2: "nav"})
check('function', 'void operator delete()', {1: "delete-operator", 2: "dlv"})
check('function', 'void operator delete[]()', {1: "delete-array-operator", 2: "dav"})
check('function', 'operator bool() const', {1: "castto-b-operatorC", 2: "NKcvbEv"})
check('function', 'void operator""_udl()', {2: 'li4_udlv'})
check('function', 'void operator * ()',
{1: "mul-operator", 2: "mlv"}, output='void operator*()')
check('function', 'void operator - ()',
{1: "sub-operator", 2: "miv"}, output='void operator-()')
check('function', 'void operator + ()',
{1: "add-operator", 2: "plv"}, output='void operator+()')
check('function', 'void operator = ()',
{1: "assign-operator", 2: "aSv"}, output='void operator=()')
check('function', 'void operator / ()',
{1: "div-operator", 2: "dvv"}, output='void operator/()')
check('function', 'void operator % ()',
{1: "mod-operator", 2: "rmv"}, output='void operator%()')
check('function', 'void operator ! ()',
{1: "not-operator", 2: "ntv"}, output='void operator!()')
check('function', 'void operator "" _udl()',
{2: 'li4_udlv'}, output='void operator""_udl()')
check('function', 'void operator~()', {1: "inv-operator", 2: "cov"})
check('function', 'void operator compl()', {2: "cov"})
check('function', 'void operator+()', {1: "add-operator", 2: "plv"})
check('function', 'void operator-()', {1: "sub-operator", 2: "miv"})
check('function', 'void operator*()', {1: "mul-operator", 2: "mlv"})
check('function', 'void operator/()', {1: "div-operator", 2: "dvv"})
check('function', 'void operator%()', {1: "mod-operator", 2: "rmv"})
check('function', 'void operator&()', {1: "and-operator", 2: "anv"})
check('function', 'void operator bitand()', {2: "anv"})
check('function', 'void operator|()', {1: "or-operator", 2: "orv"})
check('function', 'void operator bitor()', {2: "orv"})
check('function', 'void operator^()', {1: "xor-operator", 2: "eov"})
check('function', 'void operator xor()', {2: "eov"})
check('function', 'void operator=()', {1: "assign-operator", 2: "aSv"})
check('function', 'void operator+=()', {1: "add-assign-operator", 2: "pLv"})
check('function', 'void operator-=()', {1: "sub-assign-operator", 2: "mIv"})
check('function', 'void operator*=()', {1: "mul-assign-operator", 2: "mLv"})
check('function', 'void operator/=()', {1: "div-assign-operator", 2: "dVv"})
check('function', 'void operator%=()', {1: "mod-assign-operator", 2: "rMv"})
check('function', 'void operator&=()', {1: "and-assign-operator", 2: "aNv"})
check('function', 'void operator and_eq()', {2: "aNv"})
check('function', 'void operator|=()', {1: "or-assign-operator", 2: "oRv"})
check('function', 'void operator or_eq()', {2: "oRv"})
check('function', 'void operator^=()', {1: "xor-assign-operator", 2: "eOv"})
check('function', 'void operator xor_eq()', {2: "eOv"})
check('function', 'void operator<<()', {1: "lshift-operator", 2: "lsv"})
check('function', 'void operator>>()', {1: "rshift-operator", 2: "rsv"})
check('function', 'void operator<<=()', {1: "lshift-assign-operator", 2: "lSv"})
check('function', 'void operator>>=()', {1: "rshift-assign-operator", 2: "rSv"})
check('function', 'void operator==()', {1: "eq-operator", 2: "eqv"})
check('function', 'void operator!=()', {1: "neq-operator", 2: "nev"})
check('function', 'void operator not_eq()', {2: "nev"})
check('function', 'void operator<()', {1: "lt-operator", 2: "ltv"})
check('function', 'void operator>()', {1: "gt-operator", 2: "gtv"})
check('function', 'void operator<=()', {1: "lte-operator", 2: "lev"})
check('function', 'void operator>=()', {1: "gte-operator", 2: "gev"})
check('function', 'void operator!()', {1: "not-operator", 2: "ntv"})
check('function', 'void operator not()', {2: "ntv"})
check('function', 'void operator&&()', {1: "sand-operator", 2: "aav"})
check('function', 'void operator and()', {2: "aav"})
check('function', 'void operator||()', {1: "sor-operator", 2: "oov"})
check('function', 'void operator or()', {2: "oov"})
check('function', 'void operator++()', {1: "inc-operator", 2: "ppv"})
check('function', 'void operator--()', {1: "dec-operator", 2: "mmv"})
check('function', 'void operator,()', {1: "comma-operator", 2: "cmv"})
check('function', 'void operator->*()', {1: "pointer-by-pointer-operator", 2: "pmv"})
check('function', 'void operator->()', {1: "pointer-operator", 2: "ptv"})
check('function', 'void operator()()', {1: "call-operator", 2: "clv"})
check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"})
def test_class_definitions():