inital commit

This commit is contained in:
Damian committed 2026-10-12 23:32:28 +02:00
1 parent ebd073c803
commit 762d7776a4
36 files changed
+75891

No files matched your search

+848
View File
@@ -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());
}
+255
View File
@@ -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;
}
+651
View File
@@ -0,0 +1,651 @@
#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("Syntax Error: Expected token type '" + std::string(TokenTypeToString(type)) +
"' but got '" + std::string(TokenTypeToString(current.type)) +
"' (value: '" + current.value + "') " + LocationText(current));
if (!value.empty() && value != current.value)
throw std::runtime_error("Syntax Error: Expected specific value '" + value +
"' but got '" + current.value + "' " + 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;
}
std::string tokenDesc = std::string(TokenTypeToString(current.type));
if (!current.value.empty()) {
tokenDesc += " ('" + current.value + "')";
}
throw std::runtime_error("Expected expression, but found unexpected token type " + tokenDesc + " " + 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
+885
View File
@@ -0,0 +1,885 @@
#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;
}
printf("Cant get field on non table type %s!\n", StackValueTypeToString(stackTable.type).c_str());
return StackValue();
}
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);
}
}
}