~kf5jwc/imp-parser

ref: 4a8b0d0c4a9b3e235854c13031f981f55fc7ece5 imp-parser/imp_parser/parser/combinators.py -rw-r--r-- 3.9 KiB View raw
4a8b0d0c — Kyle Jones More typing! 1 year, 7 months ago
                                                                                
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import attr
from typing import Type, TypeVar, Union, Optional
from .result import Result


class Parser(object):
    def __call__(self, tokens, pos) -> Optional[Union[Type["Parser"], Result]]:
        # subclasses should change this
        # I wonder if python has a way to warn about that
        pass

    # oooh, fun. Operator overloading can get confusing.
    def __add__(self, other) -> Concat:
        return Concat(self, other)

    def __mul__(self, other) -> Exp:
        return Exp(self, other)

    def __or__(self, other) -> Alternate:
        return Alternate(self, other)

    def __xor__(self, other) -> Process:
        return Process(self, other)


@attr.s
class Reserved(Parser):
    value = attr.ib()
    tag = attr.ib()

    def __call__(self, tokens, pos) -> Optional[Result]:
        if pos < len(tokens):
            (value, tag) = tokens[pos]
            if value == self.value and tag == self.tag:
                return Result(value, pos + 1)

        return None


@attr.s
class Tag(Parser):
    tag = attr.ib()

    def __call__(self, tokens, pos) -> Optional[Result]:
        if pos < len(tokens):
            (value, tag) = tokens[pos]
            if tag is self.tag:
                return Result(value, pos + 1)

        return None


@attr.s
class Concat(Parser):
    left = attr.ib()
    right = attr.ib()

    def __call__(self, tokens, pos) -> Optional[Result]:
        left_result = self.left(tokens, pos)
        if left_result is None:
            return None

        right_result = self.right(tokens, left_result.pos)
        if right_result is None:
            return None

        combined_result = (left_result.value, right_result.value)
        return Result(combined_result, right_result.pos)


@attr.s
class Exp(Parser):
    parser = attr.ib()
    separator = attr.ib()

    def __call__(self, tokens, pos):
        def process_next(parsed):
            (sepfunc, right) = parsed
            return sepfunc(result.value, right)

        result = self.parser(tokens, pos)
        next_parser = self.separator + self.parser ^ process_next
        next_result = result

        while next_result:
            next_result = next_parser(tokens, result.pos)
            if next_result:
                result = next_result

        return result


@attr.s
class Alternate(Parser):
    left = attr.ib()
    right = attr.ib()

    def __call__(self, tokens, pos) -> Optional[Type[Parser]]:
        left_result = self.left(tokens, pos)
        if left_result is not None:
            return left_result
        return self.right(tokens, pos)


@attr.s
class Opt(Parser):
    parser = attr.ib()

    def __call__(self, tokens, pos) -> Union[Result, Type[Parser]]:
        result = self.parser(tokens, pos)
        if result is not None:
            return result
        return Result(None, pos)


@attr.s
class Rep(Parser):
    parser = attr.ib()

    def __call__(self, tokens, pos) -> Result:
        results = []
        result = self.parser(tokens, pos)

        while result is not None:
            results.append(result.value)
            pos = result.pos
            result = self.parser(tokens, pos)

        return Result(results, pos)


@attr.s
class Process(Parser):
    parser = attr.ib()
    function = attr.ib()

    def __call__(self, tokens, pos) -> Optional[Type[Parser]]:
        result = self.parser(tokens, pos)
        if result is not None:
            result.value = self.function(result.value)
        return result


@attr.s
class Lazy(Parser):
    parser_func = attr.ib()
    parser = None

    def __call__(self, tokens, pos) -> Type[Parser]:
        if not self.parser:
            self.parser = self.parser_func()
        return self.parser(tokens, pos)


@attr.s
class Phrase(Parser):
    parser = attr.ib()

    def __call__(self, tokens, pos) -> Optional[Type[Parser]]:
        result = self.parser(tokens, pos)

        if result is not None:
            if result.pos == len(tokens):
                return result

        return None