diff options
-rw-r--r-- | bench/bench_window.cpp | 15 | ||||
-rw-r--r-- | include/lib94/lib94.hpp | 7 | ||||
-rw-r--r-- | lib94/warrior.cpp | 929 | ||||
-rw-r--r-- | makefile | 2 | ||||
-rw-r--r-- | score/main.cpp | 21 | ||||
-rw-r--r-- | tabulator-mpi/main.cpp | 10 | ||||
-rw-r--r-- | warriors/big-nothing.red | 12 | ||||
-rw-r--r-- | warriors/epson.red | 22 |
8 files changed, 617 insertions, 401 deletions
diff --git a/bench/bench_window.cpp b/bench/bench_window.cpp index d872de7..767198b 100644 --- a/bench/bench_window.cpp +++ b/bench/bench_window.cpp @@ -223,22 +223,19 @@ void bench_window::on_add_warrior_dialog_response(int response_id, Gtk::FileChoo gsize length; file->load_contents(contents, length); - auto w = lib94::compile_warrior(std::string(contents, length)); - - delete contents; - - if (std::holds_alternative<lib94::warrior *>(w)) { - warriors.push_back(std::get<lib94::warrior *>(w)); + try { + warriors.push_back(lib94::compile_warrior(std::string(contents, length))); on_click_new_round(); } - - else { - Gtk::MessageDialog *md = new Gtk::MessageDialog(std::string("Failed to compile: ") + std::get<std::string>(w)); + catch (const lib94::compiler_exception &ex) { + Gtk::MessageDialog *md = new Gtk::MessageDialog(std::string("failed to compile: ") + ex.message + " on line " + std::to_string(ex.line_number)); md->set_transient_for(*this); md->set_modal(); md->signal_response().connect([md](int) {delete md;}); md->show(); } + + delete contents; } delete dialog; diff --git a/include/lib94/lib94.hpp b/include/lib94/lib94.hpp index 3ca0c7b..eeb53ec 100644 --- a/include/lib94/lib94.hpp +++ b/include/lib94/lib94.hpp @@ -59,7 +59,12 @@ namespace lib94 { std::string instruction_to_string(const instruction &instr); - std::variant<warrior *, std::string> compile_warrior(std::string source); + struct compiler_exception : public std::exception { + unsigned line_number; + std::string message; + }; + + warrior *compile_warrior(std::string source); bool save_warrior(const warrior &w, const std::filesystem::path &to); std::optional<warrior *> load_warrior(const std::filesystem::path &from); diff --git a/lib94/warrior.cpp b/lib94/warrior.cpp index 47c9b02..adcf34e 100644 --- a/lib94/warrior.cpp +++ b/lib94/warrior.cpp @@ -1,5 +1,6 @@ #include <lib94/lib94.hpp> #include <functional> +#include <cassert> #include <fstream> #include <cctype> #include <cstdio> @@ -26,65 +27,68 @@ namespace lib94 { mode_chars[instr.bmode] + std::to_string(instr.bnumber); } - number_t real_mod(number_t input) { + static number_t real_mod(number_t input) { return (input % LIB94_CORE_SIZE + LIB94_CORE_SIZE) % LIB94_CORE_SIZE; } + typedef std::map<std::string, number_t> label_set; + typedef std::map<std::string, number_t> inline_macro_set; + + struct number_expr_exception : public std::exception { + std::string message; + }; + struct number_expr { - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, const std::map<std::string, number_t> ¯o_values, number_t this_offset) = 0; + virtual number_t to_number(number_t our_offset, const inline_macro_set &inline_macros, const label_set &labels) = 0; }; - struct label_number_expr : public number_expr { - std::string label; + struct identifier_number_expr : public number_expr { + std::string identifier; - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, const std::map<std::string, number_t> ¯o_values, number_t this_offset) { - auto result = label_offsets.find(label); - if (result != label_offsets.end()) - return real_mod(result->second - this_offset); - result = macro_values.find(label); - if (result != macro_values.end()) + virtual number_t to_number(number_t our_offset, const inline_macro_set &inline_macros, const label_set &labels) { + auto result = inline_macros.find(identifier); + if (result != inline_macros.end()) return result->second; - return {}; + + result = labels.find(identifier); + if (result != labels.end()) + return real_mod(result->second - our_offset); + + number_expr_exception ex; + ex.message = "unknown label or inline macro"; + throw ex; } }; struct number_number_expr : public number_expr { number_t value; - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &, const std::map<std::string, number_t> &, number_t) { - return real_mod(value); + virtual number_t to_number(number_t, const inline_macro_set &, const label_set &) { + return value; } }; struct op_number_expr : public number_expr { std::unique_ptr<number_expr> left; std::unique_ptr<number_expr> right; - std::function<std::optional<number_t> (number_t, number_t)> op; - - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, const std::map<std::string, number_t> ¯o_values, number_t this_offset) { - std::optional<number_t> left_result = left->to_number(label_offsets, macro_values, this_offset); - std::optional<number_t> right_result = right->to_number(label_offsets, macro_values, this_offset); - if (left_result.has_value() && right_result.has_value()) { - std::optional<number_t> op_result = op(left_result.value(), right_result.value()); - if (op_result.has_value()) - return real_mod(op_result.value()); - } - return {}; + std::function<number_t (number_t, number_t)> op; + + virtual number_t to_number(number_t our_offset, const inline_macro_set &inline_macros, const label_set &labels) { + number_t left_result = left->to_number(our_offset, inline_macros, labels); + number_t right_result = right->to_number(our_offset, inline_macros, labels); + return op(left_result, right_result); } }; struct negative_number_expr : public number_expr { std::unique_ptr<number_expr> inner; - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, const std::map<std::string, number_t> ¯o_values, number_t this_offset) { - std::optional<number_t> inner_result = inner->to_number(label_offsets, macro_values, this_offset); - if (inner_result.has_value()) - return LIB94_CORE_SIZE - inner_result.value(); - return {}; + virtual number_t to_number(number_t our_offset, const inline_macro_set &inline_macros, const label_set &labels) { + return LIB94_CORE_SIZE - inner->to_number(our_offset, inline_macros, labels); } }; - bool valid_label(std::string candidate) { + static bool valid_identifier(std::string candidate) { if (candidate == "") return false; @@ -98,7 +102,7 @@ namespace lib94 { return true; } - size_t find_respecting_parentheses(std::string part, std::string candidates) { + static size_t find_respecting_parentheses(std::string part, std::string candidates) { size_t layers = 0; for (int i = part.size() - 1; i >= 0; --i) @@ -125,41 +129,67 @@ namespace lib94 { return std::string::npos; } - std::optional<std::unique_ptr<number_expr>> to_number_expr(std::string part); + static std::unique_ptr<number_expr> to_number_expr(std::string part); - std::optional<std::unique_ptr<number_expr>> make_op_expr(std::string part, size_t split) { + static std::unique_ptr<number_expr> make_op_expr(std::string part, size_t split) { std::string left = part.substr(0, split); std::string right = part.substr(split + 1); - std::optional<std::unique_ptr<number_expr>> left_expr = to_number_expr(left); - std::optional<std::unique_ptr<number_expr>> right_expr = to_number_expr(right); - if (!left_expr.has_value() || !right_expr.has_value()) - return {}; + auto left_expr = to_number_expr(left); + auto right_expr = to_number_expr(right); auto expr = std::make_unique<op_number_expr>(); - expr->left = std::move(left_expr.value()); - expr->right = std::move(right_expr.value()); + expr->left = std::move(left_expr); + expr->right = std::move(right_expr); switch (part[split]) { - case '+': expr->op = [](number_t a, number_t b) {return a + b;}; break; - case '-': expr->op = [](number_t a, number_t b) {return a - b;}; break; - case '*': expr->op = [](number_t a, number_t b) {return a * b;}; break; - case '/': expr->op = [](number_t a, number_t b) -> std::optional<number_t> { - if (b == 0) - return {}; - return a / b; - }; break; - case '%': expr->op = [](number_t a, number_t b) -> std::optional<number_t> { - if (b == 0) - return {}; - return a % b; - }; break; + + case '+': + expr->op = [](number_t a, number_t b) { + return real_mod(a + b); + }; + break; + + case '-': + expr->op = [](number_t a, number_t b) { + return real_mod(a - b); + }; + break; + + case '*': + expr->op = [](number_t a, number_t b) { + return real_mod(a * b); + }; + break; + + case '/': + expr->op = [](number_t a, number_t b) { + if (b == 0) { + number_expr_exception ex; + ex.message = "division by zero"; + throw ex; + } + return a / b; + }; + break; + + case '%': + expr->op = [](number_t a, number_t b) { + if (b == 0) { + number_expr_exception ex; + ex.message = "modulo by zero"; + throw ex; + } + return a % b; + }; + break; + } return expr; } - std::string trim_spaces(std::string str) { + static std::string trim_spaces(std::string str) { size_t start = str.find_first_not_of(' '); size_t end = str.find_last_not_of(' ') + 1; if (start == std::string::npos) @@ -167,10 +197,13 @@ namespace lib94 { return str.substr(start, end - start); } - std::optional<std::unique_ptr<number_expr>> to_number_expr(std::string part) { + static std::unique_ptr<number_expr> to_number_expr(std::string part) { part = trim_spaces(part); - if (part == "") - return {}; + if (part == "") { + number_expr_exception ex; + ex.message = "empty expression"; + throw ex; + } size_t split = find_respecting_parentheses(part, "+-"); if (split == std::string::npos) @@ -185,32 +218,37 @@ namespace lib94 { return to_number_expr(part.substr(1)); if (part[0] == '-') { - std::optional<std::unique_ptr<number_expr>> inner = to_number_expr(part.substr(1)); - if (!inner.has_value()) - return {}; + std::unique_ptr<number_expr> inner = to_number_expr(part.substr(1)); std::unique_ptr<negative_number_expr> expr = std::make_unique<negative_number_expr>(); - expr->inner = std::move(inner.value()); + expr->inner = std::move(inner); return expr; } + size_t count; + number_t number = 0; + try { - size_t count; - number_t number = std::stoul(part, &count); - if (count == part.size()) { - std::unique_ptr<number_number_expr> expr = std::make_unique<number_number_expr>(); - expr->value = number; - return expr; - } + number = real_mod(std::stoul(part, &count)); + } + catch (const std::exception &e) { + count = 0; + } + + if (count == part.size()) { + std::unique_ptr<number_number_expr> expr = std::make_unique<number_number_expr>(); + expr->value = number; + return expr; } - catch (const std::exception &e) {} - if (valid_label(part)) { - std::unique_ptr<label_number_expr> expr = std::make_unique<label_number_expr>(); - expr->label = part; + if (valid_identifier(part)) { + std::unique_ptr<identifier_number_expr> expr = std::make_unique<identifier_number_expr>(); + expr->identifier = part; return expr; } - return {}; + number_expr_exception ex; + ex.message = "unknown expression form"; + throw ex; } //using unqiue_ptr to refer to this in case i add more types in the future @@ -219,57 +257,221 @@ namespace lib94 { std::unique_ptr<number_expr> right; std::function<bool (number_t, number_t)> comparison; - std::optional<bool> is_true(const std::map<std::string, number_t> &label_offsets, const std::map<std::string, number_t> ¯o_values, number_t this_offset) { - auto left_result = left->to_number(label_offsets, macro_values, this_offset); - auto right_result = right->to_number(label_offsets, macro_values, this_offset); - if (left_result.has_value() && right_result.has_value()) - return comparison(left_result.value(), right_result.value()); - return {}; + bool is_true(number_t our_offset, const inline_macro_set &inline_macros, const label_set &labels) { + number_t left_result = left->to_number(our_offset, inline_macros, labels); + number_t right_result = right->to_number(our_offset, inline_macros, labels); + return comparison(left_result, right_result); } }; - std::optional<std::unique_ptr<assert_expr>> make_assert_expr(std::string part, std::string sep, std::function<bool (number_t, number_t)> comparison) { + struct assert_expr_exception : public std::exception { + std::string message; + }; + + static std::optional<std::unique_ptr<assert_expr>> try_make_assert_expr(std::string part, std::string sep, std::function<bool (number_t, number_t)> comparison) { size_t pos = part.find(sep); if (pos == std::string::npos) return {}; - auto left = to_number_expr(part.substr(0, pos)); - auto right = to_number_expr(part.substr(pos + sep.size())); + std::unique_ptr<number_expr> left, right; - if (!left.has_value() || !right.has_value()) - return {}; + left = to_number_expr(part.substr(0, pos)); + right = to_number_expr(part.substr(pos + sep.size())); - return std::make_unique<assert_expr>((assert_expr){ - .left = std::move(left.value()), .right = std::move(right.value()), .comparison = comparison - }); + auto expr = std::make_unique<assert_expr>(); + expr->left = std::move(left); + expr->right = std::move(right); + expr->comparison = comparison; + + return expr; } - std::optional<std::unique_ptr<assert_expr>> to_assert_expr(std::string part) { - std::optional<std::unique_ptr<assert_expr>> result; + static std::unique_ptr<assert_expr> to_assert_expr(std::string part) { + try { + auto result = try_make_assert_expr(part, "==", [](number_t a, number_t b) {return a == b;}); + if (result) + return std::move(result.value()); - result = make_assert_expr(part, "==", [](number_t a, number_t b) {return a == b;}); - if (result) - return result; + result = try_make_assert_expr(part, "<=", [](number_t a, number_t b) {return a <= b;}); + if (result) + return std::move(result.value()); - result = make_assert_expr(part, "<=", [](number_t a, number_t b) {return a <= b;}); - if (result) - return result; + result = try_make_assert_expr(part, ">=", [](number_t a, number_t b) {return a >= b;}); + if (result) + return std::move(result.value()); - result = make_assert_expr(part, ">=", [](number_t a, number_t b) {return a >= b;}); - if (result) - return result; + result = try_make_assert_expr(part, "!=", [](number_t a, number_t b) {return a != b;}); + if (result) + return std::move(result.value()); - result = make_assert_expr(part, "!=", [](number_t a, number_t b) {return a != b;}); - if (result) - return result; + result = try_make_assert_expr(part, "<", [](number_t a, number_t b) {return a < b;}); + if (result) + return std::move(result.value()); - result = make_assert_expr(part, "<", [](number_t a, number_t b) {return a < b;}); - if (result) - return result; + result = try_make_assert_expr(part, ">", [](number_t a, number_t b) {return a > b;}); + if (result) + return std::move(result.value()); + } + catch (const number_expr_exception &iex) { + assert_expr_exception ex; + ex.message = iex.message; + throw ex; + } - return make_assert_expr(part, ">", [](number_t a, number_t b) {return a > b;}); + assert_expr_exception ex; + ex.message = "unknown assert operation"; + throw ex; } + struct future_instruction { + unsigned source_line; + opcode op; + modifier mod; + mode amode; + mode bmode; + std::unique_ptr<number_expr> anumber; + std::unique_ptr<number_expr> bnumber; + }; + + struct assertion { + size_t source_line; + std::unique_ptr<assert_expr> expr; + number_t offset; + }; + + struct preprocessed_line { + unsigned source_line; + std::string the_line; + }; + + struct info_from_preprocessor { + std::vector<std::unique_ptr<assert_expr>> assertions; + std::optional<std::string> name; + std::optional<std::string> author; + }; + + static void preprocess_until_end_block(info_from_preprocessor &info, std::vector<preprocessed_line> &into, unsigned &next_line_number, std::string &source, std::optional<std::string> block_ender) { + while (source != "") { + + size_t newline = source.find('\n'); + std::string line; + + if (newline == std::string::npos) { + line = source; + source = ""; + } + else { + line = source.substr(0, newline); + source = source.substr(newline + 1); + } + + unsigned line_number = next_line_number; + ++next_line_number; + + size_t semicolon = line.find(';'); + if (semicolon != std::string::npos) { + std::string comment = trim_spaces(line.substr(semicolon + 1)); + line = line.substr(0, semicolon); + + if (comment.starts_with("assert ")) { + std::unique_ptr<assert_expr> assertion; + + try { + assertion = std::move(to_assert_expr(comment.substr(7))); + } + catch (const assert_expr_exception &iex) { + compiler_exception ex; + ex.line_number = line_number; + ex.message = iex.message; + } + + info.assertions.push_back(std::move(assertion)); + } + + else if (comment.starts_with("name ")) { + + if (info.name.has_value()) { + compiler_exception ex; + ex.line_number = line_number; + ex.message = "duplicate name comment"; + throw ex; + } + + info.name = trim_spaces(comment.substr(5)); + } + + else if (comment.starts_with("author ")) { + + if (info.author.has_value()) { + compiler_exception ex; + ex.line_number = line_number; + ex.message = "duplicate author comment"; + throw ex; + } + + info.author = trim_spaces(comment.substr(7)); + } + } + + line = trim_spaces(line); + + if (block_ender.has_value() && line == block_ender.value()) + return; + + if (line.starts_with("for ")) { + + number_t repeats; + size_t count; + try { + repeats = real_mod(std::stoul(line.substr(4), &count)); + } + catch (const std::exception &e) { + count = 0; + } + + if (!count || count != line.size() - 4) { + compiler_exception ex; + ex.line_number = line_number; + ex.message = "bad for argument"; + throw ex; + } + + std::vector<preprocessed_line> inside_for; + preprocess_until_end_block(info, inside_for, next_line_number, source, "rof"); + + for (number_t i = 0; i < repeats; ++i) + for (const preprocessed_line &l : inside_for) + into.push_back(l); + + continue; + } + + into.push_back((preprocessed_line){.source_line = line_number, .the_line = line}); + } + + if (block_ender.has_value()) { + compiler_exception ex; + ex.line_number = next_line_number; + ex.message = "end of file encountered where " + block_ender.value() + " expected"; + throw ex; + } + } + + struct future_inline_macro { + std::string name; + unsigned source_line; + std::unique_ptr<number_expr> definition; + number_t offset; + }; + + typedef std::vector<future_inline_macro> future_inline_macro_set; + + struct org_info { + std::unique_ptr<number_expr> expr; + number_t offset; + unsigned source_line; + }; + static const std::map<char, mode> mode_symbols = { {'#', IMMEDIATE}, {'$', DIRECT}, {'*', A_INDIRECT}, {'@', B_INDIRECT}, @@ -277,7 +479,15 @@ namespace lib94 { {'}', A_INCREMENT}, {'>', B_INCREMENT} }; - std::optional<std::pair<mode, std::unique_ptr<number_expr>>> to_field(std::string part) { + typedef std::pair<mode, std::unique_ptr<number_expr>> field; + + static field make_empty_field() { + auto expr = std::make_unique<number_number_expr>(); + expr->value = 0; + return std::make_pair<>(DIRECT, std::move(expr)); + } + + static field to_field(std::string part) { if (part == "") return {}; @@ -289,11 +499,51 @@ namespace lib94 { part = trim_spaces(part.substr(1)); } - std::optional<std::unique_ptr<number_expr>> expr = to_number_expr(part); - if (expr.has_value()) - return std::make_pair<>(m, std::move(expr.value())); + return std::make_pair<>(m, to_number_expr(part)); + } + + static modifier get_default_modifier(opcode op, mode amode, mode bmode) { + switch (op) { + + case DAT: + return F; + + case MOV: + case SEQ: + case SNE: + if (amode == IMMEDIATE) + return AB; + if (bmode == IMMEDIATE) + return B; + return I; + + case ADD: + case SUB: + case MUL: + case DIV: + case MOD: + if (amode == IMMEDIATE) + return AB; + if (bmode == IMMEDIATE) + return B; + return F; + + case SLT: + if (amode == IMMEDIATE) + return AB; + return B; + + case JMP: + case JMZ: + case JMN: + case DJN: + case SPL: + case NOP: + return B; - return {}; + } + + assert(false); } static const std::map<std::string, opcode> opcode_names = { @@ -304,320 +554,283 @@ namespace lib94 { {"cmp", SEQ}, {"jnz", JMN} }; - struct future_instruction { - size_t source_line; - opcode op; - modifier mod; - mode amode; - mode bmode; - std::unique_ptr<number_expr> anumber; - std::unique_ptr<number_expr> bnumber; - }; + static void process_line(std::vector<future_instruction> &into, const preprocessed_line &line, future_inline_macro_set &future_inline_macros, label_set &labels, std::optional<org_info> &org) { + assert(into.size() < LIB94_CORE_SIZE); - struct macro_definition { - size_t source_line; - std::unique_ptr<number_expr> definition; - number_t offset; - }; + if (line.the_line == "") + return; - struct assertion { - size_t source_line; - std::unique_ptr<assert_expr> expr; - number_t offset; - }; + size_t opcode_len = line.the_line.find_first_of(" ."); + if (opcode_len == std::string::npos) + opcode_len = line.the_line.size(); - std::variant<warrior *, std::string> compile_warrior(std::string source) { - for (char &ch : source) - if (ch == '\t' || ch == '\r') - ch = ' '; + std::string opcode_name = line.the_line.substr(0, opcode_len); + std::string rest = trim_spaces(line.the_line.substr(opcode_len)); - std::unique_ptr<warrior> w = std::make_unique<warrior>(); + if (opcode_name == "org" || opcode_name == "end") { - std::vector<future_instruction> instructions; - std::map<std::string, number_t> label_offsets; - std::map<std::string, macro_definition> macro_definitions; - std::vector<assertion> assertions; - std::optional<std::unique_ptr<number_expr>> org = {}; - number_t org_offset = -1; - size_t org_source_line = -1; + if (org.has_value()) { + compiler_exception ex; + ex.line_number = line.source_line; + ex.message = "duplicate org"; + throw ex; + } - size_t on_line = 0; + try { + org = std::move((org_info){ + .expr = to_number_expr(rest), + .offset = (number_t)into.size(), + .source_line = line.source_line + }); + } - while (true) { - if (source == "") - break; + catch (const number_expr_exception &iex) { + compiler_exception ex; + ex.line_number = line.source_line; + ex.message = iex.message; + throw ex; + } - ++on_line; + return; - size_t newline = source.find('\n'); - if (newline == std::string::npos) - newline = source.size(); + } - std::string line = source.substr(0, newline); - source = source.substr(newline + 1); + auto opcode_result = opcode_names.find(opcode_name); - size_t semicolon = line.find(';'); + if (opcode_result == opcode_names.end() && valid_identifier(opcode_name)) { - if (semicolon != std::string::npos) { - std::string comment = trim_spaces(line.substr(semicolon + 1)); - line = line.substr(0, semicolon); + if (rest.starts_with("equ ")) { - if (comment.starts_with("name ")) - w->name = trim_spaces(comment.substr(5)); - else if (comment.starts_with("author ")) - w->author = trim_spaces(comment.substr(7)); - - else if (comment.starts_with("assert ")) { - auto expr = to_assert_expr(comment.substr(7)); - if (expr.has_value()) - assertions.emplace_back((assertion){ - .source_line = on_line, - .expr = std::move(expr.value()), - .offset = (number_t)instructions.size() - }); - else - return std::string("bad expression on line ") + std::to_string(on_line); + try { + future_inline_macros.push_back((future_inline_macro){ + .name = opcode_name, + .source_line = line.source_line, + .definition = to_number_expr(rest.substr(4)), + .offset = (number_t)into.size() + }); } - } - for (char &ch : line) - ch = tolower(ch); + catch (const number_expr_exception &iex) { + compiler_exception ex; + ex.line_number = line.source_line; + ex.message = iex.message; + throw ex; + } - return_after_label: - line = trim_spaces(line); - if (line == "") - continue; + return; - size_t equ = line.find(" equ "); - if (equ != std::string::npos) { + } - std::string macro_name = trim_spaces(line.substr(0, equ).substr()); + if (labels.contains(opcode_name)) { + compiler_exception ex; + ex.line_number = line.source_line; + ex.message = "duplicate label"; + throw ex; + } - if (!valid_label(macro_name)) - return std::string("bad label on line ") + std::to_string(on_line); - if (macro_definitions.contains(macro_name) || label_offsets.contains(macro_name)) - return std::string("duplicate label on line ") + std::to_string(on_line); + labels[opcode_name] = into.size(); + preprocessed_line new_line = {.source_line = line.source_line, .the_line = rest}; + return process_line(into, new_line, future_inline_macros, labels, org); - auto macro_def = to_number_expr(line.substr(equ + 5)); - if (!macro_def.has_value()) - return std::string("bad expression on line ") + std::to_string(on_line); + } - macro_definitions.emplace(macro_name, - (macro_definition){ - .source_line = on_line, - .definition = std::move(macro_def.value()), - .offset = (number_t)instructions.size() - } - ); + opcode real_opcode = opcode_result->second; + std::optional<modifier> real_modifier; - continue; + if (rest != "" && rest[0] == '.') { + if (rest.starts_with(".ab")) { + real_modifier = AB; + rest = trim_spaces(rest.substr(3)); } - future_instruction instr; - instr.source_line = on_line; - - size_t sep = line.find_first_of(" ."); - if (sep == std::string::npos) - sep = line.size(); - - std::string opcode_str = trim_spaces(line.substr(0, sep)); - line = trim_spaces(line.substr(sep)); + if (rest.starts_with(".ba")) { + real_modifier = BA; + rest = trim_spaces(rest.substr(3)); + } - if (opcode_str == "org" || opcode_str == "end") { - if (org.has_value()) - return std::string("duplicate org on line ") + std::to_string(on_line); + if (rest.starts_with(".a")) { + real_modifier = A; + rest = trim_spaces(rest.substr(2)); + } - org = to_number_expr(line); + if (rest.starts_with(".b")) { + real_modifier = B; + rest = trim_spaces(rest.substr(2)); + } - if (!org.has_value()) - return std::string("bad expression on line ") + std::to_string(on_line); + if (rest.starts_with(".f")) { + real_modifier = F; + rest = trim_spaces(rest.substr(2)); + } - org_offset = instructions.size(); - org_source_line = on_line; - continue; + if (rest.starts_with(".x")) { + real_modifier = X; + rest = trim_spaces(rest.substr(2)); } - auto opcode_it = opcode_names.find(opcode_str); + if (rest.starts_with(".i")) { + real_modifier = I; + rest = trim_spaces(rest.substr(2)); + } - if (opcode_it == opcode_names.end()) { - if (!valid_label(opcode_str)) - return std::string("bad label or unknown opcode on line ") + std::to_string(on_line); + } - if (label_offsets.contains(opcode_str) || macro_definitions.contains(opcode_str)) - return std::string("duplicate label on line ") + std::to_string(on_line); + field a_field, b_field; - label_offsets[opcode_str] = (number_t)instructions.size(); + try { - goto return_after_label; + if (rest == "") { + a_field = make_empty_field(); + b_field = make_empty_field(); } - instr.op = opcode_it->second; + else { + size_t comma = rest.find(','); - bool got_mod = false; + if (comma == std::string::npos) { + a_field = to_field(rest); + b_field = make_empty_field(); + } - if (line != "" && line[0] == '.') { - line = trim_spaces(line.substr(1)); + else { + a_field = to_field(rest.substr(0, comma)); + b_field = to_field(trim_spaces(rest.substr(comma + 1))); + } + } - if (line == "") - return std::string("dot with no modifier on line ") + std::to_string(on_line); + } - got_mod = true; + catch (const number_expr_exception &iex) { + compiler_exception ex; + ex.line_number = line.source_line; + ex.message = iex.message; + throw ex; + } - size_t mod_length = 2; + if (!real_modifier.has_value()) + real_modifier = get_default_modifier(real_opcode, a_field.first, b_field.first); + + into.push_back((future_instruction){ + .source_line = line.source_line, + .op = real_opcode, + .mod = real_modifier.value(), + .amode = a_field.first, + .bmode = b_field.first, + .anumber = std::move(a_field.second), + .bnumber = std::move(b_field.second) + }); + } - if (line.starts_with("ab")) - instr.mod = AB; - else if (line.starts_with("ba")) - instr.mod = BA; - else { + warrior * compile_warrior(std::string source) { + for (char &ch : source) + if (ch == '\t' || ch == '\r') + ch = ' '; - mod_length = 1; + info_from_preprocessor info; + std::vector<preprocessed_line> lines; + unsigned line_number = 1; + preprocess_until_end_block(info, lines, line_number, source, {}); - if (line[0] == 'a') - instr.mod = A; - else if (line[0] == 'b') - instr.mod = B; - else if (line[0] == 'f') - instr.mod = F; - else if (line[0] == 'x') - instr.mod = X; - else if (line[0] == 'i') - instr.mod = I; + if (!info.name.has_value() || info.name == "") { + compiler_exception ex; + ex.line_number = line_number; + ex.message = "no warrior name"; + throw ex; + } - else - return std::string("unknown opcode modifier on line ") + std::to_string(on_line); + if (!info.author.has_value() || info.author == "") { + compiler_exception ex; + ex.line_number = line_number; + ex.message = "no warrior author"; + throw ex; + } - } + std::vector<future_instruction> future_instructions; + future_inline_macro_set future_inline_macros; + label_set labels; + std::optional<org_info> org; - line = trim_spaces(line.substr(mod_length)); - } + for (const preprocessed_line &line : lines) + process_line(future_instructions, line, future_inline_macros, labels, org); - std::optional<std::pair<mode, std::unique_ptr<number_expr>>> afield, bfield; + inline_macro_set inline_macros; + for (const auto &fim : future_inline_macros) { - if (line == "") { - afield = to_field("0"); - bfield = to_field("0"); + if (inline_macros.contains(fim.name)) { + compiler_exception ex; + ex.line_number = fim.source_line; + ex.message = "duplicate inline macro"; + throw ex; } - else { - size_t comma = line.find(","); + if (labels.contains(fim.name)) { + compiler_exception ex; + ex.line_number = fim.source_line; + ex.message = "inline macro with same name as label"; + throw ex; + } - if (comma == std::string::npos) { - afield = to_field(line); - bfield = to_field("0"); - } + try { + inline_macros[fim.name] = fim.definition->to_number(fim.offset, inline_macros, labels); + } - else { - std::string apart = trim_spaces(line.substr(0, comma)); - std::string bpart = trim_spaces(line.substr(comma + 1)); - afield = to_field(apart); - bfield = to_field(bpart); - } + catch (const number_expr_exception &iex) { + compiler_exception ex; + ex.line_number = fim.source_line; + ex.message = iex.message; + throw ex; } - if (!afield.has_value() || !bfield.has_value()) - return std::string("bad expression on line ") + std::to_string(on_line); - - instr.amode = afield.value().first; - instr.bmode = bfield.value().first; - instr.anumber = std::move(afield.value().second); - instr.bnumber = std::move(bfield.value().second); - - if (!got_mod) - switch (instr.op) { - - case DAT: - instr.mod = F; - break; - - case MOV: - case SEQ: - case SNE: - if (instr.amode == IMMEDIATE) - instr.mod = AB; - else if (instr.bmode == IMMEDIATE) - instr.mod = B; - else - instr.mod = I; - break; - - case ADD: - case SUB: - case MUL: - case DIV: - case MOD: - if (instr.amode == IMMEDIATE) - instr.mod = AB; - else if (instr.bmode == IMMEDIATE) - instr.mod = B; - else - instr.mod = F; - break; - - case SLT: - if (instr.amode == IMMEDIATE) - instr.mod = AB; - instr.mod = B; - break; - - case JMP: - case JMZ: - case JMN: - case DJN: - case SPL: - case NOP: - instr.mod = B; - break; + } - } + std::vector<instruction> actual_instructions; - instructions.push_back(std::move(instr)); - } + for (number_t offset = 0; offset < (number_t)future_instructions.size(); ++offset) { + const future_instruction &fi = future_instructions[offset]; - if (w->name == "") - return "no name"; - if (w->author == "") - return "no author"; - - std::map<std::string, number_t> macro_values; - for (const auto ¯o : macro_definitions) { - auto result = macro.second.definition->to_number(label_offsets, macro_values, macro.second.offset); - if (result.has_value()) - macro_values[macro.first] = result.value(); - else - return std::string("bad expression on line ") + std::to_string(macro.second.source_line); - } + try { + actual_instructions.push_back((instruction){ + .op = fi.op, + .mod = fi.mod, + .amode = fi.amode, + .bmode = fi.bmode, + .anumber = fi.anumber->to_number(offset, inline_macros, labels), + .bnumber = fi.bnumber->to_number(offset, inline_macros, labels) + }); + } - for (const auto &a : assertions) { - auto result = a.expr->is_true(label_offsets, macro_values, a.offset); - if (!result.has_value()) - return std::string("bad expression on line ") + std::to_string(a.source_line); - if (!result.value()) - return std::string("failed assertion on line ") + std::to_string(a.source_line); + catch (const number_expr_exception &iex) { + compiler_exception ex; + ex.line_number = fi.source_line; + ex.message = iex.message; + throw ex; + } } - if (org.has_value()) { - std::optional<number_t> org_number = org.value()->to_number(label_offsets, macro_values, org_offset); - if (org_number.has_value()) - w->org = real_mod(org_number.value() + org_offset); - else - return std::string("bad expression on line ") + std::to_string(org_source_line); - } + number_t org_result; + + if (!org.has_value()) + org_result = 0; - for (size_t i = 0; i < instructions.size(); ++i) { - const future_instruction &instr = instructions[i]; - std::optional<number_t> anumber = instr.anumber->to_number(label_offsets, macro_values, i); - std::optional<number_t> bnumber = instr.bnumber->to_number(label_offsets, macro_values, i); - if (!anumber.has_value() || !bnumber.has_value()) - return std::string("bad expression on line ") + std::to_string(instr.source_line); - w->instructions.push_back({ - .op = instr.op, .mod = instr.mod, .amode = instr.amode, .bmode = instr.bmode, - .anumber = anumber.value(), .bnumber = bnumber.value() - }); + else { + try { + org_result = real_mod(org.value().expr->to_number(org.value().offset, inline_macros, labels) + org.value().offset); + } + catch (const number_expr_exception &iex) { + compiler_exception ex; + ex.line_number = org.value().source_line; + ex.message = iex.message; + throw ex; + } } - return w.release(); + return new warrior { + .name = info.name.value(), + .author = info.author.value(), + .org = org_result, + .instructions = actual_instructions + }; } struct wheader { @@ -1,4 +1,4 @@ -CPP_ARGS = -std=c++20 -O2 -Wall -Wextra -Iinclude +CPP_ARGS = -std=c++20 -O2 -Wall -Wextra -Iinclude -ggdb GTKMM_CPP_ARGS = $(shell pkg-config --cflags gtkmm-4.0) GTKMM_LD_ARGS = $(shell pkg-config --libs gtkmm-4.0) diff --git a/score/main.cpp b/score/main.cpp index 88536bb..85a889b 100644 --- a/score/main.cpp +++ b/score/main.cpp @@ -43,22 +43,25 @@ int main(int argc, const char **argv) { file1.close(); file2.close(); - auto w1 = lib94::compile_warrior(source1); - auto w2 = lib94::compile_warrior(source2); + lib94::warrior *w1, *w2; - if (std::holds_alternative<std::string>(w1)) { - std::cout << "error compiling " << argv[1] << ": " << std::get<std::string>(w1) << '\n'; + try { + w1 = lib94::compile_warrior(source1); + } + catch (const lib94::compiler_exception &ex) { + std::cout << "error in " << argv[1] << " on line " << ex.line_number << ": " << ex.message << '\n'; return 1; } - if (std::holds_alternative<std::string>(w2)) { - std::cout << "error compiling " << argv[2] << ": " << std::get<std::string>(w2) << '\n'; + try { + w2 = lib94::compile_warrior(source1); + } + catch (const lib94::compiler_exception &ex) { + std::cout << "error in " << argv[2] << " on line " << ex.line_number << ": " << ex.message << '\n'; return 1; } - const lib94::warrior *ws[2]; - ws[0] = std::get<lib94::warrior *>(w1); - ws[1] = std::get<lib94::warrior *>(w2); + const lib94::warrior *ws[2] = {w1, w2}; print_warrior(ws[0]); print_warrior(ws[1]); diff --git a/tabulator-mpi/main.cpp b/tabulator-mpi/main.cpp index f76ed70..959cd8c 100644 --- a/tabulator-mpi/main.cpp +++ b/tabulator-mpi/main.cpp @@ -16,14 +16,14 @@ const lib94::warrior *load_warrior(const char *file) { std::string source(std::istreambuf_iterator<char>(stream), {}); - auto w = lib94::compile_warrior(source); + try { + return lib94::compile_warrior(source); + } - if (std::holds_alternative<std::string>(w)) { - fprintf(stderr, "error compiling %s: %s\n", file, std::get<std::string>(w).c_str()); + catch (const lib94::compiler_exception &ex) { + fprintf(stderr, "error in %s on line %u: %s\n", file, ex.line_number, ex.message.c_str()); exit(1); } - - return std::get<lib94::warrior *>(w); } int main(int argc, char **argv) { diff --git a/warriors/big-nothing.red b/warriors/big-nothing.red index 1d69675..6a1a0dd 100644 --- a/warriors/big-nothing.red +++ b/warriors/big-nothing.red @@ -2,13 +2,7 @@ ;name Big Nothing start - nop - nop - nop - nop - nop - nop - nop - nop - nop + for 19 + nop + rof jmp start diff --git a/warriors/epson.red b/warriors/epson.red index eeceee1..688ad8d 100644 --- a/warriors/epson.red +++ b/warriors/epson.red @@ -1,16 +1,20 @@ ;author Benji Dial ;name Epson -period equ 10 +intrascan_period equ 10 +interscan_period equ 2 -scan_init equ the_end - (the_end - scan) % period + period +;interscan period must divide intrascan period +;intrascan period must divide 8000 + +scan_init equ the_end - scan - (the_end - scan) % intrascan_period + intrascan_period scan - seq.i -period, scan_init + seq.i -intrascan_period, scan_init jmp found found_ret - add.ab #period, scan + add.ab #intrascan_period, scan seq.ab scan, scan jmp scan @@ -18,17 +22,17 @@ found_ret jmn.a scan, scan clear - mov the_end, - 1 - sub.ab #2, clear + mov the_end, scan - interscan_period + sub.ab #interscan_period, clear - seq.ab #the_end - clear - (the_end - clear) % 2 + 3, clear + seq.ab #scan_add - 1 - clear - (scan_add - 1 - scan) % interscan_period + interscan_period, clear jmp clear - mov.ab #-1, clear + mov.ab #scan - interscan_period - clear, clear jmp clear scan_add - dat 2, period + scan_init + 2 + dat interscan_period, intrascan_period + scan_init + interscan_period found mov bomb, >scan |