Bars Specification 0.1.0 — Implementation-Defined Behavior¶
The inverse of the core: a consolidated, normative enumeration of what a conforming workalike may legitimately do differently from kBars, and the constraint that still binds each such choice.
Bars conformance comes in two tiers, both carried under one shared version number (see Core vs. optional extensions):
- Core — the template system alone.
- Full — core plus the standard library: a baseline roster of built-in transforms and
@envproperties that a full-tier implementation must support.
This page governs the boundary around those two tiers: what an implementation may vary regardless of tier, namely (1) whether it ships the standard library at all — that is, whether it targets core or full — and (2) the additions and extensions it may layer on top of the standard library. A port is free to vary anything listed here without losing conformance at its chosen tier; it is not free to vary anything documented as core elsewhere in this specification (grammar, pre-pass, data model, rendering's segments/blocks/expressions/scoped-variables/partials, errors, source locations, number rendering, iteration order, and string code-point semantics), nor — at the full tier — the standard library's own pinned behavior.
The standard-library catalog itself (which transforms and @env properties make up the baseline, and
their exact semantics) is specified separately; this page describes only what surrounds it.
Built-in transform roster¶
The standard library defines a baseline roster of named transforms that a full-tier implementation must support. A core-tier implementation need not provide any transforms at all. Beyond that baseline, two kinds of variation stay implementation-defined at either tier:
- Additional transforms. An implementation may register any number of transforms beyond the
standard library, under names the standard library does not use. kBars itself registers 46 across
the string, math, list, and date/time families (
upcase,append,map,format_date_time, and so on); which of those the standard library adopts as its baseline is settled in the standard-library catalog, and any kBars transform outside that baseline is simply a kBars addition. - Extensions of a standard-library transform. An implementation may widen a standard-library
transform — for example, accepting an optional argument the standard library does not define —
provided the standard-library behavior is preserved exactly for the inputs the standard library
specifies. If the standard library requires
url_decodeto take zero arguments, an implementation whoseurl_decodealso accepts one optional argument still conforms, as long as its zero-argument behavior matches. This is a deliberately risky freedom: should a later standard-library version give that argument a meaning that differs from the implementation's, the two collide — and that is the implementer's tradeoff to make, not a conformance violation today.
What still binds: if an implementation provides a transform whose semantics correspond to one of
the five string/sort operations pinned in Data
Model (size, slice, truncate, split, sort), that
transform must count and compare in Unicode code points, not UTF-16 code units or bytes — that rule
is core regardless of what the transform is named or which tier claims it. A reference to an
unregistered transform name must be a hard failure (KB-2001, UnknownTransform); calling a
transform with the wrong argument count must be a hard failure (KB-2002, TransformArity) if the
implementation's transform model distinguishes arity at all.
See conformance/transforms/*.json and Rendering for
the kBars roster, and the Transforms reference for the full baseline catalog.
The @env namespace¶
The standard library also defines a baseline of @env properties (kBars registers now) that a
full-tier implementation must expose through an @env namespace. A core-tier implementation
need not have an @env namespace, any particular property, or any property-registration API at all.
On top of the standard-library baseline, two things stay implementation-defined:
- Additional properties. An implementation may register
@envproperties beyond the standard library's baseline. - How a clock-seeded property is sourced. What clock seeds a
now-like property is the implementation's own choice; only such a property's observable value-kind and resolution behavior are pinned by the standard library, never a specific instant.
What still binds: whenever an implementation exposes an @env-equivalent namespace, an
unresolved reference within it (unknown property name, or a sub-pointer applied to a non-container
value) must render empty — not the literal "null" that an ordinary absent-context lookup falls back
to (see Data Model). If an implementation supports the {{= }}
scoped-variable tag at all, a sub-pointer assign target (@env/a/b) must be rejected with KB-3002
(AssignTargetSubPointer). These rules are core: they bind a core-tier implementation that chooses to
offer @env just as much as they bind a full-tier one.
See conformance/env-properties/*.json and Rendering
for the kBars roster, and the Environment Properties reference for the full
baseline catalog.
Tunable limits¶
The default maximum partial-recursion depth is implementation-defined. kBars's default is its
DEFAULT_MAX_PARTIAL_DEPTH constant, currently 64 — a value specific to kBars, not a number other
ports are expected to match. kBars exposes a builder method to override it per environment; a port
may choose any other default, or no built-in default at all (requiring the host application to always
specify one), without losing conformance.
What still binds: whatever depth a port enforces, exceeding it must be a soft failure —
KB-5007 (RecursionLimit): the over-deep partial is abandoned,
rendering continues, and no exception is thrown. A port that instead lets unbounded recursion
overflow its call stack, or that throws a hard failure/crashes the process, does not conform,
regardless of what depth value triggers the check.
No conformance fixture depends on any implementation's default depth. Every fixture that
exercises the recursion limit sets an explicit maxPartialDepth in its fixture JSON (see
partials-fixture.schema.json) rather than relying on whatever default the runner under test happens
to have configured. conformance/partials/soft-failures.json's recursion-limit-soft-fails-and-stops
fixture is one example. A runner may therefore use any default (or require an explicit one, with no
built-in default at all) and still pass the suite unchanged.
The maximum block-nesting depth is also implementation-defined — but its failure mode is the
opposite of the partial-recursion limit above. kBars enforces a DEFAULT_MAX_NESTING_DEPTH
constant bounding how deeply {{#each}}/{{#if}}/{{#with}}/{{#unless}}/{{#inline}}/{{#> }}
blocks may nest inside one another. Unlike the partial-recursion limit, exceeding it is a hard
parse-time failure — KB-1003 (NestingTooDeep) — not a soft-fail
event: the template is rejected before rendering begins, with no partial output. This is deliberate:
the recursive-descent parser itself would otherwise overflow the native call stack on a
pathologically deep template, a condition no soft-fail recovery can happen after the fact. A port may
choose any depth for this ceiling (or none, if its own parser is not recursion-bound and cannot
overflow), provided that exceeding whatever ceiling it does enforce is a clean, located hard failure
rather than a crash — see Adversarial inputs.
No conformance fixture depends on any implementation's nesting-depth ceiling either.
conformance/render/torture-deep-nesting.json exercises deep-but-legal nesting at a modest, portable
depth chosen to render successfully under any reasonable implementation's ceiling — it does not probe
where any particular implementation's limit lies, since that limit is not portable.
Other explicitly-open behavior¶
- Human-readable exception message text is non-normative and may change between kBars releases;
only the
errorCodeis stable. A port's own message text is entirely its own choice — see Errors. - Which digit-generation algorithm produces shortest-round-trip number output is open (kBars
ports the digit-generation core of Ryu); any correct shortest-round-trip generator (Grisu3,
Dragon4, a bignum library, or a host
Number.prototype.toString()that happens to already be ECMA-262-conformant) is acceptable, provided the output text matches the fixtures — see Data Model. - Internal representation of the three value-model kinds (element / date-time / empty) is open; only their externally observable render/compare/iterate behavior is normative — see Data Model.