moved sources into src directory

This commit is contained in:
Bob Polis
2020-11-08 13:26:27 +01:00
parent 08c45600d2
commit 5e9b21de30
5 changed files with 0 additions and 0 deletions

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