Add jetc: a prototype JET-to-C++ compiler, plus the Tag API it needed
jetc (JetCore/jetc/) compiles a subset of JET template syntax - literal text, $[...]/#[...] variable output, <set>, <if>/<else>, <for>, <while> - into a standalone C++ program that links against CoreUtils and the same JetCore tag-class library jet-2.0 itself uses. The static tag/text structure becomes real C++ control flow (if/for/while), resolved once at build time instead of being re-scanned character-by-character from raw source on every request and, for loop bodies, on every single iteration (confirmed by reading Tag::parseContainer - it re-tokenizes its container text from scratch every time it runs, since a tag's parse and its execution are the same pass in this codebase). Anything genuinely dynamic - variable values, expr/value comparisons, loop bounds - still calls into the existing runtime (Tag::getVariable/resolveKeyword, jet::Operand) exactly as the interpreter does; compiling doesn't try to eliminate that; only the repeated re-parsing of the template's own shape. A <if type="..."> or <while type="..."> comparison type is resolved to its C++ operator once at compile time (type is a literal attribute value in practice, not runtime data) instead of being re-checked against six string constants on every evaluation. Any tag jetc doesn't have a codegen rule for (mysql, sql, call, include, a <set> with a container body, etc.) is captured as its exact raw source span and handed to the existing interpreter at compiled-program runtime via the new Tag::interpretSpan(), so an unsupported construct degrades gracefully instead of failing to compile - a real, if narrower, program rather than requiring full tag coverage up front. Tag.h/.cpp: made assignVariable public (parallel to the already-public getVariable - generated code needs a write path without being a Tag subclass) and added interpretSpan() as the fallback's entry point into parseContainer. Verified live: two representative templates (jetc/demo.jet, an if/for/set walkthrough; jetc/demo2.jet, a nested <if> inside a <for>, a <while> that mutates and rereads its own condition variable across iterations, and a fallback-interpreted <comment> tag) both compile, link, run standalone, and produce byte-identical output to jet-2.0 running the same source. Building and running jetc/demo2.jet's <while> loop surfaced a real, previously-unknown MString bug (fixed separately, see CoreUtils commit 6e4e463) along with a separate, already-existing bug in __while.cpp's value1/value2/type form (unrelated to this compiler, not fixed here - see project memory). jetc/build-compiled.sh builds the compiler and a given .jet file's compiled binary in one step; jetc/demo.jet and demo2.jet are kept as worked examples. Known limitations: <set> with a container body isn't modeled (falls back to interpretation); a fallback-interpreted tag nested inside compiled control flow uses the template's single top-level scope for parent/local context rather than a faithful per-level chain, which only matters for legacy tags that rely on nested "local" scoping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fc849c44a4
commit
d52ca61250
11 files changed
+822
-6
No files matched your search
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#include "JetParser.h"
|
||||
#include "JetCodegen.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
std::cerr << "usage: jetc <input.jet> <output.cpp>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::ifstream in(argv[1]);
|
||||
if (!in) {
|
||||
std::cerr << "jetc: cannot open " << argv[1] << 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);
|
||||
}
|
||||
|
||||
try {
|
||||
jetc::Parser parser(src);
|
||||
auto nodes = parser.parseNodes();
|
||||
jetc::Codegen codegen;
|
||||
std::string cpp = codegen.generate(nodes, std::string("compiled from ") + argv[1]);
|
||||
std::ofstream outFile(argv[2]);
|
||||
outFile << cpp;
|
||||
outFile.close();
|
||||
std::cout << "jetc: wrote " << argv[2] << std::endl;
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "jetc: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user