~washort/monte/unparser

« back to all changes in this revision

Viewing changes to monte/grammar.py

  • Committer: Allen Short
  • Date: 2012-05-16 15:14:59 UTC
  • Revision ID: washort42@gmail.com-20120516151459-k5n6sd261syuq1s1
depend on actual pymeta

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
import string, sys, linecache
 
4
from types import ModuleType as module
 
5
from pymeta.runtime import ParseError
 
6
from pymeta.grammar import OMetaGrammarMixin, OMeta, OMetaGrammar
 
7
from pymeta.builder import TreeBuilder, PythonWriter, GeneratedCodeLoader
 
8
portableOMetaGrammar = """
 
9
action ::= <spaces> (<actionCall> | <actionNoun> | <actionLiteral>)
 
10
actionCall ::= <actionNoun>:verb <token "("> <actionArgs>?:args <token ")"> => ActionCall(verb, args)
 
11
actionArgs ::= <action>:a (<token ','> <action>)*:b => [a] + b
 
12
actionNoun ::= <name>:n => ActionNoun(n)
 
13
actionLiteral ::=  (<number> | <character> | <bareString>):lit => ActionLiteral(lit)
 
14
 
 
15
ruleValue ::= <token "=>"> <action>:a => self.result(a)
 
16
semanticPredicate ::= <token "?("> <action>:a <token ")"> => self.predicate(a)
 
17
semanticAction ::= <token "!("> <action>:a <token ")"> => self.action(a)
 
18
applicationArgs ::= (<spaces> <action>)+:args <token ">"> => [self.result(a) for a in args]
 
19
string ::= <bareString>:s => self.builder.apply("tokenBR", self.name, self.action(ActionLiteral(s)))
 
20
"""
 
21
class ActionNoun(object):
 
22
    """
 
23
    A noun in a portable OMeta grammar action.
 
24
    """
 
25
    def __init__(self, name):
 
26
        self.name = name
 
27
 
 
28
 
 
29
    def visit(self, visitor):
 
30
        return visitor.name(self.name)
 
31
 
 
32
class ActionCall(object):
 
33
    """
 
34
    A call action in a portable OMeta grammar.
 
35
    """
 
36
    def __init__(self, verb, args):
 
37
        self.verb = verb
 
38
        self.args = args or []
 
39
 
 
40
 
 
41
    def visit(self, visitor):
 
42
        return visitor.call(self.verb.visit(visitor),
 
43
                            [arg.visit(visitor) for arg in self.args])
 
44
 
 
45
 
 
46
class ActionLiteral(object):
 
47
    """
 
48
    A literal value in a portable OMeta action.
 
49
    """
 
50
    def __init__(self, value):
 
51
        self.value = value
 
52
 
 
53
    def visit(self, visitor):
 
54
        return visitor.literal(self.value)
 
55
 
 
56
class PortableTreeBuilder(TreeBuilder):
 
57
    def compilePortableAction(self, action):
 
58
        return ["PortableAction", action]
 
59
 
 
60
 
 
61
class ActionVisitor:
 
62
    gensymCounter = 0
 
63
 
 
64
    def __init__(self, output):
 
65
        self.output = output
 
66
 
 
67
    def _gensym(self):
 
68
        """
 
69
        Produce a unique name for a variable in generated code.
 
70
        """
 
71
        ActionVisitor.gensymCounter += 1
 
72
        return "_A_%s" % (self.gensymCounter)
 
73
 
 
74
    def name(self, name):
 
75
        result = self._gensym()
 
76
        self.output.append('%s = self.lookupActionName(%r, _locals)' % (result, name))
 
77
        return result
 
78
 
 
79
    def call(self, verb, args):
 
80
        result = self._gensym()
 
81
        self.output.append('%s = %s(%s)' % (result, verb, ', '.join(args)))
 
82
        return result
 
83
 
 
84
    def literal(self, value):
 
85
        return repr(value)
 
86
 
 
87
 
 
88
class PortablePythonWriter(PythonWriter):
 
89
    def generate_PortableAction(self, action):
 
90
        """
 
91
        Generate Python code for an action expression.
 
92
        """
 
93
        av = ActionVisitor(self.lines)
 
94
        return action.visit(av)
 
95
 
 
96
 
 
97
def makePortableGrammar(grammar, bits, name):
 
98
    g = OMetaGrammar(grammar)
 
99
    tree = g.parseGrammar(name, PortableTreeBuilder)
 
100
    return moduleFromGrammar(tree, name, OMetaGrammar, bits)
 
101
 
 
102
def moduleFromGrammar(tree, className, superclass, globalsDict):
 
103
    pw = PortablePythonWriter(tree)
 
104
    source = pw.output()
 
105
 
 
106
    modname = "pymeta_grammar__" + className
 
107
    filename = "/pymeta_generated_code/" + modname + ".py"
 
108
    mod = module(modname)
 
109
    mod.__dict__.update(globalsDict)
 
110
    mod.__name__ = modname
 
111
    mod.__dict__[superclass.__name__] = superclass
 
112
    mod.__dict__["GrammarBase"] = superclass
 
113
    mod.__loader__ = GeneratedCodeLoader(source)
 
114
    code = compile(source, filename, "exec")
 
115
    eval(code, mod.__dict__)
 
116
    fullGlobals = dict(getattr(mod.__dict__[className], "globals", None) or {})
 
117
    fullGlobals.update(globalsDict)
 
118
    mod.__dict__[className].globals = fullGlobals
 
119
    sys.modules[modname] = mod
 
120
    linecache.getlines(filename, mod.__dict__)
 
121
    return mod.__dict__[className]
 
122
 
 
123
_PortableActionGrammar = makePortableGrammar(portableOMetaGrammar,
 
124
                                             globals(), "_PortableActionGrammar")
 
125
 
 
126
class PortableOMetaGrammar(_PortableActionGrammar):
 
127
    """
 
128
    An OMeta variant with portable syntax for actions.
 
129
    """
 
130
 
 
131
    def result(self, action):
 
132
        return self.builder.compilePortableAction(action)
 
133
 
 
134
 
 
135
    def predicate(self, action):
 
136
        return self.builder.pred(self.builder.compilePortableAction(action))
 
137
 
 
138
 
 
139
    def action(self, action):
 
140
        return self.builder.compilePortableAction(action)
 
141
 
 
142
 
 
143
 
 
144
class PortableOMeta(OMeta):
 
145
    metagrammarClass = PortableOMetaGrammar
 
146
 
 
147
    def rule_tokenBR(self):
 
148
        """
 
149
        Match and return the given string, consuming any preceding or trailing
 
150
        whitespace.
 
151
        """
 
152
        tok, _ = self.input.head()
 
153
 
 
154
        m = self.input = self.input.tail()
 
155
        try:
 
156
            self.eatWhitespace()
 
157
            for c  in tok:
 
158
                self.exactly(c)
 
159
            _, e = self.apply("br")
 
160
            return tok, e
 
161
        except ParseError:
 
162
            self.input = m
 
163
            raise
 
164
 
 
165
 
 
166
    @classmethod
 
167
    def makeGrammar(cls, grammar, globals, name="Grammar"):
 
168
        g = cls.metagrammarClass(grammar)
 
169
        tree = g.parseGrammar(name, PortableTreeBuilder)
 
170
        return moduleFromGrammar(tree, name, cls, globals)