working on documentation and more API functions
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
Regular Expressions
|
||||
|
||||
regex_match
|
||||
regex_search
|
||||
regex_search_all
|
||||
regex_replace
|
||||
regex_split
|
||||
@@ -17,6 +17,11 @@ split
|
||||
split_space
|
||||
split_utf8
|
||||
replace
|
||||
regex_match
|
||||
regex_search
|
||||
regex_search_all
|
||||
regex_replace
|
||||
regex_split
|
||||
to_lower
|
||||
to_upper
|
||||
trim
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Task API
|
||||
|
||||
kill
|
||||
task
|
||||
task_repeat
|
||||
task_pid
|
||||
task_kill
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
Types
|
||||
|
||||
0_Request
|
||||
array_merge
|
||||
DTree
|
||||
0_DTree
|
||||
get_by_path
|
||||
set_status
|
||||
String
|
||||
StringList
|
||||
StringMap
|
||||
to_bool
|
||||
to_f64
|
||||
to_u64
|
||||
|
||||
+38
-130
@@ -1,104 +1,25 @@
|
||||
struct DocPage {
|
||||
String title;
|
||||
String content;
|
||||
StringList sig_lines;
|
||||
StringList param_lines;
|
||||
StringList see_lines;
|
||||
};
|
||||
#include "lib/doc_page.h"
|
||||
|
||||
String doc_default_title(String page)
|
||||
void render_doc_page_link(String page, String label = "", String badge = "")
|
||||
{
|
||||
String page_title = page;
|
||||
if(page_title.length() > 1 && page_title[1] == '_')
|
||||
nibble(page_title, "_");
|
||||
return(page_title);
|
||||
}
|
||||
page = trim(page);
|
||||
if(page == "")
|
||||
return;
|
||||
if(label == "")
|
||||
label = doc_index_label(page);
|
||||
if(badge == "")
|
||||
badge = doc_page_kind_badge(doc_page_kind(page));
|
||||
|
||||
String doc_markdown_inline(String text)
|
||||
{
|
||||
text = trim(text);
|
||||
if(text == "")
|
||||
return("");
|
||||
String html = markdown_to_html(text);
|
||||
if(html.length() >= 7 && html.substr(0, 3) == "<p>" && html.substr(html.length() - 4) == "</p>")
|
||||
return(html.substr(3, html.length() - 7));
|
||||
return(html);
|
||||
}
|
||||
|
||||
String doc_legacy_heading(String section)
|
||||
{
|
||||
if(section == "desc")
|
||||
return("");
|
||||
if(section == "related")
|
||||
return("## PHP & JS Equivalents");
|
||||
return("## " + section);
|
||||
}
|
||||
|
||||
DocPage load_doc_page(String page)
|
||||
{
|
||||
DocPage result;
|
||||
StringList lines = split(file_get_contents("pages/" + page + ".txt"), "\n");
|
||||
String current_section = "";
|
||||
bool content_mode = false;
|
||||
StringList content_lines;
|
||||
|
||||
for(auto line : lines)
|
||||
?><a href="index.uce?p=<?= uri_encode(page) ?>"><?= label ?><?
|
||||
if(badge != "")
|
||||
{
|
||||
if(!content_mode && line != "" && line.substr(0, 1) == ":")
|
||||
{
|
||||
String section = trim(line.substr(1));
|
||||
if(section == "title" || section == "sig" || section == "params" || section == "see")
|
||||
{
|
||||
current_section = section;
|
||||
continue;
|
||||
}
|
||||
if(section == "content")
|
||||
{
|
||||
content_mode = true;
|
||||
current_section = "content";
|
||||
continue;
|
||||
}
|
||||
|
||||
current_section = "legacy";
|
||||
String heading = doc_legacy_heading(section);
|
||||
if(heading != "")
|
||||
{
|
||||
if(content_lines.size() > 0 && content_lines.back() != "")
|
||||
content_lines.push_back("");
|
||||
content_lines.push_back(heading);
|
||||
content_lines.push_back("");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(current_section == "title")
|
||||
{
|
||||
if(result.title != "")
|
||||
result.title += "\n";
|
||||
result.title += line;
|
||||
}
|
||||
else if(current_section == "sig")
|
||||
{
|
||||
result.sig_lines.push_back(line);
|
||||
}
|
||||
else if(current_section == "params")
|
||||
{
|
||||
result.param_lines.push_back(line);
|
||||
}
|
||||
else if(current_section == "see")
|
||||
{
|
||||
if(trim(line) != "")
|
||||
result.see_lines.push_back(trim(line));
|
||||
}
|
||||
else
|
||||
{
|
||||
content_lines.push_back(line);
|
||||
}
|
||||
?><span class="badge"><?= badge ?></span><?
|
||||
}
|
||||
|
||||
result.content = join(content_lines, "\n");
|
||||
result.title = trim(result.title);
|
||||
return(result);
|
||||
else
|
||||
{
|
||||
?><span class="dim">()</span><?
|
||||
}
|
||||
?></a><?
|
||||
}
|
||||
|
||||
void render_doc_params(StringList param_lines)
|
||||
@@ -130,13 +51,19 @@ void render_see_section(String name)
|
||||
s32 idx = 0;
|
||||
for(auto line : lines)
|
||||
{
|
||||
line = trim(line);
|
||||
if(line == "")
|
||||
{
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
if(idx == 0)
|
||||
{
|
||||
<><div class="category"><h3><?= line ?></h3><ul></>
|
||||
}
|
||||
else if(line != "")
|
||||
else
|
||||
{
|
||||
<><li><a href="index.uce?p=<?= uri_encode(line) ?>"><?= line ?><span class="dim">()</span></a></li></>
|
||||
?><li><? render_doc_page_link(line); ?></li><?
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
@@ -154,11 +81,19 @@ void render_doc_see_links(StringList see_lines)
|
||||
{
|
||||
if(sl[0] == '>')
|
||||
{
|
||||
render_see_section(sl.substr(1));
|
||||
String target = trim(sl.substr(1));
|
||||
if(doc_has_area(target))
|
||||
{
|
||||
render_see_section(target);
|
||||
}
|
||||
else if(doc_has_page(target))
|
||||
{
|
||||
?><div><? render_doc_page_link(target); ?></div><?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
?><div><a href="index.uce?p=<?= trim(sl) ?>"><?= trim(sl) ?><span class="dim">()</span></a></div><?
|
||||
?><div><? render_doc_page_link(trim(sl)); ?></div><?
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -216,36 +151,9 @@ RENDER(Request& context)
|
||||
for(auto file_name : ls("pages/"))
|
||||
{
|
||||
String ft = nibble(file_name, ".");
|
||||
if(ft.substr(0, 2) == "0_")
|
||||
{
|
||||
String fn = ft;
|
||||
String pre = nibble(fn, "_");
|
||||
?>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?><span class="badge">struct</span></a></div>
|
||||
<?
|
||||
}
|
||||
else if(ft.substr(0, 2) == "1_")
|
||||
{
|
||||
String fn = ft;
|
||||
String pre = nibble(fn, "_");
|
||||
?>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?><span class="badge">directive</span></a></div>
|
||||
<?
|
||||
}
|
||||
else if(ft.substr(0, 2) == "3_")
|
||||
{
|
||||
String fn = ft;
|
||||
String pre = nibble(fn, "_");
|
||||
?>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?><span class="badge">info</span></a></div>
|
||||
<?
|
||||
}
|
||||
else
|
||||
{
|
||||
?>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= ft ?><span class="dim">()</span></a></div>
|
||||
<?
|
||||
}
|
||||
String label = doc_index_label(ft);
|
||||
String badge = doc_page_kind_badge(doc_page_kind(ft));
|
||||
?><div class="func-item"><? render_doc_page_link(ft, label, badge); ?></div><?
|
||||
}
|
||||
?></div>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
struct DocPage {
|
||||
String title;
|
||||
String content;
|
||||
StringList sig_lines;
|
||||
StringList param_lines;
|
||||
StringList see_lines;
|
||||
};
|
||||
|
||||
enum class DocPageKind
|
||||
{
|
||||
function,
|
||||
struct_page,
|
||||
directive,
|
||||
info
|
||||
};
|
||||
|
||||
String doc_default_title(String page)
|
||||
{
|
||||
String page_title = page;
|
||||
if(page_title.length() > 1 && page_title[1] == '_')
|
||||
nibble(page_title, "_");
|
||||
return(page_title);
|
||||
}
|
||||
|
||||
String doc_markdown_inline(String text)
|
||||
{
|
||||
text = trim(text);
|
||||
if(text == "")
|
||||
return("");
|
||||
String html = markdown_to_html(text);
|
||||
if(html.length() >= 7 && html.substr(0, 3) == "<p>" && html.substr(html.length() - 4) == "</p>")
|
||||
return(html.substr(3, html.length() - 7));
|
||||
return(html);
|
||||
}
|
||||
|
||||
String doc_legacy_heading(String section)
|
||||
{
|
||||
if(section == "desc")
|
||||
return("");
|
||||
if(section == "related")
|
||||
return("## PHP & JS Equivalents");
|
||||
return("## " + section);
|
||||
}
|
||||
|
||||
bool doc_has_area(String name)
|
||||
{
|
||||
return(file_exists("areas/" + name + ".txt"));
|
||||
}
|
||||
|
||||
bool doc_has_page(String name)
|
||||
{
|
||||
return(file_exists("pages/" + name + ".txt"));
|
||||
}
|
||||
|
||||
DocPageKind doc_page_kind(String page)
|
||||
{
|
||||
if(page.substr(0, 2) == "0_")
|
||||
return(DocPageKind::struct_page);
|
||||
if(page.substr(0, 2) == "1_")
|
||||
return(DocPageKind::directive);
|
||||
if(page.substr(0, 2) == "3_")
|
||||
return(DocPageKind::info);
|
||||
return(DocPageKind::function);
|
||||
}
|
||||
|
||||
String doc_page_kind_badge(DocPageKind kind)
|
||||
{
|
||||
if(kind == DocPageKind::struct_page)
|
||||
return("struct");
|
||||
if(kind == DocPageKind::directive)
|
||||
return("directive");
|
||||
if(kind == DocPageKind::info)
|
||||
return("info");
|
||||
return("");
|
||||
}
|
||||
|
||||
String doc_index_label(String page)
|
||||
{
|
||||
String label = page;
|
||||
auto kind = doc_page_kind(page);
|
||||
if(kind == DocPageKind::struct_page || kind == DocPageKind::directive || kind == DocPageKind::info)
|
||||
nibble(label, "_");
|
||||
return(label);
|
||||
}
|
||||
|
||||
DocPage load_doc_page(String page)
|
||||
{
|
||||
DocPage result;
|
||||
StringList lines = split(file_get_contents("pages/" + page + ".txt"), "\n");
|
||||
String current_section = "";
|
||||
bool content_mode = false;
|
||||
StringList content_lines;
|
||||
|
||||
for(auto line : lines)
|
||||
{
|
||||
if(!content_mode && line != "" && line.substr(0, 1) == ":")
|
||||
{
|
||||
String section = trim(line.substr(1));
|
||||
if(section == "title" || section == "sig" || section == "params" || section == "see")
|
||||
{
|
||||
current_section = section;
|
||||
continue;
|
||||
}
|
||||
if(section == "content")
|
||||
{
|
||||
content_mode = true;
|
||||
current_section = "content";
|
||||
continue;
|
||||
}
|
||||
|
||||
current_section = "legacy";
|
||||
String heading = doc_legacy_heading(section);
|
||||
if(heading != "")
|
||||
{
|
||||
if(content_lines.size() > 0 && content_lines.back() != "")
|
||||
content_lines.push_back("");
|
||||
content_lines.push_back(heading);
|
||||
content_lines.push_back("");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(current_section == "title")
|
||||
{
|
||||
if(result.title != "")
|
||||
result.title += "\n";
|
||||
result.title += line;
|
||||
}
|
||||
else if(current_section == "sig")
|
||||
{
|
||||
result.sig_lines.push_back(line);
|
||||
}
|
||||
else if(current_section == "params")
|
||||
{
|
||||
result.param_lines.push_back(line);
|
||||
}
|
||||
else if(current_section == "see")
|
||||
{
|
||||
if(trim(line) != "")
|
||||
result.see_lines.push_back(trim(line));
|
||||
}
|
||||
else
|
||||
{
|
||||
content_lines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
result.content = join(content_lines, "\n");
|
||||
result.title = trim(result.title);
|
||||
return(result);
|
||||
}
|
||||
@@ -6,6 +6,11 @@ Request& context;
|
||||
|
||||
:see
|
||||
>types
|
||||
set_status
|
||||
component
|
||||
unit_render
|
||||
ws_message
|
||||
session_start
|
||||
|
||||
:content
|
||||
`Request& context` is the request-local state object passed into UCE handlers. It carries incoming request data, response state, runtime metadata, and helper trees such as `context.cfg`, `context.props`, and `context.connection`.
|
||||
|
||||
@@ -8,6 +8,8 @@ COMPONENT(Request& context)
|
||||
>component
|
||||
>component_render
|
||||
>1_RENDER
|
||||
>1_INIT
|
||||
>1_ONCE
|
||||
>1_WS
|
||||
|
||||
:content
|
||||
@@ -15,6 +17,10 @@ Defines the default component entrypoint for the current `.uce` file.
|
||||
|
||||
`component()` and `component_render()` call `COMPONENT(Request& context)` by default. Named component entrypoints use `COMPONENT:NAME(Request& context)`.
|
||||
|
||||
If the same file defines `ONCE(Request& context)`, that hook runs once per request before the first `COMPONENT()` or `COMPONENT:NAME()` call for that unit.
|
||||
|
||||
If the file defines `INIT(Request& context)`, that hook runs once when the worker loads the compiled unit into memory.
|
||||
|
||||
## Why It Exists
|
||||
|
||||
This keeps page rendering and component rendering separate:
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
:title
|
||||
INIT
|
||||
|
||||
:sig
|
||||
INIT(Request& context)
|
||||
|
||||
:see
|
||||
>1_COMPONENT
|
||||
>1_ONCE
|
||||
>1_RENDER
|
||||
>1_WS
|
||||
>3_C++ Preprocessor
|
||||
>unit_call
|
||||
|
||||
:content
|
||||
Defines a worker-load hook for the current `.uce` unit.
|
||||
|
||||
When a worker loads the unit's compiled shared object into memory, the runtime checks whether the unit exposes `INIT(Request& context)`. If it does, the hook runs once for that load before the unit begins serving later requests from that in-memory copy.
|
||||
|
||||
Because UCE usually loads units on demand during a request, `INIT()` still receives a valid `Request& context`. Use it for worker-local initialization, not for request-local state that should reset each request.
|
||||
|
||||
## Typical Uses
|
||||
|
||||
- warm caches or parse static lookup data into globals
|
||||
- initialize worker-local helper state for expensive component trees
|
||||
- perform one-time registration work for that unit's in-memory copy
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
std::map<String, String> cached_labels;
|
||||
|
||||
INIT(Request& context)
|
||||
{
|
||||
if(cached_labels.empty())
|
||||
cached_labels["ready"] = "Ready";
|
||||
}
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
<>
|
||||
<p><?= cached_labels["ready"] ?></p>
|
||||
</>
|
||||
}
|
||||
```
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: opcode-cache preload or one-time bootstrap work per worker process
|
||||
- JavaScript / Node.js: module-load initialization or lazy singleton setup
|
||||
@@ -0,0 +1,49 @@
|
||||
:title
|
||||
ONCE
|
||||
|
||||
:sig
|
||||
ONCE(Request& context)
|
||||
|
||||
:see
|
||||
>1_COMPONENT
|
||||
>1_INIT
|
||||
>1_RENDER
|
||||
>1_WS
|
||||
>3_C++ Preprocessor
|
||||
>unit_call
|
||||
|
||||
:content
|
||||
Defines a request-local one-time hook for the current `.uce` unit.
|
||||
|
||||
When a request first enters a given file through `RENDER(Request& context)`, `COMPONENT(Request& context)`, or any `COMPONENT:NAME(Request& context)` handler, the runtime checks whether that unit exposes `ONCE(Request& context)`. If it does, the hook runs before the selected render or component handler.
|
||||
|
||||
`ONCE()` is tracked per request and per resolved unit file, so repeated component calls to the same file inside one request do not rerun it.
|
||||
|
||||
## Typical Uses
|
||||
|
||||
- prepare request-local derived state on `context.call`
|
||||
- load request-scoped config or data needed by multiple named component handlers
|
||||
- normalize shared props before the unit's first render/component call
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
ONCE(Request& context)
|
||||
{
|
||||
context.call["card_defaults"]["tone"] = "info";
|
||||
}
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
<>
|
||||
<div class="card card-<?= context.call["card_defaults"]["tone"] ?>">
|
||||
<?: component(":BODY", context.props, context) ?>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
```
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: per-request bootstrap work before a template or partial first runs
|
||||
- JavaScript / Node.js: request-scoped lazy initialization before a route or component render
|
||||
@@ -6,6 +6,10 @@ RENDER(Request& context)
|
||||
|
||||
:see
|
||||
>ob
|
||||
>1_COMPONENT
|
||||
>1_INIT
|
||||
>1_ONCE
|
||||
>1_WS
|
||||
|
||||
:content
|
||||
Defines the main HTTP render handler for the current `.uce` page.
|
||||
@@ -16,10 +20,14 @@ When a page is requested over HTTP, the runtime loads the target file and calls
|
||||
|
||||
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()`.
|
||||
Reusable component handlers 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.props` tree.
|
||||
|
||||
If the file defines `ONCE(Request& context)`, the runtime calls that hook once per request before the first `RENDER()` or `COMPONENT...` entrypoint from that unit runs.
|
||||
|
||||
If the file defines `INIT(Request& context)`, the runtime calls that hook once when the worker loads the compiled unit into memory.
|
||||
|
||||
For a normal direct page request, `context.props` starts empty.
|
||||
|
||||
If the page is invoked from another UCE file via `unit_render(file_name, context)`, the callee receives that same `context`.
|
||||
|
||||
@@ -6,6 +6,10 @@ WS(Request& context)
|
||||
|
||||
:see
|
||||
>websocket
|
||||
>1_COMPONENT
|
||||
>1_INIT
|
||||
>1_ONCE
|
||||
>1_RENDER
|
||||
|
||||
:content
|
||||
Defines the WebSocket message handler for the current `.ws.uce` page.
|
||||
|
||||
@@ -8,7 +8,7 @@ UCE source preprocessing
|
||||
load
|
||||
unit_render
|
||||
unit_call
|
||||
0_context
|
||||
0_Request
|
||||
1_COMPONENT
|
||||
|
||||
:content
|
||||
@@ -25,7 +25,7 @@ The template rewriting implementation lives in `src/lib/compiler-parser.cpp`, wi
|
||||
- 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)`, `COMPONENT(Request& context)`, and `WS(Request& context)` are normal C++ macros from `src/lib/compiler.h`.
|
||||
- `RENDER(Request& context)`, `COMPONENT(Request& context)`, `ONCE(Request& context)`, `INIT(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.
|
||||
|
||||
@@ -34,7 +34,7 @@ The template rewriting implementation lives in `src/lib/compiler-parser.cpp`, wi
|
||||
- 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 region is rewritten into one or more `print(R"( ... )");` calls.
|
||||
- Each literal region is rewritten into one or more `print(R"...( ... )...");` calls using a safe raw-string delimiter selected for that literal content.
|
||||
- `<>` and `?>` both switch from code mode into literal output.
|
||||
- `</>` and `<?` both switch from literal output back into code mode.
|
||||
- `<? ... ?>` temporarily breaks out of literal printing, emits the enclosed C++ unchanged, then resumes literal output.
|
||||
@@ -46,6 +46,8 @@ The template rewriting implementation lives in `src/lib/compiler-parser.cpp`, wi
|
||||
- Lines beginning with `COMPONENT:NAME(...)` are rewritten into exported `__uce_component_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 ...`.
|
||||
- When a worker loads the compiled unit into memory, the runtime checks for `INIT(Request& context)` and calls it once for that worker-side load.
|
||||
- On each request, the first time a given unit is entered through `RENDER()` or any `COMPONENT...` handler, the runtime checks for `ONCE(Request& context)` and calls it before the render/component handler.
|
||||
|
||||
## Generated Files
|
||||
|
||||
@@ -113,15 +115,32 @@ RENDER(Request& context)
|
||||
|
||||
The loaded file is resolved relative to the current source file unless the path is already absolute.
|
||||
|
||||
One-time worker initialization plus request-local setup:
|
||||
|
||||
```cpp
|
||||
INIT(Request& context)
|
||||
{
|
||||
// load worker-local data, warm caches, or initialize globals for this unit
|
||||
}
|
||||
|
||||
ONCE(Request& context)
|
||||
{
|
||||
// prepare request-local state before the first render/component call
|
||||
context.call["page_title"] = "Demo";
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- Literal mode can start on either `<>` or `?>`.
|
||||
- Literal mode can end on either `</>` or `<?`.
|
||||
- Literal delimiters are interchangeable; the parser now treats them as one shared code-vs-literal state machine rather than as separate nested block types.
|
||||
- Literal delimiters are interchangeable; the parser treats them as one shared code-vs-literal state machine rather than as separate nested block types.
|
||||
- `#load` is recognized only when the current line starts with `#load ` at column 1.
|
||||
- `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 composition feature.
|
||||
- `INIT()` runs when the shared object is loaded into a worker during a request-triggered load, so it still receives a valid `Request& context`.
|
||||
- `ONCE()` is tracked per request and per resolved unit file. A file entered multiple times in one request only runs `ONCE()` once.
|
||||
|
||||
## Limitations
|
||||
|
||||
@@ -129,7 +148,7 @@ The loaded file is resolved relative to the current source file unless the path
|
||||
- Outside literal blocks it tracks C++ quotes and comments while deciding whether `<>` or `?>` should open literal mode.
|
||||
- It does not understand comments, raw string literals, templates, or general C++ token structure.
|
||||
- Inside literal blocks it tracks quotes and comments while scanning `<? ... ?>`, `<?= ... ?>`, and `<?: ... ?>` islands so quoted `?>` text does not close those islands early.
|
||||
- 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.
|
||||
- Literal output is emitted through C++ string literals generated by the preprocessor. The preprocessor chooses a raw-string delimiter that does not occur in the literal content, so literal text may safely contain the ordinary raw-string terminator sequence `)"`.
|
||||
- `#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
|
||||
|
||||
@@ -3,6 +3,10 @@ String
|
||||
|
||||
:see
|
||||
>types
|
||||
>string
|
||||
split_utf8
|
||||
substr
|
||||
html_escape
|
||||
|
||||
:content
|
||||
Primary string type used throughout UCE.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
:sig
|
||||
StringList
|
||||
|
||||
:see
|
||||
>types
|
||||
String
|
||||
split
|
||||
split_space
|
||||
split_utf8
|
||||
join
|
||||
regex_split
|
||||
|
||||
:content
|
||||
Sequential container of `String` values.
|
||||
|
||||
`StringList` is an alias for `std::vector<String>`.
|
||||
|
||||
It is returned by split-style helpers such as `split()`, `split_space()`, `split_utf8()`, and `regex_split()`.
|
||||
|
||||
Use `join()` when you want to turn a `StringList` back into a single `String`.
|
||||
|
||||
Related:
|
||||
|
||||
- PHP: indexed arrays of strings
|
||||
- JavaScript / Node.js: arrays of strings
|
||||
@@ -3,6 +3,10 @@ StringMap
|
||||
|
||||
:see
|
||||
>types
|
||||
0_Request
|
||||
parse_query
|
||||
encode_query
|
||||
array_merge
|
||||
|
||||
:content
|
||||
Associative container mapping `String` keys to `String` values.
|
||||
|
||||
@@ -9,6 +9,9 @@ return value : merged result
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
StringMap
|
||||
json_decode
|
||||
|
||||
:content
|
||||
Merges two maps or trees using PHP-like merge behavior.
|
||||
|
||||
@@ -3,6 +3,9 @@ String component(String name, [DTree props], [Request& context])
|
||||
|
||||
:see
|
||||
>ob
|
||||
>component_render
|
||||
>1_COMPONENT
|
||||
>1_RENDER
|
||||
|
||||
:content
|
||||
Renders another `.uce` file as a component and returns the captured output as a `String`.
|
||||
@@ -21,13 +24,17 @@ 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.
|
||||
|
||||
When a component unit defines `ONCE(Request& context)`, the runtime calls that hook once per request, per resolved component file, before the first `COMPONENT()` or `COMPONENT:NAME()` handler from that file runs.
|
||||
|
||||
## Resolution Order
|
||||
|
||||
- exact file name
|
||||
- exact file name with `.uce`
|
||||
- the same two forms under `components/`
|
||||
|
||||
## Example
|
||||
## Common Patterns
|
||||
|
||||
Default component handler:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
@@ -36,6 +43,66 @@ props["title"] = "Status";
|
||||
<><?: component("workspace/panel", props, context) ?></>
|
||||
```
|
||||
|
||||
Named component handler:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
props["title"] = "System";
|
||||
props["body"] = "Healthy";
|
||||
|
||||
print(component("components/card:BODY", props, context));
|
||||
```
|
||||
|
||||
Self-targeted named handler from inside the same file:
|
||||
|
||||
```cpp
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
<>
|
||||
<section class="card">
|
||||
<?: component(":BODY", context.props, context) ?>
|
||||
</section>
|
||||
</>
|
||||
}
|
||||
|
||||
COMPONENT:BODY(Request& context)
|
||||
{
|
||||
<>
|
||||
<p><?= context.props["body"] ?></p>
|
||||
</>
|
||||
}
|
||||
```
|
||||
|
||||
Preparing props in C++ before rendering:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
props["items"][0] = "alpha";
|
||||
props["items"][1] = "beta";
|
||||
props["items"][2] = "gamma";
|
||||
|
||||
String html = component("components/list", props, context);
|
||||
print(html);
|
||||
```
|
||||
|
||||
Embedding returned component markup inside a literal block:
|
||||
|
||||
```cpp
|
||||
<>
|
||||
<div class="panel">
|
||||
<?: component("components/card", props, context) ?>
|
||||
</div>
|
||||
</>
|
||||
```
|
||||
|
||||
Because `<?= ... ?>` escapes HTML, use `<?: ... ?>` when inserting the returned markup from `component()`.
|
||||
|
||||
## Lifecycle Notes
|
||||
|
||||
- `INIT(Request& context)` runs once when the worker loads that unit into memory.
|
||||
- `ONCE(Request& context)` runs once per request before the first component or render entrypoint from that file.
|
||||
- `component()` then calls either `COMPONENT(Request& context)` or the selected `COMPONENT:NAME(Request& context)` handler.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: reusable template partials or helper-rendered view fragments returned as strings
|
||||
|
||||
@@ -3,6 +3,10 @@ bool component_exists(String name)
|
||||
|
||||
:see
|
||||
>ob
|
||||
component
|
||||
component_render
|
||||
component_resolve
|
||||
1_COMPONENT
|
||||
|
||||
:content
|
||||
Checks whether a component file can be resolved from the current page context.
|
||||
|
||||
@@ -3,6 +3,10 @@ void component_render(String name, [DTree props], [Request& context])
|
||||
|
||||
:see
|
||||
>ob
|
||||
component
|
||||
component_exists
|
||||
component_resolve
|
||||
1_COMPONENT
|
||||
|
||||
:content
|
||||
Renders another `.uce` file as a component and writes the result directly to the current output buffer.
|
||||
@@ -13,6 +17,8 @@ Component props are passed through `context.props`, and `name:COMPONENTFUNC` may
|
||||
|
||||
When `name` starts with `:`, the runtime resolves that named handler against the current `.uce` file.
|
||||
|
||||
If the target file defines `ONCE(Request& context)`, that hook runs once per request before the file's first component or render entrypoint.
|
||||
|
||||
Use `component_render()` when you want to write component output directly from C++ code instead of capturing it as a `String`.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -3,6 +3,10 @@ String component_resolve(String name)
|
||||
|
||||
:see
|
||||
>ob
|
||||
component
|
||||
component_exists
|
||||
component_render
|
||||
1_COMPONENT
|
||||
|
||||
:content
|
||||
Resolves a component name to the concrete `.uce` file path that will be loaded.
|
||||
|
||||
@@ -5,6 +5,11 @@ f64 float_val(String s)
|
||||
s : string to be converted
|
||||
return value : a f64 containing the number (0 if no number could be identified).
|
||||
|
||||
:see
|
||||
>string
|
||||
int_val
|
||||
String
|
||||
|
||||
:content
|
||||
Extracts a floating point number from a `String`.
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ delim : optional path separator
|
||||
return value : the resolved child node, or an empty `DTree` when the path cannot be followed
|
||||
|
||||
:see
|
||||
DTree
|
||||
0_context
|
||||
0_DTree
|
||||
0_Request
|
||||
>types
|
||||
json_decode
|
||||
|
||||
:content
|
||||
Traverses a nested `DTree` without creating missing keys.
|
||||
|
||||
@@ -5,6 +5,12 @@ String html_escape(String s)
|
||||
s : string to be escaped
|
||||
return value : an HTML-safe escaped version of 's'
|
||||
|
||||
:see
|
||||
>string
|
||||
json_encode
|
||||
print
|
||||
component
|
||||
|
||||
:content
|
||||
Returns a version of the input string where special HTML characters are replaced by entities:
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ s : string to be converted
|
||||
base : number system base (default 10)
|
||||
return value : a u64 containing the number (0 if no number could be identified).
|
||||
|
||||
:see
|
||||
>string
|
||||
float_val
|
||||
String
|
||||
|
||||
:content
|
||||
Extracts an integer value from `s`.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ s : string containing JSON data
|
||||
return value : a DTree object containing the deserialized JSON data
|
||||
|
||||
:see
|
||||
DTree
|
||||
0_DTree
|
||||
json_encode
|
||||
to_bool
|
||||
to_f64
|
||||
|
||||
@@ -7,6 +7,13 @@ s : string to encode as a JSON string literal
|
||||
t : DTree object to be serialized
|
||||
return value : string containing the JSON result
|
||||
|
||||
:see
|
||||
>types
|
||||
json_decode
|
||||
0_DTree
|
||||
String
|
||||
html_escape
|
||||
|
||||
:content
|
||||
Serializes either a `String` or a `DTree` into JSON notation.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ markdown_to_html
|
||||
component
|
||||
component_render
|
||||
json_encode
|
||||
DTree
|
||||
0_DTree
|
||||
|
||||
:content
|
||||
Parses Markdown source into a structured `DTree` document tree.
|
||||
|
||||
@@ -7,7 +7,7 @@ code : optional HTTP redirect status, defaults to `302`
|
||||
|
||||
:see
|
||||
set_status
|
||||
0_context
|
||||
0_Request
|
||||
|
||||
:content
|
||||
Sets the `Location` response header and updates the current HTTP status code.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
:sig
|
||||
bool regex_match(String pattern, String subject)
|
||||
bool regex_match(String pattern, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern
|
||||
subject : string to test
|
||||
flags : optional regex flags
|
||||
return value : `true` when the entire subject matches the pattern
|
||||
|
||||
:see
|
||||
>regex
|
||||
regex_search
|
||||
regex_search_all
|
||||
regex_replace
|
||||
regex_split
|
||||
String
|
||||
|
||||
:content
|
||||
Tests whether `subject` matches `pattern` from start to end.
|
||||
|
||||
This is a full-string match, not a substring search. Use `regex_search()` when you want to find the first occurrence anywhere in a string.
|
||||
|
||||
Examples:
|
||||
|
||||
```uce
|
||||
regex_match("[A-Z][a-z]+", "Alice"); // true
|
||||
regex_match("[A-Z][a-z]+", "Alice!"); // false
|
||||
regex_match("\\d{4}-\\d{2}-\\d{2}", "2026-04-29");
|
||||
```
|
||||
|
||||
Supported flags:
|
||||
|
||||
- `i` enables case-insensitive matching.
|
||||
- `m` enables multiline `^` and `$`.
|
||||
- `s` lets `.` match newlines.
|
||||
- `x` enables extended / whitespace-insensitive pattern syntax.
|
||||
- `u` explicitly enables UTF-8 and Unicode character properties.
|
||||
- `a` disables UTF-8 / Unicode property mode for ASCII-oriented matching.
|
||||
|
||||
UCE uses PCRE2 in UTF-8 + Unicode-property mode by default, so patterns such as `\\p{L}+` work naturally on Unicode text.
|
||||
|
||||
Invalid patterns or invalid flags raise a request-visible runtime error with the PCRE2 diagnostic message.
|
||||
@@ -0,0 +1,37 @@
|
||||
:sig
|
||||
String regex_replace(String pattern, String replacement, String subject)
|
||||
String regex_replace(String pattern, String replacement, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern
|
||||
replacement : replacement string
|
||||
subject : string where replacements should happen
|
||||
flags : optional regex flags
|
||||
return value : a new string with all matches replaced
|
||||
|
||||
:see
|
||||
>regex
|
||||
regex_match
|
||||
regex_search
|
||||
regex_search_all
|
||||
regex_split
|
||||
replace
|
||||
|
||||
:content
|
||||
Replaces every match of `pattern` in `subject` and returns the transformed string.
|
||||
|
||||
Example:
|
||||
|
||||
```uce
|
||||
String html = regex_replace(
|
||||
"@([A-Za-z0-9_]+)",
|
||||
"<a href=\"/users/$1\">@$1</a>",
|
||||
"Hello @alice and @bob"
|
||||
);
|
||||
```
|
||||
|
||||
Replacement strings use PCRE2 substitution syntax, including numbered capture references such as `$1` and named references such as `${name}`.
|
||||
|
||||
For simple literal search-and-replace, use `replace()`. Use `regex_replace()` when the match condition needs a pattern, captures, character classes, anchors, or flags.
|
||||
|
||||
Invalid patterns, invalid flags, or invalid substitution syntax raise a request-visible runtime error.
|
||||
@@ -0,0 +1,59 @@
|
||||
:sig
|
||||
DTree regex_search(String pattern, String subject)
|
||||
DTree regex_search(String pattern, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern
|
||||
subject : string to search
|
||||
flags : optional regex flags
|
||||
return value : a DTree describing the first match
|
||||
|
||||
:see
|
||||
>regex
|
||||
regex_match
|
||||
regex_search_all
|
||||
regex_replace
|
||||
regex_split
|
||||
0_DTree
|
||||
|
||||
:content
|
||||
Searches `subject` for the first occurrence of `pattern` and returns structured match data.
|
||||
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree match = regex_search(
|
||||
"(?<user>[A-Za-z0-9._%+-]+)@(?<host>[A-Za-z0-9.-]+)",
|
||||
"Contact ops@example.test"
|
||||
);
|
||||
|
||||
if(match["matched"].to_bool())
|
||||
{
|
||||
print(match["match"].to_string());
|
||||
print(match["named"]["user"].to_string());
|
||||
print(match["named"]["host"].to_string());
|
||||
}
|
||||
```
|
||||
|
||||
Return shape:
|
||||
|
||||
- `matched` is a boolean.
|
||||
- `pattern` stores the original pattern.
|
||||
- `flags` stores the supplied flags, or `default`.
|
||||
- `match` is the full matched text when a match exists.
|
||||
- `start` and `end` are byte offsets into the original string.
|
||||
- `captures` is a list of capture objects, with capture `0` representing the full match.
|
||||
- `named` maps named capture groups to their captured text.
|
||||
- `named_offsets` maps named capture groups to `index`, `start`, and `end` metadata.
|
||||
|
||||
Capture entries contain:
|
||||
|
||||
```text
|
||||
capture["index"]
|
||||
capture["matched"]
|
||||
capture["start"]
|
||||
capture["end"]
|
||||
capture["text"]
|
||||
```
|
||||
|
||||
If no match is found, `matched` is `false` and match-specific fields are omitted.
|
||||
@@ -0,0 +1,39 @@
|
||||
:sig
|
||||
DTree regex_search_all(String pattern, String subject)
|
||||
DTree regex_search_all(String pattern, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern
|
||||
subject : string to search
|
||||
flags : optional regex flags
|
||||
return value : a DTree containing all non-overlapping matches
|
||||
|
||||
:see
|
||||
>regex
|
||||
regex_match
|
||||
regex_search
|
||||
regex_replace
|
||||
regex_split
|
||||
0_DTree
|
||||
|
||||
:content
|
||||
Finds every non-overlapping match of `pattern` in `subject`.
|
||||
|
||||
Example:
|
||||
|
||||
```uce
|
||||
DTree tags = regex_search_all("#(?<tag>[A-Za-z0-9_]+)", "Ship #uce and #docs");
|
||||
|
||||
tags["matches"].each([](DTree match, String key) {
|
||||
print(match["named"]["tag"].to_string(), "\n");
|
||||
});
|
||||
```
|
||||
|
||||
Return shape:
|
||||
|
||||
- `matched` is `true` when at least one match exists.
|
||||
- `count` is the number of matches.
|
||||
- `matches` is a list of entries with the same shape returned by `regex_search()`.
|
||||
- `pattern` and `flags` mirror the call inputs.
|
||||
|
||||
Zero-length matches are handled safely; the scanner advances after each zero-length match to avoid infinite loops.
|
||||
@@ -0,0 +1,38 @@
|
||||
:sig
|
||||
StringList regex_split(String pattern, String subject)
|
||||
StringList regex_split(String pattern, String subject, String flags)
|
||||
|
||||
:params
|
||||
pattern : PCRE2 regular expression pattern used as the separator
|
||||
subject : string to split
|
||||
flags : optional regex flags
|
||||
return value : a list of string parts
|
||||
|
||||
:see
|
||||
>regex
|
||||
regex_match
|
||||
regex_search
|
||||
regex_search_all
|
||||
regex_replace
|
||||
split
|
||||
join
|
||||
StringList
|
||||
|
||||
:content
|
||||
Splits `subject` wherever `pattern` matches.
|
||||
|
||||
Example:
|
||||
|
||||
```uce
|
||||
StringList tags = regex_split("\\s*,\\s*", "uce, components, markdown");
|
||||
print(join(tags, "\n"));
|
||||
```
|
||||
|
||||
This is the pattern-aware companion to `split()`.
|
||||
|
||||
Behavior notes:
|
||||
|
||||
- Separators are removed from the returned list.
|
||||
- Empty fields are preserved.
|
||||
- If the pattern does not match, the result contains the original subject as a single item.
|
||||
- Zero-length separators are handled safely to avoid infinite loops.
|
||||
@@ -6,7 +6,7 @@ code : HTTP status code
|
||||
reason : optional reason phrase override
|
||||
|
||||
:see
|
||||
0_context
|
||||
0_Request
|
||||
>types
|
||||
|
||||
:content
|
||||
|
||||
@@ -8,6 +8,9 @@ return value : 0 if signal was sent, -1 otherwise
|
||||
|
||||
:see
|
||||
>task
|
||||
task
|
||||
task_pid
|
||||
task_repeat
|
||||
|
||||
:content
|
||||
Wraps the standard POSIX `kill()` function.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
:sig
|
||||
bool DTree::to_bool()
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
json_decode
|
||||
to_f64
|
||||
to_u64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a boolean.
|
||||
|
||||
String values such as `true`, `yes`, `on`, and `1` read as true. Values such as `false`, `no`, `off`, `0`, and `null` read as false.
|
||||
|
||||
Numeric values read as true when non-zero. Empty strings read as false.
|
||||
|
||||
Use this when consuming request data, JSON-decoded values, config trees, or component props where the original input may be string-shaped.
|
||||
@@ -0,0 +1,17 @@
|
||||
:sig
|
||||
f64 DTree::to_f64()
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
json_decode
|
||||
float_val
|
||||
to_bool
|
||||
to_u64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a floating-point number.
|
||||
|
||||
String values are parsed using the same permissive conversion rules used by the runtime's scalar helpers. Boolean values become `1.0` or `0.0`.
|
||||
|
||||
Use this for numeric config values, JSON-decoded fields, component props, and request data that should be treated as a number.
|
||||
@@ -0,0 +1,17 @@
|
||||
:sig
|
||||
u64 DTree::to_u64()
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
json_decode
|
||||
int_val
|
||||
to_bool
|
||||
to_f64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as an unsigned integer.
|
||||
|
||||
String values are parsed numerically. Boolean values become `1` or `0`. Negative values clamp to `0`.
|
||||
|
||||
Use this for counts, identifiers, limits, timestamps, and other non-negative numeric values stored inside a `DTree`.
|
||||
@@ -9,6 +9,11 @@ return value : DTree* returned from function
|
||||
|
||||
:see
|
||||
>ob
|
||||
unit_load
|
||||
unit_render
|
||||
unit_info
|
||||
1_RENDER
|
||||
1_COMPONENT
|
||||
|
||||
:content
|
||||
Calls an exported function inside another UCE file.
|
||||
@@ -17,6 +22,19 @@ Use `unit_call()` when you need structured data exchange between units rather th
|
||||
|
||||
The callee must expose an `EXPORT` function whose name matches `function_name`. Arguments are passed through `call_param`, and the return value is a `DTree*` owned by the callee.
|
||||
|
||||
`unit_call()` also understands the request-bound UCE entrypoint names:
|
||||
|
||||
- `RENDER`
|
||||
- `RENDER:NAME`
|
||||
- `COMPONENT`
|
||||
- `COMPONENT:NAME`
|
||||
- `ONCE`
|
||||
- `INIT`
|
||||
|
||||
When `function_name` matches one of those macro-style entrypoints, `unit_call()` does not look for a plain `EXPORT DTree* ...` function. Instead, it translates the name to the generated C++ symbol, uses the current `Request` context, and passes `call_param` into `context.props`, matching the normal component invocation model.
|
||||
|
||||
For `RENDER...` and `COMPONENT...`, the unit's `ONCE(Request& context)` hook is still honored automatically before the selected handler runs.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
@@ -31,6 +49,25 @@ EXPORT DTree* test_func(DTree* call_param)
|
||||
unit_call("call_file_funcs.uce", "test_func");
|
||||
```
|
||||
|
||||
Calling a named component handler through `unit_call()`:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
props["title"] = "Diagnostics";
|
||||
props["body"] = "Ready";
|
||||
|
||||
unit_call("components/card.uce", "COMPONENT:BODY", &props);
|
||||
```
|
||||
|
||||
Calling a page render handler through `unit_call()`:
|
||||
|
||||
```cpp
|
||||
DTree props;
|
||||
props["section"] = "summary";
|
||||
|
||||
unit_call("reports/summary.uce", "RENDER", &props);
|
||||
```
|
||||
|
||||
Related:
|
||||
|
||||
- PHP: `include`, `require`, or calling a function from an included module, especially when returning arrays or objects instead of rendering a view
|
||||
|
||||
@@ -6,8 +6,10 @@ path : optional UCE unit path. If empty, recompiles the current executing unit.
|
||||
return value : `true` when the unit was compiled and loaded successfully
|
||||
|
||||
:see
|
||||
>runtime
|
||||
unit_info
|
||||
units_list
|
||||
unit_load
|
||||
|
||||
:content
|
||||
Triggers a manual recompile of a UCE compilation unit.
|
||||
|
||||
@@ -6,9 +6,11 @@ path : optional UCE unit path. If empty, uses the current executing unit.
|
||||
return value : metadata tree for the resolved unit, or an empty tree if the unit cannot be resolved
|
||||
|
||||
:see
|
||||
>runtime
|
||||
units_list
|
||||
unit_compile
|
||||
0_context
|
||||
0_Request
|
||||
unit_load
|
||||
|
||||
:content
|
||||
Returns runtime metadata for a UCE compilation unit.
|
||||
|
||||
@@ -6,9 +6,11 @@ file_name : UCE file to load
|
||||
return value : loaded shared unit, or `null` if the unit could not be loaded
|
||||
|
||||
:see
|
||||
>runtime
|
||||
unit_render
|
||||
unit_call
|
||||
load
|
||||
unit_info
|
||||
|
||||
:content
|
||||
Loads a UCE compilation unit and returns its in-memory `SharedUnit` record.
|
||||
|
||||
@@ -8,6 +8,10 @@ context : optional request context to pass into the target page
|
||||
|
||||
:see
|
||||
>ob
|
||||
unit_call
|
||||
unit_load
|
||||
1_RENDER
|
||||
component
|
||||
|
||||
:content
|
||||
Calls another UCE file and executes its `RENDER(Request& context)` function.
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
std::vector<String> units_list()
|
||||
|
||||
:see
|
||||
>runtime
|
||||
unit_info
|
||||
unit_compile
|
||||
unit_load
|
||||
|
||||
:content
|
||||
Returns the normalized paths of all known `.uce` units.
|
||||
|
||||
@@ -7,6 +7,13 @@ String var_dump(DTree t, String prefix = "", String postfix = "\n")
|
||||
t : object to be dumped into a string
|
||||
return value : string containing a human-friendly representation of 't'
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
StringMap
|
||||
json_encode
|
||||
print
|
||||
|
||||
:content
|
||||
Returns a string representation of `t` intended for debugging.
|
||||
|
||||
|
||||
+1
-102
@@ -1,108 +1,7 @@
|
||||
struct DocPage {
|
||||
String title;
|
||||
String content;
|
||||
StringList sig_lines;
|
||||
StringList param_lines;
|
||||
StringList see_lines;
|
||||
};
|
||||
#include "lib/doc_page.h"
|
||||
|
||||
StringMap* already_shown_items;
|
||||
|
||||
String doc_default_title(String page)
|
||||
{
|
||||
String page_title = page;
|
||||
if(page_title.length() > 1 && page_title[1] == '_')
|
||||
nibble(page_title, "_");
|
||||
return(page_title);
|
||||
}
|
||||
|
||||
String doc_markdown_inline(String text)
|
||||
{
|
||||
text = trim(text);
|
||||
if(text == "")
|
||||
return("");
|
||||
String html = markdown_to_html(text);
|
||||
if(html.length() >= 7 && html.substr(0, 3) == "<p>" && html.substr(html.length() - 4) == "</p>")
|
||||
return(html.substr(3, html.length() - 7));
|
||||
return(html);
|
||||
}
|
||||
|
||||
String doc_legacy_heading(String section)
|
||||
{
|
||||
if(section == "desc")
|
||||
return("");
|
||||
if(section == "related")
|
||||
return("## PHP & JS Equivalents");
|
||||
return("## " + section);
|
||||
}
|
||||
|
||||
DocPage load_doc_page(String page)
|
||||
{
|
||||
DocPage result;
|
||||
StringList lines = split(file_get_contents("pages/" + page + ".txt"), "\n");
|
||||
String current_section = "";
|
||||
bool content_mode = false;
|
||||
StringList content_lines;
|
||||
|
||||
for(auto line : lines)
|
||||
{
|
||||
if(!content_mode && line != "" && line.substr(0, 1) == ":")
|
||||
{
|
||||
String section = trim(line.substr(1));
|
||||
if(section == "title" || section == "sig" || section == "params" || section == "see")
|
||||
{
|
||||
current_section = section;
|
||||
continue;
|
||||
}
|
||||
if(section == "content")
|
||||
{
|
||||
content_mode = true;
|
||||
current_section = "content";
|
||||
continue;
|
||||
}
|
||||
|
||||
current_section = "legacy";
|
||||
String heading = doc_legacy_heading(section);
|
||||
if(heading != "")
|
||||
{
|
||||
if(content_lines.size() > 0 && content_lines.back() != "")
|
||||
content_lines.push_back("");
|
||||
content_lines.push_back(heading);
|
||||
content_lines.push_back("");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(current_section == "title")
|
||||
{
|
||||
if(result.title != "")
|
||||
result.title += "\n";
|
||||
result.title += line;
|
||||
}
|
||||
else if(current_section == "sig")
|
||||
{
|
||||
result.sig_lines.push_back(line);
|
||||
}
|
||||
else if(current_section == "params")
|
||||
{
|
||||
result.param_lines.push_back(line);
|
||||
}
|
||||
else if(current_section == "see")
|
||||
{
|
||||
if(trim(line) != "")
|
||||
result.see_lines.push_back(trim(line));
|
||||
}
|
||||
else
|
||||
{
|
||||
content_lines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
result.content = join(content_lines, "\n");
|
||||
result.title = trim(result.title);
|
||||
return(result);
|
||||
}
|
||||
|
||||
void render_doc_params(StringList param_lines)
|
||||
{
|
||||
if(param_lines.size() == 0)
|
||||
|
||||
Reference in New Issue
Block a user