trying to port web app starter from PHP
This commit is contained in:
+324
-53
@@ -12,25 +12,36 @@ String process_text_literal(Request* context, SharedUnit* su, String content)
|
||||
bool inside_quote = false;
|
||||
String code_buffer = "";
|
||||
bool is_field = false;
|
||||
bool escape_field = false;
|
||||
|
||||
for(u32 i = 0; i < content.length(); i++)
|
||||
{
|
||||
char c = content[i];
|
||||
char c1 = (i + 1 < content.length()) ? content[i + 1] : '\0';
|
||||
char c2 = (i + 2 < content.length()) ? content[i + 2] : '\0';
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(0):
|
||||
if(c == '<' && content[i+1] == '?')
|
||||
if(c == '<' && c1 == '?')
|
||||
{
|
||||
code_buffer = "";
|
||||
if(content[i+2] == '=')
|
||||
if(c2 == '=')
|
||||
{
|
||||
is_field = true;
|
||||
escape_field = true;
|
||||
i += 2;
|
||||
}
|
||||
else if(c2 == ':')
|
||||
{
|
||||
is_field = true;
|
||||
escape_field = false;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_field = false;
|
||||
escape_field = false;
|
||||
i += 1;
|
||||
}
|
||||
mode = 1; // code-parsing mode
|
||||
@@ -43,7 +54,7 @@ String process_text_literal(Request* context, SharedUnit* su, String content)
|
||||
case(1):
|
||||
if(inside_quote)
|
||||
{
|
||||
if(quote_char == c && content[i-1] != '\\')
|
||||
if(quote_char == c && (i == 0 || content[i-1] != '\\'))
|
||||
inside_quote = false;
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
@@ -55,19 +66,32 @@ String process_text_literal(Request* context, SharedUnit* su, String content)
|
||||
quote_char = c;
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
else if(c == '?' && content[i+1] == '>')
|
||||
else if(c == '?' && c1 == '>')
|
||||
{
|
||||
mode = 0;
|
||||
i += 1;
|
||||
if(is_field)
|
||||
{
|
||||
pc.append(
|
||||
HT_END +
|
||||
"print(html_escape( " +
|
||||
code_buffer +
|
||||
" )); " +
|
||||
HT_START
|
||||
);
|
||||
if(escape_field)
|
||||
{
|
||||
pc.append(
|
||||
HT_END +
|
||||
"print(html_escape( " +
|
||||
code_buffer +
|
||||
" )); " +
|
||||
HT_START
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(
|
||||
HT_END +
|
||||
"print( " +
|
||||
code_buffer +
|
||||
" ); " +
|
||||
HT_START
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -87,6 +111,57 @@ String process_text_literal(Request* context, SharedUnit* su, String content)
|
||||
return(HT_START + pc + HT_END);
|
||||
}
|
||||
|
||||
String preprocess_named_render_syntax(String content)
|
||||
{
|
||||
String result = "";
|
||||
String current_line = "";
|
||||
|
||||
auto flush_line = [&]() {
|
||||
if(current_line.length() == 0)
|
||||
return;
|
||||
|
||||
String line = current_line;
|
||||
String line_break = "";
|
||||
if(line.length() > 0 && line.back() == '\n')
|
||||
{
|
||||
line_break = "\n";
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
u32 indent_length = 0;
|
||||
while(indent_length < line.length() && isspace(line[indent_length]))
|
||||
indent_length += 1;
|
||||
|
||||
String indent = line.substr(0, indent_length);
|
||||
String trimmed = trim(line);
|
||||
if(trimmed.rfind("RENDER:", 0) == 0)
|
||||
{
|
||||
String signature = trimmed.substr(7);
|
||||
auto open_paren_pos = signature.find("(");
|
||||
if(open_paren_pos != String::npos)
|
||||
{
|
||||
String render_name = trim(signature.substr(0, open_paren_pos));
|
||||
String render_signature = signature.substr(open_paren_pos);
|
||||
if(render_name != "")
|
||||
line = indent + "EXPORT void render_" + safe_name(render_name) + render_signature;
|
||||
}
|
||||
}
|
||||
|
||||
result += line + line_break;
|
||||
current_line = "";
|
||||
};
|
||||
|
||||
for(auto c : content)
|
||||
{
|
||||
current_line.append(1, c);
|
||||
if(c == '\n')
|
||||
flush_line();
|
||||
}
|
||||
flush_line();
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String content)
|
||||
{
|
||||
String pc =
|
||||
@@ -104,10 +179,12 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String
|
||||
for(u32 i = 0; i < source_length; i++)
|
||||
{
|
||||
char c = content[i];
|
||||
char c1 = (i + 1 < source_length) ? content[i + 1] : '\0';
|
||||
char c2 = (i + 2 < source_length) ? content[i + 2] : '\0';
|
||||
current_line.append(1, c);
|
||||
if(mode == 1)
|
||||
{
|
||||
if(c == '<' && (content[i+1] == '/') && (content[i+2] == '>'))
|
||||
if(c == '<' && c1 == '/' && c2 == '>')
|
||||
{
|
||||
i += 2;
|
||||
pc.append(process_text_literal(context, su, html_buffer));
|
||||
@@ -119,7 +196,7 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
}
|
||||
else if(!inside_quote && c == '<' && (content[i+1] == '>'))
|
||||
else if(!inside_quote && c == '<' && c1 == '>')
|
||||
{
|
||||
mode = 1;
|
||||
token = "";
|
||||
@@ -147,16 +224,20 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String
|
||||
pc.append("#include \"" + sub_su->bin_path + "/" + sub_su->pre_file_name + "\"\n");
|
||||
}
|
||||
}
|
||||
else if(current_line.substr(0, 6) == "EXPORT" && isspace(current_line[6]))
|
||||
else
|
||||
{
|
||||
current_line = "";
|
||||
auto end_declaration_pos = content.find("{", i);
|
||||
if(end_declaration_pos != std::string::npos)
|
||||
String trimmed_line = trim(current_line);
|
||||
if(c == 10 && trimmed_line.length() > 7 && trimmed_line.substr(0, 6) == "EXPORT" && isspace(trimmed_line[6]))
|
||||
{
|
||||
pc.append(1, '\n');
|
||||
String declaration = trim(content.substr(i, end_declaration_pos - i));
|
||||
su->api_declarations.push_back(declaration+";\n");
|
||||
//printf("declaration found: %s\n", declaration.c_str());
|
||||
current_line = "";
|
||||
auto end_declaration_pos = content.find("{", i);
|
||||
if(end_declaration_pos != std::string::npos)
|
||||
{
|
||||
pc.append(1, '\n');
|
||||
String declaration = trim(content.substr(i, end_declaration_pos - i));
|
||||
su->api_declarations.push_back(declaration+";\n");
|
||||
//printf("declaration found: %s\n", declaration.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,11 +249,13 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String
|
||||
|
||||
String preprocess_shared_unit(Request* context, SharedUnit* su)
|
||||
{
|
||||
String content = file_get_contents(su->file_name);
|
||||
content = preprocess_named_render_syntax(content);
|
||||
return(
|
||||
preprocess_shared_unit_char_wise(
|
||||
context,
|
||||
su,
|
||||
file_get_contents(su->file_name)
|
||||
content
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -223,9 +306,9 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
su->on_setup = (request_handler)dlsym(su->so_handle, "set_current_request");
|
||||
if ((error = dlerror()) != NULL)
|
||||
printf("Error - %s in %s\n", error, su->file_name.c_str());
|
||||
su->on_render = (call_handler)dlsym(su->so_handle, "render");
|
||||
su->on_render = (request_ref_handler)dlsym(su->so_handle, "render");
|
||||
dlerror();
|
||||
su->on_websocket = (call_handler)dlsym(su->so_handle, "websocket");
|
||||
su->on_websocket = (request_ref_handler)dlsym(su->so_handle, "websocket");
|
||||
dlerror();
|
||||
su->api_declarations = split(file_get_contents(su->api_file_name), "\n");
|
||||
//else
|
||||
@@ -291,6 +374,11 @@ SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_opti
|
||||
{
|
||||
SharedUnit* su = context->server->units[file_name];
|
||||
auto mod_time = file_mtime(file_name);
|
||||
auto setup_template_time = file_mtime(
|
||||
context->server->config["COMPILER_SYS_PATH"] + "/" +
|
||||
context->server->config["SETUP_TEMPLATE"]);
|
||||
if(setup_template_time > mod_time)
|
||||
mod_time = setup_template_time;
|
||||
auto compiled_time = su ? file_mtime(su->so_name) : 0;
|
||||
bool do_recompile = false;
|
||||
if(su && (compiled_time < mod_time || mod_time == 0))
|
||||
@@ -378,36 +466,141 @@ SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String
|
||||
|
||||
}
|
||||
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param)
|
||||
String component_normalize_path(String name)
|
||||
{
|
||||
name = trim(name);
|
||||
if(name.length() >= 4 && name.substr(name.length() - 4) == ".uce")
|
||||
return(name);
|
||||
return(name + ".uce");
|
||||
}
|
||||
|
||||
void component_parse_target(String target, String& file_name, String& render_name)
|
||||
{
|
||||
target = trim(target);
|
||||
render_name = "render";
|
||||
auto render_split_pos = target.find(":");
|
||||
if(render_split_pos != String::npos)
|
||||
{
|
||||
render_name = trim(target.substr(render_split_pos + 1));
|
||||
target = trim(target.substr(0, render_split_pos));
|
||||
if(render_name == "")
|
||||
render_name = "render";
|
||||
}
|
||||
file_name = target;
|
||||
}
|
||||
|
||||
String component_resolve_path(String name)
|
||||
{
|
||||
String file_name;
|
||||
String render_name;
|
||||
component_parse_target(name, file_name, render_name);
|
||||
|
||||
if(file_name == "")
|
||||
return("");
|
||||
|
||||
StringList candidates;
|
||||
auto push_candidate = [&] (String candidate) {
|
||||
if(candidate == "")
|
||||
return;
|
||||
candidates.push_back(candidate);
|
||||
};
|
||||
|
||||
push_candidate(file_name);
|
||||
push_candidate(component_normalize_path(file_name));
|
||||
|
||||
if(file_name.rfind("components/", 0) != 0)
|
||||
{
|
||||
push_candidate("components/" + file_name);
|
||||
push_candidate(component_normalize_path("components/" + file_name));
|
||||
}
|
||||
|
||||
std::map<String, bool> seen;
|
||||
for(auto& candidate : candidates)
|
||||
{
|
||||
if(seen[candidate])
|
||||
continue;
|
||||
seen[candidate] = true;
|
||||
String resolved = candidate;
|
||||
if(resolved[0] != '/')
|
||||
resolved = expand_path(resolved, get_cwd());
|
||||
if(file_exists(resolved))
|
||||
return(resolved);
|
||||
}
|
||||
|
||||
return("");
|
||||
}
|
||||
|
||||
String render_handler_symbol(String render_name)
|
||||
{
|
||||
render_name = trim(render_name);
|
||||
if(render_name == "" || render_name == "render")
|
||||
return("render");
|
||||
return("render_" + safe_name(render_name));
|
||||
}
|
||||
|
||||
request_ref_handler get_render_handler(SharedUnit* su, String render_name)
|
||||
{
|
||||
String symbol = render_handler_symbol(render_name);
|
||||
if(symbol == "render")
|
||||
return(su->on_render);
|
||||
|
||||
auto it = su->api_functions.find(symbol);
|
||||
if(it != su->api_functions.end())
|
||||
return((request_ref_handler)it->second);
|
||||
|
||||
auto handler = (request_ref_handler)dlsym(su->so_handle, symbol.c_str());
|
||||
dlerror();
|
||||
su->api_functions[symbol] = (void*)handler;
|
||||
return(handler);
|
||||
}
|
||||
|
||||
bool compiler_invoke_render(Request* context, String file_name, String render_name, String* error_out = 0)
|
||||
{
|
||||
auto su = compiler_load_shared_unit(context, file_name, "", false);
|
||||
if(!su)
|
||||
return(false);
|
||||
|
||||
if(!su->on_setup)
|
||||
{
|
||||
if(error_out)
|
||||
*error_out = "internal error: set_current_request() not defined in " + file_name;
|
||||
return(false);
|
||||
}
|
||||
|
||||
auto handler = get_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";
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
su->on_setup(context);
|
||||
handler(*context);
|
||||
set_cwd(prev_wd);
|
||||
return(true);
|
||||
}
|
||||
|
||||
void compiler_invoke(Request* context, String file_name)
|
||||
{
|
||||
printf("(i) compiler_invoke file %s\n", file_name.c_str());
|
||||
auto su = compiler_load_shared_unit(context, file_name, "", false);
|
||||
if(su)
|
||||
String error_message = "";
|
||||
if(!compiler_invoke_render(context, file_name, "render", &error_message) && error_message != "")
|
||||
{
|
||||
if(!su->on_setup)
|
||||
{
|
||||
if(context->stats.invoke_count == 1)
|
||||
context->header["Content-Type"] = "text/plain";
|
||||
print("internal error: set_current_request() not defined in", file_name, "\n");
|
||||
}
|
||||
else if(!su->on_render)
|
||||
{
|
||||
if(context->stats.invoke_count == 1)
|
||||
context->header["Content-Type"] = "text/plain";
|
||||
print("no RENDER() entry point");
|
||||
}
|
||||
else
|
||||
{
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
su->on_setup(context);
|
||||
su->on_render(call_param);
|
||||
set_cwd(prev_wd);
|
||||
}
|
||||
if(context->stats.invoke_count == 1)
|
||||
context->header["Content-Type"] = "text/plain";
|
||||
print(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
void compiler_invoke_websocket(Request* context, String file_name, DTree& call_param)
|
||||
void compiler_invoke_websocket(Request* context, String file_name)
|
||||
{
|
||||
auto su = compiler_load_shared_unit(context, file_name, "", false);
|
||||
if(!su)
|
||||
@@ -428,20 +621,98 @@ void compiler_invoke_websocket(Request* context, String file_name, DTree& call_p
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
su->on_setup(context);
|
||||
su->on_websocket(call_param);
|
||||
su->on_websocket(*context);
|
||||
set_cwd(prev_wd);
|
||||
}
|
||||
|
||||
void render_file(String file_name)
|
||||
{
|
||||
//printf("(i) render_file(%s)\n", file_name.c_str());
|
||||
DTree call_param;
|
||||
compiler_invoke(context, file_name, call_param);
|
||||
compiler_invoke(context, file_name);
|
||||
}
|
||||
|
||||
void render_file(String file_name, DTree& call_param)
|
||||
void render_file(String file_name, Request& context)
|
||||
{
|
||||
compiler_invoke(context, file_name, call_param);
|
||||
compiler_invoke(&context, file_name);
|
||||
}
|
||||
|
||||
String component_resolve(String name)
|
||||
{
|
||||
return(component_resolve_path(name));
|
||||
}
|
||||
|
||||
bool component_exists(String name)
|
||||
{
|
||||
return(component_resolve(name) != "");
|
||||
}
|
||||
|
||||
String component_error_banner(String message)
|
||||
{
|
||||
return("<div class=\"banner\">" + html_escape(message) + "</div>");
|
||||
}
|
||||
|
||||
void render_component(String name)
|
||||
{
|
||||
DTree props;
|
||||
render_component(name, props, *context);
|
||||
}
|
||||
|
||||
void render_component(String name, Request& context)
|
||||
{
|
||||
DTree props;
|
||||
render_component(name, props, context);
|
||||
}
|
||||
|
||||
void render_component(String name, DTree props)
|
||||
{
|
||||
render_component(name, props, *context);
|
||||
}
|
||||
|
||||
void render_component(String name, DTree props, Request& context)
|
||||
{
|
||||
String file_name;
|
||||
String render_name;
|
||||
component_parse_target(name, file_name, render_name);
|
||||
|
||||
String resolved_name = component_resolve_path(file_name);
|
||||
if(resolved_name == "")
|
||||
{
|
||||
print(component_error_banner("component not found: " + file_name));
|
||||
return;
|
||||
}
|
||||
|
||||
DTree previous_call = context.call;
|
||||
context.call = props;
|
||||
|
||||
String error_message = "";
|
||||
if(!compiler_invoke_render(&context, resolved_name, render_name, &error_message) && error_message != "")
|
||||
print(component_error_banner(error_message));
|
||||
|
||||
context.call = previous_call;
|
||||
}
|
||||
|
||||
String component(String name)
|
||||
{
|
||||
DTree props;
|
||||
return(component(name, props, *context));
|
||||
}
|
||||
|
||||
String component(String name, Request& context)
|
||||
{
|
||||
DTree props;
|
||||
return(component(name, props, context));
|
||||
}
|
||||
|
||||
String component(String name, DTree props)
|
||||
{
|
||||
return(component(name, props, *context));
|
||||
}
|
||||
|
||||
String component(String name, DTree props, Request& context)
|
||||
{
|
||||
ob_start();
|
||||
render_component(name, props, context);
|
||||
return(ob_get_close());
|
||||
}
|
||||
|
||||
SharedUnit* load_file(String file_name)
|
||||
|
||||
Reference in New Issue
Block a user