Add basic math
[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 FurAdditionExpression = collections.namedtuple(
48     'FurAdditionExpression',
49     [
50         'left',
51         'right',
52     ],
53 )
54
55 FurSubtractionExpression = collections.namedtuple(
56     'FurSubtractionExpression',
57     [
58         'left',
59         'right',
60     ],
61 )
62
63 FurMultiplicationExpression = collections.namedtuple(
64     'FurMultiplicationExpression',
65     [
66         'left',
67         'right',
68     ],
69 )
70
71 FurIntegerDivisionExpression = collections.namedtuple(
72     'FurIntegerDivisionExpression',
73     [
74         'left',
75         'right',
76     ],
77 )
78
79 FurModularDivisionExpression = collections.namedtuple(
80     'FurModularDivisionExpression',
81     [
82         'left',
83         'right',
84     ],
85 )
86
87 def _integer_literal_expression_parser(index, tokens):
88     failure = (False, index, None)
89
90     if tokens[index].type != 'integer_literal':
91         return failure
92     value = int(tokens[index].match)
93     index += 1
94
95     return True, index, FurIntegerLiteralExpression(value=value)
96
97 def _string_literal_expression_parser(index, tokens):
98     failure = (False, index, None)
99
100     if tokens[index].type != 'single_quoted_string_literal':
101         return failure
102     value = tokens[index].match[1:-1]
103     index += 1
104
105     return True, index, FurStringLiteralExpression(value=value)
106
107 def _literal_level_expression_parser(index, tokens):
108     return _or_parser(
109         _integer_literal_expression_parser,
110         _string_literal_expression_parser,
111     )(index, tokens)
112
113 def _multiplication_level_expression_parser(index, tokens):
114     failure = (False, index, None)
115
116     success, index, result = _literal_level_expression_parser(index, tokens)
117
118     if not success:
119         return failure
120
121     while success and index < len(tokens) and tokens[index].type == 'multiplication_level_operator':
122         success = False
123
124         if index + 1 < len(tokens):
125             success, try_index, value = _literal_level_expression_parser(index + 1, tokens)
126
127         if success:
128             result = {
129                 '*': FurMultiplicationExpression,
130                 '//': FurIntegerDivisionExpression,
131                 '%': FurModularDivisionExpression,
132             }[tokens[index].match](left=result, right=value)
133             index = try_index
134
135     return True, index, result
136
137 def _addition_level_expression_parser(index, tokens):
138     failure = (False, index, None)
139
140     success, index, result = _multiplication_level_expression_parser(index, tokens)
141
142     if not success:
143         return failure
144
145     while success and index < len(tokens) and tokens[index].type == 'addition_level_operator':
146         success = False
147
148         if index + 1 < len(tokens):
149             success, try_index, value = _multiplication_level_expression_parser(index + 1, tokens)
150
151         if success:
152             result = {
153                 '+': FurAdditionExpression,
154                 '-': FurSubtractionExpression,
155             }[tokens[index].match](left=result, right=value)
156             index = try_index
157
158     return True, index, result
159
160 FunctionCall = collections.namedtuple(
161     'FunctionCall',
162     [
163         'name',
164         'arguments',
165     ],
166 )
167
168 FurProgram = collections.namedtuple(
169     'FurProgram',
170     [
171         'statement_list',
172     ],
173 )
174
175 def _function_call_parser(index, tokens):
176     failure = (False, index, None)
177
178     if tokens[index].type != 'symbol':
179         return failure
180     name = tokens[index].match
181     index += 1
182
183     if tokens[index].type != 'open_parenthese':
184         return failure
185     index += 1
186
187     success, index, argument = _addition_level_expression_parser(index, tokens)
188
189     if not success:
190         return failure
191
192     if tokens[index].type != 'close_parenthese':
193         return failure
194     index += 1
195
196     return True, index, FunctionCall(name=name, arguments=(argument,))
197
198 def _program_formatter(statement_list):
199     return FurProgram(statement_list=statement_list)
200
201 _program_parser = _zero_or_more_parser(_program_formatter, _function_call_parser)
202
203 def _parse(parser, tokens):
204     success, index, result = parser(0, tokens)
205
206     if index < len(tokens):
207         raise Exception('Unable to parse token {}'.format(tokens[index]))
208
209     if success:
210         return result
211
212     raise Exception('Unable to parse')
213
214
215 def parse(tokens):
216     return _parse(_program_parser, tokens)
217
218 if __name__ == '__main__':
219     import unittest
220
221     import tokenization
222
223     class FurStringLiteralExpressionParserTests(unittest.TestCase):
224         def test_parses_single_quoted_string_literal(self):
225             self.assertEqual(
226                 _string_literal_expression_parser(0, tokenization.tokenize("'Hello, world'")),
227                 (
228                     True,
229                     1,
230                     FurStringLiteralExpression(value='Hello, world'),
231                 ),
232             )
233
234     class FunctionCallParserTests(unittest.TestCase):
235         def test_parses_function_with_string_literal_argument(self):
236             self.assertEqual(
237                 _function_call_parser(0, tokenization.tokenize("print('Hello, world')")),
238                 (
239                     True,
240                     4,
241                     FunctionCall(
242                         name='print',
243                         arguments=(FurStringLiteralExpression(value='Hello, world'),),
244                     ),
245                 ),
246             )
247
248     unittest.main()