some cleanup

This commit is contained in:
udo
2026-04-28 12:10:07 +00:00
parent 223cf4c6e1
commit cd445f3c9b
224 changed files with 1558 additions and 38907 deletions
+21 -612
View File
@@ -29,609 +29,16 @@ void render_link_card(String href, String title, String body, String meta)
</a></>
}
bool code_is_space(char ch)
void render_code_example(Request& context, String eyebrow, String title, String summary, String code, String note, String language = "uce")
{
return(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
}
bool code_is_digit(char ch)
{
return(ch >= '0' && ch <= '9');
}
bool code_is_alpha(char ch)
{
return((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));
}
bool code_is_ident_start(char ch)
{
return(code_is_alpha(ch) || ch == '_');
}
bool code_is_ident_char(char ch)
{
return(code_is_ident_start(ch) || code_is_digit(ch));
}
bool markup_name_char(char ch)
{
return(code_is_ident_char(ch) || ch == '-' || ch == ':' || ch == '@');
}
bool code_is_punct(char ch)
{
return(ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == '(' || ch == ')' || ch == ',' || ch == ';');
}
bool code_is_operator(char ch)
{
return(ch == '=' || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' || ch == '!' || ch == '<' || ch == '>' || ch == '&' || ch == '|' || ch == '.' || ch == ':' || ch == '?' || ch == '^');
}
bool code_has_prefix(String src, u64 i, String prefix)
{
if(i + prefix.length() > src.length())
return(false);
return(substr(src, i, prefix.length()) == prefix);
}
String code_wrap(String cls, String text)
{
if(text == "")
return("");
return("<span class=\"tok tok-" + cls + "\">" + html_escape(text) + "</span>");
}
String uce_markup_open_token()
{
return("<" ">");
}
String uce_markup_close_token()
{
return("</" ">");
}
String uce_code_open_token()
{
return("<" "?");
}
String uce_code_expr_token()
{
return("<" "?=");
}
String uce_code_raw_token()
{
return("<" "?:");
}
String uce_code_close_token()
{
return("?" ">");
}
bool cpp_keyword(String word)
{
String lower = to_lower(word);
return(
word == "RENDER" ||
word == "COMPONENT" ||
word == "WS" ||
lower == "if" ||
lower == "else" ||
lower == "for" ||
lower == "while" ||
lower == "return" ||
lower == "const" ||
lower == "auto" ||
lower == "void" ||
lower == "bool" ||
lower == "true" ||
lower == "false"
);
}
bool cpp_type(String word)
{
return(
word == "String" ||
word == "DTree" ||
word == "Request" ||
word == "StringList" ||
word == "StringMap" ||
word == "u64" ||
word == "u32" ||
word == "s64" ||
word == "f64"
);
}
bool cpp_builtin_call(String word)
{
return(
word == "component" ||
word == "component_render" ||
word == "component_exists" ||
word == "component_resolve" ||
word == "markdown_to_ast" ||
word == "markdown_to_html" ||
word == "json_decode" ||
word == "json_encode" ||
word == "time_format_utc" ||
word == "first" ||
word == "contains" ||
word == "ws_send" ||
word == "ws_send_to" ||
word == "ws_message" ||
word == "ws_connection_id"
);
}
bool cpp_context_field(String word)
{
return(
word == "get" ||
word == "post" ||
word == "session" ||
word == "call" ||
word == "connection" ||
word == "params" ||
word == "cookies" ||
word == "header" ||
word == "cfg"
);
}
bool nginx_keyword(String word)
{
return(
word == "location" ||
word == "include" ||
word == "fastcgi_param" ||
word == "fastcgi_pass" ||
word == "proxy_pass" ||
word == "proxy_set_header" ||
word == "proxy_http_version" ||
word == "error_page" ||
word == "if" ||
word == "return" ||
word == "root" ||
word == "index" ||
word == "server" ||
word == "listen" ||
word == "server_name" ||
word == "map"
);
}
String highlight_cpp_fragment(String code)
{
String out;
for(u64 i = 0; i < code.length(); )
{
if(code_has_prefix(code, i, "//"))
{
u64 start = i;
while(i < code.length() && code[i] != '\n')
i += 1;
out += code_wrap("comment", substr(code, start, i - start));
continue;
}
if(code_has_prefix(code, i, "/*"))
{
u64 start = i;
i += 2;
while(i < code.length() && !code_has_prefix(code, i, "*/"))
i += 1;
if(code_has_prefix(code, i, "*/"))
i += 2;
out += code_wrap("comment", substr(code, start, i - start));
continue;
}
char ch = code[i];
if(ch == '"' || ch == '\'' || ch == '`')
{
char quote = ch;
u64 start = i;
i += 1;
while(i < code.length())
{
if(code[i] == '\\' && i + 1 < code.length())
{
i += 2;
continue;
}
if(code[i] == quote)
{
i += 1;
break;
}
i += 1;
}
out += code_wrap("string", substr(code, start, i - start));
continue;
}
if(code_is_space(ch))
{
out += substr(code, i, 1);
i += 1;
continue;
}
if(code_is_digit(ch))
{
u64 start = i;
i += 1;
while(i < code.length())
{
char next = code[i];
if(code_is_digit(next) || next == '.' || next == 'x' || next == 'X' || next == '_' || (next >= 'a' && next <= 'f') || (next >= 'A' && next <= 'F'))
i += 1;
else
break;
}
out += code_wrap("number", substr(code, start, i - start));
continue;
}
if(code_is_ident_start(ch))
{
u64 start = i;
i += 1;
while(i < code.length() && code_is_ident_char(code[i]))
i += 1;
String word = substr(code, start, i - start);
u64 j = i;
while(j < code.length() && (code[j] == ' ' || code[j] == '\t'))
j += 1;
if(cpp_keyword(word))
out += code_wrap("keyword", word);
else if(cpp_type(word))
out += code_wrap("type", word);
else if(cpp_context_field(word))
out += code_wrap("field", word);
else if(word == "context" || word == "props" || word == "payload" || word == "ws")
out += code_wrap("variable", word);
else if(cpp_builtin_call(word))
out += code_wrap("builtin", word);
else if(j < code.length() && code[j] == '(')
out += code_wrap("call", word);
else
out += code_wrap("ident", word);
continue;
}
if(code_is_punct(ch))
{
out += code_wrap("punct", substr(code, i, 1));
i += 1;
continue;
}
if(code_is_operator(ch))
{
out += code_wrap("operator", substr(code, i, 1));
i += 1;
continue;
}
out += html_escape(substr(code, i, 1));
i += 1;
}
return(out);
}
String highlight_markup_tag(String tag)
{
String out;
u64 i = 0;
if(code_has_prefix(tag, i, "</"))
{
out += code_wrap("delimiter", "</");
i += 2;
}
else
{
out += code_wrap("delimiter", "<");
i += 1;
}
u64 name_start = i;
while(i < tag.length() && markup_name_char(tag[i]))
i += 1;
if(i > name_start)
out += code_wrap("markup-tag", substr(tag, name_start, i - name_start));
while(i < tag.length())
{
if(code_is_space(tag[i]))
{
out += substr(tag, i, 1);
i += 1;
continue;
}
if(code_has_prefix(tag, i, "/>"))
{
out += code_wrap("delimiter", "/>" );
i += 2;
break;
}
if(code_has_prefix(tag, i, ">"))
{
out += code_wrap("delimiter", ">" );
i += 1;
break;
}
u64 attr_start = i;
while(i < tag.length() && markup_name_char(tag[i]))
i += 1;
if(i > attr_start)
out += code_wrap("markup-attr", substr(tag, attr_start, i - attr_start));
while(i < tag.length() && code_is_space(tag[i]))
{
out += substr(tag, i, 1);
i += 1;
}
if(i < tag.length() && tag[i] == '=')
{
out += code_wrap("operator", "=");
i += 1;
while(i < tag.length() && code_is_space(tag[i]))
{
out += substr(tag, i, 1);
i += 1;
}
if(i < tag.length() && (tag[i] == '"' || tag[i] == '\''))
{
char quote = tag[i];
u64 value_start = i;
i += 1;
while(i < tag.length() && tag[i] != quote)
i += 1;
if(i < tag.length())
i += 1;
out += code_wrap("string", substr(tag, value_start, i - value_start));
}
else
{
u64 value_start = i;
while(i < tag.length() && !code_is_space(tag[i]) && tag[i] != '>')
i += 1;
out += code_wrap("string", substr(tag, value_start, i - value_start));
}
}
}
return(out);
}
String highlight_uce_code(String code)
{
String out;
bool in_markup = false;
String markup_open = uce_markup_open_token();
String markup_close = uce_markup_close_token();
String code_open = uce_code_open_token();
String code_expr = uce_code_expr_token();
String code_raw = uce_code_raw_token();
String code_close = uce_code_close_token();
for(u64 i = 0; i < code.length(); )
{
if(!in_markup)
{
s64 next_markup = strpos(code, markup_open, i);
if(next_markup < 0)
{
out += highlight_cpp_fragment(substr(code, i));
break;
}
if((u64)next_markup > i)
out += highlight_cpp_fragment(substr(code, i, (u64)next_markup - i));
out += code_wrap("delimiter", markup_open);
i = (u64)next_markup + 2;
in_markup = true;
continue;
}
if(code_has_prefix(code, i, markup_close))
{
out += code_wrap("delimiter", markup_close);
i += 3;
in_markup = false;
continue;
}
if(code_has_prefix(code, i, code_expr) || code_has_prefix(code, i, code_raw) || code_has_prefix(code, i, code_open))
{
String opener = code_has_prefix(code, i, code_expr) ? code_expr : (code_has_prefix(code, i, code_raw) ? code_raw : code_open);
out += code_wrap("delimiter", opener);
i += opener.length();
u64 island_start = i;
while(i < code.length() && !code_has_prefix(code, i, code_close))
i += 1;
out += highlight_cpp_fragment(substr(code, island_start, i - island_start));
if(code_has_prefix(code, i, code_close))
{
out += code_wrap("delimiter", code_close);
i += 2;
}
continue;
}
if(code_has_prefix(code, i, "<"))
{
u64 tag_start = i;
char quote = 0;
i += 1;
while(i < code.length())
{
if(quote != 0)
{
if(code[i] == quote)
quote = 0;
i += 1;
continue;
}
if(code[i] == '"' || code[i] == '\'')
{
quote = code[i];
i += 1;
continue;
}
if(code[i] == '>')
{
i += 1;
break;
}
i += 1;
}
out += highlight_markup_tag(substr(code, tag_start, i - tag_start));
continue;
}
u64 text_start = i;
while(i < code.length() && code[i] != '<')
i += 1;
out += code_wrap("markup-text", substr(code, text_start, i - text_start));
}
return(out);
}
String highlight_nginx_code(String code)
{
String out;
for(u64 i = 0; i < code.length(); )
{
if(code[i] == '#')
{
u64 start = i;
while(i < code.length() && code[i] != '\n')
i += 1;
out += code_wrap("comment", substr(code, start, i - start));
continue;
}
if(code[i] == '"' || code[i] == '\'')
{
char quote = code[i];
u64 start = i;
i += 1;
while(i < code.length() && code[i] != quote)
i += 1;
if(i < code.length())
i += 1;
out += code_wrap("string", substr(code, start, i - start));
continue;
}
if(code_is_space(code[i]))
{
out += substr(code, i, 1);
i += 1;
continue;
}
if(code[i] == '$' || code[i] == '@')
{
u64 start = i;
i += 1;
while(i < code.length() && markup_name_char(code[i]))
i += 1;
out += code_wrap("variable", substr(code, start, i - start));
continue;
}
if(code_is_digit(code[i]))
{
u64 start = i;
i += 1;
while(i < code.length() && (code_is_digit(code[i]) || code[i] == '.'))
i += 1;
out += code_wrap("number", substr(code, start, i - start));
continue;
}
if(markup_name_char(code[i]))
{
u64 start = i;
i += 1;
while(i < code.length() && markup_name_char(code[i]))
i += 1;
String word = substr(code, start, i - start);
if(nginx_keyword(word))
out += code_wrap("keyword", word);
else
out += code_wrap("ident", word);
continue;
}
if(code_is_punct(code[i]))
{
out += code_wrap("punct", substr(code, i, 1));
i += 1;
continue;
}
if(code_is_operator(code[i]))
{
out += code_wrap("operator", substr(code, i, 1));
i += 1;
continue;
}
out += html_escape(substr(code, i, 1));
i += 1;
}
return(out);
}
String highlight_code(String code)
{
if(contains(code, "fastcgi_pass") || contains(code, "proxy_pass"))
return(highlight_nginx_code(code));
return(highlight_uce_code(code));
}
String highlight_code(String code, String language)
{
if(language == "nginx")
return(highlight_nginx_code(code));
if(language == "uce")
return(highlight_uce_code(code));
return(highlight_code(code));
}
String code_language_label(String language)
{
if(language == "nginx")
return("nginx");
return("uce");
}
void render_code_example(String eyebrow, String title, String summary, String code, String note, String language = "uce")
{
<><article class="code-card">
<div class="eyebrow"><?= eyebrow ?></div>
<h3><?= title ?></h3>
<p><?= summary ?></p>
<div class="code-toolbar">
<span class="code-lang"><?= code_language_label(language) ?></span>
</div>
<pre class="syntax-block"><code class="syntax-code"><?: highlight_code(code, language) ?></code></pre>
<? if(note != "") { ?><div class="code-note"><?= note ?></div><? } ?>
</article></>
DTree props;
props["eyebrow"] = eyebrow;
props["title"] = title;
props["summary"] = summary;
props["code"] = code;
props["note"] = note;
props["language"] = language;
<><?: component("components/code_example", props, context) ?></>
}
RENDER(Request& context)
@@ -670,8 +77,8 @@ RENDER(Request& context)
"{\n"
"\t<>\n"
"\t\t<article class=\"card\">\n"
"\t\t\t<h3><?= context.call[\"title\"] ?></h3>\n"
"\t\t\t<p><?= context.call[\"body\"] ?></p>\n"
"\t\t\t<h3><?= context.props[\"title\"] ?></h3>\n"
"\t\t\t<p><?= context.props[\"body\"] ?></p>\n"
"\t\t</article>\n"
"\t</>\n"
"}\n\n"
@@ -708,8 +115,10 @@ RENDER(Request& context)
"}\n\n"
"WS(Request& context)\n"
"{\n"
"\tDTree payload = json_decode(ws_message());\n"
"\tDTree payload = json_decode(context.in);\n"
"\tString connection_id = context.params[\"WS_CONNECTION_ID\"];\n"
"\tcontext.connection[\"name\"] = payload[\"name\"];\n"
"\tpayload[\"connection_id\"] = connection_id;\n"
"\tws_send(json_encode(payload));\n"
"}\n";
@@ -802,7 +211,7 @@ RENDER(Request& context)
</div>
<div class="feature-grid">
<? render_feature_card("templating", "Inline markup without framework tax", "UCE templates live directly inside handler code with escaped and raw output modes, so rendering stays local to the request logic that decides it."); ?>
<? render_feature_card("composition", "Components and sub-rendering", "Components are just .uce files. Props flow through context.call. Full pages can call other units without inventing a second application runtime."); ?>
<? render_feature_card("composition", "Components and sub-rendering", "Components are just .uce files. Props flow through context.props. Full pages can call other units without inventing a second application runtime."); ?>
<? render_feature_card("transport", "FastCGI for pages, HTTP for sockets", "Normal page renders stay on the FastCGI socket. Real WebSocket upgrades on .ws.uce endpoints are proxied to the built-in HTTP listener."); ?>
<? render_feature_card("operations", "nginx in front, systemd behind", "Static files stay static, nginx does the edge work, and the runtime handles compilation, request execution, and socket fan-out."); ?>
</div>
@@ -839,12 +248,12 @@ RENDER(Request& context)
</p>
</div>
<div class="code-grid">
<? render_code_example("templating", "A page with request data", "Drop from C++ into markup, escape output by default, and keep request handling next to the HTML it controls.", snippet_minimal, "See the live demos for forms, headers, sessions, JSON, markdown, and more."); ?>
<? render_code_example("forms", "POST + session flash", "Old-school dynamic websites still matter. UCE keeps form handling and page rendering in one file when that is the simplest thing.", snippet_forms, "Request data lives on context.get, context.post, context.cookies, and context.session."); ?>
<? render_code_example("components", "Reusable UI without a second framework", "Components are ordinary .uce files. Pass props through context.call and decide when to render or return markup.", snippet_components, "Named handlers and component_render() are also available for direct output flows."); ?>
<? render_code_example("content", "Markdown when you want it", "UCE also ships a markdown module, so content-heavy pages do not need an external rendering stack.", snippet_markdown, "The runtime supports markdown_to_ast() and markdown_to_html() for content pipelines and component hooks."); ?>
<? render_code_example("realtime", "WebSockets with broker-owned state", "A .ws.uce page can render HTML and also accept live socket messages. Connection-local state lives on context.connection.", snippet_websocket, "The published demo includes a full chat example with reconnect logic and per-connection metadata."); ?>
<? render_code_example("deploy", "nginx wiring", "Use FastCGI for ordinary requests and only send actual websocket upgrades to the runtime's built-in HTTP listener.", snippet_nginx, "Match .ws.uce before the generic .uce location so normal page loads and upgrades split correctly.", "nginx"); ?>
<? render_code_example(context, "templating", "A page with request data", "Drop from C++ into markup, escape output by default, and keep request handling next to the HTML it controls.", snippet_minimal, "See the live demos for forms, headers, sessions, JSON, markdown, and more."); ?>
<? render_code_example(context, "forms", "POST + session flash", "Old-school dynamic websites still matter. UCE keeps form handling and page rendering in one file when that is the simplest thing.", snippet_forms, "Request data lives on context.get, context.post, context.cookies, and context.session."); ?>
<? render_code_example(context, "components", "Reusable UI without a second framework", "Components are ordinary .uce files. Pass props through context.props and decide when to render or return markup.", snippet_components, "Named handlers and component_render() are also available for direct output flows."); ?>
<? render_code_example(context, "content", "Markdown when you want it", "UCE also ships a markdown module, so content-heavy pages do not need an external rendering stack.", snippet_markdown, "The runtime supports markdown_to_ast() and markdown_to_html() for content pipelines and component hooks."); ?>
<? render_code_example(context, "realtime", "WebSockets with broker-owned state", "A .ws.uce page can render HTML and also accept live socket messages. Connection-local state lives on context.connection.", snippet_websocket, "The published demo includes a full chat example with reconnect logic and per-connection metadata."); ?>
<? render_code_example(context, "deploy", "nginx wiring", "Use FastCGI for ordinary requests and only send actual websocket upgrades to the runtime's built-in HTTP listener.", snippet_nginx, "Match .ws.uce before the generic .uce location so normal page loads and upgrades split correctly.", "nginx"); ?>
</div>
</section>