Add a negation operator
[fur] / parsing.py
1 import collections
2
3 def _or_parser(*parsers):
4     def result_parser(index, tokens):
5         failure = (False, index, None)
6
7         for parser in parsers:
8             success, index, value = parser(index, tokens)
9
10             if success:
11                 return (success, index, value)
12
13         return failure
14
15     return result_parser
16
17 def _zero_or_more_parser(formatter, parser):
18     def result_parser(index, tokens):
19         values = []
20
21         while index < len(tokens):
22             success, index, value = parser(index, tokens)
23
24             if success:
25                 values.append(value)
26             else:
27                 break
28
29         return (True, index, formatter(values))
30
31     return result_parser
32
33 FurIntegerLiteralExpression = collections.namedtuple(
34     'FurIntegerLiteralExpression',
35     [
36         'value',
37     ],
38 )
39
40 FurStringLiteralExpression = collections.namedtuple(
41     'FurStringLiteralExpression',
42     [
43         'value',
44     ],
45 )
46
47 FurNegationExpression = collections.namedtuple(
48     'FurNegationExpression',
49     [
50         'value',
51     ],
52 )
53
54 FurAdditionExpression = collections.namedtuple(
55     'FurAdditionExpression',
56     [
57         'left',
58         'right',
59     ],
60 )
61
62 FurSubtractionExpression = collections.namedtuple(
63     'FurSubtractionExpression',
64     [
65         'left',
66         'right',
67     ],
68 )
69
70 FurMultiplicationExpression = collections.namedtuple(
71     'FurMultiplicationExpression',
72     [
73         'left',
74         'right',
75     ],
76 )
77
78 FurIntegerDivisionExpression = collections.namedtuple(
79     'FurIntegerDivisionExpression',
80     [
81         'left',
82         'right',
83     ],
84 )
85
86 FurModularDivisionExpression = collections.namedtuple(
87     'FurModularDivisionExpression',
88     [
89         'left',
90         'right',
91     ],
92 )
93
94 def _integer_literal_expression_parser(index, tokens):
95     failure = (False, index, None)
96
97     if tokens[index].type != 'integer_literal':
98         return failure
99     value = int(tokens[index].match)
100     index += 1
101
102     return True, index, FurIntegerLiteralExpression(value=value)
103
104 def _string_literal_expression_parser(index, tokens):
105     failure = (False, index, None)
106
107     if tokens[index].type != 'single_quoted_string_literal':
108         return failure
109     value = tokens[index].match[1:-1]
110     index += 1
111
112     return True, index, FurStringLiteralExpression(value=value)
113
114 def _negation_expression_parser(index, tokens):
115     failure = (False, index, None)
116
117     if tokens[index].match != '-':
118         return failure
119
120     success, index, value = _literal_level_expression_parser(index + 1, tokens)
121
122     if not success:
123         return failure
124
125     return (True, index, FurNegationExpression(value=value))
126
127 def _literal_level_expression_parser(index, tokens):
128     return _or_parser(
129         _negation_expression_parser,
130         _function_call_expression_parser,
131         _integer_literal_expression_parser,
132         _string_literal_expression_parser,
133     )(index, tokens)
134
135 def _multiplication_level_expression_parser(index, tokens):
136     failure = (False, index, None)
137
138     success, index, result = _literal_level_expression_parser(index, tokens)
139
140     if not success:
141         return failure
142
143     while success and index < len(tokens) and tokens[index].type == 'multiplication_level_operator':
144         success = False
145
146         if index + 1 < len(tokens):
147             success, try_index, value = _literal_level_expression_parser(index + 1, tokens)
148
149         if success:
150             result = {
151                 '*': FurMultiplicationExpression,
152                 '//': FurIntegerDivisionExpression,
153                 '%': FurModularDivisionExpression,
154             }[tokens[index].match](left=result, right=value)
155             index = try_index
156
157     return True, index, result
158
159 def _addition_level_expression_parser(index, tokens):
160     failure = (False, index, None)
161
162     success, index, result = _multiplication_level_expression_parser(index, tokens)
163
164     if not success:
165         return failure
166
167     while success and index < len(tokens) and tokens[index].type == 'addition_level_operator':
168         success = False
169
170         if index + 1 < len(tokens):
171             success, try_index, value = _multiplication_level_expression_parser(index + 1, tokens)
172
173         if success:
174             result = {
175                 '+': FurAdditionExpression,
176                 '-': FurSubtractionExpression,
177             }[tokens[index].match](left=result, right=value)
178             index = try_index
179
180     return True, index, result
181
182 def _comma_separated_list_parser(index, tokens):
183     failure = (False, index, None)
184
185     expressions = []
186
187     success, index, expression = _addition_level_expression_parser(index, tokens)
188
189     if success:
190         expressions.append(expression)
191     else:
192         return failure
193
194     while success and index < len(tokens) and tokens[index].type == 'comma':
195         success = False
196
197         if index + 1 < len(tokens):
198             success, try_index, expression = _addition_level_expression_parser(index + 1, tokens)
199
200         if success:
201             expressions.append(expression)
202             index = try_index
203
204     return True, index, tuple(expressions)
205
206
207 FurFunctionCallExpression = collections.namedtuple(
208     'FurFunctionCallExpression',
209     [
210         'name',
211         'arguments',
212     ],
213 )
214
215 FurProgram = collections.namedtuple(
216     'FurProgram',
217     [
218         'statement_list',
219     ],
220 )
221
222 def _function_call_expression_parser(index, tokens):
223     failure = (False, index, None)
224
225     if tokens[index].type != 'symbol':
226         return failure
227     name = tokens[index].match
228     index += 1
229
230     if tokens[index].type != 'open_parenthese':
231         return failure
232     index += 1
233
234     success, index, arguments = _comma_separated_list_parser(index, tokens)
235
236     if not success:
237         return failure
238
239     if tokens[index].type != 'close_parenthese':
240         raise Exception('Expected ")", found "{}" on line {}'.format(
241             tokens[index].match,
242             tokens[index].line,
243         ))
244     index += 1
245
246     return True, index, FurFunctionCallExpression(name=name, arguments=arguments)
247
248 def _program_formatter(statement_list):
249     return FurProgram(statement_list=statement_list)
250
251 _program_parser = _zero_or_more_parser(_program_formatter, _function_call_expression_parser)
252
253 def _parse(parser, tokens):
254     success, index, result = parser(0, tokens)
255
256     if index < len(tokens):
257         raise Exception('Unable to parse token {}'.format(tokens[index]))
258
259     if success:
260         return result
261
262     raise Exception('Unable to parse')
263
264 def parse(tokens):
265     return _parse(_program_parser, tokens)
266
267 if __name__ == '__main__':
268     import unittest
269
270     import tokenization
271
272     class FurStringLiteralExpressionParserTests(unittest.TestCase):
273         def test_parses_single_quoted_string_literal(self):
274             self.assertEqual(
275                 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
276                 (
277                     True,
278                     1,
279                     FurStringLiteralExpression(value='Hello, world'),
280                 ),
281             )
282
283     class FurFunctionCallExpressionParserTests(unittest.TestCase):
284         def test_parses_function_with_string_literal_argument(self):
285             self.assertEqual(
286                 _function_call_expression_parser(0, tokenization.tokenize("print('Hello, world')")),
287                 (
288                     True,
289                     4,
290                     FurFunctionCallExpression(
291                         name='print',
292                         arguments=(FurStringLiteralExpression(value='Hello, world'),),
293                     ),
294                 ),
295             )
296
297     unittest.main()