From 0b4aa7cc31d15912dff72af2c9caa2adf1f54975 Mon Sep 17 00:00:00 2001 From: niansa Date: Mon, 1 Feb 2021 13:46:04 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + CMakeLists.txt | 17 +++++ include/pilang.hpp | 66 ++++++++++++++++ main.cpp | 22 ++++++ modules/std.cpp | 41 ++++++++++ modules/stdio.cpp | 21 +++++ pilang.cpp | 185 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 353 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/pilang.hpp create mode 100644 main.cpp create mode 100644 modules/std.cpp create mode 100644 modules/stdio.cpp create mode 100644 pilang.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01e00f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2ac79a8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) + +project(pilang3 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(${PROJECT_NAME} + main.cpp + pilang.cpp + modules/std.cpp + modules/stdio.cpp +) + +target_include_directories(${PROJECT_NAME} PRIVATE + include/ +) diff --git a/include/pilang.hpp b/include/pilang.hpp new file mode 100644 index 0000000..f68dce3 --- /dev/null +++ b/include/pilang.hpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include +#include + + + +namespace Pilang3 { + using integer_type = int64_t; + + namespace exceptions { + class langException : public std::exception {}; + class BadInteger : public langException {}; + class BadArgs : public langException {}; + class NoSuchCommand : public langException {}; + class NoSuchVariable : public langException {}; + } + + class Environment; + class Evaluation; + struct Variable; + using SharedVariable = std::shared_ptr; + using SharedEvaluation = std::shared_ptr; + + struct Variable { + enum Type { + id_string, + id_integer, + id_reference, + id_evaluation, + id_type [[maybe_unused]], + id_any [[maybe_unused]], + id_null [[maybe_unused]] + } type; + + using Data = std::variant; + Data data; + }; + + using Cmdargs = std::vector; + using Cmdfnc = std::function; + using Cmddesc = std::tuple, bool>; + + extern std::unordered_map builtinCmds; + + class Environment { + public: + std::unordered_map globalVars; + void *anybuf = nullptr; + }; + + class Evaluation { + public: + Cmdargs arguments; + Cmdfnc command = nullptr; + std::string command_name; + ssize_t exprlen; + + Evaluation(Environment& env, const std::string &expression, const bool autoeval = true); + + Variable execute(Environment& env); + }; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fbdd659 --- /dev/null +++ b/main.cpp @@ -0,0 +1,22 @@ +#include +#include "pilang.hpp" + + +int main() { + using namespace Pilang3; + + // Initialise + std::string line; + Environment env; + + // CLI loop + while (true) { + std::getline(std::cin, line); + //try { + Evaluation evaluation(env, line); + evaluation.execute(env); + //} catch (exceptions::langException& e) { + // std::cout << "E: Language exception: " << &e << std::endl; + //} + } +} diff --git a/modules/std.cpp b/modules/std.cpp new file mode 100644 index 0000000..172f083 --- /dev/null +++ b/modules/std.cpp @@ -0,0 +1,41 @@ +#include +#include "pilang.hpp" +using namespace Pilang3; + + +class Std { +public: + static Variable set(Environment& env, Cmdargs& args) { + return *env.globalVars[std::get(args[0].data)]; + } + + static Variable get(Environment& env, Cmdargs& args) { + env.globalVars[std::get(args[0].data)] = std::make_shared(args[1]); + return args[1]; + } + + static Variable concat(Environment&, 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_type: fres << std::get(arg.data); break; + default: fres << "'; break; + } + } + return Variable({ + Variable::id_string, + fres.str() + }); + } + + Std() { + Pilang3::builtinCmds["set"] = {concat, 2, {Variable::id_string, Variable::id_any}, true}; + Pilang3::builtinCmds["get"] = {concat, 1, {Variable::id_string}, true}; + Pilang3::builtinCmds["concat"] = {concat, 0, {}, true}; + } +}; + +static Std inst; diff --git a/modules/stdio.cpp b/modules/stdio.cpp new file mode 100644 index 0000000..9ac39ca --- /dev/null +++ b/modules/stdio.cpp @@ -0,0 +1,21 @@ +#include +#include "pilang.hpp" +using namespace Pilang3; + + +class StdIO { +public: + static Variable print(Environment&, Cmdargs& args) { + std::cout << std::get(args[0].data) << std::endl; + return Variable({ + Variable::id_null, + 0 + }); + } + + StdIO() { + Pilang3::builtinCmds["print"] = {print, 1, {Variable::id_string}, false}; + } +}; + +static StdIO inst; diff --git a/pilang.cpp b/pilang.cpp new file mode 100644 index 0000000..29930a5 --- /dev/null +++ b/pilang.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pilang.hpp" + + + +namespace Pilang3 { + std::unordered_map, bool>> builtinCmds = { + {"concat", {[] (Environment& env, 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_type: fres << std::get(arg.data); break; + default: fres << "'; break; + } + } + return Variable({ + Variable::id_string, + fres.str() + }); + }, 0, {}, true}} + }; + + Evaluation::Evaluation(Environment& env, const std::string &expression, const bool autoeval) { + // Parse arguments if any + if (not expression.empty()) { + Variable thisarg; + bool in_command_name = true; + bool newarg = false; + bool escapenext = false; + std::string cache; + for (auto characterit = expression.begin(); 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 == ';') { + break; + } else { + in_command_name = false; + newarg = true; + } + } + } + // Start new argument + else if (newarg) { + // Skip spaces + if (character == ' ' or character == ',' or character == '\n' or 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 { + // 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 (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(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; + } + // 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; + } + } + } + } + // Find command + auto res = Pilang3::builtinCmds.find(command_name); + if (res == Pilang3::builtinCmds.end()) { + throw exceptions::NoSuchCommand(); + } + uint16_t argslen; + std::vector argstypes; + bool anymoreopts; + std::tie(command, argslen, argstypes, anymoreopts) = res->second; + // Check args + if ((arguments.size() > argslen and not anymoreopts) or (arguments.size() < argslen)) { + throw exceptions::BadArgs(); + } + for (uint16_t argidx = 0; argidx != argslen; argidx++) { + if (argstypes[argidx] != Variable::id_any and argstypes[argidx] != arguments[argidx].type) { + throw exceptions::BadArgs(); + } + } + } + } + + Variable Evaluation::execute(Environment& env) { + if (command) { + return command(env, arguments); + } else { + throw exceptions::NoSuchCommand(); + } + } +};