diff --git a/include/ast/passes/control_flow.h b/include/ast/passes/control_flow.h new file mode 100644 index 0000000..044602f --- /dev/null +++ b/include/ast/passes/control_flow.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ast/passes/pass.h" + +class ControlFlow : public Pass { + private: + bool in_function; + bool in_loop; + + public: + ControlFlow(); + void visit_function(shared_ptr) override; + void visit_return(shared_ptr) override; + void visit_loop(shared_ptr) override; + void visit_continue(shared_ptr) override; + void visit_break(shared_ptr) override; +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7de009e..948b6d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,7 @@ set( "${CMAKE_CURRENT_SOURCE_DIR}/ast/passes/def_ref.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ast/passes/type_check.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ast/passes/builtin.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ast/passes/control_flow.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/shared/type/type.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/shared/type/character.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/shared/type/integer.cpp" diff --git a/src/ast/passes/control_flow.cpp b/src/ast/passes/control_flow.cpp new file mode 100644 index 0000000..c309c13 --- /dev/null +++ b/src/ast/passes/control_flow.cpp @@ -0,0 +1,48 @@ +#include "ast/passes/control_flow.h" +#include "errors/errors.h" + +ControlFlow::ControlFlow() : Pass("Control Flow") { + this->in_function = false; + this->in_loop = false; +} + +void ControlFlow::visit_function(shared_ptr node) { + for (const auto& param : node->params) { + visit(param); + } + + in_function = true; + visit(node->body); + in_function = false; +} + +void ControlFlow::visit_return(shared_ptr node) { + if (!in_function) { + throw SyntaxError(node->token->getLine(), + "found `return` outside of function"); + } + visit(node->expr); +} + +void ControlFlow::visit_loop(shared_ptr node) { + in_loop = true; + visit(node->variable); + visit(node->condition); + visit(node->assignment); + visit(node->body); + in_loop = false; +} + +void ControlFlow::visit_continue(shared_ptr node) { + if (!in_loop) { + throw SyntaxError(node->token->getLine(), + "found `continue` outside of loop"); + } +} + +void ControlFlow::visit_break(shared_ptr node) { + if (!in_loop) { + throw SyntaxError(node->token->getLine(), + "found `break` outside of loop"); + } +} diff --git a/src/ast/passes/pass.cpp b/src/ast/passes/pass.cpp index f9674d0..b669f22 100644 --- a/src/ast/passes/pass.cpp +++ b/src/ast/passes/pass.cpp @@ -1,6 +1,7 @@ #include "ast/passes/pass.h" #include "ast/ast.h" #include "ast/passes/builtin.h" +#include "ast/passes/control_flow.h" #include "ast/passes/def_ref.h" #include "ast/passes/type_check.h" @@ -13,6 +14,7 @@ constexpr bool debug = false; void Pass::run_passes(std::shared_ptr ast, shared_ptr symtab) { std::vector> passes = { + std::make_shared(), std::make_shared(symtab), std::make_shared(), std::make_shared(symtab), diff --git a/tests/input/errors/syntax/break.in b/tests/input/errors/syntax/break.in new file mode 100644 index 0000000..79ab6a8 --- /dev/null +++ b/tests/input/errors/syntax/break.in @@ -0,0 +1,5 @@ +fn main(): i32 { + break; + return 0; +} + diff --git a/tests/input/errors/syntax/continue.in b/tests/input/errors/syntax/continue.in new file mode 100644 index 0000000..4159880 --- /dev/null +++ b/tests/input/errors/syntax/continue.in @@ -0,0 +1,4 @@ +fn main(): i32 { + continue; + return 0; +} diff --git a/tests/input/errors/syntax/return.in b/tests/input/errors/syntax/return.in new file mode 100644 index 0000000..100e220 --- /dev/null +++ b/tests/input/errors/syntax/return.in @@ -0,0 +1,5 @@ +return 0; + +fn main(): i32 { + return 0; +} diff --git a/tests/output/errors/syntax/break.out b/tests/output/errors/syntax/break.out new file mode 100644 index 0000000..8142565 --- /dev/null +++ b/tests/output/errors/syntax/break.out @@ -0,0 +1 @@ +SyntaxError on Line 2 \ No newline at end of file diff --git a/tests/output/errors/syntax/continue.out b/tests/output/errors/syntax/continue.out new file mode 100644 index 0000000..8142565 --- /dev/null +++ b/tests/output/errors/syntax/continue.out @@ -0,0 +1 @@ +SyntaxError on Line 2 \ No newline at end of file diff --git a/tests/output/errors/syntax/return.out b/tests/output/errors/syntax/return.out new file mode 100644 index 0000000..067d1d4 --- /dev/null +++ b/tests/output/errors/syntax/return.out @@ -0,0 +1 @@ +SyntaxError on Line 1 \ No newline at end of file