OR-1 dataflow CPU sketch
1"""Parser tests for the dataflow graph assembly grammar."""
2
3from textwrap import dedent
4
5import pytest
6from lark import exceptions, LarkError
7
8
9class TestInstDefs:
10 def test_basic_instructions(self, parser):
11 tree = parser.parse(dedent("""\
12 &my_add <| add
13 &my_sub <| sub
14 &my_const <| const, 10
15 &my_shift <| shl
16 &my_not <| not
17 """))
18 assert tree.data == "start"
19 assert len(tree.children) == 5
20 assert all(child.data == "inst_def" for child in tree.children)
21
22 def test_hex_const(self, parser):
23 tree = parser.parse(dedent("""\
24 &mask <| const, 0xFF
25 """))
26 assert tree.data == "start"
27 assert len(tree.children) == 1
28 assert tree.children[0].data == "inst_def"
29
30 def test_named_args(self, parser):
31 tree = parser.parse(dedent("""\
32 &serial <| ior, dest=0x45, addr=0x91, data=0x43
33 """))
34 assert tree.data == "start"
35 assert len(tree.children) == 1
36 assert tree.children[0].data == "inst_def"
37
38 def test_system_config(self, parser):
39 tree = parser.parse(dedent("""\
40 &loader <| load_inst, dest=0x01, addr=0x00, data_l=0xABCD, data_h=0x1234
41 """))
42 assert tree.data == "start"
43 assert len(tree.children) == 1
44 assert tree.children[0].data == "inst_def"
45
46
47class TestEdges:
48 def test_plain_edges(self, parser):
49 tree = parser.parse(dedent("""\
50 &a |> &b:L
51 &a |> &b:R
52 &c |> &d, &e
53 """))
54 assert tree.data == "start"
55 assert len(tree.children) == 3
56 assert all(child.data == "plain_edge" for child in tree.children)
57
58 def test_strong_inline_edge(self, parser):
59 tree = parser.parse(dedent("""\
60 add &a, &b |> &c, &d
61 """))
62 assert tree.data == "start"
63 assert len(tree.children) == 1
64 assert tree.children[0].data == "strong_edge"
65
66 def test_weak_inline_edge(self, parser):
67 tree = parser.parse(dedent("""\
68 &c, &d sub <| &a, &b
69 """))
70 assert tree.data == "start"
71 assert len(tree.children) == 1
72 assert tree.children[0].data == "weak_edge"
73
74 def test_fanout(self, parser):
75 tree = parser.parse(dedent("""\
76 &splitter <| pass
77 &input |> &splitter:L
78 &splitter |> &consumer_a:L, &consumer_b:R
79 """))
80 assert tree.data == "start"
81 assert len(tree.children) == 3
82 assert tree.children[0].data == "inst_def"
83 assert tree.children[1].data == "plain_edge"
84 assert tree.children[2].data == "plain_edge"
85
86
87class TestFunctions:
88 def test_fib_function(self, parser):
89 tree = parser.parse(dedent("""\
90 $fib |> {
91 &const_n <| const, 10
92 &sub1 <| sub
93 &sub2 <| sub
94 &branch <| sweq
95
96 &const_n |> &branch:L
97 &const_n |> &sub1:L
98 &const_n |> &sub1:R
99 &const_n |> &sub2:R
100 &sub1 |> &recurse_a:L
101 }
102 """))
103 assert tree.data == "start"
104 assert len(tree.children) == 1
105 assert tree.children[0].data == "func_def"
106
107
108class TestPlacement:
109 def test_pe_qualifiers(self, parser):
110 tree = parser.parse(dedent("""\
111 &my_add|pe0 <| add
112 &result|pe1 <| pass
113 &my_add|pe0 |> &result|pe1:L
114 """))
115 assert tree.data == "start"
116 assert len(tree.children) == 3
117 assert tree.children[0].data == "inst_def"
118 assert tree.children[1].data == "inst_def"
119 assert tree.children[2].data == "plain_edge"
120
121 def test_location_directive(self, parser):
122 tree = parser.parse(dedent("""\
123 @data_section|sm0:
124 """))
125 assert tree.data == "start"
126 assert len(tree.children) == 1
127 assert tree.children[0].data == "location_dir"
128
129 def test_location_directive_with_placement_and_colon(self, parser):
130 """AC6.1: Location directive with placement and trailing colon."""
131 tree = parser.parse(dedent("""\
132 @region|pe0:
133 """))
134 assert tree.data == "start"
135 assert len(tree.children) == 1
136 assert tree.children[0].data == "location_dir"
137
138 def test_node_ref_in_edge_context_no_colon(self, parser):
139 """AC6.2: @node without colon in edge context parses as node_ref."""
140 tree = parser.parse(dedent("""\
141 @src |> @dest:L
142 """))
143 assert tree.data == "start"
144 assert len(tree.children) == 1
145 assert tree.children[0].data == "plain_edge"
146 # Verify the edge has two node refs, not a location_dir
147
148 def test_location_directive_no_colon_produces_parse_error(self, parser):
149 """AC6.3: Location directive without trailing colon produces PARSE error."""
150 # The grammar now requires trailing colon for location_dir.
151 # A bare @ref without colon should produce a parse error.
152 with pytest.raises(LarkError):
153 parser.parse(dedent("""\
154 @data_section
155 &a <| add
156 """))
157
158
159class TestDataDefs:
160 def test_hex_data(self, parser):
161 tree = parser.parse(dedent("""\
162 @hello|sm0:0 = 0x05
163 @hello|sm0:1 = 'h', 'e'
164 @hello|sm0:2 = 'l', 'l'
165 """))
166 assert tree.data == "start"
167 assert len(tree.children) == 3
168 assert all(child.data == "data_def" for child in tree.children)
169
170 def test_macro_invocation(self, parser):
171 tree = parser.parse(dedent("""\
172 @hello = #str "hello"
173 """))
174 assert tree.data == "start"
175 assert len(tree.children) == 1
176 assert tree.children[0].data == "data_def"
177
178 def test_multi_line_string(self, parser):
179 tree = parser.parse(dedent('''\
180 @msg = "hello
181world"
182 '''))
183 assert tree.data == "start"
184 assert len(tree.children) == 1
185 assert tree.children[0].data == "data_def"
186
187 def test_raw_string(self, parser):
188 tree = parser.parse(dedent("""\
189 @path = r"no\\escapes\\here"
190 """))
191 assert tree.data == "start"
192 assert len(tree.children) == 1
193 assert tree.children[0].data == "data_def"
194
195 def test_byte_string(self, parser):
196 tree = parser.parse(dedent("""\
197 @raw_data = b"\\x01\\x02\\x03"
198 """))
199 assert tree.data == "start"
200 assert len(tree.children) == 1
201 assert tree.children[0].data == "data_def"
202
203
204class TestComments:
205 def test_inline_comments(self, parser):
206 tree = parser.parse(dedent("""\
207 &my_add <| add ; this is a comment
208 &a |> &b:L ; wire a to b left port
209 """))
210 assert tree.data == "start"
211 assert len(tree.children) == 2
212 assert tree.children[0].data == "inst_def"
213 assert tree.children[1].data == "plain_edge"
214
215
216class TestMixedPrograms:
217 def test_mixed_program(self, parser):
218 tree = parser.parse(dedent("""\
219 @counter|sm0:0 = 0x00
220
221 $main |> {
222 &init <| const, 0
223 &loop_add <| add
224 &cmp <| lte
225 &branch <| breq
226 &output <| iow, dest=0x01
227
228 &init |> &loop_add:L
229 &loop_add |> &cmp:L
230 &loop_add |> &output:L
231 }
232 """))
233 assert tree.data == "start"
234 assert len(tree.children) == 2
235 assert tree.children[0].data == "data_def"
236 assert tree.children[1].data == "func_def"
237
238
239class TestSMOps:
240 def test_read_op(self, parser):
241 tree = parser.parse(dedent("""\
242 &cell <| read
243 """))
244 assert tree.data == "start"
245 assert len(tree.children) == 1
246 assert tree.children[0].data == "inst_def"
247
248 def test_write_op(self, parser):
249 tree = parser.parse(dedent("""\
250 &cell <| write
251 """))
252 assert tree.data == "start"
253 assert len(tree.children) == 1
254 assert tree.children[0].data == "inst_def"
255
256 def test_clear_op(self, parser):
257 tree = parser.parse(dedent("""\
258 &cell <| clear
259 """))
260 assert tree.data == "start"
261 assert len(tree.children) == 1
262 assert tree.children[0].data == "inst_def"
263
264 def test_alloc_op(self, parser):
265 tree = parser.parse(dedent("""\
266 &cell <| alloc
267 """))
268 assert tree.data == "start"
269 assert len(tree.children) == 1
270 assert tree.children[0].data == "inst_def"
271
272 def test_free_op(self, parser):
273 tree = parser.parse(dedent("""\
274 &cell <| free
275 """))
276 assert tree.data == "start"
277 assert len(tree.children) == 1
278 assert tree.children[0].data == "inst_def"
279
280 def test_rd_inc_op(self, parser):
281 tree = parser.parse(dedent("""\
282 &cell <| rd_inc
283 """))
284 assert tree.data == "start"
285 assert len(tree.children) == 1
286 assert tree.children[0].data == "inst_def"
287
288 def test_rd_dec_op(self, parser):
289 tree = parser.parse(dedent("""\
290 &cell <| rd_dec
291 """))
292 assert tree.data == "start"
293 assert len(tree.children) == 1
294 assert tree.children[0].data == "inst_def"
295
296 def test_cmp_sw_op(self, parser):
297 tree = parser.parse(dedent("""\
298 &cell <| cmp_sw
299 """))
300 assert tree.data == "start"
301 assert len(tree.children) == 1
302 assert tree.children[0].data == "inst_def"
303
304
305class TestSystemPragma:
306 def test_system_pragma_minimal(self, parser):
307 tree = parser.parse(dedent("""\
308 @system pe=4, sm=1
309 """))
310 assert tree.data == "start"
311 assert len(tree.children) == 1
312 assert tree.children[0].data == "system_pragma"
313
314 def test_system_pragma_full(self, parser):
315 tree = parser.parse(dedent("""\
316 @system pe=2, sm=1, iram=128, ctx=2
317 """))
318 assert tree.data == "start"
319 assert len(tree.children) == 1
320 assert tree.children[0].data == "system_pragma"
321
322 def test_system_pragma_with_hex(self, parser):
323 tree = parser.parse(dedent("""\
324 @system pe=0x04, sm=0x01
325 """))
326 assert tree.data == "start"
327 assert len(tree.children) == 1
328 assert tree.children[0].data == "system_pragma"
329
330
331class TestUnknownOpcodes:
332 def test_unknown_opcode_fails(self, parser):
333 with pytest.raises((exceptions.UnexpectedToken, exceptions.UnexpectedCharacters)):
334 parser.parse(dedent("""\
335 &unknown <| foobar
336 """))