Added compiler, as stand-in replacement for interpreter

This commit is contained in:
Bob Polis 2022-08-19 08:47:07 +02:00
parent 37ef764af7
commit d600cfb7c5
2 changed files with 499 additions and 0 deletions

390
src/compiler.cpp Normal file
View 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
View 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_