#include #include #include #include #include #include #include #include #include #include "pilang.hpp" static inline bool is_sep(const char character) { return character == ' ' or character == '\t' or character == '\n'; } namespace Pilang3 { std::unordered_map builtinCmds; Condition::Condition(const std::string& expression) { for (auto characterit = expression.begin(); characterit < expression.end(); characterit++) { if (*characterit == ',') { exprlen = std::distance(expression.begin(), characterit); break; } } } Evaluation::Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval, const bool autoderef) { // Count off trailing spaces ssize_t tspaces = 0; for (const auto& character : expression) { if (is_sep(character)) { tspaces++; } else { break; } } // Get scope auto &scope = env->currScope(); // Parse arguments if any if (not expression.empty() and static_cast(expression.size()) != tspaces) { Variable thisarg; bool in_command_name = true; bool in_string = false; bool newarg = false; bool escapenext = false; std::string cache; for (auto characterit = expression.begin() + tspaces; characterit < expression.end(); characterit++) { char character = *characterit; // Start new command name if (in_command_name) { if (not is_sep(character) and character != ';') { cache.push_back(character); } else { command_name = cache; cache.clear(); if (character == ';') { exprlen = std::distance(expression.begin(), characterit); break; } else { in_command_name = false; newarg = true; } } } // Start new argument else if (newarg) { // Skip spaces if (is_sep(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 in_string = true; thisarg.type = Variable::id_string; } else if (character == '&') { // Expression thisarg.type = Variable::id_evaluation; } else if (character == '{' or character == '(') { // Function thisarg.type = Variable::id_function; goto parsechar; } else if (character == '!') { // Function thisarg.type = Variable::id_condition; goto parsechar; } 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 (is_sep(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) { std::get(thisarg.data).append(cache); cache.clear(); in_string = not in_string; } else if (in_string and character == '\\' and not escapenext) { escapenext = true; } else if (in_string) { cache.push_back(character); } else if (maybe_end_of_arg) { newarg = true; } } break; case Variable::id_reference: { // Reference if (maybe_end_of_arg) { if (autoderef) { auto res = scope.find(cache); if (res == scope.end()) { throw exceptions::NoSuchVariable(); } thisarg = *res->second; } else { thisarg.data = cache; } 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()), autoeval, autoderef); // Execute if possible if (autoeval and not noexec) { thisarg = evaluation->execute(env); } else { thisarg.data = SharedEvaluationWBool{evaluation, not noexec}; } // Skip over expression in iteration character = *(characterit += evaluation->exprlen + 1); newarg = true; } break; case Variable::id_function: { auto thisfunc = std::make_shared(); if (character == '(') { thisfunc->new_scope = true; // Parse while (++characterit != expression.end()) { if (*characterit == ',' or *characterit == ')') { if (not cache.empty()) { thisfunc->argumentNames.push_back(cache); cache.clear(); } if (*characterit == ')') { break; } else { continue; } } else if (is_sep(*characterit)) { // Check if seperator is after ( or , if (cache.empty()) { // If yes; ignore character continue; } else { // If no, go on } } cache.push_back(*characterit); } // Skip spaces while (characterit + 1 != expression.end() and is_sep(*(++characterit))); // Check for { if (*characterit != '{') { throw exceptions::UnexpectedEndOfExpression(); } } // Parse expression itself characterit++; while (*characterit != '}') { // Evaluate SharedEvaluation evaluation; try { evaluation = std::make_shared(env, std::string(characterit, expression.end()), false, false); } catch (exceptions::emptyExpr&) { throw exceptions::UnexpectedEndOfExpression(); } thisfunc->evalChain.push_back(evaluation); // Skip over expression in iteration characterit += evaluation->exprlen + 1; if (characterit == expression.end()) { throw exceptions::UnexpectedEndOfExpression(); } } // Append character = *(++characterit); thisarg.data = thisfunc; newarg = true; } break; case Variable::id_condition: { //TODO } break; } // Check if argument might be a condition //TODO // 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; } } } } // Check exprlen if (exprlen < 0) { throw exceptions::UnexpectedEndOfExpression(); } // Find command auto res = Pilang3::builtinCmds.find(command_name); if (res == Pilang3::builtinCmds.end()) { // Find function auto res = scope.find(command_name); if (res == scope.end() or res->second->type != Variable::id_function) { command = {command_name, {nullptr, {}, false}}; } else { auto fnc = std::get(res->second->data); // Create argument list std::vector argtype_list; argtype_list.reserve(fnc->argumentNames.size()); for (auto counter = fnc->argumentNames.size(); counter != 0; counter--) { argtype_list.push_back(Variable::id_any); } // Command is function executor command = {"", { [fnc] (SharedEnvironment env, Cmdargs& args) { return fnc->execute(env, args); }, argtype_list, false }}; } } else { std::get<1>(command) = res->second; } } else { throw exceptions::emptyExpr(); } } void Evaluation::derefer(SharedEnvironment env) { for (auto& arg : arguments) { switch (arg.type) { case Variable::id_evaluation: { SharedEvaluation evaluation; bool exec; std::tie(evaluation, exec) = std::get(arg.data); // Execute evaluation only if allowed (ie not &!) if (exec) { evaluation->derefer(env); arg = evaluation->execute(env); } } break; case Variable::id_reference: { auto &scope = env->currScope(); auto res = scope.find(std::get(arg.data)); if (res == scope.end()) { throw exceptions::NoSuchVariable(); } arg = *res->second; } break; default: continue; } } } Variable Evaluation::execute(SharedEnvironment env) { std::vector argstypes; bool anymoreopts; Cmdfnc func; Cmddesc desc; std::string name; std::tie(name, desc) = command; std::tie(func, argstypes, anymoreopts) = desc; if (func) { // Check args if ((arguments.size() > argstypes.size() and not anymoreopts) or (arguments.size() < argstypes.size())) { throw exceptions::BadArgs(); } for (uint16_t argidx = 0; argidx != argstypes.size(); argidx++) { if (argstypes[argidx] != Variable::id_any and argstypes[argidx] != arguments[argidx].type) { throw exceptions::BadArgs(); } } // Run return func(env, arguments); } else { // Find function auto &scope = env->currScope(); auto res = scope.find(command_name); if (res == scope.end() or res->second->type != Variable::id_function) { throw exceptions::NoSuchCommand(); } else { // Execute function return std::get(res->second->data)->execute(env, arguments); } } } Variable Function::execute(SharedEnvironment env, Cmdargs& args) { // Check if enough arguments were given if (args.size() != argumentNames.size()) { throw exceptions::BadArgs(); } // Go on Variable fres; // Apply arguments to new scope if (new_scope) { auto scope = env->currScope(); scope.reserve(args.size()); for (auto args_idx = args.size(); args_idx != 0; args_idx--) { scope[argumentNames[args_idx - 1]] = std::make_shared(args[args_idx - 1]); } env->variableScope.push(std::move(scope)); // Start new scope } // Copy, derefer and execute all evaluations for (auto& evaluation : evalChain) { auto thiseval = Evaluation(*evaluation); thiseval.derefer(env); fres = thiseval.execute(env); // Return on return if (fres.type == Variable::id_retval and new_scope) { auto fresdata = std::get(fres.data); if (new_scope) env->variableScope.pop(); // End scope return *fresdata; } } if (new_scope) env->variableScope.pop(); // End scope return Variable({ Variable::id_null, 0 }); } };