compiler.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import cPickle as pickle
  2. import os
  3. import sys
  4. from grammar_compiler_visitor import GrammarCompilerVisitor
  5. from hutnparser import Parser, Tree
  6. from meta_grammar import Grammar
  7. from cached_exception import CachedException
  8. import hashlib
  9. global parsers
  10. parsers = {}
  11. sys.setrecursionlimit(2000)
  12. def read(filename):
  13. with open(filename, 'r') as f:
  14. return f.read()
  15. def md5digest(filename):
  16. hasher = hashlib.md5()
  17. with open(filename, 'rb') as afile:
  18. hasher.update(afile.read())
  19. return hasher.hexdigest()
  20. def fetch_cached(filename):
  21. try:
  22. md5 = md5digest(filename)
  23. cache_folder = os.path.abspath("%s/../caches/" % (os.path.dirname(os.path.abspath(__file__))))
  24. picklefile = cache_folder + "/%s.pickle" % md5
  25. if os.path.getmtime(picklefile) > os.path.getmtime(filename):
  26. with open(picklefile, "rb") as f:
  27. return pickle.load(f)
  28. else:
  29. return None
  30. except:
  31. return None
  32. def make_cached(filename, data):
  33. md5 = md5digest(filename)
  34. cache_folder = os.path.abspath("%s/../caches/" % (os.path.dirname(os.path.abspath(__file__))))
  35. picklefile = cache_folder + "/%s.pickle" % md5
  36. with open(picklefile, "wb") as f:
  37. pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
  38. def do_parse(inputfile, grammarfile):
  39. if grammarfile not in parsers:
  40. grammar = fetch_cached(grammarfile)
  41. if grammar is None:
  42. result = parser = Parser(Grammar(), hide_implicit = True).parse(read(grammarfile))
  43. if result['status'] != Parser.Constants.Success:
  44. print 'not a valid grammar!'
  45. print result
  46. tree = result['tree']
  47. visitor = GrammarCompilerVisitor()
  48. structure = visitor.visit(tree)
  49. grammar = Grammar()
  50. grammar.rules = structure['rules']
  51. grammar.tokens = structure['tokens']
  52. make_cached(grammarfile, grammar)
  53. parsers[grammarfile] = grammar
  54. else:
  55. grammar = parsers[grammarfile]
  56. result = fetch_cached(inputfile)
  57. if result is None:
  58. result = Parser(grammar, line_position = True).parse(read(inputfile))
  59. if result['status'] != Parser.Constants.Success:
  60. msg = "%s:%s:%s: %s" % (inputfile, result["line"], result["column"], result["text"])
  61. raise Exception(msg)
  62. make_cached(inputfile, result)
  63. return result
  64. def find_file(filename, include_paths):
  65. import os.path
  66. include_paths = ["."] + \
  67. [os.path.abspath(os.path.dirname(working_file))] + \
  68. [os.path.abspath("%s/../includes/" % (os.path.dirname(os.path.abspath(__file__))))] + \
  69. include_paths + \
  70. []
  71. attempts = []
  72. for include in include_paths:
  73. testfile = include + os.sep + filename
  74. if os.path.isfile(os.path.abspath(testfile)):
  75. return os.path.abspath(testfile)
  76. else:
  77. attempts.append(os.path.abspath(testfile))
  78. else:
  79. raise Exception("Could not resolve file %s. Tried: %s" % (filename, attempts))
  80. def do_compile(inputfile, grammarfile, visitors=[], include_paths = []):
  81. import os.path
  82. global working_file
  83. working_file = os.path.abspath(inputfile)
  84. result = do_parse(inputfile, grammarfile)
  85. error = result["status"] != Parser.Constants.Success
  86. if error:
  87. msg = "%s:%s:%s: %s" % (inputfile, result["line"], result["column"], result["text"])
  88. raise Exception(msg)
  89. else:
  90. for child in result["tree"].tail:
  91. child.inputfile = inputfile
  92. included = set()
  93. while True:
  94. for i, v in enumerate(result["tree"].tail):
  95. if v.head == "include":
  96. # Expand this node
  97. for j in v.tail:
  98. if j.head == "STRVALUE":
  99. f = str(j.tail[0])[1:-1]
  100. if f in included:
  101. subtree = []
  102. else:
  103. name = str(j.tail[0])[1:-1]
  104. subtree = do_parse(find_file(name, include_paths), grammarfile)["tree"].tail
  105. if subtree is None:
  106. raise Exception("Parsing error for included file %s" % find_file(name, include_paths))
  107. for t in subtree:
  108. t.inputfile = name
  109. included.add(f)
  110. # Found the string value, so break from the inner for ("searching for element")
  111. break
  112. # Merge all nodes in
  113. before = result["tree"].tail[:i]
  114. after = result["tree"].tail[i+1:]
  115. result["tree"].tail = before + subtree + after
  116. # Found an include node, but to prevent corruption of the tree, we need to start over again, so break from the outer for loop
  117. break
  118. else:
  119. # The outer for finally finished, so there were no includes remaining, thus terminate the infinite while loop
  120. break
  121. result["tree"].fix_tracability(inputfile)
  122. for visitor in visitors:
  123. visitor.visit(result["tree"])
  124. if visitors:
  125. return visitors[-1].dump()
  126. def main(input_file, grammar_file, mode, args=[], symbols=None):
  127. from prettyprint_visitor import PrettyPrintVisitor
  128. from prettyprint_visitor import PrintVisitor
  129. from semantics_visitor import SemanticsVisitor
  130. from bootstrap_visitor import BootstrapVisitor
  131. from primitives_visitor import PrimitivesVisitor
  132. from primitives_object_visitor import PrimitivesObjectVisitor
  133. from constructors_visitor import ConstructorsVisitor
  134. from constructors_object_visitor import ConstructorsObjectVisitor
  135. from model_visitor import ModelVisitor
  136. from model_bootstrap_visitor import ModelBootstrapVisitor
  137. from model_object_visitor import ModelObjectVisitor
  138. modes = {
  139. "N" : [],
  140. "P" : [PrintVisitor],
  141. "PP" : [PrettyPrintVisitor],
  142. "S" : [SemanticsVisitor],
  143. "PS" : [SemanticsVisitor, PrimitivesVisitor],
  144. "PO" : [SemanticsVisitor, PrimitivesObjectVisitor],
  145. "BS" : [SemanticsVisitor, BootstrapVisitor],
  146. "CS" : [SemanticsVisitor, ConstructorsVisitor],
  147. "CO" : [SemanticsVisitor, ConstructorsObjectVisitor],
  148. "M" : [ModelVisitor],
  149. "MB" : [ModelBootstrapVisitor],
  150. "MO" : [ModelObjectVisitor],
  151. }
  152. try:
  153. visitors = [v(args) for v in modes[mode]]
  154. result = do_compile(input_file, grammar_file, visitors)
  155. if symbols is not None and mode == "BS":
  156. symbols.update(visitors[-1].object_symbols)
  157. except CachedException:
  158. return True
  159. return result
  160. if __name__ == "__main__":
  161. if len(sys.argv) <= 2:
  162. print("Invocation: ")
  163. print(" %s input_file grammar_file mode [mode_params]*" % sys.argv[0])
  164. sys.exit(1)
  165. else:
  166. value = main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4:])
  167. if value is not None:
  168. print(value)