diff --git a/alib2cli/src/parser/AltVisitor.Command.cpp b/alib2cli/src/parser/AltVisitor.Command.cpp index 6937dd9b2f0811fa36b31c74f356a0a6f34f0b9e..b219d843327ce6dc411c9e032b9dcae572b15358 100644 --- a/alib2cli/src/parser/AltVisitor.Command.cpp +++ b/alib2cli/src/parser/AltVisitor.Command.cpp @@ -40,9 +40,7 @@ using QualifedType = std::pair<abstraction::TypeQualifiers::TypeQualifierSet, st std::any AltVisitor::visitIfCommand(AltCliParser::IfCommandContext* ctx) { - if (isGlobalScope()) { - throw exception::CommonException("If statement available in non-global scope only."); - } + ensureScope("if", Scope::Local); auto expression = castToUnique<Expression>(visit(ctx->condition)); auto thenBranch = castToUnique<Command>(visit(ctx->then_branch)); @@ -57,9 +55,7 @@ std::any AltVisitor::visitIfCommand(AltCliParser::IfCommandContext* ctx) std::any AltVisitor::visitWhileCommand(AltCliParser::WhileCommandContext* ctx) { - if (isGlobalScope()) { - throw exception::CommonException("While statement available in non-global scope only."); - } + ensureScope("while", Scope::Local); auto condition = castToUnique<Expression>(visit(ctx->condition)); auto body = castToUnique<Command>(visit(ctx->body)); @@ -144,9 +140,7 @@ std::any AltVisitor::visitReturn(AltCliParser::ReturnContext* ctx) std::any AltVisitor::visitCycleControl(AltCliParser::CycleControlContext* ctx) { - if (isGlobalScope()) { - throw exception::CommonException("Cycle control command available in non-global scope only."); - } + ensureScope("break/continue", Scope::Local); if (ctx->KW_BREAK() != nullptr) return retPtr<Command, BreakCommand>(); @@ -215,9 +209,7 @@ std::any AltVisitor::visitHelp(AltCliParser::HelpContext* ctx) std::any AltVisitor::visitFunction(AltCliParser::FunctionContext* ctx) { - if (!isGlobalScope()) { - throw exception::CommonException("Function declaration available in global scope only."); - } + ensureScope("function", Scope::Global); std::string name; if (ctx->string() != nullptr) @@ -238,9 +230,8 @@ std::any AltVisitor::visitFunction(AltCliParser::FunctionContext* ctx) std::any AltVisitor::visitProcedure(AltCliParser::ProcedureContext* ctx) { - if (!isGlobalScope()) { - throw exception::CommonException("Procedure declaration available in global scope only."); - } + ensureScope("procedure", Scope::Global); + std::string name; if (ctx->string() != nullptr) name = cast<std::string>(visit(ctx->string())); @@ -259,9 +250,7 @@ std::any AltVisitor::visitProcedure(AltCliParser::ProcedureContext* ctx) std::any AltVisitor::visitUndeclareFunction(AltCliParser::UndeclareFunctionContext* ctx) { - if (!isGlobalScope()) { - throw exception::CommonException("Undeclare declaration available in global scope only."); - } + ensureScope("undeclare", Scope::Global); std::string name; if (ctx->identifier() != nullptr) @@ -287,8 +276,10 @@ std::any AltVisitor::visitShowMeasurement(AltCliParser::ShowMeasurementContext* measurements::MeasurementFormat format; if (ctx->KW_LIST() != nullptr) format = measurements::MeasurementFormat::LIST; - else + else if (ctx->KW_TREE() != nullptr) format = measurements::MeasurementFormat::TREE; + else + invalidParse("Invalid measurement format"); return retPtr<Command, ShowMeasurements>(format); } @@ -394,8 +385,10 @@ std::any AltVisitor::visitLoad(AltCliParser::LoadContext* ctx) if (ctx->KW_LOAD() != nullptr) return retPtr<Command, LoadCommand>(name); - else + if (ctx->KW_UNLOAD() != nullptr) return retPtr<Command, UnloadCommand>(name); + + invalidParse("Invalid (un)load command"); } std::any AltVisitor::visitCalc(AltCliParser::CalcContext* ctx) diff --git a/alib2cli/src/parser/AltVisitor.Statement.cpp b/alib2cli/src/parser/AltVisitor.Statement.cpp index d6c3469f9eb08b15cf83479504f7f52f730357ac..f8365687dcf8545c2c7de397623100b7afd9d4bc 100644 --- a/alib2cli/src/parser/AltVisitor.Statement.cpp +++ b/alib2cli/src/parser/AltVisitor.Statement.cpp @@ -29,8 +29,7 @@ std::any AltVisitor::visitSingleStatement(AltCliParser::SingleStatementContext* if (ctx->category_option() != nullptr) category = castToUnique<CategoryOption>(visit(ctx->category_option())); - return retPtr<Statement, SingleStatement>( - std::move(name), std::move(templateArgs), std::move(statements), std::move(category)); + return retPtr<Statement, SingleStatement>(std::move(name), std::move(templateArgs), std::move(statements), std::move(category)); } std::any AltVisitor::visitPreviousResultParam(AltCliParser::PreviousResultParamContext*) diff --git a/alib2cli/src/parser/AltVisitor.h b/alib2cli/src/parser/AltVisitor.h index 3f6a4746c81a13ad08a64123b1e063156f3d573e..718fe2ea9d94bf58e849cf9ea7c4b7f730f38ba2 100644 --- a/alib2cli/src/parser/AltVisitor.h +++ b/alib2cli/src/parser/AltVisitor.h @@ -194,6 +194,11 @@ struct AltVisitor : public AltCliParserBaseVisitor { std::any visitContainerStatement(AltCliParser::ContainerStatementContext* ctx) override; private: + enum class Scope { + Global, + Local, + }; + size_t nestedLevel = 0; void incNestedLevel() @@ -206,9 +211,21 @@ private: nestedLevel--; } - bool isGlobalScope() const + Scope isGlobalScope() const + { + return nestedLevel == 0 ? Scope::Global : Scope::Local; + } + + void ensureScope(const std::string& commandName, const Scope& scope, const std::source_location location = std::source_location::current()) const { - return nestedLevel == 0; + if (scope != isGlobalScope()) { + std::string scopeName = scope == Scope::Global ? "global" : "local"; + std::ostringstream out; + out << "Invalid scope for " << commandName << " command. Expected scope was '" << scopeName << "'." << std::endl; + out << "At " << location.file_name() << ":" << location.line() << std::endl; + out << "\t" << location.function_name() << std::endl; + throw std::runtime_error(out.str()); + } } protected: diff --git a/alib2cli/test-src/aql/InvalidGrammarTest.cpp b/alib2cli/test-src/aql/InvalidGrammarTest.cpp index ec3ad6cbe731d2d2bf24c96bb1df479b79313220..c2ac7be8646740bc3ca49bdfa75dd473c71426ec 100644 --- a/alib2cli/test-src/aql/InvalidGrammarTest.cpp +++ b/alib2cli/test-src/aql/InvalidGrammarTest.cpp @@ -12,13 +12,13 @@ TEST_CASE("Invalid grammar - Scope") { SECTION("Undeclare") { - CHECK_THROWS_AS(newParseString("begin undeclare a (); end"), exception::CommonException); + CHECK_THROWS_AS(newParseString("begin undeclare a (); end"), std::runtime_error); CHECK_NOTHROW(newParseString("undeclare a ()")); } SECTION("If") { - CHECK_THROWS_AS(newParseString("if (1) then print 1"), exception::CommonException); + CHECK_THROWS_AS(newParseString("if (1) then begin print 1; end"), std::runtime_error); CHECK_NOTHROW(newParseString("begin if (1) then print 1; end")); } @@ -31,25 +31,25 @@ TEST_CASE("Invalid grammar - Scope") SECTION("Break") { - CHECK_THROWS_AS(newParseString("break"), exception::CommonException); + CHECK_THROWS_AS(newParseString("break"), std::runtime_error); CHECK_NOTHROW(newParseString("begin break; end")); } SECTION("Continue") { - CHECK_THROWS_AS(newParseString("continue"), exception::CommonException); + CHECK_THROWS_AS(newParseString("continue"), std::runtime_error); CHECK_NOTHROW(newParseString("begin continue; end")); } SECTION("Function") { - CHECK_THROWS_AS(newParseString("begin function func () returning auto return 1; end"), exception::CommonException); + CHECK_THROWS_AS(newParseString("begin function func () returning auto return 1; end"), std::runtime_error); CHECK_NOTHROW(newParseString("function func () returning auto return 1")); } SECTION("Procedure") { - CHECK_THROWS_AS(newParseString("begin procedure func () print 1; end"), exception::CommonException); + CHECK_THROWS_AS(newParseString("begin procedure func () print 1; end"), std::runtime_error); CHECK_NOTHROW(newParseString("procedure func () print 1")); } }