~kf5jwc/imp-parser

ref: 9a694fa0d8b73f01beb051b54cdab8a2b000be09 imp-parser/imp_parser/parser/combinators.py -rw-r--r-- 4.0 KiB View raw
9a694fa0 — Kyle Jones I've done more type annotating, which.... helped? 1 year, 6 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
from dataclasses import dataclass
from typing import Callable, Union, Optional, Any, Tuple
from .result import Result
from ..imp_lexer import Tags


class Parser(object):
    def __call__(self: "Parser", tokens: BaseException, pos: int) -> Any:
        raise NotImplementedError

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

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

    def __or__(self: "Parser", other: Union["Tag", "Parser"]) -> "Alternate":
        return Alternate(self, other)

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


@dataclass
class Reserved(Parser):
    value: str
    tag: Tags

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

        return None


@dataclass
class Tag(Parser):
    tag: Tags

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

        return None


@dataclass
class Concat(Parser):
    left: Parser
    right: Parser

    def __call__(self, tokens, pos) -> Optional[Any]:
        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)


@dataclass
class Exp(Parser):
    parser: Parser
    separator: "Process"

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

        result = self.parser(tokens, pos)
        next_parser: Process = 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


@dataclass
class Alternate(Parser):
    left: Parser
    right: Parser

    def __call__(self, tokens, pos) -> Any:
        left_result = self.left(tokens, pos)
        if left_result is not None:
            return left_result
        return self.right(tokens, pos)


@dataclass
class Opt(Parser):
    parser: Parser

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


@dataclass
class Rep(Parser):
    parser: Parser

    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)


@dataclass
class Process(Parser):
    parser: Parser
    function: Callable

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


@dataclass
class Lazy(Parser):
    parser_func: Callable[..., Parser]
    parser: Optional[Parser] = None

    def __call__(self, tokens, pos) -> Any:
        if not self.parser:
            self.parser = self.parser_func()
        return self.parser(tokens, pos)


@dataclass
class Phrase(Parser):
    parser: Parser

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

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

        return None