aboutsummaryrefslogtreecommitdiff
path: root/yapymake/makefile/parse_util.py
blob: ad1e36324d748dbbdee2c7ff88146743bf0a7dc6 (plain)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
from typing import Callable, List, Optional, Tuple, TypeVar

__all__ = [
    'ParseResult',
    'Parser',
    'alt',
    'tag',
    'take_till1',
    'take_while1',
    'any_char',
    'all_consuming',
    'map_parser',
    'opt',
    'verify',
    'many1',
    'delimited',
    'pair',
    'preceded',
    'separated_pair',
]

# I had a really nice (well, not really, but it worked) implementation of this with Rust's `nom`,
# but then I surrendered to the borrow checker, so now I have to redo that work in a more Pythonic
# way. So I'm reimplementing the nom pieces I used in Python, because fuck it.

T = TypeVar('T')
T1 = TypeVar('T1')
T2 = TypeVar('T2')

ParseResult = Optional[Tuple[T, str]]
Parser = Callable[[str], ParseResult[T]]

def alt(*parsers: Parser[T]) -> Parser[T]:
    def parse(text: str) -> ParseResult[T]:
        for parser in parsers[:-1]:
            result = parser(text)
            if result is not None:
                return result
        return parsers[-1](text)
    return parse

def tag(tag_text: str) -> Parser[None]:
    def parse(text: str) -> ParseResult[None]:
        if text.startswith(tag_text):
            return None, text[len(tag_text):]
    return parse

def take_while1(predicate: Callable[[str], bool]) -> Parser[str]:
    def parse(text: str) -> ParseResult[str]:
        if len(text) == 0 or not predicate(text[0]):
            return None
        for i in range(1, len(text)):
            if not predicate(text[i]):
                return text[:i], text[i:]
        return text, ""
    return parse

def take_till1(predicate: Callable[[str], bool]) -> Parser[str]:
    return take_while1(lambda x: not predicate(x))

def any_char(text: str) -> ParseResult[str]:
    if len(text) > 0:
        return text[0], text[1:]

def all_consuming(parser: Parser[T]) -> Parser[T]:
    def parse(text: str) -> ParseResult[T]:
        result = parser(text)
        if result is None:
            return None
        result, extra = result
        if len(extra) > 0:
            return None
        return result, ''
    return parse

def map_parser(parser: Parser[T1], mapper: Callable[[T1], T2]) -> Parser[T2]:
    def parse(text: str) -> ParseResult[T2]:
        result = parser(text)
        if result is None:
            return None
        result, extra = result
        return mapper(result), extra
    return parse

def opt(parser: Parser[T]) -> Parser[Optional[T]]:
    def parse(text: str) -> ParseResult[Optional[T]]:
        result = parser(text)
        if result is None:
            return None, text
        return result
    return parse

def verify(parser: Parser[T], predicate: Callable[[T], bool]) -> Parser[T]:
    def parse(text: str) -> ParseResult[T]:
        result = parser(text)
        if result is None:
            return None
        result, extra = result
        if predicate(result):
            return result, extra
        return None
    return parse

def many1(parser: Parser[T]) -> Parser[List[T]]:
    def parse(text: str) -> ParseResult[List[T]]:
        parser_result = parser(text)
        if parser_result is None:
            return None
        parser_result, extra = parser_result
        result = [parser_result]

        parser_result = parser(extra)
        while parser_result is not None:
            parser_result, extra = parser_result
            result.append(parser_result)
            parser_result = parser(extra)
        return result, extra
    return parse

def delimited(before_parser: Parser[T1], parser: Parser[T], after_parser: Parser[T2]) -> Parser[T]:
    def parse(text: str) -> ParseResult[T]:
        before_result = before_parser(text)
        if before_result is None:
            return None
        _, extra = before_result

        result = parser(extra)
        if result is None:
            return None
        result, extra = result

        after_result = after_parser(extra)
        if after_result is None:
            return None
        _, extra = after_result

        return result, extra
    return parse

def pair(first_parser: Parser[T1], second_parser: Parser[T2]) -> Parser[Tuple[T1, T2]]:
    def parse(text: str) -> ParseResult[Tuple[T1, T2]]:
        first_result = first_parser(text)
        if first_result is None:
            return None
        first_result, extra = first_result

        second_result = second_parser(extra)
        if second_result is None:
            return None
        second_result, extra = second_result

        return (first_result, second_result), extra
    return parse

def preceded(before_parser: Parser[T1], parser: Parser[T]) -> Parser[T]:
    def parse(text: str) -> ParseResult[T]:
        before_result = before_parser(text)
        if before_result is None:
            return None
        _, extra = before_result

        result = parser(extra)
        if result is None:
            return None
        result, extra = result

        return result, extra
    return parse

def separated_pair(first_parser: Parser[T1], between_parser: Parser[T], second_parser: Parser[T2]) -> Parser[Tuple[T1, T2]]:
    def parse(text: str) -> ParseResult[Tuple[T1, T2]]:
        first_result = first_parser(text)
        if first_result is None:
            return None
        first_result, extra = first_result

        between_result = between_parser(extra)
        if between_result is None:
            return None
        _, extra = between_result

        second_result = second_parser(extra)
        if second_result is None:
            return None
        second_result, extra = second_result

        return (first_result, second_result), extra
    return parse