File: grammar.py

package info (click to toggle)
python-pegen 0.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 10,980 kB
  • sloc: python: 15,064; makefile: 89
file content (149 lines) | stat: -rw-r--r-- 4,039 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
"""Parser for the grammar file."""

from token import DEDENT, INDENT, NAME, NEWLINE, STRING, ENDMARKER

from story5.parser import Parser

class Rule:

    def __init__(self, name, alts):
        self.name = name
        self.alts = alts

    def __repr__(self):
        return f"Rule({self.name!r}, {self.alts})"

    def __eq__(self, other):
        if not isinstance(other, Rule):
            return NotImplemented
        return self.name == other.name and self.alts == other.alts


class Alt:

    def __init__(self, items, action=None):
        self.items = items
        self.action = action

    def __repr__(self):
        if self.action:
            return f"Alt({self.items!r}, {self.action!r})"
        else:
            return f"Alt({self.items!r})"

    def __str__(self):
        items = " ".join(self.items)
        if self.action:
            return f"{items} {{ {self.action} }}"
        else:
            return items

    def __eq__(self, other):
        if not isinstance(other, Alt):
            return NotImplemented
        return self.items == other.items and self.action == other.action


class GrammarParser(Parser):

    def grammar(self):
        pos = self.mark()
        if rule := self.rule():
            rules = [rule]
            while rule := self.rule():
                rules.append(rule)
            if self.expect(ENDMARKER):
                return rules
        self.reset(pos)
        return None

    def rule(self):
        pos = self.mark()
        if (name := self.expect(NAME)) and self.expect(":"):
            if alts := self.alts_newline():
                pass
            elif self.expect(NEWLINE):
                alts = []
            else:
                self.reset(pos)
                return None
            if alts1 := self.indented_alts():
                alts.extend(alts1)
            if alts:
                return Rule(name.string, alts)
        self.reset(pos)
        return None

    def indented_alts(self):
        pos = self.mark()
        if self.expect(INDENT):
            alts = []
            while alts1 := self.bar_alts_newline():
                alts.extend(alts1)
            if self.expect(DEDENT):
                return alts
        self.reset(pos)
        return None

    def bar_alts_newline(self):
        pos = self.mark()
        if self.expect("|") and (alts := self.alts_newline()):
            return alts
        self.reset(pos)
        return None

    def alts_newline(self):
        pos = self.mark()
        if (alts := self.alts()) and self.expect(NEWLINE):
            return alts
        self.reset(pos)
        return None

    def alts(self):
        pos = self.mark()
        if alt := self.alternative():
            alts = [alt]
            while alt := self.bar_alt():
                alts.append(alt)
            return alts
        self.reset(pos)
        return None

    def bar_alt(self):
        pos = self.mark()
        if self.expect("|") and (alt := self.alternative()):
            return alt
        self.reset(pos)
        return None

    def alternative(self):
        items = []
        while item := self.item():
            items.append(item)
        if not items:
            return None
        # Look for {...}
        action = None
        pos = self.mark()
        if self.expect("{"):
            # Collect arbitrary tokens until "}" found, skipping matching {...} pairs.
            action_tokens = []
            level = 0
            while True:
                token = self.tokenizer.get_token().string
                if token == "{":
                    level += 1
                elif token == "}":
                    level -= 1
                    if level < 0:
                        break
                action_tokens.append(token)
            action = " ".join(action_tokens)
        return Alt(items, action)

    def item(self):
        if name := self.expect(NAME):
            return name.string
        if string := self.expect(STRING):
            return string.string
        return None