IncludeCPP 3.7.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of IncludeCPP might be problematic. Click here for more details.

Files changed (49) hide show
  1. includecpp/__init__.py +59 -0
  2. includecpp/__init__.pyi +255 -0
  3. includecpp/__main__.py +4 -0
  4. includecpp/cli/__init__.py +4 -0
  5. includecpp/cli/commands.py +8270 -0
  6. includecpp/cli/config_parser.py +127 -0
  7. includecpp/core/__init__.py +19 -0
  8. includecpp/core/ai_integration.py +2132 -0
  9. includecpp/core/build_manager.py +2416 -0
  10. includecpp/core/cpp_api.py +376 -0
  11. includecpp/core/cpp_api.pyi +95 -0
  12. includecpp/core/cppy_converter.py +3448 -0
  13. includecpp/core/cssl/CSSL_DOCUMENTATION.md +2075 -0
  14. includecpp/core/cssl/__init__.py +42 -0
  15. includecpp/core/cssl/cssl_builtins.py +2271 -0
  16. includecpp/core/cssl/cssl_builtins.pyi +1393 -0
  17. includecpp/core/cssl/cssl_events.py +621 -0
  18. includecpp/core/cssl/cssl_modules.py +2803 -0
  19. includecpp/core/cssl/cssl_parser.py +2575 -0
  20. includecpp/core/cssl/cssl_runtime.py +3051 -0
  21. includecpp/core/cssl/cssl_syntax.py +488 -0
  22. includecpp/core/cssl/cssl_types.py +1512 -0
  23. includecpp/core/cssl_bridge.py +882 -0
  24. includecpp/core/cssl_bridge.pyi +488 -0
  25. includecpp/core/error_catalog.py +802 -0
  26. includecpp/core/error_formatter.py +1016 -0
  27. includecpp/core/exceptions.py +97 -0
  28. includecpp/core/path_discovery.py +77 -0
  29. includecpp/core/project_ui.py +3370 -0
  30. includecpp/core/settings_ui.py +326 -0
  31. includecpp/generator/__init__.py +1 -0
  32. includecpp/generator/parser.cpp +1903 -0
  33. includecpp/generator/parser.h +281 -0
  34. includecpp/generator/type_resolver.cpp +363 -0
  35. includecpp/generator/type_resolver.h +68 -0
  36. includecpp/py.typed +0 -0
  37. includecpp/templates/cpp.proj.template +18 -0
  38. includecpp/vscode/__init__.py +1 -0
  39. includecpp/vscode/cssl/__init__.py +1 -0
  40. includecpp/vscode/cssl/language-configuration.json +38 -0
  41. includecpp/vscode/cssl/package.json +50 -0
  42. includecpp/vscode/cssl/snippets/cssl.snippets.json +1080 -0
  43. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +341 -0
  44. includecpp-3.7.3.dist-info/METADATA +1076 -0
  45. includecpp-3.7.3.dist-info/RECORD +49 -0
  46. includecpp-3.7.3.dist-info/WHEEL +5 -0
  47. includecpp-3.7.3.dist-info/entry_points.txt +2 -0
  48. includecpp-3.7.3.dist-info/licenses/LICENSE +21 -0
  49. includecpp-3.7.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1903 @@
1
+ #include "parser.h"
2
+ #include <iostream>
3
+ #include <fstream>
4
+ #include <sstream>
5
+ #include <regex>
6
+ #include <ctime>
7
+ #include <iomanip>
8
+ #include <functional>
9
+ #include <filesystem>
10
+ #include <iterator>
11
+ #include <set>
12
+
13
+ int API::main(int argc, char* argv[]) {
14
+ if (argc < 5) {
15
+ std::cerr << "Usage: plugin_gen <plugins_dir> <bindings_output> <sources_output> <registry_output>" << std::endl;
16
+ return 1;
17
+ }
18
+
19
+ std::string plugins_dir = argv[1];
20
+ std::string bindings_output = argv[2];
21
+ std::string sources_output = argv[3];
22
+ std::string registry_output = argv[4];
23
+
24
+ std::cout << "Plugin Generator starting..." << std::endl;
25
+ std::cout << "Plugins directory: " << plugins_dir << std::endl;
26
+ std::cout << "Output: " << bindings_output << std::endl;
27
+
28
+ auto modules = parse_all_cp_files(plugins_dir);
29
+ if (modules.empty()) {
30
+ std::cerr << "ERROR: No modules found or parsed successfully in " << plugins_dir << std::endl;
31
+ std::cerr << "Please ensure:" << std::endl;
32
+ std::cerr << " 1. The plugins directory exists" << std::endl;
33
+ std::cerr << " 2. There are .cp files in the directory" << std::endl;
34
+ std::cerr << " 3. The .cp files have valid syntax" << std::endl;
35
+ return 1; // Exit with error if no modules found
36
+ }
37
+
38
+ if (!write_files(modules, bindings_output, sources_output)) {
39
+ std::cerr << "ERROR: Failed to write output files!" << std::endl;
40
+ return 1;
41
+ }
42
+
43
+ std::string registry_json = generate_registry_json(modules, plugins_dir);
44
+ std::ofstream registry_file(registry_output);
45
+ if (!registry_file.is_open()) {
46
+ std::cerr << "ERROR: Cannot open registry file for writing: " << registry_output << std::endl;
47
+ return 1;
48
+ }
49
+
50
+ registry_file << registry_json;
51
+
52
+ if (registry_file.fail() || registry_file.bad()) {
53
+ std::cerr << "ERROR: Failed to write to registry file: " << registry_output << std::endl;
54
+ registry_file.close();
55
+ return 1;
56
+ }
57
+ registry_file.close();
58
+
59
+ std::cout << "SUCCESS: Generated bindings for " << modules.size() << " module(s)" << std::endl;
60
+ return 0;
61
+ }
62
+
63
+ std::vector<ModuleDescriptor> API::parse_all_cp_files(const std::string& plugins_dir) {
64
+ std::vector<ModuleDescriptor> modules;
65
+
66
+ if (!std::filesystem::exists(plugins_dir)) {
67
+ std::cerr << "ERROR: Plugins directory does not exist: " << plugins_dir << std::endl;
68
+ std::cerr << "Current working directory: " << std::filesystem::current_path() << std::endl;
69
+ return modules;
70
+ }
71
+
72
+ std::cout << "Scanning for .cp files in: " << plugins_dir << std::endl;
73
+
74
+ int cp_file_count = 0;
75
+ for (const auto& entry : std::filesystem::directory_iterator(plugins_dir)) {
76
+ if (entry.path().extension() == ".cp") {
77
+ cp_file_count++;
78
+ std::cout << "Found .cp file: " << entry.path().filename() << std::endl;
79
+ try {
80
+ modules.push_back(parse_cp_file(entry.path().string()));
81
+ std::cout << " [OK] Successfully parsed: " << entry.path().filename() << std::endl;
82
+ } catch (const std::exception& e) {
83
+ std::cerr << " [ERROR] Parsing " << entry.path().filename() << ": " << e.what() << std::endl;
84
+ }
85
+ }
86
+ }
87
+
88
+ if (cp_file_count == 0) {
89
+ std::cerr << "ERROR: No .cp files found in directory" << std::endl;
90
+ } else {
91
+ std::cout << "Total: " << cp_file_count << " .cp file(s) found, "
92
+ << modules.size() << " successfully parsed" << std::endl;
93
+ }
94
+
95
+ return modules;
96
+ }
97
+
98
+ void API::validate_module_name(const std::string& name, int line_num) {
99
+ if (name.empty()) {
100
+ throw std::runtime_error("Line " + std::to_string(line_num) +
101
+ ": Empty module name");
102
+ }
103
+
104
+ if (!std::isalpha(static_cast<unsigned char>(name[0])) && name[0] != '_') {
105
+ throw std::runtime_error("Line " + std::to_string(line_num) +
106
+ ": Invalid module name '" + name +
107
+ "' (must start with letter or underscore)");
108
+ }
109
+
110
+ for (char c : name) {
111
+ if (!std::isalnum(static_cast<unsigned char>(c)) && c != '_') {
112
+ throw std::runtime_error("Line " + std::to_string(line_num) +
113
+ ": Invalid character '" + std::string(1, c) +
114
+ "' in module name");
115
+ }
116
+ }
117
+ }
118
+
119
+ bool API::validate_bindings_code(const std::string& code) {
120
+ int brace_count = 0;
121
+ for (char c : code) {
122
+ if (c == '{') brace_count++;
123
+ if (c == '}') brace_count--;
124
+ if (brace_count < 0) {
125
+ std::cerr << "ERROR: Unbalanced braces (too many closing braces)" << std::endl;
126
+ return false;
127
+ }
128
+ }
129
+
130
+ if (brace_count != 0) {
131
+ std::cerr << "ERROR: Unbalanced braces (missing " << brace_count << " closing braces)" << std::endl;
132
+ return false;
133
+ }
134
+
135
+ if (code.find("#include <pybind11/pybind11.h>") == std::string::npos) {
136
+ std::cerr << "ERROR: Missing required include: pybind11/pybind11.h" << std::endl;
137
+ return false;
138
+ }
139
+
140
+ if (code.find("PYBIND11_MODULE(api, m)") == std::string::npos) {
141
+ std::cerr << "ERROR: Missing PYBIND11_MODULE(api, m) definition" << std::endl;
142
+ return false;
143
+ }
144
+
145
+ if (code.find(";;") != std::string::npos) {
146
+ std::cerr << "WARNING: Double semicolon detected (possible syntax error)" << std::endl;
147
+ }
148
+
149
+ return true;
150
+ }
151
+
152
+ void API::validate_namespace_includecpp(const std::string& source_path, const std::string& module_name) {
153
+ // Check if the source file contains "namespace includecpp"
154
+ std::ifstream file(source_path);
155
+ if (!file.is_open()) {
156
+ // File doesn't exist yet or can't be opened - skip validation
157
+ return;
158
+ }
159
+
160
+ std::string content((std::istreambuf_iterator<char>(file)),
161
+ std::istreambuf_iterator<char>());
162
+ file.close();
163
+
164
+ // Look for "namespace includecpp" with optional whitespace
165
+ std::regex ns_regex(R"(\bnamespace\s+includecpp\s*\{)");
166
+ if (!std::regex_search(content, ns_regex)) {
167
+ std::string error_msg =
168
+ "\n"
169
+ "+============================================================================+\n"
170
+ "| NAMESPACE ERROR |\n"
171
+ "+============================================================================+\n"
172
+ "\n"
173
+ "Module: " + module_name + "\n"
174
+ "Source: " + source_path + "\n"
175
+ "\n"
176
+ "ERROR: Source file must contain 'namespace includecpp { ... }'\n"
177
+ "\n"
178
+ "All IncludeCPP code must be wrapped in the 'includecpp' namespace.\n"
179
+ "This is required for proper Python binding generation.\n"
180
+ "\n"
181
+ "Example:\n"
182
+ "\n"
183
+ " #include <string>\n"
184
+ " \n"
185
+ " namespace includecpp {\n"
186
+ " \n"
187
+ " int add(int a, int b) {\n"
188
+ " return a + b;\n"
189
+ " }\n"
190
+ " \n"
191
+ " class MyClass {\n"
192
+ " public:\n"
193
+ " void hello() { ... }\n"
194
+ " };\n"
195
+ " \n"
196
+ " } // namespace includecpp\n"
197
+ "\n";
198
+ throw std::runtime_error(error_msg);
199
+ }
200
+ }
201
+
202
+ ModuleDescriptor API::parse_cp_file(const std::string& filepath) {
203
+ ModuleDescriptor desc;
204
+ desc.has_header = false;
205
+ desc.expose_all = false;
206
+
207
+ std::ifstream file(filepath);
208
+ if (!file.is_open()) {
209
+ throw std::runtime_error("Cannot open file: " + filepath);
210
+ }
211
+
212
+ std::stringstream buffer;
213
+ buffer << file.rdbuf();
214
+ std::string content = buffer.str();
215
+
216
+ auto lines = split(content, '\n');
217
+
218
+ bool in_public_block = false;
219
+ std::string public_content;
220
+ int line_num = 0;
221
+
222
+ for (const auto& line_raw : lines) {
223
+ line_num++;
224
+ std::string line = trim(line_raw);
225
+ if (line.empty() || line[0] == '#') continue;
226
+
227
+ #ifdef VSRAM_DEBUG
228
+ std::cout << "DEBUG: Processing line " << line_num << ": " << line << std::endl;
229
+ #endif
230
+
231
+ if (!in_public_block) {
232
+ if (starts_with(line, "SOURCE")) {
233
+ size_t source_start = line.find('(');
234
+ size_t source_end = line.find(')');
235
+ if (source_start != std::string::npos && source_end != std::string::npos) {
236
+ desc.source_path = trim(line.substr(source_start + 1, source_end - source_start - 1));
237
+ }
238
+
239
+ size_t header_pos = line.find("HEADER");
240
+ if (header_pos != std::string::npos) {
241
+ desc.has_header = true;
242
+ size_t header_start = line.find('(', header_pos);
243
+ size_t header_end = line.find(')', header_pos);
244
+ if (header_start != std::string::npos && header_end != std::string::npos) {
245
+ desc.header_path = trim(line.substr(header_start + 1, header_end - header_start - 1));
246
+ }
247
+ }
248
+
249
+ size_t last_space = line.rfind(' ');
250
+ if (last_space != std::string::npos) {
251
+ desc.module_name = trim(line.substr(last_space + 1));
252
+ validate_module_name(desc.module_name, line_num);
253
+ }
254
+ }
255
+ // v2.0: Parse SOURCES() for multi-file modules
256
+ else if (starts_with(line, "SOURCES")) {
257
+ size_t sources_start = line.find('(');
258
+ size_t sources_end = line.find(')');
259
+ if (sources_start != std::string::npos && sources_end != std::string::npos) {
260
+ std::string sources_str = line.substr(sources_start + 1, sources_end - sources_start - 1);
261
+ auto source_files = split(sources_str, ',');
262
+ for (auto& src : source_files) {
263
+ desc.additional_sources.push_back(trim(src));
264
+ }
265
+ }
266
+ }
267
+ // v2.0: Parse DEPENDS() for module dependencies
268
+ else if (starts_with(line, "DEPENDS")) {
269
+ size_t depends_start = line.find('(');
270
+ size_t depends_end = line.find(')');
271
+ if (depends_start != std::string::npos && depends_end != std::string::npos) {
272
+ std::string depends_str = line.substr(depends_start + 1, depends_end - depends_start - 1);
273
+ auto modules = split(depends_str, ',');
274
+
275
+ for (const auto& mod : modules) {
276
+ std::string trimmed = trim(mod);
277
+ ModuleDependency dep;
278
+
279
+ // Check for type list: module[Type1, Type2]
280
+ size_t bracket_start = trimmed.find('[');
281
+ if (bracket_start != std::string::npos) {
282
+ dep.target_module = trimmed.substr(0, bracket_start);
283
+ size_t bracket_end = trimmed.find(']');
284
+ if (bracket_end != std::string::npos) {
285
+ std::string types_str = trimmed.substr(bracket_start + 1, bracket_end - bracket_start - 1);
286
+ auto types = split(types_str, ',');
287
+ for (auto& t : types) {
288
+ dep.required_types.push_back(trim(t));
289
+ }
290
+ }
291
+ } else {
292
+ dep.target_module = trimmed;
293
+ }
294
+
295
+ desc.dependencies.push_back(dep);
296
+ }
297
+ }
298
+ }
299
+ else if (starts_with(line, "PUBLIC")) {
300
+ in_public_block = true;
301
+ size_t paren = line.find('(');
302
+ if (paren != std::string::npos) {
303
+ public_content += line.substr(paren + 1);
304
+ }
305
+ }
306
+ }
307
+ else {
308
+ public_content += "\n" + line;
309
+ }
310
+ }
311
+
312
+ size_t last_paren = public_content.rfind(')');
313
+ if (last_paren != std::string::npos) {
314
+ public_content = public_content.substr(0, last_paren);
315
+ }
316
+
317
+ public_content = trim(public_content);
318
+
319
+ #ifdef VSRAM_DEBUG
320
+ std::cout << "DEBUG: public_content = [" << public_content << "]" << std::endl;
321
+ #endif
322
+
323
+ if (trim(public_content) == "ALL") {
324
+ desc.expose_all = true;
325
+ }
326
+ else {
327
+ auto public_lines = split(public_content, '\n');
328
+ #ifdef VSRAM_DEBUG
329
+ std::cout << "DEBUG: Found " << public_lines.size() << " lines in PUBLIC block" << std::endl;
330
+ #endif
331
+
332
+ for (size_t i = 0; i < public_lines.size(); ++i) {
333
+ std::string cleaned = trim(public_lines[i]);
334
+ if (cleaned.empty()) continue;
335
+
336
+ size_t and_pos = cleaned.find("&&");
337
+ if (and_pos != std::string::npos) {
338
+ cleaned = trim(cleaned.substr(0, and_pos));
339
+ }
340
+
341
+ if (cleaned.find("TEMPLATE_FUNC") != std::string::npos) {
342
+ FunctionBinding fb;
343
+ fb.is_template = true;
344
+ auto parts = split(cleaned, ' ');
345
+ if (parts.size() >= 2) {
346
+ fb.module_name = parts[0];
347
+
348
+ size_t tfunc_pos = cleaned.find("TEMPLATE_FUNC(");
349
+ if (tfunc_pos != std::string::npos) {
350
+ size_t func_start = tfunc_pos + 13;
351
+ size_t func_end = cleaned.find(')', func_start);
352
+ fb.function_name = safe_extract_between(cleaned, func_start, func_end, "TEMPLATE_FUNC");
353
+ }
354
+
355
+ size_t types_pos = cleaned.find("TYPES(");
356
+ if (types_pos != std::string::npos) {
357
+ size_t types_start = types_pos + 6;
358
+ size_t types_end = cleaned.find(')', types_start);
359
+ if (types_end != std::string::npos) {
360
+ std::string types_str = cleaned.substr(types_start, types_end - types_start);
361
+ auto types = split(types_str, ',');
362
+ for (auto& t : types) {
363
+ fb.template_types.push_back(trim(t));
364
+ }
365
+ }
366
+ }
367
+
368
+ desc.functions.push_back(fb);
369
+ }
370
+ }
371
+ else if (cleaned.find("FUNC") != std::string::npos) {
372
+ FunctionBinding fb;
373
+ auto parts = split(cleaned, ' ');
374
+ if (parts.size() >= 2) {
375
+ fb.module_name = parts[0];
376
+
377
+ size_t func_start = cleaned.find('(');
378
+ size_t func_end = cleaned.find(')');
379
+ fb.function_name = safe_extract_between(cleaned, func_start, func_end, "FUNC");
380
+ desc.functions.push_back(fb);
381
+ }
382
+ }
383
+ else if (cleaned.find("CLASS") != std::string::npos) {
384
+ ClassBinding cb;
385
+ cb.auto_bind_all = false;
386
+ auto parts = split(cleaned, ' ');
387
+ if (parts.size() >= 2) {
388
+ cb.module_name = parts[0];
389
+
390
+ size_t class_start = cleaned.find('(');
391
+ size_t class_end = cleaned.find(')');
392
+ cb.class_name = safe_extract_between(cleaned, class_start, class_end, "CLASS");
393
+
394
+ // Check for METHOD/FIELD block: CLASS(Name) { ... }
395
+ size_t brace_open = cleaned.find('{');
396
+ if (brace_open != std::string::npos) {
397
+ // Collect all lines until closing brace
398
+ std::string method_block;
399
+ bool found_close = false;
400
+
401
+ // Check if closing brace on same line
402
+ size_t brace_close = cleaned.find('}', brace_open);
403
+ if (brace_close != std::string::npos) {
404
+ method_block = cleaned.substr(brace_open + 1, brace_close - brace_open - 1);
405
+ found_close = true;
406
+ } else {
407
+ // Collect from next lines
408
+ for (size_t j = i + 1; j < public_lines.size(); ++j) {
409
+ std::string next_line = trim(public_lines[j]);
410
+ size_t close_pos = next_line.find('}');
411
+
412
+ if (close_pos != std::string::npos) {
413
+ method_block += next_line.substr(0, close_pos);
414
+ i = j; // Skip these lines in main loop
415
+ found_close = true;
416
+ break;
417
+ } else {
418
+ method_block += next_line + "\n";
419
+ }
420
+ }
421
+ }
422
+
423
+ // Parse METHOD(...) and FIELD(...) entries
424
+ auto method_lines = split(method_block, '\n');
425
+ for (const auto& mline : method_lines) {
426
+ std::string mtrim = trim(mline);
427
+ if (mtrim.empty()) continue;
428
+
429
+ // v2.4.3: Parse CONSTRUCTOR(type1, type2, ...) for parametrized constructors
430
+ if (mtrim.find("CONSTRUCTOR") != std::string::npos) {
431
+ size_t c_start = mtrim.find('(');
432
+ size_t c_end = mtrim.rfind(')');
433
+ if (c_start != std::string::npos && c_end != std::string::npos) {
434
+ std::string params_str = mtrim.substr(c_start + 1, c_end - c_start - 1);
435
+ ConstructorInfo ctor;
436
+ if (!params_str.empty()) {
437
+ auto params = split(params_str, ',');
438
+ for (auto& p : params) {
439
+ std::string param_type = trim(p);
440
+ if (!param_type.empty()) {
441
+ ctor.param_types.push_back(param_type);
442
+ }
443
+ }
444
+ }
445
+ cb.constructors.push_back(ctor);
446
+ }
447
+ }
448
+ // v2.4.13: Extended METHOD parsing with overload support
449
+ // Supports: METHOD(name), METHOD(name, type1, type2), METHOD_CONST(name, type1)
450
+ else if (mtrim.find("METHOD") != std::string::npos) {
451
+ MethodSignature sig;
452
+ bool is_const_method = mtrim.find("METHOD_CONST") != std::string::npos;
453
+
454
+ size_t m_start = mtrim.find('(');
455
+ size_t m_end = mtrim.rfind(')'); // Use rfind to handle nested templates
456
+
457
+ if (m_start != std::string::npos && m_end != std::string::npos) {
458
+ std::string content = mtrim.substr(m_start + 1, m_end - m_start - 1);
459
+
460
+ // Parse method name and optional parameter types
461
+ // Format: "methodName" or "methodName, const Circle&, const Rect&"
462
+ std::vector<std::string> parts;
463
+ int template_depth = 0;
464
+ std::string current_part;
465
+
466
+ for (char c : content) {
467
+ if (c == '<') {
468
+ template_depth++;
469
+ current_part += c;
470
+ } else if (c == '>') {
471
+ template_depth--;
472
+ current_part += c;
473
+ } else if (c == ',' && template_depth == 0) {
474
+ parts.push_back(trim(current_part));
475
+ current_part.clear();
476
+ } else {
477
+ current_part += c;
478
+ }
479
+ }
480
+ if (!current_part.empty()) {
481
+ parts.push_back(trim(current_part));
482
+ }
483
+
484
+ if (!parts.empty()) {
485
+ sig.name = parts[0];
486
+ sig.is_const = is_const_method;
487
+
488
+ // Remaining parts are parameter types for overload resolution
489
+ for (size_t i = 1; i < parts.size(); ++i) {
490
+ sig.param_types.push_back(parts[i]);
491
+ }
492
+
493
+ // v3.3.22: Also populate parameters for display in get command
494
+ for (size_t i = 0; i < sig.param_types.size(); ++i) {
495
+ ParameterInfo param;
496
+ std::string type_str = sig.param_types[i];
497
+ param.type = type_str;
498
+ param.name = "arg" + std::to_string(i + 1);
499
+ // Check for const
500
+ if (type_str.find("const ") != std::string::npos) {
501
+ param.is_const = true;
502
+ }
503
+ // Check for reference
504
+ if (!type_str.empty() && type_str.back() == '&') {
505
+ param.is_reference = true;
506
+ }
507
+ // Check for pointer
508
+ if (!type_str.empty() && type_str.back() == '*') {
509
+ param.is_pointer = true;
510
+ }
511
+ sig.parameters.push_back(param);
512
+ }
513
+
514
+ cb.method_signatures.push_back(sig);
515
+ // Also add to legacy methods list for backward compatibility
516
+ cb.methods.push_back(sig.name);
517
+ }
518
+ }
519
+ }
520
+ else if (mtrim.find("FIELD") != std::string::npos) {
521
+ size_t f_start = mtrim.find('(');
522
+ size_t f_end = mtrim.find(')');
523
+ if (f_start != std::string::npos && f_end != std::string::npos) {
524
+ std::string field_name = safe_extract_between(mtrim, f_start, f_end, "FIELD");
525
+ cb.fields.push_back(field_name);
526
+ }
527
+ }
528
+ }
529
+ }
530
+
531
+ desc.classes.push_back(cb);
532
+ }
533
+ }
534
+ else if (cleaned.find("VAR") != std::string::npos) {
535
+ VariableBinding vb;
536
+ auto parts = split(cleaned, ' ');
537
+ if (parts.size() >= 2) {
538
+ vb.module_name = parts[0];
539
+
540
+ size_t var_start = cleaned.find('(');
541
+ size_t var_end = cleaned.find(')');
542
+ vb.variable_name = safe_extract_between(cleaned, var_start, var_end, "VAR");
543
+ desc.variables.push_back(vb);
544
+ }
545
+ }
546
+ // v2.0: Parse STRUCT() for Plain-Old-Data types
547
+ else if (cleaned.find("STRUCT") != std::string::npos) {
548
+ StructBinding sb;
549
+ auto parts = split(cleaned, ' ');
550
+ if (parts.size() >= 2) {
551
+ sb.module_name = parts[0];
552
+
553
+ size_t struct_start = cleaned.find("STRUCT(");
554
+ size_t struct_end = cleaned.find(')', struct_start);
555
+ // safe_extract_between adds +1 to start_pos, so pass position of '(' not after it
556
+ size_t struct_paren = struct_start + 6; // Position of '(' in "STRUCT("
557
+ sb.struct_name = safe_extract_between(cleaned, struct_paren, struct_end, "STRUCT");
558
+
559
+ // Check for TYPES() clause for template structs
560
+ size_t types_pos = cleaned.find("TYPES(");
561
+ if (types_pos != std::string::npos) {
562
+ sb.is_template = true;
563
+ size_t types_start = types_pos + 6;
564
+ size_t types_end = cleaned.find(')', types_start);
565
+ if (types_end != std::string::npos) {
566
+ std::string types_str = cleaned.substr(types_start, types_end - types_start);
567
+ auto types = split(types_str, ',');
568
+ for (auto& t : types) {
569
+ sb.template_types.push_back(trim(t));
570
+ }
571
+ }
572
+ }
573
+
574
+ // Check for FIELD block: STRUCT(Name) { FIELD(type, name) ... }
575
+ size_t brace_open = cleaned.find('{');
576
+ if (brace_open != std::string::npos) {
577
+ std::string field_block;
578
+ bool found_close = false;
579
+
580
+ // Check if closing brace on same line
581
+ size_t brace_close = cleaned.find('}', brace_open);
582
+ if (brace_close != std::string::npos) {
583
+ field_block = cleaned.substr(brace_open + 1, brace_close - brace_open - 1);
584
+ found_close = true;
585
+ } else {
586
+ // Collect from next lines
587
+ for (size_t j = i + 1; j < public_lines.size(); ++j) {
588
+ std::string next_line = trim(public_lines[j]);
589
+ size_t close_pos = next_line.find('}');
590
+
591
+ if (close_pos != std::string::npos) {
592
+ field_block += next_line.substr(0, close_pos);
593
+ i = j; // Skip these lines in main loop
594
+ found_close = true;
595
+ break;
596
+ } else {
597
+ field_block += next_line + "\n";
598
+ }
599
+ }
600
+ }
601
+
602
+ // Parse FIELD(type, name) entries
603
+ auto field_lines = split(field_block, '\n');
604
+ for (const auto& fline : field_lines) {
605
+ std::string ftrim = trim(fline);
606
+ if (ftrim.empty()) continue;
607
+
608
+ if (ftrim.find("FIELD(") != std::string::npos) {
609
+ size_t f_start = ftrim.find('(');
610
+ size_t f_end = ftrim.find(')');
611
+ if (f_start != std::string::npos && f_end != std::string::npos) {
612
+ std::string field_content = ftrim.substr(f_start + 1, f_end - f_start - 1);
613
+ auto field_parts = split(field_content, ',');
614
+
615
+ if (field_parts.size() >= 2) {
616
+ std::string field_type = trim(field_parts[0]);
617
+ std::string field_name = trim(field_parts[1]);
618
+ sb.fields.push_back({field_type, field_name});
619
+ }
620
+ }
621
+ }
622
+ }
623
+ }
624
+
625
+ desc.structs.push_back(sb);
626
+ }
627
+ }
628
+ }
629
+ }
630
+
631
+ // Parse DOC() statements from entire file content
632
+ auto doc_map = parse_doc_statements(content);
633
+
634
+ // Apply documentation to functions
635
+ for (auto& func : desc.functions) {
636
+ std::string key = "FUNC(" + func.function_name + ")";
637
+ if (doc_map.count(key)) {
638
+ func.documentation = doc_map[key];
639
+ }
640
+ }
641
+
642
+ // Apply documentation to classes and methods
643
+ for (auto& cls : desc.classes) {
644
+ std::string class_key = "CLASS(" + cls.class_name + ")";
645
+ if (doc_map.count(class_key)) {
646
+ cls.documentation = doc_map[class_key];
647
+ }
648
+
649
+ // Apply method documentation
650
+ for (const auto& method : cls.methods) {
651
+ std::string method_key = "METHOD(" + method + ")";
652
+ if (doc_map.count(method_key)) {
653
+ cls.method_docs[method] = doc_map[method_key];
654
+ }
655
+ }
656
+ }
657
+
658
+ // Apply documentation to variables
659
+ for (auto& var : desc.variables) {
660
+ std::string key = "VAR(" + var.variable_name + ")";
661
+ if (doc_map.count(key)) {
662
+ var.documentation = doc_map[key];
663
+ }
664
+ }
665
+
666
+ // v2.0: Apply documentation to structs
667
+ for (auto& st : desc.structs) {
668
+ std::string key = "STRUCT(" + st.struct_name + ")";
669
+ if (doc_map.count(key)) {
670
+ st.documentation = doc_map[key];
671
+ }
672
+ }
673
+
674
+ // v2.3.5: Extract function signatures from C++ source file for IntelliSense
675
+ if (!desc.source_path.empty() && std::filesystem::exists(desc.source_path)) {
676
+ auto cpp_signatures = parse_cpp_function_signatures(desc.source_path);
677
+
678
+ // Match extracted signatures with declared functions
679
+ for (auto& func : desc.functions) {
680
+ for (const auto& sig : cpp_signatures) {
681
+ if (sig.function_name == func.function_name) {
682
+ func.return_type = sig.return_type;
683
+ func.parameters = sig.parameters;
684
+ func.is_const = sig.is_const;
685
+ func.is_static = sig.is_static;
686
+ func.is_inline = sig.is_inline;
687
+ func.full_signature = sig.full_signature;
688
+ break;
689
+ }
690
+ }
691
+ }
692
+
693
+ // TODO v2.3.5: Extract class method signatures (future enhancement)
694
+ // This would require more complex parsing to associate methods with classes
695
+ // For now, classes use basic method names without signatures
696
+ }
697
+
698
+ // v2.4.1: Validate that source file uses namespace includecpp
699
+ if (!desc.source_path.empty()) {
700
+ validate_namespace_includecpp(desc.source_path, desc.module_name);
701
+ }
702
+
703
+ // v3.4.1: Auto-detect header from #include directives in source file
704
+ if (!desc.has_header && !desc.source_path.empty()) {
705
+ std::ifstream source_file(desc.source_path);
706
+ if (source_file.is_open()) {
707
+ std::string line;
708
+ std::regex include_regex(R"re(^\s*#include\s*"([^"]+\.h(?:pp)?)")re");
709
+ std::smatch match;
710
+
711
+ while (std::getline(source_file, line)) {
712
+ if (std::regex_search(line, match, include_regex)) {
713
+ std::string header_name = match[1].str();
714
+ // Skip standard headers and pybind11 headers
715
+ if (header_name.find("pybind11") == std::string::npos &&
716
+ header_name.find("std") == std::string::npos) {
717
+ // Found a local header - construct the path
718
+ std::filesystem::path source_path(desc.source_path);
719
+ std::filesystem::path header_path = source_path.parent_path() / header_name;
720
+
721
+ // Check if the header file exists
722
+ if (std::filesystem::exists(header_path)) {
723
+ desc.header_path = header_path.string();
724
+ desc.has_header = true;
725
+ std::cout << "NOTE: Auto-detected header for '" << desc.module_name
726
+ << "': " << desc.header_path << std::endl;
727
+ break;
728
+ }
729
+ // Also try in include/ subdirectory
730
+ header_path = source_path.parent_path().parent_path() / "include" / header_name;
731
+ if (std::filesystem::exists(header_path)) {
732
+ desc.header_path = header_path.string();
733
+ desc.has_header = true;
734
+ std::cout << "NOTE: Auto-detected header for '" << desc.module_name
735
+ << "': " << desc.header_path << std::endl;
736
+ break;
737
+ }
738
+ }
739
+ }
740
+ }
741
+ }
742
+ }
743
+
744
+ return desc;
745
+ }
746
+
747
+ std::string API::generate_class_bindings(const ClassBinding& cls, const ModuleDescriptor& mod) {
748
+ std::ostringstream code;
749
+
750
+ code << " py::class_<" << cls.class_name << ">("
751
+ << mod.module_name << "_module, \"" << cls.class_name << "\")\n";
752
+
753
+ // v2.4.3: Generate all constructor overloads from CONSTRUCTOR() entries
754
+ if (cls.constructors.empty()) {
755
+ // Backward compatibility: default constructor if none specified
756
+ code << " .def(py::init<>())";
757
+ } else {
758
+ bool first = true;
759
+ for (const auto& ctor : cls.constructors) {
760
+ if (!first) code << "\n";
761
+ first = false;
762
+
763
+ code << " .def(py::init<";
764
+ for (size_t i = 0; i < ctor.param_types.size(); ++i) {
765
+ if (i > 0) code << ", ";
766
+ code << ctor.param_types[i];
767
+ }
768
+ code << ">())";
769
+ }
770
+ }
771
+
772
+ // Bind Initialize static method only if there's a default constructor
773
+ bool has_default_ctor = cls.constructors.empty(); // backward compatibility
774
+ if (!has_default_ctor) {
775
+ for (const auto& ctor : cls.constructors) {
776
+ if (ctor.param_types.empty()) {
777
+ has_default_ctor = true;
778
+ break;
779
+ }
780
+ }
781
+ }
782
+ if (has_default_ctor) {
783
+ code << "\n .def_static(\"Initialize\", []() { return " << cls.class_name << "(); })";
784
+ }
785
+
786
+ // v2.4.13: Group methods by name to detect overloads
787
+ std::map<std::string, std::vector<MethodSignature>> method_groups;
788
+ for (const auto& sig : cls.method_signatures) {
789
+ method_groups[sig.name].push_back(sig);
790
+ }
791
+
792
+ // v2.4.13: Handle methods with overload detection
793
+ if (!cls.method_signatures.empty()) {
794
+ // Use new signature-based bindings with overload support
795
+ for (const auto& [method_name, signatures] : method_groups) {
796
+ if (signatures.size() == 1 && signatures[0].param_types.empty()) {
797
+ // Single method without explicit signature - use simple binding
798
+ const auto& sig = signatures[0];
799
+ code << "\n .def(\"" << method_name << "\", &"
800
+ << cls.class_name << "::" << method_name << ")";
801
+ } else if (signatures.size() == 1) {
802
+ // Single method with explicit signature - still use overload_cast for safety
803
+ const auto& sig = signatures[0];
804
+ code << "\n .def(\"" << method_name << "\", "
805
+ << "py::overload_cast<";
806
+
807
+ for (size_t i = 0; i < sig.param_types.size(); ++i) {
808
+ if (i > 0) code << ", ";
809
+ code << sig.param_types[i];
810
+ }
811
+
812
+ code << ">(&" << cls.class_name << "::" << method_name;
813
+
814
+ if (sig.is_const) {
815
+ code << ", py::const_";
816
+ }
817
+ code << "))";
818
+ } else {
819
+ // Multiple overloads - generate py::overload_cast for each
820
+ for (const auto& sig : signatures) {
821
+ code << "\n .def(\"" << method_name << "\", "
822
+ << "py::overload_cast<";
823
+
824
+ for (size_t i = 0; i < sig.param_types.size(); ++i) {
825
+ if (i > 0) code << ", ";
826
+ code << sig.param_types[i];
827
+ }
828
+
829
+ code << ">(&" << cls.class_name << "::" << method_name;
830
+
831
+ if (sig.is_const) {
832
+ code << ", py::const_";
833
+ }
834
+ code << "))";
835
+ }
836
+ }
837
+ }
838
+ } else {
839
+ // Fallback: Legacy method binding (no signature info)
840
+ for (const auto& method : cls.methods) {
841
+ code << "\n .def(\"" << method << "\", &"
842
+ << cls.class_name << "::" << method << ")";
843
+ }
844
+ }
845
+
846
+ // Bind fields (read-write access)
847
+ for (const auto& field : cls.fields) {
848
+ code << "\n .def_readwrite(\"" << field
849
+ << "\", &" << cls.class_name << "::" << field << ")";
850
+ }
851
+
852
+ // v2.8: Add __repr__ for better debugging output
853
+ code << "\n .def(\"__repr__\", [](" << cls.class_name << "& self) {\n";
854
+ code << " return \"<" << cls.class_name << " object>\";\n";
855
+ code << " })";
856
+
857
+ code << ";\n\n";
858
+ return code.str();
859
+ }
860
+
861
+ // v2.0: Generate bindings for STRUCT (Plain-Old-Data types)
862
+ std::string generate_struct_bindings(const StructBinding& sb, const ModuleDescriptor& mod) {
863
+ std::ostringstream code;
864
+
865
+ if (sb.is_template) {
866
+ // Generate bindings for each template type
867
+ for (const auto& ttype : sb.template_types) {
868
+ std::string struct_full_name = sb.struct_name + "_" + ttype;
869
+ std::string cpp_type = sb.struct_name + "<" + ttype + ">";
870
+
871
+ code << " py::class_<" << cpp_type << ">(";
872
+ code << mod.module_name << "_module, \"" << struct_full_name << "\")\n";
873
+ code << " .def(py::init<>())\n";
874
+
875
+ // Fields - readwrite access
876
+ for (const auto& [field_type, field_name] : sb.fields) {
877
+ std::string actual_type = field_type;
878
+ // Replace template parameter T with actual type
879
+ if (actual_type == "T") {
880
+ actual_type = ttype;
881
+ }
882
+
883
+ code << " .def_readwrite(\"" << field_name << "\", &"
884
+ << cpp_type << "::" << field_name << ")\n";
885
+ }
886
+
887
+ // Auto-generate to_dict() method
888
+ code << " .def(\"to_dict\", [](" << cpp_type << "& self) {\n";
889
+ code << " py::dict d;\n";
890
+ for (const auto& [field_type, field_name] : sb.fields) {
891
+ code << " d[\"" << field_name << "\"] = self." << field_name << ";\n";
892
+ }
893
+ code << " return d;\n";
894
+ code << " })\n";
895
+
896
+ // Auto-generate from_dict() static method
897
+ code << " .def_static(\"from_dict\", [](py::dict d) {\n";
898
+ code << " " << cpp_type << " obj;\n";
899
+ for (const auto& [field_type, field_name] : sb.fields) {
900
+ std::string actual_type = field_type;
901
+ if (actual_type == "T") {
902
+ actual_type = ttype;
903
+ }
904
+
905
+ code << " obj." << field_name << " = d[\"" << field_name
906
+ << "\"].cast<" << actual_type << ">();\n";
907
+ }
908
+ code << " return obj;\n";
909
+ code << " })\n";
910
+
911
+ // v2.8: Add __repr__ for better debugging output
912
+ code << " .def(\"__repr__\", [](" << cpp_type << "& self) {\n";
913
+ code << " return \"<" << struct_full_name << " object>\";\n";
914
+ code << " });\n\n";
915
+ }
916
+ } else {
917
+ // Non-template struct
918
+ code << " py::class_<" << sb.struct_name << ">(";
919
+ code << mod.module_name << "_module, \"" << sb.struct_name << "\")\n";
920
+ code << " .def(py::init<>())\n";
921
+
922
+ // Fields
923
+ for (const auto& [field_type, field_name] : sb.fields) {
924
+ code << " .def_readwrite(\"" << field_name << "\", &"
925
+ << sb.struct_name << "::" << field_name << ")\n";
926
+ }
927
+
928
+ // Auto-generate to_dict() method
929
+ code << " .def(\"to_dict\", [](" << sb.struct_name << "& self) {\n";
930
+ code << " py::dict d;\n";
931
+ for (const auto& [field_type, field_name] : sb.fields) {
932
+ code << " d[\"" << field_name << "\"] = self." << field_name << ";\n";
933
+ }
934
+ code << " return d;\n";
935
+ code << " })\n";
936
+
937
+ // Auto-generate from_dict() static method
938
+ code << " .def_static(\"from_dict\", [](py::dict d) {\n";
939
+ code << " " << sb.struct_name << " obj;\n";
940
+ for (const auto& [field_type, field_name] : sb.fields) {
941
+ code << " obj." << field_name << " = d[\"" << field_name
942
+ << "\"].cast<" << field_type << ">();\n";
943
+ }
944
+ code << " return obj;\n";
945
+ code << " })\n";
946
+
947
+ // v2.8: Add __repr__ for better debugging output
948
+ code << " .def(\"__repr__\", [](" << sb.struct_name << "& self) {\n";
949
+ code << " return \"<" << sb.struct_name << " object>\";\n";
950
+ code << " });\n\n";
951
+ }
952
+
953
+ return code.str();
954
+ }
955
+
956
+ std::string API::generate_pybind11_code(const std::vector<ModuleDescriptor>& modules) {
957
+ std::ostringstream code;
958
+
959
+ code << "#include <pybind11/pybind11.h>\n";
960
+ code << "#include <pybind11/stl.h>\n";
961
+ code << "#include <pybind11/stl_bind.h>\n";
962
+ code << "#include <pybind11/operators.h>\n";
963
+ code << "#include <pybind11/functional.h>\n";
964
+ code << "#include <pybind11/complex.h>\n";
965
+ code << "#include <pybind11/chrono.h>\n\n";
966
+
967
+ // v3.3.22: Track included headers to prevent duplicates
968
+ std::set<std::string> included_headers;
969
+ std::vector<std::string> ordered_includes;
970
+
971
+ // First pass: collect all headers and detect dependencies
972
+ for (const auto& mod : modules) {
973
+ std::string include_path;
974
+ if (mod.has_header) {
975
+ include_path = replace_all(mod.header_path, "\\", "/");
976
+ } else {
977
+ // Fallback: Include source file directly (header-only in .cpp)
978
+ include_path = replace_all(mod.source_path, "\\", "/");
979
+ std::cout << "NOTE: Module '" << mod.module_name
980
+ << "' has no separate header, including source file: "
981
+ << include_path << std::endl;
982
+ }
983
+
984
+ // Skip if already included
985
+ if (included_headers.find(include_path) != included_headers.end()) {
986
+ std::cout << "NOTE: Skipping duplicate include: " << include_path << std::endl;
987
+ continue;
988
+ }
989
+
990
+ included_headers.insert(include_path);
991
+ ordered_includes.push_back(include_path);
992
+ }
993
+
994
+ // Generate includes (duplicates already filtered)
995
+ for (const auto& include_path : ordered_includes) {
996
+ code << "#include \"" << include_path << "\"\n";
997
+ }
998
+
999
+ code << "\nusing namespace includecpp;\n";
1000
+ code << "namespace py = pybind11;\n\n";
1001
+ code << "PYBIND11_MODULE(api, m) {\n";
1002
+ code << " m.doc() = \"Auto-generated C++ API bindings\";\n\n";
1003
+
1004
+ for (const auto& mod : modules) {
1005
+ code << " py::module_ " << mod.module_name << "_module = ";
1006
+ code << "m.def_submodule(\"" << mod.module_name << "\", \"";
1007
+ code << mod.module_name << " module\");\n\n";
1008
+
1009
+ for (const auto& cls : mod.classes) {
1010
+ code << generate_class_bindings(cls, mod);
1011
+ }
1012
+
1013
+ // v2.0: Generate struct bindings
1014
+ for (const auto& st : mod.structs) {
1015
+ code << generate_struct_bindings(st, mod);
1016
+ }
1017
+
1018
+ for (const auto& func : mod.functions) {
1019
+ if (func.is_template && !func.template_types.empty()) {
1020
+ for (const auto& ttype : func.template_types) {
1021
+ code << " " << mod.module_name << "_module.def(\"";
1022
+ code << func.function_name << "_" << ttype << "\", &"
1023
+ << func.function_name << "<" << ttype << ">);\n";
1024
+ }
1025
+ } else {
1026
+ code << " " << mod.module_name << "_module.def(\"";
1027
+ code << func.function_name << "\", &" << func.function_name << ");\n";
1028
+ }
1029
+ }
1030
+
1031
+ for (const auto& var : mod.variables) {
1032
+ code << " " << mod.module_name << "_module.attr(\"";
1033
+ code << var.variable_name << "\") = " << var.variable_name << ";\n";
1034
+ }
1035
+
1036
+ code << "\n";
1037
+ }
1038
+
1039
+ code << "}\n";
1040
+
1041
+ return code.str();
1042
+ }
1043
+
1044
+ bool API::write_files(const std::vector<ModuleDescriptor>& modules,
1045
+ const std::string& bindings_path,
1046
+ const std::string& sources_path) {
1047
+
1048
+ std::string bindings_code = generate_pybind11_code(modules);
1049
+
1050
+ // Validate generated code
1051
+ if (!validate_bindings_code(bindings_code)) {
1052
+ std::cerr << "ERROR: Generated bindings code validation failed!" << std::endl;
1053
+ return false;
1054
+ }
1055
+
1056
+ std::ofstream bindings_file(bindings_path);
1057
+ if (!bindings_file.is_open()) {
1058
+ std::cerr << "ERROR: Cannot open file for writing: " << bindings_path << std::endl;
1059
+ return false;
1060
+ }
1061
+
1062
+ bindings_file << bindings_code;
1063
+
1064
+ if (bindings_file.fail() || bindings_file.bad()) {
1065
+ std::cerr << "ERROR: Failed to write to file: " << bindings_path << std::endl;
1066
+ bindings_file.close();
1067
+ return false;
1068
+ }
1069
+ bindings_file.close();
1070
+
1071
+ std::ofstream sources_file(sources_path);
1072
+ if (!sources_file.is_open()) {
1073
+ std::cerr << "ERROR: Cannot open file for writing: " << sources_path << std::endl;
1074
+ return false;
1075
+ }
1076
+
1077
+ for (const auto& mod : modules) {
1078
+ // Only add to sources if module has a separate header
1079
+ // If no header, source is already included in bindings.cpp
1080
+ if (mod.has_header) {
1081
+ std::string source_path = replace_all(mod.source_path, "\\", "/");
1082
+ sources_file << source_path << "\n";
1083
+ } else {
1084
+ std::cout << "NOTE: Skipping '" << mod.source_path
1085
+ << "' from compilation (already included as header)" << std::endl;
1086
+ }
1087
+ }
1088
+
1089
+ if (sources_file.fail() || sources_file.bad()) {
1090
+ std::cerr << "ERROR: Failed to write to file: " << sources_path << std::endl;
1091
+ sources_file.close();
1092
+ return false;
1093
+ }
1094
+ sources_file.close();
1095
+
1096
+ return true;
1097
+ }
1098
+
1099
+ std::string API::compute_file_hash(const std::string& filepath) {
1100
+ if (!std::filesystem::exists(filepath)) {
1101
+ return "0";
1102
+ }
1103
+
1104
+ std::ifstream file(filepath, std::ios::binary);
1105
+ if (!file.is_open()) {
1106
+ return "0";
1107
+ }
1108
+
1109
+ std::stringstream buffer;
1110
+ buffer << file.rdbuf();
1111
+ std::string content = buffer.str();
1112
+
1113
+ std::hash<std::string> hasher;
1114
+ size_t hash_value = hasher(content);
1115
+
1116
+ std::ostringstream hash_str;
1117
+ hash_str << std::hex << hash_value;
1118
+ return hash_str.str();
1119
+ }
1120
+
1121
+ // v2.3.5: Helper function to trim whitespace
1122
+ static std::string trim_whitespace(const std::string& str) {
1123
+ size_t start = str.find_first_not_of(" \t\n\r");
1124
+ if (start == std::string::npos) return "";
1125
+ size_t end = str.find_last_not_of(" \t\n\r");
1126
+ return str.substr(start, end - start + 1);
1127
+ }
1128
+
1129
+ // v2.3.5: Parse parameter list from C++ function signature
1130
+ std::vector<ParameterInfo> API::parse_parameter_list(const std::string& params_str) {
1131
+ std::vector<ParameterInfo> parameters;
1132
+
1133
+ if (params_str.empty() || params_str == "void") {
1134
+ return parameters;
1135
+ }
1136
+
1137
+ // Split by comma (respecting template brackets)
1138
+ std::vector<std::string> param_tokens;
1139
+ int bracket_depth = 0;
1140
+ int paren_depth = 0;
1141
+ std::string current_param;
1142
+
1143
+ for (char c : params_str) {
1144
+ if (c == '<') bracket_depth++;
1145
+ else if (c == '>') bracket_depth--;
1146
+ else if (c == '(') paren_depth++;
1147
+ else if (c == ')') paren_depth--;
1148
+ else if (c == ',' && bracket_depth == 0 && paren_depth == 0) {
1149
+ param_tokens.push_back(trim_whitespace(current_param));
1150
+ current_param.clear();
1151
+ continue;
1152
+ }
1153
+ current_param += c;
1154
+ }
1155
+ if (!current_param.empty()) {
1156
+ param_tokens.push_back(trim_whitespace(current_param));
1157
+ }
1158
+
1159
+ // Parse each parameter
1160
+ for (const auto& param_str : param_tokens) {
1161
+ ParameterInfo param;
1162
+ std::string clean_param = trim_whitespace(param_str);
1163
+
1164
+ // Check for default value
1165
+ size_t eq_pos = clean_param.find('=');
1166
+ if (eq_pos != std::string::npos) {
1167
+ param.default_value = trim_whitespace(clean_param.substr(eq_pos + 1));
1168
+ clean_param = trim_whitespace(clean_param.substr(0, eq_pos));
1169
+ }
1170
+
1171
+ // Parse type and name using regex
1172
+ // Pattern: [const] type [&|*] name
1173
+ std::regex param_regex(R"(^(const\s+)?(.+?)\s*([&*])?\s*([a-zA-Z_]\w*)$)");
1174
+ std::smatch param_match;
1175
+
1176
+ if (std::regex_match(clean_param, param_match, param_regex)) {
1177
+ if (param_match[1].matched) {
1178
+ param.is_const = true;
1179
+ }
1180
+ param.type = trim_whitespace(param_match[2].str());
1181
+ if (param_match[3].matched) {
1182
+ std::string ref_or_ptr = param_match[3].str();
1183
+ if (ref_or_ptr == "&") param.is_reference = true;
1184
+ if (ref_or_ptr == "*") param.is_pointer = true;
1185
+ }
1186
+ param.name = param_match[4].str();
1187
+ } else {
1188
+ // Fallback: assume entire thing is type, no name
1189
+ param.type = clean_param;
1190
+ param.name = "";
1191
+ }
1192
+
1193
+ parameters.push_back(param);
1194
+ }
1195
+
1196
+ return parameters;
1197
+ }
1198
+
1199
+ // v2.3.5: Parse C++ function signatures from source file
1200
+ std::vector<FunctionBinding> API::parse_cpp_function_signatures(const std::string& cpp_file_path) {
1201
+ std::vector<FunctionBinding> signatures;
1202
+
1203
+ std::ifstream file(cpp_file_path);
1204
+ if (!file.is_open()) {
1205
+ return signatures;
1206
+ }
1207
+
1208
+ std::string content((std::istreambuf_iterator<char>(file)),
1209
+ std::istreambuf_iterator<char>());
1210
+ file.close();
1211
+
1212
+ // Regex pattern for function declarations/definitions
1213
+ // Matches: [qualifiers] return_type function_name(params) [const]
1214
+ std::regex func_regex(
1215
+ R"((?:^|\n)\s*(?:(static|inline|virtual|constexpr)\s+)?)"
1216
+ R"(([a-zA-Z_][\w:<>]*(?:\s*\*|\s*&)?)\s+)"
1217
+ R"(([a-zA-Z_]\w*)\s*\()"
1218
+ R"(([^)]*)\))"
1219
+ R"(\s*(const)?\s*(?:[{;]|\n))"
1220
+ );
1221
+
1222
+ auto search_begin = std::sregex_iterator(content.begin(), content.end(), func_regex);
1223
+ auto search_end = std::sregex_iterator();
1224
+
1225
+ for (std::sregex_iterator i = search_begin; i != search_end; ++i) {
1226
+ std::smatch match = *i;
1227
+
1228
+ FunctionBinding sig;
1229
+
1230
+ // Extract leading qualifier
1231
+ if (match[1].matched) {
1232
+ std::string qualifier = match[1].str();
1233
+ if (qualifier == "static") sig.is_static = true;
1234
+ if (qualifier == "inline") sig.is_inline = true;
1235
+ }
1236
+
1237
+ // Extract return type
1238
+ sig.return_type = trim_whitespace(match[2].str());
1239
+
1240
+ // Extract function name
1241
+ sig.function_name = trim_whitespace(match[3].str());
1242
+
1243
+ // Extract parameters
1244
+ std::string params_str = trim_whitespace(match[4].str());
1245
+ if (!params_str.empty() && params_str != "void") {
1246
+ sig.parameters = parse_parameter_list(params_str);
1247
+ }
1248
+
1249
+ // Extract trailing const
1250
+ if (match[5].matched) {
1251
+ sig.is_const = true;
1252
+ }
1253
+
1254
+ // Build full signature string
1255
+ sig.full_signature = match[0].str();
1256
+
1257
+ signatures.push_back(sig);
1258
+ }
1259
+
1260
+ return signatures;
1261
+ }
1262
+
1263
+ std::string API::generate_registry_json(const std::vector<ModuleDescriptor>& modules, const std::string& plugins_dir) {
1264
+ std::ostringstream json;
1265
+ json << "{\n";
1266
+ json << " \"schema_version\": \"2.0\",\n";
1267
+ json << " \"modules\": {\n";
1268
+
1269
+ for (size_t i = 0; i < modules.size(); ++i) {
1270
+ const auto& mod = modules[i];
1271
+
1272
+ json << " \"" << mod.module_name << "\": {\n";
1273
+ json << " \"sources\": [";
1274
+
1275
+ std::string source_path = replace_all(mod.source_path, "\\", "/");
1276
+ json << "\"" << source_path << "\"";
1277
+
1278
+ // v2.0: Add additional sources
1279
+ for (const auto& add_src : mod.additional_sources) {
1280
+ std::string add_src_path = replace_all(add_src, "\\", "/");
1281
+ json << ", \"" << add_src_path << "\"";
1282
+ }
1283
+
1284
+ json << "],\n";
1285
+
1286
+ if (mod.has_header) {
1287
+ std::string header_path = replace_all(mod.header_path, "\\", "/");
1288
+ json << " \"header\": \"" << header_path << "\",\n";
1289
+ } else {
1290
+ json << " \"header\": null,\n";
1291
+ }
1292
+
1293
+ std::filesystem::path cp_path = std::filesystem::path(plugins_dir) / (mod.module_name + ".cp");
1294
+ std::string cp_file = cp_path.string();
1295
+ cp_file = replace_all(cp_file, "\\", "/");
1296
+ json << " \"cp_file\": \"" << cp_file << "\",\n";
1297
+
1298
+ // v2.0: Add dependencies
1299
+ json << " \"dependencies\": [\n";
1300
+ for (size_t j = 0; j < mod.dependencies.size(); ++j) {
1301
+ const auto& dep = mod.dependencies[j];
1302
+ json << " {\n";
1303
+ json << " \"target\": \"" << dep.target_module << "\"";
1304
+ if (!dep.required_types.empty()) {
1305
+ json << ",\n \"types\": [";
1306
+ for (size_t k = 0; k < dep.required_types.size(); ++k) {
1307
+ json << "\"" << dep.required_types[k] << "\"";
1308
+ if (k < dep.required_types.size() - 1) json << ", ";
1309
+ }
1310
+ json << "]";
1311
+ }
1312
+ if (dep.is_optional) {
1313
+ json << ",\n \"optional\": true";
1314
+ } else {
1315
+ json << ",\n \"optional\": false";
1316
+ }
1317
+ json << "\n }";
1318
+ if (j < mod.dependencies.size() - 1) json << ",";
1319
+ json << "\n";
1320
+ }
1321
+ json << " ],\n";
1322
+
1323
+ // v2.3.5: Use source_hashes with full path keys for compatibility
1324
+ json << " \"source_hashes\": {\n";
1325
+
1326
+ // Hash main source with full relative path as key
1327
+ json << " \"" << source_path << "\": \"" << compute_file_hash(mod.source_path) << "\"";
1328
+
1329
+ // Hash additional sources
1330
+ for (const auto& add_src : mod.additional_sources) {
1331
+ std::string add_src_path = replace_all(add_src, "\\", "/");
1332
+ json << ",\n \"" << add_src_path << "\": \"" << compute_file_hash(add_src) << "\"";
1333
+ }
1334
+
1335
+ // Hash header if exists
1336
+ if (mod.has_header) {
1337
+ std::string header_path = replace_all(mod.header_path, "\\", "/");
1338
+ json << ",\n \"" << header_path << "\": \"" << compute_file_hash(mod.header_path) << "\"";
1339
+ }
1340
+
1341
+ // Hash .cp file with special key format (for compatibility with Python code)
1342
+ json << ",\n \"" << mod.module_name << ".cp\": \"" << compute_file_hash(cp_file) << "\"";
1343
+ json << "\n },\n";
1344
+
1345
+ // v2.0: Add structs with fields
1346
+ json << " \"structs\": [\n";
1347
+ for (size_t j = 0; j < mod.structs.size(); ++j) {
1348
+ const auto& st = mod.structs[j];
1349
+ json << " {\n";
1350
+ json << " \"name\": \"" << st.struct_name << "\"";
1351
+ if (!st.documentation.empty()) {
1352
+ json << ",\n \"doc\": \"" << replace_all(st.documentation, "\"", "\\\"") << "\"";
1353
+ }
1354
+ if (st.is_template) {
1355
+ json << ",\n \"is_template\": true";
1356
+ json << ",\n \"template_types\": [";
1357
+ for (size_t k = 0; k < st.template_types.size(); ++k) {
1358
+ json << "\"" << st.template_types[k] << "\"";
1359
+ if (k < st.template_types.size() - 1) json << ", ";
1360
+ }
1361
+ json << "]";
1362
+ } else {
1363
+ json << ",\n \"is_template\": false";
1364
+ }
1365
+ json << ",\n \"fields\": [\n";
1366
+ for (size_t k = 0; k < st.fields.size(); ++k) {
1367
+ const auto& [type, name] = st.fields[k];
1368
+ json << " {\"type\": \"" << type << "\", \"name\": \"" << name << "\"}";
1369
+ if (k < st.fields.size() - 1) json << ",";
1370
+ json << "\n";
1371
+ }
1372
+ json << " ]\n";
1373
+ json << " }";
1374
+ if (j < mod.structs.size() - 1) json << ",";
1375
+ json << "\n";
1376
+ }
1377
+ json << " ],\n";
1378
+
1379
+ // Add functions with enhanced signature metadata (v2.3.5)
1380
+ json << " \"functions\": [\n";
1381
+ for (size_t j = 0; j < mod.functions.size(); ++j) {
1382
+ const auto& func = mod.functions[j];
1383
+ json << " {\n";
1384
+ json << " \"name\": \"" << func.function_name << "\"";
1385
+
1386
+ if (!func.documentation.empty()) {
1387
+ json << ",\n \"doc\": \"" << replace_all(func.documentation, "\"", "\\\"") << "\"";
1388
+ }
1389
+
1390
+ // v2.3.5: Add return type
1391
+ json << ",\n \"return_type\": \"" << func.return_type << "\"";
1392
+
1393
+ // v2.3.5: Add parameters with types
1394
+ json << ",\n \"parameters\": [\n";
1395
+ for (size_t k = 0; k < func.parameters.size(); ++k) {
1396
+ const auto& param = func.parameters[k];
1397
+ json << " {\n";
1398
+ json << " \"name\": \"" << param.name << "\",\n";
1399
+ json << " \"type\": \"" << param.type << "\"";
1400
+ if (!param.default_value.empty()) {
1401
+ json << ",\n \"default\": \"" << replace_all(param.default_value, "\"", "\\\"") << "\"";
1402
+ }
1403
+ if (param.is_const) json << ",\n \"const\": true";
1404
+ if (param.is_reference) json << ",\n \"reference\": true";
1405
+ if (param.is_pointer) json << ",\n \"pointer\": true";
1406
+ json << "\n }";
1407
+ if (k < func.parameters.size() - 1) json << ",";
1408
+ json << "\n";
1409
+ }
1410
+ json << " ]";
1411
+
1412
+ // v2.3.5: Add function qualifiers
1413
+ if (func.is_static) json << ",\n \"static\": true";
1414
+ if (func.is_const) json << ",\n \"const\": true";
1415
+ if (func.is_inline) json << ",\n \"inline\": true";
1416
+
1417
+ // v3.1.5: Add template function info
1418
+ if (func.is_template) {
1419
+ json << ",\n \"is_template\": true";
1420
+ json << ",\n \"template_types\": [";
1421
+ for (size_t k = 0; k < func.template_types.size(); ++k) {
1422
+ json << "\"" << func.template_types[k] << "\"";
1423
+ if (k < func.template_types.size() - 1) json << ", ";
1424
+ }
1425
+ json << "]";
1426
+ }
1427
+
1428
+ json << "\n }";
1429
+ if (j < mod.functions.size() - 1) json << ",";
1430
+ json << "\n";
1431
+ }
1432
+ json << " ],\n";
1433
+
1434
+ // Add classes with methods, constructors, and documentation
1435
+ json << " \"classes\": [\n";
1436
+ for (size_t j = 0; j < mod.classes.size(); ++j) {
1437
+ const auto& cls = mod.classes[j];
1438
+ json << " {\n";
1439
+ json << " \"name\": \"" << cls.class_name << "\"";
1440
+ if (!cls.documentation.empty()) {
1441
+ json << ",\n \"doc\": \"" << replace_all(cls.documentation, "\"", "\\\"") << "\"";
1442
+ }
1443
+
1444
+ // v2.4.3: Add constructor signatures
1445
+ json << ",\n \"constructors\": [\n";
1446
+ if (cls.constructors.empty()) {
1447
+ // Default constructor
1448
+ json << " {\"params\": []}\n";
1449
+ } else {
1450
+ for (size_t k = 0; k < cls.constructors.size(); ++k) {
1451
+ const auto& ctor = cls.constructors[k];
1452
+ json << " {\"params\": [";
1453
+ for (size_t p = 0; p < ctor.param_types.size(); ++p) {
1454
+ json << "\"" << ctor.param_types[p] << "\"";
1455
+ if (p < ctor.param_types.size() - 1) json << ", ";
1456
+ }
1457
+ json << "]}";
1458
+ if (k < cls.constructors.size() - 1) json << ",";
1459
+ json << "\n";
1460
+ }
1461
+ }
1462
+ json << " ]";
1463
+
1464
+ json << ",\n \"methods\": [\n";
1465
+ if (!cls.method_signatures.empty()) {
1466
+ for (size_t k = 0; k < cls.method_signatures.size(); ++k) {
1467
+ const auto& sig = cls.method_signatures[k];
1468
+ json << " {\n";
1469
+ json << " \"name\": \"" << sig.name << "\"";
1470
+ if (!sig.documentation.empty()) {
1471
+ json << ",\n \"doc\": \"" << replace_all(sig.documentation, "\"", "\\\"") << "\"";
1472
+ }
1473
+ json << ",\n \"return_type\": \"" << sig.return_type << "\"";
1474
+ json << ",\n \"parameters\": [\n";
1475
+ for (size_t p = 0; p < sig.parameters.size(); ++p) {
1476
+ const auto& param = sig.parameters[p];
1477
+ json << " {\n";
1478
+ json << " \"name\": \"" << param.name << "\",\n";
1479
+ json << " \"type\": \"" << param.type << "\"";
1480
+ if (!param.default_value.empty()) {
1481
+ json << ",\n \"default\": \"" << replace_all(param.default_value, "\"", "\\\"") << "\"";
1482
+ }
1483
+ if (param.is_const) json << ",\n \"const\": true";
1484
+ if (param.is_reference) json << ",\n \"reference\": true";
1485
+ if (param.is_pointer) json << ",\n \"pointer\": true";
1486
+ json << "\n }";
1487
+ if (p < sig.parameters.size() - 1) json << ",";
1488
+ json << "\n";
1489
+ }
1490
+ json << " ]";
1491
+ if (sig.is_const) json << ",\n \"const\": true";
1492
+ if (sig.is_static) json << ",\n \"static\": true";
1493
+ json << "\n }";
1494
+ if (k < cls.method_signatures.size() - 1) json << ",";
1495
+ json << "\n";
1496
+ }
1497
+ } else {
1498
+ for (size_t k = 0; k < cls.methods.size(); ++k) {
1499
+ const auto& method = cls.methods[k];
1500
+ json << " {\n";
1501
+ json << " \"name\": \"" << method << "\"";
1502
+ if (cls.method_docs.count(method)) {
1503
+ json << ",\n \"doc\": \"" << replace_all(cls.method_docs.at(method), "\"", "\\\"") << "\"";
1504
+ }
1505
+ json << "\n }";
1506
+ if (k < cls.methods.size() - 1) json << ",";
1507
+ json << "\n";
1508
+ }
1509
+ }
1510
+ json << " ]\n";
1511
+ json << " }";
1512
+ if (j < mod.classes.size() - 1) json << ",";
1513
+ json << "\n";
1514
+ }
1515
+ json << " ],\n";
1516
+
1517
+ auto t = std::time(nullptr);
1518
+ std::tm tm_buffer;
1519
+ #ifdef _WIN32
1520
+ localtime_s(&tm_buffer, &t);
1521
+ #else
1522
+ localtime_r(&t, &tm_buffer);
1523
+ #endif
1524
+ std::ostringstream timestamp;
1525
+ timestamp << std::put_time(&tm_buffer, "%Y-%m-%dT%H:%M:%S");
1526
+ json << " \"last_built\": \"" << timestamp.str() << "\"\n";
1527
+
1528
+ json << " }";
1529
+ if (i < modules.size() - 1) {
1530
+ json << ",";
1531
+ }
1532
+ json << "\n";
1533
+ }
1534
+
1535
+ json << " }\n";
1536
+ json << "}\n";
1537
+ return json.str();
1538
+ }
1539
+
1540
+ std::string API::trim(const std::string& str) {
1541
+ size_t start = 0;
1542
+ while (start < str.length() && std::isspace(static_cast<unsigned char>(str[start]))) start++;
1543
+
1544
+ size_t end = str.length();
1545
+ while (end > start && std::isspace(static_cast<unsigned char>(str[end - 1]))) end--;
1546
+
1547
+ return str.substr(start, end - start);
1548
+ }
1549
+
1550
+ std::vector<std::string> API::split(const std::string& str, char delimiter) {
1551
+ std::vector<std::string> tokens;
1552
+ std::stringstream ss(str);
1553
+ std::string token;
1554
+
1555
+ while (std::getline(ss, token, delimiter)) {
1556
+ tokens.push_back(token);
1557
+ }
1558
+
1559
+ return tokens;
1560
+ }
1561
+
1562
+ std::string API::extract_between(const std::string& str, char open, char close) {
1563
+ size_t start = str.find(open);
1564
+ size_t end = str.find(close, start);
1565
+
1566
+ if (start == std::string::npos || end == std::string::npos) {
1567
+ return "";
1568
+ }
1569
+
1570
+ return str.substr(start + 1, end - start - 1);
1571
+ }
1572
+
1573
+ std::string API::safe_extract_between(const std::string& str,
1574
+ size_t start_pos,
1575
+ size_t end_pos,
1576
+ const std::string& context) {
1577
+ if (start_pos == std::string::npos || end_pos == std::string::npos) {
1578
+ throw std::runtime_error("Parse error: missing parenthesis in " + context);
1579
+ }
1580
+ if (start_pos >= end_pos) {
1581
+ throw std::runtime_error("Parse error: invalid syntax in " + context +
1582
+ " (closing parenthesis before opening)");
1583
+ }
1584
+ if (end_pos > str.length()) {
1585
+ throw std::runtime_error("Parse error: position out of bounds in " + context);
1586
+ }
1587
+ return trim(str.substr(start_pos + 1, end_pos - start_pos - 1));
1588
+ }
1589
+
1590
+ bool API::starts_with(const std::string& str, const std::string& prefix) {
1591
+ return str.size() >= prefix.size() &&
1592
+ str.compare(0, prefix.size(), prefix) == 0;
1593
+ }
1594
+
1595
+ std::string API::normalize_path(const std::string& path) {
1596
+ std::string p = trim(path);
1597
+ std::replace(p.begin(), p.end(), '/', '\\');
1598
+ return p;
1599
+ }
1600
+
1601
+ std::string API::replace_all(std::string str, const std::string& from, const std::string& to) {
1602
+ size_t start_pos = 0;
1603
+ while((start_pos = str.find(from, start_pos)) != std::string::npos) {
1604
+ str.replace(start_pos, from.length(), to);
1605
+ start_pos += to.length();
1606
+ }
1607
+ return str;
1608
+ }
1609
+
1610
+ std::string API::extract_doc_string(const std::string& str) {
1611
+ // Extract string from DOC(..., "...") - get the part after comma
1612
+ size_t comma = str.find(',');
1613
+ if (comma == std::string::npos) return "";
1614
+
1615
+ std::string after_comma = str.substr(comma + 1);
1616
+ after_comma = trim(after_comma);
1617
+
1618
+ // Find quoted string
1619
+ size_t quote1 = after_comma.find('"');
1620
+ if (quote1 == std::string::npos) return "";
1621
+
1622
+ size_t quote2 = after_comma.find('"', quote1 + 1);
1623
+ if (quote2 == std::string::npos) return "";
1624
+
1625
+ return after_comma.substr(quote1 + 1, quote2 - quote1 - 1);
1626
+ }
1627
+
1628
+ std::map<std::string, std::string> API::parse_doc_statements(const std::string& content) {
1629
+ std::map<std::string, std::string> docs;
1630
+
1631
+ // Find all DOC(...) statements
1632
+ size_t pos = 0;
1633
+ while ((pos = content.find("DOC(", pos)) != std::string::npos) {
1634
+ // Find matching closing parenthesis
1635
+ int paren_count = 1;
1636
+ size_t i = pos + 4; // Start after "DOC("
1637
+ size_t doc_start = i;
1638
+
1639
+ while (i < content.length() && paren_count > 0) {
1640
+ if (content[i] == '(') paren_count++;
1641
+ else if (content[i] == ')') paren_count--;
1642
+ i++;
1643
+ }
1644
+
1645
+ if (paren_count == 0) {
1646
+ // Extract full DOC(...) content
1647
+ std::string doc_content = content.substr(doc_start, i - doc_start - 1);
1648
+
1649
+ // Find first closing paren to get the key (FUNC(name), CLASS(name), METHOD(name))
1650
+ size_t first_close = doc_content.find(')');
1651
+ if (first_close != std::string::npos) {
1652
+ std::string key = doc_content.substr(0, first_close + 1);
1653
+ key = trim(key);
1654
+
1655
+ // Extract doc string
1656
+ std::string doc_str = extract_doc_string(doc_content);
1657
+
1658
+ if (!key.empty() && !doc_str.empty()) {
1659
+ docs[key] = doc_str;
1660
+ }
1661
+ }
1662
+ }
1663
+
1664
+ pos = i;
1665
+ }
1666
+
1667
+ return docs;
1668
+ }
1669
+
1670
+ // v2.0: TypeRegistry method implementations
1671
+ void TypeRegistry::register_struct(const std::string& module, const StructBinding& s) {
1672
+ structs_[s.struct_name] = s;
1673
+ type_to_module_[s.struct_name] = module;
1674
+ }
1675
+
1676
+ void TypeRegistry::register_class(const std::string& module, const ClassBinding& c) {
1677
+ classes_[c.class_name] = c;
1678
+ type_to_module_[c.class_name] = module;
1679
+ }
1680
+
1681
+ void TypeRegistry::register_dependency(const std::string& from_module, const ModuleDependency& dep) {
1682
+ dependencies_[from_module].push_back(dep);
1683
+ }
1684
+
1685
+ TypeMetadata TypeRegistry::resolve_type(const std::string& type_string) const {
1686
+ TypeMetadata meta;
1687
+ // Basic implementation - can be enhanced with TypeResolver
1688
+ meta.full_signature = type_string;
1689
+ meta.base_type = type_string;
1690
+
1691
+ if (type_string.find("vector") != std::string::npos || type_string.find("std::vector") != std::string::npos) {
1692
+ meta.category = TypeMetadata::VECTOR_TYPE;
1693
+ } else if (type_string.find("map") != std::string::npos || type_string.find("std::map") != std::string::npos) {
1694
+ meta.category = TypeMetadata::MAP_TYPE;
1695
+ } else if (is_struct(type_string)) {
1696
+ meta.category = TypeMetadata::STRUCT_TYPE;
1697
+ } else if (is_class(type_string)) {
1698
+ meta.category = TypeMetadata::CLASS_TYPE;
1699
+ } else {
1700
+ meta.category = TypeMetadata::PRIMITIVE;
1701
+ }
1702
+
1703
+ return meta;
1704
+ }
1705
+
1706
+ bool TypeRegistry::is_struct(const std::string& type_name) const {
1707
+ return structs_.count(type_name) > 0;
1708
+ }
1709
+
1710
+ bool TypeRegistry::is_class(const std::string& type_name) const {
1711
+ return classes_.count(type_name) > 0;
1712
+ }
1713
+
1714
+ bool TypeRegistry::type_exists(const std::string& type_name) const {
1715
+ return type_to_module_.count(type_name) > 0;
1716
+ }
1717
+
1718
+ std::vector<std::string> TypeRegistry::get_dependencies(const std::string& module) const {
1719
+ std::vector<std::string> result;
1720
+ if (dependencies_.count(module)) {
1721
+ for (const auto& dep : dependencies_.at(module)) {
1722
+ result.push_back(dep.target_module);
1723
+ }
1724
+ }
1725
+ return result;
1726
+ }
1727
+
1728
+ std::vector<std::string> TypeRegistry::get_dependency_order() const {
1729
+ return topological_sort();
1730
+ }
1731
+
1732
+ bool TypeRegistry::has_circular_dependency() const {
1733
+ std::set<std::string> visited;
1734
+ std::set<std::string> rec_stack;
1735
+ std::vector<std::string> path;
1736
+
1737
+ for (const auto& [module, _] : dependencies_) {
1738
+ if (has_cycle_dfs(module, visited, rec_stack, path)) {
1739
+ return true;
1740
+ }
1741
+ }
1742
+
1743
+ return false;
1744
+ }
1745
+
1746
+ std::vector<std::string> TypeRegistry::get_circular_path() const {
1747
+ std::set<std::string> visited;
1748
+ std::set<std::string> rec_stack;
1749
+ std::vector<std::string> path;
1750
+
1751
+ for (const auto& [module, _] : dependencies_) {
1752
+ if (has_cycle_dfs(module, visited, rec_stack, path)) {
1753
+ return path;
1754
+ }
1755
+ }
1756
+
1757
+ return {};
1758
+ }
1759
+
1760
+ std::string TypeRegistry::get_module_for_type(const std::string& type_name) const {
1761
+ if (type_to_module_.count(type_name)) {
1762
+ return type_to_module_.at(type_name);
1763
+ }
1764
+ return "";
1765
+ }
1766
+
1767
+ std::vector<std::string> TypeRegistry::get_all_modules() const {
1768
+ std::set<std::string> modules;
1769
+ for (const auto& [type, module] : type_to_module_) {
1770
+ modules.insert(module);
1771
+ }
1772
+ return std::vector<std::string>(modules.begin(), modules.end());
1773
+ }
1774
+
1775
+ std::vector<std::string> TypeRegistry::topological_sort() const {
1776
+ std::map<std::string, int> in_degree;
1777
+ std::set<std::string> all_modules;
1778
+
1779
+ // Get all modules
1780
+ for (const auto& [module, _] : dependencies_) {
1781
+ all_modules.insert(module);
1782
+ in_degree[module] = 0;
1783
+ }
1784
+
1785
+ // Calculate in-degrees
1786
+ for (const auto& [module, deps] : dependencies_) {
1787
+ for (const auto& dep : deps) {
1788
+ all_modules.insert(dep.target_module);
1789
+ in_degree[dep.target_module]++;
1790
+ }
1791
+ }
1792
+
1793
+ // Kahn's algorithm
1794
+ std::vector<std::string> result;
1795
+ std::vector<std::string> queue;
1796
+
1797
+ // Find all nodes with in-degree 0
1798
+ for (const auto& module : all_modules) {
1799
+ if (in_degree[module] == 0) {
1800
+ queue.push_back(module);
1801
+ }
1802
+ }
1803
+
1804
+ while (!queue.empty()) {
1805
+ std::string current = queue.back();
1806
+ queue.pop_back();
1807
+ result.push_back(current);
1808
+
1809
+ // Reduce in-degree for neighbors
1810
+ if (dependencies_.count(current)) {
1811
+ for (const auto& dep : dependencies_.at(current)) {
1812
+ in_degree[dep.target_module]--;
1813
+ if (in_degree[dep.target_module] == 0) {
1814
+ queue.push_back(dep.target_module);
1815
+ }
1816
+ }
1817
+ }
1818
+ }
1819
+
1820
+ return result;
1821
+ }
1822
+
1823
+ bool TypeRegistry::has_cycle_dfs(const std::string& module,
1824
+ std::set<std::string>& visited,
1825
+ std::set<std::string>& rec_stack,
1826
+ std::vector<std::string>& path) const {
1827
+ visited.insert(module);
1828
+ rec_stack.insert(module);
1829
+ path.push_back(module);
1830
+
1831
+ if (dependencies_.count(module)) {
1832
+ for (const auto& dep : dependencies_.at(module)) {
1833
+ if (!visited.count(dep.target_module)) {
1834
+ if (has_cycle_dfs(dep.target_module, visited, rec_stack, path)) {
1835
+ return true;
1836
+ }
1837
+ } else if (rec_stack.count(dep.target_module)) {
1838
+ // Found cycle
1839
+ path.push_back(dep.target_module);
1840
+ return true;
1841
+ }
1842
+ }
1843
+ }
1844
+
1845
+ rec_stack.erase(module);
1846
+ path.pop_back();
1847
+ return false;
1848
+ }
1849
+
1850
+ // v2.0: TypeMetadata conversion methods implementation
1851
+ std::string TypeMetadata::to_python_type_hint() const {
1852
+ static const std::map<std::string, std::string> type_map = {
1853
+ {"int", "int"}, {"long", "int"}, {"short", "int"},
1854
+ {"float", "float"}, {"double", "float"},
1855
+ {"bool", "bool"},
1856
+ {"string", "str"}, {"std::string", "str"},
1857
+ {"void", "None"}
1858
+ };
1859
+
1860
+ if (type_map.count(base_type)) {
1861
+ return type_map.at(base_type);
1862
+ }
1863
+
1864
+ if (category == VECTOR_TYPE && !template_args.empty()) {
1865
+ return "List[" + template_args[0].to_python_type_hint() + "]";
1866
+ }
1867
+
1868
+ if (category == MAP_TYPE && template_args.size() >= 2) {
1869
+ return "Dict[" + template_args[0].to_python_type_hint() + ", " +
1870
+ template_args[1].to_python_type_hint() + "]";
1871
+ }
1872
+
1873
+ // For custom types (struct/class)
1874
+ return base_type;
1875
+ }
1876
+
1877
+ std::string TypeMetadata::to_pybind11_type() const {
1878
+ std::ostringstream oss;
1879
+ if (is_const) oss << "const ";
1880
+ oss << base_type;
1881
+
1882
+ if (!template_args.empty()) {
1883
+ oss << "<";
1884
+ for (size_t i = 0; i < template_args.size(); ++i) {
1885
+ if (i > 0) oss << ", ";
1886
+ oss << template_args[i].to_pybind11_type();
1887
+ }
1888
+ oss << ">";
1889
+ }
1890
+
1891
+ if (is_pointer) oss << "*";
1892
+ if (is_reference) oss << "&";
1893
+
1894
+ return oss.str();
1895
+ }
1896
+
1897
+ std::string TypeMetadata::to_cpp_type() const {
1898
+ return to_pybind11_type(); // Same as pybind11 type
1899
+ }
1900
+
1901
+ int main(int argc, char* argv[]) {
1902
+ return API::main(argc, argv);
1903
+ }