Multi-day accumulated fixes: tag dispatch, session/pooling correctness, SPA rollout, jetc compiler
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.
This commit is contained in:
1 parent
54b6e522f9
commit
b0cadc3f08
162 files changed
+3567
-2887
No files matched your search
+174
-104
@@ -49,124 +49,181 @@ namespace jetc {
|
||||
return expr.c_str();
|
||||
}
|
||||
|
||||
void Codegen::emitBody(const std::vector<NodePtr> &nodes, int indent) {
|
||||
for (auto &n : nodes) emitNode(n, indent);
|
||||
void Codegen::emitBody(const std::vector<NodePtr> &nodes, int indent, std::ostringstream &target) {
|
||||
for (auto &n : nodes) emitNode(n, indent, target);
|
||||
}
|
||||
|
||||
void Codegen::emitNode(const NodePtr &n, int indent) {
|
||||
void Codegen::emitNode(const NodePtr &n, int indent, std::ostringstream &target) {
|
||||
std::string I = ind(indent);
|
||||
switch (n->kind) {
|
||||
|
||||
case NodeKind::TEXT: {
|
||||
if (n->text.empty()) break;
|
||||
out << I << "out << " << cstr(n->text) << ";\n";
|
||||
target << I << "out << " << cstr(n->text) << ";\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::VAR: {
|
||||
out << I << "{ coreutils::ZString v(" << cstr(n->varText) << "); out << scope.getVariable(v, true); }\n";
|
||||
target << I << "{ coreutils::ZString v(" << cstr(n->varText) << "); out << scope.getVariable(v, true); }\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::SET: {
|
||||
std::string k = id("set_");
|
||||
out << I << "{\n";
|
||||
out << I << " scope.keywords[\"" << k << "name\"] = " << cstr(n->setName) << ";\n";
|
||||
out << I << " scope.keywords[\"" << k << "scope\"] = " << cstr(n->setScope) << ";\n";
|
||||
out << I << " coreutils::MString val;\n";
|
||||
target << I << "{\n";
|
||||
target << I << " scope.keywords[\"" << k << "name\"] = " << cstr(n->setName) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "scope\"] = " << cstr(n->setScope) << ";\n";
|
||||
target << I << " coreutils::MString val;\n";
|
||||
if (n->setHasExpr) {
|
||||
out << I << " scope.keywords[\"" << k << "expr\"] = " << cstr(n->setExpr) << ";\n";
|
||||
out << I << " val = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).string;\n";
|
||||
target << I << " scope.keywords[\"" << k << "expr\"] = " << cstr(n->setExpr) << ";\n";
|
||||
target << I << " val = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).string;\n";
|
||||
} else {
|
||||
out << I << " scope.keywords[\"" << k << "value\"] = " << cstr(n->setHasValue ? n->setValue : std::string()) << ";\n";
|
||||
out << I << " val = scope.resolveKeyword(\"" << k << "value\");\n";
|
||||
target << I << " scope.keywords[\"" << k << "value\"] = " << cstr(n->setHasValue ? n->setValue : std::string()) << ";\n";
|
||||
target << I << " val = scope.resolveKeyword(\"" << k << "value\");\n";
|
||||
}
|
||||
out << I << " scope.assignVariable(scope.resolveKeyword(\"" << k << "name\"), val, scope.resolveKeyword(\"" << k << "scope\"));\n";
|
||||
out << I << "}\n";
|
||||
target << I << " scope.assignVariable(scope.resolveKeyword(\"" << k << "name\"), val, scope.resolveKeyword(\"" << k << "scope\"));\n";
|
||||
target << I << "}\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::IF: {
|
||||
std::string k = id("if_");
|
||||
out << I << "{\n";
|
||||
out << I << " bool cond;\n";
|
||||
target << I << "{\n";
|
||||
target << I << " bool cond;\n";
|
||||
if (n->cond.mode == CondMode::EXPR) {
|
||||
out << I << " scope.keywords[\"" << k << "expr\"] = " << cstr(n->cond.expr) << ";\n";
|
||||
out << I << " cond = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).boolean;\n";
|
||||
target << I << " scope.keywords[\"" << k << "expr\"] = " << cstr(n->cond.expr) << ";\n";
|
||||
target << I << " cond = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).boolean;\n";
|
||||
} else {
|
||||
out << I << " scope.keywords[\"" << k << "value1\"] = " << cstr(n->cond.value1) << ";\n";
|
||||
out << I << " scope.keywords[\"" << k << "value2\"] = " << cstr(n->cond.value2) << ";\n";
|
||||
out << I << " int rc = scope.resolveKeyword(\"" << k << "value1\").compare(scope.resolveKeyword(\"" << k << "value2\"));\n";
|
||||
out << I << " cond = (" << staticTypeOp(n->cond.type, "rc") << ");\n";
|
||||
target << I << " scope.keywords[\"" << k << "value1\"] = " << cstr(n->cond.value1) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "value2\"] = " << cstr(n->cond.value2) << ";\n";
|
||||
target << I << " int rc = scope.resolveKeyword(\"" << k << "value1\").compare(scope.resolveKeyword(\"" << k << "value2\"));\n";
|
||||
target << I << " cond = (" << staticTypeOp(n->cond.type, "rc") << ");\n";
|
||||
}
|
||||
out << I << " if (cond) {\n";
|
||||
emitBody(n->thenBody, indent + 2);
|
||||
target << I << " if (cond) {\n";
|
||||
emitBody(n->thenBody, indent + 2, target);
|
||||
if (n->hasElse) {
|
||||
out << I << " } else {\n";
|
||||
emitBody(n->elseBody, indent + 2);
|
||||
target << I << " } else {\n";
|
||||
emitBody(n->elseBody, indent + 2, target);
|
||||
}
|
||||
out << I << " }\n";
|
||||
out << I << "}\n";
|
||||
target << I << " }\n";
|
||||
target << I << "}\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::FOR: {
|
||||
std::string k = id("for_");
|
||||
out << I << "{\n";
|
||||
out << I << " scope.keywords[\"" << k << "start\"] = " << cstr(n->forHasStart ? n->forStart : std::string("0")) << ";\n";
|
||||
out << I << " scope.keywords[\"" << k << "end\"] = " << cstr(n->forEnd) << ";\n";
|
||||
out << I << " scope.keywords[\"" << k << "step\"] = " << cstr(n->forStep) << ";\n";
|
||||
out << I << " double ix = scope.resolveKeyword(\"" << k << "start\").asDouble();\n";
|
||||
target << I << "{\n";
|
||||
target << I << " scope.keywords[\"" << k << "start\"] = " << cstr(n->forHasStart ? n->forStart : std::string("0")) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "end\"] = " << cstr(n->forEnd) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "step\"] = " << cstr(n->forStep) << ";\n";
|
||||
target << I << " double ix = scope.resolveKeyword(\"" << k << "start\").asDouble();\n";
|
||||
if (n->forHasName) {
|
||||
out << I << " scope.keywords[\"" << k << "name\"] = " << cstr(n->forName) << ";\n";
|
||||
out << I << " scope.keywords[\"" << k << "scope\"] = " << cstr(n->forScope) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "name\"] = " << cstr(n->forName) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "scope\"] = " << cstr(n->forScope) << ";\n";
|
||||
}
|
||||
out << I << " for (; ix <= scope.resolveKeyword(\"" << k << "end\").asDouble(); ix += scope.resolveKeyword(\"" << k << "step\").asDouble()) {\n";
|
||||
target << I << " for (; ix <= scope.resolveKeyword(\"" << k << "end\").asDouble(); ix += scope.resolveKeyword(\"" << k << "step\").asDouble()) {\n";
|
||||
if (n->forHasName) {
|
||||
out << I << " coreutils::MString nameSel = scope.resolveKeyword(\"" << k << "name\");\n";
|
||||
out << I << " coreutils::MString scopeSel = scope.resolveKeyword(\"" << k << "scope\");\n";
|
||||
out << I << " if ((scopeSel == \"\") || (scopeSel == \"global\")) global.variables[nameSel] = ix;\n";
|
||||
out << I << " else if (scopeSel == \"local\") scope.local->variables[nameSel] = ix;\n";
|
||||
out << I << " else if (scopeSel == \"parent\") scope.parent->local->variables[nameSel] = ix;\n";
|
||||
out << I << " else throw coreutils::Exception(\"scope value is not valid.\");\n";
|
||||
target << I << " coreutils::MString nameSel = scope.resolveKeyword(\"" << k << "name\");\n";
|
||||
target << I << " coreutils::MString scopeSel = scope.resolveKeyword(\"" << k << "scope\");\n";
|
||||
target << I << " if ((scopeSel == \"\") || (scopeSel == \"global\")) global.variables[nameSel] = ix;\n";
|
||||
target << I << " else if (scopeSel == \"local\") scope.local->variables[nameSel] = ix;\n";
|
||||
target << I << " else if (scopeSel == \"parent\") scope.parent->local->variables[nameSel] = ix;\n";
|
||||
target << I << " else throw coreutils::Exception(\"scope value is not valid.\");\n";
|
||||
}
|
||||
emitBody(n->body, indent + 2);
|
||||
out << I << " }\n";
|
||||
out << I << "}\n";
|
||||
emitBody(n->body, indent + 2, target);
|
||||
target << I << " }\n";
|
||||
target << I << "}\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::WHILE: {
|
||||
std::string k = id("while_");
|
||||
out << I << "{\n";
|
||||
out << I << " bool cond;\n";
|
||||
target << I << "{\n";
|
||||
target << I << " bool cond;\n";
|
||||
if (n->cond.mode == CondMode::EXPR) {
|
||||
out << I << " scope.keywords[\"" << k << "expr\"] = " << cstr(n->cond.expr) << ";\n";
|
||||
out << I << " coreutils::MString exprSaved = scope.keywords[\"" << k << "expr\"];\n";
|
||||
out << I << " cond = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).boolean;\n";
|
||||
out << I << " while (cond) {\n";
|
||||
emitBody(n->body, indent + 2);
|
||||
out << I << " scope.keywords[\"" << k << "expr\"].reset();\n";
|
||||
out << I << " scope.keywords[\"" << k << "expr\"] = exprSaved;\n";
|
||||
out << I << " cond = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).boolean;\n";
|
||||
out << I << " }\n";
|
||||
target << I << " scope.keywords[\"" << k << "expr\"] = " << cstr(n->cond.expr) << ";\n";
|
||||
target << I << " coreutils::MString exprSaved = scope.keywords[\"" << k << "expr\"];\n";
|
||||
target << I << " cond = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).boolean;\n";
|
||||
target << I << " while (cond) {\n";
|
||||
emitBody(n->body, indent + 2, target);
|
||||
target << I << " scope.keywords[\"" << k << "expr\"].reset();\n";
|
||||
target << I << " scope.keywords[\"" << k << "expr\"] = exprSaved;\n";
|
||||
target << I << " cond = jet::Operand(scope.keywords[\"" << k << "expr\"], scope).boolean;\n";
|
||||
target << I << " }\n";
|
||||
} else {
|
||||
out << I << " scope.keywords[\"" << k << "value1\"] = " << cstr(n->cond.value1) << ";\n";
|
||||
out << I << " scope.keywords[\"" << k << "value2\"] = " << cstr(n->cond.value2) << ";\n";
|
||||
out << I << " int rc = scope.resolveKeyword(\"" << k << "value1\").compare(scope.resolveKeyword(\"" << k << "value2\"));\n";
|
||||
out << I << " cond = (" << staticTypeOp(n->cond.type, "rc") << ");\n";
|
||||
out << I << " while (cond) {\n";
|
||||
emitBody(n->body, indent + 2);
|
||||
out << I << " int rc2 = scope.resolveKeyword(\"" << k << "value1\").compare(scope.resolveKeyword(\"" << k << "value2\"));\n";
|
||||
out << I << " cond = (" << staticTypeOp(n->cond.type, "rc2") << ");\n";
|
||||
out << I << " }\n";
|
||||
target << I << " scope.keywords[\"" << k << "value1\"] = " << cstr(n->cond.value1) << ";\n";
|
||||
target << I << " scope.keywords[\"" << k << "value2\"] = " << cstr(n->cond.value2) << ";\n";
|
||||
target << I << " int rc = scope.resolveKeyword(\"" << k << "value1\").compare(scope.resolveKeyword(\"" << k << "value2\"));\n";
|
||||
target << I << " cond = (" << staticTypeOp(n->cond.type, "rc") << ");\n";
|
||||
target << I << " while (cond) {\n";
|
||||
emitBody(n->body, indent + 2, target);
|
||||
target << I << " int rc2 = scope.resolveKeyword(\"" << k << "value1\").compare(scope.resolveKeyword(\"" << k << "value2\"));\n";
|
||||
target << I << " cond = (" << staticTypeOp(n->cond.type, "rc2") << ");\n";
|
||||
target << I << " }\n";
|
||||
}
|
||||
out << I << "}\n";
|
||||
target << I << "}\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::FALLBACK: {
|
||||
out << I << "{ coreutils::ZString span(" << cstr(n->rawSpan) << "); scope.interpretSpan(span, out); }\n";
|
||||
target << I << "{ coreutils::ZString span(" << cstr(n->rawSpan) << "); scope.interpretSpan(span, out); }\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::CONTAINER_SLOT: {
|
||||
// Reuses the interpreter's own <container> scope-walking logic
|
||||
// verbatim (see Tag::renderContainer) rather than re-deriving
|
||||
// it - this is exactly what the runtime's own <container>
|
||||
// dispatch does when it's reached via interpretSpan, just
|
||||
// without the text-scanning step to get there.
|
||||
target << I << "scope.renderContainer(out);\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeKind::TAGDEF: {
|
||||
// Codegen can still fail for a node that parsed successfully -
|
||||
// e.g. an <if>/<while> whose type= isn't one of eq/ne/lt/le/gt/ge
|
||||
// (staticTypeOp's own check - real legacy content occasionally
|
||||
// has one of these sitting in a branch that's never actually
|
||||
// reached at runtime either, so there's nothing to "correctly"
|
||||
// compile). Parsing this definition's body already succeeded
|
||||
// (see parseTagDef's own try/catch for that stage) or this node
|
||||
// wouldn't exist as TAGDEF at all - so mirror that same
|
||||
// graceful-degradation approach here for the emit stage: try
|
||||
// building the compiled function into a throwaway stream first,
|
||||
// and only splice it into tagDefFuncs (and register the
|
||||
// compiledTags fast path) if that fully succeeds. A failure
|
||||
// here just means this one definition stays interpreted,
|
||||
// unchanged - zero risk to the rest of the file.
|
||||
std::string funcName = "jetc_tagdef_" + id("");
|
||||
std::ostringstream trial;
|
||||
bool compiledOk = true;
|
||||
try {
|
||||
trial << "static void " << funcName << "(jet::Tag &scope, coreutils::MString &out) {\n";
|
||||
emitBody(n->tagDefBody, 1, trial);
|
||||
trial << "}\n\n";
|
||||
} catch (const std::exception &) {
|
||||
compiledOk = false;
|
||||
}
|
||||
|
||||
// Emitted in place, at this TAGDEF's original position in
|
||||
// program flow - matches exactly what FALLBACK would have
|
||||
// emitted for this same <tag name="X">...</tag> span, so
|
||||
// global.tags["x"] still gets populated as before (needed as a
|
||||
// correctness fallback: any invocation this compiler doesn't
|
||||
// itself recognize - e.g. reached through content this pass
|
||||
// never compiled - still resolves it exactly as it always has).
|
||||
target << I << "{ coreutils::ZString span(" << cstr(n->rawSpan) << "); scope.interpretSpan(span, out); }\n";
|
||||
if (compiledOk) {
|
||||
// The registration into compiledTags right after is the only
|
||||
// new behavior beyond the raw registration above - a pure
|
||||
// additive fast path __dotag checks first (see __dotag.cpp);
|
||||
// a miss there falls through to the now-unchanged interpreted
|
||||
// path, which is exactly what happens when compiledOk is
|
||||
// false here too.
|
||||
tagDefFuncs << trial.str();
|
||||
target << I << "global.compiledTags[coreutils::MString(" << cstr(n->tagDefName) << ")] = &" << funcName << ";\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -177,6 +234,9 @@ namespace jetc {
|
||||
const std::vector<ApiRoute> &apiRoutes, const std::string &apiSymbol) {
|
||||
out.str("");
|
||||
out.clear();
|
||||
tagDefFuncs.str("");
|
||||
tagDefFuncs.clear();
|
||||
|
||||
out << "// " << sourceFileComment << "\n";
|
||||
out << "// Generated by jetc - DO NOT EDIT BY HAND.\n";
|
||||
out << "#include \"Global.h\"\n";
|
||||
@@ -187,51 +247,61 @@ namespace jetc {
|
||||
out << "#include \"Exception.h\"\n";
|
||||
out << "#include <iostream>\n\n";
|
||||
|
||||
// The entry function's own text is built into a separate stream so
|
||||
// tagDefFuncs - populated as a SIDE EFFECT of the emitBody() call
|
||||
// below, since TAGDEF nodes append to it as they're encountered -
|
||||
// can be spliced in ahead of it. A compiled tag-definition function
|
||||
// never needs to be forward-declared for anything defined before it
|
||||
// in program order (nothing upstream calls it directly - see
|
||||
// __dotag.cpp's runtime lookup instead), so this ordering is purely
|
||||
// for readability, not a correctness requirement.
|
||||
std::ostringstream body;
|
||||
|
||||
if (sharedMode) {
|
||||
if (!apiRoutes.empty()) {
|
||||
// A plain C struct, not any CoreUtils/JetCore type - this is the
|
||||
// one piece of the plugin ABI a host loader needs to know about
|
||||
// independent of the rest of this same-ABI C++ boundary.
|
||||
out << "struct JetApiRoute { const char *method; const char *path; };\n";
|
||||
out << "extern \"C\" const JetApiRoute *" << apiSymbol << "(int *count) {\n";
|
||||
out << " static const JetApiRoute routes[] = {\n";
|
||||
body << "struct JetApiRoute { const char *method; const char *path; };\n";
|
||||
body << "extern \"C\" const JetApiRoute *" << apiSymbol << "(int *count) {\n";
|
||||
body << " static const JetApiRoute routes[] = {\n";
|
||||
for (auto &r : apiRoutes)
|
||||
out << " { " << cstr(r.method) << ", " << cstr(r.path) << " },\n";
|
||||
out << " };\n";
|
||||
out << " *count = " << apiRoutes.size() << ";\n";
|
||||
out << " return routes;\n";
|
||||
out << "}\n\n";
|
||||
body << " { " << cstr(r.method) << ", " << cstr(r.path) << " },\n";
|
||||
body << " };\n";
|
||||
body << " *count = " << apiRoutes.size() << ";\n";
|
||||
body << " return routes;\n";
|
||||
body << "}\n\n";
|
||||
}
|
||||
out << "extern \"C\" void " << entrySymbol << "(jet::Global &global, coreutils::MString &out) {\n";
|
||||
out << " coreutils::ZString jetc_empty(\"\");\n";
|
||||
out << " coreutils::MString jetc_scopeOut;\n";
|
||||
out << " jet::Tag scope(jetc_empty, jetc_scopeOut, global, NULL, NULL);\n";
|
||||
emitBody(top, 1);
|
||||
out << "}\n";
|
||||
return out.str();
|
||||
body << "extern \"C\" void " << entrySymbol << "(jet::Global &global, coreutils::MString &out) {\n";
|
||||
body << " coreutils::ZString jetc_empty(\"\");\n";
|
||||
body << " coreutils::MString jetc_scopeOut;\n";
|
||||
body << " jet::Tag scope(jetc_empty, jetc_scopeOut, global, NULL, NULL);\n";
|
||||
emitBody(top, 1, body);
|
||||
body << "}\n";
|
||||
return out.str() + tagDefFuncs.str() + body.str();
|
||||
}
|
||||
|
||||
out << "static void jetc_render(jet::Global &global, coreutils::MString &out) {\n";
|
||||
out << " coreutils::ZString jetc_empty(\"\");\n";
|
||||
out << " coreutils::MString jetc_scopeOut;\n";
|
||||
out << " jet::Tag scope(jetc_empty, jetc_scopeOut, global, NULL, NULL);\n";
|
||||
emitBody(top, 1);
|
||||
out << "}\n\n";
|
||||
out << "int main(int argc, char **argv, char **envp) {\n";
|
||||
out << " jet::Global global(envp);\n";
|
||||
out << " coreutils::MString out;\n";
|
||||
out << " try {\n";
|
||||
out << " jetc_render(global, out);\n";
|
||||
out << " global.outputHeaders();\n";
|
||||
out << " std::cout << out;\n";
|
||||
out << " } catch(coreutils::Exception e) {\n";
|
||||
out << " std::cout << out;\n";
|
||||
out << " std::cout << \"\\n----- Error: \" << e.text << std::endl;\n";
|
||||
out << " return 1;\n";
|
||||
out << " }\n";
|
||||
out << " return 0;\n";
|
||||
out << "}\n";
|
||||
return out.str();
|
||||
body << "static void jetc_render(jet::Global &global, coreutils::MString &out) {\n";
|
||||
body << " coreutils::ZString jetc_empty(\"\");\n";
|
||||
body << " coreutils::MString jetc_scopeOut;\n";
|
||||
body << " jet::Tag scope(jetc_empty, jetc_scopeOut, global, NULL, NULL);\n";
|
||||
emitBody(top, 1, body);
|
||||
body << "}\n\n";
|
||||
body << "int main(int argc, char **argv, char **envp) {\n";
|
||||
body << " jet::Global global(envp);\n";
|
||||
body << " coreutils::MString out;\n";
|
||||
body << " try {\n";
|
||||
body << " jetc_render(global, out);\n";
|
||||
body << " global.outputHeaders();\n";
|
||||
body << " std::cout << out;\n";
|
||||
body << " } catch(coreutils::Exception e) {\n";
|
||||
body << " std::cout << out;\n";
|
||||
body << " std::cout << \"\\n----- Error: \" << e.text << std::endl;\n";
|
||||
body << " return 1;\n";
|
||||
body << " }\n";
|
||||
body << " return 0;\n";
|
||||
body << "}\n";
|
||||
return out.str() + tagDefFuncs.str() + body.str();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user