1/* 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 354