fix(header-parser): join multi-line method declarations before parsing (#91)

* fix(header-parser): join multi-line method declarations before parsing

* strip qualifiers/attributes before return-type matching (#92)

---------

Co-authored-by: Álex <alex93cabeza@gmail.com>
This commit is contained in:
haelius
2026-06-18 08:30:32 -04:00
committed by GitHub
co-authored by Álex
parent 182d850e72
commit f032cf291e
3 changed files with 146 additions and 2 deletions
@@ -8,6 +8,28 @@
#include <QRegularExpression>
#include <QStringList>
// ---------------------------------------------------------------------------
// Strip leading declaration specifiers / attributes from a return-type string.
// ---------------------------------------------------------------------------
static QString stripDeclarationSpecifiers(QString string)
{
static const QRegularExpression attributeRe("\\[\\[[^\\]]*\\]\\]");
static const QRegularExpression specifierRe(
"^(static|virtual|inline|explicit|constexpr|consteval|friend)\\s+");
string.remove(attributeRe);
string = string.trimmed();
QRegularExpressionMatch specifierMatch = specifierRe.match(string);
while (specifierMatch.hasMatch()) {
string = string.mid(specifierMatch.capturedLength()).trimmed();
specifierMatch = specifierRe.match(string);
}
return string;
}
// ---------------------------------------------------------------------------
// C++ type string → LIDL TypeExpr
// ---------------------------------------------------------------------------
@@ -139,7 +161,7 @@ static bool parseMethodLine(const QString& line, MethodDecl& out)
if (cppKeywords.contains(methodName))
return false;
out.name = methodName.toStdString();
QString retTypeStr = prefix.left(nameStart).trimmed();
QString retTypeStr = stripDeclarationSpecifiers(prefix.left(nameStart).trimmed());
out.returnType = cppTypeToLidl(retTypeStr);
// Flag methods whose impl returns LogosMap / LogosList so the generator
// can emit nlohmann→Qt conversion code in the glue layer.
@@ -257,7 +279,56 @@ ImplParseResult parseImplHeader(const QString& headerPath,
QString source = QString::fromUtf8(hf.readAll());
hf.close();
QStringList lines = source.split('\n');
// Split into physical lines, then merge any whose parentheses are still
// open into one logical line. The scanner below is line-based — it only
// accepts a method when a single trimmed line ends in ';' and
// parseMethodLine finds a balanced '(...)' on it — so without this a method
// signature wrapped across several physical lines is silently dropped.
// Parens inside comments / string / char literals are ignored.
QStringList lines;
{
const QStringList physical = source.split('\n');
QString acc;
int parenDepth = 0;
bool inBlockComment = false;
for (const QString& phys : physical) {
bool inStr = false;
bool inChr = false;
for (int i = 0; i < phys.size(); ++i) {
const QChar c = phys[i];
const QChar n = (i + 1 < phys.size()) ? phys[i + 1] : QChar();
if (inBlockComment) {
if (c == '*' && n == '/') { inBlockComment = false; ++i; }
} else if (inStr) {
if (c == '\\') ++i; else if (c == '"') inStr = false;
} else if (inChr) {
if (c == '\\') ++i; else if (c == '\'') inChr = false;
} else if (c == '/' && n == '*') {
inBlockComment = true; ++i;
} else if (c == '/' && n == '/') {
break;
} else if (c == '"') {
inStr = true;
} else if (c == '\'') {
inChr = true;
} else if (c == '(') {
++parenDepth;
} else if (c == ')') {
if (parenDepth > 0) --parenDepth;
}
}
if (acc.isEmpty())
acc = phys;
else
acc += ' ' + phys.trimmed();
if (parenDepth <= 0) {
lines.append(acc);
acc.clear();
}
}
if (!acc.isEmpty())
lines.append(acc);
}
// State machine: find "class <className>", then collect declarations.
// `InLogosEvents` is entered by the literal `logos_events:` token