diff --git a/alib2cli/src/grammar/AltCliParser.g4 b/alib2cli/src/grammar/AltCliParser.g4 index 088e9052b189e4333e94117b66cdb63ee933a354..41a5dd7a228991e709a8435c54281ea69e2c2b2b 100644 --- a/alib2cli/src/grammar/AltCliParser.g4 +++ b/alib2cli/src/grammar/AltCliParser.g4 @@ -6,7 +6,7 @@ options { // Initial rule parse - : NEWLINE* command (NEWLINE command?)* EOF + : NEWLINE* top_level_command (NEWLINE top_level_command?)* EOF | NEWLINE* EOF; arg @@ -80,7 +80,7 @@ semicolon_command : block # BlockSemicolonCommand | command_if # IfSemicolonCommand | command_while # WhileSemicolonCommand - | command SEMICOLON # SemicolonCommand + | nested_level_command SEMICOLON # SemicolonCommand ; introspect_cast_from_to @@ -97,7 +97,7 @@ introspect_command | KW_DENORMALIZATIONS # IntrospectDeormalizations | KW_VARIABLES variable? # IntrospectVariables | KW_BINDINGS binding_name? # IntrospectBindings - | KW_AST command # IntrospectAst + | KW_AST top_level_command # IntrospectAst ; batch @@ -147,13 +147,21 @@ command | block # BlockCommand | KW_EVAL ( INTEGER | identifier | string ) # Eval | KW_INTERPRET ( identifier | string ) # Interpret - | command_if # If + | KW_DECLARE qualified_type DOLAR_SIGN arg ASSIGN_OPERATOR expression # Declare // Should be top-level command? + ; + +nested_level_command + : command # NestedLevelCommand + | command_if # If | command_while # While | (KW_BREAK | KW_CONTINUE) # CycleControl - | KW_DECLARE qualified_type DOLAR_SIGN arg ASSIGN_OPERATOR expression # Declare - | KW_UNDECLARE ( INTEGER | identifier | string ) LEFT_PAREN (qualified_type (COMMA qualified_type)*)? RIGHT_PAREN # UndeclareFunction - | KW_PROCEDURE ( INTEGER | identifier | string ) LEFT_PAREN ( (runnable_param ( COMMA runnable_param )*)? ) RIGHT_PAREN command # Procedure - | KW_FUNCTION ( INTEGER | identifier | string ) LEFT_PAREN ( (runnable_param ( COMMA runnable_param )*)? ) RIGHT_PAREN KW_RETURNING qualified_type command # Function + ; + +top_level_command + : command # TopLevelCommand + | KW_UNDECLARE ( INTEGER | identifier | string ) LEFT_PAREN (qualified_type (COMMA qualified_type)*)? RIGHT_PAREN # UndeclareFunction + | KW_PROCEDURE ( INTEGER | identifier | string ) LEFT_PAREN ( (runnable_param ( COMMA runnable_param )*)? ) RIGHT_PAREN nested_level_command # Procedure + | KW_FUNCTION ( INTEGER | identifier | string ) LEFT_PAREN ( (runnable_param ( COMMA runnable_param )*)? ) RIGHT_PAREN KW_RETURNING qualified_type nested_level_command # Function ; expression diff --git a/alib2cli/src/parser/AltVisitor.Command.cpp b/alib2cli/src/parser/AltVisitor.Command.cpp index b219d843327ce6dc411c9e032b9dcae572b15358..1d35504655d8077bb6cb78e21bbddc73dd8be336 100644 --- a/alib2cli/src/parser/AltVisitor.Command.cpp +++ b/alib2cli/src/parser/AltVisitor.Command.cpp @@ -40,8 +40,6 @@ using QualifedType = std::pair<abstraction::TypeQualifiers::TypeQualifierSet, st std::any AltVisitor::visitIfCommand(AltCliParser::IfCommandContext* ctx) { - ensureScope("if", Scope::Local); - auto expression = castToUnique<Expression>(visit(ctx->condition)); auto thenBranch = castToUnique<Command>(visit(ctx->then_branch)); @@ -55,8 +53,6 @@ std::any AltVisitor::visitIfCommand(AltCliParser::IfCommandContext* ctx) std::any AltVisitor::visitWhileCommand(AltCliParser::WhileCommandContext* ctx) { - ensureScope("while", Scope::Local); - auto condition = castToUnique<Expression>(visit(ctx->condition)); auto body = castToUnique<Command>(visit(ctx->body)); @@ -67,10 +63,10 @@ std::any AltVisitor::visitParse(AltCliParser::ParseContext* ctx) { ext::vector<std::unique_ptr<Command>> commands; - if (ctx->command().empty()) { + if (ctx->top_level_command().empty()) { commands.emplace_back(new EOTCommand()); } else { - fillList<Command>(commands, ctx->command()); + fillList<Command>(commands, ctx->top_level_command()); } return new CommandList(std::move(commands)); @@ -85,13 +81,9 @@ std::any AltVisitor::visitPrint(AltCliParser::PrintContext* ctx) std::any AltVisitor::visitBlock(AltCliParser::BlockContext* ctx) { - incNestedLevel(); - ext::vector<std::unique_ptr<Command>> list; - fillList<Command>(list, ctx->semicolon_command()); - decNestedLevel(); return retPtr<Command, CommandList>(std::move(list)); } @@ -117,7 +109,7 @@ std::any AltVisitor::visitWhileSemicolonCommand(AltCliParser::WhileSemicolonComm std::any AltVisitor::visitSemicolonCommand(AltCliParser::SemicolonCommandContext* ctx) { - return visit(ctx->command()); + return visit(ctx->nested_level_command()); } std::any AltVisitor::visitQuit(AltCliParser::QuitContext* ctx) @@ -140,8 +132,6 @@ std::any AltVisitor::visitReturn(AltCliParser::ReturnContext* ctx) std::any AltVisitor::visitCycleControl(AltCliParser::CycleControlContext* ctx) { - ensureScope("break/continue", Scope::Local); - if (ctx->KW_BREAK() != nullptr) return retPtr<Command, BreakCommand>(); @@ -209,8 +199,6 @@ std::any AltVisitor::visitHelp(AltCliParser::HelpContext* ctx) std::any AltVisitor::visitFunction(AltCliParser::FunctionContext* ctx) { - ensureScope("function", Scope::Global); - std::string name; if (ctx->string() != nullptr) name = cast<std::string>(visit(ctx->string())); @@ -223,15 +211,13 @@ std::any AltVisitor::visitFunction(AltCliParser::FunctionContext* ctx) auto params = visitRunnableParams(ctx->runnable_param()); auto retType = visitQualifiedType(ctx->qualified_type()); - auto body = castToUnique<Command>(visit(ctx->command())); + auto body = castToUnique<Command>(visit(ctx->nested_level_command())); return retPtr<Command, DeclareRunnableCommand>(std::move(name), std::move(params), std::move(retType), std::move(body)); } std::any AltVisitor::visitProcedure(AltCliParser::ProcedureContext* ctx) { - ensureScope("procedure", Scope::Global); - std::string name; if (ctx->string() != nullptr) name = cast<std::string>(visit(ctx->string())); @@ -243,15 +229,13 @@ std::any AltVisitor::visitProcedure(AltCliParser::ProcedureContext* ctx) invalidParse("Invalid procedure name"); auto params = visitRunnableParams(ctx->runnable_param()); - auto body = castToUnique<Command>(visit(ctx->command())); + auto body = castToUnique<Command>(visit(ctx->nested_level_command())); return retPtr<Command, DeclareRunnableCommand>(std::move(name), std::move(params), std::move(body)); } std::any AltVisitor::visitUndeclareFunction(AltCliParser::UndeclareFunctionContext* ctx) { - ensureScope("undeclare", Scope::Global); - std::string name; if (ctx->identifier() != nullptr) name = ctx->identifier()->getText(); @@ -400,7 +384,7 @@ std::any AltVisitor::visitCalc(AltCliParser::CalcContext* ctx) std::any AltVisitor::visitIntrospectAst(AltCliParser::IntrospectAstContext* ctx) { - auto command = castToUnique<Command>(visit(ctx->command())); + auto command = castToUnique<Command>(visit(ctx->top_level_command())); return retPtr<Command, AstIntrospectionCommand>(std::move(command)); } diff --git a/alib2cli/src/parser/AltVisitor.h b/alib2cli/src/parser/AltVisitor.h index 718fe2ea9d94bf58e849cf9ea7c4b7f730f38ba2..78577768a2918d78d58fe3a085922186f8f5a9c0 100644 --- a/alib2cli/src/parser/AltVisitor.h +++ b/alib2cli/src/parser/AltVisitor.h @@ -193,41 +193,6 @@ struct AltVisitor : public AltCliParserBaseVisitor { std::any visitContainerStatement(AltCliParser::ContainerStatementContext* ctx) override; -private: - enum class Scope { - Global, - Local, - }; - - size_t nestedLevel = 0; - - void incNestedLevel() - { - nestedLevel++; - } - - void decNestedLevel() - { - nestedLevel--; - } - - 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 - { - 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: std::any defaultResult() override; diff --git a/alib2cli/test-src/aql/InvalidGrammarTest.cpp b/alib2cli/test-src/aql/InvalidGrammarTest.cpp index e329bec63e82c6d1106876dbecebb2076708acb9..55fdc5d084be1b20d81a94ea86cb8490d9c68821 100644 --- a/alib2cli/test-src/aql/InvalidGrammarTest.cpp +++ b/alib2cli/test-src/aql/InvalidGrammarTest.cpp @@ -13,44 +13,44 @@ TEST_CASE("Invalid grammar - Scope") { SECTION("Undeclare") { - CHECK_THROWS_AS(newParseString("begin undeclare a (); end"), std::runtime_error); + CHECK_THROWS_AS(newParseString("begin undeclare a (); end"), exception::CommonException); CHECK_NOTHROW(newParseString("undeclare a ()")); } SECTION("If") { - CHECK_THROWS_AS(newParseString("if (1) then begin print 1; end"), std::runtime_error); + CHECK_THROWS_AS(newParseString("if (1) then begin print 1; end"), exception::CommonException); CHECK_NOTHROW(newParseString("begin if (1) then print 1; end")); } SECTION("While") { - CHECK_THROWS_AS(newParseString("while (1) do begin print 1; end"), std::runtime_error); + CHECK_THROWS_AS(newParseString("while (1) do begin print 1; end"), exception::CommonException); CHECK_NOTHROW(newParseString("begin while (1) do print 1; end")); } SECTION("Break") { - CHECK_THROWS_AS(newParseString("break"), std::runtime_error); + CHECK_THROWS_AS(newParseString("break"), exception::CommonException); CHECK_NOTHROW(newParseString("begin break; end")); } SECTION("Continue") { - CHECK_THROWS_AS(newParseString("continue"), std::runtime_error); + CHECK_THROWS_AS(newParseString("continue"), exception::CommonException); CHECK_NOTHROW(newParseString("begin continue; end")); } SECTION("Function") { - CHECK_THROWS_AS(newParseString("begin function func () returning auto return 1; end"), std::runtime_error); + CHECK_THROWS_AS(newParseString("begin function func () returning auto return 1; end"), exception::CommonException); CHECK_NOTHROW(newParseString("function func () returning auto return 1")); } SECTION("Procedure") { - CHECK_THROWS_AS(newParseString("begin procedure func () print 1; end"), std::runtime_error); + CHECK_THROWS_AS(newParseString("begin procedure func () print 1; end"), exception::CommonException); CHECK_NOTHROW(newParseString("procedure func () print 1")); } } diff --git a/alib2cli/test-src/cli/AutocompleteTest.cpp b/alib2cli/test-src/cli/AutocompleteTest.cpp index 7d815e7da444e37ab778fa353af468020eefebeb..1f212c37d4e31789f0f3c44cc9f3bd8621babd25 100644 --- a/alib2cli/test-src/cli/AutocompleteTest.cpp +++ b/alib2cli/test-src/cli/AutocompleteTest.cpp @@ -10,7 +10,7 @@ TEST_CASE("Autocomplete") auto suggestions = autocomplete.getSuggestions("", ""); - CHECK(suggestions.size() == 26); + CHECK(suggestions.size() == 22); } SECTION("pri")