diff --git a/include/pilang.hpp b/include/pilang.hpp index 9da2200..210c85c 100644 --- a/include/pilang.hpp +++ b/include/pilang.hpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -12,13 +13,14 @@ namespace Pilang3 { using integer_type = int64_t; namespace exceptions { - class langException : public std::exception {}; - class emptyExpr : public langException {}; - class BadInteger : public langException {}; - class BadArgs : public langException {}; - class NoSuchCommand : public langException {}; - class NoSuchVariable : public langException {}; - class UnexpectedEndOfExpression : public langException {}; + class langException : public std::exception {public: const char *what() const noexcept {return "Runtime error";}}; + class emptyExpr : public langException {public: const char *what() const noexcept {return "Empty expression";}}; + class BadInteger : public langException {public: const char *what() const noexcept {return "Bad integer";}}; + class BadArgs : public langException {public: const char *what() const noexcept {return "Bad arguments";}}; + class NoSuchCommand : public langException {public: const char *what() const noexcept {return "No such command";}}; + class NoSuchVariable : public langException {public: const char *what() const noexcept {return "No such variable";}}; + class UnexpectedEndOfExpression : public langException {public: const char *what() const noexcept {return "Unexpected end of expression";}}; + class exit : public langException {public: int code; const char *what() const noexcept {return "Interpreter exit";} exit(int code) {this->code = code;}}; } class Environment; @@ -29,6 +31,7 @@ namespace Pilang3 { using SharedVariable = std::shared_ptr; using SharedEvaluation = std::shared_ptr; using SharedFunction = std::shared_ptr; + using SharedEvaluationWBool = std::tuple; struct Variable { enum Type { @@ -43,7 +46,7 @@ namespace Pilang3 { id_null [[maybe_unused]] } type; - using Data = std::variant; + using Data = std::variant; Data data; }; @@ -55,19 +58,30 @@ namespace Pilang3 { class Environment { public: - std::unordered_map globalVars; + using Scope = std::unordered_map; + + std::stack variableScope; void *anybuf = nullptr; + + Environment() { + variableScope.push({}); + } + + Scope &currScope() { + return variableScope.top(); + } }; class Evaluation { public: Cmdargs arguments; - Cmdfnc command = nullptr; + std::tuple command = {"", {nullptr, {}, false}}; std::string command_name; ssize_t exprlen = -1; - Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval = true); + Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval = true, const bool autoderef = true); + void derefer(SharedEnvironment env); Variable execute(SharedEnvironment env); }; diff --git a/main.cpp b/main.cpp index 15b2209..ff9fd9c 100644 --- a/main.cpp +++ b/main.cpp @@ -12,14 +12,25 @@ int main() { // CLI loop while (true) { - std::getline(std::cin, line); + std::cout << "> " << std::flush; + + std::string thisline; + std::getline(std::cin, thisline); + line.append(thisline); + try { Evaluation evaluation(env, line); evaluation.execute(env); + line.clear(); } catch (exceptions::emptyExpr&) { // Do nothing + } catch (exceptions::UnexpectedEndOfExpression&) { + std::cout << " "; + } catch (exceptions::exit& e) { + return e.code; } catch (exceptions::langException& e) { - std::cout << "E: Language exception: " << &e << std::endl; + std::cout << "E: Language exception: " << e.what() << std::endl; + line.clear(); } } } diff --git a/modules/std.cpp b/modules/std.cpp index 2531b6c..f14b6e0 100644 --- a/modules/std.cpp +++ b/modules/std.cpp @@ -6,6 +6,10 @@ using namespace Pilang3; class Std { public: + static Variable exit(SharedEnvironment, Cmdargs& args) { + throw exceptions::exit(std::get(args[0].data)); + } + static Variable retval(SharedEnvironment, Cmdargs& args) { return Variable({ Variable::id_retval, @@ -14,21 +18,17 @@ public: } static Variable set(SharedEnvironment env, Cmdargs& args) { - env->globalVars[std::get(args[0].data)] = std::make_shared(args[1]); + env->currScope()[std::get(args[0].data)] = std::make_shared(args[1]); return args[1]; } - static Variable get(SharedEnvironment env, Cmdargs& args) { - return *env->globalVars[std::get(args[0].data)]; - } - static Variable concat(SharedEnvironment, 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_reference: fres << "REF<" << std::get(arg.data) << '>'; break; case Variable::id_type: fres << std::get(arg.data); break; default: fres << "'; break; } @@ -40,10 +40,10 @@ public: } Std() { - Pilang3::builtinCmds["return"] = {retval, {Variable::id_any}, false}; - Pilang3::builtinCmds["set"] = {set, {Variable::id_string, Variable::id_any}, false}; - Pilang3::builtinCmds["get"] = {get, {Variable::id_string}, false}; - Pilang3::builtinCmds["concat"] = {concat, {}, true}; + builtinCmds["exit"] = {exit, {Variable::id_integer}, false}; + builtinCmds["return"] = {retval, {Variable::id_any}, false}; + builtinCmds["="] = builtinCmds["set"] = {set, {Variable::id_string, Variable::id_any}, false}; + builtinCmds["concat"] = {concat, {}, true}; } }; diff --git a/modules/stdio.cpp b/modules/stdio.cpp index 7c2af6e..28f9003 100644 --- a/modules/stdio.cpp +++ b/modules/stdio.cpp @@ -13,8 +13,18 @@ public: }); } + static Variable input(SharedEnvironment, Cmdargs&) { + std::string fres; + std::getline(std::cin, fres); + return Variable({ + Variable::id_string, + fres + }); + } + StdIO() { - Pilang3::builtinCmds["print"] = {print, {Variable::id_string}, false}; + builtinCmds["print"] = {print, {Variable::id_string}, false}; + builtinCmds["input"] = {input, {}, false}; } }; diff --git a/pilang.cpp b/pilang.cpp index 7119756..ee6894f 100644 --- a/pilang.cpp +++ b/pilang.cpp @@ -18,7 +18,7 @@ static inline bool is_sep(const char character) { namespace Pilang3 { std::unordered_map builtinCmds; - Evaluation::Evaluation(SharedEnvironment env, const std::string& expression, const bool autoeval) { + 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) { @@ -28,6 +28,8 @@ namespace Pilang3 { break; } } + // Get scope + auto scope = env->currScope(); // Parse arguments if any if (not expression.empty() and static_cast(expression.size()) != tspaces) { Variable thisarg; @@ -112,11 +114,15 @@ namespace Pilang3 { case Variable::id_reference: { // Reference if (maybe_end_of_arg) { - auto res = env->globalVars.find(cache); - if (res == env->globalVars.end()) { - throw exceptions::NoSuchVariable(); + if (autoderef) { + auto res = scope.find(cache); + if (res == scope.end()) { + throw exceptions::NoSuchVariable(); + } + thisarg = *res->second; + } else { + thisarg.data = cache; } - thisarg = *res->second; newarg = true; } else { cache.push_back(character); @@ -130,12 +136,12 @@ namespace Pilang3 { characterit++; } // Evaluate - auto evaluation = std::make_shared(env, std::string(characterit, expression.end())); + 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 = evaluation; + thisarg.data = SharedEvaluationWBool{evaluation, not noexec}; } // Skip over expression in iteration character = *(characterit += evaluation->exprlen + 1); @@ -145,7 +151,12 @@ namespace Pilang3 { auto thisfunc = std::make_shared(); while (character != '}') { // Evaluate - auto evaluation = std::make_shared(env, std::string(characterit, expression.end())); + 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 character = *(characterit += evaluation->exprlen + 1); @@ -175,49 +186,97 @@ namespace Pilang3 { 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(); + auto res = scope.find(command_name); + if (res == scope.end() or res->second->type != Variable::id_function) { + command = {command_name, {nullptr, {}, false}}; + } else { + // Execute function + command = {"", { + [res] (SharedEnvironment env, Cmdargs&) { + return std::get(res->second->data)->execute(env); + }, {}, false + }}; } - // Execute function - command = [res] (SharedEnvironment env, Cmdargs&) { - return std::get(res->second->data)->execute(env); - }; } else { - std::vector 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(); - } - } + 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) { - if (command) { - return command(env, arguments); + 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 { - throw exceptions::NoSuchCommand(); + // 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); + } } } Variable Function::execute(SharedEnvironment env) { Variable fres; + env->variableScope.push(env->currScope()); // Start new scope for (auto& evaluation : evalChain) { + evaluation->derefer(env); fres = evaluation->execute(env); if (fres.type == Variable::id_retval) { - return *std::get(fres.data); + auto fresdata = std::get(fres.data); + env->variableScope.pop(); // End scope + return *fresdata; } } + env->variableScope.pop(); // End scope return Variable({ Variable::id_null, 0