welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
document.hpp
Go to the documentation of this file.
1#pragma once
2#include <algorithm>
3#include <array>
4#include <cstddef>
5#include <meta>
6#include <string>
7#include <string_view>
8#include <type_traits>
9#include <utility>
10#include <vector>
11
12#include <welder/rods/lua/luacats/type_map.hpp> // lua_type / text helpers
13#include <welder/bind_traits.hpp> // param_types / aggregate_fields
14#include <welder/doc.hpp> // doc_of / param_docs / return_doc_of
15
34
35namespace welder::inline v0::rods::luacats {
36
37// --- signature rendering ----------------------------------------------------
38
43template <std::meta::info Fn>
44consteval auto param_lua_types() {
45 // Guard n != 0: param_types<Fn> materializes std::array<info, n> and indexes
46 // it, and std::array<T, 0>::operator[] is not usable (as the rest of welder
47 // guards too), so it must not be instantiated for a parameterless function.
48 constexpr std::size_t n{std::meta::parameters_of(Fn).size()};
49 std::array<const char*, n> out{};
50 if constexpr (n != 0) {
51 constexpr auto types{::welder::detail::param_types<Fn>()};
52 for (std::size_t i{0}; i < n; ++i)
53 out[i] = std::define_static_string(lua_type_string(types[i]));
54 }
55 return out;
56}
57
62template <std::meta::info Fn>
63std::string arg_list() {
64 static constexpr auto pds{::welder::param_docs<Fn>()};
65 std::string out{};
66 for (std::size_t i{0}; i < pds.size(); ++i) {
67 if (i)
68 out += ", ";
69 out += pds[i].name ? std::string{pds[i].name}
70 : "arg" + std::to_string(i + 1);
71 }
72 return out;
73}
74
78template <std::meta::info Fn>
79void emit_params(std::string& out) {
80 static constexpr auto pds{::welder::param_docs<Fn>()};
81 static constexpr auto types{param_lua_types<Fn>()};
82 for (std::size_t i{0}; i < pds.size(); ++i) {
83 out += "---@param ";
84 out += pds[i].name ? std::string{pds[i].name}
85 : "arg" + std::to_string(i + 1);
86 out += ' ';
87 out += types[i];
88 const std::string d{one_line(pds[i].text)};
89 if (!d.empty())
90 out += ' ' + d;
91 out += '\n';
92 }
93}
94
103template <std::meta::info Fn>
104std::string fun_signature(const std::string& ret_override) {
105 static constexpr auto pds{::welder::param_docs<Fn>()};
106 static constexpr auto types{param_lua_types<Fn>()};
107 std::string s{"fun("};
108 for (std::size_t i{0}; i < pds.size(); ++i) {
109 if (i)
110 s += ", ";
111 s += pds[i].name ? std::string{pds[i].name} : "arg" + std::to_string(i + 1);
112 s += ": ";
113 s += types[i];
114 }
115 s += ")";
116 std::string ret{ret_override};
117 if (ret.empty()) {
118 if constexpr (!std::meta::is_constructor(Fn)) {
119 using R = [:std::meta::return_type_of(Fn):];
120 if constexpr (!std::is_void_v<R>)
121 ret = lua_type(std::meta::return_type_of(Fn));
122 }
123 }
124 if (!ret.empty())
125 s += ": " + ret;
126 return s;
127}
128
133 std::string doc{};
134 std::string params{};
135 std::string ret_line{};
136 std::string args{};
137 std::string fun_sig{};
138};
139
145template <std::meta::info Fn>
146func_overload build_overload(const std::string& ret_override) {
147 func_overload o{};
148 if (const char* d{::welder::doc_of<Fn>()})
149 o.doc = d;
151 if (!ret_override.empty()) {
152 o.ret_line = "---@return " + ret_override + '\n';
153 } else if constexpr (!std::meta::is_constructor(Fn)) {
154 using R = [:std::meta::return_type_of(Fn):];
155 if constexpr (!std::is_void_v<R>) {
156 o.ret_line = "---@return ";
157 o.ret_line += lua_type(std::meta::return_type_of(Fn));
158 const std::string d{one_line(::welder::return_doc_of<Fn>())};
159 if (!d.empty())
160 o.ret_line += ' ' + d;
161 o.ret_line += '\n';
162 }
163 }
164 o.args = arg_list<Fn>();
165 o.fun_sig = fun_signature<Fn>(ret_override);
166 return o;
167}
168
173template <auto Grp, std::size_t... I>
174void collect_overloads(std::vector<func_overload>& out, std::index_sequence<I...>) {
175 (out.push_back(build_overload<Grp[I]>({})), ...);
176}
177
189inline void render_overload_group(std::string& out, const std::string& callee,
190 const std::vector<func_overload>& sigs) {
191 std::size_t primary{0};
192 for (std::size_t i{0}; i < sigs.size(); ++i)
193 if (!sigs[i].doc.empty()) {
194 primary = i;
195 break;
196 }
197 const func_overload& p{sigs[primary]};
198 emit_doc_comment(out, p.doc.empty() ? nullptr : p.doc.c_str());
199 out += p.params;
200 out += p.ret_line;
201 for (std::size_t i{0}; i < sigs.size(); ++i)
202 if (i != primary)
203 out += "---@overload " + sigs[i].fun_sig + '\n';
204 out += "function " + callee + "(" + p.args + ") end\n\n";
205}
206
207// --- the document + its writer handles --------------------------------------
208
232inline std::string apply_type_renames(
233 std::string_view text,
234 const std::vector<std::pair<std::string, std::string>>& renames) {
235 if (renames.empty())
236 return std::string{text};
237 auto is_name_char = [](char c) {
238 return c == '.' || c == '_' || (c >= 'A' && c <= 'Z') ||
239 (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
240 };
241 std::string out{};
242 out.reserve(text.size());
243 for (std::size_t i{0}; i < text.size();) {
244 if (!is_name_char(text[i])) {
245 out += text[i++];
246 continue;
247 }
248 std::size_t j{i};
249 while (j < text.size() && is_name_char(text[j]))
250 ++j;
251 const std::string_view tok{text.substr(i, j - i)};
252 const auto it{std::find_if(renames.begin(), renames.end(),
253 [&](const auto& r) { return r.first == tok; })};
254 out += (it != renames.end()) ? std::string_view{it->second} : tok;
255 i = j;
256 }
257 return out;
258}
259
262 std::string prefix{};
263 std::string doc{};
264};
265
270struct document {
271 std::vector<table_decl> tables{};
272 std::string body{};
276 std::vector<std::pair<std::string, std::string>> type_renames{};
277
281 void declare_table(std::string_view prefix, const char* doc = nullptr) {
282 for (auto& t : tables)
283 if (t.prefix == prefix) {
284 if (doc && *doc)
285 t.doc = doc;
286 return;
287 }
288 tables.push_back({std::string{prefix}, doc ? doc : ""});
289 }
290
297 void record_type_name(std::string_view raw, std::string_view styled) {
298 if (raw == styled)
299 return;
300 for (const auto& [r, s] : type_renames)
301 if (r == raw)
302 return;
303 type_renames.emplace_back(std::string{raw}, std::string{styled});
304 }
305
309 std::string render() const {
310 auto depth = [](const std::string& s) {
311 return std::count(s.begin(), s.end(), '.');
312 };
313 std::vector<table_decl> decls{tables};
314 std::stable_sort(decls.begin(), decls.end(),
315 [&](const table_decl& a, const table_decl& b) {
316 return depth(a.prefix) < depth(b.prefix);
317 });
318 std::string out{"---@meta\n\n"};
319 for (const auto& d : decls) {
320 emit_doc_comment(out, d.doc.empty() ? nullptr : d.doc.c_str());
321 out += d.prefix + " = {}\n\n";
322 }
323 out += body;
324 // Reconcile type *references* (raw C++ names, as the type map emits them)
325 // with their styled/weld_as declarations, now that every type is registered.
326 return apply_type_renames(out, type_renames);
327 }
328};
329
333 document* doc{nullptr};
334 std::string prefix{};
335};
336
343 document* doc{nullptr};
344 std::string* sink{nullptr};
350 std::string qualified{};
351 std::string cls_doc{};
352 std::string bases{};
353 std::string fields{};
354 std::string methods{};
355 std::string trailing{};
356 std::vector<func_overload> ctors{};
357
358 class_writer() = default;
359 class_writer(const class_writer&) = delete;
361 class_writer(class_writer&& o) noexcept { *this = std::move(o); }
363 doc = o.doc;
364 sink = o.sink;
365 qualified = std::move(o.qualified);
366 cls_doc = std::move(o.cls_doc);
367 bases = std::move(o.bases);
368 fields = std::move(o.fields);
369 methods = std::move(o.methods);
370 trailing = std::move(o.trailing);
371 ctors = std::move(o.ctors);
372 o.doc = nullptr; // the source no longer flushes
373 return *this;
374 }
376 if (!doc)
377 return;
378 std::string& out{sink ? *sink : doc->body};
379 emit_doc_comment(out, cls_doc.empty() ? nullptr : cls_doc.c_str());
380 out += "---@class " + qualified;
381 if (!bases.empty())
382 out += " : " + bases;
383 out += '\n';
384 out += fields;
385 out += qualified + " = {}\n\n";
386 // Constructors (all producing `Class.new`) are grouped into one documented
387 // function with `---@overload` signatures; they precede the methods.
388 if (!ctors.empty())
389 render_overload_group(out, qualified + ".new", ctors);
390 out += methods;
391 out += trailing; // nested types' blocks, after this class is complete
392 }
393};
394
398 document* doc{nullptr};
399 std::string* sink{nullptr};
402 std::string qualified{};
403 std::string enum_doc{};
404 std::string values{};
405
406 enum_writer() = default;
407 enum_writer(const enum_writer&) = delete;
409 enum_writer(enum_writer&& o) noexcept { *this = std::move(o); }
411 doc = o.doc;
412 sink = o.sink;
413 qualified = std::move(o.qualified);
414 enum_doc = std::move(o.enum_doc);
415 values = std::move(o.values);
416 o.doc = nullptr;
417 return *this;
418 }
420 if (!doc)
421 return;
422 std::string& out{sink ? *sink : doc->body};
423 emit_doc_comment(out, enum_doc.empty() ? nullptr : enum_doc.c_str());
424 out += "---@enum " + qualified + '\n';
425 out += qualified + " = {\n" + values + "}\n\n";
426 }
427};
428
429} // namespace welder::rods::luacats
Backend-agnostic selection layer: the reflection predicates and selectors that decide what participat...
Language-agnostic documentation layer: read [[=welder::doc(...)]] annotations off reflected entities ...
consteval auto param_types()
A function's parameter types, as a static array of reflections.
void render_overload_group(std::string &out, const std::string &callee, const std::vector< func_overload > &sigs)
Emit one documented function <callee>(…) for overload group sigs, with a ---@overload fun(…) line per...
Definition document.hpp:189
std::string arg_list()
The comma-joined argument-name list for Fn's declaration line (an unnamed parameter becomes arg<N>).
Definition document.hpp:63
consteval auto param_lua_types()
The LuaCATS type name of each parameter of Fn, as a splice-ready array parallel to param_docs<Fn>().
Definition document.hpp:44
consteval const char * lua_type(std::meta::info type)
lua_type_string as a static-storage C string, callable on a constant type reflection.
Definition type_map.hpp:180
std::string one_line(const char *text)
A one-line description for a @field/@param/@return tail: newlines in text collapse to spaces (LuaCATS...
Definition type_map.hpp:52
std::string fun_signature(const std::string &ret_override)
The fun(<name>: <type>, …): <ret> signature of Fn, for a ---@overload line.
Definition document.hpp:104
func_overload build_overload(const std::string &ret_override)
Build the func_overload for function/constructor Fn.
Definition document.hpp:146
std::string apply_type_renames(std::string_view text, const std::vector< std::pair< std::string, std::string > > &renames)
Rewrite every whole dotted-name token of text that is a key in renames to its mapped value.
Definition document.hpp:232
void collect_overloads(std::vector< func_overload > &out, std::index_sequence< I... >)
Append build_overload for each member of overload group Grp to out.
Definition document.hpp:174
void emit_params(std::string &out)
Emit the ---@param lines for Fn (name, LuaCATS type, and its doc).
Definition document.hpp:79
consteval std::string lua_type_string(std::meta::info type)
The LuaCATS type name for a C++ type reflection.
Definition type_map.hpp:122
void emit_doc_comment(std::string &out, const char *text)
Append text as --- comment lines (each source line prefixed), so a multiline summary lands as a LuaC...
Definition type_map.hpp:36
consteval auto param_docs()
The parameter docs of function Fn, in declaration order.
Definition doc.hpp:227
consteval detail::doc_spec< N > doc(const char(&s)[N])
Attach a docstring to a namespace, class, function, or function parameter.
consteval const char * return_doc_of()
The returns text on function Fn (documentation of its return value), or nullptr.
Definition doc.hpp:148
consteval const char * doc_of()
The doc text on Ent (a class, namespace, function, or parameter), or nullptr.
Definition doc.hpp:138
class_writer & operator=(const class_writer &)=delete
std::string qualified
Dotted LuaCATS class name.
Definition document.hpp:350
class_writer & operator=(class_writer &&o) noexcept
Definition document.hpp:362
std::vector< func_overload > ctors
The .new overloads, grouped on flush.
Definition document.hpp:356
class_writer(class_writer &&o) noexcept
Definition document.hpp:361
class_writer(const class_writer &)=delete
std::string * sink
Flush target override; null = doc->body.
Definition document.hpp:344
std::string bases
Comma-joined base names, or empty.
Definition document.hpp:352
std::string fields
Accumulated ---@field / ---@operator lines.
Definition document.hpp:353
std::string methods
Accumulated function statements.
Definition document.hpp:354
std::string cls_doc
The class doc.
Definition document.hpp:351
std::string trailing
Nested types' flushed blocks (after methods).
Definition document.hpp:355
The growing LuaCATS document shared by every writer handle.
Definition document.hpp:270
std::string render() const
The finished stub text: the ---@meta header, the module tables shallowest first, then the accumulated...
Definition document.hpp:309
std::vector< std::pair< std::string, std::string > > type_renames
Raw C++ dotted name → styled/weld_as bound name, for reconciling type references* with their declarat...
Definition document.hpp:276
void declare_table(std::string_view prefix, const char *doc=nullptr)
Record (once) a module table to declare, updating its doc if given.
Definition document.hpp:281
void record_type_name(std::string_view raw, std::string_view styled)
Register a welded type's declared name so references to it are reconciled at render.
Definition document.hpp:297
std::vector< table_decl > tables
Definition document.hpp:271
std::string * sink
Flush target override; null = doc->body (a nested enum flushes into its outer's trailing,...
Definition document.hpp:399
enum_writer & operator=(const enum_writer &)=delete
enum_writer(enum_writer &&o) noexcept
Definition document.hpp:409
enum_writer(const enum_writer &)=delete
std::string values
Accumulated Name = value, lines.
Definition document.hpp:404
enum_writer & operator=(enum_writer &&o) noexcept
Definition document.hpp:410
One overload of a callee, pre-rendered into the pieces a group needs: the summary doc,...
Definition document.hpp:132
std::string args
The function callee(<args>) name list.
Definition document.hpp:136
std::string ret_line
The ---@return … line (empty when void).
Definition document.hpp:135
std::string doc
Summary docstring (may be empty).
Definition document.hpp:133
std::string fun_sig
The fun(a: T, …): R signature.
Definition document.hpp:137
std::string params
---@param … lines (may be empty).
Definition document.hpp:134
A module handle: the shared document plus this (sub)module's dotted table path.
Definition document.hpp:332
A module/submodule table to declare (prefix = {}), with its optional doc.
Definition document.hpp:261
std::string prefix
The dotted table path, e.g.
Definition document.hpp:262
std::string doc
Its namespace doc, if any.
Definition document.hpp:263
The low-level rendering primitives of the LuaCATS stub backend: the C++→LuaCATS type map**,...