From 9f7903cae8e90070fdaba710c60ef8c29afdcc50 Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Mon, 12 Aug 2024 00:12:30 +0300 Subject: [PATCH] Daily update --- building/main.cpp | 9 +- .../new_york_transit_line/alotalot.cpp | 20 +- .../new_york_transit_line/alotalot.h | 5 +- src/http_server/new_york_transit_line/core.h | 6 +- .../execute_expression.cpp | 78 ++++++-- .../new_york_transit_line/rendering.cpp | 184 +++++++++++------- .../new_york_transit_line/templater.cpp | 19 +- .../new_york_transit_line/templater.h | 2 +- src/web_chat/main.cpp | 2 +- 9 files changed, 206 insertions(+), 119 deletions(-) diff --git a/building/main.cpp b/building/main.cpp index f591287..10466ff 100644 --- a/building/main.cpp +++ b/building/main.cpp @@ -34,6 +34,7 @@ struct CAWebChat { BuildUnitsArray runlevel_2; std::string build_type; + bool build_tests = false; std::vector warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic", "-Wno-unused-but-set-variable", "-Wno-reorder"}; @@ -57,8 +58,8 @@ struct CAWebChat { return my_flag_collection; } - CAWebChat(std::string _build_type, const NormalCBuildSystemCommandMeaning& cmd) - : build_type(std::move(_build_type)) + CAWebChat(const std::string& _build_type, bool _build_tests, const NormalCBuildSystemCommandMeaning& cmd) + : build_type(_build_type), build_tests(_build_tests) { ASSERT(build_type == "release" || build_type == "debug", "Unknown build type"); @@ -162,7 +163,9 @@ int main(int argc, char** argv) { } NormalCBuildSystemCommandMeaning cmd; regular_bs_cli_cmd_interpret(args, cmd); - CAWebChat bs("debug", cmd); + const char* BS_SCRIPT_TYPE = getenv("BS_SCRIPT_TYPE"); + const char* BS_SCRIPT_TESTS = getenv("BS_SCRIPT_TESTS"); + CAWebChat bs(BS_SCRIPT_TYPE ? BS_SCRIPT_TYPE : "release", (bool)BS_SCRIPT_TESTS, cmd); if (cmd.need_to_build) complete_tasks_of_build_units(bs.runlevel_1); umask(~0755); diff --git a/src/http_server/new_york_transit_line/alotalot.cpp b/src/http_server/new_york_transit_line/alotalot.cpp index ad6b8c8..09b9cbd 100644 --- a/src/http_server/new_york_transit_line/alotalot.cpp +++ b/src/http_server/new_york_transit_line/alotalot.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace nytl { FUp::FUp(const std::string &err, const std::string &file, const std::string &func, int line){ @@ -45,7 +46,7 @@ namespace nytl { return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\r'; } - bool isUname(const std::string &str) noexcept { + bool isUname(const std::string &str) { if (str.empty() || str == "_") return false; if (isNUM(str[0])) @@ -55,4 +56,21 @@ namespace nytl { return false; return true; } + + bool is_uname_dotted_sequence(const std::string& uinp) { + if (uinp.empty()) + return false; + std::vector r = {""}; + for (char ch: uinp) { + if (ch == '.') { + r.emplace_back(); + } else { + r.back() += ch; + } + } + for (const std::string& c: r) + if (!isUname(c)) + return false; + return true; + } } diff --git a/src/http_server/new_york_transit_line/alotalot.h b/src/http_server/new_york_transit_line/alotalot.h index 36ea1dc..491bf7b 100644 --- a/src/http_server/new_york_transit_line/alotalot.h +++ b/src/http_server/new_york_transit_line/alotalot.h @@ -22,11 +22,9 @@ namespace nytl { std::string prettyprint_errno(const std::string& pref); #define THROW(err) throw FUp(err, __FILE__, __func__, __LINE__) #define THROW_on_errno(err) THROW(prettyprint_errno(err)) -#define THROW_on_errno_pl() THROW(prettyprint_errno("")) #define ASSERT(cond, err) do { if (!(cond)) { THROW(err); } } while (0); #define ASSERT_pl(cond) ASSERT(cond, "Failed assertion `" #cond "`") #define ASSERT_on_iret(iret, err) ASSERT((iret) >= 0, prettyprint_errno(err)); -#define ASSERT_on_iret_pl(iret) ASSERT(iret >= 0, prettyprint_errno("")); bool endsIn(const std::string& a, const std::string& b); @@ -37,7 +35,8 @@ namespace nytl { bool isUNCHAR(char ch); bool isSPACE(char ch); - bool isUname(const std::string& str) noexcept; + bool isUname(const std::string& str); + bool is_uname_dotted_sequence(const std::string& uinp); } #endif diff --git a/src/http_server/new_york_transit_line/core.h b/src/http_server/new_york_transit_line/core.h index ff2f157..e3b698d 100644 --- a/src/http_server/new_york_transit_line/core.h +++ b/src/http_server/new_york_transit_line/core.h @@ -18,11 +18,11 @@ namespace nytl { /* No new JSON object will ever be created, I have one root json argument, * all the other json variables are subtrees of it */ - LocalVarValue rendering_core_execute_expression(global_elem_set_t& global_elems, - const std::vector& local_vars, json::JSON& expr); + LocalVarValue rendering_core_execute_expression(const global_elem_set_t& global_elems, + const std::vector& local_vars, const json::JSON& expr); std::string rendering_core(const std::string& entry_func, const std::vector& entry_arguments, - std::map& elem_ns, const std::function& escape); + const global_elem_set_t& elem_ns, const std::function& escape); } #endif 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 9d4f538..2655962 100644 --- a/src/http_server/new_york_transit_line/execute_expression.cpp +++ b/src/http_server/new_york_transit_line/execute_expression.cpp @@ -3,42 +3,88 @@ #include namespace nytl { - struct Frame { - json::JSON& expr; + struct EEFrame { + const json::JSON& expr; LocalVarValue& result; LocalVarValue temp_ret; size_t chain_el = 0; - Frame(json::JSON &expr, LocalVarValue &result) + EEFrame(const json::JSON &expr, LocalVarValue &result) : expr(expr), result(result) { } - uptr toMe(bool returned_from_bottom, global_elem_set_t& global_elems, + void descend(const json::JSON& what) { + if (result.is_json) { + const json::JSON& P = *result.JSON_subval; + if (P.isArray() && what.isInteger()) { + const std::vector arr_p = P.asArray(); + int64_t ind_w = what.asInteger().get_int(); + ASSERT(ind_w > 0 && ind_w < arr_p.size(), "Expression \"array[integer]\" caused out-of-bound situation"); + result = LocalVarValue{true, "", &arr_p[ind_w]}; + } else if (P.isDictionary() && what.isString()) { + const std::map dict_p = P.asDictionary(); + const std::string& key_w = what.asString(); + ASSERT(dict_p.count(key_w) == 1, "No such key exception"); + result = LocalVarValue{true, "", &dict_p.at(key_w)}; + } else + THROW("Incorrect type of \"json[json]\" expression. Unallowed signature of [] operator"); + } else { + ASSERT(what.isString(), "Expression \"element[X]\" allowed only if X is string (json object)"); + if (what.asString().empty()) + return; + if (!is_uname_dotted_sequence(what.asString())) + THROW("Incorrect X in \"element[X]\""); + result.EL_name += ("." + what.asString()); + } + } + + uptr toMe(bool returned, const global_elem_set_t& global_elems, const std::vector& local_vars) { - if (returned_from_bottom) { - ASSERT(temp_ret.is_json, "Expression \"[ element ]\" is not allowed"); + if (returned) { + ASSERT(temp_ret.is_json, "Expression \"X[ element ]\" is not allowed"); assert(temp_ret.JSON_subval); - // json::JSON - // todo: make usable const JSON + descend(*(temp_ret.JSON_subval)); } else { assert(expr.isDictionary()); - if ((*expr["V"]).isInteger()) { - size_t lv_ind = (*expr["V"]).asInteger().get_int(); + const json::JSON& val = expr["V"].g(); + if (val.isInteger()) { + size_t lv_ind = val.asInteger().get_int(); assert(lv_ind < local_vars.size()); result = local_vars[lv_ind]; - } else if ((*expr["V"]).isString()) { - std::string cur_el_name_str = (*expr["V"]).asString(); + } else if (val.isString()) { + std::string cur_el_name_str = expr["V"].g().asString(); result = LocalVarValue{false, cur_el_name_str, NULL}; } else assert(false); } - if (chain_el == (*expr["C"]).) + while (true) { + const std::vector& chain = expr["C"].g().asArray(); + if (chain_el >= chain.size()) + return NULL; + const json::JSON& t = chain[chain_el++]; + if (t.isDictionary()) + return std::make_unique(t, temp_ret); + descend(t); + } } }; - LocalVarValue rendering_core_execute_expression(global_elem_set_t& global_elems, - const std::vector& local_vars, json::JSON& expr) { - + LocalVarValue rendering_core_execute_expression(const global_elem_set_t& global_elems, + const std::vector& local_vars, const json::JSON& expr) { + bool returned = false; + std::vector> stack; + LocalVarValue result; + stack.push_back(std::make_unique(expr, result)); + while (!stack.empty()) { + EEFrame& cur = *stack.back(); + uptr todo = cur.toMe(returned, global_elems, local_vars); + returned = (bool)todo; + if (todo) + stack.push_back(todo); + else + stack.pop_back(); + } + return result; } } diff --git a/src/http_server/new_york_transit_line/rendering.cpp b/src/http_server/new_york_transit_line/rendering.cpp index f591fa9..21b17f2 100644 --- a/src/http_server/new_york_transit_line/rendering.cpp +++ b/src/http_server/new_york_transit_line/rendering.cpp @@ -1,47 +1,15 @@ #include "core.h" #include "alotalot.h" #include -#include +#include #include namespace nytl { - std::string rendering_core(const std::string& entry_func, const std::vector& entry_arguments, - std::map& elem_ns, const std::function& escape) - { - size_t cur_line_width = 0; + struct Ditch { std::string result; + size_t cur_line_width = 0; - struct Frame { - const Element* el = NULL; - /* Use by base elements to distinguish them */ - std::string base_name; - size_t part_to_do = 0; - std::vector passed_args; - /* This parameter incapsulates `cur_line_width` at some point for multiline `put-parts` */ - size_t multiline_put_start = 0; - }; - std::vector> stack; - - auto make_frame = [&](const std::string& name, std::vector passed_args){ - const Element* el = &elem_ns[name]; - Frame* us = new Frame{el, el->base ? name : "", 0, std::move(passed_args), cur_line_width}; - stack.push_back(uptr(us)); - }; - - ASSERT(elem_ns.count(entry_func) == 1, "No such element " + entry_func); - const Element& el = elem_ns[entry_func]; - ASSERT(el.arguments.size() == entry_arguments.size(), "Signature mismatch " + entry_func); - for (const json::JSON& sigtbj: el.arguments) { - ASSERT(sigtbj.type == json::true_symbol, "Signature mismatch. Entry element can take only JSON arguments"); - } - { - size_t AN = entry_arguments.size(); - std::vector entry_arguments_conv(AN); - for (size_t i = 0; i < AN; i++) - entry_arguments_conv[i] = {"", &entry_arguments[i]}; - make_frame(entry_func, entry_arguments_conv); - } - auto append2res = [&](const std::string& text) { + void append(const std::string& text) { size_t n = result.size(); result.resize(n + text.size()); memcpy((void*)&result.c_str()[n], text.c_str(), text.size()); @@ -51,48 +19,116 @@ namespace nytl { else cur_line_width++; } - }; - auto linefeed2res = [&]() { - result += "\n"; - cur_line_width = 0; - }; - while (!stack.empty()) { - Frame& cur = *stack.back(); - if (cur.el->base) { - assert(cur.passed_args.size() == 1); - const json::JSON* X = cur.passed_args[0].JSON_subval; - assert(X); - if (cur.base_name == "jesc") { - append2res(escape(json::generate_str(*X, json::print_pretty))); - } else if (cur.base_name == "str2text") { - ASSERT(X->isString(), "str2text takes json string"); - append2res(escape(X->asString())); - } else if (cur.base_name == "str2code") { - ASSERT(X->isString(), "str2code takes json string"); - append2res(X->asString()); + } + }; + +#define RFrame_passed const global_elem_set_t& elem_ns, Ditch& result, const std::function& escape + struct RFrame { + virtual uptr toMe(bool returned, RFrame_passed) {assert(false);} + + virtual ~RFrame() = default; + }; + + struct RFrame_OverParts : public RFrame{ + 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, + size_t multiline_put_start) + : name(name), + passed_args(passed_args), + multiline_put_start(multiline_put_start) { + } + + 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"); + } } - stack.pop_back(); - continue; } - if (cur.part_to_do == cur.el->parts.size()) { - stack.pop_back(); - continue; + 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; } - const ElementPart& cur_part = cur.el->parts[cur.part_to_do++]; - if (cur_part.type == element_part_types::code) { - const ElementPart::when_code_S& pt = cur_part.when_code; - append2res(pt.lines); - } else if (cur_part.type == element_part_types::put) { - const ElementPart::when_put_S& pt = cur_part.when_put; - // todo - } 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 + 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 + } } } - return result; + }; + + 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) + { + Ditch result; + + std::vector> stack; + + { + size_t AN = entry_arguments.size(); + 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); + } + while (!stack.empty()) { + // Frame& cur = *stack.back(); + + } + 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 afe157f..6e9e150 100644 --- a/src/http_server/new_york_transit_line/templater.cpp +++ b/src/http_server/new_york_transit_line/templater.cpp @@ -126,24 +126,9 @@ namespace nytl { } } - void check_uinp_element(const std::string& uinp) { - if (uinp.empty()) - THROW("empty???"); - std::vector r = {""}; - for (char ch: uinp) { - if (ch == '.') { - r.emplace_back(); - } else { - r.back() += ch; - } - } - for (const std::string& c: r) - ASSERT(isUname(c), "Incorrect name component"); - } - /* Still can throw some stuff derived from std::exception (like bad alloc) */ std::string Templater::render(const std::string& element, const std::vector &arguments) const { - check_uinp_element(element); - return rendering_core(element, arguments, elements); + ASSERT(is_uname_dotted_sequence(element), "Incorrect entry element name"); + return rendering_core(element, arguments, elements, settings.escape); } } diff --git a/src/http_server/new_york_transit_line/templater.h b/src/http_server/new_york_transit_line/templater.h index d0ebda3..fafd945 100644 --- a/src/http_server/new_york_transit_line/templater.h +++ b/src/http_server/new_york_transit_line/templater.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include "html_case.h" diff --git a/src/web_chat/main.cpp b/src/web_chat/main.cpp index f892ee4..dd07275 100644 --- a/src/web_chat/main.cpp +++ b/src/web_chat/main.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include