C++, simplify assginemnt-expression parsing

This commit is contained in:
Jakob Lykke Andersen 2022-03-27 11:41:15 +02:00
parent ccfe08746a
commit 7dd1e5bbcf
2 changed files with 62 additions and 69 deletions

View File

@ -1589,42 +1589,39 @@ class ASTBracedInitList(ASTBase):
class ASTAssignmentExpr(ASTExpression): class ASTAssignmentExpr(ASTExpression):
def __init__(self, exprs: List[Union[ASTExpression, ASTBracedInitList]], ops: List[str]): def __init__(self, leftExpr: ASTExpression, op: str,
assert len(exprs) > 0 rightExpr: Union[ASTExpression, ASTBracedInitList]):
assert len(exprs) == len(ops) + 1 self.leftExpr = leftExpr
self.exprs = exprs self.op = op
self.ops = ops self.rightExpr = rightExpr
def _stringify(self, transform: StringifyTransform) -> str: def _stringify(self, transform: StringifyTransform) -> str:
res = [] res = []
res.append(transform(self.exprs[0])) res.append(transform(self.leftExpr))
for i in range(1, len(self.exprs)): res.append(' ')
res.append(' ') res.append(self.op)
res.append(self.ops[i - 1]) res.append(' ')
res.append(' ') res.append(transform(self.rightExpr))
res.append(transform(self.exprs[i]))
return ''.join(res) return ''.join(res)
def get_id(self, version: int) -> str: def get_id(self, version: int) -> str:
# we end up generating the ID from left to right, instead of right to left
res = [] res = []
for i in range(len(self.ops)): res.append(_id_operator_v2[self.op])
res.append(_id_operator_v2[self.ops[i]]) res.append(self.leftExpr.get_id(version))
res.append(self.exprs[i].get_id(version)) res.append(self.rightExpr.get_id(version))
res.append(self.exprs[-1].get_id(version))
return ''.join(res) return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
self.exprs[0].describe_signature(signode, mode, env, symbol) self.leftExpr.describe_signature(signode, mode, env, symbol)
for i in range(1, len(self.exprs)): signode += addnodes.desc_sig_space()
signode += addnodes.desc_sig_space() if ord(self.op[0]) >= ord('a') and ord(self.op[0]) <= ord('z'):
op = self.ops[i - 1] signode += addnodes.desc_sig_keyword(self.op, self.op)
if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'): else:
signode += addnodes.desc_sig_keyword(op, op) signode += addnodes.desc_sig_operator(self.op, self.op)
else: signode += addnodes.desc_sig_space()
signode += addnodes.desc_sig_operator(op, op) self.rightExpr.describe_signature(signode, mode, env, symbol)
signode += addnodes.desc_sig_space()
self.exprs[i].describe_signature(signode, mode, env, symbol)
class ASTCommaExpr(ASTExpression): class ASTCommaExpr(ASTExpression):
@ -5654,64 +5651,58 @@ class DefinitionParser(BaseParser):
def _parse_conditional_expression_tail(self, orExprHead: ASTExpression, def _parse_conditional_expression_tail(self, orExprHead: ASTExpression,
inTemplate: bool) -> Optional[ASTConditionalExpr]: inTemplate: bool) -> Optional[ASTConditionalExpr]:
# Consumes the orExprHead on success.
# -> "?" expression ":" assignment-expression # -> "?" expression ":" assignment-expression
self.skip_ws()
if not self.skip_string("?"): if not self.skip_string("?"):
return None return None
then_expr = self._parse_expression() thenExpr = self._parse_expression()
self.skip_ws() self.skip_ws()
if not self.skip_string(":"): if not self.skip_string(":"):
self.fail('Expected ":" after "?"') self.fail('Expected ":" after then-expression in conditional expression.')
else_expr = self._parse_assignment_expression(inTemplate) elseExpr = self._parse_assignment_expression(inTemplate)
return ASTConditionalExpr(orExprHead, then_expr, else_expr) return ASTConditionalExpr(orExprHead, thenExpr, elseExpr)
def _parse_assignment_expression(self, inTemplate: bool) -> ASTExpression: def _parse_assignment_expression(self, inTemplate: bool) -> ASTExpression:
# -> conditional-expression # -> conditional-expression
# | logical-or-expression assignment-operator initializer-clause # | logical-or-expression assignment-operator initializer-clause
# | throw-expression # | yield-expression -> "co_yield" assignment-expression
# TODO: parse throw-expression: "throw" assignment-expression [opt] # | "co_yield" braced-init-list
# if not a throw expression, then: # | throw-expression -> "throw" assignment-expression[opt]
# -> conditional-expression -> # TODO: yield-expression
# TODO: throw-expression
# Now we have (after expanding conditional-expression:
# logical-or-expression # logical-or-expression
# | logical-or-expression "?" expression ":" assignment-expression # | logical-or-expression "?" expression ":" assignment-expression
# | logical-or-expression assignment-operator initializer-clause # | logical-or-expression assignment-operator initializer-clause
exprs: List[Union[ASTExpression, ASTBracedInitList]] = [] leftExpr = self._parse_logical_or_expression(inTemplate=inTemplate)
ops = [] # the ternary operator
orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) condExpr = self._parse_conditional_expression_tail(leftExpr, inTemplate)
exprs.append(orExpr) if condExpr is not None:
while True: return condExpr
oneMore = False # and actual assignment
self.skip_ws() for op in _expression_assignment_ops:
prev_expr = exprs[-1] if op[0] in 'anox':
if isinstance(prev_expr, ASTExpression): if not self.skip_word(op):
cond_expr = self._parse_conditional_expression_tail(prev_expr, inTemplate) continue
if cond_expr is not None: else:
exprs[-1] = cond_expr if not self.skip_string(op):
continue continue
for op in _expression_assignment_ops: rightExpr = self._parse_initializer_clause()
if op[0] in 'anox': return ASTAssignmentExpr(leftExpr, op, rightExpr)
if not self.skip_word(op): # just a logical-or-expression
continue return leftExpr
else:
if not self.skip_string(op):
continue
expr = self._parse_initializer_clause()
exprs.append(expr)
ops.append(op)
oneMore = True
if not oneMore:
break
if len(ops) == 0:
return cast(ASTExpression, exprs[-1])
else:
return ASTAssignmentExpr(exprs, ops)
def _parse_constant_expression(self, inTemplate: bool) -> ASTExpression: def _parse_constant_expression(self, inTemplate: bool) -> ASTExpression:
# -> conditional-expression # -> conditional-expression ->
# logical-or-expression
# | logical-or-expression "?" expression ":" assignment-expression
orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) orExpr = self._parse_logical_or_expression(inTemplate=inTemplate)
self.skip_ws() condExpr = self._parse_conditional_expression_tail(orExpr, inTemplate)
cond_expr = self._parse_conditional_expression_tail(orExpr, inTemplate) if condExpr is not None:
if cond_expr is not None: return condExpr
return cond_expr
return orExpr return orExpr
def _parse_expression(self) -> ASTExpression: def _parse_expression(self) -> ASTExpression:

View File

@ -327,7 +327,6 @@ def test_domain_cpp_ast_expressions():
exprCheck('5 ->* 42', 'pmL5EL42E') exprCheck('5 ->* 42', 'pmL5EL42E')
# conditional # conditional
exprCheck('5 ? 7 : 3', 'quL5EL7EL3E') exprCheck('5 ? 7 : 3', 'quL5EL7EL3E')
exprCheck('5 = 6 ? 7 = 8 : 3', 'aSL5EquL6EaSL7EL8EL3E')
# assignment # assignment
exprCheck('a = 5', 'aS1aL5E') exprCheck('a = 5', 'aS1aL5E')
exprCheck('a *= 5', 'mL1aL5E') exprCheck('a *= 5', 'mL1aL5E')
@ -344,6 +343,9 @@ def test_domain_cpp_ast_expressions():
exprCheck('a |= 5', 'oR1aL5E') exprCheck('a |= 5', 'oR1aL5E')
exprCheck('a or_eq 5', 'oR1aL5E') exprCheck('a or_eq 5', 'oR1aL5E')
exprCheck('a = {{1, 2, 3}}', 'aS1ailL1EL2EL3EE') exprCheck('a = {{1, 2, 3}}', 'aS1ailL1EL2EL3EE')
# complex assignment and conditional
exprCheck('5 = 6 = 7', 'aSL5EaSL6EL7E')
exprCheck('5 = 6 ? 7 = 8 : 3', 'aSL5EquL6EaSL7EL8EL3E')
# comma operator # comma operator
exprCheck('a, 5', 'cm1aL5E') exprCheck('a, 5', 'cm1aL5E')