import operator
import os
import re
import sys
if not hasattr(operator, 'methodcaller'):
def methodcaller(method, *args, **kwargs):
return lambda x: getattr(x, method)(*args, **kwargs)
operator.methodcaller = methodcaller
del methodcaller
DEFINE_END = ' ""\n\n'
def usage_and_exit(msg):
if msg:
sys.stderr.write('%s\n\n' % msg)
sys.stderr.write(
'USAGE: %s SQLITE_FILE [OUTPUT_FILE]\n'
' stdout will be used if OUTPUT_FILE is not provided.\n'
% os.path.basename(sys.argv[0]))
sys.stderr.flush()
sys.exit(1)
class Processor(object):
re_comments = re.compile(r'/\*.*?\*/', re.MULTILINE|re.DOTALL)
re_format = re.compile('-- *format: *([0-9]+)')
re_statement = re.compile('-- *STMT_([A-Z_0-9]+)( +\(([^\)]*)\))?')
re_include = re.compile('-- *include: *([-a-z]+)')
re_define = re.compile('-- *define: *([A-Z_0-9]+)')
def _sub_format(self, match):
vsn = match.group(1)
self.close_define()
self.output.write('#define %s_%s \\\n' % (self.var_name, match.group(1)))
self.var_printed = True
def _sub_statement(self, match):
name = match.group(1)
self.close_define()
self.output.write('#define STMT_%s %d\n' % (match.group(1),
self.stmt_count))
if match.group(3) == None:
info = 'NULL'
else:
info = '"' + match.group(3) + '"'
self.output.write('#define STMT_%d_INFO {"STMT_%s", %s}\n' %
(self.stmt_count, match.group(1), info))
self.output.write('#define STMT_%d \\\n' % (self.stmt_count,))
self.var_printed = True
self.stmt_count += 1
def _sub_include(self, match):
filepath = os.path.join(self.dirpath, match.group(1) + '.sql')
self.close_define()
self.process_file(open(filepath).read())
def _sub_define(self, match):
define = match.group(1)
self.output.write(' APR_STRINGIFY(%s) \\\n' % define)
def __init__(self, dirpath, output, var_name, token_map):
self.dirpath = dirpath
self.output = output
self.var_name = var_name
self.token_map = token_map
self.stmt_count = 0
self.var_printed = False
self._directives = {
self.re_format : self._sub_format,
self.re_statement : self._sub_statement,
self.re_include : self._sub_include,
self.re_define : self._sub_define,
}
def process_file(self, input):
input = self.re_comments.sub('', input)
for line in input.split('\n'):
line = line.replace('"', '\\"')
line = re.sub(
r'IS_STRICT_DESCENDANT_OF[(]([?]?[A-Za-z0-9_.]+), ([?]?[A-Za-z0-9_.]+)[)]',
r"(((\1) > (CASE (\2) WHEN '' THEN '' ELSE (\2) || '/' END))" +
r" AND ((\1) < CASE (\2) WHEN '' THEN X'FFFF' ELSE (\2) || '0' END))",
line)
line = re.sub(
r'RELPATH_SKIP_JOIN[(]([?]?[A-Za-z0-9_.]+), ' +
r'([?]?[A-Za-z0-9_.]+), ' +
r'([?]?[A-Za-z0-9_.]+)[)]',
r"(CASE WHEN (\1) = '' THEN RELPATH_JOIN(\2, \3) " +
r"WHEN (\2) = '' THEN RELPATH_SKIP_ANCESTOR(\1, \3) " +
r"WHEN SUBSTR((\3), 1, LENGTH(\1)) = (\1) " +
r"THEN " +
r"CASE WHEN LENGTH(\1) = LENGTH(\3) THEN (\2) " +
r"WHEN SUBSTR((\3), LENGTH(\1)+1, 1) = '/' " +
r"THEN (\2) || SUBSTR((\3), LENGTH(\1)+1) " +
r"END " +
r"END)",
line)
line = re.sub(
r'RELPATH_JOIN[(]([?]?[A-Za-z0-9_.]+), ([?]?[A-Za-z0-9_.]+)[)]',
r"(CASE WHEN (\1) = '' THEN (\2) " +
r"WHEN (\2) = '' THEN (\1) " +
r"ELSE (\1) || '/' || (\2) " +
r"END)",
line)
line = re.sub(
r'RELPATH_SKIP_ANCESTOR[(]([?]?[A-Za-z0-9_.]+), ' +
r'([?]?[A-Za-z0-9_.]+)[)]',
r"(CASE WHEN (\1) = '' THEN (\2) " +
r" WHEN SUBSTR((\2), 1, LENGTH(\1)) = (\1) " +
r" THEN " +
r"CASE WHEN LENGTH(\1) = LENGTH(\2) THEN '' " +
r"WHEN SUBSTR((\2), LENGTH(\1)+1, 1) = '/' " +
r"THEN SUBSTR((\2), LENGTH(\1)+2) " +
r"END" +
r" END)",
line)
for symbol, string in self.token_map.items():
line = re.sub(r'\b%s\b' % re.escape(symbol), "'%s'" % string, line)
if line.strip():
handled = False
for regex, handler in self._directives.items():
match = regex.match(line)
if match:
handler(match)
handled = True
break
if handled:
continue
if not self.var_printed:
self.output.write('#define %s \\\n' % self.var_name)
self.var_printed = True
self.output.write(' "%s " \\\n' % line.rstrip())
self.close_define()
def close_define(self):
if self.var_printed:
self.output.write(DEFINE_END)
self.var_printed = False
class NonRewritableDict(dict):
"""A dictionary that does not allow self[k]=v when k in self
(unless v is equal to the stored value).
(An entry would have to be explicitly deleted before a new value
may be entered.)
"""
def __setitem__(self, key, val):
if self.__contains__(key) and self.__getitem__(key) != val:
raise Exception("Can't re-insert key %r with value %r "
"(already present with value %r)"
% (key, val, self.__getitem__(key)))
super(NonRewritableDict, self).__setitem__(key, val)
def hotspots(fd):
hotspot = False
for line in fd:
hotspot ^= int(('svn_token_map_t', '\x7d;')[hotspot] in line)
if hotspot:
yield line
def extract_token_map(filename):
try:
fd = open(filename)
except IOError:
return {}
pattern = re.compile(r'"(.*?)".*?(MAP_\w*)')
return \
NonRewritableDict(
map(operator.itemgetter(1,0),
map(operator.methodcaller('groups'),
filter(None,
map(pattern.search,
hotspots(fd))))))
def main(input_filepath, output):
filename = os.path.basename(input_filepath)
input = open(input_filepath, 'r').read()
token_map_filename = os.path.dirname(input_filepath) + '/token-map.h'
token_map = extract_token_map(token_map_filename)
var_name = re.sub('[-.]', '_', filename).upper()
output.write(
'/* This file is automatically generated from %s and %s.\n'
' * Do not edit this file -- edit the source and rerun gen-make.py */\n'
'\n'
% (filename, token_map_filename))
proc = Processor(os.path.dirname(input_filepath), output, var_name, token_map)
proc.process_file(input)
if proc.stmt_count > 0:
output.write(
'#define %s_DECLARE_STATEMENTS(varname) \\\n' % (var_name,)
+ ' static const char * const varname[] = { \\\n'
+ ', \\\n'.join(' STMT_%d' % (i,) for i in range(proc.stmt_count))
+ ', \\\n NULL \\\n }\n')
output.write('\n')
output.write(
'#define %s_DECLARE_STATEMENT_INFO(varname) \\\n' % (var_name,)
+ ' static const char * const varname[][2] = { \\\n'
+ ', \\\n'.join(' STMT_%d_INFO' % (i) for i in range(proc.stmt_count))
+ ', \\\n {NULL, NULL} \\\n }\n')
if __name__ == '__main__':
if len(sys.argv) < 2 or len(sys.argv) > 3:
usage_and_exit('Incorrect number of arguments')
input_filepath = sys.argv[1]
if len(sys.argv) > 2:
output_file = open(sys.argv[2], 'w')
else:
output_file = sys.stdout
main(input_filepath, output_file)