Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 36 additions & 33 deletions cxxheaderparser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,13 +803,6 @@ def _parse_concept(
),
)

# fmt: off
_expr_operators = {
"<", ">", "|", "%", "^", "!", "*", "-", "+", "&", "=",
"&&", "||", "<<"
}
# fmt: on

def _parse_requires(
self,
tok: LexToken,
Expand All @@ -818,38 +811,48 @@ def _parse_requires(

rawtoks: typing.List[LexToken] = []

# The easier case -- requires requires
if tok.type == "requires":
rawtoks.append(tok)
for tt in ("(", "{"):
tok = self._next_token_must_be(tt)
# The expression in a requires clause must be one of the following:
# 1) A primary expression
# 2) A sequence of (1) joined with &&
# 3) A sequence of (2) joined with ||
#
# In terms of validity, this is equivalent to a sequence of primary expressions
# joined with && and/or ||.
#
# In general, a primary expression is one of the following:
# 1) this
# 2) a literal
# 3) an identifier expression
# 4) a lambda expression
# 5) a fold expression
# 6) a requires expression
# 7) any parenthesized expression
#
# For simplicity, we only consider the following primary expressions:
# 1) parenthesized expressions (which includes fold expressions)
# 2) requires expressions
# 3) identifer expressions (possibly qualified, possibly templated)
while True:
if tok.type == "(":
rawtoks.extend(self._consume_balanced_tokens(tok))
# .. and that's it?

# this is either a parenthesized expression or a primary clause
elif tok.type == "(":
rawtoks.extend(self._consume_balanced_tokens(tok))
else:
while True:
if tok.type == "(":
tok = self.lex.token()
elif tok.type == "requires":
rawtoks.append(tok)
for tt in ("(", "{"):
tok = self._next_token_must_be(tt)
rawtoks.extend(self._consume_balanced_tokens(tok))
else:
tok = self._parse_requires_segment(tok, rawtoks)
tok = self.lex.token()
else:
tok = self._parse_requires_segment(tok, rawtoks)

# If this is not an operator of some kind, we don't know how
# to proceed so let the next parser figure it out
if tok.value not in self._expr_operators:
break
if tok.value not in ("&&", "||"):
break

rawtoks.append(tok)
rawtoks.append(tok)

# check once more for compound operator?
tok = self.lex.token()
if tok.value in self._expr_operators:
rawtoks.append(tok)
tok = self.lex.token()
tok = self.lex.token()

self.lex.return_token(tok)
self.lex.return_token(tok)

return self._create_value(rawtoks)

Expand Down
53 changes: 53 additions & 0 deletions tests/test_concepts.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,59 @@ def test_requires_compound() -> None:
)


def test_requires_compound_parenthesized() -> None:
content = """
template <int X>
requires (X == 0) || (X == 1)
int Fibonacci() { return X; }
"""
data = parse_string(content, cleandoc=True)

assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="Fibonacci")]),
parameters=[],
has_body=True,
template=TemplateDecl(
params=[
TemplateNonTypeParam(
type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
),
name="X",
)
],
raw_requires_pre=Value(
tokens=[
Token(value="("),
Token(value="X"),
Token(value="="),
Token(value="="),
Token(value="0"),
Token(value=")"),
Token(value="||"),
Token(value="("),
Token(value="X"),
Token(value="="),
Token(value="="),
Token(value="1"),
Token(value=")"),
]
),
),
)
]
)
)


def test_requires_ad_hoc() -> None:
content = """
template<typename T>
Expand Down
Loading