Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makedep: Better handling of parentheses #626

Merged
merged 2 commits into from
May 8, 2024
Merged
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
94 changes: 43 additions & 51 deletions ac/makedep
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ re_procedure = re.compile(


# Preprocessor expression tokenization
# NOTE: Labels and attributes could be assigned here, but for now we just use
# the token string as the label.
cpp_scanner = re.Scanner([
(r'defined', lambda scanner, token: token),
(r'[_A-Za-z][_0-9a-zA-Z]*', lambda scanner, token: token),
Expand All @@ -56,13 +58,15 @@ cpp_scanner = re.Scanner([
(r'&', lambda scanner, token: token),
(r'\|\|', lambda scanner, token: token),
(r'\|', lambda scanner, token: token),
(r'^\#if', None),
(r'^ *\# *if', None),
(r'\s+', None),
])


cpp_operate = {
'(': lambda x: x,
'!': lambda x: not x,
'defined': lambda x, y: x in y,
'*': lambda x, y: x * y,
'/': lambda x, y: x // y,
'+': lambda x, y: x + y,
Expand All @@ -85,6 +89,7 @@ cpp_operate = {
cpp_op_rank = {
'(': 13,
'!': 12,
'defined': 12,
'*': 11,
'/': 11,
'+': 10,
Expand Down Expand Up @@ -527,7 +532,7 @@ def cpp_expr_eval(expr, macros=None):
if macros is None:
macros = {}

results, remainder = cpp_scanner.scan(expr)
results, remainder = cpp_scanner.scan(expr.strip())

# Abort if any characters are not tokenized
if remainder:
Expand All @@ -545,72 +550,59 @@ def cpp_expr_eval(expr, macros=None):

tokens = iter(results)
for tok in tokens:
# Evaluate "defined()" statements
if tok == 'defined':
tok = next(tokens)

parens = tok == '('
if parens:
tok = next(tokens)
if tok in cpp_op_rank.keys():
while cpp_op_rank[tok] <= cpp_op_rank[prior_op]:

# NOTE: Any key in `macros` is considered to be set, even if the
# value is None.
value = tok in macros
# Unary operators are "look ahead" so we always skip them.
# (However, `op` below could be a unary operator.)
if tok in ('!', 'defined', '('):
break

# Negation
while prior_op == '!':
second = stack.pop()
op = stack.pop()
assert op == '!'
value = cpp_operate[op](value)
prior_op = stack[-1] if stack else None

stack.append(value)

if parens:
tok = next(tokens)
assert tok == ')'
if op == '(':
value = second

elif tok.isdigit():
value = int(tok)
stack.append(value)
elif op == '!':
if isinstance(second, str):
if second.isidentifier():
second = macros.get(second, '0')
if second.isdigit():
second = int(second)

elif tok.isidentifier():
# "Identifiers that are not macros, which are all considered to be
# the number zero." (CPP manual, 4.2.2)
value = macros.get(tok, '0')
if value.isdigit():
value = int(value)
stack.append(value)
value = cpp_operate[op](second)

elif tok in cpp_op_rank.keys():
while cpp_op_rank[tok] <= cpp_op_rank[prior_op]:
elif op == 'defined':
value = cpp_operate[op](second, macros)

# Skip unary prefix operators (only '!' at the moment)
if tok == '!':
break
else:
first = stack.pop()

second = stack.pop()
op = stack.pop()
first = stack.pop()
if isinstance(first, str):
if first.isidentifier():
first = macros.get(first, '0')
if first.isdigit():
first = int(first)

value = cpp_operate[op](first, second)
prior_op = stack[-1] if stack else None
if isinstance(second, str):
if second.isidentifier():
second = macros.get(second, '0')
if second.isdigit():
second = int(second)

if prior_op == '(':
prior_op = None
if tok == ')':
stack.pop()
value = cpp_operate[op](first, second)

prior_op = stack[-1] if stack else None
stack.append(value)

if tok == ')':
prior_op = stack[-2] if stack and len(stack) > 1 else None
else:
# The ) "operator" has already been applied, so it can be dropped.
if tok != ')':
stack.append(tok)
prior_op = tok

if prior_op in ('(',):
prior_op = None
elif tok.isdigit() or tok.isidentifier():
stack.append(tok)

else:
print("Unsupported token:", tok)
Expand Down
Loading