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:
parent
b41d1c0494
commit
0236ed50ab
5 changed files with 149 additions and 55 deletions
|
@ -1,6 +1,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stack>
|
||||
#include <variant>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
|
@ -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<Variable>;
|
||||
using SharedEvaluation = std::shared_ptr<Evaluation>;
|
||||
using SharedFunction = std::shared_ptr<Function>;
|
||||
using SharedEvaluationWBool = std::tuple<SharedEvaluation, bool>;
|
||||
|
||||
struct Variable {
|
||||
enum Type {
|
||||
|
@ -43,7 +46,7 @@ namespace Pilang3 {
|
|||
id_null [[maybe_unused]]
|
||||
} 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;
|
||||
};
|
||||
|
||||
|
@ -55,19 +58,30 @@ namespace Pilang3 {
|
|||
|
||||
class Environment {
|
||||
public:
|
||||
std::unordered_map<std::string, SharedVariable> globalVars;
|
||||
using Scope = std::unordered_map<std::string, SharedVariable>;
|
||||
|
||||
std::stack<Scope> variableScope;
|
||||
void *anybuf = nullptr;
|
||||
|
||||
Environment() {
|
||||
variableScope.push({});
|
||||
}
|
||||
|
||||
Scope &currScope() {
|
||||
return variableScope.top();
|
||||
}
|
||||
};
|
||||
|
||||
class Evaluation {
|
||||
public:
|
||||
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;
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
15
main.cpp
15
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ using namespace Pilang3;
|
|||
|
||||
class Std {
|
||||
public:
|
||||
static Variable exit(SharedEnvironment, Cmdargs& args) {
|
||||
throw exceptions::exit(std::get<integer_type>(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<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];
|
||||
}
|
||||
|
||||
static Variable get(SharedEnvironment env, Cmdargs& args) {
|
||||
return *env->globalVars[std::get<std::string>(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<integer_type>(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;
|
||||
default: fres << "<ID" << arg.type << '>'; 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};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
121
pilang.cpp
121
pilang.cpp
|
@ -18,7 +18,7 @@ static inline bool is_sep(const char character) {
|
|||
namespace Pilang3 {
|
||||
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
|
||||
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<ssize_t>(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<Evaluation>(env, std::string(characterit, expression.end()));
|
||||
auto evaluation = std::make_shared<Evaluation>(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<Function>();
|
||||
while (character != '}') {
|
||||
// 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);
|
||||
// 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<SharedFunction>(res->second->data)->execute(env);
|
||||
}, {}, false
|
||||
}};
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
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<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) {
|
||||
if (command) {
|
||||
return command(env, arguments);
|
||||
std::vector<Variable::Type> 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<SharedFunction>(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<SharedVariable>(fres.data);
|
||||
auto fresdata = std::get<SharedVariable>(fres.data);
|
||||
env->variableScope.pop(); // End scope
|
||||
return *fresdata;
|
||||
}
|
||||
}
|
||||
env->variableScope.pop(); // End scope
|
||||
return Variable({
|
||||
Variable::id_null,
|
||||
0
|
||||
|
|
Loading…
Add table
Reference in a new issue