#include #include #include #include #include #include #include #include #include #include "pilang.hpp" namespace Pilang3 { std::unordered_map, bool>> builtinCmds = { {"concat", {[] (Environment& env, Cmdargs& args) { std::ostringstream fres; for (const auto& arg : args) { switch (arg.type) { case Variable::id_integer: fres << std::get(arg.data); break; case Variable::id_string: fres << std::get(arg.data); break; case Variable::id_reference: fres << std::get(arg.data).get(); break; case Variable::id_type: fres << std::get(arg.data); break; default: fres << "'; break; } } return Variable({ Variable::id_string, fres.str() }); }, 0, {}, true}} }; Evaluation::Evaluation(Environment& env, const std::string &expression, const bool autoeval) { // Parse arguments if any if (not expression.empty()) { Variable thisarg; bool in_command_name = true; bool newarg = false; bool escapenext = false; std::string cache; for (auto characterit = expression.begin(); characterit != expression.end(); characterit++) { char character = *characterit; // Start new command name if (in_command_name) { if (character != ' ' and character != '\n' and character != ';') { cache.push_back(character); } else { command_name = cache; cache.clear(); if (character == ';') { break; } else { in_command_name = false; newarg = true; } } } // Start new argument else if (newarg) { // Skip spaces if (character == ' ' or character == ',' or character == '\n' or character == ';') continue; // Guess type by first character newarg = false; if (std::isdigit(character) or character == '-' or character == '+') { // Integer thisarg.type = Variable::id_integer; goto parsechar; } else if (character == '"') { // String thisarg.type = Variable::id_string; } else if (character == '&') { // Expression thisarg.type = Variable::id_evaluation; } else { // Reference thisarg.type = Variable::id_reference; goto parsechar; } } else { parsechar: bool maybe_end_of_arg = character == ',' or character == ';'; // Do whatever (depends on type) switch (thisarg.type) { case Variable::id_integer: { // Integer if (character == ' ') continue; else if (maybe_end_of_arg) { // End argument try { thisarg.data = integer_type(std::stoi(cache)); } catch (std::invalid_argument&) { throw exceptions::BadInteger(); } newarg = true; } else { cache.push_back(character); } } break; case Variable::id_string: { // String if (character == '\\' and not escapenext) { escapenext = true; } else if (character == '"' and not escapenext) { thisarg.data = cache; newarg = true; character = *(++characterit); } else { cache.push_back(character); } } break; case Variable::id_reference: { // Reference if (maybe_end_of_arg) { auto res = env.globalVars.find(cache); if (res == env.globalVars.end()) { throw exceptions::NoSuchVariable(); } thisarg = *res->second; newarg = true; } else { cache.push_back(character); } } break; case Variable::id_evaluation: { // Check for exclaimation mark bool noexec = *characterit == '!'; if (noexec) { // Skip exclaimation mark if required characterit++; } // Evaluate auto evaluation = std::make_shared(env, std::string(characterit, expression.end())); // Execute if possible if (autoeval and not noexec) { thisarg = evaluation->execute(env); } else { thisarg.data = evaluation; } // Skip over expression in iteration character = *(characterit += evaluation->exprlen + 1); newarg = true; } break; } // Actually end argument if required if (newarg) { arguments.push_back(thisarg); cache.clear(); // Check if end of command was reached if (character == ';') { exprlen = std::distance(expression.begin(), characterit); break; } } } } // Find command auto res = Pilang3::builtinCmds.find(command_name); if (res == Pilang3::builtinCmds.end()) { throw exceptions::NoSuchCommand(); } uint16_t argslen; std::vector argstypes; bool anymoreopts; std::tie(command, argslen, argstypes, anymoreopts) = res->second; // Check args if ((arguments.size() > argslen and not anymoreopts) or (arguments.size() < argslen)) { throw exceptions::BadArgs(); } for (uint16_t argidx = 0; argidx != argslen; argidx++) { if (argstypes[argidx] != Variable::id_any and argstypes[argidx] != arguments[argidx].type) { throw exceptions::BadArgs(); } } } } Variable Evaluation::execute(Environment& env) { if (command) { return command(env, arguments); } else { throw exceptions::NoSuchCommand(); } } };