From a9b536cd3b1842ac91b362f2b45f78154f75d039 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Sat, 7 Feb 2026 01:22:48 +0100 Subject: [PATCH] updated attribute syntax to support commas and empty specifiers --- cppparser/src/parser.y | 28 ++++++++--- .../unit/attribute-specifier-sequence.cpp | 46 ++++++++++++++++++- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/cppparser/src/parser.y b/cppparser/src/parser.y index 606072ff..f963d392 100644 --- a/cppparser/src/parser.y +++ b/cppparser/src/parser.y @@ -183,7 +183,6 @@ class CppTemplateArg; cppast::CppRefType refType; unsigned int attr; Optional objAccessType; - cppast::CppExpression* attribSpecifier; cppast::CppCallArgs* attribSpecifiers; cppast::CppIfBlock* ifBlock; cppast::CppWhileBlock* whileBlock; @@ -313,8 +312,7 @@ class CppTemplateArg; %type entityaccessspecifier %type identifierlist %type functhrowspec optfuncthrowspec -%type attribspecifier -%type attribspecifiers optattribspecifiers +%type attribs optattribs attribspecifier attribspecifiers optattribspecifiers %type define %type undef %type include @@ -1703,8 +1701,26 @@ classdefnstmt : classdefn ';' [ZZVALID;] { $$ = $1; } ; +attribs + : expr { + $$ = new std::vector>; + $$->push_back(Ptr($1)); + } + | attribs ',' expr { + $$ = $1; + $$->push_back(Ptr($3)); + } + ; + +optattribs + : { + $$ = new std::vector>; + } + | attribs { $$ = $1; } + ; + attribspecifier - : '[' '[' expr ']' ']' { + : '[' '[' optattribs ']' ']' { $$ = $3; } ; @@ -1719,11 +1735,11 @@ optattribspecifiers attribspecifiers : attribspecifier { $$ = new std::vector>; - $$->push_back(Ptr($1)); + $$->insert($$->end(), std::make_move_iterator($1->begin()), std::make_move_iterator($1->end())); } | attribspecifiers attribspecifier { $$ = $1; - $$->push_back(Ptr($2)); + $$->insert($$->end(), std::make_move_iterator($2->begin()), std::make_move_iterator($2->end())); } ; diff --git a/cppparser/test/unit/attribute-specifier-sequence.cpp b/cppparser/test/unit/attribute-specifier-sequence.cpp index 6c2fb092..fe673147 100644 --- a/cppparser/test/unit/attribute-specifier-sequence.cpp +++ b/cppparser/test/unit/attribute-specifier-sequence.cpp @@ -32,6 +32,10 @@ TEST_CASE_METHOD(CppAtributeTest, "Attribute specifier sequence") [[xnet::HttpPost]] [[xnet::Route("/register")]] std::string CreateAccount( [[xnet::FromBody]] [[xnet::EnsureValid]] SignUpRequest request); + + [[xnet::HttpGet, xnet::Route("/entities"), xnet::Route("/entity")]] std::string GetEntities(); + + [[]] [[]] void UnannotatedHelper(); }; #endif auto testSnippet = getTestSnippetParseStream(__LINE__ - 2); @@ -72,7 +76,7 @@ TEST_CASE_METHOD(CppAtributeTest, "Attribute specifier sequence") CHECK((*classAttrib1Arg) == cppast::CppStringLiteralExpr("\"/plakmp\"")); const auto classMembers = GetAllOwnedEntities(*classDefn); - REQUIRE(classMembers.size() == 4); + REQUIRE(classMembers.size() == 6); const cppast::CppConstFunctionEPtr methodGetPlakMpPlayers = classMembers[1]; REQUIRE(methodGetPlakMpPlayers); @@ -95,4 +99,44 @@ TEST_CASE_METHOD(CppAtributeTest, "Attribute specifier sequence") cppast::CppConstStringLiteralExprEPtr methodAttrib1Arg = &(methodAttrib1->arg(0)); REQUIRE(methodAttrib1Arg); CHECK((*methodAttrib1Arg) == cppast::CppStringLiteralExpr("\"/players\"")); + + const cppast::CppConstFunctionEPtr methodGetEntities = classMembers[4]; + REQUIRE(methodGetEntities); + const auto* returnTypeGetEntities = methodGetEntities->returnType(); + REQUIRE(returnTypeGetEntities); + + const auto attribSeqGetEntities = GetAllAttributeSpecifiers(*returnTypeGetEntities); + REQUIRE(attribSeqGetEntities.size() == 3); + + cppast::CppConstNameExprEPtr method2Attrib0 = attribSeqGetEntities.at(0); + REQUIRE(method2Attrib0); + CHECK((*method2Attrib0) == cppast::CppNameExpr("xnet::HttpGet")); + + cppast::CppConstFunctionCallExprEPtr method2Attrib1 = attribSeqGetEntities.at(1); + REQUIRE(method2Attrib1); + cppast::CppConstNameExprEPtr method2Attrib1Func = &(method2Attrib1->function()); + REQUIRE(method2Attrib1Func); + CHECK((*method2Attrib1Func) == cppast::CppNameExpr("xnet::Route")); + REQUIRE(method2Attrib1->numArgs() == 1); + cppast::CppConstStringLiteralExprEPtr method2Attrib1Arg = &(method2Attrib1->arg(0)); + REQUIRE(method2Attrib1Arg); + CHECK((*method2Attrib1Arg) == cppast::CppStringLiteralExpr("\"/entities\"")); + + cppast::CppConstFunctionCallExprEPtr method2Attrib2 = attribSeqGetEntities.at(2); + REQUIRE(method2Attrib2); + cppast::CppConstNameExprEPtr method2Attrib2Func = &(method2Attrib2->function()); + REQUIRE(method2Attrib2Func); + CHECK((*method2Attrib2Func) == cppast::CppNameExpr("xnet::Route")); + REQUIRE(method2Attrib2->numArgs() == 1); + cppast::CppConstStringLiteralExprEPtr method2Attrib2Arg = &(method2Attrib2->arg(0)); + REQUIRE(method2Attrib2Arg); + CHECK((*method2Attrib2Arg) == cppast::CppStringLiteralExpr("\"/entity\"")); + + const cppast::CppConstFunctionEPtr methodUnannotatedHelper = classMembers[5]; + REQUIRE(methodUnannotatedHelper); + const auto* returnTypeUnannotatedHelper = methodUnannotatedHelper->returnType(); + REQUIRE(returnTypeUnannotatedHelper); + + const auto attribSeqUnannotatedHelper = GetAllAttributeSpecifiers(*returnTypeUnannotatedHelper); + REQUIRE(attribSeqUnannotatedHelper.size() == 0); }