diff --git a/src/compiler.cpp b/src/compiler.cpp new file mode 100644 index 0000000..bafb2d0 --- /dev/null +++ b/src/compiler.cpp @@ -0,0 +1,390 @@ +#include "compiler.hpp" +#include +#include +#include + +function_call::function_call(operation f, const std::string& s) : func {f}, arg {s} {} + +ref::ref(const std::string& n, size_t i, size_t l) : name {n}, prog_index {i}, lineno {l} {} + +compiler::compiler() { + // register operations + _ops["add"] = add; + _ops["sub"] = sub; + _ops["mul"] = mul; + _ops["div"] = div; + _ops["mod"] = mod; + _ops["abs"] = abs; + _ops["neg"] = neg; + _ops["dup"] = dup; + _ops["inc"] = inc; + _ops["dec"] = dec; + _ops["rev"] = rev; + _ops["slc"] = slc; + _ops["idx"] = idx; + _ops["cat"] = cat; + _ops["len"] = len; + _ops["rot"] = rot; + _ops["gto"] = gto; + _ops["geq"] = geq; + _ops["gne"] = gne; + _ops["glt"] = glt; + _ops["gle"] = gle; + _ops["ggt"] = ggt; + _ops["gge"] = gge; + _ops["fun"] = fun; + _ops["ret"] = ret; + _ops["enl"] = enl; + _ops["inp"] = inp; + _ops["out"] = out; + _ops["err"] = err; + _ops["end"] = end; +} + +void compiler::reset() { + _prog.clear(); + _values.clear(); + _labels.clear(); + _label_refs.clear(); + _vars.clear(); + _var_refs.clear(); + _calls.clear(); + _pc = 0; + _done = false; +} + +std::string compiler::eval(std::istream& in, bool& done) { + std::string result; + reset(); + + // compile, phase 1 + size_t lineno = 1; + for (std::string line; std::getline(in, line); ++lineno) { + std::string val {line.substr(1)}; + if (line[0] == ':') { // label definition + _labels.emplace(val, _prog.size()); + } else if (line[0] == '#') { // comment + continue; + } else if (std::isdigit(line[0])) { // literal int + _prog.emplace_back(push_literal, line); + } else if (line[0] == '\\') { // literal string + _prog.emplace_back(push_literal, val); + } else if (line[0] == '>') { // label ref + _label_refs.emplace_back(val, _prog.size(), lineno); + _prog.emplace_back(push_literal, ""); + } else if (line[0] == '=') { // var assignment + _vars[val] = ""; + _prog.emplace_back(assign, val); + } else if (line[0] == '$') { // var ref + _var_refs.emplace_back(val, _prog.size(), lineno); + _prog.emplace_back(push_variable, val); + } else { // operation + auto it = _ops.find(line); + if (it != _ops.end()) { + _prog.emplace_back(it->second, ""); + } else { + throw syntax_error {"undefined operation", lineno}; + } + } + } + + // compile, phase 2: resolve references + for (const ref& lr : _label_refs) { + auto it = _labels.find(lr.name); + if (it != _labels.end()) { + _prog[lr.prog_index].arg = std::to_string(it->second); + } else { + throw syntax_error {"undefined label", lr.lineno}; + } + } + for (const ref& vr : _var_refs) { + auto it = _vars.find(vr.name); + if (it == _vars.end()) { + throw syntax_error {"undefined variable", vr.lineno}; + } + } + + // run + while (_pc < _prog.size() && !_done) { + function_call info {_prog[_pc]}; // fetch next operation + (*info.func)(*this, info.arg); // call operation + } + done = _done; + + if (_values.size()) { + result = _values.back(); + } + return result; +} + +inline int compiler::pop_int() { + std::istringstream iss {pop_str()}; + int result; + iss >> result; + return result; +} + +inline std::string compiler::pop_str() { + auto val = _values.back(); + _values.pop_back(); + return val; +} + +// stack operations ------------------------------------------------------- + +void compiler::push_literal(compiler& cmp, const std::string& val) { + cmp._values.push_back(val); + cmp._pc++; +} + +void compiler::assign(compiler& cmp, const std::string& varname) { + cmp._vars[varname] = cmp._values.back(); + cmp._values.pop_back(); + cmp._pc++; +} + +void compiler::push_variable(compiler& cmp, const std::string& varname) { + cmp._values.push_back(cmp._vars[varname]); + cmp._pc++; +} + +// integer operations ----------------------------------------------------- + +void compiler::add(compiler& cmp, const std::string&) { + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + cmp._values.push_back(std::to_string(val1 + val2)); + cmp._pc++; +} + +void compiler::sub(compiler& cmp, const std::string&) { + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + cmp._values.push_back(std::to_string(val1 - val2)); + cmp._pc++; +} + +void compiler::mul(compiler& cmp, const std::string&) { + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + cmp._values.push_back(std::to_string(val1 * val2)); + cmp._pc++; +} + +void compiler::div(compiler& cmp, const std::string&) { + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + cmp._values.push_back(std::to_string(val1 / val2)); + cmp._pc++; +} + +void compiler::mod(compiler& cmp, const std::string&) { + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + cmp._values.push_back(std::to_string(val1 % val2)); + cmp._pc++; +} + +void compiler::abs(compiler& cmp, const std::string&) { + auto val = cmp.pop_int(); + cmp._values.push_back(std::to_string(val < 0 ? -val : val)); + cmp._pc++; +} + +void compiler::neg(compiler& cmp, const std::string&) { + auto val = cmp.pop_int(); + cmp._values.push_back(std::to_string(-val)); + cmp._pc++; +} + +void compiler::inc(compiler& cmp, const std::string&) { + auto val = cmp.pop_int(); + cmp._values.push_back(std::to_string(val + 1)); + cmp._pc++; +} + +void compiler::dec(compiler& cmp, const std::string&) { + auto val = cmp.pop_int(); + cmp._values.push_back(std::to_string(val - 1)); + cmp._pc++; +} + +// string operations ------------------------------------------------------ + +void compiler::dup(compiler& cmp, const std::string&) { + cmp._values.push_back(cmp._values.back()); + cmp._pc++; +} + +void compiler::rev(compiler& cmp, const std::string&) { + auto val = cmp.pop_str(); + std::string result; + for (auto it = val.rbegin(); it != val.rend(); ++it) { + result += *it; + } + cmp._values.push_back(result); + cmp._pc++; +} + +void compiler::slc(compiler& cmp, const std::string&) { + auto to = cmp.pop_int(); + auto from = cmp.pop_int(); + auto val = cmp.pop_str(); + cmp._values.push_back(val.substr(from, to - from)); + cmp._pc++; +} + +void compiler::idx(compiler& cmp, const std::string&) { + auto idx = cmp.pop_int(); + auto val = cmp.pop_str(); + val = val[idx]; + cmp._values.emplace_back(val); + cmp._pc++; +} + +void compiler::cat(compiler& cmp, const std::string&) { + auto val2 = cmp.pop_str(); + auto val1 = cmp.pop_str(); + cmp._values.emplace_back(val1 + val2); + cmp._pc++; +} + +void compiler::len(compiler& cmp, const std::string&) { + auto val = cmp.pop_str(); + cmp._values.push_back(std::to_string(val.size())); + cmp._pc++; +} + +void compiler::rot(compiler& cmp, const std::string&) { + auto val = cmp.pop_str(); + std::transform(val.begin(), val.end(), val.begin(), [](char ch) -> char { + if (ch >= 'a' && ch <= 'm') { + return ch + 13; + } else if (ch >= 'n' && ch <= 'z') { + return ch - 13; + } else if (ch >= 'A' && ch <= 'M') { + return ch + 13; + } else if (ch >= 'N' && ch <= 'Z') { + return ch - 13; + } + return ch; + }); + cmp._values.push_back(val); + cmp._pc++; +} + +void compiler::enl(compiler& cmp, const std::string&) { + auto val = cmp.pop_str(); + cmp._values.emplace_back(val + '\n'); + cmp._pc++; +} + +// tests & jumps ---------------------------------------------------------- + +void compiler::gto(compiler& cmp, const std::string&) { + cmp._pc = cmp.pop_int(); +} + +void compiler::geq(compiler& cmp, const std::string&) { + auto label = cmp.pop_int(); + auto val2 = cmp.pop_str(); + auto val1 = cmp.pop_str(); + if (val1 == val2) { + cmp._pc = label; + } else { + cmp._pc++; + } +} + +void compiler::gne(compiler& cmp, const std::string&) { + auto label = cmp.pop_int(); + auto val2 = cmp.pop_str(); + auto val1 = cmp.pop_str(); + if (val1 != val2) { + cmp._pc = label; + } else { + cmp._pc++; + } +} + +void compiler::glt(compiler& cmp, const std::string&) { + auto label = cmp.pop_int(); + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + if (val1 < val2) { + cmp._pc = label; + } else { + cmp._pc++; + } +} + +void compiler::gle(compiler& cmp, const std::string&) { + auto label = cmp.pop_int(); + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + if (val1 <= val2) { + cmp._pc = label; + } else { + cmp._pc++; + } +} + +void compiler::ggt(compiler& cmp, const std::string&) { + auto label = cmp.pop_int(); + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + if (val1 > val2) { + cmp._pc = label; + } else { + cmp._pc++; + } +} + +void compiler::gge(compiler& cmp, const std::string&) { + auto label = cmp.pop_int(); + auto val2 = cmp.pop_int(); + auto val1 = cmp.pop_int(); + if (val1 >= val2) { + cmp._pc = label; + } else { + cmp._pc++; + } +} + +// subroutines ------------------------------------------------------------ + +void compiler::fun(compiler& cmp, const std::string&) { + cmp._calls.push_back(cmp._pc + 1); // push return address + cmp.gto(cmp, ""); +} + +void compiler::ret(compiler& cmp, const std::string&) { + cmp._pc = cmp._calls.back(); + cmp._calls.pop_back(); +} + +// I/O -------------------------------------------------------------------- + +void compiler::inp(compiler& cmp, const std::string&) { + std::string val; + std::cin >> val; + cmp._values.push_back(val); + cmp._pc++; +} + +void compiler::out(compiler& cmp, const std::string&) { + std::cout << cmp._values.back() << '\n'; + cmp._pc++; +} + +void compiler::err(compiler& cmp, const std::string&) { + std::cerr << cmp._values.back() << '\n'; + cmp._pc++; +} + +// exit ------------------------------------------------------------------- + +void compiler::end(compiler& cmp, const std::string&) { + cmp._done = true; +} + diff --git a/src/compiler.hpp b/src/compiler.hpp new file mode 100644 index 0000000..3c68040 --- /dev/null +++ b/src/compiler.hpp @@ -0,0 +1,109 @@ +#ifndef _compiler_H_ +#define _compiler_H_ + +#include +#include +#include +#include +#include +#include + +class syntax_error : public std::runtime_error { +public: + syntax_error(const std::string& what_arg, size_t lineno) + : std::runtime_error {what_arg}, _lineno {lineno} {} + size_t lineno() const { return _lineno; } +private: + size_t _lineno {0}; +}; + +class compiler; + +using operation = void(*)(compiler&, const std::string&); +struct function_call { + operation func; + std::string arg; + + function_call(operation, const std::string&); +}; + +struct ref { + std::string name; + size_t prog_index; + size_t lineno; + + ref(const std::string& n, size_t i, size_t l); +}; + +class compiler { +public: + compiler(); + + std::string eval(std::istream& in, bool& done); + +private: + std::map _ops; // operation registry + std::vector _prog; // compiled program + std::map _labels; // label name => _prog index + std::vector _label_refs; // used during compilation to resolve label references + std::vector _values; // value stack + std::vector _calls; // call stack + std::map _vars; // variable name => string value + std::vector _var_refs; // used during compilation to resolve variable references + size_t _pc {0}; // current program counter (index into _prog) + bool _done {false}; + + void reset(); + + int pop_int(); + std::string pop_str(); + + // stack operations + static void push_literal(compiler&, const std::string& val); + static void assign(compiler&, const std::string& varname); + static void push_variable(compiler&, const std::string& varname); + + // integer operations + static void add(compiler&, const std::string&); + static void sub(compiler&, const std::string&); + static void mul(compiler&, const std::string&); + static void div(compiler&, const std::string&); + static void mod(compiler&, const std::string&); + static void abs(compiler&, const std::string&); + static void neg(compiler&, const std::string&); + static void inc(compiler&, const std::string&); + static void dec(compiler&, const std::string&); + + // string operations + static void dup(compiler&, const std::string&); + static void rev(compiler&, const std::string&); + static void slc(compiler&, const std::string&); + static void idx(compiler&, const std::string&); + static void cat(compiler&, const std::string&); + static void len(compiler&, const std::string&); + static void rot(compiler&, const std::string&); + static void enl(compiler&, const std::string&); + + // tests & jumps + static void gto(compiler&, const std::string&); + static void geq(compiler&, const std::string&); + static void gne(compiler&, const std::string&); + static void glt(compiler&, const std::string&); + static void gle(compiler&, const std::string&); + static void ggt(compiler&, const std::string&); + static void gge(compiler&, const std::string&); + + // subroutines + static void fun(compiler&, const std::string&); + static void ret(compiler&, const std::string&); + + // I/O + static void inp(compiler&, const std::string&); + static void out(compiler&, const std::string&); + static void err(compiler&, const std::string&); + + // exit + static void end(compiler&, const std::string&); +}; + +#endif // _compiler_H_