From 14aafe02dd9a2b693e1c5b04d73b71af47aa43a5 Mon Sep 17 00:00:00 2001 From: lawm Date: Wed, 23 Jul 2025 17:58:31 +0000 Subject: [PATCH 1/3] parser: add nested struct and union support (WerWolv/ImHex#363) This allows defining structs and unions inside another struct definition. Example: ```c struct a { u32 i ; struct b { u32 ii; }; u32 ia; union u { u32 ui; char uc[4]; }; //u au; // warning: this will add another member of the union }; a a @ 0; ``` The syntax is not exactly the same as C, and it's not anonymous, but at least you don't need to move the definition out. --- lib/source/pl/core/parser.cpp | 37 ++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/source/pl/core/parser.cpp b/lib/source/pl/core/parser.cpp index 2d2ab26f..2ecaa169 100644 --- a/lib/source/pl/core/parser.cpp +++ b/lib/source/pl/core/parser.cpp @@ -1836,7 +1836,7 @@ namespace pl::core { } if (!sequence(tkn::Operator::Colon)) { - errorDesc("Expected ':' after pointer definition, got {}.", "A pointer requires a integral type to specify its own size.", getFormattedToken(0)); + error("Expected ':' after pointer definition, got {}.", getFormattedToken(0)); return nullptr; } @@ -1844,8 +1844,6 @@ namespace pl::core { if (sizeType == nullptr) return nullptr; - auto arrayType = createShared("", type.unwrapUnchecked(), std::move(size.unwrapUnchecked())); - if (sequence(tkn::Operator::At)) { auto expression = parseMathematicalExpression(); if (expression == nullptr) @@ -1856,7 +1854,7 @@ namespace pl::core { else memberIdentifier->setType(Token::Identifier::IdentifierType::CalculatedPointer); } - return create(name, std::move(arrayType), std::move(sizeType), std::move(expression)); + return create(name, createShared("", type.unwrapUnchecked(), std::move(size.unwrapUnchecked())), std::move(sizeType), std::move(expression)); } if (memberIdentifier != nullptr) { if (m_currTemplateType.empty()) @@ -1864,7 +1862,7 @@ namespace pl::core { else memberIdentifier->setType(Token::Identifier::IdentifierType::PatternVariable); } - return create(name, std::move(arrayType), std::move(sizeType)); + return create(name, createShared("", type.unwrapUnchecked(), std::move(size.unwrapUnchecked())), std::move(sizeType)); } // [(parsePadding)|(parseMemberVariable)|(parseMemberArrayVariable)|(parseMemberPointerVariable)|(parseMemberArrayPointerVariable)] @@ -1881,6 +1879,32 @@ namespace pl::core { member = parseFunctionVariableCompoundAssignment(getValue(*identifierOffset).get()); else if (MATCHES(sequence(tkn::Literal::Identifier) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) member = parseRValueAssignment(); + // --- Begin nested struct support --- + else if (sequence(tkn::Keyword::Struct, tkn::Literal::Identifier)) { + // Parse nested struct and add as member + auto nestedStructType = parseStruct(); + if (nestedStructType == nullptr) + return nullptr; + // Add the actual struct AST node as a member + auto structNode = nestedStructType->getType(); + if (structNode == nullptr) + return nullptr; + member = hlp::safe_unique_ptr(structNode->clone()); + } + // --- End nested struct support --- + // --- Begin nested union support --- + else if (sequence(tkn::Keyword::Union, tkn::Literal::Identifier)) { + // Parse nested union and add as member + auto nestedUnionType = parseUnion(); + if (nestedUnionType == nullptr) + return nullptr; + // Add the actual union AST node as a member + auto unionNode = nestedUnionType->getType(); + if (unionNode == nullptr) + return nullptr; + member = hlp::safe_unique_ptr(unionNode->clone()); + } + // --- End nested union support --- else if (peek(tkn::Keyword::Const) || peek(tkn::Keyword::BigEndian) || peek(tkn::Keyword::LittleEndian) || peek(tkn::ValueType::Any) || peek(tkn::Literal::Identifier)) { // Some kind of variable definition @@ -2447,8 +2471,7 @@ namespace pl::core { auto initStatement = parseArrayInitExpression(name); if (initStatement == nullptr) return nullptr; - if (typedefIdentifier != nullptr) - typedefIdentifier->setType(Token::Identifier::IdentifierType::GlobalVariable); + compoundStatement.emplace_back(std::move(initStatement)); } From 477d3d200715b8233115e120a2a28387cdd152f7 Mon Sep 17 00:00:00 2001 From: lawm Date: Wed, 23 Jul 2025 21:48:19 +0000 Subject: [PATCH 2/3] parser: add anonymous nested struct and union (WerWolv/ImHex#2197) Example: ``` struct a { struct { u32 ii; }; union { u32 ui; char uc[4]; }; } ``` --- lib/source/pl/core/parser.cpp | 44 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/source/pl/core/parser.cpp b/lib/source/pl/core/parser.cpp index 2ecaa169..c7d8d36d 100644 --- a/lib/source/pl/core/parser.cpp +++ b/lib/source/pl/core/parser.cpp @@ -1880,7 +1880,8 @@ namespace pl::core { else if (MATCHES(sequence(tkn::Literal::Identifier) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) member = parseRValueAssignment(); // --- Begin nested struct support --- - else if (sequence(tkn::Keyword::Struct, tkn::Literal::Identifier)) { + else if (sequence(tkn::Keyword::Struct, tkn::Literal::Identifier) || + sequence(tkn::Keyword::Struct, tkn::Separator::LeftBrace)) { // Parse nested struct and add as member auto nestedStructType = parseStruct(); if (nestedStructType == nullptr) @@ -1893,7 +1894,8 @@ namespace pl::core { } // --- End nested struct support --- // --- Begin nested union support --- - else if (sequence(tkn::Keyword::Union, tkn::Literal::Identifier)) { + else if (sequence(tkn::Keyword::Union, tkn::Literal::Identifier) || + sequence(tkn::Keyword::Union, tkn::Separator::LeftBrace)) { // Parse nested union and add as member auto nestedUnionType = parseUnion(); if (nestedUnionType == nullptr) @@ -1978,10 +1980,19 @@ namespace pl::core { // struct Identifier { <(parseMember)...> } hlp::safe_shared_ptr Parser::parseStruct() { - const auto &typeName = getValue(-1).get(); - auto userDefinedTypeIdentifier = std::get_if(&((m_curr[-1]).value)); - if (userDefinedTypeIdentifier != nullptr) - userDefinedTypeIdentifier->setType(Token::Identifier::IdentifierType::UDT); + std::string typeName; + bool anon = false; + if (peek(tkn::Literal::Identifier, -1)) { + typeName = getValue(-1).get(); + auto userDefinedTypeIdentifier = std::get_if(&((m_curr[-1]).value)); + if (userDefinedTypeIdentifier != nullptr) + userDefinedTypeIdentifier->setType(Token::Identifier::IdentifierType::UDT); + } else { + // No identifier, generate a unique name + static int anonStructCounter = 0; + typeName = fmt::format("__anon_struct_{}", anonStructCounter++); + anon = true; + } auto typeDecl = addType(typeName, create()); if(typeDecl == nullptr) @@ -2015,7 +2026,7 @@ namespace pl::core { } while (sequence(tkn::Separator::Comma)); } - if (!sequence(tkn::Separator::LeftBrace)) { + if (!anon && !sequence(tkn::Separator::LeftBrace)) { error("Expected '{{' after struct declaration, got {}.", getFormattedToken(0)); return nullptr; } @@ -2036,10 +2047,19 @@ namespace pl::core { // union Identifier { <(parseMember)...> } hlp::safe_shared_ptr Parser::parseUnion() { - const auto &typeName = getValue(-1).get(); - auto userDefinedTypeIdentifier = std::get_if(&((m_curr[-1]).value)); - if (userDefinedTypeIdentifier != nullptr) - userDefinedTypeIdentifier->setType(Token::Identifier::IdentifierType::UDT); + std::string typeName; + bool anon = false; + if (peek(tkn::Literal::Identifier, -1)) { + typeName = getValue(-1).get(); + auto userDefinedTypeIdentifier = std::get_if(&((m_curr[-1]).value)); + if (userDefinedTypeIdentifier != nullptr) + userDefinedTypeIdentifier->setType(Token::Identifier::IdentifierType::UDT); + } else { + // No identifier, generate a unique name + static int anonUnionCounter = 0; + typeName = fmt::format("__anon_union_{}", anonUnionCounter++); + anon = true; + } auto typeDecl = addType(typeName, create()); if (typeDecl == nullptr) @@ -2051,7 +2071,7 @@ namespace pl::core { typeDecl->setTemplateParameters(unwrapSafePointerVector(this->parseTemplateList())); - if (!sequence(tkn::Separator::LeftBrace)) { + if (!anon && !sequence(tkn::Separator::LeftBrace)) { error("Expected '{{' after union declaration, got {}.", getFormattedToken(0)); return nullptr; } From 75113f5ef23b24ccd16485765d491d9b5b4b895e Mon Sep 17 00:00:00 2001 From: lawm Date: Wed, 23 Jul 2025 22:23:50 +0000 Subject: [PATCH 3/3] parser: allow member identifier for nested struct/union ```c struct a { struct { u32 ii; } sname; union u { u32 ui; char uc[4]; } uname; } ``` --- lib/source/pl/core/parser.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/source/pl/core/parser.cpp b/lib/source/pl/core/parser.cpp index c7d8d36d..26573469 100644 --- a/lib/source/pl/core/parser.cpp +++ b/lib/source/pl/core/parser.cpp @@ -1886,6 +1886,14 @@ namespace pl::core { auto nestedStructType = parseStruct(); if (nestedStructType == nullptr) return nullptr; + // Check for identifier + if (peek(tkn::Literal::Identifier)) { + std::string nextName = getValue(0).get(); + if (nestedStructType->getName().starts_with("__anon_struct_")) { + nestedStructType->setName(nextName); + } + next(); // consume the identifier + } // Add the actual struct AST node as a member auto structNode = nestedStructType->getType(); if (structNode == nullptr) @@ -1900,6 +1908,14 @@ namespace pl::core { auto nestedUnionType = parseUnion(); if (nestedUnionType == nullptr) return nullptr; + // Check for identifier + if (peek(tkn::Literal::Identifier)) { + std::string nextName = getValue(0).get(); + if (nestedUnionType->getName().starts_with("__anon_union_")) { + nestedUnionType->setName(nextName); + } + next(); // consume the identifier + } // Add the actual union AST node as a member auto unionNode = nestedUnionType->getType(); if (unionNode == nullptr)