mirror of
https://gitlab.com/niansa/pilang2.git
synced 2025-03-06 20:49:22 +01:00
301 lines
10 KiB
C++
301 lines
10 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;
|
|
}
|
|
|
|
// Function template
|
|
#define command_args std::vector<std::string> args
|
|
#define command_rawtmpl(func) func(command_args)
|
|
#define command_tmpl(func) std::string command_rawtmpl(func)
|
|
typedef std::function<command_rawtmpl(std::string)> command_type;
|
|
|
|
// Function struct and map
|
|
std::map<std::string, command_type> commands;
|
|
#define command_register(func) commands[#func] = func;
|
|
|
|
// Module includes
|
|
#include "../modules/builtin.hh"
|
|
|
|
// Module loaders
|
|
void load_modules() {
|
|
// Execute your module registration functions here
|
|
register_builtin_commands();
|
|
}
|
|
|
|
// Function executor
|
|
std::string run_command(std::string command, std::vector<std::string> args) {
|
|
//std::cout << "Running command:" << command << "!" << std::endl;
|
|
if (commands.find("cmd_" + command) == commands.end()) {
|
|
return nosuchcommand;
|
|
} else {
|
|
return commands["cmd_" + command](args);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
load_modules();
|
|
// Start CLI or run file
|
|
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;
|
|
}
|