Files
logos-cpp-sdk/cpp-generator/experimental/c_plugin_generator.cpp
2026-04-03 15:10:38 -04:00

209 lines
7.6 KiB
C++

#include "c_plugin_generator.h"
#include <QStringList>
// Convert a snake_case name to PascalCase
static QString toPascal(const QString& snake)
{
QStringList parts = snake.split('_', Qt::SkipEmptyParts);
QString result;
for (const QString& p : parts) {
if (p.isEmpty()) continue;
result += p.at(0).toUpper() + p.mid(1);
}
return result;
}
// Map LIDL type to Qt/C++ type string for the plugin interface
static QString lidlToQt(const TypeExpr& t)
{
if (t.kind == TypeExpr::Primitive) {
if (t.name == "tstr") return "QString";
if (t.name == "bstr") return "QByteArray";
if (t.name == "int") return "int";
if (t.name == "uint") return "int";
if (t.name == "float64") return "double";
if (t.name == "bool") return "bool";
if (t.name == "void") return "void";
if (t.name == "result") return "LogosResult";
if (t.name == "any") return "QVariant";
}
if (t.kind == TypeExpr::Array) {
if (!t.elements.empty() && t.elements[0].name == "tstr")
return "QStringList";
return "QVariantList";
}
if (t.kind == TypeExpr::Map)
return "QVariantMap";
return "QVariant";
}
// Map LIDL type to C type string for the function call
static QString lidlToC(const TypeExpr& t)
{
if (t.kind == TypeExpr::Primitive) {
if (t.name == "tstr") return "const char*";
if (t.name == "int") return "int64_t";
if (t.name == "uint") return "uint64_t";
if (t.name == "float64") return "double";
if (t.name == "bool") return "bool";
if (t.name == "void") return "void";
}
return "void*"; // fallback
}
// Generate expression to convert a Qt-typed parameter to C type for the call
static QString qtToCallExpr(const TypeExpr& t, const QString& paramName)
{
if (t.name == "tstr")
return paramName + ".toUtf8().constData()";
if (t.name == "int")
return "static_cast<int64_t>(" + paramName + ")";
if (t.name == "uint")
return "static_cast<uint64_t>(" + paramName + ")";
return paramName; // double, bool pass through
}
// Generate expression to convert a C return value to Qt type
static QString cRetToQt(const TypeExpr& t, const QString& expr)
{
if (t.name == "tstr")
return "QString::fromUtf8(" + expr + ")";
if (t.name == "int")
return "static_cast<int>(" + expr + ")";
if (t.name == "uint")
return "static_cast<int>(" + expr + ")";
return expr; // double, bool, void pass through
}
QString cPluginMakeHeader(const ModuleDecl& module,
const QString& cHeaderInclude,
const QString& prefix)
{
Q_UNUSED(prefix);
QString pascal = toPascal(module.name);
QString out;
out += "// AUTO-GENERATED by logos-cpp-generator --from-c-header -- do not edit\n";
out += "#pragma once\n\n";
out += "#include <QObject>\n";
out += "#include <QString>\n";
out += "#include <QVariant>\n";
out += "#include <QVariantList>\n";
out += "#include \"interface.h\"\n";
out += "#include \"logos_api.h\"\n\n";
out += "extern \"C\" {\n";
out += "#include \"" + cHeaderInclude + "\"\n";
out += "}\n\n";
// Interface class
QString ifaceName = pascal + "Interface";
QString ifaceIid = "\"org.logos." + ifaceName + "\"";
out += "class " + ifaceName + " : public PluginInterface {\n";
out += "public:\n";
out += " virtual ~" + ifaceName + "() = default;\n";
for (const MethodDecl& m : module.methods) {
out += " Q_INVOKABLE virtual " + lidlToQt(m.returnType) + " " + m.name + "(";
for (size_t i = 0; i < m.params.size(); ++i) {
if (i > 0) out += ", ";
out += lidlToQt(m.params[i].type) + " " + m.params[i].name;
}
out += ") = 0;\n";
}
out += "};\n\n";
out += "#define " + ifaceName + "_iid " + ifaceIid + "\n";
out += "Q_DECLARE_INTERFACE(" + ifaceName + ", " + ifaceName + "_iid)\n\n";
// Plugin class
QString pluginName = pascal + "Plugin";
out += "class " + pluginName + " : public QObject, public " + ifaceName + " {\n";
out += " Q_OBJECT\n";
out += " Q_PLUGIN_METADATA(IID " + ifaceName + "_iid FILE \"metadata.json\")\n";
out += " Q_INTERFACES(" + ifaceName + " PluginInterface)\n\n";
out += "public:\n";
out += " explicit " + pluginName + "(QObject* parent = nullptr);\n";
out += " ~" + pluginName + "() override;\n\n";
out += " QString name() const override { return QStringLiteral(\"" + module.name + "\"); }\n";
out += " QString version() const override { return QStringLiteral(\"" + module.version + "\"); }\n\n";
out += " Q_INVOKABLE void initLogos(LogosAPI* logosAPIInstance);\n\n";
for (const MethodDecl& m : module.methods) {
out += " Q_INVOKABLE " + lidlToQt(m.returnType) + " " + m.name + "(";
for (size_t i = 0; i < m.params.size(); ++i) {
if (i > 0) out += ", ";
out += lidlToQt(m.params[i].type) + " " + m.params[i].name;
}
out += ") override;\n";
}
out += "\nsignals:\n";
out += " void eventResponse(const QString& eventName, const QVariantList& args);\n";
out += "};\n";
return out;
}
QString cPluginMakeSource(const ModuleDecl& module,
const QString& prefix)
{
QString pascal = toPascal(module.name);
QString pluginName = pascal + "Plugin";
QString out;
out += "// AUTO-GENERATED by logos-cpp-generator --from-c-header -- do not edit\n";
out += "#include \"" + module.name + "_plugin.h\"\n";
out += "#include <QDebug>\n\n";
// Constructor
out += pluginName + "::" + pluginName + "(QObject* parent)\n";
out += " : QObject(parent)\n";
out += "{\n";
out += " qDebug() << \"" + pluginName + ": created\";\n";
out += "}\n\n";
// Destructor
out += pluginName + "::~" + pluginName + "()\n";
out += "{\n";
out += " qDebug() << \"" + pluginName + ": destroyed\";\n";
out += "}\n\n";
// initLogos — store the API pointer (required by QtProviderObject::callMethod)
out += "void " + pluginName + "::initLogos(LogosAPI* logosAPIInstance)\n";
out += "{\n";
out += " logosAPI = logosAPIInstance;\n";
out += " qDebug() << \"" + pluginName + "::initLogos called, api=\" << (void*)logosAPIInstance << \"stored=\" << (void*)logosAPI;\n";
out += "}\n\n";
// Method implementations
for (const MethodDecl& m : module.methods) {
QString retQt = lidlToQt(m.returnType);
out += retQt + " " + pluginName + "::" + m.name + "(";
for (size_t i = 0; i < m.params.size(); ++i) {
if (i > 0) out += ", ";
out += lidlToQt(m.params[i].type) + " " + m.params[i].name;
}
out += ")\n{\n";
// Build C function call
QString cFunc = prefix + m.name;
// Handle the reserved-name rename: if method is "libVersion", original C func is prefix + "version"
if (m.name.startsWith("lib") && m.name.length() > 3 && m.name[3].isUpper()) {
QString original = m.name.mid(3);
original[0] = original[0].toLower();
cFunc = prefix + original;
}
QString callArgs;
for (size_t i = 0; i < m.params.size(); ++i) {
if (i > 0) callArgs += ", ";
callArgs += qtToCallExpr(m.params[i].type, m.params[i].name);
}
QString call = cFunc + "(" + callArgs + ")";
if (m.returnType.name == "void") {
out += " " + call + ";\n";
} else {
out += " return " + cRetToQt(m.returnType, call) + ";\n";
}
out += "}\n\n";
}
return out;
}