diff --git a/include/ChaiVM/utils/instr2Raw.hpp b/include/ChaiVM/utils/instr2Raw.hpp index 198a1da..80c97a2 100644 --- a/include/ChaiVM/utils/instr2Raw.hpp +++ b/include/ChaiVM/utils/instr2Raw.hpp @@ -17,8 +17,8 @@ chai::bytecode_t instr2Raw(interpreter::Operation op, chai::bytecode_t instr2Raw(interpreter::Operation op); -chai::bytecode_t inst2RawRI(interpreter::Operation op, - interpreter::RegisterId r1, - interpreter::Immidiate imm); +chai::bytecode_t instr2RawRI(interpreter::Operation op, + interpreter::RegisterId r1, + interpreter::Immidiate imm); } // namespace chai::utils diff --git a/include/frontend/assembler/asmlex.hpp b/include/frontend/assembler/asmlex.hpp index 29b1746..1923d1c 100644 --- a/include/frontend/assembler/asmlex.hpp +++ b/include/frontend/assembler/asmlex.hpp @@ -22,6 +22,7 @@ class AsmLex final : public yyFlexLexer { OP_CURLY_BRACKET, CL_CURLY_BRACKET, COMMA, + EOF_, UNKNOWN }; @@ -77,6 +78,11 @@ class AsmLex final : public yyFlexLexer { Comma() : Lexem(LexemType::COMMA) {} ~Comma() override {} }; + class Eof final : public Lexem { + public: + Eof(LexemType t) : Lexem(t) {} + ~Eof() override {} + }; class Unknown final : public Lexem { public: Unknown() : Lexem(LexemType::UNKNOWN) {} @@ -131,6 +137,10 @@ class AsmLex final : public yyFlexLexer { LexemType::STRING, withQuotes.substr(1, withQuotes.size() - 2)); return 0; } + int processEOF() { + currentLexem_ = std::make_unique(LexemType::EOF_); + return 0; + } int processUnknown() { currentLexem_ = std::make_unique(); return 1; diff --git a/include/frontend/assembler/assembler.hpp b/include/frontend/assembler/assembler.hpp index 955fc1c..d0ed066 100644 --- a/include/frontend/assembler/assembler.hpp +++ b/include/frontend/assembler/assembler.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "ChaiVM/interpreter/autogen/operations.hpp" #include "ChaiVM/utils/file-format/chai-file.hpp" @@ -30,6 +31,10 @@ class Assembler final { */ void assemble() { processMain(); + while (lex_.currentLexem()->type != AsmLex::EOF_) { + processFunction(); + lex_.nextLexem(); + } chaiFile_.toFile(outPath_); } @@ -50,15 +55,32 @@ class Assembler final { checkError(); chaiFile_.addInstr(processInstruction()); lex_.nextLexem(); - if (lex_.currentLexem()->type == AsmLex::IDENTIFIER && - SmartOperation( - static_cast(lex_.currentLexem().get()) - ->value) == chai::interpreter::Ret) { - chaiFile_.addInstr(processInstruction()); - break; - } } } + /* + * @todo #76:90min Add function description to assembler + */ + /* + * @todo #76:90min Add function to constant pool before parsing instructions (for recursion) + */ + void processFunction() { + expectCurrentLexem(AsmLex::FUNC, "Expected function declaration"); + expectNextLexem(AsmLex::IDENTIFIER, "Expected function name"); + std::string func_name = static_cast(lex_.currentLexem().get())->value; + expectNextLexem(AsmLex::INTEGER, "Expected number of registers in function frame"); + auto num_regs = static_cast(static_cast(lex_.currentLexem().get())->value); + expectNextLexem(AsmLex::INTEGER, "Expected number of function arguments"); + auto num_args = static_cast(static_cast(lex_.currentLexem().get())->value); + expectNextLexem(AsmLex::OP_CURLY_BRACKET, "Expected opening curly bracket"); + std::vector instrs; + lex_.nextLexem(); + while (lex_.currentLexem()->type != AsmLex::CL_CURLY_BRACKET) { + instrs.push_back(processInstruction()); + lex_.nextLexem(); + } + expectCurrentLexem(AsmLex::CL_CURLY_BRACKET, "Expected closing curly bracket"); + chaiFile_.addFunction(UINT16_MAX, func_name, "V(V)", instrs, num_args, num_regs); + } chai::bytecode_t processInstruction() { SmartOperation op = SmartOperation( static_cast(lex_.currentLexem().get()) @@ -66,6 +88,9 @@ class Assembler final { if (op == chai::interpreter::Inv) { throw AssembleError("Invalid instruction", lex_.lineno()); } + if (op == chai::interpreter::Call) { + return processCall(op); + } switch (op.format()) { case chai::interpreter::N: return processN(op); @@ -85,6 +110,12 @@ class Assembler final { break; } } + chai::bytecode_t processCall(chai::interpreter::Operation op) { + auto val = static_cast( + static_cast(lex_.nextLexem().get())->value); + std::cout << "[Call]: val = " << val << std::endl; + return chai::utils::instr2RawRI(op, val, val); + } chai::bytecode_t processN(chai::interpreter::Operation op) { return chai::utils::instr2Raw(op, 0, 0); } @@ -94,29 +125,30 @@ class Assembler final { } chai::bytecode_t processRR(chai::interpreter::Operation op) { chai::interpreter::RegisterId reg1Id = processReg(); - expectComma(); + expectNextLexem(AsmLex::COMMA, "Expected coma"); chai::interpreter::RegisterId reg2Id = processReg(); return chai::utils::instr2Raw(op, reg1Id, reg2Id); } chai::bytecode_t processI(chai::interpreter::Operation op) { lex_.nextLexem(); if (lex_.currentLexem()->type == AsmLex::INTEGER) { - return chaiFile_.getWithConst( - op, static_cast( - static_cast(lex_.currentLexem().get()) - ->value)); + auto val = static_cast( + static_cast(lex_.currentLexem().get())->value); + auto imm = chaiFile_.addConst( + std::make_unique(val)); + return chai::utils::instr2Raw(op, imm); } else if (lex_.currentLexem()->type == AsmLex::FLOAT) { - return chaiFile_.getWithConst( - op, (static_cast(lex_.currentLexem().get()) - ->value)); + auto val = + static_cast(lex_.currentLexem().get())->value; + auto imm = chaiFile_.addConst( + std::make_unique(val)); + return chai::utils::instr2Raw(op, imm); } else if (lex_.currentLexem()->type == AsmLex::STRING) { std::string str = static_cast(lex_.currentLexem().get())->value; auto imm = chaiFile_.addConst( std::make_unique(str)); - auto bytecode = chai::utils::instr2Raw(op, imm); - chaiFile_.addInstr(bytecode); - return bytecode; + return chai::utils::instr2Raw(op, imm); } else { throw AssembleError("Unknown instruction type in ProcessI", lex_.lineno()); @@ -124,26 +156,26 @@ class Assembler final { } chai::bytecode_t processRI(chai::interpreter::Operation op) { chai::interpreter::RegisterId regId = processReg(); - expectComma(); + expectNextLexem(AsmLex::COMMA, "Expected coma"); lex_.nextLexem(); if (lex_.currentLexem()->type == AsmLex::INTEGER) { - return chai::utils::inst2RawRI( - op, regId, - static_cast( - static_cast(lex_.currentLexem().get()) - ->value)); + auto val = static_cast( + static_cast(lex_.currentLexem().get())->value); + auto imm = chaiFile_.addConst( + std::make_unique(val)); + return chai::utils::instr2RawRI(op, regId, imm); } else if (lex_.currentLexem()->type == AsmLex::FLOAT) { - return chai::utils::inst2RawRI( - op, regId, - static_cast(lex_.currentLexem().get())->value); + auto val = + static_cast(lex_.currentLexem().get())->value; + auto imm = chaiFile_.addConst( + std::make_unique(val)); + return chai::utils::instr2RawRI(op, regId, imm); } else if (lex_.currentLexem()->type == AsmLex::STRING) { std::string str = static_cast(lex_.currentLexem().get())->value; auto imm = chaiFile_.addConst( std::make_unique(str)); - auto bytecode = chai::utils::inst2RawRI(op, regId, imm); - chaiFile_.addInstr(bytecode); - return bytecode; + return chai::utils::instr2RawRI(op, regId, imm); } else { throw AssembleError("Unknown instruction type in processRI", lex_.lineno()); @@ -151,10 +183,7 @@ class Assembler final { } chai::interpreter::RegisterId processReg() { - lex_.nextLexem(); - if (lex_.currentLexem()->type != AsmLex::IDENTIFIER) { - throw AssembleError("Expected register", lex_.lineno()); - } + expectNextLexem(AsmLex::IDENTIFIER, "Expected register name"); return regNameToRegId( static_cast(lex_.currentLexem().get()) ->value); @@ -165,10 +194,15 @@ class Assembler final { throw AssembleError("Unknown lexem", lex_.lineno()); } } - void expectComma() { + void expectNextLexem(AsmLex::LexemType type, std::string msg) { lex_.nextLexem(); - if (lex_.currentLexem()->type != AsmLex::COMMA) { - throw AssembleError("Expected comma", lex_.lineno()); + if (lex_.currentLexem()->type != type) { + throw AssembleError(std::move(msg), lex_.lineno()); + } + } + void expectCurrentLexem(AsmLex::LexemType type, std::string msg) { + if (lex_.currentLexem()->type != type) { + throw AssembleError(std::move(msg), lex_.lineno()); } } chai::interpreter::RegisterId regNameToRegId(std::string regName) { diff --git a/src/ChaiVM/utils/instr2Raw.cpp b/src/ChaiVM/utils/instr2Raw.cpp index 2a6edff..872b1a3 100644 --- a/src/ChaiVM/utils/instr2Raw.cpp +++ b/src/ChaiVM/utils/instr2Raw.cpp @@ -19,7 +19,7 @@ chai::bytecode_t instr2Raw(Operation op, Immidiate imm) { chai::bytecode_t instr2Raw(Operation op) { return (operation2opcode(op)); } -chai::bytecode_t inst2RawRI(Operation op, RegisterId r1, Immidiate imm) { +chai::bytecode_t instr2RawRI(Operation op, RegisterId r1, Immidiate imm) { return (operation2opcode(op)) | (static_cast(imm) << 16) | (r1 << 8); } diff --git a/src/frontend/assembler/asmlex.l b/src/frontend/assembler/asmlex.l index 0bbd989..cc4b966 100644 --- a/src/frontend/assembler/asmlex.l +++ b/src/frontend/assembler/asmlex.l @@ -23,12 +23,13 @@ COMMA , {COMMENT} /* skip */ {INT} return processInt(); {FLOAT} return processFloat(); -{IDENTIFIER} return processIdentifier(); "fn" return processFunc(); "{" return processOpCurlyBracket(); "}" return processClCurlyBracket(); {COMMA} return processComma(); +{IDENTIFIER} return processIdentifier(); {STRING} return processString(); +<> return processEOF(); . return processUnknown(); %% diff --git a/test/ChaiVM/interpreter/executor-test-fixture.cpp b/test/ChaiVM/interpreter/executor-test-fixture.cpp index 8fd4ff8..cf29c54 100644 --- a/test/ChaiVM/interpreter/executor-test-fixture.cpp +++ b/test/ChaiVM/interpreter/executor-test-fixture.cpp @@ -12,7 +12,7 @@ Immidiate ExecutorTest::loadRR(chai::interpreter::Operation op, Immidiate ExecutorTest::loadRI(chai::interpreter::Operation op, chai::interpreter::RegisterId reg1, chai::interpreter::Immidiate imm) { - return chaiFile_.addInstr(chai::utils::inst2RawRI(op, reg1, imm)); + return chaiFile_.addInstr(chai::utils::instr2RawRI(op, reg1, imm)); } Immidiate ExecutorTest::loadI(chai::interpreter::Operation op, diff --git a/test/ChaiVM/interpreter/executor_test.cpp b/test/ChaiVM/interpreter/executor_test.cpp index 4599aab..7bdae5d 100644 --- a/test/ChaiVM/interpreter/executor_test.cpp +++ b/test/ChaiVM/interpreter/executor_test.cpp @@ -1,7 +1,6 @@ #include "executor-test-fixture.hpp" using chai::bytecode_t; -using chai::utils::inst2RawRI; using chai::utils::instr2Raw; using namespace chai::interpreter; using namespace chai::utils::fileformat; diff --git a/test/ChaiVM/interpreter/simple-programs-executor-test.cpp b/test/ChaiVM/interpreter/simple-programs-executor-test.cpp index 4c7a790..8c05775 100644 --- a/test/ChaiVM/interpreter/simple-programs-executor-test.cpp +++ b/test/ChaiVM/interpreter/simple-programs-executor-test.cpp @@ -1,8 +1,8 @@ #include "executor-test-fixture.hpp" using chai::bytecode_t; -using chai::utils::inst2RawRI; using chai::utils::instr2Raw; +using chai::utils::instr2RawRI; using namespace chai::interpreter; using namespace chai::utils::fileformat; @@ -82,8 +82,8 @@ TEST_F(ExecutorTest, Factorial) { UINT16_MAX, "factorial", "(I)I", std::vector{ instr2Raw(Ldra, 7, 0), // val2 - inst2RawRI(If_icmpne, one, - static_cast(3 * sizeof(bytecode_t))), + instr2RawRI(If_icmpne, one, + static_cast(3 * sizeof(bytecode_t))), instr2Raw(Ldia, one), instr2Raw(Ret), instr2Raw(Star, 2, 0), instr2Raw(Subi, one), instr2Raw(Star, 7, 0), instr2Raw(Call, func_ref), instr2Raw(Mul, 2, 0), diff --git a/test/frontend/assembler/assembler-test.cpp b/test/frontend/assembler/assembler-test.cpp index 1a7d2f2..e90f643 100644 --- a/test/frontend/assembler/assembler-test.cpp +++ b/test/frontend/assembler/assembler-test.cpp @@ -43,3 +43,19 @@ TEST_F(AssemblerTest, runWithStrings) { exec_.run(); EXPECT_EQ(codeManager_.getCnstString(exec_.acc()), "Hello world"); } + +TEST_F(AssemblerTest, runWithFunctions) { + write_input_ << "Ldia 271\n" + << "Call 8\n" + << "Ret\n" + << "fn aboba_func 50 2 {\n" + << "Ldia 125\n" + << "Ret\n" + << "}\n" << std::endl; + Assembler asM{input_, output_}; + asM.assemble(); + codeManager_.load(output_); + exec_.run(); + EXPECT_EQ(static_cast(exec_.acc()), 125); + EXPECT_EQ(exec_.getCurrentFrame(), nullptr); +} diff --git a/test/frontend/assembler/run.chai b/test/frontend/assembler/run.chai deleted file mode 100644 index ee770d1..0000000 --- a/test/frontend/assembler/run.chai +++ /dev/null @@ -1,7 +0,0 @@ -Ldia 6 -Star r2 -Ldia 8 -Star r3 -Ldra r3 -Mul r2 -Ret