From d31a8bdd4c96b0141f5a87affbb8db20c2d62e4e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 24 Sep 2024 16:00:51 +0200
Subject: [PATCH 01/94] Set version to 0.12.0

---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20c14a01..f202c93f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.14)
 
-project(libscratchcpp VERSION 0.11.0 LANGUAGES C CXX)
+project(libscratchcpp VERSION 0.12.0 LANGUAGES C CXX)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 set(CMAKE_CXX_STANDARD 17)

From 47beef7c9116ea2a90e037e113d975c1a0644524 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 28 Sep 2024 23:11:10 +0200
Subject: [PATCH 02/94] TextBubble: Add missing export

---
 include/scratchcpp/textbubble.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/scratchcpp/textbubble.h b/include/scratchcpp/textbubble.h
index ce5395f1..baec8945 100644
--- a/include/scratchcpp/textbubble.h
+++ b/include/scratchcpp/textbubble.h
@@ -11,7 +11,7 @@ namespace libscratchcpp
 class TextBubblePrivate;
 
 /*! \brief The TextBubble class represents a text bubble created using say or think block. */
-class TextBubble : public Drawable
+class LIBSCRATCHCPP_EXPORT TextBubble : public Drawable
 {
     public:
         enum class Type

From a3a1c8261c6291fa37e1265a1d5edfa06437ddba Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 29 Sep 2024 09:46:54 +0200
Subject: [PATCH 03/94] Fix a crash when unsupported graphic effects are used

---
 src/scratch/target.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/scratch/target.cpp b/src/scratch/target.cpp
index bb14bd97..7d01cebe 100644
--- a/src/scratch/target.cpp
+++ b/src/scratch/target.cpp
@@ -540,6 +540,9 @@ bool Target::touchingColor(const Value &color, const Value &mask) const
 /*! Returns the value of the given graphics effect. */
 double Target::graphicsEffectValue(IGraphicsEffect *effect) const
 {
+    if (!effect)
+        return 0;
+
     auto it = impl->graphicsEffects.find(effect);
 
     if (it == impl->graphicsEffects.cend())
@@ -551,8 +554,8 @@ double Target::graphicsEffectValue(IGraphicsEffect *effect) const
 /*! Sets the value of the given graphics effect. */
 void Target::setGraphicsEffectValue(IGraphicsEffect *effect, double value)
 {
-    assert(effect);
-    impl->graphicsEffects[effect] = effect->clamp(value);
+    if (effect)
+        impl->graphicsEffects[effect] = effect->clamp(value);
 }
 
 /*! Sets the value of all graphics effects to 0 (clears them). */

From 79b067318574a8210cf7e850b9723f7fb91317fe Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 29 Sep 2024 10:29:25 +0200
Subject: [PATCH 04/94] Fix Stage and Target test names

---
 test/scratch_classes/stage_test.cpp  | 2 +-
 test/scratch_classes/target_test.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/scratch_classes/stage_test.cpp b/test/scratch_classes/stage_test.cpp
index d21156a9..53f0477d 100644
--- a/test/scratch_classes/stage_test.cpp
+++ b/test/scratch_classes/stage_test.cpp
@@ -219,7 +219,7 @@ TEST(StageTest, TouchingPoint)
     ASSERT_TRUE(stage.touchingPoint(-12.46, 120.72));
 }
 
-TEST(SpriteTest, TouchingColor)
+TEST(StageTest, TouchingColor)
 {
     Stage stage;
     ASSERT_FALSE(stage.touchingColor(0));
diff --git a/test/scratch_classes/target_test.cpp b/test/scratch_classes/target_test.cpp
index bc4d9d95..e9d02379 100644
--- a/test/scratch_classes/target_test.cpp
+++ b/test/scratch_classes/target_test.cpp
@@ -775,7 +775,7 @@ TEST(TargetTest, GraphicsEffects)
     ASSERT_EQ(target.graphicsEffectValue(&effect2), 0);
 }
 
-TEST(SpriteTest, BubbleTypeRedraw)
+TEST(TargetTest, BubbleTypeRedraw)
 {
     Target target;
     EngineMock engine;
@@ -786,7 +786,7 @@ TEST(SpriteTest, BubbleTypeRedraw)
     target.bubble()->setType(TextBubble::Type::Think);
 }
 
-TEST(SpriteTest, BubbleTextRedraw)
+TEST(TaregtTest, BubbleTextRedraw)
 {
     Target target;
     EngineMock engine;

From 5b475d6c958cac1411e79300913cc25264b52784 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 29 Sep 2024 10:41:51 +0200
Subject: [PATCH 05/94] Update libcpr

---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f202c93f..d45ade3a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -88,7 +88,7 @@ target_link_libraries(scratchcpp PRIVATE scratchcpp-audio)
 
 if (LIBSCRATCHCPP_NETWORK_SUPPORT)
     FetchContent_Declare(cpr GIT_REPOSITORY https://github.com/libcpr/cpr.git
-	                     GIT_TAG 3b15fa82ea74739b574d705fea44959b58142eb8) # 1.10.5
+                         GIT_TAG 225b7454877805f089b3895260438e929bd6d123) # 09-22-2024
     FetchContent_MakeAvailable(cpr)
     target_link_libraries(scratchcpp PRIVATE cpr::cpr)
     target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_NETWORK_SUPPORT)

From 22d16c59d5b1568e1edef98cc108428efe9add1f Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 29 Sep 2024 11:45:09 +0200
Subject: [PATCH 06/94] Add empty block implementations to dev folder

---
 CMakeLists.txt                           |   5 +
 src/CMakeLists.txt                       |   9 +-
 src/dev/CMakeLists.txt                   |   1 +
 src/dev/blocks/CMakeLists.txt            | 115 +++++++++++++++
 src/dev/blocks/blocks.cpp                |  85 +++++++++++
 src/dev/blocks/blocks.h                  |  26 ++++
 src/dev/blocks/controlblocks.cpp         |  19 +++
 src/dev/blocks/controlblocks.h           |  19 +++
 src/dev/blocks/customblocks.cpp          |  19 +++
 src/dev/blocks/customblocks.h            |  19 +++
 src/dev/blocks/eventblocks.cpp           |  19 +++
 src/dev/blocks/eventblocks.h             |  19 +++
 src/dev/blocks/listblocks.cpp            |  19 +++
 src/dev/blocks/listblocks.h              |  19 +++
 src/dev/blocks/looksblocks.cpp           |  19 +++
 src/dev/blocks/looksblocks.h             |  19 +++
 src/dev/blocks/motionblocks.cpp          |  19 +++
 src/dev/blocks/motionblocks.h            |  19 +++
 src/dev/blocks/operatorblocks.cpp        |  19 +++
 src/dev/blocks/operatorblocks.h          |  19 +++
 src/dev/blocks/sensingblocks.cpp         |  19 +++
 src/dev/blocks/sensingblocks.h           |  19 +++
 src/dev/blocks/soundblocks.cpp           |  19 +++
 src/dev/blocks/soundblocks.h             |  19 +++
 src/dev/blocks/variableblocks.cpp        |  19 +++
 src/dev/blocks/variableblocks.h          |  19 +++
 src/engine/internal/engine.cpp           |   4 +
 test/CMakeLists.txt                      |   9 +-
 test/dev/CMakeLists.txt                  |   1 +
 test/dev/blocks/CMakeLists.txt           | 179 +++++++++++++++++++++++
 test/dev/blocks/control_blocks_test.cpp  |  15 ++
 test/dev/blocks/custom_blocks_test.cpp   |  15 ++
 test/dev/blocks/event_blocks_test.cpp    |  15 ++
 test/dev/blocks/list_blocks_test.cpp     |  15 ++
 test/dev/blocks/looks_blocks_test.cpp    |  15 ++
 test/dev/blocks/motion_blocks_test.cpp   |  15 ++
 test/dev/blocks/operator_blocks_test.cpp |  15 ++
 test/dev/blocks/sensing_blocks_test.cpp  |  15 ++
 test/dev/blocks/sound_blocks_test.cpp    |  15 ++
 test/dev/blocks/variable_blocks_test.cpp |  15 ++
 test/engine/engine_test.cpp              |  20 +++
 41 files changed, 981 insertions(+), 3 deletions(-)
 create mode 100644 src/dev/CMakeLists.txt
 create mode 100644 src/dev/blocks/CMakeLists.txt
 create mode 100644 src/dev/blocks/blocks.cpp
 create mode 100644 src/dev/blocks/blocks.h
 create mode 100644 src/dev/blocks/controlblocks.cpp
 create mode 100644 src/dev/blocks/controlblocks.h
 create mode 100644 src/dev/blocks/customblocks.cpp
 create mode 100644 src/dev/blocks/customblocks.h
 create mode 100644 src/dev/blocks/eventblocks.cpp
 create mode 100644 src/dev/blocks/eventblocks.h
 create mode 100644 src/dev/blocks/listblocks.cpp
 create mode 100644 src/dev/blocks/listblocks.h
 create mode 100644 src/dev/blocks/looksblocks.cpp
 create mode 100644 src/dev/blocks/looksblocks.h
 create mode 100644 src/dev/blocks/motionblocks.cpp
 create mode 100644 src/dev/blocks/motionblocks.h
 create mode 100644 src/dev/blocks/operatorblocks.cpp
 create mode 100644 src/dev/blocks/operatorblocks.h
 create mode 100644 src/dev/blocks/sensingblocks.cpp
 create mode 100644 src/dev/blocks/sensingblocks.h
 create mode 100644 src/dev/blocks/soundblocks.cpp
 create mode 100644 src/dev/blocks/soundblocks.h
 create mode 100644 src/dev/blocks/variableblocks.cpp
 create mode 100644 src/dev/blocks/variableblocks.h
 create mode 100644 test/dev/CMakeLists.txt
 create mode 100644 test/dev/blocks/CMakeLists.txt
 create mode 100644 test/dev/blocks/control_blocks_test.cpp
 create mode 100644 test/dev/blocks/custom_blocks_test.cpp
 create mode 100644 test/dev/blocks/event_blocks_test.cpp
 create mode 100644 test/dev/blocks/list_blocks_test.cpp
 create mode 100644 test/dev/blocks/looks_blocks_test.cpp
 create mode 100644 test/dev/blocks/motion_blocks_test.cpp
 create mode 100644 test/dev/blocks/operator_blocks_test.cpp
 create mode 100644 test/dev/blocks/sensing_blocks_test.cpp
 create mode 100644 test/dev/blocks/sound_blocks_test.cpp
 create mode 100644 test/dev/blocks/variable_blocks_test.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d45ade3a..49dbd4cc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON)
 option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON)
 option(LIBSCRATCHCPP_COMPUTED_GOTO "Support for computed goto" ON)
+option(LIBSCRATCHCPP_USE_LLVM "Compile scripts to LLVM IR (work in progress)" OFF)
 
 if (NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
 	# Computed goto not supported on anything except GCC
@@ -65,6 +66,10 @@ target_sources(scratchcpp
     include/scratchcpp/imonitorhandler.h
 )
 
+if (LIBSCRATCHCPP_USE_LLVM)
+    target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
+endif()
+
 include(FetchContent)
 set(ZIP_SRC thirdparty/zip/src)
 set(UTFCPP_SRC thirdparty/utfcpp/source)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5e570d8d..f4e1fcc2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,8 +11,15 @@ target_sources(scratchcpp
     rect_p.h
 )
 
-add_subdirectory(blocks)
+if (NOT LIBSCRATCHCPP_USE_LLVM)
+    add_subdirectory(blocks)
+endif()
+
 add_subdirectory(engine)
 add_subdirectory(internal)
 add_subdirectory(scratch)
 add_subdirectory(audio)
+
+if (LIBSCRATCHCPP_USE_LLVM)
+    add_subdirectory(dev)
+endif()
diff --git a/src/dev/CMakeLists.txt b/src/dev/CMakeLists.txt
new file mode 100644
index 00000000..62785564
--- /dev/null
+++ b/src/dev/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(blocks)
diff --git a/src/dev/blocks/CMakeLists.txt b/src/dev/blocks/CMakeLists.txt
new file mode 100644
index 00000000..d7416856
--- /dev/null
+++ b/src/dev/blocks/CMakeLists.txt
@@ -0,0 +1,115 @@
+target_sources(scratchcpp
+  PRIVATE
+    blocks.cpp
+    blocks.h
+)
+
+# Motion blocks
+option(LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS "Motion blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_MOTION_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        motionblocks.cpp
+        motionblocks.h
+    )
+endif()
+
+# Looks blocks
+option(LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS "Looks blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_LOOKS_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        looksblocks.cpp
+        looksblocks.h
+    )
+endif()
+
+# Sound blocks
+option(LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS "Sound blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_SOUND_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        soundblocks.cpp
+        soundblocks.h
+    )
+endif()
+
+# Event blocks
+option(LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS "Event blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_EVENT_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        eventblocks.cpp
+        eventblocks.h
+    )
+endif()
+
+# Control blocks
+option(LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS "Control blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_CONTROL_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        controlblocks.cpp
+        controlblocks.h
+    )
+endif()
+
+# Sensing blocks
+option(LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS "Sensing blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_SENSING_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        sensingblocks.cpp
+        sensingblocks.h
+    )
+endif()
+
+# Operator blocks
+option(LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS "Operator blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_OPERATOR_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        operatorblocks.cpp
+        operatorblocks.h
+    )
+endif()
+
+# Variable blocks
+option(LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS "Variable blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_VARIABLE_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        variableblocks.cpp
+        variableblocks.h
+    )
+endif()
+
+# List blocks
+option(LIBSCRATCHCPP_ENABLE_LIST_BLOCKS "List blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_LIST_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_LIST_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        listblocks.cpp
+        listblocks.h
+    )
+endif()
+
+# Custom blocks
+option(LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS "Custom blocks support" ON)
+if (LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS)
+    target_compile_definitions(scratchcpp PRIVATE ENABLE_CUSTOM_BLOCKS)
+    target_sources(scratchcpp
+      PRIVATE
+        customblocks.cpp
+        customblocks.h
+    )
+endif()
diff --git a/src/dev/blocks/blocks.cpp b/src/dev/blocks/blocks.cpp
new file mode 100644
index 00000000..c352547b
--- /dev/null
+++ b/src/dev/blocks/blocks.cpp
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <scratchcpp/scratchconfiguration.h>
+
+#include "blocks.h"
+
+#ifdef ENABLE_MOTION_BLOCKS
+#include "motionblocks.h"
+#endif
+#ifdef ENABLE_LOOKS_BLOCKS
+#include "looksblocks.h"
+#endif
+#ifdef ENABLE_SOUND_BLOCKS
+#include "soundblocks.h"
+#endif
+#ifdef ENABLE_EVENT_BLOCKS
+#include "eventblocks.h"
+#endif
+#ifdef ENABLE_CONTROL_BLOCKS
+#include "controlblocks.h"
+#endif
+#ifdef ENABLE_SENSING_BLOCKS
+#include "sensingblocks.h"
+#endif
+#ifdef ENABLE_OPERATOR_BLOCKS
+#include "operatorblocks.h"
+#endif
+#ifdef ENABLE_VARIABLE_BLOCKS
+#include "variableblocks.h"
+#endif
+#ifdef ENABLE_LIST_BLOCKS
+#include "listblocks.h"
+#endif
+#ifdef ENABLE_CUSTOM_BLOCKS
+#include "customblocks.h"
+#endif
+
+using namespace libscratchcpp;
+
+Blocks Blocks::m_instance;
+
+const std::vector<std::shared_ptr<IExtension>> &Blocks::extensions()
+{
+    return m_instance.m_extensions;
+}
+
+Blocks::Blocks()
+{
+#ifdef ENABLE_MOTION_BLOCKS
+    m_extensions.push_back(std::make_shared<MotionBlocks>());
+#endif
+#ifdef ENABLE_LOOKS_BLOCKS
+    m_extensions.push_back(std::make_shared<LooksBlocks>());
+#endif
+#ifdef ENABLE_SOUND_BLOCKS
+    m_extensions.push_back(std::make_shared<SoundBlocks>());
+#endif
+#ifdef ENABLE_EVENT_BLOCKS
+    m_extensions.push_back(std::make_shared<EventBlocks>());
+#endif
+#ifdef ENABLE_CONTROL_BLOCKS
+    m_extensions.push_back(std::make_shared<ControlBlocks>());
+#endif
+#ifdef ENABLE_SENSING_BLOCKS
+    m_extensions.push_back(std::make_shared<SensingBlocks>());
+#endif
+#ifdef ENABLE_OPERATOR_BLOCKS
+    m_extensions.push_back(std::make_shared<OperatorBlocks>());
+#endif
+#ifdef ENABLE_VARIABLE_BLOCKS
+    m_extensions.push_back(std::make_shared<VariableBlocks>());
+#endif
+#ifdef ENABLE_LIST_BLOCKS
+    m_extensions.push_back(std::make_shared<ListBlocks>());
+#endif
+#ifdef ENABLE_CUSTOM_BLOCKS
+    m_extensions.push_back(std::make_shared<CustomBlocks>());
+#endif
+}
+
+void Blocks::registerExtensions()
+{
+    for (auto ext : m_extensions)
+        ScratchConfiguration::registerExtension(ext);
+}
diff --git a/src/dev/blocks/blocks.h b/src/dev/blocks/blocks.h
new file mode 100644
index 00000000..56be7a34
--- /dev/null
+++ b/src/dev/blocks/blocks.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <vector>
+#include <memory>
+
+namespace libscratchcpp
+{
+
+class IExtension;
+
+class Blocks
+{
+    public:
+        static const std::vector<std::shared_ptr<IExtension>> &extensions();
+
+    private:
+        Blocks();
+        void registerExtensions();
+
+        static Blocks m_instance; // use static initialization
+        std::vector<std::shared_ptr<IExtension>> m_extensions;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/controlblocks.cpp b/src/dev/blocks/controlblocks.cpp
new file mode 100644
index 00000000..937e5943
--- /dev/null
+++ b/src/dev/blocks/controlblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "controlblocks.h"
+
+using namespace libscratchcpp;
+
+std::string ControlBlocks::name() const
+{
+    return "Control";
+}
+
+std::string ControlBlocks::description() const
+{
+    return name() + " blocks";
+}
+
+void ControlBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/controlblocks.h b/src/dev/blocks/controlblocks.h
new file mode 100644
index 00000000..155f9296
--- /dev/null
+++ b/src/dev/blocks/controlblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class ControlBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/customblocks.cpp b/src/dev/blocks/customblocks.cpp
new file mode 100644
index 00000000..4a1657b3
--- /dev/null
+++ b/src/dev/blocks/customblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "customblocks.h"
+
+using namespace libscratchcpp;
+
+std::string CustomBlocks::name() const
+{
+    return "Custom blocks";
+}
+
+std::string CustomBlocks::description() const
+{
+    return name();
+}
+
+void CustomBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/customblocks.h b/src/dev/blocks/customblocks.h
new file mode 100644
index 00000000..433afa36
--- /dev/null
+++ b/src/dev/blocks/customblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class CustomBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/eventblocks.cpp b/src/dev/blocks/eventblocks.cpp
new file mode 100644
index 00000000..07477984
--- /dev/null
+++ b/src/dev/blocks/eventblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "eventblocks.h"
+
+using namespace libscratchcpp;
+
+std::string EventBlocks::name() const
+{
+    return "Events";
+}
+
+std::string libscratchcpp::EventBlocks::description() const
+{
+    return "Event blocks";
+}
+
+void EventBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/eventblocks.h b/src/dev/blocks/eventblocks.h
new file mode 100644
index 00000000..c395c3de
--- /dev/null
+++ b/src/dev/blocks/eventblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class EventBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/listblocks.cpp b/src/dev/blocks/listblocks.cpp
new file mode 100644
index 00000000..8b4602ad
--- /dev/null
+++ b/src/dev/blocks/listblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "listblocks.h"
+
+using namespace libscratchcpp;
+
+std::string ListBlocks::name() const
+{
+    return "Lists";
+}
+
+std::string ListBlocks::description() const
+{
+    return "List blocks";
+}
+
+void ListBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/listblocks.h b/src/dev/blocks/listblocks.h
new file mode 100644
index 00000000..edf01e8c
--- /dev/null
+++ b/src/dev/blocks/listblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class ListBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/looksblocks.cpp b/src/dev/blocks/looksblocks.cpp
new file mode 100644
index 00000000..0d471fb0
--- /dev/null
+++ b/src/dev/blocks/looksblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "looksblocks.h"
+
+using namespace libscratchcpp;
+
+std::string LooksBlocks::name() const
+{
+    return "Looks";
+}
+
+std::string LooksBlocks::description() const
+{
+    return name() + " blocks";
+}
+
+void LooksBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/looksblocks.h b/src/dev/blocks/looksblocks.h
new file mode 100644
index 00000000..8bfa5f18
--- /dev/null
+++ b/src/dev/blocks/looksblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class LooksBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/motionblocks.cpp b/src/dev/blocks/motionblocks.cpp
new file mode 100644
index 00000000..e44a673c
--- /dev/null
+++ b/src/dev/blocks/motionblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "motionblocks.h"
+
+using namespace libscratchcpp;
+
+std::string MotionBlocks::name() const
+{
+    return "Motion";
+}
+
+std::string MotionBlocks::description() const
+{
+    return name() + " blocks";
+}
+
+void MotionBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/motionblocks.h b/src/dev/blocks/motionblocks.h
new file mode 100644
index 00000000..125b4073
--- /dev/null
+++ b/src/dev/blocks/motionblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class MotionBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/operatorblocks.cpp b/src/dev/blocks/operatorblocks.cpp
new file mode 100644
index 00000000..8d4cbb92
--- /dev/null
+++ b/src/dev/blocks/operatorblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "operatorblocks.h"
+
+using namespace libscratchcpp;
+
+std::string OperatorBlocks::name() const
+{
+    return "Operators";
+}
+
+std::string OperatorBlocks::description() const
+{
+    return "Operator blocks";
+}
+
+void OperatorBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/operatorblocks.h b/src/dev/blocks/operatorblocks.h
new file mode 100644
index 00000000..af5bd7b5
--- /dev/null
+++ b/src/dev/blocks/operatorblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class OperatorBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/sensingblocks.cpp b/src/dev/blocks/sensingblocks.cpp
new file mode 100644
index 00000000..42896ecb
--- /dev/null
+++ b/src/dev/blocks/sensingblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sensingblocks.h"
+
+using namespace libscratchcpp;
+
+std::string SensingBlocks::name() const
+{
+    return "Sensing";
+}
+
+std::string SensingBlocks::description() const
+{
+    return name() + " blocks";
+}
+
+void SensingBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/sensingblocks.h b/src/dev/blocks/sensingblocks.h
new file mode 100644
index 00000000..ab934fb0
--- /dev/null
+++ b/src/dev/blocks/sensingblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class SensingBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/soundblocks.cpp b/src/dev/blocks/soundblocks.cpp
new file mode 100644
index 00000000..4a440a9a
--- /dev/null
+++ b/src/dev/blocks/soundblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "soundblocks.h"
+
+using namespace libscratchcpp;
+
+std::string SoundBlocks::name() const
+{
+    return "Sound";
+}
+
+std::string SoundBlocks::description() const
+{
+    return name() + " blocks";
+}
+
+void SoundBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/soundblocks.h b/src/dev/blocks/soundblocks.h
new file mode 100644
index 00000000..afae6c9c
--- /dev/null
+++ b/src/dev/blocks/soundblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class SoundBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/blocks/variableblocks.cpp b/src/dev/blocks/variableblocks.cpp
new file mode 100644
index 00000000..6d0f464f
--- /dev/null
+++ b/src/dev/blocks/variableblocks.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "variableblocks.h"
+
+using namespace libscratchcpp;
+
+std::string VariableBlocks::name() const
+{
+    return "Variables";
+}
+
+std::string VariableBlocks::description() const
+{
+    return "Variable blocks";
+}
+
+void VariableBlocks::registerBlocks(IEngine *engine)
+{
+}
diff --git a/src/dev/blocks/variableblocks.h b/src/dev/blocks/variableblocks.h
new file mode 100644
index 00000000..6c4144ac
--- /dev/null
+++ b/src/dev/blocks/variableblocks.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/iextension.h>
+
+namespace libscratchcpp
+{
+
+class VariableBlocks : public IExtension
+{
+    public:
+        std::string name() const override;
+        std::string description() const override;
+
+        void registerBlocks(IEngine *engine) override;
+};
+
+} // namespace libscratchcpp
diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp
index 6a15c9da..d5cfda8d 100644
--- a/src/engine/internal/engine.cpp
+++ b/src/engine/internal/engine.cpp
@@ -28,7 +28,11 @@
 #include "timer.h"
 #include "clock.h"
 #include "audio/iaudioengine.h"
+#ifdef USE_LLVM
+#include "dev/blocks/blocks.h"
+#else
 #include "blocks/blocks.h"
+#endif
 #include "scratch/monitor_p.h"
 
 using namespace libscratchcpp;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 07771920..8156363b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -18,12 +18,10 @@ add_subdirectory(mocks)
 add_subdirectory(zip)
 add_subdirectory(load_project)
 add_subdirectory(project)
-add_subdirectory(compiler)
 add_subdirectory(thread)
 add_subdirectory(virtual_machine)
 add_subdirectory(scratch_classes)
 add_subdirectory(target_interfaces)
-add_subdirectory(blocks)
 add_subdirectory(scratchconfiguration)
 add_subdirectory(assets)
 add_subdirectory(script)
@@ -35,3 +33,10 @@ add_subdirectory(randomgenerator)
 add_subdirectory(rect)
 add_subdirectory(network)
 add_subdirectory(audio)
+
+if(LIBSCRATCHCPP_USE_LLVM)
+    add_subdirectory(dev)
+else()
+    add_subdirectory(compiler)
+    add_subdirectory(blocks)
+endif()
diff --git a/test/dev/CMakeLists.txt b/test/dev/CMakeLists.txt
new file mode 100644
index 00000000..62785564
--- /dev/null
+++ b/test/dev/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(blocks)
diff --git a/test/dev/blocks/CMakeLists.txt b/test/dev/blocks/CMakeLists.txt
new file mode 100644
index 00000000..3e32a0d0
--- /dev/null
+++ b/test/dev/blocks/CMakeLists.txt
@@ -0,0 +1,179 @@
+# motion_blocks_test
+if (LIBSCRATCHCPP_ENABLE_MOTION_BLOCKS)
+    add_executable(
+      motion_blocks_test
+      motion_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      motion_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(motion_blocks_test)
+endif()
+
+# looks_blocks_test
+if (LIBSCRATCHCPP_ENABLE_LOOKS_BLOCKS)
+    add_executable(
+      looks_blocks_test
+      looks_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      looks_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(looks_blocks_test)
+endif()
+
+# sound_blocks_test
+if (LIBSCRATCHCPP_ENABLE_SOUND_BLOCKS)
+    add_executable(
+      sound_blocks_test
+      sound_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      sound_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(sound_blocks_test)
+endif()
+
+# event_blocks_test
+if (LIBSCRATCHCPP_ENABLE_EVENT_BLOCKS)
+    add_executable(
+      event_blocks_test
+      event_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      event_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(event_blocks_test)
+endif()
+
+# control_blocks_test
+if (LIBSCRATCHCPP_ENABLE_CONTROL_BLOCKS)
+    add_executable(
+      control_blocks_test
+      control_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      control_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(control_blocks_test)
+endif()
+
+# sensing_blocks_test
+if (LIBSCRATCHCPP_ENABLE_SENSING_BLOCKS)
+    add_executable(
+      sensing_blocks_test
+      sensing_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      sensing_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(sensing_blocks_test)
+endif()
+
+# operator_blocks_test
+if (LIBSCRATCHCPP_ENABLE_OPERATOR_BLOCKS)
+    add_executable(
+      operator_blocks_test
+      operator_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      operator_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(operator_blocks_test)
+endif()
+
+# variable_blocks_test
+if (LIBSCRATCHCPP_ENABLE_VARIABLE_BLOCKS)
+    add_executable(
+      variable_blocks_test
+      variable_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      variable_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(variable_blocks_test)
+endif()
+
+# list_blocks_test
+if (LIBSCRATCHCPP_ENABLE_LIST_BLOCKS)
+    add_executable(
+      list_blocks_test
+      list_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      list_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(list_blocks_test)
+endif()
+
+# custom_blocks_test
+if (LIBSCRATCHCPP_ENABLE_CUSTOM_BLOCKS)
+    add_executable(
+      custom_blocks_test
+      custom_blocks_test.cpp
+    )
+
+    target_link_libraries(
+      custom_blocks_test
+      GTest::gtest_main
+      GTest::gmock_main
+      scratchcpp
+      scratchcpp_mocks
+    )
+
+    gtest_discover_tests(custom_blocks_test)
+endif()
diff --git a/test/dev/blocks/control_blocks_test.cpp b/test/dev/blocks/control_blocks_test.cpp
new file mode 100644
index 00000000..b5cff150
--- /dev/null
+++ b/test/dev/blocks/control_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/controlblocks.h"
+
+using namespace libscratchcpp;
+
+class ControlBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<ControlBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/custom_blocks_test.cpp b/test/dev/blocks/custom_blocks_test.cpp
new file mode 100644
index 00000000..a926f7db
--- /dev/null
+++ b/test/dev/blocks/custom_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/customblocks.h"
+
+using namespace libscratchcpp;
+
+class CustomBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<CustomBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/event_blocks_test.cpp b/test/dev/blocks/event_blocks_test.cpp
new file mode 100644
index 00000000..ab8370a6
--- /dev/null
+++ b/test/dev/blocks/event_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/eventblocks.h"
+
+using namespace libscratchcpp;
+
+class EventBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<EventBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/list_blocks_test.cpp b/test/dev/blocks/list_blocks_test.cpp
new file mode 100644
index 00000000..8f98a2fb
--- /dev/null
+++ b/test/dev/blocks/list_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/listblocks.h"
+
+using namespace libscratchcpp;
+
+class ListBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<ListBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/looks_blocks_test.cpp b/test/dev/blocks/looks_blocks_test.cpp
new file mode 100644
index 00000000..24887b2d
--- /dev/null
+++ b/test/dev/blocks/looks_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/looksblocks.h"
+
+using namespace libscratchcpp;
+
+class LooksBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<LooksBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/motion_blocks_test.cpp b/test/dev/blocks/motion_blocks_test.cpp
new file mode 100644
index 00000000..01b617d8
--- /dev/null
+++ b/test/dev/blocks/motion_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/motionblocks.h"
+
+using namespace libscratchcpp;
+
+class MotionBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<MotionBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/operator_blocks_test.cpp b/test/dev/blocks/operator_blocks_test.cpp
new file mode 100644
index 00000000..f29aacec
--- /dev/null
+++ b/test/dev/blocks/operator_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/operatorblocks.h"
+
+using namespace libscratchcpp;
+
+class OperatorBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<OperatorBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/sensing_blocks_test.cpp b/test/dev/blocks/sensing_blocks_test.cpp
new file mode 100644
index 00000000..170c997c
--- /dev/null
+++ b/test/dev/blocks/sensing_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/sensingblocks.h"
+
+using namespace libscratchcpp;
+
+class SensingBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<SensingBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/sound_blocks_test.cpp b/test/dev/blocks/sound_blocks_test.cpp
new file mode 100644
index 00000000..6ecce731
--- /dev/null
+++ b/test/dev/blocks/sound_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/soundblocks.h"
+
+using namespace libscratchcpp;
+
+class SoundBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<SoundBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/dev/blocks/variable_blocks_test.cpp b/test/dev/blocks/variable_blocks_test.cpp
new file mode 100644
index 00000000..0a480f33
--- /dev/null
+++ b/test/dev/blocks/variable_blocks_test.cpp
@@ -0,0 +1,15 @@
+#include <enginemock.h>
+
+#include "../common.h"
+#include "dev/blocks/variableblocks.h"
+
+using namespace libscratchcpp;
+
+class VariableBlocksTest : public testing::Test
+{
+    public:
+        void SetUp() override { m_extension = std::make_unique<VariableBlocks>(); }
+
+        std::unique_ptr<IExtension> m_extension;
+        EngineMock m_engineMock;
+};
diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp
index 3b22b0a2..7434a993 100644
--- a/test/engine/engine_test.cpp
+++ b/test/engine/engine_test.cpp
@@ -31,7 +31,9 @@
 #include "engine/internal/clock.h"
 
 // TODO: Remove this
+#ifndef USE_LLVM
 #include "blocks/eventblocks.h"
+#endif
 
 using namespace libscratchcpp;
 
@@ -122,6 +124,7 @@ TEST(EngineTest, Clear)
     ScratchConfiguration::removeExtension(extension);
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, ClearThreadAboutToStopSignal)
 {
     Project p("3_threads.sb3");
@@ -193,6 +196,7 @@ TEST(EngineTest, StopSignal)
         engine->stop();
     }
 }
+#endif
 
 TEST(EngineTest, CompileAndExecuteMonitors)
 {
@@ -346,6 +350,7 @@ TEST(EngineTest, GlobalVolume)
     engine.setGlobalVolume(92.36);
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, Step)
 {
     Project p("step.sb3");
@@ -370,6 +375,7 @@ TEST(EngineTest, Step)
         ASSERT_EQ(test2->value().toInt(), i > 1 ? 3 : 1);
     }
 }
+#endif
 
 TEST(EngineTest, EventLoop)
 {
@@ -405,6 +411,7 @@ TEST(EngineTest, Fps)
     ASSERT_EQ(engine.fps(), 250);
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, FpsProject)
 {
     Project p("2_frames.sb3");
@@ -476,6 +483,7 @@ TEST(EngineTest, FpsProject)
     EXPECT_CALL(redrawMock, redraw());
     p.run();
 }
+#endif
 
 TEST(EngineTest, TurboModeEnabled)
 {
@@ -489,6 +497,7 @@ TEST(EngineTest, TurboModeEnabled)
     ASSERT_FALSE(engine.turboModeEnabled());
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, ExecutionOrder)
 {
     Project p("execution_order.sb3");
@@ -519,6 +528,7 @@ TEST(EngineTest, ExecutionOrder)
     ASSERT_EQ(Value((*list)[11]).toString(), "Sprite1 3 msg");
     ASSERT_EQ(Value((*list)[12]).toString(), "Stage msg");
 }
+#endif
 
 TEST(EngineTest, KeyState)
 {
@@ -616,6 +626,7 @@ TEST(EngineTest, KeyState)
     ASSERT_TRUE(engine.keyPressed("any"));
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, WhenKeyPressed)
 {
     Project p("when_key_pressed.sb3");
@@ -881,6 +892,7 @@ TEST(EngineTest, MouseWheel)
     ASSERT_VAR(stage, "any");
     ASSERT_EQ(GET_VAR(stage, "any")->value().toInt(), 0);
 }
+#endif
 
 TEST(EngineTest, MouseX)
 {
@@ -1045,6 +1057,7 @@ TEST(EngineTest, Broadcasts)
     ASSERT_EQ(engine.findBroadcastById("c"), 2);
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, TargetClickScripts)
 {
     Project p("target_click_scripts.sb3");
@@ -1150,6 +1163,7 @@ TEST(EngineTest, TargetClickScripts)
     ASSERT_VAR(stage, "stage");
     ASSERT_EQ(GET_VAR(stage, "stage")->value().toInt(), 2);
 }
+#endif
 
 TEST(EngineTest, Targets)
 {
@@ -1695,6 +1709,7 @@ void questionFunction(const std::string &)
 {
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, Clones)
 {
     Project p("clones.sb3");
@@ -2002,6 +2017,7 @@ TEST(EngineTest, EdgeActivatedHats)
 
     EventBlocks::audioInput = nullptr;
 }
+#endif
 
 TEST(EngineTest, UserAgent)
 {
@@ -2012,6 +2028,7 @@ TEST(EngineTest, UserAgent)
     ASSERT_EQ(engine.userAgent(), "test");
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, UnsupportedBlocks)
 {
     Project p("unsupported_blocks.sb3");
@@ -2020,6 +2037,7 @@ TEST(EngineTest, UnsupportedBlocks)
         p.engine()->unsupportedBlocks(),
         std::unordered_set<std::string>({ "ev3_motorTurnClockwise", "ev3_motorSetPower", "ev3_getMotorPosition", "ev3_whenButtonPressed", "ev3_getBrightness", "ev3_getDistance" }));
 }
+#endif
 
 TEST(EngineTest, NoCrashAfterStop)
 {
@@ -2037,6 +2055,7 @@ TEST(EngineTest, NoCrashOnBroadcastSelfCall)
     p.run();
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, NoRefreshWhenCallingRunningBroadcast)
 {
     // Regtest for #257
@@ -2131,6 +2150,7 @@ TEST(EngineTest, StopBeforeStarting)
     ASSERT_VAR(stage, "test");
     ASSERT_TRUE(GET_VAR(stage, "test")->value().toBool());
 }
+#endif
 
 TEST(EngineTest, NoCrashWhenLoadingUndefinedVariableOrListMonitor)
 {

From b68c38b09667e64e79035f83ce8b0a83737b65e9 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 29 Sep 2024 11:46:49 +0200
Subject: [PATCH 07/94] Add LLVM unit test workflow

---
 .github/workflows/utests-llvm.yml | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 .github/workflows/utests-llvm.yml

diff --git a/.github/workflows/utests-llvm.yml b/.github/workflows/utests-llvm.yml
new file mode 100644
index 00000000..d7d1ace8
--- /dev/null
+++ b/.github/workflows/utests-llvm.yml
@@ -0,0 +1,28 @@
+name: Unit tests (LLVM compiler)
+
+on:
+  push:
+    branches: '*'
+  pull_request:
+    branches: [ "master" ]
+
+env:
+  BUILD_TYPE: Debug
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v3
+      with:
+        submodules: true
+
+    - name: Configure CMake
+      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_USE_LLVM=ON
+
+    - name: Build
+      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all)
+
+    - name: Run unit tests
+      run: ctest --test-dir build -V

From 17a8f876ef71cf99f8928152cc22d01b7431aef7 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 29 Sep 2024 12:22:06 +0200
Subject: [PATCH 08/94] Remove old compiler from LLVM builds

---
 CMakeLists.txt                           |  6 +++++-
 include/scratchcpp/block.h               |  4 ++++
 include/scratchcpp/global.h              |  6 ++++++
 include/scratchcpp/iengine.h             |  4 ++++
 include/scratchcpp/inputvalue.h          |  4 ++++
 src/engine/CMakeLists.txt                | 12 +++++++++---
 src/engine/internal/engine.cpp           | 20 ++++++++++++++++++++
 src/engine/internal/engine.h             |  8 ++++++++
 src/scratch/block.cpp                    |  4 ++++
 src/scratch/block_p.h                    |  2 ++
 src/scratch/inputvalue.cpp               |  4 ++++
 test/engine/engine_test.cpp              |  8 ++++++--
 test/load_project/load_project_test.cpp  |  2 ++
 test/mocks/enginemock.h                  |  4 ++++
 test/scratch_classes/block_test.cpp      |  8 ++++++++
 test/scratch_classes/inputvalue_test.cpp |  2 ++
 16 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 49dbd4cc..e0af9cde 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,7 +46,6 @@ target_sources(scratchcpp
     include/scratchcpp/field.h
     include/scratchcpp/script.h
     include/scratchcpp/broadcast.h
-    include/scratchcpp/compiler.h
     include/scratchcpp/virtualmachine.h
     include/scratchcpp/blockprototype.h
     include/scratchcpp/block.h
@@ -68,6 +67,11 @@ target_sources(scratchcpp
 
 if (LIBSCRATCHCPP_USE_LLVM)
     target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
+else()
+    target_sources(scratchcpp
+      PUBLIC
+        include/scratchcpp/compiler.h
+    )
 endif()
 
 include(FetchContent)
diff --git a/include/scratchcpp/block.h b/include/scratchcpp/block.h
index fe6cf382..1731a9b3 100644
--- a/include/scratchcpp/block.h
+++ b/include/scratchcpp/block.h
@@ -25,7 +25,9 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity
         Block(const std::string &id, const std::string &opcode);
         Block(const Block &) = delete;
 
+#ifndef USE_LLVM
         void compile(Compiler *compiler);
+#endif
 
         const std::string &opcode() const;
 
@@ -73,11 +75,13 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity
         void setTarget(Target *newTarget);
         Target *target() const;
 
+#ifndef USE_LLVM
         BlockComp compileFunction() const;
         void setCompileFunction(BlockComp newCompileFunction);
 
         BlockComp hatPredicateCompileFunction() const;
         void setHatPredicateCompileFunction(HatPredicateCompileFunc newHatPredicateCompileFunction);
+#endif // USE_LLVM
 
         bool mutationHasNext() const;
         void setMutationHasNext(bool newMutationHasNext);
diff --git a/include/scratchcpp/global.h b/include/scratchcpp/global.h
index c72c14f6..b77a10e9 100644
--- a/include/scratchcpp/global.h
+++ b/include/scratchcpp/global.h
@@ -24,7 +24,9 @@ namespace libscratchcpp
 {
 
 class VirtualMachine;
+#ifndef USE_LLVM
 class Compiler;
+#endif
 class Block;
 class Value;
 
@@ -35,12 +37,14 @@ class Value;
  */
 using BlockFunc = unsigned int (*)(VirtualMachine *vm);
 
+#ifndef USE_LLVM
 /*!
  * \typedef BlockComp
  *
  * BlockComp is a function pointer for functions which are used to compile blocks to bytecode.
  */
 using BlockComp = void (*)(Compiler *);
+#endif // USE_LLVM
 
 /*!
  * \typedef MonitorNameFunc
@@ -56,12 +60,14 @@ using MonitorNameFunc = const std::string &(*)(Block *);
  */
 using MonitorChangeFunc = void (*)(Block *, const Value &newValue);
 
+#ifndef USE_LLVM
 /*!
  * \typedef HatPredicateCompileFunc
  *
  * HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates to bytecode.
  */
 using HatPredicateCompileFunc = void (*)(Compiler *vm);
+#endif // USE_LLVM
 
 } // namespace libscratchcpp
 
diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h
index a8cffbc2..80fe2c89 100644
--- a/include/scratchcpp/iengine.h
+++ b/include/scratchcpp/iengine.h
@@ -225,6 +225,7 @@ class LIBSCRATCHCPP_EXPORT IEngine
         /*! Returns the list of block functions. */
         virtual const std::vector<BlockFunc> &blockFunctions() const = 0;
 
+#ifndef USE_LLVM
         /*!
          * Call this from IExtension#registerBlocks() to add a compile function to a block section.
          * \see <a href="extensions.html">Extensions</a>
@@ -237,6 +238,7 @@ class LIBSCRATCHCPP_EXPORT IEngine
          * \see <a href="extensions.html">Extensions</a>
          */
         virtual void addHatPredicateCompileFunction(IExtension *extension, const std::string &opcode, HatPredicateCompileFunc f) = 0;
+#endif // USE_LLVM
 
         /*!
          * Call this from IExtension#registerBlocks() to add a monitor name function to a block section.
@@ -356,11 +358,13 @@ class LIBSCRATCHCPP_EXPORT IEngine
         /*! Sets the list of monitors. */
         virtual void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) = 0;
 
+#ifndef USE_LLVM
         /*! Creates a monitor for the given variable if it's missing and returns it. */
         virtual Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) = 0;
 
         /*! Creates a monitor for the given list if it's missing and returns it. */
         virtual Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) = 0;
+#endif // USE_LLVM
 
         /*! Emits when a monitor is added. */
         virtual sigslot::signal<Monitor *> &monitorAdded() = 0;
diff --git a/include/scratchcpp/inputvalue.h b/include/scratchcpp/inputvalue.h
index 5ecc87df..4641bf28 100644
--- a/include/scratchcpp/inputvalue.h
+++ b/include/scratchcpp/inputvalue.h
@@ -11,7 +11,9 @@ namespace libscratchcpp
 {
 
 class Block;
+#ifndef USE_LLVM
 class Compiler;
+#endif
 class Entity;
 class InputValuePrivate;
 
@@ -35,7 +37,9 @@ class LIBSCRATCHCPP_EXPORT InputValue
 
         InputValue(Type type = Type::Number);
 
+#ifndef USE_LLVM
         void compile(Compiler *compiler);
+#endif
 
         Type type() const;
         void setType(Type newType);
diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt
index 755e771f..71402cef 100644
--- a/src/engine/CMakeLists.txt
+++ b/src/engine/CMakeLists.txt
@@ -3,9 +3,6 @@ target_sources(scratchcpp
     virtualmachine.cpp
     virtualmachine_p.cpp
     virtualmachine_p.h
-    compiler.cpp
-    compiler_p.cpp
-    compiler_p.h
     script.cpp
     script_p.cpp
     script_p.h
@@ -23,3 +20,12 @@ target_sources(scratchcpp
     internal/randomgenerator.cpp
     internal/irandomgenerator.h
 )
+
+if(NOT LIBSCRATCHCPP_USE_LLVM)
+    target_sources(scratchcpp
+      PRIVATE
+        compiler.cpp
+        compiler_p.cpp
+        compiler_p.h
+    )
+endif()
diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp
index d5cfda8d..bc3f791d 100644
--- a/src/engine/internal/engine.cpp
+++ b/src/engine/internal/engine.cpp
@@ -7,7 +7,9 @@
 #include <scratchcpp/stage.h>
 #include <scratchcpp/textbubble.h>
 #include <scratchcpp/broadcast.h>
+#ifndef USE_LLVM
 #include <scratchcpp/compiler.h>
+#endif
 #include <scratchcpp/input.h>
 #include <scratchcpp/inputvalue.h>
 #include <scratchcpp/field.h>
@@ -105,6 +107,7 @@ void Engine::clear()
 // Resolves ID references and sets pointers of entities.
 void Engine::resolveIds()
 {
+#ifndef USE_LLVM
     for (auto target : m_targets) {
         std::cout << "Processing target " << target->name() << "..." << std::endl;
         const auto &blocks = target->blocks();
@@ -256,10 +259,12 @@ void Engine::resolveIds()
         block->updateInputMap();
         block->updateFieldMap();
     }
+#endif // USE_LLVM
 }
 
 void Engine::compile()
 {
+#ifndef USE_LLVM
     // Resolve entities by ID
     resolveIds();
 
@@ -317,6 +322,7 @@ void Engine::compile()
 
     for (auto monitor : m_monitors)
         compileMonitor(monitor);
+#endif // USE_LLVM
 }
 
 void Engine::start()
@@ -945,6 +951,7 @@ const std::vector<BlockFunc> &Engine::blockFunctions() const
     return m_functions;
 }
 
+#ifndef USE_LLVM
 void Engine::addCompileFunction(IExtension *extension, const std::string &opcode, BlockComp f)
 {
     if (m_compileFunctions.find(extension) == m_compileFunctions.cend())
@@ -960,6 +967,7 @@ void Engine::addHatPredicateCompileFunction(IExtension *extension, const std::st
 
     m_hatPredicateCompileFunctions[extension][opcode] = f;
 }
+#endif // USE_LLVM
 
 void Engine::addMonitorNameFunction(IExtension *extension, const std::string &opcode, MonitorNameFunc f)
 {
@@ -979,10 +987,12 @@ void Engine::addMonitorChangeFunction(IExtension *extension, const std::string &
 
 void Engine::addHatBlock(IExtension *extension, const std::string &opcode)
 {
+#ifndef USE_LLVM
     if (m_compileFunctions.find(extension) == m_compileFunctions.cend())
         m_compileFunctions[extension] = {};
 
     m_compileFunctions[extension][opcode] = [](Compiler *compiler) {};
+#endif
 }
 
 void Engine::addInput(IExtension *extension, const std::string &name, int id)
@@ -1362,6 +1372,7 @@ void Engine::setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitor
     }
 }
 
+#ifndef USE_LLVM
 Monitor *Engine::createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction)
 {
     if (var->monitor())
@@ -1398,6 +1409,7 @@ Monitor *Engine::createListMonitor(std::shared_ptr<List> list, const std::string
         return monitor.get();
     }
 }
+#endif // USE_LLVM
 
 sigslot::signal<Monitor *> &Engine::monitorAdded()
 {
@@ -1722,8 +1734,10 @@ const std::unordered_set<std::string> &Engine::unsupportedBlocks() const
 
 void Engine::clearExtensionData()
 {
+#ifndef USE_LLVM
     m_compileFunctions.clear();
     m_hatPredicateCompileFunctions.clear();
+#endif
     m_monitorNameFunctions.clear();
     m_monitorChangeFunctions.clear();
     m_inputs.clear();
@@ -1733,16 +1747,19 @@ void Engine::clearExtensionData()
 
 IExtension *Engine::blockExtension(const std::string &opcode) const
 {
+#ifndef USE_LLVM
     for (const auto &[ext, data] : m_compileFunctions) {
         auto it = data.find(opcode);
 
         if (it != data.cend())
             return ext;
     }
+#endif // USE_LLVM
 
     return nullptr;
 }
 
+#ifndef USE_LLVM
 BlockComp Engine::resolveBlockCompileFunc(IExtension *extension, const std::string &opcode) const
 {
     if (!extension)
@@ -1776,6 +1793,7 @@ HatPredicateCompileFunc Engine::resolveHatPredicateCompileFunc(IExtension *exten
 
     return nullptr;
 }
+#endif // USE_LLVM
 
 MonitorNameFunc Engine::resolveMonitorNameFunc(IExtension *extension, const std::string &opcode) const
 {
@@ -1864,6 +1882,7 @@ int Engine::resolveFieldValue(IExtension *extension, const std::string &value) c
 
 void Engine::compileMonitor(std::shared_ptr<Monitor> monitor)
 {
+#ifndef USE_LLVM
     Target *target = monitor->sprite() ? static_cast<Target *>(monitor->sprite()) : stage();
     Compiler compiler(this, target);
     auto block = monitor->block();
@@ -1909,6 +1928,7 @@ void Engine::compileMonitor(std::shared_ptr<Monitor> monitor)
 
     for (const std::string &opcode : unsupportedBlocks)
         m_unsupportedBlocks.insert(opcode);
+#endif // USE_LLVM
 }
 
 void Engine::finalize()
diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h
index 69d64835..838936d7 100644
--- a/src/engine/internal/engine.h
+++ b/src/engine/internal/engine.h
@@ -105,8 +105,10 @@ class Engine : public IEngine
         unsigned int functionIndex(BlockFunc f) override;
         const std::vector<BlockFunc> &blockFunctions() const override;
 
+#ifndef USE_LLVM
         void addCompileFunction(IExtension *extension, const std::string &opcode, BlockComp f) override;
         void addHatPredicateCompileFunction(IExtension *extension, const std::string &opcode, HatPredicateCompileFunc f) override;
+#endif
         void addMonitorNameFunction(IExtension *extension, const std::string &opcode, MonitorNameFunc f) override;
         void addMonitorChangeFunction(IExtension *extension, const std::string &opcode, MonitorChangeFunc f) override;
         void addHatBlock(IExtension *extension, const std::string &opcode) override;
@@ -145,8 +147,10 @@ class Engine : public IEngine
 
         const std::vector<std::shared_ptr<Monitor>> &monitors() const override;
         void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) override;
+#ifndef USE_LLVM
         Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) override;
         Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) override;
+#endif
         sigslot::signal<Monitor *> &monitorAdded() override;
         sigslot::signal<Monitor *, IMonitorHandler *> &monitorRemoved() override;
 
@@ -190,8 +194,10 @@ class Engine : public IEngine
 
         void clearExtensionData();
         IExtension *blockExtension(const std::string &opcode) const;
+#ifndef USE_LLVM
         BlockComp resolveBlockCompileFunc(IExtension *extension, const std::string &opcode) const;
         HatPredicateCompileFunc resolveHatPredicateCompileFunc(IExtension *extension, const std::string &opcode) const;
+#endif
         MonitorNameFunc resolveMonitorNameFunc(IExtension *extension, const std::string &opcode) const;
         MonitorChangeFunc resolveMonitorChangeFunc(IExtension *extension, const std::string &opcode) const;
         int resolveInput(IExtension *extension, const std::string &name) const;
@@ -253,8 +259,10 @@ class Engine : public IEngine
         std::recursive_mutex m_eventLoopMutex;
         std::string m_userAgent;
 
+#ifndef USE_LLVM
         std::unordered_map<IExtension *, std::unordered_map<std::string, BlockComp>> m_compileFunctions;
         std::unordered_map<IExtension *, std::unordered_map<std::string, HatPredicateCompileFunc>> m_hatPredicateCompileFunctions;
+#endif
         std::unordered_map<IExtension *, std::unordered_map<std::string, MonitorNameFunc>> m_monitorNameFunctions;
         std::unordered_map<IExtension *, std::unordered_map<std::string, MonitorChangeFunc>> m_monitorChangeFunctions;
         std::unordered_map<IExtension *, std::unordered_map<std::string, int>> m_inputs;
diff --git a/src/scratch/block.cpp b/src/scratch/block.cpp
index 0350ccd4..e1fc3928 100644
--- a/src/scratch/block.cpp
+++ b/src/scratch/block.cpp
@@ -17,12 +17,14 @@ Block::Block(const std::string &id, const std::string &opcode) :
 {
 }
 
+#ifndef USE_LLVM
 /*! Calls the compile function. */
 void Block::compile(Compiler *compiler)
 {
     if (impl->compileFunction)
         return impl->compileFunction(compiler);
 }
+#endif // USE_LLVM
 
 /*! Returns the opcode. */
 const std::string &Block::opcode() const
@@ -30,6 +32,7 @@ const std::string &Block::opcode() const
     return impl->opcode;
 }
 
+#ifndef USE_LLVM
 /*! Returns the compile function. \see <a href="blockSections.html">Block sections</a> */
 BlockComp Block::compileFunction() const
 {
@@ -59,6 +62,7 @@ void Block::setHatPredicateCompileFunction(HatPredicateCompileFunc newHatPredica
 
     impl->hatPredicateCompileFunction = newHatPredicateCompileFunction;
 }
+#endif // USE_LLVM
 
 /*! Returns true if the block can have a block following it. */
 bool Block::mutationHasNext() const
diff --git a/src/scratch/block_p.h b/src/scratch/block_p.h
index a1aecb7b..9f777ecf 100644
--- a/src/scratch/block_p.h
+++ b/src/scratch/block_p.h
@@ -22,8 +22,10 @@ struct BlockPrivate
         BlockPrivate(const BlockPrivate &) = delete;
 
         std::string opcode;
+#ifndef USE_LLVM
         BlockComp compileFunction = nullptr;
         HatPredicateCompileFunc hatPredicateCompileFunction = nullptr;
+#endif
         std::shared_ptr<Block> next = nullptr;
         std::string nextId;
         std::shared_ptr<Block> parent = nullptr;
diff --git a/src/scratch/inputvalue.cpp b/src/scratch/inputvalue.cpp
index 46d4738e..6a15060a 100644
--- a/src/scratch/inputvalue.cpp
+++ b/src/scratch/inputvalue.cpp
@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 
 #include <scratchcpp/inputvalue.h>
+#ifndef USE_LLVM
 #include <scratchcpp/compiler.h>
+#endif
 #include <scratchcpp/block.h>
 #include <scratchcpp/broadcast.h>
 #include <scratchcpp/variable.h>
@@ -25,6 +27,7 @@ InputValue::InputValue(Type type) :
 {
 }
 
+#ifndef USE_LLVM
 /*! Compiles the input value. */
 void InputValue::compile(Compiler *compiler)
 {
@@ -48,6 +51,7 @@ void InputValue::compile(Compiler *compiler)
             break;
     }
 }
+#endif // USE_LLVM
 
 /*! Returns the type of the value. */
 InputValue::Type InputValue::type() const
diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp
index 7434a993..7b7c7202 100644
--- a/test/engine/engine_test.cpp
+++ b/test/engine/engine_test.cpp
@@ -9,7 +9,9 @@
 #include <scratchcpp/keyevent.h>
 #include <scratchcpp/monitor.h>
 #include <scratchcpp/field.h>
+#ifndef USE_LLVM
 #include <scratchcpp/compiler.h>
+#endif
 #include <scratchcpp/script.h>
 #include <scratchcpp/virtualmachine.h>
 #include <scratchcpp/thread.h>
@@ -196,7 +198,6 @@ TEST(EngineTest, StopSignal)
         engine->stop();
     }
 }
-#endif
 
 TEST(EngineTest, CompileAndExecuteMonitors)
 {
@@ -285,6 +286,7 @@ TEST(EngineTest, CompileAndExecuteMonitors)
 
     ScratchConfiguration::removeExtension(extension);
 }
+#endif
 
 TEST(EngineTest, IsRunning)
 {
@@ -1572,6 +1574,7 @@ TEST(EngineTest, Monitors)
     engine.setMonitors({ m1, m2, m3 });
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, CreateMissingMonitors)
 {
     static constexpr const char *VARIABLE_OPCODE = "data_variable";
@@ -1704,6 +1707,7 @@ TEST(EngineTest, CreateMissingMonitors)
         ASSERT_EQ(monitors[6].get(), m7);
     }
 }
+#endif // USE_LLVM
 
 void questionFunction(const std::string &)
 {
@@ -2150,7 +2154,6 @@ TEST(EngineTest, StopBeforeStarting)
     ASSERT_VAR(stage, "test");
     ASSERT_TRUE(GET_VAR(stage, "test")->value().toBool());
 }
-#endif
 
 TEST(EngineTest, NoCrashWhenLoadingUndefinedVariableOrListMonitor)
 {
@@ -2216,6 +2219,7 @@ TEST(EngineTest, NoCrashWithUndefinedVariableOrList)
     ASSERT_EQ(list->id(), "}l+w#Er5y!:*gh~5!3t%");
     ASSERT_TRUE(list->empty());
 }
+#endif
 
 TEST(EngineTest, AlwaysStopCloneThreads)
 {
diff --git a/test/load_project/load_project_test.cpp b/test/load_project/load_project_test.cpp
index 1a3fd10e..d1d48deb 100644
--- a/test/load_project/load_project_test.cpp
+++ b/test/load_project/load_project_test.cpp
@@ -179,6 +179,7 @@ TEST(LoadProjectTest, DownloadDefaultProjectFailure)
     ASSERT_TRUE(p.load());
 }
 
+#ifndef USE_LLVM
 TEST(LoadProjectTest, LoadTestProject)
 {
     int i = 0;
@@ -645,6 +646,7 @@ TEST(LoadProjectTest, LoadTestProject)
         i++;
     }
 }
+#endif // USE_LLVM
 
 TEST(LoadProjectTest, LoadTopLevelReporterProject)
 {
diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h
index d862de61..639eb606 100644
--- a/test/mocks/enginemock.h
+++ b/test/mocks/enginemock.h
@@ -87,8 +87,10 @@ class EngineMock : public IEngine
         MOCK_METHOD(unsigned int, functionIndex, (BlockFunc), (override));
         MOCK_METHOD(const std::vector<BlockFunc> &, blockFunctions, (), (const, override));
 
+#ifndef USE_LLVM
         MOCK_METHOD(void, addCompileFunction, (IExtension *, const std::string &, BlockComp), (override));
         MOCK_METHOD(void, addHatPredicateCompileFunction, (IExtension *, const std::string &, HatPredicateCompileFunc), (override));
+#endif
         MOCK_METHOD(void, addMonitorNameFunction, (IExtension *, const std::string &, MonitorNameFunc), (override));
         MOCK_METHOD(void, addMonitorChangeFunction, (IExtension *, const std::string &, MonitorChangeFunc), (override));
         MOCK_METHOD(void, addHatBlock, (IExtension *, const std::string &), (override));
@@ -127,8 +129,10 @@ class EngineMock : public IEngine
 
         MOCK_METHOD(const std::vector<std::shared_ptr<Monitor>> &, monitors, (), (const, override));
         MOCK_METHOD(void, setMonitors, (const std::vector<std::shared_ptr<Monitor>> &), (override));
+#ifndef USE_LLVM
         MOCK_METHOD(Monitor *, createVariableMonitor, (std::shared_ptr<Variable>, const std::string &, const std::string &, int, BlockComp), (override));
         MOCK_METHOD(Monitor *, createListMonitor, (std::shared_ptr<List>, const std::string &, const std::string &, int, BlockComp), (override));
+#endif
         MOCK_METHOD(sigslot::signal<Monitor *> &, monitorAdded, (), (override));
         MOCK_METHOD((sigslot::signal<Monitor *, IMonitorHandler *> &), monitorRemoved, (), (override));
 
diff --git a/test/scratch_classes/block_test.cpp b/test/scratch_classes/block_test.cpp
index a0b4545c..d184cdc0 100644
--- a/test/scratch_classes/block_test.cpp
+++ b/test/scratch_classes/block_test.cpp
@@ -3,22 +3,27 @@
 #include <scratchcpp/field.h>
 #include <scratchcpp/comment.h>
 #include <scratchcpp/target.h>
+#ifndef USE_LLVM
 #include <scratchcpp/compiler.h>
+#endif
 #include <enginemock.h>
 
 #include "../common.h"
 
 using namespace libscratchcpp;
 
+#ifndef USE_LLVM
 class BlockTestMock
 {
     public:
         MOCK_METHOD(void, compileTest, (Compiler *), ());
 };
+#endif // USE_LLVM
 
 class BlockTest : public testing::Test
 {
     public:
+#ifndef USE_LLVM
         void SetUp() override { m_mockPtr = &m_mock; }
 
         static void compileTest(Compiler *compiler)
@@ -29,6 +34,7 @@ class BlockTest : public testing::Test
 
         static inline BlockTestMock *m_mockPtr = nullptr;
         BlockTestMock m_mock;
+#endif // USE_LLVM
 };
 
 TEST_F(BlockTest, Constructors)
@@ -256,6 +262,7 @@ TEST_F(BlockTest, Target)
     ASSERT_EQ(block.target(), &target);
 }
 
+#ifndef USE_LLVM
 TEST_F(BlockTest, CompileFunction)
 {
     Block block("", "");
@@ -286,6 +293,7 @@ TEST_F(BlockTest, Compile)
     EXPECT_CALL(m_mock, compileTest(&compiler)).Times(1);
     block.compile(&compiler);
 }
+#endif // USE_LLVM
 
 TEST_F(BlockTest, MutationHasNext)
 {
diff --git a/test/scratch_classes/inputvalue_test.cpp b/test/scratch_classes/inputvalue_test.cpp
index 5a55be4d..949ed3e5 100644
--- a/test/scratch_classes/inputvalue_test.cpp
+++ b/test/scratch_classes/inputvalue_test.cpp
@@ -106,6 +106,7 @@ TEST(InputValueTest, ValuePtr)
     ASSERT_EQ(value2.type(), InputValue::Type::Variable);
 }
 
+#ifndef USE_LLVM
 TEST(InputValueTest, Compile)
 {
     EngineMock engine;
@@ -162,3 +163,4 @@ TEST(InputValueTest, Compile)
     ASSERT_EQ(compiler.variables(), std::vector<Variable *>({ variable.get() }));
     ASSERT_EQ(compiler.lists(), std::vector<List *>({ list.get() }));
 }
+#endif // USE_LLVM

From 774022a96ca2f987c0738799ee566cdaaa19c278 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 1 Oct 2024 22:08:04 +0200
Subject: [PATCH 09/94] Add LLVM

---
 CMakeLists.txt             |  7 +++++
 build/HunterPackages.cmake | 31 +++++++++++++++++++++++
 build/LLVM.cmake           | 52 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+)
 create mode 100644 build/HunterPackages.cmake
 create mode 100644 build/LLVM.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e0af9cde..4bb813ca 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,6 +67,7 @@ target_sources(scratchcpp
 
 if (LIBSCRATCHCPP_USE_LLVM)
     target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
+
 else()
     target_sources(scratchcpp
       PUBLIC
@@ -103,6 +104,12 @@ if (LIBSCRATCHCPP_NETWORK_SUPPORT)
     target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_NETWORK_SUPPORT)
 endif()
 
+if (LIBSCRATCHCPP_USE_LLVM)
+    include(build/HunterPackages.cmake)
+    include(build/LLVM.cmake)
+    target_link_libraries(scratchcpp PRIVATE LLVM)
+endif()
+
 target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY)
 target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}")
 target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION_MAJOR=${PROJECT_VERSION_MAJOR})
diff --git a/build/HunterPackages.cmake b/build/HunterPackages.cmake
new file mode 100644
index 00000000..9dc8f703
--- /dev/null
+++ b/build/HunterPackages.cmake
@@ -0,0 +1,31 @@
+# https://layle.me/posts/using-llvm-with-cmake/
+# HUNTER_URL is the URL to the latest source code archive on GitHub
+# HUNTER_SHA1 is the hash of the downloaded archive
+
+set(OLD_PROJECT_NAME ${PROJECT_NAME})
+set(PROJECT_NAME "")
+
+set(HUNTER_URL "https://github.com/scratchcpp/hunter/archive/ee768cdd2c027b5be346f114e726d4b0c4296de6.zip")
+set(HUNTER_SHA1 "4A018750743AC656A859C99C655723EAF68EE038")
+
+set(HUNTER_LLVM_VERSION 19.1.0)
+set(HUNTER_LLVM_CMAKE_ARGS
+    LLVM_ENABLE_CRASH_OVERRIDES=OFF
+    LLVM_ENABLE_ASSERTIONS=ON
+    LLVM_ENABLE_ZLIB=OFF
+    LLVM_ENABLE_RTTI=ON
+    LLVM_BUILD_EXAMPLES=OFF
+    LLVM_BUILD_TOOLS=OFF
+    LLVM_BUILD_LLVM_DYLIB=ON
+    LLVM_INCLUDE_EXAMPLES=OFF
+    LLVM_TARGETS_TO_BUILD=host
+)
+
+set(HUNTER_PACKAGES LLVM)
+
+include(FetchContent)
+message(STATUS "Fetching hunter...")
+FetchContent_Declare(SetupHunter GIT_REPOSITORY https://github.com/cpp-pm/gate)
+FetchContent_MakeAvailable(SetupHunter)
+
+set(PROJECT_NAME ${OLD_PROJECT_NAME})
diff --git a/build/LLVM.cmake b/build/LLVM.cmake
new file mode 100644
index 00000000..bc3b7a03
--- /dev/null
+++ b/build/LLVM.cmake
@@ -0,0 +1,52 @@
+# https://layle.me/posts/using-llvm-with-cmake/
+# This is an INTERFACE target for LLVM, usage:
+#   target_link_libraries(${PROJECT_NAME} <PRIVATE|PUBLIC|INTERFACE> LLVM)
+# The include directories and compile definitions will be properly handled.
+
+set(CMAKE_FOLDER_LLVM "${CMAKE_FOLDER}")
+if(CMAKE_FOLDER)
+    set(CMAKE_FOLDER "${CMAKE_FOLDER}/LLVM")
+else()
+    set(CMAKE_FOLDER "LLVM")
+endif()
+
+# Find LLVM
+find_package(LLVM REQUIRED CONFIG)
+
+message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
+message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
+
+# Split the definitions properly (https://weliveindetail.github.io/blog/post/2017/07/17/notes-setup.html)
+separate_arguments(LLVM_DEFINITIONS)
+
+# Some diagnostics (https://stackoverflow.com/a/17666004/1806760)
+message(STATUS "LLVM libraries: ${LLVM_LIBRARIES}")
+message(STATUS "LLVM includes: ${LLVM_INCLUDE_DIRS}")
+message(STATUS "LLVM definitions: ${LLVM_DEFINITIONS}")
+message(STATUS "LLVM tools: ${LLVM_TOOLS_BINARY_DIR}")
+
+if (NOT TARGET LLVM)
+    add_library(LLVM INTERFACE)
+endif()
+
+target_include_directories(LLVM SYSTEM INTERFACE ${LLVM_INCLUDE_DIRS})
+target_link_libraries(LLVM INTERFACE ${LLVM_AVAILABLE_LIBS})
+target_compile_definitions(LLVM INTERFACE ${LLVM_DEFINITIONS} -DNOMINMAX)
+
+set(CMAKE_FOLDER "${CMAKE_FOLDER_LLVM}")
+unset(CMAKE_FOLDER_LLVM)
+
+# MSVC-specific options
+if(MSVC)
+    # This assumes the installed LLVM was built in Release mode
+    set(CMAKE_C_FLAGS_RELWITHDEBINFO "/ZI /Od /Ob0 /DNDEBUG" CACHE STRING "" FORCE)
+    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/ZI /Od /Ob0 /DNDEBUG" CACHE STRING "" FORCE)
+
+    if(${LLVM_USE_CRT_RELEASE} STREQUAL "MD")
+        set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
+    elseif(${LLVM_USE_CRT_RELEASE} STREQUAL "MT")
+        set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
+    else()
+        message(FATAL_ERROR "Unsupported LLVM_USE_CRT_RELEASE=${LLVM_USE_CRT_RELEASE}")
+    endif()
+endif()

From 4fda69dc3acd1bc4db57616471d8c27ceaca5f19 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 12:14:36 +0200
Subject: [PATCH 10/94] Add ExecutionContext class

---
 CMakeLists.txt                                |  4 +++
 include/scratchcpp/dev/executioncontext.h     | 28 +++++++++++++++++++
 src/dev/CMakeLists.txt                        |  1 +
 src/dev/engine/CMakeLists.txt                 |  6 ++++
 src/dev/engine/executioncontext.cpp           | 19 +++++++++++++
 src/dev/engine/executioncontext_p.cpp         | 10 +++++++
 src/dev/engine/executioncontext_p.h           | 17 +++++++++++
 test/dev/CMakeLists.txt                       |  1 +
 test/dev/executioncontext/CMakeLists.txt      | 12 ++++++++
 .../executioncontext_test.cpp                 | 13 +++++++++
 10 files changed, 111 insertions(+)
 create mode 100644 include/scratchcpp/dev/executioncontext.h
 create mode 100644 src/dev/engine/CMakeLists.txt
 create mode 100644 src/dev/engine/executioncontext.cpp
 create mode 100644 src/dev/engine/executioncontext_p.cpp
 create mode 100644 src/dev/engine/executioncontext_p.h
 create mode 100644 test/dev/executioncontext/CMakeLists.txt
 create mode 100644 test/dev/executioncontext/executioncontext_test.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4bb813ca..804ff305 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,6 +67,10 @@ target_sources(scratchcpp
 
 if (LIBSCRATCHCPP_USE_LLVM)
     target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
+    target_sources(scratchcpp
+      PUBLIC
+        include/scratchcpp/dev/executioncontext.h
+    )
 
 else()
     target_sources(scratchcpp
diff --git a/include/scratchcpp/dev/executioncontext.h b/include/scratchcpp/dev/executioncontext.h
new file mode 100644
index 00000000..7d7ac877
--- /dev/null
+++ b/include/scratchcpp/dev/executioncontext.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "../global.h"
+#include "../spimpl.h"
+
+namespace libscratchcpp
+{
+
+class Target;
+class ExecutionContextPrivate;
+
+/*! \brief The ExecutionContext represents the execution context of a target (can be a clone) with variables, lists, etc. */
+class LIBSCRATCHCPP_EXPORT ExecutionContext
+{
+    public:
+        ExecutionContext(Target *target);
+        ExecutionContext(const ExecutionContext &) = delete;
+        virtual ~ExecutionContext() { }
+
+        Target *target() const;
+
+    private:
+        spimpl::unique_impl_ptr<ExecutionContextPrivate> impl;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/CMakeLists.txt b/src/dev/CMakeLists.txt
index 62785564..696178cd 100644
--- a/src/dev/CMakeLists.txt
+++ b/src/dev/CMakeLists.txt
@@ -1 +1,2 @@
 add_subdirectory(blocks)
+add_subdirectory(engine)
diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
new file mode 100644
index 00000000..ab5bf0e9
--- /dev/null
+++ b/src/dev/engine/CMakeLists.txt
@@ -0,0 +1,6 @@
+target_sources(scratchcpp
+  PRIVATE
+    executioncontext.cpp
+    executioncontext_p.cpp
+    executioncontext_p.h
+)
diff --git a/src/dev/engine/executioncontext.cpp b/src/dev/engine/executioncontext.cpp
new file mode 100644
index 00000000..c45156c6
--- /dev/null
+++ b/src/dev/engine/executioncontext.cpp
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <scratchcpp/dev/executioncontext.h>
+
+#include "executioncontext_p.h"
+
+using namespace libscratchcpp;
+
+/*! Constructs ExecutionContext. */
+ExecutionContext::ExecutionContext(Target *target) :
+    impl(spimpl::make_unique_impl<ExecutionContextPrivate>(target))
+{
+}
+
+/*! Returns the Target of this context. */
+Target *ExecutionContext::target() const
+{
+    return impl->target;
+}
diff --git a/src/dev/engine/executioncontext_p.cpp b/src/dev/engine/executioncontext_p.cpp
new file mode 100644
index 00000000..f2efa1e1
--- /dev/null
+++ b/src/dev/engine/executioncontext_p.cpp
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "executioncontext_p.h"
+
+using namespace libscratchcpp;
+
+ExecutionContextPrivate::ExecutionContextPrivate(Target *target) :
+    target(target)
+{
+}
diff --git a/src/dev/engine/executioncontext_p.h b/src/dev/engine/executioncontext_p.h
new file mode 100644
index 00000000..fd7fad51
--- /dev/null
+++ b/src/dev/engine/executioncontext_p.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+namespace libscratchcpp
+{
+
+class Target;
+
+struct ExecutionContextPrivate
+{
+        ExecutionContextPrivate(Target *target);
+
+        Target *target = nullptr;
+};
+
+} // namespace libscratchcpp
diff --git a/test/dev/CMakeLists.txt b/test/dev/CMakeLists.txt
index 62785564..4e299827 100644
--- a/test/dev/CMakeLists.txt
+++ b/test/dev/CMakeLists.txt
@@ -1 +1,2 @@
 add_subdirectory(blocks)
+add_subdirectory(executioncontext)
diff --git a/test/dev/executioncontext/CMakeLists.txt b/test/dev/executioncontext/CMakeLists.txt
new file mode 100644
index 00000000..ef216403
--- /dev/null
+++ b/test/dev/executioncontext/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_executable(
+  executioncontext_test
+  executioncontext_test.cpp
+)
+
+target_link_libraries(
+  executioncontext_test
+  GTest::gtest_main
+  scratchcpp
+)
+
+gtest_discover_tests(executioncontext_test)
diff --git a/test/dev/executioncontext/executioncontext_test.cpp b/test/dev/executioncontext/executioncontext_test.cpp
new file mode 100644
index 00000000..3429ffef
--- /dev/null
+++ b/test/dev/executioncontext/executioncontext_test.cpp
@@ -0,0 +1,13 @@
+#include <scratchcpp/dev/executioncontext.h>
+#include <scratchcpp/target.h>
+
+#include "../../common.h"
+
+using namespace libscratchcpp;
+
+TEST(ExecutionContextTest, Constructor)
+{
+    Target target;
+    ExecutionContext ctx(&target);
+    ASSERT_EQ(ctx.target(), &target);
+}

From b9e57e4f90f72db3d998814f6f0bfbf6efd29999 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 12:26:30 +0200
Subject: [PATCH 11/94] Add LLVMExecutionContext class

---
 src/dev/engine/CMakeLists.txt                 |  2 ++
 .../engine/internal/llvmexecutioncontext.cpp  | 20 +++++++++++++++
 .../engine/internal/llvmexecutioncontext.h    | 22 ++++++++++++++++
 test/dev/llvm/CMakeLists.txt                  | 14 +++++++++++
 test/dev/llvm/llvmexecutioncontext_test.cpp   | 25 +++++++++++++++++++
 5 files changed, 83 insertions(+)
 create mode 100644 src/dev/engine/internal/llvmexecutioncontext.cpp
 create mode 100644 src/dev/engine/internal/llvmexecutioncontext.h
 create mode 100644 test/dev/llvm/CMakeLists.txt
 create mode 100644 test/dev/llvm/llvmexecutioncontext_test.cpp

diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
index ab5bf0e9..5f772292 100644
--- a/src/dev/engine/CMakeLists.txt
+++ b/src/dev/engine/CMakeLists.txt
@@ -3,4 +3,6 @@ target_sources(scratchcpp
     executioncontext.cpp
     executioncontext_p.cpp
     executioncontext_p.h
+    internal/llvmexecutioncontext.cpp
+    internal/llvmexecutioncontext.h
 )
diff --git a/src/dev/engine/internal/llvmexecutioncontext.cpp b/src/dev/engine/internal/llvmexecutioncontext.cpp
new file mode 100644
index 00000000..5200d9fb
--- /dev/null
+++ b/src/dev/engine/internal/llvmexecutioncontext.cpp
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "llvmexecutioncontext.h"
+
+using namespace libscratchcpp;
+
+LLVMExecutionContext::LLVMExecutionContext(Target *target) :
+    ExecutionContext(target)
+{
+}
+
+size_t LLVMExecutionContext::pos() const
+{
+    return m_pos;
+}
+
+void LLVMExecutionContext::setPos(size_t newPos)
+{
+    m_pos = newPos;
+}
diff --git a/src/dev/engine/internal/llvmexecutioncontext.h b/src/dev/engine/internal/llvmexecutioncontext.h
new file mode 100644
index 00000000..98f91554
--- /dev/null
+++ b/src/dev/engine/internal/llvmexecutioncontext.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/dev/executioncontext.h>
+
+namespace libscratchcpp
+{
+
+class LLVMExecutionContext : public ExecutionContext
+{
+    public:
+        LLVMExecutionContext(Target *target);
+
+        size_t pos() const;
+        void setPos(size_t newPos);
+
+    private:
+        size_t m_pos = 0;
+};
+
+} // namespace libscratchcpp
diff --git a/test/dev/llvm/CMakeLists.txt b/test/dev/llvm/CMakeLists.txt
new file mode 100644
index 00000000..b87eaa53
--- /dev/null
+++ b/test/dev/llvm/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_executable(
+  llvm_test
+  llvmexecutioncontext_test.cpp
+)
+
+target_link_libraries(
+  llvm_test
+  GTest::gtest_main
+  GTest::gmock_main
+  scratchcpp
+  scratchcpp_mocks
+)
+
+gtest_discover_tests(llvm_test)
diff --git a/test/dev/llvm/llvmexecutioncontext_test.cpp b/test/dev/llvm/llvmexecutioncontext_test.cpp
new file mode 100644
index 00000000..9e0423d7
--- /dev/null
+++ b/test/dev/llvm/llvmexecutioncontext_test.cpp
@@ -0,0 +1,25 @@
+#include <scratchcpp/target.h>
+#include <dev/engine/internal/llvmexecutioncontext.h>
+
+#include "../../common.h"
+
+using namespace libscratchcpp;
+
+TEST(LLVMExecutionContextTest, Constructor)
+{
+    Target target;
+    LLVMExecutionContext ctx(&target);
+    ASSERT_EQ(ctx.target(), &target);
+}
+
+TEST(LLVMExecutionContextTest, Pos)
+{
+    LLVMExecutionContext ctx(nullptr);
+    ASSERT_EQ(ctx.pos(), 0);
+
+    ctx.setPos(1);
+    ASSERT_EQ(ctx.pos(), 1);
+
+    ctx.setPos(356);
+    ASSERT_EQ(ctx.pos(), 356);
+}

From 005e47992469269c488ce8850ad90bdde8123d33 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 18:41:18 +0200
Subject: [PATCH 12/94] Add ExecutableCode class

---
 CMakeLists.txt                          |  1 +
 include/scratchcpp/dev/executablecode.h | 43 +++++++++++++++++++++++++
 2 files changed, 44 insertions(+)
 create mode 100644 include/scratchcpp/dev/executablecode.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 804ff305..befee5c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@ if (LIBSCRATCHCPP_USE_LLVM)
     target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
     target_sources(scratchcpp
       PUBLIC
+        include/scratchcpp/dev/executablecode.h
         include/scratchcpp/dev/executioncontext.h
     )
 
diff --git a/include/scratchcpp/dev/executablecode.h b/include/scratchcpp/dev/executablecode.h
new file mode 100644
index 00000000..54efea83
--- /dev/null
+++ b/include/scratchcpp/dev/executablecode.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <memory>
+
+#include "../global.h"
+
+namespace libscratchcpp
+{
+
+class ExecutionContext;
+class Target;
+
+/*! \brief The ExecutableCode class represents the code of a compiled Scratch script. */
+class LIBSCRATCHCPP_EXPORT ExecutableCode
+{
+    public:
+        virtual ~ExecutableCode() { }
+
+        /*! Runs the script until it finishes or yields. */
+        virtual void run(ExecutionContext *context) = 0;
+
+        /*! Stops the code. isFinished() will return true. */
+        virtual void kill(ExecutionContext *context) = 0;
+
+        /*! Resets the code to run from the start. */
+        virtual void reset(ExecutionContext *context) = 0;
+
+        /*! Returns true if the code is stopped or finished. */
+        virtual bool isFinished(ExecutionContext *context) const = 0;
+
+        /*! Pauses the script (when it's executed using run() again) until resolvePromise() is called. */
+        virtual void promise() = 0;
+
+        /*! Resolves the promise and resumes the script. */
+        virtual void resolvePromise() = 0;
+
+        /*! Creates an execution context for the given Target. */
+        virtual std::shared_ptr<ExecutionContext> createExecutionContext(Target *target) const = 0;
+};
+
+} // namespace libscratchcpp

From ca66a7c63cf6a3149aa8b9fda37505ccb2530c11 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 19:03:17 +0200
Subject: [PATCH 13/94] Add code property to Script

---
 include/scratchcpp/script.h     |  8 ++++++++
 src/engine/script.cpp           | 14 ++++++++++++++
 src/engine/script_p.h           |  5 +++++
 test/mocks/executablecodemock.h | 21 +++++++++++++++++++++
 test/script/script_test.cpp     | 13 +++++++++++++
 5 files changed, 61 insertions(+)
 create mode 100644 test/mocks/executablecodemock.h

diff --git a/include/scratchcpp/script.h b/include/scratchcpp/script.h
index dbf82b51..895aaaf7 100644
--- a/include/scratchcpp/script.h
+++ b/include/scratchcpp/script.h
@@ -14,6 +14,9 @@ namespace libscratchcpp
 class Target;
 class Block;
 class IEngine;
+#ifdef USE_LLVM
+class ExecutableCode;
+#endif
 class Value;
 class Thread;
 class Variable;
@@ -34,6 +37,11 @@ class LIBSCRATCHCPP_EXPORT Script
         const std::vector<unsigned int> &bytecodeVector() const;
         void setBytecode(const std::vector<unsigned int> &code);
 
+#ifdef USE_LLVM
+        ExecutableCode *code() const;
+        void setCode(std::shared_ptr<ExecutableCode> code);
+#endif
+
         void setHatPredicateBytecode(const std::vector<unsigned int> &code);
         bool runHatPredicate(Target *target);
 
diff --git a/src/engine/script.cpp b/src/engine/script.cpp
index 3d5a3c85..86114b8c 100644
--- a/src/engine/script.cpp
+++ b/src/engine/script.cpp
@@ -50,6 +50,20 @@ void Script::setBytecode(const std::vector<unsigned int> &code)
     impl->bytecode = impl->bytecodeVector.data();
 }
 
+#ifdef USE_LLVM
+/*! Returns the executable code of the script. */
+ExecutableCode *Script::code() const
+{
+    return impl->code.get();
+}
+
+/*! Sets the executable code of the script. */
+void Script::setCode(std::shared_ptr<ExecutableCode> code)
+{
+    impl->code = code;
+}
+#endif // USE_LLVM
+
 /*! Sets the edge-activated hat predicate bytecode. */
 void Script::setHatPredicateBytecode(const std::vector<unsigned int> &code)
 {
diff --git a/src/engine/script_p.h b/src/engine/script_p.h
index 6d029160..bc8c3029 100644
--- a/src/engine/script_p.h
+++ b/src/engine/script_p.h
@@ -12,6 +12,7 @@ namespace libscratchcpp
 class Target;
 class Block;
 class IEngine;
+class ExecutableCode;
 class Variable;
 class List;
 
@@ -24,6 +25,10 @@ struct ScriptPrivate
         std::vector<unsigned int> bytecodeVector;
         std::vector<unsigned int> hatPredicateBytecodeVector;
 
+#ifdef USE_LLVM
+        std::shared_ptr<ExecutableCode> code;
+#endif
+
         Target *target = nullptr;
         std::shared_ptr<Block> topBlock;
         IEngine *engine = nullptr;
diff --git a/test/mocks/executablecodemock.h b/test/mocks/executablecodemock.h
new file mode 100644
index 00000000..f01b9f97
--- /dev/null
+++ b/test/mocks/executablecodemock.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <scratchcpp/dev/executablecode.h>
+#include <gmock/gmock.h>
+
+using namespace libscratchcpp;
+
+class ExecutableCodeMock : public ExecutableCode
+{
+    public:
+        MOCK_METHOD(void, run, (ExecutionContext *), (override));
+        MOCK_METHOD(void, kill, (ExecutionContext *), (override));
+        MOCK_METHOD(void, reset, (ExecutionContext *), (override));
+
+        MOCK_METHOD(bool, isFinished, (ExecutionContext *), (const, override));
+
+        MOCK_METHOD(void, promise, (), (override));
+        MOCK_METHOD(void, resolvePromise, (), (override));
+
+        MOCK_METHOD(std::shared_ptr<ExecutionContext>, createExecutionContext, (Target *), (const, override));
+};
diff --git a/test/script/script_test.cpp b/test/script/script_test.cpp
index 8096413a..f7cd9e27 100644
--- a/test/script/script_test.cpp
+++ b/test/script/script_test.cpp
@@ -7,6 +7,7 @@
 #include <scratchcpp/variable.h>
 #include <scratchcpp/list.h>
 #include <enginemock.h>
+#include <executablecodemock.h>
 
 #include "../common.h"
 
@@ -31,6 +32,17 @@ TEST_F(ScriptTest, Constructors)
     ASSERT_EQ(script.topBlock(), block);
 }
 
+#ifdef USE_LLVM
+TEST_F(ScriptTest, Code)
+{
+    Script script(nullptr, nullptr, nullptr);
+    ASSERT_EQ(script.code(), nullptr);
+
+    auto code = std::make_shared<ExecutableCodeMock>();
+    script.setCode(code);
+    ASSERT_EQ(script.code(), code.get());
+}
+#else
 TEST_F(ScriptTest, Bytecode)
 {
     Script script(nullptr, nullptr, nullptr);
@@ -248,3 +260,4 @@ TEST_F(ScriptTest, Start)
     EXPECT_CALL(m_engine, deinitClone(clone));
     clone->deleteClone();
 }
+#endif

From 43f3287f268cd5cf547deb5308f31ea14d2a0dd5 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 19:04:26 +0200
Subject: [PATCH 14/94] Script: Disable VirtualMachine code when using LLVM

---
 src/engine/script.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/engine/script.cpp b/src/engine/script.cpp
index 86114b8c..aafef1e8 100644
--- a/src/engine/script.cpp
+++ b/src/engine/script.cpp
@@ -105,6 +105,7 @@ std::shared_ptr<Thread> Script::start()
 std::shared_ptr<Thread> Script::start(Target *target)
 {
     auto thread = std::make_shared<Thread>(target, impl->engine, this);
+#ifndef USE_LLVM
     VirtualMachine *vm = thread->vm();
     vm->setBytecode(impl->bytecode);
     vm->setProcedures(impl->procedures);
@@ -161,6 +162,7 @@ std::shared_ptr<Thread> Script::start(Target *target)
         vm->setVariables(impl->variableValues.data());
         vm->setLists(impl->lists.data());
     }
+#endif
 
     return thread;
 }

From 98fa65b3ad456ae6e475dd7583a3becd679f9d1b Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 20:48:57 +0200
Subject: [PATCH 15/94] Thread: Use ExecutableCode when using LLVM

---
 src/engine/thread.cpp       | 33 +++++++++++++++
 src/engine/thread_p.h       |  9 +++++
 test/thread/thread_test.cpp | 80 +++++++++++++++++++++++++++++++------
 3 files changed, 109 insertions(+), 13 deletions(-)

diff --git a/src/engine/thread.cpp b/src/engine/thread.cpp
index 92a7fa35..2ffe2e56 100644
--- a/src/engine/thread.cpp
+++ b/src/engine/thread.cpp
@@ -2,6 +2,10 @@
 
 #include <scratchcpp/thread.h>
 #include <scratchcpp/virtualmachine.h>
+#ifdef USE_LLVM
+#include <scratchcpp/script.h>
+#include <scratchcpp/dev/executablecode.h>
+#endif
 
 #include "thread_p.h"
 
@@ -12,11 +16,16 @@ Thread::Thread(Target *target, IEngine *engine, Script *script) :
     impl(spimpl::make_unique_impl<ThreadPrivate>(target, engine, script))
 {
     impl->vm = std::make_unique<VirtualMachine>(target, engine, script, this);
+#ifdef USE_LLVM
+    impl->code = impl->script->code();
+    impl->executionContext = impl->code->createExecutionContext(target);
+#endif
 }
 
 /*! Returns the virtual machine of this thread. */
 VirtualMachine *Thread::vm() const
 {
+    // TODO: Remove this method
     return impl->vm.get();
 }
 
@@ -41,35 +50,59 @@ Script *Thread::script() const
 /*! Runs the script until it finishes or yields. */
 void Thread::run()
 {
+#ifdef USE_LLVM
+    impl->code->run(impl->executionContext.get());
+#else
     impl->vm->run();
+#endif
 }
 
 /*! Stops the script. */
 void Thread::kill()
 {
+#ifdef USE_LLVM
+    impl->code->kill(impl->executionContext.get());
+#else
     impl->vm->kill();
+#endif
 }
 
 /*! Resets the script to run from the start. */
 void Thread::reset()
 {
+#ifdef USE_LLVM
+    impl->code->reset(impl->executionContext.get());
+#else
     impl->vm->reset();
+#endif
 }
 
 /*! Returns true if the script is stopped or finished. */
 bool Thread::isFinished() const
 {
+#ifdef USE_LLVM
+    return impl->code->isFinished(impl->executionContext.get());
+#else
     return impl->vm->atEnd();
+#endif
 }
 
 /*! Pauses the script (when it's executed using run() again) until resolvePromise() is called. */
 void Thread::promise()
 {
+#ifdef USE_LLVM
+    impl->code->promise();
+#else
     impl->vm->promise();
+#endif
 }
 
 /*! Resolves the promise and resumes the script. */
 void Thread::resolvePromise()
 {
+#ifdef USE_LLVM
+    impl->code->resolvePromise();
+#else
     impl->vm->resolvePromise();
+#endif
 }
diff --git a/src/engine/thread_p.h b/src/engine/thread_p.h
index f7f9fa82..2fc3968b 100644
--- a/src/engine/thread_p.h
+++ b/src/engine/thread_p.h
@@ -7,6 +7,11 @@
 namespace libscratchcpp
 {
 
+#ifdef USE_LLVM
+class ExecutableCode;
+class ExecutionContext;
+#endif
+
 struct ThreadPrivate
 {
         ThreadPrivate(Target *target, IEngine *engine, Script *script);
@@ -15,6 +20,10 @@ struct ThreadPrivate
         Target *target = nullptr;
         IEngine *engine = nullptr;
         Script *script = nullptr;
+#ifdef USE_LLVM
+        ExecutableCode *code = nullptr;
+        std::shared_ptr<ExecutionContext> executionContext;
+#endif
 };
 
 } // namespace libscratchcpp
diff --git a/test/thread/thread_test.cpp b/test/thread/thread_test.cpp
index fb80cfe4..08f1c943 100644
--- a/test/thread/thread_test.cpp
+++ b/test/thread/thread_test.cpp
@@ -1,26 +1,80 @@
 #include <scratchcpp/thread.h>
 #include <scratchcpp/script.h>
 #include <scratchcpp/virtualmachine.h>
+#include <scratchcpp/dev/executioncontext.h>
 #include <targetmock.h>
 #include <enginemock.h>
+#include <executablecodemock.h>
 
 #include "../common.h"
 
 using namespace libscratchcpp;
 
-TEST(ThreadTest, Constructor)
+using ::testing::Return;
+
+class ThreadTest : public testing::Test
 {
-    TargetMock target;
-    EngineMock engine;
-    Script script(nullptr, nullptr, nullptr);
-    Thread thread(&target, &engine, &script);
-    ASSERT_EQ(thread.target(), &target);
-    ASSERT_EQ(thread.engine(), &engine);
-    ASSERT_EQ(thread.script(), &script);
-    ASSERT_TRUE(thread.vm());
-    ASSERT_EQ(thread.vm()->target(), &target);
-    ASSERT_EQ(thread.vm()->engine(), &engine);
-    ASSERT_EQ(thread.vm()->script(), &script);
+    public:
+        void SetUp() override
+        {
+            m_script = std::make_unique<Script>(nullptr, nullptr, nullptr);
+#ifdef USE_LLVM
+            m_code = std::make_shared<ExecutableCodeMock>();
+            m_script->setCode(m_code);
+            m_ctx = std::make_shared<ExecutionContext>(&m_target);
+            EXPECT_CALL(*m_code, createExecutionContext(&m_target)).WillOnce(Return(m_ctx));
+#endif
+            m_thread = std::make_unique<Thread>(&m_target, &m_engine, m_script.get());
+        }
+
+        std::unique_ptr<Thread> m_thread;
+        TargetMock m_target;
+        EngineMock m_engine;
+        std::unique_ptr<Script> m_script;
+#ifdef USE_LLVM
+        std::shared_ptr<ExecutableCodeMock> m_code;
+        std::shared_ptr<ExecutionContext> m_ctx;
+#endif
+};
+
+TEST_F(ThreadTest, Constructor)
+{
+    ASSERT_EQ(m_thread->target(), &m_target);
+    ASSERT_EQ(m_thread->engine(), &m_engine);
+    ASSERT_EQ(m_thread->script(), m_script.get());
+    ASSERT_TRUE(m_thread->vm());
+    ASSERT_EQ(m_thread->vm()->target(), &m_target);
+    ASSERT_EQ(m_thread->vm()->engine(), &m_engine);
+    ASSERT_EQ(m_thread->vm()->script(), m_script.get());
+}
+
+#ifdef USE_LLVM
+TEST_F(ThreadTest, Run)
+{
+    EXPECT_CALL(*m_code, run(m_ctx.get()));
+    m_thread->run();
+}
+
+TEST_F(ThreadTest, Kill)
+{
+    EXPECT_CALL(*m_code, kill(m_ctx.get()));
+    m_thread->kill();
+}
+
+TEST_F(ThreadTest, Reset)
+{
+    EXPECT_CALL(*m_code, reset(m_ctx.get()));
+    m_thread->reset();
+}
+
+TEST_F(ThreadTest, IsFinished)
+{
+    EXPECT_CALL(*m_code, isFinished(m_ctx.get())).WillOnce(Return(false));
+    ASSERT_FALSE(m_thread->isFinished());
+
+    EXPECT_CALL(*m_code, isFinished(m_ctx.get())).WillOnce(Return(true));
+    ASSERT_TRUE(m_thread->isFinished());
 }
 
-// NOTE: Since VirtualMachine is going to be removed, tests for the other methods will be added later
+// TODO: Test promise() and resolvePromise()
+#endif // USE_LLVM

From 3932538dbb4b93de5d39697e69a0b2c71150259b Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 2 Oct 2024 20:51:22 +0200
Subject: [PATCH 16/94] Add LLVMExecutableCode class

---
 src/dev/engine/CMakeLists.txt                 |   2 +
 .../engine/internal/llvmexecutablecode.cpp    |  90 +++++++++++++
 src/dev/engine/internal/llvmexecutablecode.h  |  42 ++++++
 test/dev/CMakeLists.txt                       |   1 +
 test/dev/llvm/CMakeLists.txt                  |  18 +++
 test/dev/llvm/llvmexecutablecode_test.cpp     | 127 ++++++++++++++++++
 test/dev/llvm/llvmexecutioncontext_test.cpp   |   3 +-
 test/dev/llvm/main.cpp                        |   1 +
 test/dev/llvm/testfunctions.cpp               |   9 ++
 test/dev/llvm/testfunctions.h                 |  11 ++
 test/dev/llvm/testmock.h                      |  16 +++
 11 files changed, 318 insertions(+), 2 deletions(-)
 create mode 100644 src/dev/engine/internal/llvmexecutablecode.cpp
 create mode 100644 src/dev/engine/internal/llvmexecutablecode.h
 create mode 100644 test/dev/llvm/llvmexecutablecode_test.cpp
 create mode 100644 test/dev/llvm/main.cpp
 create mode 100644 test/dev/llvm/testfunctions.cpp
 create mode 100644 test/dev/llvm/testfunctions.h
 create mode 100644 test/dev/llvm/testmock.h

diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
index 5f772292..4aa7be53 100644
--- a/src/dev/engine/CMakeLists.txt
+++ b/src/dev/engine/CMakeLists.txt
@@ -3,6 +3,8 @@ target_sources(scratchcpp
     executioncontext.cpp
     executioncontext_p.cpp
     executioncontext_p.h
+    internal/llvmexecutablecode.cpp
+    internal/llvmexecutablecode.h
     internal/llvmexecutioncontext.cpp
     internal/llvmexecutioncontext.h
 )
diff --git a/src/dev/engine/internal/llvmexecutablecode.cpp b/src/dev/engine/internal/llvmexecutablecode.cpp
new file mode 100644
index 00000000..71e24268
--- /dev/null
+++ b/src/dev/engine/internal/llvmexecutablecode.cpp
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <scratchcpp/dev/executioncontext.h>
+#include <llvm/Support/Error.h>
+#include <iostream>
+
+#include "llvmexecutablecode.h"
+#include "llvmexecutioncontext.h"
+
+using namespace libscratchcpp;
+
+LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module) :
+    m_ctx(std::make_unique<llvm::LLVMContext>()),
+    m_jit(llvm::orc::LLJITBuilder().create())
+{
+    if (!m_jit) {
+        llvm::errs() << "error: failed to create JIT: " << toString(m_jit.takeError()) << "\n";
+        return;
+    }
+
+    if (!module)
+        return;
+
+    std::string name = module->getName().str();
+    auto err = m_jit->get()->addIRModule(llvm::orc::ThreadSafeModule(std::move(module), std::move(m_ctx)));
+
+    if (err) {
+        llvm::errs() << "error: failed to add module '" << name << "' to JIT: " << toString(std::move(err)) << "\n";
+        return;
+    }
+
+    // Lookup functions
+    size_t i = 0;
+
+    while (true) {
+        auto func = m_jit->get()->lookup("f" + std::to_string(i));
+
+        if (func)
+            m_functions.push_back((FunctionType)(func->getValue()));
+        else {
+            // Ignore error
+            llvm::consumeError(func.takeError());
+            break;
+        }
+
+        i++;
+    }
+}
+
+void LLVMExecutableCode::run(ExecutionContext *context)
+{
+    LLVMExecutionContext *ctx = getContext(context);
+
+    if (ctx->pos() < m_functions.size())
+        ctx->setPos(m_functions[ctx->pos()](context->target()));
+}
+
+void LLVMExecutableCode::kill(ExecutionContext *context)
+{
+    getContext(context)->setPos(m_functions.size());
+}
+
+void LLVMExecutableCode::reset(ExecutionContext *context)
+{
+    getContext(context)->setPos(0);
+}
+
+bool LLVMExecutableCode::isFinished(ExecutionContext *context) const
+{
+    return getContext(context)->pos() >= m_functions.size();
+}
+
+void LLVMExecutableCode::promise()
+{
+}
+
+void LLVMExecutableCode::resolvePromise()
+{
+}
+
+std::shared_ptr<ExecutionContext> LLVMExecutableCode::createExecutionContext(Target *target) const
+{
+    return std::make_shared<LLVMExecutionContext>(target);
+}
+
+LLVMExecutionContext *LLVMExecutableCode::getContext(ExecutionContext *context)
+{
+    assert(dynamic_cast<LLVMExecutionContext *>(context));
+    return static_cast<LLVMExecutionContext *>(context);
+}
diff --git a/src/dev/engine/internal/llvmexecutablecode.h b/src/dev/engine/internal/llvmexecutablecode.h
new file mode 100644
index 00000000..c3c04a4f
--- /dev/null
+++ b/src/dev/engine/internal/llvmexecutablecode.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/dev/executablecode.h>
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/ExecutionEngine/Orc/LLJIT.h>
+
+namespace libscratchcpp
+{
+
+class Target;
+class LLVMExecutionContext;
+
+class LLVMExecutableCode : public ExecutableCode
+{
+    public:
+        LLVMExecutableCode(std::unique_ptr<llvm::Module> module);
+
+        void run(ExecutionContext *context) override;
+        void kill(libscratchcpp::ExecutionContext *context) override;
+        void reset(ExecutionContext *context) override;
+
+        bool isFinished(ExecutionContext *context) const override;
+
+        void promise() override;
+        void resolvePromise() override;
+
+        std::shared_ptr<ExecutionContext> createExecutionContext(Target *target) const override;
+
+    private:
+        using FunctionType = size_t (*)(Target *);
+
+        static LLVMExecutionContext *getContext(ExecutionContext *context);
+
+        std::unique_ptr<llvm::LLVMContext> m_ctx;
+        llvm::Expected<std::unique_ptr<llvm::orc::LLJIT>> m_jit;
+
+        std::vector<FunctionType> m_functions;
+};
+
+} // namespace libscratchcpp
diff --git a/test/dev/CMakeLists.txt b/test/dev/CMakeLists.txt
index 4e299827..f50914fb 100644
--- a/test/dev/CMakeLists.txt
+++ b/test/dev/CMakeLists.txt
@@ -1,2 +1,3 @@
 add_subdirectory(blocks)
 add_subdirectory(executioncontext)
+add_subdirectory(llvm)
diff --git a/test/dev/llvm/CMakeLists.txt b/test/dev/llvm/CMakeLists.txt
index b87eaa53..72d53844 100644
--- a/test/dev/llvm/CMakeLists.txt
+++ b/test/dev/llvm/CMakeLists.txt
@@ -1,6 +1,22 @@
+add_library(
+  llvm_test_functions SHARED
+  testfunctions.cpp
+  testfunctions.h
+  testmock.h
+)
+
+target_link_libraries(
+  llvm_test_functions
+  GTest::gtest_main
+  GTest::gmock_main
+  scratchcpp
+)
+
 add_executable(
   llvm_test
+  main.cpp
   llvmexecutioncontext_test.cpp
+  llvmexecutablecode_test.cpp
 )
 
 target_link_libraries(
@@ -9,6 +25,8 @@ target_link_libraries(
   GTest::gmock_main
   scratchcpp
   scratchcpp_mocks
+  llvm_test_functions
+  LLVM
 )
 
 gtest_discover_tests(llvm_test)
diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
new file mode 100644
index 00000000..95548512
--- /dev/null
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -0,0 +1,127 @@
+#include <scratchcpp/target.h>
+#include <dev/engine/internal/llvmexecutablecode.h>
+#include <dev/engine/internal/llvmexecutioncontext.h>
+#include <llvm/Support/TargetSelect.h>
+#include <llvm/IR/IRBuilder.h>
+#include <gmock/gmock.h>
+
+#include "testmock.h"
+#include "testfunctions.h"
+
+using namespace libscratchcpp;
+
+class LLVMExecutableCodeTest : public testing::Test
+{
+    public:
+        void SetUp() override
+        {
+            m_module = std::make_unique<llvm::Module>("test", m_ctx);
+            m_builder = std::make_unique<llvm::IRBuilder<>>(m_ctx);
+            test_function(nullptr, nullptr); // force dependency
+
+            llvm::InitializeNativeTarget();
+            llvm::InitializeNativeTargetAsmPrinter();
+            llvm::InitializeNativeTargetAsmParser();
+        }
+
+        llvm::Function *beginFunction(size_t index)
+        {
+            // size_t f#(Target *)
+            llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder->getInt64Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+            llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f" + std::to_string(index), m_module.get());
+
+            llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
+            m_builder->SetInsertPoint(entry);
+            return func;
+        }
+
+        void endFunction(size_t index)
+        {
+            // Return next function index
+            m_builder->CreateRet(m_builder->getInt64(index + 1));
+        }
+
+        void addTestFunction(llvm::Function *mainFunc)
+        {
+            auto ptrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+            auto func = m_module->getOrInsertFunction("test_function", llvm::FunctionType::get(m_builder->getVoidTy(), { ptrType, ptrType }, false));
+
+            llvm::Constant *mockInt = llvm::ConstantInt::get(llvm::Type::getInt64Ty(m_ctx), (uintptr_t)&m_mock, false);
+            llvm::Constant *mockPtr = llvm::ConstantExpr::getIntToPtr(mockInt, ptrType);
+
+            m_builder->CreateCall(func, { mockPtr, mainFunc->getArg(0) });
+        }
+
+        llvm::LLVMContext m_ctx;
+        std::unique_ptr<llvm::Module> m_module;
+        std::unique_ptr<llvm::IRBuilder<>> m_builder;
+        Target m_target;
+        TestMock m_mock;
+};
+
+TEST_F(LLVMExecutableCodeTest, NoFunctions)
+{
+    LLVMExecutionContext ctx(&m_target);
+    LLVMExecutableCode code(std::move(m_module));
+    ASSERT_TRUE(code.isFinished(&ctx));
+
+    code.run(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+
+    code.kill(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+
+    code.reset(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+}
+
+TEST_F(LLVMExecutableCodeTest, SingleFunction)
+{
+    auto f = beginFunction(0);
+    addTestFunction(f);
+    endFunction(0);
+
+    LLVMExecutionContext ctx(&m_target);
+    LLVMExecutableCode code(std::move(m_module));
+    ASSERT_FALSE(code.isFinished(&ctx));
+
+    EXPECT_CALL(m_mock, f(&m_target));
+    code.run(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+
+    code.kill(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+
+    code.reset(&ctx);
+    ASSERT_FALSE(code.isFinished(&ctx));
+
+    code.kill(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+
+    EXPECT_CALL(m_mock, f).Times(0);
+    code.run(&ctx);
+    ASSERT_TRUE(code.isFinished(&ctx));
+}
+
+TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
+{
+    static const int count = 5;
+
+    for (int i = 0; i < count; i++) {
+        auto f = beginFunction(i);
+        addTestFunction(f);
+        endFunction(i);
+    }
+
+    LLVMExecutionContext ctx(&m_target);
+    LLVMExecutableCode code(std::move(m_module));
+    ASSERT_FALSE(code.isFinished(&ctx));
+
+    for (int i = 0; i < count; i++) {
+        ASSERT_FALSE(code.isFinished(&ctx));
+        EXPECT_CALL(m_mock, f(&m_target));
+        code.run(&ctx);
+    }
+
+    ASSERT_TRUE(code.isFinished(&ctx));
+}
diff --git a/test/dev/llvm/llvmexecutioncontext_test.cpp b/test/dev/llvm/llvmexecutioncontext_test.cpp
index 9e0423d7..4b715c2c 100644
--- a/test/dev/llvm/llvmexecutioncontext_test.cpp
+++ b/test/dev/llvm/llvmexecutioncontext_test.cpp
@@ -1,7 +1,6 @@
 #include <scratchcpp/target.h>
 #include <dev/engine/internal/llvmexecutioncontext.h>
-
-#include "../../common.h"
+#include <gtest/gtest.h>
 
 using namespace libscratchcpp;
 
diff --git a/test/dev/llvm/main.cpp b/test/dev/llvm/main.cpp
new file mode 100644
index 00000000..0eaf481a
--- /dev/null
+++ b/test/dev/llvm/main.cpp
@@ -0,0 +1 @@
+#include "../../common.h"
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
new file mode 100644
index 00000000..ac9be765
--- /dev/null
+++ b/test/dev/llvm/testfunctions.cpp
@@ -0,0 +1,9 @@
+#include "testmock.h"
+
+using namespace libscratchcpp;
+
+extern "C" void test_function(TestMock *mock, Target *target)
+{
+    if (mock)
+        mock->f(target);
+}
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
new file mode 100644
index 00000000..948a91e1
--- /dev/null
+++ b/test/dev/llvm/testfunctions.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace libscratchcpp
+{
+
+class TestMock;
+class Target;
+
+extern "C" void test_function(TestMock *mock, Target *target);
+
+} // namespace libscratchcpp
diff --git a/test/dev/llvm/testmock.h b/test/dev/llvm/testmock.h
new file mode 100644
index 00000000..9b26d1fa
--- /dev/null
+++ b/test/dev/llvm/testmock.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <gmock/gmock.h>
+
+namespace libscratchcpp
+{
+
+class Target;
+
+class TestMock
+{
+    public:
+        MOCK_METHOD(void, f, (Target *));
+};
+
+} // namespace libscratchcpp

From 73ad410c318dd1da1609c408903fbd6d7af5503d Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 3 Oct 2024 20:03:24 +0200
Subject: [PATCH 17/94] Add ICodeBuilder internal class

---
 src/dev/engine/CMakeLists.txt          |  1 +
 src/dev/engine/internal/icodebuilder.h | 38 ++++++++++++++++++++++++++
 2 files changed, 39 insertions(+)
 create mode 100644 src/dev/engine/internal/icodebuilder.h

diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
index 4aa7be53..9ee6710d 100644
--- a/src/dev/engine/CMakeLists.txt
+++ b/src/dev/engine/CMakeLists.txt
@@ -3,6 +3,7 @@ target_sources(scratchcpp
     executioncontext.cpp
     executioncontext_p.cpp
     executioncontext_p.h
+    internal/icodebuilder.h
     internal/llvmexecutablecode.cpp
     internal/llvmexecutablecode.h
     internal/llvmexecutioncontext.cpp
diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
new file mode 100644
index 00000000..c1ea3f0a
--- /dev/null
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <string>
+#include <memory>
+
+namespace libscratchcpp
+{
+
+class Value;
+class Variable;
+class List;
+class ExecutableCode;
+
+class ICodeBuilder
+{
+    public:
+        virtual ~ICodeBuilder() { }
+
+        virtual std::shared_ptr<ExecutableCode> finalize() = 0;
+
+        virtual void addFunctionCall(const std::string &functionName, int argCount, bool returns) = 0;
+        virtual void addConstValue(const Value &value) = 0;
+        virtual void addVariableValue(Variable *variable) = 0;
+        virtual void addListContents(List *list) = 0;
+
+        virtual void beginIfStatement() = 0;
+        virtual void beginElseBranch() = 0;
+        virtual void endIf() = 0;
+
+        virtual void beginLoop() = 0;
+        virtual void endLoop() = 0;
+
+        virtual void yield() = 0;
+};
+
+} // namespace libscratchcpp

From 6a74cf11fc27a3cc096b22aa0692e65395d5f7b5 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 4 Oct 2024 15:11:58 +0200
Subject: [PATCH 18/94] Store const values in LLVMExecutableCode

---
 .../engine/internal/llvmexecutablecode.cpp    | 16 +++++-
 src/dev/engine/internal/llvmexecutablecode.h  |  5 +-
 test/dev/llvm/llvmexecutablecode_test.cpp     | 50 +++++++++++++++++--
 test/dev/llvm/testfunctions.cpp               | 21 ++++++--
 test/dev/llvm/testfunctions.h                 |  7 ++-
 5 files changed, 90 insertions(+), 9 deletions(-)

diff --git a/src/dev/engine/internal/llvmexecutablecode.cpp b/src/dev/engine/internal/llvmexecutablecode.cpp
index 71e24268..031de8d0 100644
--- a/src/dev/engine/internal/llvmexecutablecode.cpp
+++ b/src/dev/engine/internal/llvmexecutablecode.cpp
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 #include <scratchcpp/dev/executioncontext.h>
+#include <scratchcpp/value_functions.h>
 #include <llvm/Support/Error.h>
 #include <iostream>
 
@@ -9,7 +10,7 @@
 
 using namespace libscratchcpp;
 
-LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module) :
+LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module, std::vector<std::unique_ptr<ValueData>> &constValues) :
     m_ctx(std::make_unique<llvm::LLVMContext>()),
     m_jit(llvm::orc::LLJITBuilder().create())
 {
@@ -45,6 +46,19 @@ LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module) :
 
         i++;
     }
+
+    // Move const value pointers (transfer ownership)
+    for (auto &v : constValues)
+        m_constValues.push_back(std::move(v));
+
+    constValues.clear();
+}
+
+LLVMExecutableCode::~LLVMExecutableCode()
+{
+    // Free memory used by const values
+    for (const auto &v : m_constValues)
+        value_free(v.get());
 }
 
 void LLVMExecutableCode::run(ExecutionContext *context)
diff --git a/src/dev/engine/internal/llvmexecutablecode.h b/src/dev/engine/internal/llvmexecutablecode.h
index c3c04a4f..ed4ffdbe 100644
--- a/src/dev/engine/internal/llvmexecutablecode.h
+++ b/src/dev/engine/internal/llvmexecutablecode.h
@@ -3,6 +3,7 @@
 #pragma once
 
 #include <scratchcpp/dev/executablecode.h>
+#include <scratchcpp/valuedata.h>
 #include <llvm/IR/LLVMContext.h>
 #include <llvm/ExecutionEngine/Orc/LLJIT.h>
 
@@ -15,7 +16,8 @@ class LLVMExecutionContext;
 class LLVMExecutableCode : public ExecutableCode
 {
     public:
-        LLVMExecutableCode(std::unique_ptr<llvm::Module> module);
+        LLVMExecutableCode(std::unique_ptr<llvm::Module> module, std::vector<std::unique_ptr<libscratchcpp::ValueData>> &constValues);
+        ~LLVMExecutableCode();
 
         void run(ExecutionContext *context) override;
         void kill(libscratchcpp::ExecutionContext *context) override;
@@ -37,6 +39,7 @@ class LLVMExecutableCode : public ExecutableCode
         llvm::Expected<std::unique_ptr<llvm::orc::LLJIT>> m_jit;
 
         std::vector<FunctionType> m_functions;
+        std::vector<std::unique_ptr<ValueData>> m_constValues;
 };
 
 } // namespace libscratchcpp
diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
index 95548512..c311bc94 100644
--- a/test/dev/llvm/llvmexecutablecode_test.cpp
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -1,4 +1,5 @@
 #include <scratchcpp/target.h>
+#include <scratchcpp/value_functions.h>
 #include <dev/engine/internal/llvmexecutablecode.h>
 #include <dev/engine/internal/llvmexecutioncontext.h>
 #include <llvm/Support/TargetSelect.h>
@@ -52,6 +53,13 @@ class LLVMExecutableCodeTest : public testing::Test
             m_builder->CreateCall(func, { mockPtr, mainFunc->getArg(0) });
         }
 
+        void addTestPrintFunction(llvm::Value *arg1, llvm::Value *arg2)
+        {
+            auto ptrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+            auto func = m_module->getOrInsertFunction("test_print_function", llvm::FunctionType::get(m_builder->getVoidTy(), { ptrType, ptrType }, false));
+            m_builder->CreateCall(func, { arg1, arg2 });
+        }
+
         llvm::LLVMContext m_ctx;
         std::unique_ptr<llvm::Module> m_module;
         std::unique_ptr<llvm::IRBuilder<>> m_builder;
@@ -62,7 +70,8 @@ class LLVMExecutableCodeTest : public testing::Test
 TEST_F(LLVMExecutableCodeTest, NoFunctions)
 {
     LLVMExecutionContext ctx(&m_target);
-    LLVMExecutableCode code(std::move(m_module));
+    std::vector<std::unique_ptr<ValueData>> constValues;
+    LLVMExecutableCode code(std::move(m_module), constValues);
     ASSERT_TRUE(code.isFinished(&ctx));
 
     code.run(&ctx);
@@ -82,7 +91,8 @@ TEST_F(LLVMExecutableCodeTest, SingleFunction)
     endFunction(0);
 
     LLVMExecutionContext ctx(&m_target);
-    LLVMExecutableCode code(std::move(m_module));
+    std::vector<std::unique_ptr<ValueData>> constValues;
+    LLVMExecutableCode code(std::move(m_module), constValues);
     ASSERT_FALSE(code.isFinished(&ctx));
 
     EXPECT_CALL(m_mock, f(&m_target));
@@ -114,7 +124,8 @@ TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
     }
 
     LLVMExecutionContext ctx(&m_target);
-    LLVMExecutableCode code(std::move(m_module));
+    std::vector<std::unique_ptr<ValueData>> constValues;
+    LLVMExecutableCode code(std::move(m_module), constValues);
     ASSERT_FALSE(code.isFinished(&ctx));
 
     for (int i = 0; i < count; i++) {
@@ -125,3 +136,36 @@ TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
 
     ASSERT_TRUE(code.isFinished(&ctx));
 }
+
+TEST_F(LLVMExecutableCodeTest, ConstValues)
+{
+    beginFunction(0);
+    std::vector<std::unique_ptr<ValueData>> constValues;
+
+    for (int i = 0; i < 2; i++) {
+        std::unique_ptr<ValueData> value = std::make_unique<ValueData>();
+        value_init(value.get());
+        value_assign_int(value.get(), i + 1);
+        constValues.push_back(std::move(value));
+    }
+
+    auto ptrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+    llvm::Value *intAddress = m_builder->getInt64((uintptr_t)constValues[0].get());
+    llvm::Value *ptr1 = m_builder->CreateIntToPtr(intAddress, ptrType);
+
+    intAddress = m_builder->getInt64((uintptr_t)constValues[1].get());
+    llvm::Value *ptr2 = m_builder->CreateIntToPtr(intAddress, ptrType);
+
+    addTestPrintFunction(ptr1, ptr2);
+    endFunction(0);
+
+    LLVMExecutionContext ctx(&m_target);
+    LLVMExecutableCode code(std::move(m_module), constValues);
+    ASSERT_TRUE(constValues.empty());
+    ASSERT_FALSE(code.isFinished(&ctx));
+
+    testing::internal::CaptureStdout();
+    code.run(&ctx);
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), "1 2\n");
+    ASSERT_TRUE(code.isFinished(&ctx));
+}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index ac9be765..9c9ee69d 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -1,9 +1,24 @@
+#include <scratchcpp/value_functions.h>
+#include <iostream>
+
+#include "testfunctions.h"
 #include "testmock.h"
 
 using namespace libscratchcpp;
 
-extern "C" void test_function(TestMock *mock, Target *target)
+extern "C"
 {
-    if (mock)
-        mock->f(target);
+    void test_function(TestMock *mock, Target *target)
+    {
+        if (mock)
+            mock->f(target);
+    }
+
+    void test_print_function(ValueData *arg1, ValueData *arg2)
+    {
+        std::string s1, s2;
+        value_toString(arg1, &s1);
+        value_toString(arg2, &s2);
+        std::cout << s1 << " " << s2 << std::endl;
+    }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 948a91e1..44e50a16 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -5,7 +5,12 @@ namespace libscratchcpp
 
 class TestMock;
 class Target;
+class ValueData;
 
-extern "C" void test_function(TestMock *mock, Target *target);
+extern "C"
+{
+    void test_function(TestMock *mock, Target *target);
+    void test_print_function(ValueData *arg1, ValueData *arg2);
+}
 
 } // namespace libscratchcpp

From 994a719a9d697451d63e9c7bfd73a9e878f57829 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 4 Oct 2024 15:46:05 +0200
Subject: [PATCH 19/94] Test createExecutionContext() in LLVM code test

---
 test/dev/llvm/llvmexecutablecode_test.cpp | 68 +++++++++++++----------
 1 file changed, 39 insertions(+), 29 deletions(-)

diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
index c311bc94..5daf5d8f 100644
--- a/test/dev/llvm/llvmexecutablecode_test.cpp
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -67,21 +67,31 @@ class LLVMExecutableCodeTest : public testing::Test
         TestMock m_mock;
 };
 
+TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
+{
+    std::vector<std::unique_ptr<ValueData>> constValues;
+    LLVMExecutableCode code(std::move(m_module), constValues);
+    auto ctx = code.createExecutionContext(&m_target);
+    ASSERT_TRUE(ctx);
+    ASSERT_EQ(ctx->target(), &m_target);
+    ASSERT_TRUE(dynamic_cast<LLVMExecutionContext *>(ctx.get()));
+}
+
 TEST_F(LLVMExecutableCodeTest, NoFunctions)
 {
-    LLVMExecutionContext ctx(&m_target);
     std::vector<std::unique_ptr<ValueData>> constValues;
     LLVMExecutableCode code(std::move(m_module), constValues);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    auto ctx = code.createExecutionContext(&m_target);
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 
-    code.run(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.run(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 
-    code.kill(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.kill(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 
-    code.reset(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.reset(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 }
 
 TEST_F(LLVMExecutableCodeTest, SingleFunction)
@@ -90,27 +100,27 @@ TEST_F(LLVMExecutableCodeTest, SingleFunction)
     addTestFunction(f);
     endFunction(0);
 
-    LLVMExecutionContext ctx(&m_target);
     std::vector<std::unique_ptr<ValueData>> constValues;
     LLVMExecutableCode code(std::move(m_module), constValues);
-    ASSERT_FALSE(code.isFinished(&ctx));
+    auto ctx = code.createExecutionContext(&m_target);
+    ASSERT_FALSE(code.isFinished(ctx.get()));
 
     EXPECT_CALL(m_mock, f(&m_target));
-    code.run(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.run(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 
-    code.kill(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.kill(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 
-    code.reset(&ctx);
-    ASSERT_FALSE(code.isFinished(&ctx));
+    code.reset(ctx.get());
+    ASSERT_FALSE(code.isFinished(ctx.get()));
 
-    code.kill(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.kill(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 
     EXPECT_CALL(m_mock, f).Times(0);
-    code.run(&ctx);
-    ASSERT_TRUE(code.isFinished(&ctx));
+    code.run(ctx.get());
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 }
 
 TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
@@ -123,18 +133,18 @@ TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
         endFunction(i);
     }
 
-    LLVMExecutionContext ctx(&m_target);
     std::vector<std::unique_ptr<ValueData>> constValues;
     LLVMExecutableCode code(std::move(m_module), constValues);
-    ASSERT_FALSE(code.isFinished(&ctx));
+    auto ctx = code.createExecutionContext(&m_target);
+    ASSERT_FALSE(code.isFinished(ctx.get()));
 
     for (int i = 0; i < count; i++) {
-        ASSERT_FALSE(code.isFinished(&ctx));
+        ASSERT_FALSE(code.isFinished(ctx.get()));
         EXPECT_CALL(m_mock, f(&m_target));
-        code.run(&ctx);
+        code.run(ctx.get());
     }
 
-    ASSERT_TRUE(code.isFinished(&ctx));
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 }
 
 TEST_F(LLVMExecutableCodeTest, ConstValues)
@@ -159,13 +169,13 @@ TEST_F(LLVMExecutableCodeTest, ConstValues)
     addTestPrintFunction(ptr1, ptr2);
     endFunction(0);
 
-    LLVMExecutionContext ctx(&m_target);
     LLVMExecutableCode code(std::move(m_module), constValues);
+    auto ctx = code.createExecutionContext(&m_target);
     ASSERT_TRUE(constValues.empty());
-    ASSERT_FALSE(code.isFinished(&ctx));
+    ASSERT_FALSE(code.isFinished(ctx.get()));
 
     testing::internal::CaptureStdout();
-    code.run(&ctx);
+    code.run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), "1 2\n");
-    ASSERT_TRUE(code.isFinished(&ctx));
+    ASSERT_TRUE(code.isFinished(ctx.get()));
 }

From 39c3fb523196c14c6c1881b87046333a2e7c571d Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 4 Oct 2024 16:00:58 +0200
Subject: [PATCH 20/94] Test LLVMExecutableCode with another context

---
 test/dev/llvm/llvmexecutablecode_test.cpp | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
index 5daf5d8f..529dda64 100644
--- a/test/dev/llvm/llvmexecutablecode_test.cpp
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -121,6 +121,21 @@ TEST_F(LLVMExecutableCodeTest, SingleFunction)
     EXPECT_CALL(m_mock, f).Times(0);
     code.run(ctx.get());
     ASSERT_TRUE(code.isFinished(ctx.get()));
+
+    // Test with another context
+    Target anotherTarget;
+    auto anotherCtx = code.createExecutionContext(&anotherTarget);
+    ASSERT_FALSE(code.isFinished(anotherCtx.get()));
+    ASSERT_TRUE(code.isFinished(ctx.get()));
+
+    EXPECT_CALL(m_mock, f(&anotherTarget));
+    code.run(anotherCtx.get());
+    ASSERT_TRUE(code.isFinished(anotherCtx.get()));
+    ASSERT_TRUE(code.isFinished(ctx.get()));
+
+    code.reset(ctx.get());
+    ASSERT_TRUE(code.isFinished(anotherCtx.get()));
+    ASSERT_FALSE(code.isFinished(ctx.get()));
 }
 
 TEST_F(LLVMExecutableCodeTest, MultipleFunctions)

From 49cf1a03be86a54536b39e8a3a8644d40bc8835e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 4 Oct 2024 16:03:17 +0200
Subject: [PATCH 21/94] Add LLVMCodeBuilder class

---
 CMakeLists.txt                              |   4 +
 src/dev/engine/CMakeLists.txt               |   2 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 291 ++++++++++++++++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  96 +++++++
 test/dev/llvm/CMakeLists.txt                |   2 +
 test/dev/llvm/llvmcodebuilder_test.cpp      | 105 +++++++
 test/dev/llvm/testfunctions.cpp             |  52 ++++
 test/dev/llvm/testfunctions.h               |   7 +
 8 files changed, 559 insertions(+)
 create mode 100644 src/dev/engine/internal/llvmcodebuilder.cpp
 create mode 100644 src/dev/engine/internal/llvmcodebuilder.h
 create mode 100644 test/dev/llvm/llvmcodebuilder_test.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index befee5c3..f30d7038 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,7 @@ option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON)
 option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON)
 option(LIBSCRATCHCPP_COMPUTED_GOTO "Support for computed goto" ON)
 option(LIBSCRATCHCPP_USE_LLVM "Compile scripts to LLVM IR (work in progress)" OFF)
+option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF)
 
 if (NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
 	# Computed goto not supported on anything except GCC
@@ -73,6 +74,9 @@ if (LIBSCRATCHCPP_USE_LLVM)
         include/scratchcpp/dev/executioncontext.h
     )
 
+    if(LIBSCRATCHCPP_PRINT_LLVM_IR)
+        target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR)
+    endif()
 else()
     target_sources(scratchcpp
       PUBLIC
diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
index 9ee6710d..8e69321c 100644
--- a/src/dev/engine/CMakeLists.txt
+++ b/src/dev/engine/CMakeLists.txt
@@ -4,6 +4,8 @@ target_sources(scratchcpp
     executioncontext_p.cpp
     executioncontext_p.h
     internal/icodebuilder.h
+    internal/llvmcodebuilder.cpp
+    internal/llvmcodebuilder.h
     internal/llvmexecutablecode.cpp
     internal/llvmexecutablecode.h
     internal/llvmexecutioncontext.cpp
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
new file mode 100644
index 00000000..997865fb
--- /dev/null
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <llvm/Support/TargetSelect.h>
+#include <llvm/IR/Verifier.h>
+#include <llvm/ExecutionEngine/Orc/LLJIT.h>
+#include <scratchcpp/value.h>
+
+#include "llvmcodebuilder.h"
+#include "llvmexecutablecode.h"
+
+using namespace libscratchcpp;
+
+LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id) :
+    m_id(id),
+    m_module(std::make_unique<llvm::Module>(id, m_ctx)),
+    m_builder(m_ctx)
+{
+    llvm::InitializeNativeTarget();
+    llvm::InitializeNativeTargetAsmPrinter();
+    llvm::InitializeNativeTargetAsmParser();
+
+    m_constValues.push_back({});
+    m_regs.push_back({});
+    initTypes();
+}
+
+LLVMCodeBuilder::~LLVMCodeBuilder()
+{
+    for (const auto &values : m_constValues) {
+        for (const auto &v : values) {
+            if (v)
+                value_free(v.get());
+        }
+    }
+}
+
+std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
+{
+    size_t functionIndex = 0;
+    llvm::Function *currentFunc = beginFunction(functionIndex);
+
+    // Execute recorded steps
+    for (const Step &step : m_steps) {
+        switch (step.type) {
+            case Step::Type::FunctionCall: {
+                std::vector<llvm::Type *> types;
+                std::vector<llvm::Value *> args;
+
+                // Add target pointer arg
+                assert(currentFunc->arg_size() == 1);
+                types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
+                args.push_back(currentFunc->getArg(0));
+
+                // Add return value arg if the function returns
+                if (step.functionReturns) {
+                    types.push_back(m_valueDataType->getPointerTo());
+                    args.push_back(m_regs[functionIndex][step.functionReturnRegIndex]->value);
+                }
+
+                // Args
+                for (auto &arg : step.args) {
+                    types.push_back(m_valueDataType->getPointerTo());
+                    args.push_back(arg->value);
+                }
+
+                m_builder.CreateCall(resolveFunction(step.functionName, llvm::FunctionType::get(m_builder.getVoidTy(), types, false)), args);
+                break;
+            }
+
+            case Step::Type::Yield:
+                endFunction(currentFunc, functionIndex);
+                currentFunc = beginFunction(++functionIndex);
+                break;
+        }
+    }
+
+    endFunction(currentFunc, functionIndex);
+
+#ifdef PRINT_LLVM_IR
+    std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl;
+    m_module->print(llvm::outs(), nullptr);
+    std::cout << "==============" << std::endl << std::endl;
+#endif
+
+    std::vector<std::unique_ptr<ValueData>> constValues;
+
+    for (auto &values : m_constValues) {
+        for (auto &v : values)
+            constValues.push_back(std::move(v));
+    }
+
+    m_constValues.clear();
+    return std::make_shared<LLVMExecutableCode>(std::move(m_module), constValues);
+}
+
+void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, int argCount, bool returns)
+{
+    Step step(Step::Type::FunctionCall);
+    step.functionName = functionName;
+
+    assert(m_tmpRegs.size() >= argCount);
+
+    for (size_t i = m_tmpRegs.size() - argCount; i < m_tmpRegs.size(); i++)
+        step.args.push_back(m_tmpRegs[i]);
+
+    m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
+
+    if (returns) {
+        step.functionReturns = true;
+        auto reg = std::make_shared<Register>();
+        step.functionReturnRegIndex = m_regs[m_currentFunction].size();
+        m_regs[m_currentFunction].push_back(reg);
+        m_tmpRegs.push_back(reg);
+    }
+
+    m_steps.push_back(step);
+}
+
+void LLVMCodeBuilder::addConstValue(const Value &value)
+{
+    auto reg = std::make_shared<Register>();
+    reg->isConstValue = true;
+    reg->constValueIndex = m_constValues[m_currentFunction].size();
+    m_regs[m_currentFunction].push_back(reg);
+    m_tmpRegs.push_back(reg);
+
+    std::unique_ptr<ValueData> v = std::make_unique<ValueData>();
+    value_init(v.get());
+    value_assign_copy(v.get(), &value.data());
+    m_constValues[m_currentFunction].push_back(std::move(v));
+}
+
+void LLVMCodeBuilder::addVariableValue(Variable *variable)
+{
+}
+
+void LLVMCodeBuilder::addListContents(List *list)
+{
+}
+
+void LLVMCodeBuilder::beginIfStatement()
+{
+}
+
+void LLVMCodeBuilder::beginElseBranch()
+{
+}
+
+void LLVMCodeBuilder::endIf()
+{
+}
+
+void LLVMCodeBuilder::beginLoop()
+{
+}
+
+void LLVMCodeBuilder::endLoop()
+{
+}
+
+void LLVMCodeBuilder::yield()
+{
+    m_steps.push_back({ Step::Type::Yield });
+    m_currentFunction++;
+
+    assert(m_currentFunction == m_constValues.size());
+    m_constValues.push_back({});
+
+    assert(m_currentFunction == m_regs.size());
+    m_regs.push_back({});
+}
+
+void LLVMCodeBuilder::initTypes()
+{
+    // Create the ValueData struct
+    llvm::Type *intType = llvm::Type::getInt64Ty(m_ctx);                                 // long (intValue)
+    llvm::Type *doubleType = llvm::Type::getDoubleTy(m_ctx);                             // double (doubleValue)
+    llvm::Type *boolType = llvm::Type::getInt1Ty(m_ctx);                                 // bool (boolValue)
+    llvm::Type *stringPtrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); // char* (stringValue)
+
+    // Create the union type (largest type size should dominate)
+    llvm::StructType *unionType = llvm::StructType::create(m_ctx, "union");
+    unionType->setBody({ intType, doubleType, boolType, stringPtrType });
+
+    // Create the full struct type
+    llvm::Type *valueType = llvm::Type::getInt32Ty(m_ctx); // Assuming ValueType is a 32-bit enum
+    llvm::Type *sizeType = llvm::Type::getInt64Ty(m_ctx);  // size_t
+
+    // Combine them into the full struct
+    m_valueDataType = llvm::StructType::create(m_ctx, "ValueData");
+    m_valueDataType->setBody({ unionType, valueType, sizeType });
+}
+
+llvm::Function *LLVMCodeBuilder::beginFunction(size_t index)
+{
+    // size_t f#(Target *)
+    llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder.getInt64Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+    llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f" + std::to_string(index), m_module.get());
+
+    llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
+    m_builder.SetInsertPoint(entry);
+
+    // Add const value pointers
+    const auto &constValues = m_constValues[index];
+    std::vector<llvm::Value *> constPtrs;
+
+    for (size_t i = 0; i < constValues.size(); i++) {
+        llvm::Value *intAddress = m_builder.getInt64((uintptr_t)constValues[i].get());
+        llvm::Value *ptr = m_builder.CreateIntToPtr(intAddress, m_valueDataType->getPointerTo());
+        constPtrs.push_back(ptr);
+    }
+
+    // Add registers
+    auto &regs = m_regs[index];
+    size_t regIndex = 0;
+
+    for (auto &reg : regs) {
+        if (reg->isConstValue) {
+            // Do not allocate space for existing constant values
+            reg->value = constPtrs[reg->constValueIndex];
+        } else {
+            const std::string name = "r" + std::to_string(regIndex);
+
+            llvm::Value *valueData = m_builder.CreateAlloca(m_valueDataType, nullptr, name);
+            m_builder.CreateCall(resolve_value_init(), { valueData });
+
+            reg->value = valueData;
+            regIndex++;
+        }
+    }
+
+    return func;
+}
+
+void LLVMCodeBuilder::endFunction(llvm::Function *func, size_t index)
+{
+    if (!m_tmpRegs.empty()) {
+        std::cout
+            << "warning: " << m_tmpRegs.size() << " registers were leaked by script '" << m_module->getName().str() << "', function '" << func->getName().str()
+            << "' (if you see this as a regular user, this is a bug and should be reported)" << std::endl;
+    }
+
+    // Return next function index
+    m_builder.CreateRet(m_builder.getInt64(index + 1));
+
+    if (llvm::verifyFunction(*func, &llvm::errs())) {
+        llvm::errs() << "error: LLVM function verficiation failed!\n";
+        llvm::errs() << "script hat ID: " << m_id << "\n";
+        llvm::errs() << "function name: " << func->getName().data() << "\n";
+    }
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type)
+{
+    return m_module->getOrInsertFunction(name, type);
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_init()
+{
+    return resolveFunction("value_init", llvm::FunctionType::get(m_builder.getVoidTy(), m_valueDataType->getPointerTo(), false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_free()
+{
+    return resolveFunction("value_free", llvm::FunctionType::get(m_builder.getVoidTy(), m_valueDataType->getPointerTo(), false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_long()
+{
+    return resolveFunction("value_assign_long", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt64Ty() }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_double()
+{
+    return resolveFunction("value_assign_double", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getDoubleTy() }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_bool()
+{
+    return resolveFunction("value_assign_double", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt1Ty() }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_cstring()
+{
+    return resolveFunction("value_assign_cstring", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0) }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
+{
+    return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
+}
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
new file mode 100644
index 00000000..3f0a3f55
--- /dev/null
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <scratchcpp/valuedata.h>
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/IRBuilder.h>
+
+#include "icodebuilder.h"
+
+namespace libscratchcpp
+{
+
+class Target;
+
+class LLVMCodeBuilder : public ICodeBuilder
+{
+    public:
+        LLVMCodeBuilder(const std::string &id);
+        ~LLVMCodeBuilder();
+
+        std::shared_ptr<ExecutableCode> finalize() override;
+
+        void addFunctionCall(const std::string &functionName, int argCount, bool returns) override;
+        void addConstValue(const Value &value) override;
+        void addVariableValue(Variable *variable) override;
+        void addListContents(List *list) override;
+
+        void beginIfStatement() override;
+        void beginElseBranch() override;
+        void endIf() override;
+
+        void beginLoop() override;
+        void endLoop() override;
+
+        void yield() override;
+
+    private:
+        struct Register
+        {
+                llvm::Value *value = nullptr;
+                bool isConstValue = false;
+                size_t constValueIndex = 0;
+        };
+
+        struct Step
+        {
+                enum class Type
+                {
+                    FunctionCall,
+                    Yield
+                };
+
+                Step(Type type) :
+                    type(type)
+                {
+                }
+
+                Type type;
+                std::string functionName;
+                std::vector<std::shared_ptr<Register>> args;
+                bool functionReturns = false;
+                size_t functionReturnRegIndex = 0;
+        };
+
+        void initTypes();
+        llvm::Function *beginFunction(size_t index);
+        void endFunction(llvm::Function *func, size_t index);
+
+        llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type);
+        llvm::FunctionCallee resolve_value_init();
+        llvm::FunctionCallee resolve_value_free();
+        llvm::FunctionCallee resolve_value_assign_long();
+        llvm::FunctionCallee resolve_value_assign_double();
+        llvm::FunctionCallee resolve_value_assign_bool();
+        llvm::FunctionCallee resolve_value_assign_cstring();
+        llvm::FunctionCallee resolve_value_assign_special();
+
+        std::string m_id;
+        llvm::LLVMContext m_ctx;
+        std::unique_ptr<llvm::Module> m_module;
+        llvm::IRBuilder<> m_builder;
+
+        llvm::StructType *m_valueDataType = nullptr;
+
+        std::vector<Step> m_steps;
+        size_t m_currentFunction = 0;
+        std::vector<std::vector<std::unique_ptr<ValueData>>> m_constValues;
+        std::vector<std::vector<std::shared_ptr<Register>>> m_regs;
+        std::vector<std::shared_ptr<Register>> m_tmpRegs;
+
+        std::shared_ptr<ExecutableCode> m_output;
+};
+
+} // namespace libscratchcpp
diff --git a/test/dev/llvm/CMakeLists.txt b/test/dev/llvm/CMakeLists.txt
index 72d53844..abfc4d98 100644
--- a/test/dev/llvm/CMakeLists.txt
+++ b/test/dev/llvm/CMakeLists.txt
@@ -10,6 +10,7 @@ target_link_libraries(
   GTest::gtest_main
   GTest::gmock_main
   scratchcpp
+  scratchcpp_mocks
 )
 
 add_executable(
@@ -17,6 +18,7 @@ add_executable(
   main.cpp
   llvmexecutioncontext_test.cpp
   llvmexecutablecode_test.cpp
+  llvmcodebuilder_test.cpp
 )
 
 target_link_libraries(
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
new file mode 100644
index 00000000..ea3e20ac
--- /dev/null
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -0,0 +1,105 @@
+#include <scratchcpp/value.h>
+#include <scratchcpp/dev/executablecode.h>
+#include <dev/engine/internal/llvmcodebuilder.h>
+#include <gmock/gmock.h>
+#include <targetmock.h>
+
+#include "testfunctions.h"
+
+using namespace libscratchcpp;
+
+class LLVMCodeBuilderTest : public testing::Test
+{
+    public:
+        void SetUp() override
+        {
+            m_builder = std::make_unique<LLVMCodeBuilder>("test");
+            test_function(nullptr, nullptr); // force dependency
+        }
+
+        std::unique_ptr<LLVMCodeBuilder> m_builder;
+        TargetMock m_target; // NOTE: isStage() is used for call expectations
+};
+
+TEST_F(LLVMCodeBuilderTest, FunctionCalls)
+{
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+
+    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+
+    m_builder->addConstValue(1);
+    m_builder->addFunctionCall("test_function_1_arg_ret", 1, true);
+    m_builder->addConstValue(2);
+    m_builder->addConstValue(3);
+    m_builder->addFunctionCall("test_function_3_args", 3, false);
+
+    m_builder->addConstValue("test");
+    m_builder->addConstValue(4);
+    m_builder->addConstValue(5);
+    m_builder->addFunctionCall("test_function_3_args_ret", 3, true);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "no_args\n"
+        "no_args_ret\n"
+        "1_arg no_args_output\n"
+        "1_arg_ret 1\n"
+        "3_args 1_arg_output 2 3\n"
+        "3_args test 4 5\n"
+        "1_arg 3_args_output\n";
+
+    EXPECT_CALL(m_target, isStage()).Times(7);
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
+TEST_F(LLVMCodeBuilderTest, Yield)
+{
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+
+    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+
+    m_builder->yield();
+
+    m_builder->addConstValue(1);
+    m_builder->addFunctionCall("test_function_1_arg_ret", 1, true);
+    m_builder->addConstValue(2);
+    m_builder->addConstValue(3);
+    m_builder->addFunctionCall("test_function_3_args", 3, false);
+
+    m_builder->addConstValue("test");
+    m_builder->addConstValue(4);
+    m_builder->addConstValue(5);
+    m_builder->addFunctionCall("test_function_3_args_ret", 3, true);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected1 =
+        "no_args\n"
+        "no_args_ret\n"
+        "1_arg no_args_output\n";
+
+    EXPECT_CALL(m_target, isStage()).Times(3);
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
+    ASSERT_FALSE(code->isFinished(ctx.get()));
+
+    static const std::string expected2 =
+        "1_arg_ret 1\n"
+        "3_args 1_arg_output 2 3\n"
+        "3_args test 4 5\n"
+        "1_arg 3_args_output\n";
+
+    EXPECT_CALL(m_target, isStage()).Times(4);
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2);
+    ASSERT_TRUE(code->isFinished(ctx.get()));
+}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index 9c9ee69d..7de1c780 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -1,5 +1,6 @@
 #include <scratchcpp/value_functions.h>
 #include <iostream>
+#include <targetmock.h>
 
 #include "testfunctions.h"
 #include "testmock.h"
@@ -21,4 +22,55 @@ extern "C"
         value_toString(arg2, &s2);
         std::cout << s1 << " " << s2 << std::endl;
     }
+
+    void test_function_no_args(Target *target)
+    {
+        target->isStage();
+        std::cout << "no_args" << std::endl;
+    }
+
+    void test_function_no_args_ret(Target *target, ValueData *ret)
+    {
+        target->isStage();
+        std::cout << "no_args_ret" << std::endl;
+        value_assign_cstring(ret, "no_args_output");
+    }
+
+    void test_function_1_arg(Target *target, const ValueData *arg1)
+    {
+        target->isStage();
+        std::string s;
+        value_toString(arg1, &s);
+        std::cout << "1_arg " << s << std::endl;
+    }
+
+    void test_function_1_arg_ret(Target *target, ValueData *ret, const ValueData *arg1)
+    {
+        target->isStage();
+        std::string s;
+        value_toString(arg1, &s);
+        std::cout << "1_arg_ret " << s << std::endl;
+        value_assign_cstring(ret, "1_arg_output");
+    }
+
+    void test_function_3_args(Target *target, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3)
+    {
+        target->isStage();
+        std::string s1, s2, s3;
+        value_toString(arg1, &s1);
+        value_toString(arg2, &s2);
+        value_toString(arg3, &s3);
+        std::cout << "3_args " << s1 << " " << s2 << " " << s3 << std::endl;
+    }
+
+    void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3)
+    {
+        target->isStage();
+        std::string s1, s2, s3;
+        value_toString(arg1, &s1);
+        value_toString(arg2, &s2);
+        value_toString(arg3, &s3);
+        std::cout << "3_args " << s1 << " " << s2 << " " << s3 << std::endl;
+        value_assign_cstring(ret, "3_args_output");
+    }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 44e50a16..96acefa9 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -11,6 +11,13 @@ extern "C"
 {
     void test_function(TestMock *mock, Target *target);
     void test_print_function(ValueData *arg1, ValueData *arg2);
+
+    void test_function_no_args(Target *target);
+    void test_function_no_args_ret(Target *target, ValueData *ret);
+    void test_function_1_arg(Target *target, const ValueData *arg1);
+    void test_function_1_arg_ret(Target *target, ValueData *ret, const ValueData *arg1);
+    void test_function_3_args(Target *target, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
+    void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
 }
 
 } // namespace libscratchcpp

From ad875cd0e7fb342537c5c5abe18aa4c05ce22d1e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 4 Oct 2024 21:37:00 +0200
Subject: [PATCH 22/94] fix #583: Add support for "stop other scripts in stage"
 block

---
 src/blocks/controlblocks.cpp        | 1 +
 test/blocks/control_blocks_test.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/blocks/controlblocks.cpp b/src/blocks/controlblocks.cpp
index ca3c4884..ab5aab69 100644
--- a/src/blocks/controlblocks.cpp
+++ b/src/blocks/controlblocks.cpp
@@ -59,6 +59,7 @@ void ControlBlocks::registerBlocks(IEngine *engine)
     engine->addFieldValue(this, "all", StopAll);
     engine->addFieldValue(this, "this script", StopThisScript);
     engine->addFieldValue(this, "other scripts in sprite", StopOtherScriptsInSprite);
+    engine->addFieldValue(this, "other scripts in stage", StopOtherScriptsInSprite);
 }
 
 void ControlBlocks::compileRepeatForever(Compiler *compiler)
diff --git a/test/blocks/control_blocks_test.cpp b/test/blocks/control_blocks_test.cpp
index 6bb5e1ae..cee4029e 100644
--- a/test/blocks/control_blocks_test.cpp
+++ b/test/blocks/control_blocks_test.cpp
@@ -176,6 +176,7 @@ TEST_F(ControlBlocksTest, RegisterBlocks)
     EXPECT_CALL(m_engineMock, addFieldValue(m_extension.get(), "all", ControlBlocks::StopAll));
     EXPECT_CALL(m_engineMock, addFieldValue(m_extension.get(), "this script", ControlBlocks::StopThisScript));
     EXPECT_CALL(m_engineMock, addFieldValue(m_extension.get(), "other scripts in sprite", ControlBlocks::StopOtherScriptsInSprite));
+    EXPECT_CALL(m_engineMock, addFieldValue(m_extension.get(), "other scripts in stage", ControlBlocks::StopOtherScriptsInSprite));
 
     m_extension->registerBlocks(&m_engineMock);
 }

From 82623b8a1af0d0b21d2e81c75cba0907fa523816 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 4 Oct 2024 23:13:22 +0200
Subject: [PATCH 23/94] fix #578: Wait for broadcasts with different case

---
 src/engine/internal/engine.cpp                 |  11 ++++++++++-
 test/engine/engine_test.cpp                    |  17 +++++++++++++++++
 ...578_broadcast_and_wait_case_insensitive.sb3 | Bin 0 -> 996292 bytes
 3 files changed, 27 insertions(+), 1 deletion(-)
 create mode 100644 test/regtest_projects/578_broadcast_and_wait_case_insensitive.sb3

diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp
index 6a15c9da..c970a68d 100644
--- a/src/engine/internal/engine.cpp
+++ b/src/engine/internal/engine.cpp
@@ -539,6 +539,7 @@ void Engine::step()
 
     // Resolve stopped broadcast scripts
     std::vector<Broadcast *> resolved;
+    std::vector<Thread *> resolvedThreads;
 
     for (const auto &[broadcast, senderThread] : m_broadcastSenders) {
         std::unordered_map<Broadcast *, std::vector<Script *>> *broadcastMap = nullptr;
@@ -566,13 +567,21 @@ void Engine::step()
             }
         }
 
-        if (!found) {
+        if (found) {
+            // If a broadcast with the same name but different case
+            // was considered stopped before, restore the promise.
+            if (std::find(resolvedThreads.begin(), resolvedThreads.end(), senderThread) != resolvedThreads.end()) {
+                senderThread->promise();
+                resolvedThreads.erase(std::remove(resolvedThreads.begin(), resolvedThreads.end(), senderThread), resolvedThreads.end());
+            }
+        } else {
             Thread *th = senderThread;
 
             if (std::find_if(m_threads.begin(), m_threads.end(), [th](std::shared_ptr<Thread> thread) { return thread.get() == th; }) != m_threads.end())
                 th->resolvePromise();
 
             resolved.push_back(broadcast);
+            resolvedThreads.push_back(th);
         }
     }
 
diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp
index 3b22b0a2..304ce451 100644
--- a/test/engine/engine_test.cpp
+++ b/test/engine/engine_test.cpp
@@ -2249,3 +2249,20 @@ TEST(EngineTest, BroadcastStopsWaitBlocks)
     ASSERT_VAR(stage, "backdrop_passed");
     ASSERT_TRUE(GET_VAR(stage, "backdrop_passed")->value().toBool());
 }
+
+TEST(EngineTest, BroadcastAndWaitCaseInsensitive)
+{
+    // Regtest for #578
+    Project p("regtest_projects/578_broadcast_and_wait_case_insensitive.sb3");
+    ASSERT_TRUE(p.load());
+
+    auto engine = p.engine();
+
+    Stage *stage = engine->stage();
+    ASSERT_TRUE(stage);
+
+    engine->run();
+
+    ASSERT_VAR(stage, "passed");
+    ASSERT_TRUE(GET_VAR(stage, "passed")->value().toBool());
+}
diff --git a/test/regtest_projects/578_broadcast_and_wait_case_insensitive.sb3 b/test/regtest_projects/578_broadcast_and_wait_case_insensitive.sb3
new file mode 100644
index 0000000000000000000000000000000000000000..ecc70bcfa26e1d4e84647e0b3b7d643ade086cec
GIT binary patch

From 0dbe6a0506fff756a7a4050445a36e898a398abb Mon Sep 17 00:00:00 2001
From: aspizu <aspizu@protonmail.com>
Date: Sat, 5 Oct 2024 03:39:24 +0530
Subject: [PATCH 24/94] Implement method randintExcept for RandomGenerator.

---
 src/engine/internal/irandomgenerator.h        |  1 +
 src/engine/internal/randomgenerator.cpp       | 13 +++++++++++
 src/engine/internal/randomgenerator.h         |  1 +
 test/randomgenerator/randomgenerator_test.cpp | 22 +++++++++++++++++++
 4 files changed, 37 insertions(+)

diff --git a/src/engine/internal/irandomgenerator.h b/src/engine/internal/irandomgenerator.h
index c8d4a177..2228c933 100644
--- a/src/engine/internal/irandomgenerator.h
+++ b/src/engine/internal/irandomgenerator.h
@@ -12,6 +12,7 @@ class IRandomGenerator
 
         virtual long randint(long start, long end) const = 0;
         virtual double randintDouble(double start, double end) const = 0;
+        virtual long randintExcept(long start, long end, long except) const = 0;
 };
 
 } // namespace libscratchcpp
diff --git a/src/engine/internal/randomgenerator.cpp b/src/engine/internal/randomgenerator.cpp
index 06e169e7..84cca982 100644
--- a/src/engine/internal/randomgenerator.cpp
+++ b/src/engine/internal/randomgenerator.cpp
@@ -41,3 +41,16 @@ double RandomGenerator::randintDouble(double start, double end) const
     std::uniform_real_distribution<double> distribution(start, end);
     return distribution(*m_generator);
 }
+
+long RandomGenerator::randintExcept(long start, long end, long except) const
+{
+    if (start > end) {
+        std::swap(start, end);
+    }
+    std::uniform_int_distribution<long> distribution(start, end);
+    long value = distribution(*m_generator);
+    if (value == except) {
+        return randintExcept(start, end, except);
+    }
+    return value;
+}
diff --git a/src/engine/internal/randomgenerator.h b/src/engine/internal/randomgenerator.h
index 6cb72586..1358de37 100644
--- a/src/engine/internal/randomgenerator.h
+++ b/src/engine/internal/randomgenerator.h
@@ -20,6 +20,7 @@ class RandomGenerator : public IRandomGenerator
 
         long randint(long start, long end) const override;
         double randintDouble(double start, double end) const override;
+        long randintExcept(long start, long end, long except) const;
 
     private:
         static std::shared_ptr<RandomGenerator> m_instance;
diff --git a/test/randomgenerator/randomgenerator_test.cpp b/test/randomgenerator/randomgenerator_test.cpp
index 45f220af..fe327c81 100644
--- a/test/randomgenerator/randomgenerator_test.cpp
+++ b/test/randomgenerator/randomgenerator_test.cpp
@@ -43,3 +43,25 @@ TEST(RandomGeneratorTest, RandIntDouble)
         ASSERT_LE(num, 5.081);
     }
 }
+
+TEST(RandomGeneratorTest, RandInt)
+{
+    auto rng = RandomGenerator::instance();
+    ASSERT_TRUE(rng);
+
+    long num;
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(-2, 3, 0);
+        ASSERT_GE(num, -2);
+        ASSERT_LE(num, 3);
+        ASSERT_NE(num, 0);
+    }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(5, -3, 2);
+        ASSERT_GE(num, -3);
+        ASSERT_LE(num, 5);
+        ASSERT_NE(num, 2);
+    }
+}

From 9f358599b2af2b8fab680b69dc34d8dd91d2cf98 Mon Sep 17 00:00:00 2001
From: aspizu <aspizu@protonmail.com>
Date: Sat, 5 Oct 2024 03:47:05 +0530
Subject: [PATCH 25/94] Always select a different random backdrop. (Closes
 #521)

---
 src/blocks/looksblocks.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp
index 5d2cf55a..da77cc3c 100644
--- a/src/blocks/looksblocks.cpp
+++ b/src/blocks/looksblocks.cpp
@@ -964,8 +964,9 @@ void LooksBlocks::randomBackdropImpl(VirtualMachine *vm)
     if (Stage *stage = vm->engine()->stage()) {
         std::size_t count = stage->costumes().size();
 
-        if (count > 0)
-            stage->setCostumeIndex(rng->randint(0, count - 1));
+        if (count > 0) {
+            stage->setCostumeIndex(rng->randintExcept(0, count - 1, stage->costumeIndex()));
+        }
     }
 }
 

From 495f1309e9241c321539a70a7ed2ffcdbe805767 Mon Sep 17 00:00:00 2001
From: aspizu <aspizu@protonmail.com>
Date: Sat, 5 Oct 2024 03:56:44 +0530
Subject: [PATCH 26/94] Fix tests for randomBackdrop.

---
 test/blocks/looks_blocks_test.cpp | 2 +-
 test/mocks/randomgeneratormock.h  | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp
index 92c21532..ba2318bc 100644
--- a/test/blocks/looks_blocks_test.cpp
+++ b/test/blocks/looks_blocks_test.cpp
@@ -2507,7 +2507,7 @@ TEST_F(LooksBlocksTest, RandomBackdrop)
 
     stage.addCostume(std::make_shared<Costume>("backdrop4", "b4", "svg"));
 
-    EXPECT_CALL(rng, randint(0, 3)).WillOnce(Return(2));
+    EXPECT_CALL(rng, randintExcept(0, 3, 1)).WillOnce(Return(2));
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(2)->broadcast(), &thread, false));
     vm->reset();
     vm->run();
diff --git a/test/mocks/randomgeneratormock.h b/test/mocks/randomgeneratormock.h
index 42d6e999..6e07f881 100644
--- a/test/mocks/randomgeneratormock.h
+++ b/test/mocks/randomgeneratormock.h
@@ -10,4 +10,5 @@ class RandomGeneratorMock : public IRandomGenerator
     public:
         MOCK_METHOD(long, randint, (long, long), (const, override));
         MOCK_METHOD(double, randintDouble, (double, double), (const, override));
+        MOCK_METHOD(long, randintExcept, (long, long, long), (const, override));
 };

From 367363402000ce36a39d8057b698209dd7d16408 Mon Sep 17 00:00:00 2001
From: aspizu <aspizu@protonmail.com>
Date: Sat, 5 Oct 2024 04:02:43 +0530
Subject: [PATCH 27/94] Only switch to a random backdrop if there are more than
 1 backdrops.

---
 src/blocks/looksblocks.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp
index da77cc3c..f142f34c 100644
--- a/src/blocks/looksblocks.cpp
+++ b/src/blocks/looksblocks.cpp
@@ -964,7 +964,7 @@ void LooksBlocks::randomBackdropImpl(VirtualMachine *vm)
     if (Stage *stage = vm->engine()->stage()) {
         std::size_t count = stage->costumes().size();
 
-        if (count > 0) {
+        if (count > 1) {
             stage->setCostumeIndex(rng->randintExcept(0, count - 1, stage->costumeIndex()));
         }
     }

From 49a1cc9db44d7af09088f48b6b65a0fcaf8f7c65 Mon Sep 17 00:00:00 2001
From: aspizu <aspizu@protonmail.com>
Date: Sat, 5 Oct 2024 04:03:09 +0530
Subject: [PATCH 28/94] Fix test name for RandIntExcept.

---
 test/randomgenerator/randomgenerator_test.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/randomgenerator/randomgenerator_test.cpp b/test/randomgenerator/randomgenerator_test.cpp
index fe327c81..1a7fedba 100644
--- a/test/randomgenerator/randomgenerator_test.cpp
+++ b/test/randomgenerator/randomgenerator_test.cpp
@@ -44,7 +44,7 @@ TEST(RandomGeneratorTest, RandIntDouble)
     }
 }
 
-TEST(RandomGeneratorTest, RandInt)
+TEST(RandomGeneratorTest, RandIntExcept)
 {
     auto rng = RandomGenerator::instance();
     ASSERT_TRUE(rng);

From 3a68b54637d0c2e81504e23cfea89c3344555d03 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 5 Oct 2024 17:46:48 +0200
Subject: [PATCH 29/94] ValueData: Add a note about LLVM code builder

---
 include/scratchcpp/valuedata.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/scratchcpp/valuedata.h b/include/scratchcpp/valuedata.h
index 58c863cf..addaee25 100644
--- a/include/scratchcpp/valuedata.h
+++ b/include/scratchcpp/valuedata.h
@@ -32,6 +32,7 @@ extern "C"
     /*! \brief The ValueData struct holds the data of Value. It's used in compiled Scratch code for better performance. */
     struct LIBSCRATCHCPP_EXPORT ValueData
     {
+            // NOTE: Any changes must also be done in the LLVM code builder!
             union
             {
                     long intValue;

From bc135d08cc8483c4fdfab68b67f03178c230349f Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 5 Oct 2024 17:47:30 +0200
Subject: [PATCH 30/94] Add a factory for ICodeBuilder

---
 src/dev/engine/CMakeLists.txt                 |  3 +++
 .../engine/internal/codebuilderfactory.cpp    | 18 +++++++++++++++++
 src/dev/engine/internal/codebuilderfactory.h  | 20 +++++++++++++++++++
 src/dev/engine/internal/icodebuilderfactory.h | 20 +++++++++++++++++++
 test/mocks/codebuilderfactorymock.h           | 12 +++++++++++
 5 files changed, 73 insertions(+)
 create mode 100644 src/dev/engine/internal/codebuilderfactory.cpp
 create mode 100644 src/dev/engine/internal/codebuilderfactory.h
 create mode 100644 src/dev/engine/internal/icodebuilderfactory.h
 create mode 100644 test/mocks/codebuilderfactorymock.h

diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
index 8e69321c..f21d9757 100644
--- a/src/dev/engine/CMakeLists.txt
+++ b/src/dev/engine/CMakeLists.txt
@@ -4,6 +4,9 @@ target_sources(scratchcpp
     executioncontext_p.cpp
     executioncontext_p.h
     internal/icodebuilder.h
+    internal/icodebuilderfactory.h
+    internal/codebuilderfactory.cpp
+    internal/codebuilderfactory.h
     internal/llvmcodebuilder.cpp
     internal/llvmcodebuilder.h
     internal/llvmexecutablecode.cpp
diff --git a/src/dev/engine/internal/codebuilderfactory.cpp b/src/dev/engine/internal/codebuilderfactory.cpp
new file mode 100644
index 00000000..fbda1929
--- /dev/null
+++ b/src/dev/engine/internal/codebuilderfactory.cpp
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include "codebuilderfactory.h"
+#include "llvmcodebuilder.h"
+
+using namespace libscratchcpp;
+
+std::shared_ptr<CodeBuilderFactory> CodeBuilderFactory::m_instance = std::make_shared<CodeBuilderFactory>();
+
+std::shared_ptr<CodeBuilderFactory> CodeBuilderFactory::instance()
+{
+    return m_instance;
+}
+
+std::shared_ptr<ICodeBuilder> CodeBuilderFactory::create(const std::string &id) const
+{
+    return std::make_shared<LLVMCodeBuilder>(id);
+}
diff --git a/src/dev/engine/internal/codebuilderfactory.h b/src/dev/engine/internal/codebuilderfactory.h
new file mode 100644
index 00000000..ea12b288
--- /dev/null
+++ b/src/dev/engine/internal/codebuilderfactory.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "icodebuilderfactory.h"
+
+namespace libscratchcpp
+{
+
+class CodeBuilderFactory : public ICodeBuilderFactory
+{
+    public:
+        static std::shared_ptr<CodeBuilderFactory> instance();
+        std::shared_ptr<ICodeBuilder> create(const std::string &id) const override;
+
+    private:
+        static std::shared_ptr<CodeBuilderFactory> m_instance;
+};
+
+} // namespace libscratchcpp
diff --git a/src/dev/engine/internal/icodebuilderfactory.h b/src/dev/engine/internal/icodebuilderfactory.h
new file mode 100644
index 00000000..9bfb203b
--- /dev/null
+++ b/src/dev/engine/internal/icodebuilderfactory.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <memory>
+
+namespace libscratchcpp
+{
+
+class ICodeBuilder;
+
+class ICodeBuilderFactory
+{
+    public:
+        virtual ~ICodeBuilderFactory() { }
+
+        virtual std::shared_ptr<ICodeBuilder> create(const std::string &id) const = 0;
+};
+
+} // namespace libscratchcpp
diff --git a/test/mocks/codebuilderfactorymock.h b/test/mocks/codebuilderfactorymock.h
new file mode 100644
index 00000000..d8a030c5
--- /dev/null
+++ b/test/mocks/codebuilderfactorymock.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <dev/engine/internal/icodebuilderfactory.h>
+#include <gmock/gmock.h>
+
+using namespace libscratchcpp;
+
+class CodeBuilderFactoryMock : public ICodeBuilderFactory
+{
+    public:
+        MOCK_METHOD(std::shared_ptr<ICodeBuilder>, create, (const std::string &), (const, override));
+};

From 454c1821deaf6bd2ac44600e337f63fec707f96c Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 5 Oct 2024 17:49:23 +0200
Subject: [PATCH 31/94] Add new Compiler class

---
 CMakeLists.txt                           |   1 +
 include/scratchcpp/block.h               |   4 -
 include/scratchcpp/dev/compiler.h        |  56 +++++
 include/scratchcpp/global.h              |   6 -
 include/scratchcpp/iengine.h             |   4 -
 include/scratchcpp/inputvalue.h          |   5 -
 src/dev/engine/CMakeLists.txt            |   3 +
 src/dev/engine/compiler.cpp              | 199 ++++++++++++++++
 src/dev/engine/compiler_p.cpp            |  56 +++++
 src/dev/engine/compiler_p.h              |  43 ++++
 src/engine/internal/engine.cpp           |  29 +--
 src/engine/internal/engine.h             |   8 -
 src/scratch/block.cpp                    |   4 -
 src/scratch/block_p.h                    |   2 -
 src/scratch/inputvalue.cpp               |  36 ++-
 test/dev/CMakeLists.txt                  |   1 +
 test/dev/compiler/CMakeLists.txt         |  14 ++
 test/dev/compiler/compiler_test.cpp      | 291 +++++++++++++++++++++++
 test/engine/engine_test.cpp              |   2 -
 test/mocks/codebuildermock.h             |  25 ++
 test/mocks/enginemock.h                  |   4 -
 test/scratch_classes/block_test.cpp      |  12 +-
 test/scratch_classes/inputvalue_test.cpp |  80 ++++++-
 23 files changed, 815 insertions(+), 70 deletions(-)
 create mode 100644 include/scratchcpp/dev/compiler.h
 create mode 100644 src/dev/engine/compiler.cpp
 create mode 100644 src/dev/engine/compiler_p.cpp
 create mode 100644 src/dev/engine/compiler_p.h
 create mode 100644 test/dev/compiler/CMakeLists.txt
 create mode 100644 test/dev/compiler/compiler_test.cpp
 create mode 100644 test/mocks/codebuildermock.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f30d7038..a03abf46 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,6 +70,7 @@ if (LIBSCRATCHCPP_USE_LLVM)
     target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
     target_sources(scratchcpp
       PUBLIC
+        include/scratchcpp/dev/compiler.h
         include/scratchcpp/dev/executablecode.h
         include/scratchcpp/dev/executioncontext.h
     )
diff --git a/include/scratchcpp/block.h b/include/scratchcpp/block.h
index 1731a9b3..fe6cf382 100644
--- a/include/scratchcpp/block.h
+++ b/include/scratchcpp/block.h
@@ -25,9 +25,7 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity
         Block(const std::string &id, const std::string &opcode);
         Block(const Block &) = delete;
 
-#ifndef USE_LLVM
         void compile(Compiler *compiler);
-#endif
 
         const std::string &opcode() const;
 
@@ -75,13 +73,11 @@ class LIBSCRATCHCPP_EXPORT Block : public Entity
         void setTarget(Target *newTarget);
         Target *target() const;
 
-#ifndef USE_LLVM
         BlockComp compileFunction() const;
         void setCompileFunction(BlockComp newCompileFunction);
 
         BlockComp hatPredicateCompileFunction() const;
         void setHatPredicateCompileFunction(HatPredicateCompileFunc newHatPredicateCompileFunction);
-#endif // USE_LLVM
 
         bool mutationHasNext() const;
         void setMutationHasNext(bool newMutationHasNext);
diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
new file mode 100644
index 00000000..1ffc40db
--- /dev/null
+++ b/include/scratchcpp/dev/compiler.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <unordered_set>
+
+#include "../global.h"
+#include "../spimpl.h"
+
+namespace libscratchcpp
+{
+
+class IEngine;
+class Target;
+class ExecutableCode;
+class Variable;
+class List;
+class Input;
+class Field;
+class CompilerPrivate;
+
+/*! \brief The Compiler class provides API for compiling Scratch scripts. */
+class LIBSCRATCHCPP_EXPORT Compiler
+{
+    public:
+        Compiler(IEngine *engine, Target *target);
+        Compiler(const Compiler &) = delete;
+
+        IEngine *engine() const;
+        Target *target() const;
+        std::shared_ptr<Block> block() const;
+
+        std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock);
+
+        void addFunctionCall(const std::string &functionName, int argCount, bool returns);
+        void addConstValue(const Value &value);
+        void addVariableValue(Variable *variable);
+        void addListContents(List *list);
+        void addInput(const std::string &name);
+
+        void moveToIf(std::shared_ptr<Block> substack);
+        void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
+        void moveToLoop(std::shared_ptr<Block> substack);
+        void warp();
+
+        Field *field(const std::string &name) const;
+
+        const std::unordered_set<std::string> &unsupportedBlocks() const;
+
+    private:
+        void addInput(Input *input);
+
+        spimpl::unique_impl_ptr<CompilerPrivate> impl;
+};
+
+} // namespace libscratchcpp
diff --git a/include/scratchcpp/global.h b/include/scratchcpp/global.h
index b77a10e9..c72c14f6 100644
--- a/include/scratchcpp/global.h
+++ b/include/scratchcpp/global.h
@@ -24,9 +24,7 @@ namespace libscratchcpp
 {
 
 class VirtualMachine;
-#ifndef USE_LLVM
 class Compiler;
-#endif
 class Block;
 class Value;
 
@@ -37,14 +35,12 @@ class Value;
  */
 using BlockFunc = unsigned int (*)(VirtualMachine *vm);
 
-#ifndef USE_LLVM
 /*!
  * \typedef BlockComp
  *
  * BlockComp is a function pointer for functions which are used to compile blocks to bytecode.
  */
 using BlockComp = void (*)(Compiler *);
-#endif // USE_LLVM
 
 /*!
  * \typedef MonitorNameFunc
@@ -60,14 +56,12 @@ using MonitorNameFunc = const std::string &(*)(Block *);
  */
 using MonitorChangeFunc = void (*)(Block *, const Value &newValue);
 
-#ifndef USE_LLVM
 /*!
  * \typedef HatPredicateCompileFunc
  *
  * HatPredicateCompileFunc is a function pointer for functions which are used to compile edge-activated hat predicates to bytecode.
  */
 using HatPredicateCompileFunc = void (*)(Compiler *vm);
-#endif // USE_LLVM
 
 } // namespace libscratchcpp
 
diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h
index 80fe2c89..a8cffbc2 100644
--- a/include/scratchcpp/iengine.h
+++ b/include/scratchcpp/iengine.h
@@ -225,7 +225,6 @@ class LIBSCRATCHCPP_EXPORT IEngine
         /*! Returns the list of block functions. */
         virtual const std::vector<BlockFunc> &blockFunctions() const = 0;
 
-#ifndef USE_LLVM
         /*!
          * Call this from IExtension#registerBlocks() to add a compile function to a block section.
          * \see <a href="extensions.html">Extensions</a>
@@ -238,7 +237,6 @@ class LIBSCRATCHCPP_EXPORT IEngine
          * \see <a href="extensions.html">Extensions</a>
          */
         virtual void addHatPredicateCompileFunction(IExtension *extension, const std::string &opcode, HatPredicateCompileFunc f) = 0;
-#endif // USE_LLVM
 
         /*!
          * Call this from IExtension#registerBlocks() to add a monitor name function to a block section.
@@ -358,13 +356,11 @@ class LIBSCRATCHCPP_EXPORT IEngine
         /*! Sets the list of monitors. */
         virtual void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) = 0;
 
-#ifndef USE_LLVM
         /*! Creates a monitor for the given variable if it's missing and returns it. */
         virtual Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) = 0;
 
         /*! Creates a monitor for the given list if it's missing and returns it. */
         virtual Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) = 0;
-#endif // USE_LLVM
 
         /*! Emits when a monitor is added. */
         virtual sigslot::signal<Monitor *> &monitorAdded() = 0;
diff --git a/include/scratchcpp/inputvalue.h b/include/scratchcpp/inputvalue.h
index 4641bf28..32c4cb0f 100644
--- a/include/scratchcpp/inputvalue.h
+++ b/include/scratchcpp/inputvalue.h
@@ -11,9 +11,6 @@ namespace libscratchcpp
 {
 
 class Block;
-#ifndef USE_LLVM
-class Compiler;
-#endif
 class Entity;
 class InputValuePrivate;
 
@@ -37,9 +34,7 @@ class LIBSCRATCHCPP_EXPORT InputValue
 
         InputValue(Type type = Type::Number);
 
-#ifndef USE_LLVM
         void compile(Compiler *compiler);
-#endif
 
         Type type() const;
         void setType(Type newType);
diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt
index f21d9757..0f4018fd 100644
--- a/src/dev/engine/CMakeLists.txt
+++ b/src/dev/engine/CMakeLists.txt
@@ -1,5 +1,8 @@
 target_sources(scratchcpp
   PRIVATE
+    compiler.cpp
+    compiler_p.cpp
+    compiler_p.h
     executioncontext.cpp
     executioncontext_p.cpp
     executioncontext_p.h
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
new file mode 100644
index 00000000..7abb95d8
--- /dev/null
+++ b/src/dev/engine/compiler.cpp
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <scratchcpp/dev/compiler.h>
+#include <scratchcpp/block.h>
+#include <scratchcpp/input.h>
+#include <scratchcpp/inputvalue.h>
+
+#include "compiler_p.h"
+#include "internal/icodebuilderfactory.h"
+#include "internal/icodebuilder.h"
+
+using namespace libscratchcpp;
+
+/*! Constructs Compiler. */
+Compiler::Compiler(IEngine *engine, Target *target) :
+    impl(spimpl::make_unique_impl<CompilerPrivate>(engine, target))
+{
+}
+
+/*! Returns the Engine of the project. */
+IEngine *Compiler::engine() const
+{
+    return impl->engine;
+}
+
+/*! Returns the Target of this compiler. */
+Target *Compiler::target() const
+{
+    return impl->target;
+}
+
+/*! Returns currently compiled block. */
+std::shared_ptr<libscratchcpp::Block> Compiler::block() const
+{
+    return impl->block;
+}
+
+/*! Compiles the script starting with the given block. */
+std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
+{
+    impl->builder = impl->builderFactory->create(startBlock->id());
+    impl->warp = false;
+    impl->block = startBlock;
+
+    while (impl->block) {
+        size_t substacks = impl->substackTree.size();
+
+        if (impl->block->compileFunction())
+            impl->block->compile(this);
+        else {
+            std::cout << "warning: unsupported block: " << impl->block->opcode() << std::endl;
+            impl->unsupportedBlocks.insert(impl->block->opcode());
+        }
+
+        if (substacks != impl->substackTree.size())
+            continue;
+
+        impl->block = impl->block->next();
+
+        if (!impl->block && !impl->substackTree.empty())
+            impl->substackEnd();
+    }
+
+    return impl->builder->finalize();
+}
+
+/*!
+ * Adds a call to the given function.\n
+ * For example: extern "C" some_block(ValueData *ret, ValueData *arg1, ValueData *arg2) has 2 arguments
+ */
+void Compiler::addFunctionCall(const std::string &functionName, int argCount, bool returns)
+{
+    impl->builder->addFunctionCall(functionName, argCount, returns);
+}
+
+/*! Adds a constant value to the compiled code. */
+void Compiler::addConstValue(const Value &value)
+{
+    impl->builder->addConstValue(value);
+}
+
+/*! Adds the given variable to the code (to read it). */
+void Compiler::addVariableValue(Variable *variable)
+{
+    impl->builder->addVariableValue(variable);
+}
+
+/*! Adds the given list to the code (to read its string representation). */
+void Compiler::addListContents(List *list)
+{
+    impl->builder->addListContents(list);
+}
+
+/*! Compiles the given input (resolved by name) and adds it to the compiled code. */
+void Compiler::addInput(const std::string &name)
+{
+    addInput(impl->block->inputAt(impl->block->findInput(name)).get());
+}
+
+/*! Jumps to the given if substack. */
+void Compiler::moveToIf(std::shared_ptr<Block> substack)
+{
+    impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::IfStatement });
+    impl->block = substack;
+
+    if (!impl->block)
+        impl->substackEnd();
+}
+
+/*! Jumps to the given if/else substack. The second substack is used for the else branch. */
+void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2)
+{
+    impl->substackTree.push_back({ { impl->block, substack2 }, CompilerPrivate::SubstackType::IfStatement });
+    impl->block = substack1;
+
+    if (!impl->block)
+        impl->substackEnd();
+}
+
+/*! Jumps to the given loop substack. */
+void Compiler::moveToLoop(std::shared_ptr<Block> substack)
+{
+    impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
+    impl->block = substack;
+
+    if (!impl->block)
+        impl->substackEnd();
+}
+
+/*! Makes current script run without screen refresh. */
+void Compiler::warp()
+{
+    impl->warp = true;
+}
+
+/*! Convenience method which returns the field with the given name. */
+Field *Compiler::field(const std::string &name) const
+{
+    return impl->block->fieldAt(impl->block->findField(name)).get();
+}
+
+/*! Returns unsupported block opcodes which were found when compiling. */
+const std::unordered_set<std::string> &Compiler::unsupportedBlocks() const
+{
+    return impl->unsupportedBlocks;
+}
+
+void Compiler::addInput(Input *input)
+{
+    if (!input) {
+        addConstValue(Value());
+        return;
+    }
+
+    switch (input->type()) {
+        case Input::Type::Shadow:
+        case Input::Type::NoShadow: {
+            if (input->pointsToDropdownMenu())
+                addConstValue(input->selectedMenuItem());
+            else {
+                auto previousBlock = impl->block;
+                impl->block = input->valueBlock();
+
+                if (impl->block) {
+                    if (impl->block->compileFunction())
+                        impl->block->compile(this);
+                    else {
+                        std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
+                        impl->unsupportedBlocks.insert(impl->block->opcode());
+                        addConstValue(Value());
+                    }
+                } else
+                    addConstValue(input->primaryValue()->value());
+
+                impl->block = previousBlock;
+            }
+
+            break;
+        }
+
+        case Input::Type::ObscuredShadow: {
+            auto previousBlock = impl->block;
+            impl->block = input->valueBlock();
+            if (impl->block) {
+                if (impl->block->compileFunction())
+                    impl->block->compile(this);
+                else {
+                    std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
+                    impl->unsupportedBlocks.insert(impl->block->opcode());
+                    addConstValue(Value());
+                }
+            } else
+                input->primaryValue()->compile(this);
+
+            impl->block = previousBlock;
+            break;
+        }
+    }
+}
diff --git a/src/dev/engine/compiler_p.cpp b/src/dev/engine/compiler_p.cpp
new file mode 100644
index 00000000..629f56f4
--- /dev/null
+++ b/src/dev/engine/compiler_p.cpp
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <scratchcpp/block.h>
+
+#include "compiler_p.h"
+
+#include "internal/codebuilderfactory.h"
+#include "internal/icodebuilder.h"
+
+using namespace libscratchcpp;
+
+CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) :
+    engine(engine),
+    target(target)
+{
+    if (!builderFactory)
+        builderFactory = CodeBuilderFactory::instance().get();
+}
+
+void CompilerPrivate::substackEnd()
+{
+    auto parent = substackTree.back();
+
+    switch (parent.second) {
+        case SubstackType::Loop:
+            // Yield at loop end if not running without screen refresh
+            if (!warp)
+                builder->yield();
+
+            builder->endLoop();
+            break;
+
+        case SubstackType::IfStatement:
+            if (parent.first.second) {
+                builder->beginElseBranch();
+                block = parent.first.second;
+                parent.first.second = nullptr;
+                return;
+            } else
+                builder->endIf();
+
+            break;
+    }
+
+    auto parentBlock = parent.first.first;
+
+    if (parentBlock)
+        block = parentBlock->next();
+    else
+        block = nullptr;
+
+    substackTree.pop_back();
+
+    if (!block && !substackTree.empty())
+        substackEnd();
+}
diff --git a/src/dev/engine/compiler_p.h b/src/dev/engine/compiler_p.h
new file mode 100644
index 00000000..3fda8f72
--- /dev/null
+++ b/src/dev/engine/compiler_p.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <unordered_set>
+
+namespace libscratchcpp
+{
+
+class IEngine;
+class Target;
+class Block;
+class ICodeBuilderFactory;
+class ICodeBuilder;
+
+struct CompilerPrivate
+{
+        enum class SubstackType
+        {
+            Loop,
+            IfStatement
+        };
+
+        CompilerPrivate(IEngine *engine, Target *target);
+
+        void substackEnd();
+
+        IEngine *engine = nullptr;
+        Target *target = nullptr;
+
+        std::shared_ptr<Block> block;
+        std::vector<std::pair<std::pair<std::shared_ptr<Block>, std::shared_ptr<Block>>, SubstackType>> substackTree;
+        bool warp = false;
+
+        static inline ICodeBuilderFactory *builderFactory = nullptr;
+        std::shared_ptr<ICodeBuilder> builder;
+
+        std::unordered_set<std::string> unsupportedBlocks;
+};
+
+} // namespace libscratchcpp
diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp
index bc3f791d..ed8fa637 100644
--- a/src/engine/internal/engine.cpp
+++ b/src/engine/internal/engine.cpp
@@ -7,7 +7,9 @@
 #include <scratchcpp/stage.h>
 #include <scratchcpp/textbubble.h>
 #include <scratchcpp/broadcast.h>
-#ifndef USE_LLVM
+#ifdef USE_LLVM
+#include <scratchcpp/dev/compiler.h>
+#else
 #include <scratchcpp/compiler.h>
 #endif
 #include <scratchcpp/input.h>
@@ -107,7 +109,6 @@ void Engine::clear()
 // Resolves ID references and sets pointers of entities.
 void Engine::resolveIds()
 {
-#ifndef USE_LLVM
     for (auto target : m_targets) {
         std::cout << "Processing target " << target->name() << "..." << std::endl;
         const auto &blocks = target->blocks();
@@ -259,19 +260,19 @@ void Engine::resolveIds()
         block->updateInputMap();
         block->updateFieldMap();
     }
-#endif // USE_LLVM
 }
 
 void Engine::compile()
 {
-#ifndef USE_LLVM
     // Resolve entities by ID
     resolveIds();
 
     // Compile scripts to bytecode
     for (auto target : m_targets) {
         std::cout << "Compiling scripts in target " << target->name() << "..." << std::endl;
+#ifndef USE_LLVM
         std::unordered_map<std::string, unsigned int *> procedureBytecodeMap;
+#endif
         Compiler compiler(this, target.get());
         const auto &blocks = target->blocks();
         for (auto block : blocks) {
@@ -281,6 +282,9 @@ void Engine::compile()
                     auto script = std::make_shared<Script>(target.get(), block, this);
                     m_scripts[block] = script;
 
+#ifdef USE_LLVM
+                    script->setCode(compiler.compile(block));
+#else
                     compiler.compile(block);
 
                     script->setBytecode(compiler.bytecode());
@@ -290,6 +294,7 @@ void Engine::compile()
                         auto b = block->inputAt(block->findInput("custom_block"))->valueBlock();
                         procedureBytecodeMap[b->mutationPrototype()->procCode()] = script->bytecode();
                     }
+#endif // USE_LLVM
                 } else {
                     std::cout << "warning: unsupported top level block: " << block->opcode() << std::endl;
                     m_unsupportedBlocks.insert(block->opcode());
@@ -297,6 +302,7 @@ void Engine::compile()
             }
         }
 
+#ifndef USE_LLVM
         const std::vector<std::string> &procedures = compiler.procedures();
         std::vector<unsigned int *> procedureBytecodes;
         for (const std::string &code : procedures)
@@ -310,6 +316,7 @@ void Engine::compile()
                 m_scripts[block]->setLists(compiler.lists());
             }
         }
+#endif // USE_LLVM
 
         const auto &unsupportedBlocks = compiler.unsupportedBlocks();
 
@@ -322,7 +329,6 @@ void Engine::compile()
 
     for (auto monitor : m_monitors)
         compileMonitor(monitor);
-#endif // USE_LLVM
 }
 
 void Engine::start()
@@ -951,7 +957,6 @@ const std::vector<BlockFunc> &Engine::blockFunctions() const
     return m_functions;
 }
 
-#ifndef USE_LLVM
 void Engine::addCompileFunction(IExtension *extension, const std::string &opcode, BlockComp f)
 {
     if (m_compileFunctions.find(extension) == m_compileFunctions.cend())
@@ -967,7 +972,6 @@ void Engine::addHatPredicateCompileFunction(IExtension *extension, const std::st
 
     m_hatPredicateCompileFunctions[extension][opcode] = f;
 }
-#endif // USE_LLVM
 
 void Engine::addMonitorNameFunction(IExtension *extension, const std::string &opcode, MonitorNameFunc f)
 {
@@ -987,12 +991,10 @@ void Engine::addMonitorChangeFunction(IExtension *extension, const std::string &
 
 void Engine::addHatBlock(IExtension *extension, const std::string &opcode)
 {
-#ifndef USE_LLVM
     if (m_compileFunctions.find(extension) == m_compileFunctions.cend())
         m_compileFunctions[extension] = {};
 
     m_compileFunctions[extension][opcode] = [](Compiler *compiler) {};
-#endif
 }
 
 void Engine::addInput(IExtension *extension, const std::string &name, int id)
@@ -1071,6 +1073,7 @@ void Engine::addWhenTouchingObjectScript(std::shared_ptr<Block> hatBlock)
 
 void Engine::addGreenFlagScript(std::shared_ptr<Block> hatBlock)
 {
+    std::cout << "passed block: " << hatBlock << std::endl;
     addHatToMap(m_greenFlagHats, m_scripts[hatBlock].get());
 }
 
@@ -1372,7 +1375,6 @@ void Engine::setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitor
     }
 }
 
-#ifndef USE_LLVM
 Monitor *Engine::createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction)
 {
     if (var->monitor())
@@ -1409,7 +1411,6 @@ Monitor *Engine::createListMonitor(std::shared_ptr<List> list, const std::string
         return monitor.get();
     }
 }
-#endif // USE_LLVM
 
 sigslot::signal<Monitor *> &Engine::monitorAdded()
 {
@@ -1734,10 +1735,8 @@ const std::unordered_set<std::string> &Engine::unsupportedBlocks() const
 
 void Engine::clearExtensionData()
 {
-#ifndef USE_LLVM
     m_compileFunctions.clear();
     m_hatPredicateCompileFunctions.clear();
-#endif
     m_monitorNameFunctions.clear();
     m_monitorChangeFunctions.clear();
     m_inputs.clear();
@@ -1747,19 +1746,16 @@ void Engine::clearExtensionData()
 
 IExtension *Engine::blockExtension(const std::string &opcode) const
 {
-#ifndef USE_LLVM
     for (const auto &[ext, data] : m_compileFunctions) {
         auto it = data.find(opcode);
 
         if (it != data.cend())
             return ext;
     }
-#endif // USE_LLVM
 
     return nullptr;
 }
 
-#ifndef USE_LLVM
 BlockComp Engine::resolveBlockCompileFunc(IExtension *extension, const std::string &opcode) const
 {
     if (!extension)
@@ -1793,7 +1789,6 @@ HatPredicateCompileFunc Engine::resolveHatPredicateCompileFunc(IExtension *exten
 
     return nullptr;
 }
-#endif // USE_LLVM
 
 MonitorNameFunc Engine::resolveMonitorNameFunc(IExtension *extension, const std::string &opcode) const
 {
diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h
index 838936d7..69d64835 100644
--- a/src/engine/internal/engine.h
+++ b/src/engine/internal/engine.h
@@ -105,10 +105,8 @@ class Engine : public IEngine
         unsigned int functionIndex(BlockFunc f) override;
         const std::vector<BlockFunc> &blockFunctions() const override;
 
-#ifndef USE_LLVM
         void addCompileFunction(IExtension *extension, const std::string &opcode, BlockComp f) override;
         void addHatPredicateCompileFunction(IExtension *extension, const std::string &opcode, HatPredicateCompileFunc f) override;
-#endif
         void addMonitorNameFunction(IExtension *extension, const std::string &opcode, MonitorNameFunc f) override;
         void addMonitorChangeFunction(IExtension *extension, const std::string &opcode, MonitorChangeFunc f) override;
         void addHatBlock(IExtension *extension, const std::string &opcode) override;
@@ -147,10 +145,8 @@ class Engine : public IEngine
 
         const std::vector<std::shared_ptr<Monitor>> &monitors() const override;
         void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) override;
-#ifndef USE_LLVM
         Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) override;
         Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) override;
-#endif
         sigslot::signal<Monitor *> &monitorAdded() override;
         sigslot::signal<Monitor *, IMonitorHandler *> &monitorRemoved() override;
 
@@ -194,10 +190,8 @@ class Engine : public IEngine
 
         void clearExtensionData();
         IExtension *blockExtension(const std::string &opcode) const;
-#ifndef USE_LLVM
         BlockComp resolveBlockCompileFunc(IExtension *extension, const std::string &opcode) const;
         HatPredicateCompileFunc resolveHatPredicateCompileFunc(IExtension *extension, const std::string &opcode) const;
-#endif
         MonitorNameFunc resolveMonitorNameFunc(IExtension *extension, const std::string &opcode) const;
         MonitorChangeFunc resolveMonitorChangeFunc(IExtension *extension, const std::string &opcode) const;
         int resolveInput(IExtension *extension, const std::string &name) const;
@@ -259,10 +253,8 @@ class Engine : public IEngine
         std::recursive_mutex m_eventLoopMutex;
         std::string m_userAgent;
 
-#ifndef USE_LLVM
         std::unordered_map<IExtension *, std::unordered_map<std::string, BlockComp>> m_compileFunctions;
         std::unordered_map<IExtension *, std::unordered_map<std::string, HatPredicateCompileFunc>> m_hatPredicateCompileFunctions;
-#endif
         std::unordered_map<IExtension *, std::unordered_map<std::string, MonitorNameFunc>> m_monitorNameFunctions;
         std::unordered_map<IExtension *, std::unordered_map<std::string, MonitorChangeFunc>> m_monitorChangeFunctions;
         std::unordered_map<IExtension *, std::unordered_map<std::string, int>> m_inputs;
diff --git a/src/scratch/block.cpp b/src/scratch/block.cpp
index e1fc3928..0350ccd4 100644
--- a/src/scratch/block.cpp
+++ b/src/scratch/block.cpp
@@ -17,14 +17,12 @@ Block::Block(const std::string &id, const std::string &opcode) :
 {
 }
 
-#ifndef USE_LLVM
 /*! Calls the compile function. */
 void Block::compile(Compiler *compiler)
 {
     if (impl->compileFunction)
         return impl->compileFunction(compiler);
 }
-#endif // USE_LLVM
 
 /*! Returns the opcode. */
 const std::string &Block::opcode() const
@@ -32,7 +30,6 @@ const std::string &Block::opcode() const
     return impl->opcode;
 }
 
-#ifndef USE_LLVM
 /*! Returns the compile function. \see <a href="blockSections.html">Block sections</a> */
 BlockComp Block::compileFunction() const
 {
@@ -62,7 +59,6 @@ void Block::setHatPredicateCompileFunction(HatPredicateCompileFunc newHatPredica
 
     impl->hatPredicateCompileFunction = newHatPredicateCompileFunction;
 }
-#endif // USE_LLVM
 
 /*! Returns true if the block can have a block following it. */
 bool Block::mutationHasNext() const
diff --git a/src/scratch/block_p.h b/src/scratch/block_p.h
index 9f777ecf..a1aecb7b 100644
--- a/src/scratch/block_p.h
+++ b/src/scratch/block_p.h
@@ -22,10 +22,8 @@ struct BlockPrivate
         BlockPrivate(const BlockPrivate &) = delete;
 
         std::string opcode;
-#ifndef USE_LLVM
         BlockComp compileFunction = nullptr;
         HatPredicateCompileFunc hatPredicateCompileFunction = nullptr;
-#endif
         std::shared_ptr<Block> next = nullptr;
         std::string nextId;
         std::shared_ptr<Block> parent = nullptr;
diff --git a/src/scratch/inputvalue.cpp b/src/scratch/inputvalue.cpp
index 6a15060a..57b9e79b 100644
--- a/src/scratch/inputvalue.cpp
+++ b/src/scratch/inputvalue.cpp
@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 
 #include <scratchcpp/inputvalue.h>
-#ifndef USE_LLVM
+#ifdef USE_LLVM
+#include <scratchcpp/dev/compiler.h>
+#else
 #include <scratchcpp/compiler.h>
 #endif
 #include <scratchcpp/block.h>
@@ -27,7 +29,6 @@ InputValue::InputValue(Type type) :
 {
 }
 
-#ifndef USE_LLVM
 /*! Compiles the input value. */
 void InputValue::compile(Compiler *compiler)
 {
@@ -36,22 +37,47 @@ void InputValue::compile(Compiler *compiler)
             // TODO: Add support for colors
             break;
 
-        case Type::Variable:
+        case Type::Variable: {
             assert(impl->valuePtr);
+#ifdef USE_LLVM
+            Variable *var = dynamic_cast<Variable *>(impl->valuePtr.get());
+
+            if (var)
+                compiler->addVariableValue(var);
+            else
+                compiler->addConstValue(Value());
+#else
             compiler->addInstruction(vm::OP_READ_VAR, { compiler->variableIndex(impl->valuePtr) });
+#endif
+
             break;
+        }
 
-        case Type::List:
+        case Type::List: {
             assert(impl->valuePtr);
+#ifdef USE_LLVM
+            List *list = dynamic_cast<List *>(impl->valuePtr.get());
+
+            if (list)
+                compiler->addListContents(list);
+            else
+                compiler->addConstValue(Value());
+#else
             compiler->addInstruction(vm::OP_READ_LIST, { compiler->listIndex(impl->valuePtr) });
+#endif
+
             break;
+        }
 
         default:
+#ifdef USE_LLVM
+            compiler->addConstValue(impl->value);
+#else
             compiler->addInstruction(vm::OP_CONST, { compiler->constIndex(this) });
+#endif
             break;
     }
 }
-#endif // USE_LLVM
 
 /*! Returns the type of the value. */
 InputValue::Type InputValue::type() const
diff --git a/test/dev/CMakeLists.txt b/test/dev/CMakeLists.txt
index f50914fb..79ee0e67 100644
--- a/test/dev/CMakeLists.txt
+++ b/test/dev/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(blocks)
 add_subdirectory(executioncontext)
 add_subdirectory(llvm)
+add_subdirectory(compiler)
diff --git a/test/dev/compiler/CMakeLists.txt b/test/dev/compiler/CMakeLists.txt
new file mode 100644
index 00000000..546ac5c6
--- /dev/null
+++ b/test/dev/compiler/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_executable(
+  compiler_test
+  compiler_test.cpp
+)
+
+target_link_libraries(
+  compiler_test
+  GTest::gtest_main
+  GTest::gmock_main
+  scratchcpp
+  scratchcpp_mocks
+)
+
+gtest_discover_tests(compiler_test)
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
new file mode 100644
index 00000000..f103bd86
--- /dev/null
+++ b/test/dev/compiler/compiler_test.cpp
@@ -0,0 +1,291 @@
+#include <scratchcpp/dev/compiler.h>
+#include <scratchcpp/block.h>
+#include <scratchcpp/variable.h>
+#include <scratchcpp/list.h>
+#include <scratchcpp/input.h>
+#include <scratchcpp/inputvalue.h>
+#include <scratchcpp/field.h>
+#include <dev/engine/compiler_p.h>
+#include <enginemock.h>
+#include <targetmock.h>
+#include <codebuilderfactorymock.h>
+#include <codebuildermock.h>
+#include <executablecodemock.h>
+
+#include "../common.h"
+
+using namespace libscratchcpp;
+
+using ::testing::Return;
+using ::testing::_;
+
+class CompilerTest : public testing::Test
+{
+    public:
+        void SetUp() override
+        {
+            CompilerPrivate::builderFactory = &m_builderFactory;
+            m_builder = std::make_shared<CodeBuilderMock>();
+            m_code = std::make_shared<ExecutableCodeMock>();
+        }
+
+        void TearDown() override
+        {
+            CompilerPrivate::builderFactory = nullptr;
+            m_builder.reset();
+            m_compareBlock.reset();
+            m_testVar.reset();
+        }
+
+        void compile(Compiler &compiler, std::shared_ptr<Block> block)
+        {
+            ASSERT_EQ(compiler.block(), nullptr);
+            EXPECT_CALL(m_builderFactory, create(block->id())).WillOnce(Return(m_builder));
+            EXPECT_CALL(*m_builder, finalize()).WillOnce(Return(m_code));
+            ASSERT_EQ(compiler.compile(block), m_code);
+            ASSERT_EQ(compiler.block(), nullptr);
+        }
+
+        EngineMock m_engine;
+        TargetMock m_target;
+        CodeBuilderFactoryMock m_builderFactory;
+        static inline std::shared_ptr<CodeBuilderMock> m_builder;
+        std::shared_ptr<ExecutableCodeMock> m_code;
+        static inline std::shared_ptr<Block> m_compareBlock;
+        static inline std::shared_ptr<Variable> m_testVar;
+};
+
+TEST_F(CompilerTest, Constructors)
+{
+    Compiler compiler(&m_engine, &m_target);
+    ASSERT_EQ(compiler.engine(), &m_engine);
+    ASSERT_EQ(compiler.target(), &m_target);
+}
+
+TEST_F(CompilerTest, AddFunctionCall)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    m_compareBlock = block;
+    block->setCompileFunction([](Compiler *compiler) {
+        ASSERT_EQ(compiler->block(), m_compareBlock);
+        EXPECT_CALL(*m_builder, addFunctionCall("test1", 3, false));
+        compiler->addFunctionCall("test1", 3, false);
+
+        EXPECT_CALL(*m_builder, addFunctionCall("test2", 0, true));
+        compiler->addFunctionCall("test2", 0, true);
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, AddConstValue)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        compiler->addConstValue(1);
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("test")));
+        compiler->addConstValue("test");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value(3.5)));
+        compiler->addConstValue(3.5);
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, AddVariableValue)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    block->setCompileFunction([](Compiler *compiler) {
+        Variable var1("", ""), var2("", "");
+        EXPECT_CALL(*m_builder, addVariableValue(&var1));
+        compiler->addVariableValue(&var1);
+
+        EXPECT_CALL(*m_builder, addVariableValue(&var2));
+        compiler->addVariableValue(&var2);
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, AddListValue)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    block->setCompileFunction([](Compiler *compiler) {
+        List list1("", ""), list2("", "");
+        EXPECT_CALL(*m_builder, addListContents(&list1));
+        compiler->addListContents(&list1);
+
+        EXPECT_CALL(*m_builder, addListContents(&list2));
+        compiler->addListContents(&list2);
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, AddInput)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    auto valueBlock = std::make_shared<Block>("b", "");
+    m_compareBlock = valueBlock;
+    valueBlock->setCompileFunction([](Compiler *compiler) {
+        ASSERT_EQ(compiler->block(), m_compareBlock);
+        compiler->addConstValue("value block");
+    });
+
+    auto menu = std::make_shared<Block>("c", "");
+    menu->setShadow(true);
+    menu->addField(std::make_shared<Field>("MENU", "selected item"));
+
+    auto shadow = std::make_shared<Input>("SHADOW", Input::Type::Shadow);
+    shadow->setPrimaryValue("test");
+    block->addInput(shadow);
+
+    auto shadowBlock = std::make_shared<Input>("SHADOW_BLOCK", Input::Type::Shadow);
+    shadowBlock->setValueBlock(valueBlock);
+    block->addInput(shadowBlock);
+
+    auto shadowMenu = std::make_shared<Input>("SHADOW_MENU", Input::Type::Shadow);
+    shadowMenu->setValueBlock(menu);
+    block->addInput(shadowMenu);
+
+    auto noShadow = std::make_shared<Input>("NO_SHADOW", Input::Type::NoShadow);
+    noShadow->setPrimaryValue("test");
+    block->addInput(noShadow);
+
+    auto noShadowBlock = std::make_shared<Input>("NO_SHADOW_BLOCK", Input::Type::NoShadow);
+    noShadowBlock->setValueBlock(valueBlock);
+    block->addInput(noShadowBlock);
+
+    auto noShadowMenu = std::make_shared<Input>("NO_SHADOW_MENU", Input::Type::Shadow);
+    noShadowMenu->setValueBlock(menu);
+    block->addInput(noShadowMenu);
+
+    auto obscuredShadow = std::make_shared<Input>("OBSCURED_SHADOW", Input::Type::ObscuredShadow);
+    obscuredShadow->setPrimaryValue("test");
+    block->addInput(obscuredShadow);
+
+    auto obscuredShadowVariable = std::make_shared<Input>("OBSCURED_SHADOW_VARIABLE", Input::Type::ObscuredShadow);
+    m_testVar = std::make_shared<Variable>("", "");
+    obscuredShadowVariable->primaryValue()->setValuePtr(m_testVar);
+    block->addInput(obscuredShadowVariable);
+
+    auto obscuredShadowBlock = std::make_shared<Input>("OBSCURED_SHADOW_BLOCK", Input::Type::ObscuredShadow);
+    obscuredShadowBlock->setValueBlock(valueBlock);
+    block->addInput(obscuredShadowBlock);
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, addConstValue(Value()));
+        compiler->addInput("INVALID");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("test")));
+        compiler->addInput("SHADOW");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("value block")));
+        compiler->addInput("SHADOW_BLOCK");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("selected item")));
+        compiler->addInput("SHADOW_MENU");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("test")));
+        compiler->addInput("NO_SHADOW");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("value block")));
+        compiler->addInput("NO_SHADOW_BLOCK");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("selected item")));
+        compiler->addInput("NO_SHADOW_MENU");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("test")));
+        compiler->addInput("OBSCURED_SHADOW");
+
+        EXPECT_CALL(*m_builder, addVariableValue(m_testVar.get()));
+        compiler->addInput("OBSCURED_SHADOW_VARIABLE");
+
+        EXPECT_CALL(*m_builder, addConstValue(Value("value block")));
+        compiler->addInput("OBSCURED_SHADOW_BLOCK");
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, Field)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    block->addField(std::make_shared<Field>("TEST", "test"));
+    block->setCompileFunction([](Compiler *compiler) {
+        ASSERT_EQ(compiler->field("INVALID"), nullptr);
+        ASSERT_EQ(compiler->field("TEST"), compiler->block()->fieldAt(0).get());
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, UnsupportedBlocks)
+{
+    auto valueBlock1 = std::make_shared<Block>("v1", "value_block1");
+    auto valueBlock2 = std::make_shared<Block>("v2", "value_block2");
+    valueBlock2->setCompileFunction([](Compiler *) {});
+
+    auto valueBlock3 = std::make_shared<Block>("v3", "value_block3");
+    auto valueBlock4 = std::make_shared<Block>("v4", "value_block4");
+    valueBlock4->setCompileFunction([](Compiler *) {});
+
+    auto valueBlock5 = std::make_shared<Block>("v5", "value_block5");
+    auto valueBlock6 = std::make_shared<Block>("v6", "value_block6");
+    valueBlock6->setCompileFunction([](Compiler *) {});
+
+    auto block1 = std::make_shared<Block>("b1", "block1");
+
+    auto block2 = std::make_shared<Block>("b2", "block2");
+    block2->setParent(block1);
+    block1->setNext(block2);
+
+    auto block3 = std::make_shared<Block>("b3", "block3");
+    auto input1 = std::make_shared<Input>("INPUT1", Input::Type::Shadow);
+    input1->setValueBlock(valueBlock1);
+    block3->addInput(input1);
+    auto input2 = std::make_shared<Input>("INPUT2", Input::Type::Shadow);
+    input2->setValueBlock(valueBlock2);
+    block3->addInput(input2);
+    auto input3 = std::make_shared<Input>("INPUT3", Input::Type::NoShadow);
+    input3->setValueBlock(valueBlock3);
+    block3->addInput(input3);
+    auto input4 = std::make_shared<Input>("INPUT4", Input::Type::NoShadow);
+    input4->setValueBlock(valueBlock4);
+    block3->addInput(input4);
+    auto input5 = std::make_shared<Input>("INPUT5", Input::Type::ObscuredShadow);
+    input5->setValueBlock(valueBlock5);
+    block3->addInput(input5);
+    auto input6 = std::make_shared<Input>("INPUT6", Input::Type::ObscuredShadow);
+    input6->setValueBlock(valueBlock6);
+    block3->addInput(input6);
+    block3->setCompileFunction([](Compiler *compiler) {
+        compiler->addInput("INPUT1");
+        compiler->addInput("INPUT2");
+        compiler->addInput("INPUT3");
+        compiler->addInput("INPUT4");
+        compiler->addInput("INPUT5");
+        compiler->addInput("INPUT6");
+    });
+    block3->setParent(block2);
+    block2->setNext(block3);
+
+    auto block4 = std::make_shared<Block>("b4", "block4");
+    block4->setParent(block3);
+    block3->setNext(block4);
+
+    Compiler compiler(&m_engine, &m_target);
+    EXPECT_CALL(*m_builder, addConstValue).WillRepeatedly(Return());
+    compile(compiler, block1);
+
+    ASSERT_EQ(compiler.unsupportedBlocks(), std::unordered_set<std::string>({ "block1", "block2", "value_block1", "value_block3", "value_block5", "block4" }));
+}
diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp
index 7b7c7202..329bb28a 100644
--- a/test/engine/engine_test.cpp
+++ b/test/engine/engine_test.cpp
@@ -1574,7 +1574,6 @@ TEST(EngineTest, Monitors)
     engine.setMonitors({ m1, m2, m3 });
 }
 
-#ifndef USE_LLVM
 TEST(EngineTest, CreateMissingMonitors)
 {
     static constexpr const char *VARIABLE_OPCODE = "data_variable";
@@ -1707,7 +1706,6 @@ TEST(EngineTest, CreateMissingMonitors)
         ASSERT_EQ(monitors[6].get(), m7);
     }
 }
-#endif // USE_LLVM
 
 void questionFunction(const std::string &)
 {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
new file mode 100644
index 00000000..018a1b6f
--- /dev/null
+++ b/test/mocks/codebuildermock.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <dev/engine/internal/icodebuilder.h>
+#include <gmock/gmock.h>
+
+using namespace libscratchcpp;
+
+class CodeBuilderMock : public ICodeBuilder
+{
+    public:
+        MOCK_METHOD(std::shared_ptr<ExecutableCode>, finalize, (), (override));
+        MOCK_METHOD(void, addFunctionCall, (const std::string &, int, bool), (override));
+        MOCK_METHOD(void, addConstValue, (const Value &), (override));
+        MOCK_METHOD(void, addVariableValue, (Variable *), (override));
+        MOCK_METHOD(void, addListContents, (List *), (override));
+
+        MOCK_METHOD(void, beginIfStatement, (), (override));
+        MOCK_METHOD(void, beginElseBranch, (), (override));
+        MOCK_METHOD(void, endIf, (), (override));
+
+        MOCK_METHOD(void, beginLoop, (), (override));
+        MOCK_METHOD(void, endLoop, (), (override));
+
+        MOCK_METHOD(void, yield, (), (override));
+};
diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h
index 639eb606..d862de61 100644
--- a/test/mocks/enginemock.h
+++ b/test/mocks/enginemock.h
@@ -87,10 +87,8 @@ class EngineMock : public IEngine
         MOCK_METHOD(unsigned int, functionIndex, (BlockFunc), (override));
         MOCK_METHOD(const std::vector<BlockFunc> &, blockFunctions, (), (const, override));
 
-#ifndef USE_LLVM
         MOCK_METHOD(void, addCompileFunction, (IExtension *, const std::string &, BlockComp), (override));
         MOCK_METHOD(void, addHatPredicateCompileFunction, (IExtension *, const std::string &, HatPredicateCompileFunc), (override));
-#endif
         MOCK_METHOD(void, addMonitorNameFunction, (IExtension *, const std::string &, MonitorNameFunc), (override));
         MOCK_METHOD(void, addMonitorChangeFunction, (IExtension *, const std::string &, MonitorChangeFunc), (override));
         MOCK_METHOD(void, addHatBlock, (IExtension *, const std::string &), (override));
@@ -129,10 +127,8 @@ class EngineMock : public IEngine
 
         MOCK_METHOD(const std::vector<std::shared_ptr<Monitor>> &, monitors, (), (const, override));
         MOCK_METHOD(void, setMonitors, (const std::vector<std::shared_ptr<Monitor>> &), (override));
-#ifndef USE_LLVM
         MOCK_METHOD(Monitor *, createVariableMonitor, (std::shared_ptr<Variable>, const std::string &, const std::string &, int, BlockComp), (override));
         MOCK_METHOD(Monitor *, createListMonitor, (std::shared_ptr<List>, const std::string &, const std::string &, int, BlockComp), (override));
-#endif
         MOCK_METHOD(sigslot::signal<Monitor *> &, monitorAdded, (), (override));
         MOCK_METHOD((sigslot::signal<Monitor *, IMonitorHandler *> &), monitorRemoved, (), (override));
 
diff --git a/test/scratch_classes/block_test.cpp b/test/scratch_classes/block_test.cpp
index d184cdc0..6317287f 100644
--- a/test/scratch_classes/block_test.cpp
+++ b/test/scratch_classes/block_test.cpp
@@ -3,7 +3,9 @@
 #include <scratchcpp/field.h>
 #include <scratchcpp/comment.h>
 #include <scratchcpp/target.h>
-#ifndef USE_LLVM
+#ifdef USE_LLVM
+#include <scratchcpp/dev/compiler.h>
+#else
 #include <scratchcpp/compiler.h>
 #endif
 #include <enginemock.h>
@@ -12,18 +14,15 @@
 
 using namespace libscratchcpp;
 
-#ifndef USE_LLVM
 class BlockTestMock
 {
     public:
         MOCK_METHOD(void, compileTest, (Compiler *), ());
 };
-#endif // USE_LLVM
 
 class BlockTest : public testing::Test
 {
     public:
-#ifndef USE_LLVM
         void SetUp() override { m_mockPtr = &m_mock; }
 
         static void compileTest(Compiler *compiler)
@@ -34,7 +33,6 @@ class BlockTest : public testing::Test
 
         static inline BlockTestMock *m_mockPtr = nullptr;
         BlockTestMock m_mock;
-#endif // USE_LLVM
 };
 
 TEST_F(BlockTest, Constructors)
@@ -262,7 +260,6 @@ TEST_F(BlockTest, Target)
     ASSERT_EQ(block.target(), &target);
 }
 
-#ifndef USE_LLVM
 TEST_F(BlockTest, CompileFunction)
 {
     Block block("", "");
@@ -285,7 +282,7 @@ TEST_F(BlockTest, Compile)
 {
     Block block("", "");
     EngineMock engine;
-    Compiler compiler(&engine);
+    Compiler compiler(&engine, nullptr);
 
     block.compile(&compiler); // test with null compile function
 
@@ -293,7 +290,6 @@ TEST_F(BlockTest, Compile)
     EXPECT_CALL(m_mock, compileTest(&compiler)).Times(1);
     block.compile(&compiler);
 }
-#endif // USE_LLVM
 
 TEST_F(BlockTest, MutationHasNext)
 {
diff --git a/test/scratch_classes/inputvalue_test.cpp b/test/scratch_classes/inputvalue_test.cpp
index 949ed3e5..40cb5175 100644
--- a/test/scratch_classes/inputvalue_test.cpp
+++ b/test/scratch_classes/inputvalue_test.cpp
@@ -3,14 +3,25 @@
 #include <scratchcpp/broadcast.h>
 #include <scratchcpp/variable.h>
 #include <scratchcpp/list.h>
+#ifdef USE_LLVM
+#include <scratchcpp/dev/compiler.h>
+#else
 #include <scratchcpp/compiler.h>
+#endif
 #include <scratchcpp/input.h>
 #include <enginemock.h>
+#include <targetmock.h>
+#include <codebuilderfactorymock.h>
+#include <codebuildermock.h>
 
 #include "../common.h"
+#include "dev/engine/compiler_p.h"
 
 using namespace libscratchcpp;
 
+using ::testing::Return;
+using ::testing::_;
+
 TEST(InputValueTest, Constructors)
 {
     InputValue value1;
@@ -106,7 +117,74 @@ TEST(InputValueTest, ValuePtr)
     ASSERT_EQ(value2.type(), InputValue::Type::Variable);
 }
 
-#ifndef USE_LLVM
+#ifdef USE_LLVM
+TEST(InputValueTest, Compile)
+{
+    EngineMock engine;
+    TargetMock target;
+    CodeBuilderFactoryMock builderFactory;
+    auto builder = std::make_shared<CodeBuilderMock>();
+    CompilerPrivate::builderFactory = &builderFactory;
+    Compiler compiler(&engine, &target);
+
+    auto block = std::make_shared<Block>("", "");
+    EXPECT_CALL(builderFactory, create(_)).WillOnce(Return(builder));
+    EXPECT_CALL(*builder, finalize);
+    compiler.compile(block);
+    CompilerPrivate::builderFactory = nullptr;
+
+    auto input = std::make_shared<Input>("", Input::Type::Shadow);
+    input->setPrimaryValue(5);
+    block->addInput(input);
+
+    InputValue *value = input->primaryValue();
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::Number);
+    value->compile(&compiler);
+
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::PositiveNumber);
+    value->compile(&compiler);
+
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::PositiveInteger);
+    value->compile(&compiler);
+
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::Integer);
+    value->compile(&compiler);
+
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::Angle);
+    value->compile(&compiler);
+
+    // TODO: Add support for colors
+    // TODO: Use a loop from type 4 to 11
+    /*value->setType(InputValue::Type::Color);
+    value->compile(&compiler);*/
+
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::String);
+    value->compile(&compiler);
+
+    EXPECT_CALL(*builder, addConstValue(input->primaryValue()->value()));
+    value->setType(InputValue::Type::Broadcast);
+    value->compile(&compiler);
+
+    auto variable = std::make_shared<Variable>("", "");
+    auto list = std::make_shared<List>("", "");
+
+    EXPECT_CALL(*builder, addVariableValue(variable.get()));
+    value->setType(InputValue::Type::Variable);
+    value->setValuePtr(variable);
+    value->compile(&compiler);
+
+    EXPECT_CALL(*builder, addListContents(list.get()));
+    value->setType(InputValue::Type::List);
+    value->setValuePtr(list);
+    value->compile(&compiler);
+}
+#else
 TEST(InputValueTest, Compile)
 {
     EngineMock engine;

From 2c673538b2bc4289a69164834b81e6c5afbc57e2 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 5 Oct 2024 18:41:28 +0200
Subject: [PATCH 32/94] Disable test for #578 when using LLVM

---
 test/engine/engine_test.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp
index 99738f50..a648739f 100644
--- a/test/engine/engine_test.cpp
+++ b/test/engine/engine_test.cpp
@@ -2272,6 +2272,7 @@ TEST(EngineTest, BroadcastStopsWaitBlocks)
     ASSERT_TRUE(GET_VAR(stage, "backdrop_passed")->value().toBool());
 }
 
+#ifndef USE_LLVM
 TEST(EngineTest, BroadcastAndWaitCaseInsensitive)
 {
     // Regtest for #578
@@ -2288,3 +2289,4 @@ TEST(EngineTest, BroadcastAndWaitCaseInsensitive)
     ASSERT_VAR(stage, "passed");
     ASSERT_TRUE(GET_VAR(stage, "passed")->value().toBool());
 }
+#endif // USE_LLVM

From a6de26e05299debcb024ab012139c674d20bfa7f Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 5 Oct 2024 18:41:45 +0200
Subject: [PATCH 33/94] Add missing value_functions.h entry

---
 CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a03abf46..29f589fa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,7 @@ target_sources(scratchcpp
     include/scratchcpp/sound.h
     include/scratchcpp/value.h
     include/scratchcpp/valuedata.h
+    include/scratchcpp/value_functions.h
     include/scratchcpp/entity.h
     include/scratchcpp/variable.h
     include/scratchcpp/list.h

From f26e8cf42fad917ad81656c52abf4029c43ec7dc Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 6 Oct 2024 10:56:05 +0200
Subject: [PATCH 34/94] LLVMCodeBuilder: Implement if statements

---
 src/dev/engine/internal/llvmcodebuilder.cpp |  75 +++++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  15 +-
 test/dev/llvm/llvmcodebuilder_test.cpp      | 171 ++++++++++++++++++++
 test/dev/llvm/testfunctions.cpp             |   5 +
 test/dev/llvm/testfunctions.h               |   2 +
 5 files changed, 267 insertions(+), 1 deletion(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 997865fb..a5396d51 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -38,6 +38,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 {
     size_t functionIndex = 0;
     llvm::Function *currentFunc = beginFunction(functionIndex);
+    std::vector<IfStatement> ifStatements;
 
     // Execute recorded steps
     for (const Step &step : m_steps) {
@@ -71,6 +72,68 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 endFunction(currentFunc, functionIndex);
                 currentFunc = beginFunction(++functionIndex);
                 break;
+
+            case Step::Type::BeginIf: {
+                IfStatement statement;
+                statement.beforeIf = m_builder.GetInsertBlock();
+                statement.body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                // Convert last reg to bool
+                assert(step.args.size() == 1);
+                statement.condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
+
+                // Switch to body branch
+                m_builder.SetInsertPoint(statement.body);
+
+                ifStatements.push_back(statement);
+                break;
+            }
+
+            case Step::Type::BeginElse: {
+                assert(!ifStatements.empty());
+                IfStatement &statement = ifStatements.back();
+
+                // Jump to the branch after the if statement
+                assert(!statement.afterIf);
+                statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                m_builder.CreateBr(statement.afterIf);
+
+                // Create else branch
+                assert(!statement.elseBranch);
+                statement.elseBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                // Since there's an else branch, the conditional instruction should jump to it
+                m_builder.SetInsertPoint(statement.beforeIf);
+                m_builder.CreateCondBr(statement.condition, statement.body, statement.elseBranch);
+
+                // Switch to the else branch
+                m_builder.SetInsertPoint(statement.elseBranch);
+                break;
+            }
+
+            case Step::Type::EndIf: {
+                assert(!ifStatements.empty());
+                IfStatement &statement = ifStatements.back();
+
+                // Jump to the branch after the if statement
+                if (!statement.afterIf)
+                    statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                m_builder.CreateBr(statement.afterIf);
+
+                if (statement.elseBranch) {
+                } else {
+                    // If there wasn't an 'else' branch, create a conditional instruction which skips the if statement if false
+                    m_builder.SetInsertPoint(statement.beforeIf);
+                    m_builder.CreateCondBr(statement.condition, statement.body, statement.afterIf);
+                }
+
+                // Switch to the branch after the if statement
+                m_builder.SetInsertPoint(statement.afterIf);
+
+                ifStatements.pop_back();
+                break;
+            }
         }
     }
 
@@ -140,14 +203,21 @@ void LLVMCodeBuilder::addListContents(List *list)
 
 void LLVMCodeBuilder::beginIfStatement()
 {
+    Step step(Step::Type::BeginIf);
+    assert(!m_tmpRegs.empty());
+    step.args.push_back(m_tmpRegs.back());
+    m_tmpRegs.pop_back();
+    m_steps.push_back(step);
 }
 
 void LLVMCodeBuilder::beginElseBranch()
 {
+    m_steps.push_back(Step(Step::Type::BeginElse));
 }
 
 void LLVMCodeBuilder::endIf()
 {
+    m_steps.push_back(Step(Step::Type::EndIf));
 }
 
 void LLVMCodeBuilder::beginLoop()
@@ -289,3 +359,8 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
 {
     return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
 }
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool()
+{
+    return resolveFunction("value_toBool", llvm::FunctionType::get(m_builder.getInt1Ty(), m_valueDataType->getPointerTo(), false));
+}
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 3f0a3f55..4a938f19 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -49,7 +49,10 @@ class LLVMCodeBuilder : public ICodeBuilder
                 enum class Type
                 {
                     FunctionCall,
-                    Yield
+                    Yield,
+                    BeginIf,
+                    BeginElse,
+                    EndIf
                 };
 
                 Step(Type type) :
@@ -64,6 +67,15 @@ class LLVMCodeBuilder : public ICodeBuilder
                 size_t functionReturnRegIndex = 0;
         };
 
+        struct IfStatement
+        {
+                llvm::Value *condition = nullptr;
+                llvm::BasicBlock *beforeIf = nullptr;
+                llvm::BasicBlock *body = nullptr;
+                llvm::BasicBlock *elseBranch = nullptr;
+                llvm::BasicBlock *afterIf = nullptr;
+        };
+
         void initTypes();
         llvm::Function *beginFunction(size_t index);
         void endFunction(llvm::Function *func, size_t index);
@@ -76,6 +88,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::FunctionCallee resolve_value_assign_bool();
         llvm::FunctionCallee resolve_value_assign_cstring();
         llvm::FunctionCallee resolve_value_assign_special();
+        llvm::FunctionCallee resolve_value_toBool();
 
         std::string m_id;
         llvm::LLVMContext m_ctx;
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index ea3e20ac..2d1782d0 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -8,6 +8,8 @@
 
 using namespace libscratchcpp;
 
+using ::testing::Return;
+
 class LLVMCodeBuilderTest : public testing::Test
 {
     public:
@@ -103,3 +105,172 @@ TEST_F(LLVMCodeBuilderTest, Yield)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2);
     ASSERT_TRUE(code->isFinished(ctx.get()));
 }
+
+TEST_F(LLVMCodeBuilderTest, IfStatement)
+{
+    // Without else branch (const condition)
+    m_builder->addConstValue("true");
+    m_builder->beginIfStatement();
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->endIf();
+
+    m_builder->addConstValue("false");
+    m_builder->beginIfStatement();
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->endIf();
+
+    // Without else branch (condition returned by function)
+    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addConstValue("no_args_output");
+    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->beginIfStatement();
+    m_builder->addConstValue(0);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endIf();
+
+    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addConstValue("");
+    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->beginIfStatement();
+    m_builder->addConstValue(1);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endIf();
+
+    // With else branch (const condition)
+    m_builder->addConstValue("true");
+    m_builder->beginIfStatement();
+    m_builder->addConstValue(2);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->beginElseBranch();
+    m_builder->addConstValue(3);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endIf();
+
+    m_builder->addConstValue("false");
+    m_builder->beginIfStatement();
+    m_builder->addConstValue(4);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->beginElseBranch();
+    m_builder->addConstValue(5);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endIf();
+
+    // With else branch (condition returned by function)
+    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addConstValue("no_args_output");
+    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->beginIfStatement();
+    m_builder->addConstValue(6);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->beginElseBranch();
+    m_builder->addConstValue(7);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endIf();
+
+    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addConstValue("");
+    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->beginIfStatement();
+    m_builder->addConstValue(8);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->beginElseBranch();
+    m_builder->addConstValue(9);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endIf();
+
+    // Nested 1
+    m_builder->addConstValue(true);
+    m_builder->beginIfStatement();
+    {
+        m_builder->addConstValue(false);
+        m_builder->beginIfStatement();
+        {
+            m_builder->addConstValue(0);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->beginElseBranch();
+        {
+            m_builder->addConstValue(1);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+
+            m_builder->addConstValue(false);
+            m_builder->beginIfStatement();
+            m_builder->beginElseBranch();
+            {
+                m_builder->addConstValue(2);
+                m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            }
+            m_builder->endIf();
+        }
+        m_builder->endIf();
+    }
+    m_builder->beginElseBranch();
+    {
+        m_builder->addConstValue(true);
+        m_builder->beginIfStatement();
+        {
+            m_builder->addConstValue(3);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->beginElseBranch();
+        {
+            m_builder->addConstValue(4);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->endIf();
+    }
+    m_builder->endIf();
+
+    // Nested 2
+    m_builder->addConstValue(false);
+    m_builder->beginIfStatement();
+    {
+        m_builder->addConstValue(false);
+        m_builder->beginIfStatement();
+        {
+            m_builder->addConstValue(5);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->beginElseBranch();
+        {
+            m_builder->addConstValue(6);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->endIf();
+    }
+    m_builder->beginElseBranch();
+    {
+        m_builder->addConstValue(true);
+        m_builder->beginIfStatement();
+        {
+            m_builder->addConstValue(7);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->beginElseBranch();
+        m_builder->endIf();
+    }
+    m_builder->endIf();
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "no_args\n"
+        "no_args_ret\n"
+        "1_arg 0\n"
+        "no_args_ret\n"
+        "1_arg 2\n"
+        "1_arg 5\n"
+        "no_args_ret\n"
+        "1_arg 6\n"
+        "no_args_ret\n"
+        "1_arg 9\n"
+        "1_arg 1\n"
+        "1_arg 2\n"
+        "1_arg 7\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index 7de1c780..fa409631 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -73,4 +73,9 @@ extern "C"
         std::cout << "3_args " << s1 << " " << s2 << " " << s3 << std::endl;
         value_assign_cstring(ret, "3_args_output");
     }
+
+    void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b)
+    {
+        value_assign_bool(ret, value_equals(a, b));
+    }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 96acefa9..02636b94 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -18,6 +18,8 @@ extern "C"
     void test_function_1_arg_ret(Target *target, ValueData *ret, const ValueData *arg1);
     void test_function_3_args(Target *target, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
     void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
+
+    void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b);
 }
 
 } // namespace libscratchcpp

From 51e5450a5451491fc9f2e1bd6d87889f8476ff7e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 12:07:20 +0200
Subject: [PATCH 35/94] Compiler: Add Input method

---
 include/scratchcpp/dev/compiler.h   |   1 +
 src/dev/engine/compiler.cpp         |   6 +
 test/dev/compiler/compiler_test.cpp | 198 ++++++++++++++++++++++++++++
 3 files changed, 205 insertions(+)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index 1ffc40db..4ad8f522 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -43,6 +43,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
         void moveToLoop(std::shared_ptr<Block> substack);
         void warp();
 
+        Input *input(const std::string &name) const;
         Field *field(const std::string &name) const;
 
         const std::unordered_set<std::string> &unsupportedBlocks() const;
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 7abb95d8..730199fd 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -133,6 +133,12 @@ void Compiler::warp()
     impl->warp = true;
 }
 
+/*! Convenience method which returns the field with the given name. */
+Input *Compiler::input(const std::string &name) const
+{
+    return impl->block->inputAt(impl->block->findInput(name)).get();
+}
+
 /*! Convenience method which returns the field with the given name. */
 Field *Compiler::field(const std::string &name) const
 {
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index f103bd86..6e82a1dc 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -216,6 +216,204 @@ TEST_F(CompilerTest, AddInput)
     compile(compiler, block);
 }
 
+TEST_F(CompilerTest, MoveToIf)
+{
+    Compiler compiler(&m_engine, &m_target);
+    EXPECT_CALL(*m_builder, beginElseBranch).Times(0);
+
+    auto if1 = std::make_shared<Block>("", "if");
+    if1->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement).Times(0);
+        EXPECT_CALL(*m_builder, endIf).Times(0);
+        compiler->moveToIf(nullptr);
+    });
+
+    auto if2 = std::make_shared<Block>("", "if");
+    if1->setNext(if2);
+    if2->setParent(if1);
+    if2->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement());
+        EXPECT_CALL(*m_builder, addConstValue(Value()));
+        EXPECT_CALL(*m_builder, endIf());
+        compiler->moveToIf(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    auto substack1 = std::make_shared<Block>("", "substack");
+    substack1->setParent(if2);
+    substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(Value()); });
+
+    auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack1);
+    if2->addInput(input);
+
+    compile(compiler, if1);
+}
+
+TEST_F(CompilerTest, MoveToIfElse)
+{
+    Compiler compiler(&m_engine, &m_target);
+
+    auto if1 = std::make_shared<Block>("", "if");
+    if1->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement).Times(0);
+        EXPECT_CALL(*m_builder, beginElseBranch).Times(0);
+        EXPECT_CALL(*m_builder, endIf).Times(0);
+        compiler->moveToIfElse(nullptr, nullptr);
+    });
+
+    auto if2 = std::make_shared<Block>("", "if");
+    if1->setNext(if2);
+    if2->setParent(if1);
+    if2->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement());
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        EXPECT_CALL(*m_builder, beginElseBranch());
+        EXPECT_CALL(*m_builder, addConstValue(Value(2)));
+        EXPECT_CALL(*m_builder, endIf());
+        compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock());
+    });
+
+    auto substack1 = std::make_shared<Block>("", "substack");
+    substack1->setParent(if2);
+    substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
+
+    auto substack2 = std::make_shared<Block>("", "substack");
+    substack2->setParent(if2);
+    substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
+
+    auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack1);
+    if2->addInput(input);
+    input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
+    input->setValueBlock(substack2);
+    if2->addInput(input);
+
+    // Nested
+    auto if3 = std::make_shared<Block>("", "if");
+    if2->setNext(if3);
+    if3->setParent(if2);
+    if3->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement()).Times(3);
+        EXPECT_CALL(*m_builder, beginElseBranch()).Times(3);
+        EXPECT_CALL(*m_builder, endIf()).Times(3);
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        EXPECT_CALL(*m_builder, addConstValue(Value(2)));
+        EXPECT_CALL(*m_builder, addConstValue(Value(3)));
+        EXPECT_CALL(*m_builder, addConstValue(Value(4)));
+        compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock());
+    });
+
+    // if
+    auto ifSubstack1 = std::make_shared<Block>("", "if");
+    ifSubstack1->setParent(if3);
+    ifSubstack1->setCompileFunction([](Compiler *compiler) { compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock()); });
+
+    substack1 = std::make_shared<Block>("", "substack");
+    substack1->setParent(ifSubstack1);
+    substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
+
+    substack2 = std::make_shared<Block>("", "substack");
+    substack2->setParent(ifSubstack1);
+    substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
+
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack1);
+    ifSubstack1->addInput(input);
+    input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
+    input->setValueBlock(substack2);
+    ifSubstack1->addInput(input);
+
+    // else
+    auto ifSubstack2 = std::make_shared<Block>("", "if");
+    ifSubstack2->setParent(if3);
+    ifSubstack2->setCompileFunction([](Compiler *compiler) { compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), compiler->input("SUBSTACK2")->valueBlock()); });
+
+    substack1 = std::make_shared<Block>("", "substack");
+    substack1->setParent(ifSubstack2);
+    substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(3); });
+
+    substack2 = std::make_shared<Block>("", "substack");
+    substack2->setParent(ifSubstack2);
+    substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(4); });
+
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack1);
+    ifSubstack2->addInput(input);
+    input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
+    input->setValueBlock(substack2);
+    ifSubstack2->addInput(input);
+
+    // End if
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(ifSubstack1);
+    if3->addInput(input);
+    input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
+    input->setValueBlock(ifSubstack2);
+    if3->addInput(input);
+
+    // Empty 'then' branch
+    auto if4 = std::make_shared<Block>("", "if");
+    if3->setNext(if4);
+    if4->setParent(if3);
+    if4->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement());
+        EXPECT_CALL(*m_builder, beginElseBranch());
+        EXPECT_CALL(*m_builder, addConstValue(Value(2)));
+        EXPECT_CALL(*m_builder, endIf());
+        compiler->moveToIfElse(nullptr, compiler->input("SUBSTACK2")->valueBlock());
+    });
+
+    substack2 = std::make_shared<Block>("", "substack");
+    substack2->setParent(if4);
+    substack2->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
+
+    input = std::make_shared<Input>("SUBSTACK2", Input::Type::NoShadow);
+    input->setValueBlock(substack2);
+    if4->addInput(input);
+
+    // Empty 'else' branch
+    auto if5 = std::make_shared<Block>("", "if");
+    if4->setNext(if5);
+    if5->setParent(if4);
+    if5->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginIfStatement());
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        EXPECT_CALL(*m_builder, beginElseBranch()).Times(0);
+        EXPECT_CALL(*m_builder, endIf());
+        compiler->moveToIfElse(compiler->input("SUBSTACK")->valueBlock(), nullptr);
+    });
+
+    substack1 = std::make_shared<Block>("", "substack");
+    substack1->setParent(if5);
+    substack1->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
+
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack1);
+    if5->addInput(input);
+
+    // Code after the if statement
+    auto block = std::make_shared<Block>("", "");
+    block->setParent(if5);
+    if5->setNext(block);
+    block->setCompileFunction([](Compiler *compiler) { compiler->addConstValue("after"); });
+
+    EXPECT_CALL(*m_builder, addConstValue(Value("after")));
+    compile(compiler, if1);
+}
+
+TEST_F(CompilerTest, Input)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    block->addInput(std::make_shared<Input>("TEST", Input::Type::Shadow));
+    block->setCompileFunction([](Compiler *compiler) {
+        ASSERT_EQ(compiler->input("INVALID"), nullptr);
+        ASSERT_EQ(compiler->input("TEST"), compiler->block()->inputAt(0).get());
+    });
+
+    compile(compiler, block);
+}
+
 TEST_F(CompilerTest, Field)
 {
     Compiler compiler(&m_engine, &m_target);

From ec325a3f78a2cbc3c6fe5e91f3eaacee8ee352d5 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 6 Oct 2024 12:59:27 +0200
Subject: [PATCH 36/94] Compiler: Implement if statements

---
 src/dev/engine/compiler.cpp   | 23 ++++++++++++++++++-----
 src/dev/engine/compiler_p.cpp |  2 +-
 src/dev/engine/compiler_p.h   |  1 +
 3 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 730199fd..91b75698 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -39,6 +39,8 @@ std::shared_ptr<libscratchcpp::Block> Compiler::block() const
 std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
 {
     impl->builder = impl->builderFactory->create(startBlock->id());
+    impl->substackTree.clear();
+    impl->substackHit = false;
     impl->warp = false;
     impl->block = startBlock;
 
@@ -52,10 +54,13 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
             impl->unsupportedBlocks.insert(impl->block->opcode());
         }
 
-        if (substacks != impl->substackTree.size())
+        if (impl->substackHit) {
+            impl->substackHit = false;
             continue;
+        }
 
-        impl->block = impl->block->next();
+        if (impl->block)
+            impl->block = impl->block->next();
 
         if (!impl->block && !impl->substackTree.empty())
             impl->substackEnd();
@@ -100,18 +105,25 @@ void Compiler::addInput(const std::string &name)
 /*! Jumps to the given if substack. */
 void Compiler::moveToIf(std::shared_ptr<Block> substack)
 {
+    if (!substack)
+        return; // ignore empty if statements
+
+    impl->substackHit = true;
     impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::IfStatement });
     impl->block = substack;
-
-    if (!impl->block)
-        impl->substackEnd();
+    impl->builder->beginIfStatement();
 }
 
 /*! Jumps to the given if/else substack. The second substack is used for the else branch. */
 void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2)
 {
+    if (!substack1 && !substack2)
+        return; // ignore empty if statements
+
+    impl->substackHit = true;
     impl->substackTree.push_back({ { impl->block, substack2 }, CompilerPrivate::SubstackType::IfStatement });
     impl->block = substack1;
+    impl->builder->beginIfStatement();
 
     if (!impl->block)
         impl->substackEnd();
@@ -120,6 +132,7 @@ void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Bl
 /*! Jumps to the given loop substack. */
 void Compiler::moveToLoop(std::shared_ptr<Block> substack)
 {
+    impl->substackHit = true;
     impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
     impl->block = substack;
 
diff --git a/src/dev/engine/compiler_p.cpp b/src/dev/engine/compiler_p.cpp
index 629f56f4..ea8705f4 100644
--- a/src/dev/engine/compiler_p.cpp
+++ b/src/dev/engine/compiler_p.cpp
@@ -19,7 +19,7 @@ CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) :
 
 void CompilerPrivate::substackEnd()
 {
-    auto parent = substackTree.back();
+    auto &parent = substackTree.back();
 
     switch (parent.second) {
         case SubstackType::Loop:
diff --git a/src/dev/engine/compiler_p.h b/src/dev/engine/compiler_p.h
index 3fda8f72..059d143b 100644
--- a/src/dev/engine/compiler_p.h
+++ b/src/dev/engine/compiler_p.h
@@ -32,6 +32,7 @@ struct CompilerPrivate
 
         std::shared_ptr<Block> block;
         std::vector<std::pair<std::pair<std::shared_ptr<Block>, std::shared_ptr<Block>>, SubstackType>> substackTree;
+        bool substackHit = false;
         bool warp = false;
 
         static inline ICodeBuilderFactory *builderFactory = nullptr;

From ab5d461c853c3f31492159a58cdac09939ab6920 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 10:23:05 +0200
Subject: [PATCH 37/94] ICodeBuilder: Add beginRepeatLoop() method

---
 src/dev/engine/internal/icodebuilder.h      | 2 +-
 src/dev/engine/internal/llvmcodebuilder.cpp | 2 +-
 src/dev/engine/internal/llvmcodebuilder.h   | 2 +-
 test/mocks/codebuildermock.h                | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index c1ea3f0a..7123c914 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -29,7 +29,7 @@ class ICodeBuilder
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
 
-        virtual void beginLoop() = 0;
+        virtual void beginRepeatLoop() = 0;
         virtual void endLoop() = 0;
 
         virtual void yield() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index a5396d51..98abb186 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -220,7 +220,7 @@ void LLVMCodeBuilder::endIf()
     m_steps.push_back(Step(Step::Type::EndIf));
 }
 
-void LLVMCodeBuilder::beginLoop()
+void LLVMCodeBuilder::beginRepeatLoop()
 {
 }
 
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 4a938f19..5f6d9077 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -31,7 +31,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void beginElseBranch() override;
         void endIf() override;
 
-        void beginLoop() override;
+        void beginRepeatLoop() override;
         void endLoop() override;
 
         void yield() override;
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 018a1b6f..8ba3e286 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -18,7 +18,7 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));
 
-        MOCK_METHOD(void, beginLoop, (), (override));
+        MOCK_METHOD(void, beginRepeatLoop, (), (override));
         MOCK_METHOD(void, endLoop, (), (override));
 
         MOCK_METHOD(void, yield, (), (override));

From 24333c7861b3202fb4fd576477f9031d05549dc9 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 10:24:25 +0200
Subject: [PATCH 38/94] LLVMCodeBuilder: Implement repeat loop

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 83 ++++++++++++++++++++
 src/dev/engine/internal/llvmcodebuilder.h   | 13 +++-
 test/dev/llvm/llvmcodebuilder_test.cpp      | 86 +++++++++++++++++++++
 test/dev/llvm/testfunctions.cpp             |  5 ++
 test/dev/llvm/testfunctions.h               |  1 +
 5 files changed, 187 insertions(+), 1 deletion(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 98abb186..7155c5a8 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -39,6 +39,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     size_t functionIndex = 0;
     llvm::Function *currentFunc = beginFunction(functionIndex);
     std::vector<IfStatement> ifStatements;
+    std::vector<Loop> loops;
 
     // Execute recorded steps
     for (const Step &step : m_steps) {
@@ -134,6 +135,77 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 ifStatements.pop_back();
                 break;
             }
+
+            case Step::Type::BeginRepeatLoop: {
+                Loop loop;
+                loop.isRepeatLoop = true;
+
+                // index = 0
+                llvm::Constant *zero = llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true);
+                loop.index = m_builder.CreateAlloca(m_builder.getInt64Ty());
+                m_builder.CreateStore(zero, loop.index);
+
+                // Create branches
+                llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                // Convert last reg to double
+                assert(step.args.size() == 1);
+                llvm::Value *count = m_builder.CreateCall(resolve_value_toDouble(), step.args[0]->value);
+
+                // Clamp count if <= 0 (we can skip the loop if count is not positive)
+                llvm::Value *comparison = m_builder.CreateFCmpULE(count, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)));
+                m_builder.CreateCondBr(comparison, loop.afterLoop, roundBranch);
+
+                // Round (Scratch-specific behavior)
+                m_builder.SetInsertPoint(roundBranch);
+                llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, { count->getType() });
+                count = m_builder.CreateCall(roundFunc, { count });
+                count = m_builder.CreateFPToSI(count, m_builder.getInt64Ty()); // cast to signed integer
+
+                // Jump to condition branch
+                m_builder.CreateBr(loop.conditionBranch);
+
+                // Check index
+                m_builder.SetInsertPoint(loop.conditionBranch);
+
+                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                if (!loop.afterLoop)
+                    loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index);
+                comparison = m_builder.CreateICmpULT(currentIndex, count);
+                m_builder.CreateCondBr(comparison, body, loop.afterLoop);
+
+                // Switch to body branch
+                m_builder.SetInsertPoint(body);
+
+                loops.push_back(loop);
+                break;
+            }
+
+            case Step::Type::EndLoop: {
+                assert(!loops.empty());
+                Loop &loop = loops.back();
+
+                if (loop.isRepeatLoop) {
+                    // Increment index
+                    llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index);
+                    llvm::Value *incremented = m_builder.CreateAdd(currentIndex, llvm::ConstantInt::get(m_builder.getInt64Ty(), 1, true));
+                    m_builder.CreateStore(incremented, loop.index);
+                }
+
+                // Jump to the condition branch
+                m_builder.CreateBr(loop.conditionBranch);
+
+                // Switch to the branch after the loop
+                m_builder.SetInsertPoint(loop.afterLoop);
+
+                loops.pop_back();
+                break;
+            }
         }
     }
 
@@ -222,10 +294,16 @@ void LLVMCodeBuilder::endIf()
 
 void LLVMCodeBuilder::beginRepeatLoop()
 {
+    Step step(Step::Type::BeginRepeatLoop);
+    assert(!m_tmpRegs.empty());
+    step.args.push_back(m_tmpRegs.back());
+    m_tmpRegs.pop_back();
+    m_steps.push_back(step);
 }
 
 void LLVMCodeBuilder::endLoop()
 {
+    m_steps.push_back(Step(Step::Type::EndLoop));
 }
 
 void LLVMCodeBuilder::yield()
@@ -360,6 +438,11 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special()
     return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false));
 }
 
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toDouble()
+{
+    return resolveFunction("value_toDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), m_valueDataType->getPointerTo(), false));
+}
+
 llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool()
 {
     return resolveFunction("value_toBool", llvm::FunctionType::get(m_builder.getInt1Ty(), m_valueDataType->getPointerTo(), false));
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 5f6d9077..d515ea28 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -52,7 +52,9 @@ class LLVMCodeBuilder : public ICodeBuilder
                     Yield,
                     BeginIf,
                     BeginElse,
-                    EndIf
+                    EndIf,
+                    BeginRepeatLoop,
+                    EndLoop
                 };
 
                 Step(Type type) :
@@ -76,6 +78,14 @@ class LLVMCodeBuilder : public ICodeBuilder
                 llvm::BasicBlock *afterIf = nullptr;
         };
 
+        struct Loop
+        {
+                bool isRepeatLoop = false;
+                llvm::Value *index = nullptr;
+                llvm::BasicBlock *conditionBranch = nullptr;
+                llvm::BasicBlock *afterLoop = nullptr;
+        };
+
         void initTypes();
         llvm::Function *beginFunction(size_t index);
         void endFunction(llvm::Function *func, size_t index);
@@ -88,6 +98,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::FunctionCallee resolve_value_assign_bool();
         llvm::FunctionCallee resolve_value_assign_cstring();
         llvm::FunctionCallee resolve_value_assign_special();
+        llvm::FunctionCallee resolve_value_toDouble();
         llvm::FunctionCallee resolve_value_toBool();
 
         std::string m_id;
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 2d1782d0..f544ffd3 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -274,3 +274,89 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
+
+TEST_F(LLVMCodeBuilderTest, RepeatLoop)
+{
+    // Const count
+    m_builder->addConstValue("-5");
+    m_builder->beginRepeatLoop();
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->endLoop();
+
+    m_builder->addConstValue(0);
+    m_builder->beginRepeatLoop();
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->endLoop();
+
+    m_builder->addConstValue(3);
+    m_builder->beginRepeatLoop();
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->endLoop();
+
+    m_builder->addConstValue("2");
+    m_builder->beginRepeatLoop();
+    m_builder->addConstValue(0);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->endLoop();
+
+    // Count returned by function
+    m_builder->addConstValue(2);
+    m_builder->addFunctionCall("test_const", 1, true);
+    m_builder->beginRepeatLoop();
+    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->endLoop();
+
+    // Nested
+    m_builder->addConstValue(2);
+    m_builder->beginRepeatLoop();
+    {
+        m_builder->addConstValue(2);
+        m_builder->beginRepeatLoop();
+        {
+            m_builder->addConstValue(1);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->endLoop();
+
+        m_builder->addConstValue(2);
+        m_builder->addFunctionCall("test_function_1_arg", 1, false);
+
+        m_builder->addConstValue(3);
+        m_builder->beginRepeatLoop();
+        {
+            m_builder->addConstValue(3);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        }
+        m_builder->endLoop();
+    }
+    m_builder->endLoop();
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "no_args\n"
+        "no_args\n"
+        "no_args\n"
+        "1_arg 0\n"
+        "1_arg 0\n"
+        "no_args\n"
+        "no_args\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 2\n"
+        "1_arg 3\n"
+        "1_arg 3\n"
+        "1_arg 3\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 2\n"
+        "1_arg 3\n"
+        "1_arg 3\n"
+        "1_arg 3\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index fa409631..8c543518 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -78,4 +78,9 @@ extern "C"
     {
         value_assign_bool(ret, value_equals(a, b));
     }
+
+    void test_const(Target *target, ValueData *ret, ValueData *v)
+    {
+        value_assign_copy(ret, v);
+    }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 02636b94..f4431714 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -20,6 +20,7 @@ extern "C"
     void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
 
     void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b);
+    void test_const(Target *target, ValueData *ret, ValueData *v);
 }
 
 } // namespace libscratchcpp

From e97b27c652449e71f8f635ff85bbbe3cc342c041 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 12:11:14 +0200
Subject: [PATCH 39/94] Compiler: Implement repeat loop

---
 include/scratchcpp/dev/compiler.h   |  2 +-
 src/dev/engine/compiler.cpp         |  5 +-
 src/dev/engine/compiler_p.cpp       |  4 +-
 test/dev/compiler/compiler_test.cpp | 78 +++++++++++++++++++++++++++++
 4 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index 4ad8f522..dc7ba5d6 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -40,7 +40,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
 
         void moveToIf(std::shared_ptr<Block> substack);
         void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
-        void moveToLoop(std::shared_ptr<Block> substack);
+        void moveToRepeatLoop(std::shared_ptr<Block> substack);
         void warp();
 
         Input *input(const std::string &name) const;
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 91b75698..0024d55f 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -129,12 +129,13 @@ void Compiler::moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Bl
         impl->substackEnd();
 }
 
-/*! Jumps to the given loop substack. */
-void Compiler::moveToLoop(std::shared_ptr<Block> substack)
+/*! Jumps to the given repeat loop substack. */
+void Compiler::moveToRepeatLoop(std::shared_ptr<Block> substack)
 {
     impl->substackHit = true;
     impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
     impl->block = substack;
+    impl->builder->beginRepeatLoop();
 
     if (!impl->block)
         impl->substackEnd();
diff --git a/src/dev/engine/compiler_p.cpp b/src/dev/engine/compiler_p.cpp
index ea8705f4..cff14fe9 100644
--- a/src/dev/engine/compiler_p.cpp
+++ b/src/dev/engine/compiler_p.cpp
@@ -24,8 +24,8 @@ void CompilerPrivate::substackEnd()
     switch (parent.second) {
         case SubstackType::Loop:
             // Yield at loop end if not running without screen refresh
-            if (!warp)
-                builder->yield();
+            /*if (!warp)
+                builder->yield();*/
 
             builder->endLoop();
             break;
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index 6e82a1dc..d2bc4947 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -401,6 +401,84 @@ TEST_F(CompilerTest, MoveToIfElse)
     compile(compiler, if1);
 }
 
+TEST_F(CompilerTest, MoveToRepeatLoop)
+{
+    Compiler compiler(&m_engine, &m_target);
+
+    auto l1 = std::make_shared<Block>("", "loop");
+    l1->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatLoop());
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToRepeatLoop(nullptr);
+    });
+
+    auto l2 = std::make_shared<Block>("", "loop");
+    l1->setNext(l2);
+    l2->setParent(l1);
+    l2->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatLoop());
+        EXPECT_CALL(*m_builder, addConstValue(Value(2)));
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToRepeatLoop(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    auto substack = std::make_shared<Block>("", "substack");
+    substack->setParent(l2);
+    substack->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
+
+    auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack);
+    l2->addInput(input);
+
+    // Nested
+    auto l3 = std::make_shared<Block>("", "loop");
+    l2->setNext(l3);
+    l3->setParent(l2);
+    l3->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatLoop()).Times(2);
+        EXPECT_CALL(*m_builder, endLoop()).Times(2);
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        compiler->moveToRepeatLoop(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    // Begin loop
+    auto loopSubstack = std::make_shared<Block>("", "loop");
+    loopSubstack->setParent(l3);
+    loopSubstack->setCompileFunction([](Compiler *compiler) { compiler->moveToRepeatLoop(compiler->input("SUBSTACK")->valueBlock()); });
+
+    substack = std::make_shared<Block>("", "substack");
+    substack->setParent(loopSubstack);
+    substack->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
+
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack);
+    loopSubstack->addInput(input);
+
+    // End loop
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(loopSubstack);
+    l3->addInput(input);
+
+    // Empty loop body
+    auto l4 = std::make_shared<Block>("", "loop");
+    l3->setNext(l4);
+    l4->setParent(l3);
+    l4->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatLoop());
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToRepeatLoop(nullptr);
+    });
+
+    // Code after the loop
+    auto block = std::make_shared<Block>("", "");
+    block->setParent(l4);
+    l4->setNext(block);
+    block->setCompileFunction([](Compiler *compiler) { compiler->addConstValue("after"); });
+
+    EXPECT_CALL(*m_builder, addConstValue(Value("after")));
+    compile(compiler, l1);
+}
+
 TEST_F(CompilerTest, Input)
 {
     Compiler compiler(&m_engine, &m_target);

From 94089593838e06aad5dc2b6a663a551d00eaf85e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 22:03:20 +0200
Subject: [PATCH 40/94] LLVMCodeBuilder: Implement while loop

---
 src/dev/engine/internal/icodebuilder.h      |  2 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 42 +++++++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  4 ++
 test/dev/llvm/llvmcodebuilder_test.cpp      | 77 +++++++++++++++++++++
 test/dev/llvm/testfunctions.cpp             | 28 ++++++++
 test/dev/llvm/testfunctions.h               |  7 ++
 test/mocks/codebuildermock.h                |  2 +
 7 files changed, 162 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 7123c914..1210bc16 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -30,6 +30,8 @@ class ICodeBuilder
         virtual void endIf() = 0;
 
         virtual void beginRepeatLoop() = 0;
+        virtual void beginWhileLoop() = 0;
+        virtual void beginLoopCondition() = 0;
         virtual void endLoop() = 0;
 
         virtual void yield() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 7155c5a8..52804367 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -186,6 +186,34 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::BeginWhileLoop: {
+                assert(!loops.empty());
+                Loop &loop = loops.back();
+
+                // Create branches
+                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                // Convert last reg to bool and add condition
+                assert(step.args.size() == 1);
+                llvm::Value *condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
+                m_builder.CreateCondBr(condition, body, loop.afterLoop);
+
+                // Switch to body branch
+                m_builder.SetInsertPoint(body);
+                break;
+            }
+
+            case Step::Type::BeginLoopCondition: {
+                Loop loop;
+                loop.isRepeatLoop = false;
+                loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                m_builder.CreateBr(loop.conditionBranch);
+                m_builder.SetInsertPoint(loop.conditionBranch);
+                loops.push_back(loop);
+                break;
+            }
+
             case Step::Type::EndLoop: {
                 assert(!loops.empty());
                 Loop &loop = loops.back();
@@ -301,6 +329,20 @@ void LLVMCodeBuilder::beginRepeatLoop()
     m_steps.push_back(step);
 }
 
+void LLVMCodeBuilder::beginWhileLoop()
+{
+    Step step(Step::Type::BeginWhileLoop);
+    assert(!m_tmpRegs.empty());
+    step.args.push_back(m_tmpRegs.back());
+    m_tmpRegs.pop_back();
+    m_steps.push_back(step);
+}
+
+void LLVMCodeBuilder::beginLoopCondition()
+{
+    m_steps.push_back(Step(Step::Type::BeginLoopCondition));
+}
+
 void LLVMCodeBuilder::endLoop()
 {
     m_steps.push_back(Step(Step::Type::EndLoop));
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index d515ea28..00006890 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -32,6 +32,8 @@ class LLVMCodeBuilder : public ICodeBuilder
         void endIf() override;
 
         void beginRepeatLoop() override;
+        void beginWhileLoop() override;
+        void beginLoopCondition() override;
         void endLoop() override;
 
         void yield() override;
@@ -54,6 +56,8 @@ class LLVMCodeBuilder : public ICodeBuilder
                     BeginElse,
                     EndIf,
                     BeginRepeatLoop,
+                    BeginWhileLoop,
+                    BeginLoopCondition,
                     EndLoop
                 };
 
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index f544ffd3..f2abe9c6 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -360,3 +360,80 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
+
+TEST_F(LLVMCodeBuilderTest, WhileLoop)
+{
+    // Const condition
+    m_builder->beginLoopCondition();
+    m_builder->addConstValue("false");
+    m_builder->beginWhileLoop();
+    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->endLoop();
+
+    m_builder->beginLoopCondition();
+    m_builder->addConstValue(false);
+    m_builder->beginWhileLoop();
+    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->endLoop();
+
+    // Condition returned by function
+    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->beginLoopCondition();
+    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addConstValue(2);
+    m_builder->addFunctionCall("test_lower_than", 2, true);
+    m_builder->beginWhileLoop();
+    m_builder->addConstValue(0);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_increment_counter", 0, false);
+    m_builder->endLoop();
+
+    // Nested
+    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->beginLoopCondition();
+    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addConstValue(3);
+    m_builder->addFunctionCall("test_lower_than", 2, true);
+    m_builder->beginWhileLoop();
+    {
+        m_builder->beginLoopCondition();
+        m_builder->addFunctionCall("test_get_counter", 0, true);
+        m_builder->addConstValue(3);
+        m_builder->addFunctionCall("test_lower_than", 2, true);
+        m_builder->beginWhileLoop();
+        {
+            m_builder->addConstValue(1);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_increment_counter", 0, false);
+        }
+        m_builder->endLoop();
+
+        m_builder->addConstValue(2);
+        m_builder->addFunctionCall("test_function_1_arg", 1, false);
+
+        m_builder->beginLoopCondition();
+        m_builder->addConstValue(false);
+        m_builder->beginWhileLoop();
+        {
+            m_builder->addFunctionCall("test_unreachable", 0, false);
+        }
+        m_builder->endLoop();
+    }
+    m_builder->endLoop();
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "1_arg 0\n"
+        "1_arg 0\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 2\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index 8c543518..7052795f 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -7,6 +7,8 @@
 
 using namespace libscratchcpp;
 
+static int counter = 0;
+
 extern "C"
 {
     void test_function(TestMock *mock, Target *target)
@@ -79,8 +81,34 @@ extern "C"
         value_assign_bool(ret, value_equals(a, b));
     }
 
+    void test_unreachable(Target *target)
+    {
+        std::cout << "error: unreachable reached" << std::endl;
+        exit(1);
+    }
+
+    void test_lower_than(Target *target, ValueData *ret, ValueData *a, ValueData *b)
+    {
+        value_assign_bool(ret, value_lower(a, b));
+    }
+
     void test_const(Target *target, ValueData *ret, ValueData *v)
     {
         value_assign_copy(ret, v);
     }
+
+    void test_reset_counter(Target *target)
+    {
+        counter = 0;
+    }
+
+    void test_increment_counter(Target *target)
+    {
+        counter++;
+    }
+
+    void test_get_counter(Target *target, ValueData *ret)
+    {
+        value_assign_int(ret, counter);
+    }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index f4431714..39b27353 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -20,7 +20,14 @@ extern "C"
     void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
 
     void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b);
+    void test_lower_than(Target *target, ValueData *ret, ValueData *a, ValueData *b);
     void test_const(Target *target, ValueData *ret, ValueData *v);
+
+    void test_unreachable(Target *target);
+
+    void test_reset_counter(Target *target);
+    void test_increment_counter(Target *target);
+    void test_get_counter(Target *target, ValueData *ret);
 }
 
 } // namespace libscratchcpp
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 8ba3e286..54efa59b 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -19,6 +19,8 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, endIf, (), (override));
 
         MOCK_METHOD(void, beginRepeatLoop, (), (override));
+        MOCK_METHOD(void, beginWhileLoop, (), (override));
+        MOCK_METHOD(void, beginLoopCondition, (), (override));
         MOCK_METHOD(void, endLoop, (), (override));
 
         MOCK_METHOD(void, yield, (), (override));

From 92ad9fa0ceca4e586c8fbc6e7d2fefbdc09ea5d9 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 22:34:00 +0200
Subject: [PATCH 41/94] Compiler: Implement while loop

---
 include/scratchcpp/dev/compiler.h   |  2 +
 src/dev/engine/compiler.cpp         | 18 ++++++
 test/dev/compiler/compiler_test.cpp | 90 +++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index dc7ba5d6..5d2dc592 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -41,6 +41,8 @@ class LIBSCRATCHCPP_EXPORT Compiler
         void moveToIf(std::shared_ptr<Block> substack);
         void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
         void moveToRepeatLoop(std::shared_ptr<Block> substack);
+        void moveToWhileLoop(std::shared_ptr<Block> substack);
+        void beginLoopCondition();
         void warp();
 
         Input *input(const std::string &name) const;
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 0024d55f..3e87432a 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -141,6 +141,24 @@ void Compiler::moveToRepeatLoop(std::shared_ptr<Block> substack)
         impl->substackEnd();
 }
 
+/*! Jumps to the given while loop substack. */
+void Compiler::moveToWhileLoop(std::shared_ptr<Block> substack)
+{
+    impl->substackHit = true;
+    impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
+    impl->block = substack;
+    impl->builder->beginWhileLoop();
+
+    if (!impl->block)
+        impl->substackEnd();
+}
+
+/*! Begins a while/until loop condition. */
+void Compiler::beginLoopCondition()
+{
+    impl->builder->beginLoopCondition();
+}
+
 /*! Makes current script run without screen refresh. */
 void Compiler::warp()
 {
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index d2bc4947..4802fae9 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -479,6 +479,96 @@ TEST_F(CompilerTest, MoveToRepeatLoop)
     compile(compiler, l1);
 }
 
+TEST_F(CompilerTest, MoveToWhileLoop)
+{
+    Compiler compiler(&m_engine, &m_target);
+
+    auto l1 = std::make_shared<Block>("", "loop");
+    l1->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginWhileLoop());
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToWhileLoop(nullptr);
+    });
+
+    auto l2 = std::make_shared<Block>("", "loop");
+    l1->setNext(l2);
+    l2->setParent(l1);
+    l2->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginWhileLoop());
+        EXPECT_CALL(*m_builder, addConstValue(Value(2)));
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToWhileLoop(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    auto substack = std::make_shared<Block>("", "substack");
+    substack->setParent(l2);
+    substack->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
+
+    auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack);
+    l2->addInput(input);
+
+    // Nested
+    auto l3 = std::make_shared<Block>("", "loop");
+    l2->setNext(l3);
+    l3->setParent(l2);
+    l3->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginWhileLoop()).Times(2);
+        EXPECT_CALL(*m_builder, endLoop()).Times(2);
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        compiler->moveToWhileLoop(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    // Begin loop
+    auto loopSubstack = std::make_shared<Block>("", "loop");
+    loopSubstack->setParent(l3);
+    loopSubstack->setCompileFunction([](Compiler *compiler) { compiler->moveToWhileLoop(compiler->input("SUBSTACK")->valueBlock()); });
+
+    substack = std::make_shared<Block>("", "substack");
+    substack->setParent(loopSubstack);
+    substack->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
+
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack);
+    loopSubstack->addInput(input);
+
+    // End loop
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(loopSubstack);
+    l3->addInput(input);
+
+    // Empty loop body
+    auto l4 = std::make_shared<Block>("", "loop");
+    l3->setNext(l4);
+    l4->setParent(l3);
+    l4->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginWhileLoop());
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToWhileLoop(nullptr);
+    });
+
+    // Code after the loop
+    auto block = std::make_shared<Block>("", "");
+    block->setParent(l4);
+    l4->setNext(block);
+    block->setCompileFunction([](Compiler *compiler) { compiler->addConstValue("after"); });
+
+    EXPECT_CALL(*m_builder, addConstValue(Value("after")));
+    compile(compiler, l1);
+}
+
+TEST_F(CompilerTest, BeginLoopCondition)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("a", "");
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginLoopCondition());
+        compiler->beginLoopCondition();
+    });
+
+    compile(compiler, block);
+}
+
 TEST_F(CompilerTest, Input)
 {
     Compiler compiler(&m_engine, &m_target);

From d19dbfa70625e17094b150348f18162d2722835c Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 22:44:19 +0200
Subject: [PATCH 42/94] LLVMCodeBuilder: Implement repeat until loop

---
 src/dev/engine/internal/icodebuilder.h      |  1 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 27 +++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  2 +
 test/dev/llvm/llvmcodebuilder_test.cpp      | 80 +++++++++++++++++++++
 test/dev/llvm/testfunctions.cpp             |  5 ++
 test/dev/llvm/testfunctions.h               |  1 +
 test/mocks/codebuildermock.h                |  1 +
 7 files changed, 117 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 1210bc16..5a0ed8be 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -31,6 +31,7 @@ class ICodeBuilder
 
         virtual void beginRepeatLoop() = 0;
         virtual void beginWhileLoop() = 0;
+        virtual void beginRepeatUntilLoop() = 0;
         virtual void beginLoopCondition() = 0;
         virtual void endLoop() = 0;
 
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 52804367..943ff064 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -204,6 +204,24 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::BeginRepeatUntilLoop: {
+                assert(!loops.empty());
+                Loop &loop = loops.back();
+
+                // Create branches
+                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+
+                // Convert last reg to bool and add condition
+                assert(step.args.size() == 1);
+                llvm::Value *condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
+                m_builder.CreateCondBr(condition, loop.afterLoop, body);
+
+                // Switch to body branch
+                m_builder.SetInsertPoint(body);
+                break;
+            }
+
             case Step::Type::BeginLoopCondition: {
                 Loop loop;
                 loop.isRepeatLoop = false;
@@ -338,6 +356,15 @@ void LLVMCodeBuilder::beginWhileLoop()
     m_steps.push_back(step);
 }
 
+void LLVMCodeBuilder::beginRepeatUntilLoop()
+{
+    Step step(Step::Type::BeginRepeatUntilLoop);
+    assert(!m_tmpRegs.empty());
+    step.args.push_back(m_tmpRegs.back());
+    m_tmpRegs.pop_back();
+    m_steps.push_back(step);
+}
+
 void LLVMCodeBuilder::beginLoopCondition()
 {
     m_steps.push_back(Step(Step::Type::BeginLoopCondition));
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 00006890..530f244d 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -33,6 +33,7 @@ class LLVMCodeBuilder : public ICodeBuilder
 
         void beginRepeatLoop() override;
         void beginWhileLoop() override;
+        void beginRepeatUntilLoop() override;
         void beginLoopCondition() override;
         void endLoop() override;
 
@@ -57,6 +58,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     EndIf,
                     BeginRepeatLoop,
                     BeginWhileLoop,
+                    BeginRepeatUntilLoop,
                     BeginLoopCondition,
                     EndLoop
                 };
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index f2abe9c6..18942ea0 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -437,3 +437,83 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop)
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
+
+TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop)
+{
+    // Const condition
+    m_builder->beginLoopCondition();
+    m_builder->addConstValue("true");
+    m_builder->beginRepeatUntilLoop();
+    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->endLoop();
+
+    m_builder->beginLoopCondition();
+    m_builder->addConstValue(true);
+    m_builder->beginRepeatUntilLoop();
+    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->endLoop();
+
+    // Condition returned by function
+    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->beginLoopCondition();
+    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addConstValue(2);
+    m_builder->addFunctionCall("test_lower_than", 2, true);
+    m_builder->addFunctionCall("test_not", 1, true);
+    m_builder->beginRepeatUntilLoop();
+    m_builder->addConstValue(0);
+    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_increment_counter", 0, false);
+    m_builder->endLoop();
+
+    // Nested
+    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->beginLoopCondition();
+    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addConstValue(3);
+    m_builder->addFunctionCall("test_lower_than", 2, true);
+    m_builder->addFunctionCall("test_not", 1, true);
+    m_builder->beginRepeatUntilLoop();
+    {
+        m_builder->beginLoopCondition();
+        m_builder->addFunctionCall("test_get_counter", 0, true);
+        m_builder->addConstValue(3);
+        m_builder->addFunctionCall("test_lower_than", 2, true);
+        m_builder->addFunctionCall("test_not", 1, true);
+        m_builder->beginRepeatUntilLoop();
+        {
+            m_builder->addConstValue(1);
+            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_increment_counter", 0, false);
+        }
+        m_builder->endLoop();
+
+        m_builder->addConstValue(2);
+        m_builder->addFunctionCall("test_function_1_arg", 1, false);
+
+        m_builder->beginLoopCondition();
+        m_builder->addConstValue(true);
+        m_builder->beginRepeatUntilLoop();
+        {
+            m_builder->addFunctionCall("test_unreachable", 0, false);
+        }
+        m_builder->endLoop();
+    }
+    m_builder->endLoop();
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "1_arg 0\n"
+        "1_arg 0\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 2\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index 7052795f..c31d148f 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -97,6 +97,11 @@ extern "C"
         value_assign_copy(ret, v);
     }
 
+    void test_not(Target *target, ValueData *ret, ValueData *arg)
+    {
+        value_assign_bool(ret, !value_toBool(arg));
+    }
+
     void test_reset_counter(Target *target)
     {
         counter = 0;
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 39b27353..b0142013 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -21,6 +21,7 @@ extern "C"
 
     void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b);
     void test_lower_than(Target *target, ValueData *ret, ValueData *a, ValueData *b);
+    void test_not(Target *target, ValueData *ret, ValueData *arg);
     void test_const(Target *target, ValueData *ret, ValueData *v);
 
     void test_unreachable(Target *target);
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 54efa59b..ac9ca92d 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -20,6 +20,7 @@ class CodeBuilderMock : public ICodeBuilder
 
         MOCK_METHOD(void, beginRepeatLoop, (), (override));
         MOCK_METHOD(void, beginWhileLoop, (), (override));
+        MOCK_METHOD(void, beginRepeatUntilLoop, (), (override));
         MOCK_METHOD(void, beginLoopCondition, (), (override));
         MOCK_METHOD(void, endLoop, (), (override));
 

From 4e53ef65da1564bc1f356fc62b65d5131b22ff73 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 8 Oct 2024 22:48:42 +0200
Subject: [PATCH 43/94] Compiler: Implement repeat until loop

---
 include/scratchcpp/dev/compiler.h   |  1 +
 src/dev/engine/compiler.cpp         | 12 +++++
 test/dev/compiler/compiler_test.cpp | 78 +++++++++++++++++++++++++++++
 3 files changed, 91 insertions(+)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index 5d2dc592..aa094c46 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -42,6 +42,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
         void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
         void moveToRepeatLoop(std::shared_ptr<Block> substack);
         void moveToWhileLoop(std::shared_ptr<Block> substack);
+        void moveToRepeatUntilLoop(std::shared_ptr<Block> substack);
         void beginLoopCondition();
         void warp();
 
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 3e87432a..84376d1f 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -153,6 +153,18 @@ void Compiler::moveToWhileLoop(std::shared_ptr<Block> substack)
         impl->substackEnd();
 }
 
+/*! Jumps to the given until loop substack. */
+void Compiler::moveToRepeatUntilLoop(std::shared_ptr<Block> substack)
+{
+    impl->substackHit = true;
+    impl->substackTree.push_back({ { impl->block, nullptr }, CompilerPrivate::SubstackType::Loop });
+    impl->block = substack;
+    impl->builder->beginRepeatUntilLoop();
+
+    if (!impl->block)
+        impl->substackEnd();
+}
+
 /*! Begins a while/until loop condition. */
 void Compiler::beginLoopCondition()
 {
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index 4802fae9..ba615499 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -557,6 +557,84 @@ TEST_F(CompilerTest, MoveToWhileLoop)
     compile(compiler, l1);
 }
 
+TEST_F(CompilerTest, MoveToRepeatUntilLoop)
+{
+    Compiler compiler(&m_engine, &m_target);
+
+    auto l1 = std::make_shared<Block>("", "loop");
+    l1->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatUntilLoop());
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToRepeatUntilLoop(nullptr);
+    });
+
+    auto l2 = std::make_shared<Block>("", "loop");
+    l1->setNext(l2);
+    l2->setParent(l1);
+    l2->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatUntilLoop());
+        EXPECT_CALL(*m_builder, addConstValue(Value(2)));
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToRepeatUntilLoop(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    auto substack = std::make_shared<Block>("", "substack");
+    substack->setParent(l2);
+    substack->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(2); });
+
+    auto input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack);
+    l2->addInput(input);
+
+    // Nested
+    auto l3 = std::make_shared<Block>("", "loop");
+    l2->setNext(l3);
+    l3->setParent(l2);
+    l3->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatUntilLoop()).Times(2);
+        EXPECT_CALL(*m_builder, endLoop()).Times(2);
+        EXPECT_CALL(*m_builder, addConstValue(Value(1)));
+        compiler->moveToRepeatUntilLoop(compiler->input("SUBSTACK")->valueBlock());
+    });
+
+    // Begin loop
+    auto loopSubstack = std::make_shared<Block>("", "loop");
+    loopSubstack->setParent(l3);
+    loopSubstack->setCompileFunction([](Compiler *compiler) { compiler->moveToRepeatUntilLoop(compiler->input("SUBSTACK")->valueBlock()); });
+
+    substack = std::make_shared<Block>("", "substack");
+    substack->setParent(loopSubstack);
+    substack->setCompileFunction([](Compiler *compiler) { compiler->addConstValue(1); });
+
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(substack);
+    loopSubstack->addInput(input);
+
+    // End loop
+    input = std::make_shared<Input>("SUBSTACK", Input::Type::NoShadow);
+    input->setValueBlock(loopSubstack);
+    l3->addInput(input);
+
+    // Empty loop body
+    auto l4 = std::make_shared<Block>("", "loop");
+    l3->setNext(l4);
+    l4->setParent(l3);
+    l4->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, beginRepeatUntilLoop());
+        EXPECT_CALL(*m_builder, endLoop());
+        compiler->moveToRepeatUntilLoop(nullptr);
+    });
+
+    // Code after the loop
+    auto block = std::make_shared<Block>("", "");
+    block->setParent(l4);
+    l4->setNext(block);
+    block->setCompileFunction([](Compiler *compiler) { compiler->addConstValue("after"); });
+
+    EXPECT_CALL(*m_builder, addConstValue(Value("after")));
+    compile(compiler, l1);
+}
+
 TEST_F(CompilerTest, BeginLoopCondition)
 {
     Compiler compiler(&m_engine, &m_target);

From 51a18983a8b0cce75092bd641ea2927592b350e1 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 10 Oct 2024 22:21:42 +0200
Subject: [PATCH 44/94] Drop integer value type

---
 include/scratchcpp/valuedata.h      |  10 +--
 src/scratch/inputvalue.cpp          |   6 +-
 src/scratch/value_functions.cpp     | 134 +++++++++++-----------------
 src/scratch/value_functions_p.h     |  24 ++---
 test/scratch_classes/value_test.cpp |  56 ++++++------
 5 files changed, 94 insertions(+), 136 deletions(-)

diff --git a/include/scratchcpp/valuedata.h b/include/scratchcpp/valuedata.h
index addaee25..b3af3e1d 100644
--- a/include/scratchcpp/valuedata.h
+++ b/include/scratchcpp/valuedata.h
@@ -18,10 +18,9 @@ enum class LIBSCRATCHCPP_EXPORT SpecialValue
 
 enum class LIBSCRATCHCPP_EXPORT ValueType
 {
-    Integer = 0,
-    Double = 1,
-    Bool = 2,
-    String = 3,
+    Number = 0,
+    Bool = 1,
+    String = 2,
     Infinity = -1,
     NegativeInfinity = -2,
     NaN = -3
@@ -35,8 +34,7 @@ extern "C"
             // NOTE: Any changes must also be done in the LLVM code builder!
             union
             {
-                    long intValue;
-                    double doubleValue;
+                    double numberValue;
                     bool boolValue;
                     char *stringValue;
             };
diff --git a/src/scratch/inputvalue.cpp b/src/scratch/inputvalue.cpp
index 57b9e79b..9b6b732d 100644
--- a/src/scratch/inputvalue.cpp
+++ b/src/scratch/inputvalue.cpp
@@ -18,9 +18,9 @@
 using namespace libscratchcpp;
 
 static const std::map<ValueType, InputValue::Type> VALUE_TYPE_MAP = {
-    { ValueType::Integer, InputValue::Type::Number }, { ValueType::Double, InputValue::Type::Number },   { ValueType::Bool, InputValue::Type::String },
-    { ValueType::String, InputValue::Type::String },  { ValueType::Infinity, InputValue::Type::String }, { ValueType::NegativeInfinity, InputValue::Type::String },
-    { ValueType::NaN, InputValue::Type::String }
+    { ValueType::Number, InputValue::Type::Number },           { ValueType::Bool, InputValue::Type::String },
+    { ValueType::String, InputValue::Type::String },           { ValueType::Infinity, InputValue::Type::String },
+    { ValueType::NegativeInfinity, InputValue::Type::String }, { ValueType::NaN, InputValue::Type::String }
 };
 
 /*! Constructs InputValue with the given type. */
diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index cfa8db4a..f32506b6 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -27,8 +27,8 @@ extern "C"
     /*! Initializes the given value. */
     void value_init(ValueData *v)
     {
-        v->type = ValueType::Integer;
-        v->intValue = 0;
+        v->type = ValueType::Number;
+        v->numberValue = 0;
     }
 
     /* assign */
@@ -45,8 +45,8 @@ extern "C"
         else if (std::isnan(numberValue))
             v->type = ValueType::NaN;
         else {
-            v->type = ValueType::Double;
-            v->doubleValue = value_floatToDouble(numberValue);
+            v->type = ValueType::Number;
+            v->numberValue = value_floatToDouble(numberValue);
         }
     }
 
@@ -62,8 +62,8 @@ extern "C"
         else if (std::isnan(numberValue))
             v->type = ValueType::NaN;
         else {
-            v->type = ValueType::Double;
-            v->doubleValue = numberValue;
+            v->type = ValueType::Number;
+            v->numberValue = numberValue;
         }
     }
 
@@ -72,8 +72,8 @@ extern "C"
     {
         value_free(v);
 
-        v->type = ValueType::Integer;
-        v->intValue = numberValue;
+        v->type = ValueType::Number;
+        v->numberValue = numberValue;
     }
 
     /*! Assigns number of type 'size_t' to the given value. */
@@ -81,8 +81,8 @@ extern "C"
     {
         value_free(v);
 
-        v->type = ValueType::Integer;
-        v->intValue = numberValue;
+        v->type = ValueType::Number;
+        v->numberValue = numberValue;
     }
 
     /*! Assigns number of type 'long' to the given value. */
@@ -90,8 +90,8 @@ extern "C"
     {
         value_free(v);
 
-        v->type = ValueType::Integer;
-        v->intValue = numberValue;
+        v->type = ValueType::Number;
+        v->numberValue = numberValue;
     }
 
     /*! Assigns boolean to the given value. */
@@ -141,20 +141,17 @@ extern "C"
         else if (specialValue == SpecialValue::NaN)
             v->type = ValueType::NaN;
         else {
-            v->type = ValueType::Integer;
-            v->intValue = 0;
+            v->type = ValueType::Number;
+            v->numberValue = 0;
         }
     }
 
     /*! Assigns another value to the given value. */
     void value_assign_copy(ValueData *v, const libscratchcpp::ValueData *another)
     {
-        if (another->type == ValueType::Integer) {
+        if (another->type == ValueType::Number) {
             value_free(v);
-            v->intValue = another->intValue;
-        } else if (another->type == ValueType::Double) {
-            value_free(v);
-            v->doubleValue = another->doubleValue;
+            v->numberValue = another->numberValue;
         } else if (another->type == ValueType::Bool) {
             value_free(v);
             v->boolValue = another->boolValue;
@@ -178,10 +175,8 @@ extern "C"
         switch (v->type) {
             case ValueType::Infinity:
                 return true;
-            case ValueType::Integer:
-                return value_isInf(v->intValue);
-            case ValueType::Double:
-                return value_isInf(v->doubleValue);
+            case ValueType::Number:
+                return value_isInf(v->numberValue);
             case ValueType::String:
                 return strcmp(v->stringValue, "Infinity") == 0;
             default:
@@ -195,10 +190,8 @@ extern "C"
         switch (v->type) {
             case ValueType::NegativeInfinity:
                 return true;
-            case ValueType::Integer:
-                return value_isNegativeInf(-v->intValue);
-            case ValueType::Double:
-                return value_isNegativeInf(-v->doubleValue);
+            case ValueType::Number:
+                return value_isNegativeInf(-v->numberValue);
             case ValueType::String:
                 return strcmp(v->stringValue, "-Infinity") == 0;
             default:
@@ -212,9 +205,9 @@ extern "C"
         switch (v->type) {
             case ValueType::NaN:
                 return true;
-            case ValueType::Double:
-                assert(!std::isnan(v->doubleValue));
-                return std::isnan(v->doubleValue);
+            case ValueType::Number:
+                assert(!std::isnan(v->numberValue));
+                return std::isnan(v->numberValue);
             case ValueType::String:
                 return strcmp(v->stringValue, "NaN") == 0;
             default:
@@ -225,7 +218,7 @@ extern "C"
     /*! Returns true if the given value is a number. */
     bool value_isNumber(const libscratchcpp::ValueData *v)
     {
-        return v->type == ValueType::Integer || v->type == ValueType::Double;
+        return v->type == ValueType::Number;
     }
 
     /*! Returns true if the given value is a number or can be converted to a number. */
@@ -240,8 +233,7 @@ extern "C"
         assert(v->type != ValueType::Infinity && v->type != ValueType::NegativeInfinity);
 
         switch (v->type) {
-            case ValueType::Integer:
-            case ValueType::Double:
+            case ValueType::Number:
             case ValueType::Bool:
                 return true;
             case ValueType::String:
@@ -256,16 +248,15 @@ extern "C"
     {
         // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L157-L181
         switch (v->type) {
-            case ValueType::Integer:
             case ValueType::Bool:
             case ValueType::Infinity:
             case ValueType::NegativeInfinity:
             case ValueType::NaN:
                 return true;
-            case ValueType::Double: {
+            case ValueType::Number: {
                 double intpart;
-                std::modf(v->doubleValue, &intpart);
-                return v->doubleValue == intpart;
+                std::modf(v->numberValue, &intpart);
+                return v->numberValue == intpart;
             }
             case ValueType::String:
                 return value_checkString(v->stringValue) == 1;
@@ -291,10 +282,8 @@ extern "C"
     /*! Returns the long representation of the given value. */
     long value_toLong(const libscratchcpp::ValueData *v)
     {
-        if (v->type == ValueType::Integer)
-            return v->intValue;
-        else if (v->type == ValueType::Double)
-            return v->doubleValue;
+        if (v->type == ValueType::Number)
+            return v->numberValue;
         else if (v->type == ValueType::Bool)
             return v->boolValue;
         else if (v->type == ValueType::String)
@@ -306,10 +295,8 @@ extern "C"
     /*! Returns the int representation of the given value. */
     int value_toInt(const libscratchcpp::ValueData *v)
     {
-        if (v->type == ValueType::Integer)
-            return v->intValue;
-        else if (v->type == ValueType::Double)
-            return v->doubleValue;
+        if (v->type == ValueType::Number)
+            return v->numberValue;
         else if (v->type == ValueType::Bool)
             return v->boolValue;
         else if (v->type == ValueType::String)
@@ -321,10 +308,8 @@ extern "C"
     /*! Returns the double representation of the given value. */
     double value_toDouble(const libscratchcpp::ValueData *v)
     {
-        if (v->type == ValueType::Double)
-            return v->doubleValue;
-        else if (v->type == ValueType::Integer)
-            return v->intValue;
+        if (v->type == ValueType::Number)
+            return v->numberValue;
         else if (v->type == ValueType::Bool)
             return v->boolValue;
         else if (v->type == ValueType::String)
@@ -342,10 +327,8 @@ extern "C"
     {
         if (v->type == ValueType::Bool) {
             return v->boolValue;
-        } else if (v->type == ValueType::Integer) {
-            return v->intValue != 0;
-        } else if (v->type == ValueType::Double) {
-            return v->doubleValue != 0;
+        } else if (v->type == ValueType::Number) {
+            return v->numberValue != 0;
         } else if (v->type == ValueType::String) {
             return strlen(v->stringValue) != 0 && !value_stringsEqual(v->stringValue, "false") && strcmp(v->stringValue, "0") != 0;
         } else if (v->type == ValueType::Infinity || v->type == ValueType::NegativeInfinity) {
@@ -362,10 +345,8 @@ extern "C"
     {
         if (v->type == ValueType::String)
             dst->assign(v->stringValue);
-        else if (v->type == ValueType::Integer)
-            dst->assign(std::to_string(v->intValue));
-        else if (v->type == ValueType::Double)
-            dst->assign(value_doubleToString(v->doubleValue));
+        else if (v->type == ValueType::Number)
+            dst->assign(value_doubleToString(v->numberValue));
         else if (v->type == ValueType::Bool)
             dst->assign(v->boolValue ? "true" : "false");
         else if (v->type == ValueType::Infinity)
@@ -391,11 +372,8 @@ extern "C"
     /*! Adds the given values and writes the result to dst. */
     void value_add(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if (v1->type == ValueType::Integer && v2->type == ValueType::Integer) {
-            value_assign_long(dst, v1->intValue + v2->intValue);
-            return;
-        } else if (v1->type == ValueType::Double && v2->type == ValueType::Double) {
-            value_assign_double(dst, v1->doubleValue + v2->doubleValue);
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number) {
+            value_assign_double(dst, v1->numberValue + v2->numberValue);
             return;
         } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) {
             value_assign_long(dst, v1->boolValue + v2->boolValue);
@@ -419,11 +397,8 @@ extern "C"
     /*! Subtracts the given values and writes the result to dst. */
     void value_subtract(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if (v1->type == ValueType::Integer && v2->type == ValueType::Integer) {
-            value_assign_long(dst, v1->intValue - v2->intValue);
-            return;
-        } else if (v1->type == ValueType::Double && v2->type == ValueType::Double) {
-            value_assign_double(dst, v1->doubleValue - v2->doubleValue);
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number) {
+            value_assign_double(dst, v1->numberValue - v2->numberValue);
             return;
         } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) {
             value_assign_long(dst, v1->boolValue - v2->boolValue);
@@ -447,10 +422,9 @@ extern "C"
     /*! Multiplies the given values and writes the result to dst. */
     void value_multiply(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if (v1->type == ValueType::Integer && v2->type == ValueType::Integer)
-            value_assign_long(dst, v1->intValue * v2->intValue);
-        else if (v1->type == ValueType::Double && v2->type == ValueType::Double)
-            value_assign_double(dst, v1->doubleValue * v2->doubleValue);
+
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
+            value_assign_double(dst, v1->numberValue * v2->numberValue);
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             value_assign_long(dst, v1->boolValue * v2->boolValue);
         else {
@@ -529,10 +503,8 @@ extern "C"
         // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150
         assert(v1 && v2);
 
-        if (v1->type == ValueType::Integer && v2->type == ValueType::Integer)
-            return v1->intValue == v2->intValue;
-        else if (v1->type == ValueType::Double && v2->type == ValueType::Double)
-            return v1->doubleValue == v2->doubleValue;
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
+            return v1->numberValue == v2->numberValue;
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             return v1->boolValue == v2->boolValue;
 
@@ -568,10 +540,8 @@ extern "C"
     {
         assert(v1 && v2);
 
-        if (v1->type == ValueType::Integer && v2->type == ValueType::Integer)
-            return v1->intValue > v2->intValue;
-        else if (v1->type == ValueType::Double && v2->type == ValueType::Double)
-            return v1->doubleValue > v2->doubleValue;
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
+            return v1->numberValue > v2->numberValue;
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             return v1->boolValue > v2->boolValue;
         else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
@@ -605,10 +575,8 @@ extern "C"
     {
         assert(v1 && v2);
 
-        if (v1->type == ValueType::Integer && v2->type == ValueType::Integer)
-            return v1->intValue < v2->intValue;
-        else if (v1->type == ValueType::Double && v2->type == ValueType::Double)
-            return v1->doubleValue < v2->doubleValue;
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
+            return v1->numberValue < v2->numberValue;
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             return v1->boolValue < v2->boolValue;
         else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h
index 3ccd447d..f71b1238 100644
--- a/src/scratch/value_functions_p.h
+++ b/src/scratch/value_functions_p.h
@@ -483,10 +483,8 @@ extern "C"
                 *ok = false;
 
             return 0;
-        } else if (v->type == ValueType::Integer)
-            return v->intValue;
-        else if (v->type == ValueType::Double)
-            return v->doubleValue;
+        } else if (v->type == ValueType::Number)
+            return v->numberValue;
         else if (v->type == ValueType::Bool)
             return v->boolValue;
         else {
@@ -500,10 +498,8 @@ extern "C"
 
     bool value_isPositive(const ValueData *v)
     {
-        if (v->type == ValueType::Integer)
-            return v->intValue > 0;
-        else if (v->type == ValueType::Double)
-            return v->doubleValue > 0;
+        if (v->type == ValueType::Number)
+            return v->numberValue > 0;
         else if (v->type == ValueType::Bool)
             return v->boolValue;
         else if (static_cast<int>(v->type) < 0) {
@@ -518,10 +514,8 @@ extern "C"
 
     bool value_isNegative(const ValueData *v)
     {
-        if (v->type == ValueType::Integer)
-            return v->intValue < 0;
-        else if (v->type == ValueType::Double)
-            return v->doubleValue < 0;
+        if (v->type == ValueType::Number)
+            return v->numberValue < 0;
         else if (v->type == ValueType::Bool)
             return false;
         else if (static_cast<int>(v->type) < 0) {
@@ -536,10 +530,8 @@ extern "C"
 
     bool value_isZero(const ValueData *v)
     {
-        if (v->type == ValueType::Integer)
-            return v->intValue == 0;
-        else if (v->type == ValueType::Double)
-            return v->doubleValue == 0;
+        if (v->type == ValueType::Number)
+            return v->numberValue == 0;
         else if (v->type == ValueType::Bool)
             return !v->boolValue;
         else if (static_cast<int>(v->type) < 0)
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 814f1428..1f83a9c6 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -8,8 +8,8 @@ using namespace libscratchcpp;
 TEST(ValueTest, DefaultConstructor)
 {
     Value v;
-    ASSERT_EQ(v.toInt(), 0);
-    ASSERT_EQ(v.type(), ValueType::Integer);
+    ASSERT_EQ(v.toDouble(), 0);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -25,7 +25,7 @@ TEST(ValueTest, FloatConstructor)
     {
         Value v(3.14f);
         ASSERT_EQ(v.toDouble(), 3.14);
-        ASSERT_EQ(v.type(), ValueType::Double);
+        ASSERT_EQ(v.type(), ValueType::Number);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
@@ -78,7 +78,7 @@ TEST(ValueTest, DoubleConstructor)
     {
         Value v(static_cast<double>(3.14));
         ASSERT_EQ(v.toDouble(), 3.14);
-        ASSERT_EQ(v.type(), ValueType::Double);
+        ASSERT_EQ(v.type(), ValueType::Number);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
@@ -92,7 +92,7 @@ TEST(ValueTest, DoubleConstructor)
     {
         Value v(static_cast<double>(-5.0));
         ASSERT_EQ(v.toDouble(), -5.0);
-        ASSERT_EQ(v.type(), ValueType::Double);
+        ASSERT_EQ(v.type(), ValueType::Number);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
@@ -144,7 +144,7 @@ TEST(ValueTest, IntConstructor)
 {
     Value v(static_cast<int>(55));
     ASSERT_EQ(v.toInt(), 55);
-    ASSERT_EQ(v.type(), ValueType::Integer);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -159,7 +159,7 @@ TEST(ValueTest, SizeTConstructor)
 {
     Value v(static_cast<size_t>(100));
     ASSERT_EQ(v.toLong(), 100);
-    ASSERT_EQ(v.type(), ValueType::Integer);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -172,9 +172,9 @@ TEST(ValueTest, SizeTConstructor)
 
 TEST(ValueTest, LongConstructor)
 {
-    Value v(999999999999999999L);
-    ASSERT_EQ(v.toLong(), 999999999999999999L);
-    ASSERT_EQ(v.type(), ValueType::Integer);
+    Value v(123L);
+    ASSERT_EQ(v.toLong(), 123L);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -541,7 +541,7 @@ TEST(ValueTest, FloatAssignment)
     Value v;
     v = 3.14f;
     ASSERT_EQ(v.toDouble(), 3.14);
-    ASSERT_EQ(v.type(), ValueType::Double);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -586,7 +586,7 @@ TEST(ValueTest, DoubleAssignment)
     Value v;
     v = static_cast<double>(3.14);
     ASSERT_EQ(v.toDouble(), 3.14);
-    ASSERT_EQ(v.type(), ValueType::Double);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -631,7 +631,7 @@ TEST(ValueTest, IntAssignment)
     Value v;
     v = static_cast<int>(55);
     ASSERT_EQ(v.toInt(), 55);
-    ASSERT_EQ(v.type(), ValueType::Integer);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -644,9 +644,9 @@ TEST(ValueTest, IntAssignment)
 TEST(ValueTest, LongAssignment)
 {
     Value v;
-    v = 999999999999999999L;
-    ASSERT_EQ(v.toLong(), 999999999999999999L);
-    ASSERT_EQ(v.type(), ValueType::Integer);
+    v = 123L;
+    ASSERT_EQ(v.toLong(), 123L);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
@@ -1106,10 +1106,10 @@ TEST(ValueTest, ToInt)
 
 TEST(ValueTest, ToLong)
 {
-    Value v = 999999999999999999L;
-    ASSERT_EQ(v.toLong(), 999999999999999999L);
-    v = -999999999999999999L;
-    ASSERT_EQ(v.toLong(), -999999999999999999L);
+    Value v = 123L;
+    ASSERT_EQ(v.toLong(), 123L);
+    v = -123L;
+    ASSERT_EQ(v.toLong(), -123L);
 
     v = 2.54;
     ASSERT_EQ(v.toLong(), 2);
@@ -1671,11 +1671,11 @@ TEST(ValueTest, ToString)
     ASSERT_EQ(v.toString(), "-2147483647");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
 
-    v = 999999999999999999L;
-    ASSERT_EQ(v.toString(), "999999999999999999");
+    v = 512L;
+    ASSERT_EQ(v.toString(), "512");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    v = -999999999999999999L;
-    ASSERT_EQ(v.toString(), "-999999999999999999");
+    v = -512L;
+    ASSERT_EQ(v.toString(), "-512");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
 
     v = 2.0;
@@ -1910,9 +1910,9 @@ TEST(ValueTest, SubtractFunction)
     v.subtract(25);
     ASSERT_EQ(v.toInt(), 25);
 
-    v = 999999999999999999L;
+    v = 256L;
     v.subtract(1);
-    ASSERT_EQ(v.toLong(), 999999999999999998L);
+    ASSERT_EQ(v.toLong(), 255L);
 
     v = -500;
     v.subtract(25);
@@ -2002,9 +2002,9 @@ TEST(ValueTest, MultiplyFunction)
     v.multiply(2);
     ASSERT_EQ(v.toInt(), 100);
 
-    v = 999999999999999999L;
+    v = 128L;
     v.multiply(2);
-    ASSERT_EQ(v.toLong(), 1999999999999999998L);
+    ASSERT_EQ(v.toLong(), 256L);
 
     v = -500;
     v.multiply(25);

From dba78e73db3f1bc09c8d165f6d3dd3c617ec7871 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 10 Oct 2024 22:47:04 +0200
Subject: [PATCH 45/94] Fix InputValue test build

---
 test/scratch_classes/inputvalue_test.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/test/scratch_classes/inputvalue_test.cpp b/test/scratch_classes/inputvalue_test.cpp
index 40cb5175..e58e6b49 100644
--- a/test/scratch_classes/inputvalue_test.cpp
+++ b/test/scratch_classes/inputvalue_test.cpp
@@ -11,8 +11,10 @@
 #include <scratchcpp/input.h>
 #include <enginemock.h>
 #include <targetmock.h>
+#ifdef USE_LLVM
 #include <codebuilderfactorymock.h>
 #include <codebuildermock.h>
+#endif
 
 #include "../common.h"
 #include "dev/engine/compiler_p.h"

From cb3570c8a680071b83be683508a1898f1e623325 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 11 Oct 2024 10:45:23 +0200
Subject: [PATCH 46/94] Add more value casting functions

---
 include/scratchcpp/value_functions.h |   6 +
 src/scratch/value_functions.cpp      |  62 +++++-
 src/scratch/value_functions_p.h      |  22 +--
 test/scratch_classes/value_test.cpp  | 284 +++++++++++++++++++++++++++
 4 files changed, 360 insertions(+), 14 deletions(-)

diff --git a/include/scratchcpp/value_functions.h b/include/scratchcpp/value_functions.h
index f5707166..5ff2c1e4 100644
--- a/include/scratchcpp/value_functions.h
+++ b/include/scratchcpp/value_functions.h
@@ -36,8 +36,14 @@ extern "C"
     LIBSCRATCHCPP_EXPORT double value_toDouble(const ValueData *v);
     LIBSCRATCHCPP_EXPORT bool value_toBool(const ValueData *v);
     LIBSCRATCHCPP_EXPORT void value_toString(const ValueData *v, std::string *dst);
+    LIBSCRATCHCPP_EXPORT char *value_toCString(const ValueData *v);
     LIBSCRATCHCPP_EXPORT void value_toUtf16(const ValueData *v, std::u16string *dst);
 
+    LIBSCRATCHCPP_EXPORT char *value_doubleToCString(double v);
+    LIBSCRATCHCPP_EXPORT const char *value_boolToCString(bool v);
+    LIBSCRATCHCPP_EXPORT double value_stringToDouble(const char *s);
+    LIBSCRATCHCPP_EXPORT bool value_stringToBool(const char *s);
+
     LIBSCRATCHCPP_EXPORT void value_add(const ValueData *v1, const ValueData *v2, ValueData *dst);
     LIBSCRATCHCPP_EXPORT void value_subtract(const ValueData *v1, const ValueData *v2, ValueData *dst);
     LIBSCRATCHCPP_EXPORT void value_multiply(const ValueData *v1, const ValueData *v2, ValueData *dst);
diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index f32506b6..4a5f6a8b 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -330,7 +330,7 @@ extern "C"
         } else if (v->type == ValueType::Number) {
             return v->numberValue != 0;
         } else if (v->type == ValueType::String) {
-            return strlen(v->stringValue) != 0 && !value_stringsEqual(v->stringValue, "false") && strcmp(v->stringValue, "0") != 0;
+            return value_stringToBool(v->stringValue);
         } else if (v->type == ValueType::Infinity || v->type == ValueType::NegativeInfinity) {
             return true;
         } else if (v->type == ValueType::NaN) {
@@ -346,7 +346,7 @@ extern "C"
         if (v->type == ValueType::String)
             dst->assign(v->stringValue);
         else if (v->type == ValueType::Number)
-            dst->assign(value_doubleToString(v->numberValue));
+            value_doubleToString(v->numberValue, dst);
         else if (v->type == ValueType::Bool)
             dst->assign(v->boolValue ? "true" : "false");
         else if (v->type == ValueType::Infinity)
@@ -359,6 +359,19 @@ extern "C"
             dst->clear();
     }
 
+    /*!
+     * Returns the string representation of the given value.
+     * \note It is the caller's responsibility to free allocated memory.
+     */
+    char *value_toCString(const ValueData *v)
+    {
+        std::string out;
+        value_toString(v, &out);
+        char *ret = (char *)malloc((out.size() + 1) * sizeof(char));
+        strncpy(ret, out.c_str(), out.size() + 1);
+        return ret;
+    }
+
     /*! Writes the UTF-16 representation of the given value to dst. */
     void value_toUtf16(const libscratchcpp::ValueData *v, std::u16string *dst)
     {
@@ -367,6 +380,51 @@ extern "C"
         dst->assign(utf8::utf8to16(s));
     }
 
+    /*!
+     * Converts the given number to string.
+     * \note It is the caller's responsibility to free allocated memory.
+     */
+    char *value_doubleToCString(double v)
+    {
+        std::string out;
+        value_doubleToString(v, &out);
+        char *ret = (char *)malloc((out.size() + 1) * sizeof(char));
+        strncpy(ret, out.c_str(), out.size() + 1);
+        return ret;
+    }
+
+    /*!
+     * Converts the given boolean to string.
+     * \note Do not free allocated memory!
+     */
+    const char *value_boolToCString(bool v)
+    {
+        if (v) {
+            static const char *ret = "true";
+            return ret;
+        } else {
+            static const char *ret = "false";
+            return ret;
+        }
+    }
+
+    /*! Converts the given string to double. */
+    double value_stringToDouble(const char *s)
+    {
+        if (strcmp(s, "Infinity") == 0)
+            return std::numeric_limits<double>::infinity();
+        else if (strcmp(s, "-Infinity") == 0)
+            return -std::numeric_limits<double>::infinity();
+
+        return value_stringToDoubleImpl(s);
+    }
+
+    /*! Converts the given string to boolean. */
+    bool value_stringToBool(const char *s)
+    {
+        return strlen(s) != 0 && !value_stringsEqual(s, "false") && strcmp(s, "0") != 0;
+    }
+
     /* operations */
 
     /*! Adds the given values and writes the result to dst. */
diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h
index f71b1238..e41c8a0a 100644
--- a/src/scratch/value_functions_p.h
+++ b/src/scratch/value_functions_p.h
@@ -227,7 +227,7 @@ extern "C"
         }
     }
 
-    inline double value_stringToDouble(const char *s, bool *ok = nullptr)
+    inline double value_stringToDoubleImpl(const char *s, bool *ok = nullptr)
     {
         if (ok)
             *ok = false;
@@ -392,7 +392,7 @@ extern "C"
 
     inline long value_stringToLong(const char *s, bool *ok = nullptr)
     {
-        return value_stringToDouble(s, ok);
+        return value_stringToDoubleImpl(s, ok);
     }
 
     inline bool value_stringIsInt(const char *s, int n)
@@ -410,7 +410,7 @@ extern "C"
     }
 }
 
-inline std::string value_doubleToString(double v)
+inline void value_doubleToString(double v, std::string *dst)
 {
     std::stringstream stream;
 
@@ -424,22 +424,20 @@ inline std::string value_doubleToString(double v)
     } else
         stream << std::setprecision(std::max(16u, value_digitCount(v))) << v;
 
-    std::string s = stream.str();
+    dst->assign(stream.str());
     std::size_t index;
 
     for (int i = 0; i < 2; i++) {
         if (i == 0)
-            index = s.find("e+");
+            index = dst->find("e+");
         else
-            index = s.find("e-");
+            index = dst->find("e-");
 
         if (index != std::string::npos) {
-            while ((s.size() >= index + 3) && (s[index + 2] == '0'))
-                s.erase(index + 2, 1);
+            while ((dst->size() >= index + 3) && ((*dst)[index + 2] == '0'))
+                dst->erase(index + 2, 1);
         }
     }
-
-    return s;
 }
 
 extern "C"
@@ -459,7 +457,7 @@ extern "C"
             value_stringToLong(str, &ok);
             return ok ? 1 : 0;
         } else {
-            value_stringToDouble(str, &ok);
+            value_stringToDoubleImpl(str, &ok);
             return ok ? 2 : 0;
         }
     }
@@ -473,7 +471,7 @@ extern "C"
         // Since functions calling this already prioritize int, double and bool,
         // we can optimize by prioritizing the other types here.
         if (v->type == ValueType::String)
-            return value_stringToDouble(v->stringValue, ok);
+            return value_stringToDoubleImpl(v->stringValue, ok);
         else if (v->type == ValueType::Infinity)
             return std::numeric_limits<double>::infinity();
         else if (v->type == ValueType::NegativeInfinity)
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 1f83a9c6..82dc4748 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -1664,152 +1664,236 @@ TEST(ValueTest, ToBool)
 
 TEST(ValueTest, ToString)
 {
+    std::vector<char *> cStrings;
     Value v = 2147483647;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2147483647");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = -2147483647;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2147483647");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 512L;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "512");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = -512L;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-512");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 2.0;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = 2.0f;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2.0;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = -2.0f;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 2.54;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2.54");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = 2.54f;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2.54");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2.54;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2.54");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = -2.54f;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2.54");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 2550.625021000115;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2550.625021000115");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 2550.625021000115f;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2550.625");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2550.625021000115;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2550.625021000115");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2550.625021000115f;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2550.625");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 9.4324e+20;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "943240000000000000000");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2.591e-2;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-0.02591");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 9.4324e+21;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "9.4324e+21");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2.591e-13;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2.591e-13");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 0.001;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "0.001");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = -0.001;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-0.001");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = false;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "false");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = true;
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "true");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "2147483647";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2147483647");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "-2147483647";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2147483647");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "999999999999999999";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "999999999999999999");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "-999999999999999999";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-999999999999999999");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "255.625";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "255.625");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "-255.625";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-255.625");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "9432.4e-12";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "9432.4e-12");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "-9432.4e-12";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-9432.4e-12");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "9432.4e+6";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "9432.4e+6");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "-9432.4e+6";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-9432.4e+6");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "false";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "false");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "true";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "true");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "Infinity";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_TRUE(v.isInfinity());
     ASSERT_EQ(v.toString(), "Infinity");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "-Infinity";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_TRUE(v.isNegativeInfinity());
     ASSERT_EQ(v.toString(), "-Infinity");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
     v = "NaN";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_TRUE(v.isNaN());
     ASSERT_EQ(v.toString(), "NaN");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = "something";
+    cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "something");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
+
+    for (char *s : cStrings)
+        free(s);
 }
 
 TEST(ValueTest, AddFunction)
@@ -2942,3 +3026,203 @@ TEST(ValueTest, EqualityOperators)
         ASSERT_TRUE(v4 != v7);
     }
 }
+
+TEST(ValueTest, DoubleToCString)
+{
+    char *ret;
+    ret = value_doubleToCString(2.0);
+    ASSERT_EQ(strcmp(ret, "2"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-2.0);
+    ASSERT_EQ(strcmp(ret, "-2"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(2.54);
+    ASSERT_EQ(strcmp(ret, "2.54"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-2.54);
+    ASSERT_EQ(strcmp(ret, "-2.54"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(2550.625021000115);
+    ASSERT_EQ(strcmp(ret, "2550.625021000115"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-2550.625021000115);
+    ASSERT_EQ(strcmp(ret, "-2550.625021000115"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(9.4324e+20);
+    ASSERT_EQ(strcmp(ret, "943240000000000000000"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-2.591e-2);
+    ASSERT_EQ(strcmp(ret, "-0.02591"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(9.4324e+21);
+    ASSERT_EQ(strcmp(ret, "9.4324e+21"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-2.591e-13);
+    ASSERT_EQ(strcmp(ret, "-2.591e-13"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(0.001);
+    ASSERT_EQ(strcmp(ret, "0.001"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-0.001);
+    ASSERT_EQ(strcmp(ret, "-0.001"), 0);
+    free(ret);
+}
+
+TEST(ValueTest, BoolToCString)
+{
+    const char *ret;
+    ret = value_boolToCString(true);
+    ASSERT_EQ(strcmp(ret, "true"), 0);
+
+    ret = value_boolToCString(false);
+    ASSERT_EQ(strcmp(ret, "false"), 0);
+}
+
+TEST(ValueTest, StringToDouble)
+{
+    ASSERT_EQ(value_stringToDouble("2147483647"), 2147483647.0);
+    ASSERT_EQ(value_stringToDouble("-2147483647"), -2147483647.0);
+
+    ASSERT_EQ(value_stringToDouble("255.625"), 255.625);
+    ASSERT_EQ(value_stringToDouble("-255.625"), -255.625);
+
+    ASSERT_EQ(value_stringToDouble("0.15"), 0.15);
+    ASSERT_EQ(value_stringToDouble("-0.15"), -0.15);
+
+    ASSERT_EQ(value_stringToDouble("+.15"), 0.15);
+    ASSERT_EQ(value_stringToDouble(".15"), 0.15);
+    ASSERT_EQ(value_stringToDouble("-.15"), -0.15);
+
+    ASSERT_EQ(value_stringToDouble("1."), 1.0);
+    ASSERT_EQ(value_stringToDouble("+1."), 1.0);
+    ASSERT_EQ(value_stringToDouble("-1."), -1.0);
+
+    ASSERT_EQ(value_stringToDouble("0+5"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0-5"), 0.0);
+
+    ASSERT_EQ(value_stringToDouble("9432.4e-12"), 9.4324e-9);
+    ASSERT_EQ(value_stringToDouble("-9432.4e-12"), -9.4324e-9);
+
+    ASSERT_EQ(value_stringToDouble("9432.4e+6"), 9.4324e+9);
+    ASSERT_EQ(value_stringToDouble("-9432.4e+6"), -9.4324e+9);
+
+    ASSERT_EQ(value_stringToDouble("1 2 3"), 0.0);
+
+    ASSERT_EQ(value_stringToDouble("false"), 0.0);
+    ASSERT_EQ(value_stringToDouble("true"), 0.0);
+
+    ASSERT_TRUE(value_stringToDouble("Infinity") > 0 && std::isinf(value_stringToDouble("Infinity")));
+    ASSERT_TRUE(value_stringToDouble("-Infinity") < 0 && std::isinf(value_stringToDouble("-Infinity")));
+    ASSERT_EQ(value_stringToDouble("NaN"), 0.0);
+
+    ASSERT_EQ(value_stringToDouble("something"), 0.0);
+
+    // Hex
+    ASSERT_EQ(value_stringToDouble("0xafe"), 2814.0);
+    ASSERT_EQ(value_stringToDouble("   0xafe"), 2814.0);
+    ASSERT_EQ(value_stringToDouble("0xafe   "), 2814.0);
+    ASSERT_EQ(value_stringToDouble("   0xafe   "), 2814.0);
+    ASSERT_EQ(value_stringToDouble("0x0afe"), 2814.0);
+    ASSERT_EQ(value_stringToDouble("0xBaCD"), 47821.0);
+    ASSERT_EQ(value_stringToDouble("0XBaCD"), 47821.0);
+    ASSERT_EQ(value_stringToDouble("0xAbG"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0xabf.d"), 0.0);
+    ASSERT_EQ(value_stringToDouble("+0xa"), 0.0);
+    ASSERT_EQ(value_stringToDouble("-0xa"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0x+a"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0x-a"), 0.0);
+
+    // Octal
+    ASSERT_EQ(value_stringToDouble("0o506"), 326.0);
+    ASSERT_EQ(value_stringToDouble("   0o506"), 326.0);
+    ASSERT_EQ(value_stringToDouble("0o506   "), 326.0);
+    ASSERT_EQ(value_stringToDouble("   0o506   "), 326.0);
+    ASSERT_EQ(value_stringToDouble("0o0506"), 326.0);
+    ASSERT_EQ(value_stringToDouble("0O17206"), 7814.0);
+    ASSERT_EQ(value_stringToDouble("0o5783"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0o573.2"), 0.0);
+    ASSERT_EQ(value_stringToDouble("+0o2"), 0.0);
+    ASSERT_EQ(value_stringToDouble("-0o2"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0o+2"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0o-2"), 0.0);
+
+    // Binary
+    ASSERT_EQ(value_stringToDouble("0b101101"), 45.0);
+    ASSERT_EQ(value_stringToDouble("   0b101101"), 45.0);
+    ASSERT_EQ(value_stringToDouble("0b101101   "), 45.0);
+    ASSERT_EQ(value_stringToDouble("   0b101101   "), 45.0);
+    ASSERT_EQ(value_stringToDouble("0b0101101"), 45.0);
+    ASSERT_EQ(value_stringToDouble("0B1110100110"), 934.0);
+    ASSERT_EQ(value_stringToDouble("0b100112001"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0b10011001.1"), 0.0);
+    ASSERT_EQ(value_stringToDouble("+0b1"), 0.0);
+    ASSERT_EQ(value_stringToDouble("-0b1"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0b+1"), 0.0);
+    ASSERT_EQ(value_stringToDouble("0b-1"), 0.0);
+}
+
+TEST(ValueTest, StringToBool)
+{
+    ASSERT_EQ(value_stringToBool("2147483647"), true);
+    ASSERT_EQ(value_stringToBool("-2147483647"), true);
+
+    ASSERT_EQ(value_stringToBool("255.625"), true);
+    ASSERT_EQ(value_stringToBool("-255.625"), true);
+
+    ASSERT_EQ(value_stringToBool("0"), false);
+
+    ASSERT_EQ(value_stringToBool("false"), false);
+    ASSERT_EQ(value_stringToBool("true"), true);
+    ASSERT_EQ(value_stringToBool("FaLsE"), false);
+    ASSERT_EQ(value_stringToBool("TrUe"), true);
+
+    ASSERT_EQ(value_stringToBool("Infinity"), true);
+    ASSERT_EQ(value_stringToBool("-Infinity"), true);
+    ASSERT_EQ(value_stringToBool("NaN"), true);
+
+    ASSERT_EQ(value_stringToBool("something"), true);
+    ASSERT_EQ(value_stringToBool(""), false);
+
+    // Hex
+    ASSERT_TRUE(value_stringToBool("0xafe"));
+    ASSERT_TRUE(value_stringToBool("   0xafe"));
+    ASSERT_TRUE(value_stringToBool("0xafe   "));
+    ASSERT_TRUE(value_stringToBool("   0xafe   "));
+    ASSERT_TRUE(value_stringToBool("0x0afe"));
+    ASSERT_TRUE(value_stringToBool("0xBaCD"));
+    ASSERT_TRUE(value_stringToBool("0x0"));
+    ASSERT_TRUE(value_stringToBool("0XBaCD"));
+    ASSERT_TRUE(value_stringToBool("0xAbG"));
+    ASSERT_TRUE(value_stringToBool("0xabf.d"));
+
+    // Octal
+    ASSERT_TRUE(value_stringToBool("0o506"));
+    ASSERT_TRUE(value_stringToBool("   0o506"));
+    ASSERT_TRUE(value_stringToBool("0o506   "));
+    ASSERT_TRUE(value_stringToBool("   0o506   "));
+    ASSERT_TRUE(value_stringToBool("0o0506"));
+    ASSERT_TRUE(value_stringToBool("0O17206"));
+    ASSERT_TRUE(value_stringToBool("0o5783"));
+    ASSERT_TRUE(value_stringToBool("0o573.2"));
+
+    // Binary
+    ASSERT_TRUE(value_stringToBool("0b101101"));
+    ASSERT_TRUE(value_stringToBool("   0b101101"));
+    ASSERT_TRUE(value_stringToBool("0b101101   "));
+    ASSERT_TRUE(value_stringToBool("   0b101101   "));
+    ASSERT_TRUE(value_stringToBool("0b0101101"));
+    ASSERT_TRUE(value_stringToBool("0B1110100110"));
+    ASSERT_TRUE(value_stringToBool("0b100112001"));
+    ASSERT_TRUE(value_stringToBool("0b10011001.1"));
+}

From f3546e66e4dc72ee8b75bcd0ea2457a6b74a02ec Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 11 Oct 2024 10:56:37 +0200
Subject: [PATCH 47/94] Implement block function static types

---
 include/scratchcpp/dev/compiler.h           |  11 +-
 src/dev/engine/compiler.cpp                 |   4 +-
 src/dev/engine/internal/icodebuilder.h      |   5 +-
 src/dev/engine/internal/llvmcodebuilder.cpp | 306 +++++++++++++++++---
 src/dev/engine/internal/llvmcodebuilder.h   |  27 +-
 test/dev/compiler/compiler_test.cpp         |  11 +-
 test/dev/llvm/llvmcodebuilder_test.cpp      | 190 ++++++------
 test/dev/llvm/testfunctions.cpp             |  61 ++--
 test/dev/llvm/testfunctions.h               |  20 +-
 test/mocks/codebuildermock.h                |   2 +-
 10 files changed, 446 insertions(+), 191 deletions(-)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index aa094c46..5432a520 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -3,6 +3,7 @@
 #pragma once
 
 #include <unordered_set>
+#include <vector>
 
 #include "../global.h"
 #include "../spimpl.h"
@@ -23,6 +24,14 @@ class CompilerPrivate;
 class LIBSCRATCHCPP_EXPORT Compiler
 {
     public:
+        enum class StaticType
+        {
+            Void,
+            Number,
+            Bool,
+            String
+        };
+
         Compiler(IEngine *engine, Target *target);
         Compiler(const Compiler &) = delete;
 
@@ -32,7 +41,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
 
         std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock);
 
-        void addFunctionCall(const std::string &functionName, int argCount, bool returns);
+        void addFunctionCall(const std::string &functionName, StaticType returnType = StaticType::Void, const std::vector<StaticType> &argTypes = {});
         void addConstValue(const Value &value);
         void addVariableValue(Variable *variable);
         void addListContents(List *list);
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 84376d1f..b3f4613f 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -73,9 +73,9 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
  * Adds a call to the given function.\n
  * For example: extern "C" some_block(ValueData *ret, ValueData *arg1, ValueData *arg2) has 2 arguments
  */
-void Compiler::addFunctionCall(const std::string &functionName, int argCount, bool returns)
+void Compiler::addFunctionCall(const std::string &functionName, StaticType returnType, const std::vector<StaticType> &argTypes)
 {
-    impl->builder->addFunctionCall(functionName, argCount, returns);
+    impl->builder->addFunctionCall(functionName, returnType, argTypes);
 }
 
 /*! Adds a constant value to the compiled code. */
diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 5a0ed8be..5101ae75 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -2,8 +2,7 @@
 
 #pragma once
 
-#include <string>
-#include <memory>
+#include <scratchcpp/dev/compiler.h>
 
 namespace libscratchcpp
 {
@@ -20,7 +19,7 @@ class ICodeBuilder
 
         virtual std::shared_ptr<ExecutableCode> finalize() = 0;
 
-        virtual void addFunctionCall(const std::string &functionName, int argCount, bool returns) = 0;
+        virtual void addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const std::vector<Compiler::StaticType> &argTypes) = 0;
         virtual void addConstValue(const Value &value) = 0;
         virtual void addVariableValue(Variable *variable) = 0;
         virtual void addListContents(List *list) = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 943ff064..5862b21b 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -10,6 +10,12 @@
 
 using namespace libscratchcpp;
 
+static std::unordered_map<ValueType, Compiler::StaticType> TYPE_MAP = {
+    { ValueType::Number, Compiler::StaticType::Number },           { ValueType::Bool, Compiler::StaticType::Bool },
+    { ValueType::String, Compiler::StaticType::String },           { ValueType::Infinity, Compiler::StaticType::Number },
+    { ValueType::NegativeInfinity, Compiler::StaticType::Number }, { ValueType::NaN, Compiler::StaticType::Number }
+};
+
 LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id) :
     m_id(id),
     m_module(std::make_unique<llvm::Module>(id, m_ctx)),
@@ -40,6 +46,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     llvm::Function *currentFunc = beginFunction(functionIndex);
     std::vector<IfStatement> ifStatements;
     std::vector<Loop> loops;
+    m_heap.clear();
 
     // Execute recorded steps
     for (const Step &step : m_steps) {
@@ -53,23 +60,26 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
                 args.push_back(currentFunc->getArg(0));
 
-                // Add return value arg if the function returns
-                if (step.functionReturns) {
-                    types.push_back(m_valueDataType->getPointerTo());
-                    args.push_back(m_regs[functionIndex][step.functionReturnRegIndex]->value);
-                }
-
                 // Args
                 for (auto &arg : step.args) {
-                    types.push_back(m_valueDataType->getPointerTo());
-                    args.push_back(arg->value);
+                    types.push_back(getType(arg.first));
+                    args.push_back(castValue(arg.second, arg.first));
+                }
+
+                llvm::Value *ret = m_builder.CreateCall(resolveFunction(step.functionName, llvm::FunctionType::get(getType(step.functionReturnType), types, false)), args);
+
+                if (step.functionReturnReg) {
+                    step.functionReturnReg->value = ret;
+
+                    if (step.functionReturnType == Compiler::StaticType::String)
+                        m_heap.push_back(step.functionReturnReg->value);
                 }
 
-                m_builder.CreateCall(resolveFunction(step.functionName, llvm::FunctionType::get(m_builder.getVoidTy(), types, false)), args);
                 break;
             }
 
             case Step::Type::Yield:
+                freeHeap();
                 endFunction(currentFunc, functionIndex);
                 currentFunc = beginFunction(++functionIndex);
                 break;
@@ -79,9 +89,12 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 statement.beforeIf = m_builder.GetInsertBlock();
                 statement.body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
 
-                // Convert last reg to bool
+                // Use last reg
                 assert(step.args.size() == 1);
-                statement.condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
+                const auto &reg = step.args[0];
+                assert(reg.first == Compiler::StaticType::Bool);
+                freeHeap();
+                statement.condition = castValue(reg.second, reg.first);
 
                 // Switch to body branch
                 m_builder.SetInsertPoint(statement.body);
@@ -97,6 +110,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 // Jump to the branch after the if statement
                 assert(!statement.afterIf);
                 statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                freeHeap();
                 m_builder.CreateBr(statement.afterIf);
 
                 // Create else branch
@@ -120,6 +134,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 if (!statement.afterIf)
                     statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
 
+                freeHeap();
                 m_builder.CreateBr(statement.afterIf);
 
                 if (statement.elseBranch) {
@@ -150,12 +165,15 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
                 loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
 
-                // Convert last reg to double
+                // Use last reg for count
                 assert(step.args.size() == 1);
-                llvm::Value *count = m_builder.CreateCall(resolve_value_toDouble(), step.args[0]->value);
+                const auto &reg = step.args[0];
+                assert(reg.first == Compiler::StaticType::Number);
+                llvm::Value *count = castValue(reg.second, reg.first);
 
                 // Clamp count if <= 0 (we can skip the loop if count is not positive)
                 llvm::Value *comparison = m_builder.CreateFCmpULE(count, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)));
+                freeHeap();
                 m_builder.CreateCondBr(comparison, loop.afterLoop, roundBranch);
 
                 // Round (Scratch-specific behavior)
@@ -194,9 +212,12 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
                 loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
 
-                // Convert last reg to bool and add condition
+                // Use last reg
                 assert(step.args.size() == 1);
-                llvm::Value *condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
+                const auto &reg = step.args[0];
+                assert(reg.first == Compiler::StaticType::Bool);
+                llvm::Value *condition = castValue(reg.second, reg.first);
+                freeHeap();
                 m_builder.CreateCondBr(condition, body, loop.afterLoop);
 
                 // Switch to body branch
@@ -212,9 +233,12 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
                 loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
 
-                // Convert last reg to bool and add condition
+                // Use last reg
                 assert(step.args.size() == 1);
-                llvm::Value *condition = m_builder.CreateCall(resolve_value_toBool(), step.args[0]->value);
+                const auto &reg = step.args[0];
+                assert(reg.first == Compiler::StaticType::Bool);
+                llvm::Value *condition = castValue(reg.second, reg.first);
+                freeHeap();
                 m_builder.CreateCondBr(condition, loop.afterLoop, body);
 
                 // Switch to body branch
@@ -226,6 +250,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 Loop loop;
                 loop.isRepeatLoop = false;
                 loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                freeHeap();
                 m_builder.CreateBr(loop.conditionBranch);
                 m_builder.SetInsertPoint(loop.conditionBranch);
                 loops.push_back(loop);
@@ -244,6 +269,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 }
 
                 // Jump to the condition branch
+                freeHeap();
                 m_builder.CreateBr(loop.conditionBranch);
 
                 // Switch to the branch after the loop
@@ -255,6 +281,8 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
         }
     }
 
+    freeHeap();
+
     endFunction(currentFunc, functionIndex);
 
 #ifdef PRINT_LLVM_IR
@@ -274,22 +302,25 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     return std::make_shared<LLVMExecutableCode>(std::move(m_module), constValues);
 }
 
-void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, int argCount, bool returns)
+void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const std::vector<Compiler::StaticType> &argTypes)
 {
     Step step(Step::Type::FunctionCall);
     step.functionName = functionName;
 
-    assert(m_tmpRegs.size() >= argCount);
+    assert(m_tmpRegs.size() >= argTypes.size());
+    size_t j = 0;
 
-    for (size_t i = m_tmpRegs.size() - argCount; i < m_tmpRegs.size(); i++)
-        step.args.push_back(m_tmpRegs[i]);
+    for (size_t i = m_tmpRegs.size() - argTypes.size(); i < m_tmpRegs.size(); i++)
+        step.args.push_back({ argTypes[j++], m_tmpRegs[i] });
 
-    m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
+    m_tmpRegs.erase(m_tmpRegs.end() - argTypes.size(), m_tmpRegs.end());
 
-    if (returns) {
-        step.functionReturns = true;
-        auto reg = std::make_shared<Register>();
-        step.functionReturnRegIndex = m_regs[m_currentFunction].size();
+    step.functionReturnType = returnType;
+
+    if (returnType != Compiler::StaticType::Void) {
+        auto reg = std::make_shared<Register>(returnType);
+        reg->isRawValue = true;
+        step.functionReturnReg = reg;
         m_regs[m_currentFunction].push_back(reg);
         m_tmpRegs.push_back(reg);
     }
@@ -299,7 +330,8 @@ void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, int argCo
 
 void LLVMCodeBuilder::addConstValue(const Value &value)
 {
-    auto reg = std::make_shared<Register>();
+    auto reg = std::make_shared<Register>(TYPE_MAP[value.type()]);
+    reg->isRawValue = false;
     reg->isConstValue = true;
     reg->constValueIndex = m_constValues[m_currentFunction].size();
     m_regs[m_currentFunction].push_back(reg);
@@ -323,7 +355,7 @@ void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
     assert(!m_tmpRegs.empty());
-    step.args.push_back(m_tmpRegs.back());
+    step.args.push_back({ Compiler::StaticType::Bool, m_tmpRegs.back() });
     m_tmpRegs.pop_back();
     m_steps.push_back(step);
 }
@@ -342,7 +374,7 @@ void LLVMCodeBuilder::beginRepeatLoop()
 {
     Step step(Step::Type::BeginRepeatLoop);
     assert(!m_tmpRegs.empty());
-    step.args.push_back(m_tmpRegs.back());
+    step.args.push_back({ Compiler::StaticType::Number, m_tmpRegs.back() });
     m_tmpRegs.pop_back();
     m_steps.push_back(step);
 }
@@ -351,7 +383,7 @@ void LLVMCodeBuilder::beginWhileLoop()
 {
     Step step(Step::Type::BeginWhileLoop);
     assert(!m_tmpRegs.empty());
-    step.args.push_back(m_tmpRegs.back());
+    step.args.push_back({ Compiler::StaticType::Bool, m_tmpRegs.back() });
     m_tmpRegs.pop_back();
     m_steps.push_back(step);
 }
@@ -360,7 +392,7 @@ void LLVMCodeBuilder::beginRepeatUntilLoop()
 {
     Step step(Step::Type::BeginRepeatUntilLoop);
     assert(!m_tmpRegs.empty());
-    step.args.push_back(m_tmpRegs.back());
+    step.args.push_back({ Compiler::StaticType::Bool, m_tmpRegs.back() });
     m_tmpRegs.pop_back();
     m_steps.push_back(step);
 }
@@ -390,14 +422,13 @@ void LLVMCodeBuilder::yield()
 void LLVMCodeBuilder::initTypes()
 {
     // Create the ValueData struct
-    llvm::Type *intType = llvm::Type::getInt64Ty(m_ctx);                                 // long (intValue)
-    llvm::Type *doubleType = llvm::Type::getDoubleTy(m_ctx);                             // double (doubleValue)
+    llvm::Type *doubleType = llvm::Type::getDoubleTy(m_ctx);                             // double (numberValue)
     llvm::Type *boolType = llvm::Type::getInt1Ty(m_ctx);                                 // bool (boolValue)
     llvm::Type *stringPtrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); // char* (stringValue)
 
     // Create the union type (largest type size should dominate)
     llvm::StructType *unionType = llvm::StructType::create(m_ctx, "union");
-    unionType->setBody({ intType, doubleType, boolType, stringPtrType });
+    unionType->setBody({ doubleType, boolType, stringPtrType });
 
     // Create the full struct type
     llvm::Type *valueType = llvm::Type::getInt32Ty(m_ctx); // Assuming ValueType is a 32-bit enum
@@ -436,13 +467,13 @@ llvm::Function *LLVMCodeBuilder::beginFunction(size_t index)
             // Do not allocate space for existing constant values
             reg->value = constPtrs[reg->constValueIndex];
         } else {
-            const std::string name = "r" + std::to_string(regIndex);
+            /*const std::string name = "r" + std::to_string(regIndex);
 
             llvm::Value *valueData = m_builder.CreateAlloca(m_valueDataType, nullptr, name);
             m_builder.CreateCall(resolve_value_init(), { valueData });
 
             reg->value = valueData;
-            regIndex++;
+            regIndex++;*/
         }
     }
 
@@ -467,6 +498,184 @@ void LLVMCodeBuilder::endFunction(llvm::Function *func, size_t index)
     }
 }
 
+void LLVMCodeBuilder::freeHeap()
+{
+    // Free dynamically allocated memory
+    for (llvm::Value *ptr : m_heap)
+        m_builder.CreateFree(ptr);
+
+    m_heap.clear();
+}
+
+llvm::Value *LLVMCodeBuilder::castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType)
+{
+    if (reg->isRawValue)
+        return castRawValue(reg, targetType);
+
+    assert(reg->type != Compiler::StaticType::Void && targetType != Compiler::StaticType::Void);
+
+    switch (targetType) {
+        case Compiler::StaticType::Number:
+            switch (reg->type) {
+                case Compiler::StaticType::Number: {
+                    // Read number directly
+                    llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0);
+                    return m_builder.CreateLoad(m_builder.getDoubleTy(), ptr);
+                }
+
+                case Compiler::StaticType::Bool: {
+                    // Read boolean and cast to double
+                    llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0);
+                    llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr);
+                    return m_builder.CreateSIToFP(boolValue, m_builder.getDoubleTy());
+                }
+
+                case Compiler::StaticType::String: {
+                    // Convert string to double
+                    return m_builder.CreateCall(resolve_value_toDouble(), reg->value);
+                }
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+
+        case Compiler::StaticType::Bool:
+            switch (reg->type) {
+                case Compiler::StaticType::Number: {
+                    // True if != 0
+                    llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0);
+                    llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr);
+                    return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)));
+                }
+
+                case Compiler::StaticType::Bool: {
+                    // Read boolean directly
+                    llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0);
+                    return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr);
+                }
+
+                case Compiler::StaticType::String:
+                    // Convert string to bool
+                    return m_builder.CreateCall(resolve_value_toBool(), reg->value);
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+
+        case Compiler::StaticType::String:
+            switch (reg->type) {
+                case Compiler::StaticType::Number:
+                case Compiler::StaticType::Bool: {
+                    // Cast to string
+                    llvm::Value *ptr = m_builder.CreateCall(resolve_value_toCString(), reg->value);
+                    m_heap.push_back(ptr); // deallocate later
+                    return ptr;
+                }
+
+                case Compiler::StaticType::String: {
+                    // Read string pointer directly
+                    llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0);
+                    return m_builder.CreateLoad(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), ptr);
+                }
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+
+        default:
+            assert(false);
+            return nullptr;
+    }
+}
+
+llvm::Value *LLVMCodeBuilder::castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType)
+{
+    if (reg->type == targetType)
+        return reg->value;
+
+    switch (targetType) {
+        case Compiler::StaticType::Number:
+            switch (reg->type) {
+                case Compiler::StaticType::Bool:
+                    // Cast bool to double
+                    return m_builder.CreateSIToFP(reg->value, m_builder.getDoubleTy());
+
+                case Compiler::StaticType::String: {
+                    // Convert string to double
+                    return m_builder.CreateCall(resolve_value_stringToDouble(), reg->value);
+                }
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+
+        case Compiler::StaticType::Bool:
+            switch (reg->type) {
+                case Compiler::StaticType::Number:
+                    // Cast double to bool (true if != 0)
+                    return m_builder.CreateFCmpONE(reg->value, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)));
+
+                case Compiler::StaticType::String:
+                    // Convert string to bool
+                    return m_builder.CreateCall(resolve_value_stringToBool(), reg->value);
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+
+        case Compiler::StaticType::String:
+            switch (reg->type) {
+                case Compiler::StaticType::Number: {
+                    // Convert double to string
+                    llvm::Value *ptr = m_builder.CreateCall(resolve_value_doubleToCString(), reg->value);
+                    m_heap.push_back(ptr); // deallocate later
+                    return ptr;
+                }
+
+                case Compiler::StaticType::Bool: {
+                    // Convert bool to string
+                    llvm::Value *ptr = m_builder.CreateCall(resolve_value_boolToCString(), reg->value);
+                    // NOTE: Dot not deallocate later
+                    return ptr;
+                }
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+
+        default:
+            assert(false);
+            return nullptr;
+    }
+}
+
+llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type)
+{
+    switch (type) {
+        case Compiler::StaticType::Void:
+            return m_builder.getVoidTy();
+
+        case Compiler::StaticType::Number:
+            return m_builder.getDoubleTy();
+
+        case Compiler::StaticType::Bool:
+            return m_builder.getInt1Ty();
+
+        case Compiler::StaticType::String:
+            return llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+
+        default:
+            assert(false);
+            return nullptr;
+    }
+}
+
 llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type)
 {
     return m_module->getOrInsertFunction(name, type);
@@ -516,3 +725,28 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool()
 {
     return resolveFunction("value_toBool", llvm::FunctionType::get(m_builder.getInt1Ty(), m_valueDataType->getPointerTo(), false));
 }
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toCString()
+{
+    return resolveFunction("value_toCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_valueDataType->getPointerTo(), false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_doubleToCString()
+{
+    return resolveFunction("value_doubleToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_builder.getDoubleTy(), false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_boolToCString()
+{
+    return resolveFunction("value_doubleToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_builder.getInt1Ty(), false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToDouble()
+{
+    return resolveFunction("value_stringToDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToBool()
+{
+    return resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder.getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false));
+}
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 530f244d..5f6a2263 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -22,7 +22,7 @@ class LLVMCodeBuilder : public ICodeBuilder
 
         std::shared_ptr<ExecutableCode> finalize() override;
 
-        void addFunctionCall(const std::string &functionName, int argCount, bool returns) override;
+        void addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const std::vector<Compiler::StaticType> &argTypes) override;
         void addConstValue(const Value &value) override;
         void addVariableValue(Variable *variable) override;
         void addListContents(List *list) override;
@@ -42,7 +42,14 @@ class LLVMCodeBuilder : public ICodeBuilder
     private:
         struct Register
         {
+                Register(Compiler::StaticType type) :
+                    type(type)
+                {
+                }
+
+                Compiler::StaticType type = Compiler::StaticType::Void;
                 llvm::Value *value = nullptr;
+                bool isRawValue = false;
                 bool isConstValue = false;
                 size_t constValueIndex = 0;
         };
@@ -70,9 +77,9 @@ class LLVMCodeBuilder : public ICodeBuilder
 
                 Type type;
                 std::string functionName;
-                std::vector<std::shared_ptr<Register>> args;
-                bool functionReturns = false;
-                size_t functionReturnRegIndex = 0;
+                std::vector<std::pair<Compiler::StaticType, std::shared_ptr<Register>>> args; // target type, register
+                Compiler::StaticType functionReturnType = Compiler::StaticType::Void;
+                std::shared_ptr<Register> functionReturnReg;
         };
 
         struct IfStatement
@@ -96,6 +103,11 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::Function *beginFunction(size_t index);
         void endFunction(llvm::Function *func, size_t index);
 
+        void freeHeap();
+        llvm::Value *castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
+        llvm::Value *castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
+        llvm::Type *getType(Compiler::StaticType type);
+
         llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type);
         llvm::FunctionCallee resolve_value_init();
         llvm::FunctionCallee resolve_value_free();
@@ -106,6 +118,11 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::FunctionCallee resolve_value_assign_special();
         llvm::FunctionCallee resolve_value_toDouble();
         llvm::FunctionCallee resolve_value_toBool();
+        llvm::FunctionCallee resolve_value_toCString();
+        llvm::FunctionCallee resolve_value_doubleToCString();
+        llvm::FunctionCallee resolve_value_boolToCString();
+        llvm::FunctionCallee resolve_value_stringToDouble();
+        llvm::FunctionCallee resolve_value_stringToBool();
 
         std::string m_id;
         llvm::LLVMContext m_ctx;
@@ -120,6 +137,8 @@ class LLVMCodeBuilder : public ICodeBuilder
         std::vector<std::vector<std::shared_ptr<Register>>> m_regs;
         std::vector<std::shared_ptr<Register>> m_tmpRegs;
 
+        std::vector<llvm::Value *> m_heap;
+
         std::shared_ptr<ExecutableCode> m_output;
 };
 
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index ba615499..9a461bb5 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -40,6 +40,7 @@ class CompilerTest : public testing::Test
         void compile(Compiler &compiler, std::shared_ptr<Block> block)
         {
             ASSERT_EQ(compiler.block(), nullptr);
+            // TODO: Test warp
             EXPECT_CALL(m_builderFactory, create(block->id())).WillOnce(Return(m_builder));
             EXPECT_CALL(*m_builder, finalize()).WillOnce(Return(m_code));
             ASSERT_EQ(compiler.compile(block), m_code);
@@ -69,11 +70,13 @@ TEST_F(CompilerTest, AddFunctionCall)
     m_compareBlock = block;
     block->setCompileFunction([](Compiler *compiler) {
         ASSERT_EQ(compiler->block(), m_compareBlock);
-        EXPECT_CALL(*m_builder, addFunctionCall("test1", 3, false));
-        compiler->addFunctionCall("test1", 3, false);
+        std::vector<Compiler::StaticType> args = { Compiler::StaticType::Number, Compiler::StaticType::Bool };
+        EXPECT_CALL(*m_builder, addFunctionCall("test1", Compiler::StaticType::Void, args));
+        compiler->addFunctionCall("test1", Compiler::StaticType::Void, args);
 
-        EXPECT_CALL(*m_builder, addFunctionCall("test2", 0, true));
-        compiler->addFunctionCall("test2", 0, true);
+        args = { Compiler::StaticType::String };
+        EXPECT_CALL(*m_builder, addFunctionCall("test2", Compiler::StaticType::Bool, args));
+        compiler->addFunctionCall("test2", Compiler::StaticType::Bool, args);
     });
 
     compile(compiler, block);
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 18942ea0..b061831e 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -25,22 +25,22 @@ class LLVMCodeBuilderTest : public testing::Test
 
 TEST_F(LLVMCodeBuilderTest, FunctionCalls)
 {
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
 
-    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
-    m_builder->addConstValue(1);
-    m_builder->addFunctionCall("test_function_1_arg_ret", 1, true);
-    m_builder->addConstValue(2);
-    m_builder->addConstValue(3);
-    m_builder->addFunctionCall("test_function_3_args", 3, false);
+    m_builder->addConstValue("1");
+    m_builder->addFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String });
+    m_builder->addConstValue("2");
+    m_builder->addConstValue("3");
+    m_builder->addFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
 
     m_builder->addConstValue("test");
-    m_builder->addConstValue(4);
-    m_builder->addConstValue(5);
-    m_builder->addFunctionCall("test_function_3_args_ret", 3, true);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addConstValue("4");
+    m_builder->addConstValue("5");
+    m_builder->addFunctionCall("test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     auto code = m_builder->finalize();
     auto ctx = code->createExecutionContext(&m_target);
 
@@ -61,24 +61,24 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls)
 
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
 
-    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
     m_builder->yield();
 
-    m_builder->addConstValue(1);
-    m_builder->addFunctionCall("test_function_1_arg_ret", 1, true);
-    m_builder->addConstValue(2);
+    m_builder->addConstValue("1");
+    m_builder->addFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String });
+    m_builder->addConstValue("2");
     m_builder->addConstValue(3);
-    m_builder->addFunctionCall("test_function_3_args", 3, false);
+    m_builder->addFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
 
     m_builder->addConstValue("test");
-    m_builder->addConstValue(4);
-    m_builder->addConstValue(5);
-    m_builder->addFunctionCall("test_function_3_args_ret", 3, true);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addConstValue("4");
+    m_builder->addConstValue("5");
+    m_builder->addFunctionCall("test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     auto code = m_builder->finalize();
     auto ctx = code->createExecutionContext(&m_target);
 
@@ -111,71 +111,71 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
     // Without else branch (const condition)
     m_builder->addConstValue("true");
     m_builder->beginIfStatement();
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endIf();
 
     m_builder->addConstValue("false");
     m_builder->beginIfStatement();
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endIf();
 
     // Without else branch (condition returned by function)
-    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
     m_builder->addConstValue("no_args_output");
-    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String });
     m_builder->beginIfStatement();
     m_builder->addConstValue(0);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endIf();
 
-    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
     m_builder->addConstValue("");
-    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String });
     m_builder->beginIfStatement();
     m_builder->addConstValue(1);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endIf();
 
     // With else branch (const condition)
     m_builder->addConstValue("true");
     m_builder->beginIfStatement();
     m_builder->addConstValue(2);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->beginElseBranch();
     m_builder->addConstValue(3);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endIf();
 
     m_builder->addConstValue("false");
     m_builder->beginIfStatement();
     m_builder->addConstValue(4);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->beginElseBranch();
     m_builder->addConstValue(5);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endIf();
 
     // With else branch (condition returned by function)
-    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
     m_builder->addConstValue("no_args_output");
-    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String });
     m_builder->beginIfStatement();
     m_builder->addConstValue(6);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->beginElseBranch();
     m_builder->addConstValue(7);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endIf();
 
-    m_builder->addFunctionCall("test_function_no_args_ret", 0, true);
+    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
     m_builder->addConstValue("");
-    m_builder->addFunctionCall("test_equals", 2, true);
+    m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String });
     m_builder->beginIfStatement();
     m_builder->addConstValue(8);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->beginElseBranch();
     m_builder->addConstValue(9);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endIf();
 
     // Nested 1
@@ -186,19 +186,19 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
         m_builder->beginIfStatement();
         {
             m_builder->addConstValue(0);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->beginElseBranch();
         {
             m_builder->addConstValue(1);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
             m_builder->addConstValue(false);
             m_builder->beginIfStatement();
             m_builder->beginElseBranch();
             {
                 m_builder->addConstValue(2);
-                m_builder->addFunctionCall("test_function_1_arg", 1, false);
+                m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
             }
             m_builder->endIf();
         }
@@ -210,12 +210,12 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
         m_builder->beginIfStatement();
         {
             m_builder->addConstValue(3);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->beginElseBranch();
         {
             m_builder->addConstValue(4);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->endIf();
     }
@@ -229,12 +229,12 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
         m_builder->beginIfStatement();
         {
             m_builder->addConstValue(5);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->beginElseBranch();
         {
             m_builder->addConstValue(6);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->endIf();
     }
@@ -244,7 +244,7 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
         m_builder->beginIfStatement();
         {
             m_builder->addConstValue(7);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->beginElseBranch();
         m_builder->endIf();
@@ -280,30 +280,30 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
     // Const count
     m_builder->addConstValue("-5");
     m_builder->beginRepeatLoop();
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     m_builder->addConstValue(0);
     m_builder->beginRepeatLoop();
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     m_builder->addConstValue(3);
     m_builder->beginRepeatLoop();
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     m_builder->addConstValue("2");
     m_builder->beginRepeatLoop();
     m_builder->addConstValue(0);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endLoop();
 
     // Count returned by function
     m_builder->addConstValue(2);
-    m_builder->addFunctionCall("test_const", 1, true);
+    m_builder->addFunctionCall("test_const", Compiler::StaticType::Number, { Compiler::StaticType::Number });
     m_builder->beginRepeatLoop();
-    m_builder->addFunctionCall("test_function_no_args", 0, false);
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     // Nested
@@ -314,18 +314,18 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
         m_builder->beginRepeatLoop();
         {
             m_builder->addConstValue(1);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->endLoop();
 
         m_builder->addConstValue(2);
-        m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         m_builder->addConstValue(3);
         m_builder->beginRepeatLoop();
         {
             m_builder->addConstValue(3);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
         }
         m_builder->endLoop();
     }
@@ -367,55 +367,55 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop)
     m_builder->beginLoopCondition();
     m_builder->addConstValue("false");
     m_builder->beginWhileLoop();
-    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     m_builder->beginLoopCondition();
     m_builder->addConstValue(false);
     m_builder->beginWhileLoop();
-    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     // Condition returned by function
-    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {});
     m_builder->beginLoopCondition();
-    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {});
     m_builder->addConstValue(2);
-    m_builder->addFunctionCall("test_lower_than", 2, true);
+    m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number });
     m_builder->beginWhileLoop();
     m_builder->addConstValue(0);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
-    m_builder->addFunctionCall("test_increment_counter", 0, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+    m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     // Nested
-    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {});
     m_builder->beginLoopCondition();
-    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {});
     m_builder->addConstValue(3);
-    m_builder->addFunctionCall("test_lower_than", 2, true);
+    m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number });
     m_builder->beginWhileLoop();
     {
         m_builder->beginLoopCondition();
-        m_builder->addFunctionCall("test_get_counter", 0, true);
+        m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {});
         m_builder->addConstValue(3);
-        m_builder->addFunctionCall("test_lower_than", 2, true);
+        m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number });
         m_builder->beginWhileLoop();
         {
             m_builder->addConstValue(1);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
-            m_builder->addFunctionCall("test_increment_counter", 0, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+            m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {});
         }
         m_builder->endLoop();
 
         m_builder->addConstValue(2);
-        m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         m_builder->beginLoopCondition();
         m_builder->addConstValue(false);
         m_builder->beginWhileLoop();
         {
-            m_builder->addFunctionCall("test_unreachable", 0, false);
+            m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {});
         }
         m_builder->endLoop();
     }
@@ -444,58 +444,58 @@ TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop)
     m_builder->beginLoopCondition();
     m_builder->addConstValue("true");
     m_builder->beginRepeatUntilLoop();
-    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     m_builder->beginLoopCondition();
     m_builder->addConstValue(true);
     m_builder->beginRepeatUntilLoop();
-    m_builder->addFunctionCall("test_unreachable", 0, false);
+    m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     // Condition returned by function
-    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {});
     m_builder->beginLoopCondition();
-    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {});
     m_builder->addConstValue(2);
-    m_builder->addFunctionCall("test_lower_than", 2, true);
-    m_builder->addFunctionCall("test_not", 1, true);
+    m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
     m_builder->beginRepeatUntilLoop();
     m_builder->addConstValue(0);
-    m_builder->addFunctionCall("test_function_1_arg", 1, false);
-    m_builder->addFunctionCall("test_increment_counter", 0, false);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+    m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
     // Nested
-    m_builder->addFunctionCall("test_reset_counter", 0, false);
+    m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {});
     m_builder->beginLoopCondition();
-    m_builder->addFunctionCall("test_get_counter", 0, true);
+    m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {});
     m_builder->addConstValue(3);
-    m_builder->addFunctionCall("test_lower_than", 2, true);
-    m_builder->addFunctionCall("test_not", 1, true);
+    m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
     m_builder->beginRepeatUntilLoop();
     {
         m_builder->beginLoopCondition();
-        m_builder->addFunctionCall("test_get_counter", 0, true);
+        m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {});
         m_builder->addConstValue(3);
-        m_builder->addFunctionCall("test_lower_than", 2, true);
-        m_builder->addFunctionCall("test_not", 1, true);
+        m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number });
+        m_builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
         m_builder->beginRepeatUntilLoop();
         {
             m_builder->addConstValue(1);
-            m_builder->addFunctionCall("test_function_1_arg", 1, false);
-            m_builder->addFunctionCall("test_increment_counter", 0, false);
+            m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+            m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {});
         }
         m_builder->endLoop();
 
         m_builder->addConstValue(2);
-        m_builder->addFunctionCall("test_function_1_arg", 1, false);
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         m_builder->beginLoopCondition();
         m_builder->addConstValue(true);
         m_builder->beginRepeatUntilLoop();
         {
-            m_builder->addFunctionCall("test_unreachable", 0, false);
+            m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {});
         }
         m_builder->endLoop();
     }
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index c31d148f..d61ba4f8 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -1,4 +1,4 @@
-#include <scratchcpp/value_functions.h>
+#include <scratchcpp/value.h>
 #include <iostream>
 #include <targetmock.h>
 
@@ -31,54 +31,45 @@ extern "C"
         std::cout << "no_args" << std::endl;
     }
 
-    void test_function_no_args_ret(Target *target, ValueData *ret)
+    char *test_function_no_args_ret(Target *target)
     {
         target->isStage();
         std::cout << "no_args_ret" << std::endl;
-        value_assign_cstring(ret, "no_args_output");
+        Value v("no_args_output");
+        return value_toCString(&v.data());
     }
 
-    void test_function_1_arg(Target *target, const ValueData *arg1)
+    void test_function_1_arg(Target *target, const char *arg1)
     {
         target->isStage();
-        std::string s;
-        value_toString(arg1, &s);
-        std::cout << "1_arg " << s << std::endl;
+        std::cout << "1_arg " << arg1 << std::endl;
     }
 
-    void test_function_1_arg_ret(Target *target, ValueData *ret, const ValueData *arg1)
+    char *test_function_1_arg_ret(Target *target, const char *arg1)
     {
         target->isStage();
-        std::string s;
-        value_toString(arg1, &s);
-        std::cout << "1_arg_ret " << s << std::endl;
-        value_assign_cstring(ret, "1_arg_output");
+        std::cout << "1_arg_ret " << arg1 << std::endl;
+        Value v("1_arg_output");
+        return value_toCString(&v.data());
     }
 
-    void test_function_3_args(Target *target, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3)
+    void test_function_3_args(Target *target, const char *arg1, const char *arg2, const char *arg3)
     {
         target->isStage();
-        std::string s1, s2, s3;
-        value_toString(arg1, &s1);
-        value_toString(arg2, &s2);
-        value_toString(arg3, &s3);
-        std::cout << "3_args " << s1 << " " << s2 << " " << s3 << std::endl;
+        std::cout << "3_args " << arg1 << " " << arg2 << " " << arg3 << std::endl;
     }
 
-    void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3)
+    char *test_function_3_args_ret(Target *target, const char *arg1, const char *arg2, const char *arg3)
     {
         target->isStage();
-        std::string s1, s2, s3;
-        value_toString(arg1, &s1);
-        value_toString(arg2, &s2);
-        value_toString(arg3, &s3);
-        std::cout << "3_args " << s1 << " " << s2 << " " << s3 << std::endl;
-        value_assign_cstring(ret, "3_args_output");
+        std::cout << "3_args " << arg1 << " " << arg2 << " " << arg3 << std::endl;
+        Value v("3_args_output");
+        return value_toCString(&v.data());
     }
 
-    void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b)
+    bool test_equals(Target *target, const char *a, const char *b)
     {
-        value_assign_bool(ret, value_equals(a, b));
+        return strcmp(a, b) == 0;
     }
 
     void test_unreachable(Target *target)
@@ -87,19 +78,19 @@ extern "C"
         exit(1);
     }
 
-    void test_lower_than(Target *target, ValueData *ret, ValueData *a, ValueData *b)
+    bool test_lower_than(Target *target, double a, double b)
     {
-        value_assign_bool(ret, value_lower(a, b));
+        return a < b;
     }
 
-    void test_const(Target *target, ValueData *ret, ValueData *v)
+    double test_const(Target *target, double v)
     {
-        value_assign_copy(ret, v);
+        return v;
     }
 
-    void test_not(Target *target, ValueData *ret, ValueData *arg)
+    bool test_not(Target *target, bool arg)
     {
-        value_assign_bool(ret, !value_toBool(arg));
+        return !arg;
     }
 
     void test_reset_counter(Target *target)
@@ -112,8 +103,8 @@ extern "C"
         counter++;
     }
 
-    void test_get_counter(Target *target, ValueData *ret)
+    double test_get_counter(Target *target)
     {
-        value_assign_int(ret, counter);
+        return counter;
     }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index b0142013..8c87cb62 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -13,22 +13,22 @@ extern "C"
     void test_print_function(ValueData *arg1, ValueData *arg2);
 
     void test_function_no_args(Target *target);
-    void test_function_no_args_ret(Target *target, ValueData *ret);
-    void test_function_1_arg(Target *target, const ValueData *arg1);
-    void test_function_1_arg_ret(Target *target, ValueData *ret, const ValueData *arg1);
-    void test_function_3_args(Target *target, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
-    void test_function_3_args_ret(Target *target, ValueData *ret, const ValueData *arg1, const ValueData *arg2, const ValueData *arg3);
+    char *test_function_no_args_ret(Target *target);
+    void test_function_1_arg(Target *target, const char *arg1);
+    char *test_function_1_arg_ret(Target *target, const char *arg1);
+    void test_function_3_args(Target *target, const char *arg1, const char *arg2, const char *arg3);
+    char *test_function_3_args_ret(Target *target, const char *arg1, const char *arg2, const char *arg3);
 
-    void test_equals(Target *target, ValueData *ret, ValueData *a, ValueData *b);
-    void test_lower_than(Target *target, ValueData *ret, ValueData *a, ValueData *b);
-    void test_not(Target *target, ValueData *ret, ValueData *arg);
-    void test_const(Target *target, ValueData *ret, ValueData *v);
+    bool test_equals(Target *target, const char *a, const char *b);
+    bool test_lower_than(Target *target, double a, double b);
+    bool test_not(Target *target, bool arg);
+    double test_const(Target *target, double v);
 
     void test_unreachable(Target *target);
 
     void test_reset_counter(Target *target);
     void test_increment_counter(Target *target);
-    void test_get_counter(Target *target, ValueData *ret);
+    double test_get_counter(Target *target);
 }
 
 } // namespace libscratchcpp
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index ac9ca92d..0000ed25 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -9,7 +9,7 @@ class CodeBuilderMock : public ICodeBuilder
 {
     public:
         MOCK_METHOD(std::shared_ptr<ExecutableCode>, finalize, (), (override));
-        MOCK_METHOD(void, addFunctionCall, (const std::string &, int, bool), (override));
+        MOCK_METHOD(void, addFunctionCall, (const std::string &, Compiler::StaticType, const std::vector<Compiler::StaticType> &), (override));
         MOCK_METHOD(void, addConstValue, (const Value &), (override));
         MOCK_METHOD(void, addVariableValue, (Variable *), (override));
         MOCK_METHOD(void, addListContents, (List *), (override));

From 736270d797d5bfed4565c3739a9b3575c9d13acc Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 11 Oct 2024 12:02:56 +0200
Subject: [PATCH 48/94] Do not use pointers to LLVM IR constants

---
 src/dev/engine/internal/llvmcodebuilder.cpp   | 79 ++++++-------------
 src/dev/engine/internal/llvmcodebuilder.h     |  8 +-
 .../engine/internal/llvmexecutablecode.cpp    | 15 +---
 src/dev/engine/internal/llvmexecutablecode.h  |  4 +-
 test/dev/llvm/llvmcodebuilder_test.cpp        | 31 ++++++++
 test/dev/llvm/llvmexecutablecode_test.cpp     | 45 +----------
 test/dev/llvm/testfunctions.cpp               | 15 ++++
 test/dev/llvm/testfunctions.h                 |  4 +
 8 files changed, 83 insertions(+), 118 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 5862b21b..998cf9f4 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -3,7 +3,6 @@
 #include <llvm/Support/TargetSelect.h>
 #include <llvm/IR/Verifier.h>
 #include <llvm/ExecutionEngine/Orc/LLJIT.h>
-#include <scratchcpp/value.h>
 
 #include "llvmcodebuilder.h"
 #include "llvmexecutablecode.h"
@@ -30,16 +29,6 @@ LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id) :
     initTypes();
 }
 
-LLVMCodeBuilder::~LLVMCodeBuilder()
-{
-    for (const auto &values : m_constValues) {
-        for (const auto &v : values) {
-            if (v)
-                value_free(v.get());
-        }
-    }
-}
-
 std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 {
     size_t functionIndex = 0;
@@ -291,15 +280,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     std::cout << "==============" << std::endl << std::endl;
 #endif
 
-    std::vector<std::unique_ptr<ValueData>> constValues;
-
-    for (auto &values : m_constValues) {
-        for (auto &v : values)
-            constValues.push_back(std::move(v));
-    }
-
-    m_constValues.clear();
-    return std::make_shared<LLVMExecutableCode>(std::move(m_module), constValues);
+    return std::make_shared<LLVMExecutableCode>(std::move(m_module));
 }
 
 void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const std::vector<Compiler::StaticType> &argTypes)
@@ -331,16 +312,10 @@ void LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler:
 void LLVMCodeBuilder::addConstValue(const Value &value)
 {
     auto reg = std::make_shared<Register>(TYPE_MAP[value.type()]);
-    reg->isRawValue = false;
     reg->isConstValue = true;
-    reg->constValueIndex = m_constValues[m_currentFunction].size();
+    reg->constValue = value;
     m_regs[m_currentFunction].push_back(reg);
     m_tmpRegs.push_back(reg);
-
-    std::unique_ptr<ValueData> v = std::make_unique<ValueData>();
-    value_init(v.get());
-    value_assign_copy(v.get(), &value.data());
-    m_constValues[m_currentFunction].push_back(std::move(v));
 }
 
 void LLVMCodeBuilder::addVariableValue(Variable *variable)
@@ -448,35 +423,6 @@ llvm::Function *LLVMCodeBuilder::beginFunction(size_t index)
     llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
     m_builder.SetInsertPoint(entry);
 
-    // Add const value pointers
-    const auto &constValues = m_constValues[index];
-    std::vector<llvm::Value *> constPtrs;
-
-    for (size_t i = 0; i < constValues.size(); i++) {
-        llvm::Value *intAddress = m_builder.getInt64((uintptr_t)constValues[i].get());
-        llvm::Value *ptr = m_builder.CreateIntToPtr(intAddress, m_valueDataType->getPointerTo());
-        constPtrs.push_back(ptr);
-    }
-
-    // Add registers
-    auto &regs = m_regs[index];
-    size_t regIndex = 0;
-
-    for (auto &reg : regs) {
-        if (reg->isConstValue) {
-            // Do not allocate space for existing constant values
-            reg->value = constPtrs[reg->constValueIndex];
-        } else {
-            /*const std::string name = "r" + std::to_string(regIndex);
-
-            llvm::Value *valueData = m_builder.CreateAlloca(m_valueDataType, nullptr, name);
-            m_builder.CreateCall(resolve_value_init(), { valueData });
-
-            reg->value = valueData;
-            regIndex++;*/
-        }
-    }
-
     return func;
 }
 
@@ -509,6 +455,9 @@ void LLVMCodeBuilder::freeHeap()
 
 llvm::Value *LLVMCodeBuilder::castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType)
 {
+    if (reg->isConstValue)
+        return castConstValue(reg->constValue, targetType);
+
     if (reg->isRawValue)
         return castRawValue(reg, targetType);
 
@@ -655,6 +604,24 @@ llvm::Value *LLVMCodeBuilder::castRawValue(std::shared_ptr<Register> reg, Compil
     }
 }
 
+llvm::Value *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType)
+{
+    switch (targetType) {
+        case Compiler::StaticType::Number:
+            return llvm::ConstantFP::get(m_ctx, llvm::APFloat(value.toDouble()));
+
+        case Compiler::StaticType::Bool:
+            return m_builder.getInt1(value.toBool());
+
+        case Compiler::StaticType::String:
+            return m_builder.CreateGlobalStringPtr(value.toString());
+
+        default:
+            assert(false);
+            return nullptr;
+    }
+}
+
 llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type)
 {
     switch (type) {
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 5f6a2263..78923bcb 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -2,7 +2,7 @@
 
 #pragma once
 
-#include <scratchcpp/valuedata.h>
+#include <scratchcpp/value.h>
 #include <llvm/IR/LLVMContext.h>
 #include <llvm/IR/Module.h>
 #include <llvm/IR/IRBuilder.h>
@@ -18,7 +18,6 @@ class LLVMCodeBuilder : public ICodeBuilder
 {
     public:
         LLVMCodeBuilder(const std::string &id);
-        ~LLVMCodeBuilder();
 
         std::shared_ptr<ExecutableCode> finalize() override;
 
@@ -51,7 +50,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                 llvm::Value *value = nullptr;
                 bool isRawValue = false;
                 bool isConstValue = false;
-                size_t constValueIndex = 0;
+                Value constValue;
         };
 
         struct Step
@@ -106,6 +105,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void freeHeap();
         llvm::Value *castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
         llvm::Value *castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
+        llvm::Value *castConstValue(const Value &value, Compiler::StaticType targetType);
         llvm::Type *getType(Compiler::StaticType type);
 
         llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type);
@@ -133,7 +133,7 @@ class LLVMCodeBuilder : public ICodeBuilder
 
         std::vector<Step> m_steps;
         size_t m_currentFunction = 0;
-        std::vector<std::vector<std::unique_ptr<ValueData>>> m_constValues;
+        std::vector<Value> m_constValues;
         std::vector<std::vector<std::shared_ptr<Register>>> m_regs;
         std::vector<std::shared_ptr<Register>> m_tmpRegs;
 
diff --git a/src/dev/engine/internal/llvmexecutablecode.cpp b/src/dev/engine/internal/llvmexecutablecode.cpp
index 031de8d0..c38fe784 100644
--- a/src/dev/engine/internal/llvmexecutablecode.cpp
+++ b/src/dev/engine/internal/llvmexecutablecode.cpp
@@ -10,7 +10,7 @@
 
 using namespace libscratchcpp;
 
-LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module, std::vector<std::unique_ptr<ValueData>> &constValues) :
+LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module) :
     m_ctx(std::make_unique<llvm::LLVMContext>()),
     m_jit(llvm::orc::LLJITBuilder().create())
 {
@@ -46,19 +46,6 @@ LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module, std
 
         i++;
     }
-
-    // Move const value pointers (transfer ownership)
-    for (auto &v : constValues)
-        m_constValues.push_back(std::move(v));
-
-    constValues.clear();
-}
-
-LLVMExecutableCode::~LLVMExecutableCode()
-{
-    // Free memory used by const values
-    for (const auto &v : m_constValues)
-        value_free(v.get());
 }
 
 void LLVMExecutableCode::run(ExecutionContext *context)
diff --git a/src/dev/engine/internal/llvmexecutablecode.h b/src/dev/engine/internal/llvmexecutablecode.h
index ed4ffdbe..312ef597 100644
--- a/src/dev/engine/internal/llvmexecutablecode.h
+++ b/src/dev/engine/internal/llvmexecutablecode.h
@@ -16,8 +16,7 @@ class LLVMExecutionContext;
 class LLVMExecutableCode : public ExecutableCode
 {
     public:
-        LLVMExecutableCode(std::unique_ptr<llvm::Module> module, std::vector<std::unique_ptr<libscratchcpp::ValueData>> &constValues);
-        ~LLVMExecutableCode();
+        LLVMExecutableCode(std::unique_ptr<llvm::Module> module);
 
         void run(ExecutionContext *context) override;
         void kill(libscratchcpp::ExecutionContext *context) override;
@@ -39,7 +38,6 @@ class LLVMExecutableCode : public ExecutableCode
         llvm::Expected<std::unique_ptr<llvm::orc::LLJIT>> m_jit;
 
         std::vector<FunctionType> m_functions;
-        std::vector<std::unique_ptr<ValueData>> m_constValues;
 };
 
 } // namespace libscratchcpp
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index b061831e..ddd759d8 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -59,6 +59,37 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
+TEST_F(LLVMCodeBuilderTest, ConstCasting)
+{
+    m_builder->addConstValue(5.2);
+    m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+    m_builder->addConstValue(-24.156);
+    m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+
+    m_builder->addConstValue(true);
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue(false);
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue("hello world");
+    m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "5.2\n"
+        "-24.156\n"
+        "1\n"
+        "0\n"
+        "hello world\n";
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
index 529dda64..95993e0b 100644
--- a/test/dev/llvm/llvmexecutablecode_test.cpp
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -69,8 +69,7 @@ class LLVMExecutableCodeTest : public testing::Test
 
 TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
 {
-    std::vector<std::unique_ptr<ValueData>> constValues;
-    LLVMExecutableCode code(std::move(m_module), constValues);
+    LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
     ASSERT_TRUE(ctx);
     ASSERT_EQ(ctx->target(), &m_target);
@@ -79,8 +78,7 @@ TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
 
 TEST_F(LLVMExecutableCodeTest, NoFunctions)
 {
-    std::vector<std::unique_ptr<ValueData>> constValues;
-    LLVMExecutableCode code(std::move(m_module), constValues);
+    LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
     ASSERT_TRUE(code.isFinished(ctx.get()));
 
@@ -100,8 +98,7 @@ TEST_F(LLVMExecutableCodeTest, SingleFunction)
     addTestFunction(f);
     endFunction(0);
 
-    std::vector<std::unique_ptr<ValueData>> constValues;
-    LLVMExecutableCode code(std::move(m_module), constValues);
+    LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
     ASSERT_FALSE(code.isFinished(ctx.get()));
 
@@ -148,8 +145,7 @@ TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
         endFunction(i);
     }
 
-    std::vector<std::unique_ptr<ValueData>> constValues;
-    LLVMExecutableCode code(std::move(m_module), constValues);
+    LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
     ASSERT_FALSE(code.isFinished(ctx.get()));
 
@@ -161,36 +157,3 @@ TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
 
     ASSERT_TRUE(code.isFinished(ctx.get()));
 }
-
-TEST_F(LLVMExecutableCodeTest, ConstValues)
-{
-    beginFunction(0);
-    std::vector<std::unique_ptr<ValueData>> constValues;
-
-    for (int i = 0; i < 2; i++) {
-        std::unique_ptr<ValueData> value = std::make_unique<ValueData>();
-        value_init(value.get());
-        value_assign_int(value.get(), i + 1);
-        constValues.push_back(std::move(value));
-    }
-
-    auto ptrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
-    llvm::Value *intAddress = m_builder->getInt64((uintptr_t)constValues[0].get());
-    llvm::Value *ptr1 = m_builder->CreateIntToPtr(intAddress, ptrType);
-
-    intAddress = m_builder->getInt64((uintptr_t)constValues[1].get());
-    llvm::Value *ptr2 = m_builder->CreateIntToPtr(intAddress, ptrType);
-
-    addTestPrintFunction(ptr1, ptr2);
-    endFunction(0);
-
-    LLVMExecutableCode code(std::move(m_module), constValues);
-    auto ctx = code.createExecutionContext(&m_target);
-    ASSERT_TRUE(constValues.empty());
-    ASSERT_FALSE(code.isFinished(ctx.get()));
-
-    testing::internal::CaptureStdout();
-    code.run(ctx.get());
-    ASSERT_EQ(testing::internal::GetCapturedStdout(), "1 2\n");
-    ASSERT_TRUE(code.isFinished(ctx.get()));
-}
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index d61ba4f8..be1d434c 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -107,4 +107,19 @@ extern "C"
     {
         return counter;
     }
+
+    void test_print_number(Target *target, double v)
+    {
+        std::cout << v << std::endl;
+    }
+
+    void test_print_bool(Target *target, bool v)
+    {
+        std::cout << v << std::endl;
+    }
+
+    void test_print_string(Target *target, const char *v)
+    {
+        std::cout << v << std::endl;
+    }
 }
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 8c87cb62..74e706bd 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -29,6 +29,10 @@ extern "C"
     void test_reset_counter(Target *target);
     void test_increment_counter(Target *target);
     double test_get_counter(Target *target);
+
+    void test_print_number(Target *target, double v);
+    void test_print_bool(Target *target, bool v);
+    void test_print_string(Target *target, const char *v);
 }
 
 } // namespace libscratchcpp

From ffbaf94ad6d5cf96de45bcab32caec38fa5ac295 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 11 Oct 2024 14:43:10 +0200
Subject: [PATCH 49/94] LLVMCodeBuilder: Fix typos

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 998cf9f4..5a2c0955 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -550,7 +550,7 @@ llvm::Value *LLVMCodeBuilder::castRawValue(std::shared_ptr<Register> reg, Compil
             switch (reg->type) {
                 case Compiler::StaticType::Bool:
                     // Cast bool to double
-                    return m_builder.CreateSIToFP(reg->value, m_builder.getDoubleTy());
+                    return m_builder.CreateUIToFP(reg->value, m_builder.getDoubleTy());
 
                 case Compiler::StaticType::String: {
                     // Convert string to double
@@ -705,7 +705,7 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_doubleToCString()
 
 llvm::FunctionCallee LLVMCodeBuilder::resolve_value_boolToCString()
 {
-    return resolveFunction("value_doubleToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_builder.getInt1Ty(), false));
+    return resolveFunction("value_boolToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_builder.getInt1Ty(), false));
 }
 
 llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToDouble()

From 78bd34b68b44b34c93fdb416e617eb3178bce52c Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 11 Oct 2024 14:43:44 +0200
Subject: [PATCH 50/94] Add test for raw value casting

---
 test/dev/llvm/llvmcodebuilder_test.cpp | 103 ++++++++++++++++++++++++-
 test/dev/llvm/testfunctions.cpp        |  13 +++-
 test/dev/llvm/testfunctions.h          |   4 +-
 3 files changed, 116 insertions(+), 4 deletions(-)

diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index ddd759d8..642a0cb2 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -63,16 +63,106 @@ TEST_F(LLVMCodeBuilderTest, ConstCasting)
 {
     m_builder->addConstValue(5.2);
     m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+    m_builder->addConstValue("-24.156");
+    m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+
+    m_builder->addConstValue(true);
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue(0);
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue("false");
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue("123");
+    m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+    m_builder->addConstValue("hello world");
+    m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "5.2\n"
+        "-24.156\n"
+        "1\n"
+        "0\n"
+        "0\n"
+        "123\n"
+        "hello world\n";
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
+TEST_F(LLVMCodeBuilderTest, RawValueCasting)
+{
+    // Number -> number
+    m_builder->addConstValue(5.2);
+    m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+
+    // Number -> bool
     m_builder->addConstValue(-24.156);
+    m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue(0);
+    m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    // Number -> string
+    m_builder->addConstValue(59.8);
+    m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+    // Bool -> number
+    m_builder->addConstValue(true);
+    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
+    m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+
+    m_builder->addConstValue(false);
+    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
     m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
 
+    // Bool -> bool
     m_builder->addConstValue(true);
+    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
     m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
 
     m_builder->addConstValue(false);
+    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    // Bool -> string
+    m_builder->addConstValue(true);
+    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
+    m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+    m_builder->addConstValue(false);
+    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
+    m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+    // String -> number
+    m_builder->addConstValue("5.2");
+    m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String });
+    m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
+
+    // String -> bool
+    m_builder->addConstValue("abc");
+    m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String });
+    m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
+
+    m_builder->addConstValue("false");
+    m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String });
     m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool });
 
+    // String -> string
     m_builder->addConstValue("hello world");
+    m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String });
     m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
     auto code = m_builder->finalize();
@@ -80,7 +170,16 @@ TEST_F(LLVMCodeBuilderTest, ConstCasting)
 
     static const std::string expected =
         "5.2\n"
-        "-24.156\n"
+        "1\n"
+        "0\n"
+        "59.8\n"
+        "1\n"
+        "0\n"
+        "1\n"
+        "0\n"
+        "true\n"
+        "false\n"
+        "5.2\n"
         "1\n"
         "0\n"
         "hello world\n";
@@ -332,7 +431,7 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
 
     // Count returned by function
     m_builder->addConstValue(2);
-    m_builder->addFunctionCall("test_const", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+    m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
     m_builder->beginRepeatLoop();
     m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endLoop();
diff --git a/test/dev/llvm/testfunctions.cpp b/test/dev/llvm/testfunctions.cpp
index be1d434c..86ecb8b6 100644
--- a/test/dev/llvm/testfunctions.cpp
+++ b/test/dev/llvm/testfunctions.cpp
@@ -83,11 +83,22 @@ extern "C"
         return a < b;
     }
 
-    double test_const(Target *target, double v)
+    double test_const_number(Target *target, double v)
     {
         return v;
     }
 
+    bool test_const_bool(Target *target, bool v)
+    {
+        return v;
+    }
+
+    char *test_const_string(Target *target, const char *v)
+    {
+        Value value(v);
+        return value_toCString(&value.data());
+    }
+
     bool test_not(Target *target, bool arg)
     {
         return !arg;
diff --git a/test/dev/llvm/testfunctions.h b/test/dev/llvm/testfunctions.h
index 74e706bd..94f8a76a 100644
--- a/test/dev/llvm/testfunctions.h
+++ b/test/dev/llvm/testfunctions.h
@@ -22,7 +22,9 @@ extern "C"
     bool test_equals(Target *target, const char *a, const char *b);
     bool test_lower_than(Target *target, double a, double b);
     bool test_not(Target *target, bool arg);
-    double test_const(Target *target, double v);
+    double test_const_number(Target *target, double v);
+    bool test_const_bool(Target *target, bool v);
+    char *test_const_string(Target *target, const char *v);
 
     void test_unreachable(Target *target);
 

From 6d9a04d3353ed54defad315b4a400efbd1328f83 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 11 Oct 2024 22:32:46 +0200
Subject: [PATCH 51/94] Drop multiple functions in compiled code

---
 src/dev/engine/internal/llvmcodebuilder.cpp   | 89 ++++++++-----------
 src/dev/engine/internal/llvmcodebuilder.h     |  2 -
 .../engine/internal/llvmexecutablecode.cpp    | 41 ++++-----
 src/dev/engine/internal/llvmexecutablecode.h  |  6 +-
 .../engine/internal/llvmexecutioncontext.cpp  | 10 ---
 .../engine/internal/llvmexecutioncontext.h    |  6 +-
 test/dev/llvm/llvmcodebuilder_test.cpp        |  4 +-
 test/dev/llvm/llvmexecutablecode_test.cpp     | 61 +++----------
 test/dev/llvm/llvmexecutioncontext_test.cpp   | 12 ---
 9 files changed, 76 insertions(+), 155 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 5a2c0955..47626f1d 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -31,8 +31,14 @@ LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id) :
 
 std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 {
-    size_t functionIndex = 0;
-    llvm::Function *currentFunc = beginFunction(functionIndex);
+    // Create function
+    // void f(Target *)
+    llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder.getVoidTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+    llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get());
+
+    llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
+    m_builder.SetInsertPoint(entry);
+
     std::vector<IfStatement> ifStatements;
     std::vector<Loop> loops;
     m_heap.clear();
@@ -45,9 +51,9 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 std::vector<llvm::Value *> args;
 
                 // Add target pointer arg
-                assert(currentFunc->arg_size() == 1);
+                assert(func->arg_size() == 1);
                 types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0));
-                args.push_back(currentFunc->getArg(0));
+                args.push_back(func->getArg(0));
 
                 // Args
                 for (auto &arg : step.args) {
@@ -69,14 +75,13 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 
             case Step::Type::Yield:
                 freeHeap();
-                endFunction(currentFunc, functionIndex);
-                currentFunc = beginFunction(++functionIndex);
+                // TODO: Implement yielding
                 break;
 
             case Step::Type::BeginIf: {
                 IfStatement statement;
                 statement.beforeIf = m_builder.GetInsertBlock();
-                statement.body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                statement.body = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 // Use last reg
                 assert(step.args.size() == 1);
@@ -98,13 +103,13 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 
                 // Jump to the branch after the if statement
                 assert(!statement.afterIf);
-                statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", func);
                 freeHeap();
                 m_builder.CreateBr(statement.afterIf);
 
                 // Create else branch
                 assert(!statement.elseBranch);
-                statement.elseBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                statement.elseBranch = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 // Since there's an else branch, the conditional instruction should jump to it
                 m_builder.SetInsertPoint(statement.beforeIf);
@@ -121,7 +126,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 
                 // Jump to the branch after the if statement
                 if (!statement.afterIf)
-                    statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                    statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 freeHeap();
                 m_builder.CreateBr(statement.afterIf);
@@ -150,9 +155,9 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 m_builder.CreateStore(zero, loop.index);
 
                 // Create branches
-                llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
-                loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
-                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(m_ctx, "", func);
+                loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", func);
+                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 // Use last reg for count
                 assert(step.args.size() == 1);
@@ -177,10 +182,10 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 // Check index
                 m_builder.SetInsertPoint(loop.conditionBranch);
 
-                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 if (!loop.afterLoop)
-                    loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                    loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index);
                 comparison = m_builder.CreateICmpULT(currentIndex, count);
@@ -198,8 +203,8 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 Loop &loop = loops.back();
 
                 // Create branches
-                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
-                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", func);
+                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 // Use last reg
                 assert(step.args.size() == 1);
@@ -219,8 +224,8 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 Loop &loop = loops.back();
 
                 // Create branches
-                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
-                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", func);
+                loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func);
 
                 // Use last reg
                 assert(step.args.size() == 1);
@@ -238,7 +243,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
             case Step::Type::BeginLoopCondition: {
                 Loop loop;
                 loop.isRepeatLoop = false;
-                loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", currentFunc);
+                loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", func);
                 freeHeap();
                 m_builder.CreateBr(loop.conditionBranch);
                 m_builder.SetInsertPoint(loop.conditionBranch);
@@ -272,7 +277,19 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 
     freeHeap();
 
-    endFunction(currentFunc, functionIndex);
+    // End and verify the function
+    if (!m_tmpRegs.empty()) {
+        std::cout
+            << "warning: " << m_tmpRegs.size() << " registers were leaked by script '" << m_module->getName().str() << "', function '" << func->getName().str()
+            << "' (if you see this as a regular user, this is a bug and should be reported)" << std::endl;
+    }
+
+    m_builder.CreateRetVoid();
+
+    if (llvm::verifyFunction(*func, &llvm::errs())) {
+        llvm::errs() << "error: LLVM function verficiation failed!\n";
+        llvm::errs() << "script hat ID: " << m_id << "\n";
+    }
 
 #ifdef PRINT_LLVM_IR
     std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl;
@@ -414,36 +431,6 @@ void LLVMCodeBuilder::initTypes()
     m_valueDataType->setBody({ unionType, valueType, sizeType });
 }
 
-llvm::Function *LLVMCodeBuilder::beginFunction(size_t index)
-{
-    // size_t f#(Target *)
-    llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder.getInt64Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
-    llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f" + std::to_string(index), m_module.get());
-
-    llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
-    m_builder.SetInsertPoint(entry);
-
-    return func;
-}
-
-void LLVMCodeBuilder::endFunction(llvm::Function *func, size_t index)
-{
-    if (!m_tmpRegs.empty()) {
-        std::cout
-            << "warning: " << m_tmpRegs.size() << " registers were leaked by script '" << m_module->getName().str() << "', function '" << func->getName().str()
-            << "' (if you see this as a regular user, this is a bug and should be reported)" << std::endl;
-    }
-
-    // Return next function index
-    m_builder.CreateRet(m_builder.getInt64(index + 1));
-
-    if (llvm::verifyFunction(*func, &llvm::errs())) {
-        llvm::errs() << "error: LLVM function verficiation failed!\n";
-        llvm::errs() << "script hat ID: " << m_id << "\n";
-        llvm::errs() << "function name: " << func->getName().data() << "\n";
-    }
-}
-
 void LLVMCodeBuilder::freeHeap()
 {
     // Free dynamically allocated memory
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 78923bcb..0eba59f2 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -99,8 +99,6 @@ class LLVMCodeBuilder : public ICodeBuilder
         };
 
         void initTypes();
-        llvm::Function *beginFunction(size_t index);
-        void endFunction(llvm::Function *func, size_t index);
 
         void freeHeap();
         llvm::Value *castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
diff --git a/src/dev/engine/internal/llvmexecutablecode.cpp b/src/dev/engine/internal/llvmexecutablecode.cpp
index c38fe784..6e40cb4d 100644
--- a/src/dev/engine/internal/llvmexecutablecode.cpp
+++ b/src/dev/engine/internal/llvmexecutablecode.cpp
@@ -31,44 +31,33 @@ LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module) :
     }
 
     // Lookup functions
-    size_t i = 0;
-
-    while (true) {
-        auto func = m_jit->get()->lookup("f" + std::to_string(i));
-
-        if (func)
-            m_functions.push_back((FunctionType)(func->getValue()));
-        else {
-            // Ignore error
-            llvm::consumeError(func.takeError());
-            break;
-        }
-
-        i++;
-    }
+    m_mainFunction = (MainFunctionType)lookupFunction("f");
+    assert(m_mainFunction);
 }
 
 void LLVMExecutableCode::run(ExecutionContext *context)
 {
     LLVMExecutionContext *ctx = getContext(context);
 
-    if (ctx->pos() < m_functions.size())
-        ctx->setPos(m_functions[ctx->pos()](context->target()));
+    if (!ctx->finished) {
+        m_mainFunction(context->target());
+        ctx->finished = true;
+    }
 }
 
 void LLVMExecutableCode::kill(ExecutionContext *context)
 {
-    getContext(context)->setPos(m_functions.size());
+    getContext(context)->finished = true;
 }
 
 void LLVMExecutableCode::reset(ExecutionContext *context)
 {
-    getContext(context)->setPos(0);
+    getContext(context)->finished = false;
 }
 
 bool LLVMExecutableCode::isFinished(ExecutionContext *context) const
 {
-    return getContext(context)->pos() >= m_functions.size();
+    return getContext(context)->finished;
 }
 
 void LLVMExecutableCode::promise()
@@ -84,6 +73,18 @@ std::shared_ptr<ExecutionContext> LLVMExecutableCode::createExecutionContext(Tar
     return std::make_shared<LLVMExecutionContext>(target);
 }
 
+uint64_t LLVMExecutableCode::lookupFunction(const std::string &name)
+{
+    auto func = m_jit->get()->lookup(name);
+
+    if (func)
+        return func->getValue();
+    else {
+        llvm::errs() << "error: failed to lookup LLVM function: " << toString(func.takeError()) << "\n";
+        return 0;
+    }
+}
+
 LLVMExecutionContext *LLVMExecutableCode::getContext(ExecutionContext *context)
 {
     assert(dynamic_cast<LLVMExecutionContext *>(context));
diff --git a/src/dev/engine/internal/llvmexecutablecode.h b/src/dev/engine/internal/llvmexecutablecode.h
index 312ef597..ce9fd371 100644
--- a/src/dev/engine/internal/llvmexecutablecode.h
+++ b/src/dev/engine/internal/llvmexecutablecode.h
@@ -30,14 +30,16 @@ class LLVMExecutableCode : public ExecutableCode
         std::shared_ptr<ExecutionContext> createExecutionContext(Target *target) const override;
 
     private:
-        using FunctionType = size_t (*)(Target *);
+        uint64_t lookupFunction(const std::string &name);
+
+        using MainFunctionType = size_t (*)(Target *);
 
         static LLVMExecutionContext *getContext(ExecutionContext *context);
 
         std::unique_ptr<llvm::LLVMContext> m_ctx;
         llvm::Expected<std::unique_ptr<llvm::orc::LLJIT>> m_jit;
 
-        std::vector<FunctionType> m_functions;
+        MainFunctionType m_mainFunction;
 };
 
 } // namespace libscratchcpp
diff --git a/src/dev/engine/internal/llvmexecutioncontext.cpp b/src/dev/engine/internal/llvmexecutioncontext.cpp
index 5200d9fb..b399541d 100644
--- a/src/dev/engine/internal/llvmexecutioncontext.cpp
+++ b/src/dev/engine/internal/llvmexecutioncontext.cpp
@@ -8,13 +8,3 @@ LLVMExecutionContext::LLVMExecutionContext(Target *target) :
     ExecutionContext(target)
 {
 }
-
-size_t LLVMExecutionContext::pos() const
-{
-    return m_pos;
-}
-
-void LLVMExecutionContext::setPos(size_t newPos)
-{
-    m_pos = newPos;
-}
diff --git a/src/dev/engine/internal/llvmexecutioncontext.h b/src/dev/engine/internal/llvmexecutioncontext.h
index 98f91554..e789aeea 100644
--- a/src/dev/engine/internal/llvmexecutioncontext.h
+++ b/src/dev/engine/internal/llvmexecutioncontext.h
@@ -12,11 +12,7 @@ class LLVMExecutionContext : public ExecutionContext
     public:
         LLVMExecutionContext(Target *target);
 
-        size_t pos() const;
-        void setPos(size_t newPos);
-
-    private:
-        size_t m_pos = 0;
+        bool finished = false; // TODO: Remove this
 };
 
 } // namespace libscratchcpp
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 642a0cb2..1c2048e4 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -189,7 +189,7 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
-TEST_F(LLVMCodeBuilderTest, Yield)
+/*TEST_F(LLVMCodeBuilderTest, Yield)
 {
     m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
 
@@ -234,7 +234,7 @@ TEST_F(LLVMCodeBuilderTest, Yield)
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2);
     ASSERT_TRUE(code->isFinished(ctx.get()));
-}
+}*/
 
 TEST_F(LLVMCodeBuilderTest, IfStatement)
 {
diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
index 95993e0b..7c5e818b 100644
--- a/test/dev/llvm/llvmexecutablecode_test.cpp
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -25,22 +25,18 @@ class LLVMExecutableCodeTest : public testing::Test
             llvm::InitializeNativeTargetAsmParser();
         }
 
-        llvm::Function *beginFunction(size_t index)
+        llvm::Function *beginFunction()
         {
-            // size_t f#(Target *)
-            llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder->getInt64Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
-            llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f" + std::to_string(index), m_module.get());
+            // void f(Target *)
+            llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder->getVoidTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+            llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get());
 
             llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
             m_builder->SetInsertPoint(entry);
             return func;
         }
 
-        void endFunction(size_t index)
-        {
-            // Return next function index
-            m_builder->CreateRet(m_builder->getInt64(index + 1));
-        }
+        void endFunction() { m_builder->CreateRetVoid(); }
 
         void addTestFunction(llvm::Function *mainFunc)
         {
@@ -69,6 +65,8 @@ class LLVMExecutableCodeTest : public testing::Test
 
 TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
 {
+    beginFunction();
+    endFunction();
     LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
     ASSERT_TRUE(ctx);
@@ -76,27 +74,11 @@ TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
     ASSERT_TRUE(dynamic_cast<LLVMExecutionContext *>(ctx.get()));
 }
 
-TEST_F(LLVMExecutableCodeTest, NoFunctions)
-{
-    LLVMExecutableCode code(std::move(m_module));
-    auto ctx = code.createExecutionContext(&m_target);
-    ASSERT_TRUE(code.isFinished(ctx.get()));
-
-    code.run(ctx.get());
-    ASSERT_TRUE(code.isFinished(ctx.get()));
-
-    code.kill(ctx.get());
-    ASSERT_TRUE(code.isFinished(ctx.get()));
-
-    code.reset(ctx.get());
-    ASSERT_TRUE(code.isFinished(ctx.get()));
-}
-
-TEST_F(LLVMExecutableCodeTest, SingleFunction)
+TEST_F(LLVMExecutableCodeTest, MainFunction)
 {
-    auto f = beginFunction(0);
+    auto f = beginFunction();
     addTestFunction(f);
-    endFunction(0);
+    endFunction();
 
     LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
@@ -134,26 +116,3 @@ TEST_F(LLVMExecutableCodeTest, SingleFunction)
     ASSERT_TRUE(code.isFinished(anotherCtx.get()));
     ASSERT_FALSE(code.isFinished(ctx.get()));
 }
-
-TEST_F(LLVMExecutableCodeTest, MultipleFunctions)
-{
-    static const int count = 5;
-
-    for (int i = 0; i < count; i++) {
-        auto f = beginFunction(i);
-        addTestFunction(f);
-        endFunction(i);
-    }
-
-    LLVMExecutableCode code(std::move(m_module));
-    auto ctx = code.createExecutionContext(&m_target);
-    ASSERT_FALSE(code.isFinished(ctx.get()));
-
-    for (int i = 0; i < count; i++) {
-        ASSERT_FALSE(code.isFinished(ctx.get()));
-        EXPECT_CALL(m_mock, f(&m_target));
-        code.run(ctx.get());
-    }
-
-    ASSERT_TRUE(code.isFinished(ctx.get()));
-}
diff --git a/test/dev/llvm/llvmexecutioncontext_test.cpp b/test/dev/llvm/llvmexecutioncontext_test.cpp
index 4b715c2c..8a9d3e4b 100644
--- a/test/dev/llvm/llvmexecutioncontext_test.cpp
+++ b/test/dev/llvm/llvmexecutioncontext_test.cpp
@@ -10,15 +10,3 @@ TEST(LLVMExecutionContextTest, Constructor)
     LLVMExecutionContext ctx(&target);
     ASSERT_EQ(ctx.target(), &target);
 }
-
-TEST(LLVMExecutionContextTest, Pos)
-{
-    LLVMExecutionContext ctx(nullptr);
-    ASSERT_EQ(ctx.pos(), 0);
-
-    ctx.setPos(1);
-    ASSERT_EQ(ctx.pos(), 1);
-
-    ctx.setPos(356);
-    ASSERT_EQ(ctx.pos(), 356);
-}

From 6ffc3a74933e9423432862ce3c49780b88165fcc Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 12 Oct 2024 19:46:16 +0200
Subject: [PATCH 52/94] Update random backdrop tests

---
 test/blocks/looks_blocks_test.cpp | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp
index ba2318bc..041c32b8 100644
--- a/test/blocks/looks_blocks_test.cpp
+++ b/test/blocks/looks_blocks_test.cpp
@@ -1992,7 +1992,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl)
     LooksBlocks::rng = &rng;
 
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(3)->broadcast(), &thread, false));
-    EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(3));
+    EXPECT_CALL(rng, randintExcept(0, 5, 0)).WillOnce(Return(3));
     stage.setCostumeIndex(0);
     vm->setBytecode(bytecode15);
     vm->run();
@@ -2001,7 +2001,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl)
     ASSERT_EQ(stage.costumeIndex(), 3);
 
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(5)->broadcast(), &thread, false));
-    EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(5));
+    EXPECT_CALL(rng, randintExcept(0, 5, 3)).WillOnce(Return(5));
     vm->reset();
     vm->run();
 
@@ -2011,7 +2011,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl)
     stage.addCostume(std::make_shared<Costume>("random backdrop", "b7", "svg"));
 
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(6)->broadcast(), &thread, false));
-    EXPECT_CALL(rng, randint).Times(0);
+    EXPECT_CALL(rng, randintExcept).Times(0);
     vm->reset();
     vm->run();
 
@@ -2337,7 +2337,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl)
     LooksBlocks::rng = &rng;
 
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(3)->broadcast(), &thread, true));
-    EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(3));
+    EXPECT_CALL(rng, randintExcept(0, 5, 0)).WillOnce(Return(3));
     stage.setCostumeIndex(0);
     vm->resolvePromise();
     vm->setBytecode(bytecode15);
@@ -2349,7 +2349,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl)
     ASSERT_EQ(stage.costumeIndex(), 3);
 
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(5)->broadcast(), &thread, true));
-    EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(5));
+    EXPECT_CALL(rng, randintExcept(0, 5, 3)).WillOnce(Return(5));
     vm->reset();
     vm->run();
     vm->resolvePromise();
@@ -2361,7 +2361,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl)
     stage.addCostume(std::make_shared<Costume>("random backdrop", "b7", "svg"));
 
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(6)->broadcast(), &thread, true));
-    EXPECT_CALL(rng, randint).Times(0);
+    EXPECT_CALL(rng, randintExcept).Times(0);
     vm->reset();
     vm->run();
     vm->resolvePromise();
@@ -2488,7 +2488,7 @@ TEST_F(LooksBlocksTest, RandomBackdrop)
     RandomGeneratorMock rng;
     LooksBlocks::rng = &rng;
 
-    EXPECT_CALL(rng, randint).Times(0);
+    EXPECT_CALL(rng, randintExcept).Times(0);
     vm->run();
 
     ASSERT_EQ(vm->registerCount(), 0);
@@ -2496,8 +2496,9 @@ TEST_F(LooksBlocksTest, RandomBackdrop)
     stage.addCostume(std::make_shared<Costume>("backdrop1", "b1", "svg"));
     stage.addCostume(std::make_shared<Costume>("backdrop2", "b2", "svg"));
     stage.addCostume(std::make_shared<Costume>("backdrop3", "b3", "svg"));
+    stage.setCostumeIndex(0);
 
-    EXPECT_CALL(rng, randint(0, 2)).WillOnce(Return(1));
+    EXPECT_CALL(rng, randintExcept(0, 2, 0)).WillOnce(Return(1));
     EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(1)->broadcast(), &thread, false));
     vm->reset();
     vm->run();

From 763e82119444e8b1d182a3c91402ae8234b92102 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 12 Oct 2024 20:34:32 +0200
Subject: [PATCH 53/94] RandomGenerator: Optimize randintExcept()

---
 src/engine/internal/randomgenerator.cpp       | 23 ++++++++++---
 test/randomgenerator/randomgenerator_test.cpp | 32 +++++++++++++++++++
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/src/engine/internal/randomgenerator.cpp b/src/engine/internal/randomgenerator.cpp
index 84cca982..e9e7c572 100644
--- a/src/engine/internal/randomgenerator.cpp
+++ b/src/engine/internal/randomgenerator.cpp
@@ -47,10 +47,23 @@ long RandomGenerator::randintExcept(long start, long end, long except) const
     if (start > end) {
         std::swap(start, end);
     }
-    std::uniform_int_distribution<long> distribution(start, end);
-    long value = distribution(*m_generator);
-    if (value == except) {
-        return randintExcept(start, end, except);
+
+    if (except < start || except > end) {
+        return randint(start, end);
+    } else if (start == end) {
+        return start;
+    } else if (end - start == 1) {
+        if (except == start)
+            return end;
+        else
+            return start;
+    }
+
+    if (randint(0, 1) == 0) {
+        std::uniform_int_distribution<long> distribution(start, except - 1);
+        return distribution(*m_generator);
+    } else {
+        std::uniform_int_distribution<long> distribution(except + 1, end);
+        return distribution(*m_generator);
     }
-    return value;
 }
diff --git a/test/randomgenerator/randomgenerator_test.cpp b/test/randomgenerator/randomgenerator_test.cpp
index 1a7fedba..06b9dda0 100644
--- a/test/randomgenerator/randomgenerator_test.cpp
+++ b/test/randomgenerator/randomgenerator_test.cpp
@@ -64,4 +64,36 @@ TEST(RandomGeneratorTest, RandIntExcept)
         ASSERT_LE(num, 5);
         ASSERT_NE(num, 2);
     }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(5, 8, 2);
+        ASSERT_GE(num, 5);
+        ASSERT_LE(num, 8);
+    }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(5, 8, 10);
+        ASSERT_GE(num, 5);
+        ASSERT_LE(num, 8);
+    }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(2, 2, 2);
+        ASSERT_EQ(num, 2);
+    }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(2, 2, 5);
+        ASSERT_EQ(num, 2);
+    }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(1, 2, 1);
+        ASSERT_EQ(num, 2);
+    }
+
+    for (int i = 0; i < 25; i++) {
+        num = rng->randintExcept(1, 2, 2);
+        ASSERT_EQ(num, 1);
+    }
 }

From 2b031c7c383705cfddcb1c351d2b7d2caed15f27 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 12 Oct 2024 17:31:16 +0200
Subject: [PATCH 54/94] Implement suspendable code

---
 src/dev/engine/compiler.cpp                   |   2 +-
 .../engine/internal/codebuilderfactory.cpp    |   4 +-
 src/dev/engine/internal/codebuilderfactory.h  |   2 +-
 src/dev/engine/internal/icodebuilderfactory.h |   2 +-
 src/dev/engine/internal/llvmcodebuilder.cpp   | 157 ++++++++++++-
 src/dev/engine/internal/llvmcodebuilder.h     |  21 +-
 .../engine/internal/llvmexecutablecode.cpp    |  32 ++-
 src/dev/engine/internal/llvmexecutablecode.h  |   4 +-
 .../engine/internal/llvmexecutioncontext.cpp  |  20 ++
 .../engine/internal/llvmexecutioncontext.h    |  10 +-
 test/dev/compiler/compiler_test.cpp           |   2 +-
 test/dev/llvm/llvmcodebuilder_test.cpp        | 209 ++++++++++++++----
 test/dev/llvm/llvmexecutablecode_test.cpp     |  37 +++-
 test/mocks/codebuilderfactorymock.h           |   2 +-
 test/scratch_classes/inputvalue_test.cpp      |   2 +-
 15 files changed, 426 insertions(+), 80 deletions(-)

diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index b3f4613f..355fec48 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -38,7 +38,7 @@ std::shared_ptr<libscratchcpp::Block> Compiler::block() const
 /*! Compiles the script starting with the given block. */
 std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
 {
-    impl->builder = impl->builderFactory->create(startBlock->id());
+    impl->builder = impl->builderFactory->create(startBlock->id(), false);
     impl->substackTree.clear();
     impl->substackHit = false;
     impl->warp = false;
diff --git a/src/dev/engine/internal/codebuilderfactory.cpp b/src/dev/engine/internal/codebuilderfactory.cpp
index fbda1929..5f955bdc 100644
--- a/src/dev/engine/internal/codebuilderfactory.cpp
+++ b/src/dev/engine/internal/codebuilderfactory.cpp
@@ -12,7 +12,7 @@ std::shared_ptr<CodeBuilderFactory> CodeBuilderFactory::instance()
     return m_instance;
 }
 
-std::shared_ptr<ICodeBuilder> CodeBuilderFactory::create(const std::string &id) const
+std::shared_ptr<ICodeBuilder> CodeBuilderFactory::create(const std::string &id, bool warp) const
 {
-    return std::make_shared<LLVMCodeBuilder>(id);
+    return std::make_shared<LLVMCodeBuilder>(id, warp);
 }
diff --git a/src/dev/engine/internal/codebuilderfactory.h b/src/dev/engine/internal/codebuilderfactory.h
index ea12b288..8de574ab 100644
--- a/src/dev/engine/internal/codebuilderfactory.h
+++ b/src/dev/engine/internal/codebuilderfactory.h
@@ -11,7 +11,7 @@ class CodeBuilderFactory : public ICodeBuilderFactory
 {
     public:
         static std::shared_ptr<CodeBuilderFactory> instance();
-        std::shared_ptr<ICodeBuilder> create(const std::string &id) const override;
+        std::shared_ptr<ICodeBuilder> create(const std::string &id, bool warp) const override;
 
     private:
         static std::shared_ptr<CodeBuilderFactory> m_instance;
diff --git a/src/dev/engine/internal/icodebuilderfactory.h b/src/dev/engine/internal/icodebuilderfactory.h
index 9bfb203b..4438e5f8 100644
--- a/src/dev/engine/internal/icodebuilderfactory.h
+++ b/src/dev/engine/internal/icodebuilderfactory.h
@@ -14,7 +14,7 @@ class ICodeBuilderFactory
     public:
         virtual ~ICodeBuilderFactory() { }
 
-        virtual std::shared_ptr<ICodeBuilder> create(const std::string &id) const = 0;
+        virtual std::shared_ptr<ICodeBuilder> create(const std::string &id, bool warp) const = 0;
 };
 
 } // namespace libscratchcpp
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 47626f1d..70b60963 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -3,6 +3,7 @@
 #include <llvm/Support/TargetSelect.h>
 #include <llvm/IR/Verifier.h>
 #include <llvm/ExecutionEngine/Orc/LLJIT.h>
+#include <llvm/Passes/PassBuilder.h>
 
 #include "llvmcodebuilder.h"
 #include "llvmexecutablecode.h"
@@ -15,10 +16,12 @@ static std::unordered_map<ValueType, Compiler::StaticType> TYPE_MAP = {
     { ValueType::NegativeInfinity, Compiler::StaticType::Number }, { ValueType::NaN, Compiler::StaticType::Number }
 };
 
-LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id) :
+LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id, bool warp) :
     m_id(id),
     m_module(std::make_unique<llvm::Module>(id, m_ctx)),
-    m_builder(m_ctx)
+    m_builder(m_ctx),
+    m_defaultWarp(warp),
+    m_warp(warp)
 {
     llvm::InitializeNativeTarget();
     llvm::InitializeNativeTargetAsmPrinter();
@@ -32,13 +35,20 @@ LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id) :
 std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 {
     // Create function
-    // void f(Target *)
-    llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder.getVoidTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+    // void *f(Target *)
+    llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+    llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, pointerType, false);
     llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get());
 
     llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
     m_builder.SetInsertPoint(entry);
 
+    // Init coroutine
+    Coroutine coro;
+
+    if (!m_warp)
+        coro = initCoroutine(func);
+
     std::vector<IfStatement> ifStatements;
     std::vector<Loop> loops;
     m_heap.clear();
@@ -74,8 +84,17 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
             }
 
             case Step::Type::Yield:
-                freeHeap();
-                // TODO: Implement yielding
+                if (!m_warp) {
+                    freeHeap();
+                    llvm::BasicBlock *resumeBranch = llvm::BasicBlock::Create(m_ctx, "", func);
+                    llvm::Value *noneToken = llvm::ConstantTokenNone::get(m_ctx);
+                    llvm::Value *suspendResult = m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_suspend), { noneToken, m_builder.getInt1(false) });
+                    llvm::SwitchInst *sw = m_builder.CreateSwitch(suspendResult, coro.suspend, 2);
+                    sw->addCase(m_builder.getInt8(0), resumeBranch);
+                    sw->addCase(m_builder.getInt8(1), coro.cleanup);
+                    m_builder.SetInsertPoint(resumeBranch);
+                }
+
                 break;
 
             case Step::Type::BeginIf: {
@@ -277,6 +296,17 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 
     freeHeap();
 
+    // Add final suspend point
+    if (!m_warp) {
+        llvm::BasicBlock *endBranch = llvm::BasicBlock::Create(m_ctx, "end", func);
+        llvm::Value *suspendResult =
+            m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_suspend), { llvm::ConstantTokenNone::get(m_ctx), m_builder.getInt1(true) });
+        llvm::SwitchInst *sw = m_builder.CreateSwitch(suspendResult, coro.suspend, 2);
+        sw->addCase(m_builder.getInt8(0), endBranch);
+        sw->addCase(m_builder.getInt8(1), coro.cleanup);
+        m_builder.SetInsertPoint(endBranch);
+    }
+
     // End and verify the function
     if (!m_tmpRegs.empty()) {
         std::cout
@@ -284,13 +314,41 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
             << "' (if you see this as a regular user, this is a bug and should be reported)" << std::endl;
     }
 
-    m_builder.CreateRetVoid();
+    if (m_warp)
+        m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType));
+    else
+        m_builder.CreateBr(coro.cleanup);
 
-    if (llvm::verifyFunction(*func, &llvm::errs())) {
-        llvm::errs() << "error: LLVM function verficiation failed!\n";
-        llvm::errs() << "script hat ID: " << m_id << "\n";
+    verifyFunction(func);
+
+    // Create resume function
+    // bool resume(void *)
+    funcType = llvm::FunctionType::get(m_builder.getInt1Ty(), pointerType, false);
+    func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "resume", m_module.get());
+
+    entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
+    m_builder.SetInsertPoint(entry);
+
+    if (m_warp)
+        m_builder.CreateRet(m_builder.getInt1(true));
+    else {
+        llvm::Value *coroHandle = func->getArg(0);
+        m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_resume), { coroHandle });
+        llvm::Value *done = m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_done), { coroHandle });
+        m_builder.CreateRet(done);
     }
 
+    verifyFunction(func);
+
+#ifdef PRINT_LLVM_IR
+    std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl;
+    m_module->print(llvm::outs(), nullptr);
+    std::cout << "==============" << std::endl << std::endl;
+#endif
+
+    // Optimize
+    optimize();
+
 #ifdef PRINT_LLVM_IR
     std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl;
     m_module->print(llvm::outs(), nullptr);
@@ -396,6 +454,9 @@ void LLVMCodeBuilder::beginLoopCondition()
 
 void LLVMCodeBuilder::endLoop()
 {
+    if (!m_warp)
+        m_steps.push_back(Step(Step::Type::Yield));
+
     m_steps.push_back(Step(Step::Type::EndLoop));
 }
 
@@ -431,6 +492,82 @@ void LLVMCodeBuilder::initTypes()
     m_valueDataType->setBody({ unionType, valueType, sizeType });
 }
 
+LLVMCodeBuilder::Coroutine LLVMCodeBuilder::initCoroutine(llvm::Function *func)
+{
+    // Set presplitcoroutine attribute
+    func->setPresplitCoroutine();
+
+    // Coroutine intrinsics
+    llvm::Function *coroId = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_id);
+    llvm::Function *coroSize = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_size, m_builder.getInt64Ty());
+    llvm::Function *coroBegin = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_begin);
+    llvm::Function *coroEnd = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_end);
+    llvm::Function *coroFree = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_free);
+
+    // Init coroutine
+    Coroutine coro;
+    llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+    llvm::Constant *nullPointer = llvm::ConstantPointerNull::get(pointerType);
+    llvm::Value *coroIdRet = m_builder.CreateCall(coroId, { m_builder.getInt32(8), nullPointer, nullPointer, nullPointer });
+
+    // Allocate memory
+    llvm::Value *coroSizeRet = m_builder.CreateCall(coroSize, std::nullopt, "size");
+    llvm::Function *mallocFunc = llvm::Function::Create(llvm::FunctionType::get(pointerType, { m_builder.getInt64Ty() }, false), llvm::Function::ExternalLinkage, "malloc", m_module.get());
+    llvm::Value *alloc = m_builder.CreateCall(mallocFunc, coroSizeRet, "mem");
+
+    // Begin
+    coro.handle = m_builder.CreateCall(coroBegin, { coroIdRet, alloc });
+    llvm::BasicBlock *entry = m_builder.GetInsertBlock();
+
+    // Create suspend branch
+    coro.suspend = llvm::BasicBlock::Create(m_ctx, "suspend", func);
+    m_builder.SetInsertPoint(coro.suspend);
+    m_builder.CreateCall(coroEnd, { coro.handle, m_builder.getInt1(false), llvm::ConstantTokenNone::get(m_ctx) });
+    m_builder.CreateRet(coro.handle);
+
+    // Create free branch
+    llvm::BasicBlock *freeBranch = llvm::BasicBlock::Create(m_ctx, "free", func);
+    m_builder.SetInsertPoint(freeBranch);
+    m_builder.CreateFree(alloc);
+    m_builder.CreateBr(coro.suspend);
+
+    // Create cleanup branch
+    coro.cleanup = llvm::BasicBlock::Create(m_ctx, "cleanup", func);
+    m_builder.SetInsertPoint(coro.cleanup);
+    llvm::Value *mem = m_builder.CreateCall(coroFree, { coroIdRet, coro.handle });
+    llvm::Value *needFree = m_builder.CreateIsNotNull(mem);
+    m_builder.CreateCondBr(needFree, freeBranch, coro.suspend);
+
+    m_builder.SetInsertPoint(entry);
+    return coro;
+}
+
+void LLVMCodeBuilder::verifyFunction(llvm::Function *func)
+{
+    if (llvm::verifyFunction(*func, &llvm::errs())) {
+        llvm::errs() << "error: LLVM function verficiation failed!\n";
+        llvm::errs() << "script hat ID: " << m_id << "\n";
+    }
+}
+
+void LLVMCodeBuilder::optimize()
+{
+    llvm::PassBuilder passBuilder;
+    llvm::LoopAnalysisManager loopAnalysisManager;
+    llvm::FunctionAnalysisManager functionAnalysisManager;
+    llvm::CGSCCAnalysisManager cGSCCAnalysisManager;
+    llvm::ModuleAnalysisManager moduleAnalysisManager;
+
+    passBuilder.registerModuleAnalyses(moduleAnalysisManager);
+    passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager);
+    passBuilder.registerFunctionAnalyses(functionAnalysisManager);
+    passBuilder.registerLoopAnalyses(loopAnalysisManager);
+    passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager);
+
+    llvm::ModulePassManager modulePassManager = passBuilder.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3);
+    modulePassManager.run(*m_module, moduleAnalysisManager);
+}
+
 void LLVMCodeBuilder::freeHeap()
 {
     // Free dynamically allocated memory
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 0eba59f2..c61404fb 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -17,7 +17,7 @@ class Target;
 class LLVMCodeBuilder : public ICodeBuilder
 {
     public:
-        LLVMCodeBuilder(const std::string &id);
+        LLVMCodeBuilder(const std::string &id, bool warp);
 
         std::shared_ptr<ExecutableCode> finalize() override;
 
@@ -98,8 +98,25 @@ class LLVMCodeBuilder : public ICodeBuilder
                 llvm::BasicBlock *afterLoop = nullptr;
         };
 
+        struct Coroutine
+        {
+                llvm::Value *handle = nullptr;
+                llvm::BasicBlock *suspend = nullptr;
+                llvm::BasicBlock *cleanup = nullptr;
+        };
+
+        struct Procedure
+        {
+                // TODO: Implement procedures
+                bool warp = false;
+        };
+
         void initTypes();
 
+        Coroutine initCoroutine(llvm::Function *func);
+        void verifyFunction(llvm::Function *func);
+        void optimize();
+
         void freeHeap();
         llvm::Value *castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
         llvm::Value *castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
@@ -134,6 +151,8 @@ class LLVMCodeBuilder : public ICodeBuilder
         std::vector<Value> m_constValues;
         std::vector<std::vector<std::shared_ptr<Register>>> m_regs;
         std::vector<std::shared_ptr<Register>> m_tmpRegs;
+        bool m_defaultWarp = false;
+        bool m_warp = false;
 
         std::vector<llvm::Value *> m_heap;
 
diff --git a/src/dev/engine/internal/llvmexecutablecode.cpp b/src/dev/engine/internal/llvmexecutablecode.cpp
index 6e40cb4d..6c2059ba 100644
--- a/src/dev/engine/internal/llvmexecutablecode.cpp
+++ b/src/dev/engine/internal/llvmexecutablecode.cpp
@@ -33,31 +33,51 @@ LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr<llvm::Module> module) :
     // Lookup functions
     m_mainFunction = (MainFunctionType)lookupFunction("f");
     assert(m_mainFunction);
+    m_resumeFunction = (ResumeFunctionType)lookupFunction("resume");
+    assert(m_resumeFunction);
 }
 
 void LLVMExecutableCode::run(ExecutionContext *context)
 {
     LLVMExecutionContext *ctx = getContext(context);
 
-    if (!ctx->finished) {
-        m_mainFunction(context->target());
-        ctx->finished = true;
+    if (ctx->finished())
+        return;
+
+    if (ctx->coroutineHandle()) {
+        bool done = m_resumeFunction(ctx->coroutineHandle());
+
+        if (done)
+            ctx->setCoroutineHandle(nullptr);
+
+        ctx->setFinished(done);
+    } else {
+        void *handle = m_mainFunction(context->target());
+
+        if (!handle)
+            ctx->setFinished(true);
+
+        ctx->setCoroutineHandle(handle);
     }
 }
 
 void LLVMExecutableCode::kill(ExecutionContext *context)
 {
-    getContext(context)->finished = true;
+    LLVMExecutionContext *ctx = getContext(context);
+    ctx->setCoroutineHandle(nullptr);
+    ctx->setFinished(true);
 }
 
 void LLVMExecutableCode::reset(ExecutionContext *context)
 {
-    getContext(context)->finished = false;
+    LLVMExecutionContext *ctx = getContext(context);
+    ctx->setCoroutineHandle(nullptr);
+    ctx->setFinished(false);
 }
 
 bool LLVMExecutableCode::isFinished(ExecutionContext *context) const
 {
-    return getContext(context)->finished;
+    return getContext(context)->finished();
 }
 
 void LLVMExecutableCode::promise()
diff --git a/src/dev/engine/internal/llvmexecutablecode.h b/src/dev/engine/internal/llvmexecutablecode.h
index ce9fd371..4094fee8 100644
--- a/src/dev/engine/internal/llvmexecutablecode.h
+++ b/src/dev/engine/internal/llvmexecutablecode.h
@@ -32,7 +32,8 @@ class LLVMExecutableCode : public ExecutableCode
     private:
         uint64_t lookupFunction(const std::string &name);
 
-        using MainFunctionType = size_t (*)(Target *);
+        using MainFunctionType = void *(*)(Target *);
+        using ResumeFunctionType = bool (*)(void *);
 
         static LLVMExecutionContext *getContext(ExecutionContext *context);
 
@@ -40,6 +41,7 @@ class LLVMExecutableCode : public ExecutableCode
         llvm::Expected<std::unique_ptr<llvm::orc::LLJIT>> m_jit;
 
         MainFunctionType m_mainFunction;
+        ResumeFunctionType m_resumeFunction;
 };
 
 } // namespace libscratchcpp
diff --git a/src/dev/engine/internal/llvmexecutioncontext.cpp b/src/dev/engine/internal/llvmexecutioncontext.cpp
index b399541d..5934a90a 100644
--- a/src/dev/engine/internal/llvmexecutioncontext.cpp
+++ b/src/dev/engine/internal/llvmexecutioncontext.cpp
@@ -8,3 +8,23 @@ LLVMExecutionContext::LLVMExecutionContext(Target *target) :
     ExecutionContext(target)
 {
 }
+
+void *LLVMExecutionContext::coroutineHandle() const
+{
+    return m_coroutineHandle;
+}
+
+void LLVMExecutionContext::setCoroutineHandle(void *newCoroutineHandle)
+{
+    m_coroutineHandle = newCoroutineHandle;
+}
+
+bool LLVMExecutionContext::finished() const
+{
+    return m_finished;
+}
+
+void LLVMExecutionContext::setFinished(bool newFinished)
+{
+    m_finished = newFinished;
+}
diff --git a/src/dev/engine/internal/llvmexecutioncontext.h b/src/dev/engine/internal/llvmexecutioncontext.h
index e789aeea..c6357819 100644
--- a/src/dev/engine/internal/llvmexecutioncontext.h
+++ b/src/dev/engine/internal/llvmexecutioncontext.h
@@ -12,7 +12,15 @@ class LLVMExecutionContext : public ExecutionContext
     public:
         LLVMExecutionContext(Target *target);
 
-        bool finished = false; // TODO: Remove this
+        void *coroutineHandle() const;
+        void setCoroutineHandle(void *newCoroutineHandle);
+
+        bool finished() const;
+        void setFinished(bool newFinished);
+
+    private:
+        void *m_coroutineHandle = nullptr;
+        bool m_finished = false;
 };
 
 } // namespace libscratchcpp
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index 9a461bb5..e96d2cc5 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -41,7 +41,7 @@ class CompilerTest : public testing::Test
         {
             ASSERT_EQ(compiler.block(), nullptr);
             // TODO: Test warp
-            EXPECT_CALL(m_builderFactory, create(block->id())).WillOnce(Return(m_builder));
+            EXPECT_CALL(m_builderFactory, create(block->id(), false)).WillOnce(Return(m_builder));
             EXPECT_CALL(*m_builder, finalize()).WillOnce(Return(m_code));
             ASSERT_EQ(compiler.compile(block), m_code);
             ASSERT_EQ(compiler.block(), nullptr);
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 1c2048e4..656acdeb 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -15,52 +15,60 @@ class LLVMCodeBuilderTest : public testing::Test
     public:
         void SetUp() override
         {
-            m_builder = std::make_unique<LLVMCodeBuilder>("test");
             test_function(nullptr, nullptr); // force dependency
         }
 
+        void createBuilder(bool warp) { m_builder = std::make_unique<LLVMCodeBuilder>("test", warp); }
+
         std::unique_ptr<LLVMCodeBuilder> m_builder;
         TargetMock m_target; // NOTE: isStage() is used for call expectations
 };
 
 TEST_F(LLVMCodeBuilderTest, FunctionCalls)
 {
-    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
+    static const std::vector<bool> warpList = { false, true };
 
-    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
-    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+    for (bool warp : warpList) {
+        createBuilder(warp);
+        m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
 
-    m_builder->addConstValue("1");
-    m_builder->addFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String });
-    m_builder->addConstValue("2");
-    m_builder->addConstValue("3");
-    m_builder->addFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+        m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
-    m_builder->addConstValue("test");
-    m_builder->addConstValue("4");
-    m_builder->addConstValue("5");
-    m_builder->addFunctionCall("test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
-    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
-    auto code = m_builder->finalize();
-    auto ctx = code->createExecutionContext(&m_target);
+        m_builder->addConstValue("1");
+        m_builder->addFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String });
+        m_builder->addConstValue("2");
+        m_builder->addConstValue("3");
+        m_builder->addFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
 
-    static const std::string expected =
-        "no_args\n"
-        "no_args_ret\n"
-        "1_arg no_args_output\n"
-        "1_arg_ret 1\n"
-        "3_args 1_arg_output 2 3\n"
-        "3_args test 4 5\n"
-        "1_arg 3_args_output\n";
-
-    EXPECT_CALL(m_target, isStage()).Times(7);
-    testing::internal::CaptureStdout();
-    code->run(ctx.get());
-    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+        m_builder->addConstValue("test");
+        m_builder->addConstValue("4");
+        m_builder->addConstValue("5");
+        m_builder->addFunctionCall("test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        static const std::string expected =
+            "no_args\n"
+            "no_args_ret\n"
+            "1_arg no_args_output\n"
+            "1_arg_ret 1\n"
+            "3_args 1_arg_output 2 3\n"
+            "3_args test 4 5\n"
+            "1_arg 3_args_output\n";
+
+        EXPECT_CALL(m_target, isStage()).Times(7);
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+    }
 }
 
 TEST_F(LLVMCodeBuilderTest, ConstCasting)
 {
+    createBuilder(true);
+
     m_builder->addConstValue(5.2);
     m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number });
     m_builder->addConstValue("-24.156");
@@ -100,6 +108,8 @@ TEST_F(LLVMCodeBuilderTest, ConstCasting)
 
 TEST_F(LLVMCodeBuilderTest, RawValueCasting)
 {
+    createBuilder(true);
+
     // Number -> number
     m_builder->addConstValue(5.2);
     m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
@@ -189,26 +199,33 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
-/*TEST_F(LLVMCodeBuilderTest, Yield)
+TEST_F(LLVMCodeBuilderTest, Yield)
 {
-    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
+    auto build = [this]() {
+        m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
 
-    m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
-    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+        m_builder->addFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {});
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
-    m_builder->yield();
+        m_builder->yield();
 
-    m_builder->addConstValue("1");
-    m_builder->addFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String });
-    m_builder->addConstValue("2");
-    m_builder->addConstValue(3);
-    m_builder->addFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+        m_builder->addConstValue("1");
+        m_builder->addFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String });
+        m_builder->addConstValue("2");
+        m_builder->addConstValue(3);
+        m_builder->addFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+
+        m_builder->addConstValue("test");
+        m_builder->addConstValue("4");
+        m_builder->addConstValue("5");
+        m_builder->addFunctionCall("test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
+        m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+    };
+
+    // Without warp
+    createBuilder(false);
+    build();
 
-    m_builder->addConstValue("test");
-    m_builder->addConstValue("4");
-    m_builder->addConstValue("5");
-    m_builder->addFunctionCall("test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String });
-    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     auto code = m_builder->finalize();
     auto ctx = code->createExecutionContext(&m_target);
 
@@ -234,10 +251,32 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting)
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2);
     ASSERT_TRUE(code->isFinished(ctx.get()));
-}*/
+
+    // With warp
+    createBuilder(true);
+    build();
+    code = m_builder->finalize();
+    ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected =
+        "no_args\n"
+        "no_args_ret\n"
+        "1_arg no_args_output\n"
+        "1_arg_ret 1\n"
+        "3_args 1_arg_output 2 3\n"
+        "3_args test 4 5\n"
+        "1_arg 3_args_output\n";
+
+    EXPECT_CALL(m_target, isStage()).Times(7);
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
 
 TEST_F(LLVMCodeBuilderTest, IfStatement)
 {
+    createBuilder(true);
+
     // Without else branch (const condition)
     m_builder->addConstValue("true");
     m_builder->beginIfStatement();
@@ -405,8 +444,11 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
+// TODO: Write a test for count rounding
 TEST_F(LLVMCodeBuilderTest, RepeatLoop)
 {
+    createBuilder(true);
+
     // Const count
     m_builder->addConstValue("-5");
     m_builder->beginRepeatLoop();
@@ -489,10 +531,39 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
     testing::internal::CaptureStdout();
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+
+    // Yield
+    createBuilder(false);
+
+    m_builder->addConstValue(3);
+    m_builder->beginRepeatLoop();
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
+    m_builder->endLoop();
+
+    code = m_builder->finalize();
+    ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected1 = "no_args\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+
+    for (int i = 0; i < 3; i++) {
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
+        ASSERT_FALSE(code->isFinished(ctx.get()));
+    }
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_TRUE(testing::internal::GetCapturedStdout().empty());
+    ASSERT_TRUE(code->isFinished(ctx.get()));
 }
 
 TEST_F(LLVMCodeBuilderTest, WhileLoop)
 {
+    createBuilder(true);
+
     // Const condition
     m_builder->beginLoopCondition();
     m_builder->addConstValue("false");
@@ -566,10 +637,35 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop)
     testing::internal::CaptureStdout();
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+
+    // Yield
+    createBuilder(false);
+
+    m_builder->beginLoopCondition();
+    m_builder->addConstValue(true);
+    m_builder->beginWhileLoop();
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
+    m_builder->endLoop();
+
+    code = m_builder->finalize();
+    ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected1 = "no_args\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+
+    for (int i = 0; i < 10; i++) {
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
+        ASSERT_FALSE(code->isFinished(ctx.get()));
+    }
 }
 
 TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop)
 {
+    createBuilder(true);
+
     // Const condition
     m_builder->beginLoopCondition();
     m_builder->addConstValue("true");
@@ -646,4 +742,27 @@ TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop)
     testing::internal::CaptureStdout();
     code->run(ctx.get());
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+
+    // Yield
+    createBuilder(false);
+
+    m_builder->beginLoopCondition();
+    m_builder->addConstValue(false);
+    m_builder->beginRepeatUntilLoop();
+    m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
+    m_builder->endLoop();
+
+    code = m_builder->finalize();
+    ctx = code->createExecutionContext(&m_target);
+
+    static const std::string expected1 = "no_args\n";
+
+    EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false));
+
+    for (int i = 0; i < 10; i++) {
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
+        ASSERT_FALSE(code->isFinished(ctx.get()));
+    }
 }
diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp
index 7c5e818b..fea3f778 100644
--- a/test/dev/llvm/llvmexecutablecode_test.cpp
+++ b/test/dev/llvm/llvmexecutablecode_test.cpp
@@ -25,10 +25,13 @@ class LLVMExecutableCodeTest : public testing::Test
             llvm::InitializeNativeTargetAsmParser();
         }
 
-        llvm::Function *beginFunction()
+        inline llvm::Constant *nullPointer() { return llvm::ConstantPointerNull::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0)); }
+
+        llvm::Function *beginMainFunction()
         {
-            // void f(Target *)
-            llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder->getVoidTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+            // void *f(Target *)
+            llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+            llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, pointerType, false);
             llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get());
 
             llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
@@ -36,7 +39,18 @@ class LLVMExecutableCodeTest : public testing::Test
             return func;
         }
 
-        void endFunction() { m_builder->CreateRetVoid(); }
+        llvm::Function *beginResumeFunction()
+        {
+            // bool f(void *)
+            llvm::FunctionType *funcType = llvm::FunctionType::get(m_builder->getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false);
+            llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "resume", m_module.get());
+
+            llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func);
+            m_builder->SetInsertPoint(entry);
+            return func;
+        }
+
+        void endFunction(llvm::Value *ret) { m_builder->CreateRet(ret); }
 
         void addTestFunction(llvm::Function *mainFunc)
         {
@@ -65,8 +79,12 @@ class LLVMExecutableCodeTest : public testing::Test
 
 TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
 {
-    beginFunction();
-    endFunction();
+    beginMainFunction();
+    endFunction(nullPointer());
+
+    beginResumeFunction();
+    endFunction(m_builder->getInt1(true));
+
     LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
     ASSERT_TRUE(ctx);
@@ -76,9 +94,12 @@ TEST_F(LLVMExecutableCodeTest, CreateExecutionContext)
 
 TEST_F(LLVMExecutableCodeTest, MainFunction)
 {
-    auto f = beginFunction();
+    auto f = beginMainFunction();
     addTestFunction(f);
-    endFunction();
+    endFunction(nullPointer());
+
+    beginResumeFunction();
+    endFunction(m_builder->getInt1(true));
 
     LLVMExecutableCode code(std::move(m_module));
     auto ctx = code.createExecutionContext(&m_target);
diff --git a/test/mocks/codebuilderfactorymock.h b/test/mocks/codebuilderfactorymock.h
index d8a030c5..7bf04a5b 100644
--- a/test/mocks/codebuilderfactorymock.h
+++ b/test/mocks/codebuilderfactorymock.h
@@ -8,5 +8,5 @@ using namespace libscratchcpp;
 class CodeBuilderFactoryMock : public ICodeBuilderFactory
 {
     public:
-        MOCK_METHOD(std::shared_ptr<ICodeBuilder>, create, (const std::string &), (const, override));
+        MOCK_METHOD(std::shared_ptr<ICodeBuilder>, create, (const std::string &, bool), (const, override));
 };
diff --git a/test/scratch_classes/inputvalue_test.cpp b/test/scratch_classes/inputvalue_test.cpp
index e58e6b49..d26dd9d5 100644
--- a/test/scratch_classes/inputvalue_test.cpp
+++ b/test/scratch_classes/inputvalue_test.cpp
@@ -130,7 +130,7 @@ TEST(InputValueTest, Compile)
     Compiler compiler(&engine, &target);
 
     auto block = std::make_shared<Block>("", "");
-    EXPECT_CALL(builderFactory, create(_)).WillOnce(Return(builder));
+    EXPECT_CALL(builderFactory, create(_, _)).WillOnce(Return(builder));
     EXPECT_CALL(*builder, finalize);
     compiler.compile(block);
     CompilerPrivate::builderFactory = nullptr;

From a723413f70c746f698c5a2f8250ff24d1641662e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 12 Oct 2024 22:01:39 +0200
Subject: [PATCH 55/94] Remove obsolete value assignment and operator functions

---
 include/scratchcpp/value.h           |  45 +-----
 include/scratchcpp/value_functions.h |   4 -
 src/scratch/value_functions.cpp      |  52 +------
 src/scratch/value_functions_p.h      |  21 ++-
 test/scratch_classes/value_test.cpp  | 221 ---------------------------
 5 files changed, 20 insertions(+), 323 deletions(-)

diff --git a/include/scratchcpp/value.h b/include/scratchcpp/value.h
index df4db60e..21f0f5f8 100644
--- a/include/scratchcpp/value.h
+++ b/include/scratchcpp/value.h
@@ -22,38 +22,18 @@ class LIBSCRATCHCPP_EXPORT Value
 {
     public:
         /*! Constructs a number Value. */
-        Value(float numberValue)
-        {
-            value_init(&m_data);
-            value_assign_float(&m_data, numberValue);
-        }
-
-        /*! Constructs a number Value. */
-        Value(double numberValue)
+        Value(double numberValue = 0.0)
         {
             value_init(&m_data);
             value_assign_double(&m_data, numberValue);
         }
 
         /*! Constructs a number Value. */
-        Value(int numberValue = 0)
+        template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+        Value(T numberValue)
         {
             value_init(&m_data);
-            value_assign_int(&m_data, numberValue);
-        }
-
-        /*! Constructs a number Value. */
-        Value(size_t numberValue)
-        {
-            value_init(&m_data);
-            value_assign_size_t(&m_data, numberValue);
-        }
-
-        /*! Constructs a number Value. */
-        Value(long numberValue)
-        {
-            value_init(&m_data);
-            value_assign_long(&m_data, numberValue);
+            value_assign_double(&m_data, numberValue);
         }
 
         /*! Constructs a boolean Value. */
@@ -181,27 +161,16 @@ class LIBSCRATCHCPP_EXPORT Value
         /*! Replaces the value with modulo of the value and the given value. */
         void mod(const Value &v) { value_mod(&m_data, &v.m_data, &m_data); }
 
-        const Value &operator=(float v)
-        {
-            value_assign_float(&m_data, v);
-            return *this;
-        }
-
         const Value &operator=(double v)
         {
             value_assign_double(&m_data, v);
             return *this;
         }
 
-        const Value &operator=(int v)
-        {
-            value_assign_int(&m_data, v);
-            return *this;
-        }
-
-        const Value &operator=(long v)
+        template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+        const Value &operator=(T v)
         {
-            value_assign_long(&m_data, v);
+            value_assign_double(&m_data, v);
             return *this;
         }
 
diff --git a/include/scratchcpp/value_functions.h b/include/scratchcpp/value_functions.h
index 5ff2c1e4..e3671ab2 100644
--- a/include/scratchcpp/value_functions.h
+++ b/include/scratchcpp/value_functions.h
@@ -11,11 +11,7 @@ extern "C"
 
     LIBSCRATCHCPP_EXPORT void value_init(ValueData *v);
 
-    LIBSCRATCHCPP_EXPORT void value_assign_float(ValueData *v, float numberValue);
     LIBSCRATCHCPP_EXPORT void value_assign_double(ValueData *v, double numberValue);
-    LIBSCRATCHCPP_EXPORT void value_assign_int(ValueData *v, int numberValue);
-    LIBSCRATCHCPP_EXPORT void value_assign_size_t(ValueData *v, size_t numberValue);
-    LIBSCRATCHCPP_EXPORT void value_assign_long(ValueData *v, long numberValue);
     LIBSCRATCHCPP_EXPORT void value_assign_bool(ValueData *v, bool boolValue);
     LIBSCRATCHCPP_EXPORT void value_assign_string(ValueData *v, const std::string &stringValue);
     LIBSCRATCHCPP_EXPORT void value_assign_cstring(ValueData *v, const char *stringValue);
diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index 4a5f6a8b..ef496d24 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -33,23 +33,6 @@ extern "C"
 
     /* assign */
 
-    /*! Assigns number of type 'float' to the given value. */
-    void value_assign_float(ValueData *v, float numberValue)
-    {
-        value_free(v);
-
-        if (value_isInf(numberValue))
-            v->type = ValueType::Infinity;
-        else if (value_isNegativeInf(numberValue))
-            v->type = ValueType::NegativeInfinity;
-        else if (std::isnan(numberValue))
-            v->type = ValueType::NaN;
-        else {
-            v->type = ValueType::Number;
-            v->numberValue = value_floatToDouble(numberValue);
-        }
-    }
-
     /*! Assigns number of type 'double' to the given value. */
     void value_assign_double(ValueData *v, double numberValue)
     {
@@ -67,33 +50,6 @@ extern "C"
         }
     }
 
-    /*! Assigns number of type 'int' to the given value. */
-    void value_assign_int(ValueData *v, int numberValue)
-    {
-        value_free(v);
-
-        v->type = ValueType::Number;
-        v->numberValue = numberValue;
-    }
-
-    /*! Assigns number of type 'size_t' to the given value. */
-    void value_assign_size_t(ValueData *v, size_t numberValue)
-    {
-        value_free(v);
-
-        v->type = ValueType::Number;
-        v->numberValue = numberValue;
-    }
-
-    /*! Assigns number of type 'long' to the given value. */
-    void value_assign_long(ValueData *v, long numberValue)
-    {
-        value_free(v);
-
-        v->type = ValueType::Number;
-        v->numberValue = numberValue;
-    }
-
     /*! Assigns boolean to the given value. */
     void value_assign_bool(ValueData *v, bool boolValue)
     {
@@ -434,7 +390,7 @@ extern "C"
             value_assign_double(dst, v1->numberValue + v2->numberValue);
             return;
         } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) {
-            value_assign_long(dst, v1->boolValue + v2->boolValue);
+            value_assign_double(dst, v1->boolValue + v2->boolValue);
             return;
         } else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
             if ((v1->type == ValueType::Infinity && v2->type == ValueType::NegativeInfinity) || (v1->type == ValueType::NegativeInfinity && v2->type == ValueType::Infinity)) {
@@ -459,7 +415,7 @@ extern "C"
             value_assign_double(dst, v1->numberValue - v2->numberValue);
             return;
         } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) {
-            value_assign_long(dst, v1->boolValue - v2->boolValue);
+            value_assign_double(dst, v1->boolValue - v2->boolValue);
             return;
         } else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
             if ((v1->type == ValueType::Infinity && v2->type == ValueType::Infinity) || (v1->type == ValueType::NegativeInfinity && v2->type == ValueType::NegativeInfinity)) {
@@ -484,7 +440,7 @@ extern "C"
         if (v1->type == ValueType::Number && v2->type == ValueType::Number)
             value_assign_double(dst, v1->numberValue * v2->numberValue);
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
-            value_assign_long(dst, v1->boolValue * v2->boolValue);
+            value_assign_double(dst, v1->boolValue * v2->boolValue);
         else {
             const ValueType t1 = v1->type, t2 = v2->type;
 
@@ -535,7 +491,7 @@ extern "C"
                     value_assign_special(dst, SpecialValue::NegativeInfinity);
             }
         } else if (v2->type == ValueType::Infinity || v2->type == ValueType::NegativeInfinity) {
-            value_assign_long(dst, 0);
+            value_assign_double(dst, 0);
         } else
             value_assign_double(dst, value_toDouble(v1) / value_toDouble(v2));
     }
diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h
index e41c8a0a..7fe91823 100644
--- a/src/scratch/value_functions_p.h
+++ b/src/scratch/value_functions_p.h
@@ -38,20 +38,17 @@ inline unsigned int value_digitCount(T v)
     return i + j;
 }
 
-template<typename T>
-inline bool value_isInf(T v)
-{
-    return v > 0 && std::isinf(v);
-}
-
-template<typename T>
-inline bool value_isNegativeInf(T v)
-{
-    return v < 0 && std::isinf(v);
-}
-
 extern "C"
 {
+    inline bool value_isInf(double v)
+    {
+        return v > 0 && std::isinf(v);
+    }
+
+    inline bool value_isNegativeInf(double v)
+    {
+        return v < 0 && std::isinf(v);
+    }
 
     inline long value_convert_int_str(const char *str, int n, bool *ok)
     {
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 82dc4748..92792da0 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -20,59 +20,6 @@ TEST(ValueTest, DefaultConstructor)
     ASSERT_FALSE(v.isString());
 }
 
-TEST(ValueTest, FloatConstructor)
-{
-    {
-        Value v(3.14f);
-        ASSERT_EQ(v.toDouble(), 3.14);
-        ASSERT_EQ(v.type(), ValueType::Number);
-        ASSERT_FALSE(v.isInfinity());
-        ASSERT_FALSE(v.isNegativeInfinity());
-        ASSERT_FALSE(v.isNaN());
-        ASSERT_TRUE(v.isNumber());
-        ASSERT_TRUE(v.isValidNumber());
-        ASSERT_FALSE(v.isInt());
-        ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
-    }
-
-    {
-        Value v(std::numeric_limits<float>::infinity());
-        ASSERT_EQ(v.type(), ValueType::Infinity);
-        ASSERT_TRUE(v.isInfinity());
-        ASSERT_FALSE(v.isNegativeInfinity());
-        ASSERT_FALSE(v.isNaN());
-        ASSERT_FALSE(v.isNumber());
-        ASSERT_TRUE(v.isValidNumber());
-        ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
-    }
-
-    {
-        Value v(-std::numeric_limits<float>::infinity());
-        ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
-        ASSERT_FALSE(v.isInfinity());
-        ASSERT_TRUE(v.isNegativeInfinity());
-        ASSERT_FALSE(v.isNaN());
-        ASSERT_FALSE(v.isNumber());
-        ASSERT_TRUE(v.isValidNumber());
-        ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
-    }
-
-    {
-        Value v(std::numeric_limits<float>::quiet_NaN());
-        ASSERT_EQ(v.type(), ValueType::NaN);
-        ASSERT_FALSE(v.isInfinity());
-        ASSERT_FALSE(v.isNegativeInfinity());
-        ASSERT_TRUE(v.isNaN());
-        ASSERT_FALSE(v.isNumber());
-        ASSERT_FALSE(v.isValidNumber());
-        ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
-    }
-}
-
 TEST(ValueTest, DoubleConstructor)
 {
     {
@@ -140,51 +87,6 @@ TEST(ValueTest, DoubleConstructor)
     }
 }
 
-TEST(ValueTest, IntConstructor)
-{
-    Value v(static_cast<int>(55));
-    ASSERT_EQ(v.toInt(), 55);
-    ASSERT_EQ(v.type(), ValueType::Number);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_TRUE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_TRUE(v.isInt());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, SizeTConstructor)
-{
-    Value v(static_cast<size_t>(100));
-    ASSERT_EQ(v.toLong(), 100);
-    ASSERT_EQ(v.type(), ValueType::Number);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_TRUE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_TRUE(v.isInt());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, LongConstructor)
-{
-    Value v(123L);
-    ASSERT_EQ(v.toLong(), 123L);
-    ASSERT_EQ(v.type(), ValueType::Number);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_TRUE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_TRUE(v.isInt());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
 TEST(ValueTest, BoolConstructor)
 {
     {
@@ -536,51 +438,6 @@ TEST(ValueTest, CopyConstructor)
     }
 }
 
-TEST(ValueTest, FloatAssignment)
-{
-    Value v;
-    v = 3.14f;
-    ASSERT_EQ(v.toDouble(), 3.14);
-    ASSERT_EQ(v.type(), ValueType::Number);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_TRUE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-
-    v = std::numeric_limits<float>::infinity();
-    ASSERT_EQ(v.type(), ValueType::Infinity);
-    ASSERT_TRUE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-
-    v = -std::numeric_limits<float>::infinity();
-    ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_TRUE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-
-    v = -std::numeric_limits<float>::quiet_NaN();
-    ASSERT_EQ(v.type(), ValueType::NaN);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_TRUE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_FALSE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
 TEST(ValueTest, DoubleAssignment)
 {
     Value v;
@@ -626,36 +483,6 @@ TEST(ValueTest, DoubleAssignment)
     ASSERT_FALSE(v.isString());
 }
 
-TEST(ValueTest, IntAssignment)
-{
-    Value v;
-    v = static_cast<int>(55);
-    ASSERT_EQ(v.toInt(), 55);
-    ASSERT_EQ(v.type(), ValueType::Number);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_TRUE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, LongAssignment)
-{
-    Value v;
-    v = 123L;
-    ASSERT_EQ(v.toLong(), 123L);
-    ASSERT_EQ(v.type(), ValueType::Number);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_TRUE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
 TEST(ValueTest, BoolAssignment)
 {
     {
@@ -962,13 +789,9 @@ TEST(ValueTest, ToInt)
 
     v = 2.54;
     ASSERT_EQ(v.toInt(), 2);
-    v = 2.54f;
-    ASSERT_EQ(v.toInt(), 2);
 
     v = -2.54;
     ASSERT_EQ(v.toInt(), -2);
-    v = -2.54f;
-    ASSERT_EQ(v.toInt(), -2);
 
     v = false;
     ASSERT_EQ(v.toInt(), 0);
@@ -1113,13 +936,9 @@ TEST(ValueTest, ToLong)
 
     v = 2.54;
     ASSERT_EQ(v.toLong(), 2);
-    v = 2.54f;
-    ASSERT_EQ(v.toLong(), 2);
 
     v = -2.54;
     ASSERT_EQ(v.toLong(), -2);
-    v = -2.54f;
-    ASSERT_EQ(v.toLong(), -2);
 
     v = false;
     ASSERT_EQ(v.toLong(), 0);
@@ -1262,13 +1081,9 @@ TEST(ValueTest, ToDouble)
 
     v = 2.54;
     ASSERT_EQ(v.toDouble(), 2.54);
-    v = 2.54f;
-    ASSERT_EQ(v.toDouble(), 2.54);
 
     v = -2.54;
     ASSERT_EQ(v.toDouble(), -2.54);
-    v = -2.54f;
-    ASSERT_EQ(v.toDouble(), -2.54);
 
     v = false;
     ASSERT_EQ(v.toDouble(), 0.0);
@@ -1500,13 +1315,9 @@ TEST(ValueTest, ToBool)
 
     v = 2.54;
     ASSERT_EQ(v.toBool(), true);
-    v = 2.54f;
-    ASSERT_EQ(v.toBool(), true);
 
     v = -2.54;
     ASSERT_EQ(v.toBool(), true);
-    v = -2.54f;
-    ASSERT_EQ(v.toBool(), true);
 
     v = false;
     ASSERT_EQ(v.toBool(), false);
@@ -1692,44 +1503,24 @@ TEST(ValueTest, ToString)
     ASSERT_EQ(v.toString(), "2");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
-    v = 2.0f;
-    cStrings.push_back(value_toCString(&v.data()));
-    ASSERT_EQ(v.toString(), "2");
-    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2.0;
     cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
-    v = -2.0f;
-    cStrings.push_back(value_toCString(&v.data()));
-    ASSERT_EQ(v.toString(), "-2");
-    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 2.54;
     cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2.54");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
-    v = 2.54f;
-    cStrings.push_back(value_toCString(&v.data()));
-    ASSERT_EQ(v.toString(), "2.54");
-    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = -2.54;
     cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2.54");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
-    v = -2.54f;
-    cStrings.push_back(value_toCString(&v.data()));
-    ASSERT_EQ(v.toString(), "-2.54");
-    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
     v = 2550.625021000115;
     cStrings.push_back(value_toCString(&v.data()));
@@ -1737,24 +1528,12 @@ TEST(ValueTest, ToString)
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
-    v = 2550.625021000115f;
-    cStrings.push_back(value_toCString(&v.data()));
-    ASSERT_EQ(v.toString(), "2550.625");
-    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    ASSERT_EQ(std::string(cStrings.back()), v.toString());
-
     v = -2550.625021000115;
     cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "-2550.625021000115");
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
-    v = -2550.625021000115f;
-    cStrings.push_back(value_toCString(&v.data()));
-    ASSERT_EQ(v.toString(), "-2550.625");
-    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
-    ASSERT_EQ(std::string(cStrings.back()), v.toString());
-
     v = 9.4324e+20;
     cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "943240000000000000000");

From 27fa1cbc9b0dc0ba30c6042bd982df5c6b4c2977 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sat, 12 Oct 2024 22:12:17 +0200
Subject: [PATCH 56/94] Remove special value type checks

---
 src/scratch/value_functions.cpp     | 146 +++-------------------------
 test/scratch_classes/value_test.cpp |  74 +++++++-------
 2 files changed, 48 insertions(+), 172 deletions(-)

diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index ef496d24..804b7885 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -38,16 +38,8 @@ extern "C"
     {
         value_free(v);
 
-        if (value_isInf(numberValue))
-            v->type = ValueType::Infinity;
-        else if (value_isNegativeInf(numberValue))
-            v->type = ValueType::NegativeInfinity;
-        else if (std::isnan(numberValue))
-            v->type = ValueType::NaN;
-        else {
-            v->type = ValueType::Number;
-            v->numberValue = numberValue;
-        }
+        v->type = ValueType::Number;
+        v->numberValue = numberValue;
     }
 
     /*! Assigns boolean to the given value. */
@@ -68,16 +60,7 @@ extern "C"
     /*! Assigns C string to the given value. */
     void value_assign_cstring(ValueData *v, const char *stringValue)
     {
-        if (strcmp(stringValue, "Infinity") == 0) {
-            value_free(v);
-            v->type = ValueType::Infinity;
-        } else if (strcmp(stringValue, "-Infinity") == 0) {
-            value_free(v);
-            v->type = ValueType::NegativeInfinity;
-        } else if (strcmp(stringValue, "NaN") == 0) {
-            value_free(v);
-            v->type = ValueType::NaN;
-        } else if (v->type == ValueType::String) {
+        if (v->type == ValueType::String) {
             value_replaceStr(v, stringValue);
         } else {
             value_free(v);
@@ -147,7 +130,7 @@ extern "C"
             case ValueType::NegativeInfinity:
                 return true;
             case ValueType::Number:
-                return value_isNegativeInf(-v->numberValue);
+                return value_isNegativeInf(v->numberValue);
             case ValueType::String:
                 return strcmp(v->stringValue, "-Infinity") == 0;
             default:
@@ -162,7 +145,6 @@ extern "C"
             case ValueType::NaN:
                 return true;
             case ValueType::Number:
-                assert(!std::isnan(v->numberValue));
                 return std::isnan(v->numberValue);
             case ValueType::String:
                 return strcmp(v->stringValue, "NaN") == 0;
@@ -386,122 +368,36 @@ extern "C"
     /*! Adds the given values and writes the result to dst. */
     void value_add(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number) {
-            value_assign_double(dst, v1->numberValue + v2->numberValue);
-            return;
-        } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) {
-            value_assign_double(dst, v1->boolValue + v2->boolValue);
-            return;
-        } else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
-            if ((v1->type == ValueType::Infinity && v2->type == ValueType::NegativeInfinity) || (v1->type == ValueType::NegativeInfinity && v2->type == ValueType::Infinity)) {
-                value_assign_special(dst, SpecialValue::NaN);
-                return;
-            } else if (v1->type == ValueType::Infinity || v2->type == ValueType::Infinity) {
-                value_assign_special(dst, SpecialValue::Infinity);
-                return;
-            } else if (v1->type == ValueType::NegativeInfinity || v2->type == ValueType::NegativeInfinity) {
-                value_assign_special(dst, SpecialValue::NegativeInfinity);
-                return;
-            }
-        }
-
         value_assign_double(dst, value_toDouble(v1) + value_toDouble(v2));
     }
 
     /*! Subtracts the given values and writes the result to dst. */
     void value_subtract(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number) {
-            value_assign_double(dst, v1->numberValue - v2->numberValue);
-            return;
-        } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) {
-            value_assign_double(dst, v1->boolValue - v2->boolValue);
-            return;
-        } else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
-            if ((v1->type == ValueType::Infinity && v2->type == ValueType::Infinity) || (v1->type == ValueType::NegativeInfinity && v2->type == ValueType::NegativeInfinity)) {
-                value_assign_special(dst, SpecialValue::NaN);
-                return;
-            } else if (v1->type == ValueType::Infinity || v2->type == ValueType::NegativeInfinity) {
-                value_assign_special(dst, SpecialValue::Infinity);
-                return;
-            } else if (v1->type == ValueType::NegativeInfinity || v2->type == ValueType::Infinity) {
-                value_assign_special(dst, SpecialValue::NegativeInfinity);
-                return;
-            }
-        }
-
         value_assign_double(dst, value_toDouble(v1) - value_toDouble(v2));
     }
 
     /*! Multiplies the given values and writes the result to dst. */
     void value_multiply(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
-            value_assign_double(dst, v1->numberValue * v2->numberValue);
-        else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
-            value_assign_double(dst, v1->boolValue * v2->boolValue);
-        else {
-            const ValueType t1 = v1->type, t2 = v2->type;
-
-            if ((static_cast<int>(t1) < 0 && t1 != ValueType::NaN) || (static_cast<int>(t2) < 0 && t2 != ValueType::NaN)) {
-                if (t1 == ValueType::Infinity || t1 == ValueType::NegativeInfinity || t2 == ValueType::Infinity || t2 == ValueType::NegativeInfinity) {
-                    bool mode = (t1 == ValueType::Infinity || t2 == ValueType::Infinity);
-                    const ValueData *value;
-
-                    if ((t1 == ValueType::Infinity && (t2 == ValueType::Infinity || t2 == ValueType::NegativeInfinity)) || (t2 != ValueType::Infinity && t2 != ValueType::NegativeInfinity))
-                        value = v2;
-                    else
-                        value = v1;
-
-                    if (value_isPositive(value))
-                        value_assign_special(dst, mode ? SpecialValue::Infinity : SpecialValue::NegativeInfinity);
-                    else if (value_isNegative(value))
-                        value_assign_special(dst, mode ? SpecialValue::NegativeInfinity : SpecialValue::Infinity);
-                    else
-                        value_assign_special(dst, SpecialValue::NaN);
-                }
-            } else
-                value_assign_double(dst, value_toDouble(v1) * value_toDouble(v2));
-        }
+        value_assign_double(dst, value_toDouble(v1) * value_toDouble(v2));
     }
 
     /*! Divides the given values and writes the result to dst. */
     void value_divide(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if (value_isZero(v1) && value_isZero(v2))
-            value_assign_special(dst, SpecialValue::NaN);
-        else if (value_toDouble(v2) == 0) {
-            if (value_isPositive(v1))
-                value_assign_special(dst, SpecialValue::Infinity);
-            else
-                value_assign_special(dst, SpecialValue::NegativeInfinity);
-        } else if ((v1->type == ValueType::Infinity || v1->type == ValueType::NegativeInfinity) && (v2->type == ValueType::Infinity || v2->type == ValueType::NegativeInfinity)) {
-            value_assign_special(dst, SpecialValue::NaN);
-        } else if (v1->type == ValueType::Infinity || v1->type == ValueType::NegativeInfinity) {
-            if (value_toDouble(v2) < 0) {
-                if (v1->type == ValueType::Infinity)
-                    value_assign_special(dst, SpecialValue::NegativeInfinity);
-                else
-                    value_assign_special(dst, SpecialValue::Infinity);
-            } else {
-                if (v1->type == ValueType::Infinity)
-                    value_assign_special(dst, SpecialValue::Infinity);
-                else
-                    value_assign_special(dst, SpecialValue::NegativeInfinity);
-            }
-        } else if (v2->type == ValueType::Infinity || v2->type == ValueType::NegativeInfinity) {
-            value_assign_double(dst, 0);
-        } else
-            value_assign_double(dst, value_toDouble(v1) / value_toDouble(v2));
+        value_assign_double(dst, value_toDouble(v1) / value_toDouble(v2));
     }
 
     /*! Calculates the modulo the given values and writes the result to dst. */
     void value_mod(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2, ValueData *dst)
     {
-        if ((v2 == 0) || (v1->type == ValueType::Infinity || v1->type == ValueType::NegativeInfinity))
+        double a = value_toDouble(v1);
+        double b = value_toDouble(v2);
+
+        if ((b == 0) || std::isinf(a))
             value_assign_special(dst, SpecialValue::NaN);
-        else if (v2->type == ValueType::Infinity || v2->type == ValueType::NegativeInfinity)
+        else if (std::isinf(b))
             value_assign_double(dst, value_toDouble(v1));
         else if (value_isNegative(v1) || value_isNegative(v2))
             value_assign_double(dst, fmod(value_toDouble(v2) + fmod(value_toDouble(v1), -value_toDouble(v2)), value_toDouble(v2)));
@@ -558,16 +454,6 @@ extern "C"
             return v1->numberValue > v2->numberValue;
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             return v1->boolValue > v2->boolValue;
-        else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
-            if (v1->type == ValueType::Infinity) {
-                return v2->type != ValueType::Infinity;
-            } else if (v1->type == ValueType::NegativeInfinity)
-                return false;
-            else if (v2->type == ValueType::Infinity)
-                return false;
-            else if (v2->type == ValueType::NegativeInfinity)
-                return true;
-        }
 
         double n1, n2;
 
@@ -593,16 +479,6 @@ extern "C"
             return v1->numberValue < v2->numberValue;
         else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             return v1->boolValue < v2->boolValue;
-        else if ((static_cast<int>(v1->type) < 0) || (static_cast<int>(v2->type) < 0)) {
-            if (v1->type == ValueType::Infinity) {
-                return false;
-            } else if (v1->type == ValueType::NegativeInfinity)
-                return v2->type != ValueType::NegativeInfinity;
-            else if (v2->type == ValueType::Infinity)
-                return v1->type != ValueType::Infinity;
-            else if (v2->type == ValueType::NegativeInfinity)
-                return false;
-        }
 
         return value_toDouble(v1) < value_toDouble(v2);
         double n1, n2;
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 92792da0..61ed118b 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -52,11 +52,11 @@ TEST(ValueTest, DoubleConstructor)
 
     {
         Value v(std::numeric_limits<double>::infinity());
-        ASSERT_EQ(v.type(), ValueType::Infinity);
+        ASSERT_EQ(v.type(), ValueType::Number);
         ASSERT_TRUE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
-        ASSERT_FALSE(v.isNumber());
+        ASSERT_TRUE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
         ASSERT_FALSE(v.isString());
@@ -64,11 +64,11 @@ TEST(ValueTest, DoubleConstructor)
 
     {
         Value v(-std::numeric_limits<double>::infinity());
-        ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
+        ASSERT_EQ(v.type(), ValueType::Number);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_TRUE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
-        ASSERT_FALSE(v.isNumber());
+        ASSERT_TRUE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
         ASSERT_FALSE(v.isString());
@@ -76,11 +76,11 @@ TEST(ValueTest, DoubleConstructor)
 
     {
         Value v(std::numeric_limits<double>::quiet_NaN());
-        ASSERT_EQ(v.type(), ValueType::NaN);
+        ASSERT_EQ(v.type(), ValueType::Number);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_TRUE(v.isNaN());
-        ASSERT_FALSE(v.isNumber());
+        ASSERT_TRUE(v.isNumber());
         ASSERT_FALSE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
         ASSERT_FALSE(v.isString());
@@ -209,38 +209,38 @@ TEST(ValueTest, StdStringConstructor)
 
     {
         Value v(std::string("Infinity"));
-        ASSERT_EQ(v.type(), ValueType::Infinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_TRUE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v(std::string("-Infinity"));
-        ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_TRUE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v(std::string("NaN"));
-        ASSERT_EQ(v.type(), ValueType::NaN);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_TRUE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_FALSE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 }
 
@@ -262,38 +262,38 @@ TEST(ValueTest, CStringConstructor)
 
     {
         Value v("Infinity");
-        ASSERT_EQ(v.type(), ValueType::Infinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_TRUE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v("-Infinity");
-        ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_TRUE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v("NaN");
-        ASSERT_EQ(v.type(), ValueType::NaN);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_TRUE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_FALSE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 }
 
@@ -453,31 +453,31 @@ TEST(ValueTest, DoubleAssignment)
     ASSERT_FALSE(v.isString());
 
     v = std::numeric_limits<double>::infinity();
-    ASSERT_EQ(v.type(), ValueType::Infinity);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_TRUE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
+    ASSERT_TRUE(v.isNumber());
     ASSERT_TRUE(v.isValidNumber());
     ASSERT_FALSE(v.isBool());
     ASSERT_FALSE(v.isString());
 
     v = -std::numeric_limits<double>::infinity();
-    ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_TRUE(v.isNegativeInfinity());
     ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
+    ASSERT_TRUE(v.isNumber());
     ASSERT_TRUE(v.isValidNumber());
     ASSERT_FALSE(v.isBool());
     ASSERT_FALSE(v.isString());
 
     v = -std::numeric_limits<double>::quiet_NaN();
-    ASSERT_EQ(v.type(), ValueType::NaN);
+    ASSERT_EQ(v.type(), ValueType::Number);
     ASSERT_FALSE(v.isInfinity());
     ASSERT_FALSE(v.isNegativeInfinity());
     ASSERT_TRUE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
+    ASSERT_TRUE(v.isNumber());
     ASSERT_FALSE(v.isValidNumber());
     ASSERT_FALSE(v.isBool());
     ASSERT_FALSE(v.isString());
@@ -534,42 +534,42 @@ TEST(ValueTest, StdStringAssignment)
         Value v;
         v = std::string("Infinity");
         ASSERT_EQ(v.toString(), "Infinity");
-        ASSERT_EQ(v.type(), ValueType::Infinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_TRUE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v;
         v = std::string("-Infinity");
         ASSERT_EQ(v.toString(), "-Infinity");
-        ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_TRUE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v;
         v = std::string("NaN");
         ASSERT_EQ(v.toString(), "NaN");
-        ASSERT_EQ(v.type(), ValueType::NaN);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_TRUE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_FALSE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 }
 
@@ -593,42 +593,42 @@ TEST(ValueTest, CStringAssignment)
         Value v;
         v = "Infinity";
         ASSERT_EQ(v.toString(), "Infinity");
-        ASSERT_EQ(v.type(), ValueType::Infinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_TRUE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v;
         v = "-Infinity";
         ASSERT_EQ(v.toString(), "-Infinity");
-        ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_TRUE(v.isNegativeInfinity());
         ASSERT_FALSE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_TRUE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 
     {
         Value v;
         v = "NaN";
         ASSERT_EQ(v.toString(), "NaN");
-        ASSERT_EQ(v.type(), ValueType::NaN);
+        ASSERT_EQ(v.type(), ValueType::String);
         ASSERT_FALSE(v.isInfinity());
         ASSERT_FALSE(v.isNegativeInfinity());
         ASSERT_TRUE(v.isNaN());
         ASSERT_FALSE(v.isNumber());
         ASSERT_FALSE(v.isValidNumber());
         ASSERT_FALSE(v.isBool());
-        ASSERT_FALSE(v.isString());
+        ASSERT_TRUE(v.isString());
     }
 }
 
@@ -1358,7 +1358,7 @@ TEST(ValueTest, ToBool)
     v = "-Infinity";
     ASSERT_EQ(v.toBool(), true);
     v = "NaN";
-    ASSERT_EQ(v.toBool(), false);
+    ASSERT_EQ(v.toBool(), true);
 
     v = "something";
     ASSERT_EQ(v.toBool(), true);

From 3ac5779ef600751d022825847c34912413c0c69e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 12:32:35 +0200
Subject: [PATCH 57/94] Remove Infinity, -Infinity and NaN types

---
 include/scratchcpp/value.h                    |   7 -
 include/scratchcpp/value_functions.h          |   1 -
 include/scratchcpp/valuedata.h                |  12 +-
 src/blocks/operatorblocks.cpp                 |  16 +-
 src/dev/engine/internal/llvmcodebuilder.cpp   |   7 +-
 src/engine/virtualmachine_p.cpp               |  18 +-
 src/scratch/inputvalue.cpp                    |   7 +-
 src/scratch/value_functions.cpp               |  63 ++---
 src/scratch/value_functions_p.h               |  56 ++---
 test/blocks/list_blocks_test.cpp              |   4 +-
 test/blocks/looks_blocks_test.cpp             |  16 +-
 test/blocks/operator_blocks_test.cpp          |  16 +-
 test/scratch_classes/value_test.cpp           | 231 ++++--------------
 test/virtual_machine/virtual_machine_test.cpp |  35 ++-
 14 files changed, 158 insertions(+), 331 deletions(-)

diff --git a/include/scratchcpp/value.h b/include/scratchcpp/value.h
index 21f0f5f8..96c781f3 100644
--- a/include/scratchcpp/value.h
+++ b/include/scratchcpp/value.h
@@ -57,13 +57,6 @@ class LIBSCRATCHCPP_EXPORT Value
             value_assign_cstring(&m_data, stringValue);
         }
 
-        /*! Constructs a special Value. */
-        Value(SpecialValue specialValue)
-        {
-            value_init(&m_data);
-            value_assign_special(&m_data, specialValue);
-        }
-
         /*! Constructs value from ValueData. */
         Value(const ValueData &v)
         {
diff --git a/include/scratchcpp/value_functions.h b/include/scratchcpp/value_functions.h
index e3671ab2..d4409a0b 100644
--- a/include/scratchcpp/value_functions.h
+++ b/include/scratchcpp/value_functions.h
@@ -15,7 +15,6 @@ extern "C"
     LIBSCRATCHCPP_EXPORT void value_assign_bool(ValueData *v, bool boolValue);
     LIBSCRATCHCPP_EXPORT void value_assign_string(ValueData *v, const std::string &stringValue);
     LIBSCRATCHCPP_EXPORT void value_assign_cstring(ValueData *v, const char *stringValue);
-    LIBSCRATCHCPP_EXPORT void value_assign_special(ValueData *v, SpecialValue specialValue);
     LIBSCRATCHCPP_EXPORT void value_assign_copy(ValueData *v, const ValueData *another);
 
     LIBSCRATCHCPP_EXPORT bool value_isInfinity(const ValueData *v);
diff --git a/include/scratchcpp/valuedata.h b/include/scratchcpp/valuedata.h
index b3af3e1d..571928ca 100644
--- a/include/scratchcpp/valuedata.h
+++ b/include/scratchcpp/valuedata.h
@@ -9,21 +9,11 @@
 namespace libscratchcpp
 {
 
-enum class LIBSCRATCHCPP_EXPORT SpecialValue
-{
-    Infinity,
-    NegativeInfinity,
-    NaN
-};
-
 enum class LIBSCRATCHCPP_EXPORT ValueType
 {
     Number = 0,
     Bool = 1,
-    String = 2,
-    Infinity = -1,
-    NegativeInfinity = -2,
-    NaN = -3
+    String = 2
 };
 
 extern "C"
diff --git a/src/blocks/operatorblocks.cpp b/src/blocks/operatorblocks.cpp
index cd200ad6..376ae27a 100644
--- a/src/blocks/operatorblocks.cpp
+++ b/src/blocks/operatorblocks.cpp
@@ -261,9 +261,9 @@ unsigned int OperatorBlocks::op_ln(VirtualMachine *vm)
 {
     const Value &v = *vm->getInput(0, 1);
     if (v < 0)
-        vm->replaceReturnValue(Value(SpecialValue::NaN), 1);
+        vm->replaceReturnValue(std::numeric_limits<double>::quiet_NaN(), 1);
     else if (v == 0 || v.isNaN())
-        vm->replaceReturnValue(Value(SpecialValue::NegativeInfinity), 1);
+        vm->replaceReturnValue(-std::numeric_limits<double>::infinity(), 1);
     else if (!v.isInfinity())
         vm->replaceReturnValue(std::log(v.toDouble()), 1);
     return 0;
@@ -273,9 +273,9 @@ unsigned int OperatorBlocks::op_log(VirtualMachine *vm)
 {
     const Value &v = *vm->getInput(0, 1);
     if (v < 0)
-        vm->replaceReturnValue(Value(SpecialValue::NaN), 1);
+        vm->replaceReturnValue(std::numeric_limits<double>::quiet_NaN(), 1);
     else if (v == 0 || v.isNaN())
-        vm->replaceReturnValue(Value(SpecialValue::NegativeInfinity), 1);
+        vm->replaceReturnValue(-std::numeric_limits<double>::infinity(), 1);
     else if (!v.isInfinity())
         vm->replaceReturnValue(std::log10(v.toDouble()), 1);
     return 0;
@@ -284,7 +284,9 @@ unsigned int OperatorBlocks::op_log(VirtualMachine *vm)
 unsigned int OperatorBlocks::op_eexp(VirtualMachine *vm)
 {
     const Value *v = vm->getInput(0, 1);
-    if (v->isNegativeInfinity())
+    if (v->isNaN())
+        vm->replaceReturnValue(1, 1);
+    else if (v->isNegativeInfinity())
         vm->replaceReturnValue(0, 1);
     else if (!v->isInfinity())
         vm->replaceReturnValue(std::exp(v->toDouble()), 1);
@@ -294,7 +296,9 @@ unsigned int OperatorBlocks::op_eexp(VirtualMachine *vm)
 unsigned int OperatorBlocks::op_10exp(VirtualMachine *vm)
 {
     const Value *v = vm->getInput(0, 1);
-    if (v->isNegativeInfinity())
+    if (v->isNaN())
+        vm->replaceReturnValue(1, 1);
+    else if (v->isNegativeInfinity())
         vm->replaceReturnValue(0, 1);
     else if (!v->isInfinity())
         vm->replaceReturnValue(std::pow(10, v->toDouble()), 1);
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 70b60963..54abde95 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -10,11 +10,8 @@
 
 using namespace libscratchcpp;
 
-static std::unordered_map<ValueType, Compiler::StaticType> TYPE_MAP = {
-    { ValueType::Number, Compiler::StaticType::Number },           { ValueType::Bool, Compiler::StaticType::Bool },
-    { ValueType::String, Compiler::StaticType::String },           { ValueType::Infinity, Compiler::StaticType::Number },
-    { ValueType::NegativeInfinity, Compiler::StaticType::Number }, { ValueType::NaN, Compiler::StaticType::Number }
-};
+static std::unordered_map<ValueType, Compiler::StaticType>
+    TYPE_MAP = { { ValueType::Number, Compiler::StaticType::Number }, { ValueType::Bool, Compiler::StaticType::Bool }, { ValueType::String, Compiler::StaticType::String } };
 
 LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id, bool warp) :
     m_id(id),
diff --git a/src/engine/virtualmachine_p.cpp b/src/engine/virtualmachine_p.cpp
index 558d7b76..6addee35 100644
--- a/src/engine/virtualmachine_p.cpp
+++ b/src/engine/virtualmachine_p.cpp
@@ -461,7 +461,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value *v = READ_REG(0, 1);
         if (v->isNegativeInfinity())
-            REPLACE_RET_VALUE(Value(SpecialValue::Infinity), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::infinity(), 1);
         else if (!v->isInfinity())
             REPLACE_RET_VALUE(std::abs(v->toDouble()), 1);
         DISPATCH();
@@ -487,7 +487,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value &v = *READ_REG(0, 1);
         if (v < 0)
-            REPLACE_RET_VALUE(Value(SpecialValue::NaN), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else if (!v.isInfinity())
             REPLACE_RET_VALUE(std::sqrt(v.toDouble()), 1);
         DISPATCH();
@@ -497,7 +497,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value *v = READ_REG(0, 1);
         if (v->isInfinity() || v->isNegativeInfinity())
-            REPLACE_RET_VALUE(Value(SpecialValue::NaN), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else
             REPLACE_RET_VALUE(std::sin(v->toDouble() * pi / 180), 1);
         DISPATCH();
@@ -507,7 +507,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value *v = READ_REG(0, 1);
         if (v->isInfinity() || v->isNegativeInfinity())
-            REPLACE_RET_VALUE(Value(SpecialValue::NaN), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else
             REPLACE_RET_VALUE(std::cos(v->toDouble() * pi / 180), 1);
         DISPATCH();
@@ -517,7 +517,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value *v = READ_REG(0, 1);
         if (v->isInfinity() || v->isNegativeInfinity())
-            REPLACE_RET_VALUE(Value(SpecialValue::NaN), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else {
             long mod;
             if (v->toLong() < 0)
@@ -525,9 +525,9 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
             else
                 mod = v->toLong() % 360;
             if (mod == 90)
-                REPLACE_RET_VALUE(Value(SpecialValue::Infinity), 1);
+                REPLACE_RET_VALUE(std::numeric_limits<double>::infinity(), 1);
             else if (mod == 270)
-                REPLACE_RET_VALUE(Value(SpecialValue::NegativeInfinity), 1);
+                REPLACE_RET_VALUE(-std::numeric_limits<double>::infinity(), 1);
             else
                 REPLACE_RET_VALUE(std::tan(v->toDouble() * pi / 180), 1);
         }
@@ -538,7 +538,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value &v = *READ_REG(0, 1);
         if (v < -1 || v > 1)
-            REPLACE_RET_VALUE(Value(SpecialValue::NaN), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else
             REPLACE_RET_VALUE(std::asin(v.toDouble()) * 180 / pi, 1);
         DISPATCH();
@@ -548,7 +548,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     {
         const Value &v = *READ_REG(0, 1);
         if (v < -1 || v > 1)
-            REPLACE_RET_VALUE(Value(SpecialValue::NaN), 1);
+            REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else
             REPLACE_RET_VALUE(std::acos(v.toDouble()) * 180 / pi, 1);
         DISPATCH();
diff --git a/src/scratch/inputvalue.cpp b/src/scratch/inputvalue.cpp
index 9b6b732d..2d66269d 100644
--- a/src/scratch/inputvalue.cpp
+++ b/src/scratch/inputvalue.cpp
@@ -17,11 +17,8 @@
 
 using namespace libscratchcpp;
 
-static const std::map<ValueType, InputValue::Type> VALUE_TYPE_MAP = {
-    { ValueType::Number, InputValue::Type::Number },           { ValueType::Bool, InputValue::Type::String },
-    { ValueType::String, InputValue::Type::String },           { ValueType::Infinity, InputValue::Type::String },
-    { ValueType::NegativeInfinity, InputValue::Type::String }, { ValueType::NaN, InputValue::Type::String }
-};
+static const std::map<ValueType, InputValue::Type>
+    VALUE_TYPE_MAP = { { ValueType::Number, InputValue::Type::Number }, { ValueType::Bool, InputValue::Type::String }, { ValueType::String, InputValue::Type::String } };
 
 /*! Constructs InputValue with the given type. */
 InputValue::InputValue(Type type) :
diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index 804b7885..d13264d0 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -68,23 +68,6 @@ extern "C"
         }
     }
 
-    /*! Assigns special value to the given value. */
-    void value_assign_special(ValueData *v, SpecialValue specialValue)
-    {
-        value_free(v);
-
-        if (specialValue == SpecialValue::Infinity)
-            v->type = ValueType::Infinity;
-        else if (specialValue == SpecialValue::NegativeInfinity)
-            v->type = ValueType::NegativeInfinity;
-        else if (specialValue == SpecialValue::NaN)
-            v->type = ValueType::NaN;
-        else {
-            v->type = ValueType::Number;
-            v->numberValue = 0;
-        }
-    }
-
     /*! Assigns another value to the given value. */
     void value_assign_copy(ValueData *v, const libscratchcpp::ValueData *another)
     {
@@ -112,8 +95,6 @@ extern "C"
     bool value_isInfinity(const libscratchcpp::ValueData *v)
     {
         switch (v->type) {
-            case ValueType::Infinity:
-                return true;
             case ValueType::Number:
                 return value_isInf(v->numberValue);
             case ValueType::String:
@@ -127,8 +108,6 @@ extern "C"
     bool value_isNegativeInfinity(const libscratchcpp::ValueData *v)
     {
         switch (v->type) {
-            case ValueType::NegativeInfinity:
-                return true;
             case ValueType::Number:
                 return value_isNegativeInf(v->numberValue);
             case ValueType::String:
@@ -142,8 +121,6 @@ extern "C"
     bool value_isNaN(const libscratchcpp::ValueData *v)
     {
         switch (v->type) {
-            case ValueType::NaN:
-                return true;
             case ValueType::Number:
                 return std::isnan(v->numberValue);
             case ValueType::String:
@@ -168,8 +145,6 @@ extern "C"
         if (value_isInfinity(v) || value_isNegativeInfinity(v))
             return true;
 
-        assert(v->type != ValueType::Infinity && v->type != ValueType::NegativeInfinity);
-
         switch (v->type) {
             case ValueType::Number:
             case ValueType::Bool:
@@ -187,11 +162,12 @@ extern "C"
         // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L157-L181
         switch (v->type) {
             case ValueType::Bool:
-            case ValueType::Infinity:
-            case ValueType::NegativeInfinity:
-            case ValueType::NaN:
                 return true;
+
             case ValueType::Number: {
+                if (std::isinf(v->numberValue) || std::isnan(v->numberValue))
+                    return true;
+
                 double intpart;
                 std::modf(v->numberValue, &intpart);
                 return v->numberValue == intpart;
@@ -220,9 +196,9 @@ extern "C"
     /*! Returns the long representation of the given value. */
     long value_toLong(const libscratchcpp::ValueData *v)
     {
-        if (v->type == ValueType::Number)
+        if (v->type == ValueType::Number) {
             return v->numberValue;
-        else if (v->type == ValueType::Bool)
+        } else if (v->type == ValueType::Bool)
             return v->boolValue;
         else if (v->type == ValueType::String)
             return value_stringToLong(v->stringValue);
@@ -252,10 +228,6 @@ extern "C"
             return v->boolValue;
         else if (v->type == ValueType::String)
             return value_stringToDouble(v->stringValue);
-        else if (v->type == ValueType::Infinity)
-            return std::numeric_limits<double>::infinity();
-        else if (v->type == ValueType::NegativeInfinity)
-            return -std::numeric_limits<double>::infinity();
         else
             return 0;
     }
@@ -269,10 +241,6 @@ extern "C"
             return v->numberValue != 0;
         } else if (v->type == ValueType::String) {
             return value_stringToBool(v->stringValue);
-        } else if (v->type == ValueType::Infinity || v->type == ValueType::NegativeInfinity) {
-            return true;
-        } else if (v->type == ValueType::NaN) {
-            return false;
         } else {
             return false;
         }
@@ -287,12 +255,6 @@ extern "C"
             value_doubleToString(v->numberValue, dst);
         else if (v->type == ValueType::Bool)
             dst->assign(v->boolValue ? "true" : "false");
-        else if (v->type == ValueType::Infinity)
-            dst->assign("Infinity");
-        else if (v->type == ValueType::NegativeInfinity)
-            dst->assign("-Infinity");
-        else if (v->type == ValueType::NaN)
-            dst->assign("NaN");
         else
             dst->clear();
     }
@@ -396,7 +358,7 @@ extern "C"
         double b = value_toDouble(v2);
 
         if ((b == 0) || std::isinf(a))
-            value_assign_special(dst, SpecialValue::NaN);
+            value_assign_double(dst, std::numeric_limits<double>::quiet_NaN());
         else if (std::isinf(b))
             value_assign_double(dst, value_toDouble(v1));
         else if (value_isNegative(v1) || value_isNegative(v2))
@@ -413,9 +375,12 @@ extern "C"
         // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150
         assert(v1 && v2);
 
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number) {
+            if (std::isnan(v1->numberValue) && std::isnan(v2->numberValue))
+                return true;
+
             return v1->numberValue == v2->numberValue;
-        else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
+        } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
             return v1->boolValue == v2->boolValue;
 
         bool ok;
@@ -436,8 +401,8 @@ extern "C"
 
         // Handle the special case of Infinity
         if ((static_cast<int>(v1->type) < 0) && (static_cast<int>(v2->type) < 0)) {
-            assert(v1->type != ValueType::NaN);
-            assert(v2->type != ValueType::NaN);
+            assert(!value_isNaN(v1));
+            assert(!value_isNaN(v2));
             return v1->type == v2->type;
         }
 
diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h
index 7fe91823..cffe2ab7 100644
--- a/src/scratch/value_functions_p.h
+++ b/src/scratch/value_functions_p.h
@@ -366,22 +366,17 @@ extern "C"
                 *ok = true;
 
             return ret;
-        } else
-            return 0;
+        }
 
         // Special values
-        if (strncmp(s, "Infinity", len) == 0) {
+        if (value_stringsEqual(s, "Infinity")) {
             if (ok)
                 *ok = true;
             return std::numeric_limits<double>::infinity();
-        } else if (strncmp(s, "-Infinity", len) == 0) {
+        } else if (value_stringsEqual(s, "-Infinity")) {
             if (ok)
                 *ok = true;
             return -std::numeric_limits<double>::infinity();
-        } else if (strncmp(s, "NaN", len) == 0) {
-            if (ok)
-                *ok = true;
-            return std::numeric_limits<double>::quiet_NaN();
         }
 
         return 0;
@@ -389,7 +384,12 @@ extern "C"
 
     inline long value_stringToLong(const char *s, bool *ok = nullptr)
     {
-        return value_stringToDoubleImpl(s, ok);
+        const double ret = value_stringToDoubleImpl(s, ok);
+
+        if (std::isinf(ret))
+            return 0;
+
+        return ret;
     }
 
     inline bool value_stringIsInt(const char *s, int n)
@@ -409,6 +409,18 @@ extern "C"
 
 inline void value_doubleToString(double v, std::string *dst)
 {
+    if (std::isinf(v)) {
+        if (v > 0)
+            dst->assign("Infinity");
+        else
+            dst->assign("-Infinity");
+
+        return;
+    } else if (std::isnan(v)) {
+        dst->assign("NaN");
+        return;
+    }
+
     std::stringstream stream;
 
     if (v != 0) {
@@ -469,18 +481,14 @@ extern "C"
         // we can optimize by prioritizing the other types here.
         if (v->type == ValueType::String)
             return value_stringToDoubleImpl(v->stringValue, ok);
-        else if (v->type == ValueType::Infinity)
-            return std::numeric_limits<double>::infinity();
-        else if (v->type == ValueType::NegativeInfinity)
-            return -std::numeric_limits<double>::infinity();
-        else if (v->type == ValueType::NaN) {
-            if (ok)
+        else if (v->type == ValueType::Number) {
+            if (std::isnan(v->numberValue)) {
                 *ok = false;
+                return 0;
+            }
 
-            return 0;
-        } else if (v->type == ValueType::Number)
             return v->numberValue;
-        else if (v->type == ValueType::Bool)
+        } else if (v->type == ValueType::Bool)
             return v->boolValue;
         else {
             assert(false); // this should never happen
@@ -497,12 +505,6 @@ extern "C"
             return v->numberValue > 0;
         else if (v->type == ValueType::Bool)
             return v->boolValue;
-        else if (static_cast<int>(v->type) < 0) {
-            if (v->type == ValueType::Infinity)
-                return true;
-            else
-                return false;
-        }
 
         return value_toDouble(v) > 0;
     }
@@ -513,12 +515,6 @@ extern "C"
             return v->numberValue < 0;
         else if (v->type == ValueType::Bool)
             return false;
-        else if (static_cast<int>(v->type) < 0) {
-            if (v->type == ValueType::NegativeInfinity)
-                return true;
-            else
-                return false;
-        }
 
         return value_toDouble(v) < 0;
     }
diff --git a/test/blocks/list_blocks_test.cpp b/test/blocks/list_blocks_test.cpp
index 90577da8..adafc8b6 100644
--- a/test/blocks/list_blocks_test.cpp
+++ b/test/blocks/list_blocks_test.cpp
@@ -418,7 +418,7 @@ TEST_F(ListBlocksTest, ListContainsItem)
 
     // [list2] contains -Infinity
     auto list2 = std::make_shared<List>("d", "list2");
-    auto block2 = createListItemBlock("c", "data_listcontainsitem", list2, SpecialValue::NegativeInfinity);
+    auto block2 = createListItemBlock("c", "data_listcontainsitem", list2, -std::numeric_limits<double>::infinity());
 
     compiler.init();
     compiler.setBlock(block1);
@@ -428,7 +428,7 @@ TEST_F(ListBlocksTest, ListContainsItem)
     compiler.end();
 
     ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_LIST_CONTAINS, 0, vm::OP_CONST, 1, vm::OP_LIST_CONTAINS, 1, vm::OP_HALT }));
-    ASSERT_EQ(compiler.constValues(), std::vector<Value>({ "hello world", Value(SpecialValue::NegativeInfinity) }));
+    ASSERT_EQ(compiler.constValues(), std::vector<Value>({ "hello world", -std::numeric_limits<double>::infinity() }));
     ASSERT_TRUE(compiler.variables().empty());
     ASSERT_EQ(
         compiler.lists(),
diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp
index 041c32b8..5defa145 100644
--- a/test/blocks/looks_blocks_test.cpp
+++ b/test/blocks/looks_blocks_test.cpp
@@ -1476,7 +1476,9 @@ TEST_F(LooksBlocksTest, SwitchCostumeToImpl)
     static unsigned int bytecode13[] = { vm::OP_START, vm::OP_CONST, 12, vm::OP_EXEC, 0, vm::OP_HALT };
     static unsigned int bytecode14[] = { vm::OP_START, vm::OP_CONST, 13, vm::OP_EXEC, 0, vm::OP_HALT };
     static BlockFunc functions[] = { &LooksBlocks::switchCostumeTo };
-    static Value constValues[] = { "costume2", 0, 1, 2, 3, "2", "3", SpecialValue::NaN, SpecialValue::Infinity, SpecialValue::NegativeInfinity, "", "   ", "next costume", "previous costume" };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "costume2", 0, 1, 2, 3, "2", "3", nan, inf, -inf, "", "   ", "next costume", "previous costume" };
 
     Target target;
     target.addCostume(std::make_shared<Costume>("costume1", "c1", "svg"));
@@ -1791,9 +1793,9 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl)
     static unsigned int bytecode14[] = { vm::OP_START, vm::OP_CONST, 13, vm::OP_EXEC, 0, vm::OP_HALT };
     static unsigned int bytecode15[] = { vm::OP_START, vm::OP_CONST, 14, vm::OP_EXEC, 0, vm::OP_HALT };
     static BlockFunc functions[] = { &LooksBlocks::switchBackdropTo };
-    static Value constValues[] = {
-        "backdrop2", 0, 1, 2, 3, "2", "3", SpecialValue::NaN, SpecialValue::Infinity, SpecialValue::NegativeInfinity, "", "   ", "next backdrop", "previous backdrop", "random backdrop"
-    };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "backdrop2", 0, 1, 2, 3, "2", "3", nan, inf, -inf, "", "   ", "next backdrop", "previous backdrop", "random backdrop" };
 
     Target target;
 
@@ -2068,9 +2070,9 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl)
     static unsigned int bytecode14[] = { vm::OP_START, vm::OP_CONST, 13, vm::OP_EXEC, 0, vm::OP_HALT };
     static unsigned int bytecode15[] = { vm::OP_START, vm::OP_CONST, 14, vm::OP_EXEC, 0, vm::OP_HALT };
     static BlockFunc functions[] = { &LooksBlocks::switchBackdropToAndWait };
-    static Value constValues[] = {
-        "backdrop2", 0, 1, 2, 3, "2", "3", SpecialValue::NaN, SpecialValue::Infinity, SpecialValue::NegativeInfinity, "", "   ", "next backdrop", "previous backdrop", "random backdrop"
-    };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "backdrop2", 0, 1, 2, 3, "2", "3", nan, inf, -inf, "", "   ", "next backdrop", "previous backdrop", "random backdrop" };
 
     Target target;
 
diff --git a/test/blocks/operator_blocks_test.cpp b/test/blocks/operator_blocks_test.cpp
index d902c700..3bb97f5c 100644
--- a/test/blocks/operator_blocks_test.cpp
+++ b/test/blocks/operator_blocks_test.cpp
@@ -832,7 +832,9 @@ TEST_F(OperatorBlocksTest, MathOpLn)
         vm::OP_HALT
     };
     static BlockFunc functions[] = { &OperatorBlocks::op_ln };
-    static Value constValues[] = { SpecialValue::NegativeInfinity, -2.5, 0, SpecialValue::NaN, std::exp(1), 50, SpecialValue::Infinity };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { -inf, -2.5, 0, nan, std::exp(1), 50, inf };
 
     VirtualMachine vm;
     vm.setBytecode(bytecode);
@@ -885,7 +887,9 @@ TEST_F(OperatorBlocksTest, MathOpLog)
         vm::OP_HALT
     };
     static BlockFunc functions[] = { &OperatorBlocks::op_log };
-    static Value constValues[] = { SpecialValue::NegativeInfinity, -2.5, 0, SpecialValue::NaN, 100, 1500, SpecialValue::Infinity };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { -inf, -2.5, 0, nan, 100, 1500, inf };
 
     VirtualMachine vm;
     vm.setBytecode(bytecode);
@@ -938,7 +942,9 @@ TEST_F(OperatorBlocksTest, MathOpExp)
         vm::OP_HALT
     };
     static BlockFunc functions[] = { &OperatorBlocks::op_eexp };
-    static Value constValues[] = { SpecialValue::NegativeInfinity, -3.25, 0, SpecialValue::NaN, 1, 5, SpecialValue::Infinity };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { -inf, -3.25, 0, nan, 1, 5, inf };
 
     VirtualMachine vm;
     vm.setBytecode(bytecode);
@@ -991,7 +997,9 @@ TEST_F(OperatorBlocksTest, MathOp10Exp)
         vm::OP_HALT
     };
     static BlockFunc functions[] = { &OperatorBlocks::op_10exp };
-    static Value constValues[] = { SpecialValue::NegativeInfinity, -2, 0, SpecialValue::NaN, 1, 5.5, SpecialValue::Infinity };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { -inf, -2, 0, nan, 1, 5.5, inf };
 
     VirtualMachine vm;
     vm.setBytecode(bytecode);
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 61ed118b..bb7c71f0 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -297,48 +297,6 @@ TEST(ValueTest, CStringConstructor)
     }
 }
 
-TEST(ValueTest, InfinityConstructor)
-{
-    Value v(SpecialValue::Infinity);
-    ASSERT_EQ(v.type(), ValueType::Infinity);
-    ASSERT_TRUE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_TRUE(v.isInt());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, NegativeInfinityConstructor)
-{
-    Value v(SpecialValue::NegativeInfinity);
-    ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_TRUE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_TRUE(v.isInt());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, NaNConstructor)
-{
-    Value v(SpecialValue::NaN);
-    ASSERT_EQ(v.type(), ValueType::NaN);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_TRUE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_FALSE(v.isValidNumber());
-    ASSERT_TRUE(v.isInt());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
 TEST(ValueTest, CopyConstructor)
 {
     {
@@ -396,46 +354,6 @@ TEST(ValueTest, CopyConstructor)
         ASSERT_FALSE(v2.isBool());
         ASSERT_TRUE(v2.isString());
     }
-
-    {
-        Value v1(SpecialValue::NaN);
-        Value v2(v1);
-        ASSERT_EQ(v2.toDouble(), 0);
-        ASSERT_EQ(v1.type(), v2.type());
-        ASSERT_FALSE(v2.isInfinity());
-        ASSERT_FALSE(v2.isNegativeInfinity());
-        ASSERT_TRUE(v2.isNaN());
-        ASSERT_FALSE(v2.isNumber());
-        ASSERT_FALSE(v2.isValidNumber());
-        ASSERT_FALSE(v2.isBool());
-        ASSERT_FALSE(v2.isString());
-    }
-
-    {
-        Value v1(SpecialValue::Infinity);
-        Value v2(v1);
-        ASSERT_EQ(v1.type(), v2.type());
-        ASSERT_TRUE(v2.isInfinity());
-        ASSERT_FALSE(v2.isNegativeInfinity());
-        ASSERT_FALSE(v2.isNaN());
-        ASSERT_FALSE(v2.isNumber());
-        ASSERT_TRUE(v2.isValidNumber());
-        ASSERT_FALSE(v2.isBool());
-        ASSERT_FALSE(v2.isString());
-    }
-
-    {
-        Value v1(SpecialValue::NegativeInfinity);
-        Value v2(v1);
-        ASSERT_EQ(v1.type(), v2.type());
-        ASSERT_FALSE(v2.isInfinity());
-        ASSERT_TRUE(v2.isNegativeInfinity());
-        ASSERT_FALSE(v2.isNaN());
-        ASSERT_FALSE(v2.isNumber());
-        ASSERT_TRUE(v2.isValidNumber());
-        ASSERT_FALSE(v2.isBool());
-        ASSERT_FALSE(v2.isString());
-    }
 }
 
 TEST(ValueTest, DoubleAssignment)
@@ -632,48 +550,6 @@ TEST(ValueTest, CStringAssignment)
     }
 }
 
-TEST(ValueTest, InfinityAssignment)
-{
-    Value v;
-    v = SpecialValue::Infinity;
-    ASSERT_EQ(v.type(), ValueType::Infinity);
-    ASSERT_TRUE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, NegativeInfinityAssignment)
-{
-    Value v;
-    v = SpecialValue::NegativeInfinity;
-    ASSERT_EQ(v.type(), ValueType::NegativeInfinity);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_TRUE(v.isNegativeInfinity());
-    ASSERT_FALSE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_TRUE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
-TEST(ValueTest, NaNAssignment)
-{
-    Value v;
-    v = SpecialValue::NaN;
-    ASSERT_EQ(v.type(), ValueType::NaN);
-    ASSERT_FALSE(v.isInfinity());
-    ASSERT_FALSE(v.isNegativeInfinity());
-    ASSERT_TRUE(v.isNaN());
-    ASSERT_FALSE(v.isNumber());
-    ASSERT_FALSE(v.isValidNumber());
-    ASSERT_FALSE(v.isBool());
-    ASSERT_FALSE(v.isString());
-}
-
 TEST(ValueTest, CopyAssignment)
 {
     {
@@ -735,49 +611,6 @@ TEST(ValueTest, CopyAssignment)
         ASSERT_FALSE(v2.isBool());
         ASSERT_TRUE(v2.isString());
     }
-
-    {
-        Value v1(SpecialValue::NaN);
-        Value v2;
-        v2 = v1;
-        ASSERT_EQ(v2.toDouble(), 0);
-        ASSERT_EQ(v1.type(), v2.type());
-        ASSERT_FALSE(v2.isInfinity());
-        ASSERT_FALSE(v2.isNegativeInfinity());
-        ASSERT_TRUE(v2.isNaN());
-        ASSERT_FALSE(v2.isNumber());
-        ASSERT_FALSE(v2.isValidNumber());
-        ASSERT_FALSE(v2.isBool());
-        ASSERT_FALSE(v2.isString());
-    }
-
-    {
-        Value v1(SpecialValue::Infinity);
-        Value v2;
-        v2 = v1;
-        ASSERT_EQ(v1.type(), v2.type());
-        ASSERT_TRUE(v2.isInfinity());
-        ASSERT_FALSE(v2.isNegativeInfinity());
-        ASSERT_FALSE(v2.isNaN());
-        ASSERT_FALSE(v2.isNumber());
-        ASSERT_TRUE(v2.isValidNumber());
-        ASSERT_FALSE(v2.isBool());
-        ASSERT_FALSE(v2.isString());
-    }
-
-    {
-        Value v1(SpecialValue::NegativeInfinity);
-        Value v2;
-        v2 = v1;
-        ASSERT_EQ(v1.type(), v2.type());
-        ASSERT_FALSE(v2.isInfinity());
-        ASSERT_TRUE(v2.isNegativeInfinity());
-        ASSERT_FALSE(v2.isNaN());
-        ASSERT_FALSE(v2.isNumber());
-        ASSERT_TRUE(v2.isValidNumber());
-        ASSERT_FALSE(v2.isBool());
-        ASSERT_FALSE(v2.isString());
-    }
 }
 
 TEST(ValueTest, ToInt)
@@ -1671,6 +1504,22 @@ TEST(ValueTest, ToString)
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
+    v = std::numeric_limits<double>::infinity();
+    cStrings.push_back(value_toCString(&v.data()));
+    ASSERT_EQ(v.toString(), "Infinity");
+    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
+    v = -std::numeric_limits<double>::infinity();
+    cStrings.push_back(value_toCString(&v.data()));
+    ASSERT_EQ(v.toString(), "-Infinity");
+    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
+    v = std::numeric_limits<double>::quiet_NaN();
+    cStrings.push_back(value_toCString(&v.data()));
+    ASSERT_EQ(v.toString(), "NaN");
+    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
+
     for (char *s : cStrings)
         free(s);
 }
@@ -2425,12 +2274,12 @@ TEST(ValueTest, EqualityOperators)
     }
 
     {
-        Value v1(SpecialValue::Infinity);
-        Value v2(SpecialValue::Infinity);
-        Value v3(SpecialValue::NegativeInfinity);
-        Value v4(SpecialValue::NegativeInfinity);
-        Value v5(SpecialValue::NaN);
-        Value v6(SpecialValue::NaN);
+        Value v1(std::numeric_limits<double>::infinity());
+        Value v2(std::numeric_limits<double>::infinity());
+        Value v3(-std::numeric_limits<double>::infinity());
+        Value v4(-std::numeric_limits<double>::infinity());
+        Value v5(std::numeric_limits<double>::quiet_NaN());
+        Value v6(std::numeric_limits<double>::quiet_NaN());
 
         ASSERT_TRUE(v1 == v2);
         ASSERT_FALSE(v1 != v2);
@@ -2563,9 +2412,9 @@ TEST(ValueTest, EqualityOperators)
     {
         Value v1 = 5;
         Value v2 = 0;
-        Value v3(SpecialValue::Infinity);
-        Value v4(SpecialValue::NegativeInfinity);
-        Value v5(SpecialValue::NaN);
+        Value v3(std::numeric_limits<double>::infinity());
+        Value v4(-std::numeric_limits<double>::infinity());
+        Value v5(std::numeric_limits<double>::quiet_NaN());
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
@@ -2667,9 +2516,9 @@ TEST(ValueTest, EqualityOperators)
     {
         Value v1 = true;
         Value v2 = false;
-        Value v3(SpecialValue::Infinity);
-        Value v4(SpecialValue::NegativeInfinity);
-        Value v5(SpecialValue::NaN);
+        Value v3(std::numeric_limits<double>::infinity());
+        Value v4(-std::numeric_limits<double>::infinity());
+        Value v5(std::numeric_limits<double>::quiet_NaN());
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
@@ -2691,9 +2540,9 @@ TEST(ValueTest, EqualityOperators)
         Value v4 = "-infinity";
         Value v5 = "NaN";
         Value v6 = "nan";
-        Value v7(SpecialValue::Infinity);
-        Value v8(SpecialValue::NegativeInfinity);
-        Value v9(SpecialValue::NaN);
+        Value v7(std::numeric_limits<double>::infinity());
+        Value v8(-std::numeric_limits<double>::infinity());
+        Value v9(std::numeric_limits<double>::quiet_NaN());
 
         // Infinity
         ASSERT_TRUE(v1 == v7);
@@ -2761,9 +2610,9 @@ TEST(ValueTest, EqualityOperators)
         Value v2 = " ";
         Value v3 = "";
         Value v4 = "0";
-        Value v5(SpecialValue::Infinity);
-        Value v6(SpecialValue::NegativeInfinity);
-        Value v7(SpecialValue::NaN);
+        Value v5(std::numeric_limits<double>::infinity());
+        Value v6(-std::numeric_limits<double>::infinity());
+        Value v7(std::numeric_limits<double>::quiet_NaN());
 
         // Infinity
         ASSERT_FALSE(v1 == v5);
@@ -2856,6 +2705,18 @@ TEST(ValueTest, DoubleToCString)
     ret = value_doubleToCString(-0.001);
     ASSERT_EQ(strcmp(ret, "-0.001"), 0);
     free(ret);
+
+    ret = value_doubleToCString(std::numeric_limits<double>::infinity());
+    ASSERT_EQ(strcmp(ret, "Infinity"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-std::numeric_limits<double>::infinity());
+    ASSERT_EQ(strcmp(ret, "-Infinity"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(std::numeric_limits<double>::quiet_NaN());
+    ASSERT_EQ(strcmp(ret, "NaN"), 0);
+    free(ret);
 }
 
 TEST(ValueTest, BoolToCString)
diff --git a/test/virtual_machine/virtual_machine_test.cpp b/test/virtual_machine/virtual_machine_test.cpp
index a6e123f4..892db4d5 100644
--- a/test/virtual_machine/virtual_machine_test.cpp
+++ b/test/virtual_machine/virtual_machine_test.cpp
@@ -995,7 +995,9 @@ TEST(VirtualMachineTest, OP_LIST_DEL)
         0,        OP_CONST, 9,  OP_LIST_DEL, 0, OP_READ_LIST, 0, OP_CONST, 10, OP_LIST_DEL, 0, OP_READ_LIST, 0, OP_CONST, 11, OP_LIST_DEL, 0, OP_READ_LIST,
         0,        OP_CONST, 12, OP_LIST_DEL, 0, OP_READ_LIST, 0, OP_CONST, 13, OP_LIST_DEL, 0, OP_READ_LIST, 0, OP_HALT
     };
-    static Value constValues[] = { 3, 1, "6", 0, 7, -1, 9, SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN, "invalid", "last", "random", "all" };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { 3, 1, "6", 0, 7, -1, 9, -inf, inf, nan, "invalid", "last", "random", "all" };
     List list1("", "list1");
     list1.append("a");
     list1.append("b");
@@ -1067,7 +1069,9 @@ TEST(VirtualMachineTest, OP_LIST_INSERT)
         0,        OP_CONST,       0,  OP_CONST,       10, OP_LIST_INSERT, 0, OP_READ_LIST,   0, OP_CONST,       0,  OP_CONST,       11, OP_LIST_INSERT, 0, OP_READ_LIST,   0, OP_CONST,
         0,        OP_CONST,       12, OP_LIST_INSERT, 0,  OP_READ_LIST,   0, OP_CONST,       0, OP_CONST,       13, OP_LIST_INSERT, 0,  OP_READ_LIST,   0, OP_HALT
     };
-    static Value constValues[] = { "new item", "3", 1, 10, 0, 12, -1, 14, SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN, "last", "random", "invalid" };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "new item", "3", 1, 10, 0, 12, -1, 14, -inf, inf, nan, "last", "random", "invalid" };
     List list1("", "list1");
     list1.append("a");
     list1.append("b");
@@ -1118,7 +1122,9 @@ TEST(VirtualMachineTest, OP_LIST_REPLACE)
         0,        OP_CONST,        10, OP_CONST,        0, OP_LIST_REPLACE, 0, OP_READ_LIST,    0,  OP_CONST,        11, OP_CONST,        11, OP_LIST_REPLACE, 0, OP_READ_LIST,    0, OP_CONST,
         12,       OP_CONST,        14, OP_LIST_REPLACE, 0, OP_READ_LIST,    0, OP_CONST,        13, OP_CONST,        0,  OP_LIST_REPLACE, 0,  OP_READ_LIST,    0, OP_HALT
     };
-    static Value constValues[] = { "new item", 3, "1", 8, 0, 9, -1, 12, SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN, "last", "random", "invalid", "test" };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "new item", 3, "1", 8, 0, 9, -1, 12, -inf, inf, nan, "last", "random", "invalid", "test" };
     List list1("", "list1");
     list1.append("a");
     list1.append("b");
@@ -1166,7 +1172,9 @@ TEST(VirtualMachineTest, OP_LIST_GET_ITEM)
         0,        OP_CONST, 5,  OP_LIST_GET_ITEM, 0, OP_CONST, 6,  OP_LIST_GET_ITEM, 0, OP_CONST, 7,  OP_LIST_GET_ITEM, 0, OP_CONST, 8, OP_LIST_GET_ITEM, 0, OP_CONST, 9, OP_LIST_GET_ITEM,
         0,        OP_CONST, 10, OP_LIST_GET_ITEM, 0, OP_CONST, 11, OP_LIST_GET_ITEM, 0, OP_CONST, 12, OP_LIST_GET_ITEM, 0, OP_HALT
     };
-    static Value constValues[] = { 3, 1, "8", 0, 9, -1, 12, SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN, "last", "random", "invalid" };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { 3, 1, "8", 0, 9, -1, 12, -inf, inf, nan, "last", "random", "invalid" };
     List list1("", "list1");
     list1.append("a");
     list1.append("b");
@@ -1213,7 +1221,9 @@ TEST(VirtualMachineTest, OP_LIST_INDEX_OF)
         OP_START, OP_CONST,         0, OP_LIST_INDEX_OF, 0, OP_CONST,         1, OP_LIST_INDEX_OF, 0, OP_CONST,         2, OP_LIST_INDEX_OF, 0, OP_CONST,         3, OP_LIST_INDEX_OF, 0, OP_CONST,
         4,        OP_LIST_INDEX_OF, 0, OP_CONST,         5, OP_LIST_INDEX_OF, 0, OP_CONST,         6, OP_LIST_INDEX_OF, 0, OP_CONST,         7, OP_LIST_INDEX_OF, 0, OP_HALT
     };
-    static Value constValues[] = { "c", "A", "e", "", "invalid", SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "c", "A", "e", "", "invalid", -inf, inf, nan };
     List list1("", "list1");
     list1.append("a");
     list1.append("b");
@@ -1221,7 +1231,7 @@ TEST(VirtualMachineTest, OP_LIST_INDEX_OF)
     list1.append("d");
     list1.append("e");
     list1.append("");
-    list1.append(SpecialValue::Infinity);
+    list1.append(inf);
     list1.append(8);
     List *lists[] = { &list1 };
 
@@ -1268,7 +1278,9 @@ TEST(VirtualMachineTest, OP_LIST_CONTAINS)
         OP_START, OP_CONST,         0, OP_LIST_CONTAINS, 0, OP_CONST,         1, OP_LIST_CONTAINS, 0, OP_CONST,         2, OP_LIST_CONTAINS, 0, OP_CONST,         3, OP_LIST_CONTAINS, 0, OP_CONST,
         4,        OP_LIST_CONTAINS, 0, OP_CONST,         5, OP_LIST_CONTAINS, 0, OP_CONST,         6, OP_LIST_CONTAINS, 0, OP_CONST,         7, OP_LIST_CONTAINS, 0, OP_HALT
     };
-    static Value constValues[] = { "c", "A", "e", "", "invalid", SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "c", "A", "e", "", "invalid", -inf, inf, nan };
     List list1("", "list1");
     list1.append("a");
     list1.append("b");
@@ -1276,7 +1288,7 @@ TEST(VirtualMachineTest, OP_LIST_CONTAINS)
     list1.append("d");
     list1.append("e");
     list1.append("");
-    list1.append(SpecialValue::Infinity);
+    list1.append(inf);
     list1.append(8);
     List *lists[] = { &list1 };
 
@@ -1299,7 +1311,8 @@ TEST(VirtualMachineTest, OP_LIST_CONTAINS)
 TEST(VirtualMachineTest, OP_STR_CONCAT)
 {
     static unsigned int bytecode[] = { OP_START, OP_CONST, 0, OP_CONST, 1, OP_STR_CONCAT, OP_CONST, 2, OP_STR_CONCAT, OP_CONST, 3, OP_STR_CONCAT, OP_HALT };
-    static Value constValues[] = { "abc ", "def", " ghi", SpecialValue::NegativeInfinity };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static Value constValues[] = { "abc ", "def", " ghi", -inf };
 
     VirtualMachine vm;
     vm.setBytecode(bytecode);
@@ -1316,7 +1329,9 @@ TEST(VirtualMachineTest, OP_STR_AT)
         OP_STR_AT, OP_CONST, 0, OP_CONST, 6,  OP_STR_AT, OP_CONST, 0, OP_CONST, 7,  OP_STR_AT, OP_CONST, 0, OP_CONST, 8,  OP_STR_AT, OP_CONST, 0, OP_CONST, 9, OP_STR_AT, OP_CONST, 0, OP_CONST, 10,
         OP_STR_AT, OP_CONST, 0, OP_CONST, 11, OP_STR_AT, OP_CONST, 0, OP_CONST, 12, OP_STR_AT, OP_CONST, 0, OP_CONST, 13, OP_STR_AT, OP_HALT
     };
-    static Value constValues[] = { "abcd efg  hijý", 3, 1, 14, 0, 15, -1, 16, SpecialValue::NegativeInfinity, SpecialValue::Infinity, SpecialValue::NaN, "last", "random", "invalid" };
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+    static Value constValues[] = { "abcd efg  hijý", 3, 1, 14, 0, 15, -1, 16, -inf, inf, nan, "last", "random", "invalid" };
 
     VirtualMachine vm;
     vm.setBytecode(bytecode);

From b96ae5b3c26352a335c5c8d28ceb6d969e48560b Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 18:44:12 +0200
Subject: [PATCH 58/94] Fix coroutines without any suspend point

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 27 ++++++++++++++++++---
 src/dev/engine/internal/llvmcodebuilder.h   |  2 ++
 test/dev/llvm/llvmcodebuilder_test.cpp      | 13 ++++++++++
 3 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 54abde95..4eaeb3e9 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -31,6 +31,14 @@ LLVMCodeBuilder::LLVMCodeBuilder(const std::string &id, bool warp) :
 
 std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
 {
+    // Do not create coroutine if there are no yield instructions
+    if (!m_warp) {
+        auto it = std::find_if(m_steps.begin(), m_steps.end(), [](const Step &step) { return step.type == Step::Type::Yield; });
+
+        if (it == m_steps.end())
+            m_warp = true;
+    }
+
     // Create function
     // void *f(Target *)
     llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
@@ -83,6 +91,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
+                    m_builder.CreateStore(m_builder.getInt1(true), coro.didSuspend);
                     llvm::BasicBlock *resumeBranch = llvm::BasicBlock::Create(m_ctx, "", func);
                     llvm::Value *noneToken = llvm::ConstantTokenNone::get(m_ctx);
                     llvm::Value *suspendResult = m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_suspend), { noneToken, m_builder.getInt1(false) });
@@ -296,11 +305,16 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     // Add final suspend point
     if (!m_warp) {
         llvm::BasicBlock *endBranch = llvm::BasicBlock::Create(m_ctx, "end", func);
+        llvm::BasicBlock *finalSuspendBranch = llvm::BasicBlock::Create(m_ctx, "finalSuspend", func);
+        m_builder.CreateCondBr(m_builder.CreateLoad(m_builder.getInt1Ty(), coro.didSuspend), finalSuspendBranch, endBranch);
+
+        m_builder.SetInsertPoint(finalSuspendBranch);
         llvm::Value *suspendResult =
             m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_suspend), { llvm::ConstantTokenNone::get(m_ctx), m_builder.getInt1(true) });
         llvm::SwitchInst *sw = m_builder.CreateSwitch(suspendResult, coro.suspend, 2);
-        sw->addCase(m_builder.getInt8(0), endBranch);
+        sw->addCase(m_builder.getInt8(0), endBranch); // unreachable
         sw->addCase(m_builder.getInt8(1), coro.cleanup);
+
         m_builder.SetInsertPoint(endBranch);
     }
 
@@ -314,7 +328,7 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     if (m_warp)
         m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType));
     else
-        m_builder.CreateBr(coro.cleanup);
+        m_builder.CreateBr(coro.freeMemRet);
 
     verifyFunction(func);
 
@@ -514,6 +528,8 @@ LLVMCodeBuilder::Coroutine LLVMCodeBuilder::initCoroutine(llvm::Function *func)
 
     // Begin
     coro.handle = m_builder.CreateCall(coroBegin, { coroIdRet, alloc });
+    coro.didSuspend = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, "didSuspend");
+    m_builder.CreateStore(m_builder.getInt1(false), coro.didSuspend);
     llvm::BasicBlock *entry = m_builder.GetInsertBlock();
 
     // Create suspend branch
@@ -522,7 +538,12 @@ LLVMCodeBuilder::Coroutine LLVMCodeBuilder::initCoroutine(llvm::Function *func)
     m_builder.CreateCall(coroEnd, { coro.handle, m_builder.getInt1(false), llvm::ConstantTokenNone::get(m_ctx) });
     m_builder.CreateRet(coro.handle);
 
-    // Create free branch
+    // Create free branches
+    coro.freeMemRet = llvm::BasicBlock::Create(m_ctx, "freeMemRet", func);
+    m_builder.SetInsertPoint(coro.freeMemRet);
+    m_builder.CreateFree(alloc);
+    m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType));
+
     llvm::BasicBlock *freeBranch = llvm::BasicBlock::Create(m_ctx, "free", func);
     m_builder.SetInsertPoint(freeBranch);
     m_builder.CreateFree(alloc);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index c61404fb..a0baafa0 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -103,6 +103,8 @@ class LLVMCodeBuilder : public ICodeBuilder
                 llvm::Value *handle = nullptr;
                 llvm::BasicBlock *suspend = nullptr;
                 llvm::BasicBlock *cleanup = nullptr;
+                llvm::BasicBlock *freeMemRet = nullptr;
+                llvm::Value *didSuspend = nullptr;
         };
 
         struct Procedure
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 656acdeb..9d6c76f2 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -62,6 +62,7 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls)
         testing::internal::CaptureStdout();
         code->run(ctx.get());
         ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+        ASSERT_TRUE(code->isFinished(ctx.get()));
     }
 }
 
@@ -558,6 +559,18 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
     code->run(ctx.get());
     ASSERT_TRUE(testing::internal::GetCapturedStdout().empty());
     ASSERT_TRUE(code->isFinished(ctx.get()));
+
+    // No warp no-op loop
+    createBuilder(false);
+
+    m_builder->addConstValue(0); // don't yield
+    m_builder->beginRepeatLoop();
+    m_builder->endLoop();
+
+    code = m_builder->finalize();
+    ctx = code->createExecutionContext(&m_target);
+    code->run(ctx.get());
+    ASSERT_TRUE(code->isFinished(ctx.get()));
 }
 
 TEST_F(LLVMCodeBuilderTest, WhileLoop)

From f2e045707c832a8087d7ddcee19730eaa789a141 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 18:48:32 +0200
Subject: [PATCH 59/94] Add test for repeat loop count rounding

---
 test/dev/llvm/llvmcodebuilder_test.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 9d6c76f2..4b095d66 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -445,7 +445,6 @@ TEST_F(LLVMCodeBuilderTest, IfStatement)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
-// TODO: Write a test for count rounding
 TEST_F(LLVMCodeBuilderTest, RepeatLoop)
 {
     createBuilder(true);
@@ -466,12 +465,18 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
     m_builder->addFunctionCall("test_function_no_args", Compiler::StaticType::Void, {});
     m_builder->endLoop();
 
-    m_builder->addConstValue("2");
+    m_builder->addConstValue("2.4");
     m_builder->beginRepeatLoop();
     m_builder->addConstValue(0);
     m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
     m_builder->endLoop();
 
+    m_builder->addConstValue("2.5");
+    m_builder->beginRepeatLoop();
+    m_builder->addConstValue(1);
+    m_builder->addFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String });
+    m_builder->endLoop();
+
     // Count returned by function
     m_builder->addConstValue(2);
     m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
@@ -513,6 +518,9 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop)
         "no_args\n"
         "1_arg 0\n"
         "1_arg 0\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
+        "1_arg 1\n"
         "no_args\n"
         "no_args\n"
         "1_arg 1\n"

From ebce2574f42ac9341d2031fed52e42f3e3774df0 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 19:22:24 +0200
Subject: [PATCH 60/94] LLVMCodeBuilder: Implement add operator

---
 src/dev/engine/internal/icodebuilder.h      |  1 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 43 ++++++++++++++++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  5 +++
 test/dev/llvm/llvmcodebuilder_test.cpp      | 45 +++++++++++++++++++++
 test/mocks/codebuildermock.h                |  1 +
 5 files changed, 95 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 5101ae75..4a651a02 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -24,6 +24,7 @@ class ICodeBuilder
         virtual void addVariableValue(Variable *variable) = 0;
         virtual void addListContents(List *list) = 0;
 
+        virtual void createAdd() = 0;
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 4eaeb3e9..99a3159b 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -88,6 +88,16 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::Add: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0];
+                const auto &arg2 = step.args[1];
+                llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first));
+                llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first));
+                step.functionReturnReg->value = m_builder.CreateFAdd(num1, num2);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -412,6 +422,11 @@ void LLVMCodeBuilder::addListContents(List *list)
 {
 }
 
+void LLVMCodeBuilder::createAdd()
+{
+    createOp(Step::Type::Add, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
@@ -785,6 +800,34 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type)
     }
 }
 
+llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num)
+{
+    // Replace NaN with zero
+    llvm::Value *isNaN = m_builder.CreateFCmpUNO(num, num);
+    return m_builder.CreateSelect(isNaN, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num);
+}
+
+void LLVMCodeBuilder::createOp(Step::Type type, size_t argCount)
+{
+    Step step(type);
+
+    assert(m_tmpRegs.size() >= argCount);
+    size_t j = 0;
+
+    for (size_t i = m_tmpRegs.size() - argCount; i < m_tmpRegs.size(); i++)
+        step.args.push_back({ Compiler::StaticType::Number, m_tmpRegs[i] });
+
+    m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
+
+    auto ret = std::make_shared<Register>(Compiler::StaticType::Number);
+    ret->isRawValue = true;
+    step.functionReturnReg = ret;
+    m_regs[m_currentFunction].push_back(ret);
+    m_tmpRegs.push_back(ret);
+
+    m_steps.push_back(step);
+}
+
 llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type)
 {
     return m_module->getOrInsertFunction(name, type);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index a0baafa0..80c7ecc6 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -26,6 +26,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void addVariableValue(Variable *variable) override;
         void addListContents(List *list) override;
 
+        void createAdd() override;
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -58,6 +59,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                 enum class Type
                 {
                     FunctionCall,
+                    Add,
                     Yield,
                     BeginIf,
                     BeginElse,
@@ -124,6 +126,9 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::Value *castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
         llvm::Value *castConstValue(const Value &value, Compiler::StaticType targetType);
         llvm::Type *getType(Compiler::StaticType type);
+        llvm::Value *removeNaN(llvm::Value *num);
+
+        void createOp(Step::Type type, size_t argCount);
 
         llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type);
         llvm::FunctionCallee resolve_value_init();
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 4b095d66..061f611f 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -200,6 +200,51 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
+TEST_F(LLVMCodeBuilderTest, Add)
+{
+    std::string expected;
+
+    auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createAdd();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->addConstValue(v2);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->createAdd();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(expectedResult).toString() + '\n';
+        expected += str;
+        expected += str;
+    };
+
+    createBuilder(true);
+
+    addOpTest(50, 25, 75);
+    addOpTest(-500, 25, -475);
+    addOpTest(-500, -25, -525);
+    addOpTest("2.54", "6.28", 8.82);
+    addOpTest(2.54, "-6.28", -3.74);
+    addOpTest(true, true, 2);
+    addOpTest("Infinity", "Infinity", std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", "-Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest("-Infinity", "Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest("-Infinity", "-Infinity", -std::numeric_limits<double>::infinity());
+    addOpTest(1, "NaN", 1);
+    addOpTest("NaN", 1, 1);
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 0000ed25..a766b9f4 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -14,6 +14,7 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, addVariableValue, (Variable *), (override));
         MOCK_METHOD(void, addListContents, (List *), (override));
 
+        MOCK_METHOD(void, createAdd, (), (override));
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From 3260662e5ddde89bb13c793649ee88416ba5b0b1 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 19:37:36 +0200
Subject: [PATCH 61/94] LLVMCodeBuilder: Implement subtract operator

---
 src/dev/engine/internal/icodebuilder.h      |  1 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 15 +++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  2 +
 test/dev/llvm/llvmcodebuilder_test.cpp      | 45 +++++++++++++++++++++
 test/mocks/codebuildermock.h                |  1 +
 5 files changed, 64 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 4a651a02..1835df00 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -25,6 +25,7 @@ class ICodeBuilder
         virtual void addListContents(List *list) = 0;
 
         virtual void createAdd() = 0;
+        virtual void createSub() = 0;
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 99a3159b..a0fe572c 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -98,6 +98,16 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::Sub: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0];
+                const auto &arg2 = step.args[1];
+                llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first));
+                llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first));
+                step.functionReturnReg->value = m_builder.CreateFSub(num1, num2);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -427,6 +437,11 @@ void LLVMCodeBuilder::createAdd()
     createOp(Step::Type::Add, 2);
 }
 
+void LLVMCodeBuilder::createSub()
+{
+    createOp(Step::Type::Sub, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 80c7ecc6..a7422800 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -27,6 +27,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void addListContents(List *list) override;
 
         void createAdd() override;
+        void createSub() override;
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -60,6 +61,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                 {
                     FunctionCall,
                     Add,
+                    Sub,
                     Yield,
                     BeginIf,
                     BeginElse,
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 061f611f..822e15a4 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -245,6 +245,51 @@ TEST_F(LLVMCodeBuilderTest, Add)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
+TEST_F(LLVMCodeBuilderTest, Subtract)
+{
+    std::string expected;
+
+    auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createSub();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->addConstValue(v2);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->createSub();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(expectedResult).toString() + '\n';
+        expected += str;
+        expected += str;
+    };
+
+    createBuilder(true);
+
+    addOpTest(50, 25, 25);
+    addOpTest(-500, 25, -525);
+    addOpTest(-500, -25, -475);
+    addOpTest("2.54", "6.28", -3.74);
+    addOpTest(2.54, "-6.28", 8.82);
+    addOpTest(true, true, 0);
+    addOpTest("Infinity", "Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest("Infinity", "-Infinity", std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", "Infinity", -std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", "-Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest(1, "NaN", 1);
+    addOpTest("NaN", 1, -1);
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index a766b9f4..c2888407 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -15,6 +15,7 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, addListContents, (List *), (override));
 
         MOCK_METHOD(void, createAdd, (), (override));
+        MOCK_METHOD(void, createSub, (), (override));
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From e75947e3fabd2eef6531fc0ed194215999eb0839 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 19:40:24 +0200
Subject: [PATCH 62/94] LLVMCodeBuilder: Implement multiply operator

---
 src/dev/engine/internal/icodebuilder.h      |  1 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 15 +++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  2 +
 test/dev/llvm/llvmcodebuilder_test.cpp      | 50 +++++++++++++++++++++
 test/mocks/codebuildermock.h                |  1 +
 5 files changed, 69 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 1835df00..e2139b57 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -26,6 +26,7 @@ class ICodeBuilder
 
         virtual void createAdd() = 0;
         virtual void createSub() = 0;
+        virtual void createMul() = 0;
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index a0fe572c..be0955ca 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -108,6 +108,16 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::Mul: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0];
+                const auto &arg2 = step.args[1];
+                llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first));
+                llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first));
+                step.functionReturnReg->value = m_builder.CreateFMul(num1, num2);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -442,6 +452,11 @@ void LLVMCodeBuilder::createSub()
     createOp(Step::Type::Sub, 2);
 }
 
+void LLVMCodeBuilder::createMul()
+{
+    createOp(Step::Type::Mul, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index a7422800..fbf782b3 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -28,6 +28,7 @@ class LLVMCodeBuilder : public ICodeBuilder
 
         void createAdd() override;
         void createSub() override;
+        void createMul() override;
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -62,6 +63,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     FunctionCall,
                     Add,
                     Sub,
+                    Mul,
                     Yield,
                     BeginIf,
                     BeginElse,
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 822e15a4..d1d3f733 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -290,6 +290,56 @@ TEST_F(LLVMCodeBuilderTest, Subtract)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
+TEST_F(LLVMCodeBuilderTest, Multiply)
+{
+    std::string expected;
+
+    auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createMul();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->addConstValue(v2);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->createMul();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(expectedResult).toString() + '\n';
+        expected += str;
+        expected += str;
+    };
+
+    createBuilder(true);
+
+    addOpTest(50, 2, 100);
+    addOpTest(-500, 25, -12500);
+    addOpTest("-500", -25, 12500);
+    addOpTest("2.54", "6.28", 15.9512);
+    addOpTest(true, true, 1);
+    addOpTest("Infinity", "Infinity", std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", 0, std::numeric_limits<double>::quiet_NaN());
+    addOpTest("Infinity", 2, std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", -2, -std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", "-Infinity", -std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", "Infinity", -std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", 0, std::numeric_limits<double>::quiet_NaN());
+    addOpTest("-Infinity", 2, -std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", -2, std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", "-Infinity", std::numeric_limits<double>::infinity());
+    addOpTest(1, "NaN", 0);
+    addOpTest("NaN", 1, 0);
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index c2888407..777938bc 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -16,6 +16,7 @@ class CodeBuilderMock : public ICodeBuilder
 
         MOCK_METHOD(void, createAdd, (), (override));
         MOCK_METHOD(void, createSub, (), (override));
+        MOCK_METHOD(void, createMul, (), (override));
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From 31a4e3685cd8d9222c950190e0a887665457b488 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 19:55:47 +0200
Subject: [PATCH 63/94] Fix conversion of -0.0 to string

---
 src/scratch/value_functions_p.h     |  5 ++++-
 test/scratch_classes/value_test.cpp | 20 ++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h
index cffe2ab7..1c660731 100644
--- a/src/scratch/value_functions_p.h
+++ b/src/scratch/value_functions_p.h
@@ -409,7 +409,10 @@ extern "C"
 
 inline void value_doubleToString(double v, std::string *dst)
 {
-    if (std::isinf(v)) {
+    if (v == 0) {
+        dst->assign("0");
+        return;
+    } else if (std::isinf(v)) {
         if (v > 0)
             dst->assign("Infinity");
         else
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index bb7c71f0..e869a88c 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -1331,6 +1331,18 @@ TEST(ValueTest, ToString)
     ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
     ASSERT_EQ(std::string(cStrings.back()), v.toString());
 
+    v = 0.0;
+    cStrings.push_back(value_toCString(&v.data()));
+    ASSERT_EQ(v.toString(), "0");
+    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
+
+    v = -0.0;
+    cStrings.push_back(value_toCString(&v.data()));
+    ASSERT_EQ(v.toString(), "0");
+    ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
+    ASSERT_EQ(std::string(cStrings.back()), v.toString());
+
     v = 2.0;
     cStrings.push_back(value_toCString(&v.data()));
     ASSERT_EQ(v.toString(), "2");
@@ -2658,6 +2670,14 @@ TEST(ValueTest, EqualityOperators)
 TEST(ValueTest, DoubleToCString)
 {
     char *ret;
+    ret = value_doubleToCString(0.0);
+    ASSERT_EQ(strcmp(ret, "0"), 0);
+    free(ret);
+
+    ret = value_doubleToCString(-0.0);
+    ASSERT_EQ(strcmp(ret, "0"), 0);
+    free(ret);
+
     ret = value_doubleToCString(2.0);
     ASSERT_EQ(strcmp(ret, "2"), 0);
     free(ret);

From 9183e2be8917e983bf5d7cd50cc6efa4a33d7b3d Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 19:56:13 +0200
Subject: [PATCH 64/94] LLVMCodeBuilder: Implement divide operator

---
 src/dev/engine/internal/icodebuilder.h      |  2 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 15 ++++++
 src/dev/engine/internal/llvmcodebuilder.h   |  3 ++
 test/dev/llvm/llvmcodebuilder_test.cpp      | 59 +++++++++++++++++++++
 test/mocks/codebuildermock.h                |  2 +
 5 files changed, 81 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index e2139b57..c3ddd9dd 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -27,6 +27,8 @@ class ICodeBuilder
         virtual void createAdd() = 0;
         virtual void createSub() = 0;
         virtual void createMul() = 0;
+        virtual void createDiv() = 0;
+
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index be0955ca..6a12ad59 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -118,6 +118,16 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::Div: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0];
+                const auto &arg2 = step.args[1];
+                llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first));
+                llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first));
+                step.functionReturnReg->value = m_builder.CreateFDiv(num1, num2);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -457,6 +467,11 @@ void LLVMCodeBuilder::createMul()
     createOp(Step::Type::Mul, 2);
 }
 
+void LLVMCodeBuilder::createDiv()
+{
+    createOp(Step::Type::Div, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index fbf782b3..fb7366cc 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -29,6 +29,8 @@ class LLVMCodeBuilder : public ICodeBuilder
         void createAdd() override;
         void createSub() override;
         void createMul() override;
+        void createDiv() override;
+
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -64,6 +66,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     Add,
                     Sub,
                     Mul,
+                    Div,
                     Yield,
                     BeginIf,
                     BeginElse,
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index d1d3f733..406b0db0 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -340,6 +340,65 @@ TEST_F(LLVMCodeBuilderTest, Multiply)
     ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
+TEST_F(LLVMCodeBuilderTest, Divide)
+{
+    std::string expected;
+
+    auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createDiv();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->addConstValue(v2);
+        m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+        m_builder->createDiv();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(expectedResult).toString() + '\n';
+        expected += str;
+        expected += str;
+    };
+
+    createBuilder(true);
+
+    addOpTest(50, 2, 25);
+    addOpTest(-500, 25, -20);
+    addOpTest("-500", -25, 20);
+    addOpTest("3.5", "2.5", 1.4);
+    addOpTest(true, true, 1);
+    addOpTest("Infinity", "Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest("Infinity", 0, std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", 2, std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", -2, -std::numeric_limits<double>::infinity());
+    addOpTest("Infinity", "-Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest("-Infinity", "Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest("-Infinity", 0, -std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", 2, -std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", -2, std::numeric_limits<double>::infinity());
+    addOpTest("-Infinity", "-Infinity", std::numeric_limits<double>::quiet_NaN());
+    addOpTest(0, "Infinity", 0);
+    addOpTest(2, "Infinity", 0);
+    addOpTest(-2, "Infinity", 0);
+    addOpTest(0, "-Infinity", 0);
+    addOpTest(2, "-Infinity", 0);
+    addOpTest(-2, "-Infinity", 0);
+    addOpTest(1, "NaN", std::numeric_limits<double>::infinity());
+    addOpTest("NaN", 1, 0);
+    addOpTest(5, 0, std::numeric_limits<double>::infinity());
+    addOpTest(-5, 0, -std::numeric_limits<double>::infinity());
+    addOpTest(0, 0, std::numeric_limits<double>::quiet_NaN());
+
+    auto code = m_builder->finalize();
+    auto ctx = code->createExecutionContext(&m_target);
+
+    testing::internal::CaptureStdout();
+    code->run(ctx.get());
+    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 777938bc..fa452b5c 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -17,6 +17,8 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, createAdd, (), (override));
         MOCK_METHOD(void, createSub, (), (override));
         MOCK_METHOD(void, createMul, (), (override));
+        MOCK_METHOD(void, createDiv, (), (override));
+
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From 9e7d3d0df756a378e7bf9c72064591f97bff1cde Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 13 Oct 2024 20:01:08 +0200
Subject: [PATCH 65/94] Compiler: Add methods for add, sub, mul and div
 operators

---
 include/scratchcpp/dev/compiler.h   |  5 +++
 src/dev/engine/compiler.cpp         | 24 +++++++++++++
 test/dev/compiler/compiler_test.cpp | 52 +++++++++++++++++++++++++++++
 3 files changed, 81 insertions(+)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index 5432a520..bb5da1d5 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -47,6 +47,11 @@ class LIBSCRATCHCPP_EXPORT Compiler
         void addListContents(List *list);
         void addInput(const std::string &name);
 
+        void createAdd();
+        void createSub();
+        void createMul();
+        void createDiv();
+
         void moveToIf(std::shared_ptr<Block> substack);
         void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
         void moveToRepeatLoop(std::shared_ptr<Block> substack);
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 355fec48..bb999224 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -102,6 +102,30 @@ void Compiler::addInput(const std::string &name)
     addInput(impl->block->inputAt(impl->block->findInput(name)).get());
 }
 
+/*! Creates an add instruction from the last 2 values. */
+void Compiler::createAdd()
+{
+    impl->builder->createAdd();
+}
+
+/*! Creates a subtract instruction from the last 2 values. */
+void Compiler::createSub()
+{
+    impl->builder->createSub();
+}
+
+/*! Creates a multiply instruction from the last 2 values. */
+void Compiler::createMul()
+{
+    impl->builder->createMul();
+}
+
+/*! Creates a divide instruction from the last 2 values. */
+void Compiler::createDiv()
+{
+    impl->builder->createDiv();
+}
+
 /*! Jumps to the given if substack. */
 void Compiler::moveToIf(std::shared_ptr<Block> substack)
 {
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index e96d2cc5..1b2044ac 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -219,6 +219,58 @@ TEST_F(CompilerTest, AddInput)
     compile(compiler, block);
 }
 
+TEST_F(CompilerTest, CreateAdd)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createAdd);
+        compiler->createAdd();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateSub)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createSub);
+        compiler->createSub();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateMul)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createMul);
+        compiler->createMul();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateDiv)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createDiv);
+        compiler->createDiv();
+    });
+
+    compile(compiler, block);
+}
+
 TEST_F(CompilerTest, MoveToIf)
 {
     Compiler compiler(&m_engine, &m_target);

From 833565be284c98b8f7c5c444a055b3d9eb598523 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Mon, 14 Oct 2024 23:59:03 +0200
Subject: [PATCH 66/94] LLVMConstValue: Return constant in castConstValue()

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 2 +-
 src/dev/engine/internal/llvmcodebuilder.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 6a12ad59..92b5b82e 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -806,7 +806,7 @@ llvm::Value *LLVMCodeBuilder::castRawValue(std::shared_ptr<Register> reg, Compil
     }
 }
 
-llvm::Value *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType)
+llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType)
 {
     switch (targetType) {
         case Compiler::StaticType::Number:
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index fb7366cc..02db5c8b 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -131,7 +131,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void freeHeap();
         llvm::Value *castValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
         llvm::Value *castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
-        llvm::Value *castConstValue(const Value &value, Compiler::StaticType targetType);
+        llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType);
         llvm::Type *getType(Compiler::StaticType type);
         llvm::Value *removeNaN(llvm::Value *num);
 

From 361fbb0fc4dc2fa106ff264a308087984a1dbd07 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Mon, 14 Oct 2024 23:59:37 +0200
Subject: [PATCH 67/94] LLVMCodeBuilder: Fix ValueData definition

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 92b5b82e..24d98c0a 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -546,19 +546,11 @@ void LLVMCodeBuilder::yield()
 void LLVMCodeBuilder::initTypes()
 {
     // Create the ValueData struct
-    llvm::Type *doubleType = llvm::Type::getDoubleTy(m_ctx);                             // double (numberValue)
-    llvm::Type *boolType = llvm::Type::getInt1Ty(m_ctx);                                 // bool (boolValue)
-    llvm::Type *stringPtrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); // char* (stringValue)
+    llvm::Type *unionType = m_builder.getInt64Ty(); // 64 bits is the largest size
 
-    // Create the union type (largest type size should dominate)
-    llvm::StructType *unionType = llvm::StructType::create(m_ctx, "union");
-    unionType->setBody({ doubleType, boolType, stringPtrType });
-
-    // Create the full struct type
     llvm::Type *valueType = llvm::Type::getInt32Ty(m_ctx); // Assuming ValueType is a 32-bit enum
     llvm::Type *sizeType = llvm::Type::getInt64Ty(m_ctx);  // size_t
 
-    // Combine them into the full struct
     m_valueDataType = llvm::StructType::create(m_ctx, "ValueData");
     m_valueDataType->setBody({ unionType, valueType, sizeType });
 }

From 0e92dc8d7ba59a11d743d7d0083d6a983a7276a5 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 00:00:12 +0200
Subject: [PATCH 68/94] LLVMCodeBuilder: Make createOp() universal

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 12 ++++++------
 src/dev/engine/internal/llvmcodebuilder.h   |  3 ++-
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 24d98c0a..386ada09 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -454,22 +454,22 @@ void LLVMCodeBuilder::addListContents(List *list)
 
 void LLVMCodeBuilder::createAdd()
 {
-    createOp(Step::Type::Add, 2);
+    createOp(Step::Type::Add, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createSub()
 {
-    createOp(Step::Type::Sub, 2);
+    createOp(Step::Type::Sub, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createMul()
 {
-    createOp(Step::Type::Mul, 2);
+    createOp(Step::Type::Mul, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createDiv()
 {
-    createOp(Step::Type::Div, 2);
+    createOp(Step::Type::Div, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::beginIfStatement()
@@ -844,7 +844,7 @@ llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num)
     return m_builder.CreateSelect(isNaN, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num);
 }
 
-void LLVMCodeBuilder::createOp(Step::Type type, size_t argCount)
+void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, size_t argCount)
 {
     Step step(type);
 
@@ -856,7 +856,7 @@ void LLVMCodeBuilder::createOp(Step::Type type, size_t argCount)
 
     m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
 
-    auto ret = std::make_shared<Register>(Compiler::StaticType::Number);
+    auto ret = std::make_shared<Register>(retType);
     ret->isRawValue = true;
     step.functionReturnReg = ret;
     m_regs[m_currentFunction].push_back(ret);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 02db5c8b..7d8c1996 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -135,7 +135,8 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::Type *getType(Compiler::StaticType type);
         llvm::Value *removeNaN(llvm::Value *num);
 
-        void createOp(Step::Type type, size_t argCount);
+        void createOp(Step::Type type, Compiler::StaticType retType, size_t argCount);
+        llvm::Value *createValue(std::shared_ptr<Register> reg);
 
         llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type);
         llvm::FunctionCallee resolve_value_init();

From 9752ff907b46ff3da9b33136b61009557f3f6c50 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 11:28:23 +0200
Subject: [PATCH 69/94] LLVMCodeBuilder: Don't print optimized IR

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 386ada09..e1447f15 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -400,12 +400,6 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
     // Optimize
     optimize();
 
-#ifdef PRINT_LLVM_IR
-    std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl;
-    m_module->print(llvm::outs(), nullptr);
-    std::cout << "==============" << std::endl << std::endl;
-#endif
-
     return std::make_shared<LLVMExecutableCode>(std::move(m_module));
 }
 

From 624392848bda74b72391cbd0352a24330d573053 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 11:29:59 +0200
Subject: [PATCH 70/94] LLVMCodeBuilder: Make operator tests easier to debug

---
 test/dev/llvm/llvmcodebuilder_test.cpp | 93 ++++++++++++++------------
 1 file changed, 49 insertions(+), 44 deletions(-)

diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 406b0db0..1e7f9403 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -9,6 +9,7 @@
 using namespace libscratchcpp;
 
 using ::testing::Return;
+using ::testing::Eq;
 
 class LLVMCodeBuilderTest : public testing::Test
 {
@@ -205,6 +206,8 @@ TEST_F(LLVMCodeBuilderTest, Add)
     std::string expected;
 
     auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        createBuilder(true);
+
         m_builder->addConstValue(v1);
         m_builder->addConstValue(v2);
         m_builder->createAdd();
@@ -218,11 +221,17 @@ TEST_F(LLVMCodeBuilderTest, Add)
         m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         std::string str = Value(expectedResult).toString() + '\n';
-        expected += str;
-        expected += str;
-    };
+        std::string expected = str + str;
 
-    createBuilder(true);
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
 
     addOpTest(50, 25, 75);
     addOpTest(-500, 25, -475);
@@ -236,13 +245,6 @@ TEST_F(LLVMCodeBuilderTest, Add)
     addOpTest("-Infinity", "-Infinity", -std::numeric_limits<double>::infinity());
     addOpTest(1, "NaN", 1);
     addOpTest("NaN", 1, 1);
-
-    auto code = m_builder->finalize();
-    auto ctx = code->createExecutionContext(&m_target);
-
-    testing::internal::CaptureStdout();
-    code->run(ctx.get());
-    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
 TEST_F(LLVMCodeBuilderTest, Subtract)
@@ -250,6 +252,8 @@ TEST_F(LLVMCodeBuilderTest, Subtract)
     std::string expected;
 
     auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        createBuilder(true);
+
         m_builder->addConstValue(v1);
         m_builder->addConstValue(v2);
         m_builder->createSub();
@@ -263,11 +267,17 @@ TEST_F(LLVMCodeBuilderTest, Subtract)
         m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         std::string str = Value(expectedResult).toString() + '\n';
-        expected += str;
-        expected += str;
-    };
+        std::string expected = str + str;
 
-    createBuilder(true);
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
 
     addOpTest(50, 25, 25);
     addOpTest(-500, 25, -525);
@@ -281,13 +291,6 @@ TEST_F(LLVMCodeBuilderTest, Subtract)
     addOpTest("-Infinity", "-Infinity", std::numeric_limits<double>::quiet_NaN());
     addOpTest(1, "NaN", 1);
     addOpTest("NaN", 1, -1);
-
-    auto code = m_builder->finalize();
-    auto ctx = code->createExecutionContext(&m_target);
-
-    testing::internal::CaptureStdout();
-    code->run(ctx.get());
-    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
 TEST_F(LLVMCodeBuilderTest, Multiply)
@@ -295,6 +298,8 @@ TEST_F(LLVMCodeBuilderTest, Multiply)
     std::string expected;
 
     auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        createBuilder(true);
+
         m_builder->addConstValue(v1);
         m_builder->addConstValue(v2);
         m_builder->createMul();
@@ -308,11 +313,17 @@ TEST_F(LLVMCodeBuilderTest, Multiply)
         m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         std::string str = Value(expectedResult).toString() + '\n';
-        expected += str;
-        expected += str;
-    };
+        std::string expected = str + str;
 
-    createBuilder(true);
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
 
     addOpTest(50, 2, 100);
     addOpTest(-500, 25, -12500);
@@ -331,13 +342,6 @@ TEST_F(LLVMCodeBuilderTest, Multiply)
     addOpTest("-Infinity", "-Infinity", std::numeric_limits<double>::infinity());
     addOpTest(1, "NaN", 0);
     addOpTest("NaN", 1, 0);
-
-    auto code = m_builder->finalize();
-    auto ctx = code->createExecutionContext(&m_target);
-
-    testing::internal::CaptureStdout();
-    code->run(ctx.get());
-    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
 TEST_F(LLVMCodeBuilderTest, Divide)
@@ -345,6 +349,8 @@ TEST_F(LLVMCodeBuilderTest, Divide)
     std::string expected;
 
     auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) {
+        createBuilder(true);
+
         m_builder->addConstValue(v1);
         m_builder->addConstValue(v2);
         m_builder->createDiv();
@@ -358,11 +364,17 @@ TEST_F(LLVMCodeBuilderTest, Divide)
         m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
         std::string str = Value(expectedResult).toString() + '\n';
-        expected += str;
-        expected += str;
-    };
+        std::string expected = str + str;
 
-    createBuilder(true);
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
 
     addOpTest(50, 2, 25);
     addOpTest(-500, 25, -20);
@@ -390,13 +402,6 @@ TEST_F(LLVMCodeBuilderTest, Divide)
     addOpTest(5, 0, std::numeric_limits<double>::infinity());
     addOpTest(-5, 0, -std::numeric_limits<double>::infinity());
     addOpTest(0, 0, std::numeric_limits<double>::quiet_NaN());
-
-    auto code = m_builder->finalize();
-    auto ctx = code->createExecutionContext(&m_target);
-
-    testing::internal::CaptureStdout();
-    code->run(ctx.get());
-    ASSERT_EQ(testing::internal::GetCapturedStdout(), expected);
 }
 
 TEST_F(LLVMCodeBuilderTest, Yield)

From ff362614d4a2534b1f83db7e057abbef55db5508 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 12:12:46 +0200
Subject: [PATCH 71/94] Compiler: Add Unknown static type

---
 include/scratchcpp/dev/compiler.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index bb5da1d5..e98b1605 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -29,7 +29,8 @@ class LIBSCRATCHCPP_EXPORT Compiler
             Void,
             Number,
             Bool,
-            String
+            String,
+            Unknown
         };
 
         Compiler(IEngine *engine, Target *target);

From 29859b51d0bb03809c8e8abbf174c55504bf54fc Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 12:13:40 +0200
Subject: [PATCH 72/94] LLVMCodeBuilder: Implement equal comparison

---
 src/dev/engine/internal/icodebuilder.h      |   1 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 223 +++++++++++++++++++-
 src/dev/engine/internal/llvmcodebuilder.h   |  15 ++
 test/dev/llvm/llvmcodebuilder_test.cpp      | 191 +++++++++++++++++
 test/mocks/codebuildermock.h                |   1 +
 5 files changed, 429 insertions(+), 2 deletions(-)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index c3ddd9dd..6fd80778 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -29,6 +29,7 @@ class ICodeBuilder
         virtual void createMul() = 0;
         virtual void createDiv() = 0;
 
+        virtual void createCmpEQ() = 0;
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index e1447f15..cc51e404 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -128,6 +128,14 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::CmpEQ: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0].second;
+                const auto &arg2 = step.args[1].second;
+                step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::EQ);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -466,6 +474,11 @@ void LLVMCodeBuilder::createDiv()
     createOp(Step::Type::Div, Compiler::StaticType::Number, 2);
 }
 
+void LLVMCodeBuilder::createCmpEQ()
+{
+    createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
@@ -831,11 +844,15 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type)
     }
 }
 
+llvm::Value *LLVMCodeBuilder::isNaN(llvm::Value *num)
+{
+    return m_builder.CreateFCmpUNO(num, num);
+}
+
 llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num)
 {
     // Replace NaN with zero
-    llvm::Value *isNaN = m_builder.CreateFCmpUNO(num, num);
-    return m_builder.CreateSelect(isNaN, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num);
+    return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num);
 }
 
 void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, size_t argCount)
@@ -859,6 +876,184 @@ void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, si
     m_steps.push_back(step);
 }
 
+llvm::Value *LLVMCodeBuilder::createValue(std::shared_ptr<Register> reg)
+{
+    if (reg->isConstValue) {
+        // Create a constant ValueData instance and store it
+        llvm::Constant *value = castConstValue(reg->constValue, TYPE_MAP[reg->constValue.type()]);
+        llvm::Value *ret = m_builder.CreateAlloca(m_valueDataType);
+
+        if (reg->constValue.type() == ValueType::String)
+            value = llvm::ConstantExpr::getPtrToInt(value, m_valueDataType->getElementType(0));
+        else
+            value = llvm::ConstantExpr::getBitCast(value, m_valueDataType->getElementType(0));
+
+        llvm::Constant *type = m_builder.getInt32(static_cast<uint32_t>(reg->constValue.type()));
+        llvm::Constant *constValue = llvm::ConstantStruct::get(m_valueDataType, { value, type, m_builder.getInt64(0) });
+        m_builder.CreateStore(constValue, ret);
+
+        return ret;
+    } else if (reg->isRawValue) {
+        llvm::Value *value = castRawValue(reg, reg->type);
+        llvm::Value *ret = m_builder.CreateAlloca(m_valueDataType);
+
+        // Store value
+        llvm::Value *valueField = m_builder.CreateStructGEP(m_valueDataType, ret, 0);
+        m_builder.CreateStore(value, valueField);
+
+        auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [&reg](const std::pair<ValueType, Compiler::StaticType> &pair) { return pair.second == reg->type; });
+
+        if (it == TYPE_MAP.end()) {
+            assert(false);
+            return nullptr;
+        }
+
+        // Store type
+        llvm::Value *typeField = m_builder.CreateStructGEP(m_valueDataType, ret, 1);
+        ValueType type = it->first;
+        m_builder.CreateStore(m_builder.getInt32(static_cast<uint32_t>(type)), typeField);
+
+        return ret;
+    } else
+        return reg->value;
+}
+
+llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr<Register> arg1, std::shared_ptr<Register> arg2, Comparison type)
+{
+    auto type1 = arg1->type;
+    auto type2 = arg2->type;
+
+    if (arg1->isConstValue && arg2->isConstValue) {
+        // If both operands are constant, perform the comparison at compile time
+        bool result = false;
+
+        switch (type) {
+            case Comparison::EQ:
+                result = arg1->constValue == arg2->constValue;
+                break;
+
+            case Comparison::GT:
+                result = arg1->constValue > arg2->constValue;
+                break;
+
+            case Comparison::LT:
+                result = arg1->constValue < arg2->constValue;
+                break;
+
+            default:
+                assert(false);
+                return nullptr;
+        }
+
+        return m_builder.getInt1(result);
+    } else {
+        // Optimize comparison of constant with number/bool
+        if (arg1->isConstValue && arg1->constValue.isValidNumber() && (type2 == Compiler::StaticType::Number || type2 == Compiler::StaticType::Bool))
+            type1 = Compiler::StaticType::Number;
+
+        if (arg2->isConstValue && arg2->constValue.isValidNumber() && (type1 == Compiler::StaticType::Number || type1 == Compiler::StaticType::Bool))
+            type2 = Compiler::StaticType::Number;
+
+        // Optimize number and bool comparison
+        if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool)
+            type2 = Compiler::StaticType::Number;
+
+        if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number)
+            type1 = Compiler::StaticType::Number;
+
+        if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) {
+            // If the types are different or at least one of them
+            // is unknown, we must use value functions
+            llvm::Value *value1 = createValue(arg1);
+            llvm::Value *value2 = createValue(arg2);
+
+            switch (type) {
+                case Comparison::EQ:
+                    return m_builder.CreateCall(resolve_value_equals(), { value1, value2 });
+
+                case Comparison::GT:
+                    return m_builder.CreateCall(resolve_value_greater(), { value1, value2 });
+
+                case Comparison::LT:
+                    return m_builder.CreateCall(resolve_value_lower(), { value1, value2 });
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+        } else {
+            // Compare raw values
+            llvm::Value *value1 = castValue(arg1, type1);
+            llvm::Value *value2 = castValue(arg2, type2);
+            assert(type1 == type2);
+
+            switch (type1) {
+                case Compiler::StaticType::Number: {
+                    // Compare two numbers
+                    switch (type) {
+                        case Comparison::EQ: {
+                            llvm::Value *nan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN
+                            llvm::Value *cmp = m_builder.CreateFCmpOEQ(value1, value2);
+                            return m_builder.CreateSelect(nan, m_builder.getInt1(true), cmp);
+                        }
+
+                        case Comparison::GT:
+                            return m_builder.CreateFCmpOGT(value1, value2);
+
+                        case Comparison::LT:
+                            return m_builder.CreateFCmpOLT(value1, value2);
+
+                        default:
+                            assert(false);
+                            return nullptr;
+                    }
+                }
+
+                case Compiler::StaticType::Bool:
+                    // Compare two booleans
+                    switch (type) {
+                        case Comparison::EQ:
+                            return m_builder.CreateICmpEQ(value1, value2);
+
+                        case Comparison::GT:
+                            return m_builder.CreateICmpSGT(value1, value2);
+
+                        case Comparison::LT:
+                            return m_builder.CreateICmpSLT(value1, value2);
+
+                        default:
+                            assert(false);
+                            return nullptr;
+                    }
+
+                case Compiler::StaticType::String: {
+                    // Compare two strings
+                    llvm::Value *cmpRet = m_builder.CreateCall(resolve_strcasecmp(), { value1, value2 });
+
+                    switch (type) {
+                        case Comparison::EQ:
+                            return m_builder.CreateICmpEQ(cmpRet, m_builder.getInt32(0));
+
+                        case Comparison::GT:
+                            return m_builder.CreateICmpSGT(cmpRet, m_builder.getInt32(0));
+
+                        case Comparison::LT:
+                            return m_builder.CreateICmpSLT(cmpRet, m_builder.getInt32(0));
+
+                        default:
+                            assert(false);
+                            return nullptr;
+                    }
+                }
+
+                default:
+                    assert(false);
+                    return nullptr;
+            }
+        }
+    }
+}
+
 llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type)
 {
     return m_module->getOrInsertFunction(name, type);
@@ -933,3 +1128,27 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToBool()
 {
     return resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder.getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false));
 }
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_equals()
+{
+    llvm::Type *valuePtr = m_valueDataType->getPointerTo();
+    return resolveFunction("value_equals", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_greater()
+{
+    llvm::Type *valuePtr = m_valueDataType->getPointerTo();
+    return resolveFunction("value_greater", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_value_lower()
+{
+    llvm::Type *valuePtr = m_valueDataType->getPointerTo();
+    return resolveFunction("value_lower", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false));
+}
+
+llvm::FunctionCallee LLVMCodeBuilder::resolve_strcasecmp()
+{
+    llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0);
+    return resolveFunction("strcasecmp", llvm::FunctionType::get(m_builder.getInt32Ty(), { pointerType, pointerType }, false));
+}
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 7d8c1996..6cec0cc6 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -31,6 +31,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void createMul() override;
         void createDiv() override;
 
+        void createCmpEQ() override;
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -67,6 +68,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     Sub,
                     Mul,
                     Div,
+                    CmpEQ,
                     Yield,
                     BeginIf,
                     BeginElse,
@@ -122,6 +124,13 @@ class LLVMCodeBuilder : public ICodeBuilder
                 bool warp = false;
         };
 
+        enum class Comparison
+        {
+            EQ,
+            GT,
+            LT
+        };
+
         void initTypes();
 
         Coroutine initCoroutine(llvm::Function *func);
@@ -133,10 +142,12 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::Value *castRawValue(std::shared_ptr<Register> reg, Compiler::StaticType targetType);
         llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType);
         llvm::Type *getType(Compiler::StaticType type);
+        llvm::Value *isNaN(llvm::Value *num);
         llvm::Value *removeNaN(llvm::Value *num);
 
         void createOp(Step::Type type, Compiler::StaticType retType, size_t argCount);
         llvm::Value *createValue(std::shared_ptr<Register> reg);
+        llvm::Value *createComparison(std::shared_ptr<Register> arg1, std::shared_ptr<Register> arg2, Comparison type);
 
         llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type);
         llvm::FunctionCallee resolve_value_init();
@@ -153,6 +164,10 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::FunctionCallee resolve_value_boolToCString();
         llvm::FunctionCallee resolve_value_stringToDouble();
         llvm::FunctionCallee resolve_value_stringToBool();
+        llvm::FunctionCallee resolve_value_equals();
+        llvm::FunctionCallee resolve_value_greater();
+        llvm::FunctionCallee resolve_value_lower();
+        llvm::FunctionCallee resolve_strcasecmp();
 
         std::string m_id;
         llvm::LLVMContext m_ctx;
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 1e7f9403..9e444826 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -21,6 +21,27 @@ class LLVMCodeBuilderTest : public testing::Test
 
         void createBuilder(bool warp) { m_builder = std::make_unique<LLVMCodeBuilder>("test", warp); }
 
+        void callConstFuncForType(ValueType type)
+        {
+            switch (type) {
+                case ValueType::Number:
+                    m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number });
+                    break;
+
+                case ValueType::Bool:
+                    m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool });
+                    break;
+
+                case ValueType::String:
+                    m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String });
+                    break;
+
+                default:
+                    FAIL();
+                    break;
+            }
+        }
+
         std::unique_ptr<LLVMCodeBuilder> m_builder;
         TargetMock m_target; // NOTE: isStage() is used for call expectations
 };
@@ -404,6 +425,176 @@ TEST_F(LLVMCodeBuilderTest, Divide)
     addOpTest(0, 0, std::numeric_limits<double>::quiet_NaN());
 }
 
+TEST_F(LLVMCodeBuilderTest, EqualComparison)
+{
+    auto addOpTest = [this](Value v1, Value v2, bool expectedResult) {
+        createBuilder(true);
+
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createCmpEQ();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        callConstFuncForType(v1.type());
+        m_builder->addConstValue(v2);
+        callConstFuncForType(v2.type());
+        m_builder->createCmpEQ();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(expectedResult).toString() + '\n';
+        std::string expected = str + str;
+
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
+
+    addOpTest(10, 10, true);
+    addOpTest(10, 8, false);
+    addOpTest(8, 10, false);
+
+    addOpTest(-4.25, -4.25, true);
+    addOpTest(-4.25, 5.312, false);
+    addOpTest(5.312, -4.25, false);
+
+    addOpTest(true, true, true);
+    addOpTest(true, false, false);
+    addOpTest(false, true, false);
+
+    addOpTest(1, true, true);
+    addOpTest(1, false, false);
+
+    addOpTest("abC def", "abC def", true);
+    addOpTest("abC def", "abc dEf", true);
+    addOpTest("abC def", "ghi Jkl", false);
+    addOpTest("abC def", "hello world", false);
+
+    addOpTest(" ", "", false);
+    addOpTest(" ", "0", false);
+    addOpTest(" ", 0, false);
+    addOpTest(0, " ", false);
+    addOpTest("", "0", false);
+    addOpTest("", 0, false);
+    addOpTest(0, "", false);
+    addOpTest("0", 0, true);
+    addOpTest(0, "0", true);
+
+    addOpTest(5.25, "5.25", true);
+    addOpTest("5.25", 5.25, true);
+    addOpTest(5.25, " 5.25", true);
+    addOpTest(" 5.25", 5.25, true);
+    addOpTest(5.25, "5.25 ", true);
+    addOpTest("5.25 ", 5.25, true);
+    addOpTest(5.25, " 5.25 ", true);
+    addOpTest(" 5.25 ", 5.25, true);
+    addOpTest(5.25, "5.26", false);
+    addOpTest("5.26", 5.25, false);
+    addOpTest("5.25", "5.26", false);
+    addOpTest(5, "5  ", true);
+    addOpTest("5  ", 5, true);
+    addOpTest(0, "1", false);
+    addOpTest("1", 0, false);
+    addOpTest(0, "test", false);
+    addOpTest("test", 0, false);
+
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+
+    addOpTest(inf, inf, true);
+    addOpTest(-inf, -inf, true);
+    addOpTest(nan, nan, true);
+    addOpTest(inf, -inf, false);
+    addOpTest(-inf, inf, false);
+    addOpTest(inf, nan, false);
+    addOpTest(nan, inf, false);
+    addOpTest(-inf, nan, false);
+    addOpTest(nan, -inf, false);
+
+    addOpTest(5, inf, false);
+    addOpTest(5, -inf, false);
+    addOpTest(5, nan, false);
+    addOpTest(0, nan, false);
+
+    addOpTest(true, "true", true);
+    addOpTest("true", true, true);
+    addOpTest(false, "false", true);
+    addOpTest("false", false, true);
+    addOpTest(false, "true", false);
+    addOpTest("true", false, false);
+    addOpTest(true, "false", false);
+    addOpTest("false", true, false);
+    addOpTest(true, "TRUE", true);
+    addOpTest("TRUE", true, true);
+    addOpTest(false, "FALSE", true);
+    addOpTest("FALSE", false, true);
+
+    addOpTest(true, "00001", true);
+    addOpTest("00001", true, true);
+    addOpTest(true, "00000", false);
+    addOpTest("00000", true, false);
+    addOpTest(false, "00000", true);
+    addOpTest("00000", false, true);
+
+    addOpTest("true", 1, false);
+    addOpTest(1, "true", false);
+    addOpTest("true", 0, false);
+    addOpTest(0, "true", false);
+    addOpTest("false", 0, false);
+    addOpTest(0, "false", false);
+    addOpTest("false", 1, false);
+    addOpTest(1, "false", false);
+
+    addOpTest("true", "TRUE", true);
+    addOpTest("true", "FALSE", false);
+    addOpTest("false", "FALSE", true);
+    addOpTest("false", "TRUE", false);
+
+    addOpTest(true, inf, false);
+    addOpTest(true, -inf, false);
+    addOpTest(true, nan, false);
+    addOpTest(false, inf, false);
+    addOpTest(false, -inf, false);
+    addOpTest(false, nan, false);
+
+    addOpTest("Infinity", inf, true);
+    addOpTest("Infinity", -inf, false);
+    addOpTest("Infinity", nan, false);
+    addOpTest("infinity", inf, true);
+    addOpTest("infinity", -inf, false);
+    addOpTest("infinity", nan, false);
+    addOpTest("-Infinity", inf, false);
+    addOpTest("-Infinity", -inf, true);
+    addOpTest("-Infinity", nan, false);
+    addOpTest("-infinity", inf, false);
+    addOpTest("-infinity", -inf, true);
+    addOpTest("-infinity", nan, false);
+    addOpTest("NaN", inf, false);
+    addOpTest("NaN", -inf, false);
+    addOpTest("NaN", nan, true);
+    addOpTest("nan", inf, false);
+    addOpTest("nan", -inf, false);
+    addOpTest("nan", nan, true);
+
+    addOpTest(inf, "abc", false);
+    addOpTest(inf, " ", false);
+    addOpTest(inf, "", false);
+    addOpTest(inf, "0", false);
+    addOpTest(-inf, "abc", false);
+    addOpTest(-inf, " ", false);
+    addOpTest(-inf, "", false);
+    addOpTest(-inf, "0", false);
+    addOpTest(nan, "abc", false);
+    addOpTest(nan, " ", false);
+    addOpTest(nan, "", false);
+    addOpTest(nan, "0", false);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index fa452b5c..473ded0d 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -19,6 +19,7 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, createMul, (), (override));
         MOCK_METHOD(void, createDiv, (), (override));
 
+        MOCK_METHOD(void, createCmpEQ, (), (override));
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From c847df46e321616370bdbd09ed89f31695ea62f5 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 12:53:55 +0200
Subject: [PATCH 73/94] Fix GT and LT value comparison to match Scratch

---
 src/engine/virtualmachine_p.cpp     | 10 ++--
 src/scratch/value_functions.cpp     | 80 +------------------------
 src/scratch/value_functions_p.h     | 40 +++++++++++++
 test/scratch_classes/value_test.cpp | 92 ++++++++++++++++++++++++++++-
 4 files changed, 140 insertions(+), 82 deletions(-)

diff --git a/src/engine/virtualmachine_p.cpp b/src/engine/virtualmachine_p.cpp
index 6addee35..b1768b91 100644
--- a/src/engine/virtualmachine_p.cpp
+++ b/src/engine/virtualmachine_p.cpp
@@ -537,20 +537,22 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     OP(ASIN) :
     {
         const Value &v = *READ_REG(0, 1);
-        if (v < -1 || v > 1)
+        const double num = v.toDouble();
+        if (num < -1 || num > 1)
             REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else
-            REPLACE_RET_VALUE(std::asin(v.toDouble()) * 180 / pi, 1);
+            REPLACE_RET_VALUE(std::asin(num) * 180 / pi, 1);
         DISPATCH();
     }
 
     OP(ACOS) :
     {
         const Value &v = *READ_REG(0, 1);
-        if (v < -1 || v > 1)
+        const double num = v.toDouble();
+        if (num < -1 || num > 1)
             REPLACE_RET_VALUE(std::numeric_limits<double>::quiet_NaN(), 1);
         else
-            REPLACE_RET_VALUE(std::acos(v.toDouble()) * 180 / pi, 1);
+            REPLACE_RET_VALUE(std::acos(num) * 180 / pi, 1);
         DISPATCH();
     }
 
diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index d13264d0..b7b678b7 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -372,93 +372,19 @@ extern "C"
     /*! Returns true if the given values are equal. */
     bool value_equals(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2)
     {
-        // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150
-        assert(v1 && v2);
-
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number) {
-            if (std::isnan(v1->numberValue) && std::isnan(v2->numberValue))
-                return true;
-
-            return v1->numberValue == v2->numberValue;
-        } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
-            return v1->boolValue == v2->boolValue;
-
-        bool ok;
-        double n1 = value_getNumber(v1, &ok);
-        double n2;
-
-        if (ok)
-            n2 = value_getNumber(v2, &ok);
-
-        if (!ok) {
-            // At least one argument can't be converted to a number
-            // Scratch compares strings as case insensitive
-            std::u16string s1, s2;
-            value_toUtf16(v1, &s1);
-            value_toUtf16(v2, &s2);
-            return value_u16StringsEqual(s1, s2);
-        }
-
-        // Handle the special case of Infinity
-        if ((static_cast<int>(v1->type) < 0) && (static_cast<int>(v2->type) < 0)) {
-            assert(!value_isNaN(v1));
-            assert(!value_isNaN(v2));
-            return v1->type == v2->type;
-        }
-
-        // Compare as numbers
-        return n1 == n2;
+        return value_compare(v1, v2) == 0;
     }
 
     /*! Returns true if the first value is greater than the second value. */
     bool value_greater(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2)
     {
-        assert(v1 && v2);
-
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
-            return v1->numberValue > v2->numberValue;
-        else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
-            return v1->boolValue > v2->boolValue;
-
-        double n1, n2;
-
-        if (v1->type == ValueType::String)
-            n1 = value_stringToDouble(v1->stringValue);
-        else
-            n1 = value_toDouble(v1);
-
-        if (v2->type == ValueType::String)
-            n2 = value_stringToDouble(v2->stringValue);
-        else
-            n2 = value_toDouble(v2);
-
-        return n1 > n2;
+        return value_compare(v1, v2) > 0;
     }
 
     /*! Returns true if the first value is lower than the second value. */
     bool value_lower(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2)
     {
-        assert(v1 && v2);
-
-        if (v1->type == ValueType::Number && v2->type == ValueType::Number)
-            return v1->numberValue < v2->numberValue;
-        else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
-            return v1->boolValue < v2->boolValue;
-
-        return value_toDouble(v1) < value_toDouble(v2);
-        double n1, n2;
-
-        if (v1->type == ValueType::String)
-            n1 = value_stringToDouble(v1->stringValue);
-        else
-            n1 = value_toDouble(v1);
-
-        if (v2->type == ValueType::String)
-            n2 = value_stringToDouble(v2->stringValue);
-        else
-            n2 = value_toDouble(v2);
-
-        return n1 < n2;
+        return value_compare(v1, v2) < 0;
     }
 }
 
diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h
index 1c660731..ca497e99 100644
--- a/src/scratch/value_functions_p.h
+++ b/src/scratch/value_functions_p.h
@@ -539,6 +539,46 @@ extern "C"
         else
             return false;
     }
+
+    inline double value_compare(const ValueData *v1, const ValueData *v2)
+    {
+        // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150
+        assert(v1 && v2);
+
+        if (v1->type == ValueType::Number && v2->type == ValueType::Number && !std::isnan(v1->numberValue) && !std::isnan(v2->numberValue)) {
+            // Handle the special case of Infinity
+            if ((value_isInf(v1->numberValue) && value_isInf(v2->numberValue)) || (value_isNegativeInf(v1->numberValue) && value_isNegativeInf(v2->numberValue)))
+                return 0;
+
+            return v1->numberValue - v2->numberValue;
+        } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool)
+            return v1->boolValue - v2->boolValue;
+
+        bool ok;
+        double n1 = value_getNumber(v1, &ok);
+        double n2;
+
+        if (ok)
+            n2 = value_getNumber(v2, &ok);
+
+        if (!ok) {
+            // At least one argument can't be converted to a number
+            // Scratch compares strings as case insensitive
+            char *s1 = value_toCString(v1);
+            char *s2 = value_toCString(v2);
+            int ret = strcasecmp(s1, s2);
+            free(s1);
+            free(s2);
+            return ret;
+        }
+
+        // Handle the special case of Infinity
+        if ((value_isInf(n1) && value_isInf(n2)) || (value_isNegativeInf(n1) && value_isNegativeInf(n2)))
+            return 0;
+
+        // Compare as numbers
+        return n1 - n2;
+    }
 }
 
 } // namespace libscratchcpp
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index e869a88c..87d8d382 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -2188,7 +2188,7 @@ TEST(ValueTest, ModOperator)
     ASSERT_EQ(v % "-Infinity", -150);
 }
 
-TEST(ValueTest, EqualityOperators)
+TEST(ValueTest, ComparisonOperators)
 {
     {
         Value v1 = 10;
@@ -2200,9 +2200,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_TRUE(v1 > v3);
+        ASSERT_FALSE(v1 < v3);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_TRUE(v2 > v3);
+        ASSERT_FALSE(v2 < v3);
     }
 
     {
@@ -2215,9 +2219,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_TRUE(v1 < v3);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_FALSE(v2 > v3);
+        ASSERT_TRUE(v2 < v3);
     }
 
     {
@@ -2230,9 +2238,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_TRUE(v1 > v3);
+        ASSERT_FALSE(v1 < v3);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_TRUE(v2 > v3);
+        ASSERT_FALSE(v2 < v3);
     }
 
     {
@@ -2252,12 +2264,18 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v4);
         ASSERT_TRUE(v1 != v4);
+        ASSERT_FALSE(v1 > v4);
+        ASSERT_TRUE(v1 < v4);
 
         ASSERT_FALSE(v2 == v4);
         ASSERT_TRUE(v2 != v4);
+        ASSERT_FALSE(v2 > v4);
+        ASSERT_TRUE(v2 < v4);
 
         ASSERT_FALSE(v3 == v4);
         ASSERT_TRUE(v3 != v4);
+        ASSERT_FALSE(v3 > v4);
+        ASSERT_TRUE(v3 < v4);
     }
 
     {
@@ -2268,18 +2286,28 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v2);
         ASSERT_TRUE(v1 != v2);
+        ASSERT_TRUE(v1 > v2);
+        ASSERT_FALSE(v1 < v2);
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_TRUE(v1 < v3);
 
         ASSERT_FALSE(v1 == v4);
         ASSERT_TRUE(v1 != v4);
+        ASSERT_FALSE(v1 > v4);
+        ASSERT_TRUE(v1 < v4);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_FALSE(v2 > v3);
+        ASSERT_TRUE(v2 < v3);
 
         ASSERT_FALSE(v2 == v4);
         ASSERT_TRUE(v2 != v4);
+        ASSERT_FALSE(v2 > v4);
+        ASSERT_TRUE(v2 < v4);
 
         ASSERT_TRUE(v3 == v4);
         ASSERT_FALSE(v3 != v4);
@@ -2304,6 +2332,8 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_TRUE(v1 > v3);
+        ASSERT_FALSE(v1 < v3);
 
         ASSERT_FALSE(v1 == v5);
         ASSERT_TRUE(v1 != v5);
@@ -2322,9 +2352,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_TRUE(v1 < v3);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_FALSE(v2 > v3);
+        ASSERT_TRUE(v2 < v3);
     }
 
     {
@@ -2337,9 +2371,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_TRUE(v1 > v3);
+        ASSERT_FALSE(v1 < v3);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_TRUE(v2 > v3);
+        ASSERT_FALSE(v2 < v3);
     }
 
     {
@@ -2370,9 +2408,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v7);
         ASSERT_TRUE(v1 != v7);
+        ASSERT_FALSE(v1 > v7);
+        ASSERT_TRUE(v1 < v7);
 
         ASSERT_FALSE(v2 == v7);
         ASSERT_TRUE(v2 != v7);
+        ASSERT_FALSE(v2 > v7);
+        ASSERT_TRUE(v2 < v7);
 
         ASSERT_FALSE(v3 == v7);
         ASSERT_TRUE(v3 != v7);
@@ -2401,6 +2443,8 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_TRUE(v1 < v3);
 
         ASSERT_FALSE(v1 == v4);
         ASSERT_TRUE(v1 != v4);
@@ -2416,9 +2460,13 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_TRUE(v1 > v3);
+        ASSERT_FALSE(v1 < v3);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_TRUE(v2 > v3);
+        ASSERT_FALSE(v2 < v3);
     }
 
     {
@@ -2430,15 +2478,29 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_TRUE(v1 < v3);
 
         ASSERT_FALSE(v1 == v4);
         ASSERT_TRUE(v1 != v4);
+        ASSERT_TRUE(v1 > v4);
+        ASSERT_FALSE(v1 < v4);
 
         ASSERT_FALSE(v1 == v5);
         ASSERT_TRUE(v1 != v5);
 
         ASSERT_FALSE(v2 == v5);
         ASSERT_TRUE(v2 != v5);
+        ASSERT_FALSE(v2 > v5);
+        ASSERT_TRUE(v2 < v5);
+
+        // NaN vs Infinity
+        ASSERT_FALSE(v3 > v5);
+        ASSERT_TRUE(v2 < v5);
+
+        // NaN vs -Infinity
+        ASSERT_FALSE(v4 > v5);
+        ASSERT_TRUE(v2 < v5);
     }
 
     {
@@ -2451,15 +2513,23 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_TRUE(v1 == v3);
         ASSERT_FALSE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_FALSE(v1 < v3);
 
         ASSERT_TRUE(v2 == v4);
         ASSERT_FALSE(v2 != v4);
+        ASSERT_FALSE(v2 > v4);
+        ASSERT_FALSE(v2 < v4);
 
         ASSERT_FALSE(v1 == v2);
         ASSERT_TRUE(v1 != v2);
+        ASSERT_TRUE(v1 > v2);
+        ASSERT_FALSE(v1 < v2);
 
         ASSERT_FALSE(v2 == v3);
         ASSERT_TRUE(v2 != v3);
+        ASSERT_FALSE(v2 > v3);
+        ASSERT_TRUE(v2 < v3);
 
         ASSERT_TRUE(v1 == v5);
         ASSERT_FALSE(v1 != v5);
@@ -2479,6 +2549,8 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_FALSE(v1 == v4);
         ASSERT_TRUE(v1 != v4);
+        ASSERT_TRUE(v1 > v4);
+        ASSERT_FALSE(v1 < v4);
 
         ASSERT_TRUE(v2 == v4);
         ASSERT_FALSE(v2 != v4);
@@ -2559,12 +2631,18 @@ TEST(ValueTest, EqualityOperators)
         // Infinity
         ASSERT_TRUE(v1 == v7);
         ASSERT_FALSE(v1 != v7);
+        ASSERT_FALSE(v1 > v7);
+        ASSERT_FALSE(v1 < v7);
 
         ASSERT_FALSE(v1 == v8);
         ASSERT_TRUE(v1 != v8);
+        ASSERT_TRUE(v1 > v8);
+        ASSERT_FALSE(v1 < v8);
 
         ASSERT_FALSE(v1 == v9);
         ASSERT_TRUE(v1 != v9);
+        ASSERT_FALSE(v1 > v7);
+        ASSERT_FALSE(v1 < v7);
 
         // infinity
         ASSERT_TRUE(v2 == v7);
@@ -2615,6 +2693,18 @@ TEST(ValueTest, EqualityOperators)
 
         ASSERT_TRUE(v6 == v9);
         ASSERT_FALSE(v6 != v9);
+
+        // NaN vs Infinity
+        ASSERT_TRUE(v5 > v7);
+        ASSERT_FALSE(v5 < v7);
+        ASSERT_FALSE(v1 > v9);
+        ASSERT_TRUE(v1 < v9);
+
+        // NaN vs -Infinity
+        ASSERT_TRUE(v5 > v8);
+        ASSERT_FALSE(v5 < v8);
+        ASSERT_FALSE(v3 > v9);
+        ASSERT_TRUE(v3 < v9);
     }
 
     {

From a70d2800d10e55b672631a2ec4e951fe8a38ce42 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 15:50:47 +0200
Subject: [PATCH 74/94] LLVMCodeBuilder: Remove expected result from comparison
 test

---
 test/dev/llvm/llvmcodebuilder_test.cpp | 272 ++++++++++++-------------
 1 file changed, 136 insertions(+), 136 deletions(-)

diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 9e444826..82bc89d9 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -427,7 +427,7 @@ TEST_F(LLVMCodeBuilderTest, Divide)
 
 TEST_F(LLVMCodeBuilderTest, EqualComparison)
 {
-    auto addOpTest = [this](Value v1, Value v2, bool expectedResult) {
+    auto addOpTest = [this](Value v1, Value v2) {
         createBuilder(true);
 
         m_builder->addConstValue(v1);
@@ -442,7 +442,7 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison)
         m_builder->createCmpEQ();
         m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
 
-        std::string str = Value(expectedResult).toString() + '\n';
+        std::string str = Value(v1 == v2).toString() + '\n';
         std::string expected = str + str;
 
         auto code = m_builder->finalize();
@@ -455,144 +455,144 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison)
         ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
     };
 
-    addOpTest(10, 10, true);
-    addOpTest(10, 8, false);
-    addOpTest(8, 10, false);
-
-    addOpTest(-4.25, -4.25, true);
-    addOpTest(-4.25, 5.312, false);
-    addOpTest(5.312, -4.25, false);
-
-    addOpTest(true, true, true);
-    addOpTest(true, false, false);
-    addOpTest(false, true, false);
-
-    addOpTest(1, true, true);
-    addOpTest(1, false, false);
-
-    addOpTest("abC def", "abC def", true);
-    addOpTest("abC def", "abc dEf", true);
-    addOpTest("abC def", "ghi Jkl", false);
-    addOpTest("abC def", "hello world", false);
-
-    addOpTest(" ", "", false);
-    addOpTest(" ", "0", false);
-    addOpTest(" ", 0, false);
-    addOpTest(0, " ", false);
-    addOpTest("", "0", false);
-    addOpTest("", 0, false);
-    addOpTest(0, "", false);
-    addOpTest("0", 0, true);
-    addOpTest(0, "0", true);
-
-    addOpTest(5.25, "5.25", true);
-    addOpTest("5.25", 5.25, true);
-    addOpTest(5.25, " 5.25", true);
-    addOpTest(" 5.25", 5.25, true);
-    addOpTest(5.25, "5.25 ", true);
-    addOpTest("5.25 ", 5.25, true);
-    addOpTest(5.25, " 5.25 ", true);
-    addOpTest(" 5.25 ", 5.25, true);
-    addOpTest(5.25, "5.26", false);
-    addOpTest("5.26", 5.25, false);
-    addOpTest("5.25", "5.26", false);
-    addOpTest(5, "5  ", true);
-    addOpTest("5  ", 5, true);
-    addOpTest(0, "1", false);
-    addOpTest("1", 0, false);
-    addOpTest(0, "test", false);
-    addOpTest("test", 0, false);
+    addOpTest(10, 10);
+    addOpTest(10, 8);
+    addOpTest(8, 10);
+
+    addOpTest(-4.25, -4.25);
+    addOpTest(-4.25, 5.312);
+    addOpTest(5.312, -4.25);
+
+    addOpTest(true, true);
+    addOpTest(true, false);
+    addOpTest(false, true);
+
+    addOpTest(1, true);
+    addOpTest(1, false);
+
+    addOpTest("abC def", "abC def");
+    addOpTest("abC def", "abc dEf");
+    addOpTest("abC def", "ghi Jkl");
+    addOpTest("abC def", "hello world");
+
+    addOpTest(" ", "");
+    addOpTest(" ", "0");
+    addOpTest(" ", 0);
+    addOpTest(0, " ");
+    addOpTest("", "0");
+    addOpTest("", 0);
+    addOpTest(0, "");
+    addOpTest("0", 0);
+    addOpTest(0, "0");
+
+    addOpTest(5.25, "5.25");
+    addOpTest("5.25", 5.25);
+    addOpTest(5.25, " 5.25");
+    addOpTest(" 5.25", 5.25);
+    addOpTest(5.25, "5.25 ");
+    addOpTest("5.25 ", 5.25);
+    addOpTest(5.25, " 5.25 ");
+    addOpTest(" 5.25 ", 5.25);
+    addOpTest(5.25, "5.26");
+    addOpTest("5.26", 5.25);
+    addOpTest("5.25", "5.26");
+    addOpTest(5, "5  ");
+    addOpTest("5  ", 5);
+    addOpTest(0, "1");
+    addOpTest("1", 0);
+    addOpTest(0, "test");
+    addOpTest("test", 0);
 
     static const double inf = std::numeric_limits<double>::infinity();
     static const double nan = std::numeric_limits<double>::quiet_NaN();
 
-    addOpTest(inf, inf, true);
-    addOpTest(-inf, -inf, true);
-    addOpTest(nan, nan, true);
-    addOpTest(inf, -inf, false);
-    addOpTest(-inf, inf, false);
-    addOpTest(inf, nan, false);
-    addOpTest(nan, inf, false);
-    addOpTest(-inf, nan, false);
-    addOpTest(nan, -inf, false);
-
-    addOpTest(5, inf, false);
-    addOpTest(5, -inf, false);
-    addOpTest(5, nan, false);
-    addOpTest(0, nan, false);
-
-    addOpTest(true, "true", true);
-    addOpTest("true", true, true);
-    addOpTest(false, "false", true);
-    addOpTest("false", false, true);
-    addOpTest(false, "true", false);
-    addOpTest("true", false, false);
-    addOpTest(true, "false", false);
-    addOpTest("false", true, false);
-    addOpTest(true, "TRUE", true);
-    addOpTest("TRUE", true, true);
-    addOpTest(false, "FALSE", true);
-    addOpTest("FALSE", false, true);
-
-    addOpTest(true, "00001", true);
-    addOpTest("00001", true, true);
-    addOpTest(true, "00000", false);
-    addOpTest("00000", true, false);
-    addOpTest(false, "00000", true);
-    addOpTest("00000", false, true);
-
-    addOpTest("true", 1, false);
-    addOpTest(1, "true", false);
-    addOpTest("true", 0, false);
-    addOpTest(0, "true", false);
-    addOpTest("false", 0, false);
-    addOpTest(0, "false", false);
-    addOpTest("false", 1, false);
-    addOpTest(1, "false", false);
-
-    addOpTest("true", "TRUE", true);
-    addOpTest("true", "FALSE", false);
-    addOpTest("false", "FALSE", true);
-    addOpTest("false", "TRUE", false);
-
-    addOpTest(true, inf, false);
-    addOpTest(true, -inf, false);
-    addOpTest(true, nan, false);
-    addOpTest(false, inf, false);
-    addOpTest(false, -inf, false);
-    addOpTest(false, nan, false);
-
-    addOpTest("Infinity", inf, true);
-    addOpTest("Infinity", -inf, false);
-    addOpTest("Infinity", nan, false);
-    addOpTest("infinity", inf, true);
-    addOpTest("infinity", -inf, false);
-    addOpTest("infinity", nan, false);
-    addOpTest("-Infinity", inf, false);
-    addOpTest("-Infinity", -inf, true);
-    addOpTest("-Infinity", nan, false);
-    addOpTest("-infinity", inf, false);
-    addOpTest("-infinity", -inf, true);
-    addOpTest("-infinity", nan, false);
-    addOpTest("NaN", inf, false);
-    addOpTest("NaN", -inf, false);
-    addOpTest("NaN", nan, true);
-    addOpTest("nan", inf, false);
-    addOpTest("nan", -inf, false);
-    addOpTest("nan", nan, true);
-
-    addOpTest(inf, "abc", false);
-    addOpTest(inf, " ", false);
-    addOpTest(inf, "", false);
-    addOpTest(inf, "0", false);
-    addOpTest(-inf, "abc", false);
-    addOpTest(-inf, " ", false);
-    addOpTest(-inf, "", false);
-    addOpTest(-inf, "0", false);
-    addOpTest(nan, "abc", false);
-    addOpTest(nan, " ", false);
-    addOpTest(nan, "", false);
-    addOpTest(nan, "0", false);
+    addOpTest(inf, inf);
+    addOpTest(-inf, -inf);
+    addOpTest(nan, nan);
+    addOpTest(inf, -inf);
+    addOpTest(-inf, inf);
+    addOpTest(inf, nan);
+    addOpTest(nan, inf);
+    addOpTest(-inf, nan);
+    addOpTest(nan, -inf);
+
+    addOpTest(5, inf);
+    addOpTest(5, -inf);
+    addOpTest(5, nan);
+    addOpTest(0, nan);
+
+    addOpTest(true, "true");
+    addOpTest("true", true);
+    addOpTest(false, "false");
+    addOpTest("false", false);
+    addOpTest(false, "true");
+    addOpTest("true", false);
+    addOpTest(true, "false");
+    addOpTest("false", true);
+    addOpTest(true, "TRUE");
+    addOpTest("TRUE", true);
+    addOpTest(false, "FALSE");
+    addOpTest("FALSE", false);
+
+    addOpTest(true, "00001");
+    addOpTest("00001", true);
+    addOpTest(true, "00000");
+    addOpTest("00000", true);
+    addOpTest(false, "00000");
+    addOpTest("00000", false);
+
+    addOpTest("true", 1);
+    addOpTest(1, "true");
+    addOpTest("true", 0);
+    addOpTest(0, "true");
+    addOpTest("false", 0);
+    addOpTest(0, "false");
+    addOpTest("false", 1);
+    addOpTest(1, "false");
+
+    addOpTest("true", "TRUE");
+    addOpTest("true", "FALSE");
+    addOpTest("false", "FALSE");
+    addOpTest("false", "TRUE");
+
+    addOpTest(true, inf);
+    addOpTest(true, -inf);
+    addOpTest(true, nan);
+    addOpTest(false, inf);
+    addOpTest(false, -inf);
+    addOpTest(false, nan);
+
+    addOpTest("Infinity", inf);
+    addOpTest("Infinity", -inf);
+    addOpTest("Infinity", nan);
+    addOpTest("infinity", inf);
+    addOpTest("infinity", -inf);
+    addOpTest("infinity", nan);
+    addOpTest("-Infinity", inf);
+    addOpTest("-Infinity", -inf);
+    addOpTest("-Infinity", nan);
+    addOpTest("-infinity", inf);
+    addOpTest("-infinity", -inf);
+    addOpTest("-infinity", nan);
+    addOpTest("NaN", inf);
+    addOpTest("NaN", -inf);
+    addOpTest("NaN", nan);
+    addOpTest("nan", inf);
+    addOpTest("nan", -inf);
+    addOpTest("nan", nan);
+
+    addOpTest(inf, "abc");
+    addOpTest(inf, " ");
+    addOpTest(inf, "");
+    addOpTest(inf, "0");
+    addOpTest(-inf, "abc");
+    addOpTest(-inf, " ");
+    addOpTest(-inf, "");
+    addOpTest(-inf, "0");
+    addOpTest(nan, "abc");
+    addOpTest(nan, " ");
+    addOpTest(nan, "");
+    addOpTest(nan, "0");
 }
 
 TEST_F(LLVMCodeBuilderTest, Yield)

From cdbba2e2065cd40d266578619be2adacf6e10628 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 21:55:40 +0200
Subject: [PATCH 75/94] Add more comparison edge case tests

---
 test/scratch_classes/value_test.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 87d8d382..6a918f72 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -2606,15 +2606,33 @@ TEST(ValueTest, ComparisonOperators)
 
         ASSERT_FALSE(v1 == v3);
         ASSERT_TRUE(v1 != v3);
+        ASSERT_FALSE(v1 > v3);
+        ASSERT_TRUE(v1 < v3);
 
         ASSERT_FALSE(v1 == v4);
         ASSERT_TRUE(v1 != v4);
+        ASSERT_TRUE(v1 > v4);
+        ASSERT_FALSE(v1 < v4);
 
         ASSERT_FALSE(v1 == v5);
         ASSERT_TRUE(v1 != v5);
+        ASSERT_TRUE(v1 > v5);
+        ASSERT_FALSE(v1 < v5);
 
         ASSERT_FALSE(v2 == v5);
         ASSERT_TRUE(v2 != v5);
+        ASSERT_FALSE(v2 > v5);
+        ASSERT_TRUE(v2 < v5);
+    }
+
+    {
+        Value v1 = 0;
+        Value v2(std::numeric_limits<double>::quiet_NaN());
+
+        ASSERT_FALSE(v1 == v2);
+        ASSERT_TRUE(v1 != v2);
+        ASSERT_FALSE(v1 > v2);
+        ASSERT_TRUE(v1 < v2);
     }
 
     {

From d146cf3c128fb114734d6dc4994470b12cde758b Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 21:56:49 +0200
Subject: [PATCH 76/94] LLVMCodeBuilder: Implement greater than comparison

---
 src/dev/engine/internal/icodebuilder.h      |   1 +
 src/dev/engine/internal/llvmcodebuilder.cpp |  47 ++++-
 src/dev/engine/internal/llvmcodebuilder.h   |   2 +
 test/dev/llvm/llvmcodebuilder_test.cpp      | 181 ++++++++++++++++++++
 test/mocks/codebuildermock.h                |   1 +
 5 files changed, 227 insertions(+), 5 deletions(-)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 6fd80778..353ab798 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -30,6 +30,7 @@ class ICodeBuilder
         virtual void createDiv() = 0;
 
         virtual void createCmpEQ() = 0;
+        virtual void createCmpGT() = 0;
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index cc51e404..9026e6ad 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -136,6 +136,14 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::CmpGT: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0].second;
+                const auto &arg2 = step.args[1].second;
+                step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::GT);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -479,6 +487,11 @@ void LLVMCodeBuilder::createCmpEQ()
     createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, 2);
 }
 
+void LLVMCodeBuilder::createCmpGT()
+{
+    createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
@@ -955,11 +968,17 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr<Register> arg1, s
             type2 = Compiler::StaticType::Number;
 
         // Optimize number and bool comparison
-        if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool)
+        int optNumberBool = 0;
+
+        if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool) {
             type2 = Compiler::StaticType::Number;
+            optNumberBool = 2; // operand 2 was bool
+        }
 
-        if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number)
+        if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number) {
             type1 = Compiler::StaticType::Number;
+            optNumberBool = 1; // operand 1 was bool
+        }
 
         if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) {
             // If the types are different or at least one of them
@@ -997,8 +1016,25 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr<Register> arg1, s
                             return m_builder.CreateSelect(nan, m_builder.getInt1(true), cmp);
                         }
 
-                        case Comparison::GT:
-                            return m_builder.CreateFCmpOGT(value1, value2);
+                        case Comparison::GT: {
+                            llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN
+                            llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, value2);
+                            llvm::Value *nan;
+                            llvm::Value *nanCmp;
+
+                            if (optNumberBool == 1) {
+                                nan = isNaN(value2);
+                                nanCmp = castValue(arg1, Compiler::StaticType::Bool);
+                            } else if (optNumberBool == 2) {
+                                nan = isNaN(value1);
+                                nanCmp = m_builder.CreateNot(castValue(arg2, Compiler::StaticType::Bool));
+                            } else {
+                                nan = isNaN(value1);
+                                nanCmp = m_builder.CreateFCmpUGT(value1, value2);
+                            }
+
+                            return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp));
+                        }
 
                         case Comparison::LT:
                             return m_builder.CreateFCmpOLT(value1, value2);
@@ -1016,7 +1052,8 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr<Register> arg1, s
                             return m_builder.CreateICmpEQ(value1, value2);
 
                         case Comparison::GT:
-                            return m_builder.CreateICmpSGT(value1, value2);
+                            // value1 && !value2
+                            return m_builder.CreateAnd(value1, m_builder.CreateNot(value2));
 
                         case Comparison::LT:
                             return m_builder.CreateICmpSLT(value1, value2);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 6cec0cc6..8f61aeec 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -32,6 +32,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         void createDiv() override;
 
         void createCmpEQ() override;
+        void createCmpGT() override;
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -69,6 +70,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     Mul,
                     Div,
                     CmpEQ,
+                    CmpGT,
                     Yield,
                     BeginIf,
                     BeginElse,
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 82bc89d9..553ce67c 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -595,6 +595,187 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison)
     addOpTest(nan, "0");
 }
 
+TEST_F(LLVMCodeBuilderTest, GreaterThanComparison)
+{
+    auto addOpTest = [this](Value v1, Value v2) {
+        createBuilder(true);
+
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createCmpGT();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        callConstFuncForType(v1.type());
+        m_builder->addConstValue(v2);
+        callConstFuncForType(v2.type());
+        m_builder->createCmpGT();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(v1 > v2).toString() + '\n';
+        std::string expected = str + str;
+
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
+
+    addOpTest(10, 10);
+    addOpTest(10, 8);
+    addOpTest(8, 10);
+
+    addOpTest(-4.25, -4.25);
+    addOpTest(-4.25, 5.312);
+    addOpTest(5.312, -4.25);
+
+    addOpTest(true, true);
+    addOpTest(true, false);
+    addOpTest(false, true);
+
+    addOpTest(1, true);
+    addOpTest(1, false);
+
+    addOpTest("abC def", "abC def");
+    addOpTest("abC def", "abc dEf");
+    addOpTest("abC def", "ghi Jkl");
+    addOpTest("ghi Jkl", "abC def");
+    addOpTest("abC def", "hello world");
+
+    addOpTest(" ", "");
+    addOpTest(" ", "0");
+    addOpTest(" ", 0);
+    addOpTest(0, " ");
+    addOpTest("", "0");
+    addOpTest("", 0);
+    addOpTest(0, "");
+    addOpTest("0", 0);
+    addOpTest(0, "0");
+
+    addOpTest(5.25, "5.25");
+    addOpTest("5.25", 5.25);
+    addOpTest(5.25, " 5.25");
+    addOpTest(" 5.25", 5.25);
+    addOpTest(5.25, "5.25 ");
+    addOpTest("5.25 ", 5.25);
+    addOpTest(5.25, " 5.25 ");
+    addOpTest(" 5.25 ", 5.25);
+    addOpTest(5.25, "5.26");
+    addOpTest("5.26", 5.25);
+    addOpTest("5.25", "5.26");
+    addOpTest(5, "5  ");
+    addOpTest("5  ", 5);
+    addOpTest(0, "1");
+    addOpTest("1", 0);
+    addOpTest(0, "test");
+    addOpTest("test", 0);
+
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+
+    addOpTest(inf, inf);
+    addOpTest(-inf, -inf);
+    addOpTest(nan, nan);
+    addOpTest(inf, -inf);
+    addOpTest(-inf, inf);
+    addOpTest(inf, nan);
+    addOpTest(nan, inf);
+    addOpTest(-inf, nan);
+    addOpTest(nan, -inf);
+
+    addOpTest(5, inf);
+    addOpTest(inf, 5);
+    addOpTest(5, -inf);
+    addOpTest(-inf, 5);
+    addOpTest(5, nan);
+    addOpTest(nan, 5);
+    addOpTest(0, nan);
+    addOpTest(nan, 0);
+
+    addOpTest(true, "true");
+    addOpTest("true", true);
+    addOpTest(false, "false");
+    addOpTest("false", false);
+    addOpTest(false, "true");
+    addOpTest("true", false);
+    addOpTest(true, "false");
+    addOpTest("false", true);
+    addOpTest(true, "TRUE");
+    addOpTest("TRUE", true);
+    addOpTest(false, "FALSE");
+    addOpTest("FALSE", false);
+
+    addOpTest(true, "00001");
+    addOpTest("00001", true);
+    addOpTest(true, "00000");
+    addOpTest("00000", true);
+    addOpTest(false, "00000");
+    addOpTest("00000", false);
+
+    addOpTest("true", 1);
+    addOpTest(1, "true");
+    addOpTest("true", 0);
+    addOpTest(0, "true");
+    addOpTest("false", 0);
+    addOpTest(0, "false");
+    addOpTest("false", 1);
+    addOpTest(1, "false");
+
+    addOpTest("true", "TRUE");
+    addOpTest("true", "FALSE");
+    addOpTest("false", "FALSE");
+    addOpTest("false", "TRUE");
+
+    addOpTest(true, inf);
+    addOpTest(inf, true);
+    addOpTest(true, -inf);
+    addOpTest(-inf, true);
+    addOpTest(true, nan);
+    addOpTest(nan, true);
+    addOpTest(false, inf);
+    addOpTest(inf, false);
+    addOpTest(false, -inf);
+    addOpTest(-inf, false);
+    addOpTest(false, nan);
+    addOpTest(nan, false);
+
+    addOpTest("Infinity", inf);
+    addOpTest("Infinity", -inf);
+    addOpTest("Infinity", nan);
+    addOpTest("infinity", inf);
+    addOpTest("infinity", -inf);
+    addOpTest("infinity", nan);
+    addOpTest("-Infinity", inf);
+    addOpTest("-Infinity", -inf);
+    addOpTest("-Infinity", nan);
+    addOpTest("-infinity", inf);
+    addOpTest("-infinity", -inf);
+    addOpTest("-infinity", nan);
+    addOpTest("NaN", inf);
+    addOpTest("NaN", -inf);
+    addOpTest("NaN", nan);
+    addOpTest("nan", inf);
+    addOpTest("nan", -inf);
+    addOpTest("nan", nan);
+
+    addOpTest(inf, "abc");
+    addOpTest(inf, " ");
+    addOpTest(inf, "");
+    addOpTest(inf, "0");
+    addOpTest(-inf, "abc");
+    addOpTest(-inf, " ");
+    addOpTest(-inf, "");
+    addOpTest(-inf, "0");
+    addOpTest(nan, "abc");
+    addOpTest(nan, " ");
+    addOpTest(nan, "");
+    addOpTest(nan, "0");
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 473ded0d..43a604e9 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -20,6 +20,7 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, createDiv, (), (override));
 
         MOCK_METHOD(void, createCmpEQ, (), (override));
+        MOCK_METHOD(void, createCmpGT, (), (override));
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From 379a12e307ac98dade77c0db01dd527fe2ddc717 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 22:06:44 +0200
Subject: [PATCH 77/94] LLVMCodeBuilder: Implement lower than comparison

---
 src/dev/engine/internal/icodebuilder.h      |  2 ++
 src/dev/engine/internal/llvmcodebuilder.cpp | 37 +++++++++++++++++++--
 src/dev/engine/internal/llvmcodebuilder.h   |  3 ++
 test/dev/llvm/llvmcodebuilder_test.cpp      | 30 +++++++++++++++--
 test/mocks/codebuildermock.h                |  2 ++
 5 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index 353ab798..dc3850d6 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -31,6 +31,8 @@ class ICodeBuilder
 
         virtual void createCmpEQ() = 0;
         virtual void createCmpGT() = 0;
+        virtual void createCmpLT() = 0;
+
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 9026e6ad..cd2a232f 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -144,6 +144,14 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::CmpLT: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0].second;
+                const auto &arg2 = step.args[1].second;
+                step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::LT);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -492,6 +500,11 @@ void LLVMCodeBuilder::createCmpGT()
     createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, 2);
 }
 
+void LLVMCodeBuilder::createCmpLT()
+{
+    createOp(Step::Type::CmpLT, Compiler::StaticType::Bool, 2);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
@@ -1036,8 +1049,25 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr<Register> arg1, s
                             return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp));
                         }
 
-                        case Comparison::LT:
-                            return m_builder.CreateFCmpOLT(value1, value2);
+                        case Comparison::LT: {
+                            llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN
+                            llvm::Value *cmp = m_builder.CreateFCmpOLT(value1, value2);
+                            llvm::Value *nan;
+                            llvm::Value *nanCmp;
+
+                            if (optNumberBool == 1) {
+                                nan = isNaN(value2);
+                                nanCmp = m_builder.CreateNot(castValue(arg1, Compiler::StaticType::Bool));
+                            } else if (optNumberBool == 2) {
+                                nan = isNaN(value1);
+                                nanCmp = castValue(arg2, Compiler::StaticType::Bool);
+                            } else {
+                                nan = isNaN(value2);
+                                nanCmp = m_builder.CreateFCmpULT(value1, value2);
+                            }
+
+                            return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp));
+                        }
 
                         default:
                             assert(false);
@@ -1056,7 +1086,8 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr<Register> arg1, s
                             return m_builder.CreateAnd(value1, m_builder.CreateNot(value2));
 
                         case Comparison::LT:
-                            return m_builder.CreateICmpSLT(value1, value2);
+                            // value2 && !value1
+                            return m_builder.CreateAnd(value2, m_builder.CreateNot(value1));
 
                         default:
                             assert(false);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 8f61aeec..39cbf623 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -33,6 +33,8 @@ class LLVMCodeBuilder : public ICodeBuilder
 
         void createCmpEQ() override;
         void createCmpGT() override;
+        void createCmpLT() override;
+
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -71,6 +73,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     Div,
                     CmpEQ,
                     CmpGT,
+                    CmpLT,
                     Yield,
                     BeginIf,
                     BeginElse,
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 553ce67c..98c4cb83 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -595,9 +595,10 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison)
     addOpTest(nan, "0");
 }
 
-TEST_F(LLVMCodeBuilderTest, GreaterThanComparison)
+TEST_F(LLVMCodeBuilderTest, GreaterAndLowerThanComparison)
 {
     auto addOpTest = [this](Value v1, Value v2) {
+        // GT
         createBuilder(true);
 
         m_builder->addConstValue(v1);
@@ -622,7 +623,32 @@ TEST_F(LLVMCodeBuilderTest, GreaterThanComparison)
         code->run(ctx.get());
         const std::string quotes1 = v1.isString() ? "\"" : "";
         const std::string quotes2 = v2.isString() ? "\"" : "";
-        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "GT: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+
+        // LT
+        createBuilder(true);
+
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createCmpLT();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        callConstFuncForType(v1.type());
+        m_builder->addConstValue(v2);
+        callConstFuncForType(v2.type());
+        m_builder->createCmpLT();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        str = Value(v1 < v2).toString() + '\n';
+        expected = str + str;
+
+        code = m_builder->finalize();
+        ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "LT: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
     };
 
     addOpTest(10, 10);
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index 43a604e9..c2560c12 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -21,6 +21,8 @@ class CodeBuilderMock : public ICodeBuilder
 
         MOCK_METHOD(void, createCmpEQ, (), (override));
         MOCK_METHOD(void, createCmpGT, (), (override));
+        MOCK_METHOD(void, createCmpLT, (), (override));
+
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From e6edce7e728fb53f6af5e87dad952855c0778ac6 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Tue, 15 Oct 2024 22:48:03 +0200
Subject: [PATCH 78/94] Compiler: Add methods for comparison operators

---
 include/scratchcpp/dev/compiler.h   |  4 +++
 src/dev/engine/compiler.cpp         | 18 +++++++++++++
 test/dev/compiler/compiler_test.cpp | 39 +++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index e98b1605..82cc3dc6 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -53,6 +53,10 @@ class LIBSCRATCHCPP_EXPORT Compiler
         void createMul();
         void createDiv();
 
+        void createCmpEQ();
+        void createCmpGT();
+        void createCmpLT();
+
         void moveToIf(std::shared_ptr<Block> substack);
         void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
         void moveToRepeatLoop(std::shared_ptr<Block> substack);
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index bb999224..4293341b 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -126,6 +126,24 @@ void Compiler::createDiv()
     impl->builder->createDiv();
 }
 
+/*! Creates an equality comparison instruction using the last 2 values. */
+void Compiler::createCmpEQ()
+{
+    impl->builder->createCmpEQ();
+}
+
+/*! Creates a greater than comparison instruction using the last 2 values. */
+void Compiler::createCmpGT()
+{
+    impl->builder->createCmpGT();
+}
+
+/*! Creates a lower than comparison instruction using the last 2 values. */
+void Compiler::createCmpLT()
+{
+    impl->builder->createCmpLT();
+}
+
 /*! Jumps to the given if substack. */
 void Compiler::moveToIf(std::shared_ptr<Block> substack)
 {
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index 1b2044ac..2a0089d2 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -271,6 +271,45 @@ TEST_F(CompilerTest, CreateDiv)
     compile(compiler, block);
 }
 
+TEST_F(CompilerTest, CreateCmpEQ)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createCmpEQ);
+        compiler->createCmpEQ();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateCmpGT)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createCmpGT);
+        compiler->createCmpGT();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateCmpLT)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createCmpLT);
+        compiler->createCmpLT();
+    });
+
+    compile(compiler, block);
+}
+
 TEST_F(CompilerTest, MoveToIf)
 {
     Compiler compiler(&m_engine, &m_target);

From e1ad826b9ff7951def3411e1b2b009557bc191a8 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 16 Oct 2024 10:10:17 +0200
Subject: [PATCH 79/94] Fix conversion of NaN to bool

---
 src/scratch/value_functions.cpp     | 2 +-
 test/scratch_classes/value_test.cpp | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index b7b678b7..f8763200 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -238,7 +238,7 @@ extern "C"
         if (v->type == ValueType::Bool) {
             return v->boolValue;
         } else if (v->type == ValueType::Number) {
-            return v->numberValue != 0;
+            return v->numberValue != 0 && !std::isnan(v->numberValue);
         } else if (v->type == ValueType::String) {
             return value_stringToBool(v->stringValue);
         } else {
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index 6a918f72..f2e24d7e 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -1152,6 +1152,15 @@ TEST(ValueTest, ToBool)
     v = -2.54;
     ASSERT_EQ(v.toBool(), true);
 
+    v = std::numeric_limits<double>::infinity();
+    ASSERT_EQ(v.toBool(), true);
+
+    v = -std::numeric_limits<double>::infinity();
+    ASSERT_EQ(v.toBool(), true);
+
+    v = std::numeric_limits<double>::quiet_NaN();
+    ASSERT_EQ(v.toBool(), false);
+
     v = false;
     ASSERT_EQ(v.toBool(), false);
     v = true;

From eca7a73246bfd3c329c2963b1c0cb44234199679 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 16 Oct 2024 10:11:51 +0200
Subject: [PATCH 80/94] LLVMCodeBuilder: Implement 'and' and 'or' gates

---
 src/dev/engine/internal/icodebuilder.h      |   2 +
 src/dev/engine/internal/llvmcodebuilder.cpp |  48 ++++--
 src/dev/engine/internal/llvmcodebuilder.h   |   6 +-
 test/dev/llvm/llvmcodebuilder_test.cpp      | 160 ++++++++++++++++++++
 test/mocks/codebuildermock.h                |   2 +
 5 files changed, 208 insertions(+), 10 deletions(-)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index dc3850d6..be921b2d 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -33,6 +33,8 @@ class ICodeBuilder
         virtual void createCmpGT() = 0;
         virtual void createCmpLT() = 0;
 
+        virtual void createAnd() = 0;
+        virtual void createOr() = 0;
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index cd2a232f..47707e15 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -152,6 +152,26 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::And: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0];
+                const auto &arg2 = step.args[1];
+                llvm::Value *bool1 = castValue(arg1.second, arg1.first);
+                llvm::Value *bool2 = castValue(arg2.second, arg2.first);
+                step.functionReturnReg->value = m_builder.CreateAnd(bool1, bool2);
+                break;
+            }
+
+            case Step::Type::Or: {
+                assert(step.args.size() == 2);
+                const auto &arg1 = step.args[0];
+                const auto &arg2 = step.args[1];
+                llvm::Value *bool1 = castValue(arg1.second, arg1.first);
+                llvm::Value *bool2 = castValue(arg2.second, arg2.first);
+                step.functionReturnReg->value = m_builder.CreateOr(bool1, bool2);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -472,37 +492,47 @@ void LLVMCodeBuilder::addListContents(List *list)
 
 void LLVMCodeBuilder::createAdd()
 {
-    createOp(Step::Type::Add, Compiler::StaticType::Number, 2);
+    createOp(Step::Type::Add, Compiler::StaticType::Number, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createSub()
 {
-    createOp(Step::Type::Sub, Compiler::StaticType::Number, 2);
+    createOp(Step::Type::Sub, Compiler::StaticType::Number, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createMul()
 {
-    createOp(Step::Type::Mul, Compiler::StaticType::Number, 2);
+    createOp(Step::Type::Mul, Compiler::StaticType::Number, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createDiv()
 {
-    createOp(Step::Type::Div, Compiler::StaticType::Number, 2);
+    createOp(Step::Type::Div, Compiler::StaticType::Number, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createCmpEQ()
 {
-    createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, 2);
+    createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createCmpGT()
 {
-    createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, 2);
+    createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, Compiler::StaticType::Number, 2);
 }
 
 void LLVMCodeBuilder::createCmpLT()
 {
-    createOp(Step::Type::CmpLT, Compiler::StaticType::Bool, 2);
+    createOp(Step::Type::CmpLT, Compiler::StaticType::Bool, Compiler::StaticType::Number, 2);
+}
+
+void LLVMCodeBuilder::createAnd()
+{
+    createOp(Step::Type::And, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 2);
+}
+
+void LLVMCodeBuilder::createOr()
+{
+    createOp(Step::Type::Or, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 2);
 }
 
 void LLVMCodeBuilder::beginIfStatement()
@@ -881,7 +911,7 @@ llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num)
     return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num);
 }
 
-void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, size_t argCount)
+void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount)
 {
     Step step(type);
 
@@ -889,7 +919,7 @@ void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, si
     size_t j = 0;
 
     for (size_t i = m_tmpRegs.size() - argCount; i < m_tmpRegs.size(); i++)
-        step.args.push_back({ Compiler::StaticType::Number, m_tmpRegs[i] });
+        step.args.push_back({ argType, m_tmpRegs[i] });
 
     m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end());
 
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 39cbf623..86b5d1ec 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -35,6 +35,8 @@ class LLVMCodeBuilder : public ICodeBuilder
         void createCmpGT() override;
         void createCmpLT() override;
 
+        void createAnd() override;
+        void createOr() override;
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -74,6 +76,8 @@ class LLVMCodeBuilder : public ICodeBuilder
                     CmpEQ,
                     CmpGT,
                     CmpLT,
+                    And,
+                    Or,
                     Yield,
                     BeginIf,
                     BeginElse,
@@ -150,7 +154,7 @@ class LLVMCodeBuilder : public ICodeBuilder
         llvm::Value *isNaN(llvm::Value *num);
         llvm::Value *removeNaN(llvm::Value *num);
 
-        void createOp(Step::Type type, Compiler::StaticType retType, size_t argCount);
+        void createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount);
         llvm::Value *createValue(std::shared_ptr<Register> reg);
         llvm::Value *createComparison(std::shared_ptr<Register> arg1, std::shared_ptr<Register> arg2, Comparison type);
 
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 98c4cb83..68afd160 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -802,6 +802,166 @@ TEST_F(LLVMCodeBuilderTest, GreaterAndLowerThanComparison)
     addOpTest(nan, "0");
 }
 
+TEST_F(LLVMCodeBuilderTest, AndOr)
+{
+    auto addOpTest = [this](Value v1, Value v2) {
+        // And
+        createBuilder(true);
+
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createAnd();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        callConstFuncForType(v1.type());
+        m_builder->addConstValue(v2);
+        callConstFuncForType(v2.type());
+        m_builder->createAnd();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(v1.toBool() && v2.toBool()).toString() + '\n';
+        std::string expected = str + str;
+
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes1 = v1.isString() ? "\"" : "";
+        const std::string quotes2 = v2.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "AND: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+
+        // Or
+        createBuilder(true);
+
+        m_builder->addConstValue(v1);
+        m_builder->addConstValue(v2);
+        m_builder->createOr();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v1);
+        callConstFuncForType(v1.type());
+        m_builder->addConstValue(v2);
+        callConstFuncForType(v2.type());
+        m_builder->createOr();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        str = Value(v1.toBool() || v2.toBool()).toString() + '\n';
+        expected = str + str;
+
+        code = m_builder->finalize();
+        ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "OR: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2;
+    };
+
+    addOpTest(10, 8);
+    addOpTest(-4.25, -4.25);
+    addOpTest(-4.25, 5.312);
+
+    addOpTest(true, true);
+    addOpTest(true, false);
+    addOpTest(false, true);
+
+    addOpTest(1, true);
+    addOpTest(1, false);
+
+    addOpTest("abc", "def");
+    addOpTest("true", "true");
+    addOpTest("true", "false");
+    addOpTest("false", "true");
+    addOpTest("false", "false");
+
+    addOpTest(5.25, "5.25");
+    addOpTest("5.25", 5.25);
+    addOpTest(0, "1");
+    addOpTest("1", 0);
+    addOpTest(0, "test");
+    addOpTest("test", 0);
+
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+
+    addOpTest(inf, inf);
+    addOpTest(-inf, -inf);
+    addOpTest(nan, nan);
+    addOpTest(inf, -inf);
+    addOpTest(-inf, inf);
+    addOpTest(inf, nan);
+    addOpTest(nan, inf);
+    addOpTest(-inf, nan);
+    addOpTest(nan, -inf);
+
+    addOpTest(true, "true");
+    addOpTest("true", true);
+    addOpTest(false, "false");
+    addOpTest("false", false);
+    addOpTest(false, "true");
+    addOpTest("true", false);
+    addOpTest(true, "false");
+    addOpTest("false", true);
+    addOpTest(true, "TRUE");
+    addOpTest("TRUE", true);
+    addOpTest(false, "FALSE");
+    addOpTest("FALSE", false);
+
+    addOpTest(true, "00001");
+    addOpTest("00001", true);
+    addOpTest(true, "00000");
+    addOpTest("00000", true);
+    addOpTest(false, "00000");
+    addOpTest("00000", false);
+
+    addOpTest("true", 1);
+    addOpTest(1, "true");
+    addOpTest("true", 0);
+    addOpTest(0, "true");
+    addOpTest("false", 0);
+    addOpTest(0, "false");
+    addOpTest("false", 1);
+    addOpTest(1, "false");
+
+    addOpTest("true", "TRUE");
+    addOpTest("true", "FALSE");
+    addOpTest("false", "FALSE");
+    addOpTest("false", "TRUE");
+
+    addOpTest(true, inf);
+    addOpTest(inf, true);
+    addOpTest(true, -inf);
+    addOpTest(-inf, true);
+    addOpTest(true, nan);
+    addOpTest(nan, true);
+    addOpTest(false, inf);
+    addOpTest(inf, false);
+    addOpTest(false, -inf);
+    addOpTest(-inf, false);
+    addOpTest(false, nan);
+    addOpTest(nan, false);
+
+    addOpTest("Infinity", inf);
+    addOpTest("Infinity", -inf);
+    addOpTest("Infinity", nan);
+    addOpTest("infinity", inf);
+    addOpTest("infinity", -inf);
+    addOpTest("infinity", nan);
+    addOpTest("-Infinity", inf);
+    addOpTest("-Infinity", -inf);
+    addOpTest("-Infinity", nan);
+    addOpTest("-infinity", inf);
+    addOpTest("-infinity", -inf);
+    addOpTest("-infinity", nan);
+    addOpTest("NaN", inf);
+    addOpTest("NaN", -inf);
+    addOpTest("NaN", nan);
+    addOpTest("nan", inf);
+    addOpTest("nan", -inf);
+    addOpTest("nan", nan);
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index c2560c12..f1fb3f9c 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -23,6 +23,8 @@ class CodeBuilderMock : public ICodeBuilder
         MOCK_METHOD(void, createCmpGT, (), (override));
         MOCK_METHOD(void, createCmpLT, (), (override));
 
+        MOCK_METHOD(void, createAnd, (), (override));
+        MOCK_METHOD(void, createOr, (), (override));
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From 4d9f6fa30a5f24e2019a49308c3f0188d3205e54 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 16 Oct 2024 11:05:09 +0200
Subject: [PATCH 81/94] LLVMCodeBuilder: Implement not gate

---
 src/dev/engine/internal/icodebuilder.h      |  2 +
 src/dev/engine/internal/llvmcodebuilder.cpp | 13 +++++
 src/dev/engine/internal/llvmcodebuilder.h   |  3 +
 test/dev/llvm/llvmcodebuilder_test.cpp      | 62 +++++++++++++++++++++
 test/mocks/codebuildermock.h                |  2 +
 5 files changed, 82 insertions(+)

diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h
index be921b2d..f5852098 100644
--- a/src/dev/engine/internal/icodebuilder.h
+++ b/src/dev/engine/internal/icodebuilder.h
@@ -35,6 +35,8 @@ class ICodeBuilder
 
         virtual void createAnd() = 0;
         virtual void createOr() = 0;
+        virtual void createNot() = 0;
+
         virtual void beginIfStatement() = 0;
         virtual void beginElseBranch() = 0;
         virtual void endIf() = 0;
diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index 47707e15..b35efa2b 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -172,6 +172,14 @@ std::shared_ptr<ExecutableCode> LLVMCodeBuilder::finalize()
                 break;
             }
 
+            case Step::Type::Not: {
+                assert(step.args.size() == 1);
+                const auto &arg = step.args[0];
+                llvm::Value *value = castValue(arg.second, arg.first);
+                step.functionReturnReg->value = m_builder.CreateNot(value);
+                break;
+            }
+
             case Step::Type::Yield:
                 if (!m_warp) {
                     freeHeap();
@@ -535,6 +543,11 @@ void LLVMCodeBuilder::createOr()
     createOp(Step::Type::Or, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 2);
 }
 
+void LLVMCodeBuilder::createNot()
+{
+    createOp(Step::Type::Not, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 1);
+}
+
 void LLVMCodeBuilder::beginIfStatement()
 {
     Step step(Step::Type::BeginIf);
diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h
index 86b5d1ec..5fb17df5 100644
--- a/src/dev/engine/internal/llvmcodebuilder.h
+++ b/src/dev/engine/internal/llvmcodebuilder.h
@@ -37,6 +37,8 @@ class LLVMCodeBuilder : public ICodeBuilder
 
         void createAnd() override;
         void createOr() override;
+        void createNot() override;
+
         void beginIfStatement() override;
         void beginElseBranch() override;
         void endIf() override;
@@ -78,6 +80,7 @@ class LLVMCodeBuilder : public ICodeBuilder
                     CmpLT,
                     And,
                     Or,
+                    Not,
                     Yield,
                     BeginIf,
                     BeginElse,
diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp
index 68afd160..6715cc0e 100644
--- a/test/dev/llvm/llvmcodebuilder_test.cpp
+++ b/test/dev/llvm/llvmcodebuilder_test.cpp
@@ -962,6 +962,68 @@ TEST_F(LLVMCodeBuilderTest, AndOr)
     addOpTest("nan", nan);
 }
 
+TEST_F(LLVMCodeBuilderTest, Not)
+{
+    auto addOpTest = [this](Value v) {
+        createBuilder(true);
+
+        m_builder->addConstValue(v);
+        m_builder->createNot();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        m_builder->addConstValue(v);
+        callConstFuncForType(v.type());
+        m_builder->createNot();
+        m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String });
+
+        std::string str = Value(!v.toBool()).toString() + '\n';
+        std::string expected = str + str;
+
+        auto code = m_builder->finalize();
+        auto ctx = code->createExecutionContext(&m_target);
+
+        testing::internal::CaptureStdout();
+        code->run(ctx.get());
+        const std::string quotes = v.isString() ? "\"" : "";
+        ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "NOT: " << quotes << v.toString() << quotes;
+    };
+
+    addOpTest(10);
+    addOpTest(-4.25);
+    addOpTest(5.312);
+    addOpTest(1);
+    addOpTest(0);
+
+    addOpTest(true);
+    addOpTest(false);
+
+    addOpTest("abc");
+    addOpTest("5.25");
+    addOpTest("0");
+
+    static const double inf = std::numeric_limits<double>::infinity();
+    static const double nan = std::numeric_limits<double>::quiet_NaN();
+
+    addOpTest(inf);
+    addOpTest(-inf);
+    addOpTest(nan);
+
+    addOpTest("true");
+    addOpTest("false");
+    addOpTest("TRUE");
+    addOpTest("FALSE");
+
+    addOpTest("00001");
+    addOpTest("00000");
+
+    addOpTest("Infinity");
+    addOpTest("infinity");
+    addOpTest("-Infinity");
+    addOpTest("-infinity");
+    addOpTest("NaN");
+    addOpTest("nan");
+}
+
 TEST_F(LLVMCodeBuilderTest, Yield)
 {
     auto build = [this]() {
diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h
index f1fb3f9c..3271451d 100644
--- a/test/mocks/codebuildermock.h
+++ b/test/mocks/codebuildermock.h
@@ -25,6 +25,8 @@ class CodeBuilderMock : public ICodeBuilder
 
         MOCK_METHOD(void, createAnd, (), (override));
         MOCK_METHOD(void, createOr, (), (override));
+        MOCK_METHOD(void, createNot, (), (override));
+
         MOCK_METHOD(void, beginIfStatement, (), (override));
         MOCK_METHOD(void, beginElseBranch, (), (override));
         MOCK_METHOD(void, endIf, (), (override));

From 00234a679075ff44b1af6e3fd8f015f45936454e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 17 Oct 2024 15:43:36 +0200
Subject: [PATCH 82/94] Rect: Add contains() method

Resolves: #419
---
 include/scratchcpp/rect.h |  1 +
 src/rect.cpp              | 16 ++++++++++++++++
 test/rect/rect_test.cpp   | 38 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 55 insertions(+)

diff --git a/include/scratchcpp/rect.h b/include/scratchcpp/rect.h
index cedcbe6e..f5c91a91 100644
--- a/include/scratchcpp/rect.h
+++ b/include/scratchcpp/rect.h
@@ -35,6 +35,7 @@ class LIBSCRATCHCPP_EXPORT Rect
         void snapToInt();
 
         bool intersects(const Rect &rect) const;
+        bool contains(double x, double y) const;
 
     private:
         spimpl::impl_ptr<RectPrivate> impl;
diff --git a/src/rect.cpp b/src/rect.cpp
index e2ffe180..0f637d4d 100644
--- a/src/rect.cpp
+++ b/src/rect.cpp
@@ -106,3 +106,19 @@ bool Rect::intersects(const Rect &rect) const
     // Check if the rectangles intersect
     return !(x2 < rectX1 || rectX2 < x1 || y2 < rectY1 || rectY2 < y1);
 }
+
+/*! Returns true if the given point is inside or on the edge of the rectangle. */
+bool Rect::contains(double x, double y) const
+{
+    double bottom, top;
+
+    if (impl->top > impl->bottom) {
+        bottom = impl->bottom;
+        top = impl->top;
+    } else {
+        bottom = impl->top;
+        top = impl->bottom;
+    }
+
+    return (x >= impl->left && x <= impl->right && y >= bottom && y <= top);
+}
diff --git a/test/rect/rect_test.cpp b/test/rect/rect_test.cpp
index fb4dd437..6621ddf5 100644
--- a/test/rect/rect_test.cpp
+++ b/test/rect/rect_test.cpp
@@ -230,3 +230,41 @@ TEST(RectTest, Intersects)
         ASSERT_FALSE(rect2_ydown.intersects(rect1_ydown));
     }
 }
+
+TEST(RectTest, Contains)
+{
+    Rect rect(-50, 25, 150, -75);
+    Rect rect_ydown(-50, -75, 150, 25);
+
+    ASSERT_FALSE(rect.contains(-75, 30));
+    ASSERT_FALSE(rect.contains(-75, 10));
+    ASSERT_FALSE(rect.contains(-45, 30));
+    ASSERT_FALSE(rect.contains(151, -76));
+    ASSERT_FALSE(rect.contains(149, -76));
+    ASSERT_FALSE(rect.contains(151, -74));
+
+    ASSERT_FALSE(rect_ydown.contains(-75, 30));
+    ASSERT_FALSE(rect_ydown.contains(-75, 10));
+    ASSERT_FALSE(rect_ydown.contains(-45, 30));
+    ASSERT_FALSE(rect_ydown.contains(151, -76));
+    ASSERT_FALSE(rect_ydown.contains(149, -76));
+    ASSERT_FALSE(rect_ydown.contains(151, -74));
+
+    ASSERT_TRUE(rect.contains(-50, -75));
+    ASSERT_TRUE(rect.contains(150, -75));
+    ASSERT_TRUE(rect.contains(150, 25));
+    ASSERT_TRUE(rect.contains(-50, 25));
+    ASSERT_TRUE(rect.contains(-40, -70));
+    ASSERT_TRUE(rect.contains(100, 0));
+    ASSERT_TRUE(rect.contains(0, -40));
+    ASSERT_TRUE(rect.contains(5, 24));
+
+    ASSERT_TRUE(rect_ydown.contains(-50, -75));
+    ASSERT_TRUE(rect_ydown.contains(150, -75));
+    ASSERT_TRUE(rect_ydown.contains(150, 25));
+    ASSERT_TRUE(rect_ydown.contains(-50, 25));
+    ASSERT_TRUE(rect_ydown.contains(-40, -70));
+    ASSERT_TRUE(rect_ydown.contains(100, 0));
+    ASSERT_TRUE(rect_ydown.contains(0, -40));
+    ASSERT_TRUE(rect_ydown.contains(5, 24));
+}

From 6e5931fa7923863e5ec654ac72859379678cc197 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Wed, 23 Oct 2024 08:38:10 +0200
Subject: [PATCH 83/94] Rect: Add clamp() method

Resolves: #527
---
 include/scratchcpp/rect.h |  1 +
 src/rect.cpp              | 15 +++++++++
 test/rect/rect_test.cpp   | 66 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 82 insertions(+)

diff --git a/include/scratchcpp/rect.h b/include/scratchcpp/rect.h
index f5c91a91..b502ec04 100644
--- a/include/scratchcpp/rect.h
+++ b/include/scratchcpp/rect.h
@@ -32,6 +32,7 @@ class LIBSCRATCHCPP_EXPORT Rect
         double width() const;
         double height() const;
 
+        void clamp(double left, double top, double right, double bottom);
         void snapToInt();
 
         bool intersects(const Rect &rect) const;
diff --git a/src/rect.cpp b/src/rect.cpp
index 0f637d4d..4f29d71c 100644
--- a/src/rect.cpp
+++ b/src/rect.cpp
@@ -88,6 +88,21 @@ void Rect::snapToInt()
     impl->top = std::ceil(impl->top);
 }
 
+/*! Clamps the rectangle to the given bounds. */
+void Rect::clamp(double left, double top, double right, double bottom)
+{
+    // https://github.com/scratchfoundation/scratch-render/blob/c3ede9c3d54769730c7b023021511e2aba167b1f/src/Rectangle.js#L121-L131
+    impl->left = std::max(impl->left, left);
+    impl->right = std::min(impl->right, right);
+    impl->bottom = std::max(impl->bottom, bottom);
+    impl->top = std::min(impl->top, top);
+
+    impl->left = std::min(impl->left, right);
+    impl->right = std::max(impl->right, left);
+    impl->bottom = std::min(impl->bottom, top);
+    impl->top = std::max(impl->top, bottom);
+}
+
 /*! Returns true if the rectangle intersects the given rectangle. */
 bool Rect::intersects(const Rect &rect) const
 {
diff --git a/test/rect/rect_test.cpp b/test/rect/rect_test.cpp
index 6621ddf5..dfc0889e 100644
--- a/test/rect/rect_test.cpp
+++ b/test/rect/rect_test.cpp
@@ -78,6 +78,72 @@ TEST(RectTest, Height)
     ASSERT_EQ(rect.height(), 35.272);
 }
 
+TEST(RectTest, Clamp)
+{
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-40, 30, 160, -80);
+        ASSERT_EQ(rect.left(), -40);
+        ASSERT_EQ(rect.top(), 25);
+        ASSERT_EQ(rect.right(), 150);
+        ASSERT_EQ(rect.bottom(), -75);
+    }
+
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-50, 24, 160, -75);
+        ASSERT_EQ(rect.left(), -50);
+        ASSERT_EQ(rect.top(), 24);
+        ASSERT_EQ(rect.right(), 150);
+        ASSERT_EQ(rect.bottom(), -75);
+    }
+
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-50, 25, 145, -75);
+        ASSERT_EQ(rect.left(), -50);
+        ASSERT_EQ(rect.top(), 25);
+        ASSERT_EQ(rect.right(), 145);
+        ASSERT_EQ(rect.bottom(), -75);
+    }
+
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-50, 25, 150, -68);
+        ASSERT_EQ(rect.left(), -50);
+        ASSERT_EQ(rect.top(), 25);
+        ASSERT_EQ(rect.right(), 150);
+        ASSERT_EQ(rect.bottom(), -68);
+    }
+
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-100, 10, 50, -120);
+        ASSERT_EQ(rect.left(), -50);
+        ASSERT_EQ(rect.top(), 10);
+        ASSERT_EQ(rect.right(), 50);
+        ASSERT_EQ(rect.bottom(), -75);
+    }
+
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-100, 30, 160, -120);
+        ASSERT_EQ(rect.left(), -50);
+        ASSERT_EQ(rect.top(), 25);
+        ASSERT_EQ(rect.right(), 150);
+        ASSERT_EQ(rect.bottom(), -75);
+    }
+
+    {
+        Rect rect(-50, 25, 150, -75);
+        rect.clamp(-50, 25, 150, -75);
+        ASSERT_EQ(rect.left(), -50);
+        ASSERT_EQ(rect.top(), 25);
+        ASSERT_EQ(rect.right(), 150);
+        ASSERT_EQ(rect.bottom(), -75);
+    }
+}
+
 TEST(RectTest, SnapToInt)
 {
     {

From 9911185a68fa7b023b8a0e9ea7f0282de806d05c Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 24 Oct 2024 15:54:37 +0200
Subject: [PATCH 84/94] Rect: Add intersection() method

Resolves: #418
---
 include/scratchcpp/rect.h |  2 +
 src/rect.cpp              |  9 ++++
 test/rect/rect_test.cpp   | 88 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 99 insertions(+)

diff --git a/include/scratchcpp/rect.h b/include/scratchcpp/rect.h
index b502ec04..70a8b832 100644
--- a/include/scratchcpp/rect.h
+++ b/include/scratchcpp/rect.h
@@ -38,6 +38,8 @@ class LIBSCRATCHCPP_EXPORT Rect
         bool intersects(const Rect &rect) const;
         bool contains(double x, double y) const;
 
+        static void intersection(const Rect &a, const Rect &b, Rect &dst);
+
     private:
         spimpl::impl_ptr<RectPrivate> impl;
 };
diff --git a/src/rect.cpp b/src/rect.cpp
index 4f29d71c..f17b5d7d 100644
--- a/src/rect.cpp
+++ b/src/rect.cpp
@@ -137,3 +137,12 @@ bool Rect::contains(double x, double y) const
 
     return (x >= impl->left && x <= impl->right && y >= bottom && y <= top);
 }
+
+/*! Saves the intersection of the given rectangles (in Scratch space) to dst. */
+void Rect::intersection(const Rect &a, const Rect &b, Rect &dst)
+{
+    dst.impl->left = std::max(a.impl->left, b.impl->left);
+    dst.impl->right = std::min(a.impl->right, b.impl->right);
+    dst.impl->top = std::min(a.impl->top, b.impl->top);
+    dst.impl->bottom = std::max(a.impl->bottom, b.impl->bottom);
+}
diff --git a/test/rect/rect_test.cpp b/test/rect/rect_test.cpp
index dfc0889e..9316f206 100644
--- a/test/rect/rect_test.cpp
+++ b/test/rect/rect_test.cpp
@@ -334,3 +334,91 @@ TEST(RectTest, Contains)
     ASSERT_TRUE(rect_ydown.contains(0, -40));
     ASSERT_TRUE(rect_ydown.contains(5, 24));
 }
+
+TEST(RectTest, Intersection)
+{
+    Rect rect1(-50, 25, 150, -75);
+    Rect dst;
+
+    Rect::intersection(rect1, rect1, dst);
+    ASSERT_EQ(dst.left(), rect1.left());
+    ASSERT_EQ(dst.top(), rect1.top());
+    ASSERT_EQ(dst.right(), rect1.right());
+    ASSERT_EQ(dst.bottom(), rect1.bottom());
+
+    // left
+    {
+        Rect rect2(-75, 0, 125, -50);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 0);
+        ASSERT_EQ(dst.right(), 125);
+        ASSERT_EQ(dst.bottom(), -50);
+    }
+
+    {
+        Rect rect2(-100, 10, -50, -90);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 10);
+        ASSERT_EQ(dst.right(), -50);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    // top
+    {
+        Rect rect2(-25, 50, 125, 10);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -25);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 125);
+        ASSERT_EQ(dst.bottom(), 10);
+    }
+
+    {
+        Rect rect2(-100, 50, 200, 25);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), 25);
+    }
+
+    // right
+    {
+        Rect rect2(125, 0, 200, -50);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), 125);
+        ASSERT_EQ(dst.top(), 0);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -50);
+    }
+
+    {
+        Rect rect2(150, 10, 200, -90);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), 150);
+        ASSERT_EQ(dst.top(), 10);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    // bottom
+    {
+        Rect rect2(-25, -50, 125, -100);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -25);
+        ASSERT_EQ(dst.top(), -50);
+        ASSERT_EQ(dst.right(), 125);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    {
+        Rect rect2(-100, -75, 200, -100);
+        Rect::intersection(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), -75);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+}

From 73795b0413baeb85928ebfb7c31e98778d5e395e Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 24 Oct 2024 16:07:38 +0200
Subject: [PATCH 85/94] Add united() method to Rect

---
 include/scratchcpp/rect.h |  1 +
 src/rect.cpp              |  9 ++++
 test/rect/rect_test.cpp   | 88 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+)

diff --git a/include/scratchcpp/rect.h b/include/scratchcpp/rect.h
index 70a8b832..b5fd685c 100644
--- a/include/scratchcpp/rect.h
+++ b/include/scratchcpp/rect.h
@@ -39,6 +39,7 @@ class LIBSCRATCHCPP_EXPORT Rect
         bool contains(double x, double y) const;
 
         static void intersection(const Rect &a, const Rect &b, Rect &dst);
+        static void united(const Rect &a, const Rect &b, Rect &dst);
 
     private:
         spimpl::impl_ptr<RectPrivate> impl;
diff --git a/src/rect.cpp b/src/rect.cpp
index f17b5d7d..cb8e0864 100644
--- a/src/rect.cpp
+++ b/src/rect.cpp
@@ -146,3 +146,12 @@ void Rect::intersection(const Rect &a, const Rect &b, Rect &dst)
     dst.impl->top = std::min(a.impl->top, b.impl->top);
     dst.impl->bottom = std::max(a.impl->bottom, b.impl->bottom);
 }
+
+/*! Saves the union of the given rectangles (in Scratch space) to dst. */
+void Rect::united(const Rect &a, const Rect &b, Rect &dst)
+{
+    dst.impl->left = std::min(a.impl->left, b.impl->left);
+    dst.impl->right = std::max(a.impl->right, b.impl->right);
+    dst.impl->top = std::max(a.impl->top, b.impl->top);
+    dst.impl->bottom = std::min(a.impl->bottom, b.impl->bottom);
+}
diff --git a/test/rect/rect_test.cpp b/test/rect/rect_test.cpp
index 9316f206..689cb8d7 100644
--- a/test/rect/rect_test.cpp
+++ b/test/rect/rect_test.cpp
@@ -422,3 +422,91 @@ TEST(RectTest, Intersection)
         ASSERT_EQ(dst.bottom(), -75);
     }
 }
+
+TEST(RectTest, United)
+{
+    Rect rect1(-50, 25, 150, -75);
+    Rect dst;
+
+    Rect::united(rect1, rect1, dst);
+    ASSERT_EQ(dst.left(), rect1.left());
+    ASSERT_EQ(dst.top(), rect1.top());
+    ASSERT_EQ(dst.right(), rect1.right());
+    ASSERT_EQ(dst.bottom(), rect1.bottom());
+
+    // left
+    {
+        Rect rect2(-75, 0, 125, -50);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -75);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    {
+        Rect rect2(-100, 10, -50, -90);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -100);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -90);
+    }
+
+    // top
+    {
+        Rect rect2(-25, 50, 125, 10);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 50);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    {
+        Rect rect2(-100, 50, 200, 25);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -100);
+        ASSERT_EQ(dst.top(), 50);
+        ASSERT_EQ(dst.right(), 200);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    // right
+    {
+        Rect rect2(125, 0, 200, -50);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 200);
+        ASSERT_EQ(dst.bottom(), -75);
+    }
+
+    {
+        Rect rect2(150, 10, 200, -90);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 200);
+        ASSERT_EQ(dst.bottom(), -90);
+    }
+
+    // bottom
+    {
+        Rect rect2(-25, -50, 125, -100);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -50);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 150);
+        ASSERT_EQ(dst.bottom(), -100);
+    }
+
+    {
+        Rect rect2(-100, -75, 200, -100);
+        Rect::united(rect1, rect2, dst);
+        ASSERT_EQ(dst.left(), -100);
+        ASSERT_EQ(dst.top(), 25);
+        ASSERT_EQ(dst.right(), 200);
+        ASSERT_EQ(dst.bottom(), -100);
+    }
+}

From 18f4617a12965ad3f58f6f496b785fbab4d9e67c Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 24 Oct 2024 16:09:06 +0200
Subject: [PATCH 86/94] Rect: Rename intersection() to intersected()

---
 include/scratchcpp/rect.h |  2 +-
 src/rect.cpp              |  2 +-
 test/rect/rect_test.cpp   | 20 ++++++++++----------
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/include/scratchcpp/rect.h b/include/scratchcpp/rect.h
index b5fd685c..b4771f3f 100644
--- a/include/scratchcpp/rect.h
+++ b/include/scratchcpp/rect.h
@@ -38,7 +38,7 @@ class LIBSCRATCHCPP_EXPORT Rect
         bool intersects(const Rect &rect) const;
         bool contains(double x, double y) const;
 
-        static void intersection(const Rect &a, const Rect &b, Rect &dst);
+        static void intersected(const Rect &a, const Rect &b, Rect &dst);
         static void united(const Rect &a, const Rect &b, Rect &dst);
 
     private:
diff --git a/src/rect.cpp b/src/rect.cpp
index cb8e0864..f6f0d844 100644
--- a/src/rect.cpp
+++ b/src/rect.cpp
@@ -139,7 +139,7 @@ bool Rect::contains(double x, double y) const
 }
 
 /*! Saves the intersection of the given rectangles (in Scratch space) to dst. */
-void Rect::intersection(const Rect &a, const Rect &b, Rect &dst)
+void Rect::intersected(const Rect &a, const Rect &b, Rect &dst)
 {
     dst.impl->left = std::max(a.impl->left, b.impl->left);
     dst.impl->right = std::min(a.impl->right, b.impl->right);
diff --git a/test/rect/rect_test.cpp b/test/rect/rect_test.cpp
index 689cb8d7..fe781efb 100644
--- a/test/rect/rect_test.cpp
+++ b/test/rect/rect_test.cpp
@@ -335,12 +335,12 @@ TEST(RectTest, Contains)
     ASSERT_TRUE(rect_ydown.contains(5, 24));
 }
 
-TEST(RectTest, Intersection)
+TEST(RectTest, Intersected)
 {
     Rect rect1(-50, 25, 150, -75);
     Rect dst;
 
-    Rect::intersection(rect1, rect1, dst);
+    Rect::intersected(rect1, rect1, dst);
     ASSERT_EQ(dst.left(), rect1.left());
     ASSERT_EQ(dst.top(), rect1.top());
     ASSERT_EQ(dst.right(), rect1.right());
@@ -349,7 +349,7 @@ TEST(RectTest, Intersection)
     // left
     {
         Rect rect2(-75, 0, 125, -50);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), -50);
         ASSERT_EQ(dst.top(), 0);
         ASSERT_EQ(dst.right(), 125);
@@ -358,7 +358,7 @@ TEST(RectTest, Intersection)
 
     {
         Rect rect2(-100, 10, -50, -90);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), -50);
         ASSERT_EQ(dst.top(), 10);
         ASSERT_EQ(dst.right(), -50);
@@ -368,7 +368,7 @@ TEST(RectTest, Intersection)
     // top
     {
         Rect rect2(-25, 50, 125, 10);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), -25);
         ASSERT_EQ(dst.top(), 25);
         ASSERT_EQ(dst.right(), 125);
@@ -377,7 +377,7 @@ TEST(RectTest, Intersection)
 
     {
         Rect rect2(-100, 50, 200, 25);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), -50);
         ASSERT_EQ(dst.top(), 25);
         ASSERT_EQ(dst.right(), 150);
@@ -387,7 +387,7 @@ TEST(RectTest, Intersection)
     // right
     {
         Rect rect2(125, 0, 200, -50);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), 125);
         ASSERT_EQ(dst.top(), 0);
         ASSERT_EQ(dst.right(), 150);
@@ -396,7 +396,7 @@ TEST(RectTest, Intersection)
 
     {
         Rect rect2(150, 10, 200, -90);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), 150);
         ASSERT_EQ(dst.top(), 10);
         ASSERT_EQ(dst.right(), 150);
@@ -406,7 +406,7 @@ TEST(RectTest, Intersection)
     // bottom
     {
         Rect rect2(-25, -50, 125, -100);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), -25);
         ASSERT_EQ(dst.top(), -50);
         ASSERT_EQ(dst.right(), 125);
@@ -415,7 +415,7 @@ TEST(RectTest, Intersection)
 
     {
         Rect rect2(-100, -75, 200, -100);
-        Rect::intersection(rect1, rect2, dst);
+        Rect::intersected(rect1, rect2, dst);
         ASSERT_EQ(dst.left(), -50);
         ASSERT_EQ(dst.top(), -75);
         ASSERT_EQ(dst.right(), 150);

From e2fa17fb8d36adcae958cdfac648d1a794d1e653 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 24 Oct 2024 16:13:45 +0200
Subject: [PATCH 87/94] Add 'const' to list getters in IProjectReader

Resolves: #416
---
 src/internal/iprojectreader.h   | 8 ++++----
 src/internal/scratch3reader.cpp | 8 ++++----
 src/internal/scratch3reader.h   | 8 ++++----
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/internal/iprojectreader.h b/src/internal/iprojectreader.h
index dec3b38d..e7223c87 100644
--- a/src/internal/iprojectreader.h
+++ b/src/internal/iprojectreader.h
@@ -31,10 +31,10 @@ class IProjectReader
         virtual bool loadData(const std::string &data) = 0;
         virtual bool isValid() = 0;
         virtual void clear() = 0;
-        virtual const std::vector<std::shared_ptr<Target>> &targets() = 0;
-        virtual const std::vector<std::shared_ptr<Broadcast>> &broadcasts() = 0;
-        virtual const std::vector<std::shared_ptr<Monitor>> &monitors() = 0;
-        virtual const std::vector<std::string> &extensions() = 0;
+        virtual const std::vector<std::shared_ptr<Target>> &targets() const = 0;
+        virtual const std::vector<std::shared_ptr<Broadcast>> &broadcasts() const = 0;
+        virtual const std::vector<std::shared_ptr<Monitor>> &monitors() const = 0;
+        virtual const std::vector<std::string> &extensions() const = 0;
         virtual const std::string &userAgent() const = 0;
 
     protected:
diff --git a/src/internal/scratch3reader.cpp b/src/internal/scratch3reader.cpp
index e7e72b4a..7e903326 100644
--- a/src/internal/scratch3reader.cpp
+++ b/src/internal/scratch3reader.cpp
@@ -527,22 +527,22 @@ void Scratch3Reader::clear()
     m_extensions.clear();
 }
 
-const std::vector<std::shared_ptr<Target>> &Scratch3Reader::targets()
+const std::vector<std::shared_ptr<Target>> &Scratch3Reader::targets() const
 {
     return m_targets;
 }
 
-const std::vector<std::shared_ptr<Broadcast>> &Scratch3Reader::broadcasts()
+const std::vector<std::shared_ptr<Broadcast>> &Scratch3Reader::broadcasts() const
 {
     return m_broadcasts;
 }
 
-const std::vector<std::shared_ptr<Monitor>> &Scratch3Reader::monitors()
+const std::vector<std::shared_ptr<Monitor>> &Scratch3Reader::monitors() const
 {
     return m_monitors;
 }
 
-const std::vector<std::string> &Scratch3Reader::extensions()
+const std::vector<std::string> &Scratch3Reader::extensions() const
 {
     return m_extensions;
 }
diff --git a/src/internal/scratch3reader.h b/src/internal/scratch3reader.h
index 1f9d11a0..84849b5a 100644
--- a/src/internal/scratch3reader.h
+++ b/src/internal/scratch3reader.h
@@ -17,10 +17,10 @@ class Scratch3Reader : public IProjectReader
         bool loadData(const std::string &data) override;
         bool isValid() override;
         void clear() override;
-        const std::vector<std::shared_ptr<Target>> &targets() override;
-        const std::vector<std::shared_ptr<Broadcast>> &broadcasts() override;
-        const std::vector<std::shared_ptr<Monitor>> &monitors() override;
-        const std::vector<std::string> &extensions() override;
+        const std::vector<std::shared_ptr<Target>> &targets() const override;
+        const std::vector<std::shared_ptr<Broadcast>> &broadcasts() const override;
+        const std::vector<std::shared_ptr<Monitor>> &monitors() const override;
+        const std::vector<std::string> &extensions() const override;
         const std::string &userAgent() const override;
 
     private:

From 99847bdf5da8d14fca0092add51e15510ea2c0a2 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 24 Oct 2024 16:30:47 +0200
Subject: [PATCH 88/94] KeyEvent: Implement operator==

Resolves: #455
---
 include/scratchcpp/keyevent.h          |  2 ++
 test/scratch_classes/keyevent_test.cpp | 30 ++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/include/scratchcpp/keyevent.h b/include/scratchcpp/keyevent.h
index 2dd80715..012deb89 100644
--- a/include/scratchcpp/keyevent.h
+++ b/include/scratchcpp/keyevent.h
@@ -31,6 +31,8 @@ class LIBSCRATCHCPP_EXPORT KeyEvent
         Type type() const;
         const std::string &name() const;
 
+        friend bool operator==(const KeyEvent &ev1, const KeyEvent &ev2) { return ev1.name() == ev2.name(); }
+
     private:
         spimpl::impl_ptr<KeyEventPrivate> impl;
 };
diff --git a/test/scratch_classes/keyevent_test.cpp b/test/scratch_classes/keyevent_test.cpp
index 280f6482..c9f89a93 100644
--- a/test/scratch_classes/keyevent_test.cpp
+++ b/test/scratch_classes/keyevent_test.cpp
@@ -213,3 +213,33 @@ TEST(KeyEventTest, Constructors)
         ASSERT_EQ(event.name(), "down arrow");
     }
 }
+
+TEST(KeyEventTest, EqualityOperator)
+{
+    KeyEvent ev1(KeyEvent::Type::Any);
+    KeyEvent ev2(KeyEvent::Type::Any);
+    KeyEvent ev3(KeyEvent::Type::Space);
+    KeyEvent ev4(KeyEvent::Type::Space);
+    KeyEvent ev5(KeyEvent::Type::Left);
+    KeyEvent ev6(KeyEvent::Type::Left);
+    KeyEvent ev7(KeyEvent::Type::Right);
+    KeyEvent ev8(KeyEvent::Type::Right);
+    KeyEvent ev9("a");
+    KeyEvent ev10("A");
+    KeyEvent ev11("8");
+    KeyEvent ev12("8");
+    KeyEvent ev13("space");
+
+    ASSERT_TRUE(ev1 == ev2);
+    ASSERT_TRUE(ev3 == ev4);
+    ASSERT_TRUE(ev5 == ev6);
+    ASSERT_TRUE(ev7 == ev8);
+    ASSERT_TRUE(ev9 == ev10);
+    ASSERT_TRUE(ev11 == ev12);
+    ASSERT_TRUE(ev3 == ev13);
+
+    ASSERT_FALSE(ev1 == ev3);
+    ASSERT_FALSE(ev4 == ev6);
+    ASSERT_FALSE(ev7 == ev10);
+    ASSERT_FALSE(ev11 == ev13);
+}

From d37f31232887cb588333731b56d019cbb8ea86ae Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Thu, 24 Oct 2024 17:40:17 +0200
Subject: [PATCH 89/94] Drawable: Add layerOrderChanged signal

Resolves: #587
---
 include/scratchcpp/drawable.h          |  2 ++
 src/scratch/drawable.cpp               |  7 +++++++
 src/scratch/drawable_p.h               |  3 +++
 test/scratch_classes/drawable_test.cpp | 15 +++++++++++++++
 4 files changed, 27 insertions(+)

diff --git a/include/scratchcpp/drawable.h b/include/scratchcpp/drawable.h
index 8424bf59..1958ccec 100644
--- a/include/scratchcpp/drawable.h
+++ b/include/scratchcpp/drawable.h
@@ -3,6 +3,7 @@
 #pragma once
 
 #include "global.h"
+#include "signal.h"
 #include "spimpl.h"
 
 namespace libscratchcpp
@@ -27,6 +28,7 @@ class LIBSCRATCHCPP_EXPORT Drawable
 
         int layerOrder() const;
         virtual void setLayerOrder(int newLayerOrder);
+        sigslot::signal<int> &layerOrderChanged() const;
 
         IEngine *engine() const;
         virtual void setEngine(IEngine *engine);
diff --git a/src/scratch/drawable.cpp b/src/scratch/drawable.cpp
index 2b5e530d..56748443 100644
--- a/src/scratch/drawable.cpp
+++ b/src/scratch/drawable.cpp
@@ -22,6 +22,13 @@ int Drawable::layerOrder() const
 void Drawable::setLayerOrder(int newLayerOrder)
 {
     impl->layerOrder = newLayerOrder;
+    impl->layerOrderChanged(newLayerOrder);
+}
+
+/*! Emits when the layer number changes. */
+sigslot::signal<int> &Drawable::layerOrderChanged() const
+{
+    return impl->layerOrderChanged;
 }
 
 /*! Returns the engine. */
diff --git a/src/scratch/drawable_p.h b/src/scratch/drawable_p.h
index 12a06331..1c20cc6c 100644
--- a/src/scratch/drawable_p.h
+++ b/src/scratch/drawable_p.h
@@ -2,6 +2,8 @@
 
 #pragma once
 
+#include <scratchcpp/signal.h>
+
 namespace libscratchcpp
 {
 
@@ -11,6 +13,7 @@ struct DrawablePrivate
 {
         int layerOrder = 0;
         IEngine *engine = nullptr;
+        mutable sigslot::signal<int> layerOrderChanged;
 };
 
 } // namespace libscratchcpp
diff --git a/test/scratch_classes/drawable_test.cpp b/test/scratch_classes/drawable_test.cpp
index b8a80670..407af05d 100644
--- a/test/scratch_classes/drawable_test.cpp
+++ b/test/scratch_classes/drawable_test.cpp
@@ -5,6 +5,12 @@
 
 using namespace libscratchcpp;
 
+class LayerOrderMock
+{
+    public:
+        MOCK_METHOD(void, f, (int));
+};
+
 TEST(DrawableTest, IsTarget)
 {
     Drawable drawable;
@@ -21,8 +27,17 @@ TEST(DrawableTest, LayerOrder)
 {
     Drawable drawable;
     ASSERT_EQ(drawable.layerOrder(), 0);
+
+    LayerOrderMock spy;
+    drawable.layerOrderChanged().connect(&LayerOrderMock::f, &spy);
+
+    EXPECT_CALL(spy, f(2));
     drawable.setLayerOrder(2);
     ASSERT_EQ(drawable.layerOrder(), 2);
+
+    EXPECT_CALL(spy, f(0));
+    drawable.setLayerOrder(0);
+    ASSERT_EQ(drawable.layerOrder(), 0);
 }
 
 TEST(TargetTest, Engine)

From 2bae9ccf317930b64ce681b06d57479c90343b14 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 1 Nov 2024 12:02:29 +0100
Subject: [PATCH 90/94] Fix NaN to double conversion

---
 src/scratch/value_functions.cpp     | 6 +++---
 test/scratch_classes/value_test.cpp | 2 ++
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp
index f8763200..b35092d0 100644
--- a/src/scratch/value_functions.cpp
+++ b/src/scratch/value_functions.cpp
@@ -222,9 +222,9 @@ extern "C"
     /*! Returns the double representation of the given value. */
     double value_toDouble(const libscratchcpp::ValueData *v)
     {
-        if (v->type == ValueType::Number)
-            return v->numberValue;
-        else if (v->type == ValueType::Bool)
+        if (v->type == ValueType::Number) {
+            return std::isnan(v->numberValue) ? 0 : v->numberValue;
+        } else if (v->type == ValueType::Bool)
             return v->boolValue;
         else if (v->type == ValueType::String)
             return value_stringToDouble(v->stringValue);
diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp
index f2e24d7e..359bc946 100644
--- a/test/scratch_classes/value_test.cpp
+++ b/test/scratch_classes/value_test.cpp
@@ -981,6 +981,8 @@ TEST(ValueTest, ToDouble)
     ASSERT_TRUE(v.toDouble() < 0 && std::isinf(v.toDouble()));
     v = "NaN";
     ASSERT_EQ(v.toDouble(), 0.0);
+    v = std::numeric_limits<double>::quiet_NaN();
+    ASSERT_EQ(v.toDouble(), 0.0);
 
     v = "something";
     ASSERT_EQ(v.toDouble(), 0.0);

From 11583d160fbc55605208da67b6a1d6622e30d06d Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Fri, 1 Nov 2024 15:33:39 +0100
Subject: [PATCH 91/94] LLVMCodeBuilder: Fix NaN constants

---
 src/dev/engine/internal/llvmcodebuilder.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp
index b35efa2b..22f3d552 100644
--- a/src/dev/engine/internal/llvmcodebuilder.cpp
+++ b/src/dev/engine/internal/llvmcodebuilder.cpp
@@ -877,8 +877,10 @@ llvm::Value *LLVMCodeBuilder::castRawValue(std::shared_ptr<Register> reg, Compil
 llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType)
 {
     switch (targetType) {
-        case Compiler::StaticType::Number:
-            return llvm::ConstantFP::get(m_ctx, llvm::APFloat(value.toDouble()));
+        case Compiler::StaticType::Number: {
+            const double nan = std::numeric_limits<double>::quiet_NaN();
+            return llvm::ConstantFP::get(m_ctx, llvm::APFloat(value.isNaN() ? nan : value.toDouble()));
+        }
 
         case Compiler::StaticType::Bool:
             return m_builder.getInt1(value.toBool());

From 994a83ed41f3d33db5c0afec63b663ea3a420a57 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 3 Nov 2024 19:26:43 +0100
Subject: [PATCH 92/94] Compiler: Remove obsolete substacks variable

---
 src/dev/engine/compiler.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 4293341b..a9435696 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -45,8 +45,6 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
     impl->block = startBlock;
 
     while (impl->block) {
-        size_t substacks = impl->substackTree.size();
-
         if (impl->block->compileFunction())
             impl->block->compile(this);
         else {

From 1d1867fae290285ee37e6169abb010804300186a Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 3 Nov 2024 19:27:01 +0100
Subject: [PATCH 93/94] Compiler: Update addFunctionCall() description

---
 src/dev/engine/compiler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index a9435696..271fff30 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -69,7 +69,7 @@ std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBl
 
 /*!
  * Adds a call to the given function.\n
- * For example: extern "C" some_block(ValueData *ret, ValueData *arg1, ValueData *arg2) has 2 arguments
+ * For example: extern "C" bool some_block(Target *target, double arg1, const char *arg2)
  */
 void Compiler::addFunctionCall(const std::string &functionName, StaticType returnType, const std::vector<StaticType> &argTypes)
 {

From d9e0e1124174a23d9f2783e756027da2f57d67b5 Mon Sep 17 00:00:00 2001
From: adazem009 <68537469+adazem009@users.noreply.github.com>
Date: Sun, 3 Nov 2024 20:21:30 +0100
Subject: [PATCH 94/94] Compiler: Add methods for 'and', 'or', 'not' gates

---
 include/scratchcpp/dev/compiler.h   |  4 +++
 src/dev/engine/compiler.cpp         | 18 +++++++++++++
 test/dev/compiler/compiler_test.cpp | 39 +++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+)

diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h
index 82cc3dc6..3b579023 100644
--- a/include/scratchcpp/dev/compiler.h
+++ b/include/scratchcpp/dev/compiler.h
@@ -57,6 +57,10 @@ class LIBSCRATCHCPP_EXPORT Compiler
         void createCmpGT();
         void createCmpLT();
 
+        void createAnd();
+        void createOr();
+        void createNot();
+
         void moveToIf(std::shared_ptr<Block> substack);
         void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
         void moveToRepeatLoop(std::shared_ptr<Block> substack);
diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp
index 271fff30..7ff6737e 100644
--- a/src/dev/engine/compiler.cpp
+++ b/src/dev/engine/compiler.cpp
@@ -142,6 +142,24 @@ void Compiler::createCmpLT()
     impl->builder->createCmpLT();
 }
 
+/*! Creates an AND operation using the last 2 values. */
+void Compiler::createAnd()
+{
+    impl->builder->createAnd();
+}
+
+/*! Creates an OR operation using the last 2 values. */
+void Compiler::createOr()
+{
+    impl->builder->createOr();
+}
+
+/*! Creates a NOT operation using the last value. */
+void Compiler::createNot()
+{
+    impl->builder->createNot();
+}
+
 /*! Jumps to the given if substack. */
 void Compiler::moveToIf(std::shared_ptr<Block> substack)
 {
diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp
index 2a0089d2..0ebe69c5 100644
--- a/test/dev/compiler/compiler_test.cpp
+++ b/test/dev/compiler/compiler_test.cpp
@@ -310,6 +310,45 @@ TEST_F(CompilerTest, CreateCmpLT)
     compile(compiler, block);
 }
 
+TEST_F(CompilerTest, CreateAnd)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createAnd);
+        compiler->createAnd();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateOr)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createOr);
+        compiler->createOr();
+    });
+
+    compile(compiler, block);
+}
+
+TEST_F(CompilerTest, CreateNot)
+{
+    Compiler compiler(&m_engine, &m_target);
+    auto block = std::make_shared<Block>("", "");
+
+    block->setCompileFunction([](Compiler *compiler) {
+        EXPECT_CALL(*m_builder, createNot);
+        compiler->createNot();
+    });
+
+    compile(compiler, block);
+}
+
 TEST_F(CompilerTest, MoveToIf)
 {
     Compiler compiler(&m_engine, &m_target);
