Austin Schuh | 208337d | 2022-01-01 14:29:11 -0800 | [diff] [blame] | 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 | |
| 135 | file: |
| 136 | lines END { if (pioasm.error_count || pioasm.write_output()) YYABORT; } |
| 137 | ; |
| 138 | |
| 139 | lines: |
| 140 | line |
| 141 | | lines NEWLINE line; |
| 142 | |
| 143 | line: |
| 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 | |
| 154 | code_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; |
| 158 | label_decl: |
| 159 | symbol_def COLON { $1->is_label = true; $$ = $1; } |
| 160 | |
| 161 | directive: |
| 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; |
| 180 | value: 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; |
| 185 | expression: |
| 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; |
| 198 | instruction: |
| 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; |
| 206 | base_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; |
| 223 | delay: |
| 224 | LBRACKET expression RBRACKET { $$ = $2; } |
| 225 | |
| 226 | %type <std::shared_ptr<resolvable>> sideset; |
| 227 | sideset: |
| 228 | SIDE value { $$ = $2; } |
| 229 | |
| 230 | %type <enum condition> condition; |
| 231 | condition: |
| 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; |
| 242 | wait_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 | |
| 248 | comma: COMMA | %empty /* not a huge fan of forcing commas */ |
| 249 | |
| 250 | %type <enum in_out_set> in_source; |
| 251 | in_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; |
| 260 | out_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; |
| 270 | mov_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; |
| 279 | mov_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; |
| 288 | mov_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; |
| 294 | set_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; |
| 301 | if_full: |
| 302 | IFFULL { $$ = true; } |
| 303 | | %empty { $$ = false; } |
| 304 | |
| 305 | %type <bool> if_empty; |
| 306 | if_empty: |
| 307 | IFEMPTY { $$ = true; } |
| 308 | | %empty { $$ = false; } |
| 309 | |
| 310 | %type <bool> blocking; |
| 311 | blocking: |
| 312 | BLOCK { $$ = true; } |
| 313 | | NOBLOCK { $$ = false; } |
| 314 | | %empty { $$ = true; } |
| 315 | |
| 316 | %type <enum irq> irq_modifiers; |
| 317 | irq_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; |
| 325 | symbol_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 | %% |
| 331 | void 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 | |