From 83c17c22e6787be1fffcc763d41ee649220a04bc Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Wed, 14 Aug 2024 00:26:13 +0300 Subject: [PATCH] Daily update --- .../new_york_transit_line/alotalot.cpp | 14 + .../new_york_transit_line/alotalot.h | 3 + src/http_server/new_york_transit_line/core.h | 16 +- .../new_york_transit_line/parser.cpp | 349 +++++++++++++++++- .../new_york_transit_line/rendering.cpp | 31 +- .../new_york_transit_line/templater.h | 1 + 6 files changed, 390 insertions(+), 24 deletions(-) diff --git a/src/http_server/new_york_transit_line/alotalot.cpp b/src/http_server/new_york_transit_line/alotalot.cpp index 517b270..c1b12dd 100644 --- a/src/http_server/new_york_transit_line/alotalot.cpp +++ b/src/http_server/new_york_transit_line/alotalot.cpp @@ -42,6 +42,10 @@ namespace nytl { return isALPHA(ch) || isNUM(ch) || ch == '-' || ch == '_'; } + bool isUNCHARnonNUM(char ch) { + return isALPHA(ch) || ch == '-' || ch == '_'; + } + bool isSPACE(char ch) { return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n'; } @@ -73,4 +77,14 @@ namespace nytl { return false; return true; } + + std::string make_uppercase(const std::string &source) { + std::string result(source); + for (size_t i = 0; i < source.size(); i++) { + char ch = source[i]; + if ('a' <= ch && ch <= 'z') + result[i] = (char)(ch - 'a' + 'A'); + } + return result; + } } diff --git a/src/http_server/new_york_transit_line/alotalot.h b/src/http_server/new_york_transit_line/alotalot.h index a78344a..07a898f 100644 --- a/src/http_server/new_york_transit_line/alotalot.h +++ b/src/http_server/new_york_transit_line/alotalot.h @@ -39,10 +39,13 @@ namespace nytl { bool isALPHA(char ch); bool isNUM(char ch); bool isUNCHAR(char ch); + bool isUNCHARnonNUM(char ch); bool isSPACE(char ch); bool isUname(const std::string& str); bool is_uname_dotted_sequence(const std::string& uinp); + + std::string make_uppercase(const std::string& source); } #endif diff --git a/src/http_server/new_york_transit_line/core.h b/src/http_server/new_york_transit_line/core.h index 384bbdc..b567ac5 100644 --- a/src/http_server/new_york_transit_line/core.h +++ b/src/http_server/new_york_transit_line/core.h @@ -13,17 +13,23 @@ namespace nytl { size_t line = 0; }; -#ifdef EOFVAL -#error Son in my shift -#endif -#define EOFVAL -999 + constexpr int EOFVAL = -999; + int peep(ParsingContext& ctx); - void skip(ParsingContext& ctx); + char skip(ParsingContext& ctx); void skip(ParsingContext& ctx, char ch); void skipWhitespace(ParsingContext& ctx); + void skipString(ParsingContext& ctx, const std::string& str); + + /* Returns empty if what is ahead of us is not name */ + std::string readName(ParsingContext& ctx); + + /* Returns empty if what is ahead of us is not uint */ + std::string readUint(ParsingContext& ctx); + std::vector splitIntoLines(const std::string& str); std::string concatenateLines(const std::vector& lines); void one_part_update_min_start_wsp_non_empty(const std::string& str, bool is_first, size_t& min); diff --git a/src/http_server/new_york_transit_line/parser.cpp b/src/http_server/new_york_transit_line/parser.cpp index 9fb7b5b..db43dd7 100644 --- a/src/http_server/new_york_transit_line/parser.cpp +++ b/src/http_server/new_york_transit_line/parser.cpp @@ -40,19 +40,19 @@ namespace nytl { return ctx.text[ctx.pos]; } - void advance(ParsingContext& ctx) { + char advance(ParsingContext& ctx) { if (ctx.text[ctx.pos] == '\n') { ctx.line++; ctx.column = 0; } else { ctx.column++; } - ctx.pos++; + return ctx.text[ctx.pos++]; } - void skip(ParsingContext& ctx) { + char skip(ParsingContext& ctx) { ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF"); - advance(ctx); + return advance(ctx); } void skip(ParsingContext& ctx, char ch) { @@ -62,10 +62,38 @@ namespace nytl { } void skipWhitespace(ParsingContext &ctx) { - while (peep(ctx) > 0 && isSPACE((char)peep(ctx))) + while (peep(ctx) >= 0 && isSPACE((char)peep(ctx))) skip(ctx); } + void skipString(ParsingContext &ctx, const std::string &str) { + for (char ch: str) + skip(ctx, ch); + } + + std::string readName(ParsingContext &ctx) { + std::string result; + int f = peep(ctx); + if (f >= 0 && isUNCHARnonNUM((char)f)) { + skip(ctx); + result += (char)f; + while (peep(ctx) >= 0 && isUNCHAR((char)peep(ctx))) + result += skip(ctx); + } + return result; + } + + std::string readUint(ParsingContext &ctx) { + if (peep(ctx) == '0') { + skip(ctx); + return "0"; + } + std::string result; + while (peep(ctx) >= 0 && isNUM((char)peep(ctx))) + result += skip(ctx); + return result; + } + std::vector splitIntoLines(const std::string &str) { std::vector result = {""}; for (char ch: str) { @@ -122,6 +150,317 @@ namespace nytl { el.parts[0].when_code.lines = mv(P); } + /* This function parses variable type */ + struct TPFrame { + json::JSON& result; + + explicit TPFrame(json::JSON& result_): result(result_){} + + uptr toMe(bool returned, ParsingContext& ctx) { + if (!returned) { + std::string nm = readName(ctx); + ASSERT(!nm.empty, "Type specification expected"); + nm = make_uppercase(nm); + if (nm == "JSON") { + result = json::JSON(true); + return NULL; + } + ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)") + skip(ctx, '('); + result = json::JSON(json::array); + assert(result.isArray()); + } + skipWhitespace(ctx); + if (peep(ctx) == ')') + return NULL; + result.asArray().emplace_back(); + return std::make_unique(result.asArray().back()); + } + }; + + json::JSON parse_type(ParsingContext& ctx) { + json::JSON result; + std::vector> stack; + stack.push_back(mv(std::make_unique(result))); + bool returned = false; + while (!stack.empty) { + uptr ret = stack.back()->toMe(returned, ctx); + returned = !(bool)ret; + if (ret) + stack.push_back(mv(ret)); + else + stack.pop_back(); + } + return result; + } + + /* From arg name to arg ID */ + typedef std::map arg_name_list_t; + + struct EPFrame { + json::JSON& result; + + explicit EPFrame(json::JSON& result_): result(result_){} + + uptr toMe(bool returned, ParsingContext& ctx, const arg_name_list_t& local_var_names) { + if (!returned) { + std::string first = readName(ctx); + ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable"); + ASSERT(first != "_", "_ ??? ARE YOU KIDDING???"); + if (local_var_names.count(first) == 1) { + result["V"] = json::JSON(json::Integer((int64_t)local_var_names.at(first))); + } else { + result["V"] = json::JSON(first); + } + result["C"] = json::JSON(json::array); + } else { + skipWhitespace(ctx); + skip(ctx, ']'); + } + std::vector& chain = result["C"].g().asArray(); + while (true) { + if (peep(ctx) == '.') { + skip(ctx, '.'); + chain.emplace_back(); + std::string t; + t = readName(ctx); + if (!t.empty()) { + chain.back() = json::JSON(t); + continue; + } + t = readUint(ctx); + if (!t.empty()) { + size_t v = std::stoul(t); + ASSERT(v < INT64_MAX, "Index is too big"); + chain.back() = json::JSON((int64_t)v); + continue; + } + THROW("Bad expression after . operator in expression"); + } else if (peep(ctx) == '[') { + skip(ctx, '['); + skipWhitespace(ctx); + chain.emplace_back(); + return std::make_unique(chain.back()); + } else + return NULL; + } + } + }; + + json::JSON parse_expression(ParsingContext& ctx, const arg_name_list_t& local_var_names) { + json::JSON result; + std::vector> stack; + stack.push_back(mv(std::make_unique(result))); + bool returned = false; + while (!stack.empty) { + uptr ret = stack.back()->toMe(returned, ctx, local_var_names); + returned = !(bool)ret; + if (ret) + stack.push_back(mv(ret)); + else + stack.pop_back(); + } + return result; + } + + std::string read_code_up_to_mag_block_start(ParsingContext& ctx, const TemplaterSettings& syntax) { + size_t begin = ctx.pos; + while (peep(ctx) != EOFVAL && peep(ctx) != syntax.magic_block_start[0]) { + skip(ctx); + } + size_t end = ctx.pos; + return ctx.text.substr(begin, end - begin); + } + + void skip_magic_block_start(ParsingContext& ctx, const TemplaterSettings& syntax) { + skipString(ctx, syntax.magic_block_start); + skipWhitespace(ctx); + } + + void skip_magic_block_end(ParsingContext& ctx, const TemplaterSettings& syntax) { + skipWhitespace(ctx); + skipString(ctx, syntax.magic_block_end); + } + + bool isIt_magic_block_end(ParsingContext& ctx, const TemplaterSettings& syntax) { + return peep(ctx) == syntax.magic_block_start[0]; + } + + struct ECPFrame { + enum block_type{ + gone_for_nothing, + gone_for_for, + gone_for_ref, + }; + std::string el_name; + block_type myself; + arg_name_list_t local_var_names; + int& ret_data_int; // Received from the top (and passed down to get for_put LF mode value) + Element& result; + + block_type stopped_for = gone_for_nothing; + size_t free_hidden = 0; + + ECPFrame(const std::string& el_name, block_type myself, const arg_name_list_t &local_var_names, int &ret_data_int, + Element& result) + : el_name(el_name), + myself(myself), + local_var_names(local_var_names), + ret_data_int(ret_data_int), + result(result) { + } + + uptr toMe(bool returned, ParsingContext& ctx, const TemplaterSettings& syntax, global_elem_set_t& elem_ns) { + if (returned) { + if (stopped_for == gone_for_for) { + assert(result.parts.back().type == element_part_type_t::for_put); + if (ret_data_int == 1) + result.parts.back().when_for_put.line_feed = false; + else if (ret_data_int == 2) + result.parts.back().when_for_put.line_feed = true; + else + assert(false); + } + // skip_magic_block_start(ctx, syntax); + // std::string ender = make_uppercase(readName(ctx)); + // if (gone_for == gone_for_for) { + // ASSERT(ender == "ENDFOR", "Expected ENDFOR"); + // skipWhitespace(ctx); + // std::string lf_arg = make_uppercase(readName(ctx)); + // if (lf_arg == "LF") { + // result.back().when_for_put.line_feed = true; + // } else if (lf_arg == "NOLF") { + // result.back().when_for_put.line_feed = false; + // } else + // ASSERT(lf_arg == "", "Expected nothing, LF or NOLF"); + // skip_magic_block_end(ctx, syntax); + // } else if (gone_for == gone_for_ref) { + // skip_magic_block_start(ctx, syntax); + // ASSERT(ender == "ENDREF", "Expected ENDREF"); + // } else + // assert(false); + // skip_magic_block_end(ctx, syntax); + } + ya_e_ya_h_i_ya_g_d_o: + result.parts.emplace_back(); + result.parts.back().when_code.lines = read_code_up_to_mag_block_start(ctx, syntax); + skip_magic_block_start(ctx, syntax); + if (isIt_magic_block_end(ctx, syntax)) { + skip_magic_block_end(ctx, syntax); + goto ya_e_ya_h_i_ya_g_d_o; + } + std::string op = make_uppercase(readName(ctx)); + if (op == "FOR") { + result.parts.emplace_back(); + result.parts.back().type = element_part_type_t::for_put; + ElementPart::when_for_put_S& P = result.parts.back().when_for_put; + skipWhitespace(ctx); + std::string V1 = readName(ctx); + ASSERT(!V1.empty(), "Expected variable name"); + skipWhitespace(ctx); + bool have_colon_and_2 = false; + std::string V2; + if (peep(ctx) == ':') { + have_colon_and_2 = true; + skip(ctx, ':'); + skipWhitespace(ctx); + V2 = readName(ctx); + skipWhitespace(ctx); + } + op = make_uppercase(readName(ctx)); + ASSERT(op == "IN", "Expected IN"); + skipWhitespace(ctx); + P.ref_over = parse_expression(ctx, local_var_names); + P.internal_element = el_name + ".~" + std::to_string(free_hidden++); + Element& newborn = elem_ns[P.internal_element]; + newborn.is_hidden = true; + arg_name_list_t local_var_names_of_nxt = local_var_names; + if (V1 != "_") { + ASSERT(local_var_names_of_nxt.count(V1) == 0, "Repeated local variable"); + size_t k = local_var_names_of_nxt.size(); + local_var_names_of_nxt.emplace(V1, k); + (have_colon_and_2 ? P.where_key_var : P.where_value_var) = (ssize_t)k; + } + if (have_colon_and_2 && V2 != "_") { + ASSERT(local_var_names_of_nxt.count(V2) == 0, "Repeated local variable"); + size_t k = local_var_names_of_nxt.size(); + local_var_names_of_nxt.emplace(V2, k); + P.where_value_var = (ssize_t)k; + } + skip_magic_block_end(ctx, syntax); + /* Yep, I am passing this int random data reference, that was actually given to me, I CAN DO THAT TRUST ME */ + stopped_for = gone_for_for; + return std::make_unique(P.internal_element, gone_for_for, local_var_names_of_nxt, + ret_data_int, newborn); + } + if (op == "REF") { + result.parts.emplace_back(); + result.parts.back().type = element_part_type_t::ref_put; + ElementPart::when_ref_put_S& P = result.parts.back().when_ref_put; + skipWhitespace(ctx); + std::string Vn = readName(ctx); + ASSERT(!Vn.empty(), "Expected variable name"); + ASSERT(Vn != "_", "Are you kidding???"); + skipWhitespace(ctx); + op = make_uppercase(readName(ctx)); + ASSERT(op == "AS", "Expected AS"); + skipWhitespace(ctx); + P.ref_over = parse_expression(ctx, local_var_names); + P.internal_element = el_name + ".~" + std::to_string(free_hidden++); + Element& newborn = elem_ns[P.internal_element]; + newborn.is_hidden = true; + arg_name_list_t local_var_names_of_nxt = local_var_names; + size_t k = local_var_names_of_nxt.size(); + local_var_names_of_nxt.emplace(Vn, k); + skip_magic_block_end(ctx, syntax); + stopped_for = gone_for_ref; + return std::make_unique(P.internal_element, gone_for_ref, local_var_names_of_nxt, + ret_data_int, newborn); + } + if (op == "PUT") { + result.parts.emplace_back(); + result.parts.back().type = element_part_type_t::put; + ElementPart::when_put_S& P = result.parts.back().when_put; + skipWhitespace(ctx); + P.called_element = parse_expression(ctx, local_var_names); + while (true) { + skipWhitespace(ctx); + if (isIt_magic_block_end(ctx, syntax)) { + skip_magic_block_end(ctx, syntax); + break; + } + P.passed_arguments.push_back(parse_expression(ctx, local_var_names)); + } + goto ya_e_ya_h_i_ya_g_d_o; + } + auto mediocre_operator = [&](const std::string& base_el) -> void { + result.parts.emplace_back(); + result.parts.back().type = element_part_type_t::put; + ElementPart::when_put_S& P = result.parts.back().when_put; + P.called_element["V"] = json::JSON(base_el); + P.called_element["C"] = json::JSON(json::array); + skipWhitespace(ctx); + }; + // todo REF, PUT, WRITE, ROUGHINSERT todo enders todo else (error) + } + }; + + void parse_element_content(const std::string& el_name, ParsingContext& ctx, const TemplaterSettings& syntax, + const arg_name_list_t& local_var_names, Element& result, global_elem_set_t& elem_ns) { + int random_junk; // Used onlt for for_put blocks + std::vector> stack; + stack.push_back(mv(std::make_unique(el_name, ECPFrame::gone_for_nothing, local_var_names, random_junk, result))); + bool returned = false; + while (!stack.empty()) { + uptr ret = stack.back()->toMe(returned, ctx, syntax, elem_ns); + returned = !(bool)ret; + if (ret) + stack.push_back(mv(ret)); + else + stack.pop_back(); + } + } + void parse_special_file(const std::string& filename, const std::string& content, std::map& result) { diff --git a/src/http_server/new_york_transit_line/rendering.cpp b/src/http_server/new_york_transit_line/rendering.cpp index 5d1ffe5..f9ab229 100644 --- a/src/http_server/new_york_transit_line/rendering.cpp +++ b/src/http_server/new_york_transit_line/rendering.cpp @@ -121,20 +121,23 @@ namespace nytl { 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"); + /* hidden elements (internal) do not need any check */ + if (!el.is_hidden) { + 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"); + } } } } diff --git a/src/http_server/new_york_transit_line/templater.h b/src/http_server/new_york_transit_line/templater.h index 0e247f2..ae3451f 100644 --- a/src/http_server/new_york_transit_line/templater.h +++ b/src/http_server/new_york_transit_line/templater.h @@ -50,6 +50,7 @@ namespace nytl { std::vector arguments; /* `base` is true for builtin elements (jesc str2code str2text). Parts for such ' are empty */ bool base = false; + bool is_hidden = false; std::vector parts; };