Changeset: 75cc03f0d4d6 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=75cc03f0d4d6
Added Files:
testing/melcheck.py
Modified Files:
testing/exportutils.py
testing/listexports.py.in
testing/malcheck.py
Branch: default
Log Message:
Initial version of a melcheck.py program to check mel_func array contents.
diffs (truncated from 395 to 300 lines):
diff --git a/testing/exportutils.py b/testing/exportutils.py
--- a/testing/exportutils.py
+++ b/testing/exportutils.py
@@ -5,119 +5,157 @@
# Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
import re
-
-# a function-like #define that we expand to also find exports hidden
-# in preprocessor macros
-defre = re.compile(r'^[ \t]*#[ \t]*define[ \t]+' # #define
- r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)' # name being
defined
- r'\((?P<args>[a-zA-Z0-9_, \t]*)\)[ \t]*' # arguments
- r'(?P<def>.*)$', # macro replacement
- re.MULTILINE)
-# line starting with a "#"
-cldef = re.compile(r'^[ \t]*#', re.MULTILINE)
+import os
-# white space
-spcre = re.compile(r'\s+')
+# macro definition
+macrore = re.compile(r'\s*#\s*define\s+' # #define
+ r'(?P<name>\w+)' # name being defined
+ r'(?:\((?P<args>[^)]*)\))?\s*' # optional arguments
+ r'(?P<repl>.*)') # replacement
+# include file
+inclre = re.compile(r'\s*#\s*include\s+"(?P<file>[^"]*)"')
+# comments (/* ... */ where ... is as short as possible)
+cmtre = re.compile(r'/\*[^*]*(\*(?=[^/])[^*]*)*\*/|//.*')
+# horizontal white space
+spcre = re.compile(r'[ \t]+')
+# identifier
+identre = re.compile(r'\b(?P<ident>[a-zA-Z_]\w*)\b')
+# undef
+undefre = re.compile(r'\s*#\s*undef\s+(?P<name>\w+)')
+ifre = re.compile(r'\s*#\s*if') # #if or #ifdef
+elifre = re.compile(r'\s*#\s*elif')
+elsere = re.compile(r'\s*#\s*else')
+endifre = re.compile(r'\s*#\s*endif')
-# some regexps helping to normalize a declaration
-strre = re.compile(r'([^ *])\*')
-comre = re.compile(r',\s*')
-
-# comments (/* ... */ where ... is as short as possible)
-cmtre = re.compile(r'/\*.*?\*/|//[^\n]*', re.DOTALL)
-
-deepnesting = False
+nested = r'' #
r'(?:\([^()]*(?:\([^()]*(?:\([^()]*(?:\([^()]*(?:\([^()]*(?:\([^()]*\)[^()]*)*\)[^()]*)*\)[^()]*)*\)[^()]*)*\)[^()]*)*\)[^()]*)*'
-# do something a bit like the C preprocessor
-#
-# we expand function-like macros and remove all ## sequences from the
-# replacement (even when there are no adjacent parameters that were
-# replaced), but this is good enough for our purpose of finding
-# exports that are hidden away in several levels of macro definitions
-#
-# we assume that there are no continuation lines in the input
-def preprocess(data, printdef=False):
- # remove C comments
- res = cmtre.search(data)
+def process(line, funmac, macros, infunmac=False):
+ nline = ''
+ pos = 0
+ res = identre.search(line)
while res is not None:
- data = data[:res.start(0)] + ' ' + data[res.end(0):]
- res = cmtre.search(data, res.start(0))
- # remove \ <newline> combo's
+ name = res.group('ident')
+ if name in macros:
+ macros2 = macros.copy()
+ del macros2[name]
+ repl = process(macros[name], funmac, macros2)
+ if line[res.start(0)-1:res.start(0)] == '#' and \
+ line[res.start(0)-2:res.start(0)] != '##':
+ nline += line[pos:res.start(0)-1]
+ nline += '"' + repl.replace('\\', r'\\').replace('"', r'\"') +
'"'
+ else:
+ nline += line[pos:res.start(0)] + repl
+ pos = res.end(0)
+ elif name in funmac:
+ args, repl = funmac[name]
+ pat = r'\s*\('
+ sep = r''
+ for arg in args:
+ pat += sep + r'([^,()]*(?:\([^()]*'+nested+r'\)[^,()]*)*)'
+ sep = r','
+ pat += r'\s*\)'
+ r = re.compile(pat)
+ res2 = r.match(line, pos=res.end(0))
+ if res2 is not None:
+ macros2 = {}
+ i = 1
+ for arg in args:
+ macros2[arg] = res2.group(i).strip()
+ i += 1
+ repl = process(repl, {}, macros2, True)
+ funmac2 = funmac.copy()
+ del funmac2[name]
+ repl = process(repl, funmac2, macros)
+ repl = repl.replace('##', '')
+ nline += line[pos:res.start(0)] + repl
+ pos = res2.end(0)
+ else:
+ nline += line[pos:res.end(0)]
+ pos = res.end(0)
+ else:
+ nline += line[pos:res.end(0)]
+ pos = res.end(0)
+ res = identre.search(line, pos=pos)
+ nline += line[pos:]
+ return nline
+
+def readfile(f, funmac=None, macros=None, files=None, printdef=False):
+ data = open(f).read()
+ dirname, f = os.path.split(f)
+ data = cmtre.sub(' ', data)
data = data.replace('\\\n', '')
-
- defines = {}
+ data = spcre.sub(' ', data)
+ data = data.splitlines()
+ if funmac is None:
+ funmac = {}
+ if macros is None:
+ macros = {}
+ if files is None:
+ files = set()
+ files.add(f)
ndata = []
- for line in data.split('\n'):
- res = defre.match(line)
- if res is not None:
- name, args, body = res.groups()
- args = tuple([x.strip() for x in args.split(',')])
- if len(args) == 1 and args[0] == '':
- args = () # empty argument list
- if name not in ('__attribute__', '__format__', '__alloc_size__')
and \
- (name not in defines or not defines[name][1].strip()):
- defines[name] = (args, body)
+ skip = []
+ for line in data:
+ if endifre.match(line) is not None:
if printdef:
ndata.append(line)
- else:
- changed = True
- while changed:
- line, changed = replace(line, defines, [])
- if printdef or not cldef.match(line):
+ if skip:
+ del skip[-1]
+ continue
+ if ifre.match(line) is not None:
+ if printdef:
ndata.append(line)
- return '\n'.join(ndata)
-
-def replace(line, defines, tried):
- changed = False
- # match argument to macro with optionally several levels
- # of parentheses
- if deepnesting: # optionally deeply nested parentheses
- nested =
r'(?:\([^()]*(?:\([^()]*(?:\([^()]*(?:\([^()]*(?:\([^()]*(?:\([^()]*\)[^()]*)*\)[^()]*)*\)[^()]*)*\)[^()]*)*\)[^()]*)*\)[^()]*)*'
- else:
- nested = ''
- for name, (args, body) in defines.items():
- if name in tried:
+ skip.append(False)
+ continue
+ if elifre.match(line) or elsere.match(line):
+ if printdef:
+ ndata.append(line)
+ if skip:
+ skip[-1] = True
+ continue
+ if skip and skip[-1]:
+ if printdef:
+ ndata.append(line)
continue
- args, body = defines[name]
- pat = r'\b%s\b\(' % name
- sep = r''
- for arg in args:
- pat = pat + sep + r'([^,()]*(?:\([^()]*' + nested + r'\)[^,()]*)*)'
- sep = ','
- pat += r'\)'
- repl = {}
- r = re.compile(pat)
- res = r.search(line)
- while res is not None:
- bd = body
- changed = True
- if len(args) > 0:
- pars = [x.strip() for x in res.groups()]
- pat = r'\b(?:'
- sep = ''
- for arg, par in zip(args, pars):
- repl[arg] = par
- pat += sep + arg
- sep = '|'
- pat += r')\b'
- r2 = re.compile(pat)
- res2 = r2.search(bd)
- while res2 is not None:
- arg = res2.group(0)
- if bd[res2.start(0)-1:res2.start(0)] == '#' and \
- bd[res2.start(0)-2:res2.start(0)] != '##':
- # replace #ARG by stringified replacement
- pos = res2.start(0) + len(repl[arg]) + 1
- bd = bd[:res2.start(0)-1] + '"' + repl[arg] + '"' +
bd[res2.end(0):]
- else:
- pos = res2.start(0) + len(repl[arg])
- bd = bd[:res2.start(0)] + repl[arg] + bd[res2.end(0):]
- res2 = r2.search(bd, pos)
- bd = bd.replace('##', '')
- bd, changed = replace(bd, defines, tried + [name])
- line = line[:res.start(0)] + bd + line[res.end(0):]
- res = r.search(line, res.start(0) + len(bd))
- return line, changed
+ res = macrore.match(line)
+ if res is not None:
+ if printdef:
+ ndata.append(line)
+ name = res.group('name')
+ args = res.group('args')
+ repl = res.group('repl')
+ if args:
+ args = tuple([x.strip() for x in args.split(',')])
+ if len(args) == 1 and args[0] == '':
+ args = () # empty argument list
+ funmac[name] = (args, repl)
+ continue
+ macros[name] = repl
+ continue
+ res = inclre.match(line)
+ if res is not None:
+ fn = res.group('file')
+ if '/' not in fn and os.path.exists(os.path.join(dirname, fn)) and
fn not in files:
+ incdata = readfile(os.path.join(dirname, fn), funmac, macros,
files, printdef)
+ ndata.extend(incdata)
+ continue
+ ndata.append(line)
+ continue
+ res = undefre.match(line)
+ if res is not None:
+ name = res.group('name')
+ if name in macros:
+ del macros[name]
+ if name in funmac:
+ del funmac[name]
+ continue
+ line = process(line, funmac, macros)
+ ndata.append(line)
+ files.remove(f)
+ return ndata
+
+def preprocess(f, printdef=False):
+ return '\n'.join(readfile(f, printdef=printdef))
def normalize(decl):
decl = spcre.sub(' ', decl) \
@@ -137,4 +175,4 @@ def normalize(decl):
if __name__ == '__main__':
import sys
for f in sys.argv[1:]:
- print(preprocess(open(f).read(), printdef=True))
+ print(preprocess(f, printdef=True))
diff --git a/testing/listexports.py.in b/testing/listexports.py.in
--- a/testing/listexports.py.in
+++ b/testing/listexports.py.in
@@ -37,9 +37,8 @@ nmere = re.compile(r'\b(?P<name>[a-zA-Z_
def extract(f):
decls = []
- data = open(f).read()
- data = exportutils.preprocess(data)
+ data = exportutils.preprocess(f)
res = expre.search(data)
while res is not None:
diff --git a/testing/malcheck.py b/testing/malcheck.py
--- a/testing/malcheck.py
+++ b/testing/malcheck.py
@@ -82,8 +82,8 @@ odecls = {}
pdecls = {}
def process(f):
- data = open(f).read()
if f.endswith('.mal'):
+ data = open(f).read()
data = re.sub(r'[ \t]*#.*', '', data) # remove comments
for res in comreg.finditer(data):
malf, args, rets, func = res.groups()
@@ -121,7 +121,7 @@ def process(f):
malf, args, rets, func = res.groups()
malpats.append((malf, func, f))
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list