#include #include #include "doctest/doctest/doctest.h" #include "../src/LLVMVisitor/LLVMVisitor.h" TEST_SUITE_BEGIN("LLVMVisitor"); void freeNodes(Value* v) { if (dynamic_cast(v) != nullptr) { for (Value* val : static_cast(v)->get_args()) freeNodes(val); } else if (dynamic_cast(v) != nullptr) { for (Value* val : static_cast(v)->get_value()) freeNodes(val); } delete v; } std::string fileStr(const char* fileName) { std::ifstream inFile(fileName); if (!inFile.is_open()) return ""; std::stringstream buff; buff << inFile.rdbuf(); inFile.close(); return buff.str(); } TEST_SUITE("Arithmetic") { TEST_CASE("+_SumsIntov1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* fadd = new Funcall("+", { new Integer(3), new Integer(7) }); fadd->Visit(visitor); visitor.insertMain(); freeNodes(fadd); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = add i32 3, 7\n ret i32 0\n}\n"); } TEST_CASE("+_SumsIntov1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* fadd = new Funcall("+", { new Float(5.2), new Float(3.333336) }); fadd->Visit(visitor); visitor.insertMain(); freeNodes(fadd); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fadd double 5.200000, 3.333336\n ret i32 0\n}\n"); } TEST_CASE("-_SumsIntov1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* fsub = new Funcall("-", { new Integer(3), new Integer(7) }); fsub->Visit(visitor); visitor.insertMain(); freeNodes(fsub); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = sub i32 3, 7\n ret i32 0\n}\n"); } TEST_CASE("-_SumsIntov1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* fsub = new Funcall("-", { new Float(5.2), new Float(3.333336) }); fsub->Visit(visitor); visitor.insertMain(); freeNodes(fsub); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fsub double 5.200000, 3.333336\n ret i32 0\n}\n"); } TEST_CASE("*_SumsIntov1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* fmul = new Funcall("*", { new Integer(3), new Integer(7) }); fmul->Visit(visitor); visitor.insertMain(); freeNodes(fmul); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = mul i32 3, 7\n ret i32 0\n}\n"); } TEST_CASE("*_SumsIntov1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* fmul = new Funcall("*", { new Float(5.2), new Float(3.333336) }); fmul->Visit(visitor); visitor.insertMain(); freeNodes(fmul); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fmul double 5.200000, 3.333336\n ret i32 0\n}\n"); } TEST_CASE("/_SumsIntov1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* fdiv = new Funcall("/", { new Integer(3), new Integer(7) }); fdiv->Visit(visitor); visitor.insertMain(); freeNodes(fdiv); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = div i32 3, 7\n ret i32 0\n}\n"); } TEST_CASE("/_SumsIntov1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* fdiv = new Funcall("/", { new Float(5.2), new Float(3.333336) }); fdiv->Visit(visitor); visitor.insertMain(); freeNodes(fdiv); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fdiv double 5.200000, 3.333336\n ret i32 0\n}\n"); } } TEST_SUITE("Boolean") { TEST_CASE("=_StoresComparisonInv1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* eq = new Funcall("=", { new Integer(1), new Integer(2) }); eq->Visit(visitor); visitor.insertMain(); freeNodes(eq); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp eq i32 1, 2\n ret i32 0\n}\n"); } TEST_CASE("=_StoresComparisonInv1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* eq = new Funcall("=", { new Float(3.33333), new Float(2.001) }); eq->Visit(visitor); visitor.insertMain(); freeNodes(eq); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fcmp oeq double 3.333330, 2.001000\n ret i32 0\n}\n"); } TEST_CASE(">_StoresComparisonInv1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* gt = new Funcall(">", { new Integer(1), new Integer(2) }); gt->Visit(visitor); visitor.insertMain(); freeNodes(gt); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp sgt i32 1, 2\n ret i32 0\n}\n"); } TEST_CASE(">_StoresComparisonInv1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* gt = new Funcall(">", { new Float(3.33333), new Float(2.001) }); gt->Visit(visitor); visitor.insertMain(); freeNodes(gt); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fcmp ogt double 3.333330, 2.001000\n ret i32 0\n}\n"); } TEST_CASE(">=_StoresComparisonInv1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* ge = new Funcall(">=", { new Integer(1), new Integer(2) }); ge->Visit(visitor); visitor.insertMain(); freeNodes(ge); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp sge i32 1, 2\n ret i32 0\n}\n"); } TEST_CASE(">=_StoresComparisonInv1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* ge = new Funcall(">=", { new Float(3.33333), new Float(2.001) }); ge->Visit(visitor); visitor.insertMain(); freeNodes(ge); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fcmp oge double 3.333330, 2.001000\n ret i32 0\n}\n"); } TEST_CASE("<_StoresComparisonInv1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* lt = new Funcall("<", { new Integer(1), new Integer(2) }); lt->Visit(visitor); visitor.insertMain(); freeNodes(lt); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp slt i32 1, 2\n ret i32 0\n}\n"); } TEST_CASE("<_StoresComparisonInv1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* lt = new Funcall("<", { new Float(3.33333), new Float(2.001) }); lt->Visit(visitor); visitor.insertMain(); freeNodes(lt); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = fcmp olt double 3.333330, 2.001000\n ret i32 0\n}\n"); } TEST_CASE("<=_StoresComparisonInv1OfMain_WhenGivenTwoInts") { { LLVMVisitor visitor("temp.ll"); Value* le = new Funcall("<=", { new Integer(1), new Integer(2) }); le->Visit(visitor); visitor.insertMain(); freeNodes(le); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp sle i32 1, 2\n ret i32 0\n}\n"); } TEST_CASE("<=_StoresComparisonInv1OfMain_WhenGivenTwoFloats") { { LLVMVisitor visitor("temp.ll"); Value* le = new Funcall("<=", { new Float(3.33333), new Float(2.001) }); le->Visit(visitor); visitor.insertMain(); freeNodes(le); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n\t%v1 = fcmp ole double 3.333330, 2.001000\n\tret i32 0\n}\n"); } } TEST_SUITE("Others") { TEST_CASE("defun_CreatesNoArguments_WhenEmptyListIsGiven") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Integer(4) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef() {\n ret i32 4\n}\n\n"); } TEST_CASE("defun_CreatesOneArgument_WhenOneIsGiven") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({ new Name("a") }), new Integer(4) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef(i32 %_a) {\n ret i32 4\n}\n\n"); } TEST_CASE("defun_CreatesTwoArguments_WhenTwoAreGiven") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({ new Name("a"), new Name("b") }), new Integer(4) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef(i32 %_a, i32 %_b) {\n ret i32 4\n}\n\n"); } TEST_CASE("defun_ReturnsInteger_WhenIntegerIsOnlyExpression") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Integer(4) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef() {\n ret i32 4\n}\n\n"); } TEST_CASE("defun_ReturnsInteger_WhenIntegerIsLastExpression") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Float(574.2), new Funcall("+", { new Float(1.0), new Float(0.1) }), new Integer(4) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef() {\n %v0 = fadd double 1.000000, 0.100000\n ret i32 4\n}\n\n"); } TEST_CASE("defun_ReturnsFloat_WhenFloatIsOnlyExpression") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Float(4.0) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define double @tef() {\n ret double 4.000000\n}\n\n"); } TEST_CASE("defun_ReturnsFloat_WhenFloatIsLastExpression") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Integer(57), new Funcall("+", { new Integer(1), new Integer(1) }), new Float(4.0) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define double @tef() {\n %v0 = add i32 1, 1\n ret double 4.000000\n}\n\n"); } TEST_CASE("defvar_CreatesGlobalVariable_WhenGivenInteger") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defvar", { new Name("myvar"), new Integer(5) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "@myvar = global i32 5\n"); } TEST_CASE("defvar_CreatesGlobalVariable_WhenGivenFloat") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defvar", { new Name("myvar"), new Float(4.956) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "@myvar = global double 4.956000\n"); } TEST_CASE("if_CreatesProperConstruction_InsidedefunWhenNoElse") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Funcall("if", { new Funcall("=", { new Integer(0), new Integer(1) }), new Integer(3) }) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef() {\n %v0 = icmp eq i32 0, 1\n %v1 = alloca i32\n br i1 %v0, label %v2, label %v3\n\n v2:\n store i32 3, ptr %v1\n br label %v3\n\n v3:\n %v4 = load i32, ptr %v1\n ret i32 %v4\n}\n\n"); } TEST_CASE("if_CreatesProperConstruction_InsidedefunWhenWithElse") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("defun", { new Name("tef"), new List({}), new Funcall("if", { new Funcall("=", { new Integer(0), new Integer(1) }), new Integer(3), new Integer(5) }) }); defun->Visit(visitor); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @tef() {\n %v0 = icmp eq i32 0, 1\n %v1 = alloca i32\n br i1 %v0, label %v2, label %v3\n\n v2:\n store i32 3, ptr %v1\n br label %v4\n\n v3:\n store i32 5, ptr %v1\n br label %v4\n\n v4:\n %v5 = load i32, ptr %v1\n ret i32 %v5\n}\n\n"); } TEST_CASE("if_CreatesProperConstruction_InsidemainWhenNoElse") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("if", { new Funcall("=", { new Integer(0), new Integer(1) }), new Integer(3) }); defun->Visit(visitor); visitor.insertMain(); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp eq i32 0, 1\n %v2 = alloca i32\n br i1 %v1, label %v3, label %v4\n\n v3:\n store i32 3, ptr %v2\n br label %v4\n\n v4:\n %v5 = load i32, ptr %v2\n ret i32 0\n}\n"); } TEST_CASE("if_CreatesProperConstruction_InsidemainWhenWithElse") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("if", { new Funcall("=", { new Integer(0), new Integer(1) }), new Integer(3), new Integer(5) }); defun->Visit(visitor); visitor.insertMain(); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp eq i32 0, 1\n %v2 = alloca i32\n br i1 %v1, label %v3, label %v4\n\n v3:\n store i32 3, ptr %v2\n br label %v5\n\n v4:\n store i32 5, ptr %v2\n br label %v5\n\n v5:\n %v6 = load i32, ptr %v2\n ret i32 0\n}\n"); } TEST_CASE("if_CreatesProperConstruction_InsidemainWhenIfElseHasIfElseInIf") { { LLVMVisitor visitor("temp.ll"); Value* defun = new Funcall("if", { new Funcall("=", { new Integer(0), new Integer(0) }), new Funcall("if", { new Funcall(">", { new Integer(5), new Integer(4) }), new Integer(7), new Integer(9) }), new Integer(5) }); defun->Visit(visitor); visitor.insertMain(); freeNodes(defun); } CHECK_EQ(fileStr("temp.ll"), "define i32 @main() {\n %v1 = icmp eq i32 0, 0\n %v2 = alloca i32\n br i1 %v1, label %v3, label %v4\n\n v3:\n %v6 = icmp sgt i32 5, 4\n %v7 = alloca i32\n br i1 %v6, label %v8, label %v9\n\n v8:\n store i32 7, ptr %v7\n br label %v10\n\n v9:\n store i32 9, ptr %v7\n br label %v10\n\n v10:\n %v11 = load i32, ptr %v7\n store i32 %v11, ptr %v2\n br label %v5\n\n v4:\n store i32 5, ptr %v2\n br label %v5\n\n v5:\n %v12 = load i32, ptr %v2\n ret i32 0\n}\n"); } } TEST_SUITE_END();