aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Tools/c-analyzer/c_parser/preprocessor/gcc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/c_parser/preprocessor/gcc.py')
-rw-r--r--Tools/c-analyzer/c_parser/preprocessor/gcc.py123
1 files changed, 123 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_parser/preprocessor/gcc.py b/Tools/c-analyzer/c_parser/preprocessor/gcc.py
new file mode 100644
index 00000000000..bb404a487b7
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/preprocessor/gcc.py
@@ -0,0 +1,123 @@
+import os.path
+import re
+
+from . import common as _common
+
+
+TOOL = 'gcc'
+
+# https://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
+LINE_MARKER_RE = re.compile(r'^# (\d+) "([^"]+)"(?: [1234])*$')
+PREPROC_DIRECTIVE_RE = re.compile(r'^\s*#\s*(\w+)\b.*')
+COMPILER_DIRECTIVE_RE = re.compile(r'''
+ ^
+ (.*?) # <before>
+ (__\w+__) # <directive>
+ \s*
+ [(] [(]
+ (
+ [^()]*
+ (?:
+ [(]
+ [^()]*
+ [)]
+ [^()]*
+ )*
+ ) # <args>
+ ( [)] [)] )? # <closed>
+''', re.VERBOSE)
+
+POST_ARGS = (
+ '-pthread',
+ '-std=c99',
+ #'-g',
+ #'-Og',
+ #'-Wno-unused-result',
+ #'-Wsign-compare',
+ #'-Wall',
+ #'-Wextra',
+ '-E',
+)
+
+
+def preprocess(filename, incldirs=None, macros=None, samefiles=None):
+ text = _common.preprocess(
+ TOOL,
+ filename,
+ incldirs=incldirs,
+ macros=macros,
+ #preargs=PRE_ARGS,
+ postargs=POST_ARGS,
+ executable=['gcc'],
+ compiler='unix',
+ )
+ return _iter_lines(text, filename, samefiles)
+
+
+def _iter_lines(text, filename, samefiles, *, raw=False):
+ lines = iter(text.splitlines())
+
+ # Build the lines and filter out directives.
+ partial = 0 # depth
+ origfile = None
+ for line in lines:
+ m = LINE_MARKER_RE.match(line)
+ if m:
+ lno, origfile = m.groups()
+ lno = int(lno)
+ elif _filter_orig_file(origfile, filename, samefiles):
+ if (m := PREPROC_DIRECTIVE_RE.match(line)):
+ name, = m.groups()
+ if name != 'pragma':
+ raise Exception(line)
+ else:
+ if not raw:
+ line, partial = _strip_directives(line, partial=partial)
+ yield _common.SourceLine(
+ _common.FileInfo(filename, lno),
+ 'source',
+ line or '',
+ None,
+ )
+ lno += 1
+
+
+def _strip_directives(line, partial=0):
+ # We assume there are no string literals with parens in directive bodies.
+ while partial > 0:
+ if not (m := re.match(r'[^{}]*([()])', line)):
+ return None, partial
+ delim, = m.groups()
+ partial += 1 if delim == '(' else -1 # opened/closed
+ line = line[m.end():]
+
+ line = re.sub(r'__extension__', '', line)
+
+ while (m := COMPILER_DIRECTIVE_RE.match(line)):
+ before, _, _, closed = m.groups()
+ if closed:
+ line = f'{before} {line[m.end():]}'
+ else:
+ after, partial = _strip_directives(line[m.end():], 2)
+ line = f'{before} {after or ""}'
+ if partial:
+ break
+
+ return line, partial
+
+
+def _filter_orig_file(origfile, current, samefiles):
+ if origfile == current:
+ return True
+ if origfile == '<stdin>':
+ return True
+ if os.path.isabs(origfile):
+ return False
+
+ for filename in samefiles or ():
+ if filename.endswith(os.path.sep):
+ filename += os.path.basename(current)
+ if origfile == filename:
+ return True
+
+ return False