/* 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include // 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 cmdargs; std::map variables; std::map markers; bool interactivecli; bool catcherrs = false; std::string lasterr = ""; std::vector 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 strsplit(std::string_view s, char delimiter, std::vector::size_type times = 0) { std::vector 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 args #define command_rawtmpl(func) func(command_args) #define command_tmpl(func) std::string command_rawtmpl(func) typedef std::function command_type; // Function struct and map std::map 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 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 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 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 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; }