diff --git a/cpp-generator/experimental/lidl_parser.cpp b/cpp-generator/experimental/lidl_parser.cpp index 62f298a..318f7a1 100644 --- a/cpp-generator/experimental/lidl_parser.cpp +++ b/cpp-generator/experimental/lidl_parser.cpp @@ -158,6 +158,14 @@ private: if (!expect(LidlToken::RParen, "method parameters")) return false; if (!expect(LidlToken::Arrow, "method return type")) return false; if (!parseTypeExpr(md.returnType)) return false; + // Optional trailing doc: `-> ret description "..."`. Carries the + // method's doc comment across a .lidl round-trip so introspection + // (lm / getMethods) still surfaces it. + if (at(LidlToken::Description)) { + ++m_pos; + if (!at(LidlToken::StringLit)) { error("Expected string after method 'description'"); return false; } + md.description = current().text; ++m_pos; + } // Restore the return-shape flags from the parsed type so a .lidl // round-trip carries the same semantics the impl-header parser sets // (it derives them from C++ types: StdLogosResult -> result, @@ -185,6 +193,11 @@ private: if (!expect(LidlToken::LParen, "event parameters")) return false; if (!parseParams(ed.params)) return false; if (!expect(LidlToken::RParen, "event parameters")) return false; + if (at(LidlToken::Description)) { + ++m_pos; + if (!at(LidlToken::StringLit)) { error("Expected string after event 'description'"); return false; } + ed.description = current().text; ++m_pos; + } mod.events.append(ed); return true; } diff --git a/cpp-generator/experimental/lidl_serializer.cpp b/cpp-generator/experimental/lidl_serializer.cpp index df8ad3f..a22976e 100644 --- a/cpp-generator/experimental/lidl_serializer.cpp +++ b/cpp-generator/experimental/lidl_serializer.cpp @@ -1,6 +1,17 @@ #include "lidl_serializer.h" #include +// Escape a description for a "..."-delimited LIDL string literal (the lexer +// decodes \\ \" \n \t). Keeps method/event docs intact across a .lidl +// round-trip so introspection (lm / getMethods) still shows them. +static QString lidlEscapeStr(QString in) { + in.replace('\\', "\\\\"); + in.replace('"', "\\\""); + in.replace('\n', "\\n"); + in.replace('\t', "\\t"); + return in; +} + static QString serializeTypeExpr(const TypeExpr& te) { switch (te.kind) { case TypeExpr::Primitive: case TypeExpr::Named: return te.name; @@ -23,16 +34,16 @@ QString lidlSerialize(const ModuleDecl& module) { QTextStream s(&out); s << "module " << module.name << " {\n"; if (!module.version.isEmpty()) s << " version \"" << module.version << "\"\n"; - if (!module.description.isEmpty()) s << " description \"" << module.description << "\"\n"; + if (!module.description.isEmpty()) s << " description \"" << lidlEscapeStr(module.description) << "\"\n"; if (!module.category.isEmpty()) s << " category \"" << module.category << "\"\n"; s << " depends ["; for (int i = 0; i < module.depends.size(); ++i) { s << module.depends[i]; if (i + 1 < module.depends.size()) s << ", "; } s << "]\n"; for (const TypeDecl& td : module.types) { s << "\n type " << td.name << " {\n"; for (const FieldDecl& fd : td.fields) { s << " "; if (fd.optional) s << "? "; s << fd.name << ": " << serializeTypeExpr(fd.type) << "\n"; } s << " }\n"; } if (!module.methods.isEmpty()) s << "\n"; - for (const MethodDecl& md : module.methods) { s << " method " << md.name << "("; serializeParams(s, md.params); s << ") -> " << serializeTypeExpr(md.returnType) << "\n"; } + for (const MethodDecl& md : module.methods) { s << " method " << md.name << "("; serializeParams(s, md.params); s << ") -> " << serializeTypeExpr(md.returnType); if (!md.description.isEmpty()) s << " description \"" << lidlEscapeStr(md.description) << "\""; s << "\n"; } if (!module.events.isEmpty()) s << "\n"; - for (const EventDecl& ed : module.events) { s << " event " << ed.name << "("; serializeParams(s, ed.params); s << ")\n"; } + for (const EventDecl& ed : module.events) { s << " event " << ed.name << "("; serializeParams(s, ed.params); s << ")"; if (!ed.description.isEmpty()) s << " description \"" << lidlEscapeStr(ed.description) << "\""; s << "\n"; } s << "}\n"; return out; }