d52ca61250d52f4b608153138e871eef7c497256
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d52ca61250 |
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> |
||
|
|
fc849c44a4 |
Back $[@name] session variables with a JSON file via JFile, reusing the @ sigil
The @ sigil previously read through global.getSession(global.sessionId), a MySQL-session binding that was already confirmed dead this session - Global::sessionId is never assigned anywhere in the codebase, so any $[@name] read would always throw. The design goal: persist session state as a JSON file at sessiondir/<sessionId>.json, using CoreUtils's existing JFile (an MFile+JString combination with load-on-construct/save-on- destruct RAII semantics), reusing @ rather than introducing a new sigil since the old @ usage was unreachable in practice anyway. Global.h/.cpp: added Global::sessionFile (a JFile*, NULL unless a request is in cgi mode with sessiondir set). Its destructor now explicitly deletes sessionFile - JFile's actual disk write happens in MFile's destructor via update(), so this has to run for persistence to happen at all (a raw `new`'d pointer that's never deleted, as cgiData still is, would never save anything back to disk). __jet.cpp: the sessiondir block now builds the session file path from the sessiondir keyword and the active session id (existing cookie or newly issued) and constructs global.sessionFile there, for both branches. The "pull sessionfile from sessiondir" comment is now implemented - JFile's own constructor already handles "file doesn't exist yet" by loading as empty, so one construction point serves both a resumed and a brand-new session. Tag.cpp: getVariable's "@" branch now reads via (*global.sessionFile)[name] with a clear error if no session is active, instead of the dead MySQL lookup. Added Tag::assignVariable (declared in Tag.h), a single write path both storeVariable overloads now funnel through, so a "@name"- prefixed target (e.g. <set name="@x" value="y"/>) is routed to the session file instead of the normal global/local/parent variables maps - without this, the read side would have been read-only, which isn't a very useful session mechanism. This surfaced two real, pre-existing CoreUtils bugs, both fixed separately (see CoreUtils commit 3e6e410): JFile's virtual-base diamond skipping JString's own empty-object seed, and MString's ZString-source copy paths pairing an absolute data pointer with a cursor-relative length - both were silently corrupting any cursor-advanced ZString (exactly what assignVariable produces when it strips the "@" prefix via ifNext() before storing). Verified live: two genuinely separate jet-2.0 process invocations sharing one session cookie, where the first writes two session variables and the second - a fresh process with no shared memory - reads both back correctly from the JSON file on disk. Also re-ran tests/posttest.sh (unchanged output, no regression) and the full CoreUtils test suite (no regressions). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
6f2f3d7339 |
Fix testpost.jet: cgi="true" doesn't match __jet.cpp's cgi=="yes" check
$[^session] (a cookie lookup, global.cookies.data[name]) was always empty because __jet.cpp's cgi keyword check compares against "yes" (a deliberate choice from an earlier session, not the codebase-wide true/false convention used by filterblanklines/trimlines/eval etc.) - testpost.jet had cgi="true", a stale mismatch that predates that decision, so the entire cgi block (including reading HTTP_COOKIE into global.cookies) never ran at all. Fixed the test fixture to say cgi="yes" to match. Verified live via tests/posttest.sh against the current build: $[^session] now correctly prints 18fd56a83f (posttest.sh's simulated HTTP_COOKIE session id), and dump.txt's *** COOKIES *** section shows session=[18fd56a83f] instead of empty. dump.txt committed with this fresh, correct output. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
51133e0c76 |
Wire CGI form data through the new Parser-based CGIFormData
Global now owns a long-lived StreamReader (cgiReader) that CGIFormData
(a Parser) holds a reference to - declared before cgiData so it's
constructed first and destroyed last. __jet.cpp's POST handling
constructs CGIFormData against it and feeds the request body off stdin
in 4KB chunks, driving doParse() after each feed rather than blocking to
read the whole body up front - the same feed()/doParse() pattern a
socket-fed StreamReader would use, keeping memory bounded for large
multipart uploads.
Also fixes two real, pre-existing bugs surfaced while getting an actual
end-to-end CGI request working through jet-2.0 (not just compiling):
- Tag::Tag's opening-tag parser was gated on "parent && in.ifNext(\"<\")",
but jet-2.0.cpp's main() constructs the top-level __jet with
parent=NULL - meaning the outer <jet ...> tag's own attributes
(cgi="yes" and everything else) were never actually parsed at all, just
echoed as literal passthrough content. parent=NULL is used at exactly
one call site in the whole codebase, so dropping the "parent &&"
requirement only changes behavior for that one top-level case - every
nested tag construction already passes a real parent.
- __jet.cpp's boundary-extraction code calls contentType.find("boundary=")
to locate the multipart boundary parameter, but find() repositions
contentType's own cursor to the match as a side effect. That same
cursor-shifted contentType then got passed into CGIFormData's
constructor, whose startsWith("multipart/form-data")/
startsWith("application/x-www-form-urlencoded") checks are
cursor-relative and so silently matched neither, falling through to an
unrecognized content type that parsed nothing (reported COMPLETE
immediately, no error, just empty results). Fixed with
contentType.reset() before the CGIFormData construction.
Verified with a real end-to-end run: jet-2.0 invoked with simulated CGI
environment variables (REQUEST_METHOD=POST, CONTENT_TYPE with a
boundary=, CONTENT_LENGTH) and a genuine multipart/form-data body
(including a file-upload part) piped over stdin, using $[:name] in a
.jet script - correctly returns each field's value and correctly leaves
the skipped upload field empty.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
2d9505cd33 |
Fix jet cgi=yes CGI variable loading and related bugs
- __jet.cpp: the cgi keyword check compared against "true" but the
documented/intended value is "yes", so <jet cgi="yes" ...> never
actually turned CGI mode on; fixed. Also replaced the hardcoded
fake multipart boundary ("xxxx") with real extraction from the
Content-Type header's boundary= parameter.
- Global.h: default-initialize cgiData to NULL (a GET request with
cgi="yes" never allocates it, so it was previously left
dangling); Global.cpp now sets the cookie jar's delimiter to ";"
to match RFC 6265, since it defaulted to CGIFormattedData's
&-based form-data delimiter.
- Global.cpp: fix addSession()'s missing throw that let it silently
continue past a duplicate session id.
- Tag.cpp: fix the CGI-variable lookup branch, which failed to
compile (cgiData is a pointer; needed to dereference it before
indexing) and added a null check so a CGI request that never
populated cgiData (bad Content-Type, or the null-safety gap
above) throws a clear error instead of crashing; implemented the
previously-empty session-variable lookup branch; left the
unreachable duplicate environment-variable branch as-is with a
comment, since fixing its sigil is a user-facing template-syntax
decision, not a bug fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|