import cPickle as pickle import os import sys from grammar_compiler_visitor import GrammarCompilerVisitor from hutnparser import Parser, Tree from meta_grammar import Grammar from cached_exception import CachedException global parsers parsers = {} def read(filename): with open(filename, 'r') as f: return f.read() def do_parse(inputfile, grammarfile): new_grammar = True picklefile = grammarfile + ".pickle" if grammarfile not in parsers: try: if os.path.getmtime(picklefile) > os.path.getmtime(grammarfile): # Pickle is more recent than grammarfile, so use it grammar = pickle.load(open(picklefile, 'rb')) new_grammar = False else: # Will be catched immediately raise Exception("Pickle is invalid!") except: result = parser = Parser(Grammar(), hide_implicit = True).parse(read(grammarfile)) if result['status'] != Parser.Constants.Success: print 'not a valid grammar!' print result tree = result['tree'] visitor = GrammarCompilerVisitor() structure = visitor.visit(tree) grammar = Grammar() grammar.rules = structure['rules'] grammar.tokens = structure['tokens'] pickle.dump(grammar, open(picklefile, 'wb'), pickle.HIGHEST_PROTOCOL) parsers[grammarfile] = grammar else: new_grammar = False grammar = parsers[grammarfile] picklefile = inputfile + ".pickle" try: if new_grammar: # Stop anyway, as the grammar is new raise Exception() if os.path.getmtime(picklefile) > os.path.getmtime(inputfile): # Pickle is more recent than inputfile, so use it result = pickle.load(open(picklefile, 'rb')) else: # Inputfile has changed raise Exception() except: result = Parser(grammar, line_position = True).parse(read(inputfile)) if result['status'] != Parser.Constants.Success: msg = "%s:%s:%s: %s" % (inputfile, result["line"], result["column"], result["text"]) raise Exception(msg) pickle.dump(result, open(picklefile, 'wb'), pickle.HIGHEST_PROTOCOL) return result def find_file(filename, include_paths): import os.path include_paths = ["."] + \ [os.path.abspath(os.path.dirname(working_file))] + \ [os.path.abspath("%s/../includes/" % (os.path.dirname(os.path.abspath(__file__))))] + \ include_paths + \ [] attempts = [] for include in include_paths: testfile = include + os.sep + filename if os.path.isfile(os.path.abspath(testfile)): return os.path.abspath(testfile) else: attempts.append(os.path.abspath(testfile)) else: raise Exception("Could not resolve file %s. Tried: %s" % (filename, attempts)) def do_compile(inputfile, grammarfile, visitors=[], include_paths = []): import os.path global working_file working_file = os.path.abspath(inputfile) result = do_parse(inputfile, grammarfile) error = result["status"] != Parser.Constants.Success if error: msg = "%s:%s:%s: %s" % (inputfile, result["line"], result["column"], result["text"]) raise Exception(msg) else: for child in result["tree"].tail: child.inputfile = inputfile included = set() while True: for i, v in enumerate(result["tree"].tail): if v.head == "include": # Expand this node for j in v.tail: if j.head == "STRVALUE": f = str(j.tail[0])[1:-1] if f in included: subtree = [] else: name = str(j.tail[0])[1:-1] subtree = do_parse(find_file(name, include_paths), grammarfile)["tree"].tail if subtree is None: raise Exception("Parsing error for included file %s" % find_file(name, include_paths)) for t in subtree: t.inputfile = name included.add(f) # Found the string value, so break from the inner for ("searching for element") break # Merge all nodes in before = result["tree"].tail[:i] after = result["tree"].tail[i+1:] result["tree"].tail = before + subtree + after # Found an include node, but to prevent corruption of the tree, we need to start over again, so break from the outer for loop break else: # The outer for finally finished, so there were no includes remaining, thus terminate the infinite while loop break result["tree"].fix_tracability(inputfile) for visitor in visitors: visitor.visit(result["tree"]) if visitors: return visitors[-1].dump() def main(input_file, grammar_file, mode, args=[], symbols=None): from prettyprint_visitor import PrettyPrintVisitor from prettyprint_visitor import PrintVisitor from semantics_visitor import SemanticsVisitor from bootstrap_visitor import BootstrapVisitor from primitives_visitor import PrimitivesVisitor from primitives_object_visitor import PrimitivesObjectVisitor from constructors_visitor import ConstructorsVisitor from constructors_object_visitor import ConstructorsObjectVisitor from model_visitor import ModelVisitor from model_object_visitor import ModelObjectVisitor modes = { "N" : [], "P" : [PrintVisitor], "PP" : [PrettyPrintVisitor], "S" : [SemanticsVisitor], "PS" : [SemanticsVisitor, PrimitivesVisitor], "PO" : [SemanticsVisitor, PrimitivesObjectVisitor], "BS" : [SemanticsVisitor, BootstrapVisitor], "CS" : [SemanticsVisitor, ConstructorsVisitor], "CO" : [SemanticsVisitor, ConstructorsObjectVisitor], "M" : [ModelVisitor], "MO" : [ModelObjectVisitor], } try: visitors = [v(args) for v in modes[mode]] result = do_compile(input_file, grammar_file, visitors) if symbols is not None and mode == "BS": symbols.update(visitors[-1].object_symbols) except CachedException: return True return result if __name__ == "__main__": if len(sys.argv) <= 2: print("Invocation: ") print(" %s input_file grammar_file mode [mode_params]*" % sys.argv[0]) sys.exit(1) else: value = main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4:]) if value is not None: print(value)