moved sources into src directory
This commit is contained in:
354
src/interpreter.cpp
Normal file
354
src/interpreter.cpp
Normal file
@ -0,0 +1,354 @@
|
||||
//
|
||||
// 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>
|
||||
#include <sstream>
|
||||
|
||||
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]};
|
||||
|
||||
if (std::isdigit(code[0])) { // check literal int
|
||||
_values.push_back(code);
|
||||
_pc++;
|
||||
} else if (code[0] == '\\' || code[0] == '>') { // check literal string or label ref
|
||||
_values.push_back(code.substr(1));
|
||||
_pc++;
|
||||
} else if (code[0] == '=') { // check var assignment
|
||||
_vars[code.substr(1)] = _values.back();
|
||||
_values.pop_back();
|
||||
_pc++;
|
||||
} else if (code[0] == '$') { // check var ref
|
||||
_values.push_back(_vars[code.substr(1)]);
|
||||
_pc++;
|
||||
} else {
|
||||
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();
|
||||
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() {
|
||||
std::istringstream iss {pop_str()};
|
||||
int result;
|
||||
iss >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// I/O --------------------------------------------------------------------
|
||||
|
||||
void interpreter::inp() {
|
||||
std::string val;
|
||||
std::cin >> val;
|
||||
_values.push_back(val);
|
||||
_pc++;
|
||||
}
|
||||
|
||||
void interpreter::out() {
|
||||
std::cout << _values.back() << '\n';
|
||||
_pc++;
|
||||
}
|
||||
|
||||
void interpreter::err() {
|
||||
std::cerr << _values.back() << '\n';
|
||||
_pc++;
|
||||
}
|
88
src/interpreter.hpp
Normal file
88
src/interpreter.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// interpreter.hpp
|
||||
// curly
|
||||
//
|
||||
// Created by Bob Polis at 2020-09-05
|
||||
// Copyright (c) 2020 SwiftCoder. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _interpreter_H_
|
||||
#define _interpreter_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
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 interpreter {
|
||||
public:
|
||||
interpreter() = default;
|
||||
|
||||
std::string eval(std::istream& in, bool& done);
|
||||
|
||||
private:
|
||||
std::vector<std::string> _prog; // program lines without label defs or comments
|
||||
std::vector<std::string> _values; // value stack
|
||||
std::vector<size_t> _calls; // call stack
|
||||
std::map<std::string, size_t> _labels; // label name => prog line index
|
||||
std::map<std::string, std::string> _vars; // variable name => string value
|
||||
size_t _pc {0}; // current program counter (index into _prog)
|
||||
std::vector<size_t> _pc_offsets; // removed line indices for: prog index => source line
|
||||
|
||||
void reset();
|
||||
void exec_instruction(const std::string& code, bool& done);
|
||||
|
||||
int pop_int();
|
||||
std::string pop_str();
|
||||
|
||||
// integer operations
|
||||
void add();
|
||||
void sub();
|
||||
void mul();
|
||||
void div();
|
||||
void mod();
|
||||
void abs();
|
||||
void neg();
|
||||
void inc();
|
||||
void dec();
|
||||
|
||||
// string operations
|
||||
void dup();
|
||||
void rev();
|
||||
void slc();
|
||||
void idx();
|
||||
void cat();
|
||||
void len();
|
||||
void rot();
|
||||
void enl();
|
||||
|
||||
// tests & jumps
|
||||
void gto();
|
||||
void geq();
|
||||
void gne();
|
||||
void glt();
|
||||
void gle();
|
||||
void ggt();
|
||||
void gge();
|
||||
|
||||
// subroutines
|
||||
void fun();
|
||||
void ret();
|
||||
|
||||
// I/O
|
||||
void inp();
|
||||
void out();
|
||||
void err();
|
||||
};
|
||||
|
||||
#endif // _interpreter_H_
|
94
src/main.cpp
Normal file
94
src/main.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// main.cpp
|
||||
// curly
|
||||
//
|
||||
// Created by Bob Polis at 2020-09-01
|
||||
// Copyright (c) 2020 SwiftCoder. All rights reserved.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <getopt.h>
|
||||
#include <libsclogging.hpp>
|
||||
#include "requester.hpp"
|
||||
#include "interpreter.hpp"
|
||||
|
||||
sc::logger logger {"curly", sc::loglevel::info};
|
||||
|
||||
void print_help() {
|
||||
std::cout << "usage: curly [-h|--version]\n";
|
||||
std::cout << " -h, --help show this help text and exit\n";
|
||||
std::cout << " --version show version number and exit\n";
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
std::cout << "curly version 1.0\n";
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
try {
|
||||
int opt_char, opt_val;
|
||||
struct option long_options[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"version", no_argument, &opt_val, 1},
|
||||
{nullptr, 0, nullptr, 0}
|
||||
};
|
||||
while ((opt_char = getopt_long(argc, const_cast<char* const *>(argv), "h", long_options, nullptr)) != -1) {
|
||||
std::string arg {optarg ? optarg : ""};
|
||||
switch (opt_char) {
|
||||
case 0: {
|
||||
// handle long-only options here
|
||||
switch (opt_val) {
|
||||
case 1:
|
||||
print_version();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case '?':
|
||||
throw std::runtime_error("unrecognized option");
|
||||
}
|
||||
}
|
||||
interpreter proc;
|
||||
bool done {false};
|
||||
if (optind == argc) {
|
||||
// here when no file args
|
||||
std::string base_url {"https://www.swiftcoder.nl/cpp1/"};
|
||||
std::string next_url {"start.txt"};
|
||||
std::string code;
|
||||
requester req;
|
||||
while (!done) {
|
||||
code = req.get(base_url + next_url);
|
||||
std::istringstream in {code};
|
||||
next_url = proc.eval(in, done);
|
||||
SCInfo(logger, next_url);
|
||||
}
|
||||
std::cout << next_url << '\n';
|
||||
}
|
||||
for (int i = optind; i < argc; ++i) {
|
||||
try {
|
||||
done = false;
|
||||
std::ifstream file {argv[i]};
|
||||
proc.eval(file, done);
|
||||
} catch (const syntax_error& err) {
|
||||
std::cerr << "curly: syntax error in " << argv[i];
|
||||
std::cerr << ", at line " << err.lineno();
|
||||
std::cerr << ": " << err.what() << '\n';
|
||||
} catch (const std::runtime_error& ex) {
|
||||
std::cerr << "curly: " << ex.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "curly: " << ex.what() << '\n';
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
42
src/requester.cpp
Normal file
42
src/requester.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// requester.cpp
|
||||
// curly
|
||||
//
|
||||
// Created by Bob Polis at 2020-09-01
|
||||
// Copyright (c) 2020 SwiftCoder. All rights reserved.
|
||||
//
|
||||
|
||||
#include "requester.hpp"
|
||||
#include <libsclogging.hpp>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
extern sc::logger logger;
|
||||
|
||||
size_t requester::write_data(char *buf, size_t sz, size_t nmemb, void *user_data) {
|
||||
size_t realsize = sz * nmemb;
|
||||
SCInfo(logger, "received ", realsize, " bytes");
|
||||
std::string* text {reinterpret_cast<std::string*>(user_data)};
|
||||
text->append(buf, realsize);
|
||||
return realsize;
|
||||
}
|
||||
|
||||
requester::requester() {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
_h.reset(curl_easy_init());
|
||||
curl_easy_setopt(_h.get(), CURLOPT_WRITEFUNCTION, write_data);
|
||||
}
|
||||
|
||||
requester::~requester() {
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
std::string requester::get(const std::string &url) {
|
||||
std::string text;
|
||||
curl_easy_setopt(_h.get(), CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(_h.get(), CURLOPT_WRITEDATA, &text);
|
||||
auto success = curl_easy_perform(_h.get());
|
||||
if (success != CURLE_OK) throw std::runtime_error("could not get remote data");
|
||||
return text;
|
||||
}
|
38
src/requester.hpp
Normal file
38
src/requester.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// requester.hpp
|
||||
// curly
|
||||
//
|
||||
// Created by Bob Polis at 2020-09-01
|
||||
// Copyright (c) 2020 SwiftCoder. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _requester_H_
|
||||
#define _requester_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <curl/curl.h>
|
||||
|
||||
class requester {
|
||||
public:
|
||||
// callback for data receiving
|
||||
static size_t write_data(char* buf, size_t sz, size_t nmemb, void* user_data);
|
||||
|
||||
// this class is a RAII class for a curl handle
|
||||
requester();
|
||||
~requester();
|
||||
|
||||
// forbid copying and moving
|
||||
requester(const requester&) = delete;
|
||||
requester& operator=(const requester&) = delete;
|
||||
requester(requester&&) = delete;
|
||||
requester& operator=(requester&&) = delete;
|
||||
|
||||
// perform a http get request
|
||||
std::string get(const std::string& url);
|
||||
|
||||
private:
|
||||
std::unique_ptr<CURL, void(*)(CURL*)> _h {nullptr, curl_easy_cleanup};
|
||||
};
|
||||
|
||||
#endif // _requester_H_
|
Reference in New Issue
Block a user