decided in favor of dedicated COMPONENT() macro, updates to documentation

This commit is contained in:
udo
2026-04-19 11:34:03 +00:00
parent be514d63d6
commit 2b5586d7df
56 changed files with 926 additions and 401 deletions
+2
View File
@@ -1,9 +1,11 @@
String Functions
ascii_safe_name
concat
filter
first
join
json_encode
nibble
print
split
+1
View File
@@ -11,6 +11,7 @@ file_put_contents
get_cwd
ls
mkdir
path_join
set_cwd
shell_escape
shell_exec
+2
View File
@@ -1,5 +1,7 @@
Types
DTree
get_by_path
set_status
String
StringMap
+6 -1
View File
@@ -28,6 +28,9 @@ Name of the session cookie
:DTree var
Variable user-defined data
:DTree cfg
Request-local configuration tree. Apps can keep structured configuration here and traverse it with `context.cfg.get_by_path("path/to/value")`.
:DTree call
Invocation or message-local structured data
@@ -43,6 +46,9 @@ Headers to be sent back to the browser
:StringList set_cookies;
Cookies that should be sent back to the browser
:context.set_status(s32 code[, String reason])
Sets the HTTP status line and updates `context.flags.status`. When `reason` is omitted, UCE uses a built-in standard reason phrase for common status codes.
:u64 random_seed
The current request's "random" noise generator seed
@@ -64,4 +70,3 @@ Invokes another UCE file using the current or supplied request context
:see
>types
+36
View File
@@ -0,0 +1,36 @@
:sig
COMPONENT(Request& context)
:desc
Defines the default component entrypoint for the current `.uce` file.
`component()` and `render_component()` invoke `COMPONENT(Request& context)` by default. Named component entrypoints use `COMPONENT:NAME(Request& context)`.
This keeps page rendering and component rendering separate:
- direct HTTP requests call `RENDER(Request& context)`
- WebSocket messages call `WS(Request& context)`
- component helpers call `COMPONENT(Request& context)` or `COMPONENT:NAME(Request& context)`
A file intended only for component reuse can define `COMPONENT()` without defining `RENDER()`.
Inside component handlers, props arrive through `context.call`.
When you call `component(":NAME", props, context)` or `render_component(":NAME", props, context)`, the runtime resolves `:NAME` against the current `.uce` file instead of requiring the file name again.
Examples:
`COMPONENT(Request& context)`
`{`
` <><section><?: component(":BODY", context.call, context) ?></section></>`
`}`
`COMPONENT:BODY(Request& context)`
`{`
` <><p><?= context.call["body"] ?></p></>`
`}`
:see
>component
>render_component
>1_RENDER
>1_WS
+3 -5
View File
@@ -6,19 +6,17 @@ Defines the main HTTP render handler for the current `.uce` page.
When a page is requested over HTTP, the runtime loads the target file and calls its `RENDER(Request& context)` function.
Pages may also export additional named render handlers with `RENDER:NAME(Request& context)`.
Named render handlers are not used for the page's direct HTTP entrypoint. They are intended for component-style sub-rendering through helpers such as `component("components/card:BODY", props, context)` or `render_component("components/card:BODY", props, context)`.
The default page entrypoint is always the plain `RENDER(Request& context)` handler.
Reusable component handlers now live on `COMPONENT(Request& context)` and `COMPONENT:NAME(Request& context)`. The component helpers call those handlers, not `RENDER()`.
The request environment is passed explicitly through `context`, including params, cookies, post data, session state, headers, uploaded files, and the current `context.call` tree.
For a normal direct page request, `context.call` starts empty.
If the page is invoked from another UCE file via `render_file(file_name, context)`, the callee receives that same `context`.
Pages intended to serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. In that case `RENDER(Request& context)` serves the initial HTTP response and `WS(Request& context)` handles subsequent WebSocket messages.
Pages intended to serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when they intentionally need both page and component behavior in one unit. In that case `RENDER(Request& context)` serves the direct HTTP response, `WS(Request& context)` handles subsequent WebSocket messages, and `COMPONENT()` remains available only through the component helpers.
:see
>ob
+4 -1
View File
@@ -12,7 +12,8 @@ The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all
- Inside a literal block, `<?= expression ?>` emits `print(html_escape(expression));`.
- Inside a literal block, `<?: expression ?>` emits `print(expression);` without HTML escaping.
- `#load "other.uce"` injects another UCE unit at compile time.
- `RENDER(Request& context)` and `WS(Request& context)` are normal C++ macros from `src/lib/compiler.h`.
- `RENDER(Request& context)`, `COMPONENT(Request& context)`, and `WS(Request& context)` are normal C++ macros from `src/lib/compiler.h`.
- `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
@@ -25,6 +26,7 @@ The implementation lives in `src/lib/compiler.cpp`. It does not try to parse all
- `<?: ... ?>` 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.
- Lines beginning with `COMPONENT:NAME(...)` are rewritten into exported `component_render_NAME(...)` functions for the component helpers.
- 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 ...`.
@@ -93,3 +95,4 @@ load
render_file
call_file
0_context
1_COMPONENT
+1
View File
@@ -18,6 +18,7 @@ Useful methods include:
`to_string()`
`to_json()`
`get_type_name()`
`get_by_path()`
`set_bool()`
`remove()`
`clear()`
+14
View File
@@ -0,0 +1,14 @@
:sig
String ascii_safe_name(String raw)
:params
raw : input string to normalize
return value : ASCII-safe identifier made from letters, digits, and underscores
:desc
Builds a conservative identifier by keeping ASCII letters, digits, and underscores and dropping other characters.
This is useful when turning user- or config-provided names into handler suffixes, DOM-safe variable stems, or CSS/JS hook names.
:see
>string
+4 -2
View File
@@ -10,9 +10,11 @@ Component props are passed in `context.call`.
Because `<?= ... ?>` HTML-escapes its value, embed component markup with `<?: component(...) ?>`, `print(component(...))`, or use `render_component(...)` for direct output.
When `name` contains a colon, such as `components/card:BODY`, the part after the colon selects a named render handler exported from the component file through `RENDER:BODY(Request& context)`.
When `name` contains a colon, such as `components/card:BODY`, the part after the colon selects a named component handler exported from the component file through `COMPONENT:BODY(Request& context)`.
The default handler is `RENDER(Request& context)`.
The default handler is `COMPONENT(Request& context)`.
When `name` starts with a colon, such as `:BODY`, the target resolves against the current `.uce` file so component files can call their own named handlers without repeating the file name.
Resolution order is:
exact file name
+20
View File
@@ -0,0 +1,20 @@
:sig
DTree DTree::get_by_path(String path, String delim = "/")
:params
path : slash-delimited path to traverse
delim : optional path separator
return value : the resolved child node, or an empty `DTree` when the path cannot be followed
:desc
Traverses a nested `DTree` without creating missing keys.
Empty path segments are ignored, so leading and trailing `/` characters are harmless. If any intermediate node is not a map or a segment is missing, `get_by_path()` returns an empty `DTree`.
Typical usage:
`context.cfg.get_by_path("theme/options/portal-dark/label").to_string()`
:see
DTree
0_context
>types
+6 -1
View File
@@ -1,10 +1,15 @@
:sig
String json_encode(String s)
String json_encode(DTree t)
:params
s : string to encode as a JSON string literal
t : DTree object to be serialized
return value : string containing the JSON result
:desc
Serializes a DTree structure 't' into a String in JSON notation.
Serializes either a `String` or a `DTree` into JSON notation.
When passed a `String`, `json_encode()` returns a quoted and escaped JSON string literal.
When passed a `DTree`, scalar values are serialized directly and nested map values are emitted as JSON objects.
+15
View File
@@ -0,0 +1,15 @@
:sig
String path_join(String base, String child)
:params
base : parent path
child : child path or absolute override
return value : combined path
:desc
Joins two filesystem-style path fragments with a single `/` when needed.
If `child` is empty, `base` is returned. If `child` already starts with `/`, it is returned unchanged. This makes `path_join()` a better fit for app-level path assembly than open-coded string concatenation.
:see
>sys
+3 -1
View File
@@ -6,7 +6,9 @@ Renders another `.uce` file as a component and writes the result directly to the
This is the direct-output counterpart to `component()`.
Component props are passed through `context.call`, and `name:RENDERFUNC` may be used to select a named handler exported by `RENDER:RENDERFUNC(Request& context)`.
Component props are passed through `context.call`, and `name:COMPONENTFUNC` may be used to select a named handler exported by `COMPONENT:COMPONENTFUNC(Request& context)`.
When `name` starts with `:`, the runtime resolves that named handler against the current `.uce` file.
Use `render_component()` when you want to write component output directly from C++ code instead of capturing it as a `String`.
+15
View File
@@ -0,0 +1,15 @@
:sig
void context.set_status(s32 code, String reason = "")
:params
code : HTTP status code
reason : optional reason phrase override
:desc
Sets the current request status line and mirrors the numeric status into `context.flags.status`.
When `reason` is omitted, UCE fills in a standard reason phrase for common HTTP status codes such as `200`, `302`, `400`, `404`, and `500`.
:see
0_context
>types