From 6ba49b30cf1e33fb0dd6782a404ec34b7964cb59 Mon Sep 17 00:00:00 2001 From: Martin Dobrev Date: Tue, 24 Nov 2020 18:06:26 +0200 Subject: Feature/object getters (#101) * Add member access expression. * Implement property getters. * Add test for property getters. * Add test for property setter. * Implement while and do-while Signed-off-by: Martin Dobrev * Add implement byte code generation for break/continue in for/while. --- JSImpl/src/ASTInterpreter.cpp | 9 +- JSImpl/src/ASTInterpreter.h | 2 +- JSImpl/src/ASTPrinter.cpp | 7 +- JSImpl/src/ByteCodeGenerator.cpp | 119 ++++++++++++++++ JSImpl/src/ExpressionDefinitions.h | 14 +- JSImpl/src/ExpressionVisitor.h | 6 +- JSImpl/src/ExpressionsFwd.h | 5 +- JSImpl/src/Parser.cpp | 47 ++++--- JSImpl/src/main.cpp | 20 +-- JSImpl/test/CodeGenerationTests.cpp | 272 +++++++++++++++++++++++++++++++++++- 10 files changed, 463 insertions(+), 38 deletions(-) (limited to 'JSImpl') diff --git a/JSImpl/src/ASTInterpreter.cpp b/JSImpl/src/ASTInterpreter.cpp index f550da9..f56706a 100644 --- a/JSImpl/src/ASTInterpreter.cpp +++ b/JSImpl/src/ASTInterpreter.cpp @@ -240,6 +240,7 @@ public: } virtual void Visit(IdentifierExpression* e) override { m_LValue = m_Interpreter->ModifyVariable(e->GetName()); } + virtual void Visit(Call* e) override { e->GetObjectOrCall()->Accept(*this); } ASTInterpreter* m_Interpreter; ASTInterpreter::ValuePtr m_LValue; @@ -565,9 +566,13 @@ void ASTInterpreter::Visit(EmptyExpression* e) { NOT_IMPLEMENTED; } -void ASTInterpreter::Visit(CallExpression* e) +void ASTInterpreter::Visit(Call* e) { - NOT_IMPLEMENTED; + e->GetObjectOrCall()->Accept(*this); + if (e->GetMember()) + { + e->GetMember()->Accept(*this); + } } void ASTInterpreter::Print(Printer& p) diff --git a/JSImpl/src/ASTInterpreter.h b/JSImpl/src/ASTInterpreter.h index cce6108..0c2c776 100644 --- a/JSImpl/src/ASTInterpreter.h +++ b/JSImpl/src/ASTInterpreter.h @@ -38,7 +38,7 @@ public: virtual void Visit(FunctionDeclaration* e) override; virtual void Visit(TopStatements* e) override; virtual void Visit(EmptyExpression* e) override; - virtual void Visit(CallExpression* e) override; + virtual void Visit(Call* e) override; ValuePtr& ModifyVariable(const IPLString& name); bool HasVariable(const IPLString& name); diff --git a/JSImpl/src/ASTPrinter.cpp b/JSImpl/src/ASTPrinter.cpp index 8c5783f..4a05510 100644 --- a/JSImpl/src/ASTPrinter.cpp +++ b/JSImpl/src/ASTPrinter.cpp @@ -126,11 +126,14 @@ public: virtual void Visit(CaseStatement *) override; virtual void Visit(WhileStatement* e) override; virtual void Visit(ForStatement* e) override; + virtual void Visit(Break* e) override; + virtual void Visit(Continue* e) override; virtual void Visit(FunctionDeclaration* e) override; virtual void Visit(TopStatements* e) override; virtual void Visit(EmptyExpression* e) override; - virtual void Visit(CallExpression* e) override; - + virtual void Visit(Call* e) override; + virtual void Visit(MemberAccess* e) override; + void Print( const char* name, const ExpressionPtr& expr, bool isLast); void Print( const char* name, const IPLVector& list, bool isLast); void Print( const char* name, const IPLVector& list, bool isLast); diff --git a/JSImpl/src/ByteCodeGenerator.cpp b/JSImpl/src/ByteCodeGenerator.cpp index 31a80bd..4d5cb09 100644 --- a/JSImpl/src/ByteCodeGenerator.cpp +++ b/JSImpl/src/ByteCodeGenerator.cpp @@ -22,10 +22,15 @@ public: virtual void Visit(EmptyExpression* e) override { (void)e; } virtual void Visit(IfStatement* e) override; virtual void Visit(ForStatement* e) override; + virtual void Visit(WhileStatement* e) override; + virtual void Visit(Break* e) override; + virtual void Visit(Continue* e) override; virtual void Visit(UnaryExpression* e) override; virtual void Visit(LiteralField* e) override; virtual void Visit(LiteralObject* e) override; virtual void Visit(LiteralString* e) override; + virtual void Visit(MemberAccess* e) override; + virtual void Visit(Call* e) override; IPLString GetCode(); unsigned ResolveRegisterName(IPLString& name); @@ -101,16 +106,62 @@ private: void PushConst(double c); IPLString CreateRegister(); bool CheckOpCode(int opcode) { return opcode >= Instruction::Type::FIRST && opcode <= Instruction::Type::LAST; } + + void OpenBreakScope() { m_BreakScopeStack.push(IPLVector()); }; + void OpenContinueScope() { m_ContinueScopeStack.push(IPLVector()); }; + void CloseBreakScope(size_t addr); + void CloseContinueScope(size_t addr); + void PushBreak(); + void PushContinue(); private: IPLVector m_RegisterTable; IPLVector m_Code; IPLVector m_Source; + IPLStack> m_BreakScopeStack; + IPLStack> m_ContinueScopeStack; + IPLStack m_RegisterStack; IPLString m_OutputCode; ByteCodeGeneratorOptions m_Options; }; +void ByteCodeGenerator::PushBreak() +{ + assert(!m_BreakScopeStack.empty()); + auto addr = PushInstruction(Instruction::Type::JMP, 0); + m_BreakScopeStack.top().push_back(addr); +} + +void ByteCodeGenerator::PushContinue() +{ + assert(!m_ContinueScopeStack.empty()); + auto addr = PushInstruction(Instruction::Type::JMP, 0); + m_ContinueScopeStack.top().push_back(addr); +} + +void ByteCodeGenerator::CloseBreakScope(size_t addr) +{ + auto currentScope = m_BreakScopeStack.top(); + for (const auto& br : currentScope) + { + assert(m_Code[br].Descriptor == Instruction::Type::JMP); + m_Code[br].Values.Address[0] = addr; + } + m_BreakScopeStack.pop(); +}; + +void ByteCodeGenerator::CloseContinueScope(size_t addr) +{ + auto currentScope = m_ContinueScopeStack.top(); + for (const auto& br : currentScope) + { + assert(m_Code[br].Descriptor == Instruction::Type::JMP); + m_Code[br].Values.Address[0] = addr; + } + m_ContinueScopeStack.pop(); +}; + size_t ByteCodeGenerator::PushInstruction(Instruction::Type opcode, const IPLString& arg0, const IPLString& arg1, const IPLString& arg2) { assert(CheckOpCode(opcode)); @@ -337,15 +388,60 @@ void ByteCodeGenerator::Visit(IfStatement* e) void ByteCodeGenerator::Visit(ForStatement* e) { + OpenBreakScope(); + OpenContinueScope(); + e->GetInitialization()->Accept(*this); auto compareAddress = m_Code.size(); e->GetCondition()->Accept(*this); auto endAddress = PushInstruction(Instruction::Type::JMPF, m_RegisterStack.top(), (size_t)0); m_RegisterStack.pop(); e->GetBody()->Accept(*this); + + auto iterationAddress = m_Code.size(); e->GetIteration()->Accept(*this); + PushInstruction(Instruction::Type::JMP, compareAddress); m_Code[endAddress].Values.Address[0] = m_Code.size(); + + CloseBreakScope(m_Code.size()); + CloseContinueScope(iterationAddress); +} + +void ByteCodeGenerator::Visit(WhileStatement* e) +{ + auto startAddress = m_Code.size(); + + OpenBreakScope(); + OpenContinueScope(); + + if (e->GetDoWhile()) + { + e->GetBody()->Accept(*this); + e->GetCondition()->Accept(*this); + PushInstruction(Instruction::Type::JMPT, m_RegisterStack.top(), startAddress); + } + else { + e->GetCondition()->Accept(*this); + auto endAddress = PushInstruction(Instruction::Type::JMPF, m_RegisterStack.top(), (size_t)0); + m_RegisterStack.pop(); + e->GetBody()->Accept(*this); + PushInstruction(Instruction::Type::JMP, startAddress); + m_Code[endAddress].Values.Address[0] = m_Code.size(); + } + + CloseBreakScope(m_Code.size()); + CloseContinueScope(startAddress); +} + +void ByteCodeGenerator::Visit(Break*) +{ + PushBreak(); +} + +void ByteCodeGenerator::Visit(Continue*) +{ + PushContinue(); } void ByteCodeGenerator::Visit(IdentifierExpression* e) @@ -485,6 +581,29 @@ void ByteCodeGenerator::Visit(LiteralString* e) m_RegisterStack.push(stringReg); } +void ByteCodeGenerator::Visit(Call* e) +{ + e->GetObjectOrCall()->Accept(*this); + if (e->GetMember()) + { + e->GetMember()->Accept(*this); + } +} + +void ByteCodeGenerator::Visit(MemberAccess* e) +{ + auto objectReg = m_RegisterStack.top(); + m_RegisterStack.pop(); + + auto identifierNameReg = CreateRegister(); + PushInstruction(Instruction::Type::STRING, identifierNameReg, e->GetName()); + + auto resultReg = CreateRegister(); + PushInstruction(Instruction::Type::GET, objectReg, identifierNameReg, resultReg); + + m_RegisterStack.push(resultReg); +} + unsigned ByteCodeGenerator::ResolveRegisterName(IPLString& name) { auto it = std::find_if(m_RegisterTable.begin(), m_RegisterTable.end(), [&](IPLString& current) { diff --git a/JSImpl/src/ExpressionDefinitions.h b/JSImpl/src/ExpressionDefinitions.h index 1fad948..33f13b3 100644 --- a/JSImpl/src/ExpressionDefinitions.h +++ b/JSImpl/src/ExpressionDefinitions.h @@ -79,8 +79,13 @@ MACRO(IPLVector, Values, IPLVector()) #define CALL_EXPRESSION_MEMBERS(MACRO)\ - MACRO(ExpressionPtr, Identifier, ExpressionPtr())\ - MACRO(ExpressionPtr, Arguments, ExpressionPtr()) + MACRO(ExpressionPtr, ObjectOrCall, ExpressionPtr())\ + MACRO(ExpressionPtr, Member, ExpressionPtr()) + +#define MEMBER_ACCESS_EXPRESSION_MEMBERS(MACRO)\ + MACRO(IPLString, Name, "") + +#define MEMBER_NO_MEMBERS(MACRO) #define EXPRESSION_DEFINITION_ITERATOR(MACRO)\ MACRO(LiteralNull, NO_MEMBERS)\ @@ -105,4 +110,7 @@ MACRO(FunctionDeclaration, FUNCTION_EXPRESSION_MEMBERS)\ MACRO(TopStatements, TOP_EXPRESSION_MEMBERS)\ MACRO(EmptyExpression, NO_MEMBERS)\ - MACRO(CallExpression, CALL_EXPRESSION_MEMBERS) + MACRO(Call, CALL_EXPRESSION_MEMBERS)\ + MACRO(MemberAccess, MEMBER_ACCESS_EXPRESSION_MEMBERS)\ + MACRO(Break, MEMBER_NO_MEMBERS)\ + MACRO(Continue, MEMBER_NO_MEMBERS) diff --git a/JSImpl/src/ExpressionVisitor.h b/JSImpl/src/ExpressionVisitor.h index a44f581..8fbab7d 100644 --- a/JSImpl/src/ExpressionVisitor.h +++ b/JSImpl/src/ExpressionVisitor.h @@ -26,10 +26,14 @@ public: virtual void Visit(CaseStatement *e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(WhileStatement* e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(ForStatement* e) { (void)e; NOT_IMPLEMENTED; } + virtual void Visit(Break* e) { (void)e; NOT_IMPLEMENTED; } + virtual void Visit(Continue* e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(FunctionDeclaration* e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(TopStatements* e) { (void)e; NOT_IMPLEMENTED; } virtual void Visit(EmptyExpression* e) { (void)e; NOT_IMPLEMENTED; } - virtual void Visit(CallExpression* e) { (void)e; NOT_IMPLEMENTED; } + virtual void Visit(Call* e) { (void)e; NOT_IMPLEMENTED; } + virtual void Visit(MemberAccess* e) { (void)e; NOT_IMPLEMENTED; } + virtual void Visit(Expression* e) { (void)e; NOT_IMPLEMENTED; } // Unkown case }; \ No newline at end of file diff --git a/JSImpl/src/ExpressionsFwd.h b/JSImpl/src/ExpressionsFwd.h index 38f9330..753167d 100644 --- a/JSImpl/src/ExpressionsFwd.h +++ b/JSImpl/src/ExpressionsFwd.h @@ -25,6 +25,9 @@ class ForStatement; class FunctionDeclaration; class TopStatements; class EmptyExpression; -class CallExpression; +class Call; +class MemberAccess; +class Break; +class Continue; using ExpressionPtr = IPLSharedPtr; diff --git a/JSImpl/src/Parser.cpp b/JSImpl/src/Parser.cpp index bc4920f..4bc97ce 100644 --- a/JSImpl/src/Parser.cpp +++ b/JSImpl/src/Parser.cpp @@ -23,6 +23,7 @@ private: bool MatchOneOf(IPLVector types); bool Match(TokenType type); + bool Peek(TokenType type); ExpressionPtr RegularExpression(); ExpressionPtr ParenthesizedExpression(); ExpressionPtr PrimaryExpression(NormalType a); @@ -125,6 +126,11 @@ bool Parser::Match(TokenType type) return false; } +bool Parser::Peek(TokenType type) +{ + return type == m_Tokens[m_Current].Type; +} + ExpressionPtr Parser::RegularExpression() { return nullptr; @@ -383,23 +389,32 @@ ExpressionPtr Parser::LeftSideExpression(NormalType a) ExpressionPtr Parser::CallExpression(NormalType a) { ExpressionPtr result; + auto ss = Snapshot(); - if (result = PrimaryExpression(a)) + if (auto pe = PrimaryExpression(a)) { - return result; + result = IPLMakeSharePtr(pe); } - Restore(ss); - if (result = FullNewExpression()) + else { - return result; + Restore(ss); } - - if (result = CallExpressionHelper(a)) + if (auto fne = FullNewExpression()) { - return result; + result = IPLMakeSharePtr<::Call>(fne); } - // TODO error - return nullptr; + + ExpressionPtr right; + do + { + right = CallExpressionHelper(a); + if (right) + { + result = IPLMakeSharePtr<::Call>(result, right); + } + } while (right); + + return result; } ExpressionPtr Parser::CallExpressionHelper(NormalType a) @@ -420,9 +435,7 @@ ExpressionPtr Parser::CallExpressionHelper(NormalType a) { if (Match(TokenType::Identifier)) { - auto next = CallExpressionHelper(a); - // return meaningfull expr - return nullptr; + return IPLMakeSharePtr(Prev().Lexeme); } // TODO error return nullptr; @@ -991,10 +1004,8 @@ ExpressionPtr Parser::ContinueStatement() { if (Match(TokenType::Continue)) { - auto type = TokenType::Continue; ExpressionPtr expr = OptionalLabel(); - bool suffix = true; - return IPLMakeSharePtr(expr, type, suffix); + return IPLMakeSharePtr(); } return nullptr; } @@ -1003,10 +1014,8 @@ ExpressionPtr Parser::BreakStatement() { if (Match(TokenType::Break)) { - auto type = TokenType::Break; ExpressionPtr expr = OptionalLabel(); - bool suffix = true; - return IPLMakeSharePtr(expr, type, suffix); + return IPLMakeSharePtr(); } return nullptr; } diff --git a/JSImpl/src/main.cpp b/JSImpl/src/main.cpp index 06421a7..aee465d 100644 --- a/JSImpl/src/main.cpp +++ b/JSImpl/src/main.cpp @@ -76,15 +76,16 @@ void Generate() { //Tokenize("var a = 0; var b = a + 123;a+b;", tokens); /*Tokenize("var a; if(a < 4) { a = 8; if(a < 7) {var b = 4;}} else { a = 2} ", tokens);*/ - IPLString source = "var i = 0; \n" - "for (var j = 0; j < 10; j++)\n" - "{\n" - " i = i + j;\n" - "}"; + //IPLString source = "var i = 0; \n" + // "for (var j = 0; j < 10; j++)\n" + // "{\n" + // " i = i + j;\n" + // "}"; + IPLString source = "var a = 5; do{ a--; if(a == 4) continue; }while(a != 0)\n"; auto tokens = Tokenize(source.c_str()).tokens; auto asmb = GenerateByteCode(Parse(tokens), source, - ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, true)); + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); //sets the color to intense red on blue background #if defined(_WIN32) @@ -231,13 +232,16 @@ private: int main(int argc, char* argv[]) { + (void)argv; + (void)argc; // You can try => // Working dir: $(ProjectDir) // Command arguments: --input ./../examples/code.js --output ./../examples/some.ast --ast - CammandLineApp cmd; - cmd.RunAsCommandLine(argc, argv); + //CammandLineApp cmd; + //cmd.RunAsCommandLine(argc, argv); //auto r = Tokenize("a#4", {true ,true}); //RunParseCalc(); + Generate(); #if defined(_WIN32) //std::system("pause"); #endif diff --git a/JSImpl/test/CodeGenerationTests.cpp b/JSImpl/test/CodeGenerationTests.cpp index 4a8ebbe..dd13855 100644 --- a/JSImpl/test/CodeGenerationTests.cpp +++ b/JSImpl/test/CodeGenerationTests.cpp @@ -235,4 +235,274 @@ TEST(CodeGen, NestedObjects) "17: halt\n"; ASSERT_TRUE(asmb == expected); -} \ No newline at end of file +} + + +TEST(CodeGen, ObjectGetters) +{ + IPLString source = "var a = {c: 3}; var k = a.c.t;\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 9\n" + "1: const r2 3.000000\n" + "2: string r3 c\n" + "3: set r1 r3 r2\n" + "4: mov r0 r1\n" + "5: string r5 c\n" + "6: get r0 r5 r6\n" + "7: string r7 t\n" + "8: get r6 r7 r8\n" + "9: mov r4 r8\n" + "10: pop 9\n" + "11: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + +TEST(CodeGen, ObjectSetter) +{ + IPLString source = "var a = {c: 3}; a.c = 5;\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 7\n" + "1: const r2 3.000000\n" + "2: string r3 c\n" + "3: set r1 r3 r2\n" + "4: mov r0 r1\n" + "5: const r4 5.000000\n" + "6: string r5 c\n" + "7: get r0 r5 r6\n" + "8: mov r6 r4\n" + "9: pop 7\n" + "10: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + + +TEST(CodeGen, BasicDoWhile) +{ + IPLString source = "var a = 5; do{ a--; }while(a != 0)\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 6\n" + "1: const r1 5.000000\n" + "2: mov r0 r1\n" + "3: const r2 1.000000\n" + "4: mov r3 r0\n" + "5: sub r0 r0 r2\n" + "6: const r4 0.000000\n" + "7: neq r5 r0 r4\n" + "8: jmpt r5 3\n" + "9: pop 6\n" + "10: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + + +TEST(CodeGen, BasicWhile) +{ + IPLString source = "var a = 5; while(a != 0){ a--; }\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 6\n" + "1: const r1 5.000000\n" + "2: mov r0 r1\n" + "3: const r2 0.000000\n" + "4: neq r3 r0 r2\n" + "5: jmpf r3 10\n" + "6: const r4 1.000000\n" + "7: mov r5 r0\n" + "8: sub r0 r0 r4\n" + "9: jmp 3\n" + "10: pop 6\n" + "11: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + +TEST(CodeGen, ContinueDoWhile) +{ + IPLString source = "var a = 5;\n" + "do {\n" + " a--;\n" + " if(a == 4) continue;\n" + "}while(a != 0)\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 8\n" + "1: const r1 5.000000\n" + "2: mov r0 r1\n" + "3: const r2 1.000000\n" + "4: mov r3 r0\n" + "5: sub r0 r0 r2\n" + "6: const r4 4.000000\n" + "7: eq r5 r0 r4\n" + "8: jmpf r5 10\n" + "9: jmp 3\n" + "10: const r6 0.000000\n" + "11: neq r7 r0 r6\n" + "12: jmpt r7 3\n" + "13: pop 8\n" + "14: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + +TEST(CodeGen, BreakDoWhile) +{ + IPLString source = "var a = 5;\n" + "do {\n" + " a--;\n" + " if(a == 4) break;\n" + "}while(a != 0)\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 8\n" + "1: const r1 5.000000\n" + "2: mov r0 r1\n" + "3: const r2 1.000000\n" + "4: mov r3 r0\n" + "5: sub r0 r0 r2\n" + "6: const r4 4.000000\n" + "7: eq r5 r0 r4\n" + "8: jmpf r5 10\n" + "9: jmp 13\n" + "10: const r6 0.000000\n" + "11: neq r7 r0 r6\n" + "12: jmpt r7 3\n" + "13: pop 8\n" + "14: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + + +TEST(CodeGen, BreakWhile) +{ + IPLString source = "var a = 5;\n" + "while(a != 0) {\n" + " a--;\n" + " if(a == 4) break;\n" + "}\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 8\n" + "1: const r1 5.000000\n" + "2: mov r0 r1\n" + "3: const r2 0.000000\n" + "4: neq r3 r0 r2\n" + "5: jmpf r3 14\n" + "6: const r4 1.000000\n" + "7: mov r5 r0\n" + "8: sub r0 r0 r4\n" + "9: const r6 4.000000\n" + "10: eq r7 r0 r6\n" + "11: jmpf r7 13\n" + "12: jmp 14\n" + "13: jmp 3\n" + "14: pop 8\n" + "15: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + +TEST(CodeGen, ContinueWhile) +{ + IPLString source = "var a = 5;\n" + "while(a != 0) {\n" + " a--;\n" + " if(a == 4) continue;\n" + "}\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 8\n" + "1: const r1 5.000000\n" + "2: mov r0 r1\n" + "3: const r2 0.000000\n" + "4: neq r3 r0 r2\n" + "5: jmpf r3 14\n" + "6: const r4 1.000000\n" + "7: mov r5 r0\n" + "8: sub r0 r0 r4\n" + "9: const r6 4.000000\n" + "10: eq r7 r0 r6\n" + "11: jmpf r7 13\n" + "12: jmp 3\n" + "13: jmp 3\n" + "14: pop 8\n" + "15: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + +TEST(CodeGen, ContinueFor) +{ + IPLString source = "for (var i = 0; i < 5; i++ ){ if(i == 1) continue; }\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 8\n" + "1: const r1 0.000000\n" + "2: mov r0 r1\n" + "3: const r2 5.000000\n" + "4: less r3 r0 r2\n" + "5: jmpf r3 14\n" + "6: const r4 1.000000\n" + "7: eq r5 r0 r4\n" + "8: jmpf r5 10\n" + "9: jmp 10\n" + "10: const r6 1.000000\n" + "11: mov r7 r0\n" + "12: add r0 r0 r6\n" + "13: jmp 3\n" + "14: pop 8\n" + "15: halt\n"; + + ASSERT_TRUE(asmb == expected); +} + +TEST(CodeGen, BreakFor) +{ + IPLString source = "for (var i = 0; i < 5; i++ ){ if(i == 1) break; }\n"; + IPLVector tokens = Tokenize(source.c_str()).tokens; + auto ast = Parse(tokens); + auto asmb = GenerateByteCode(ast, source, + ByteCodeGeneratorOptions(ByteCodeGeneratorOptions::OptimizationsType::None, false)); + IPLString expected = "0: push 8\n" + "1: const r1 0.000000\n" + "2: mov r0 r1\n" + "3: const r2 5.000000\n" + "4: less r3 r0 r2\n" + "5: jmpf r3 14\n" + "6: const r4 1.000000\n" + "7: eq r5 r0 r4\n" + "8: jmpf r5 10\n" + "9: jmp 14\n" + "10: const r6 1.000000\n" + "11: mov r7 r0\n" + "12: add r0 r0 r6\n" + "13: jmp 3\n" + "14: pop 8\n" + "15: halt\n"; + + ASSERT_TRUE(asmb == expected); +} -- cgit v1.3.1-23-gd82d