Added compiler, as stand-in replacement for interpreter
This commit is contained in:
parent
37ef764af7
commit
d600cfb7c5
390
src/compiler.cpp
Normal file
390
src/compiler.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
#include "compiler.hpp"
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
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;
|
||||
}
|
||||
|
109
src/compiler.hpp
Normal file
109
src/compiler.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
#ifndef _compiler_H_
|
||||
#define _compiler_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <set>
|
||||
|
||||
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<std::string, operation> _ops; // operation registry
|
||||
std::vector<function_call> _prog; // compiled program
|
||||
std::map<std::string, size_t> _labels; // label name => _prog index
|
||||
std::vector<ref> _label_refs; // used during compilation to resolve label references
|
||||
std::vector<std::string> _values; // value stack
|
||||
std::vector<size_t> _calls; // call stack
|
||||
std::map<std::string, std::string> _vars; // variable name => string value
|
||||
std::vector<ref> _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_
|
Loading…
x
Reference in New Issue
Block a user