162 lines
5.2 KiB
Python
162 lines
5.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# preprocessor.py - simple preprocessor for plugin template files
|
|
# This file is part of xedit
|
|
#
|
|
# Copyright (C) 2006 - Steve Frécinaux
|
|
#
|
|
# xedit is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# xedit is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with xedit; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
# Boston, MA 02110-1301 USA
|
|
|
|
import sys
|
|
import re
|
|
|
|
class DeepnessException(Exception):
|
|
def __init__(self):
|
|
Exception.__init__(self)
|
|
|
|
statements = [re.compile("^##\s*%s\s*$" % pattern) for pattern in
|
|
['(?P<stmt>ifdef|ifndef)\s+(?P<key>[^\s]+)',
|
|
'(?P<stmt>elif|if)\s+(?P<expr>.+)',
|
|
'(?P<stmt>else|endif)',
|
|
'(?P<stmt>define)\s+(?P<key>[^\s]+)(\s+(?P<val>.+))?',
|
|
'(?P<stmt>undef)\s+(?P<key>[^\s]+)']]
|
|
variable = re.compile("##\((?P<name>[a-zA-Z_][a-zA-Z0-9_]*)(?P<mods>(\.[a-z]+)+)?\)")
|
|
|
|
|
|
def _eval(expr, macros):
|
|
return eval(expr,
|
|
{'defined': lambda x: macros.has_key(x)},
|
|
macros)
|
|
|
|
def _subvar(match, macros):
|
|
name = match.group('name')
|
|
if name in macros:
|
|
val = str(macros[name])
|
|
if val is None:
|
|
return ''
|
|
else:
|
|
return ''
|
|
|
|
mods = match.group('mods')
|
|
if mods is not None:
|
|
for mod in mods[1:].split('.'):
|
|
if mod == 'lower':
|
|
val = val.lower()
|
|
elif mod == 'upper':
|
|
val = val.upper()
|
|
elif mod == 'camel':
|
|
val = ''.join(i.capitalize()
|
|
for i in val.split('_'))
|
|
return val
|
|
|
|
def process(infile = sys.stdin, outfile = sys.stdout, macros = {}):
|
|
if not isinstance(infile, file):
|
|
infile = open(infile, mode = 'r')
|
|
close_infile = True
|
|
else:
|
|
close_infile = False
|
|
|
|
if not isinstance(outfile, file):
|
|
outfile = open(outfile, mode = 'w')
|
|
close_outfile = True
|
|
else:
|
|
close_outfile = False
|
|
|
|
deepness = 0
|
|
writing_disabled = None
|
|
|
|
for line in infile:
|
|
# Skip comments
|
|
if line[0:3].lower() == '##c':
|
|
continue
|
|
|
|
# Check whether current line is a preprocessor directive
|
|
for statement in statements:
|
|
match = statement.match(line)
|
|
if match: break
|
|
|
|
if match is not None:
|
|
stmt = match.group('stmt')
|
|
|
|
if stmt == "define":
|
|
if writing_disabled is None:
|
|
key = match.group('key')
|
|
val = match.group('val')
|
|
macros[key] = val
|
|
|
|
elif stmt == "undef":
|
|
if writing_disabled is None:
|
|
key = match.group('key')
|
|
if key in macros:
|
|
del macros[key]
|
|
|
|
elif stmt == "ifdef":
|
|
deepness += 1
|
|
if writing_disabled is None and \
|
|
match.group('key') not in macros:
|
|
writing_disabled = deepness
|
|
|
|
elif stmt == "ifndef":
|
|
deepness += 1
|
|
if writing_disabled is None and \
|
|
match.group('key') in macros:
|
|
writing_disabled = deepness
|
|
|
|
elif stmt == "if":
|
|
deepness += 1
|
|
if writing_disabled is None and \
|
|
not _eval(match.group('expr'), macros):
|
|
writing_disabled = deepness
|
|
|
|
elif stmt == "elif":
|
|
if deepness == 0:
|
|
raise DeepnessException()
|
|
if writing_disabled is None and \
|
|
not _eval(match.group('expr'), macros):
|
|
writing_disabled = deepness
|
|
elif writing_disabled == deepness:
|
|
writing_disabled = None
|
|
|
|
elif stmt == "else":
|
|
if deepness == 0:
|
|
raise DeepnessException()
|
|
if writing_disabled is None:
|
|
writing_disabled = deepness
|
|
elif writing_disabled == deepness:
|
|
writing_disabled = None
|
|
|
|
elif stmt == "endif":
|
|
if deepness == 0:
|
|
raise DeepnessException()
|
|
if writing_disabled is not None and \
|
|
writing_disabled == deepness:
|
|
writing_disabled = None
|
|
deepness -= 1
|
|
|
|
# Do variable substitution in the remaining lines
|
|
elif writing_disabled is None:
|
|
outfile.write(re.sub(variable,
|
|
lambda m: _subvar(m, macros),
|
|
line))
|
|
|
|
if deepness != 0:
|
|
raise DeepnessException()
|
|
|
|
if close_infile: infile.close()
|
|
if close_outfile: outfile.close()
|
|
|
|
# ex:ts=4:et:
|