diff options
-rw-r--r-- | lib94/warrior.cpp | 159 | ||||
-rw-r--r-- | warriors/dwarf.red | 6 |
2 files changed, 148 insertions, 17 deletions
diff --git a/lib94/warrior.cpp b/lib94/warrior.cpp index 274a31f..bb65828 100644 --- a/lib94/warrior.cpp +++ b/lib94/warrior.cpp @@ -31,26 +31,27 @@ namespace lib94 { } struct number_expr { - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) = 0; + 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; }; struct label_number_expr : public number_expr { std::string label; - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) { + 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 {}; - return real_mod(result->second - this_offset); + if (result != label_offsets.end()) + return real_mod(result->second - this_offset); + result = macro_values.find(label); + if (result != macro_values.end()) + return result->second; + return {}; } }; struct number_number_expr : public number_expr { number_t value; - virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) { - (void)label_offsets; - (void)this_offset; + 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); } }; @@ -60,9 +61,9 @@ namespace lib94 { 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, number_t this_offset) { - std::optional<number_t> left_result = left->to_number(label_offsets, this_offset); - std::optional<number_t> right_result = right->to_number(label_offsets, this_offset); + 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()) @@ -176,6 +177,63 @@ namespace lib94 { return {}; } + //using unqiue_ptr to refer to this in case i add more types in the future + struct assert_expr { + std::unique_ptr<number_expr> left; + 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 {}; + } + }; + + std::optional<std::unique_ptr<assert_expr>> 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())); + + if (!left.has_value() || !right.has_value()) + return {}; + + return std::make_unique<assert_expr>((assert_expr){ + .left = std::move(left.value()), .right = std::move(right.value()), .comparison = comparison + }); + } + + std::optional<std::unique_ptr<assert_expr>> to_assert_expr(std::string part) { + std::optional<std::unique_ptr<assert_expr>> result; + + result = make_assert_expr(part, "==", [](number_t a, number_t b) {return a == b;}); + if (result) + return result; + + result = make_assert_expr(part, "<=", [](number_t a, number_t b) {return a <= b;}); + if (result) + return result; + + result = make_assert_expr(part, ">=", [](number_t a, number_t b) {return a >= b;}); + if (result) + return result; + + result = make_assert_expr(part, "!=", [](number_t a, number_t b) {return a != b;}); + if (result) + return result; + + result = make_assert_expr(part, "<", [](number_t a, number_t b) {return a < b;}); + if (result) + return result; + + return make_assert_expr(part, ">", [](number_t a, number_t b) {return a > b;}); + } + static const std::map<char, mode> mode_symbols = { {'#', IMMEDIATE}, {'$', DIRECT}, {'*', A_INDIRECT}, {'@', B_INDIRECT}, @@ -220,6 +278,18 @@ namespace lib94 { std::unique_ptr<number_expr> bnumber; }; + struct macro_definition { + size_t source_line; + std::unique_ptr<number_expr> definition; + number_t offset; + }; + + struct assertion { + size_t source_line; + std::unique_ptr<assert_expr> expr; + number_t offset; + }; + std::variant<warrior *, std::string> compile_warrior(std::string source) { for (char &ch : source) if (ch == '\t' || ch == '\r') @@ -229,6 +299,8 @@ namespace lib94 { 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; @@ -258,6 +330,18 @@ namespace lib94 { 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); + } } for (char &ch : line) @@ -273,7 +357,7 @@ namespace lib94 { if (!valid_label(label)) return std::string("bad label on line ") + std::to_string(on_line); - if (label_offsets.contains(label)) + if (label_offsets.contains(label) || macro_definitions.contains(label)) return std::string("duplicate label on line ") + std::to_string(on_line); label_offsets[label] = instructions.size(); @@ -283,13 +367,39 @@ namespace lib94 { if (line == "") continue; + size_t equ = line.find(" equ "); + if (equ != std::string::npos) { + + std::string macro_name = trim_spaces(line.substr(0, equ).substr()); + + 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); + + 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() + } + ); + + continue; + + } + future_instruction instr; instr.source_line = on_line; std::string opcode_str = line.substr(0, 3); line = trim_spaces(line.substr(3)); - if (opcode_str == "org") { + if (opcode_str == "org" || opcode_str == "end") { if (org.has_value()) return std::string("duplicate org on line ") + std::to_string(on_line); @@ -436,8 +546,25 @@ namespace lib94 { 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); + } + + 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); + } + if (org.has_value()) { - std::optional<number_t> org_number = org.value()->to_number(label_offsets, org_offset); + std::optional<number_t> org_number = org.value()->to_number(label_offsets, macro_values, org_offset); if (org_number.has_value()) w->org = org_number.value() + org_offset; else @@ -446,8 +573,8 @@ namespace lib94 { 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, i); - std::optional<number_t> bnumber = instr.bnumber->to_number(label_offsets, 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({ diff --git a/warriors/dwarf.red b/warriors/dwarf.red index cfa3b86..17c075c 100644 --- a/warriors/dwarf.red +++ b/warriors/dwarf.red @@ -1,6 +1,10 @@ ;author standard ;name dwarf +;assert (0 - bomb_step) % bomb_step == 0 + +bomb_step equ 4 + mov 0-1, 3 -add.ab #4, 0-1 +add.ab #bomb_step, 0-1 jmp 0-2 |