changing doc format and HTML literals

This commit is contained in:
udo
2026-04-22 01:56:46 +00:00
parent 8223dcc6b3
commit f7b066b374
123 changed files with 1917 additions and 1611 deletions
+75 -47
View File
@@ -1,12 +1,23 @@
:title
C++ Preprocessor
:sig
UCE source preprocessing
:desc
:see
load
unit_render
unit_call
0_context
1_COMPONENT
:content
UCE runs a small custom source-to-source preprocessor before Clang sees a `.uce` or `.ws.uce` file.
The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all of C++. Instead, it performs a narrow character-wise rewrite that understands UCE literal blocks, inline code islands, `#load`, and `EXPORT` harvesting, then writes a generated `.cpp` file and compiles that file into a shared object.
:Syntax
## Syntax
- `<> ... </>` enters literal-output mode.
- Inside a literal block, `<? ... ?>` emits raw C++.
- Inside a literal block, `<?= expression ?>` emits `print(html_escape(expression));`.
@@ -16,13 +27,14 @@ The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all
- `COMPONENT:NAME(Request& context)` is rewritten by the custom pass into an exported named component handler.
- `EXPORT` is also a normal C++ macro, but the custom pass additionally records exported declarations for metadata.
:Pipeline
## Pipeline
- The generated file starts by including `COMPILER_SYS_PATH/src/lib/uce_lib.h`.
- It then inlines the configured setup template from `SETUP_TEMPLATE` (by default `scripts/setup.h.template`), which defines the internal hook `__uce_set_current_request(Request*)`.
- It inserts `#line 1` before page code so compiler diagnostics point back to the original `.uce` file.
- Each literal block is rewritten into one or more `print(R"( ... )");` calls.
- `<? ... ?>` temporarily breaks out of literal printing, emits the enclosed C++ unchanged, then resumes literal output.
- `<?= ... ?>` becomes `print(html_escape(...));`. The runtime currently provides `html_escape()` overloads for `String`, `u64`, and `f64`.
- `<?= ... ?>` becomes `print(html_escape(...));`.
- `<?: ... ?>` becomes `print(...);` and is intended for trusted markup or already-escaped content.
- `#load "file.uce"` is replaced with a generated C++ `#include` that points at the loaded unit's preprocessed `.cpp` file under `BIN_DIRECTORY`.
- Lines beginning with `EXPORT` are scanned so their declarations can be written to a sibling `.exports.txt` file.
@@ -31,53 +43,74 @@ The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all
- The final generated source is written to `BIN_DIRECTORY + src_path + "/" + source_file + ".cpp"`.
- `scripts/compile` then compiles that generated `.cpp` into `source_file + ".so"` with `clang++ -shared -std=c++20 ...`.
:GeneratedFiles
- Source file: `/some/path/page.uce`
- Generated C++: `BIN_DIRECTORY/some/path/page.uce.cpp`
- Shared object: `BIN_DIRECTORY/some/path/page.uce.so`
- Export list: `BIN_DIRECTORY/some/path/page.uce.exports.txt`
## Generated Files
:Example
Example 1: literal output with escaped data
`RENDER(Request& context)`
`{`
` &lt;&gt;&lt;h1&gt;&lt;?= context.params["DOCUMENT_URI"] ?&gt;&lt;/h1&gt;&lt;/&gt;`
`}`
For a source file like `/some/path/page.uce`, the preprocessor produces:
- generated C++: `BIN_DIRECTORY/some/path/page.uce.cpp`
- shared object: `BIN_DIRECTORY/some/path/page.uce.so`
- export list: `BIN_DIRECTORY/some/path/page.uce.exports.txt`
## Examples
Literal output with escaped data:
```cpp
RENDER(Request& context)
{
<><h1><?= context.params["DOCUMENT_URI"] ?></h1></>
}
```
Roughly becomes:
`print(R"(&lt;h1&gt;)");`
`print(html_escape(context.params["DOCUMENT_URI"]));`
`print(R"(&lt;/h1&gt;)");`
Example 1b: literal output with trusted unescaped markup
`RENDER(Request& context)`
`{`
` &lt;&gt;&lt;div class="panel"&gt;&lt;?: component("components/card", context.call, context) ?&gt;&lt;/div&gt;&lt;/&gt;`
`}`
```cpp
print(R"(<h1>)");
print(html_escape(context.params["DOCUMENT_URI"]));
print(R"(</h1>)");
```
Literal output with trusted unescaped markup:
```cpp
RENDER(Request& context)
{
<><div class="panel"><?: component("components/card", context.call, context) ?></div></>
}
```
Roughly becomes:
`print(R"(&lt;div class="panel"&gt;)");`
`print(component("components/card", context.call, context));`
`print(R"(&lt;/div&gt;)");`
Example 2: compile-time composition
`#load "partials/nav.uce"`
`RENDER(Request& context)`
`{`
` &lt;&gt;&lt;body&gt;...&lt;/body&gt;&lt;/&gt;`
`}`
```cpp
print(R"(<div class="panel">)");
print(component("components/card", context.call, context));
print(R"(</div>)");
```
Compile-time composition:
```cpp
#load "partials/nav.uce"
RENDER(Request& context)
{
<><body>...</body></>
}
```
The loaded file is resolved relative to the current source file unless the path is already absolute.
:Rules
## Rules
- Literal mode starts only on the exact token `<>`.
- Literal mode ends only on the exact token `</>`.
- `#load` is recognized only when the current line starts with `#load ` at column 1.
- `EXPORT` harvesting likewise only triggers when the current line starts with `EXPORT` at column 1 and is followed by whitespace.
- `EXPORT` harvesting only triggers when the current line starts with `EXPORT` at column 1 and is followed by whitespace.
- Relative `#load` paths are expanded against the including unit's source directory.
- `unit_render()` and `unit_call()` are runtime APIs; `#load` is a compile-time include/composition feature.
- `unit_render()` and `unit_call()` are runtime APIs. `#load` is a compile-time composition feature.
## Limitations
:Limitations
- This pass is character-wise, not a full parser.
- Outside literal blocks it only tracks double-quoted C++ strings while deciding whether `<>` should open literal mode.
- It does not understand comments, raw string literals, templates, or general C++ token structure.
@@ -86,18 +119,13 @@ The loaded file is resolved relative to the current source file unless the path
- Because literal output is emitted as a C++ raw string literal `R"( ... )"`, literal content must not contain the exact terminator sequence `)"` or the generated C++ will break.
- `#load` depends on the target unit's generated `.cpp` existing and being compilable. If the target cannot be preprocessed or compiled correctly, the including file will fail to compile as well.
:Debugging
- When a page is compiled, inspect the generated file under `BIN_DIRECTORY` first. That file shows the exact C++ produced by the UCE preprocessor.
## Debugging
- Inspect the generated file under `BIN_DIRECTORY` first. That file shows the exact C++ produced by the UCE preprocessor.
- Compiler errors usually point back to the `.uce` source because the preprocessor inserts `#line 1`, but the generated `.cpp` is still the best place to inspect expansion problems.
- If a `#load` include looks wrong, check the current file's directory, the configured `BIN_DIRECTORY`, and whether the loaded page already produced its own generated `.cpp`.
:see
load
unit_render
unit_call
0_context
1_COMPONENT
## Related Concepts
:related
**PHP:** Template tags like `<?php ... ?>`, `<?= ... ?>`, output buffering, and compile-time include patterns
**JavaScript / Node.js:** JSX transforms, tagged templates, server-side rendering pipelines, and build-time HTML generation
- PHP: template tags like `<?php ... ?>`, `<?= ... ?>`, output buffering, and compile-time include patterns
- JavaScript / Node.js: JSX transforms, tagged templates, server-side rendering pipelines, and build-time HTML generation