Website Summary Files Commits Branches @commit @diff @stats
path: root / JSImpl
diff options
context:
space:
mode:
Commit6ba49b30cf1e33fb0dd6782a404ec34b7964cb59 (patch)
Author
 Martin Dobrev <martindobrev0@gmail.com>
2020-11-24 18:06:26 +0200
Committer
 GitHub <noreply@github.com>
2020-11-24 18:06:26 +0200
Filesa89b314c4d0b942d2350b9adbd741d6fa3375bfb /JSImpl
IPL-6ba49b30cf1e33fb0dd6782a404ec34b7964cb59.tar
IPL-6ba49b30cf1e33fb0dd6782a404ec34b7964cb59.tar.gz
IPL-6ba49b30cf1e33fb0dd6782a404ec34b7964cb59.zip
Parent4e883334a14ff22f12765204d6bf223d07c0298f (diff)
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 <martindobrev0@gmail.com> * Add implement byte code generation for break/continue in for/while.
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<ExpressionPtr>& list, bool isLast);
void Print( const char* name, const IPLVector<IPLString>& 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<size_t>()); };
+ void OpenContinueScope() { m_ContinueScopeStack.push(IPLVector<size_t>()); };
+ void CloseBreakScope(size_t addr);
+ void CloseContinueScope(size_t addr);
+ void PushBreak();
+ void PushContinue();
private:
IPLVector<IPLString> m_RegisterTable;
IPLVector<Instruction> m_Code;
IPLVector<IPLString> m_Source;
+ IPLStack<IPLVector<size_t>> m_BreakScopeStack;
+ IPLStack<IPLVector<size_t>> m_ContinueScopeStack;
+
IPLStack<IPLString> 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<ExpressionPtr>, Values, IPLVector<ExpressionPtr>())
#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<Expression>;
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<TokenType> 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<Call>(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<MemberAccess>(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<UnaryExpression>(expr, type, suffix);
+ return IPLMakeSharePtr<Continue>();
}
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<UnaryExpression>(expr, type, suffix);
+ return IPLMakeSharePtr<Break>();
}
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<Token> 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<Token> 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<Token> 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<Token> 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<Token> 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<Token> 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<Token> 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<Token> 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<Token> 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<Token> 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);
+}