summaryrefslogtreecommitdiffstats
path: root/SCons/cpp.py
diff options
context:
space:
mode:
authorJoseph Brill <48932340+jcbrill@users.noreply.github.com>2024-11-10 16:21:53 (GMT)
committerJoseph Brill <48932340+jcbrill@users.noreply.github.com>2024-11-10 16:21:53 (GMT)
commit382d1e293a1ddf268d62e993e4f7ae3c75501633 (patch)
treeff729f859b21e8648e410378d098a330c941ed6d /SCons/cpp.py
parentde084c80eb08f44ff55ea15eba275fda7d1382c7 (diff)
downloadSCons-382d1e293a1ddf268d62e993e4f7ae3c75501633.zip
SCons-382d1e293a1ddf268d62e993e4f7ae3c75501633.tar.gz
SCons-382d1e293a1ddf268d62e993e4f7ae3c75501633.tar.bz2
Fix CPP conditional scanner (cpp.py) and test suite (cppTests.py).
Changes: - Preserve non-integer literals that contain valid integer specifications. - Add binary integer specifications - Add octal integer specification - Zero (0) is considered an octal number - Add negative lookbehind/lookahead for number specifications (text is not word/token based) - Add method to evaluate constant expression for define constant expressions - Replace int conversion with constant evaluation expression - int conversion failed for hex numbers due to default base 10 [int(s)] vs unknown base [int(s, 0)] - Add additional tests
Diffstat (limited to 'SCons/cpp.py')
-rw-r--r--SCons/cpp.py52
1 files changed, 45 insertions, 7 deletions
diff --git a/SCons/cpp.py b/SCons/cpp.py
index 1093ae2..63e49ae 100644
--- a/SCons/cpp.py
+++ b/SCons/cpp.py
@@ -146,13 +146,27 @@ expr = '|'.join(map(re.escape, l))
# ...and compile the expression.
CPP_to_Python_Ops_Expression = re.compile(expr)
+# integer specifications
+int_suffix_opt = r'(?:[uU](?:l{1,2}|L{1,2}|[zZ]|wb|WB)?|(?:l{1,2}|L{1,2}|[zZ]|wb|WB)[uU]?)?'
+
+hex_integer = fr'(0[xX][0-9A-Fa-f]+){int_suffix_opt}'
+bin_integer = fr'(0[bB][01]+){int_suffix_opt}'
+oct_integer = fr'(0[0-7]*){int_suffix_opt}'
+dec_integer = fr'([1-9][0-9]*){int_suffix_opt}'
+
+int_boundary = r'[a-zA-Z0-9_]'
+int_neg_lookbehind = fr'(?<!{int_boundary})'
+int_neg_lookahead = fr'(?!{int_boundary})'
+
# A separate list of expressions to be evaluated and substituted
# sequentially, not all at once.
CPP_to_Python_Eval_List = [
- [r'defined\s+(\w+)', '"\\1" in __dict__'],
- [r'defined\s*\((\w+)\)', '"\\1" in __dict__'],
- [r'(0x[0-9A-Fa-f]+)(?:L|UL)?', '\\1'],
- [r'(\d+)(?:L|UL)?', '\\1'],
+ [r'defined\s+(\w+)', '"\\1" in __dict__'],
+ [r'defined\s*\((\w+)\)', '"\\1" in __dict__'],
+ [fr'{int_neg_lookbehind}{hex_integer}{int_neg_lookahead}', '\\1'],
+ [fr'{int_neg_lookbehind}{bin_integer}{int_neg_lookahead}', '\\1'],
+ [fr'{int_neg_lookbehind}{oct_integer}{int_neg_lookahead}', '0o\\1'],
+ [fr'{int_neg_lookbehind}{dec_integer}{int_neg_lookahead}', '\\1'],
]
# Replace the string representations of the regular expressions in the
@@ -250,6 +264,9 @@ class PreProcessor:
self.cpp_namespace = dict.copy()
self.cpp_namespace['__dict__'] = self.cpp_namespace
+ # Namespace for constant expression evaluation (literals only)
+ self.constant_expression_namespace = {}
+
# Return all includes without resolving
if all:
self.do_include = self.all_include
@@ -369,6 +386,25 @@ class PreProcessor:
def scons_current_file(self, t) -> None:
self.current_file = t[1]
+ def eval_constant_expression(self, s):
+ """
+ Evaluates a C preprocessor expression.
+
+ This is done by converting it to a Python equivalent and
+ eval()ing it in the C preprocessor namespace we use to
+ track #define values.
+
+ Returns None if the eval() result is not an integer.
+ """
+ s = CPP_to_Python(s)
+ try:
+ rval = eval(s, self.constant_expression_namespace)
+ except (NameError, TypeError, SyntaxError) as e:
+ rval = None
+ if not isinstance(rval, int):
+ rval = None
+ return rval
+
def eval_expression(self, t):
"""
Evaluates a C preprocessor expression.
@@ -507,9 +543,11 @@ class PreProcessor:
Default handling of a #define line.
"""
_, name, args, expansion = t
- try:
- expansion = int(expansion)
- except (TypeError, ValueError):
+
+ rval = self.eval_constant_expression(expansion)
+ if rval is not None:
+ expansion = rval
+ else:
# handle "defined" chain "! (defined (A) || defined (B)" ...
if "defined " in expansion:
self.cpp_namespace[name] = self.eval_expression(t[2:])