#!/usr/bin/python import sys, re, os, os.path, time, getopt lineDirectiveRe = \ re.compile("#(?:[ \t]*line)?[ \t]+([0-9]+)[ \t]+\"([^\"]*)\"") class LineReader(object): def __init__(self, fp): self.__fp = fp self.__line = 0 self.__path = fp.name def __str__(self): return "%s:%d" % (self.__path, self.__line) def directive(self, offset = 0): d = "# %d \"%s\"" % (self.__line + offset, self.__path) return d def __iter__(self): return self def next(self): self.__line += 1 text = self.__fp.next() ld = lineDirectiveRe.match(text) if ld: self.__line = int(ld.group(1)) - 1 self.__path = ld.group(2) return text def close(self): self.__fp.close() def abort(lr, msg): print >> sys.stderr, "%s: %s" % (str(lr), msg) sys.exit(1) def parseCommand(line, lr, cmds): line = line.strip() end = len(line) space = line.find(" ") if space != -1 and space < end: end = space semi = line.find(";") if semi != -1 and semi < end: end = semi cmd = line[0:end] nArgs, wantList = cmds.get(cmd, (None, False)) if nArgs == None: return None line = line[end:].strip() while not line.endswith(";"): line += lr.next().strip() line = line[:-1].strip() if wantList: pstart = line.find("(") if pstart == -1 or not line.endswith(")"): if pstart != -1 and line.find(")", pstart) != -1: abort(lr, "Junk after argument list (forgotten semi-colon?)") abort(lr, "Expected parenthesized argument list") fixed = line[:pstart].strip() rest = line[pstart+1:-1].strip() restArgs = [a.strip() for a in rest.split(",")] else: fixed = line restArgs = [] fargs = fixed.split() if len(fargs) != nArgs: abort(lr, "%s expected %d fixed arguments, got %d" % (cmd, nArgs, len(fargs))) return cmd, fargs, restArgs def expand(src, dst, tmpl, prefix, args): tmplName, tmplPrefix, tmplArgs, tmplData = tmpl if len(args) != len(tmplArgs): abort(src, "Template %s expected %d args, got %d" % (tmplName, len(tmplArgs), len(args))) tmplData = "".join(tmplData) # XXX This substitution is stupid. Prefixes should only be # replaced at the beginning of C tokens and arguments should only # be replaced as whole tokens. tmplData = tmplData.replace(tmplPrefix + "_", prefix + "_") for arg, tmplArg in zip(args, tmplArgs): tmplData = tmplData.replace(tmplArg, arg) dst.write(tmplData) def generate(srcFile, dstFile): if srcFile == "-": src = sys.stdin else: src = file(srcFile, "r") src = LineReader(src) if dstFile == "-": dst = sys.stdout else: dst = file(dstFile, "w") print >> dst, "// Autogenerated by %s on %s" % \ (" ".join(sys.argv), time.asctime()) templates = {} inTemplate = False lineDirty = True cmds = {"%instance": (2, True), "%template": (2, True), "%endtemplate": (0, False)} for line in src: if line[0] == "%": cmd = parseCommand(line, src, cmds) if cmd: if cmd[0] == "%instance": if inTemplate: abort(src, "Instances in templates not implemented") _, (tmplName, prefix), args = cmd tmpl = templates.get(tmplName, None) if tmpl == None: abort(src, "Unknown template %s" % tmplName) expand(src, dst, tmpl, prefix, args) lineDirty = True elif cmd[0] == "%template": if inTemplate: abort(src, "Nested template definition") _, (tmplName, prefix), args = cmd if tmplName in templates: abort(src, "Template %s already defined" % tmplName) tmplData = [] templates[tmplName] = (tmplName, prefix, args, tmplData) inTemplate = True elif cmd[0] == "%endtemplate": if not inTemplate: abort(src, "Dangling endtemplate") inTemplate = False print >> dst, src.directive(-1) else: assert False continue if inTemplate: assert not lineDirty if len(tmplData) == 0: tmplData.append(src.directive() + "\n") tmplData.append(line) else: if lineDirty: print >> dst, src.directive() lineDirty = False dst.write(line) src.close() dst.close() def usage(argv, err=None): if err: print >> sys.stderr, err print >> sys.stderr, "Usage: %s [-o out] in" % argv[0] sys.exit(1) def main(argv): try: opts, args = getopt.getopt(sys.argv[1:], "lo:") except getopt.GetoptError, err: usage(argv, str(err)) if len(args) != 1: usage(argv, "Expected one file name") srcFile = args[0] dstFile = "-" for o, a in opts: if o == "-o": dstFile = a else: assert False, o generate(srcFile, dstFile) if __name__ == "__main__": main(sys.argv)