mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 17:51:07 +00:00
208 lines
7.4 KiB
C++
208 lines
7.4 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
|
|
out += "void " + pluginName + "::initLogos(LogosAPI* logosAPIInstance)\n";
|
|
out += "{\n";
|
|
out += " Q_UNUSED(logosAPIInstance);\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;
|
|
}
|