1
0
Fork 0
mirror of https://gitlab.com/niansa/pilang3.git synced 2025-03-06 20:49:20 +01:00

Implemented basic functions and scoping

This commit is contained in:
niansa 2021-02-10 19:40:37 +01:00
parent b41d1c0494
commit 0236ed50ab
5 changed files with 149 additions and 55 deletions

View file

@ -1,6 +1,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <stack>
#include <variant> #include <variant>
#include <exception> #include <exception>
#include <memory> #include <memory>
@ -12,13 +13,14 @@ namespace Pilang3 {
using integer_type = int64_t; using integer_type = int64_t;
namespace exceptions { namespace exceptions {
class langException : public std::exception {}; class langException : public std::exception {public: const char *what() const noexcept {return "Runtime error";}};
class emptyExpr : public langException {}; class emptyExpr : public langException {public: const char *what() const noexcept {return "Empty expression";}};
class BadInteger : public langException {}; class BadInteger : public langException {public: const char *what() const noexcept {return "Bad integer";}};
class BadArgs : public langException {}; class BadArgs : public langException {public: const char *what() const noexcept {return "Bad arguments";}};
class NoSuchCommand : public langException {}; class NoSuchCommand : public langException {public: const char *what() const noexcept {return "No such command";}};
class NoSuchVariable : public langException {}; class NoSuchVariable : public langException {public: const char *what() const noexcept {return "No such variable";}};
class UnexpectedEndOfExpression : public langException {}; 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; class Environment;
@ -29,6 +31,7 @@ namespace Pilang3 {
using SharedVariable = std::shared_ptr<Variable>; using SharedVariable = std::shared_ptr<Variable>;
using SharedEvaluation = std::shared_ptr<Evaluation>; using SharedEvaluation = std::shared_ptr<Evaluation>;
using SharedFunction = std::shared_ptr<Function>; using SharedFunction = std::shared_ptr<Function>;
using SharedEvaluationWBool = std::tuple<SharedEvaluation, bool>;
struct Variable { struct Variable {
enum Type { enum Type {
@ -43,7 +46,7 @@ namespace Pilang3 {
id_null [[maybe_unused]] id_null [[maybe_unused]]
} type; } type;
using Data = std::variant<std::string, integer_type, SharedVariable, SharedEvaluation, SharedFunction, Type>; using Data = std::variant<std::string, integer_type, SharedVariable, SharedEvaluationWBool, SharedFunction, Type>;
Data data; Data data;
}; };
@ -55,19 +58,30 @@ namespace Pilang3 {
class Environment { class Environment {
public: public:
std::unordered_map<std::string, SharedVariable> globalVars; using Scope = std::unordered_map<std::string, SharedVariable>;
std::stack<Scope> variableScope;
void *anybuf = nullptr; void *anybuf = nullptr;
Environment() {
variableScope.push({});
}
Scope &currScope() {
return variableScope.top();
}
}; };
class Evaluation { class Evaluation {
public: public:
Cmdargs arguments; Cmdargs arguments;
Cmdfnc command = nullptr; std::tuple<std::string /*Command name in case cmddesc was not found in eval time*/, Cmddesc> command = {"", {nullptr, {}, false}};
std::string command_name; std::string command_name;
ssize_t exprlen = -1; 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); Variable execute(SharedEnvironment env);
}; };

View file

@ -12,14 +12,25 @@ int main() {
// CLI loop // CLI loop
while (true) { while (true) {
std::getline(std::cin, line); std::cout << "> " << std::flush;
std::string thisline;
std::getline(std::cin, thisline);
line.append(thisline);
try { try {
Evaluation evaluation(env, line); Evaluation evaluation(env, line);
evaluation.execute(env); evaluation.execute(env);
line.clear();
} catch (exceptions::emptyExpr&) { } catch (exceptions::emptyExpr&) {
// Do nothing // Do nothing
} catch (exceptions::UnexpectedEndOfExpression&) {
std::cout << " ";
} catch (exceptions::exit& e) {
return e.code;
} catch (exceptions::langException& e) { } catch (exceptions::langException& e) {
std::cout << "E: Language exception: " << &e << std::endl; std::cout << "E: Language exception: " << e.what() << std::endl;
line.clear();
} }
} }
} }

View file

@ -6,6 +6,10 @@ using namespace Pilang3;
class Std { class Std {
public: public:
static Variable exit(SharedEnvironment, Cmdargs& args) {
throw exceptions::exit(std::get<integer_type>(args[0].data));
}
static Variable retval(SharedEnvironment, Cmdargs& args) { static Variable retval(SharedEnvironment, Cmdargs& args) {
return Variable({ return Variable({
Variable::id_retval, Variable::id_retval,
@ -14,21 +18,17 @@ public:
} }
static Variable set(SharedEnvironment env, Cmdargs& args) { static Variable set(SharedEnvironment env, Cmdargs& args) {
env->globalVars[std::get<std::string>(args[0].data)] = std::make_shared<Variable>(args[1]); env->currScope()[std::get<std::string>(args[0].data)] = std::make_shared<Variable>(args[1]);
return args[1]; return args[1];
} }
static Variable get(SharedEnvironment env, Cmdargs& args) {
return *env->globalVars[std::get<std::string>(args[0].data)];
}
static Variable concat(SharedEnvironment, Cmdargs& args) { static Variable concat(SharedEnvironment, Cmdargs& args) {
std::ostringstream fres; std::ostringstream fres;
for (const auto& arg : args) { for (const auto& arg : args) {
switch (arg.type) { switch (arg.type) {
case Variable::id_integer: fres << std::get<integer_type>(arg.data); break; case Variable::id_integer: fres << std::get<integer_type>(arg.data); break;
case Variable::id_string: fres << std::get<std::string>(arg.data); break; case Variable::id_string: fres << std::get<std::string>(arg.data); break;
case Variable::id_reference: fres << std::get<SharedVariable>(arg.data).get(); break; case Variable::id_reference: fres << "REF<" << std::get<std::string>(arg.data) << '>'; break;
case Variable::id_type: fres << std::get<Variable::Type>(arg.data); break; case Variable::id_type: fres << std::get<Variable::Type>(arg.data); break;
default: fres << "<ID" << arg.type << '>'; break; default: fres << "<ID" << arg.type << '>'; break;
} }
@ -40,10 +40,10 @@ public:
} }
Std() { Std() {
Pilang3::builtinCmds["return"] = {retval, {Variable::id_any}, false}; builtinCmds["exit"] = {exit, {Variable::id_integer}, false};
Pilang3::builtinCmds["set"] = {set, {Variable::id_string, Variable::id_any}, false}; builtinCmds["return"] = {retval, {Variable::id_any}, false};
Pilang3::builtinCmds["get"] = {get, {Variable::id_string}, false}; builtinCmds["="] = builtinCmds["set"] = {set, {Variable::id_string, Variable::id_any}, false};
Pilang3::builtinCmds["concat"] = {concat, {}, true}; builtinCmds["concat"] = {concat, {}, true};
} }
}; };

View file

@ -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() { StdIO() {
Pilang3::builtinCmds["print"] = {print, {Variable::id_string}, false}; builtinCmds["print"] = {print, {Variable::id_string}, false};
builtinCmds["input"] = {input, {}, false};
} }
}; };

View file

@ -18,7 +18,7 @@ static inline bool is_sep(const char character) {
namespace Pilang3 { namespace Pilang3 {
std::unordered_map<std::string, Cmddesc> builtinCmds; std::unordered_map<std::string, Cmddesc> 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 // Count off trailing spaces
ssize_t tspaces = 0; ssize_t tspaces = 0;
for (const auto& character : expression) { for (const auto& character : expression) {
@ -28,6 +28,8 @@ namespace Pilang3 {
break; break;
} }
} }
// Get scope
auto scope = env->currScope();
// Parse arguments if any // Parse arguments if any
if (not expression.empty() and static_cast<ssize_t>(expression.size()) != tspaces) { if (not expression.empty() and static_cast<ssize_t>(expression.size()) != tspaces) {
Variable thisarg; Variable thisarg;
@ -112,11 +114,15 @@ namespace Pilang3 {
case Variable::id_reference: { case Variable::id_reference: {
// Reference // Reference
if (maybe_end_of_arg) { if (maybe_end_of_arg) {
auto res = env->globalVars.find(cache); if (autoderef) {
if (res == env->globalVars.end()) { auto res = scope.find(cache);
throw exceptions::NoSuchVariable(); if (res == scope.end()) {
throw exceptions::NoSuchVariable();
}
thisarg = *res->second;
} else {
thisarg.data = cache;
} }
thisarg = *res->second;
newarg = true; newarg = true;
} else { } else {
cache.push_back(character); cache.push_back(character);
@ -130,12 +136,12 @@ namespace Pilang3 {
characterit++; characterit++;
} }
// Evaluate // Evaluate
auto evaluation = std::make_shared<Evaluation>(env, std::string(characterit, expression.end())); auto evaluation = std::make_shared<Evaluation>(env, std::string(characterit, expression.end()), autoeval, autoderef);
// Execute if possible // Execute if possible
if (autoeval and not noexec) { if (autoeval and not noexec) {
thisarg = evaluation->execute(env); thisarg = evaluation->execute(env);
} else { } else {
thisarg.data = evaluation; thisarg.data = SharedEvaluationWBool{evaluation, not noexec};
} }
// Skip over expression in iteration // Skip over expression in iteration
character = *(characterit += evaluation->exprlen + 1); character = *(characterit += evaluation->exprlen + 1);
@ -145,7 +151,12 @@ namespace Pilang3 {
auto thisfunc = std::make_shared<Function>(); auto thisfunc = std::make_shared<Function>();
while (character != '}') { while (character != '}') {
// Evaluate // Evaluate
auto evaluation = std::make_shared<Evaluation>(env, std::string(characterit, expression.end())); SharedEvaluation evaluation;
try {
evaluation = std::make_shared<Evaluation>(env, std::string(characterit, expression.end()), false, false);
} catch (exceptions::emptyExpr&) {
throw exceptions::UnexpectedEndOfExpression();
}
thisfunc->evalChain.push_back(evaluation); thisfunc->evalChain.push_back(evaluation);
// Skip over expression in iteration // Skip over expression in iteration
character = *(characterit += evaluation->exprlen + 1); character = *(characterit += evaluation->exprlen + 1);
@ -175,49 +186,97 @@ namespace Pilang3 {
auto res = Pilang3::builtinCmds.find(command_name); auto res = Pilang3::builtinCmds.find(command_name);
if (res == Pilang3::builtinCmds.end()) { if (res == Pilang3::builtinCmds.end()) {
// Find function // Find function
auto res = env->globalVars.find(command_name); auto res = scope.find(command_name);
if (res == env->globalVars.end() or res->second->type != Variable::id_function) { if (res == scope.end() or res->second->type != Variable::id_function) {
throw exceptions::NoSuchCommand(); command = {command_name, {nullptr, {}, false}};
} else {
// Execute function
command = {"", {
[res] (SharedEnvironment env, Cmdargs&) {
return std::get<SharedFunction>(res->second->data)->execute(env);
}, {}, false
}};
} }
// Execute function
command = [res] (SharedEnvironment env, Cmdargs&) {
return std::get<SharedFunction>(res->second->data)->execute(env);
};
} else { } else {
std::vector<Variable::Type> argstypes; std::get<1>(command) = res->second;
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 { } else {
throw exceptions::emptyExpr(); 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<SharedEvaluationWBool>(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<std::string>(arg.data));
if (res == scope.end()) {
throw exceptions::NoSuchVariable();
}
arg = *res->second;
} break;
default: continue;
}
}
}
Variable Evaluation::execute(SharedEnvironment env) { Variable Evaluation::execute(SharedEnvironment env) {
if (command) { std::vector<Variable::Type> argstypes;
return command(env, arguments); 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 { } 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<SharedFunction>(res->second->data)->execute(env);
}
} }
} }
Variable Function::execute(SharedEnvironment env) { Variable Function::execute(SharedEnvironment env) {
Variable fres; Variable fres;
env->variableScope.push(env->currScope()); // Start new scope
for (auto& evaluation : evalChain) { for (auto& evaluation : evalChain) {
evaluation->derefer(env);
fres = evaluation->execute(env); fres = evaluation->execute(env);
if (fres.type == Variable::id_retval) { if (fres.type == Variable::id_retval) {
return *std::get<SharedVariable>(fres.data); auto fresdata = std::get<SharedVariable>(fres.data);
env->variableScope.pop(); // End scope
return *fresdata;
} }
} }
env->variableScope.pop(); // End scope
return Variable({ return Variable({
Variable::id_null, Variable::id_null,
0 0