diff --git a/CMakeLists.txt b/CMakeLists.txt index 33e2610..4de271e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(SPL) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -add_compile_options(-O0) +add_compile_options(-O3) file(GLOB_RECURSE TEST_SOURCES tests/*.cpp) file(GLOB_RECURSE SOURCES src/*.cpp) @@ -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) \ No newline at end of file diff --git a/include/utils.h b/include/utils.h index 18b3efb..55b9f62 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,6 +1,8 @@ #pragma once +#include "ast.h" #include "vm.h" +#include #define GENERATE_EMIT_FUNCTION(OPERATION) \ static inline void emit##OPERATION(Program &program, Segment &segment, const std::string &identifier) { \ @@ -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(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, \ @@ -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(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(ast); - return deduceType(program, segment, unary->expression); - } - case AbstractSyntaxTree::Type::BinaryExpression: { - auto binary = dynamic_cast(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(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, @@ -156,29 +85,9 @@ 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); +Instruction emitLoad(Variable::Type, const Token &token); +void typeCast(std::vector &instructions, Variable::Type from, Variable::Type to); \ No newline at end of file diff --git a/src/ast.cpp b/src/ast.cpp index c7d12bb..a43a793 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -1,14 +1,8 @@ #include "ast.h" #include "utils.h" -#include #include -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"; @@ -21,22 +15,7 @@ void Node::compile(Program &program, Segment &segment) const { switch (token.type) { case Number: { auto type = deduceType(program, segment, (AbstractSyntaxTree *) this); - switch (type) { - case Variable::Type::I32: - return segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::LoadI32, - .params = {.i32 = std::stoi(token.value)}, - }); - case Variable::Type::I64: - return segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::LoadI64, - .params = {.i64 = std::stol(token.value)}, - }); - default: - throw std::runtime_error("[Node::compile] Invalid type: " + token.value); - } + return segment.instructions.push_back(emitLoad(type, token)); } case Identifier: { return emitLoad(program, segment, token.value); @@ -69,28 +48,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)); diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..ec9eff1 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,140 @@ +#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(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(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(ast); + return deduceType(program, segment, unary->expression); + } + case AbstractSyntaxTree::Type::BinaryExpression: { + auto binary = dynamic_cast(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(ast); + auto function = program.find_function(segment, 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) + } +} + +Instruction emitLoad(Variable::Type type, const Token &token) { + switch (type) { + case Variable::Type::I32: + return Instruction{ + .type = Instruction::InstructionType::LoadI32, + .params = {.i32 = std::stoi(token.value)}, + }; + case Variable::Type::I64: + return Instruction{ + .type = Instruction::InstructionType::LoadI64, + .params = {.i64 = std::stol(token.value)}, + }; + default: + throw std::runtime_error("Invalid type: " + token.value); + } +} + +void typeCast(std::vector &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"); + } +}