From be37e4f5abc7e2884c432f4d6e026cc6ac8dc0ea Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Mon, 12 Aug 2024 19:43:35 +0300 Subject: [PATCH] Very raw version of nytl, no parsing yet --- assets/{html => HypertextPages}/chat.html | 0 .../{html => HypertextPages}/list-rooms.html | 0 assets/{html => HypertextPages}/profile.html | 0 .../registration.html | 0 .../new_york_transit_line/alotalot.h | 6 + .../execute_expression.cpp | 6 +- .../new_york_transit_line/parser.cpp | 10 +- .../new_york_transit_line/rendering.cpp | 277 +++++++++++++----- .../new_york_transit_line/templater.cpp | 6 +- .../new_york_transit_line/templater.h | 8 +- src/http_server/nytl_tests/test0.cpp | 17 ++ src/web_chat/main.cpp | 3 +- 12 files changed, 236 insertions(+), 97 deletions(-) rename assets/{html => HypertextPages}/chat.html (100%) rename assets/{html => HypertextPages}/list-rooms.html (100%) rename assets/{html => HypertextPages}/profile.html (100%) rename assets/{html => HypertextPages}/registration.html (100%) create mode 100644 src/http_server/nytl_tests/test0.cpp diff --git a/assets/html/chat.html b/assets/HypertextPages/chat.html similarity index 100% rename from assets/html/chat.html rename to assets/HypertextPages/chat.html diff --git a/assets/html/list-rooms.html b/assets/HypertextPages/list-rooms.html similarity index 100% rename from assets/html/list-rooms.html rename to assets/HypertextPages/list-rooms.html diff --git a/assets/html/profile.html b/assets/HypertextPages/profile.html similarity index 100% rename from assets/html/profile.html rename to assets/HypertextPages/profile.html diff --git a/assets/html/registration.html b/assets/HypertextPages/registration.html similarity index 100% rename from assets/html/registration.html rename to assets/HypertextPages/registration.html diff --git a/src/http_server/new_york_transit_line/alotalot.h b/src/http_server/new_york_transit_line/alotalot.h index 491bf7b..a78344a 100644 --- a/src/http_server/new_york_transit_line/alotalot.h +++ b/src/http_server/new_york_transit_line/alotalot.h @@ -11,6 +11,12 @@ namespace nytl { template using uptr = std::unique_ptr; + + template + constexpr std::remove_reference_t&& + mv(Tp&& t) noexcept + { return static_cast&&>(t); } + class FUp : public std::exception{ std::string WHAT; public: diff --git a/src/http_server/new_york_transit_line/execute_expression.cpp b/src/http_server/new_york_transit_line/execute_expression.cpp index 2655962..440918d 100644 --- a/src/http_server/new_york_transit_line/execute_expression.cpp +++ b/src/http_server/new_york_transit_line/execute_expression.cpp @@ -58,8 +58,8 @@ namespace nytl { } else assert(false); } + const std::vector& chain = expr["C"].g().asArray(); while (true) { - const std::vector& chain = expr["C"].g().asArray(); if (chain_el >= chain.size()) return NULL; const json::JSON& t = chain[chain_el++]; @@ -79,9 +79,9 @@ namespace nytl { while (!stack.empty()) { EEFrame& cur = *stack.back(); uptr todo = cur.toMe(returned, global_elems, local_vars); - returned = (bool)todo; + returned = !(bool)todo; if (todo) - stack.push_back(todo); + stack.push_back(mv(todo)); else stack.pop_back(); } diff --git a/src/http_server/new_york_transit_line/parser.cpp b/src/http_server/new_york_transit_line/parser.cpp index 303c9d8..2dee160 100644 --- a/src/http_server/new_york_transit_line/parser.cpp +++ b/src/http_server/new_york_transit_line/parser.cpp @@ -22,7 +22,7 @@ namespace nytl { } void parse_bare_file(const std::string& filename, const std::string& content, - std::map& result) + global_elem_set_t& result) { ASSERT(result.count(filename) == 0, "Repeated element " + filename); std::vector lines; @@ -66,23 +66,19 @@ namespace nytl { } Element& el = result[filename]; el.parts = {ElementPart{element_part_types::code}}; - // std::string concatenated = ""; - // el.parts[0].when_code.lines = std::move(lines); std::string lines_cat; - // todo: concatenate lines size_t n = lines.size(); for (size_t i = 0; i < n; i++) { lines_cat += lines[i]; if (i + 1 < n) lines_cat += '\n'; } - json::JSON a; - json::JSON b; + el.parts[0].when_code.lines = mv(lines_cat); } void parse_special_file(const std::string& filename, const std::string& content, std::map& result) { - // todo + THROW("Don't know how to parse it yet"); } } diff --git a/src/http_server/new_york_transit_line/rendering.cpp b/src/http_server/new_york_transit_line/rendering.cpp index 21b17f2..5d1ffe5 100644 --- a/src/http_server/new_york_transit_line/rendering.cpp +++ b/src/http_server/new_york_transit_line/rendering.cpp @@ -9,21 +9,39 @@ namespace nytl { std::string result; size_t cur_line_width = 0; - void append(const std::string& text) { + void append(const std::string& text, size_t wsp_before_newlines, bool& newlined_somewhere) { size_t n = result.size(); - result.resize(n + text.size()); - memcpy((void*)&result.c_str()[n], text.c_str(), text.size()); - for (char ch: text) { - if (ch == '\n') + size_t m = text.size(); + result.reserve(n + m); + for (size_t i = 0; i < m; i++) { + if (text[i] == '\n') { + newlined_somewhere = true; cur_line_width = 0; - else + } else { + if (cur_line_width == 0 && newlined_somewhere) { + result.resize(result.size() + wsp_before_newlines, ' '); + cur_line_width = wsp_before_newlines; + } cur_line_width++; + } + result += text[i]; } } }; #define RFrame_passed const global_elem_set_t& elem_ns, Ditch& result, const std::function& escape struct RFrame { + size_t wsp_before_newlines = 0; + bool newlined_somewhere = false; + + void append(const std::string& text, Ditch& ditch) { + ditch.append(text, wsp_before_newlines, newlined_somewhere); + } + + explicit RFrame(size_t multiline_put_start) + : wsp_before_newlines(multiline_put_start) { + } + virtual uptr toMe(bool returned, RFrame_passed) {assert(false);} virtual ~RFrame() = default; @@ -33,85 +51,181 @@ namespace nytl { std::string name; std::vector passed_args; /* This parameter incapsulates `cur_line_width` at some point for multiline `put-parts` */ - size_t multiline_put_start = 0; /* main iterator of this frame. Persistent across control returns */ size_t part_to_do = 0; - RFrame_OverParts(const std::string &name, const std::vector &passed_args, + RFrame_OverParts(const std::string &name, const std::vector& passed_args, size_t multiline_put_start) - : name(name), - passed_args(passed_args), - multiline_put_start(multiline_put_start) { + : RFrame(multiline_put_start), name(name), + passed_args(passed_args) { } - uptr toMe(bool returned, RFrame_passed) override { - if (!returned) - ASSERT(elem_ns.count(name) == 1, "No such element"); - const Element& el = elem_ns.at(name); - if (!returned) { - /* Continue to do checks */ - size_t n = el.arguments.size(); - ASSERT(n == passed_args.size(), "Argument count mismatch"); - for (size_t i = 0; i < n; i++) { - if (el.arguments[i].type == json::true_symbol) { - ASSERT(passed_args[i].is_json, "Expected json element argument, got element"); - } else { - // If not json is expected, element must be expected - assert(el.arguments[i].isArray()); - ASSERT(!passed_args[i].is_json, "Expected element element arguemnt, got json"); - ASSERT(elem_ns.count(passed_args[i].EL_name), "No such element, can't compare signatures of argument value"); - const Element& arg_element = elem_ns.at(passed_args[i].EL_name); - // ASSERT(passed_args); - if(el.arguments[i].asArray() != arg_element.arguments) - THROW("Signature of argument " + std::to_string(i) + " does not match"); - } - } - } - if (el.base) { - assert(!returned); - assert(passed_args.size() == 1); - const json::JSON* X = passed_args[0].JSON_subval; - assert(X); - if (name == "jesc") { - result.append(escape(json::generate_str(*X, json::print_pretty))); - } else if (name == "str2text") { - ASSERT(X->isString(), "str2text takes json string"); - result.append(escape(X->asString())); - } else if (name == "str2code") { - ASSERT(X->isString(), "str2code takes json string"); - result.append(X->asString()); - } - return NULL; - } - while (true) { - if (part_to_do == el.parts.size()) - return NULL; - const ElementPart& cur_part = el.parts[part_to_do++]; - if (cur_part.type == element_part_types::code) { - const ElementPart::when_code_S& pt = cur_part.when_code; - result.append(pt.lines); - } else if (cur_part.type == element_part_types::put) { - const ElementPart::when_put_S& pt = cur_part.when_put; - LocalVarValue called_element_expv = rendering_core_execute_expression(elem_ns, passed_args, pt.called_element); - ASSERT(!called_element_expv.is_json, "Can't PUT json variable"); - size_t AN = pt.passed_arguments.size(); - std::vector passed_arguments_expv(AN); - for (size_t i = 0; i < AN; i++) - passed_arguments_expv[i] = rendering_core_execute_expression(elem_ns, passed_args, pt.passed_arguments[i]); - return uptr(new RFrame_OverParts(called_element_expv.EL_name, passed_arguments_expv, result.cur_line_width)); - } else if (cur_part.type == element_part_types::for_put) { - const ElementPart::when_for_put_S& pt = cur_part.when_for_put; - // todo - } else if (cur_part.type == element_part_types::ref_put) { - const ElementPart::when_ref_put_S& pt = cur_part.when_ref_put; - // todo - } - } + uptr toMe(bool returned, RFrame_passed) override; + }; + + struct RFrame_OverJSON: public RFrame { + const ElementPart::when_for_put_S& part; + /* During the course of iteration execution, given arg list will expand and shrink back */ + std::vector saved_args_plus_iter; + + RFrame_OverJSON(const ElementPart::when_for_put_S &part, size_t multiline_put_start, + const std::vector &saved_args) + : RFrame(multiline_put_start), part(part), + saved_args_plus_iter(saved_args) { + if (part.where_key_var > -1) + this->saved_args_plus_iter.emplace_back(); + if (part.where_value_var > -1) + this->saved_args_plus_iter.emplace_back(); } }; + struct RFrame_OverArray : public RFrame_OverJSON { + const std::vector& arr; + size_t it = 0; + /* Crutch. I can't pass simple integer as nytl local variable, I need persistent json wrapper */ + json::JSON additional_json_wrapper; + + RFrame_OverArray(const ElementPart::when_for_put_S& part, size_t multiline_put_start, const std::vector &saved_args, + const std::vector &arr): RFrame_OverJSON(part, multiline_put_start, saved_args), + arr(arr) { + if (part.where_key_var < 0) + additional_json_wrapper = json::JSON(json::Integer(0l)); + } + + uptr toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result, + const std::function &escape) override; + }; + + struct RFrame_OverDictionary: public RFrame_OverJSON { + const std::map& dict; + std::map::const_iterator it; + /* Crutch */ + json::JSON addition_json_wrapper; + + RFrame_OverDictionary(const ElementPart::when_for_put_S& part, size_t multiline_put_start, const std::vector &saved_args_plus_iter, + const std::map &dict): RFrame_OverJSON(part, multiline_put_start, saved_args_plus_iter), + dict(dict) { + it = dict.begin(); + if (part.where_key_var < 0) + addition_json_wrapper = json::JSON(""); + } + + uptr toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result, + const std::function &escape) override; + }; + + uptr RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result, + const std::function &escape) { + if (!returned) + ASSERT(elem_ns.count(name) == 1, "No such element"); + const Element& el = elem_ns.at(name); + if (!returned) { + /* Continue to do checks */ + size_t n = el.arguments.size(); + ASSERT(n == passed_args.size(), "Argument count mismatch"); + for (size_t i = 0; i < n; i++) { + if (el.arguments[i].type == json::true_symbol) { + ASSERT(passed_args[i].is_json, "Expected json element argument, got element"); + } else { + // If not json is expected, element must be expected + assert(el.arguments[i].isArray()); + ASSERT(!passed_args[i].is_json, "Expected element element arguemnt, got json"); + ASSERT(elem_ns.count(passed_args[i].EL_name), "No such element, can't compare signatures of argument value"); + const Element& arg_element = elem_ns.at(passed_args[i].EL_name); + // ASSERT(passed_args); + if(el.arguments[i].asArray() != arg_element.arguments) + THROW("Signature of argument " + std::to_string(i) + " does not match"); + } + } + } + if (el.base) { + assert(!returned); + assert(passed_args.size() == 1); + const json::JSON* X = passed_args[0].JSON_subval; + assert(X); + if (name == "jesc") { + append(escape(json::generate_str(*X, json::print_pretty)), result); + } else if (name == "str2text") { + ASSERT(X->isString(), "str2text takes json string"); + append(escape(X->asString()), result); + } else if (name == "str2code") { + ASSERT(X->isString(), "str2code takes json string"); + append(X->asString(), result); + } + return NULL; + } + while (true) { + if (part_to_do == el.parts.size()) + return NULL; + const ElementPart& cur_part = el.parts[part_to_do++]; + if (cur_part.type == element_part_types::code) { + const ElementPart::when_code_S& pt = cur_part.when_code; + append(pt.lines, result); + } else if (cur_part.type == element_part_types::put) { + const ElementPart::when_put_S& pt = cur_part.when_put; + LocalVarValue called_element_expv = rendering_core_execute_expression(elem_ns, passed_args, pt.called_element); + ASSERT(!called_element_expv.is_json, "Can't PUT json variable"); + size_t AN = pt.passed_arguments.size(); + std::vector passed_arguments_expv(AN); + for (size_t i = 0; i < AN; i++) + passed_arguments_expv[i] = rendering_core_execute_expression(elem_ns, passed_args, pt.passed_arguments[i]); + return std::make_unique(called_element_expv.EL_name, passed_arguments_expv, + result.cur_line_width); + } else if (cur_part.type == element_part_types::for_put) { + const ElementPart::when_for_put_S& pt = cur_part.when_for_put; + LocalVarValue iting_over = rendering_core_execute_expression(elem_ns, passed_args, pt.ref_over); + ASSERT(iting_over.is_json, "Can't iterate over element"); + const json::JSON& container = *iting_over.JSON_subval; + if (container.isArray()) { + return std::make_unique(pt, result.cur_line_width, passed_args, container.asArray()); + } else if (container.isDictionary()) { + return std::make_unique(pt, result.cur_line_width, passed_args, container.asDictionary()); + } else + THROW("Can't iterate over non-natalistic jsobject"); + } else if (cur_part.type == element_part_types::ref_put) { + const ElementPart::when_ref_put_S& pt = cur_part.when_ref_put; + std::vector more_variables(passed_args.size() + 1); + std::copy(passed_args.begin(), passed_args.end(), more_variables.begin()); + more_variables.back() = rendering_core_execute_expression(elem_ns, passed_args, pt.ref_over); + return std::make_unique(pt.internal_element, more_variables, result.cur_line_width); + } + } + } + + uptr RFrame_OverArray::toMe(bool returned, RFrame_passed) { + if (returned && part.line_feed) + append("\n", result); + if (it >= arr.size()) + return NULL; + if (part.where_key_var > -1) { + additional_json_wrapper.asInteger() = json::Integer((int64_t)it); + saved_args_plus_iter[part.where_key_var] = {true, "", &additional_json_wrapper}; + } + if (part.where_value_var > -1) { + saved_args_plus_iter[part.where_value_var] = {true, "", &(arr[it])}; + } + it++; + return std::make_unique(part.internal_element, saved_args_plus_iter, wsp_before_newlines); + } + + uptr RFrame_OverDictionary::toMe(bool returned, RFrame_passed) { + if (returned && part.line_feed) + append("\n", result); + if (it == dict.end()) + return NULL; + if (part.where_key_var > -1) { + addition_json_wrapper.asString() = it->first; + saved_args_plus_iter[part.where_key_var] = {true, "", &addition_json_wrapper}; + } + if (part.where_value_var > -1) { + saved_args_plus_iter[part.where_value_var] = {true, "", &it->second}; + } + ++it; + return std::make_unique(part.internal_element, saved_args_plus_iter, wsp_before_newlines); + } + std::string rendering_core(const std::string& entry_func, const std::vector& entry_arguments, - const global_elem_set_t& elem_ns, const std::function& escape) + const global_elem_set_t& elem_ns, const std::function& escape) { Ditch result; @@ -122,13 +236,18 @@ namespace nytl { std::vector entry_arguments_conv(AN); for (size_t i = 0; i < AN; i++) entry_arguments_conv[i] = {true, "", &entry_arguments[i]}; - // stack.push_back(std::make_unique<>()); - // make_frame(entry_func, entry_arguments_conv); + stack.push_back(std::make_unique(entry_func, entry_arguments_conv, 0)); } + bool returned = false; while (!stack.empty()) { - // Frame& cur = *stack.back(); - + uptr ret = stack.back()->toMe(returned, elem_ns, result, escape); + returned = !(bool)ret; + if (ret) + stack.push_back(mv(ret)); + else + stack.pop_back(); } + assert(returned); return result.result; } } diff --git a/src/http_server/new_york_transit_line/templater.cpp b/src/http_server/new_york_transit_line/templater.cpp index 6e9e150..e129063 100644 --- a/src/http_server/new_york_transit_line/templater.cpp +++ b/src/http_server/new_york_transit_line/templater.cpp @@ -38,7 +38,7 @@ namespace nytl { std::vector todo; todo.emplace_back(); while (!todo.empty()) { - std::string cur = std::move(todo.back()); + std::string cur = mv(todo.back()); todo.pop_back(); std::string path_to_cur_dir = rules.root_dir_path + "/" + cur; DIR* D = opendir(path_to_cur_dir.c_str()); @@ -61,7 +61,7 @@ namespace nytl { ASSERT_on_iret(ret, "stat(" + path_to_cur_child + ")"); if (S_ISDIR(info.st_mode)) { if (isUname(child_entry)) - todo.push_back(cur + "/" + child_entry); + todo.push_back(cur.empty() ? child_entry : cur + "/" + child_entry); } else if (S_ISREG(info.st_mode)) { auto replace_sep = [](const std::string& slashed) -> std::string { std::string dotted; @@ -76,7 +76,7 @@ namespace nytl { }; auto np_reg_categ_result = [&](const std::string& no_postfix, bool applied) { if (isUname(no_postfix)) - result.push_back({path_to_cur_child, replace_sep(cur + "/" + no_postfix), applied}); + result.push_back({path_to_cur_child, replace_sep(cur.empty() ? no_postfix : cur + "/" + no_postfix), applied}); }; if (endsIn(child_entry, rules.postfix_rule_for_element_cont)) { np_reg_categ_result(throwout_postfix(child_entry, rules.postfix_rule_for_element_cont.size()), true); diff --git a/src/http_server/new_york_transit_line/templater.h b/src/http_server/new_york_transit_line/templater.h index fafd945..0e247f2 100644 --- a/src/http_server/new_york_transit_line/templater.h +++ b/src/http_server/new_york_transit_line/templater.h @@ -34,14 +34,14 @@ namespace nytl { } when_put; struct when_for_put_S { expression_t ref_over; - bool have_av_key = false; - bool have_av_value = false; - expression_t internal_element; + ssize_t where_key_var = -1; + ssize_t where_value_var = -1; + std::string internal_element; bool line_feed = true; } when_for_put; struct when_ref_put_S { expression_t ref_over; - expression_t internal_element; + std::string internal_element; } when_ref_put; }; diff --git a/src/http_server/nytl_tests/test0.cpp b/src/http_server/nytl_tests/test0.cpp new file mode 100644 index 0000000..5336d0b --- /dev/null +++ b/src/http_server/nytl_tests/test0.cpp @@ -0,0 +1,17 @@ +#include + +/* Yep, tests for nytl depend on assets for website. Yep, I see no problem with that */ + +int main(int argc, char** argv) { + if (argc < 2) { + fprintf(stderr, "Usage: test assets_dir"); + exit(1); + } + + std::string dir_path = argv[1]; + nytl::Templater templater(nytl::TemplaterSettings{nytl::TemplaterDetourRules{dir_path}}); + templater.update(); + std::string answer = templater.render("chat", {}); + printf("%s\n<>\n", answer.c_str()); + return 0; +} \ No newline at end of file diff --git a/src/web_chat/main.cpp b/src/web_chat/main.cpp index dd07275..745b5d7 100644 --- a/src/web_chat/main.cpp +++ b/src/web_chat/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include bool termination = false; @@ -54,7 +55,7 @@ int main(int argc, char** argv){ een9::StaticAssetManagerSlaveModule samI; samI.update({ - een9::StaticAssetManagerRule{assets_dir + "/html", "/assets/html", {{".html", "text/html"}} }, + een9::StaticAssetManagerRule{assets_dir + "/HypertextPages", "/assets/html", {{".html", "text/html"}} }, een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} }, een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} }, een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {