Skip to content

Commit

Permalink
VM: code organization
Browse files Browse the repository at this point in the history
  • Loading branch information
mrunix00 committed Jul 7, 2024
1 parent 5fdee67 commit 1f3b51f
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 125 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ flex_target(lexer src/lexer.l "${LEXER_OUT}" DEFINES_FILE ${LEXER_DIR}/lexer.h)
bison_target(parser src/parser.y "${PARSER_OUT}" DEFINES_FILE ${PARSER_DIR}/parser.h)
add_flex_bison_dependency(lexer parser)

add_executable(SPL main.cpp ${SOURCES} ${LEXER_OUT} ${PARSER_OUT})
add_executable(SPL main.cpp ${SOURCES} ${LEXER_OUT} ${PARSER_OUT}
src/utils.cpp)
add_executable(tests ${TEST_SOURCES} ${SOURCES} ${LEXER_OUT} ${PARSER_OUT})
target_link_libraries(SPL)
target_link_libraries(tests GTest::gtest_main GTest::gmock_main)
106 changes: 7 additions & 99 deletions include/utils.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "ast.h"
#include "vm.h"
#include <stdexcept>

#define GENERATE_EMIT_FUNCTION(OPERATION) \
static inline void emit##OPERATION(Program &program, Segment &segment, const std::string &identifier) { \
Expand Down Expand Up @@ -38,20 +40,6 @@
GENERATE_EMIT_FUNCTION(Load)
GENERATE_EMIT_FUNCTION(Store)

static inline Variable::Type varTypeConvert(AbstractSyntaxTree *ast) {
if (ast->nodeType != AbstractSyntaxTree::Type::Node)
throw std::runtime_error("[Declaration::compile] Invalid type: " + ast->typeStr);
auto token = dynamic_cast<Node *>(ast)->token;
switch (token.type) {
case I32:
return Variable::Type::I32;
case I64:
return Variable::Type::I64;
default:
throw std::runtime_error("[Declaration::compile] Invalid type: " + token.value);
}
}

#define DECLARE_NUMBER_VAR(TYPE, PARAM) \
case TYPE: { \
segment.instructions.push_back({.type = Instruction::InstructionType::Load##TYPE, \
Expand Down Expand Up @@ -82,65 +70,6 @@ static inline Variable::Type varTypeConvert(AbstractSyntaxTree *ast) {
segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::OP##TYPE}); \
break;

static Variable::Type deduceType(Program &program, Segment &segment, AbstractSyntaxTree *ast) {
switch (ast->nodeType) {
case AbstractSyntaxTree::Type::Node: {
auto token = dynamic_cast<Node *>(ast)->token;
switch (token.type) {
case Number: {
try {
std::stoi(token.value);
return Variable::Type::I32;
} catch (std::out_of_range &) {
goto long64;
}
long64:
try {
std::stol(token.value);
return Variable::Type::I64;
} catch (std::exception &) {
throw std::runtime_error("Invalid number: " + token.value);
}
}
case Identifier: {
if (segment.find_local(token.value) != -1)
return segment.locals[token.value].type;
if (program.find_global(token.value) != -1)
return program.segments[0].locals[token.value].type;
throw std::runtime_error("Identifier not found: " + token.value);
}
default:
throw std::runtime_error("Invalid type: " + token.value);
}
}
case AbstractSyntaxTree::Type::UnaryExpression: {
auto unary = dynamic_cast<UnaryExpression *>(ast);
return deduceType(program, segment, unary->expression);
}
case AbstractSyntaxTree::Type::BinaryExpression: {
auto binary = dynamic_cast<BinaryExpression *>(ast);
auto left = deduceType(program, segment, binary->left);
auto right = deduceType(program, segment, binary->right);
if ((left == Variable::Type::I32 || left == Variable::Type::I64) &&
(right == Variable::Type::I32 || right == Variable::Type::I64))
return left == Variable::Type::I64 || right == Variable::Type::I64 ? Variable::Type::I64
: Variable::Type::I32;
if (left != right)
throw std::runtime_error("Type mismatch");
return left;
}
case AbstractSyntaxTree::Type::FunctionCall: {
// TODO: add support for multiple return types
auto call = dynamic_cast<FunctionCall *>(ast);
auto function = program.find_function(program.segments[segment.id], call->identifier.token.value);
if (function == -1)
throw std::runtime_error("Function not found: " + call->identifier.token.value);
return Variable::Type::I32;
}
default:
throw std::runtime_error("Invalid type: " + ast->typeStr);
}
}

enum class GenericInstruction {
Add,
Expand All @@ -156,29 +85,8 @@ enum class GenericInstruction {
NotEqual
};

#define TYPE_CASE(INS) \
case GenericInstruction::INS: { \
switch (type) { \
case Variable::Type::I32: \
return {Instruction::InstructionType::INS##I32}; \
case Variable::Type::I64: \
return {Instruction::InstructionType::INS##I64}; \
default: \
throw std::runtime_error("[getInstructionWithType] Invalid type"); \
} \
}
static inline Instruction getInstructionWithType(GenericInstruction instruction, Variable::Type type) {
switch (instruction) {
TYPE_CASE(Add)
TYPE_CASE(Sub)
TYPE_CASE(Mul)
TYPE_CASE(Div)
TYPE_CASE(Mod)
TYPE_CASE(Equal)
TYPE_CASE(Less)
TYPE_CASE(Greater)
TYPE_CASE(GreaterEqual)
TYPE_CASE(LessEqual)
TYPE_CASE(NotEqual)
}
}
void assert(bool condition, const char *message);
Variable::Type varTypeConvert(AbstractSyntaxTree *ast);
Variable::Type deduceType(Program &program, Segment &segment, AbstractSyntaxTree *ast);
Instruction getInstructionWithType(GenericInstruction instruction, Variable::Type type);
void typeCast(std::vector<Instruction> &instructions, Variable::Type from, Variable::Type to);
28 changes: 3 additions & 25 deletions src/ast.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
#include "ast.h"
#include "utils.h"

#include <stdexcept>
#include <utility>

static inline void assert(bool condition, const char *message) {
if (!condition)
throw std::runtime_error("Assertion failed: " + std::string(message));
}

Node::Node(Token token) : token(std::move(token)) {
nodeType = Type::Node;
typeStr = "Node";
Expand Down Expand Up @@ -69,28 +63,12 @@ void BinaryExpression::compile(Program &program, Segment &segment) const {
}
auto leftType = deduceType(program, segment, left);
auto rightType = deduceType(program, segment, right);
auto finalType = deduceType(program, segment, (AbstractSyntaxTree*) this);
auto finalType = deduceType(program, segment, (AbstractSyntaxTree *) this);

left->compile(program, segment);
if (leftType != finalType) {
switch (leftType) {
case Variable::Type::I32:
segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::ConvertI32toI64});
break;
default:
throw std::runtime_error("[BinaryExpression::compile] Invalid type: " + left->typeStr);
}
}
typeCast(segment.instructions, leftType, finalType);
right->compile(program, segment);
if (rightType != finalType) {
switch (rightType) {
case Variable::Type::I32:
segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::ConvertI32toI64});
break;
default:
throw std::runtime_error("[BinaryExpression::compile] Invalid type: "+ right->typeStr);
}
}
typeCast(segment.instructions, rightType, finalType);
switch (op.type) {
case Plus:
return segment.instructions.push_back(getInstructionWithType(GenericInstruction::Add, finalType));
Expand Down
123 changes: 123 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "utils.h"

void assert(bool condition, const char *message) {
if (!condition)
throw std::runtime_error("Assertion failed: " + std::string(message));
}

Variable::Type varTypeConvert(AbstractSyntaxTree *ast) {
if (ast->nodeType != AbstractSyntaxTree::Type::Node)
throw std::runtime_error("[Declaration::compile] Invalid type: " + ast->typeStr);
auto token = dynamic_cast<Node *>(ast)->token;
switch (token.type) {
case I32:
return Variable::Type::I32;
case I64:
return Variable::Type::I64;
default:
throw std::runtime_error("[Declaration::compile] Invalid type: " + token.value);
}
}

Variable::Type deduceType(Program &program, Segment &segment, AbstractSyntaxTree *ast) {
switch (ast->nodeType) {
case AbstractSyntaxTree::Type::Node: {
auto token = dynamic_cast<Node *>(ast)->token;
switch (token.type) {
case Number: {
try {
std::stoi(token.value);
return Variable::Type::I32;
} catch (std::out_of_range &) {
goto long64;
}
long64:
try {
std::stol(token.value);
return Variable::Type::I64;
} catch (std::exception &) {
throw std::runtime_error("Invalid number: " + token.value);
}
}
case Identifier: {
if (segment.find_local(token.value) != -1)
return segment.locals[token.value].type;
if (program.find_global(token.value) != -1)
return program.segments[0].locals[token.value].type;
throw std::runtime_error("Identifier not found: " + token.value);
}
default:
throw std::runtime_error("Invalid type: " + token.value);
}
}
case AbstractSyntaxTree::Type::UnaryExpression: {
auto unary = dynamic_cast<UnaryExpression *>(ast);
return deduceType(program, segment, unary->expression);
}
case AbstractSyntaxTree::Type::BinaryExpression: {
auto binary = dynamic_cast<BinaryExpression *>(ast);
auto left = deduceType(program, segment, binary->left);
auto right = deduceType(program, segment, binary->right);
if ((left == Variable::Type::I32 || left == Variable::Type::I64) &&
(right == Variable::Type::I32 || right == Variable::Type::I64))
return left == Variable::Type::I64 || right == Variable::Type::I64 ? Variable::Type::I64
: Variable::Type::I32;
if (left != right)
throw std::runtime_error("Type mismatch");
return left;
}
case AbstractSyntaxTree::Type::FunctionCall: {
// TODO: add support for multiple return types
auto call = dynamic_cast<FunctionCall *>(ast);
auto function = program.find_function(program.segments[segment.id], call->identifier.token.value);
if (function == -1)
throw std::runtime_error("Function not found: " + call->identifier.token.value);
return Variable::Type::I32;
}
default:
throw std::runtime_error("Invalid type: " + ast->typeStr);
}
}

#define TYPE_CASE(INS) \
case GenericInstruction::INS: { \
switch (type) { \
case Variable::Type::I32: \
return {Instruction::InstructionType::INS##I32}; \
case Variable::Type::I64: \
return {Instruction::InstructionType::INS##I64}; \
default: \
throw std::runtime_error("[getInstructionWithType] Invalid type"); \
} \
}
Instruction getInstructionWithType(GenericInstruction instruction, Variable::Type type) {
switch (instruction) {
TYPE_CASE(Add)
TYPE_CASE(Sub)
TYPE_CASE(Mul)
TYPE_CASE(Div)
TYPE_CASE(Mod)
TYPE_CASE(Equal)
TYPE_CASE(Less)
TYPE_CASE(Greater)
TYPE_CASE(GreaterEqual)
TYPE_CASE(LessEqual)
TYPE_CASE(NotEqual)
}
}

void typeCast(std::vector<Instruction> &instructions, Variable::Type from, Variable::Type to) {
if (from == to)
return;
switch (from) {
case Variable::Type::I32:
switch (to) {
case Variable::Type::I64:
return instructions.push_back({.type = Instruction::InstructionType::ConvertI32toI64});
default:
throw std::runtime_error("Invalid type cast");
}
default:
throw std::runtime_error("Invalid type cast");
}
}

0 comments on commit 1f3b51f

Please sign in to comment.