blob: 467ca890f170a4254859bd96229e57d026e88df1 [file] [log] [blame]
Austin Schuh208337d2022-01-01 14:29:11 -08001/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7%skeleton "lalr1.cc" /* -*- C++ -*- */
8%require "3.4.2"
9%defines
10
11%define api.token.constructor
12%define api.value.type variant
13/*%define parse.assert*/
14%define api.location.file "location.h"
15%define parse.lac full
16/* define parse.trace*/
17%define parse.error verbose
18%no-lines
19%locations
20
21%code requires {
22 #include <string>
23 #include <fstream>
24 #include <sstream>
25 #include "pio_types.h"
26 struct pio_assembler;
27
28 #ifdef _MSC_VER
29 #pragma warning(disable : 4065) // default only switch statement
30 #endif
31}
32
33// The parsing context.
34%param { pio_assembler& pioasm }
35
36%code {
37 #include "pio_assembler.h"
38 #ifdef _MSC_VER
39 #pragma warning(disable : 4244) // possible loss of data (valid warning, but there is a software check / missing cast)
40 #endif
41}
42
43%define api.token.prefix {TOK_}
44
45%token
46 END 0 "end of file"
47
48 NEWLINE "end of line"
49 COMMA ","
50 COLON ":"
51
52 LPAREN "("
53 RPAREN ")"
54 LBRACKET "["
55 RBRACKET "]"
56 PLUS "+"
57 MINUS "-"
58 MULTIPLY "*"
59 DIVIDE "/"
60 OR "|"
61 AND "&"
62 XOR "^"
63 POST_DECREMENT "--"
64 NOT_EQUAL "!="
65 NOT "!"
66 REVERSE "::"
67 EQUAL "="
68
69 PROGRAM ".program"
70 WRAP_TARGET ".wrap_target"
71 WRAP ".wrap"
72 DEFINE ".define"
73 SIDE_SET ".side_set"
74 WORD ".word"
75 ORIGIN ".origin"
76 LANG_OPT ".lang_opt"
77
78 JMP "jmp"
79 WAIT "wait"
80 IN "in"
81 OUT "out"
82 PUSH "push"
83 PULL "pull"
84 MOV "mov"
85 IRQ "irq"
86 SET "set"
87 NOP "nop"
88
89 PIN "pin"
90 GPIO "gpio"
91 OSRE "osre"
92
93 PINS "pins"
94 NULL "null"
95 PINDIRS "pindirs"
96 BLOCK "block"
97 NOBLOCK "noblock"
98 IFEMPTY "ifempty"
99 IFFULL "iffull"
100 NOWAIT "nowait"
101 CLEAR "clear"
102 REL "rel"
103 X "x"
104 Y "y"
105 EXEC "exec"
106 PC "pc"
107 ISR "isr"
108 OSR "osr"
109 OPTIONAL "opt"
110 SIDE "side"
111 STATUS "status"
112 PUBLIC "public"
113;
114
115%token
116 <std::string> ID "identifier"
117 <std::string> STRING "string"
118 <std::string> NON_WS "text"
119 <std::string> CODE_BLOCK_START "code block"
120 <std::string> CODE_BLOCK_CONTENTS "%}" // bit ugly but if there is no end this is what we will be missing
121 <std::string> UNKNOWN_DIRECTIVE
122 <int> INT "integer"
123;
124
125
126%left REVERSE
127%left PLUS MINUS
128%left MULTIPLY DIVIDE
129%left AND OR XOR
130
131%printer { yyo << "..."; } <*>;
132
133%%
134
135file:
136 lines END { if (pioasm.error_count || pioasm.write_output()) YYABORT; }
137 ;
138
139lines:
140 line
141 | lines NEWLINE line;
142
143line:
144 PROGRAM ID { if (!pioasm.add_program(@$, $2)) { std::stringstream msg; msg << "program " << $2 << " already exists"; error(@$, msg.str()); abort(); } }
145 | directive
146 | instruction { pioasm.get_current_program(@1, "instruction").add_instruction($1); }
147 | label_decl instruction { auto &p = pioasm.get_current_program(@2, "instruction"); p.add_label($1); p.add_instruction($2); }
148 | label_decl { pioasm.get_current_program(@1, "label").add_label($1); }
149 | code_block
150 | %empty
151 | error { if (pioasm.error_count > 6) { std::cerr << "\ntoo many errors; aborting.\n"; YYABORT; } }
152 ;
153
154code_block:
155 CODE_BLOCK_START CODE_BLOCK_CONTENTS { std::string of = $1; if (of.empty()) of = output_format::default_name; pioasm.get_current_program(@$, "code block", false, false).add_code_block( code_block(@$, of, $2)); }
156
157%type <std::shared_ptr<symbol>> label_decl;
158label_decl:
159 symbol_def COLON { $1->is_label = true; $$ = $1; }
160
161directive:
162 DEFINE symbol_def expression { $2->is_label = false; $2->value = $3; pioasm.get_current_program(@1, ".define", false, false).add_symbol($2); }
163 | ORIGIN value { pioasm.get_current_program(@1, ".origin", true).set_origin(@$, $2); }
164 | SIDE_SET value OPTIONAL PINDIRS { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, true, true); }
165 | SIDE_SET value OPTIONAL { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, true, false); }
166 | SIDE_SET value PINDIRS { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, false, true); }
167 | SIDE_SET value { pioasm.get_current_program(@1, ".side_set", true).set_sideset(@$, $2, false, false); }
168 | WRAP_TARGET { pioasm.get_current_program(@1, ".wrap_target").set_wrap_target(@$); }
169 | WRAP { pioasm.get_current_program(@1, ".wrap").set_wrap(@$); }
170 | WORD value { pioasm.get_current_program(@1, "instruction").add_instruction(std::shared_ptr<instruction>(new instr_word(@$, $2))); }
171 | LANG_OPT NON_WS NON_WS EQUAL INT { pioasm.get_current_program(@1, ".lang_opt").add_lang_opt($2, $3, std::to_string($5)); }
172 | LANG_OPT NON_WS NON_WS EQUAL STRING { pioasm.get_current_program(@1, ".lang_opt").add_lang_opt($2, $3, $5); }
173 | LANG_OPT NON_WS NON_WS EQUAL NON_WS { pioasm.get_current_program(@1, ".lang_opt").add_lang_opt($2, $3, $5); }
174 | LANG_OPT error { error(@$, "expected format is .lang_opt language option_name = option_value"); }
175 | UNKNOWN_DIRECTIVE { std::stringstream msg; msg << "unknown directive " << $1; throw syntax_error(@$, msg.str()); }
176 ;
177
178/* value is a more limited top level expression... requiring parenthesis */
179%type <std::shared_ptr<resolvable>> value;
180value: INT { $$ = resolvable_int(@$, $1); }
181 | ID { $$ = std::shared_ptr<resolvable>(new name_ref(@$, $1)); }
182 | LPAREN expression RPAREN { $$ = $2; }
183
184%type <std::shared_ptr<resolvable>> expression;
185expression:
186 value
187 | expression PLUS expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::add, $1, $3)); }
188 | expression MINUS expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::subtract, $1, $3)); }
189 | expression MULTIPLY expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::multiply, $1, $3)); }
190 | expression DIVIDE expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::divide, $1, $3)); }
191 | expression OR expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::or_, $1, $3)); }
192 | expression AND expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::and_, $1, $3)); }
193 | expression XOR expression { $$ = std::shared_ptr<binary_operation>(new binary_operation(@$, binary_operation::xor_, $1, $3)); }
194 | MINUS expression { $$ = std::shared_ptr<unary_operation>(new unary_operation(@$, unary_operation::negate, $2)); }
195 | REVERSE expression { $$ = std::shared_ptr<unary_operation>(new unary_operation(@$, unary_operation::reverse, $2)); }
196
197%type <std::shared_ptr<instruction>> instruction;
198instruction:
199 base_instruction sideset delay { $$ = $1; $$->sideset = $2; $$->delay = $3; }
200 | base_instruction delay sideset { $$ = $1; $$->delay = $2; $$->sideset = $3; }
201 | base_instruction sideset { $$ = $1; $$->sideset = $2; $$->delay = resolvable_int(@$, 0); }
202 | base_instruction delay { $$ = $1; $$->delay = $2; }
203 | base_instruction { $$ = $1; $$->delay = resolvable_int(@$, 0); }
204
205%type <std::shared_ptr<instruction>> base_instruction;
206base_instruction:
207 NOP { $$ = std::shared_ptr<instruction>(new instr_nop(@$)); }
208 | JMP condition comma expression { $$ = std::shared_ptr<instruction>(new instr_jmp(@$, $2, $4)); }
209 | WAIT value wait_source { $$ = std::shared_ptr<instruction>(new instr_wait(@$, $2, $3)); }
210 | WAIT value COMMA value { std::stringstream msg; location l; l.begin = @2.end; l.end = @3.end; msg << "expected irq, gpio or pin after the polarity value and before the \",\""; throw yy::parser::syntax_error(l, msg.str()); }
211 | WAIT wait_source { $$ = std::shared_ptr<instruction>(new instr_wait(@$, resolvable_int(@$, 1), $2)); }
212 | IN in_source comma value { $$ = std::shared_ptr<instruction>(new instr_in(@$, $2, $4)); }
213 | OUT out_target comma value { $$ = std::shared_ptr<instruction>(new instr_out(@$, $2, $4)); }
214 | PUSH if_full blocking { $$ = std::shared_ptr<instruction>(new instr_push(@$, $2, $3)); }
215 | PULL if_empty blocking { $$ = std::shared_ptr<instruction>(new instr_pull(@$, $2, $3)); }
216 | MOV mov_target comma mov_op mov_source { $$ = std::shared_ptr<instruction>(new instr_mov(@$, $2, $5, $4)); }
217 | IRQ irq_modifiers value REL { $$ = std::shared_ptr<instruction>(new instr_irq(@$, $2, $3, true)); }
218 | IRQ irq_modifiers value { $$ = std::shared_ptr<instruction>(new instr_irq(@$, $2, $3)); }
219 | SET set_target comma value { $$ = std::shared_ptr<instruction>(new instr_set(@$, $2, $4)); }
220;
221
222%type <std::shared_ptr<resolvable>> delay;
223delay:
224 LBRACKET expression RBRACKET { $$ = $2; }
225
226%type <std::shared_ptr<resolvable>> sideset;
227sideset:
228 SIDE value { $$ = $2; }
229
230%type <enum condition> condition;
231condition:
232 NOT X { $$ = condition::xz; }
233 | X POST_DECREMENT { $$ = condition::xnz__; }
234 | NOT Y { $$ = condition::yz; }
235 | Y POST_DECREMENT { $$ = condition::ynz__; }
236 | X NOT_EQUAL Y { $$ = condition::xney; }
237 | PIN { $$ = condition::pin; }
238 | NOT OSRE { $$ = condition::osrez; }
239 | %empty { $$ = condition::al; }
240
241%type <std::shared_ptr<wait_source>> wait_source;
242wait_source:
243 IRQ comma value REL { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::irq, $3, true)); }
244 | IRQ comma value { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::irq, $3, false)); }
245 | GPIO comma value { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::gpio, $3)); }
246 | PIN comma value { $$ = std::shared_ptr<wait_source>(new wait_source(wait_source::pin, $3)); }
247
248comma: COMMA | %empty /* not a huge fan of forcing commas */
249
250%type <enum in_out_set> in_source;
251in_source: PINS { $$ = in_out_set::in_out_set_pins; }
252 | X { $$ = in_out_set::in_out_set_x; }
253 | Y { $$ = in_out_set::in_out_set_y; }
254 | NULL { $$ = in_out_set::in_out_null; }
255 | ISR { $$ = in_out_set::in_out_isr; }
256 | OSR { $$ = in_out_set::in_osr; }
257 | STATUS { $$ = in_out_set::in_status; }
258
259%type <enum in_out_set> out_target;
260out_target: PINS { $$ = in_out_set::in_out_set_pins; }
261 | X { $$ = in_out_set::in_out_set_x; }
262 | Y { $$ = in_out_set::in_out_set_y; }
263 | NULL { $$ = in_out_set::in_out_null; }
264 | PINDIRS { $$ = in_out_set::in_out_set_pindirs; }
265 | ISR { $$ = in_out_set::in_out_isr; }
266 | PC { $$ = in_out_set::out_set_pc; }
267 | EXEC { $$ = in_out_set::out_exec; }
268
269%type <enum mov> mov_target;
270mov_target: PINS { $$ = mov::pins; }
271 | X { $$ = mov::x; }
272 | Y { $$ = mov::y; }
273 | EXEC { $$ = mov::exec; }
274 | PC { $$ = mov::pc; }
275 | ISR { $$ = mov::isr; }
276 | OSR { $$ = mov::osr; }
277
278%type <enum mov> mov_source;
279mov_source: PINS { $$ = mov::pins; }
280 | X { $$ = mov::x; }
281 | Y { $$ = mov::y; }
282 | NULL { $$ = mov::null; }
283 | STATUS { $$ = mov::status; }
284 | ISR { $$ = mov::isr; }
285 | OSR { $$ = mov::osr; }
286
287%type <enum mov_op> mov_op;
288mov_op:
289 NOT { $$ = mov_op::invert; }
290 | REVERSE { $$ = mov_op::bit_reverse; }
291 | %empty { $$ = mov_op::none; }
292
293%type <enum in_out_set> set_target;
294set_target:
295 PINS { $$ = in_out_set::in_out_set_pins; }
296 | X { $$ = in_out_set::in_out_set_x; }
297 | Y { $$ = in_out_set::in_out_set_y; }
298 | PINDIRS { $$ = in_out_set::in_out_set_pindirs; }
299
300%type <bool> if_full;
301if_full:
302 IFFULL { $$ = true; }
303 | %empty { $$ = false; }
304
305%type <bool> if_empty;
306if_empty:
307 IFEMPTY { $$ = true; }
308 | %empty { $$ = false; }
309
310%type <bool> blocking;
311blocking:
312 BLOCK { $$ = true; }
313 | NOBLOCK { $$ = false; }
314 | %empty { $$ = true; }
315
316%type <enum irq> irq_modifiers;
317irq_modifiers:
318 CLEAR { $$ = irq::clear; }
319 | WAIT { $$ = irq::set_wait; }
320 | NOWAIT { $$ = irq::set; }
321 | SET { $$ = irq::set; }
322 | %empty { $$ = irq::set; }
323
324%type <std::shared_ptr<symbol>> symbol_def;
325symbol_def:
326 ID { $$ = std::shared_ptr<symbol>(new symbol(@$, $1)); }
327 | PUBLIC ID { $$ = std::shared_ptr<symbol>(new symbol(@$, $2, true)); }
328 | MULTIPLY ID { $$ = std::shared_ptr<symbol>(new symbol(@$, $2, true)); }
329
330%%
331void yy::parser::error(const location_type& l, const std::string& m)
332{
333 if (l.begin.filename) {
334 std::cerr << l << ": " << m << '\n';
335 pioasm.error_count++;
336 if (l.begin.line == l.end.line && *l.begin.filename == *l.end.filename) {
337 std::ifstream file(l.begin.filename->c_str());
338 std::string line;
339 for(int i = 0; i < l.begin.line; ++i) {
340 std::getline(file, line);
341 }
342 fprintf(stderr, "%5d | %s\n", l.begin.line, line.c_str());
343 fprintf(stderr, "%5s | %*s", "", l.begin.column, "^");
344 for (int i = l.begin.column; i < l.end.column - 1; i++) {
345 putc ('~', stderr);
346 }
347 putc ('\n', stderr);
348 }
349 } else {
350 std::cerr << m << '\n';
351 }
352}
353