From 28dec5384c6fc2cf27b80ee8fb002c57e58bc6b8 Mon Sep 17 00:00:00 2001
From: Andreev Gregory <grinadand@gmail.com>
Date: Wed, 14 Aug 2024 13:59:54 +0300
Subject: [PATCH] FIXED SOME TABULATION BUGS, DO NOT LOOK AT MY PREVIOUS SYSTEM

---
 assets/HypertextPages/test.nytl.html          |  3 -
 .../new_york_transit_line/alotalot.cpp        |  5 ++
 .../new_york_transit_line/alotalot.h          |  8 +-
 src/http_server/new_york_transit_line/core.h  |  4 +
 .../new_york_transit_line/debug_print.cpp     | 49 ++++++++++++
 .../execute_expression.cpp                    |  1 +
 .../new_york_transit_line/parser.cpp          | 76 ++++++++++---------
 .../new_york_transit_line/rendering.cpp       | 13 ++--
 .../new_york_transit_line/templater.h         | 20 ++---
 .../nytl_tests/HypertextPages/test.nytl.html  |  8 ++
 src/http_server/nytl_tests/test0.cpp          | 11 ++-
 11 files changed, 132 insertions(+), 66 deletions(-)
 delete mode 100644 assets/HypertextPages/test.nytl.html
 create mode 100644 src/http_server/new_york_transit_line/debug_print.cpp
 create mode 100644 src/http_server/nytl_tests/HypertextPages/test.nytl.html

diff --git a/assets/HypertextPages/test.nytl.html b/assets/HypertextPages/test.nytl.html
deleted file mode 100644
index ff78735..0000000
--- a/assets/HypertextPages/test.nytl.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{% ELDEF main %}
-AAAAAAAA
-{% ENDELDEF %}
\ No newline at end of file
diff --git a/src/http_server/new_york_transit_line/alotalot.cpp b/src/http_server/new_york_transit_line/alotalot.cpp
index c1b12dd..c27206b 100644
--- a/src/http_server/new_york_transit_line/alotalot.cpp
+++ b/src/http_server/new_york_transit_line/alotalot.cpp
@@ -87,4 +87,9 @@ namespace nytl {
         }
         return result;
     }
+
+    void rstrip(std::string &str) {
+        while (!str.empty() && isSPACE(str.back()))
+            str.resize(str.size() - 1);
+    }
 }
diff --git a/src/http_server/new_york_transit_line/alotalot.h b/src/http_server/new_york_transit_line/alotalot.h
index 07a898f..8e8217c 100644
--- a/src/http_server/new_york_transit_line/alotalot.h
+++ b/src/http_server/new_york_transit_line/alotalot.h
@@ -1,12 +1,12 @@
 #ifndef NEW_YORK_TRANSIT_LINE_ALOTALOT_H
 #define NEW_YORK_TRANSIT_LINE_ALOTALOT_H
 
-/* A little of this, a little of that
- * DO NOT EXPORT THIS FILE */
-
 #include <stdexcept>
 #include <memory>
 
+/* A little of this, a little of that
+ * DO NOT EXPORT THIS FILE */
+
 namespace nytl {
     template<typename T>
     using uptr = std::unique_ptr<T>;
@@ -46,6 +46,8 @@ namespace nytl {
     bool is_uname_dotted_sequence(const std::string& uinp);
 
     std::string make_uppercase(const std::string& source);
+
+    void rstrip(std::string& str);
 }
 
 #endif
diff --git a/src/http_server/new_york_transit_line/core.h b/src/http_server/new_york_transit_line/core.h
index 49b4273..e03b649 100644
--- a/src/http_server/new_york_transit_line/core.h
+++ b/src/http_server/new_york_transit_line/core.h
@@ -4,7 +4,11 @@
 #include "templater.h"
 #include <functional>
 
+/* Do not export this header */
+
 namespace nytl {
+    void debug_print_templater(const Templater& T);
+
     /* ============== For parsing =============================*/
     void parse_bare_file(const std::string& filename, const std::string& content,
             global_elem_set_t& result);
diff --git a/src/http_server/new_york_transit_line/debug_print.cpp b/src/http_server/new_york_transit_line/debug_print.cpp
new file mode 100644
index 0000000..063f17d
--- /dev/null
+++ b/src/http_server/new_york_transit_line/debug_print.cpp
@@ -0,0 +1,49 @@
+#include "templater.h"
+#include "alotalot.h"
+#include "jsonincpp/string_representation.h"
+#include <assert.h>
+
+namespace nytl {
+    void debug_print_templater(const Templater& T) {
+        printf("===== TEMPLATER INTERNAL RESOURCES =====\n");
+        for (auto& p: T.elements) {
+            printf("=== %s element =====\n", p.first.c_str());
+            const Element& el = p.second;
+            printf("%s, %s\n", el.base ? "BASE" : "NOT BASE", el.is_hidden ? "HIDDEN" : "NOT HIDDEN");
+            if (!el.is_hidden) {
+                std::string signature;
+                for (const json::JSON& arg_type: el.arguments) {
+                    if (!signature.empty())
+                        signature += "  ";
+                    signature += json::generate_str(arg_type, json::print_compact);
+                }
+                printf("Signature: %s\n", signature.c_str());
+            }
+            for (const ElementPart& part: el.parts) {
+                if (part.type == ElementPart::p_code) {
+                    printf("code:   <b><e><f><o><r><e><><l><f>\n%s\n<a><f><t><e><r><><l><f>\n", part.when_code.lines.c_str());
+                } else if (part.type == ElementPart::p_for_put) {
+                    const ElementPart::when_for_put_S& P = part.when_for_put;
+                    printf("dor cycle call:\ninternal_element: %s,\nref_over:%s,\nwhere_key_var: %ld, where_value_var: %ld, %s\n",
+                        P.internal_element.c_str(), json::generate_str(P.ref_over, json::print_pretty).c_str(),
+                        P.where_key_var, P.where_value_var, P.line_feed ? "LF" : "NOLF");
+                } else if (part.type == ElementPart::p_ref_put) {
+                    const ElementPart::when_ref_put_S& P = part.when_ref_put;
+                    printf("ref block call:\ninternal_element: %s\nref_over:%s\n",
+                        P.internal_element.c_str(), json::generate_str(P.ref_over, json::print_pretty).c_str());
+                } else {
+                   assert(part.type == ElementPart::p_put);
+                    const ElementPart::when_put_S& P = part.when_put;
+                    printf("PUT:\ncalled_element: %s\n",
+                        json::generate_str(P.called_element, json::print_pretty).c_str());
+                    for (size_t i = 0; i < P.passed_arguments.size(); i++) {
+                        printf("passed_arguments[%lu] = %s\n", i,
+                            json::generate_str(P.passed_arguments[i], json::print_pretty).c_str());
+                    }
+                }
+            }
+            printf("=== That was element %s ====\n", p.first.c_str());
+        }
+        printf("===== DEBUG IS OVER =====\n");
+    }
+}
\ No newline at end of file
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 440918d..1d7a371 100644
--- a/src/http_server/new_york_transit_line/execute_expression.cpp
+++ b/src/http_server/new_york_transit_line/execute_expression.cpp
@@ -3,6 +3,7 @@
 #include <assert.h>
 
 namespace nytl {
+    /* Expression Execution Frame */
     struct EEFrame {
         const json::JSON& expr;
         LocalVarValue& result;
diff --git a/src/http_server/new_york_transit_line/parser.cpp b/src/http_server/new_york_transit_line/parser.cpp
index dbc69da..da8b884 100644
--- a/src/http_server/new_york_transit_line/parser.cpp
+++ b/src/http_server/new_york_transit_line/parser.cpp
@@ -16,11 +16,6 @@ namespace nytl {
         return first_nw_char(str) == str.size();
     }
 
-    void rstrip(std::string& str) {
-        while (!str.empty() && isSPACE(str.back()))
-            str.resize(str.size() - 1);
-    }
-
     std::string clement_lstrip(const std::string& str) {
         size_t gone = 0;
         size_t n = str.size();
@@ -28,10 +23,10 @@ namespace nytl {
             if (str[i] == '\n') {
                 gone = i + 1;
             } else if (!isSPACE(str[i])) {
-                return str.substr(gone);
+                break;
             }
         }
-        return "";
+        return str.substr(gone);
     }
 
     struct ParsingContext {
@@ -125,22 +120,29 @@ namespace nytl {
         return result;
     }
 
-    void one_part_update_min_start_wsp_non_empty(const std::string& str, bool is_first, size_t& min) {
+    bool is_relevant_in_tab_cut(size_t PN, size_t I, size_t LN, size_t j, const std::string& line) {
+        if (j == 0 && I != 0)
+            return false;
+        if (!is_space_only(line))
+            return true;
+        return j + 1 == LN && I + 1 < PN;
+    }
+
+    void one_part_update_min_start_wsp_non_empty(const std::string& str, size_t I, size_t PN, size_t& min) {
         std::vector<std::string> lines = splitIntoLines(str);
-        size_t L = lines.size();
-        for (size_t i = is_first ? 0 : 1; i < L; i++) {
-            size_t first_nw = first_nw_char(lines[i]);
-            if (first_nw < lines[i].size())
-                min = std::min(min, first_nw);
+        size_t LN = lines.size();
+        for (size_t j = 0; j < LN; j++) {
+            if (is_relevant_in_tab_cut(PN, I, LN, j, lines[j]))
+                min = std::min(min, first_nw_char(lines[j]));
         }
     }
 
-    std::string one_part_cut_excess_tab(const std::string& str, bool is_first, size_t cut) {
+    std::string one_part_cut_excess_tab(const std::string& str, size_t I, size_t PN, size_t cut) {
         std::vector<std::string> lines = splitIntoLines(str);
-        size_t L = lines.size();
-        for (size_t i = is_first ? 0 : 1; i < L; i++) {
-            if (!is_space_only(lines[i]))
-                lines[i] = lines[i].substr(cut);
+        size_t LN = lines.size();
+        for (size_t j = 0; j < LN; j++) {
+            if (is_relevant_in_tab_cut(PN, I, LN, j, lines[j]))
+                lines[j] = lines[j].substr(cut);
         }
         return concatenateLines(lines);
     }
@@ -149,17 +151,17 @@ namespace nytl {
                              global_elem_set_t& result)
     {
         ASSERT(result.count(filename) == 0, "Repeated element " + filename);
-        std::string P = clement_lstrip(content);
-        rstrip(P);
+        std::string txt = clement_lstrip(content);
+        rstrip(txt);
         size_t cut = 9999999999999;
-        one_part_update_min_start_wsp_non_empty(P, true, cut);
-        P = one_part_cut_excess_tab(P, true, cut);
+        one_part_update_min_start_wsp_non_empty(txt, 0, 1, cut);
+        txt = one_part_cut_excess_tab(txt, 0, 1, cut);
         Element& el = result[filename];
-        el.parts = {ElementPart{element_part_types::code}};
-        el.parts[0].when_code.lines = mv(P);
+        el.parts = {ElementPart{}};
+        el.parts[0].when_code.lines = mv(txt);
     }
 
-    /* This function parses variable type */
+    /* Type parsing frame */
     struct TPFrame {
         json::JSON& result;
 
@@ -206,6 +208,7 @@ namespace nytl {
     /* From arg name to arg ID */
     typedef std::map<std::string, size_t> arg_name_list_t;
 
+    /* Expression parsing frame */
     struct EPFrame {
         json::JSON& result;
 
@@ -295,6 +298,7 @@ namespace nytl {
         return peep(ctx) == syntax.magic_block_end[0];
     }
 
+    /* Element content parsing frame */
     struct ECPFrame {
         enum block_type{
             gone_for_nothing,
@@ -322,7 +326,7 @@ namespace nytl {
         uptr<ECPFrame> 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);
+                    assert(result.parts.back().type == ElementPart::p_for_put);
                     if (ret_data_int == 1)
                         result.parts.back().when_for_put.line_feed = false;
                     else if (ret_data_int == 2)
@@ -344,7 +348,7 @@ namespace nytl {
             std::string op = make_uppercase(readName(ctx));
             if (op == "FOR") {
                 result.parts.emplace_back();
-                result.parts.back().type = element_part_type_t::for_put;
+                result.parts.back().type = ElementPart::p_for_put;
                 ElementPart::when_for_put_S& P = result.parts.back().when_for_put;
                 skipWhitespace(ctx);
                 std::string V1 = readName(ctx);
@@ -387,7 +391,7 @@ namespace nytl {
             }
             if (op == "REF") {
                 result.parts.emplace_back();
-                result.parts.back().type = element_part_type_t::ref_put;
+                result.parts.back().type = ElementPart::p_ref_put;
                 ElementPart::when_ref_put_S& P = result.parts.back().when_ref_put;
                 skipWhitespace(ctx);
                 std::string Vn = readName(ctx);
@@ -411,7 +415,7 @@ namespace nytl {
             }
             if (op == "PUT") {
                 result.parts.emplace_back();
-                result.parts.back().type = element_part_type_t::put;
+                result.parts.back().type = ElementPart::p_put;
                 ElementPart::when_put_S& P = result.parts.back().when_put;
                 skipWhitespace(ctx);
                 P.called_element = parse_expression(ctx, local_var_names);
@@ -427,7 +431,7 @@ namespace nytl {
             }
             auto mediocre_operator = [&](const std::string& base_el) -> void {
                 result.parts.emplace_back();
-                result.parts.back().type = element_part_type_t::put;
+                result.parts.back().type = ElementPart::p_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);
@@ -446,20 +450,20 @@ namespace nytl {
                 ASSERT(myself == gone_for_nothing, "Unexpected end of element");
                 skip_magic_block_end(ctx, syntax);
                 if (!result.parts.empty()) {
-                    if (result.parts[0].type == element_part_type_t::code)
+                    if (result.parts[0].type == ElementPart::p_code)
                         result.parts[0].when_code.lines = clement_lstrip(result.parts[0].when_code.lines);
-                    if (result.parts.back().type == element_part_type_t::code)
+                    if (result.parts.back().type == ElementPart::p_code)
                         rstrip(result.parts.back().when_code.lines);
                     size_t cut = 999999999999;
                     size_t N = result.parts.size();
                     for (size_t i = 0; i < N; i++) {
-                        if (result.parts[i].type == element_part_type_t::code) {
-                            one_part_update_min_start_wsp_non_empty(result.parts[i].when_code.lines, i == 0, cut);
+                        if (result.parts[i].type == ElementPart::p_code) {
+                            one_part_update_min_start_wsp_non_empty(result.parts[i].when_code.lines, i, N, cut);
                         }
                     }
                     for (size_t i = 0; i < N; i++) {
-                        if (result.parts[i].type == element_part_type_t::code) {
-                            result.parts[i].when_code.lines = one_part_cut_excess_tab(result.parts[i].when_code.lines, i == 0, cut);
+                        if (result.parts[i].type == ElementPart::p_code) {
+                            result.parts[i].when_code.lines = one_part_cut_excess_tab(result.parts[i].when_code.lines, i, N, cut);
                         }
                     }
                 }
diff --git a/src/http_server/new_york_transit_line/rendering.cpp b/src/http_server/new_york_transit_line/rendering.cpp
index f9ab229..1aa3f26 100644
--- a/src/http_server/new_york_transit_line/rendering.cpp
+++ b/src/http_server/new_york_transit_line/rendering.cpp
@@ -114,6 +114,7 @@ namespace nytl {
             const std::function<std::string(std::string)> &escape) override;
     };
 
+    /* Rendering Frame */
     uptr<RFrame> RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
         const std::function<std::string(std::string)> &escape) {
         if (!returned)
@@ -147,7 +148,9 @@ namespace nytl {
             const json::JSON* X = passed_args[0].JSON_subval;
             assert(X);
             if (name == "jesc") {
-                append(escape(json::generate_str(*X, json::print_pretty)), result);
+                std::string escaped_json = escape(json::generate_str(*X, json::print_pretty));
+                rstrip(escaped_json);
+                append(escaped_json, result);
             } else if (name == "str2text") {
                 ASSERT(X->isString(), "str2text takes json string");
                 append(escape(X->asString()), result);
@@ -161,10 +164,10 @@ namespace nytl {
             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) {
+            if (cur_part.type == ElementPart::p_code) {
                 const ElementPart::when_code_S& pt = cur_part.when_code;
                 append(pt.lines, result);
-            } else if (cur_part.type == element_part_types::put) {
+            } else if (cur_part.type == ElementPart::p_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");
@@ -174,7 +177,7 @@ namespace nytl {
                     passed_arguments_expv[i] = rendering_core_execute_expression(elem_ns, passed_args, pt.passed_arguments[i]);
                 return std::make_unique<RFrame_OverParts>(called_element_expv.EL_name, passed_arguments_expv,
                                                           result.cur_line_width);
-            } else if (cur_part.type == element_part_types::for_put) {
+            } else if (cur_part.type == ElementPart::p_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");
@@ -185,7 +188,7 @@ namespace nytl {
                     return std::make_unique<RFrame_OverDictionary>(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) {
+            } else if (cur_part.type == ElementPart::p_ref_put) {
                 const ElementPart::when_ref_put_S& pt = cur_part.when_ref_put;
                 std::vector<LocalVarValue> more_variables(passed_args.size() + 1);
                 std::copy(passed_args.begin(), passed_args.end(), more_variables.begin());
diff --git a/src/http_server/new_york_transit_line/templater.h b/src/http_server/new_york_transit_line/templater.h
index ae3451f..d1018d9 100644
--- a/src/http_server/new_york_transit_line/templater.h
+++ b/src/http_server/new_york_transit_line/templater.h
@@ -10,21 +10,15 @@
 namespace nytl {
     typedef json::JSON expression_t;
 
-    namespace element_part_types {
-        enum element_part_type_E {
-            code,
-            /* write statements really mean PUT str2text X */
-            put,
-            for_put,
-            ref_put
-        };
-    }
-
-    typedef element_part_types::element_part_type_E element_part_type_t;
-
     struct ElementPart {
         /* Used with all types */
-        element_part_type_t type = element_part_types::code;
+        enum element_part_type_E {
+            p_code,
+            /* write statements really mean PUT str2text X */
+            p_put,
+            p_for_put,
+            p_ref_put
+        } type = p_code;
         struct when_code_S {
             std::string lines;
         } when_code;
diff --git a/src/http_server/nytl_tests/HypertextPages/test.nytl.html b/src/http_server/nytl_tests/HypertextPages/test.nytl.html
new file mode 100644
index 0000000..90c1f6f
--- /dev/null
+++ b/src/http_server/nytl_tests/HypertextPages/test.nytl.html
@@ -0,0 +1,8 @@
+{% ELDEF main JSON abc %}
+        {% PUT jesc abc %}
+
+        AAAAAAAA
+    BBB
+        {% PUT jesc abc %}
+
+{% ENDELDEF %}
\ No newline at end of file
diff --git a/src/http_server/nytl_tests/test0.cpp b/src/http_server/nytl_tests/test0.cpp
index 9d930f8..6d9732d 100644
--- a/src/http_server/nytl_tests/test0.cpp
+++ b/src/http_server/nytl_tests/test0.cpp
@@ -1,6 +1,5 @@
 #include <new_york_transit_line/templater.h>
-
-/* Yep, tests for nytl depend on assets for website. Yep, I see no problem with that */
+#include <new_york_transit_line/core.h>
 
 int main(int argc, char** argv) {
     if (argc < 2) {
@@ -11,10 +10,10 @@ int main(int argc, char** argv) {
     std::string dir_path = argv[1];
     nytl::Templater templater(nytl::TemplaterSettings{nytl::TemplaterDetourRules{dir_path}});
     templater.update();
-    std::string answer = templater.render("list-rooms", {});
-    printf("%s\n<a><f><t><e><r><><l><f>\n", answer.c_str());
-    std::string answer2 = templater.render("test", {});
-    printf("%s\n<a><f><t><e><r><><l><f>\n", answer.c_str());
+    nytl::debug_print_templater(templater);
+
+    std::string answer2 = templater.render("test", {json::JSON()});
+    printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str());
 
     return 0;
 }
\ No newline at end of file