JetCore engine (Tag.cpp/.h, Global.cpp/.h, __*.cpp tag classes): - Numerous __mysql.cpp connection-pooling correctness fixes (stale result sets, double-indirection sessionid bugs, field caching, pool keying) and a session-registration-before-connection-confirmed ordering fix (dangling pointer on a failed connect) - Tag.cpp: large body of fixes to the tag-dispatch/container-scanning logic (bare-name fallback, database aliasing, single-quote handling, @-prefix session variable resolution, TAGDEF caching, nested split-tag scanning for if/while/until/ifrow so an inner one doesn't corrupt an outer one's own boundary scan) plus the process-first rendering order fix (a <process> now runs before <checksession>/page resolution, fixing a real "blank page after successful login" bug) - Global.h/.cpp: added imapSessions (parallel to the existing __mysql* session map) for the new <imap> tag family - __whiledir.cpp/__include.cpp: caching and directory-name-aliasing fixes - __call.cpp/.h: fixed a dangling-pointer bug - __write.cpp: fixed an O_WRONLY-only open that couldn't create a new file - __ifrow.cpp/__sql.cpp: fixed the same sessionid double-indirection bug __mysql.cpp had (resolveKeyword's result was being used as a map key lookup instead of the resolved value itself) jetc compiler (jetc/JetParser.cpp/.h, JetCodegen.cpp/.h, JetAST.h, jetc.cpp, build-shared.sh, build-compiled.sh): - Parser fixes: inline <include>, nested-quote handling, void-tag recognition - Codegen fixes and expanded AST support - build-shared.sh: atomic rename into place for a freshly-built .jtc (a live server dlopen()ing the same path mid-rebuild was corrupting its own already-mapped view of the old file) and a staleness check so a cached JetCore .o doesn't silently keep serving yesterday's fixes forever - Added -lssl to both build scripts' link lines (needed once the new <imap> tag's client-side TLS code existed - previously nothing in this codebase used OpenSSL's SSL/TLS client API, only libcrypto) Site content (jetc/barant-site/): SPA (fetch-and-swap navigation) rollout across essentially every .page/.jet/.section/.tag/.proc/.method file - dynamic-content fixes, fragment-mode stripping, native $[@name] session variables replacing older mechanisms, and this session's own checkout.page redesign, login start-param redirect fix, and per-page side-menu (menu.bar/$[PAGE].menu) refresh support. Completed the earlier main.css/control.css move from assets/ to assets/images/ (git still showed the old paths as tracked-but-deleted until now). Deliberately left out of this commit: dozens of .pre-<description>- <date> manual backup files (this session's and earlier ones' own before/after snapshots, not meant for permanent history), compiled .jtc build outputs, ad hoc test binaries and scripts under jetc/ (login_asan, wcwc_test, optest2_bin, etc.), and jetc/__pycache__/ - none of these are meant to be tracked; a .gitignore covering them would be worth adding separately.
124 lines
4.7 KiB
C++
124 lines
4.7 KiB
C++
#include "JetParser.h"
|
|
#include "JetCodegen.h"
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <cctype>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
std::string baseNameNoExt(const std::string &path) {
|
|
size_t slash = path.find_last_of("/\\");
|
|
std::string base = (slash == std::string::npos) ? path : path.substr(slash + 1);
|
|
size_t dot = base.find_last_of('.');
|
|
if (dot != std::string::npos) base = base.substr(0, dot);
|
|
return base;
|
|
}
|
|
|
|
std::string sanitizeIdent(const std::string &s) {
|
|
std::string r;
|
|
for (char c : s) r += (std::isalnum((unsigned char)c) || c == '_') ? c : '_';
|
|
return r;
|
|
}
|
|
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
bool shared = false;
|
|
std::vector<std::string> pos;
|
|
for (int i = 1; i < argc; ++i) {
|
|
std::string a = argv[i];
|
|
if (a == "--shared") shared = true;
|
|
else pos.push_back(a);
|
|
}
|
|
if (pos.size() < 2) {
|
|
std::cerr << "usage: jetc [--shared] <input.jet> <output.cpp>" << std::endl;
|
|
std::cerr << " --shared emit a dlsym()-able entry point (see build-shared.sh)" << std::endl;
|
|
std::cerr << " instead of a standalone main()." << std::endl;
|
|
return 1;
|
|
}
|
|
const std::string &inputPath = pos[0];
|
|
const std::string &outputPath = pos[1];
|
|
|
|
std::ifstream in(inputPath);
|
|
if (!in) {
|
|
std::cerr << "jetc: cannot open " << inputPath << std::endl;
|
|
return 1;
|
|
}
|
|
std::ostringstream ss;
|
|
ss << in.rdbuf();
|
|
std::string src = ss.str();
|
|
|
|
// Skip a leading shebang line, mirroring jet-2.0.cpp's own data.goeol()
|
|
// call (run before it constructs the top-level __jet tag) so a
|
|
// "#!../jet-2.0" first line doesn't get compiled in as literal output.
|
|
if (src.rfind("#!", 0) == 0) {
|
|
size_t nl = src.find('\n');
|
|
src = (nl == std::string::npos) ? std::string() : src.substr(nl + 1);
|
|
}
|
|
|
|
// Consume zero or more leading "#api METHOD /path" directive lines
|
|
// (right after the optional shebang), the same way the shebang itself
|
|
// is stripped - a plain directive line, not a new tag/attribute concept,
|
|
// since jetc's parser has no existing notion of the enclosing <jet> tag's
|
|
// attributes to hang this off of. Each becomes one route this template
|
|
// wants to serve once loaded as a .jtc (see JetCodegen's apiRoutes).
|
|
std::vector<jetc::ApiRoute> apiRoutes;
|
|
while (src.rfind("#api ", 0) == 0) {
|
|
size_t nl = src.find('\n');
|
|
std::string line = (nl == std::string::npos) ? src : src.substr(0, nl);
|
|
src = (nl == std::string::npos) ? std::string() : src.substr(nl + 1);
|
|
|
|
std::istringstream ls(line);
|
|
std::string tag, method, path;
|
|
ls >> tag >> method >> path;
|
|
if (method.empty() || path.empty() || path[0] != '/') {
|
|
std::cerr << "jetc: malformed #api directive (expected \"#api METHOD /path\"): "
|
|
<< line << std::endl;
|
|
return 1;
|
|
}
|
|
for (auto &c : method) c = (char)std::toupper((unsigned char)c);
|
|
apiRoutes.push_back({method, path});
|
|
}
|
|
|
|
try {
|
|
std::string baseDir;
|
|
{
|
|
size_t slash = inputPath.find_last_of("/\\");
|
|
if (slash != std::string::npos) baseDir = inputPath.substr(0, slash);
|
|
}
|
|
jetc::Parser parser(src, baseDir);
|
|
auto nodes = parser.parseNodes();
|
|
jetc::Codegen codegen;
|
|
// Both symbols are derived from the input filename (not the output
|
|
// path) so they stay stable and predictable for whatever builds and
|
|
// dlsym()'s the resulting .jtc - unique per template, so multiple
|
|
// compiled templates' .jtc's can coexist in one process even under
|
|
// dlopen(..., RTLD_GLOBAL) instead of requiring isolated handles.
|
|
std::string baseIdent = sanitizeIdent(baseNameNoExt(inputPath));
|
|
std::string symbol = "jetc_render_" + baseIdent;
|
|
std::string apiSymbol = "jetc_api_" + baseIdent;
|
|
if (!apiRoutes.empty() && !shared)
|
|
std::cerr << "jetc: warning: #api directive(s) present but --shared not specified - "
|
|
"route metadata will not be emitted" << std::endl;
|
|
std::string cpp = codegen.generate(nodes, std::string("compiled from ") + inputPath, shared,
|
|
symbol, apiRoutes, apiSymbol);
|
|
std::ofstream outFile(outputPath);
|
|
outFile << cpp;
|
|
outFile.close();
|
|
if (shared) {
|
|
std::cout << "jetc: wrote " << outputPath << " (entry symbol: " << symbol << ")";
|
|
if (!apiRoutes.empty())
|
|
std::cout << " (api symbol: " << apiSymbol << ", " << apiRoutes.size() << " route(s))";
|
|
std::cout << std::endl;
|
|
} else {
|
|
std::cout << "jetc: wrote " << outputPath << std::endl;
|
|
}
|
|
} catch (std::exception &e) {
|
|
std::cerr << "jetc: " << e.what() << std::endl;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|