From 321a6846a76f7e4657095cacc201f7d393c01ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C5=A0torc?= <storcond@fit.cvut.cz> Date: Tue, 4 Apr 2023 13:18:06 +0200 Subject: [PATCH] cli: Make scope checking more clear --- alib2cli/src/parser/AltVisitor.Command.cpp | 33 ++++++++------------ alib2cli/src/parser/AltVisitor.Statement.cpp | 3 +- alib2cli/src/parser/AltVisitor.h | 21 +++++++++++-- alib2cli/test-src/aql/InvalidGrammarTest.cpp | 12 +++---- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/alib2cli/src/parser/AltVisitor.Command.cpp b/alib2cli/src/parser/AltVisitor.Command.cpp index 6937dd9b2..b219d8433 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 d6c3469f9..f8365687d 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 3f6a4746c..718fe2ea9 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 ec3ad6cbe..c2ac7be86 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")); } } -- GitLab