8.0 KiB
Logos Code Generator — Project Description
Project Structure
cpp-generator/
├── main.cpp # Entry point — dispatches to legacy or experimental
├── CMakeLists.txt # Build config
├── compile.sh # Standalone build script
├── legacy/ # Original generator (unchanged from master)
│ ├── main.cpp # legacy_main() — plugin/metadata/provider-header modes
│ ├── generator_lib.h/cpp # Shared utilities, type mapping, header parser
│ └── legacy_main.h # Forward declaration
├── experimental/ # New LIDL + impl-header generator
│ ├── lidl_ast.h # AST types (TypeExpr, ModuleDecl, MethodDecl, etc.)
│ ├── lidl_lexer.h/cpp # LIDL tokenizer
│ ├── lidl_parser.h/cpp # LIDL recursive descent parser
│ ├── lidl_validator.h/cpp # Semantic validation
│ ├── lidl_serializer.h/cpp # AST → LIDL text pretty-printer
│ ├── lidl_gen_client.h/cpp # Client stub generation + helpers
│ ├── lidl_gen_provider.h/cpp # Provider glue + dispatch generation
│ └── impl_header_parser.h/cpp # C++ header → ModuleDecl parser
└── docs/ # This documentation
Components
Entry Point (main.cpp)
Checks for --from-header or --lidl flags before creating QCoreApplication. If neither is present, falls through to legacy_main().
AST (lidl_ast.h)
Shared data model used by all pipelines:
TypeExpr— type expression withKind(Primitive, Array, Map, Optional, Named),name, andelementsParamDecl— parameter name + typeMethodDecl— method name, params, return typeEventDecl— event name + paramsFieldDecl— struct field name, type, optional flagTypeDecl— named struct type with fieldsModuleDecl— complete module: name, version, description, category, depends, types, methods, events
All types have operator== for testing.
Lexer (lidl_lexer.h/cpp)
Tokenizes LIDL source. Token types: Module, TypeKw, Method, Event, Version, Description, Category, Depends, Ident, StringLit, symbols ({, }, (, ), [, ], :, ,, ->, ?), Eof, Error. Tracks line/column for error reporting.
Parser (lidl_parser.h/cpp)
Recursive descent parser. Grammar:
module = "module" IDENT "{" body "}"
body = (metadata | type_def | method_def | event_def)*
metadata = "version" STRING | "description" STRING | "category" STRING
| "depends" "[" (IDENT ("," IDENT)*)? "]"
type_def = "type" IDENT "{" field* "}"
field = "?"? IDENT ":" type_expr
method_def = "method" IDENT "(" params ")" "->" type_expr
event_def = "event" IDENT "(" params ")"
params = (IDENT ":" type_expr ("," IDENT ":" type_expr)*)?
type_expr = IDENT | "[" type_expr "]" | "{" type_expr ":" type_expr "}"
| "?" type_expr
Validator (lidl_validator.h/cpp)
Checks: empty module name, duplicate type/method/event names, builtin type shadowing, unknown named type references, duplicate parameter names within methods.
Serializer (lidl_serializer.h/cpp)
Converts ModuleDecl back to LIDL text. Used for roundtrip testing (parse → serialize → parse → compare).
Type Mapping (lidl_gen_client.h/cpp)
lidlTypeToQt(TypeExpr)— maps LIDL types to Qt type stringslidlToPascalCase(name)— convertssnake_casetoPascalCaselidlMakeHeader(ModuleDecl)— generates client API headerlidlMakeSource(ModuleDecl)— generates client API sourcelidlGenerateMetadataJson(ModuleDecl)— generates metadata.json content
Provider Generation (lidl_gen_provider.h/cpp)
lidlTypeToStd(TypeExpr)— maps LIDL types to C++ std type stringslidlIsStdConvertible(TypeExpr)— checks if a type has a pure C++ representationlidlMakeProviderHeader(ModuleDecl, implClass, implHeader)— generates Qt glue headerlidlMakeProviderDispatch(ModuleDecl)— generates callMethod/getMethods dispatchlidlGenerateProviderGlue(lidlPath, ...)— full pipeline from .lidl file
Impl Header Parser (impl_header_parser.h/cpp)
parseImplHeader(headerPath, className, metadataPath, err)— parses C++ header + metadata.json into ModuleDecl- State machine:
LookingForClass→InClass→InPublic/InPrivate - Skips: constructors, destructors, typedefs, using, friend, enum, struct declarations
- Template-aware parameter splitting (handles
std::vector<std::string>correctly)
CLI Usage
From C++ impl header (primary use case for universal modules)
logos-cpp-generator --from-header src/my_module_impl.h \
--backend qt \
--impl-class MyModuleImpl \
--impl-header my_module_impl.h \
--metadata metadata.json \
--output-dir ./generated_code
Generates: my_module_qt_glue.h, my_module_dispatch.cpp
From LIDL file — provider glue
logos-cpp-generator --lidl my_module.lidl \
--backend qt \
--impl-class MyModuleImpl \
--impl-header my_module_impl.h \
--output-dir ./generated_code
From LIDL file — client stubs
logos-cpp-generator --lidl my_module.lidl \
--output-dir ./generated_code \
--module-only
Legacy modes (unchanged)
logos-cpp-generator /path/to/plugin.so --output-dir ./generated
logos-cpp-generator --metadata metadata.json --general-only --output-dir ./generated
logos-cpp-generator --provider-header src/provider.h --output-dir ./generated
Building
The generator is built as part of logos-cpp-sdk:
ws build logos-cpp-sdk # builds everything including the generator
The generator binary is available as logos-cpp-generator in module build environments (provided by logos-module-builder's nativeBuildInputs).
Testing
Tests are in tests/experimental/:
ws test logos-cpp-sdk # runs all tests including experimental
Test coverage:
| Test file | What it tests |
|---|---|
test_lidl_lexer.cpp |
Tokenization: keywords, identifiers, symbols, strings, escapes, comments, errors, line/column tracking |
test_lidl_parser.cpp |
Parsing: metadata, methods, events, types, type expressions (array, map, optional, all primitives), error cases |
test_lidl_validator.cpp |
Validation: duplicates, shadowing, unknown types, duplicate params |
test_lidl_serializer.cpp |
Serialization: all constructs, roundtrip (parse → serialize → parse → compare) |
test_lidl_type_mapping.cpp |
lidlTypeToQt, lidlTypeToStd, lidlIsStdConvertible, lidlToPascalCase |
test_lidl_gen_provider.cpp |
Provider header + dispatch generation: class names, includes, macros, wrapper methods, conversions, events |
test_lidl_gen_client.cpp |
Client stub generation: sync/async methods, events, metadata JSON, edge cases |
test_impl_header_parser.cpp |
Header parsing: type mapping, access specifiers, skipping private/protected, error cases |
Fixture files in tests/experimental/fixtures/:
sample_impl.h— module with all supported type variationssample_metadata.json— metadata with dependenciescomplex_impl.h— module with multiple access specifier sectionsempty_class_impl.h— class with no public methodsempty_metadata.json— minimal metadata
Known Limitations
- The impl header parser is lightweight (regex + state machine). It does not handle:
- Multi-line method declarations
- Default parameter values
- Method definitions in the header (only declarations ending with
;) - Nested classes
- Template methods
- LIDL does not support generic/parameterized types or inheritance
- Only the
qtbackend is implemented for--from-header; future backends (CBOR, Rust) are planned - Client stub generation (
lidlMakeHeader/lidlMakeSource) is only available from LIDL files, not from--from-header