1
0
Fork 0
mirror of https://gitlab.com/niansa/pilang3.git synced 2025-03-06 20:49:20 +01:00
pilang3/pilang.cpp
2021-02-04 13:45:05 +01:00

226 lines
9.4 KiB
C++

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <variant>
#include <exception>
#include <memory>
#include <sstream>
#include <functional>
#include "pilang.hpp"
static inline bool is_sep(const char character) {
return character == ' ' or character == '\t' or character == '\n';
}
namespace Pilang3 {
std::unordered_map<std::string, Cmddesc> 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 (is_sep(character)) {
tspaces++;
} else {
break;
}
}
// Parse arguments if any
if (not expression.empty() and static_cast<ssize_t>(expression.size()) != tspaces) {
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 (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
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 (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) {
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 {
std::vector<Variable::Type> argstypes;
bool anymoreopts;
std::tie(command, argstypes, anymoreopts) = res->second;
// 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();
}
}
}
} else {
throw exceptions::emptyExpr();
}
}
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
});
}
};