curly/interpreter.cpp

383 lines
8.4 KiB
C++
Raw Normal View History

//
// interpreter.cpp
// curly
//
// Created by Bob Polis at 2020-09-05
// Copyright (c) 2020 SwiftCoder. All rights reserved.
//
#include "interpreter.hpp"
#include <cctype>
#include <algorithm>
2020-09-22 13:38:17 +02:00
#include <sstream>
int to_int(const std::string& val) {
std::istringstream iss {val};
int result;
iss >> result;
return result;
}
void interpreter::reset() {
_prog.clear();
_values.clear();
_labels.clear();
_vars.clear();
_calls.clear();
_pc_offsets.clear();
_pc = 0;
}
std::string interpreter::eval(std::istream& in, bool& done) {
std::string result;
reset();
done = false;
// first pass: read program & resolve labels, ignore comments
for (std::string line; std::getline(in, line);) {
if (line[0] == ':') { // check label definition
_labels.emplace(line.substr(1), _prog.size());
_pc_offsets.push_back(_prog.size());
} else if (line[0] == '#') { // check comment
_pc_offsets.push_back(_prog.size());
} else {
_prog.push_back(line);
}
}
// second pass: run program
while (_pc < _prog.size() && !done) {
// fetch next instruction
std::string code {_prog[_pc]};
// check literal int
if (std::isdigit(code[0])) {
_values.push_back(code);
_pc++;
continue;
}
// check literal string
if (code[0] == '\\') {
_values.push_back(code.substr(1));
_pc++;
continue;
}
// check label ref
if (code[0] == '>') {
_values.push_back(code.substr(1));
_pc++;
continue;
}
// check var assignment
if (code[0] == '=') {
_vars[code.substr(1)] = _values.back();
_values.pop_back();
_pc++;
continue;
}
// check var ref
if (code[0] == '$') {
_values.push_back(_vars[code.substr(1)]);
_pc++;
continue;
}
// exec instruction
exec_instruction(code, done);
}
if (_values.size()) {
result = _values.back();
}
return result;
}
void interpreter::exec_instruction(const std::string& code, bool& done) {
if (code == "end") done = true;
else if (code == "add") add();
else if (code == "sub") sub();
else if (code == "mul") mul();
else if (code == "div") div();
else if (code == "mod") mod();
else if (code == "abs") abs();
else if (code == "neg") neg();
else if (code == "dup") dup();
else if (code == "inc") inc();
else if (code == "dec") dec();
else if (code == "rev") rev();
else if (code == "slc") slc();
else if (code == "idx") idx();
else if (code == "cat") cat();
else if (code == "len") len();
else if (code == "rot") rot();
else if (code == "gto") gto();
else if (code == "geq") geq();
else if (code == "gne") gne();
else if (code == "glt") glt();
else if (code == "gle") gle();
else if (code == "ggt") ggt();
else if (code == "gge") gge();
else if (code == "fun") fun();
else if (code == "ret") ret();
else if (code == "enl") enl();
2020-09-21 10:17:16 +02:00
else if (code == "inp") inp();
else if (code == "out") out();
else if (code == "err") err();
else {
auto it = std::upper_bound(_pc_offsets.begin(), _pc_offsets.end(), _pc);
long num_removed_source_lines {it - _pc_offsets.begin()};
size_t lineno {num_removed_source_lines + _pc + 1};
throw syntax_error {code, lineno};
}
}
inline int interpreter::pop_int() {
int val {to_int(_values.back())};
_values.pop_back();
return val;
}
inline std::string interpreter::pop_str() {
auto val = _values.back();
_values.pop_back();
return val;
}
// integer operations -----------------------------------------------------
void interpreter::add() {
auto val2 = pop_int();
auto val1 = pop_int();
_values.push_back(std::to_string(val1 + val2));
_pc++;
}
void interpreter::sub() {
auto val2 = pop_int();
auto val1 = pop_int();
_values.push_back(std::to_string(val1 - val2));
_pc++;
}
void interpreter::mul() {
auto val2 = pop_int();
auto val1 = pop_int();
_values.push_back(std::to_string(val1 * val2));
_pc++;
}
void interpreter::div() {
auto val2 = pop_int();
auto val1 = pop_int();
_values.push_back(std::to_string(val1 / val2));
_pc++;
}
void interpreter::mod() {
auto val2 = pop_int();
auto val1 = pop_int();
_values.push_back(std::to_string(val1 % val2));
_pc++;
}
void interpreter::abs() {
auto val = pop_int();
_values.push_back(std::to_string(val < 0 ? -val : val));
_pc++;
}
void interpreter::neg() {
auto val = pop_int();
_values.push_back(std::to_string(-val));
_pc++;
}
void interpreter::inc() {
auto val = pop_int();
_values.push_back(std::to_string(val + 1));
_pc++;
}
void interpreter::dec() {
auto val = pop_int();
_values.push_back(std::to_string(val - 1));
_pc++;
}
// string operations ------------------------------------------------------
void interpreter::dup() {
_values.push_back(_values.back());
_pc++;
}
void interpreter::rev() {
auto val = pop_str();
std::string result;
for (auto it = val.rbegin(); it != val.rend(); ++it) {
result += *it;
}
_values.push_back(result);
_pc++;
}
void interpreter::slc() {
auto to = pop_int();
auto from = pop_int();
auto val = pop_str();
_values.push_back(val.substr(from, to - from));
_pc++;
}
void interpreter::idx() {
auto idx = pop_int();
auto val = pop_str();
val = val[idx];
_values.emplace_back(val);
_pc++;
}
void interpreter::cat() {
auto val2 = pop_str();
auto val1 = pop_str();
_values.emplace_back(val1 + val2);
_pc++;
}
void interpreter::len() {
auto val = pop_str();
_values.push_back(std::to_string(val.size()));
_pc++;
}
void interpreter::rot() {
auto val = 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;
});
_values.push_back(val);
_pc++;
}
void interpreter::enl() {
auto val = pop_str();
_values.emplace_back(val + '\n');
_pc++;
}
// tests & jumps ----------------------------------------------------------
void interpreter::gto() {
_pc = _labels[pop_str()];
}
void interpreter::geq() {
auto label = pop_str();
auto val2 = pop_str();
auto val1 = pop_str();
if (val1 == val2) {
_pc = _labels[label];
} else {
_pc++;
}
}
void interpreter::gne() {
auto label = pop_str();
auto val2 = pop_str();
auto val1 = pop_str();
if (val1 != val2) {
_pc = _labels[label];
} else {
_pc++;
}
}
void interpreter::glt() {
auto label = pop_str();
auto val2 = pop_int();
auto val1 = pop_int();
if (val1 < val2) {
_pc = _labels[label];
} else {
_pc++;
}
}
void interpreter::gle() {
auto label = pop_str();
auto val2 = pop_int();
auto val1 = pop_int();
if (val1 <= val2) {
_pc = _labels[label];
} else {
_pc++;
}
}
void interpreter::ggt() {
auto label = pop_str();
auto val2 = pop_int();
auto val1 = pop_int();
if (val1 > val2) {
_pc = _labels[label];
} else {
_pc++;
}
}
void interpreter::gge() {
auto label = pop_str();
auto val2 = pop_int();
auto val1 = pop_int();
if (val1 >= val2) {
_pc = _labels[label];
} else {
_pc++;
}
}
// subroutines ------------------------------------------------------------
void interpreter::fun() {
_calls.push_back(_pc + 1); // push return address
gto();
}
void interpreter::ret() {
_pc = _calls.back();
_calls.pop_back();
}
// debugging --------------------------------------------------------------
2020-09-21 10:17:16 +02:00
void interpreter::inp() {
std::string val;
std::cin >> val;
_values.push_back(val);
2020-09-21 10:17:16 +02:00
_pc++;
}
void interpreter::out() {
std::cout << _values.back() << '\n';
_pc++;
}
2020-09-21 10:17:16 +02:00
void interpreter::err() {
std::cerr << _values.back() << '\n';
2020-09-21 10:17:16 +02:00
_pc++;
}