diff --git a/include/pilang.hpp b/include/pilang.hpp index 2706012..6edfec0 100644 --- a/include/pilang.hpp +++ b/include/pilang.hpp @@ -45,6 +45,7 @@ namespace Pilang3 { smallerthanorequals, ncpp_and, ncpp_or, + ncpp_not, _end }; using array = std::vector; @@ -104,7 +105,7 @@ namespace Pilang3 { std::string command_name; ssize_t exprlen = -1; - Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval = true, const bool autoderef = true); + Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval = true, const bool subderef = true); void derefer(SharedEnvironment env); Variable execute(SharedEnvironment env); diff --git a/main.cpp b/main.cpp index ff9fd9c..137f3bc 100644 --- a/main.cpp +++ b/main.cpp @@ -20,6 +20,7 @@ int main() { try { Evaluation evaluation(env, line); + evaluation.derefer(env); evaluation.execute(env); line.clear(); } catch (exceptions::emptyExpr&) { diff --git a/pilang.cpp b/pilang.cpp index 8b2afa8..516a945 100644 --- a/pilang.cpp +++ b/pilang.cpp @@ -13,6 +13,9 @@ static inline bool is_sep(const char character) { return character == ' ' or character == '\t' or character == '\n'; } +static inline bool is_end_of_arg(const char character) { + return character == ',' or character == ';'; +} namespace Pilang3 { @@ -28,7 +31,7 @@ namespace Pilang3 { } } - Evaluation::Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval, const bool autoderef) { + Evaluation::Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval, const bool subderef) { // Count off trailing spaces ssize_t tspaces = 0; for (const auto& character : expression) { @@ -48,6 +51,7 @@ namespace Pilang3 { bool is_condition = false; bool newarg = false; bool escapenext = false; + bool parsethis = true; condition::array condition_cache; std::string cache; for (auto characterit = expression.begin() + tspaces; characterit < expression.end(); characterit++) { @@ -74,6 +78,7 @@ namespace Pilang3 { if (is_sep(character)) continue; // Guess type by first character newarg = false; + parsethis = true; if (std::isdigit(character) or character == '-' or character == '+') { // Integer thisarg.type = Variable::id_integer; @@ -82,7 +87,6 @@ namespace Pilang3 { // String in_string = true; thisarg.type = Variable::id_string; - thisarg.data = std::string(); } else if (character == '&') { // Expression thisarg.type = Variable::id_evaluation; @@ -90,28 +94,33 @@ namespace Pilang3 { // Function thisarg.type = Variable::id_function; goto parsechar; - } else if (character == '!') { - // Condition - is_condition = true; - thisarg.type = Variable::id_null; - goto parsechar; - } else { + } else if (std::isalpha(character) or character == '_') { // Reference thisarg.type = Variable::id_reference; goto parsechar; + } else { + // Condition + if (not is_condition) { + is_condition = true; + arguments.pop_back(); + } + parsethis = false; + goto parsechar; } } else { parsechar: - bool maybe_end_of_arg = character == ',' or character == ';'; + bool maybe_end_of_arg = is_end_of_arg(character); // Do whatever (depends on type) + if (parsethis) switch (thisarg.type) { case Variable::id_integer: { // Integer - if (is_sep(character)) continue; - else if (maybe_end_of_arg) { + bool c_is_sep = is_sep(character); + if (c_is_sep and cache.empty()) continue; + else if (maybe_end_of_arg or c_is_sep) { // End argument try { - thisarg.data = integer_type(std::stoi(cache)); + thisarg.data = integer_type(std::stol(cache)); } catch (std::invalid_argument&) { throw exceptions::BadInteger(); } @@ -123,24 +132,19 @@ namespace Pilang3 { case Variable::id_string: { // String if (character == '"' and not escapenext) { - std::get(thisarg.data).append(cache); - cache.clear(); - in_string = not in_string; + thisarg.data = cache; + in_string = false; + newarg = true; } 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 (maybe_end_of_arg or is_sep(character)) { thisarg.data = cache; - if (autoderef) { - Variable::derefer(env, thisarg); - } newarg = true; } else { cache.push_back(character); @@ -154,7 +158,11 @@ namespace Pilang3 { characterit++; } // Evaluate - auto evaluation = std::make_shared(env, std::string(characterit, expression.end()), autoeval, autoderef); + auto evaluation = std::make_shared(env, std::string(characterit, expression.end()), autoeval, subderef); + // Derefer if possible + if (subderef) { + evaluation->derefer(env); + } // Execute if possible if (autoeval and not noexec) { thisarg = evaluation->execute(env); @@ -222,16 +230,18 @@ namespace Pilang3 { newarg = true; } break; } - if (not (in_string or newarg)) { - // Check if argument might be a condition - if (character == '!' or character == '=') { - is_condition = true; - } + if (not in_string) { // Continue building condition if (is_condition) { uint64_t cond_extends = condition::_none; + // Check if too close to end of expression + if (maybe_end_of_arg) { + goto cond_append; + } else if (characterit + 1 == expression.end()) { + throw exceptions::UnexpectedEndOfExpression(); + } // Equals - if (character == '=' and *(characterit + 1) == '=') { + else if (character == '=' and *(characterit + 1) == '=') { cond_extends = condition::equals; } // Not equals @@ -262,24 +272,36 @@ namespace Pilang3 { else if (character == '|' and *(characterit + 1) == '|') { cond_extends = condition::ncpp_or; } + // Not + else if (character == '!') { + cond_extends = condition::ncpp_not; + } + // Something else + else { + throw exceptions::langException(); // TODO: proper exception + } // Extend if (cond_extends != condition::_none) { - condition_cache.push_back(new Variable(thisarg)); - condition_cache.push_back(reinterpret_cast(cond_extends)); - newarg = true; // Note: some conditional strings have just one character - if (cond_extends != condition::biggerthan and cond_extends != condition::smallerthan) { + if (cond_extends != condition::biggerthan and cond_extends != condition::smallerthan and cond_extends != condition::ncpp_not) { characterit += 1; } + // Append variable + cond_append: + condition_cache.push_back(new Variable(thisarg)); + newarg = true; + // If required end condition + if (maybe_end_of_arg) { + thisarg.data = condition_cache; + thisarg.type = Variable::id_condition; + condition_cache.clear(); + is_condition = false; + newarg = true; + } else { + condition_cache.push_back(reinterpret_cast(cond_extends)); + } } } - } else if (newarg and is_condition) { - // End condition - condition_cache.push_back(new Variable(thisarg)); - is_condition = false; - thisarg.type = Variable::id_condition; - thisarg.data = condition_cache; - condition_cache.clear(); } // Actually end argument if required if (newarg) { @@ -329,6 +351,21 @@ namespace Pilang3 { } } + template + static bool anycomp(const uint64_t comparator, const Variable& a, const Variable& b) { + T val[] = {std::get(a.data), std::get(b.data)}; + switch (comparator) { + using namespace condition; + case equals: return val[0] == val[1]; break; + case not_equals: return val[0] != val[1]; break; + case biggerthan: return val[0] > val[1]; break; + case smallerthan: return val[0] < val[1]; break; + case biggerthanorequals: return val[0] >= val[1]; break; + case smallerthanorequals: return val[0] <= val[1]; break; + default: throw exceptions::langException(); // TODO: Proper exception + } + } + void Variable::derefer(SharedEnvironment env, Variable& var) { switch (var.type) { case Variable::id_evaluation: { @@ -343,13 +380,40 @@ namespace Pilang3 { } break; case Variable::id_reference: { auto &scope = env->currScope(); - auto x = std::get(var.data); auto res = scope.find(std::get(var.data)); if (res == scope.end()) { throw exceptions::NoSuchVariable(); } var = *res->second; } break; + case Variable::id_condition: { + auto condition = std::get(var.data); + // We only support a size of 3 (as of now) + if (condition.size() != 3) { + throw exceptions::langException(); // TODO: Proper exception + } + // Derefer both sides + Variable::derefer(env, *condition[0]); + Variable::derefer(env, *condition[2]); + // Both sides must be of the same type + if (condition[0]->type != condition[2]->type) { + throw exceptions::langException(); // TODO: Proper exception + } + auto type = condition[0]->type; + auto comparator = reinterpret_cast(condition[1]); + bool fres = false; + // Compare integer + if (type == Variable::id_integer) { + fres = anycomp(comparator, *condition[0], *condition[2]); + } + // Compare string + else if (type == Variable::id_string) { + fres = anycomp(comparator, *condition[0], *condition[2]); + } + // Store result + var.type = Variable::id_integer; + var.data = fres; + } break; default: return; } }