mirror of
https://gitlab.com/niansa/pilang3.git
synced 2025-03-06 20:49:20 +01:00
Initial commit
This commit is contained in:
commit
0b4aa7cc31
7 changed files with 353 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
CMakeLists.txt.user
|
17
CMakeLists.txt
Normal file
17
CMakeLists.txt
Normal file
|
@ -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/
|
||||
)
|
66
include/pilang.hpp
Normal file
66
include/pilang.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
|
||||
|
||||
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<Variable>;
|
||||
using SharedEvaluation = std::shared_ptr<Evaluation>;
|
||||
|
||||
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<std::string, integer_type, SharedVariable, Type, SharedEvaluation>;
|
||||
Data data;
|
||||
};
|
||||
|
||||
using Cmdargs = std::vector<Variable>;
|
||||
using Cmdfnc = std::function<Variable (Environment&, Cmdargs&)>;
|
||||
using Cmddesc = std::tuple<Cmdfnc, uint16_t, std::vector<Variable::Type>, bool>;
|
||||
|
||||
extern std::unordered_map<std::string, Cmddesc> builtinCmds;
|
||||
|
||||
class Environment {
|
||||
public:
|
||||
std::unordered_map<std::string, SharedVariable> 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);
|
||||
};
|
||||
};
|
22
main.cpp
Normal file
22
main.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <iostream>
|
||||
#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;
|
||||
//}
|
||||
}
|
||||
}
|
41
modules/std.cpp
Normal file
41
modules/std.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <sstream>
|
||||
#include "pilang.hpp"
|
||||
using namespace Pilang3;
|
||||
|
||||
|
||||
class Std {
|
||||
public:
|
||||
static Variable set(Environment& env, Cmdargs& args) {
|
||||
return *env.globalVars[std::get<std::string>(args[0].data)];
|
||||
}
|
||||
|
||||
static Variable get(Environment& env, Cmdargs& args) {
|
||||
env.globalVars[std::get<std::string>(args[0].data)] = std::make_shared<Variable>(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<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_type: fres << std::get<Variable::Type>(arg.data); break;
|
||||
default: fres << "<ID" << arg.type << '>'; 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;
|
21
modules/stdio.cpp
Normal file
21
modules/stdio.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <iostream>
|
||||
#include "pilang.hpp"
|
||||
using namespace Pilang3;
|
||||
|
||||
|
||||
class StdIO {
|
||||
public:
|
||||
static Variable print(Environment&, Cmdargs& args) {
|
||||
std::cout << std::get<std::string>(args[0].data) << std::endl;
|
||||
return Variable({
|
||||
Variable::id_null,
|
||||
0
|
||||
});
|
||||
}
|
||||
|
||||
StdIO() {
|
||||
Pilang3::builtinCmds["print"] = {print, 1, {Variable::id_string}, false};
|
||||
}
|
||||
};
|
||||
|
||||
static StdIO inst;
|
185
pilang.cpp
Normal file
185
pilang.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include "pilang.hpp"
|
||||
|
||||
|
||||
|
||||
namespace Pilang3 {
|
||||
std::unordered_map<std::string, std::tuple<Cmdfnc, uint16_t, std::vector<Variable::Type>, 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<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_type: fres << std::get<Variable::Type>(arg.data); break;
|
||||
default: fres << "<ID" << arg.type << '>'; 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<Evaluation>(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<Variable::Type> 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();
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Add table
Reference in a new issue