diff --git a/CMakeLists.txt b/CMakeLists.txt index 429baa6..718a11b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ add_library(lib${PROJECT_NAME} SHARED src/assembler/assembler.cc src/assembler/assembler.hh src/assembler/as_exceptions.hh + src/bytecode/bc_exceptions.hh ) target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings}) diff --git a/src/assembler/assembler.cc b/src/assembler/assembler.cc index e5b0792..f5fe59f 100644 --- a/src/assembler/assembler.cc +++ b/src/assembler/assembler.cc @@ -1,7 +1,10 @@ #include "assembler.hh" +#include + #include "as_exceptions.hh" #include "../bytecode/bytecode.hh" +#include "../vm/instruction.hh" using namespace std::string_literals; @@ -49,13 +52,25 @@ ByteArray Assembler::assemble() } else if (section == Section::Function && t.type == TokenType::Instruction) { std::string instruction = std::get(t.token); - int oper = 0; + std::optional oper = {}; Token tt = lexer_.ingest(); if (tt.type == TokenType::Integer) { oper = std::get(tt.token); tt = lexer_.ingest(); } - emit_instruction(function_id, instruction, oper); + + auto oinst = vm::translate_instruction(instruction, oper); + if (!oinst) + throw AssemblyError("Invalid or misused instruction '" + instruction + "'", tt.line, tt.column); + + bp.functions.at(function_id).code.append_byte((uint8_t) *oinst); + switch (vm::instruction_operand_type(*oinst)) { + case vm::OperandType::Int8: bp.functions.at(function_id).code.append_int8((int8_t) *oper); break; + case vm::OperandType::Int16: bp.functions.at(function_id).code.append_int16((int16_t) *oper); break; + case vm::OperandType::Int32: bp.functions.at(function_id).code.append_int32(*oper); break; + case vm::OperandType::NoOperand: default: + } + if (tt.type != TokenType::Enter) throw AssemblyError("Expected enter", tt.line, tt.column); @@ -70,11 +85,6 @@ ByteArray Assembler::assemble() return bc::Bytecode::generate(bp); } -void Assembler::emit_instruction(uint32_t function_id, std::string const& inst, int oper) -{ - -} - TokenValue Assembler::expect_token(TokenType type) { Token t = lexer_.ingest(); diff --git a/src/assembler/assembler.hh b/src/assembler/assembler.hh index 492c714..00f87e5 100644 --- a/src/assembler/assembler.hh +++ b/src/assembler/assembler.hh @@ -1,6 +1,7 @@ #ifndef TYCHE_ASSEMBLER_HH #define TYCHE_ASSEMBLER_HH +#include #include #include "lexer.hh" @@ -19,7 +20,6 @@ private: Lexer lexer_; TokenValue expect_token(TokenType type); - void emit_instruction(uint32_t function_id, std::string const& inst, int oper); }; } // tyche diff --git a/src/bytecode/bc_exceptions.hh b/src/bytecode/bc_exceptions.hh new file mode 100644 index 0000000..f5a83c3 --- /dev/null +++ b/src/bytecode/bc_exceptions.hh @@ -0,0 +1,15 @@ +#ifndef TYCHE_BC_EXCEPTIONS_HH +#define TYCHE_BC_EXCEPTIONS_HH + +#include + +namespace tyche::bc { + +class BytecodeParsingError : public std::runtime_error { +public: + explicit BytecodeParsingError(std::string const& str) : std::runtime_error(str.c_str()) {} +}; + +} + +#endif //TYCHE_BC_EXCEPTIONS_HH diff --git a/src/bytecode/bytecode.cc b/src/bytecode/bytecode.cc index 1dd8d02..996ba2e 100644 --- a/src/bytecode/bytecode.cc +++ b/src/bytecode/bytecode.cc @@ -1,4 +1,6 @@ #include "bytecode.hh" + +#include "bc_exceptions.hh" #include "../common/overloaded.hh" namespace tyche::bc { diff --git a/src/common/bytearray.hh b/src/common/bytearray.hh index 5f7a2fd..c20a011 100644 --- a/src/common/bytearray.hh +++ b/src/common/bytearray.hh @@ -53,11 +53,6 @@ private: std::vector data_ {}; }; -class BytecodeParsingError : public std::runtime_error { -public: - explicit BytecodeParsingError(std::string const& str) : std::runtime_error(str.c_str()) {} -}; - } #endif //TYCHE_BYTEARRAY_HH diff --git a/src/vm/instruction.cc b/src/vm/instruction.cc index 5508962..f719fb8 100644 --- a/src/vm/instruction.cc +++ b/src/vm/instruction.cc @@ -1,7 +1,62 @@ #include "instruction.hh" +#include +#include + namespace tyche::vm { +const std::unordered_map instruction_names = { + { "pushi", vm::Instruction::PushInt8 }, + { "pushc", vm::Instruction::PushConstant8 }, + { "pushz", vm::Instruction::PushZero }, + { "pusht", vm::Instruction::PushTrue }, + { "newa", vm::Instruction::NewArray }, + { "newt", vm::Instruction::NewTable }, + { "pop", vm::Instruction::Pop }, + { "dup", vm::Instruction::Duplicate }, + { "setl", vm::Instruction::SetLocal8 }, + { "getl", vm::Instruction::GetLocal8 }, + { "setg", vm::Instruction::SetGlobal8 }, + { "getl", vm::Instruction::GetGlobal8 }, + { "call8", vm::Instruction::Call8 }, + { "ret", vm::Instruction::Return }, + { "retn", vm::Instruction::ReturnNil }, + { "getkv", vm::Instruction::GetKeyValue }, + { "setkv", vm::Instruction::SetKeyValue }, + { "geta", vm::Instruction::GetArrayItem }, + { "seta", vm::Instruction::SetArrayItem }, + { "appnd", vm::Instruction::Append }, + { "next", vm::Instruction::Next }, + { "smt", vm::Instruction::SetMetatable }, + { "mt", vm::Instruction::GetMetatable }, + { "sum", vm::Instruction::Sum }, + { "sub", vm::Instruction::Subtract }, + { "mul", vm::Instruction::Multiply }, + { "div", vm::Instruction::Divide }, + { "idiv", vm::Instruction::DivideInt }, + { "eq", vm::Instruction::Equals }, + { "neq", vm::Instruction::NotEquals }, + { "lt", vm::Instruction::LessThan }, + { "lte", vm::Instruction::LessThanEq }, + { "gt", vm::Instruction::GreaterThan }, + { "gte", vm::Instruction::GreaterThanEq }, + { "and", vm::Instruction::And }, + { "or", vm::Instruction::Or }, + { "xor", vm::Instruction::Xor }, + { "len", vm::Instruction::Len }, + { "type", vm::Instruction::Type }, + { "cast", vm::Instruction::Cast }, + { "ver", vm::Instruction::Version }, + { "bz", vm::Instruction::BranchIfZero8 }, + { "bnz", vm::Instruction::BranchIfNotZero8 }, + { "jmp", vm::Instruction::Jump8 }, + { "cmpl", vm::Instruction::Compile }, + { "asmbl", vm::Instruction::Assemble }, + { "load", vm::Instruction::Load }, +}; + + + std::pair debug_instruction(Instruction inst, int oper) { std::string out; @@ -142,4 +197,27 @@ OperandType instruction_operand_type(Instruction inst) return OperandType::NoOperand; } +std::optional translate_instruction(std::string const& txt, std::optional op) +{ + auto it = instruction_names.find(txt); + if (it == instruction_names.end()) + return {}; + Instruction inst = it->second; + OperandType optype = instruction_operand_type(inst); + + if (optype == OperandType::NoOperand && op) + return {}; + if (optype != OperandType::NoOperand && !op) + return {}; + + if (optype == OperandType::NoOperand) + return inst; + + if (op >= std::numeric_limits::min() && op <= std::numeric_limits::max()) + return inst; + if (op >= std::numeric_limits::min() && op <= std::numeric_limits::max()) + return (Instruction) ((uint8_t) inst + OPCODE_NEXT_SIZE); + return (Instruction) ((uint8_t) inst + (OPCODE_NEXT_SIZE * 2)); +} + } \ No newline at end of file diff --git a/src/vm/instruction.hh b/src/vm/instruction.hh index 1710cdf..e87622a 100644 --- a/src/vm/instruction.hh +++ b/src/vm/instruction.hh @@ -2,6 +2,7 @@ #define TYCHE_INSTRUCTION_HH #include +#include #include #include @@ -9,6 +10,8 @@ namespace tyche::vm { +constexpr uint8_t OPCODE_NEXT_SIZE = 0x20; + enum class Instruction : uint8_t { // stack operations @@ -101,6 +104,8 @@ std::pair debug_instruction(bc::Bytecode const& bt, uint32_ enum class OperandType { NoOperand, Int8, Int16, Int32 }; OperandType instruction_operand_type(Instruction instruction); +std::optional translate_instruction(std::string const& txt, std::optional op); + } #endif //TYCHE_INSTRUCTION_HH