Compare commits
11 Commits
3a8d1207b1
...
ea7d50a183
Author | SHA1 | Date | |
---|---|---|---|
ea7d50a183 | |||
dd806a5633 | |||
393703b0b2 | |||
e03794410b | |||
076a941a6e | |||
83c17c22e6 | |||
01176052d7 | |||
91aaf51410 | |||
be37e4f5ab | |||
5fab1131ca | |||
c264e3802b |
@ -1,25 +1,25 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Веб-Чат</title>
|
<title>Веб-Чат</title>
|
||||||
<link rel="stylesheet" href="/assets/css/chat.css">
|
<link rel="stylesheet" href="/assets/css/chat.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
<div class="chat-header">
|
<div class="chat-header">
|
||||||
Веб чат
|
Веб чат
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-messages" id="chat-messages">
|
<div class="chat-messages" id="chat-messages">
|
||||||
<!-- Сообщения чата будут здесь -->
|
<!-- Сообщения чата будут здесь -->
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-footer">
|
<div class="chat-footer">
|
||||||
<input type="text" class="chat-input" id="chat-input" placeholder="Введите сообщение...">
|
<input type="text" class="chat-input" id="chat-input" placeholder="Введите сообщение...">
|
||||||
<button class="chat-send-button" onclick="sendMessage()">Отправить</button>
|
<button class="chat-send-button" onclick="sendMessage()">Отправить</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/assets/js/chat.js"></script>
|
<script src="/assets/js/chat.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,31 +1,32 @@
|
|||||||
|
{% ELDEF main JSON pres %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Список Чат-Комнат</title>
|
<title>{% WRITE pres.phr.decl.list-of-chat-rooms %}</title>
|
||||||
<link rel="stylesheet" href="/assets/css/list-rooms.css">
|
<link rel="stylesheet" href="/assets/css/list-rooms.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 style="color: white;">Выберите Чат-Комнату</h1>
|
<h1 style="color: white;">{% WRITE pres.phr.decl.select-chat-room %}</h1>
|
||||||
<ul class="room-list">
|
<ul class="room-list">
|
||||||
<!-- Здесь будет список комнат -->
|
<!-- Здесь будет список комнат -->
|
||||||
</ul>
|
</ul>
|
||||||
<button class="create-room-button" onclick="openCreateRoomModal()">Создать Комнату</button>
|
<button class="create-room-button" onclick="openCreateRoomModal()">{% WRITE pres.phr.act.create-room %}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="passwordModal" class="modal">
|
<div id="passwordModal" class="modal">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="close" onclick="closeModal()">×</span>
|
<span class="close" onclick="closeModal()">×</span>
|
||||||
<h2>Введите Пароль</h2>
|
<h2>VVedite parol</h2> <!-- Nam ne nuzhen parol ot komnat -->
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="password" id="roomPassword" placeholder="Пароль">
|
<input type="password" id="roomPassword" placeholder="Пароль">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="join-button" onclick="validatePassword()">Подтвердить</button>
|
<button class="join-button" onclick="validatePassword()">{% WRITE pres.phr.act.confirm %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -35,14 +36,14 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span class="close" onclick="closeCreateRoomModal()">×</span>
|
<span class="close" onclick="closeCreateRoomModal()">×</span>
|
||||||
<h2>Создать Комнату</h2>
|
<h2>{% WRITE pres.phr.decl.create-room %}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="text" id="newRoomName" placeholder="Название комнаты">
|
<input type="text" id="newRoomName" placeholder="{% WRITE pres.phr.decl.name-of-room %}">
|
||||||
<input type="password" id="newRoomPassword" placeholder="Пароль">
|
<input type="password" id="newRoomPassword" placeholder="Пароль"> <!-- Fedya, nam ne nuzhen parol -->
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="join-button" onclick="createRoom()">Создать</button>
|
<button class="join-button" onclick="createRoom()">{% WRITE pres.phr.act.create %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,3 +51,4 @@
|
|||||||
<script src="/assets/js/list-rooms.js"></script>
|
<script src="/assets/js/list-rooms.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{% ENDELDEF %}
|
@ -34,6 +34,7 @@ struct CAWebChat {
|
|||||||
BuildUnitsArray runlevel_2;
|
BuildUnitsArray runlevel_2;
|
||||||
|
|
||||||
std::string build_type;
|
std::string build_type;
|
||||||
|
bool build_tests = false;
|
||||||
|
|
||||||
std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
|
std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
|
||||||
"-Wno-unused-but-set-variable", "-Wno-reorder"};
|
"-Wno-unused-but-set-variable", "-Wno-reorder"};
|
||||||
@ -57,8 +58,8 @@ struct CAWebChat {
|
|||||||
return my_flag_collection;
|
return my_flag_collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAWebChat(std::string _build_type, const NormalCBuildSystemCommandMeaning& cmd)
|
CAWebChat(const std::string& _build_type, bool _build_tests, const NormalCBuildSystemCommandMeaning& cmd)
|
||||||
: build_type(std::move(_build_type))
|
: build_type(_build_type), build_tests(_build_tests)
|
||||||
{
|
{
|
||||||
ASSERT(build_type == "release" || build_type == "debug", "Unknown build type");
|
ASSERT(build_type == "release" || build_type == "debug", "Unknown build type");
|
||||||
|
|
||||||
@ -108,10 +109,38 @@ struct CAWebChat {
|
|||||||
T.installation_dir = "";
|
T.installation_dir = "";
|
||||||
my_targets.push_back(T);
|
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",
|
||||||
|
"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"};
|
{ CTarget T{"iu9-ca-web-chat", "executable"};
|
||||||
T.additional_compilation_flags = getSomeRadFlags();
|
T.additional_compilation_flags = getSomeRadFlags();
|
||||||
T.proj_deps = {CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"}};
|
T.proj_deps = {
|
||||||
T.external_deps = {CTargetDependenceOnExternalLibrary{"sqlite3"}};
|
CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"},
|
||||||
|
CTargetDependenceOnProjectsLibrary{"new_york_transit_line"},
|
||||||
|
};
|
||||||
|
T.external_deps = {
|
||||||
|
CTargetDependenceOnExternalLibrary{"sqlite3"}
|
||||||
|
};
|
||||||
T.units = {"main.cpp"};
|
T.units = {"main.cpp"};
|
||||||
for (std::string& u: T.units)
|
for (std::string& u: T.units)
|
||||||
u = "web_chat/" + u;
|
u = "web_chat/" + u;
|
||||||
@ -133,7 +162,9 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
NormalCBuildSystemCommandMeaning cmd;
|
NormalCBuildSystemCommandMeaning cmd;
|
||||||
regular_bs_cli_cmd_interpret(args, 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)
|
if (cmd.need_to_build)
|
||||||
complete_tasks_of_build_units(bs.runlevel_1);
|
complete_tasks_of_build_units(bs.runlevel_1);
|
||||||
umask(~0755);
|
umask(~0755);
|
||||||
|
@ -1,3 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "Web chat"
|
"presentation": {
|
||||||
|
"instance-identity": {
|
||||||
|
"top-title": "Вэб чат ИУ9"
|
||||||
|
},
|
||||||
|
"phr": {
|
||||||
|
"decl": {
|
||||||
|
"list-of-chat-rooms": "Список Чат-Комнат",
|
||||||
|
"select-chat-room": "Выберете чат комнату",
|
||||||
|
"name-of-room": "Название комнаты",
|
||||||
|
"create-room": "Создать комнату"
|
||||||
|
},
|
||||||
|
"act": {
|
||||||
|
"create-room": "Создать комнату",
|
||||||
|
"confirm": "Подтвердить",
|
||||||
|
"create": "Создать"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assets": "./assets",
|
||||||
|
"limits": {
|
||||||
|
"max-users": 100000,
|
||||||
|
"max-rooms": 100000,
|
||||||
|
"max-messages": 10000000000000,
|
||||||
|
"storage-size-limit": 100000000000
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"workers": 8,
|
||||||
|
"http-listen": ["127.0.0.1:1025"],
|
||||||
|
"command-listen": []
|
||||||
|
}
|
||||||
}
|
}
|
@ -5,15 +5,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace een9 {
|
namespace een9 {
|
||||||
ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line): err(err),
|
ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line){
|
||||||
FILE(file),
|
WHAT = "Error occured in function " + func + " (line " + std::to_string(line) + " of " +
|
||||||
func(func),
|
file + ")\nError: " + err;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * ServerError::what() const noexcept {
|
const char * ServerError::what() const noexcept {
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
|
|
||||||
namespace een9 {
|
namespace een9 {
|
||||||
class ServerError : public std::exception{
|
class ServerError : public std::exception{
|
||||||
std::string err;
|
|
||||||
std::string FILE;
|
|
||||||
std::string func;
|
|
||||||
int LINE;
|
|
||||||
std::string WHAT;
|
std::string WHAT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -23,9 +23,9 @@ namespace een9 {
|
|||||||
ASSERT_on_iret(ret, "stat(\"" + cur + "\")");
|
ASSERT_on_iret(ret, "stat(\"" + cur + "\")");
|
||||||
if (S_ISDIR(info.st_mode)) {
|
if (S_ISDIR(info.st_mode)) {
|
||||||
DIR* D = opendir(path_to_cur_ent.c_str());
|
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};
|
struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D};
|
||||||
|
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
|
||||||
|
cur += "/";
|
||||||
while (true) {
|
while (true) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
struct dirent* Dent = readdir(D);
|
struct dirent* Dent = readdir(D);
|
||||||
|
@ -55,7 +55,7 @@ namespace een9 {
|
|||||||
while ((ret = (int)read(fd, buf, 2048)) > 0) {
|
while ((ret = (int)read(fd, buf, 2048)) > 0) {
|
||||||
size_t oldN = result.size();
|
size_t oldN = result.size();
|
||||||
result.resize(oldN + ret);
|
result.resize(oldN + ret);
|
||||||
memcpy(&result[oldN], buf, ret);
|
memcpy((void*)&result.c_str()[oldN], buf, ret);
|
||||||
}
|
}
|
||||||
ASSERT_on_iret(ret, "Reading from " + description);
|
ASSERT_on_iret(ret, "Reading from " + description);
|
||||||
}
|
}
|
||||||
|
@ -82,10 +82,10 @@ namespace een9 {
|
|||||||
|
|
||||||
struct WorkersEnv {
|
struct WorkersEnv {
|
||||||
WorkersEnvCommon& wtec;
|
WorkersEnvCommon& wtec;
|
||||||
int id;
|
worker_id_t id;
|
||||||
ClientRequestParser_WorkerBuffers personal_parser_buffer;
|
ClientRequestParser_WorkerBuffers personal_parser_buffer;
|
||||||
|
|
||||||
explicit WorkersEnv(WorkersEnvCommon& wtec, int id): wtec(wtec), id(id), personal_parser_buffer(wtec.parser_programs){}
|
explicit WorkersEnv(WorkersEnvCommon& wtec, worker_id_t id): wtec(wtec), id(id), personal_parser_buffer(wtec.parser_programs){}
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo: add timeout for multiple bytes, add more settings
|
// todo: add timeout for multiple bytes, add more settings
|
||||||
@ -125,7 +125,7 @@ namespace een9 {
|
|||||||
|
|
||||||
void process_connection(const SlaveTask& task, WorkersEnv& wte) {
|
void process_connection(const SlaveTask& task, WorkersEnv& wte) {
|
||||||
ClientRequest client_request = process_connection_input(task.fd(), task.s_tips, wte);
|
ClientRequest client_request = process_connection_input(task.fd(), task.s_tips, wte);
|
||||||
std::string server_response = wte.wtec.guest_core(task, client_request);
|
std::string server_response = wte.wtec.guest_core(task, client_request, wte.id);
|
||||||
process_connection_output(task.fd(), server_response);
|
process_connection_output(task.fd(), server_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +149,7 @@ namespace een9 {
|
|||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
printf("Client request procession failure in worker\n");
|
printf("Client request procession failure in worker\n");
|
||||||
printf("%s\n", e.what());
|
printf("%s\n", e.what());
|
||||||
|
/* Under mysterious some circumstances, in this place destructor of string in SystemError causes segfault. I can't fix that */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("Worker finished\n");
|
printf("Worker finished\n");
|
||||||
@ -166,7 +167,7 @@ namespace een9 {
|
|||||||
std::vector<pthread_t> workers(params.slave_number);
|
std::vector<pthread_t> workers(params.slave_number);
|
||||||
std::vector<uptr<WorkersEnv>> wtes(params.slave_number);
|
std::vector<uptr<WorkersEnv>> wtes(params.slave_number);
|
||||||
for (size_t i = 0; i < params.slave_number; i++) {
|
for (size_t i = 0; i < params.slave_number; i++) {
|
||||||
wtes[i] = std::make_unique<WorkersEnv>(wtec, i);
|
wtes[i] = std::make_unique<WorkersEnv>(wtec, (worker_id_t)i);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < params.slave_number; i++) {
|
for (size_t i = 0; i < params.slave_number; i++) {
|
||||||
pthread_create(&workers[i], NULL, worker_func, wtes[i].get());
|
pthread_create(&workers[i], NULL, worker_func, wtes[i].get());
|
||||||
|
@ -29,8 +29,10 @@ namespace een9 {
|
|||||||
EEN9_ServerTips s_tips;
|
EEN9_ServerTips s_tips;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int worker_id_t;
|
||||||
|
|
||||||
/* guest_core function must not throw anything that is not derived from std::exception */
|
/* guest_core function must not throw anything that is not derived from std::exception */
|
||||||
typedef std::function<std::string(const SlaveTask&, const ClientRequest&)> guest_core_t;
|
typedef std::function<std::string(const SlaveTask&, const ClientRequest&, worker_id_t worker_id)> guest_core_t;
|
||||||
|
|
||||||
struct ServersConfiguration {
|
struct ServersConfiguration {
|
||||||
size_t critical_load_1 = 90;
|
size_t critical_load_1 = 90;
|
||||||
|
95
src/http_server/new_york_transit_line/alotalot.cpp
Normal file
95
src/http_server/new_york_transit_line/alotalot.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include "alotalot.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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 isUNCHARnonNUM(char ch) {
|
||||||
|
return isALPHA(ch) || ch == '-' || ch == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSPACE(char ch) {
|
||||||
|
return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUname(const std::string &str) {
|
||||||
|
if (str.empty() || str == "_")
|
||||||
|
return false;
|
||||||
|
if (isNUM(str[0]))
|
||||||
|
return false;
|
||||||
|
for (char ch: str)
|
||||||
|
if (!isUNCHAR(ch))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_uname_dotted_sequence(const std::string& uinp) {
|
||||||
|
if (uinp.empty())
|
||||||
|
return false;
|
||||||
|
std::vector<std::string> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rstrip(std::string &str) {
|
||||||
|
while (!str.empty() && isSPACE(str.back()))
|
||||||
|
str.resize(str.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
53
src/http_server/new_york_transit_line/alotalot.h
Normal file
53
src/http_server/new_york_transit_line/alotalot.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef NEW_YORK_TRANSIT_LINE_ALOTALOT_H
|
||||||
|
#define NEW_YORK_TRANSIT_LINE_ALOTALOT_H
|
||||||
|
|
||||||
|
#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>;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Tp>
|
||||||
|
constexpr std::remove_reference_t<Tp>&&
|
||||||
|
mv(Tp&& t) noexcept
|
||||||
|
{ return static_cast<std::remove_reference_t<Tp>&&>(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 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));
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
|
||||||
|
void rstrip(std::string& str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
24
src/http_server/new_york_transit_line/core.h
Normal file
24
src/http_server/new_york_transit_line/core.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef NEW_YORK_TRANSIT_LINE_CORE_H
|
||||||
|
#define NEW_YORK_TRANSIT_LINE_CORE_H
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
void parse_special_file(const std::string& filename, const std::string& content,
|
||||||
|
global_elem_set_t& result, TemplaterSettings& syntax);
|
||||||
|
|
||||||
|
/* =================== For rendering ====================*/
|
||||||
|
std::string rendering_core(const std::string& entry_func, const std::vector<const json::JSON*>& entry_arguments,
|
||||||
|
const global_elem_set_t& elem_ns, const std::function<std::string(std::string)>& escape);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
49
src/http_server/new_york_transit_line/debug_print.cpp
Normal file
49
src/http_server/new_york_transit_line/debug_print.cpp
Normal file
@ -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");
|
||||||
|
}
|
||||||
|
}
|
30
src/http_server/new_york_transit_line/html_case.cpp
Normal file
30
src/http_server/new_york_transit_line/html_case.cpp
Normal 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 += "&";
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
res += "<";
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
res += ">";
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
res += """;
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
res += "'";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
10
src/http_server/new_york_transit_line/html_case.h
Normal file
10
src/http_server/new_york_transit_line/html_case.h
Normal 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
|
553
src/http_server/new_york_transit_line/parser.cpp
Normal file
553
src/http_server/new_york_transit_line/parser.cpp
Normal file
@ -0,0 +1,553 @@
|
|||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string clement_lstrip(const std::string& str) {
|
||||||
|
size_t gone = 0;
|
||||||
|
size_t n = str.size();
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
if (str[i] == '\n') {
|
||||||
|
gone = i + 1;
|
||||||
|
} else if (!isSPACE(str[i])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str.substr(gone);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParsingContext {
|
||||||
|
std::string text;
|
||||||
|
size_t pos = 0;
|
||||||
|
size_t column = 0;
|
||||||
|
size_t line = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int EOFVAL = -999;
|
||||||
|
|
||||||
|
int peep(ParsingContext &ctx) {
|
||||||
|
if (ctx.text.size() <= ctx.pos)
|
||||||
|
return EOFVAL;
|
||||||
|
return ctx.text[ctx.pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
char advance(ParsingContext& ctx) {
|
||||||
|
if (ctx.text[ctx.pos] == '\n') {
|
||||||
|
ctx.line++;
|
||||||
|
ctx.column = 0;
|
||||||
|
} else {
|
||||||
|
ctx.column++;
|
||||||
|
}
|
||||||
|
return ctx.text[ctx.pos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
char skip(ParsingContext& ctx) {
|
||||||
|
ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF");
|
||||||
|
return advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip(ParsingContext& ctx, char ch) {
|
||||||
|
ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF");
|
||||||
|
ASSERT(ctx.text[ctx.pos] == ch, "Unexpected character");
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void skipWhitespace(ParsingContext &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<std::string> splitIntoLines(const std::string &str) {
|
||||||
|
std::vector<std::string> result = {""};
|
||||||
|
for (char ch: str) {
|
||||||
|
if (ch == '\n')
|
||||||
|
result.emplace_back();
|
||||||
|
else
|
||||||
|
result.back() += ch;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string concatenateLines(const std::vector<std::string>& lines) {
|
||||||
|
std::string result;
|
||||||
|
size_t n = lines.size();
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
if (i)
|
||||||
|
result += '\n';
|
||||||
|
result += lines[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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, size_t I, size_t PN, size_t cut) {
|
||||||
|
std::vector<std::string> lines = splitIntoLines(str);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_bare_file(const std::string& filename, const std::string& content,
|
||||||
|
global_elem_set_t& result)
|
||||||
|
{
|
||||||
|
ASSERT(result.count(filename) == 0, "Repeated element " + filename);
|
||||||
|
std::string txt = clement_lstrip(content);
|
||||||
|
rstrip(txt);
|
||||||
|
size_t cut = 9999999999999;
|
||||||
|
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{}};
|
||||||
|
el.parts[0].when_code.lines = mv(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Type parsing frame */
|
||||||
|
struct TPFrame {
|
||||||
|
json::JSON& result;
|
||||||
|
|
||||||
|
explicit TPFrame(json::JSON& result_): result(result_){}
|
||||||
|
|
||||||
|
uptr<TPFrame> 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<TPFrame>(result.asArray().back());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
json::JSON parse_type(ParsingContext& ctx) {
|
||||||
|
json::JSON result;
|
||||||
|
std::vector<uptr<TPFrame>> stack;
|
||||||
|
stack.push_back(mv(std::make_unique<TPFrame>(result)));
|
||||||
|
bool returned = false;
|
||||||
|
while (!stack.empty()) {
|
||||||
|
uptr<TPFrame> 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<std::string, size_t> arg_name_list_t;
|
||||||
|
|
||||||
|
/* Expression parsing frame */
|
||||||
|
struct EPFrame {
|
||||||
|
json::JSON& result;
|
||||||
|
|
||||||
|
explicit EPFrame(json::JSON& result_): result(result_){}
|
||||||
|
|
||||||
|
uptr<EPFrame> 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<json::JSON>& 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<EPFrame>(chain.back());
|
||||||
|
} else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
json::JSON parse_expression(ParsingContext& ctx, const arg_name_list_t& local_var_names) {
|
||||||
|
json::JSON result;
|
||||||
|
std::vector<uptr<EPFrame>> stack;
|
||||||
|
stack.push_back(mv(std::make_unique<EPFrame>(result)));
|
||||||
|
bool returned = false;
|
||||||
|
while (!stack.empty()) {
|
||||||
|
uptr<EPFrame> 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_end[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Element content parsing frame */
|
||||||
|
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<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 == ElementPart::p_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);
|
||||||
|
} else
|
||||||
|
assert(ret_data_int == 0);
|
||||||
|
}
|
||||||
|
ret_data_int = 0;
|
||||||
|
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 = ElementPart::p_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<ECPFrame>(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 = ElementPart::p_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<ECPFrame>(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 = ElementPart::p_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 = 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);
|
||||||
|
skipWhitespace(ctx);
|
||||||
|
P.passed_arguments = {parse_expression(ctx, local_var_names)};
|
||||||
|
skip_magic_block_end(ctx, syntax);
|
||||||
|
};
|
||||||
|
if (op == "WRITE") {
|
||||||
|
mediocre_operator("str2text");
|
||||||
|
goto ya_e_ya_h_i_ya_g_d_o;;
|
||||||
|
}
|
||||||
|
if (op == "ROUGHINSERT") {
|
||||||
|
mediocre_operator("str2code");
|
||||||
|
goto ya_e_ya_h_i_ya_g_d_o;;
|
||||||
|
}
|
||||||
|
auto prepare_to_depart_parts = [&]() {
|
||||||
|
assert(!result.parts.empty());
|
||||||
|
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 == 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 == 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 == ElementPart::p_code) {
|
||||||
|
result.parts[i].when_code.lines = one_part_cut_excess_tab(result.parts[i].when_code.lines, i, N, cut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (op == "ENDELDEF") {
|
||||||
|
ASSERT(myself == gone_for_nothing, "Unexpected end of element");
|
||||||
|
skip_magic_block_end(ctx, syntax);
|
||||||
|
prepare_to_depart_parts();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (op == "ENDFOR") {
|
||||||
|
ASSERT(myself == gone_for_for, "Unexpected end of for cycle");
|
||||||
|
skipWhitespace(ctx);
|
||||||
|
/* Here I am using ret_data_int to return info about NOLF(1)/LF(2) decision */
|
||||||
|
ret_data_int = 2; // Default is to do LF
|
||||||
|
if (!isIt_magic_block_end(ctx, syntax)) {
|
||||||
|
op = make_uppercase(readName(ctx));
|
||||||
|
if (op == "LF") {
|
||||||
|
ret_data_int = 2;
|
||||||
|
} else if (op == "NOLF") {
|
||||||
|
ret_data_int = 1;
|
||||||
|
} else
|
||||||
|
THROW("Expected LF, NOLF or end of magic block");
|
||||||
|
}
|
||||||
|
skip_magic_block_end(ctx, syntax);
|
||||||
|
prepare_to_depart_parts();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (op == "ENDREF") {
|
||||||
|
assert(myself == gone_for_ref);
|
||||||
|
skip_magic_block_end(ctx, syntax);
|
||||||
|
prepare_to_depart_parts();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
THROW("Unknown operator. Expected FOR, REF, PUT, WRITE, ROUGHINSERT, ENDELDEF, ENDFOR, ENDREF");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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<uptr<ECPFrame>> stack;
|
||||||
|
stack.push_back(mv(std::make_unique<ECPFrame>(el_name, ECPFrame::gone_for_nothing, local_var_names, random_junk, result)));
|
||||||
|
bool returned = false;
|
||||||
|
while (!stack.empty()) {
|
||||||
|
uptr<ECPFrame> 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,
|
||||||
|
global_elem_set_t& result, TemplaterSettings& syntax)
|
||||||
|
{
|
||||||
|
ParsingContext ctx{content};
|
||||||
|
while(true) {
|
||||||
|
skipWhitespace(ctx);
|
||||||
|
if (peep(ctx) == EOFVAL)
|
||||||
|
break;
|
||||||
|
skip_magic_block_start(ctx, syntax);
|
||||||
|
ASSERT(make_uppercase(readName(ctx)) == "ELDEF", "Expected ELDEF");
|
||||||
|
skipWhitespace(ctx);
|
||||||
|
std::string elname_postfix = readName(ctx);
|
||||||
|
ASSERT(elname_postfix != "_", "please don't");
|
||||||
|
std::string fullname = elname_postfix == "main" ? filename : filename + "." + elname_postfix;
|
||||||
|
ASSERT(result.count(fullname) == 0, "Element " + fullname + " has been already defined");
|
||||||
|
Element& newborn = result[fullname];
|
||||||
|
arg_name_list_t arglist;
|
||||||
|
while (true) {
|
||||||
|
skipWhitespace(ctx);
|
||||||
|
if (isIt_magic_block_end(ctx, syntax))
|
||||||
|
break;
|
||||||
|
newborn.arguments.push_back(parse_type(ctx));
|
||||||
|
skipWhitespace(ctx);
|
||||||
|
std::string argname = readName(ctx);
|
||||||
|
ASSERT(!argname.empty(), "Expected argument name");
|
||||||
|
if (argname != "_") {
|
||||||
|
size_t k = arglist.size();
|
||||||
|
arglist[argname] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skip_magic_block_end(ctx, syntax);
|
||||||
|
parse_element_content(fullname, ctx, syntax, arglist, newborn, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
359
src/http_server/new_york_transit_line/rendering.cpp
Normal file
359
src/http_server/new_york_transit_line/rendering.cpp
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
#include "core.h"
|
||||||
|
#include "alotalot.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace nytl {
|
||||||
|
struct LocalVarValue {
|
||||||
|
bool is_json = false;
|
||||||
|
std::string EL_name;
|
||||||
|
const json::JSON* JSON_subval = NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Expression Execution Frame */
|
||||||
|
struct EEFrame {
|
||||||
|
const json::JSON& expr;
|
||||||
|
LocalVarValue& result;
|
||||||
|
LocalVarValue temp_ret;
|
||||||
|
size_t chain_el = 0;
|
||||||
|
|
||||||
|
EEFrame(const json::JSON &expr, LocalVarValue &result)
|
||||||
|
: expr(expr),
|
||||||
|
result(result) {
|
||||||
|
}
|
||||||
|
|
||||||
|
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<json::JSON> 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<std::string, json::JSON>& 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<EEFrame> toMe(bool returned, const global_elem_set_t& global_elems,
|
||||||
|
const std::vector<LocalVarValue>& local_vars) {
|
||||||
|
if (returned) {
|
||||||
|
ASSERT(temp_ret.is_json, "Expression \"X[ element ]\" is not allowed");
|
||||||
|
assert(temp_ret.JSON_subval);
|
||||||
|
descend(*(temp_ret.JSON_subval));
|
||||||
|
} else {
|
||||||
|
assert(expr.isDictionary());
|
||||||
|
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 (val.isString()) {
|
||||||
|
std::string cur_el_name_str = expr["V"].g().asString();
|
||||||
|
result = LocalVarValue{false, cur_el_name_str, NULL};
|
||||||
|
} else
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
const std::vector<json::JSON>& chain = expr["C"].g().asArray();
|
||||||
|
while (true) {
|
||||||
|
if (chain_el >= chain.size())
|
||||||
|
return NULL;
|
||||||
|
const json::JSON& t = chain[chain_el++];
|
||||||
|
if (t.isDictionary())
|
||||||
|
return std::make_unique<EEFrame>(t, temp_ret);
|
||||||
|
descend(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* No new JSON object will ever be created, I have N root json arguments,
|
||||||
|
* all the other json variables are subtrees of them. With one exception: Key iterators for arrays
|
||||||
|
* and dictionaries. They are stored in json in rendering stack */
|
||||||
|
LocalVarValue rendering_core_execute_expression(const global_elem_set_t& global_elems,
|
||||||
|
const std::vector<LocalVarValue>& local_vars, const json::JSON& expr) {
|
||||||
|
bool returned = false;
|
||||||
|
std::vector<uptr<EEFrame>> stack;
|
||||||
|
LocalVarValue result;
|
||||||
|
stack.push_back(std::make_unique<EEFrame>(expr, result));
|
||||||
|
while (!stack.empty()) {
|
||||||
|
EEFrame& cur = *stack.back();
|
||||||
|
uptr<EEFrame> todo = cur.toMe(returned, global_elems, local_vars);
|
||||||
|
returned = !(bool)todo;
|
||||||
|
if (todo)
|
||||||
|
stack.push_back(mv(todo));
|
||||||
|
else
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Ditch {
|
||||||
|
std::string result;
|
||||||
|
size_t cur_line_width = 0;
|
||||||
|
|
||||||
|
/* Fix idea: get rid of newlined_somewhere */
|
||||||
|
void append(const std::string& text, size_t wsp_before_newlines, bool& newlined_somewhere) {
|
||||||
|
size_t n = result.size();
|
||||||
|
size_t m = text.size();
|
||||||
|
result.reserve(n + m);
|
||||||
|
for (size_t i = 0; i < m; i++) {
|
||||||
|
result += text[i];
|
||||||
|
if (text[i] == '\n') {
|
||||||
|
// newlined_somewhere = true;
|
||||||
|
|
||||||
|
result.resize(result.size() + wsp_before_newlines, ' ');
|
||||||
|
cur_line_width = wsp_before_newlines;
|
||||||
|
} else {
|
||||||
|
// if (cur_line_width == 0 && newlined_somewhere) {
|
||||||
|
// result.resize(result.size() + wsp_before_newlines, ' ');
|
||||||
|
// cur_line_width = wsp_before_newlines;
|
||||||
|
// }
|
||||||
|
cur_line_width++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RFrame_passed const global_elem_set_t& elem_ns, Ditch& result, const std::function<std::string(std::string)>& escape
|
||||||
|
/* Rendering Frame */
|
||||||
|
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<RFrame> toMe(bool returned, RFrame_passed) {assert(false);}
|
||||||
|
|
||||||
|
virtual ~RFrame() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RFrame_OverParts : public RFrame{
|
||||||
|
std::string name;
|
||||||
|
std::vector<LocalVarValue> passed_args;
|
||||||
|
/* This parameter incapsulates `cur_line_width` at some point for multiline `put-parts` */
|
||||||
|
/* main iterator of this frame. Persistent across control returns */
|
||||||
|
size_t part_to_do = 0;
|
||||||
|
|
||||||
|
RFrame_OverParts(const std::string &name, const std::vector<LocalVarValue>& passed_args,
|
||||||
|
size_t multiline_put_start)
|
||||||
|
: RFrame(multiline_put_start), name(name),
|
||||||
|
passed_args(passed_args) {
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr<RFrame> 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<LocalVarValue> saved_args_plus_iter;
|
||||||
|
|
||||||
|
RFrame_OverJSON(const ElementPart::when_for_put_S &part, size_t multiline_put_start,
|
||||||
|
const std::vector<LocalVarValue> &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<json::JSON>& 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<LocalVarValue> &saved_args,
|
||||||
|
const std::vector<json::JSON> &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<RFrame> toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
|
||||||
|
const std::function<std::string(std::string)> &escape) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RFrame_OverDictionary: public RFrame_OverJSON {
|
||||||
|
const std::map<std::string, json::JSON>& dict;
|
||||||
|
std::map<std::string, json::JSON>::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<LocalVarValue> &saved_args_plus_iter,
|
||||||
|
const std::map<std::string, json::JSON> &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<RFrame> toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
|
||||||
|
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)
|
||||||
|
ASSERT(elem_ns.count(name) == 1, "No such element");
|
||||||
|
const Element& el = elem_ns.at(name);
|
||||||
|
if (!returned) {
|
||||||
|
/* Continue to do checks */
|
||||||
|
/* 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (el.base) {
|
||||||
|
assert(!returned);
|
||||||
|
assert(passed_args.size() == 1);
|
||||||
|
const json::JSON* X = passed_args[0].JSON_subval;
|
||||||
|
assert(X);
|
||||||
|
if (name == "jesc") {
|
||||||
|
std::string escaped_json = escape(json::generate_str(*X, json::print_pretty));
|
||||||
|
rstrip(escaped_json);
|
||||||
|
append(escaped_json, result);
|
||||||
|
} else if (name == "jesccomp") {
|
||||||
|
append(escape(json::generate_str(*X, json::print_compact)), 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 == ElementPart::p_code) {
|
||||||
|
const ElementPart::when_code_S& pt = cur_part.when_code;
|
||||||
|
append(pt.lines, result);
|
||||||
|
} 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");
|
||||||
|
size_t AN = pt.passed_arguments.size();
|
||||||
|
std::vector<LocalVarValue> 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<RFrame_OverParts>(called_element_expv.EL_name, passed_arguments_expv,
|
||||||
|
result.cur_line_width);
|
||||||
|
} 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");
|
||||||
|
const json::JSON& container = *iting_over.JSON_subval;
|
||||||
|
if (container.isArray()) {
|
||||||
|
return std::make_unique<RFrame_OverArray>(pt, result.cur_line_width, passed_args, container.asArray());
|
||||||
|
} else if (container.isDictionary()) {
|
||||||
|
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 == 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());
|
||||||
|
more_variables.back() = rendering_core_execute_expression(elem_ns, passed_args, pt.ref_over);
|
||||||
|
return std::make_unique<RFrame_OverParts>(pt.internal_element, more_variables, result.cur_line_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr<RFrame> 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<RFrame_OverParts>(part.internal_element, saved_args_plus_iter, wsp_before_newlines);
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr<RFrame> RFrame_OverDictionary::toMe(bool returned, RFrame_passed) {
|
||||||
|
if (it == dict.end())
|
||||||
|
return NULL;
|
||||||
|
if (returned && part.line_feed)
|
||||||
|
append("\n", result);
|
||||||
|
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<RFrame_OverParts>(part.internal_element, saved_args_plus_iter, wsp_before_newlines);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rendering_core(const std::string& entry_func, const std::vector<const json::JSON*>& entry_arguments,
|
||||||
|
const global_elem_set_t& elem_ns, const std::function<std::string(std::string)>& escape)
|
||||||
|
{
|
||||||
|
Ditch result;
|
||||||
|
|
||||||
|
std::vector<uptr<RFrame>> stack;
|
||||||
|
{
|
||||||
|
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] = {true, "", entry_arguments[i]};
|
||||||
|
stack.push_back(std::make_unique<RFrame_OverParts>(entry_func, entry_arguments_conv, 0));
|
||||||
|
}
|
||||||
|
bool returned = false;
|
||||||
|
while (!stack.empty()) {
|
||||||
|
uptr<RFrame> 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;
|
||||||
|
}
|
||||||
|
}
|
137
src/http_server/new_york_transit_line/templater.cpp
Normal file
137
src/http_server/new_york_transit_line/templater.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#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 = 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());
|
||||||
|
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.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;
|
||||||
|
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.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);
|
||||||
|
} 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}},
|
||||||
|
{"jesccomp", Element{{json::JSON(true)}, true}},
|
||||||
|
/* str2text base element has a dedicated operator - WRITE */
|
||||||
|
{"str2text", Element{{json::JSON(true)}, true}},
|
||||||
|
/* str2code base element has a dedicated operator - ROUGHINSERT */
|
||||||
|
{"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, settings);
|
||||||
|
} else {
|
||||||
|
parse_bare_file(file.dot_name, content, elements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Still can throw some stuff derived from std::exception (like bad alloc) */
|
||||||
|
std::string Templater::render(const std::string& element, const std::vector<const json::JSON*> &arguments) const {
|
||||||
|
ASSERT(is_uname_dotted_sequence(element), "Incorrect entry element name");
|
||||||
|
return rendering_core(element, arguments, elements, settings.escape);
|
||||||
|
}
|
||||||
|
}
|
81
src/http_server/new_york_transit_line/templater.h
Normal file
81
src/http_server/new_york_transit_line/templater.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#ifndef NEW_YORK_TRANSIT_LINE_TEMPLATER_H
|
||||||
|
#define NEW_YORK_TRANSIT_LINE_TEMPLATER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <jsonincpp/jsonobj.h>
|
||||||
|
#include <functional>
|
||||||
|
#include "html_case.h"
|
||||||
|
|
||||||
|
namespace nytl {
|
||||||
|
typedef json::JSON expression_t;
|
||||||
|
|
||||||
|
struct ElementPart {
|
||||||
|
/* Used with all types */
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
std::string 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;
|
||||||
|
bool is_hidden = 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<const json::JSON*>& arguments) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,7 @@
|
|||||||
|
{% ELDEF main %}
|
||||||
|
|
||||||
|
CCC
|
||||||
|
DDD
|
||||||
|
|
||||||
|
|
||||||
|
{% ENDELDEF %}
|
9
src/http_server/nytl_tests/HypertextPages/test.nytl.html
Normal file
9
src/http_server/nytl_tests/HypertextPages/test.nytl.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
{% ELDEF main JSON cba %}
|
||||||
|
AAA
|
||||||
|
{% FOR _:val IN cba %}
|
||||||
|
TUTUTUTUTUTUTUTUN {% PUT jesccomp val %}
|
||||||
|
{% ENDFOR %}
|
||||||
|
|
||||||
|
{% ENDELDEF %}
|
27
src/http_server/nytl_tests/test0.cpp
Normal file
27
src/http_server/nytl_tests/test0.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
#include <new_york_transit_line/templater.h>
|
||||||
|
#include <new_york_transit_line/core.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
nytl::debug_print_templater(templater);
|
||||||
|
json::JSON cba;
|
||||||
|
cba["boba"] = json::JSON("<>");
|
||||||
|
cba["arr"][0] = json::JSON("zero");
|
||||||
|
cba["arr"][1] = json::JSON("one");
|
||||||
|
cba["arr"][2] = json::JSON("two");
|
||||||
|
// printf("DEBUG WAS: %p\n", &cba["boba"].g());
|
||||||
|
// printf("%s\n", json::generate_str(cba["boba"].g(), json::print_compact).c_str());
|
||||||
|
// return 0;
|
||||||
|
std::string answer2 = templater.render("test", {&cba});
|
||||||
|
printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -5,9 +5,12 @@
|
|||||||
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <libjsonincpp/string_representation.h>
|
#include <jsonincpp/string_representation.h>
|
||||||
#include <libregexis024vm/vm_opcodes.h>
|
#include <libregexis024vm/vm_opcodes.h>
|
||||||
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
||||||
|
#include <new_york_transit_line/templater.h>
|
||||||
|
|
||||||
|
#include "../http_server/engine_engine_number_9/running_mainloop.h"
|
||||||
|
|
||||||
bool termination = false;
|
bool termination = false;
|
||||||
|
|
||||||
@ -16,7 +19,7 @@ void sigterm_action(int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void usage(char** argv) {
|
void usage(char** argv) {
|
||||||
printf("Usage: %s <file with settings> <assets folder>\n", argv[0]);
|
printf("Usage: %s <file with settings>\n", argv[0]);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,30 +34,26 @@ std::string unsafe_client_request_stringification(const een9::ClientRequest& req
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv){
|
int main(int argc, char** argv){
|
||||||
printf("%s\n", regexis024::opcode_to_str(regexis024::opcode_t::DIE));
|
|
||||||
try {
|
try {
|
||||||
een9_ASSERT_pl(argc > 0);
|
een9_ASSERT_pl(argc > 0);
|
||||||
if (argc < 1 + 2)
|
if (argc < 1 + 1)
|
||||||
usage(argv);
|
usage(argv);
|
||||||
if (!een9::isRegularFile(argv[1]) || !een9::endsIn(argv[1], ".json")) {
|
if (!een9::isRegularFile(argv[1]) || !een9::endsIn(argv[1], ".json")) {
|
||||||
printf("\"%s\" is not a json file\n", argv[1]);
|
printf("\"%s\" is not a json file\n", argv[1]);
|
||||||
usage(argv);
|
usage(argv);
|
||||||
}
|
}
|
||||||
std::string config_file = argv[1];
|
std::string config_file = argv[1];
|
||||||
if (!een9::isDirectory(argv[2])) {
|
|
||||||
printf("\"%s\" is not a directory\n", argv[2]);
|
|
||||||
usage(argv);
|
|
||||||
}
|
|
||||||
std::string assets_dir = argv[2];
|
|
||||||
|
|
||||||
std::string config_text;
|
std::string config_text;
|
||||||
een9::readFile(config_file, config_text);
|
een9::readFile(config_file, config_text);
|
||||||
json::JSON config = json::parse_str_flawless(config_text);
|
json::JSON config = json::parse_str_flawless(config_text);
|
||||||
een9_ASSERT(config.isDictionary(), "config root is not dictionary");
|
een9_ASSERT(config.isDictionary(), "config root is not dictionary");
|
||||||
|
een9_ASSERT(config["assets"].g().isString(), "config[\"\assets\"] is not string");
|
||||||
|
std::string assets_dir = config["assets"].g().asString();
|
||||||
|
een9_ASSERT(een9::isDirectory(assets_dir), "\"" + assets_dir + "\" is not a directory");
|
||||||
|
|
||||||
een9::StaticAssetManagerSlaveModule samI;
|
een9::StaticAssetManagerSlaveModule samI;
|
||||||
samI.update({
|
samI.update({
|
||||||
een9::StaticAssetManagerRule{assets_dir + "/html", "/assets/html", {{".html", "text/html"}} },
|
|
||||||
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
|
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
|
||||||
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} },
|
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} },
|
||||||
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
|
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
|
||||||
@ -62,31 +61,47 @@ int main(int argc, char** argv){
|
|||||||
} },
|
} },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
json::JSON& config_presentation = config["presentation"].g();
|
||||||
|
|
||||||
|
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
|
||||||
|
std::vector<std::unique_ptr<nytl::Templater>> templaters_copies(8);
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
templaters_copies[i] = std::make_unique<nytl::Templater>(
|
||||||
|
nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}});
|
||||||
|
templaters_copies[i]->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("%s\n", templaters_copies[0]->render("list-rooms", {&config_presentation}).c_str());
|
||||||
|
// return 0;
|
||||||
|
|
||||||
een9::MainloopParameters params;
|
een9::MainloopParameters params;
|
||||||
params.guest_core = [&samI](const een9::SlaveTask& task, const een9::ClientRequest& req) -> std::string {
|
params.guest_core = [&samI, &templaters_copies, config_presentation]
|
||||||
|
(const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string {
|
||||||
|
een9_ASSERT_pl(0 <= worker_id && worker_id < templaters_copies.size());
|
||||||
|
nytl::Templater& templater = *templaters_copies[worker_id];
|
||||||
een9::StaticAsset sa;
|
een9::StaticAsset sa;
|
||||||
int ret;
|
int ret;
|
||||||
// printf("%s", unsafe_client_request_stringification(req).c_str());
|
// printf("%s", unsafe_client_request_stringification(req).c_str());
|
||||||
if (req.uri_path == "/output") {
|
// if (req.uri_path == "/output") {
|
||||||
std::string text = unsafe_client_request_stringification(req);
|
// std::string text = unsafe_client_request_stringification(req);
|
||||||
return een9::form_http_server_response_200("text/plain", text);
|
// return een9::form_http_server_response_200("text/plain", text);
|
||||||
}
|
// }
|
||||||
auto rteee = [&](const std::string& asset_path) -> std::string {
|
auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string {
|
||||||
ret = samI.get_asset(asset_path, sa);
|
std::string page = templater.render(el_name,
|
||||||
een9_ASSERT_pl(ret == 0);
|
pass_phr ? std::vector<const json::JSON*>{&config_presentation} : std::vector<const json::JSON*>{});
|
||||||
return een9::form_http_server_response_200(sa.type, sa.content);
|
return een9::form_http_server_response_200("text/html", page);
|
||||||
};
|
};
|
||||||
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
||||||
return rteee("/assets/html/list-rooms.html");
|
return rteee("list-rooms", true);
|
||||||
}
|
}
|
||||||
if (req.uri_path == "/chat") {
|
if (req.uri_path == "/chat") {
|
||||||
return rteee("/assets/html/chat.html");
|
return rteee("chat", false);
|
||||||
}
|
}
|
||||||
if (req.uri_path == "/profile") {
|
if (req.uri_path == "/profile") {
|
||||||
return rteee("/assets/html/profile.html");
|
return rteee("profile", false);
|
||||||
}
|
}
|
||||||
if (req.uri_path == "/registration") {
|
if (req.uri_path == "/registration") {
|
||||||
return rteee("/assets/html/registration.html");
|
return rteee("registration", false);
|
||||||
}
|
}
|
||||||
/* Trying to interpret request as asset lookup */
|
/* Trying to interpret request as asset lookup */
|
||||||
ret = samI.get_asset(req.uri_path, sa);
|
ret = samI.get_asset(req.uri_path, sa);
|
||||||
@ -95,6 +110,7 @@ int main(int argc, char** argv){
|
|||||||
}
|
}
|
||||||
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
|
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
|
||||||
};
|
};
|
||||||
|
|
||||||
params.ports_to_listen = {1025};
|
params.ports_to_listen = {1025};
|
||||||
params.slave_number = 8;
|
params.slave_number = 8;
|
||||||
params.open_admin_listener = false;
|
params.open_admin_listener = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user