The compiled dialect¶
situ compiles a bounded subset of Python to JavaScript and rejects everything else at compile time with a CompileError that names the problem and usually the fix. This is the same design stance as the numeric subset-of-Python compilers (Numba, JAX): a well-understood subset with loud rejection, so a missing feature surfaces as an error during development.
The compiler carries 111 distinct rejection sites; the greatest hits are catalogued in Compile errors.
Expressions¶
Usable in binder values (:show="…", :text="…") and event actions:
| Construct | Compiles to | Notes |
|---|---|---|
| names | signal reads (S.get) or row-var reads |
unknown names are rejected |
| literals | JS literals | str/int/float/bool/None |
and / or / not |
&& / \|\| / ! |
|
== != < <= > >= |
strict ===-family, numeric-normalized |
when either side is numeric, operands are wrapped in Number(...) — never JS loose equality |
+ - * / % |
JS arithmetic | |
x if c else y |
ternary | |
x in y / not in |
Set.has on a Local[set], .includes on strings |
can't chain with relational ops |
| f-strings | template literals | conversions (!r) and format specs (:.2f) are rejected |
t.field (row var) |
a dataset / row-object read |
only inside a :each scope or on data-id rows |
| subscripts, slices | JS indexing / .slice |
no slice step |
| dict / list literals | object / array literals | dict keys must be string constants |
Whitelisted calls: len (→ .length/.size), sum(<generator>) (→ .reduce, single generator, no filter), min/max (≥ 2 args), abs, round, today() (→ the local ISO date), and the string methods strip lower upper startswith endswith.
Statements (local handler bodies)¶
| Accepted | Notes |
|---|---|
name = expr |
a local-signal write (S.set) or a plain let — single target only |
x += / -= / *= / /= / %= |
augmented assignment |
if / elif / else |
|
return (incl. early) |
|
s.add(x) s.discard(x) l.append(x) l.remove(x) |
on Local[set] / Local[list] — immutable shim helpers, so subscribers always fire |
reset("name", ...) |
back to declared defaults |
global / nonlocal |
emit nothing — they make bare-name signal writes valid CPython |
pass, docstrings |
Rejected — always loudly¶
for / while / with / try / yield / lambda / comprehensions (beyond the one sum generator) / import / nested def or class / unknown calls or attributes — and every seam violation. Each rejection is a one-line message; the catch-all reads statement not in the compiled dialect: <NodeType>.
Semantics worth knowing¶
- Numbers stay numbers. A
:bindon<input type="number">stores a JS number, socount == 0andcount > 50behave like Python. Comparisons are strict equality after explicit numeric coercion. Decimalstays a string (in declui and by convention): a JS float would corrupt money. Equality works; relational comparison on Decimals is rejected where it would be lexical.today()lowers to the local date as ISOYYYY-MM-DD— safe to compare lexically against<input type="date">values.sum(x.price for x in items)works on aLocal[list]— the one comprehension-shaped construct, chosen because rollups are ubiquitous.
When you hit the wall¶
The wall is the feature. When the dialect rejects your code, the fix is one of:
- Move it to the server — add an
await(a facade call) and let it become a command; loops over data belong incontext()or the facade. - Restructure into dialect — e.g. replace a loop-accumulate with
sum(...), or atrywith a validity check. - It is runtime user input — then it can't be compiled by anyone; that's what the one sanctioned interpreter boundary looks like (the spreadsheet demo's
:cellsformula engine).
If a rejection message doesn't point you at the fix, that's a bug in the message — the error texts are maintained as part of the API.