#! /usr/bin/env python __rcsId__ = """$Id: ifdef.py,v 1.4 2000/10/31 22:40:07 bellman Exp $""" import sys import string import exceptions import getopt import fileinput # KNOWN BUGS: # - Too few comments! # - No documentation! # - Multiple #else allowed between #if* and #endif # - Bad or no error handling in main() # FUTURE ENHANCEMENTS: # - Allow ! before names after #else and #endif class BadNesting(exceptions.Exception): pass class WrongName(BadNesting): pass class UnknowDirective(exceptions.Exception): pass class NotAtToplevel(exceptions.Exception): pass class IfDefState: def __init__(self, predefs): # Dictionary of all symbols and their values self.__defines = predefs.copy() # Stack of all ifdef's encountered, the innermost last. Each # element is a two-tuple of the ifdef symbol name and the resulting # state after applying the innermost level of ifdef. self.__ifdefs = [(None, 1)] # "Cache" of self.__ifdefs[-1][1] self.__isactive = 1 def istrue(self, symbol): """Check if symbol is defined and true or not.""" return not not self.__defines.get(symbol, 0) def isactive(self): return self.__isactive def at_toplevel(self): return len(self.__ifdefs) == 1 def define(self, name, value): if self.__isactive: self.__defines[name] = value def undef(self, name): if self.__isactive: if self.__defines.has_key(name): del self.__defines[name] def ifdef(self, name): new_isactive = self.__isactive and self.istrue(name) self.__ifdefs.append((name, new_isactive)) self.__isactive = new_isactive def ifndef(self, name): new_isactive = self.__isactive and not self.istrue(name) self.__ifdefs.append((name, new_isactive)) self.__isactive = new_isactive def else_(self, name=None): if len(self.__ifdefs) < 2: raise BadNesting("else at top level") last = self.__ifdefs[-1] if name and name != last[0]: raise WrongName("Wrong name for else: got %s, expected %s" % (name, last[0])) new_isactive = self.__ifdefs[-2][1] and not last[1] self.__ifdefs[-1] = (last[0], new_isactive) self.__isactive = new_isactive def endif(self, name=None): if len(self.__ifdefs) < 2: raise BadNesting("Too many endif") last = self.__ifdefs[-1] if name and name != last[0]: raise WrongName("Wrong name for endif: got %s, expected %s" % (name, last[0])) del self.__ifdefs[-1] self.__isactive = self.__ifdefs[-1][1] class Processor: def __init__(self, predefs, character='#'): self.__state = IfDefState(predefs) self.__character = character def parse_line(self, line): l = string.strip(line) if l[:1] != self.__character: return (None, None, line) directive,name,rest = (string.split(l[1:], None, 2) + ["", ""])[:3] return (directive, name, rest) def do_line(self, line): st = self.__state directive,name,rest = self.parse_line(line) if not directive: if self.__state.isactive(): return line elif directive == "define": try: value = int(rest) except: value = rest or 1 st.define(name, value) elif directive == "undef": st.undef(name) elif directive == "ifdef": st.ifdef(name) elif directive == "ifndef": st.ifndef(name) elif directive == "else": st.else_(name) elif directive == "endif": st.endif(name) else: raise UnknownDirective("Unknown directive: %s" % (directive,)) return "" def eof(self): if not self.__state.at_toplevel(): raise NotAtToplevel("Not enough #endif's") def usage(argv): if len(argv) > 0: progname = argv[0] else: progname = "ifdef" sys.stderr.write("Usage: %s [-cchar] [-Dsymbol[=value]] ...\n" % (progname,)) sys.exit(64) def main(argv): defs = {} try: optlist,args = getopt.getopt(argv[1:], "c:D:") except getopt.error: usage(argv) character = "#" for opt,optval in optlist: if opt == "-D": name,val = (string.split(optval, "=", 1) + ["1"])[:2] try: val = int(val) except: pass defs[name] = val elif opt == "-c": if len(optval) != 1: usage(argv) character = optval if len(args) == 0: # Not the most beautiful way to do this... args = [ "-" ] for filename in args: p = Processor(defs, character) for l in fileinput.FileInput(filename): sys.stdout.write(p.do_line(l)) p.eof() if __name__ == "__main__": main(sys.argv)