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