summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bench/bench_window.cpp15
-rw-r--r--include/lib94/lib94.hpp7
-rw-r--r--lib94/warrior.cpp929
-rw-r--r--makefile2
-rw-r--r--score/main.cpp21
-rw-r--r--tabulator-mpi/main.cpp10
-rw-r--r--warriors/big-nothing.red12
-rw-r--r--warriors/epson.red22
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> &macro_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> &macro_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> &macro_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> &macro_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> &macro_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 &macro : 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 {
diff --git a/makefile b/makefile
index 5b55db5..48c77f5 100644
--- a/makefile
+++ b/makefile
@@ -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