diff python/yacc.py @ 319:8d07a4254f04

Work on burg
author Windel Bouwman
date Sat, 18 Jan 2014 18:58:43 +0100
parents e84047f29c78
children 8c569fbe60e4
line wrap: on
line diff
--- a/python/yacc.py	Tue Dec 31 12:38:15 2013 +0100
+++ b/python/yacc.py	Sat Jan 18 18:58:43 2014 +0100
@@ -1,6 +1,28 @@
 #!/usr/bin/python
 
-""" Parser generator utility """
+"""
+Parser generator utility. This script can generate a python script from a 
+grammar description.
+
+Invoke the script on a grammar specification file:
+
+.. code::
+
+    $ ./yacc.py test.x -o test_parser.py
+
+And use the generated parser by deriving a user class:
+
+
+.. code::
+
+    import test_parser
+    class MyParser(test_parser.Parser):
+        pass
+    p = MyParser()
+    p.parse()
+
+
+"""
 
 import argparse
 import re
@@ -131,6 +153,7 @@
             else:
                 headers.append(self.consume('HEADER')[1])
         self.consume('%%')
+        self.headers = headers
         self.grammar = Grammar(terminals)
         while self.Peak != 'eof':
             self.parse_rule()
@@ -140,6 +163,7 @@
         return self.consume('ID')[1]
 
     def parse_rhs(self):
+        """ Parse the right hand side of a rule definition """
         symbols = []
         while self.Peak not in [';', 'BRACEDCODE', '|']:
             symbols.append(self.parse_symbol())
@@ -151,6 +175,7 @@
         return symbols, action
 
     def parse_rule(self):
+        """ Parse a rule definition """
         p = self.parse_symbol()
         self.consume(':')
         symbols, action = self.parse_rhs()
@@ -166,47 +191,51 @@
     def __init__(self):
         pass
 
-    def generate(self, grammar, output_file):
+    def generate(self, grammar, headers, output_file):
         print_grammar(grammar)
         self.grammar = grammar
+        self.headers = headers
         self.action_table, self.goto_table = grammar.doGenerate()
         self.generate_python_script(output_file)
 
-    def generate_python_script(self, f):
+    def generate_python_script(self, output_file):
         """ Generate python script with the parser table """
-        print('#!/usr/bin/python', file=f)
+        print('#!/usr/bin/python', file=output_file)
         stamp = datetime.datetime.now().ctime()
-        print('""" Automatically generated by xacc on {} """'.format(stamp), file=f)
-        print('from pyyacc import LRParser, Reduce, Shift, Accept, Production, Grammar', file=f)
-        print('from ppci import Token', file=f)
-        print(file=f)
-        print('class Parser(LRParser):', file=f)
-        print('    def __init__(self):', file=f)
+        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)
+        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)
         # Generate rules:
-        print('        self.start_symbol = "{}"'.format(self.grammar.start_symbol), file=f)
-        print('        self.grammar = Grammar({})'.format(self.grammar.terminals), file=f)
+        print('        self.start_symbol = "{}"'.format(self.grammar.start_symbol), file=output_file)
+        print('        self.grammar = Grammar({})'.format(self.grammar.terminals), file=output_file)
         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=f)
+            print('        self.grammar.add_production("{}", {}, self.{})'.format(rule.name, rule.symbols, rule.f_name), file=output_file)
         # Fill action table:
-        print('        self.action_table = {}', file=f)
+        print('        self.action_table = {}', file=output_file)
         for state in self.action_table:
             action = self.action_table[state]
-            print('        self.action_table[{}] = {}'.format(state, action), file=f)
-        print('', file=f)
+            print('        self.action_table[{}] = {}'.format(state, action), file=output_file)
+        print('', file=output_file)
 
         # Fill goto table:
-        print('        self.goto_table = {}', file=f)
+        print('        self.goto_table = {}', file=output_file)
         for gt in self.goto_table:
             to = self.goto_table[gt]
-            print('        self.goto_table[{}] = {}'.format(gt, to), file=f)
-        print('', file=f)
+            print('        self.goto_table[{}] = {}'.format(gt, to), file=output_file)
+        print('', file=output_file)
 
         # 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=f)
+            print('    def {}(self, {}):'.format(rule.f_name, args), file=output_file)
             if rule.f == None:
                 semantics = 'pass'
             else:
@@ -215,8 +244,7 @@
                     semantics = 'pass'
             for n in range(M):
                 semantics = semantics.replace('${}'.format(n + 1), 'arg{}'.format(n + 1))
-            print('        {}'.format(semantics), file=f)
-            print('', file=f)
+            print('        {}'.format(semantics), file=output_file)
 
 
 def main():
@@ -238,7 +266,7 @@
     # Sequence source through the generator parts:
     lexer.feed(src)
     grammar = parser.parse_grammar()
-    generator.generate(grammar, args.output)
+    generator.generate(grammar, parser.headers, args.output)
     args.output.close()