From 45e7cba2d7985284cd066033607cb39e772dfaa5 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 27 Nov 2011 15:53:01 +0000 Subject: Add command line options parsing --- pp2cc.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 104 insertions(+), 33 deletions(-) diff --git a/pp2cc.py b/pp2cc.py index 224a88f..bc782c3 100755 --- a/pp2cc.py +++ b/pp2cc.py @@ -10,7 +10,7 @@ permission. The generated output (assembly and messages) are not subject to this license. """ -import sys, re +import sys, re, os from pycparser import c_parser, c_ast __author__ = "Peter Wu" @@ -209,13 +209,9 @@ class Parse(object): @classmethod def is_identifier(cls, text): return cls.re_identifier.match(text) - def __init__(self, filename, source=''): - if source is "": - file = open(filename, 'r') - source = file.read() - file.close() - self.node = parser.parse(source, filename=filename); + def __init__(self): self.logger = Logger() + self.node = None # word size in bits self.WORDSIZE = 18 @@ -252,6 +248,12 @@ class Parse(object): self.asm = Asm() # watches which registers are used and which are not self.registers = Registers() + def loadFile(self, filename): + """Loads a file and parses the contents of it""" + file = open(filename, 'r') + source = file.read() + file.close() + self.node = parser.parse(source, filename=filename); def show(self): self.node.show(showcoord=True) def uniqLbl(self, labelname): @@ -283,26 +285,25 @@ class Parse(object): self.codeSegment += self.parseStatement(thing, root_node) def getSource(self): """Retrieves the ASM source. You need to compile it first""" - self.addSource("@DATA") + output = [] + output.append("@DATA") for varName in self.globalVars: padding = " " * (16 - len(varName) - 1) - self.addSource(self.nameToVar(varName) + padding + " DW 1") - self.addSource() - self.addSource("@CODE") + output.append(self.nameToVar(varName) + padding + " DW 1") + output.append("") + output.append("@CODE") # initialization of global variables - for line in self.globalInit: - self.addSource(line) + output += self.globalInit if not "main" in self.functions: self.logger.warning("No main function found with label 'fn_main'") - self.addSource(self.asm.branch_op("BRA", "fn_main")) - self.addSource() - for line in self.codeSegment: - self.addSource(line) - self.addSource() - self.addSource("@END") - def addSource(self, line=''): - print line + output.append(self.asm.branch_op("BRA", "fn_main")) + output.append("") + output += self.codeSegment + output.append("") + output.append("@END") + output.append("") + return os.linesep.join(output) def nameToVar(self, name): """Returns the variable name which will be used in assembly @@ -862,15 +863,85 @@ class Parse(object): def parseExpression(self, node, parent): pass -try: - filename = sys.argv[1] - parse = Parse(filename) - if len(sys.argv) == 3: - parse.show() - #node.ext[0].ext[0].show() - else: - parse.compile() - parse.getSource() -except c_parser.ParseError: - e = sys.exc_info()[1] - print "Parse error:" + str(e) +if __name__ == "__main__": + settings = { + "input_files": [], + "dump_parsed_files": False, + "output_filename": "" + } + set_arg = None + + # arguments processing + class ArgumentError(Exception): + pass + def usage(): + print """Usage: python pp2cc.py [options] filename.. +Multiple input files can be specified, options can be specified before and +after filenames. +Options: + -o filename The name of the output file. It defaults to the first + input file from which the extension is removed, and .asm is + added. + --tree Instead of compiling the file into assembly, show the parse + tree.""" + try: + for arg in sys.argv[1:]: + if not set_arg is None: + assert set_arg in settings, "The command line option cannot be found in the settings" + setting = settings[set_arg] + if isinstance(setting, str): + settings[set_arg] = arg + elif isinstance(arg, list): + settings.append(arg) + elif isinstance(arg, bool): + raise NotImplementedError("Booleans cannot be set in options") + set_arg = None + elif len(arg): + # if the argument is an option + if arg[0] == "-": + if arg == "-o": + set_arg = "output_filename" + elif arg == "--tree": + settings["dump_parsed_files"] = True + elif arg == "-h" or arg == "--help" or arg == "--usage": + usage() + sys.exit(0) + else: + raise ArgumentError("Unknown commandline argument '{}'".format(arg)) + else: # must be a file + settings["input_files"].append(arg) + if not set_arg is None: + raise ArgumentError("Expected a value for option '{}'".format(sys.argv[-1])) + if not settings["input_files"]: + raise ArgumentError("No input files") + except ArgumentError: + e = sys.exc_info()[1] + print str(e) + print "For usage, run: python pp2cc.py --help" + sys.exit(255) + # end of arguments processing + + if not settings["output_filename"]: + settings["output_filename"] = os.path.splitext(settings["input_files"][0])[0] + ".asm" + + try: + parse = Parse() + + for filename in settings["input_files"]: + parse.loadFile(filename) + if settings["dump_parsed_files"]: + parse.show() + else: + parse.compile() + + if not settings["dump_parsed_files"]: + source = parse.getSource() + outf = open(settings["output_filename"], "w") + outf.write(source) + outf.close() + print "Compilation succesful" + print "The output is written to", settings["output_filename"] + except c_parser.ParseError: + e = sys.exc_info()[1] + print "Parse error:", str(e) + sys.exit(1) -- cgit v1.2.1