mirror of
https://gitlab.com/niansa/pilang3.git
synced 2025-03-06 20:49:20 +01:00
221 lines
9.3 KiB
C++
221 lines
9.3 KiB
C++
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <variant>
|
|
#include <exception>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <functional>
|
|
#include "pilang.hpp"
|
|
|
|
|
|
|
|
namespace Pilang3 {
|
|
std::unordered_map<std::string, std::tuple<Cmdfnc, uint16_t, std::vector<Variable::Type>, bool>> builtinCmds;
|
|
|
|
Evaluation::Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval) {
|
|
// Count off trailing spaces
|
|
ssize_t tspaces = 0;
|
|
for (const auto& character : expression) {
|
|
if (character == ' ' or character == '\t') {
|
|
tspaces++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// 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() + tspaces; 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 == ';') {
|
|
exprlen = std::distance(expression.begin(), characterit);
|
|
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 if (character == '{') {
|
|
// Function
|
|
thisarg.type = Variable::id_function;
|
|
} 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<Evaluation>(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;
|
|
case Variable::id_function: {
|
|
auto thisfunc = std::make_shared<Function>();
|
|
while (character != '}') {
|
|
// Evaluate
|
|
auto evaluation = std::make_shared<Evaluation>(env, std::string(characterit, expression.end()));
|
|
thisfunc->evalChain.push_back(evaluation);
|
|
// Skip over expression in iteration
|
|
character = *(characterit += evaluation->exprlen + 1);
|
|
}
|
|
character = *(++characterit);
|
|
thisarg.data = thisfunc;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 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 = env->globalVars.find(command_name);
|
|
if (res == env->globalVars.end() or res->second->type != Variable::id_function) {
|
|
throw exceptions::NoSuchCommand();
|
|
}
|
|
// Execute function
|
|
command = [res] (SharedEnvironment env, Cmdargs&) {
|
|
return std::get<SharedFunction>(res->second->data)->execute(env);
|
|
};
|
|
} else {
|
|
uint16_t argslen;
|
|
std::vector<Variable::Type> 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(SharedEnvironment env) {
|
|
if (command) {
|
|
return command(env, arguments);
|
|
} else {
|
|
throw exceptions::NoSuchCommand();
|
|
}
|
|
}
|
|
|
|
Variable Function::execute(SharedEnvironment env) {
|
|
Variable fres;
|
|
for (auto& evaluation : evalChain) {
|
|
fres = evaluation->execute(env);
|
|
if (fres.type == Variable::id_retval) {
|
|
return *std::get<SharedVariable>(fres.data);
|
|
}
|
|
}
|
|
return Variable({
|
|
Variable::id_null,
|
|
0
|
|
});
|
|
}
|
|
};
|