1
0
Fork 0
mirror of https://gitlab.com/niansa/pilang2.git synced 2025-03-06 20:49:22 +01:00
pilang2/source/pilang2.cpp
2020-05-27 13:20:23 +02:00

615 lines
19 KiB
C++

/*
This file is part of pilang.
pilang is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pilang is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pilang. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <string>
#include <string_view>
#include <exception>
#include <map>
#include <vector>
#include <regex>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <csignal>
#include <unistd.h>
#include <wrapper.hh>
// Return type defs
std::string success = "Success";
std::string failed = "Failed";
std::string nosuchcommand = "No such command";
std::string nosuchvariable = "No such variable";
std::string badarguments = "Bad arguments";
// Exception defs
class markergoto : public std::string {};
class pilerror : public std::string {};
std::string markergotoname;
// Environment defs
std::vector<std::string> cmdargs;
std::map<std::string, std::string> variables;
std::map<std::string, int> markers;
bool interactivecli;
bool catcherrs = false;
std::string lasterr = "";
std::vector<std::string> markersetqueue;
std::string execline(std::string commandstr);
// Restart program on segmenation fault
void sigsegv_handler(int signal) {
std::cerr << std::endl << "Pilang crashed: signal " << signal << "=SIGSEGV (memory error)" << std::endl;
exit(177);
}
// Helper functions
bool is_float(std::string s) {
std::istringstream iss(s);
float f;
iss >> std::noskipws >> f;
return iss.eof() && !iss.fail();
}
std::string str_lower(std::string string) {
std::transform(string.begin(), string.end(), string.begin(), ::tolower);
return string;
}
std::string str_upper(std::string string) {
std::transform(string.begin(), string.end(), string.begin(), ::toupper);
return string;
}
std::string ltrim(const std::string& s) {
static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended};
return std::regex_replace(s, lws, "");
}
std::string rtrim(const std::string& s) {
static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended};
return std::regex_replace(s, tws, "");
}
std::string trim(const std::string& s) {
return ltrim(rtrim(s));
}
std::vector<std::string> strsplit(std::string_view s, char delimiter, std::vector<std::string>::size_type times = 0) {
std::vector<std::string> to_return;
decltype(s.size()) start = 0, finish = 0;
while ((finish = s.find_first_of(delimiter, start)) != std::string_view::npos) {
to_return.emplace_back(s.substr(start, finish - start));
start = finish + 1;
if (to_return.size() == times) { break; }
}
to_return.emplace_back(s.substr(start));
return to_return;
}
void do_markersetqueue(int currline) {
for (const std::string & markername : markersetqueue) {
markers[markername] = currline;
}
markersetqueue = {};
}
std::string get_rstring(std::string rstring) {
if (rstring == nosuchcommand or rstring == nosuchvariable or rstring == badarguments) {
lasterr = rstring;
if (catcherrs)
throw pilerror();
}
return rstring;
}
// Builtin functions
std::string cmd_ret(std::vector<std::string> args) {
std::string retstring;
for (const std::string & arg : args){
retstring.append(arg);
}
return retstring;
}
std::string cmd_catch(std::vector<std::string> args) {
if (args.size() != 1) {
return badarguments;
}
std::string boolstr = *args.begin();
if (str_lower(boolstr) == "true")
catcherrs = true;
else if (str_lower(boolstr) == "false")
catcherrs = false;
else
return badarguments;
return success;
}
std::string cmd_get(std::vector<std::string> args) {
if (args.size() != 2) {
return badarguments;
}
auto argsit = args.begin();
std::string string = *argsit;
argsit++;
long unsigned int position = std::stoi(*argsit);
if (position < string.length()) {
std::string res;
res.push_back(string.at(position));
return res;
} else {
return badarguments;
}
}
std::string cmd_len(std::vector<std::string> args) {
int result = 0;
for (const std::string & arg : args){
result += arg.length();
}
return std::to_string(result);
}
std::string cmd_exit(std::vector<std::string> args) {
//Check amount of arguments
if (args.size() != 1) {
return badarguments;
}
std::string exitcode = *args.begin();
if (is_float(exitcode))
_exit(std::stoi(exitcode));
else
return badarguments;
}
std::string cmd_marker(std::vector<std::string> args) {
// Check amount of arguments
if (args.size() != 2) {
return badarguments;
}
// Get arguments
auto argsit = args.begin();
std::string action = *argsit;
argsit++;
std::string markername = *argsit;
// Set or goto
if (action == "goto") {
markergotoname = markername;
throw markergoto();
} else if (action == "set") {
markersetqueue.push_back(markername);
} else {
return badarguments;
}
return success;
}
std::string cmd_printnnl(std::vector<std::string> args) {
std::cout << cmd_ret(args) << std::flush;
return success;
}
std::string cmd_print(std::vector<std::string> args) {
auto result = cmd_printnnl(args);
std::cout << std::endl;
return result;
}
std::string cmd_input(std::vector<std::string> args) {
std::string hint = cmd_ret(args);
cmd_printnnl({hint});
std::string result;
std::getline(readkbd(hint), result);
return result;
}
std::string cmd_set(std::vector<std::string> args) {
//Check amount of arguments
if (args.size() != 2) {
return badarguments;
}
// Get arguments
auto argsit = args.begin();
std::string key = *argsit;
argsit++;
std::string value = *argsit;
// Store in environment
variables[key] = value;
// Return success
return success;
}
std::string cmd_append(std::vector<std::string> args) {
//Check amount of arguments
if (args.size() < 2) {
return badarguments;
}
// Get arguments
auto argsit = args.begin();
std::string variablename = *argsit;
argsit++;
for (; argsit != args.end(); argsit++)
variables[variablename].append(*argsit);
return success;
}
std::string cmd_incdec(bool type, std::vector<std::string> args) {
//Check amount of arguments
if (args.size() < 1 or args.size() > 2) {
return badarguments;
}
// Get arguments
auto argsit = args.begin();
std::string variablename = *argsit;
float incby;
if (args.size() == 2) {
argsit++;
incby = std::atof((*argsit).c_str());
} else {
incby = 1;
}
// Modify variable if it exists
if (variables.find(variablename) == variables.end()) {
return nosuchvariable;
} else {
float currval = std::atof(variables[variablename].c_str());
if (type)
currval += incby;
else
currval -= incby;
variables[variablename] = std::to_string(currval);
}
return success;
}
std::string cmd_del(std::vector<std::string> args) {
if (args.size() < 1) {
return badarguments;
}
for (const std::string & arg : args) {
auto varit = variables.find(arg);
if (varit != variables.end()) {
variables.erase(variables.find(arg));
}
}
return success;
}
std::string cmd_eval(std::vector<std::string> args) {
if (args.size() < 1) {
return badarguments;
}
std::string result;
for (const std::string & arg : args) {
result = execline(arg);
}
return result;
}
std::string cmd_cmp(std::vector<std::string> args) {
bool matches;
//Check amount of arguments
if (args.size() < 4) {
return badarguments;
}
// Get arguments
auto argsit = args.begin();
std::string action = *argsit;
argsit++;
std::string var1 = *argsit;
argsit++;
std::string var2 = *argsit;
argsit++;
// Compare
if (action == "equal")
matches = var1 == var2;
else if (action == "nonequal")
matches = var1 != var2;
else if (action == "bigger")
matches = std::atof(var1.c_str()) > std::atof(var2.c_str());
else if (action == "smaller")
matches = std::atof(var1.c_str()) < std::atof(var2.c_str());
else if (action == "isnum" or action == "isnotnum") {
matches = is_float(var1) and is_float(var2);
if (action == "isnotnum")
matches = !matches;
} else
return badarguments;
// Eval on match
if (matches) {
std::string result;
while (argsit != args.end()) {
result = execline(*argsit);
argsit++;
}
return result;
} else {
return failed;
}
}
std::string cmd_lowerupper(bool type, std::vector<std::string> args) {
if (args.size() < 1) {
return badarguments;
}
std::string result;
for (auto argsit = args.begin(); argsit != args.end(); argsit++) {
if (type)
result.append(str_upper(*argsit));
else
result.append(str_lower(*argsit));
}
return result;
}
std::string cmd_addsub(bool type, std::vector<std::string> args) {
//Check amount of arguments
if (args.size() < 2) {
return badarguments;
}
// Initialise variables
bool asint = false;
float numarg;
// Get arguments
auto argsit = args.begin();
if (*argsit == "asint") {
asint = true;
argsit++;
}
// Initialise initial result
if (!is_float(*argsit))
return badarguments;
float result = std::atof((*argsit).c_str());
argsit++;
// Calculaté
for (; argsit != args.end(); argsit++) {
if (!is_float(*argsit))
return badarguments;
numarg = std::atof((*argsit).c_str());
if (type)
result += numarg;
else
result -= numarg;
}
// Return result
if (asint)
return std::to_string((int) result);
else
return std::to_string(result);
}
std::string cmd_sleep(std::vector<std::string> args) {
// Check amount of arguments
if (args.size() != 1) {
return badarguments;
}
// Check argument
auto argsit = args.begin();
if (!is_float(*argsit))
return badarguments;
// Get argument
float sleepsecs = std::atof((*argsit).c_str());
sleep(sleepsecs);
return success;
}
std::string cmd_argv(std::vector<std::string> args) {
// Check amount of arguments
if (args.size() != 1) {
return badarguments;
}
// Check argument
auto argsit = args.begin();
if (!is_float(*argsit))
return badarguments;
long unsigned int argnum = std::atoi((*argsit).c_str());
if (argnum >= cmdargs.size())
return badarguments;
// Get argument
return cmdargs[argnum];
}
std::string cmd_argc(std::vector<std::string> args) {
return std::to_string(cmdargs.size());
}
std::string run_command(std::string command, std::vector<std::string> args) {
//std::cout << "Running command:" << command << "!" << std::endl;
if (command == "ret")
return cmd_ret(args);
else if (command == "catch")
return cmd_catch(args);
else if (command == "get")
return cmd_get(args);
else if (command == "len")
return cmd_len(args);
else if (command == "exit")
return cmd_exit(args);
else if (command == "marker")
return cmd_marker(args);
else if (command == "print")
return cmd_print(args);
else if (command == "printnnl")
return cmd_printnnl(args);
else if (command == "input")
return cmd_input(args);
else if (command == "set")
return cmd_set(args);
else if (command == "append")
return cmd_append(args);
else if (command == "inc")
return cmd_incdec(true, args);
else if (command == "dec")
return cmd_incdec(false, args);
else if (command == "del")
return cmd_del(args);
else if (command == "eval")
return cmd_eval(args);
else if (command == "cmp")
return cmd_cmp(args);
else if (command == "lower")
return cmd_lowerupper(false, args);
else if (command == "upper")
return cmd_lowerupper(true, args);
else if (command == "add")
return cmd_addsub(true, args);
else if (command == "sub")
return cmd_addsub(false, args);
else if (command == "sleep")
return cmd_sleep(args);
else if (command == "argv")
return cmd_argv(args);
else if (command == "argc")
return cmd_argc(args);
else
return nosuchcommand;
}
// Interpreter initialiser
void init_interpreter(int argc, char *argv[]) {
// Load modules from modules directory
//struct dirent *dp;
//DIR *dfd;
//char filename_qfd[264];
//char moduledir [] = "modules";
//dfd = opendir(moduledir);
//while ((dp = readdir(dfd)) != NULL) {
// sprintf(filename_qfd , "%s/%s",moduledir,dp->d_name);
// // Skip . and ..
// if (strcmp(dp->d_name,"..") or strcmp(dp->d_name,"."))
// continue;
// // Load file
// std::cout << "Module loading not yet implemented for: " << filename_qfd << std::endl;
//}
// Generate list of arguments
for (int elemnum = 0; elemnum < argc; elemnum++) {
cmdargs.push_back(std::string(argv[elemnum]));
}
}
std::string execline(std::string commandstr) {
commandstr = ltrim(commandstr);
if (commandstr[0] == '#')
return success;
//std::cout << "$ " << commandstr << std::endl;
if (rtrim(commandstr) == "") { // Command is empty
return commandstr;
} else if (commandstr.rfind('~', 0) == 0) { // Command is a variable
commandstr.erase(0, 1);
// Check if variable exists
if (variables.find(commandstr) == variables.end()) {
return nosuchvariable;
} else {
return variables[commandstr];
}
} else { // Command is an actual command
std::vector<std::string> cmdsplit = strsplit(commandstr, ' ', 1);
auto cmdsplit_it = cmdsplit.begin();
std::string command = *cmdsplit_it;
std::string argsstr = "";
cmdsplit_it++;
if (cmdsplit.size() == 2) {
argsstr = *cmdsplit_it;
argsstr.push_back(',');
}
std::vector<std::string> args;
if (argsstr != ",") {
std::string currarg = "";
bool instring = false;
bool arginstring = false;
bool charig = false;
bool skipall = false;
for (std::string::iterator currchar = argsstr.begin(); currchar < argsstr.end(); currchar++) {
if (skipall and *currchar != ',') {
continue;
} else if (*currchar == '"' and !skipall) {
instring = !instring;
if (currarg == "")
arginstring = true;
} else if (*currchar == '#' and !charig and !instring) {
*(currchar + 1) = ',';
skipall = true;
} else if (*currchar == ',' and !charig and !instring) {
if (currarg.length() != 0 and *currarg.begin() == '$' and !arginstring) {
currarg.erase(0, 1);
currarg = execline(currarg);
}
args.push_back(currarg);
currarg = "";
arginstring = false;
} else if (*currchar == '\\' and !charig) {
charig = true;
} else {
currarg.push_back(*currchar);
charig = false;
}
}
}
//std::cout << "Args len: " << args.size() << std::endl;
return get_rstring(run_command(command, args));
}
}
// Main CLI
int _main(int argc, char *argv[]) {
using namespace std;
signal(SIGSEGV, sigsegv_handler);
std::string line;
if (argc < 2) { // Start interactive CLI
// Show copyright note
cout << "pilang Copyright (C) 2020 niansa" << endl;
cout << "This program comes with ABSOLUTELY NO WARRANTY; for details type `warranty'." << endl;
cout << "This is free software, and you are welcome to redistribute it" << endl;
cout << "under certain conditions; type `license' for details." << endl;
cout << endl;
// Initialise stuff
init_interpreter(argc, argv);
interactivecli = true;
// Input loop
while (true) {
line = "";
cout << ">>> " << flush;
std::getline(readkbd("Type in command..."), line);
try {
cout << execline(line) << endl;
} catch (markergoto) { // Catch marker goto
cerr << "Unable to use markers in CLI mode!" << endl;
} catch (pilerror) {
cerr << "In CLI: " << lasterr << " (catched)" << endl;
}
}
} else {
// Read from argv[1] into vector
std::ifstream scriptfile(argv[1]);
std::vector<std::string> lines;
std::string line;
while (std::getline(scriptfile, line)) {
lines.push_back(line);
if (scriptfile.eof()) { break; } // EOF
}
scriptfile.close();
// Initialise stuff
init_interpreter(argc, argv);
interactivecli = false;
// Add initial marker in line 0
markersetqueue.push_back("start");
do_markersetqueue(0);
// Execute vector line-by-line
auto linesit = lines.begin();
for (int currline = 0; linesit != lines.end(); currline++) {
try {
execline(*linesit);
do_markersetqueue(currline); // Do markerqueue
} catch (markergoto) { // Catch marker goto
do_markersetqueue(currline); // Do markerqueue even if an exception occured
if (markers.find(markergotoname) != markers.end()) {
currline = markers[markergotoname];
linesit = lines.begin() + currline;
} else {
cerr << "No such marker: \"" << markergotoname << "\" (critical)" << endl;
return 78;
}
} catch (pilerror) {
cerr << "Line " << currline << ": " << lasterr << " (catched)" << endl;
return 77;
}
linesit++;
}
}
return 0;
}