diff python/yacc.py @ 321:8c569fbe60e4

Load yacc and burg dynamic
author Windel Bouwman
date Sun, 19 Jan 2014 18:48:45 +0100
parents 8d07a4254f04
children 44f336460c2a
line wrap: on
line diff
--- a/python/yacc.py	Sun Jan 19 16:09:44 2014 +0100
+++ b/python/yacc.py	Sun Jan 19 18:48:45 2014 +0100
@@ -22,12 +22,25 @@
     p.parse()
 
 
+Alternatively you can load the parser on the fly:
+
+.. code::
+
+    import yacc
+    parser_mod = yacc.load_as_module('mygrammar.x')
+    class MyParser(parser_mod.Parser):
+        pass
+    p = MyParser()
+    p.parse()
+
 """
 
 import argparse
 import re
 import sys
 import datetime
+import types
+import io
 from pyyacc import Grammar, print_grammar
 
 
@@ -101,7 +114,6 @@
         t = self.token
         if t[0] != 'eof':
             self.token = self.tokens.__next__()
-        #print(t)
         return t
 
 
@@ -192,50 +204,54 @@
         pass
 
     def generate(self, grammar, headers, output_file):
-        print_grammar(grammar)
+        self.output_file = output_file
         self.grammar = grammar
         self.headers = headers
         self.action_table, self.goto_table = grammar.doGenerate()
-        self.generate_python_script(output_file)
+        self.generate_python_script()
 
-    def generate_python_script(self, output_file):
+    def print(self, *args):
+        """ Print helper function that prints to output file """
+        print(*args, file=self.output_file)
+
+    def generate_python_script(self):
         """ Generate python script with the parser table """
-        print('#!/usr/bin/python', file=output_file)
+        self.print('#!/usr/bin/python')
         stamp = datetime.datetime.now().ctime()
-        print('""" Automatically generated by xacc on {} """'.format(stamp), file=output_file)
-        print('from pyyacc import LRParser, Reduce, Shift, Accept, Production, Grammar', file=output_file)
-        print('from ppci import Token', file=output_file)
-        print(file=output_file)
+        self.print('""" Automatically generated by xacc on {} """'.format(stamp))
+        self.print('from pyyacc import LRParser, Reduce, Shift, Accept, Production, Grammar')
+        self.print('from ppci import Token')
+        self.print('')
         for h in self.headers:
             print(h, file=output_file)
-        print(file=output_file)
-        print('class Parser(LRParser):', file=output_file)
-        print('    def __init__(self):', file=output_file)
+        self.print('')
+        self.print('class Parser(LRParser):')
+        self.print('    def __init__(self):')
         # Generate rules:
-        print('        self.start_symbol = "{}"'.format(self.grammar.start_symbol), file=output_file)
-        print('        self.grammar = Grammar({})'.format(self.grammar.terminals), file=output_file)
+        self.print('        self.start_symbol = "{}"'.format(self.grammar.start_symbol))
+        self.print('        self.grammar = Grammar({})'.format(self.grammar.terminals))
         for rule_number, rule in enumerate(self.grammar.productions):
             rule.f_name = 'action_{}_{}'.format(rule.name, rule_number)
-            print('        self.grammar.add_production("{}", {}, self.{})'.format(rule.name, rule.symbols, rule.f_name), file=output_file)
+            self.print('        self.grammar.add_production("{}", {}, self.{})'.format(rule.name, rule.symbols, rule.f_name))
         # Fill action table:
-        print('        self.action_table = {}', file=output_file)
+        self.print('        self.action_table = {}')
         for state in self.action_table:
             action = self.action_table[state]
-            print('        self.action_table[{}] = {}'.format(state, action), file=output_file)
-        print('', file=output_file)
+            self.print('        self.action_table[{}] = {}'.format(state, action))
+        self.print('')
 
         # Fill goto table:
-        print('        self.goto_table = {}', file=output_file)
+        self.print('        self.goto_table = {}')
         for gt in self.goto_table:
             to = self.goto_table[gt]
-            print('        self.goto_table[{}] = {}'.format(gt, to), file=output_file)
-        print('', file=output_file)
+            self.print('        self.goto_table[{}] = {}'.format(gt, to))
+        self.print('')
 
         # Generate a function for each action:
         for rule in self.grammar.productions:
             M = len(rule.symbols)
             args = ', '.join('arg{}'.format(n + 1) for n in range(M))
-            print('    def {}(self, {}):'.format(rule.f_name, args), file=output_file)
+            self.print('    def {}(self, {}):'.format(rule.f_name, args))
             if rule.f == None:
                 semantics = 'pass'
             else:
@@ -244,17 +260,29 @@
                     semantics = 'pass'
             for n in range(M):
                 semantics = semantics.replace('${}'.format(n + 1), 'arg{}'.format(n + 1))
-            print('        {}'.format(semantics), file=output_file)
+            self.print('        {}'.format(semantics))
 
 
-def main():
+def make_argument_parser():
     # Parse arguments:
     parser = argparse.ArgumentParser(description='xacc compiler compiler')
     parser.add_argument('source', type=argparse.FileType('r'), \
       help='the parser specification')
     parser.add_argument('-o', '--output', type=argparse.FileType('w'), \
         default=sys.stdout)
-    args = parser.parse_args()
+
+
+def load_as_module(filename):
+    """ Load a parser spec file, generate LR tables and create module """
+    ob = io.StringIO()
+    args = argparse.Namespace(source=open(filename), output=ob)
+    main(args)
+
+    parser_mod = types.ModuleType('generated_parser')
+    exec(ob.getvalue(), parser_mod.__dict__)
+    return parser_mod
+
+def main(args):
     src = args.source.read()
     args.source.close()
 
@@ -266,9 +294,10 @@
     # Sequence source through the generator parts:
     lexer.feed(src)
     grammar = parser.parse_grammar()
+    # TODO: optionize this: print_grammar(grammar)
     generator.generate(grammar, parser.headers, args.output)
-    args.output.close()
 
 
 if __name__ == '__main__':
-    main()
+    args = make_argument_parser().parse_args()
+    main(args)