Files
JetCore/jetc/build-compiled.sh
T
Brad ArantandClaude Sonnet 5 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>
2026-08-23 19:18:41 -07:00

47 lines
1.4 KiB
Bash
Executable File

#!/bin/bash
# Builds jetc itself (if needed), compiles a .jet file to C++, and links the
# result into a standalone executable against CoreUtils + the JetCore
# runtime (everything jet-2.0 itself links against, minus jet-2.0.cpp's own
# main() - the compiled program brings its own).
#
# Usage: ./build-compiled.sh <input.jet> <output-binary-name>
set -e
JC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CU="$JC/../CoreUtils"
HERE="$JC/jetc"
if [ $# -ne 2 ]; then
echo "usage: $0 <input.jet> <output-binary-name>"
exit 1
fi
INPUT="$1"
OUTNAME="$2"
if [ ! -x "$HERE/jetc" ]; then
echo "Building jetc..."
g++ -g -std=c++23 -o "$HERE/jetc" "$HERE/jetc.cpp" "$HERE/JetParser.cpp" "$HERE/JetCodegen.cpp"
fi
echo "Compiling $INPUT -> $OUTNAME.compiled.cpp"
"$HERE/jetc" "$INPUT" "$HERE/$OUTNAME.compiled.cpp"
echo "Building JetCore runtime objects (everything Tag::interpretSpan's fallback path may need)..."
list=""
for f in "$JC"/*.cpp; do
base=$(basename "$f")
if [ "$base" = "jet-2.0.cpp" ]; then continue; fi
obj="$HERE/${base%.cpp}.o"
if [ ! -f "$obj" ]; then
g++ -g -c -std=c++23 -I"$CU" -I"$JC" "$f" -o "$obj" &
fi
list="$list $obj"
done
wait
g++ -g -c -std=c++23 -I"$CU" -I"$JC" "$HERE/$OUTNAME.compiled.cpp" -o "$HERE/$OUTNAME.compiled.o"
g++ -g -o "$HERE/$OUTNAME" "$HERE/$OUTNAME.compiled.o" $list -std=c++23 -L"$CU" -lCoreUtils -lmysqlclient -lcrypto
echo "Built $HERE/$OUTNAME"