~corey.bryant/ubuntu/utopic/python-pyscss/mir

« back to all changes in this revision

Viewing changes to scss/src/grammar/yapps2.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-09-24 21:54:24 UTC
  • mto: This revision was merged to the branch mainline in revision 7.
  • Revision ID: package-import@ubuntu.com-20140924215424-yqmn0dajgwcamfxw
Tags: upstream-1.2.1
ImportĀ upstreamĀ versionĀ 1.2.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
# Yapps 3.0 - yet another python parser system
 
4
# Amit J Patel, January 1999
 
5
# German M. Bravo, December 2011
 
6
# See http://theory.stanford.edu/~amitp/Yapps/ for documentation and updates
 
7
 
 
8
# v3.0.0 changes (December 2011)
 
9
# * PEP 8 cleanups
 
10
# * Optimizations in the scanning (added cache and cleanup() for it)
 
11
# v2.0.1 changes (October 2001):
 
12
# * The exceptions inherit the standard Exception class (thanks Rich Salz)
 
13
# * The scanner can use either a different set of regular expressions
 
14
#   per instance, or allows the subclass to define class fields with
 
15
#   the patterns.  This improves performance when many Scanner objects
 
16
#   are being created, because the regular expressions don't have to
 
17
#   be recompiled each time. (thanks Amaury Forgeot d'Arc)
 
18
# v2.0.2 changes (April 2002)
 
19
# * Bug fix: generating the 'else' clause when the comment was too
 
20
#   long.  v2.0.1 was missing a newline.  (thanks Steven Engelhardt)
 
21
# v2.0.3 changes (August 2002)
 
22
# * Bug fix: inline tokens using the r"" syntax.
 
23
# v.2.0.4 changes (July 2003)
 
24
# * Style change: Replaced `expr` with repr(expr)
 
25
# * Style change: Changed (b >= a and b < c) into (a <= b < c)
 
26
# * Bug fix: identifiers in grammar rules that had digits in them were
 
27
#   not accessible in the {{python code}} section
 
28
# * Bug fix: made the SyntaxError exception class call
 
29
#   Exception.__init__ (thanks Alex Verstak)
 
30
# * Style change: replaced raise "string exception" with raise
 
31
#   ClassException(...) (thanks Alex Verstak)
 
32
 
 
33
from yappsrt import *
 
34
import sys
 
35
import re
 
36
 
 
37
 
 
38
INDENT = " " * 4
 
39
 
 
40
 
 
41
class Generator:
 
42
    def __init__(self, name, options, tokens, rules):
 
43
        self.change_count = 0
 
44
        self.name = name
 
45
        self.options = options
 
46
        self.preparser = ''
 
47
        self.postparser = None
 
48
 
 
49
        self.tokens = {}  # Map from tokens to regexps
 
50
        self.sets = {}  # Map for restriction sets
 
51
        self.ignore = []  # List of token names to ignore in parsing
 
52
        self.terminals = []  # List of token names (to maintain ordering)
 
53
 
 
54
        for n, t in tokens:
 
55
            if n == '#ignore':
 
56
                n = t
 
57
                self.ignore.append(n)
 
58
            if n in self.tokens.keys() and self.tokens[n] != t:
 
59
                if n not in self.ignore:
 
60
                    print 'Warning: token', n, 'multiply defined.'
 
61
            else:
 
62
                self.terminals.append(n)
 
63
            self.tokens[n] = t
 
64
 
 
65
        self.rules = {}  # Map from rule names to parser nodes
 
66
        self.params = {}  # Map from rule names to parameters
 
67
        self.goals = []  # List of rule names (to maintain ordering)
 
68
        for n, p, r in rules:
 
69
            self.params[n] = p
 
70
            self.rules[n] = r
 
71
            self.goals.append(n)
 
72
 
 
73
        self.output = sys.stdout
 
74
 
 
75
    def __getitem__(self, name):
 
76
        # Get options
 
77
        return self.options.get(name, 0)
 
78
 
 
79
    def non_ignored_tokens(self):
 
80
        return filter(lambda x, i=self.ignore: x not in i, self.terminals)
 
81
 
 
82
    def changed(self):
 
83
        self.change_count = 1 + self.change_count
 
84
 
 
85
    def subset(self, a, b):
 
86
        "See if all elements of a are inside b"
 
87
        for x in a:
 
88
            if x not in b:
 
89
                return 0
 
90
        return 1
 
91
 
 
92
    def equal_set(self, a, b):
 
93
        "See if a and b have the same elements"
 
94
        if len(a) != len(b):
 
95
            return 0
 
96
        if a == b:
 
97
            return 1
 
98
        return self.subset(a, b) and self.subset(b, a)
 
99
 
 
100
    def add_to(self, parent, additions):
 
101
        "Modify parent to include all elements in additions"
 
102
        for x in additions:
 
103
            if x not in parent:
 
104
                parent.append(x)
 
105
                self.changed()
 
106
 
 
107
    def equate(self, a, b):
 
108
        self.add_to(a, b)
 
109
        self.add_to(b, a)
 
110
 
 
111
    def write(self, *args):
 
112
        for a in args:
 
113
            self.output.write(a)
 
114
 
 
115
    def in_test(self, r, x, full, b):
 
116
        if not b:
 
117
            return '0'
 
118
        if len(b) == 1:
 
119
            return '%s == %s' % (x, repr(b[0]))
 
120
        if full and len(b) > len(full) / 2:
 
121
            # Reverse the sense of the test.
 
122
            not_b = filter(lambda x, b=b:
 
123
            x not in b, full)
 
124
            return self.not_in_test(r, x, full, not_b)
 
125
        n = None
 
126
        for k, v in self.sets.items():
 
127
            if v == b:
 
128
                n = k
 
129
        if n is None:
 
130
            n = '%s_chks' % r
 
131
            while n in self.sets:
 
132
                n += '_'
 
133
            self.sets[n] = b
 
134
        b_set = 'self.%s' % n
 
135
        return '%s in %s' % (x, b_set)
 
136
 
 
137
    def not_in_test(self, r, x, full, b):
 
138
        if not b:
 
139
            return '1'
 
140
        if len(b) == 1:
 
141
            return '%s != %s' % (x, repr(b[0]))
 
142
        n = None
 
143
        for k, v in self.sets.items():
 
144
            if v == b:
 
145
                n = k
 
146
        if n is None:
 
147
            n = '%s_chks' % r
 
148
            while n in self.sets:
 
149
                n += '_'
 
150
            self.sets[n] = b
 
151
        b_set = 'self.%s' % n
 
152
        return '%s not in %s' % (x, b_set)
 
153
 
 
154
    def peek_call(self, r, a):
 
155
        n = None
 
156
        for k, v in self.sets.items():
 
157
            if v == a:
 
158
                n = k
 
159
        if n is None:
 
160
            n = '%s_rsts' % r
 
161
            while n in self.sets:
 
162
                n += '_'
 
163
            self.sets[n] = a
 
164
        a_set = 'self.%s' % n
 
165
        if self.equal_set(a, self.non_ignored_tokens()):
 
166
            a_set = ''
 
167
        if self['context-insensitive-scanner']:
 
168
            a_set = ''
 
169
        return 'self._peek(%s)' % a_set
 
170
 
 
171
    def peek_test(self, r, a, b):
 
172
        if self.subset(a, b):
 
173
            return '1'
 
174
        if self['context-insensitive-scanner']:
 
175
            a = self.non_ignored_tokens()
 
176
        return self.in_test(r, self.peek_call(r, a), a, b)
 
177
 
 
178
    def not_peek_test(self, r, a, b):
 
179
        if self.subset(a, b):
 
180
            return '0'
 
181
        return self.not_in_test(r, self.peek_call(r, a), a, b)
 
182
 
 
183
    def calculate(self):
 
184
        while 1:
 
185
            for r in self.goals:
 
186
                self.rules[r].setup(self, r)
 
187
            if self.change_count == 0:
 
188
                break
 
189
            self.change_count = 0
 
190
 
 
191
        while 1:
 
192
            for r in self.goals:
 
193
                self.rules[r].update(self)
 
194
            if self.change_count == 0:
 
195
                break
 
196
            self.change_count = 0
 
197
 
 
198
    def dump_information(self):
 
199
        self.calculate()
 
200
        for r in self.goals:
 
201
            print '    _____' + '_' * len(r)
 
202
            print ('___/Rule ' + r + '\\' + '_' * 80)[:79]
 
203
            queue = [self.rules[r]]
 
204
            while queue:
 
205
                top = queue[0]
 
206
                del queue[0]
 
207
 
 
208
                print repr(top)
 
209
                top.first.sort()
 
210
                top.follow.sort()
 
211
                eps = []
 
212
                if top.accepts_epsilon:
 
213
                    eps = ['(null)']
 
214
                print '     FIRST:', join(top.first + eps, ', ')
 
215
                print '    FOLLOW:', join(top.follow, ', ')
 
216
                for x in top.get_children():
 
217
                    queue.append(x)
 
218
 
 
219
    def generate_output(self):
 
220
 
 
221
        self.calculate()
 
222
        self.write(self.preparser)
 
223
        # TODO: remove "import *" construct
 
224
        self.write("import re\n")
 
225
        self.write("from string import *\n")
 
226
        self.write("from yappsrt import *\n")
 
227
        self.write("\n\n")
 
228
        self.write("class ", self.name, "Scanner(Scanner):\n")
 
229
        self.write("    patterns = None\n")
 
230
        self.write("    _patterns = [\n")
 
231
        for p in self.terminals:
 
232
            self.write("        (%s, %s),\n" % (
 
233
                repr(p), repr(self.tokens[p])))
 
234
        self.write("    ]\n\n")
 
235
        self.write("    def __init__(self, input=None):\n")
 
236
        self.write("        if hasattr(self, 'setup_patterns'):\n")
 
237
        self.write("            self.setup_patterns(self._patterns)\n")
 
238
        self.write("        elif self.patterns is None:\n")
 
239
        self.write("            self.__class__.patterns = []\n")
 
240
        self.write("            for t, p in self._patterns:\n")
 
241
        self.write("                self.patterns.append((t, re.compile(p)))\n")
 
242
        self.write("        super(", self.name, "Scanner, self).__init__(None, %s, input)\n" %
 
243
                   repr(self.ignore))
 
244
        self.write("\n\n")
 
245
 
 
246
        self.write("class ", self.name, "(Parser):\n")
 
247
        for r in self.goals:
 
248
            self.write(INDENT, "def ", r, "(self")
 
249
            if self.params[r]:
 
250
                self.write(", ", self.params[r])
 
251
            self.write("):\n")
 
252
            self.rules[r].output(self, INDENT + INDENT)
 
253
            self.write("\n")
 
254
 
 
255
        for n, s in self.sets.items():
 
256
            self.write("    %s = %s\n" % (n, set(s)))
 
257
 
 
258
        if self.postparser is not None:
 
259
            self.write(self.postparser)
 
260
        else:
 
261
            self.write("\n")
 
262
            self.write("P = ", self.name, "(", self.name, "Scanner())\n")
 
263
            self.write("def parse(rule, text, *args):\n")
 
264
            self.write("    P.reset(text)\n")
 
265
            self.write("    return wrap_error_reporter(P, rule, *args)\n")
 
266
            self.write("\n")
 
267
 
 
268
            self.write("if __name__ == '__main__':\n")
 
269
            self.write(INDENT, "from sys import argv, stdin\n")
 
270
            self.write(INDENT, "if len(argv) >= 2:\n")
 
271
            self.write(INDENT * 2, "if len(argv) >= 3:\n")
 
272
            self.write(INDENT * 3, "f = open(argv[2],'r')\n")
 
273
            self.write(INDENT * 2, "else:\n")
 
274
            self.write(INDENT * 3, "f = stdin\n")
 
275
            self.write(INDENT * 2, "print parse(argv[1], f.read())\n")
 
276
            self.write(INDENT, "else: print 'Args:  <rule> [<filename>]'\n")
 
277
 
 
278
 
 
279
######################################################################
 
280
 
 
281
 
 
282
class Node:
 
283
    def __init__(self):
 
284
        self.first = []
 
285
        self.follow = []
 
286
        self.accepts_epsilon = 0
 
287
        self.rule = '?'
 
288
 
 
289
    def setup(self, gen, rule):
 
290
        # Setup will change accepts_epsilon,
 
291
        # sometimes from 0 to 1 but never 1 to 0.
 
292
        # It will take a finite number of steps to set things up
 
293
        self.rule = rule
 
294
 
 
295
    def used(self, vars):
 
296
        "Return two lists: one of vars used, and the other of vars assigned"
 
297
        return vars, []
 
298
 
 
299
    def get_children(self):
 
300
        "Return a list of sub-nodes"
 
301
        return []
 
302
 
 
303
    def __repr__(self):
 
304
        return str(self)
 
305
 
 
306
    def update(self, gen):
 
307
        if self.accepts_epsilon:
 
308
            gen.add_to(self.first, self.follow)
 
309
 
 
310
    def output(self, gen, indent):
 
311
        "Write out code to _gen_ with _indent_:string indentation"
 
312
        gen.write(indent, "assert 0  # Invalid parser node\n")
 
313
 
 
314
 
 
315
class Terminal(Node):
 
316
    def __init__(self, token):
 
317
        Node.__init__(self)
 
318
        self.token = token
 
319
        self.accepts_epsilon = 0
 
320
 
 
321
    def __str__(self):
 
322
        return self.token
 
323
 
 
324
    def update(self, gen):
 
325
        Node.update(self, gen)
 
326
        if self.first != [self.token]:
 
327
            self.first = [self.token]
 
328
            gen.changed()
 
329
 
 
330
    def output(self, gen, indent):
 
331
        gen.write(indent)
 
332
        if re.match('[a-zA-Z_][a-zA-Z_0-9]*$', self.token):
 
333
            gen.write(self.token, " = ")
 
334
        gen.write("self._scan(%s)\n" % repr(self.token))
 
335
 
 
336
 
 
337
class Eval(Node):
 
338
    def __init__(self, expr):
 
339
        Node.__init__(self)
 
340
        self.expr = expr
 
341
 
 
342
    def setup(self, gen, rule):
 
343
        Node.setup(self, gen, rule)
 
344
        if not self.accepts_epsilon:
 
345
            self.accepts_epsilon = 1
 
346
            gen.changed()
 
347
 
 
348
    def __str__(self):
 
349
        return '{{ %s }}' % self.expr.strip()
 
350
 
 
351
    def output(self, gen, indent):
 
352
        gen.write(indent, self.expr.strip(), '\n')
 
353
 
 
354
 
 
355
class NonTerminal(Node):
 
356
    def __init__(self, name, args):
 
357
        Node.__init__(self)
 
358
        self.name = name
 
359
        self.args = args
 
360
 
 
361
    def setup(self, gen, rule):
 
362
        Node.setup(self, gen, rule)
 
363
        try:
 
364
            self.target = gen.rules[self.name]
 
365
            if self.accepts_epsilon != self.target.accepts_epsilon:
 
366
                self.accepts_epsilon = self.target.accepts_epsilon
 
367
                gen.changed()
 
368
        except KeyError:  # Oops, it's nonexistent
 
369
            print 'Error: no rule <%s>' % self.name
 
370
            self.target = self
 
371
 
 
372
    def __str__(self):
 
373
        return '<%s>' % self.name
 
374
 
 
375
    def update(self, gen):
 
376
        Node.update(self, gen)
 
377
        gen.equate(self.first, self.target.first)
 
378
        gen.equate(self.follow, self.target.follow)
 
379
 
 
380
    def output(self, gen, indent):
 
381
        gen.write(indent)
 
382
        gen.write(self.name, " = ")
 
383
        gen.write("self.", self.name, "(", self.args, ")\n")
 
384
 
 
385
 
 
386
class Sequence(Node):
 
387
    def __init__(self, *children):
 
388
        Node.__init__(self)
 
389
        self.children = children
 
390
 
 
391
    def setup(self, gen, rule):
 
392
        Node.setup(self, gen, rule)
 
393
        for c in self.children:
 
394
            c.setup(gen, rule)
 
395
 
 
396
        if not self.accepts_epsilon:
 
397
            # If it's not already accepting epsilon, it might now do so.
 
398
            for c in self.children:
 
399
                # any non-epsilon means all is non-epsilon
 
400
                if not c.accepts_epsilon:
 
401
                    break
 
402
            else:
 
403
                self.accepts_epsilon = 1
 
404
                gen.changed()
 
405
 
 
406
    def get_children(self):
 
407
        return self.children
 
408
 
 
409
    def __str__(self):
 
410
        return '( %s )' % join(map(lambda x: str(x), self.children))
 
411
 
 
412
    def update(self, gen):
 
413
        Node.update(self, gen)
 
414
        for g in self.children:
 
415
            g.update(gen)
 
416
 
 
417
        empty = 1
 
418
        for g_i in range(len(self.children)):
 
419
            g = self.children[g_i]
 
420
 
 
421
            if empty:
 
422
                gen.add_to(self.first, g.first)
 
423
            if not g.accepts_epsilon:
 
424
                empty = 0
 
425
 
 
426
            if g_i == len(self.children) - 1:
 
427
                next = self.follow
 
428
            else:
 
429
                next = self.children[1 + g_i].first
 
430
            gen.add_to(g.follow, next)
 
431
 
 
432
        if self.children:
 
433
            gen.add_to(self.follow, self.children[-1].follow)
 
434
 
 
435
    def output(self, gen, indent):
 
436
        if self.children:
 
437
            for c in self.children:
 
438
                c.output(gen, indent)
 
439
        else:
 
440
            # Placeholder for empty sequences, just in case
 
441
            gen.write(indent, 'pass\n')
 
442
 
 
443
class Choice(Node):
 
444
    def __init__(self, *children):
 
445
        Node.__init__(self)
 
446
        self.children = children
 
447
 
 
448
    def setup(self, gen, rule):
 
449
        Node.setup(self, gen, rule)
 
450
        for c in self.children:
 
451
            c.setup(gen, rule)
 
452
 
 
453
        if not self.accepts_epsilon:
 
454
            for c in self.children:
 
455
                if c.accepts_epsilon:
 
456
                    self.accepts_epsilon = 1
 
457
                    gen.changed()
 
458
 
 
459
    def get_children(self):
 
460
        return self.children
 
461
 
 
462
    def __str__(self):
 
463
        return '( %s )' % join(map(lambda x: str(x), self.children), ' | ')
 
464
 
 
465
    def update(self, gen):
 
466
        Node.update(self, gen)
 
467
        for g in self.children:
 
468
            g.update(gen)
 
469
 
 
470
        for g in self.children:
 
471
            gen.add_to(self.first, g.first)
 
472
            gen.add_to(self.follow, g.follow)
 
473
        for g in self.children:
 
474
            gen.add_to(g.follow, self.follow)
 
475
        if self.accepts_epsilon:
 
476
            gen.add_to(self.first, self.follow)
 
477
 
 
478
    def output(self, gen, indent):
 
479
        test = "if"
 
480
        gen.write(indent, "_token_ = ", gen.peek_call(self.rule, self.first), "\n")
 
481
        tokens_seen = []
 
482
        tokens_unseen = self.first[:]
 
483
        if gen['context-insensitive-scanner']:
 
484
            # Context insensitive scanners can return ANY token,
 
485
            # not only the ones in first.
 
486
            tokens_unseen = gen.non_ignored_tokens()
 
487
        for c in self.children:
 
488
            testset = c.first[:]
 
489
            removed = []
 
490
            for x in testset:
 
491
                if x in tokens_seen:
 
492
                    testset.remove(x)
 
493
                    removed.append(x)
 
494
                if x in tokens_unseen:
 
495
                    tokens_unseen.remove(x)
 
496
            tokens_seen = tokens_seen + testset
 
497
            if removed:
 
498
                if not testset:
 
499
                    print 'Error in rule', self.rule + ':', c, 'never matches.'
 
500
                else:
 
501
                    print 'Warning:', self
 
502
                print ' * These tokens are being ignored:', join(removed, ', ')
 
503
                print '   due to previous choices using them.'
 
504
 
 
505
            if testset:
 
506
                if not tokens_unseen:  # context sensitive scanners only!
 
507
                    if test == 'if':
 
508
                        # if it's the first AND last test, then
 
509
                        # we can simply put the code without an if/else
 
510
                        c.output(gen, indent)
 
511
                    else:
 
512
                        gen.write(indent, "else:")
 
513
                        t = gen.in_test(self.rule, '', [], testset)
 
514
                        if len(t) < 70 - len(indent):
 
515
                            gen.write("  #", t)
 
516
                        gen.write("\n")
 
517
                        c.output(gen, indent + INDENT)
 
518
                else:
 
519
                    gen.write(indent, test, " ",
 
520
                              gen.in_test(self.rule, '_token_', tokens_unseen, testset),
 
521
                              ":\n")
 
522
                    c.output(gen, indent + INDENT)
 
523
                test = "elif"
 
524
 
 
525
        if gen['context-insensitive-scanner'] and tokens_unseen:
 
526
            gen.write(indent, "else:\n")
 
527
            gen.write(indent, INDENT, "raise SyntaxError(self._pos, ")
 
528
            gen.write("'Could not match ", self.rule, "')\n")
 
529
 
 
530
 
 
531
class Wrapper(Node):
 
532
    def __init__(self, child):
 
533
        Node.__init__(self)
 
534
        self.child = child
 
535
 
 
536
    def setup(self, gen, rule):
 
537
        Node.setup(self, gen, rule)
 
538
        self.child.setup(gen, rule)
 
539
 
 
540
    def get_children(self):
 
541
        return [self.child]
 
542
 
 
543
    def update(self, gen):
 
544
        Node.update(self, gen)
 
545
        self.child.update(gen)
 
546
        gen.add_to(self.first, self.child.first)
 
547
        gen.equate(self.follow, self.child.follow)
 
548
 
 
549
 
 
550
class Option(Wrapper):
 
551
    def setup(self, gen, rule):
 
552
        Wrapper.setup(self, gen, rule)
 
553
        if not self.accepts_epsilon:
 
554
            self.accepts_epsilon = 1
 
555
            gen.changed()
 
556
 
 
557
    def __str__(self):
 
558
        return '[ %s ]' % str(self.child)
 
559
 
 
560
    def output(self, gen, indent):
 
561
        if self.child.accepts_epsilon:
 
562
            print 'Warning in rule', self.rule + ': contents may be empty.'
 
563
        gen.write(indent, "if %s:\n" %
 
564
                  gen.peek_test(self.rule, self.first, self.child.first))
 
565
        self.child.output(gen, indent + INDENT)
 
566
 
 
567
 
 
568
class Plus(Wrapper):
 
569
    def setup(self, gen, rule):
 
570
        Wrapper.setup(self, gen, rule)
 
571
        if self.accepts_epsilon != self.child.accepts_epsilon:
 
572
            self.accepts_epsilon = self.child.accepts_epsilon
 
573
            gen.changed()
 
574
 
 
575
    def __str__(self):
 
576
        return '%s+' % str(self.child)
 
577
 
 
578
    def update(self, gen):
 
579
        Wrapper.update(self, gen)
 
580
        gen.add_to(self.follow, self.first)
 
581
 
 
582
    def output(self, gen, indent):
 
583
        if self.child.accepts_epsilon:
 
584
            print 'Warning in rule', self.rule + ':'
 
585
            print ' * The repeated pattern could be empty.  The resulting'
 
586
            print '   parser may not work properly.'
 
587
        gen.write(indent, "while 1:\n")
 
588
        self.child.output(gen, indent + INDENT)
 
589
        union = self.first[:]
 
590
        gen.add_to(union, self.follow)
 
591
        gen.write(indent + INDENT, "if %s:\n" %
 
592
                  gen.not_peek_test(self.rule, union, self.child.first))
 
593
        gen.write(indent + INDENT * 2, "break\n")
 
594
 
 
595
 
 
596
class Star(Plus):
 
597
    def setup(self, gen, rule):
 
598
        Wrapper.setup(self, gen, rule)
 
599
        if not self.accepts_epsilon:
 
600
            self.accepts_epsilon = 1
 
601
            gen.changed()
 
602
 
 
603
    def __str__(self):
 
604
        return '%s*' % str(self.child)
 
605
 
 
606
    def output(self, gen, indent):
 
607
        if self.child.accepts_epsilon:
 
608
            print 'Warning in rule', self.rule + ':'
 
609
            print ' * The repeated pattern could be empty.  The resulting'
 
610
            print '   parser probably will not work properly.'
 
611
        gen.write(indent, "while %s:\n" %
 
612
                  gen.peek_test(self.rule, self.follow, self.child.first))
 
613
        self.child.output(gen, indent + INDENT)
 
614
 
 
615
######################################################################
 
616
# The remainder of this file is from parsedesc.{g,py}
 
617
 
 
618
 
 
619
def append(lst, x):
 
620
    "Imperative append"
 
621
    lst.append(x)
 
622
    return lst
 
623
 
 
624
 
 
625
def add_inline_token(tokens, str):
 
626
    tokens.insert(0, (str, eval(str, {}, {})))
 
627
    return Terminal(str)
 
628
 
 
629
 
 
630
def cleanup_choice(lst):
 
631
    if len(lst) == 0:
 
632
        return Sequence([])
 
633
    if len(lst) == 1:
 
634
        return lst[0]
 
635
    return apply(Choice, tuple(lst))
 
636
 
 
637
 
 
638
def cleanup_sequence(lst):
 
639
    if len(lst) == 1:
 
640
        return lst[0]
 
641
    return apply(Sequence, tuple(lst))
 
642
 
 
643
 
 
644
def cleanup_rep(node, rep):
 
645
    if rep == 'star':
 
646
        return Star(node)
 
647
    elif rep == 'plus':
 
648
        return Plus(node)
 
649
    else:
 
650
        return node
 
651
 
 
652
 
 
653
def resolve_name(tokens, id, args):
 
654
    if id in map(lambda x: x[0], tokens):
 
655
        # It's a token
 
656
        if args:
 
657
            print 'Warning: ignoring parameters on TOKEN %s<<%s>>' % (id, args)
 
658
        return Terminal(id)
 
659
    else:
 
660
        # It's a name, so assume it's a nonterminal
 
661
        return NonTerminal(id, args)
 
662
 
 
663
 
 
664
from string import *
 
665
from yappsrt import *
 
666
 
 
667
 
 
668
class ParserDescriptionScanner(Scanner):
 
669
    def __init__(self, str):
 
670
        Scanner.__init__(self, [
 
671
            ('"rule"', 'rule'),
 
672
            ('"ignore"', 'ignore'),
 
673
            ('"token"', 'token'),
 
674
            ('"option"', 'option'),
 
675
            ('":"', ':'),
 
676
            ('"parser"', 'parser'),
 
677
            ('[ \011\015\012]+', '[ \011\015\012]+'),
 
678
            ('#.*?\015?\012', '#.*?\015?\012'),
 
679
            ('END', '$'),
 
680
            ('ATTR', '<<.+?>>'),
 
681
            ('STMT', '{{.+?}}'),
 
682
            ('ID', '[a-zA-Z_][a-zA-Z_0-9]*'),
 
683
            ('STR', '[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"'),
 
684
            ('LP', '\\('),
 
685
            ('RP', '\\)'),
 
686
            ('LB', '\\['),
 
687
            ('RB', '\\]'),
 
688
            ('OR', '[|]'),
 
689
            ('STAR', '[*]'),
 
690
            ('PLUS', '[+]'),
 
691
        ], ['[ \011\015\012]+', '#.*?\015?\012'], str)
 
692
 
 
693
 
 
694
class ParserDescription(Parser):
 
695
    def Parser(self):
 
696
        self._scan('"parser"')
 
697
        ID = self._scan('ID')
 
698
        self._scan('":"')
 
699
        Options = self.Options()
 
700
        Tokens = self.Tokens()
 
701
        Rules = self.Rules(Tokens)
 
702
        END = self._scan('END')
 
703
        return Generator(ID, Options, Tokens, Rules)
 
704
 
 
705
    def Options(self):
 
706
        opt = {}
 
707
        while self._peek(set(['"option"', '"token"', '"ignore"', 'END', '"rule"'])) == '"option"':
 
708
            self._scan('"option"')
 
709
            self._scan('":"')
 
710
            Str = self.Str()
 
711
            opt[Str] = 1
 
712
        return opt
 
713
 
 
714
    def Tokens(self):
 
715
        tok = []
 
716
        while self._peek(set(['"token"', '"ignore"', 'END', '"rule"'])) in ['"token"', '"ignore"']:
 
717
            _token_ = self._peek(set(['"token"', '"ignore"']))
 
718
            if _token_ == '"token"':
 
719
                self._scan('"token"')
 
720
                ID = self._scan('ID')
 
721
                self._scan('":"')
 
722
                Str = self.Str()
 
723
                tok.append((ID, Str))
 
724
            else:  # == '"ignore"'
 
725
                self._scan('"ignore"')
 
726
                self._scan('":"')
 
727
                Str = self.Str()
 
728
                tok.append(('#ignore', Str))
 
729
        return tok
 
730
 
 
731
    def Rules(self, tokens):
 
732
        rul = []
 
733
        while self._peek(set(['"rule"', 'END'])) == '"rule"':
 
734
            self._scan('"rule"')
 
735
            ID = self._scan('ID')
 
736
            OptParam = self.OptParam()
 
737
            self._scan('":"')
 
738
            ClauseA = self.ClauseA(tokens)
 
739
            rul.append((ID, OptParam, ClauseA))
 
740
        return rul
 
741
 
 
742
    def ClauseA(self, tokens):
 
743
        ClauseB = self.ClauseB(tokens)
 
744
        v = [ClauseB]
 
745
        while self._peek(set(['OR', 'RP', 'RB', '"rule"', 'END'])) == 'OR':
 
746
            OR = self._scan('OR')
 
747
            ClauseB = self.ClauseB(tokens)
 
748
            v.append(ClauseB)
 
749
        return cleanup_choice(v)
 
750
 
 
751
    def ClauseB(self, tokens):
 
752
        v = []
 
753
        while self._peek(set(['STR', 'ID', 'LP', 'LB', 'STMT', 'OR', 'RP', 'RB', '"rule"', 'END'])) in ['STR', 'ID', 'LP', 'LB', 'STMT']:
 
754
            ClauseC = self.ClauseC(tokens)
 
755
            v.append(ClauseC)
 
756
        return cleanup_sequence(v)
 
757
 
 
758
    def ClauseC(self, tokens):
 
759
        ClauseD = self.ClauseD(tokens)
 
760
        _token_ = self._peek(set(['PLUS', 'STAR', 'STR', 'ID', 'LP', 'LB', 'STMT', 'OR', 'RP', 'RB', '"rule"', 'END']))
 
761
        if _token_ == 'PLUS':
 
762
            PLUS = self._scan('PLUS')
 
763
            return Plus(ClauseD)
 
764
        elif _token_ == 'STAR':
 
765
            STAR = self._scan('STAR')
 
766
            return Star(ClauseD)
 
767
        else:
 
768
            return ClauseD
 
769
 
 
770
    def ClauseD(self, tokens):
 
771
        _token_ = self._peek(set(['STR', 'ID', 'LP', 'LB', 'STMT']))
 
772
        if _token_ == 'STR':
 
773
            STR = self._scan('STR')
 
774
            t = (STR, eval(STR, {}, {}))
 
775
            if t not in tokens:
 
776
                tokens.insert(0, t)
 
777
            return Terminal(STR)
 
778
        elif _token_ == 'ID':
 
779
            ID = self._scan('ID')
 
780
            OptParam = self.OptParam()
 
781
            return resolve_name(tokens, ID, OptParam)
 
782
        elif _token_ == 'LP':
 
783
            LP = self._scan('LP')
 
784
            ClauseA = self.ClauseA(tokens)
 
785
            RP = self._scan('RP')
 
786
            return ClauseA
 
787
        elif _token_ == 'LB':
 
788
            LB = self._scan('LB')
 
789
            ClauseA = self.ClauseA(tokens)
 
790
            RB = self._scan('RB')
 
791
            return Option(ClauseA)
 
792
        else:  # == 'STMT'
 
793
            STMT = self._scan('STMT')
 
794
            return Eval(STMT[2:-2])
 
795
 
 
796
    def OptParam(self):
 
797
        if self._peek(set(['ATTR', '":"', 'PLUS', 'STAR', 'STR', 'ID', 'LP', 'LB', 'STMT', 'OR', 'RP', 'RB', '"rule"', 'END'])) == 'ATTR':
 
798
            ATTR = self._scan('ATTR')
 
799
            return ATTR[2:-2]
 
800
        return ''
 
801
 
 
802
    def Str(self):
 
803
        STR = self._scan('STR')
 
804
        return eval(STR, {}, {})
 
805
 
 
806
 
 
807
# This replaces the default main routine
 
808
 
 
809
 
 
810
yapps_options = [
 
811
    ('context-insensitive-scanner', 'context-insensitive-scanner',
 
812
     'Scan all tokens (see docs)')
 
813
    ]
 
814
 
 
815
 
 
816
def generate(inputfilename, outputfilename='', dump=0, **flags):
 
817
    """Generate a grammar, given an input filename (X.g)
 
818
    and an output filename (defaulting to X.py)."""
 
819
 
 
820
    if not outputfilename:
 
821
        if inputfilename[-2:] == '.g':
 
822
            outputfilename = inputfilename[:-2] + '.py'
 
823
        else:
 
824
            raise Exception("Missing output filename")
 
825
 
 
826
    print 'Input Grammar:', inputfilename
 
827
    print 'Output File:', outputfilename
 
828
 
 
829
    DIVIDER = '\n%%\n'  # This pattern separates the pre/post parsers
 
830
    preparser, postparser = None, None  # Code before and after the parser desc
 
831
 
 
832
    # Read the entire file
 
833
    s = open(inputfilename, 'r').read()
 
834
 
 
835
    # See if there's a separation between the pre-parser and parser
 
836
    f = find(s, DIVIDER)
 
837
    if f >= 0:
 
838
        preparser, s = s[:f] + '\n\n', s[f + len(DIVIDER):]
 
839
 
 
840
    # See if there's a separation between the parser and post-parser
 
841
    f = find(s, DIVIDER)
 
842
    if f >= 0:
 
843
        s, postparser = s[:f], '\n\n' + s[f + len(DIVIDER):]
 
844
 
 
845
    # Create the parser and scanner
 
846
    p = ParserDescription(ParserDescriptionScanner(s))
 
847
    if not p:
 
848
        return
 
849
 
 
850
    # Now parse the file
 
851
    t = wrap_error_reporter(p, 'Parser')
 
852
    if not t:
 
853
        return  # Error
 
854
    if preparser is not None:
 
855
        t.preparser = preparser
 
856
    if postparser is not None:
 
857
        t.postparser = postparser
 
858
 
 
859
    # Check the options
 
860
    for f in t.options.keys():
 
861
        for opt, _, _ in yapps_options:
 
862
            if f == opt:
 
863
                break
 
864
        else:
 
865
            print 'Warning: unrecognized option', f
 
866
    # Add command line options to the set
 
867
    for f in flags.keys():
 
868
        t.options[f] = flags[f]
 
869
 
 
870
    # Generate the output
 
871
    if dump:
 
872
        t.dump_information()
 
873
    else:
 
874
        t.output = open(outputfilename, 'w')
 
875
        t.generate_output()
 
876
 
 
877
if __name__ == '__main__':
 
878
    import getopt
 
879
    optlist, args = getopt.getopt(sys.argv[1:], 'f:', ['dump'])
 
880
    if not args or len(args) > 2:
 
881
        print 'Usage:'
 
882
        print '  python', sys.argv[0], '[flags] input.g [output.py]'
 
883
        print 'Flags:'
 
884
        print ('  --dump' + ' ' * 40)[:35] + 'Dump out grammar information'
 
885
        for flag, _, doc in yapps_options:
 
886
            print ('  -f' + flag + ' ' * 40)[:35] + doc
 
887
    else:
 
888
        # Read in the options and create a list of flags
 
889
        flags = {}
 
890
        for opt in optlist:
 
891
            for flag, name, _ in yapps_options:
 
892
                if opt == ('-f', flag):
 
893
                    flags[name] = 1
 
894
                    break
 
895
            else:
 
896
                if opt == ('--dump', ''):
 
897
                    flags['dump'] = 1
 
898
                else:
 
899
                    print 'Warning: unrecognized option', opt[0], opt[1]
 
900
 
 
901
        apply(generate, tuple(args), flags)