working on documentation and more API functions

This commit is contained in:
udo
2026-04-29 12:09:37 +00:00
parent cd445f3c9b
commit 9f7625c7fd
94 changed files with 1896 additions and 458 deletions
+56 -13
View File
@@ -20,6 +20,50 @@ bool compiler_code_state_is_neutral(const CompilerCodeState& state)
return(!state.inside_quote && !state.inside_line_comment && !state.inside_block_comment);
}
String compiler_cpp_raw_string_delimiter(const String& content)
{
StringList candidates = {
"",
"UCE",
"UCE_LITERAL",
"uce_literal_0",
"uce_literal_1"
};
u64 hash = 1469598103934665603ULL;
for(unsigned char c : content)
{
hash ^= c;
hash *= 1099511628211ULL;
}
for(u32 i = 0; i < 64; i += 1)
{
String suffix = to_hex<u64>(hash ^ ((u64)i * 0x9E3779B97F4A7C15ULL), 12);
candidates.push_back("UCE" + suffix);
}
for(auto& delimiter : candidates)
{
String terminator = ")" + delimiter + "\"";
if(content.find(terminator) == String::npos)
return(delimiter);
}
return("");
}
String compiler_cpp_string_literal(const String& content)
{
String delimiter = compiler_cpp_raw_string_delimiter(content);
if(delimiter != "" || content.find(")\"") == String::npos)
return("R\"" + delimiter + "(" + content + ")" + delimiter + "\"");
// This fallback is only reachable for deliberately adversarial content that
// contains every generated raw-string delimiter candidate.
return(json_escape(content));
}
void compiler_code_state_consume(CompilerCodeState& state, String& buffer, const String& content, u32& i)
{
char c = content[i];
@@ -172,16 +216,15 @@ void compiler_append_text_literal_output(String& parsed_content, String& literal
{
if(literal_buffer == "")
return;
parsed_content.append("print(R\"(" + literal_buffer + ")\");");
parsed_content.append("print(" + compiler_cpp_string_literal(literal_buffer) + ");");
literal_buffer.clear();
}
String compiler_process_text_literal(Request* context, SharedUnit* su, String content)
{
String parsed_content;
String html_output_start = "print(R\"(";
String html_output_end = ")\");";
String code_buffer = "";
String literal_buffer = "";
CompilerCodeState code_state;
bool inside_code = false;
bool is_field = false;
@@ -200,6 +243,7 @@ String compiler_process_text_literal(Request* context, SharedUnit* su, String co
inside_code = true;
code_buffer = "";
code_state = CompilerCodeState();
compiler_append_text_literal_output(parsed_content, literal_buffer);
if(c2 == '=')
{
is_field = true;
@@ -221,7 +265,7 @@ String compiler_process_text_literal(Request* context, SharedUnit* su, String co
continue;
}
parsed_content.append(1, c);
literal_buffer.append(1, c);
continue;
}
@@ -234,27 +278,23 @@ String compiler_process_text_literal(Request* context, SharedUnit* su, String co
if(escape_field)
{
parsed_content.append(
html_output_end +
"print(html_escape( " +
code_buffer +
" )); " +
html_output_start
" )); "
);
}
else
{
parsed_content.append(
html_output_end +
"print( " +
code_buffer +
" ); " +
html_output_start
" ); "
);
}
}
else
{
parsed_content.append(html_output_end + code_buffer + html_output_start);
parsed_content.append(code_buffer);
}
continue;
}
@@ -269,7 +309,10 @@ String compiler_process_text_literal(Request* context, SharedUnit* su, String co
compiler_code_state_consume(code_state, code_buffer, content, i);
}
return(html_output_start + parsed_content + html_output_end);
if(literal_buffer != "")
compiler_append_text_literal_output(parsed_content, literal_buffer);
return(parsed_content);
}
String compiler_rewrite_named_render_syntax(String content)
@@ -444,4 +487,4 @@ String compiler_preprocess_source(Request* context, SharedUnit* su, String conte
{
content = compiler_rewrite_named_render_syntax(content);
return(compiler_preprocess_shared_unit_char_wise(context, su, content));
}
}
+338 -111
View File
@@ -13,6 +13,8 @@ const char* UCE_SETUP_SYMBOL = "__uce_set_current_request";
const char* UCE_RENDER_SYMBOL = "__uce_render";
const char* UCE_COMPONENT_SYMBOL = "__uce_component";
const char* UCE_WEBSOCKET_SYMBOL = "__uce_websocket";
const char* UCE_ONCE_SYMBOL = "__uce_once";
const char* UCE_INIT_SYMBOL = "__uce_init";
const u64 UCE_UNIT_ABI_VERSION = 1;
struct SharedUnitFilesystemState
@@ -48,6 +50,10 @@ struct SharedUnitCompileCheck
bool needs_compile = false;
};
void compiler_unload_failed_shared_unit(SharedUnit* su);
bool compiler_run_unit_init(Request* context, SharedUnit* su, String* error_out = 0);
bool compiler_run_unit_once_if_needed(Request* context, SharedUnit* su, String* error_out = 0);
bool compiler_config_truthy(String raw, bool default_value)
{
raw = to_lower(trim(raw));
@@ -67,19 +73,6 @@ bool compiler_jit_compile_on_request_enabled(Request* context)
return(compiler_config_truthy(context->server->config["JIT_COMPILE_ON_REQUEST"], true));
}
u64 compiler_failure_retry_seconds(Request* context)
{
if(!context || !context->server)
return(10);
auto raw = trim(context->server->config["COMPILE_FAILURE_RETRY_SECONDS"]);
if(raw == "")
return(10);
auto value = int_val(raw);
if(value < 0)
return(0);
return((u64)value);
}
bool compiler_is_u64_string(String value)
{
value = trim(value);
@@ -167,16 +160,16 @@ bool compiler_failure_retry_deferred(Request* context, SharedUnit* su, const Sha
{
if(!context || !su)
return(false);
auto retry_seconds = compiler_failure_retry_seconds(context);
if(retry_seconds == 0)
return(false);
if(!state.compile_output_exists || state.compile_output_time == 0)
return(false);
if(state.source_time == 0)
return(false);
if(state.source_time > state.compile_output_time)
return(false);
return(time() < (u64)state.compile_output_time + retry_seconds);
auto required_failure_inputs_time = std::max({
state.source_time,
state.setup_template_time,
state.compiler_abi_time
});
return(state.compile_output_time >= required_failure_inputs_time);
}
String compiler_failure_output_for_state(SharedUnit* su, const SharedUnitFilesystemState& state)
@@ -653,6 +646,8 @@ void load_shared_unit(Request* context, SharedUnit* su)
su->on_render = 0;
su->on_component = 0;
su->on_websocket = 0;
su->on_once = 0;
su->on_init = 0;
su->on_setup = 0;
su->so_handle = 0;
su->compiler_messages = "";
@@ -695,17 +690,31 @@ void load_shared_unit(Request* context, SharedUnit* su)
dlerror();
su->on_websocket = (request_ref_handler)dlsym(su->so_handle, UCE_WEBSOCKET_SYMBOL);
dlerror();
su->on_once = (request_ref_handler)dlsym(su->so_handle, UCE_ONCE_SYMBOL);
dlerror();
su->on_init = (request_ref_handler)dlsym(su->so_handle, UCE_INIT_SYMBOL);
dlerror();
su->api_declarations = split(file_get_contents(su->api_file_name), "\n");
String init_error = "";
if(!compiler_run_unit_init(context, su, &init_error))
{
if(init_error != "")
su->compiler_messages = init_error;
compiler_unload_failed_shared_unit(su);
}
//else
// printf("(i) loaded unit %s\n", su->file_name.c_str());
}
else
{
const char* dl_error = dlerror();
su->compiler_messages = "could not open " + su->so_name;
if(dl_error && String(dl_error) != "")
su->compiler_messages += ": " + String(dl_error);
su->compile_status = "load_error";
su->compile_error_status = su->compiler_messages;
su->last_error = time();
printf("Error loading unit %s, could not open %s\n", su->file_name.c_str(), su->so_name.c_str());
printf("Error loading unit %s, %s\n", su->file_name.c_str(), su->compiler_messages.c_str());
}
}
@@ -1176,6 +1185,183 @@ struct UnitInvocationScope
}
};
enum class UnitCallMacroKind
{
none,
render,
component,
once,
init
};
struct UnitCallMacroTarget
{
UnitCallMacroKind kind = UnitCallMacroKind::none;
String handler_name;
};
struct RequestPropsScope
{
Request* context = 0;
DTree previous_props;
RequestPropsScope(Request* context, const DTree& props)
{
this->context = context;
if(this->context)
{
previous_props = this->context->props;
this->context->props = props;
}
}
~RequestPropsScope()
{
if(context)
context->props = previous_props;
}
};
void compiler_unload_failed_shared_unit(SharedUnit* su)
{
if(!su)
return;
if(su->so_handle)
dlclose(su->so_handle);
su->so_handle = 0;
su->api_functions.clear();
su->on_setup = 0;
su->on_render = 0;
su->on_component = 0;
su->on_websocket = 0;
su->on_once = 0;
su->on_init = 0;
}
bool compiler_run_unit_init(Request* context, SharedUnit* su, String* error_out)
{
if(!su || !su->on_init)
return(true);
if(!context)
{
if(error_out)
*error_out = "internal error: INIT() requires a Request context";
return(false);
}
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + su->file_name;
return(false);
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
try
{
su->on_init(*context);
return(true);
}
catch(...)
{
su->runtime_error_status = "uncaught exception during INIT";
su->compile_status = "load_error";
su->compile_error_status = su->runtime_error_status;
su->last_error = time();
if(error_out)
*error_out = su->runtime_error_status;
return(false);
}
}
bool compiler_run_unit_once_if_needed(Request* context, SharedUnit* su, String* error_out)
{
if(!su || !su->on_once)
return(true);
if(!context)
{
if(error_out)
*error_out = "internal error: ONCE() requires a Request context";
return(false);
}
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + su->file_name;
return(false);
}
if(context->once_units.find(su->file_name) != context->once_units.end())
return(true);
context->once_units.insert(su->file_name);
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
try
{
su->on_once(*context);
return(true);
}
catch(...)
{
context->once_units.erase(su->file_name);
su->runtime_error_status = "uncaught exception during ONCE";
su->last_error = time();
if(error_out)
*error_out = su->runtime_error_status;
throw;
}
}
String unit_call_macro_trim(String function_name)
{
function_name = trim(function_name);
if(function_name.length() >= 2 && function_name.substr(function_name.length() - 2) == "()")
function_name = trim(function_name.substr(0, function_name.length() - 2));
return(function_name);
}
UnitCallMacroTarget unit_call_macro_target(String function_name)
{
UnitCallMacroTarget target;
function_name = unit_call_macro_trim(function_name);
if(function_name == "RENDER")
{
target.kind = UnitCallMacroKind::render;
return(target);
}
if(function_name.rfind("RENDER:", 0) == 0)
{
target.kind = UnitCallMacroKind::render;
target.handler_name = trim(function_name.substr(7));
return(target);
}
if(function_name == "COMPONENT")
{
target.kind = UnitCallMacroKind::component;
return(target);
}
if(function_name.rfind("COMPONENT:", 0) == 0)
{
target.kind = UnitCallMacroKind::component;
target.handler_name = trim(function_name.substr(10));
return(target);
}
if(function_name == "ONCE")
{
target.kind = UnitCallMacroKind::once;
return(target);
}
if(function_name == "INIT")
{
target.kind = UnitCallMacroKind::init;
return(target);
}
return(target);
}
}
String component_normalize_path(String name)
@@ -1262,6 +1448,70 @@ String component_handler_symbol(String render_name)
return(String(UCE_COMPONENT_SYMBOL) + "_" + safe_name(render_name));
}
String compiler_missing_request_handler_message(UnitCallMacroKind kind, String handler_name)
{
handler_name = trim(handler_name);
if(kind == UnitCallMacroKind::render)
{
if(handler_name == "" || handler_name == "render")
return("no RENDER() entry point");
return("no RENDER:" + handler_name + "() entry point");
}
if(kind == UnitCallMacroKind::component)
{
if(handler_name == "")
return("no COMPONENT() entry point");
return("no COMPONENT:" + handler_name + "() entry point");
}
if(kind == UnitCallMacroKind::once)
return("no ONCE() entry point");
if(kind == UnitCallMacroKind::init)
return("no INIT() entry point");
return("request handler not found");
}
bool compiler_prepare_request_handler(Request* context, SharedUnit* su, String* error_out = 0, bool run_once = false)
{
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + su->file_name;
return(false);
}
if(run_once && !compiler_run_unit_once_if_needed(context, su, error_out))
return(false);
return(true);
}
void compiler_execute_request_handler(
Request* context,
SharedUnit* su,
request_ref_handler handler,
bool count_request,
String runtime_error_status
)
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
compiler_begin_render_result(su, count_request);
try
{
handler(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
runtime_error_status
);
throw;
}
}
request_ref_handler get_page_render_handler(SharedUnit* su, String render_name)
{
String symbol = page_render_handler_symbol(render_name);
@@ -1300,46 +1550,24 @@ bool compiler_invoke_render(Request* context, String file_name, String render_na
if(!su)
return(false);
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + file_name;
if(!compiler_prepare_request_handler(context, su, error_out, true))
return(false);
}
auto handler = get_page_render_handler(su, render_name);
if(!handler)
{
if(error_out)
{
if(trim(render_name) == "" || trim(render_name) == "render")
*error_out = "no RENDER() entry point";
else
*error_out = "no RENDER:" + render_name + "() entry point";
}
*error_out = compiler_missing_request_handler_message(UnitCallMacroKind::render, render_name);
return(false);
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
bool count_request = compiler_is_request_entry_unit(context, su);
compiler_begin_render_result(su, count_request);
try
{
handler(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
"uncaught exception during render"
);
throw;
}
compiler_execute_request_handler(
context,
su,
handler,
compiler_is_request_entry_unit(context, su),
"uncaught exception during render"
);
return(true);
}
@@ -1349,45 +1577,24 @@ bool compiler_invoke_component(Request* context, String file_name, String render
if(!su)
return(false);
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + file_name;
if(!compiler_prepare_request_handler(context, su, error_out, true))
return(false);
}
auto handler = get_component_handler(su, render_name);
if(!handler)
{
if(error_out)
{
if(trim(render_name) == "")
*error_out = "no COMPONENT() entry point";
else
*error_out = "no COMPONENT:" + render_name + "() entry point";
}
*error_out = compiler_missing_request_handler_message(UnitCallMacroKind::component, render_name);
return(false);
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
compiler_begin_render_result(su, false);
try
{
handler(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
"uncaught exception during component render"
);
throw;
}
compiler_execute_request_handler(
context,
su,
handler,
false,
"uncaught exception during component render"
);
return(true);
}
@@ -1421,26 +1628,13 @@ void compiler_invoke_websocket(Request* context, String file_name)
return;
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
bool count_request = compiler_is_request_entry_unit(context, su);
compiler_begin_render_result(su, count_request);
try
{
su->on_websocket(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
"uncaught exception during websocket handler"
);
throw;
}
compiler_execute_request_handler(
context,
su,
su->on_websocket,
compiler_is_request_entry_unit(context, su),
"uncaught exception during websocket handler"
);
}
void unit_render(String file_name)
@@ -1499,14 +1693,11 @@ void component_render(String name, DTree props, Request& context)
return;
}
DTree previous_props = context.props;
context.props = props;
RequestPropsScope props_scope(&context, props);
String error_message = "";
if(!compiler_invoke_component(&context, resolved_name, render_name, &error_message) && error_message != "")
print(component_error_banner(error_message));
context.props = previous_props;
}
String component(String name)
@@ -1558,16 +1749,52 @@ DTree* unit_call(String file_name, String function_name, DTree* call_param)
}
else
{
auto f = (dtree_call_handler)dlsym(su->so_handle, function_name.c_str());
if(!f)
auto macro_target = unit_call_macro_target(function_name);
if(macro_target.kind != UnitCallMacroKind::none)
{
print("Error: unit_call() function '", function_name, "' not found");
RequestPropsScope props_scope(context, (call_param ? *call_param : DTree()));
String error_message = "";
if(macro_target.kind == UnitCallMacroKind::render)
{
if(!compiler_invoke_render(context, su->file_name, macro_target.handler_name, &error_message) && error_message != "")
print("Error: unit_call() ", error_message);
}
else if(macro_target.kind == UnitCallMacroKind::component)
{
if(!compiler_invoke_component(context, su->file_name, macro_target.handler_name, &error_message) && error_message != "")
print("Error: unit_call() ", error_message);
}
else
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
request_ref_handler handler = 0;
if(macro_target.kind == UnitCallMacroKind::once)
handler = su->on_once;
else if(macro_target.kind == UnitCallMacroKind::init)
handler = su->on_init;
if(!handler)
print("Error: unit_call() ", compiler_missing_request_handler_message(macro_target.kind, macro_target.handler_name));
else
handler(*context);
}
}
else
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
result = f(call_param);
auto f = (dtree_call_handler)dlsym(su->so_handle, function_name.c_str());
if(!f)
{
print("Error: unit_call() function '", function_name, "' not found");
}
else
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
result = f(call_param);
}
}
}
}
+2
View File
@@ -2,6 +2,8 @@
#define RENDER(X) extern "C" void __uce_render(X)
#define COMPONENT(X) extern "C" void __uce_component(X)
#define ONCE(X) extern "C" void __uce_once(X)
#define INIT(X) extern "C" void __uce_init(X)
#define WS(X) extern "C" void __uce_websocket(X)
#define EXPORT extern "C"
+345
View File
@@ -1,5 +1,9 @@
#include "functionlib.h"
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include <stdexcept>
String var_dump(StringMap map, String prefix, String postfix)
{
String result = "";
@@ -151,6 +155,347 @@ String replace(String s, String search, String replace_with)
return(result);
}
namespace {
String regex_flags_label(String flags)
{
return(flags == "" ? "default" : flags);
}
void regex_throw(String function_name, String message)
{
throw std::runtime_error(function_name + "(): " + message);
}
String regex_pcre2_error(int error_code)
{
PCRE2_UCHAR buffer[256];
pcre2_get_error_message(error_code, buffer, sizeof(buffer));
return(String(reinterpret_cast<char*>(buffer)));
}
uint32_t regex_compile_options(String flags, String function_name)
{
uint32_t options = PCRE2_UTF | PCRE2_UCP;
for(char flag : flags)
{
switch(flag)
{
case('i'):
options |= PCRE2_CASELESS;
break;
case('m'):
options |= PCRE2_MULTILINE;
break;
case('s'):
options |= PCRE2_DOTALL;
break;
case('x'):
options |= PCRE2_EXTENDED;
break;
case('u'):
options |= PCRE2_UTF | PCRE2_UCP;
break;
case('a'):
options &= ~PCRE2_UTF;
options &= ~PCRE2_UCP;
break;
default:
regex_throw(function_name, "unknown regex flag '" + String(1, flag) + "'");
}
}
return(options);
}
struct RegexCode {
pcre2_code* code = 0;
RegexCode(String pattern, String flags, String function_name)
{
int error_code = 0;
PCRE2_SIZE error_offset = 0;
code = pcre2_compile(
reinterpret_cast<PCRE2_SPTR>(pattern.c_str()),
pattern.length(),
regex_compile_options(flags, function_name),
&error_code,
&error_offset,
0
);
if(!code)
regex_throw(function_name, "could not compile pattern at offset " + std::to_string((u64)error_offset) + ": " + regex_pcre2_error(error_code));
pcre2_jit_compile(code, PCRE2_JIT_COMPLETE);
}
~RegexCode()
{
if(code)
pcre2_code_free(code);
}
};
struct RegexMatchData {
pcre2_match_data* data = 0;
RegexMatchData(pcre2_code* code)
{
data = pcre2_match_data_create_from_pattern(code, 0);
if(!data)
regex_throw("regex", "could not allocate match data");
}
~RegexMatchData()
{
if(data)
pcre2_match_data_free(data);
}
};
String regex_subject_slice(String subject, PCRE2_SIZE start, PCRE2_SIZE end)
{
if(start == PCRE2_UNSET || end == PCRE2_UNSET || end < start || start > subject.length())
return("");
if(end > subject.length())
end = subject.length();
return(subject.substr(start, end - start));
}
size_t regex_next_utf8_offset(String subject, size_t offset)
{
if(offset >= subject.length())
return(subject.length() + 1);
unsigned char c = (unsigned char)subject[offset];
size_t step = 1;
if((c & 0x80) == 0)
step = 1;
else if((c & 0xE0) == 0xC0)
step = 2;
else if((c & 0xF0) == 0xE0)
step = 3;
else if((c & 0xF8) == 0xF0)
step = 4;
if(offset + step > subject.length())
step = 1;
return(offset + step);
}
void regex_add_named_captures(DTree& result, pcre2_code* code, String subject, PCRE2_SIZE* ovector, int rc)
{
uint32_t name_count = 0;
uint32_t entry_size = 0;
PCRE2_SPTR name_table = 0;
pcre2_pattern_info(code, PCRE2_INFO_NAMECOUNT, &name_count);
if(name_count == 0)
return;
pcre2_pattern_info(code, PCRE2_INFO_NAMEENTRYSIZE, &entry_size);
pcre2_pattern_info(code, PCRE2_INFO_NAMETABLE, &name_table);
for(uint32_t i = 0; i < name_count; i += 1)
{
PCRE2_SPTR entry = name_table + (i * entry_size);
uint32_t group_index = (entry[0] << 8) | entry[1];
String name(reinterpret_cast<const char*>(entry + 2));
if(group_index >= (uint32_t)rc)
continue;
PCRE2_SIZE start = ovector[group_index * 2];
PCRE2_SIZE end = ovector[group_index * 2 + 1];
if(start == PCRE2_UNSET || end == PCRE2_UNSET)
continue;
result["named"][name] = regex_subject_slice(subject, start, end);
result["named_offsets"][name]["index"] = (f64)group_index;
result["named_offsets"][name]["start"] = (f64)start;
result["named_offsets"][name]["end"] = (f64)end;
}
}
DTree regex_build_match_tree(String pattern, String flags, String subject, pcre2_code* code, pcre2_match_data* match_data, int rc)
{
DTree result;
result["matched"].set_bool(rc >= 0);
result["pattern"] = pattern;
result["flags"] = regex_flags_label(flags);
if(rc < 0)
return(result);
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data);
result["start"] = (f64)ovector[0];
result["end"] = (f64)ovector[1];
result["match"] = regex_subject_slice(subject, ovector[0], ovector[1]);
for(int i = 0; i < rc; i += 1)
{
DTree capture;
PCRE2_SIZE start = ovector[i * 2];
PCRE2_SIZE end = ovector[i * 2 + 1];
capture["index"] = (f64)i;
capture["matched"].set_bool(start != PCRE2_UNSET && end != PCRE2_UNSET);
if(start != PCRE2_UNSET && end != PCRE2_UNSET)
{
capture["start"] = (f64)start;
capture["end"] = (f64)end;
capture["text"] = regex_subject_slice(subject, start, end);
}
result["captures"].push(capture);
}
regex_add_named_captures(result, code, subject, ovector, rc);
return(result);
}
int regex_match_at(RegexCode& regex, RegexMatchData& match_data, String subject, size_t offset, uint32_t options, String function_name)
{
int rc = pcre2_match(
regex.code,
reinterpret_cast<PCRE2_SPTR>(subject.c_str()),
subject.length(),
offset,
options,
match_data.data,
0
);
if(rc == PCRE2_ERROR_NOMATCH)
return(rc);
if(rc < 0)
regex_throw(function_name, "match failed: " + regex_pcre2_error(rc));
return(rc);
}
}
bool regex_match(String pattern, String subject, String flags)
{
RegexCode regex(pattern, flags, "regex_match");
RegexMatchData match_data(regex.code);
int rc = regex_match_at(regex, match_data, subject, 0, PCRE2_ANCHORED | PCRE2_ENDANCHORED, "regex_match");
return(rc >= 0);
}
DTree regex_search(String pattern, String subject, String flags)
{
RegexCode regex(pattern, flags, "regex_search");
RegexMatchData match_data(regex.code);
int rc = regex_match_at(regex, match_data, subject, 0, 0, "regex_search");
return(regex_build_match_tree(pattern, flags, subject, regex.code, match_data.data, rc));
}
DTree regex_search_all(String pattern, String subject, String flags)
{
RegexCode regex(pattern, flags, "regex_search_all");
RegexMatchData match_data(regex.code);
DTree result;
result["matched"].set_bool(false);
result["pattern"] = pattern;
result["flags"] = regex_flags_label(flags);
size_t offset = 0;
while(offset <= subject.length())
{
int rc = regex_match_at(regex, match_data, subject, offset, 0, "regex_search_all");
if(rc == PCRE2_ERROR_NOMATCH)
break;
DTree match = regex_build_match_tree(pattern, flags, subject, regex.code, match_data.data, rc);
result["matches"].push(match);
result["matched"].set_bool(true);
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data.data);
size_t start = ovector[0];
size_t end = ovector[1];
if(end > offset)
offset = end;
else
offset = regex_next_utf8_offset(subject, offset);
}
result["count"] = (f64)result["matches"].deref()._map.size();
return(result);
}
String regex_replace(String pattern, String replacement, String subject, String flags)
{
RegexCode regex(pattern, flags, "regex_replace");
PCRE2_SIZE output_length = 0;
uint32_t options = PCRE2_SUBSTITUTE_GLOBAL | PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
int rc = pcre2_substitute(
regex.code,
reinterpret_cast<PCRE2_SPTR>(subject.c_str()),
subject.length(),
0,
options,
0,
0,
reinterpret_cast<PCRE2_SPTR>(replacement.c_str()),
replacement.length(),
0,
&output_length
);
if(rc != PCRE2_ERROR_NOMEMORY && rc < 0)
regex_throw("regex_replace", "substitution failed: " + regex_pcre2_error(rc));
String output;
output.resize(output_length);
rc = pcre2_substitute(
regex.code,
reinterpret_cast<PCRE2_SPTR>(subject.c_str()),
subject.length(),
0,
PCRE2_SUBSTITUTE_GLOBAL,
0,
0,
reinterpret_cast<PCRE2_SPTR>(replacement.c_str()),
replacement.length(),
reinterpret_cast<PCRE2_UCHAR*>(&output[0]),
&output_length
);
if(rc < 0)
regex_throw("regex_replace", "substitution failed: " + regex_pcre2_error(rc));
output.resize(output_length);
return(output);
}
StringList regex_split(String pattern, String subject, String flags)
{
RegexCode regex(pattern, flags, "regex_split");
RegexMatchData match_data(regex.code);
StringList result;
size_t offset = 0;
size_t last_end = 0;
while(offset <= subject.length())
{
int rc = regex_match_at(regex, match_data, subject, offset, 0, "regex_split");
if(rc == PCRE2_ERROR_NOMATCH)
break;
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data.data);
size_t start = ovector[0];
size_t end = ovector[1];
result.push_back(subject.substr(last_end, start - last_end));
last_end = end;
if(end > offset)
offset = end;
else
offset = regex_next_utf8_offset(subject, offset);
}
result.push_back(subject.substr(last_end));
return(result);
}
String trim(String raw)
{
s64 len = raw.length();
+5
View File
@@ -14,6 +14,11 @@ bool str_starts_with(String haystack, String needle);
bool str_ends_with(String haystack, String needle);
bool contains(String haystack, String needle);
String replace(String s, String search, String replace_with);
bool regex_match(String pattern, String subject, String flags = "");
DTree regex_search(String pattern, String subject, String flags = "");
DTree regex_search_all(String pattern, String subject, String flags = "");
String regex_replace(String pattern, String replacement, String subject, String flags = "");
StringList regex_split(String pattern, String subject, String flags = "");
String trim(String raw);
StringList split_space(String str);
+5 -1
View File
@@ -7,6 +7,7 @@
#include <functional>
#include <sstream>
#include <atomic>
#include <set>
typedef unsigned char u8;
typedef signed char s8;
@@ -84,6 +85,8 @@ struct SharedUnit {
request_ref_handler on_render = 0;
request_ref_handler on_component = 0;
request_ref_handler on_websocket = 0;
request_ref_handler on_once = 0;
request_ref_handler on_init = 0;
String compiler_messages;
String compile_status = "unknown";
@@ -156,8 +159,9 @@ struct Request {
StringMap cookies;
StringMap session;
String session_loaded_hash = "";
std::set<String> once_units;
//DTree var;
DTree call;
DTree cfg;
DTree props;
DTree connection;
+7
View File
@@ -702,6 +702,13 @@ String ws_connection_id()
return(context->resources.websocket_connection_id);
}
String ws_message()
{
if(!context)
return("");
return(context->in);
}
String ws_scope()
{
return(current_ws_scope());