Daily saving of progress

This commit is contained in:
Андреев Григорий 2024-08-10 23:56:07 +03:00
parent 3a8d1207b1
commit c264e3802b
15 changed files with 671 additions and 18 deletions

View File

@ -108,10 +108,39 @@ struct CAWebChat {
T.installation_dir = "";
my_targets.push_back(T);
}
{ CTarget T{"new_york_transit_line", "shared_library"};
T.additional_compilation_flags = getSomeRadFlags();
T.external_deps = {
CTargetDependenceOnExternalLibrary{"libjsonincpp", {true, true}},
};
T.units = {
"alotalot.cpp",
"execute_expression.cpp",
"html_case.cpp",
"parser.cpp",
"rendering.cpp",
"templater.cpp",
};
for (std::string& u: T.units)
u = "http_server/new_york_transit_line/" + u;
T.include_pr = "http_server";
T.exported_headers = {
"templater.h",
"html_case.h",
};
for (std::string& u: T.exported_headers)
u = "new_york_transit_line/" + u;
my_targets.push_back(T);
}
{ CTarget T{"iu9-ca-web-chat", "executable"};
T.additional_compilation_flags = getSomeRadFlags();
T.proj_deps = {CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"}};
T.external_deps = {CTargetDependenceOnExternalLibrary{"sqlite3"}};
T.proj_deps = {
CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"},
CTargetDependenceOnProjectsLibrary{"new_york_transit_line"},
};
T.external_deps = {
CTargetDependenceOnExternalLibrary{"sqlite3"}
};
T.units = {"main.cpp"};
for (std::string& u: T.units)
u = "web_chat/" + u;

View File

@ -5,15 +5,9 @@
#include <string.h>
namespace een9 {
ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line): err(err),
FILE(file),
func(func),
LINE(line) {
char buf[4096];
snprintf(buf, 4096, "Error occured in function %s (line %d of %s)\n"
"Error: %s",
func.c_str(), LINE, FILE.c_str(), err.c_str());
WHAT = buf;
ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line){
WHAT = "Error occured in function " + func + " (line " + std::to_string(line) + " of " +
file + ")\nError: " + err;
}
const char * ServerError::what() const noexcept {

View File

@ -6,10 +6,6 @@
namespace een9 {
class ServerError : public std::exception{
std::string err;
std::string FILE;
std::string func;
int LINE;
std::string WHAT;
public:

View File

@ -23,9 +23,9 @@ namespace een9 {
ASSERT_on_iret(ret, "stat(\"" + cur + "\")");
if (S_ISDIR(info.st_mode)) {
DIR* D = opendir(path_to_cur_ent.c_str());
cur += "/";
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D};
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
cur += "/";
while (true) {
errno = 0;
struct dirent* Dent = readdir(D);

View File

@ -55,7 +55,7 @@ namespace een9 {
while ((ret = (int)read(fd, buf, 2048)) > 0) {
size_t oldN = result.size();
result.resize(oldN + ret);
memcpy(&result[oldN], buf, ret);
memcpy((void*)&result.c_str()[oldN], buf, ret);
}
ASSERT_on_iret(ret, "Reading from " + description);
}

View File

@ -0,0 +1,58 @@
#include "alotalot.h"
#include <algorithm>
#include <errno.h>
#include <string.h>
namespace nytl {
FUp::FUp(const std::string &err, const std::string &file, const std::string &func, int line){
WHAT = "Error occured in function " + func + " (line " + std::to_string(line) + " of " +
file + ")\nError: " + err;
}
const char * FUp::what() const noexcept {
return WHAT.c_str();
}
std::string prettyprint_errno(const std::string &pref) {
const char* d = strerrorname_np(errno);
return pref.empty() ? std::string(d) : std::string(pref) + ": " + d;
}
bool endsIn(const std::string &a, const std::string &b) {
if (b.size() > a.size())
return false;
return std::equal(a.end() - (ssize_t)b.size(), a.end(), b.begin());
}
std::string throwout_postfix(const std::string &a, size_t bsz) {
return a.substr(0, a.size() >= bsz ? a.size() - bsz : 0);
}
bool isALPHA(char ch) {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
}
bool isNUM(char ch) {
return '0' <= ch && ch <= '9';
}
bool isUNCHAR(char ch) {
return isALPHA(ch) || isNUM(ch) || ch == '-' || ch == '_';
}
bool isSPACE(char ch) {
return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\r';
}
bool isUname(const std::string &str) noexcept {
if (str.empty() || str == "_")
return false;
if (isNUM(str[0]))
return false;
for (char ch: str)
if (!isUNCHAR(ch))
return false;
return true;
}
}

View File

@ -0,0 +1,43 @@
#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>
namespace nytl {
template<typename T>
using uptr = std::unique_ptr<T>;
class FUp : public std::exception{
std::string WHAT;
public:
FUp(const std::string &err, const std::string &file, const std::string &func, int line);
const char *what() const noexcept override;
};
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);
std::string throwout_postfix(const std::string& a, size_t bsz);
bool isALPHA(char ch);
bool isNUM(char ch);
bool isUNCHAR(char ch);
bool isSPACE(char ch);
bool isUname(const std::string& str) noexcept;
}
#endif

View File

@ -0,0 +1,28 @@
#ifndef NEW_YORK_TRANSIT_LINE_CORE_H
#define NEW_YORK_TRANSIT_LINE_CORE_H
#include "templater.h"
#include <functional>
namespace nytl {
void parse_bare_file(const std::string& filename, const std::string& content,
global_elem_set_t& result);
void parse_special_file(const std::string& filename, const std::string& content,
global_elem_set_t& result);
struct LocalVarValue {
bool is_json = false;
std::string EL_name;
const json::JSON* JSON_subval = NULL;
};
/* 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<LocalVarValue>& local_vars, json::JSON& expr);
std::string rendering_core(const std::string& entry_func, const std::vector<json::JSON>& entry_arguments,
std::map<std::string, Element>& elem_ns, const std::function<std::string(std::string)>& escape);
}
#endif

View File

@ -0,0 +1,44 @@
#include "core.h"
#include "alotalot.h"
#include <assert.h>
namespace nytl {
struct Frame {
json::JSON& expr;
LocalVarValue& result;
LocalVarValue temp_ret;
size_t chain_el = 0;
Frame(json::JSON &expr, LocalVarValue &result)
: expr(expr),
result(result) {
}
uptr<Frame> toMe(bool returned_from_bottom, global_elem_set_t& global_elems,
const std::vector<LocalVarValue>& local_vars) {
if (returned_from_bottom) {
ASSERT(temp_ret.is_json, "Expression \"[ element ]\" is not allowed");
assert(temp_ret.JSON_subval);
// json::JSON
// todo: make usable const JSON
} else {
assert(expr.isDictionary());
if ((*expr["V"]).isInteger()) {
size_t lv_ind = (*expr["V"]).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();
result = LocalVarValue{false, cur_el_name_str, NULL};
} else
assert(false);
}
if (chain_el == (*expr["C"]).)
}
};
LocalVarValue rendering_core_execute_expression(global_elem_set_t& global_elems,
const std::vector<LocalVarValue>& local_vars, json::JSON& expr) {
}
}

View File

@ -0,0 +1,30 @@
#include "html_case.h"
namespace nytl {
std::string html_case_espace_string(const std::string &inp) {
std::string res;
res.reserve(inp.size());
for (char ch: inp) {
switch (ch) {
case '&':
res += "&amp";
break;
case '<':
res += "&lt";
break;
case '>':
res += "&gt";
break;
case '"':
res += "&quot";
break;
case '\'':
res += "&#39";
break;
default:
res += ch;
}
}
return res;
}
}

View File

@ -0,0 +1,10 @@
#ifndef NEW_YORK_TRANSIT_LINE_HTML_CASE_H
#define NEW_YORK_TRANSIT_LINE_HTML_CASE_H
#include <string>
namespace nytl {
std::string html_case_espace_string(const std::string &inp);
}
#endif

View File

@ -0,0 +1,88 @@
#include "core.h"
#include "alotalot.h"
#include <vector>
#include <assert.h>
namespace nytl {
size_t first_nw_char(const std::string& str) {
size_t i = 0;
for (; i < str.size(); i++)
if (!isSPACE(str[i]))
break;
return i;
}
bool is_space_only(const std::string& str) {
return first_nw_char(str) == str.size();
}
void rstrip(std::string& str) {
while (!str.empty() && isSPACE(str.back()))
str.resize(str.size() - 1);
}
void parse_bare_file(const std::string& filename, const std::string& content,
std::map<std::string, Element>& result)
{
ASSERT(result.count(filename) == 0, "Repeated element " + filename);
std::vector<std::string> lines;
bool had_nw_line = false;
size_t smallest_tab;
std::string current_line;
auto finish = [&]() {
size_t tab_sz = first_nw_char(current_line);
if (tab_sz == current_line.size()) {
if (had_nw_line)
lines.emplace_back();
} else {
if (had_nw_line) {
if (smallest_tab > tab_sz)
smallest_tab = tab_sz;
} else {
smallest_tab = tab_sz;
had_nw_line = true;
}
rstrip(current_line);
lines.push_back(current_line);
}
current_line.clear();
};
for (char ch: content) {
if (ch == '\n') {
finish();
} else {
current_line += ch;
}
}
finish();
while (!lines.empty() && lines.back().empty())
lines.pop_back();
for (std::string& line: lines) {
if (!line.empty()) {
assert(line.size() > smallest_tab);
line = line.substr(smallest_tab);
}
}
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;
}
void parse_special_file(const std::string& filename, const std::string& content,
std::map<std::string, Element>& result)
{
// todo
}
}

View File

@ -0,0 +1,98 @@
#include "core.h"
#include "alotalot.h"
#include <string.h>
#include <libjsonincpp/string_representation.h>
#include <assert.h>
namespace nytl {
std::string rendering_core(const std::string& entry_func, const std::vector<json::JSON>& entry_arguments,
std::map<std::string, Element>& elem_ns, const std::function<std::string(std::string)>& escape)
{
size_t cur_line_width = 0;
std::string result;
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<LocalVarValue> passed_args;
/* This parameter incapsulates `cur_line_width` at some point for multiline `put-parts` */
size_t multiline_put_start = 0;
};
std::vector<uptr<Frame>> stack;
auto make_frame = [&](const std::string& name, std::vector<LocalVarValue> 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<Frame>(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<LocalVarValue> 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) {
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')
cur_line_width = 0;
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());
}
stack.pop_back();
continue;
}
if (cur.part_to_do == cur.el->parts.size()) {
stack.pop_back();
continue;
}
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
}
}
return result;
}
}

View File

@ -0,0 +1,149 @@
#include "templater.h"
#include <sys/stat.h>
#include <dirent.h>
#include "alotalot.h"
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "core.h"
namespace nytl {
/* Throws std::runtime_exception on incorrect settings */
void check_settings(const TemplaterSettings& settings) {
#define lmao_serving_kid_with_identity_issues throw std::runtime_error("What was wrong with {% %} ????")
if (settings.magic_block_start.empty() || settings.magic_block_end.empty())
lmao_serving_kid_with_identity_issues;
char incode = settings.magic_block_start[0];
if (isSPACE(incode) || isALPHA(incode) || isNUM(incode))
lmao_serving_kid_with_identity_issues;
char ender = settings.magic_block_end[0];
if (isUNCHAR(ender) || ender == ':' || isSPACE(ender) || ender == '[' || ender == ']' || ender == '.')
lmao_serving_kid_with_identity_issues;
}
Templater::Templater(TemplaterSettings settings): settings(std::move(settings)) {
check_settings(this->settings);
}
struct InterestingFile {
std::string path;
std::string dot_name;
bool special_syntax_applied;
};
std::vector<InterestingFile> indexing_detour(const TemplaterDetourRules& rules) {
std::vector<InterestingFile> result;
int ret;
std::vector<std::string> todo;
todo.emplace_back();
while (!todo.empty()) {
std::string cur = std::move(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());
struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D};
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
while (true) {
errno = 0;
struct dirent* Dent = readdir(D);
if (Dent == NULL) {
if (errno == 0)
break;
THROW_on_errno("dirent in \"" + cur + "\"");
}
std::string child_entry = Dent->d_name;
if (child_entry == "." || child_entry == "..")
continue;
std::string path_to_cur_child = path_to_cur_dir + "/" + child_entry;
struct stat info;
ret = stat(path_to_cur_child.c_str(), &info);
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);
} else if (S_ISREG(info.st_mode)) {
auto replace_sep = [](const std::string& slashed) -> std::string {
std::string dotted;
dotted.reserve(slashed.size());
for (char ch: slashed) {
if (ch == '/')
dotted += '.';
else
dotted += ch;
}
return dotted;
};
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});
};
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);
} else if (endsIn(child_entry, rules.postfix_rule_for_static_files)) {
np_reg_categ_result(throwout_postfix(child_entry, rules.postfix_rule_for_static_files.size()), false);
}
} else {
THROW("unknown fs entry type \"" + cur + "\"");
}
}
}
return result;
}
std::string readFile(const std::string& path) {
std::string result;
int ret;
int fd = open(path.c_str(), O_RDONLY);
ASSERT_on_iret(fd, "Opening \"" + path + "\"");
char buf[2048];
while ((ret = (int)read(fd, buf, 2048)) > 0) {
size_t oldN = result.size();
result.resize(oldN + ret);
memcpy((void*)&result.c_str()[oldN], buf, ret);
}
if (ret < 0) {
close(fd);
THROW("reading file");
}
return result;
}
void Templater::update() {
elements = {
{"jesc", Element{{json::JSON(true)}, true}},
{"str2text", Element{{json::JSON(true)}, true}},
{"str2code", Element{{json::JSON(true)}, true}},
};
std::vector<InterestingFile> intersting_files = indexing_detour(settings.det);
for (const InterestingFile& file: intersting_files) {
std::string content = readFile(file.path);
if (file.special_syntax_applied) {
parse_special_file(file.dot_name, content, elements);
} else {
parse_bare_file(file.dot_name, content, elements);
}
}
}
void check_uinp_element(const std::string& uinp) {
if (uinp.empty())
THROW("empty???");
std::vector<std::string> 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<json::JSON> &arguments) const {
check_uinp_element(element);
return rendering_core(element, arguments, elements);
}
}

View File

@ -0,0 +1,86 @@
#ifndef NEW_YORK_TRANSIT_LINE_TEMPLATER_H
#define NEW_YORK_TRANSIT_LINE_TEMPLATER_H
#include <vector>
#include <string>
#include <libjsonincpp/jsonobj.h>
#include <functional>
#include "html_case.h"
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;
struct when_code_S {
std::string lines;
} when_code;
struct when_put_S {
expression_t called_element;
std::vector<expression_t> passed_arguments;
} 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;
bool line_feed = true;
} when_for_put;
struct when_ref_put_S {
expression_t ref_over;
expression_t internal_element;
} when_ref_put;
};
struct Element {
/* Stores signature of element */
std::vector<json::JSON> arguments;
/* `base` is true for builtin elements (jesc str2code str2text). Parts for such ' are empty */
bool base = false;
std::vector<ElementPart> parts;
};
struct TemplaterDetourRules {
std::string root_dir_path;
std::string postfix_rule_for_element_cont = ".nytl.html";
std::string postfix_rule_for_static_files = ".html";
};
struct TemplaterSettings {
TemplaterDetourRules det;
std::string magic_block_start = "{%";
std::string magic_block_end = "%}";
std::function<std::string(std::string)> escape = html_case_espace_string;
};
typedef std::map<std::string, Element> global_elem_set_t;
struct Templater {
TemplaterSettings settings;
global_elem_set_t elements;
explicit Templater(TemplaterSettings settings);
/* Throws exception, derived from std::exception */
void update();
/* Throws exception, derived from std::exception */
std::string render(const std::string& element, const std::vector<json::JSON>& arguments) const;
};
}
#endif