diff --git a/src/language/ASTNodeJumpPlacementChecker.cpp b/src/language/ASTNodeJumpPlacementChecker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef1610ec702d06229612f1f7af1eb4cce6a4c29d
--- /dev/null
+++ b/src/language/ASTNodeJumpPlacementChecker.cpp
@@ -0,0 +1,32 @@
+#include <ASTNodeJumpPlacementChecker.hpp>
+
+#include <PEGGrammar.hpp>
+
+#include <vector>
+
+void
+ASTNodeJumpPlacementChecker::_checkJumpPlacement(ASTNode& n, bool is_inside_loop)
+{
+  if (n.is<language::for_statement>() or n.is<language::do_while_statement>() or n.is<language::while_statement>()) {
+    for (auto& child : n.children) {
+      this->_checkJumpPlacement(*child, true);
+    }
+  } else if (n.is<language::break_kw>() or n.is<language::continue_kw>()) {
+    if (not is_inside_loop) {
+      std::ostringstream error_message;
+      error_message << "unexpected '" << rang::fgB::red << n.string() << rang::fg::reset
+                    << "' outside of loop or switch statement";
+      throw parse_error(error_message.str(), std::vector{n.begin()});
+    }
+  } else {
+    for (auto& child : n.children) {
+      this->_checkJumpPlacement(*child, is_inside_loop);
+    }
+  }
+}
+
+ASTNodeJumpPlacementChecker::ASTNodeJumpPlacementChecker(ASTNode& n)
+{
+  Assert(n.is_root());
+  this->_checkJumpPlacement(n, false);
+}
diff --git a/src/language/ASTNodeJumpPlacementChecker.hpp b/src/language/ASTNodeJumpPlacementChecker.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..23da8732a589f68e4a1fc22e003c79c6eb42cbae
--- /dev/null
+++ b/src/language/ASTNodeJumpPlacementChecker.hpp
@@ -0,0 +1,15 @@
+#ifndef AST_NODE_JUMP_PLACEMENT_CHECKER_HPP
+#define AST_NODE_JUMP_PLACEMENT_CHECKER_HPP
+
+#include <ASTNode.hpp>
+
+class ASTNodeJumpPlacementChecker
+{
+ private:
+  void _checkJumpPlacement(ASTNode& node, bool is_inside_loop);
+
+ public:
+  ASTNodeJumpPlacementChecker(ASTNode& root_node);
+};
+
+#endif   // AST_NODE_JUMP_PLACEMENT_CHECKER_HPP
diff --git a/src/language/CMakeLists.txt b/src/language/CMakeLists.txt
index b6a386df04e1444825b7083bab7e81ff275418ef..ae4c4956356698eaa518f370c585f9540b53b3d8 100644
--- a/src/language/CMakeLists.txt
+++ b/src/language/CMakeLists.txt
@@ -14,6 +14,7 @@ add_library(
   ASTNodeDataTypeChecker.cpp
   ASTNodeExpressionBuilder.cpp
   ASTNodeIncDecExpressionBuilder.cpp
+  ASTNodeJumpPlacementChecker.cpp
   ASTNodeUnaryOperatorExpressionBuilder.cpp
   ASTNodeValueBuilder.cpp
   ASTPrinter.cpp
diff --git a/src/language/PugsParser.cpp b/src/language/PugsParser.cpp
index b9b0a4677a64aba4c75cfbab911f67c9075bbf23..518250a2921e42a5d83dcb62658165ca34204d72 100644
--- a/src/language/PugsParser.cpp
+++ b/src/language/PugsParser.cpp
@@ -22,6 +22,8 @@
 #include <ASTNodeDataTypeBuilder.hpp>
 #include <ASTNodeDataTypeChecker.hpp>
 
+#include <ASTNodeJumpPlacementChecker.hpp>
+
 #include <ASTNodeExpressionBuilder.hpp>
 
 #include <ASTSymbolInitializationChecker.hpp>
@@ -34,37 +36,6 @@
 
 namespace language
 {
-namespace internal
-{
-void
-check_break_or_continue_placement(const ASTNode& n, bool is_inside_loop)
-{
-  if (n.is<language::for_statement>() or n.is<language::do_while_statement>() or n.is<language::while_statement>()) {
-    for (auto& child : n.children) {
-      check_break_or_continue_placement(*child, true);
-    }
-  } else if (n.is<language::break_kw>() or n.is<language::continue_kw>()) {
-    if (not is_inside_loop) {
-      std::ostringstream error_message;
-      error_message << "unexpected '" << rang::fgB::red << n.string() << rang::fg::reset
-                    << "' outside of loop or switch statement";
-      throw parse_error(error_message.str(), std::vector{n.begin()});
-    }
-  } else {
-    for (auto& child : n.children) {
-      check_break_or_continue_placement(*child, is_inside_loop);
-    }
-  }
-}
-}   // namespace internal
-
-void
-check_break_or_continue_placement(const ASTNode& n)
-{
-  Assert(n.is_root());
-  internal::check_break_or_continue_placement(n, false);
-}
-
 namespace internal
 {
 void
@@ -126,7 +97,7 @@ parser(const std::string& filename)
 
     ASTNodeValueBuilder{*root_node};
 
-    language::check_break_or_continue_placement(*root_node);
+    ASTNodeJumpPlacementChecker{*root_node};
 
     // optimizations
     language::simplify_declarations(*root_node);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index c3c89e619c510b836484f66048830754613deccf..246771d4842ef6fb58b969dd0c705e645d9dfa35 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -10,6 +10,7 @@ add_executable (unit_tests
   test_ASTNodeDataType.cpp
   test_ASTNodeDataTypeBuilder.cpp
   test_ASTNodeDataTypeChecker.cpp
+  test_ASTNodeJumpPlacementChecker.cpp
   test_ASTNodeValueBuilder.cpp
   test_ASTPrinter.cpp
   test_ASTSymbolTableBuilder.cpp
diff --git a/tests/test_ASTNodeJumpPlacementChecker.cpp b/tests/test_ASTNodeJumpPlacementChecker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7a7679199089225fc33175f2fda16759b0bf0346
--- /dev/null
+++ b/tests/test_ASTNodeJumpPlacementChecker.cpp
@@ -0,0 +1,147 @@
+#include <catch2/catch.hpp>
+
+#include <ASTBuilder.hpp>
+
+#include <ASTNodeDataTypeBuilder.hpp>
+#include <ASTNodeJumpPlacementChecker.hpp>
+
+TEST_CASE("ASTNodeJumpPlacementChecker", "[language]")
+{
+  SECTION("break")
+  {
+    SECTION("in for loop")
+    {
+      std::string_view data = R"(
+for(;;) {
+  break;
+}
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      REQUIRE_NOTHROW(ASTNodeJumpPlacementChecker{*ast});
+    }
+
+    SECTION("in while loop")
+    {
+      std::string_view data = R"(
+while(true) {
+  break;
+}
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      REQUIRE_NOTHROW(ASTNodeJumpPlacementChecker{*ast});
+    }
+
+    SECTION("in do while loop")
+    {
+      std::string_view data = R"(
+do {
+  break;
+} while(true);
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      REQUIRE_NOTHROW(ASTNodeJumpPlacementChecker{*ast});
+    }
+
+    SECTION("misplaced")
+    {
+      std::string_view data = R"(
+{
+  break;
+}
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      ast->children[0]->m_data_type = ASTNodeDataType::undefined_t;
+
+      REQUIRE_THROWS_AS(ASTNodeJumpPlacementChecker{*ast}, parse_error);
+    }
+  }
+
+  SECTION("continue")
+  {
+    SECTION("in for loop")
+    {
+      std::string_view data = R"(
+for(;;) {
+  continue;
+}
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      REQUIRE_NOTHROW(ASTNodeJumpPlacementChecker{*ast});
+    }
+
+    SECTION("in while loop")
+    {
+      std::string_view data = R"(
+while(true) {
+  continue;
+}
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      REQUIRE_NOTHROW(ASTNodeJumpPlacementChecker{*ast});
+    }
+
+    SECTION("in do while loop")
+    {
+      std::string_view data = R"(
+do {
+  continue;
+} while(true);
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      REQUIRE_NOTHROW(ASTNodeJumpPlacementChecker{*ast});
+    }
+
+    SECTION("misplaced")
+    {
+      std::string_view data = R"(
+{
+  continue;
+}
+)";
+
+      string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTNodeDataTypeBuilder{*ast};
+
+      ast->children[0]->m_data_type = ASTNodeDataType::undefined_t;
+
+      REQUIRE_THROWS_AS(ASTNodeJumpPlacementChecker{*ast}, parse_error);
+    }
+  }
+}