diff --git a/doc/areas/string.txt b/doc/areas/string.txt index db2554c..711a9dc 100644 --- a/doc/areas/string.txt +++ b/doc/areas/string.txt @@ -5,6 +5,7 @@ first join nibble split -str_to_lower -str_to_upper +replace +to_lower +to_upper trim diff --git a/doc/index.uce b/doc/index.uce index 407484b..482ae4b 100644 --- a/doc/index.uce +++ b/doc/index.uce @@ -43,18 +43,26 @@ RENDER() for(auto file_name : ls("pages/")) { String ft = nibble(file_name, "."); - if(ft[1] == '_') + if(ft.substr(0, 2) == "0_") { String fn = ft; String pre = nibble(fn, "_"); ?> -
: struct
+
: struct
+ +
: directive
-
()
+
()
string diff --git a/doc/pages/split.txt b/doc/pages/split.txt index 484a7a1..0f69a71 100644 --- a/doc/pages/split.txt +++ b/doc/pages/split.txt @@ -1,9 +1,11 @@ :sig -StringList split(String str, String delim = "\n") +StringList split(String str, String delim) +StringList split(String str) + :params str : string to be split -delim : delimiter (defaults to newline character) +delim : optional, delimiter (if omitted, any sequence of whitespace characters will count as a delimiter) return value : a list of strings :desc diff --git a/doc/pages/str_to_lower.txt b/doc/pages/to_lower.txt similarity index 90% rename from doc/pages/str_to_lower.txt rename to doc/pages/to_lower.txt index 9d4da66..714f606 100644 --- a/doc/pages/str_to_lower.txt +++ b/doc/pages/to_lower.txt @@ -1,5 +1,5 @@ :sig -String str_to_lower(String s) +String to_lower(String s) :params s : the string to be converted diff --git a/doc/pages/str_to_upper.txt b/doc/pages/to_upper.txt similarity index 90% rename from doc/pages/str_to_upper.txt rename to doc/pages/to_upper.txt index b3eea34..2711fc0 100644 --- a/doc/pages/str_to_upper.txt +++ b/doc/pages/to_upper.txt @@ -1,5 +1,5 @@ :sig -String str_to_upper(String s) +String to_upper(String s) :params s : the string to be converted diff --git a/examples/blog/index.uce b/examples/blog/index.uce new file mode 100644 index 0000000..12ea541 --- /dev/null +++ b/examples/blog/index.uce @@ -0,0 +1,9 @@ +#load "lib.uce" + +RENDER() +{ + <> + Hello world2 + + +} diff --git a/examples/blog/lib.uce b/examples/blog/lib.uce new file mode 100644 index 0000000..8962a5a --- /dev/null +++ b/examples/blog/lib.uce @@ -0,0 +1,5 @@ + +String bla(String x) +{ + return(x + "!"); +} diff --git a/scripts/setup.h.template b/scripts/setup.h.template new file mode 100644 index 0000000..efacf04 --- /dev/null +++ b/scripts/setup.h.template @@ -0,0 +1,8 @@ +/*load_declarations*/ + +extern "C" void set_current_request(Request* _request) +{ + context = _request; + signal(SIGSEGV, on_segfault); + /*load_units*/ +} diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp index 2f46bfa..f877300 100644 --- a/src/lib/compiler.cpp +++ b/src/lib/compiler.cpp @@ -87,18 +87,19 @@ String process_html_literal(Request* context, SharedUnit* su, String content) return(HT_START + pc + HT_END); } -String preprocess_shared_unit(Request* context, SharedUnit* su) +String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String content) { - String content = file_get_contents(su->file_name); - printf("(c) compiling with root dir %s\n", context->server->config.COMPILER_SYS_PATH.c_str()); - String pc = ("#include \"")+context->server->config.COMPILER_SYS_PATH +"/src/lib/uce_lib.h\" \n"; + String pc = "#include \"" + su->src_file_name + ".setup.h" + "\"\n"; String token = ""; String html_buffer = ""; u8 mode = 0; bool inside_quote = false; - for(u32 i = 0; i < content.length(); i++) + u32 source_length = content.length(); + String current_line = ""; + for(u32 i = 0; i < source_length; i++) { char c = content[i]; + current_line.append(1, c); if(mode == 2) { auto end_pos = content.find(String("", i); @@ -142,14 +143,54 @@ String preprocess_shared_unit(Request* context, SharedUnit* su) inside_quote = !inside_quote; pc.append(1, c); } - else + else // we're in C++ code here { pc.append(1, c); + if(c == 10 && current_line.substr(0, 6) == "#load ") + { + //printf("#load directive\n"); + pc.resize(pc.length() - current_line.length()); + nibble(current_line, "\""); + String unit_name = nibble(current_line, "\""); + SharedUnit* sub_su = compiler_load_shared_unit(context, unit_name, su->src_path, true); + if(sub_su) + { + pc.append("#include \"" + sub_su->pre_file_name + "\""); + } + } + else if(current_line.length() == 4 && current_line.substr(0, 4) == "API ") + { + auto end_declaration_pos = content.find("{", i); + if(end_declaration_pos != std::string::npos) + { + // remove string "API " from output + pc.resize(pc.length() - 4); + String declaration = trim(content.substr(i, end_declaration_pos - i)); + String return_type_and_name = nibble(declaration, "("); + StringList rtn_list = split(return_type_and_name); + String fn_name = rtn_list.back(); rtn_list.pop_back(); + su->api_declarations.push_back(fn_name + ":" + join(rtn_list, " ") + ":(" + declaration + "\n"); + printf("declaration found: %s\n", declaration.c_str()); + } + } } + if(c == 10) + current_line = ""; } return(pc); } +String preprocess_shared_unit(Request* context, SharedUnit* su) +{ + return( + preprocess_shared_unit_char_wise( + context, + su, + file_get_contents(su->file_name) + ) + ); +} + void setup_unit_paths(Request* context, SharedUnit* su, String file_name) { su->file_name = file_name; @@ -166,6 +207,8 @@ void setup_unit_paths(Request* context, SharedUnit* su, String file_name) su->pre_file_name = su->src_file_name + ".cpp"; su->so_name = su->bin_path + "/" + su->bin_file_name; + su->api_file_name = su->bin_path + "/" + su->src_file_name + ".api.txt"; + su->setup_file_name = su->bin_path + "/" + su->src_file_name + ".setup.h"; } void load_shared_unit(Request* context, SharedUnit* su, String file_name) @@ -178,6 +221,8 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name) if(!file_exists(su->so_name)) { + if(su->opt_so_optional) + return; printf("(i) unit file not found: %s\n", su->so_name.c_str()); su->compiler_messages = "unit file not found"; return; @@ -201,6 +246,22 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name) } } +String compile_setup_file(Request* context, SharedUnit* su) +{ + String result = + String("#ifndef UCE_LIB_INCLUDED\n") + + "#define UCE_LIB_INCLUDED\n" + + ("#include \"")+context->server->config.COMPILER_SYS_PATH +"/src/lib/uce_lib.h\" \n"+ + file_get_contents(context->server->config.SETUP_TEMPLATE) + + "#endif \n"; + StringList declarations; + StringList load_units; + + result = replace(result, "/*load_declarations*/", join(declarations, "\n")); + result = replace(result, "/*load_units*/", join(load_units, "\n")); + return(result); +} + void compile_shared_unit(Request* context, SharedUnit* su, String file_name) { //setup_unit_paths(context, su, file_name); @@ -213,16 +274,19 @@ void compile_shared_unit(Request* context, SharedUnit* su, String file_name) shell_exec("mkdir -p " + shell_escape(su->pre_path)); file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su)); + file_put_contents(su->setup_file_name, compile_setup_file(context, su)); + file_put_contents(su->api_file_name, join(su->api_declarations, "\n")); - printf("Config.COMPILE_SCRIPT %s\n", String(su->pre_path + "/" + su->pre_file_name).c_str()); + //printf("Config.COMPILE_SCRIPT %s\n", String(su->pre_path + "/" + su->pre_file_name).c_str()); - su->compiler_messages = trim(shell_exec(context->server->config.COMPILE_SCRIPT+" "+ - shell_escape(su->src_path)+" "+ - shell_escape(su->bin_path)+" "+ - shell_escape(su->file_name)+" "+ - shell_escape(su->pre_file_name)+" "+ - shell_escape(su->bin_file_name) - )); + if(!su->opt_so_optional) + su->compiler_messages = trim(shell_exec(context->server->config.COMPILE_SCRIPT+" "+ + shell_escape(su->src_path)+" "+ + shell_escape(su->bin_path)+" "+ + shell_escape(su->file_name)+" "+ + shell_escape(su->pre_file_name)+" "+ + shell_escape(su->bin_file_name) + )); if(su->compiler_messages.length() > 0) { @@ -235,27 +299,35 @@ void compile_shared_unit(Request* context, SharedUnit* su, String file_name) } } -SharedUnit* get_shared_unit(Request* context, String file_name) +SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_optional) { SharedUnit* su = context->server->units[file_name]; auto mod_time = file_mtime(file_name); + auto compiled_time = su ? file_mtime(su->so_name) : 0; bool do_recompile = false; - if(su && (su->last_compiled < mod_time || mod_time == 0)) + if(su && (compiled_time < mod_time || mod_time == 0)) { delete su; su = 0; do_recompile = true; } + else if(su && (su->last_compiled < mod_time)) + { + delete su; + su = 0; + do_recompile = false; + } if(!su) { su = new SharedUnit(); setup_unit_paths(context, su, file_name); - if(!do_recompile) + su->opt_so_optional = opt_so_optional; + if(compiled_time != 0) { // if we didn't decide to force recompile yet, we need to check // (this case should only happen if the SU cache for that entry is cold // but the SO itself exists _AND_ is stale) - su->last_compiled = file_mtime(su->so_name); + su->last_compiled = compiled_time; if(su->last_compiled < mod_time || mod_time == 0) do_recompile = true; } @@ -301,25 +373,53 @@ void compiler_invoke(Request* context, String file_name, DTree& call_param) printf("Error loading unit %s\n", file_name.c_str()); print("Error loading unit: "+file_name); } + else if(su->compiler_messages.length() > 0) + { + context->header["Content-Type"] = "text/plain"; + print(su->compiler_messages); + } else if(!su->on_render) { context->header["Content-Type"] = "text/plain"; - print("Compiler error: "+su->compiler_messages); + print("no RENDER() entry point"); } else { - if(su->compiler_messages.length() > 0) - print(su->compiler_messages); - else - { - String prev_wd = get_cwd(); - set_cwd(su->src_path); - su->on_setup(context); - su->on_render(call_param); - set_cwd(prev_wd); - } + String prev_wd = get_cwd(); + set_cwd(su->src_path); + su->on_setup(context); + su->on_render(call_param); + set_cwd(prev_wd); } } +SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String current_path, bool opt_so_optional) +{ + if(file_name[0] != '/') + { + file_name = expand_path(file_name, current_path); + } + + switch_to_system_alloc(); + auto su = get_shared_unit(context, file_name, opt_so_optional); + switch_to_arena(context->mem); + if(!su) + { + printf("Error loading unit %s\n", file_name.c_str()); + print("Error loading unit: "+file_name); + return(0); + } + else if(su->compiler_messages.length() > 0) + { + context->header["Content-Type"] = "text/plain"; + print(su->compiler_messages); + return(0); + } + else + { + return(su); + } + +} diff --git a/src/lib/compiler.h b/src/lib/compiler.h index d937ecf..4263e6c 100644 --- a/src/lib/compiler.h +++ b/src/lib/compiler.h @@ -5,6 +5,6 @@ String preprocess_shared_unit(Request* context, SharedUnit* su); void setup_unit_paths(Request* context, SharedUnit* su, String file_name); void load_shared_unit(Request* context, SharedUnit* su, String file_name); void compile_shared_unit(Request* context, SharedUnit* su, String file_name); -SharedUnit* get_shared_unit(Request* context, String file_name); +SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_optional = false); void compiler_invoke(Request* context, String file_name, DTree& call_param); - +SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String current_path, bool opt_so_optional = false); diff --git a/src/lib/functionlib.cpp b/src/lib/functionlib.cpp index 58092f4..8b12e01 100644 --- a/src/lib/functionlib.cpp +++ b/src/lib/functionlib.cpp @@ -40,7 +40,7 @@ u8 hex_to_u8(String src) return(char_to_u8(src[0])*16 + char_to_u8(src[1])); } -String str_to_lower(String s) +String to_lower(String s) { String result = ""; for(auto c : s) @@ -52,7 +52,7 @@ String str_to_lower(String s) return(result); } -String str_to_upper(String s) +String to_upper(String s) { String result = ""; for(auto c : s) @@ -64,6 +64,27 @@ String str_to_upper(String s) return(result); } +String replace(String s, String search, String replace_with) +{ + s64 last_spos = 0; + auto spos = s.find(search); + if(spos == std::string::npos) + return(s); + String result = ""; + auto slen = search.length(); + while(spos != std::string::npos) + { + if(spos - last_spos > 0) + result.append(s.substr(last_spos, spos - last_spos)); + result.append(replace_with); + last_spos = spos + slen; + spos = s.find(search, last_spos); + } + if(last_spos < s.length()) + result.append(s.substr(last_spos)); + return(result); +} + String trim(String raw) { u32 len = raw.length(); @@ -80,6 +101,30 @@ String trim(String raw) return(raw.substr(start_pos, 1 + end_pos - start_pos)); } +StringList split(String str) +{ + StringList result; + String current_token = ""; + for(auto c : str) + { + if(isspace(c)) + { + if(current_token != "") + { + result.push_back(current_token); + current_token = ""; + } + } + else + { + current_token.append(1, c); + } + } + if(current_token != "") + result.push_back(current_token); + return(result); +} + StringList split(String str, String delim) { StringList result; diff --git a/src/lib/functionlib.h b/src/lib/functionlib.h index 9aa750c..d941f37 100644 --- a/src/lib/functionlib.h +++ b/src/lib/functionlib.h @@ -2,11 +2,13 @@ u8 char_to_u8(char input); u8 hex_to_u8(String src); u64 int_val(String s, u32 base = 10); -String str_to_lower(String s); -String str_to_upper(String s); +String to_lower(String s); +String to_upper(String s); +String replace(String s, String search, String replace_with); String trim(String raw); -StringList split(String str, String delim = "\n"); +StringList split(String str); +StringList split(String str, String delim); String join(StringList l, String delim = "\n"); String nibble(String& haystack, String delim); void json_consume_space(String s, u32& i); diff --git a/src/lib/sys.cpp b/src/lib/sys.cpp index 2036a2c..2317fa0 100644 --- a/src/lib/sys.cpp +++ b/src/lib/sys.cpp @@ -32,7 +32,19 @@ String shell_exec(String cmd) String shell_escape(String raw) { // FIXME - return("\"" + raw + "\""); + String result = ""; + for(auto c : raw) + { + if(c == '\'') + { + result.append("'\\''"); + } + else + { + result.append(1, c); + } + } + return("\'" + result + "\'"); /* ` U+0060 (Grave Accent) Backtick Command substitution ~ U+007E Tilde Tilde expansion @@ -164,11 +176,14 @@ void unlink(String file_name) remove(file_name.c_str()); } -String expand_path(String path) +String expand_path(String path, String relative_to_path) { String result; - auto base_path = split(get_cwd(), "/"); + if(relative_to_path == "") + relative_to_path = get_cwd(); + + auto base_path = split(relative_to_path, "/"); auto rel_path = split(path, "/"); for(auto& s : rel_path) diff --git a/src/lib/sys.h b/src/lib/sys.h index 07db8f5..53a70fa 100644 --- a/src/lib/sys.h +++ b/src/lib/sys.h @@ -22,7 +22,7 @@ String get_cwd(); void set_cwd(String path); time_t file_mtime(String file_name); void unlink(String file_name); -String expand_path(String path); +String expand_path(String path, String relative_to_path = ""); StringList ls(String dir); f64 microtime(); diff --git a/src/lib/types.h b/src/lib/types.h index 39e2a6e..da5fcb1 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -149,6 +149,7 @@ struct ServerSettings { String BIN_DIRECTORY = "/tmp/uce/work"; String COMPILE_SCRIPT = "scripts/compile"; + String SETUP_TEMPLATE = "scripts/setup.h.template"; String LIT_ESC = "3d5b5_1"; String CONTENT_TYPE = "text/html; charset=utf-8"; String SOCKET_PATH = "/run/uce.sock"; @@ -166,6 +167,10 @@ struct SharedUnit { String file_name; String so_name; + String api_file_name; + String setup_file_name; + StringList api_declarations; + std::map api_functions; String src_path; String bin_path; @@ -182,6 +187,8 @@ struct SharedUnit { String compiler_messages; time_t last_compiled; + bool opt_so_optional = false; + ~SharedUnit(); }; diff --git a/src/lib/uce_lib.h b/src/lib/uce_lib.h index c554ebd..5ca6367 100644 --- a/src/lib/uce_lib.h +++ b/src/lib/uce_lib.h @@ -7,8 +7,4 @@ #include "compiler.h" #include "mysql-connector.h" -extern "C" void set_current_request(Request* _request) -{ - context = _request; - signal(SIGSEGV, on_segfault); -} + diff --git a/test/index.uce b/test/index.uce index 4270828..9dbe440 100644 --- a/test/index.uce +++ b/test/index.uce @@ -20,11 +20,13 @@ RENDER()
  • URI
  • Cookies
  • Session
  • +
  • String replace
  • DTree
  • JSON
  • Memcached
  • MySQL Connector
  • File I/O
  • +
  • Shell Stuff
  • File Append
  • RNG/Noise
  • Task API
  • diff --git a/test/shell.uce b/test/shell.uce new file mode 100644 index 0000000..024b34a --- /dev/null +++ b/test/shell.uce @@ -0,0 +1,32 @@ + + +RENDER() +{ + + <> + +

    + UCE Test: + Shell Stuff +

    + +
    shell_exec()
    + +
    + +
    shell_escape()
    + +
    + +
    params) ?>
    + + +} diff --git a/test/str_replace.uce b/test/str_replace.uce new file mode 100644 index 0000000..138b0b5 --- /dev/null +++ b/test/str_replace.uce @@ -0,0 +1,33 @@ + +void str_replace_test(String s, String sr, String rp) +{ + print("in '", s, "' replace all '", sr, "' with '", rp, "' => "); + print(replace(s, sr, rp)); +} + +RENDER() +{ + + <> + +

    + UCE Test: + String Replace +

    + +
    + +
    + +
    + +
    + +
    + +
    + +
    params) ?>
    + + +}