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
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to