dlang-sqlite inital
This commit is contained in:
commit
ce0a6c08e4
160 files changed
+362540
No files matched your search
@@ -0,0 +1,32 @@
|
||||
# CMakeList.txt : CMake project for dlang.plugin.sdk, include source and define
|
||||
# project specific logic here.
|
||||
#
|
||||
|
||||
# Add source to this project's executable.
|
||||
include_directories(dlang/)
|
||||
add_library (dlang.sqlite SHARED
|
||||
"entry.cpp"
|
||||
"dlang/src/compiler.cpp"
|
||||
"dlang/src/lexer.cpp"
|
||||
"dlang/src/parser.cpp"
|
||||
"dlang/src/vm.cpp"
|
||||
)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.12)
|
||||
set_property(TARGET dlang.sqlite PROPERTY CXX_STANDARD 20)
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
sqlite3
|
||||
GIT_REPOSITORY https://github.com/sjinks/sqlite3-cmake.git
|
||||
GIT_TAG v3.49.1
|
||||
)
|
||||
FetchContent_MakeAvailable(sqlite3)
|
||||
|
||||
target_link_libraries(dlang.sqlite PRIVATE
|
||||
SQLite::SQLite3
|
||||
)
|
||||
|
||||
# TODO: Add tests and install targets if needed.
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef DLANG_COMPILER_HPP
|
||||
#define DLANG_COMPILER_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "types/include.hpp"
|
||||
#include "program.hpp"
|
||||
|
||||
#include "parser/expressions.hpp"
|
||||
#include "parser/statements.hpp"
|
||||
|
||||
class Compiler
|
||||
{
|
||||
private:
|
||||
std::vector<double> _bytecode;
|
||||
std::vector<std::string> _stringTable;
|
||||
std::vector<std::string> _includes;
|
||||
Program* _program = nullptr;
|
||||
|
||||
private:
|
||||
void Emit(Opcodes op);
|
||||
void Emit(const std::string& str);
|
||||
void Emit(double value);
|
||||
|
||||
void Compile(Statement* stmt);
|
||||
void Compile(Expression* expr);
|
||||
|
||||
void FinalCompile();
|
||||
|
||||
public:
|
||||
Compiler(Program* program);
|
||||
|
||||
void Compile();
|
||||
const std::vector<double>& GetBytecode() const;
|
||||
|
||||
void Print();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef DLANG_HPP
|
||||
#define DLANG_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "types/include.hpp"
|
||||
|
||||
#include "parser/expressions.hpp"
|
||||
#include "parser/statements.hpp"
|
||||
|
||||
#include "lexer.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "compiler.hpp"
|
||||
|
||||
#include "vm.hpp"
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "libs/dev.hpp"
|
||||
#include "libs/os.hpp"
|
||||
#include "libs/math.hpp"
|
||||
#include "libs/io.hpp"
|
||||
#include "libs/debug.hpp"
|
||||
#include "libs/string.hpp"
|
||||
#include "libs/table.hpp"
|
||||
#include "libs/list.hpp"
|
||||
#include "libs/regex.hpp"
|
||||
|
||||
static void OpenStandardLibs(DlangVM* vm)
|
||||
{
|
||||
openlib_dev(vm);
|
||||
openlib_os(vm);
|
||||
openlib_math(vm);
|
||||
openlib_io(vm);
|
||||
openlib_debug(vm);
|
||||
openlib_string(vm);
|
||||
openlib_table(vm);
|
||||
openlib_list(vm);
|
||||
openlib_regex(vm);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
#ifndef DLANG_HELPERS_HPP
|
||||
#define DLANG_HELPERS_HPP
|
||||
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#define SOCKET int
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#define closesocket close
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
static bool isvalidnumber(char c)
|
||||
{
|
||||
return ::isdigit(c) || c == '.' || c == ',';
|
||||
}
|
||||
|
||||
static bool IsStringNumber(const std::string& input)
|
||||
{
|
||||
return std::all_of(
|
||||
input.begin(),
|
||||
input.end(),
|
||||
::isvalidnumber
|
||||
);
|
||||
}
|
||||
|
||||
static bool IsIdentifier(const std::string& input)
|
||||
{
|
||||
if (!::isdigit(input.at(0)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string DoubleToString(double val) {
|
||||
double intPart;
|
||||
double fracPart = std::modf(val, &intPart);
|
||||
|
||||
if (fracPart == 0.0) {
|
||||
return std::to_string(static_cast<long long>(val));
|
||||
}
|
||||
else {
|
||||
std::ostringstream oss;
|
||||
oss << val;
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string NoSpacesString(const std::string& input)
|
||||
{
|
||||
std::string output = "";
|
||||
for (const auto& c : input)
|
||||
if (::isdigit(c) || ::isalpha(c) || c == '.')
|
||||
output += c;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::string getLatestVersion(const std::string& host, const std::string& path) {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return "";
|
||||
#endif
|
||||
|
||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
struct hostent* server = gethostbyname(host.c_str());
|
||||
if (!server) {
|
||||
closesocket(sock);
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
::memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
::memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
|
||||
serv_addr.sin_port = htons(80);
|
||||
|
||||
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) {
|
||||
closesocket(sock);
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string request = "GET " + path + " HTTP/1.0\r\nHost: " + host + "\r\nConnection: close\r\n\r\n";
|
||||
send(sock, request.c_str(), (int)request.length(), 0);
|
||||
|
||||
std::string response;
|
||||
char buffer[1024];
|
||||
int bytesReceived;
|
||||
while ((bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0) {
|
||||
buffer[bytesReceived] = '\0';
|
||||
response += buffer;
|
||||
}
|
||||
|
||||
closesocket(sock);
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
size_t pos = response.find("\r\n\r\n");
|
||||
if (pos != std::string::npos) {
|
||||
return NoSpacesString(response.substr(pos + 4));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#define dlang_pop(vm) vm->Pop()
|
||||
#define dlang_top(vm) (vm->stack.top())
|
||||
#define dlang_tonumber(vm) std::get<double>(vm->stack.pop().data)
|
||||
#define dlang_toboolean(vm) std::get<bool>(vm->stack.pop().data)
|
||||
#define dlang_tostring(vm) std::get<std::string>(vm->stack.pop().data)
|
||||
#define dlang_totoken(vm) (vm->stack.pop())
|
||||
#define dlang_totable(vm) std::get<std::shared_ptr<DlangTable>>(vm->stack.pop().data)
|
||||
#define dlang_tolist(vm) std::get<std::shared_ptr<DlangList>>(vm->stack.pop().data)
|
||||
#define dlang_tocfunction(vm) std::get<NativeFunctionPtr>(vm->stack.pop().data)
|
||||
|
||||
#define dlang_pushnumber(vm, number) vm->PushNumber(number)
|
||||
#define dlang_pushboolean(vm, boolean) vm->PushBoolean(boolean)
|
||||
#define dlang_pushstring(vm, stringv) vm->PushString(stringv)
|
||||
#define dlang_pushnil(vm) vm->stack.push(StackValue())
|
||||
#define dlang_push(vm, stackval) vm->stack.push(stackval)
|
||||
|
||||
#define dlang_isnumber(vm) (vm->stack.peek().type == StackValueType::Int)
|
||||
#define dlang_isstring(vm) (vm->stack.peek().type == StackValueType::String)
|
||||
#define dlang_isboolean(vm) (vm->stack.peek().type == StackValueType::Bool)
|
||||
#define dlang_istable(vm) (vm->stack.peek().type == StackValueType::Table)
|
||||
#define dlang_islist(vm) (vm->stack.peek().type == StackValueType::List)
|
||||
#define dlang_isfunction(vm) (vm->stack.peek().type == StackValueType::Function)
|
||||
#define dlang_isnil(vm) (vm->stack.peek().type == StackValueType::NIL)
|
||||
|
||||
#define dlang_pushcfunction(vm, func) vm->PushCFunction(func)
|
||||
#define dlang_setglobal(vm, name) vm->globals.Set(name, vm->stack.pop())
|
||||
#define dlang_getglobal(vm, name) vm->stack.push(vm->globals.Get(name))
|
||||
#define dlang_call(vm, args) vm->CallFunc(args)
|
||||
|
||||
#define dlang_newtable(vm) vm->stack.push(StackValue(StackValueType::Table, std::make_shared<DlangTable>()))
|
||||
#define dlang_setfield(vm, keyName) vm->SetField(keyName)
|
||||
#define dlang_getfield(vm, keyName) vm->GetField(keyName)
|
||||
// dlang_getfieldp will pop the table from stack and not return it
|
||||
#define dlang_getfieldp(vm, keyName) vm->GetField(keyName, true)
|
||||
|
||||
#define dlang_newlist(vm) vm->PushList(std::make_shared<DlangList>());
|
||||
#define dlang_setindex(vm) vm->SetListIndex()
|
||||
#define dlang_getindex(vm) vm->GetListIndex()
|
||||
#define dlang_pushindex(vm) vm->PushListIndex();
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,91 @@
|
||||
#ifndef _LANGUAGE_HPP
|
||||
#define _LANGUAGE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace Dlang::Language
|
||||
{
|
||||
// --- Single Character Tokens (char) ---
|
||||
inline constexpr char op_endl = ';';
|
||||
inline constexpr char op_exprstart = '(';
|
||||
inline constexpr char op_exprend = ')';
|
||||
inline constexpr char op_arraystart = '{';
|
||||
inline constexpr char op_arrayend = '}';
|
||||
inline constexpr char op_liststart = '[';
|
||||
inline constexpr char op_listend = ']';
|
||||
inline constexpr char op_equals = '=';
|
||||
inline constexpr char op_multi = '*';
|
||||
inline constexpr char op_add = '+';
|
||||
inline constexpr char op_sub = '-';
|
||||
inline constexpr char op_div = '/';
|
||||
inline constexpr char op_separator = ',';
|
||||
inline constexpr char op_above = '>';
|
||||
inline constexpr char op_below = '<';
|
||||
inline constexpr char op_colon = ':';
|
||||
inline constexpr char op_dot = '.';
|
||||
inline constexpr char op_not = '!';
|
||||
inline constexpr char op_size = '#';
|
||||
|
||||
inline constexpr std::array single_char_tokens = {
|
||||
op_endl, op_exprstart, op_exprend, op_arraystart, op_arrayend,
|
||||
op_liststart, op_listend, op_equals, op_multi, op_add, op_sub,
|
||||
op_div, op_separator, op_above, op_below, op_colon, op_dot, op_not,
|
||||
op_size
|
||||
};
|
||||
|
||||
// --- Multi-Character Operators (const char*) ---
|
||||
inline constexpr const char* op_eq = "==";
|
||||
inline constexpr const char* op_neq = "!=";
|
||||
inline constexpr const char* op_gte = ">=";
|
||||
inline constexpr const char* op_lte = "<=";
|
||||
inline constexpr const char* op_add_assign = "+=";
|
||||
inline constexpr const char* op_sub_assign = "-=";
|
||||
inline constexpr const char* op_mul_assign = "*=";
|
||||
inline constexpr const char* op_div_assign = "/=";
|
||||
inline constexpr const char* op_and = "&&";
|
||||
inline constexpr const char* op_or = "||";
|
||||
|
||||
// --- Keywords (const char*) ---
|
||||
inline constexpr const char* kw_var = "var";
|
||||
inline constexpr const char* kw_while = "while";
|
||||
inline constexpr const char* kw_do = "do";
|
||||
inline constexpr const char* kw_end = "end";
|
||||
inline constexpr const char* kw_for = "for";
|
||||
inline constexpr const char* kw_foreach = "foreach";
|
||||
inline constexpr const char* kw_in = "in";
|
||||
inline constexpr const char* kw_if = "if";
|
||||
inline constexpr const char* kw_then = "then";
|
||||
inline constexpr const char* kw_else = "else";
|
||||
inline constexpr const char* kw_func = "func";
|
||||
inline constexpr const char* kw_return = "return";
|
||||
inline constexpr const char* kw_include = "include";
|
||||
|
||||
inline constexpr std::array<std::string_view, 13> keywords = {
|
||||
kw_var, kw_while, kw_do, kw_end, kw_for, kw_foreach,
|
||||
kw_in, kw_if, kw_then, kw_else, kw_func, kw_return, kw_include
|
||||
};
|
||||
|
||||
// --- Booleans ---
|
||||
inline constexpr const char* kw_true = "true";
|
||||
inline constexpr const char* kw_false = "false";
|
||||
|
||||
constexpr bool IsSingleCharToken(char c)
|
||||
{
|
||||
return std::find(single_char_tokens.begin(), single_char_tokens.end(), c) != single_char_tokens.end();
|
||||
}
|
||||
|
||||
constexpr bool IsKeyword(std::string_view token)
|
||||
{
|
||||
return std::find(keywords.begin(), keywords.end(), token) != keywords.end();
|
||||
}
|
||||
|
||||
constexpr bool IsBoolean(std::string_view token)
|
||||
{
|
||||
return token == kw_true || token == kw_false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _LANGUAGE_HPP
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef DLANG_LEXER_HPP
|
||||
#define DLANG_LEXER_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "types/include.hpp"
|
||||
|
||||
class Lexer
|
||||
{
|
||||
private:
|
||||
std::vector<PreToken> _tokens;
|
||||
std::vector<Token> _organizedTokens;
|
||||
|
||||
public:
|
||||
Lexer() = default;
|
||||
|
||||
void Parse(const std::string& code);
|
||||
void ParseOrganizedTokens();
|
||||
|
||||
std::vector<PreToken> GetTokens() const;
|
||||
std::vector<Token> GetOrganizedTokens() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef DLANG_LIBS_DEBUG_HPP
|
||||
#define DLANG_LIBS_DEBUG_HPP
|
||||
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativeStackDump(DlangVM* vm)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (int i = 0; i < dlang_top(vm); i++)
|
||||
{
|
||||
auto value = vm->stack[i];
|
||||
|
||||
if (value.type == StackValueType::NIL)
|
||||
printf("%i\t%s\n");
|
||||
else if (value.type == StackValueType::Int)
|
||||
printf("%i\t%s\n", i, std::to_string(std::get<double>(value.data)).c_str());
|
||||
else if (value.type == StackValueType::Bool)
|
||||
printf("%i\t%s\n", i, std::get<bool>(value.data) ? "True" : "False");
|
||||
else if (value.type == StackValueType::String)
|
||||
printf("%i\t%s\n", i, std::get<std::string>(value.data).c_str());
|
||||
else if (value.type == StackValueType::Function)
|
||||
{
|
||||
auto func = std::get<std::shared_ptr<DlangFunction>>(value.data);
|
||||
|
||||
if (func->isNative)
|
||||
printf("%i\tfunction\tnative\t0x%llX\n", i, func->nativeFunction);
|
||||
else
|
||||
{
|
||||
printf("%i\tfunction\tnon-native\tlocation: %i\n", i, static_cast<int64_t>(func->bodyAddress));
|
||||
printf("\tParameters:\n");
|
||||
|
||||
for (const auto& param : func->parameters)
|
||||
printf("\t\t%s\n", param.c_str());
|
||||
}
|
||||
}
|
||||
else if (value.type == StackValueType::Table)
|
||||
printf("%i\ttable\t0x%llX\n", i, std::get<std::shared_ptr<DlangTable>>(value.data).get());
|
||||
else
|
||||
printf("%i\tunknown\n", i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void NativeGlobalsDump(DlangVM* vm)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (int i = 0; i < vm->globals.top(); i++)
|
||||
{
|
||||
auto value = vm->globals.Get(i);
|
||||
|
||||
if (value.type == StackValueType::NIL)
|
||||
printf("%i\t%s\n");
|
||||
else if (value.type == StackValueType::Int)
|
||||
printf("%i\t%s\n", i, std::to_string(std::get<double>(value.data)).c_str());
|
||||
else if (value.type == StackValueType::Bool)
|
||||
printf("%i\t%s\n", i, std::get<bool>(value.data) ? "True" : "False");
|
||||
else if (value.type == StackValueType::String)
|
||||
printf("%i\t%s\n", i, std::get<std::string>(value.data).c_str());
|
||||
else if (value.type == StackValueType::Function)
|
||||
{
|
||||
auto func = std::get<std::shared_ptr<DlangFunction>>(value.data);
|
||||
|
||||
if (func->isNative)
|
||||
printf("%i\tfunction\tnative\t0x%llX\n", i, func->nativeFunction);
|
||||
else
|
||||
{
|
||||
printf("%i\tfunction\tnon-native\tlocation: %i\n", i, static_cast<int64_t>(func->bodyAddress));
|
||||
printf("\tParameters:\n");
|
||||
|
||||
for (const auto& param : func->parameters)
|
||||
printf("\t\t%s\n", param.c_str());
|
||||
}
|
||||
}
|
||||
else if (value.type == StackValueType::Table)
|
||||
printf("%i\ttable\t0x%llX\n", i, std::get<std::shared_ptr<DlangTable>>(value.data).get());
|
||||
else
|
||||
printf("%i\tunknown\n", i);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline void openlib_debug(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeStackDump);
|
||||
dlang_setfield(vm, "stackDump");
|
||||
|
||||
dlang_pushcfunction(vm, NativeGlobalsDump);
|
||||
dlang_setfield(vm, "globalsDump");
|
||||
|
||||
dlang_setglobal(vm, "debug");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifndef DLANG_LIBS_DEV_HPP
|
||||
#define DLANG_LIBS_DEV_HPP
|
||||
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativePrint(DlangVM* vm)
|
||||
{
|
||||
std::stringstream ss;
|
||||
auto argCount = dlang_top(vm);
|
||||
|
||||
std::vector<StackValue> values;
|
||||
for (int i = 0; i < argCount; ++i)
|
||||
values.push_back(dlang_totoken(vm));
|
||||
|
||||
for (auto it = values.rbegin(); it != values.rend(); ++it)
|
||||
{
|
||||
if (it->type == StackValueType::NIL)
|
||||
{
|
||||
printf("nil");
|
||||
}
|
||||
else {
|
||||
std::visit([&ss](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
ss << (arg ? "true" : "false") << " ";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double>) {
|
||||
ss << DoubleToString(arg) << " ";
|
||||
}
|
||||
else {
|
||||
ss << arg << " ";
|
||||
}
|
||||
}, it->data);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << ss.str() << '\n';
|
||||
}
|
||||
|
||||
static void NativeToNumber(DlangVM* vm)
|
||||
{
|
||||
auto content = dlang_tostring(vm);
|
||||
try {
|
||||
dlang_pushnumber(vm, std::stod(content));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
dlang_pushnumber(vm, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void NativeIsNil(DlangVM* vm)
|
||||
{
|
||||
dlang_pushboolean(vm, dlang_isnil(vm));
|
||||
}
|
||||
|
||||
static void NativeIsString(DlangVM* vm)
|
||||
{
|
||||
dlang_pushboolean(vm, dlang_isstring(vm));
|
||||
}
|
||||
|
||||
static void NativeIsNumber(DlangVM* vm)
|
||||
{
|
||||
dlang_pushboolean(vm, dlang_isnumber(vm));
|
||||
}
|
||||
|
||||
static void NativeIsBoolean(DlangVM* vm)
|
||||
{
|
||||
dlang_pushboolean(vm, dlang_isboolean(vm));
|
||||
}
|
||||
|
||||
static void NativeRequire(DlangVM* vm)
|
||||
{
|
||||
auto name = dlang_tostring(vm);
|
||||
|
||||
if (vm->loadedPlugins.Contains(name))
|
||||
{
|
||||
dlang_pushstring(vm, "plugin already loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
void* rawPtr = pluginManager->GetOrLoadInitFunc("./plugins/" + name);
|
||||
if (rawPtr)
|
||||
{
|
||||
vm->loadedPlugins.push(name);
|
||||
|
||||
using InitFunc = void(*)(DlangVM*);
|
||||
auto init = reinterpret_cast<InitFunc>(rawPtr);
|
||||
init(vm);
|
||||
|
||||
dlang_pushstring(vm, "plugin loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
dlang_pushstring(vm, "Failed to load plugin!");
|
||||
return;
|
||||
}
|
||||
|
||||
inline void openlib_dev(DlangVM* vm)
|
||||
{
|
||||
dlang_pushcfunction(vm, NativePrint);
|
||||
dlang_setglobal(vm, "print");
|
||||
|
||||
dlang_pushcfunction(vm, NativeToNumber);
|
||||
dlang_setglobal(vm, "tonumber");
|
||||
|
||||
dlang_pushcfunction(vm, NativeIsNil);
|
||||
dlang_setglobal(vm, "isnil");
|
||||
|
||||
dlang_pushcfunction(vm, NativeIsString);
|
||||
dlang_setglobal(vm, "isstring");
|
||||
|
||||
dlang_pushcfunction(vm, NativeIsNumber);
|
||||
dlang_setglobal(vm, "isnumber");
|
||||
|
||||
dlang_pushcfunction(vm, NativeIsBoolean);
|
||||
dlang_setglobal(vm, "isbool");
|
||||
|
||||
dlang_pushcfunction(vm, NativeRequire);
|
||||
dlang_setglobal(vm, "require");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef DLANG_LIBS_IO_HPP
|
||||
#define DLANG_LIBS_IO_HPP
|
||||
|
||||
#include <fstream>
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativeRead(DlangVM* vm)
|
||||
{
|
||||
bool binary = false;
|
||||
if (dlang_isboolean(vm))
|
||||
binary = dlang_toboolean(vm);
|
||||
std::string path = dlang_tostring(vm);
|
||||
|
||||
std::ifstream stream(path, (binary ? std::ios::binary : std::ios::in));
|
||||
if (!stream.is_open())
|
||||
{
|
||||
dlang_pushstring(vm, "Failed to open file!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string content(
|
||||
(std::istreambuf_iterator<char>(stream)),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
|
||||
stream.close();
|
||||
|
||||
dlang_pushstring(vm, content);
|
||||
}
|
||||
|
||||
static void NativeWrite(DlangVM* vm)
|
||||
{
|
||||
bool binary = false;
|
||||
if (dlang_isboolean(vm))
|
||||
binary = dlang_toboolean(vm);
|
||||
|
||||
std::string content = dlang_tostring(vm);
|
||||
std::string path = dlang_tostring(vm);
|
||||
|
||||
std::ofstream stream(path, (binary ? std::ios::binary : std::ios::out));
|
||||
if (!stream.is_open())
|
||||
{
|
||||
dlang_pushboolean(vm, false);
|
||||
return;
|
||||
}
|
||||
|
||||
stream.write(reinterpret_cast<const char*>(content.data()), content.size());
|
||||
stream.close();
|
||||
|
||||
dlang_pushboolean(vm, true);
|
||||
}
|
||||
|
||||
static void NativeInput(DlangVM* vm)
|
||||
{
|
||||
if (dlang_isstring(vm))
|
||||
{
|
||||
auto promp = dlang_tostring(vm);
|
||||
printf("%s", promp.c_str());
|
||||
}
|
||||
|
||||
std::string inputLine;
|
||||
if (std::getline(std::cin, inputLine))
|
||||
dlang_pushstring(vm, inputLine);
|
||||
else
|
||||
dlang_pushstring(vm, "");
|
||||
}
|
||||
|
||||
inline void openlib_io(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeRead);
|
||||
dlang_setfield(vm, "read");
|
||||
|
||||
dlang_pushcfunction(vm, NativeWrite);
|
||||
dlang_setfield(vm, "write");
|
||||
|
||||
dlang_pushcfunction(vm, NativeInput);
|
||||
dlang_setfield(vm, "input");
|
||||
|
||||
dlang_setglobal(vm, "io");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
#ifndef DLANG_LIBS_LIST_HPP
|
||||
#define DLANG_LIBS_LIST_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
void NativeListInsert(DlangVM* vm)
|
||||
{
|
||||
auto newListOrValue = dlang_pop(vm);
|
||||
auto insertList = dlang_tolist(vm);
|
||||
|
||||
if (newListOrValue.type == StackValueType::List)
|
||||
{
|
||||
auto list = std::get<std::shared_ptr<DlangList>>(newListOrValue.data);
|
||||
|
||||
for (const auto& val : list->values)
|
||||
insertList->values.push_back(val);
|
||||
}
|
||||
else if (newListOrValue.type == StackValueType::Int ||
|
||||
newListOrValue.type == StackValueType::Bool ||
|
||||
newListOrValue.type == StackValueType::String)
|
||||
insertList->values.push_back(newListOrValue);
|
||||
|
||||
dlang_push(vm, StackValue(StackValueType::List, insertList));
|
||||
}
|
||||
|
||||
void NativeListRemove(DlangVM* vm)
|
||||
{
|
||||
auto idx = dlang_tonumber(vm);
|
||||
auto list = dlang_tolist(vm);
|
||||
|
||||
list->values.erase(list->values.begin() + idx);
|
||||
|
||||
dlang_push(vm, StackValue(StackValueType::List, list));
|
||||
}
|
||||
|
||||
void NativeListConcat(DlangVM* vm)
|
||||
{
|
||||
std::string seperator = "";
|
||||
if (dlang_isstring(vm))
|
||||
seperator = dlang_tostring(vm);
|
||||
|
||||
auto list = dlang_tolist(vm);
|
||||
|
||||
std::stringstream ss;
|
||||
for (const auto& item : list->values)
|
||||
{
|
||||
switch (item.type)
|
||||
{
|
||||
case StackValueType::Int:
|
||||
ss << std::get<double>(item.data);
|
||||
break;
|
||||
case StackValueType::Bool:
|
||||
ss << std::get<bool>(item.data) ? "True" : "False";
|
||||
break;
|
||||
case StackValueType::String:
|
||||
ss << std::get<std::string>(item.data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!seperator.empty())
|
||||
ss << seperator;
|
||||
}
|
||||
|
||||
dlang_pushstring(vm, ss.str().substr(0, ss.str().size() - seperator.size()));
|
||||
}
|
||||
|
||||
inline void openlib_list(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeListInsert);
|
||||
dlang_setfield(vm, "insert");
|
||||
|
||||
dlang_pushcfunction(vm, NativeListRemove);
|
||||
dlang_setfield(vm, "remove");
|
||||
|
||||
dlang_pushcfunction(vm, NativeListConcat);
|
||||
dlang_setfield(vm, "concat");
|
||||
|
||||
dlang_setglobal(vm, "list");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
#ifndef DLANG_LIBS_MATH_HPP
|
||||
#define DLANG_LIBS_MATH_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <random>
|
||||
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativeMathAbs(DlangVM* vm)
|
||||
{
|
||||
auto val = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::fabs(val));
|
||||
}
|
||||
|
||||
static void NativeMathFloor(DlangVM* vm)
|
||||
{
|
||||
double val = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::floor(val));
|
||||
}
|
||||
|
||||
static void NativeMathCeil(DlangVM* vm)
|
||||
{
|
||||
double val = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::ceil(val));
|
||||
}
|
||||
|
||||
static void NativeMathSqrt(DlangVM* vm)
|
||||
{
|
||||
double val = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::sqrt(val));
|
||||
}
|
||||
|
||||
static void NativeMathPow(DlangVM* vm)
|
||||
{
|
||||
double exp = dlang_tonumber(vm);
|
||||
double base = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::pow(base, exp));
|
||||
}
|
||||
|
||||
static void NativeMathSin(DlangVM* vm)
|
||||
{
|
||||
double val = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::sin(val));
|
||||
}
|
||||
|
||||
static void NativeMathCos(DlangVM* vm)
|
||||
{
|
||||
double val = dlang_tonumber(vm);
|
||||
dlang_pushnumber(vm, std::cos(val));
|
||||
}
|
||||
|
||||
thread_local static std::random_device rd;
|
||||
thread_local static std::mt19937 gen(rd());
|
||||
|
||||
static void NativeMathSeed(DlangVM* vm)
|
||||
{
|
||||
double seedVal = dlang_tonumber(vm);
|
||||
gen.seed(static_cast<unsigned int>(seedVal));
|
||||
}
|
||||
|
||||
static void NativeMathRandom(DlangVM* vm)
|
||||
{
|
||||
double max = dlang_tonumber(vm);
|
||||
double min = dlang_tonumber(vm);
|
||||
|
||||
std::uniform_real_distribution<double> dis(min, max);
|
||||
dlang_pushnumber(vm, dis(gen));
|
||||
}
|
||||
|
||||
inline void openlib_math(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathAbs);
|
||||
dlang_setfield(vm, "abs");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathFloor);
|
||||
dlang_setfield(vm, "floor");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathCeil);
|
||||
dlang_setfield(vm, "ceil");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathSqrt);
|
||||
dlang_setfield(vm, "sqrt");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathPow);
|
||||
dlang_setfield(vm, "pow");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathSin);
|
||||
dlang_setfield(vm, "sin");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathCos);
|
||||
dlang_setfield(vm, "cos");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathSeed);
|
||||
dlang_setfield(vm, "seed");
|
||||
|
||||
dlang_pushcfunction(vm, NativeMathRandom);
|
||||
dlang_setfield(vm, "random");
|
||||
|
||||
dlang_setglobal(vm, "math");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef DLANG_LIBS_OS_HPP
|
||||
#define DLANG_LIBS_OS_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <filesystem>
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativeOsTime(DlangVM* vm)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
|
||||
dlang_pushnumber(vm, duration);
|
||||
}
|
||||
|
||||
static void NativeSleep(DlangVM* vm) {
|
||||
double ms = dlang_tonumber(vm);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int64_t>(ms)));
|
||||
}
|
||||
|
||||
static void NativeClock(DlangVM* vm)
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
|
||||
dlang_pushnumber(vm, duration);
|
||||
}
|
||||
|
||||
static void NativeCreateDir(DlangVM* vm)
|
||||
{
|
||||
auto dir = dlang_tostring(vm);
|
||||
std::filesystem::create_directories(dir);
|
||||
}
|
||||
|
||||
static void NativeRemove(DlangVM* vm)
|
||||
{
|
||||
auto path = dlang_tostring(vm);
|
||||
std::filesystem::remove_all(path);
|
||||
}
|
||||
|
||||
static void NativeListDir(DlangVM* vm)
|
||||
{
|
||||
auto dir = dlang_tostring(vm);
|
||||
|
||||
dlang_newlist(vm);
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(dir))
|
||||
{
|
||||
dlang_pushstring(vm, entry.path().string());
|
||||
dlang_pushindex(vm);
|
||||
}
|
||||
}
|
||||
|
||||
inline void openlib_os(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeOsTime);
|
||||
dlang_setfield(vm, "time");
|
||||
|
||||
dlang_pushcfunction(vm, NativeSleep);
|
||||
dlang_setfield(vm, "sleep");
|
||||
|
||||
dlang_pushcfunction(vm, NativeClock);
|
||||
dlang_setfield(vm, "clock");
|
||||
|
||||
dlang_pushcfunction(vm, NativeCreateDir);
|
||||
dlang_setfield(vm, "createdir");
|
||||
|
||||
dlang_pushcfunction(vm, NativeRemove);
|
||||
dlang_setfield(vm, "remove");
|
||||
|
||||
dlang_pushcfunction(vm, NativeListDir);
|
||||
dlang_setfield(vm, "listdir");
|
||||
|
||||
dlang_setglobal(vm, "os");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef DLANG_LIBS_REGEX_HPP
|
||||
#define DLANG_LIBS_REGEX_HPP
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativeRegexMatch(DlangVM* vm)
|
||||
{
|
||||
auto pattern = dlang_tostring(vm);
|
||||
auto text = dlang_tostring(vm);
|
||||
|
||||
try {
|
||||
std::regex rx(pattern);
|
||||
bool matched = std::regex_match(text, rx);
|
||||
dlang_pushnumber(vm, matched ? 1.0 : 0.0);
|
||||
}
|
||||
catch (...) {
|
||||
dlang_pushnumber(vm, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
static void NativeRegexSearch(DlangVM* vm)
|
||||
{
|
||||
auto pattern = dlang_tostring(vm);
|
||||
auto text = dlang_tostring(vm);
|
||||
|
||||
try {
|
||||
std::regex rx(pattern);
|
||||
bool found = std::regex_search(text, rx);
|
||||
dlang_pushnumber(vm, found ? 1.0 : 0.0);
|
||||
}
|
||||
catch (...) {
|
||||
dlang_pushnumber(vm, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
static void NativeRegexReplace(DlangVM* vm)
|
||||
{
|
||||
auto fmt = dlang_tostring(vm);
|
||||
auto pattern = dlang_tostring(vm);
|
||||
auto text = dlang_tostring(vm);
|
||||
|
||||
try {
|
||||
std::regex rx(pattern);
|
||||
std::string result = std::regex_replace(text, rx, fmt);
|
||||
dlang_pushstring(vm, result);
|
||||
dlang_pushindex(vm);
|
||||
}
|
||||
catch (...) {
|
||||
dlang_pushstring(vm, text);
|
||||
dlang_pushindex(vm);
|
||||
}
|
||||
}
|
||||
|
||||
inline void openlib_regex(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeRegexMatch);
|
||||
dlang_setfield(vm, "match");
|
||||
|
||||
dlang_pushcfunction(vm, NativeRegexSearch);
|
||||
dlang_setfield(vm, "search");
|
||||
|
||||
dlang_pushcfunction(vm, NativeRegexReplace);
|
||||
dlang_setfield(vm, "replace");
|
||||
|
||||
dlang_setglobal(vm, "regex");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
#ifndef DLANG_LIBS_STRING_HPP
|
||||
#define DLANG_LIBS_STRING_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
|
||||
static void NativeSubString(DlangVM* vm)
|
||||
{
|
||||
double to = -1;
|
||||
if (dlang_top(vm) > 2)
|
||||
to = dlang_tonumber(vm);
|
||||
|
||||
auto from = dlang_tonumber(vm);
|
||||
auto str = dlang_tostring(vm);
|
||||
|
||||
if (to == -1)
|
||||
dlang_pushstring(vm, str.substr(from));
|
||||
else
|
||||
dlang_pushstring(vm, str.substr(from, to));
|
||||
}
|
||||
|
||||
static void NativeContains(DlangVM* vm)
|
||||
{
|
||||
auto needle = dlang_tostring(vm);
|
||||
auto haystack = dlang_tostring(vm);
|
||||
|
||||
auto found = haystack.find(needle);
|
||||
dlang_pushboolean(vm, found != std::string::npos);
|
||||
}
|
||||
|
||||
static void NativeReplace(DlangVM* vm)
|
||||
{
|
||||
auto to = dlang_tostring(vm);
|
||||
auto from = dlang_tostring(vm);
|
||||
auto str = dlang_tostring(vm);
|
||||
|
||||
size_t startPos = 0;
|
||||
if (!from.empty())
|
||||
{
|
||||
while ((startPos = str.find(from, startPos)) != std::string::npos)
|
||||
{
|
||||
str.replace(startPos, from.length(), to);
|
||||
startPos += to.length();
|
||||
}
|
||||
}
|
||||
|
||||
dlang_pushstring(vm, str);
|
||||
}
|
||||
|
||||
static void NativeLower(DlangVM* vm)
|
||||
{
|
||||
auto str = dlang_tostring(vm);
|
||||
|
||||
std::transform(
|
||||
str.begin(), str.end(),
|
||||
str.begin(), ::tolower
|
||||
);
|
||||
|
||||
dlang_pushstring(vm, str);
|
||||
}
|
||||
|
||||
static void NativeUpper(DlangVM* vm)
|
||||
{
|
||||
auto str = dlang_tostring(vm);
|
||||
|
||||
std::transform(
|
||||
str.begin(), str.end(),
|
||||
str.begin(), ::toupper
|
||||
);
|
||||
|
||||
dlang_pushstring(vm, str);
|
||||
}
|
||||
|
||||
static void NativeSplit(DlangVM* vm)
|
||||
{
|
||||
auto delimiter = dlang_tostring(vm);
|
||||
auto str = dlang_tostring(vm);
|
||||
|
||||
std::vector<std::string> parts;
|
||||
|
||||
if (delimiter.empty()) {
|
||||
for (char c : str) {
|
||||
parts.push_back(std::string(1, c));
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t pos = 0;
|
||||
size_t delimSize = delimiter.size();
|
||||
|
||||
while ((pos = str.find(delimiter)) != std::string::npos) {
|
||||
parts.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos + delimSize);
|
||||
}
|
||||
|
||||
parts.push_back(str);
|
||||
}
|
||||
|
||||
dlang_newlist(vm);
|
||||
for (size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
dlang_pushstring(vm, parts[i]);
|
||||
dlang_pushindex(vm);
|
||||
}
|
||||
}
|
||||
|
||||
static void NativeFind(DlangVM* vm)
|
||||
{
|
||||
auto needle = dlang_tostring(vm);
|
||||
auto str = dlang_tostring(vm);
|
||||
|
||||
size_t pos = str.find(needle);
|
||||
|
||||
dlang_pushnumber(vm, static_cast<double>(pos));
|
||||
}
|
||||
|
||||
inline void openlib_string(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeSubString);
|
||||
dlang_setfield(vm, "substr");
|
||||
|
||||
dlang_pushcfunction(vm, NativeContains);
|
||||
dlang_setfield(vm, "contains");
|
||||
|
||||
dlang_pushcfunction(vm, NativeReplace);
|
||||
dlang_setfield(vm, "replace");
|
||||
|
||||
dlang_pushcfunction(vm, NativeLower);
|
||||
dlang_setfield(vm, "lower");
|
||||
|
||||
dlang_pushcfunction(vm, NativeUpper);
|
||||
dlang_setfield(vm, "upper");
|
||||
|
||||
dlang_pushcfunction(vm, NativeSplit);
|
||||
dlang_setfield(vm, "split");
|
||||
|
||||
dlang_pushcfunction(vm, NativeFind);
|
||||
dlang_setfield(vm, "find");
|
||||
|
||||
dlang_setglobal(vm, "string");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
#ifndef DLANG_LIBS_TABLE_HPP
|
||||
#define DLANG_LIBS_TABLE_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "../vm.hpp"
|
||||
#include "../helpers.hpp"
|
||||
#include "../json.hpp"
|
||||
|
||||
void NativeTableInsert(DlangVM* vm)
|
||||
{
|
||||
auto newTable = dlang_totable(vm);
|
||||
auto insertTable = dlang_totable(vm);
|
||||
|
||||
for (const auto [key, val] : newTable->fields)
|
||||
insertTable->fields[key] = val;
|
||||
|
||||
dlang_push(vm, StackValue(StackValueType::Table, insertTable));
|
||||
}
|
||||
|
||||
void NativeTableRemove(DlangVM* vm)
|
||||
{
|
||||
auto key = dlang_tostring(vm);
|
||||
auto table = dlang_totable(vm);
|
||||
|
||||
auto it = table->fields.find(key);
|
||||
if (it != table->fields.end())
|
||||
table->fields.erase(it);
|
||||
|
||||
dlang_push(vm, StackValue(StackValueType::Table, table));
|
||||
}
|
||||
|
||||
nlohmann::json DlangValueToJson(const StackValue& val)
|
||||
{
|
||||
switch (val.type)
|
||||
{
|
||||
case StackValueType::Int: return std::get<double>(val.data);
|
||||
case StackValueType::Bool: return std::get<bool>(val.data);
|
||||
case StackValueType::String: return std::get<std::string>(val.data);
|
||||
case StackValueType::NIL: return nullptr;
|
||||
|
||||
case StackValueType::Table:
|
||||
{
|
||||
auto tablePtr = std::get<std::shared_ptr<DlangTable>>(val.data);
|
||||
nlohmann::json jObj = nlohmann::json::object();
|
||||
|
||||
for (const auto& [key, fieldVal] : tablePtr->fields)
|
||||
jObj[key] = DlangValueToJson(fieldVal);
|
||||
|
||||
return jObj;
|
||||
}
|
||||
|
||||
case StackValueType::List:
|
||||
{
|
||||
auto listPtr = std::get<std::shared_ptr<DlangList>>(val.data);
|
||||
nlohmann::json jArr = nlohmann::json::array();
|
||||
|
||||
for (const auto& elemVal : listPtr->values)
|
||||
jArr.push_back(DlangValueToJson(elemVal));
|
||||
|
||||
return jArr;
|
||||
}
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PushJsonToVM(DlangVM* vm, const nlohmann::json& j)
|
||||
{
|
||||
if (j.is_number())
|
||||
{
|
||||
dlang_pushnumber(vm, j.get<double>());
|
||||
}
|
||||
else if (j.is_boolean())
|
||||
{
|
||||
dlang_pushboolean(vm, j.get<bool>());
|
||||
}
|
||||
else if (j.is_string())
|
||||
{
|
||||
dlang_pushstring(vm, j.get<std::string>());
|
||||
}
|
||||
else if (j.is_array())
|
||||
{
|
||||
dlang_newlist(vm);
|
||||
auto listPtr = dlang_tolist(vm);
|
||||
|
||||
for (const auto& element : j)
|
||||
{
|
||||
PushJsonToVM(vm, element);
|
||||
listPtr->values.push_back(vm->stack.pop());
|
||||
}
|
||||
|
||||
vm->stack.push(StackValue(StackValueType::List, listPtr));
|
||||
}
|
||||
else if (j.is_object())
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
auto tablePtr = dlang_totable(vm);
|
||||
|
||||
for (auto it = j.begin(); it != j.end(); ++it)
|
||||
{
|
||||
PushJsonToVM(vm, it.value());
|
||||
tablePtr->fields[it.key()] = vm->stack.pop();
|
||||
}
|
||||
|
||||
vm->stack.push(StackValue(StackValueType::Table, tablePtr));
|
||||
}
|
||||
else
|
||||
{
|
||||
dlang_pushnil(vm);
|
||||
}
|
||||
}
|
||||
|
||||
void NativeJsonEncode(DlangVM* vm)
|
||||
{
|
||||
int indent = 0;
|
||||
if (dlang_isnumber(vm))
|
||||
indent = dlang_tonumber(vm);
|
||||
|
||||
if (!dlang_istable(vm))
|
||||
{
|
||||
dlang_pushnil(vm);
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json j = DlangValueToJson(dlang_pop(vm));
|
||||
|
||||
dlang_pushstring(vm, j.dump(indent));
|
||||
}
|
||||
|
||||
void NativeJsonDecode(DlangVM* vm)
|
||||
{
|
||||
std::string jsonStr = dlang_tostring(vm);
|
||||
|
||||
try
|
||||
{
|
||||
nlohmann::json j = nlohmann::json::parse(jsonStr);
|
||||
PushJsonToVM(vm, j);
|
||||
}
|
||||
catch (const nlohmann::json::parse_error& e)
|
||||
{
|
||||
throw std::runtime_error("JSON Parse Error: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
inline void openlib_table(DlangVM* vm)
|
||||
{
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeTableInsert);
|
||||
dlang_setfield(vm, "insert");
|
||||
|
||||
dlang_pushcfunction(vm, NativeTableRemove);
|
||||
dlang_setfield(vm, "remove");
|
||||
|
||||
dlang_pushcfunction(vm, NativeJsonEncode);
|
||||
dlang_setfield(vm, "json_encode");
|
||||
|
||||
dlang_pushcfunction(vm, NativeJsonDecode);
|
||||
dlang_setfield(vm, "json_decode");
|
||||
|
||||
dlang_setglobal(vm, "table");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef DLANG_PARSER_HPP
|
||||
#define DLANG_PARSER_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "types/include.hpp"
|
||||
#include "program.hpp"
|
||||
|
||||
#include "parser/expressions.hpp"
|
||||
#include "parser/statements.hpp"
|
||||
|
||||
#include "language.hpp"
|
||||
|
||||
class Parser
|
||||
{
|
||||
private:
|
||||
std::vector<Token> _tokens;
|
||||
size_t _pos = 0;
|
||||
|
||||
private:
|
||||
Token& Current();
|
||||
Token& Consume();
|
||||
Token& Peek(size_t offset = 1);
|
||||
bool Match(TokenType type, const std::string& value);
|
||||
Token& ShouldBe(TokenType type, const std::string& value = "");
|
||||
bool Match(TokenType type, char value);
|
||||
Token& ShouldBe(TokenType type, char value);
|
||||
|
||||
public:
|
||||
Parser(const std::vector<Token>& tokens);
|
||||
Program ParseProgram();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Statement> ParseStatement();
|
||||
std::unique_ptr<Statement> ParseVariableDeclaration();
|
||||
std::unique_ptr<Statement> ParseAssignment();
|
||||
std::unique_ptr<Statement> ParseWhileDeclaration();
|
||||
std::unique_ptr<Statement> ParseForDeclaration();
|
||||
std::unique_ptr<Statement> ParseForeachDeclaration();
|
||||
std::unique_ptr<Statement> ParseIfDeclaration();
|
||||
std::unique_ptr<Statement> ParseFunctionDeclaration();
|
||||
std::unique_ptr<Statement> ParseReturnStatement();
|
||||
std::unique_ptr<Statement> ParseIncludeStatement();
|
||||
|
||||
std::unique_ptr<Expression> ParseFunctionCall(std::unique_ptr<Expression> callee);
|
||||
std::unique_ptr<Expression> ParseExpression();
|
||||
std::unique_ptr<Expression> ParsePrimary();
|
||||
std::unique_ptr<Expression> ParseMultiplicative();
|
||||
std::unique_ptr<Expression> ParseAdditive();
|
||||
std::unique_ptr<Expression> ParseComparison();
|
||||
std::unique_ptr<Expression> ParseTableLiteral();
|
||||
std::unique_ptr<Expression> ParseListLiteral();
|
||||
std::unique_ptr<Expression> ParseLogicalAndOr();
|
||||
std::unique_ptr<Expression> ParseFunctionExpression();
|
||||
std::unique_ptr<Expression> ParseSizeExpression();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef DLANG_EXPRESSIONS_HPP
|
||||
#define DLANG_EXPRESSIONS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "../types/include.hpp"
|
||||
|
||||
struct Expression {
|
||||
virtual ~Expression() = default;
|
||||
virtual void Print(int indent = 0) const = 0;
|
||||
};
|
||||
|
||||
struct NumberExpression : Expression {
|
||||
double value;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "Number: " << value << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
struct BooleanExpression : Expression {
|
||||
bool value;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "Boolean: " << (value ? "true" : "false") << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
struct StringExpression : Expression {
|
||||
std::string value;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "string: " << value << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
struct IdentifierExpression : Expression {
|
||||
std::string name;
|
||||
std::string keyName;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "Identifier: " << name << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
struct BinaryExpression : Expression {
|
||||
std::unique_ptr<Expression> left;
|
||||
std::string op;
|
||||
std::unique_ptr<Expression> right;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "Binary: " << op << '\n';
|
||||
|
||||
left->Print(indent + 2);
|
||||
right->Print(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct CallExpression : Expression
|
||||
{
|
||||
std::unique_ptr<Expression> callee;
|
||||
std::vector< std::unique_ptr<Expression>> expressions;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << " Call" << "\n";
|
||||
callee->Print(indent + 2);
|
||||
for (const auto& expr : expressions)
|
||||
expr->Print(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct TableLiteralExpression : Expression
|
||||
{
|
||||
std::vector<std::pair<std::string, std::unique_ptr<Expression>>> fields;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "TableLiteral {\n";
|
||||
|
||||
for (const auto& [key, expr] : fields)
|
||||
{
|
||||
std::cout << std::string(indent + 2, ' ')
|
||||
<< key << ":\n";
|
||||
if (expr)
|
||||
{
|
||||
expr->Print(indent + 4);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "}\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct ListLiteralExpression : Expression
|
||||
{
|
||||
std::vector<std::unique_ptr<Expression>> fields;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "TableLiteral {\n";
|
||||
|
||||
for (const auto& val : fields)
|
||||
val->Print(indent + 2);
|
||||
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "}\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct MemberAccessExpression : Expression
|
||||
{
|
||||
std::unique_ptr<Expression> object;
|
||||
std::string memberName;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "MemberAccess: ." << memberName << '\n';
|
||||
|
||||
if (object)
|
||||
{
|
||||
object->Print(indent + 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct IndexAccessExpression : Expression
|
||||
{
|
||||
std::unique_ptr<Expression> object;
|
||||
std::unique_ptr<Expression> memberIndex;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "IndexAccess: ." << '\n';
|
||||
|
||||
memberIndex->Print(indent);
|
||||
|
||||
if (object)
|
||||
{
|
||||
object->Print(indent + 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SizeExpression : Expression
|
||||
{
|
||||
std::string identifier;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "Identifier: " << identifier << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
#ifndef DLANG_STATEMENTS_HPP
|
||||
#define DLANG_STATEMENTS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "expressions.hpp"
|
||||
|
||||
struct Statement
|
||||
{
|
||||
virtual ~Statement() = default;
|
||||
virtual void Print(int indent = 0) const = 0;
|
||||
};
|
||||
|
||||
struct VariableDeclaration : Statement
|
||||
{
|
||||
std::string name;
|
||||
std::unique_ptr<Expression> initializer;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "VariableDeclaration: " << name << '\n';
|
||||
|
||||
initializer->Print(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct Assignment : Statement
|
||||
{
|
||||
std::unique_ptr<Expression> lhs;
|
||||
std::unique_ptr<Expression> value;
|
||||
std::string assignmentOperator;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ')
|
||||
<< "Assignment" << '\n';
|
||||
lhs->Print(indent + 2);
|
||||
value->Print(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpressionStatement : Statement {
|
||||
std::unique_ptr<Expression> expression;
|
||||
|
||||
void Print(int indent = 0) const override {
|
||||
expression->Print(indent);
|
||||
}
|
||||
};
|
||||
|
||||
struct WhileStatement : Statement
|
||||
{
|
||||
std::unique_ptr<Expression> condition;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "WhileStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Condition:\n";
|
||||
condition->Print(indent + 4);
|
||||
std::cout << std::string(indent + 2, ' ') << "Body:\n";
|
||||
for (const auto& stmt : body)
|
||||
{
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ForStatement : Statement
|
||||
{
|
||||
std::string var;
|
||||
double from;
|
||||
double to;
|
||||
double step = 1;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "ForStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "var: " << var << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "from: " << from << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "to: " << to << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "step: " << step << "\n";
|
||||
for (const auto& stmt : body)
|
||||
{
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ForeachStatement : Statement
|
||||
{
|
||||
std::string var;
|
||||
std::string list;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "ForeachStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "var: " << var << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "list: " << list << "\n";
|
||||
for (const auto& stmt : body)
|
||||
{
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ForeachTableStatement : Statement
|
||||
{
|
||||
std::string varKey;
|
||||
std::string varValue;
|
||||
std::string table;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "ForeachTableStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "var key: " << varKey << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "var value: " << varValue << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "table: " << table << "\n";
|
||||
for (const auto& stmt : body)
|
||||
{
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct IfStatement : Statement
|
||||
{
|
||||
std::unique_ptr<Expression> expression;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
std::vector<std::unique_ptr<Statement>> elseBody;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "IfStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "expression:\n";
|
||||
expression->Print(indent + 4);
|
||||
std::cout << std::string(indent + 2, ' ') << "Body:\n";
|
||||
for (const auto& stmt : body)
|
||||
stmt->Print(indent + 4);
|
||||
std::cout << std::string(indent + 2, ' ') << "Else Body:\n";
|
||||
for (const auto& stmt : elseBody)
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionStatement : Statement
|
||||
{
|
||||
std::string name;
|
||||
std::vector<std::string> parameters;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "FunctionStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "name: " << name << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Paramters:\n";
|
||||
for (const auto& param : parameters)
|
||||
std::cout << std::string(indent + 4, ' ') << param << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Body:\n";
|
||||
for (const auto& stmt : body)
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionExpression : Expression
|
||||
{
|
||||
std::vector<std::string> parameters;
|
||||
std::vector<std::unique_ptr<Statement>> body;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "FunctionExpression\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Paramters:\n";
|
||||
for (const auto& param : parameters)
|
||||
std::cout << std::string(indent + 4, ' ') << param << "\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Body:\n";
|
||||
for (const auto& stmt : body)
|
||||
stmt->Print(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReturnStatement : Statement
|
||||
{
|
||||
std::unique_ptr<Expression> expression;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "ReturnStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Expression:\n";
|
||||
if(expression) expression->Print(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
struct IncludeStatement : Statement
|
||||
{
|
||||
std::string path;
|
||||
|
||||
void Print(int indent = 0) const override
|
||||
{
|
||||
std::cout << std::string(indent, ' ') << "IncludeStatement\n";
|
||||
std::cout << std::string(indent + 2, ' ') << "Path: " << path << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#ifndef MODULES_HPP
|
||||
#define MODULES_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define PLATFORM_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__)
|
||||
#define PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
#else
|
||||
#error "Unsupported platform (Windows and Linux only)."
|
||||
#endif
|
||||
|
||||
class ModuleManager {
|
||||
public:
|
||||
ModuleManager() = default;
|
||||
|
||||
~ModuleManager() {
|
||||
UnloadAll();
|
||||
}
|
||||
|
||||
ModuleManager(const ModuleManager&) = delete;
|
||||
ModuleManager& operator=(const ModuleManager&) = delete;
|
||||
|
||||
void* GetOrLoadInitFunc(const std::string& path, const std::string& initFuncName = "initialize") {
|
||||
auto it = initFunctions.find(path);
|
||||
if (it != initFunctions.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void* funcPtr = nullptr;
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
HMODULE handle = LoadLibraryA((path + ".dll").c_str());
|
||||
if (!handle) {
|
||||
std::cerr << "[ModuleManager] Failed to load DLL (" << path << "): Code " << GetLastError() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
funcPtr = reinterpret_cast<void*>(GetProcAddress(handle, initFuncName.c_str()));
|
||||
if (!funcPtr) {
|
||||
std::cerr << "[ModuleManager] Function '" << initFuncName << "' not found in: " << path << std::endl;
|
||||
FreeLibrary(handle);
|
||||
return nullptr;
|
||||
}
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
void* handle = dlopen((path + ".so").c_str(), RTLD_NOW);
|
||||
if (!handle) {
|
||||
std::cerr << "[ModuleManager] Failed to load SO (" << path << "): " << dlerror() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dlerror();
|
||||
|
||||
funcPtr = dlsym(handle, initFuncName.c_str());
|
||||
const char* dlsym_error = dlerror();
|
||||
if (dlsym_error) {
|
||||
std::cerr << "[ModuleManager] Function '" << initFuncName << "' not found: " << dlsym_error << std::endl;
|
||||
dlclose(handle);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
modules[path] = handle;
|
||||
initFunctions[path] = funcPtr;
|
||||
|
||||
std::cout << "[ModuleManager] Module loaded: " << path << std::endl;
|
||||
return funcPtr;
|
||||
}
|
||||
|
||||
bool UnloadModule(const std::string& path) {
|
||||
auto it = modules.find(path);
|
||||
if (it == modules.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
FreeLibrary(static_cast<HMODULE>(it->second));
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
dlclose(it->second);
|
||||
#endif
|
||||
|
||||
modules.erase(it);
|
||||
initFunctions.erase(path);
|
||||
std::cout << "[ModuleManager] Module unloaded: " << path << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadAll() {
|
||||
for (auto& [path, handle] : modules) {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
FreeLibrary(static_cast<HMODULE>(handle));
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
dlclose(handle);
|
||||
#endif
|
||||
}
|
||||
modules.clear();
|
||||
initFunctions.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
std::unordered_map<std::string, HMODULE> modules;
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
std::unordered_map<std::string, void*> modules;
|
||||
#endif
|
||||
|
||||
std::unordered_map<std::string, void*> initFunctions;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef DLANG_PROGRAM_HPP
|
||||
#define DLANG_PROGRAM_HPP
|
||||
|
||||
#include <vector>
|
||||
#include "parser/statements.hpp"
|
||||
|
||||
struct Program
|
||||
{
|
||||
std::vector<std::unique_ptr<Statement>> statements;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,848 @@
|
||||
#include "../compiler.hpp"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "../lexer.hpp"
|
||||
#include "../parser.hpp"
|
||||
|
||||
void Compiler::Emit(Opcodes op)
|
||||
{
|
||||
_bytecode.push_back(static_cast<double>(op));
|
||||
}
|
||||
|
||||
void Compiler::Emit(const std::string& str)
|
||||
{
|
||||
auto it = std::find(_stringTable.begin(), _stringTable.end(), str);
|
||||
if (it != _stringTable.end())
|
||||
{
|
||||
auto index = std::distance(_stringTable.begin(), it);
|
||||
Emit(index);
|
||||
}
|
||||
else {
|
||||
_stringTable.push_back(str);
|
||||
Emit(_stringTable.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::Emit(double value)
|
||||
{
|
||||
_bytecode.push_back(value);
|
||||
}
|
||||
|
||||
void Compiler::Compile(Statement* stmt)
|
||||
{
|
||||
if (auto* varDecl = dynamic_cast<VariableDeclaration*>(stmt))
|
||||
{
|
||||
Compile(varDecl->initializer.get());
|
||||
Emit(Opcodes::DEFINE_VAR);
|
||||
Emit(varDecl->name);
|
||||
}
|
||||
else if (auto* assign = dynamic_cast<Assignment*>(stmt))
|
||||
{
|
||||
if (auto* memberAccess = dynamic_cast<MemberAccessExpression*>(assign->lhs.get()))
|
||||
{
|
||||
Compile(memberAccess->object.get());
|
||||
Compile(assign->value.get());
|
||||
|
||||
Emit(Opcodes::SET_FIELD);
|
||||
Emit(memberAccess->memberName);
|
||||
Emit(Opcodes::POP);
|
||||
}
|
||||
else if (auto* indexMemberAccess = dynamic_cast<IndexAccessExpression*>(assign->lhs.get()))
|
||||
{
|
||||
Compile(indexMemberAccess->object.get());
|
||||
Compile(indexMemberAccess->memberIndex.get());
|
||||
Compile(assign->value.get());
|
||||
|
||||
Emit(Opcodes::SET_INDEX);
|
||||
Emit(Opcodes::POP);
|
||||
}
|
||||
else if (auto* idExpr = dynamic_cast<IdentifierExpression*>(assign->lhs.get()))
|
||||
{
|
||||
if (assign->assignmentOperator == "+=" ||
|
||||
assign->assignmentOperator == "-=" ||
|
||||
assign->assignmentOperator == "*=" ||
|
||||
assign->assignmentOperator == "/=")
|
||||
{
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idExpr->name);
|
||||
Compile(assign->value.get());
|
||||
|
||||
if (assign->assignmentOperator == "+=")
|
||||
Emit(Opcodes::ADD);
|
||||
|
||||
if (assign->assignmentOperator == "-=")
|
||||
Emit(Opcodes::SUB);
|
||||
|
||||
if (assign->assignmentOperator == "*=")
|
||||
Emit(Opcodes::MUL);
|
||||
|
||||
if (assign->assignmentOperator == "/=")
|
||||
Emit(Opcodes::DIV);
|
||||
}
|
||||
else {
|
||||
Compile(assign->value.get());
|
||||
}
|
||||
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(idExpr->name);
|
||||
}
|
||||
}
|
||||
else if (auto* exprStmt = dynamic_cast<ExpressionStatement*>(stmt))
|
||||
{
|
||||
Compile(exprStmt->expression.get());
|
||||
Emit(Opcodes::POP);
|
||||
}
|
||||
else if (auto* whileStmt = dynamic_cast<WhileStatement*>(stmt))
|
||||
{
|
||||
auto loopStart = _bytecode.size();
|
||||
Compile(whileStmt->condition.get());
|
||||
|
||||
auto jzAddress = _bytecode.size();
|
||||
Emit(Opcodes::JZ);
|
||||
Emit(0); // Dummy, will be backpatched
|
||||
|
||||
for (const auto& bodyStmt : whileStmt->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(loopStart);
|
||||
|
||||
_bytecode[jzAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
}
|
||||
else if (auto* forStmt = dynamic_cast<ForStatement*>(stmt))
|
||||
{
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(forStmt->from);
|
||||
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(forStmt->var);
|
||||
|
||||
auto loopStart = _bytecode.size();
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(forStmt->var);
|
||||
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(forStmt->to);
|
||||
|
||||
Emit(Opcodes::LESS_THAN);
|
||||
|
||||
auto jzAddress = _bytecode.size();
|
||||
Emit(Opcodes::JZ);
|
||||
Emit(0); // Dummy, will be backpatched
|
||||
|
||||
for (const auto& bodyStmt : forStmt->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(forStmt->var);
|
||||
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(forStmt->step);
|
||||
|
||||
Emit(Opcodes::ADD);
|
||||
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(forStmt->var);
|
||||
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(loopStart);
|
||||
|
||||
_bytecode[jzAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
}
|
||||
else if (auto* foreachStmt = dynamic_cast<ForeachStatement*>(stmt))
|
||||
{
|
||||
std::string idxVar = "__foreach_idx_" + foreachStmt->var;
|
||||
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(0);
|
||||
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(idxVar);
|
||||
|
||||
auto loopStart = _bytecode.size();
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idxVar);
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(foreachStmt->list);
|
||||
Emit(Opcodes::SIZE);
|
||||
|
||||
Emit(Opcodes::LESS_THAN);
|
||||
|
||||
auto jzAddress = _bytecode.size();
|
||||
Emit(Opcodes::JZ);
|
||||
Emit(0); // Backpatching this adress
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(foreachStmt->list);
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idxVar);
|
||||
Emit(Opcodes::GET_INDEX);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(foreachStmt->var);
|
||||
|
||||
for (const auto& bodyStmt : foreachStmt->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idxVar);
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(1);
|
||||
Emit(Opcodes::ADD);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(idxVar);
|
||||
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(loopStart);
|
||||
|
||||
_bytecode[jzAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
}
|
||||
else if (auto* foreachTableStmt = dynamic_cast<ForeachTableStatement*>(stmt))
|
||||
{
|
||||
std::string keysListVar = "__foreach_tbl_keys_" + foreachTableStmt->varKey + "_" + foreachTableStmt->varValue;
|
||||
std::string idxVar = "__foreach_tbl_idx_" + foreachTableStmt->varKey + "_" + foreachTableStmt->varValue;
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(foreachTableStmt->table);
|
||||
Emit(Opcodes::KEYS);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(keysListVar);
|
||||
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(0);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(idxVar);
|
||||
|
||||
auto loopStart = _bytecode.size();
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idxVar);
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(keysListVar);
|
||||
Emit(Opcodes::SIZE);
|
||||
|
||||
Emit(Opcodes::LESS_THAN);
|
||||
|
||||
auto jzAddress = _bytecode.size();
|
||||
Emit(Opcodes::JZ);
|
||||
Emit(0);
|
||||
|
||||
std::string currentKeyVar = "__foreach_curr_key_" + foreachTableStmt->varKey;
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(keysListVar);
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idxVar);
|
||||
Emit(Opcodes::GET_INDEX);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(currentKeyVar);
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(currentKeyVar);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(foreachTableStmt->varKey);
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(foreachTableStmt->table);
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(currentKeyVar);
|
||||
Emit(Opcodes::GET_INDEX);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(foreachTableStmt->varValue);
|
||||
|
||||
for (const auto& bodyStmt : foreachTableStmt->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(idxVar);
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(1);
|
||||
Emit(Opcodes::ADD);
|
||||
Emit(Opcodes::STORE_VAR);
|
||||
Emit(idxVar);
|
||||
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(loopStart);
|
||||
|
||||
_bytecode[jzAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
}
|
||||
else if (auto* ifStmt = dynamic_cast<IfStatement*>(stmt))
|
||||
{
|
||||
Compile(ifStmt->expression.get());
|
||||
|
||||
auto jzAddress = _bytecode.size();
|
||||
Emit(Opcodes::JZ);
|
||||
Emit(0); // Dummy, will be backpatched
|
||||
|
||||
for (const auto& bodyStmt : ifStmt->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
if (!ifStmt->elseBody.empty())
|
||||
{
|
||||
auto jumpAddress = _bytecode.size();
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(0); // Dummy, will be backpatched
|
||||
|
||||
_bytecode[jzAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
|
||||
for (const auto& bodyStmt : ifStmt->elseBody)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
_bytecode[jumpAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
}
|
||||
else
|
||||
_bytecode[jzAddress + 1] = static_cast<double>(_bytecode.size());
|
||||
}
|
||||
else if (auto* funcStmt = dynamic_cast<FunctionStatement*>(stmt))
|
||||
{
|
||||
Emit(Opcodes::FUNC_DEF);
|
||||
Emit(funcStmt->name);
|
||||
|
||||
Emit(static_cast<double>(funcStmt->parameters.size()));
|
||||
for (const auto& param : funcStmt->parameters)
|
||||
Emit(param);
|
||||
|
||||
auto bodyStartPlaceholder = _bytecode.size();
|
||||
Emit(0); // Dummy body start
|
||||
|
||||
auto bodyEndPlaceholder = _bytecode.size();
|
||||
Emit(0); // Dummy body end
|
||||
|
||||
auto skipJumpAddress = _bytecode.size();
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(0); // Dummy skip target
|
||||
|
||||
auto realBodyStart = static_cast<double>(_bytecode.size());
|
||||
_bytecode[bodyStartPlaceholder] = realBodyStart;
|
||||
|
||||
for (const auto& bodyStmt : funcStmt->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
Emit(Opcodes::RET);
|
||||
|
||||
auto realBodyEnd = static_cast<double>(_bytecode.size());
|
||||
_bytecode[bodyEndPlaceholder] = realBodyEnd;
|
||||
_bytecode[skipJumpAddress + 1] = realBodyEnd;
|
||||
}
|
||||
else if (auto* retStmt = dynamic_cast<ReturnStatement*>(stmt))
|
||||
{
|
||||
if (retStmt->expression)
|
||||
Compile(retStmt->expression.get());
|
||||
|
||||
Emit(Opcodes::RET);
|
||||
}
|
||||
else if (auto* incStmt = dynamic_cast<IncludeStatement*>(stmt))
|
||||
{
|
||||
auto path = incStmt->path;
|
||||
|
||||
if (std::find(_includes.begin(), _includes.end(), path) != _includes.end())
|
||||
return;
|
||||
|
||||
_includes.push_back(path);
|
||||
|
||||
if (!std::filesystem::exists(path))
|
||||
throw std::runtime_error("File '" + path + "' does not exist!");
|
||||
|
||||
std::ifstream file(path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Failed to open file '" + path + "'!");
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::string sourceCode;
|
||||
sourceCode.resize(size);
|
||||
|
||||
if (!file.read(&sourceCode[0], size))
|
||||
throw std::runtime_error("Failed to read file '" + path + "', possibly corrupt?");
|
||||
|
||||
Lexer lexer;
|
||||
lexer.Parse(sourceCode);
|
||||
lexer.ParseOrganizedTokens();
|
||||
auto tokens = lexer.GetOrganizedTokens();
|
||||
|
||||
Parser parser(tokens);
|
||||
auto includedProgram = parser.ParseProgram();
|
||||
|
||||
for (const auto& stmt : includedProgram.statements)
|
||||
Compile(stmt.get());
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::Compile(Expression* expr)
|
||||
{
|
||||
if (auto* num = dynamic_cast<NumberExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::PUSH_INT);
|
||||
Emit(num->value);
|
||||
}
|
||||
else if (auto* id = dynamic_cast<IdentifierExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(id->name);
|
||||
}
|
||||
else if (auto* bin = dynamic_cast<BinaryExpression*>(expr))
|
||||
{
|
||||
Compile(bin->left.get());
|
||||
Compile(bin->right.get());
|
||||
|
||||
char op = bin->op.at(0);
|
||||
switch (op)
|
||||
{
|
||||
case '+': Emit(Opcodes::ADD); return; break;
|
||||
case '-': Emit(Opcodes::SUB); return; break;
|
||||
case '*': Emit(Opcodes::MUL); return; break;
|
||||
case '/': Emit(Opcodes::DIV); return; break;
|
||||
case '>': Emit(Opcodes::GREATER_THAN); return; break;
|
||||
case '<': Emit(Opcodes::LESS_THAN); return; break;
|
||||
}
|
||||
|
||||
if (bin->op == "==")
|
||||
Emit(Opcodes::CMP);
|
||||
else if (bin->op == ">=")
|
||||
Emit(Opcodes::GTE);
|
||||
else if (bin->op == "<=")
|
||||
Emit(Opcodes::LTE);
|
||||
else if (bin->op == "&&")
|
||||
Emit(Opcodes::AND);
|
||||
else if (bin->op == "||")
|
||||
Emit(Opcodes::OR);
|
||||
else if (bin->op == "!=")
|
||||
Emit(Opcodes::NTE);
|
||||
}
|
||||
else if (auto* boolean = dynamic_cast<BooleanExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::PUSH_BOOL);
|
||||
Emit(boolean->value == true ? 1 : 0);
|
||||
}
|
||||
else if (auto* string = dynamic_cast<StringExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::PUSH_STRING);
|
||||
Emit(string->value);
|
||||
}
|
||||
else if (auto* call = dynamic_cast<CallExpression*>(expr))
|
||||
{
|
||||
for (const auto& arg : call->expressions)
|
||||
{
|
||||
Compile(arg.get());
|
||||
}
|
||||
|
||||
if (call->callee)
|
||||
Compile(call->callee.get());
|
||||
|
||||
Emit(Opcodes::CALL);
|
||||
Emit(static_cast<double>(call->expressions.size()));
|
||||
}
|
||||
else if (auto* table = dynamic_cast<TableLiteralExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::NEW_TABLE);
|
||||
|
||||
for (const auto& [key, valExpr] : table->fields)
|
||||
{
|
||||
if (valExpr)
|
||||
Compile(valExpr.get());
|
||||
|
||||
Emit(Opcodes::SET_FIELD);
|
||||
Emit(key);
|
||||
}
|
||||
}
|
||||
else if (auto* list = dynamic_cast<ListLiteralExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::NEW_LIST);
|
||||
|
||||
for (const auto& valExpr : list->fields)
|
||||
{
|
||||
if (valExpr)
|
||||
Compile(valExpr.get());
|
||||
|
||||
Emit(Opcodes::PUSH_LIST);
|
||||
}
|
||||
}
|
||||
else if (auto* tableMember = dynamic_cast<MemberAccessExpression*>(expr))
|
||||
{
|
||||
if(tableMember->object) Compile(tableMember->object.get());
|
||||
Emit(Opcodes::GET_FIELD);
|
||||
Emit(tableMember->memberName);
|
||||
}
|
||||
else if (auto* listMember = dynamic_cast<IndexAccessExpression*>(expr))
|
||||
{
|
||||
if(listMember->object) Compile(listMember->object.get());
|
||||
Compile(listMember->memberIndex.get());
|
||||
Emit(Opcodes::GET_INDEX);
|
||||
}
|
||||
else if (auto* funcExpr = dynamic_cast<FunctionExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::FUNC_DEF);
|
||||
Emit(""); // Name
|
||||
|
||||
Emit(static_cast<double>(funcExpr->parameters.size()));
|
||||
for (const auto& param : funcExpr->parameters)
|
||||
Emit(param);
|
||||
|
||||
auto bodyStartPlaceholder = _bytecode.size();
|
||||
Emit(0); // Dummy body start
|
||||
|
||||
auto bodyEndPlaceholder = _bytecode.size();
|
||||
Emit(0); // Dummy body end
|
||||
|
||||
auto skipJumpAddress = _bytecode.size();
|
||||
Emit(Opcodes::JUMP);
|
||||
Emit(0); // Dummy jump target
|
||||
|
||||
auto realBodyStart = static_cast<double>(_bytecode.size());
|
||||
_bytecode[bodyStartPlaceholder] = realBodyStart;
|
||||
|
||||
for (const auto& bodyStmt : funcExpr->body)
|
||||
Compile(bodyStmt.get());
|
||||
|
||||
Emit(Opcodes::RET);
|
||||
|
||||
auto realBodyEnd = static_cast<double>(_bytecode.size());
|
||||
_bytecode[bodyEndPlaceholder] = realBodyEnd;
|
||||
_bytecode[skipJumpAddress + 1] = realBodyEnd;
|
||||
}
|
||||
else if (auto sizeStmt = dynamic_cast<SizeExpression*>(expr))
|
||||
{
|
||||
Emit(Opcodes::PUSH_VAR);
|
||||
Emit(sizeStmt->identifier);
|
||||
Emit(Opcodes::SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::FinalCompile()
|
||||
{
|
||||
// build binary header
|
||||
std::vector<double> header = { 0xFF, 0x8C, 0xFF }; // Magic bytes to tell dlang header here
|
||||
|
||||
header.push_back(static_cast<double>(_stringTable.size()));
|
||||
for (const auto& string : _stringTable)
|
||||
{
|
||||
header.push_back(static_cast<double>(string.size()));
|
||||
|
||||
for (auto c : string)
|
||||
header.push_back(c);
|
||||
}
|
||||
|
||||
_bytecode.insert(_bytecode.begin(), header.begin(), header.end());
|
||||
}
|
||||
|
||||
Compiler::Compiler(Program* program) : _program(program) { }
|
||||
|
||||
void Compiler::Compile()
|
||||
{
|
||||
for (const auto& stmt : _program->statements)
|
||||
Compile(stmt.get());
|
||||
|
||||
Emit(Opcodes::EOC);
|
||||
|
||||
FinalCompile();
|
||||
}
|
||||
|
||||
const std::vector<double>& Compiler::GetBytecode() const
|
||||
{
|
||||
return _bytecode;
|
||||
}
|
||||
|
||||
void Compiler::Print()
|
||||
{
|
||||
std::stringstream disassembly;
|
||||
size_t ip = 0;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 1. HEADER VALIDATIE (0xFF 0x8C 0xFF)
|
||||
// -------------------------------------------------------------
|
||||
if (_bytecode.size() < 3 ||
|
||||
_bytecode[0] != 0xFF ||
|
||||
_bytecode[1] != 0x8C ||
|
||||
_bytecode[2] != 0xFF)
|
||||
{
|
||||
std::printf("Failed to load dlang header from bytecode!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
disassembly << "=================================================\n";
|
||||
disassembly << "=== HEADER ===\n";
|
||||
disassembly << "0000: FF 8C FF HEADER (Magic Bytes)\n";
|
||||
ip += 3;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 2. STRING TABLE UTLEZEN EN FORMATTEREN
|
||||
// -------------------------------------------------------------
|
||||
std::vector<std::string> localStringTable;
|
||||
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
size_t stringTableSize = static_cast<size_t>(_bytecode[ip++]);
|
||||
disassembly << "=== STRING TABLE (" << stringTableSize << " entries) ===\n";
|
||||
|
||||
for (size_t i = 0; i < stringTableSize; ++i)
|
||||
{
|
||||
if (ip >= _bytecode.size()) break;
|
||||
|
||||
size_t strStartIp = ip;
|
||||
size_t length = static_cast<size_t>(_bytecode[ip++]);
|
||||
|
||||
std::string str = "";
|
||||
std::stringstream strHexStream;
|
||||
|
||||
// Lengte-byte toevoegen
|
||||
strHexStream << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << static_cast<double>(length) << " ";
|
||||
|
||||
constexpr size_t MAX_HEX_BYTES_TO_SHOW = 12;
|
||||
|
||||
for (size_t j = 0; j < length && ip < _bytecode.size(); ++j)
|
||||
{
|
||||
double c = _bytecode[ip++];
|
||||
str += static_cast<char>(c);
|
||||
|
||||
if (j < MAX_HEX_BYTES_TO_SHOW)
|
||||
{
|
||||
strHexStream << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << c << " ";
|
||||
}
|
||||
else if (j == MAX_HEX_BYTES_TO_SHOW)
|
||||
{
|
||||
strHexStream << "... ";
|
||||
}
|
||||
}
|
||||
|
||||
localStringTable.push_back(str);
|
||||
|
||||
disassembly << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << strStartIp << ": ";
|
||||
|
||||
std::string hexStr = strHexStream.str();
|
||||
if (hexStr.length() > 40)
|
||||
{
|
||||
hexStr = hexStr.substr(0, 37) + "...";
|
||||
}
|
||||
|
||||
disassembly << std::left << std::setw(40) << std::setfill(' ') << hexStr;
|
||||
disassembly << "STRING[" << std::dec << i << "]: \"" << str << "\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
disassembly << "=== INSTRUCTIONS ===\n";
|
||||
|
||||
auto getString = [&localStringTable](double rawIndex) -> std::string {
|
||||
size_t strIdx = static_cast<size_t>(rawIndex);
|
||||
if (strIdx < localStringTable.size())
|
||||
return localStringTable[strIdx];
|
||||
return "<invalid string index " + std::to_string(strIdx) + ">";
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 3. OPCODES DISASSEMBLEN
|
||||
// -------------------------------------------------------------
|
||||
while (ip < _bytecode.size())
|
||||
{
|
||||
size_t startIp = ip;
|
||||
double rawOp = _bytecode[ip++];
|
||||
Opcodes op = static_cast<Opcodes>(rawOp);
|
||||
|
||||
std::vector<double> instructionBytes;
|
||||
instructionBytes.push_back(rawOp);
|
||||
|
||||
std::string instructionText = OpcodeToString(op);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
// -------------------------------------------------------------
|
||||
// GETALLEN / BOOLEANS
|
||||
// -------------------------------------------------------------
|
||||
case Opcodes::PUSH_INT:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double val = _bytecode[ip++];
|
||||
instructionBytes.push_back(val);
|
||||
instructionText += " " + std::to_string(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcodes::PUSH_BOOL:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double raw = _bytecode[ip++];
|
||||
instructionBytes.push_back(raw);
|
||||
bool val = (raw == 1.0);
|
||||
instructionText += (val ? " true" : " false");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// STRINGS & VARIABLE NAMES (STRING TABLE LOOKUPS)
|
||||
// -------------------------------------------------------------
|
||||
case Opcodes::PUSH_STRING:
|
||||
case Opcodes::PUSH_FUNC:
|
||||
case Opcodes::PUSH_VAR:
|
||||
case Opcodes::DEFINE_VAR:
|
||||
case Opcodes::STORE_VAR:
|
||||
case Opcodes::GET_FIELD:
|
||||
case Opcodes::SET_FIELD:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double index = _bytecode[ip++];
|
||||
instructionBytes.push_back(index);
|
||||
instructionText += " \"" + getString(index) + "\"";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// CALLS, TABELLEN EN LIJSTEN
|
||||
// -------------------------------------------------------------
|
||||
case Opcodes::CALL:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double argCount = _bytecode[ip++];
|
||||
instructionBytes.push_back(argCount);
|
||||
instructionText += " (" + std::to_string(argCount) + " args)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcodes::NEW_TABLE:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double fieldCount = _bytecode[ip++];
|
||||
instructionBytes.push_back(fieldCount);
|
||||
instructionText += " (" + std::to_string(fieldCount) + " fields)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcodes::NEW_LIST:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double elemCount = _bytecode[ip++];
|
||||
instructionBytes.push_back(elemCount);
|
||||
instructionText += " (" + std::to_string(elemCount) + " elements)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// JUMPS & CONTROL FLOW
|
||||
// -------------------------------------------------------------
|
||||
case Opcodes::JZ:
|
||||
case Opcodes::JUMP:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double target = _bytecode[ip++];
|
||||
instructionBytes.push_back(target);
|
||||
instructionText += " @" + std::to_string(static_cast<size_t>(target));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// FUNCTION DEFINITION
|
||||
// -------------------------------------------------------------
|
||||
case Opcodes::FUNC_DEF:
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double nameLen = _bytecode[ip++];
|
||||
instructionBytes.push_back(nameLen);
|
||||
|
||||
std::string funcName = "";
|
||||
for (double i = 0; i < nameLen && ip < _bytecode.size(); ++i)
|
||||
{
|
||||
double c = _bytecode[ip++];
|
||||
instructionBytes.push_back(c);
|
||||
funcName += static_cast<char>(c);
|
||||
}
|
||||
|
||||
instructionText += " " + funcName + "(";
|
||||
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double paramCount = _bytecode[ip++];
|
||||
instructionBytes.push_back(paramCount);
|
||||
|
||||
for (double p = 0; p < paramCount; ++p)
|
||||
{
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double paramLen = _bytecode[ip++];
|
||||
instructionBytes.push_back(paramLen);
|
||||
|
||||
std::string paramName = "";
|
||||
for (double i = 0; i < paramLen && ip < _bytecode.size(); ++i)
|
||||
{
|
||||
double c = _bytecode[ip++];
|
||||
instructionBytes.push_back(c);
|
||||
paramName += static_cast<char>(c);
|
||||
}
|
||||
|
||||
instructionText += paramName;
|
||||
if (p < paramCount - 1) instructionText += ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instructionText += ")";
|
||||
|
||||
if (ip < _bytecode.size())
|
||||
{
|
||||
double bodyAddr = _bytecode[ip++];
|
||||
instructionBytes.push_back(bodyAddr);
|
||||
instructionText += " @" + std::to_string(static_cast<size_t>(bodyAddr));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Opcodes::EOC:
|
||||
case Opcodes::ADD:
|
||||
case Opcodes::SUB:
|
||||
case Opcodes::MUL:
|
||||
case Opcodes::DIV:
|
||||
case Opcodes::GREATER_THAN:
|
||||
case Opcodes::LESS_THAN:
|
||||
case Opcodes::RET:
|
||||
case Opcodes::POP:
|
||||
case Opcodes::CMP:
|
||||
case Opcodes::GTE:
|
||||
case Opcodes::LTE:
|
||||
case Opcodes::AND:
|
||||
case Opcodes::OR:
|
||||
case Opcodes::NTE:
|
||||
case Opcodes::PUSH_LIST:
|
||||
case Opcodes::GET_INDEX:
|
||||
case Opcodes::SET_INDEX:
|
||||
case Opcodes::SIZE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// FORMATTING & OUTPUT FOR OPCODES
|
||||
// -------------------------------------------------------------
|
||||
disassembly << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << startIp << ": ";
|
||||
|
||||
std::stringstream hexStream;
|
||||
for (auto b : instructionBytes)
|
||||
hexStream << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << b << " ";
|
||||
|
||||
std::string hexStr = hexStream.str();
|
||||
if (hexStr.length() > 40)
|
||||
{
|
||||
hexStr = hexStr.substr(0, 37) + "...";
|
||||
}
|
||||
|
||||
disassembly << std::left << std::setw(40) << std::setfill(' ') << hexStr;
|
||||
disassembly << instructionText << "\n";
|
||||
}
|
||||
|
||||
std::printf("%s", disassembly.str().c_str());
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "../lexer.hpp"
|
||||
#include "../helpers.hpp"
|
||||
#include "../language.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Dlang::Language;
|
||||
|
||||
void Lexer::Parse(const std::string& code)
|
||||
{
|
||||
int line = 1, column = 0;
|
||||
std::string stringBuilder;
|
||||
bool insideString = false;
|
||||
bool insideComment = false;
|
||||
|
||||
auto flushBuilder = [&]()
|
||||
{
|
||||
if (!stringBuilder.empty())
|
||||
{
|
||||
if (!std::all_of(stringBuilder.begin(), stringBuilder.end(), ::isspace))
|
||||
{
|
||||
_tokens.push_back({ stringBuilder, line, column });
|
||||
}
|
||||
stringBuilder.clear();
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < code.size(); ++i)
|
||||
{
|
||||
char c = code.at(i);
|
||||
column++;
|
||||
|
||||
if (c == '\t') continue;
|
||||
|
||||
if (!insideString && (i + 1 < code.size()))
|
||||
{
|
||||
if ((c == '/' && code.at(i + 1) == '/') || (c == '-' && code.at(i + 1) == '-'))
|
||||
{
|
||||
i++;
|
||||
insideComment = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
if (insideComment)
|
||||
insideComment = false;
|
||||
|
||||
line++;
|
||||
column = 1;
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
if (insideComment)
|
||||
continue;
|
||||
|
||||
if (insideString)
|
||||
{
|
||||
stringBuilder += c;
|
||||
if (c == '\'' || c == '"')
|
||||
{
|
||||
_tokens.push_back({ stringBuilder, line, column });
|
||||
stringBuilder.clear();
|
||||
insideString = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\'' || c == '"')
|
||||
{
|
||||
flushBuilder();
|
||||
insideString = true;
|
||||
stringBuilder += c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ' ' || c == '\r')
|
||||
{
|
||||
flushBuilder();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsSingleCharToken(c))
|
||||
{
|
||||
flushBuilder();
|
||||
_tokens.push_back({ std::string(1, c), line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
stringBuilder += c;
|
||||
}
|
||||
|
||||
flushBuilder();
|
||||
}
|
||||
|
||||
void Lexer::ParseOrganizedTokens()
|
||||
{
|
||||
for (size_t i = 0; i < _tokens.size(); ++i)
|
||||
{
|
||||
static auto peek = [&](int off = 1)
|
||||
{
|
||||
if ((i + off) >= _tokens.size())
|
||||
return PreToken::CreateEmpty();
|
||||
|
||||
return _tokens.at(i + off);
|
||||
};
|
||||
|
||||
auto tokenData = _tokens.at(i);
|
||||
auto token = tokenData.value;
|
||||
auto line = tokenData.line;
|
||||
auto column = tokenData.column;
|
||||
|
||||
char c = token.at(0);
|
||||
|
||||
if (IsSingleCharToken(c))
|
||||
{
|
||||
if (peek().value[0] == op_equals)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case op_equals: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_eq), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_above: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_gte), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_below: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_lte), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_not: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_neq), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_add: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_add_assign), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_sub: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_sub_assign), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_multi: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_mul_assign), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
case op_div: {
|
||||
i++;
|
||||
_organizedTokens.push_back({ std::string(op_div_assign), TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_organizedTokens.push_back({ token, TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsStringNumber(token))
|
||||
{
|
||||
bool isNeg = false;
|
||||
|
||||
if (!_organizedTokens.empty() && _organizedTokens.back().value[0] == op_sub)
|
||||
{
|
||||
if (_organizedTokens.size() >= 2)
|
||||
{
|
||||
auto& prevToken = _organizedTokens[_organizedTokens.size() - 2];
|
||||
|
||||
bool isUnaryContext = (prevToken.type == TokenType::SYNTAX &&
|
||||
(prevToken.value[0] == op_equals) ||
|
||||
prevToken.value[0] == op_exprstart ||
|
||||
prevToken.value[0] == op_separator ||
|
||||
prevToken.value[0] == op_colon ||
|
||||
prevToken.value[0] == op_arraystart ||
|
||||
prevToken.value[0] == op_arraystart) ||
|
||||
(prevToken.type == TokenType::KEYWORD);
|
||||
|
||||
if (isUnaryContext)
|
||||
{
|
||||
_organizedTokens.pop_back();
|
||||
isNeg = true;
|
||||
}
|
||||
}
|
||||
else if (_organizedTokens.size() == 1)
|
||||
{
|
||||
_organizedTokens.pop_back();
|
||||
isNeg = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (peek().value[0] == op_dot && IsStringNumber(peek(2).value))
|
||||
{
|
||||
auto dotToken = _tokens.at(++i);
|
||||
auto secondNum = _tokens.at(++i);
|
||||
|
||||
auto number = (isNeg ? "-" : "") + token + dotToken.value + secondNum.value;
|
||||
_organizedTokens.push_back({ number, TokenType::NUMBER, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
_organizedTokens.push_back({ (isNeg ? "-" : "") + token, TokenType::NUMBER, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsBoolean(token))
|
||||
{
|
||||
_organizedTokens.push_back({ token, TokenType::BOOLEAN, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsKeyword(token))
|
||||
{
|
||||
_organizedTokens.push_back({ token, TokenType::KEYWORD, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == op_and || token == op_or)
|
||||
{
|
||||
_organizedTokens.push_back({ token, TokenType::SYNTAX, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((token.front() == '\'' || token.front() == '"') && (token.back() == '\'' || token.back() == '"'))
|
||||
{
|
||||
_organizedTokens.push_back({ token.substr(1, token.size() - 2), TokenType::STRING, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsIdentifier(token))
|
||||
{
|
||||
_organizedTokens.push_back({ token, TokenType::IDENTIFIER, line, column });
|
||||
continue;
|
||||
}
|
||||
|
||||
_organizedTokens.push_back({ token, TokenType::UNIDENTIFIED, line, column });
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PreToken> Lexer::GetTokens() const
|
||||
{
|
||||
return _tokens;
|
||||
}
|
||||
|
||||
std::vector<Token> Lexer::GetOrganizedTokens() const
|
||||
{
|
||||
return _organizedTokens;
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
#include "../parser.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
std::string LocationText(const Token& t)
|
||||
{
|
||||
return "at line " + std::to_string(t.line) + " column " + std::to_string(t.column);
|
||||
}
|
||||
|
||||
Token& Parser::Current()
|
||||
{
|
||||
if (_pos < _tokens.size())
|
||||
return _tokens[_pos];
|
||||
|
||||
throw std::runtime_error("Unidentified token!");
|
||||
}
|
||||
|
||||
Token& Parser::Consume()
|
||||
{
|
||||
if (_pos < _tokens.size())
|
||||
return _tokens[_pos++];
|
||||
|
||||
throw std::runtime_error("Unidentified token!");
|
||||
}
|
||||
|
||||
Token& Parser::Peek(size_t offset)
|
||||
{
|
||||
if (_pos + offset < _tokens.size())
|
||||
return _tokens[_pos + offset];
|
||||
|
||||
throw std::runtime_error("Unidentified token!");
|
||||
}
|
||||
|
||||
bool Parser::Match(TokenType type, const std::string& value)
|
||||
{
|
||||
const auto& current = Current();
|
||||
|
||||
if (current.type != type || current.value != value)
|
||||
return false;
|
||||
|
||||
Consume();
|
||||
return true;
|
||||
}
|
||||
|
||||
Token& Parser::ShouldBe(TokenType type, const std::string& value)
|
||||
{
|
||||
const auto& current = Current();
|
||||
|
||||
if (current.type != type)
|
||||
throw std::runtime_error("Unexpected type '" + std::string(TokenTypeToString(current.type)) + "' where '" + std::string(TokenTypeToString(type)) + "' should be " + LocationText(current));
|
||||
|
||||
if(!value.empty() && value != current.value)
|
||||
throw std::runtime_error("Unexpected value '" + current.value + "' where '" + value + "' should be " + LocationText(current));
|
||||
|
||||
return Consume();
|
||||
}
|
||||
|
||||
bool Parser::Match(TokenType type, char value)
|
||||
{
|
||||
return Match(type, std::string(1, value));
|
||||
}
|
||||
|
||||
Token& Parser::ShouldBe(TokenType type, char value)
|
||||
{
|
||||
return ShouldBe(type, std::string(1, value));
|
||||
}
|
||||
|
||||
Parser::Parser(const std::vector<Token>& tokens) : _tokens(tokens) { }
|
||||
|
||||
Program Parser::ParseProgram()
|
||||
{
|
||||
Program program;
|
||||
|
||||
while (_pos < _tokens.size())
|
||||
program.statements.push_back(ParseStatement());
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
using namespace Dlang::Language;
|
||||
#pragma region STATEMENTS
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseStatement()
|
||||
{
|
||||
if (Match(TokenType::KEYWORD, kw_var))
|
||||
return ParseVariableDeclaration();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_while))
|
||||
return ParseWhileDeclaration();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_for))
|
||||
return ParseForDeclaration();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_foreach))
|
||||
return ParseForeachDeclaration();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_if))
|
||||
return ParseIfDeclaration();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_func))
|
||||
return ParseFunctionDeclaration();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_return))
|
||||
return ParseReturnStatement();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_include))
|
||||
return ParseIncludeStatement();
|
||||
|
||||
auto expr = ParseExpression();
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_equals))
|
||||
{
|
||||
auto op = Peek(-1).value;
|
||||
if (!dynamic_cast<IdentifierExpression*>(expr.get()) &&
|
||||
!dynamic_cast<MemberAccessExpression*>(expr.get()) &&
|
||||
!dynamic_cast<IndexAccessExpression*>(expr.get()))
|
||||
{
|
||||
throw std::runtime_error("Invalid left-hand side in assignment");
|
||||
}
|
||||
|
||||
auto value = ParseExpression();
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
|
||||
auto assignment = std::make_unique<Assignment>();
|
||||
assignment->lhs = std::move(expr);
|
||||
assignment->value = std::move(value);
|
||||
assignment->assignmentOperator = op;
|
||||
return assignment;
|
||||
}
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_add_assign) || Match(TokenType::SYNTAX, op_sub_assign) || Match(TokenType::SYNTAX, op_mul_assign) || Match(TokenType::SYNTAX, op_div_assign))
|
||||
{
|
||||
std::string op = Peek(-1).value;
|
||||
|
||||
if (!dynamic_cast<IdentifierExpression*>(expr.get()) &&
|
||||
!dynamic_cast<MemberAccessExpression*>(expr.get()) &&
|
||||
!dynamic_cast<IndexAccessExpression*>(expr.get()))
|
||||
{
|
||||
throw std::runtime_error("Invalid left-hand side in assignment");
|
||||
}
|
||||
|
||||
auto value = ParseExpression();
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
|
||||
auto assignment = std::make_unique<Assignment>();
|
||||
assignment->lhs = std::move(expr);
|
||||
assignment->value = std::move(value);
|
||||
assignment->assignmentOperator = op;
|
||||
return assignment;
|
||||
}
|
||||
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
|
||||
auto stmt = std::make_unique<ExpressionStatement>();
|
||||
stmt->expression = std::move(expr);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseVariableDeclaration()
|
||||
{
|
||||
auto name = ShouldBe(TokenType::IDENTIFIER);
|
||||
ShouldBe(TokenType::SYNTAX, op_equals);
|
||||
auto initializer = ParseExpression();
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
|
||||
auto declaration = std::make_unique<VariableDeclaration>();
|
||||
declaration->name = name.value;
|
||||
declaration->initializer = std::move(initializer);
|
||||
|
||||
return declaration;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseAssignment()
|
||||
{
|
||||
auto lhs = ParseExpression();
|
||||
|
||||
if (!dynamic_cast<IdentifierExpression*>(lhs.get()) &&
|
||||
!dynamic_cast<MemberAccessExpression*>(lhs.get()))
|
||||
{
|
||||
throw std::runtime_error("Invalid left-hand side in assignment");
|
||||
}
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_equals);
|
||||
auto value = ParseExpression();
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
|
||||
auto assignment = std::make_unique<Assignment>();
|
||||
assignment->lhs = std::move(lhs);
|
||||
assignment->value = std::move(value);
|
||||
|
||||
return assignment;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseWhileDeclaration()
|
||||
{
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
|
||||
auto stmt = std::make_unique<WhileStatement>();
|
||||
stmt->condition = ParseExpression();
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
ShouldBe(TokenType::KEYWORD, kw_do);
|
||||
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->body.push_back(ParseStatement());
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseForDeclaration()
|
||||
{
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
auto varName = ShouldBe(TokenType::IDENTIFIER);
|
||||
ShouldBe(TokenType::SYNTAX, op_separator);
|
||||
auto from = ShouldBe(TokenType::NUMBER);
|
||||
ShouldBe(TokenType::SYNTAX, op_separator);
|
||||
auto to = ShouldBe(TokenType::NUMBER);
|
||||
|
||||
auto stmt = std::make_unique<ForStatement>();
|
||||
stmt->var = varName.value;
|
||||
stmt->from = std::stod(from.value);
|
||||
stmt->to = std::stod(to.value);
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_separator))
|
||||
{
|
||||
auto step = ShouldBe(TokenType::NUMBER);
|
||||
stmt->step = std::stod(step.value);
|
||||
}
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
ShouldBe(TokenType::KEYWORD, kw_do);
|
||||
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->body.push_back(ParseStatement());
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseForeachDeclaration()
|
||||
{
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
auto varName = ShouldBe(TokenType::IDENTIFIER);
|
||||
if (Match(TokenType::KEYWORD, kw_in))
|
||||
{
|
||||
auto list = ShouldBe(TokenType::IDENTIFIER);
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
ShouldBe(TokenType::KEYWORD, kw_do);
|
||||
|
||||
auto stmt = std::make_unique<ForeachStatement>();
|
||||
stmt->var = varName.value;
|
||||
stmt->list = list.value;
|
||||
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->body.push_back(ParseStatement());
|
||||
|
||||
return stmt;
|
||||
}
|
||||
else {
|
||||
ShouldBe(TokenType::SYNTAX, op_separator);
|
||||
auto value = ShouldBe(TokenType::IDENTIFIER);
|
||||
ShouldBe(TokenType::KEYWORD, kw_in);
|
||||
auto table = ShouldBe(TokenType::IDENTIFIER);
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
ShouldBe(TokenType::KEYWORD, kw_do);
|
||||
|
||||
auto stmt = std::make_unique<ForeachTableStatement>();
|
||||
stmt->varKey = varName.value;
|
||||
stmt->varValue = value.value;
|
||||
stmt->table = table.value;
|
||||
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->body.push_back(ParseStatement());
|
||||
|
||||
return stmt;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseIfDeclaration()
|
||||
{
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
auto expr = ParseExpression();
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
ShouldBe(TokenType::KEYWORD, kw_then);
|
||||
|
||||
auto stmt = std::make_unique<IfStatement>();
|
||||
stmt->expression = std::move(expr);
|
||||
|
||||
Token t;
|
||||
while (!Match(TokenType::KEYWORD, kw_else) && !Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->body.push_back(ParseStatement());
|
||||
|
||||
if (Peek(-1).type == TokenType::KEYWORD && Peek(-1).value == kw_else)
|
||||
{
|
||||
if (Match(TokenType::KEYWORD, kw_if))
|
||||
stmt->elseBody.push_back(ParseIfDeclaration());
|
||||
else
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->elseBody.push_back(ParseStatement());
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseFunctionDeclaration()
|
||||
{
|
||||
auto funcName = ShouldBe(TokenType::IDENTIFIER);
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
std::vector<std::string> params;
|
||||
if (!Match(TokenType::SYNTAX, op_exprend))
|
||||
{
|
||||
do
|
||||
{
|
||||
Token param = ShouldBe(TokenType::IDENTIFIER);
|
||||
params.push_back(param.value);
|
||||
} while (Match(TokenType::SYNTAX, op_separator));
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
}
|
||||
|
||||
auto stmt = std::make_unique<FunctionStatement>();
|
||||
stmt->name = funcName.value;
|
||||
stmt->parameters = params;
|
||||
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
stmt->body.push_back(ParseStatement());
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseReturnStatement()
|
||||
{
|
||||
auto stmt = std::make_unique<ReturnStatement>();
|
||||
|
||||
if (!Match(TokenType::SYNTAX, op_endl))
|
||||
{
|
||||
stmt->expression = ParseExpression();
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Statement> Parser::ParseIncludeStatement()
|
||||
{
|
||||
auto stmt = std::make_unique<IncludeStatement>();
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
auto path = ShouldBe(TokenType::STRING).value;
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
ShouldBe(TokenType::SYNTAX, op_endl);
|
||||
stmt->path = path;
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region EXPRESSIONS
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseFunctionCall(std::unique_ptr<Expression> callee)
|
||||
{
|
||||
auto stmt = std::make_unique<CallExpression>();
|
||||
stmt->callee = std::move(callee);
|
||||
|
||||
if (!Match(TokenType::SYNTAX, op_exprend))
|
||||
{
|
||||
do {
|
||||
stmt->expressions.push_back(ParseExpression());
|
||||
} while (Match(TokenType::SYNTAX, op_separator));
|
||||
|
||||
if (!Match(TokenType::SYNTAX, op_exprend))
|
||||
throw std::runtime_error("Expected ')' after arguments " + LocationText(Current()));
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseExpression()
|
||||
{
|
||||
return ParseLogicalAndOr();
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParsePrimary()
|
||||
{
|
||||
if (Match(TokenType::SYNTAX, op_exprstart))
|
||||
{
|
||||
auto expression = ParseExpression();
|
||||
|
||||
if (!Match(TokenType::SYNTAX, op_exprend))
|
||||
throw std::runtime_error("Expected ')' " + LocationText(Current()));
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_arraystart))
|
||||
return ParseTableLiteral();
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_liststart))
|
||||
return ParseListLiteral();
|
||||
|
||||
if (Match(TokenType::KEYWORD, kw_func))
|
||||
return ParseFunctionExpression();
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_size))
|
||||
return ParseSizeExpression();
|
||||
|
||||
const auto& current = Current();
|
||||
|
||||
if (current.type == TokenType::NUMBER)
|
||||
{
|
||||
auto token = ShouldBe(TokenType::NUMBER);
|
||||
|
||||
auto expression = std::make_unique<NumberExpression>();
|
||||
expression->value = std::stod(token.value);
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
if (current.type == TokenType::BOOLEAN)
|
||||
{
|
||||
auto token = ShouldBe(TokenType::BOOLEAN);
|
||||
|
||||
auto expression = std::make_unique<BooleanExpression>();
|
||||
expression->value = (token.value == kw_true ? true : false);
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
if (current.type == TokenType::STRING)
|
||||
{
|
||||
auto token = ShouldBe(TokenType::STRING);
|
||||
|
||||
auto expression = std::make_unique<StringExpression>();
|
||||
expression->value = token.value;
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
if (current.type == TokenType::IDENTIFIER)
|
||||
{
|
||||
auto token = ShouldBe(TokenType::IDENTIFIER);
|
||||
|
||||
auto idExpr = std::make_unique<IdentifierExpression>();
|
||||
idExpr->name = token.value;
|
||||
|
||||
std::unique_ptr<Expression> left;
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_exprstart))
|
||||
left = ParseFunctionCall(std::move(idExpr));
|
||||
else
|
||||
left = std::move(idExpr);
|
||||
|
||||
while (Match(TokenType::SYNTAX, op_dot) || Match(TokenType::SYNTAX, op_liststart))
|
||||
{
|
||||
if (Peek(-1).value[0] == op_dot)
|
||||
{
|
||||
auto memberExpr = std::make_unique<MemberAccessExpression>();
|
||||
memberExpr->object = std::move(left);
|
||||
memberExpr->memberName = ShouldBe(TokenType::IDENTIFIER).value;
|
||||
left = std::move(memberExpr);
|
||||
|
||||
if (Match(TokenType::SYNTAX, op_exprstart))
|
||||
{
|
||||
left = ParseFunctionCall(std::move(left));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto indexExpr = ParseExpression();
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_listend);
|
||||
|
||||
auto indexAccess = std::make_unique<IndexAccessExpression>();
|
||||
indexAccess->object = std::move(left);
|
||||
indexAccess->memberIndex = std::move(indexExpr);
|
||||
|
||||
left = std::move(indexAccess);
|
||||
}
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Expected expression " + LocationText(current));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseMultiplicative()
|
||||
{
|
||||
auto left = ParsePrimary();
|
||||
|
||||
while (Current().type == TokenType::SYNTAX &&
|
||||
(Current().value[0] == op_multi || Current().value[0] == op_div))
|
||||
{
|
||||
auto op = Consume();
|
||||
auto right = ParsePrimary();
|
||||
|
||||
auto expression = std::make_unique<BinaryExpression>();
|
||||
expression->left = std::move(left);
|
||||
expression->op = op.value;
|
||||
expression->right = std::move(right);
|
||||
|
||||
left = std::move(expression);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseAdditive()
|
||||
{
|
||||
auto left = ParseMultiplicative();
|
||||
|
||||
while (Current().type == TokenType::SYNTAX &&
|
||||
(Current().value[0] == op_add || Current().value[0] == op_sub))
|
||||
{
|
||||
auto op = Consume();
|
||||
|
||||
auto right = ParseMultiplicative();
|
||||
|
||||
auto expression = std::make_unique<BinaryExpression>();
|
||||
|
||||
expression->left = std::move(left);
|
||||
expression->op = op.value;
|
||||
expression->right = std::move(right);
|
||||
|
||||
left = std::move(expression);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseComparison()
|
||||
{
|
||||
auto left = ParseAdditive();
|
||||
|
||||
while (
|
||||
Match(TokenType::SYNTAX, op_above) ||
|
||||
Match(TokenType::SYNTAX, op_below) ||
|
||||
Match(TokenType::SYNTAX, op_eq) ||
|
||||
Match(TokenType::SYNTAX, op_gte) ||
|
||||
Match(TokenType::SYNTAX, op_lte) ||
|
||||
Match(TokenType::SYNTAX, op_neq)
|
||||
) {
|
||||
auto op = Peek(-1);
|
||||
auto right = ParseAdditive();
|
||||
|
||||
auto expr = std::make_unique<BinaryExpression>();
|
||||
expr->left = std::move(left);
|
||||
expr->op = op.value;
|
||||
expr->right = std::move(right);
|
||||
|
||||
left = std::move(expr);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseTableLiteral()
|
||||
{
|
||||
auto expr = std::make_unique<TableLiteralExpression>();
|
||||
|
||||
while (!Match(TokenType::SYNTAX, op_arrayend))
|
||||
{
|
||||
std::string key = ShouldBe(TokenType::IDENTIFIER).value;
|
||||
ShouldBe(TokenType::SYNTAX, op_colon);
|
||||
|
||||
auto valueExpr = ParseExpression();
|
||||
expr->fields.push_back({ key, std::move(valueExpr) });
|
||||
|
||||
Match(TokenType::SYNTAX, op_separator);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseListLiteral()
|
||||
{
|
||||
auto expr = std::make_unique<ListLiteralExpression>();
|
||||
|
||||
while (!Match(TokenType::SYNTAX, op_listend))
|
||||
{
|
||||
auto valueExpr = ParseExpression();
|
||||
expr->fields.push_back(std::move(valueExpr));
|
||||
|
||||
Match(TokenType::SYNTAX, op_separator);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseLogicalAndOr()
|
||||
{
|
||||
auto left = ParseComparison();
|
||||
|
||||
while (Match(TokenType::SYNTAX, op_and) || Match(TokenType::SYNTAX, op_or))
|
||||
{
|
||||
auto op = Peek(-1);
|
||||
auto right = ParseComparison();
|
||||
|
||||
auto expr = std::make_unique<BinaryExpression>();
|
||||
expr->left = std::move(left);
|
||||
expr->op = op.value;
|
||||
expr->right = std::move(right);
|
||||
|
||||
left = std::move(expr);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseFunctionExpression()
|
||||
{
|
||||
ShouldBe(TokenType::SYNTAX, op_exprstart);
|
||||
std::vector<std::string> params;
|
||||
if (!Match(TokenType::SYNTAX, op_exprend))
|
||||
{
|
||||
do
|
||||
{
|
||||
Token param = ShouldBe(TokenType::IDENTIFIER);
|
||||
params.push_back(param.value);
|
||||
} while (Match(TokenType::SYNTAX, op_separator));
|
||||
|
||||
ShouldBe(TokenType::SYNTAX, op_exprend);
|
||||
}
|
||||
|
||||
auto expr = std::make_unique<FunctionExpression>();
|
||||
expr->parameters = params;
|
||||
|
||||
while (!Match(TokenType::KEYWORD, kw_end))
|
||||
expr->body.push_back(ParseStatement());
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> Parser::ParseSizeExpression()
|
||||
{
|
||||
auto target = ShouldBe(TokenType::IDENTIFIER).value;
|
||||
|
||||
auto stmt = std::make_unique<SizeExpression>();
|
||||
stmt->identifier = target;
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -0,0 +1,882 @@
|
||||
#include "../vm.hpp"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include "../helpers.hpp"
|
||||
|
||||
std::string DlangVM::GetString(const ByteCodeArray& bytecode, size_t& ip)
|
||||
{
|
||||
auto idx = bytecode[ip++];
|
||||
return strings[idx];
|
||||
}
|
||||
|
||||
std::string DlangVM::GetRealString(const ByteCodeArray& bytecode, size_t& ip)
|
||||
{
|
||||
double length = bytecode[ip++];
|
||||
std::string str = "";
|
||||
for (double i = 0; i < length; ++i)
|
||||
str += static_cast<char>(bytecode[ip++]);
|
||||
return str;
|
||||
}
|
||||
|
||||
void DlangVM::Execute(ByteCodeArray bytecode)
|
||||
{
|
||||
size_t ip = 0;
|
||||
auto bytecodeSize = bytecode.size();
|
||||
|
||||
#if DEBUG_MODE == 1
|
||||
printf("==================== VM ====================\n");
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
#endif
|
||||
|
||||
while (ip < bytecodeSize)
|
||||
{
|
||||
auto op = static_cast<Opcodes>(bytecode[ip++]);
|
||||
switch (op)
|
||||
{
|
||||
#pragma region VM CONTROL OPCODES
|
||||
case Opcodes::EOC: break; break;
|
||||
case Opcodes::POP: Pop(); break;
|
||||
|
||||
case Opcodes::PUSH_INT: {
|
||||
auto val = bytecode[ip++];
|
||||
PushNumber(val);
|
||||
} break;
|
||||
|
||||
case Opcodes::PUSH_BOOL: {
|
||||
auto val = bytecode[ip++];
|
||||
PushBoolean(val == 1);
|
||||
} break;
|
||||
|
||||
case Opcodes::PUSH_STRING: {
|
||||
auto val = GetString(bytecode, ip);
|
||||
PushString(val);
|
||||
} break;
|
||||
|
||||
case Opcodes::PUSH_VAR: {
|
||||
auto val = GetString(bytecode, ip);
|
||||
PushVar(val);
|
||||
} break;
|
||||
|
||||
case Opcodes::PUSH_FUNC: {
|
||||
auto func = GetString(bytecode, ip);
|
||||
stack.push(
|
||||
globals.Get(func)
|
||||
);
|
||||
} break;
|
||||
|
||||
case Opcodes::DEFINE_VAR:
|
||||
case Opcodes::STORE_VAR: {
|
||||
auto name = GetString(bytecode, ip);
|
||||
auto val = Pop();
|
||||
|
||||
SetVariable(name, val);
|
||||
} break;
|
||||
|
||||
case Opcodes::NEW_TABLE: PushTable(std::make_shared<DlangTable>()); break;
|
||||
|
||||
case Opcodes::SET_FIELD: {
|
||||
std::string key = GetString(bytecode, ip);
|
||||
SetField(key);
|
||||
} break;
|
||||
|
||||
case Opcodes::GET_FIELD: {
|
||||
std::string key = GetString(bytecode, ip);
|
||||
stack.push(
|
||||
GetField(key)
|
||||
);
|
||||
} break;
|
||||
|
||||
case Opcodes::KEYS: stack.push(GetTableKeys()); break;
|
||||
|
||||
case Opcodes::NEW_LIST: PushList(std::make_shared<DlangList>()); break;
|
||||
case Opcodes::PUSH_LIST: PushListIndex(); break;
|
||||
case Opcodes::SET_INDEX: SetListIndex(); break;
|
||||
case Opcodes::GET_INDEX: stack.push(GetListIndex()); break;
|
||||
|
||||
case Opcodes::SIZE: PushNumber(GetSize()); break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region VM MATH OPCODES
|
||||
case Opcodes::ADD: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if ((a.type != StackValueType::Int || b.type != StackValueType::Int) &&
|
||||
(a.type != StackValueType::String && b.type != StackValueType::String))
|
||||
throw std::runtime_error("ADD Operation error, not all variables are integer or strings!");
|
||||
|
||||
bool fullInt = (a.type == StackValueType::Int && b.type == StackValueType::Int);
|
||||
|
||||
if (fullInt)
|
||||
stack.push(StackValue(StackValueType::Int,
|
||||
std::get<double>(a.data) +
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
else
|
||||
{
|
||||
std::string content;
|
||||
if (a.type == StackValueType::Int)
|
||||
content += DoubleToString(std::get<double>(a.data));
|
||||
else
|
||||
content += std::get<std::string>(a.data);
|
||||
|
||||
if (b.type == StackValueType::Int)
|
||||
content += DoubleToString(std::get<double>(b.data));
|
||||
else
|
||||
content += std::get<std::string>(b.data);
|
||||
|
||||
stack.push(StackValue(StackValueType::String, content));
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case Opcodes::SUB: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if (a.type != StackValueType::Int || b.type != StackValueType::Int)
|
||||
throw std::runtime_error("SUB Operation error, not all variables are integer!");
|
||||
|
||||
stack.push(StackValue(StackValueType::Int,
|
||||
std::get<double>(a.data) -
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
} break;
|
||||
|
||||
case Opcodes::MUL: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if (a.type != StackValueType::Int || b.type != StackValueType::Int)
|
||||
throw std::runtime_error("ADD Operation error, not all variables are integer!");
|
||||
|
||||
stack.push(StackValue(StackValueType::Int,
|
||||
std::get<double>(a.data) *
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
} break;
|
||||
|
||||
case Opcodes::DIV: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if (a.type != StackValueType::Int || b.type != StackValueType::Int)
|
||||
throw std::runtime_error("ADD Operation error, not all variables are integer!");
|
||||
|
||||
stack.push(StackValue(StackValueType::Int,
|
||||
std::get<double>(a.data) /
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
} break;
|
||||
|
||||
case Opcodes::GREATER_THAN: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if (a.type != StackValueType::Int || b.type != StackValueType::Int)
|
||||
throw std::runtime_error("GREATHER_THAN Operation error, not all variables are integer!");
|
||||
|
||||
stack.push(StackValue(StackValueType::Bool,
|
||||
std::get<double>(a.data) >
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
} break;
|
||||
|
||||
case Opcodes::LESS_THAN: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if (a.type != StackValueType::Int || b.type != StackValueType::Int)
|
||||
throw std::runtime_error("LESS_THAN Operation error, not all variables are integer!");
|
||||
|
||||
stack.push(StackValue(StackValueType::Bool,
|
||||
std::get<double>(a.data) <
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
} break;
|
||||
|
||||
case Opcodes::CMP: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
if (a.type != StackValueType::Int || b.type != StackValueType::Int)
|
||||
throw std::runtime_error("LESS_THAN Operation error, not all variables are integer!");
|
||||
|
||||
stack.push(StackValue(StackValueType::Bool,
|
||||
std::get<double>(a.data) ==
|
||||
std::get<double>(b.data)
|
||||
));
|
||||
} break;
|
||||
|
||||
case Opcodes::GTE: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
StackValue result;
|
||||
result.type = StackValueType::Bool;
|
||||
|
||||
if (a.type == StackValueType::Int && b.type == StackValueType::Int) {
|
||||
result.data = (std::get<double>(a.data) >= std::get<double>(b.data));
|
||||
}
|
||||
|
||||
stack.push(result);
|
||||
} break;
|
||||
|
||||
case Opcodes::LTE: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
StackValue result;
|
||||
result.type = StackValueType::Bool;
|
||||
|
||||
if (a.type == StackValueType::Int && b.type == StackValueType::Int) {
|
||||
result.data = (std::get<double>(a.data) <= std::get<double>(b.data));
|
||||
}
|
||||
|
||||
stack.push(result);
|
||||
} break;
|
||||
|
||||
case Opcodes::AND: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
StackValue result;
|
||||
result.type = StackValueType::Bool;
|
||||
|
||||
if (a.type == StackValueType::Bool && b.type == StackValueType::Bool) {
|
||||
bool lVal = std::get<bool>(a.data);
|
||||
bool rVal = std::get<bool>(b.data);
|
||||
result.data = (lVal && rVal);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Logical AND (&&) requires boolean operands!");
|
||||
}
|
||||
|
||||
stack.push(result);
|
||||
} break;
|
||||
|
||||
case Opcodes::OR: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
StackValue result;
|
||||
result.type = StackValueType::Bool;
|
||||
|
||||
if (a.type == StackValueType::Bool && b.type == StackValueType::Bool) {
|
||||
bool lVal = std::get<bool>(a.data);
|
||||
bool rVal = std::get<bool>(b.data);
|
||||
result.data = (lVal || rVal);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Logical OR (||) requires boolean operands!");
|
||||
}
|
||||
|
||||
stack.push(result);
|
||||
} break;
|
||||
|
||||
case Opcodes::NTE: {
|
||||
auto b = stack.pop();
|
||||
auto a = stack.pop();
|
||||
|
||||
StackValue result;
|
||||
result.type = StackValueType::Bool;
|
||||
|
||||
if (a.type == StackValueType::Int && b.type == StackValueType::Int) {
|
||||
result.data = (std::get<double>(a.data) != std::get<double>(b.data));
|
||||
}
|
||||
|
||||
stack.push(result);
|
||||
} break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region LOGIC OPCODES
|
||||
case Opcodes::CALL: {
|
||||
auto argCount = bytecode[ip++];
|
||||
|
||||
auto func = stack.pop();
|
||||
if (func.type != StackValueType::Function)
|
||||
throw std::runtime_error("Ohno, missing a function on the stack for call!");
|
||||
|
||||
auto function = std::get<std::shared_ptr<DlangFunction>>(func.data);
|
||||
|
||||
if (function->isNative)
|
||||
function->nativeFunction(this);
|
||||
else {
|
||||
CallFrame newFrame;
|
||||
newFrame.returnIp = ip;
|
||||
newFrame.previousBytecode = bytecode;
|
||||
|
||||
for (double i = (argCount - 1); i >= 0; --i)
|
||||
newFrame.locals.Set(function->parameters[i], stack.pop());
|
||||
|
||||
_callStack.push(newFrame);
|
||||
|
||||
bytecode = function->bytecodeChunk;
|
||||
ip = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Opcodes::RET: {
|
||||
if (_callStack.top() <= 0)
|
||||
return; // End because return was called in global file
|
||||
|
||||
StackValue returnValue;
|
||||
bool assigned = false;
|
||||
if (stack.top() > 0)
|
||||
{
|
||||
returnValue = stack.pop();
|
||||
assigned = true;
|
||||
}
|
||||
|
||||
CallFrame oldFrame = _callStack.pop();
|
||||
|
||||
ip = oldFrame.returnIp;
|
||||
bytecode = oldFrame.previousBytecode;
|
||||
|
||||
if (assigned)
|
||||
stack.push(returnValue);
|
||||
|
||||
} break;
|
||||
|
||||
case Opcodes::FUNC_DEF: {
|
||||
auto name = GetString(bytecode, ip);
|
||||
auto paramCount = bytecode[ip++];
|
||||
|
||||
std::vector<std::string> parameters;
|
||||
for (double i = 0; i < paramCount; ++i)
|
||||
parameters.push_back(GetString(bytecode, ip));
|
||||
|
||||
double bodyStart = bytecode[ip++];
|
||||
double bodyEnd = bytecode[ip++];
|
||||
|
||||
std::vector<double> funcChunk(
|
||||
bytecode.begin() + static_cast<size_t>(bodyStart),
|
||||
bytecode.begin() + static_cast<size_t>(bodyEnd)
|
||||
);
|
||||
|
||||
auto funcObj = std::make_shared<DlangFunction>();
|
||||
funcObj->isNative = false;
|
||||
funcObj->parameters = parameters;
|
||||
funcObj->bytecodeChunk = std::move(funcChunk);
|
||||
|
||||
StackValue v;
|
||||
v.type = StackValueType::Function;
|
||||
v.data = funcObj;
|
||||
|
||||
if (!name.empty())
|
||||
globals.Set(name, v);
|
||||
else
|
||||
stack.push(v);
|
||||
} break;
|
||||
|
||||
case Opcodes::JZ: {
|
||||
double targetAddress = bytecode[ip++];
|
||||
auto val = stack.pop();
|
||||
bool isFalse = false;
|
||||
|
||||
std::visit([&isFalse](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
isFalse = !arg;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double>) {
|
||||
isFalse = (arg == 0);
|
||||
}
|
||||
else throw std::runtime_error("Invalid type for is zero check!");
|
||||
}, val.data);
|
||||
|
||||
if (isFalse)
|
||||
ip = targetAddress;
|
||||
} break;
|
||||
|
||||
case Opcodes::JUMP: {
|
||||
double targetAddress = bytecode[ip++];
|
||||
ip = targetAddress;
|
||||
} break;
|
||||
#pragma endregion
|
||||
|
||||
default:
|
||||
printf("Invalid opcode 0x%llX!\n", op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_MODE == 1
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
auto durationMicroSeconds = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
||||
|
||||
printf("=================== END ====================\n");
|
||||
printf("VM took %lld ms (%lld microseconds)\n", duration, durationMicroSeconds);
|
||||
#endif
|
||||
}
|
||||
|
||||
StackValue DlangVM::Pop()
|
||||
{
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
void DlangVM::Pop(double amount)
|
||||
{
|
||||
amount = static_cast<int>(amount);
|
||||
stack.pop(amount);
|
||||
}
|
||||
|
||||
void DlangVM::PushNumber(double number)
|
||||
{
|
||||
stack.push(
|
||||
StackValue(
|
||||
StackValueType::Int, number
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DlangVM::PushBoolean(bool value)
|
||||
{
|
||||
stack.push(
|
||||
StackValue(
|
||||
StackValueType::Bool, value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DlangVM::PushString(const std::string& str)
|
||||
{
|
||||
stack.push(
|
||||
StackValue(
|
||||
StackValueType::String, str
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DlangVM::PushVar(const std::string& var)
|
||||
{
|
||||
stack.push(
|
||||
GetVariable(var)
|
||||
);
|
||||
}
|
||||
|
||||
void DlangVM::PushCFunction(CFunction func)
|
||||
{
|
||||
StackValue s;
|
||||
s.type = StackValueType::Function;
|
||||
s.data = std::move(DlangFunction::CreateNative(func));
|
||||
stack.push(s);
|
||||
}
|
||||
|
||||
bool DlangVM::CallFunc(double argCount)
|
||||
{
|
||||
auto func = stack.pop();
|
||||
if (func.type != StackValueType::Function)
|
||||
return false;
|
||||
|
||||
auto function = std::get<std::shared_ptr<DlangFunction>>(func.data);
|
||||
if (function->isNative)
|
||||
function->nativeFunction(this);
|
||||
else {
|
||||
|
||||
CallFrame newFrame;
|
||||
|
||||
std::vector<StackValue> providedArgs(argCount);
|
||||
for (int i = argCount - 1; i >= 0; --i) {
|
||||
providedArgs[i] = stack.pop();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < function->parameters.size(); ++i) {
|
||||
std::string paramName = function->parameters[i];
|
||||
|
||||
if (i < argCount) {
|
||||
newFrame.locals.Set(paramName, providedArgs[i]);
|
||||
}
|
||||
else {
|
||||
newFrame.locals.Set(paramName, StackValue());
|
||||
}
|
||||
}
|
||||
|
||||
_callStack.push(newFrame);
|
||||
|
||||
Execute(function->bytecodeChunk);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DlangVM::PushTable(std::shared_ptr<DlangTable> table)
|
||||
{
|
||||
stack.push(
|
||||
StackValue(
|
||||
StackValueType::Table,
|
||||
table
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DlangVM::SetField(const std::string& key)
|
||||
{
|
||||
auto val = Pop();
|
||||
auto stackTable = Pop();
|
||||
|
||||
if (stackTable.type == StackValueType::Table)
|
||||
{
|
||||
auto table = std::get<std::shared_ptr<DlangTable>>(stackTable.data);
|
||||
table->fields[key] = val;
|
||||
PushTable(table);
|
||||
}
|
||||
}
|
||||
|
||||
StackValue DlangVM::GetField(const std::string& key, bool popTable)
|
||||
{
|
||||
auto stackTable = Pop();
|
||||
|
||||
if (stackTable.type == StackValueType::Table)
|
||||
{
|
||||
auto table = std::get<std::shared_ptr<DlangTable>>(stackTable.data);
|
||||
|
||||
if (!popTable)
|
||||
PushTable(table);
|
||||
|
||||
auto it = table->fields.find(key);
|
||||
if (it == table->fields.end())
|
||||
{
|
||||
printf("[VM Error]: Field '%s' is not found!\n", key.c_str());
|
||||
return StackValue();
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
StackValue DlangVM::GetTableKeys()
|
||||
{
|
||||
auto stackTable = Pop();
|
||||
|
||||
if (stackTable.type == StackValueType::Table)
|
||||
{
|
||||
auto table = std::get<std::shared_ptr<DlangTable>>(stackTable.data);
|
||||
auto keyList = std::make_shared<DlangList>();
|
||||
|
||||
for (const auto& [key, _] : table->fields)
|
||||
keyList->values.push_back(
|
||||
StackValue(
|
||||
StackValueType::String,
|
||||
key
|
||||
)
|
||||
);
|
||||
|
||||
return StackValue(StackValueType::List, std::move(keyList));
|
||||
}
|
||||
else {
|
||||
printf("Failed to keys from non table type %s!\n", StackValueTypeToString(stackTable.type).c_str());
|
||||
return StackValue();
|
||||
}
|
||||
}
|
||||
|
||||
void DlangVM::PushList(std::shared_ptr<DlangList> list)
|
||||
{
|
||||
stack.push(
|
||||
StackValue(
|
||||
StackValueType::List,
|
||||
list
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
double DlangVM::PushListIndex()
|
||||
{
|
||||
auto val = Pop();
|
||||
auto stackList = Pop();
|
||||
|
||||
if (stackList.type == StackValueType::List)
|
||||
{
|
||||
auto list = std::get<std::shared_ptr<DlangList>>(stackList.data);
|
||||
list->Push(val);
|
||||
PushList(list);
|
||||
return list->values.size() - 1;
|
||||
}
|
||||
else {
|
||||
printf("Failed to push list index to type '%s'!\n", StackValueTypeToString(stackList.type).c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DlangVM::SetListIndex()
|
||||
{
|
||||
auto val = Pop();
|
||||
auto stackIndex = Pop();
|
||||
auto stackList = Pop();
|
||||
|
||||
if (stackIndex.type != StackValueType::Int)
|
||||
{
|
||||
printf("Failed to set non number type index %s!\n", StackValueTypeToString(stackIndex.type).c_str());
|
||||
stack.push(StackValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (stackList.type == StackValueType::List)
|
||||
{
|
||||
auto list = std::get<std::shared_ptr<DlangList>>(stackList.data);
|
||||
auto index = static_cast<int>(std::get<double>(stackIndex.data));
|
||||
list->values[index] = val;
|
||||
stack.push(stackList);
|
||||
}
|
||||
else if (stackList.type == StackValueType::String)
|
||||
{
|
||||
auto& str = std::get<std::string>(stackList.data);
|
||||
auto index = static_cast<int>(std::get<double>(stackIndex.data));
|
||||
|
||||
if (index > 0 && index < str.size())
|
||||
{
|
||||
if (std::holds_alternative<std::string>(val.data)) {
|
||||
str[index] = std::get<std::string>(val.data)[0];
|
||||
}
|
||||
else if (std::holds_alternative<double>(val.data)) {
|
||||
str[index] = static_cast<char>(std::get<double>(val.data));
|
||||
}
|
||||
}
|
||||
|
||||
stack.push(stackList);
|
||||
}
|
||||
else {
|
||||
printf("Failed to set index on non list/string type %s!\n", StackValueTypeToString(stackList.type).c_str());
|
||||
stack.push(StackValue());
|
||||
}
|
||||
}
|
||||
|
||||
StackValue DlangVM::GetListIndex()
|
||||
{
|
||||
auto stackIndex = Pop();
|
||||
auto stackObj = Pop();
|
||||
|
||||
if (stackIndex.type != StackValueType::Int && stackIndex.type != StackValueType::String)
|
||||
{
|
||||
printf("Failed to set non number/string type index %s!\n", StackValueTypeToString(stackIndex.type).c_str());
|
||||
return StackValue();
|
||||
}
|
||||
|
||||
if (stackObj.type == StackValueType::List && stackIndex.type == StackValueType::Int)
|
||||
{
|
||||
auto list = std::get<std::shared_ptr<DlangList>>(stackObj.data);
|
||||
auto index = std::get<double>(stackIndex.data);
|
||||
return list->values[index];
|
||||
} else if (stackObj.type == StackValueType::String && stackIndex.type == StackValueType::Int)
|
||||
{
|
||||
auto str = std::get<std::string>(stackObj.data);
|
||||
auto index = std::get<double>(stackIndex.data);
|
||||
return StackValue(
|
||||
StackValueType::String,
|
||||
str.at(index) + ""
|
||||
);
|
||||
}
|
||||
else if (stackObj.type == StackValueType::Table && stackIndex.type == StackValueType::String)
|
||||
{
|
||||
auto table = std::get<std::shared_ptr<DlangTable>>(stackObj.data);
|
||||
auto key = std::get<std::string>(stackIndex.data);
|
||||
|
||||
auto it = table->fields.find(key);
|
||||
if (it != table->fields.end())
|
||||
return it->second;
|
||||
|
||||
|
||||
return StackValue();
|
||||
}
|
||||
else {
|
||||
printf("Invalid type to index %s!\n", StackValueTypeToString(stackObj.type).c_str());
|
||||
return StackValue();
|
||||
}
|
||||
}
|
||||
|
||||
double DlangVM::GetSize()
|
||||
{
|
||||
auto stackObj = Pop();
|
||||
|
||||
if (stackObj.type == StackValueType::Table)
|
||||
{
|
||||
auto table = std::get<std::shared_ptr<DlangTable>>(stackObj.data);
|
||||
return table->fields.size();
|
||||
}
|
||||
else if (stackObj.type == StackValueType::List)
|
||||
{
|
||||
auto list = std::get<std::shared_ptr<DlangList>>(stackObj.data);
|
||||
return list->values.size();
|
||||
}
|
||||
else if (stackObj.type == StackValueType::String)
|
||||
{
|
||||
auto str = std::get<std::string>(stackObj.data);
|
||||
return str.size();
|
||||
}
|
||||
else {
|
||||
printf("Cant get size of type %s!\n", StackValueTypeToString(stackObj.type).c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DlangVM::DlangVM()
|
||||
{
|
||||
auto ver = getLatestVersion("downloads.dlang.eu", "/latest.txt");
|
||||
if (ver != VERSION && VERSION != "{DEVBUILD}")
|
||||
printf("[WARNING]: YOU ARE RUNNING AN OUTDATED VERSION OF DLANG!\n");
|
||||
|
||||
if (!pluginManager)
|
||||
pluginManager = new ModuleManager();
|
||||
}
|
||||
|
||||
void DlangVM::RunString(const std::string& code, std::vector<double>* extract)
|
||||
{
|
||||
Lexer lexer;
|
||||
lexer.Parse(code);
|
||||
lexer.ParseOrganizedTokens();
|
||||
auto parsedTokens = lexer.GetOrganizedTokens();
|
||||
|
||||
#if DEBUG_MODE == 1
|
||||
for (const auto& token : parsedTokens)
|
||||
printf("[%s]:\t\t%s\n", TokenTypeToString(token.type), token.value.c_str());
|
||||
|
||||
printf("=================================================\n");
|
||||
#endif
|
||||
|
||||
Parser parser(parsedTokens);
|
||||
auto program = parser.ParseProgram();
|
||||
|
||||
#if DEBUG_MODE == 1
|
||||
for (const auto& statement : program.statements)
|
||||
statement->Print();
|
||||
|
||||
printf("=================================================\n");
|
||||
#endif
|
||||
|
||||
Compiler compiler(&program);
|
||||
compiler.Compile();
|
||||
|
||||
#if DEBUG_MODE == 1
|
||||
compiler.Print();
|
||||
#endif
|
||||
|
||||
if (extract)
|
||||
*extract = compiler.GetBytecode();
|
||||
else
|
||||
RunBytecode(compiler.GetBytecode());
|
||||
}
|
||||
|
||||
void DlangVM::RunFile(const std::string& fileName, std::vector<double>* extract)
|
||||
{
|
||||
if (!std::filesystem::exists(fileName))
|
||||
throw std::runtime_error("File '" + fileName + "' does not exist!");
|
||||
|
||||
std::ifstream file(fileName, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Failed to open file '" + fileName + "'!");
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::string content;
|
||||
content.resize(size);
|
||||
|
||||
if (!file.read(&content[0], size))
|
||||
throw std::runtime_error("Failed to read file '" + fileName + "', possibly corrupt?");
|
||||
|
||||
RunString(content, extract);
|
||||
}
|
||||
|
||||
void DlangVM::RunBinaryFile(const std::string& fileName)
|
||||
{
|
||||
if (!std::filesystem::exists(fileName))
|
||||
throw std::runtime_error("File '" + fileName + "' does not exist!");
|
||||
|
||||
std::ifstream file(fileName, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Failed to open file '" + fileName + "'!");
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<double> bytecode(size / sizeof(double));
|
||||
if (file.read(reinterpret_cast<char*>(bytecode.data()), size)) {
|
||||
RunBytecode(bytecode);
|
||||
}
|
||||
else throw std::runtime_error("Failed to read binary dlang file!");
|
||||
}
|
||||
|
||||
std::vector<double> DlangVM::ParseHeader(const std::vector<double>& bytecode)
|
||||
{
|
||||
if (bytecode.size() < 3)
|
||||
throw std::runtime_error("Bytecode too short!");
|
||||
|
||||
size_t ip = 0;
|
||||
|
||||
if (bytecode[ip++] != 0xFF ||
|
||||
bytecode[ip++] != 0x8C ||
|
||||
bytecode[ip++] != 0xFF)
|
||||
throw std::runtime_error("Failed to load dlang header from bytecode!");
|
||||
|
||||
size_t stringTableSize = static_cast<size_t>(bytecode[ip++]);
|
||||
|
||||
for (size_t i = 0; i < stringTableSize; ++i)
|
||||
strings.push(GetRealString(bytecode, ip));
|
||||
|
||||
std::vector<double> instructions(bytecode.begin() + ip, bytecode.end());
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
void DlangVM::RunBytecode(const std::vector<double>& bytecode)
|
||||
{
|
||||
auto instructions = ParseHeader(bytecode);
|
||||
Execute(instructions);
|
||||
}
|
||||
|
||||
StackValue DlangVM::GetVariable(const std::string& name)
|
||||
{
|
||||
if (_callStack.top() > 0)
|
||||
{
|
||||
auto& currentFrame = _callStack.peek();
|
||||
auto local = currentFrame.locals.Get(name);
|
||||
if (local.type != StackValueType::NIL)
|
||||
return local;
|
||||
}
|
||||
|
||||
auto glob = globals.Get(name);
|
||||
if (glob.type != StackValueType::NIL)
|
||||
return glob;
|
||||
|
||||
return StackValue();
|
||||
}
|
||||
|
||||
void DlangVM::SetVariable(const std::string& name, StackValue value)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
if (_callStack.top() > 0)
|
||||
{
|
||||
auto& currentFrame = _callStack.peek();
|
||||
auto local = currentFrame.locals.Get(name);
|
||||
if (local.type != StackValueType::NIL)
|
||||
{
|
||||
currentFrame.locals.Set(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
auto glob = globals.Get(name);
|
||||
if (glob.type != StackValueType::NIL)
|
||||
{
|
||||
globals.Set(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
if (_callStack.top() > 0)
|
||||
{
|
||||
_callStack.peek().locals.Set(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
globals.Set(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef DLANG_TYPES_INCLUDE_HPP
|
||||
#define DLANG_TYPES_INCLUDE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
class DlangTable;
|
||||
class DlangList;
|
||||
struct DlangFunction;
|
||||
|
||||
using VMDataTypes = std::variant<double, bool, std::string, std::shared_ptr<DlangTable>, std::shared_ptr<DlangFunction>, std::shared_ptr<DlangList>>;
|
||||
using CFunction = void(*)(class DlangVM* vm);
|
||||
|
||||
#define TOKEN_TYPES \
|
||||
X(UNIDENTIFIED) \
|
||||
X(KEYWORD) \
|
||||
X(SYNTAX) \
|
||||
X(IDENTIFIER) \
|
||||
X(NUMBER) \
|
||||
X(BOOLEAN) \
|
||||
X(STRING) \
|
||||
X(MATH_IDENT)
|
||||
|
||||
#define OPCODE_TYPES \
|
||||
X(EOC, 0x100) \
|
||||
X(PUSH_INT, 0x101) \
|
||||
X(PUSH_BOOL, 0x102) \
|
||||
X(PUSH_STRING, 0x103) \
|
||||
X(PUSH_VAR, 0x104) \
|
||||
X(DEFINE_VAR, 0x105) \
|
||||
X(STORE_VAR, 0x106) \
|
||||
X(ADD, 0x107) \
|
||||
X(SUB, 0x108) \
|
||||
X(MUL, 0x109) \
|
||||
X(DIV, 0x10A) \
|
||||
X(CALL, 0x10B) \
|
||||
X(JZ, 0x10C) \
|
||||
X(JUMP, 0x10D) \
|
||||
X(GREATER_THAN, 0x10E) \
|
||||
X(LESS_THAN, 0x10F) \
|
||||
X(FUNC_DEF, 0x11A) \
|
||||
X(RET, 0x11B) \
|
||||
X(NEW_TABLE, 0x11C) \
|
||||
X(GET_FIELD, 0x11D) \
|
||||
X(SET_FIELD, 0x11E) \
|
||||
X(POP, 0x11F) \
|
||||
X(CMP, 0x12A) \
|
||||
X(GTE, 0x12B) \
|
||||
X(LTE, 0x12C) \
|
||||
X(AND, 0x12D) \
|
||||
X(OR, 0x12E) \
|
||||
X(PUSH_FUNC, 0x12F) \
|
||||
X(NTE, 0x13A) \
|
||||
X(NEW_LIST, 0x13B) \
|
||||
X(PUSH_LIST, 0x13C) \
|
||||
X(GET_INDEX, 0x13D) \
|
||||
X(SET_INDEX, 0x13E) \
|
||||
X(SIZE, 0x13F) \
|
||||
X(KEYS, 0x14A)
|
||||
|
||||
#include "token.hpp"
|
||||
#include "opcodes.hpp"
|
||||
#include "stack_value.hpp"
|
||||
#include "stack.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
#ifndef DLANG_TYPES_LIST_HPP
|
||||
#define DLANG_TYPES_LIST_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template<typename type>
|
||||
class List
|
||||
{
|
||||
private:
|
||||
type* _list = nullptr;
|
||||
size_t _size = 0;
|
||||
size_t _capacity = 0;
|
||||
|
||||
void grow()
|
||||
{
|
||||
size_t newCapacity = (_capacity == 0) ? 50 : _capacity + 50;
|
||||
type* newList = new type[newCapacity];
|
||||
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
newList[i] = _list[i];
|
||||
|
||||
delete[] _list;
|
||||
|
||||
_list = newList;
|
||||
_capacity = newCapacity;
|
||||
}
|
||||
|
||||
public:
|
||||
List() = default;
|
||||
|
||||
List(const List& other)
|
||||
{
|
||||
_size = other._size;
|
||||
_capacity = other._capacity;
|
||||
if (_capacity > 0)
|
||||
{
|
||||
_list = new type[_capacity];
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
_list[i] = other._list[i];
|
||||
}
|
||||
}
|
||||
|
||||
List& operator=(const List& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
delete[] _list;
|
||||
_list = nullptr;
|
||||
_size = other._size;
|
||||
_capacity = other._capacity;
|
||||
|
||||
if (_capacity > 0)
|
||||
{
|
||||
_list = new type[_capacity];
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
_list[i] = other._list[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~List()
|
||||
{
|
||||
try {
|
||||
delete[] _list;
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
int push(type val)
|
||||
{
|
||||
if (_size >= _capacity)
|
||||
grow();
|
||||
|
||||
_list[_size] = val;
|
||||
return static_cast<int>(_size++);
|
||||
}
|
||||
|
||||
size_t size() const { return _size; }
|
||||
size_t capacity() const { return _capacity; }
|
||||
|
||||
type& operator[](size_t index)
|
||||
{
|
||||
return _list[index];
|
||||
}
|
||||
|
||||
bool Contains(type t) const
|
||||
{
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
if (_list[i] == t)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int GetOrSet(type value)
|
||||
{
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
if (_list[i] == value)
|
||||
return i;
|
||||
|
||||
return push(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename type>
|
||||
class NamedList
|
||||
{
|
||||
private:
|
||||
struct DataType
|
||||
{
|
||||
type data;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
private:
|
||||
List<DataType> _list;
|
||||
|
||||
public:
|
||||
void Set(const std::string& name, type t)
|
||||
{
|
||||
for (int i = 0; i < _list.size(); i++)
|
||||
{
|
||||
if (_list[i].name == name)
|
||||
{
|
||||
_list[i].data = t;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DataType dt;
|
||||
dt.name = name;
|
||||
dt.data = t;
|
||||
_list.push(dt);
|
||||
}
|
||||
|
||||
type Get(const std::string& name)
|
||||
{
|
||||
for (int i = 0; i < _list.size(); i++)
|
||||
{
|
||||
if (_list[i].name == name)
|
||||
return _list[i].data;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
type Get(int index)
|
||||
{
|
||||
if (index >= _list.size())
|
||||
return {};
|
||||
|
||||
return _list[index].data;
|
||||
}
|
||||
|
||||
int top()
|
||||
{
|
||||
return _list.size();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef DLANG_TYPES_OPCODES_HPP
|
||||
#define DLANG_TYPES_OPCODES_HPP
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
#include <cstdint>
|
||||
|
||||
enum class Opcodes : int64_t {
|
||||
#define X(name, val) name = val,
|
||||
OPCODE_TYPES
|
||||
#undef X
|
||||
};
|
||||
|
||||
constexpr const char* OpcodeToString(Opcodes type)
|
||||
{
|
||||
switch (type) {
|
||||
#define X(name, val) case Opcodes::name: return #name;
|
||||
OPCODE_TYPES
|
||||
#undef X
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef DLANG_TYPES_STACK_HPP
|
||||
#define DLANG_TYPES_STACK_HPP
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include "list.hpp"
|
||||
|
||||
template<typename type, int size>
|
||||
class Stack
|
||||
{
|
||||
private:
|
||||
int _top = 0;
|
||||
type _stack[size];
|
||||
|
||||
public:
|
||||
void push(type t)
|
||||
{
|
||||
if (_top >= size)
|
||||
throw std::runtime_error("Stack tried to go over the stack limit!");
|
||||
|
||||
_stack[_top++] = t;
|
||||
}
|
||||
|
||||
type pop()
|
||||
{
|
||||
if (_top <= 0)
|
||||
return type();
|
||||
|
||||
return _stack[--_top];
|
||||
}
|
||||
|
||||
void pop(int amount)
|
||||
{
|
||||
if ((_top - amount) < 0)
|
||||
throw std::runtime_error("Stack underflow, attempted to pop more items than available on the stack!");
|
||||
|
||||
_top -= amount;
|
||||
}
|
||||
|
||||
type& peek()
|
||||
{
|
||||
if (_top <= 0)
|
||||
{
|
||||
static type empty = {};
|
||||
return empty;
|
||||
}
|
||||
|
||||
return _stack[_top - 1];
|
||||
}
|
||||
|
||||
int top() { return _top; }
|
||||
int max() const { return size; }
|
||||
|
||||
type operator[](int index)
|
||||
{
|
||||
return _stack[index];
|
||||
}
|
||||
};
|
||||
|
||||
struct CallFrame
|
||||
{
|
||||
size_t returnIp;
|
||||
std::vector<double> previousBytecode;
|
||||
NamedList<StackValue> locals;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#ifndef DLANG_TYPES_STACK_VALUE_HPP
|
||||
#define DLANG_TYPES_STACK_VALUE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
|
||||
enum class StackValueType
|
||||
{
|
||||
NIL,
|
||||
Int,
|
||||
Bool,
|
||||
String,
|
||||
Table,
|
||||
Function,
|
||||
List
|
||||
};
|
||||
|
||||
static std::string StackValueTypeToString(StackValueType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case StackValueType::NIL: return "NIL"; break;
|
||||
case StackValueType::Int: return "Int"; break;
|
||||
case StackValueType::Bool: return "Bool"; break;
|
||||
case StackValueType::String: return "String"; break;
|
||||
case StackValueType::Table: return "Table"; break;
|
||||
case StackValueType::Function: return "Function"; break;
|
||||
case StackValueType::List: return "List"; break;
|
||||
default: return "Unknown"; break;
|
||||
}
|
||||
}
|
||||
|
||||
struct StackValue
|
||||
{
|
||||
StackValueType type;
|
||||
VMDataTypes data;
|
||||
|
||||
StackValue() { this->type = StackValueType::NIL; };
|
||||
StackValue(StackValueType _type, VMDataTypes _data)
|
||||
{
|
||||
this->type = _type;
|
||||
this->data = std::move(_data);
|
||||
}
|
||||
};
|
||||
|
||||
struct DlangFunction
|
||||
{
|
||||
double bodyAddress;
|
||||
std::vector<std::string> parameters;
|
||||
std::vector<double> bytecodeChunk;
|
||||
|
||||
bool isNative;
|
||||
CFunction nativeFunction;
|
||||
|
||||
static std::shared_ptr<DlangFunction> CreateNative(CFunction function)
|
||||
{
|
||||
auto func = std::make_shared<DlangFunction>();
|
||||
func->isNative = true;
|
||||
func->nativeFunction = function;
|
||||
return func;
|
||||
}
|
||||
|
||||
static std::shared_ptr<DlangFunction> CreateInternal(const std::string& name, double bodyAddy, std::vector<std::string> params)
|
||||
{
|
||||
auto func = std::make_shared<DlangFunction>();
|
||||
func->bodyAddress = bodyAddy;
|
||||
func->parameters = params;
|
||||
func->isNative = false;
|
||||
return func;
|
||||
}
|
||||
};
|
||||
|
||||
class DlangTable
|
||||
{
|
||||
public:
|
||||
std::unordered_map<std::string, StackValue> fields;
|
||||
|
||||
void Set(const std::string& key, StackValue val)
|
||||
{
|
||||
fields[key] = val;
|
||||
}
|
||||
|
||||
bool Get(const std::string& key, StackValue& outVal)
|
||||
{
|
||||
auto it = fields.find(key);
|
||||
if (it != fields.end())
|
||||
{
|
||||
outVal = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class DlangList
|
||||
{
|
||||
public:
|
||||
std::vector<StackValue> values;
|
||||
|
||||
void Push(StackValue val)
|
||||
{
|
||||
values.push_back(val);
|
||||
}
|
||||
|
||||
StackValue Get(int index)
|
||||
{
|
||||
if (index > values.size())
|
||||
return StackValue(); // push nil
|
||||
|
||||
return values.at(index);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef DLANG_TYPES_TOKEN_HPP
|
||||
#define DLANG_TYPES_TOKEN_HPP
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
|
||||
enum class TokenType : int {
|
||||
#define X(name) name,
|
||||
TOKEN_TYPES
|
||||
#undef X
|
||||
};
|
||||
|
||||
constexpr const char* TokenTypeToString(TokenType type)
|
||||
{
|
||||
switch (type) {
|
||||
#define X(name) case TokenType::name: return #name;
|
||||
TOKEN_TYPES
|
||||
#undef X
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
struct PreToken
|
||||
{
|
||||
std::string value;
|
||||
int line;
|
||||
int column;
|
||||
|
||||
static PreToken CreateEmpty()
|
||||
{
|
||||
PreToken t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
struct Token
|
||||
{
|
||||
std::string value;
|
||||
TokenType type;
|
||||
|
||||
int line;
|
||||
int column;
|
||||
|
||||
void Print()
|
||||
{
|
||||
printf("[%d:%d] %s: %s\n", line, column, TokenTypeToString(type), value.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef DLANG_VM_HPP
|
||||
#define DLANG_VM_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "lexer.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "compiler.hpp"
|
||||
#include "plugins.hpp"
|
||||
|
||||
inline ModuleManager* pluginManager = nullptr;
|
||||
|
||||
#define DEBUG_MODE 1
|
||||
#define VERSION "{DEVBUILD}"
|
||||
|
||||
using ByteCodeArray = std::vector<double>;
|
||||
|
||||
class DlangVM
|
||||
{
|
||||
public:
|
||||
Stack<StackValue, 25> stack;
|
||||
NamedList<StackValue> globals;
|
||||
List<std::string> strings;
|
||||
List<std::string> loadedPlugins;
|
||||
private:
|
||||
Stack<CallFrame, 25> _callStack;
|
||||
|
||||
private:
|
||||
std::string GetString(const ByteCodeArray& bytecode, size_t& ip);
|
||||
std::string GetRealString(const ByteCodeArray& bytecode, size_t& ip);
|
||||
void Execute(std::vector<double> bytecode);
|
||||
|
||||
public:
|
||||
StackValue Pop();
|
||||
void Pop(double amount);
|
||||
|
||||
void PushNumber(double number);
|
||||
void PushBoolean(bool value);
|
||||
void PushString(const std::string& str);
|
||||
void PushVar(const std::string& var);
|
||||
|
||||
void PushCFunction(CFunction func);
|
||||
bool CallFunc(double argCount);
|
||||
|
||||
void PushTable(std::shared_ptr<DlangTable> table);
|
||||
void SetField(const std::string& key);
|
||||
StackValue GetField(const std::string& key, bool popTable = true);
|
||||
StackValue GetTableKeys();
|
||||
|
||||
void PushList(std::shared_ptr<DlangList> list);
|
||||
double PushListIndex();
|
||||
void SetListIndex();
|
||||
StackValue GetListIndex();
|
||||
|
||||
double GetSize(); // Function to get size of List, Table and String
|
||||
|
||||
public:
|
||||
DlangVM();
|
||||
|
||||
void RunString(const std::string& code, ByteCodeArray* extract = nullptr);
|
||||
void RunFile(const std::string& fileName, ByteCodeArray* extract = nullptr);
|
||||
void RunBinaryFile(const std::string& fileName);
|
||||
ByteCodeArray ParseHeader(const ByteCodeArray& bytecode);
|
||||
void RunBytecode(const ByteCodeArray& bytecode);
|
||||
|
||||
public:
|
||||
StackValue GetVariable(const std::string& name);
|
||||
void SetVariable(const std::string& name, StackValue value);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,231 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <variant>
|
||||
#include <sqlite3.h>
|
||||
#include <dlang.hpp>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
using SqlValue = std::variant<std::nullptr_t, double, std::string>;
|
||||
using SqlRow = std::unordered_map<std::string, SqlValue>;
|
||||
using SqlQueryResult = std::vector<SqlRow>;
|
||||
|
||||
struct DbResult {
|
||||
bool success;
|
||||
std::string errorMessage;
|
||||
SqlQueryResult rows;
|
||||
int rowsAffected;
|
||||
};
|
||||
|
||||
struct AsyncTask {
|
||||
StackValue callbackVal;
|
||||
DbResult result;
|
||||
};
|
||||
|
||||
static sqlite3* g_db = nullptr;
|
||||
static std::queue<AsyncTask> g_completedTasks;
|
||||
static std::mutex g_queueMutex;
|
||||
|
||||
static DbResult ExecuteQueryInternal(sqlite3* db, const std::string& query) {
|
||||
DbResult res{ true, "", {}, 0 };
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
|
||||
if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
res.success = false;
|
||||
res.errorMessage = sqlite3_errmsg(db);
|
||||
return res;
|
||||
}
|
||||
|
||||
int colCount = sqlite3_column_count(stmt);
|
||||
|
||||
while (true) {
|
||||
int stepRes = sqlite3_step(stmt);
|
||||
if (stepRes == SQLITE_ROW) {
|
||||
SqlRow row;
|
||||
for (int i = 0; i < colCount; ++i) {
|
||||
const char* colName = sqlite3_column_name(stmt, i);
|
||||
int type = sqlite3_column_type(stmt, i);
|
||||
|
||||
switch (type) {
|
||||
case SQLITE_INTEGER:
|
||||
row[colName] = static_cast<double>(sqlite3_column_int64(stmt, i));
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
row[colName] = sqlite3_column_double(stmt, i);
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
row[colName] = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, i)));
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
default:
|
||||
row[colName] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
res.rows.push_back(row);
|
||||
}
|
||||
else if (stepRes == SQLITE_DONE) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
res.success = false;
|
||||
res.errorMessage = sqlite3_errmsg(db);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res.rowsAffected = sqlite3_changes(db);
|
||||
sqlite3_finalize(stmt);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void PushDbResultToVM(DlangVM* vm, const DbResult& res) {
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushboolean(vm, res.success);
|
||||
dlang_setfield(vm, "success");
|
||||
|
||||
if (!res.success) {
|
||||
dlang_pushstring(vm, res.errorMessage.c_str());
|
||||
dlang_setfield(vm, "error");
|
||||
}
|
||||
|
||||
dlang_pushnumber(vm, res.rowsAffected);
|
||||
dlang_setfield(vm, "rowsAffected");
|
||||
|
||||
dlang_newlist(vm);
|
||||
|
||||
for (const auto& row : res.rows) {
|
||||
dlang_newtable(vm);
|
||||
|
||||
for (const auto& [colName, val] : row) {
|
||||
if (std::holds_alternative<double>(val)) {
|
||||
dlang_pushnumber(vm, std::get<double>(val));
|
||||
}
|
||||
else if (std::holds_alternative<std::string>(val)) {
|
||||
dlang_pushstring(vm, std::get<std::string>(val).c_str());
|
||||
}
|
||||
else {
|
||||
dlang_pushnil(vm);
|
||||
}
|
||||
dlang_setfield(vm, colName.c_str());
|
||||
}
|
||||
|
||||
dlang_push(vm, dlang_pop(vm));
|
||||
dlang_pushindex(vm);
|
||||
}
|
||||
|
||||
dlang_setfield(vm, "rows");
|
||||
}
|
||||
|
||||
void NativeSqliteOpen(DlangVM* vm) {
|
||||
if (!dlang_isstring(vm)) {
|
||||
dlang_pushboolean(vm, false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string dbPath = dlang_tostring(vm);
|
||||
if (g_db) {
|
||||
sqlite3_close(g_db);
|
||||
g_db = nullptr;
|
||||
}
|
||||
|
||||
int rc = sqlite3_open(dbPath.c_str(), &g_db);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_exec(g_db, "PRAGMA journal_mode = WAL;", nullptr, nullptr, nullptr);
|
||||
sqlite3_exec(g_db, "PRAGMA synchronous = NORMAL;", nullptr, nullptr, nullptr);
|
||||
dlang_pushboolean(vm, true);
|
||||
}
|
||||
else {
|
||||
dlang_pushboolean(vm, false);
|
||||
}
|
||||
}
|
||||
|
||||
void NativeSqliteQuery(DlangVM* vm) {
|
||||
if (!g_db || !dlang_isstring(vm)) {
|
||||
DbResult err{ false, "Geen DB of ongeldige query", {}, 0 };
|
||||
PushDbResultToVM(vm, err);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string query = dlang_tostring(vm);
|
||||
DbResult res = ExecuteQueryInternal(g_db, query);
|
||||
PushDbResultToVM(vm, res);
|
||||
}
|
||||
|
||||
void NativeSqliteQueryAsync(DlangVM* vm) {
|
||||
if (!g_db) return;
|
||||
|
||||
if (!dlang_isstring(vm)) return;
|
||||
std::string query = dlang_tostring(vm);
|
||||
|
||||
if (!dlang_isfunction(vm)) return;
|
||||
StackValue callbackVal = dlang_totoken(vm);
|
||||
|
||||
std::thread([query, callbackVal]() {
|
||||
DbResult res = ExecuteQueryInternal(g_db, query);
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_queueMutex);
|
||||
g_completedTasks.push({ callbackVal, res });
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void NativeSqlitePoll(DlangVM* vm) {
|
||||
std::queue<AsyncTask> localTasks;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_queueMutex);
|
||||
localTasks.swap(g_completedTasks);
|
||||
}
|
||||
|
||||
while (!localTasks.empty()) {
|
||||
auto task = localTasks.front();
|
||||
localTasks.pop();
|
||||
|
||||
PushDbResultToVM(vm, task.result);
|
||||
dlang_push(vm, task.callbackVal);
|
||||
dlang_call(vm, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void NativeSqliteClose(DlangVM* vm) {
|
||||
if (g_db) {
|
||||
sqlite3_close(g_db);
|
||||
g_db = nullptr;
|
||||
}
|
||||
dlang_pushboolean(vm, true);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
EXPORT void initialize(DlangVM* vm) {
|
||||
dlang_newtable(vm);
|
||||
|
||||
dlang_pushcfunction(vm, NativeSqliteOpen);
|
||||
dlang_setfield(vm, "open");
|
||||
|
||||
dlang_pushcfunction(vm, NativeSqliteQuery);
|
||||
dlang_setfield(vm, "query");
|
||||
|
||||
dlang_pushcfunction(vm, NativeSqliteQueryAsync);
|
||||
dlang_setfield(vm, "query_async");
|
||||
|
||||
dlang_pushcfunction(vm, NativeSqlitePoll);
|
||||
dlang_setfield(vm, "poll");
|
||||
|
||||
dlang_pushcfunction(vm, NativeSqliteClose);
|
||||
dlang_setfield(vm, "close");
|
||||
|
||||
dlang_setglobal(vm, "sqlite");
|
||||
|
||||
std::cout << ">>> SQLite Module Initialized (Queue-based Async) <<<" << std::endl;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user