summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Wu <lekensteyn@gmail.com>2011-11-27 15:53:01 +0000
committerPeter Wu <lekensteyn@gmail.com>2011-11-27 15:53:01 +0000
commit45e7cba2d7985284cd066033607cb39e772dfaa5 (patch)
tree9cdad549608cb0fd18457351ef47d14a3bb963bb
parent14d0835e355eb6044f8fb090ec7a70b267bda48b (diff)
downloadpp2cc-45e7cba2d7985284cd066033607cb39e772dfaa5.tar.gz
Add command line options parsing
-rwxr-xr-xpp2cc.py137
1 files 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)