mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
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:
@@ -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
|
||||
|
||||
@@ -14,6 +14,13 @@ public:
|
||||
QStringList listNames();
|
||||
QVariantList anyList();
|
||||
StdLogosResult fetchResult();
|
||||
[[nodiscard]] StdLogosResult fetchResultNodiscard();
|
||||
static StdLogosResult fetchResultStatic();
|
||||
[[nodiscard]] static StdLogosResult fetchResultNodiscardStatic();
|
||||
static [[nodiscard]] StdLogosResult fetchResultStaticNodiscard();
|
||||
[[nodiscard]] [[deprecated]] StdLogosResult fetchResultMultiAttr();
|
||||
inline static StdLogosResult fetchResultInlineStatic();
|
||||
[[nodiscard]] consteval StdLogosResult fetchResultConsteval();
|
||||
|
||||
// A std::function member: the parser must skip it (not treat it as a
|
||||
// callable method). Used to predate the typed `logos_events:` mechanism
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <QTemporaryDir>
|
||||
#include "impl_header_parser.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
@@ -310,6 +311,41 @@ TEST_F(ImplHeaderParserTest, UniversalTypesAndMetadataEvents)
|
||||
EXPECT_FALSE(fetchResult->jsonReturn);
|
||||
EXPECT_TRUE(fetchResult->resultReturn);
|
||||
|
||||
auto fetchResultNodiscard = findMethod("fetchResultNodiscard");
|
||||
ASSERT_NE(fetchResultNodiscard, nullptr);
|
||||
EXPECT_EQ(fetchResultNodiscard->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultNodiscard->resultReturn);
|
||||
|
||||
auto fetchResultStatic = findMethod("fetchResultStatic");
|
||||
ASSERT_NE(fetchResultStatic, nullptr);
|
||||
EXPECT_EQ(fetchResultStatic->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultStatic->resultReturn);
|
||||
|
||||
auto fetchResultNodiscardStatic = findMethod("fetchResultNodiscardStatic");
|
||||
ASSERT_NE(fetchResultNodiscardStatic, nullptr);
|
||||
EXPECT_EQ(fetchResultNodiscardStatic->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultNodiscardStatic->resultReturn);
|
||||
|
||||
auto fetchResultStaticNodiscard = findMethod("fetchResultStaticNodiscard");
|
||||
ASSERT_NE(fetchResultStaticNodiscard, nullptr);
|
||||
EXPECT_EQ(fetchResultStaticNodiscard->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultStaticNodiscard->resultReturn);
|
||||
|
||||
auto fetchResultMultiAttr = findMethod("fetchResultMultiAttr");
|
||||
ASSERT_NE(fetchResultMultiAttr, nullptr);
|
||||
EXPECT_EQ(fetchResultMultiAttr->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultMultiAttr->resultReturn);
|
||||
|
||||
auto fetchResultInlineStatic = findMethod("fetchResultInlineStatic");
|
||||
ASSERT_NE(fetchResultInlineStatic, nullptr);
|
||||
EXPECT_EQ(fetchResultInlineStatic->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultInlineStatic->resultReturn);
|
||||
|
||||
auto fetchResultConsteval = findMethod("fetchResultConsteval");
|
||||
ASSERT_NE(fetchResultConsteval, nullptr);
|
||||
EXPECT_EQ(fetchResultConsteval->returnType.name, "result");
|
||||
EXPECT_TRUE(fetchResultConsteval->resultReturn);
|
||||
|
||||
for (const auto& m : r.module.methods) {
|
||||
EXPECT_NE(m.name, "void") << "Keyword should not appear as method name";
|
||||
}
|
||||
@@ -420,3 +456,33 @@ TEST_F(ImplHeaderParserTest, SameLineSectionSpecifiers)
|
||||
EXPECT_EQ(findMethod("versionReady"), nullptr);
|
||||
EXPECT_EQ(findMethod("downloadProgress"), nullptr);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ImplHeaderParserTest, ParsesMultiLineSignature)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString hp = dir.filePath("ml_impl.h");
|
||||
{
|
||||
QFile f(hp);
|
||||
ASSERT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
f.write(
|
||||
"#pragma once\n"
|
||||
"#include <string>\n"
|
||||
"class MlImpl {\n"
|
||||
"public:\n"
|
||||
" std::string single(const std::string& a);\n"
|
||||
" std::string wrapped(const std::string& first,\n"
|
||||
" const std::string& second);\n"
|
||||
"};\n");
|
||||
}
|
||||
auto r = parseImplHeader(hp, "MlImpl",
|
||||
fixturesDir() + "/sample_metadata.json", err);
|
||||
ASSERT_FALSE(r.hasError()) << r.error.toStdString();
|
||||
|
||||
QStringList names;
|
||||
for (const auto& m : r.module.methods) names << QString::fromStdString(m.name);
|
||||
EXPECT_TRUE(names.contains("single"));
|
||||
EXPECT_TRUE(names.contains("wrapped"))
|
||||
<< "got: " << names.join(",").toStdString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user