Browse documentation
Docs/Language

Grammars And Parsers

Grammars are immutable Grid values. A grammar declares language semantics; a parser generator declares an execution strategy; applying one to the other produces a parser value. Parsing…

Watch it in Grid

See the concept in Grid.

Use the film for the product interaction and this article for the complete contract and reference detail.

Grammars And Parsers

Grammars are immutable Grid values. A grammar declares language semantics; a parser generator declares an execution strategy; applying one to the other produces a parser value. Parsing returns a result value containing either a parse tree or a structured parse error.

These values can be named, imported from .gs modules, passed through ordinary bindings, inspected, and resolved without converting them to source text.

Choose Semantics First

Grid supports several grammar formalisms without treating them as aliases:

Formalism Generators Choice meaning
CFG LALR, EARLEY, AUTO unordered alternatives
PEG PACKRAT, AUTO ordered, first-success choice
REGULAR DFA, AUTO one whole-input regular language

AUTO chooses only within the grammar's declared formalism. It never turns a CFG into a PEG or changes ordered choice into unordered choice. An explicitly incompatible pairing produces GRID_PARSER_GENERATOR_INCOMPATIBLE.

CFG Example

GRAMMAR arithmetic IS CFG
  TOKEN NUMBER = /(?:0|[1-9][0-9]*)(?:\.[0-9]+)?/
  SKIP /\s+/
  START expression
  expression = expression "+" term | term
  term = term "*" primary | primary
  primary = value:NUMBER | "(" expression ")"
END

fast = arithmetic USING LALR
general = arithmetic USING EARLEY
result = PARSE("1 + 2 * 3", fast)

LALR rejects conflicts while building the parser. Earley accepts general CFGs and reports an ambiguous input as GRID_PARSE_AMBIGUOUS by default. Select one deterministic derivation explicitly when that is the intended contract:

first = arithmetic USING EARLEY(ambiguity = FIRST)

PEG And Regular Examples

PEG uses / for ordered choice:

GRAMMAR command IS PEG
  START command
  command = "start" / "stop"
END

parser = command USING PACKRAT
result = PARSE("start", parser)

A regular grammar has exactly one MATCH declaration:

GRAMMAR identifier IS REGULAR
  MATCH /[A-Za-z_][A-Za-z0-9_]*/
END

parser = identifier USING DFA
result = PARSE("grid_21", parser)

Generators Are Values

Use GENERATOR when the strategy itself should be named or shared:

bounded = GENERATOR DFA(
  max_input_bytes = 4096,
  max_steps = 100000,
  max_nodes = 10000
)

parser = identifier USING bounded

The same binding can live in a .gs module and be imported with USE. Canonical formatting omits options that retain their defaults.

Grammar Body

CFG and PEG blocks accept:

  • START rule to select the entry rule; the first rule is the default.
  • SKIP /pattern/ for ignored text between tokens.
  • TOKEN name [PRIORITY integer] [HIGHLIGHT category] = /pattern/ for named tokens. HIGHLIGHT names the semantic-token category editors color the token with wherever the grammar's notation appears (TREE patterns, laws and goals); word literals color as keywords and symbol literals as punctuation without it. Categories come from the language service's legend (docs/language/vocabulary.json). The clause is editor metadata and never changes the grammar value or its identity.
  • rule = expression for productions.
  • literals ("text"), inline patterns (/pattern/), token/rule names, grouping, named captures (name:expression), and postfix ?, *, +, or {min,max} repetition.
  • BUILDS constructor(capture, ...) after a production alternative names the semantic constructor for the node that alternative produces — BUILDS add(left, right), or nullary BUILDS zero(). BUILDS capture (a bare capture name) passes that child through without adding a node.

The declared constructors form the grammar's tree signature. Every grammar-bound tree declaration (PATTERN, THEORY, ALGEBRA, TRANSFORM, TRANSLATE, SOLVER, THEOREM) binds a rule's sort with ON grammar.rule — for example ON calc.expression — and its pattern, law, and goal fragments are written in the grammar's own concrete notation: for a grammar that spells addition plus(x, zero), a law reads plus(x, zero) == x, using the grammar's spelling rather than the constructor name. Metavariables carry no sigil, and constructors are not author-facing functions.

Token ties at the same priority are errors rather than source-order guesses. Token references are case-insensitive, while their authored spelling is preserved in trees and formatted source. String literals accept ordinary UTF-8 and \uXXXX / \u{...} escapes. Regex delimiters are escaped as \/.

Zero-width token, skip, and literal patterns are rejected. Write () for an intentional empty production; this keeps nullable structure visible instead of hiding it in an empty string.

Grammar-Bound Trees

The TREE authoring and implementation surface is available through TREE-11. TREE graduation certification remains through TREE-6. TREE-7's focused implementation closeout is recorded complete; its retained macOS/Linux optimized-profile matrix is still required. The former TREE-8/9 transactional cancellation and native-codec defects are recorded closed. Graduation through TREE-11 remains held for the TREE-7 matrix, TREE-10 link/size/performance evidence and the complete TREE-11 graduation/canary/rollback evidence. These outstanding checks do not mean the authoring features are unavailable. Feature criteria never authorize or block a product operation. TREE-7's production decision kernels are sorted-reader-native; a recursive owned value is constructed only at a bounded, interruption-aware public portability, publication, or replay freeze after the decision. Live callback decision and traversal work debits the caller-owned meter once; physical canonical encoding polls the same operation-local interruption fence in bounded chunks without a duplicate logical byte charge. TREE-10's generic compiler transform is likewise real, but it executes only in explicit qualification. The faster handwritten fold is the only normal compiler path, so ordinary compilation performs neither dual execution nor a discarded Tree result.

TREE-0 provides the contextual declaration scanner, source-preserving syntax model, namespace/callable checks, exact fragment partition diagnostics, and canonical formatting. TREE-1 adds grammar-rooted native syntax_tree, tree_term, and tree_context values. Current models can inspect sorts, structure, terminals, fingerprints, origins, free names, and bindings; render validated trees; select subtrees; form contexts; and perform bounded hygienic context plugging. TREE-2 adds eager portable grammar-bound PATTERN/tree_pattern values, source-ordered WITHOUT fragments, exact bounded scalar/optional/sequence matching, canonical matches/captures/contexts, and TREE.MATCH backed by compiled structural discriminators and variadic-boundary pruning. These operations execute only inside the owning model's compiler-admitted grammar/signature authority and cross native process or HTTP boundaries only through the authenticated native Tree transport. TREE-3 adds eager immutable THEORY values with regular and non-regular many-sorted conditional equations, heterogeneous inclusion/import, assumed authority, and binding-valid direction metadata. Theory declarations are inert: they do not add a Workbook value, Graph or Predicate region, analysis operation, or executable expression. TREE-3 also makes Predicate-backed WHEN conditions on PATTERN current. A conditional match uses candidate-local structural facts plus the model's exact Predicate program and resident Graph revision, and succeeds only with supported-and-not-refuted evidence. TREE-4 adds eager immutable ALGEBRA/tree_algebra values whose explicit associative, commutative, one-sided identity, idempotent, and alpha roles cite exact theory laws. MODULO patterns use those profiles for bounded variadic ACUI matching while publishing original occurrence paths and origins. TREE.NORMALIZE(tree, algebra, work) performs the same exact bottom-up canonicalization atomically and returns a replay-authorized tree_operation_result. TREE-5 adds eager immutable TRANSFORM/tree_transform values, mixed theory-law and directed-rule candidate streams, captured/derived/fresh bindings, exact or algebraic matching, author-selected orientation, top-down/bottom-up and once/repeat traversal, THEN composition, hygienic transactional replacement, and refusal-biased REQUIRE TERMINATING / REQUIRE CONFLUENT certificates. A named transform is called directly with one tree; TREE.APPLY(operation, tree) applies an aliased or selected handle under the transform's identity-bearing limit and returns the same bounded, replay-authorized tree_operation_result. Predicate-backed conditions admit a candidate only in the exact supported evidence state. TREE-6 adds eager immutable TRANSLATE/tree_translation values over independently authenticated source and target grammar domains. Exact sort-pair rules combine captured, derived, and fresh target bindings with explicit token maps and converters, strict-descendant recursion, exact delegation, optional and sequence splicing, target-language hygiene, signature validation, and render/reparse validation. Named one-tree calls and dynamic TREE.APPLY(operation, tree) use the same transactional executor and publish the same authenticated complete or incomplete operation result, derivation, condition evidence, and replay transport.

Compiler V3 synthesis recognizes each callback result against its declared canonical sort and the defining grammar rule. For a NUMBER binding, a returned "8" denotes that NUMBER member even when the grammar starts at a larger program such as PUSH 8. Later bindings can consume the actual produced member, including optional, sequence and record values. The exact-sort example produces PAIR 8 8 through both named and dynamic transform calls. The same recognition applies to translations and operational solver steps. Previously stored evidence retains its original replay behavior; private fragment evidence grants no public tree or execution authority, and checked-theorem restrictions remain unchanged.

TREE-7 is implemented across authoring, evaluation, native transport, and public catalog surfaces, with TREE graduation still under the revalidation described above. TREE.UNIFY(left, right, algebra_or_exact, family_limit, work) returns canonical complete or sound-prefix constrained families. TREE.SUBSTITUTE(term, family, context, work) has exactly four arguments; context is :left, :right, or :qualified, and no three-argument or side-inferred form exists. TREE.NARROW(term, theory, algebra_or_exact, step_limit, work) returns one-step successors in occurrence, flattened-law, and direction order. A flattened semantic law retains its complete ordered source-origin vector as authority but still contributes one attempt per direction, not one attempt per origin. Symbolic results remain tree_term values through the append-only GRIDUTRM owner. Newly produced symbolic values use GRIDUTRM schema 2: a cycle-free envelope retains the exact substitution or narrowing invocation so the value can be cold-replayed from its authenticated producer entry without trusting a live result cache. Capture-avoidance terminals retain their stable original source, exact grammar-owned quoting/wrapper recipe, authenticated rename transcript prefix, and complete origin; rename chains require no intermediate symbolic value. Legacy schema-1 owner bytes remain readable only with their exact retained live replay authority and are never silently upgraded. GRIDTVRS stays at schema 2 with its existing native tags. TREE-7-reachable patterns use self-contained GRIDPATT schema 5, and narrowing theories use self-contained executable GRIDTHTY schema 2; each exact render companion is identity-bearing inside the public value, so a ground result renders and reparses without a constructor printer or companion registry. Reachability is the frozen whole-module conservative may-flow fixed point seeded by the exact term, theory, and algebra operands of TREE.UNIFY, TREE.SUBSTITUTE, and TREE.NARROW after imports and callables are finalized. It follows every assignment revision, alias, container or branch, rule writer, callable parameter/capture/return, theory include, and algebra theory reference. One exact immutable producer uses one upgraded representation for all consumers; dynamic inputs must already carry that representation at runtime, unreachable TREE-0-through-TREE-6 values retain their bytes, and a missing exact companion fails lowering rather than being guessed or rebuilt.

Exact and algebraic unification, capture-avoiding substitution, one-step narrowing, and theorem-facing reconstruction make their semantic decisions over sorted readers with bounded iterative traversal. Nested algebra, hygiene, residual, capture, and reconstruction work remains inside one caller-owned work/interruption fence. Physical canonical encoding polls that same fence in bounded chunks after the single logical traversal charge. A public recursive owner is therefore a portable result or replay image, not an internal execution language. Candidate owners stay private until final-public publication and deduplication; only a unique public result commits authority or a solver state.

TREE-8 implements immutable SOLVER/tree_solver values and model-scoped TREE.SOLVE; TREE graduation remains held. A solver binds one rooted grammar domain, one or more ordered USING, STEPS, or TACTICS sources, an optional algebra and orientation, exactly one EQUAL, root/subtree MATCH, or NORMAL goal, one deterministic search strategy, and explicit positive depth, state, solution, and work bounds. Breadth-first, iterative-deepening, best-first, bidirectional breadth-first, and bidirectional best-first use their frozen total orders. Equality search accepts only equality authority; current sources are assumed theory laws and explicitly equality-preserving transforms. Operational directed steps can reach MATCH or NORMAL but cannot appear in an equality certificate.

SOLVER equivalence ON calc.expression
  USING calc_laws
  GOAL EQUAL
  SEARCH BREADTH FIRST
  DEPTH 2
  STATES 32
  SOLUTIONS 1
  LIMIT 100000
END

proof = equivalence(left_tree, right_tree)

The calc grammar and calc_laws theory are the worked example in the cookbook, which assembles this solver, its theory, and a checked theorem into one complete model.

A named solver is called directly, like a named transform: an EQUAL solver takes two trees, while MATCH and NORMAL solvers take one. TREE.SOLVE(solver, input) executes a MATCH or NORMAL solver, while TREE.SOLVE(solver, left, right) executes an EQUAL solver. An arity/goal mismatch fails closed, a MATCH goal requires a pattern that retains its executable template companion (GRID_TREE_EXECUTABLE_PATTERN_REQUIRED), and an EQUAL solver whose sources carry no equality authority is rejected with GRID_TREE_SOLVER_EQUALITY_SOURCE. The returned tree_solver_result exposes the exact seven-field record status, solutions, explored, frontier, work, complete, stop; its authenticated logical checkpoint remains private. Only complete frontier exhaustion returns :unsolved. Depth, state, work, and stricter process bounds return incomplete :limit with the exact :depth, :states, :work, or :process stop. Reaching the solution quota returns :solved with :quota, and interruption returns incomplete :interrupted. Solutions retain replay-checked certificates and conditioned MATCH evidence, resident WHY uses those same derivations, and cold, warm, resumed, and parallel execution publish the same canonical prefix.

TREE-9 implements SEARCH SATURATE BY cost and THEOREM; TREE graduation remains held. Saturation uses bounded equality classes and congruence closure for assumed or proved equality while tracking operational directed reachability separately for MATCH and NORMAL; it preserves the ordinary source, condition, cost, bound, and proof semantics, and extraction uses the named cost.

A theorem states a universally quantified equation and requires proof by a named GOAL EQUAL solver over the same root:

THEOREM plus_zero(x) ON calc.expression USING equivalence:
  plus(x, zero) == x

Compact and indented law forms are both valid, with one optional outer WHEN proposition that becomes a scoped proof premise rather than a published model fact. Declared parameters are rigid during the proof, and every parameter must appear in the equation (GRID_TREE_PARAMETER_UNUSED). If the solver returns anything short of a complete replayable certificate, the declaration fails with GRID_THEOREM_UNPROVED. A proved theorem carries :proved authority — distinct from the :assumed authority of theory laws — and is accepted wherever a declaration clause accepts an equation source: THEORY ... INCLUDES theorem, an ALGEBRA role clause citing a bare theorem name in place of theory.law, TRANSFORM ... USING theorem, and SOLVER ... USING theorem. An algebra whose roles cite theorem-backed laws works in MODULO patterns like any other algebra. Production and cold replay retain exact theorem, statement, solver invocation, premise, source, residual, goal, and saturation-event coordinates.

The recognized declaration heads are PATTERN, THEORY, ALGEBRA, TRANSFORM, TRANSLATE, SOLVER, and THEOREM. PATTERN and THEORY are current through TREE-3, ALGEBRA is current at TREE-4, TRANSFORM is current at TREE-5, TRANSLATE is current at TREE-6, SOLVER is current at TREE-8, and THEOREM is current at TREE-9. The heads are contextual rather than globally reserved: an ordinary identifier with one of those spellings keeps its existing meaning unless the complete declaration header matches. Compact and indentation-delimited items retain the bound grammar's bytes. Formatting may normalize Grid-owned declaration indentation and the uniquely selected outer ==, =>, and WHEN separators, but it does not reflow or canonicalize the grammar-owned fragments between them.

The exact reserved direct heads are TREE.SORT, TREE.SIZE, TREE.DEPTH, TREE.CHILDREN, TREE.CONSTRUCTOR, TREE.SYMBOL, TREE.TERMINAL, TREE.FINGERPRINT, TREE.ORIGIN, TREE.FREE_NAMES, TREE.BINDINGS, TREE.RENDER, TREE.AT, TREE.CONTEXT, TREE.PLUG, TREE.MATCH, TREE.NORMALIZE, TREE.APPLY, TREE.UNIFY, TREE.SUBSTITUTE, TREE.NARROW, and TREE.SOLVE. A well-formed declaration or reserved call whose semantic milestone is not available receives one GRID_TREE_FEATURE_UNAVAILABLE diagnostic over the complete construct. The diagnostic names both the required milestone and the current TREE-11 source-availability ceiling. This code-facing ceiling is distinct from the feature-graduation boundary described above. Malformed constructs receive their specific syntax or fragment diagnostic instead.

The milestone descriptions above explain the available grammar-bound semantics; availability does not assert fresh qualification. Generic hierarchy adapters and TREE.VIEW, TREE.FOLD, TREE.WALK and TREE.MAP_VALUES are proposals, not current functions.

Grid-native symbolic mathematics — contextual symbol declarations, ordinary operator composition over the shipped non-source math.expression term signature, and the closed SYMBOLIC.* registry — ships as its own subsystem on this substrate; see symbolic-mathematics.md. It is not part of the generic TREE authoring surface described here, and no separate symbolic source grammar exists: symbolic expressions are written as ordinary Grid formulas. A bundled authorable mathematical theory library and a domain-specific solver catalog for the TREE declarations above remain deferred.

Results, Bounds, And Persistence

PARSE(text, parser) is total: a language rejection is a parse-result value, not a failed workbook evaluation. PARSE_RESULT_OK, PARSE_RESULT_TREE, and PARSE_RESULT_ERROR provide formula-level projections. Errors are ordinary records with code, message, offset, expected, and found fields.

Successful trees retain their source. PARSE_TREE_SOURCE(tree) returns it, PARSE_TREE_ROOT(tree) returns a nested node record, and PARSE_TREE_CAPTURE(tree, "name") returns all matching node records in source order. Each node has symbol, capture, start, end, text, and children fields, so normal record and vector operations are sufficient for traversal.

Trees Are Native Graph Sources

A successful parse tree can directly seed a resident Graph. No node/edge Frame and no JSON conversion is involved:

result = PARSE("1 + 2 * 3", fast)
tree = PARSE_RESULT_TREE(result)
graph syntax = GRAPH(tree)

node_count = syntax.NODE_COUNT()
root_symbol = syntax.NODE_PROPERTY(1, "symbol")
frame children = syntax.NEIGHBORS(1)

The Graph is directed from parent to child and uses deterministic one-based preorder integer node IDs for the current tree revision. Each node carries kind = :parse_node, symbol, optional capture, byte-offset start/end, source_cell, root, and leaf. The root additionally carries tree_schema_version, grammar_fingerprint, and parser_generator.

Every edge carries kind = :child and a one-based ordinal; an edge whose child has a capture also carries that capture as field. Graph adjacency has canonical graph ordering, so syntax consumers must use ordinal whenever child order matters. Source text remains stored once in the native parse-tree value and is addressed through the node spans rather than duplicated into Graph properties.

Because this is an ordinary resident GraphHandle, persistent patches, traversal, path, pattern, view, and bounded Graph algorithms work unchanged. It may also be projected into Predicate reasoning:

KNOWLEDGE GRAPH syntax
predicate child is transitive
is_descendant = MUST(1 child 7)

Inspecting is_descendant through Cell Inspection or gridctl cell why <model-id> is_descendant retains the contributing child-edge identities and source-cell byte spans. WHY is an inspection operation, not a workbook formula. A rejected parse remains a parse-result error value; extract a successful tree before constructing its Graph.

GRAPH also accepts a grammar-bound syntax_tree, including the tree produced by TREE.PLUG. That projection retains syntax occurrence/path metadata and terminal payloads; it does not use the parse-node property schema described above. Both forms reuse the resident graph when the tree is unchanged and publish a new graph revision when the source tree changes.

Every generator carries deterministic input-byte, work-step, and parse-node budgets. Grammar normalization, LALR construction, and DFA construction also have hard compilation bounds. A parse budget failure is GRID_PARSE_BUDGET_EXCEEDED; a construction bound is reported before the parser becomes usable.

Authored limits may lower, but never raise, the process ceilings: 1 MiB of input, 10 million work steps, 1 million result nodes, and a parse-tree depth of 1024. Grammar definitions are also bounded to 4,096 rules/tokens, 16,384 expression nodes, and 8 MiB of aggregate authored grammar data.

Persisted compiled models store grammar definitions and parser recipes only. Compiled tables, memo caches, and DFA state are reconstructed process-locally after hydration and never enter the bytecode identity. Equal recipes share a weak process-local compiled artifact cache, while workbook and collection residency accounting includes the compiled automaton retained by each live parser value.

Ordinary data hierarchies

TREE.VIEW declares an ordered child relationship over nested Records and Lists. It creates ordinary immutable data, with no grammar, sort or proof authority.

assembly = {cost: 20, children: [
  {cost: 5, children: [{cost: 3, children: []}]},
  {cost: 7, children: []}
]}
view = TREE.VIEW(assembly, node => node.children,
                 {maxNodes: 10000, maxDepth: 32})
total = TREE.FOLD(view, {work: 1000000}) WITH node, child_costs DO
  node.cost + SUM(child_costs)
END
# total = 35

The child selector must return a List at every occurrence, including an empty List at leaves. Shared payloads count once per occurrence. Order is the selected List order. A direct selector such as node => node.children separates that field from the payload: callbacks see the node's other fields and the traversal owns its children. Computed pure selectors also work, but do not acquire an inverse adapter.

The view is a versioned flat native Record with format = "grid.hierarchy/1", preorder parents, payloads and optional childField. The single root has a blank parent; nonroot parents are zero-based indexes. This is a data snapshot, not a process-local pointer or an authority token. Operations validate the portable relationships at ingress and reuse them without rerunning the selector. Normal model dependencies retain the authored source and callable: source edits rebuild the view; saving/reopening the model replays that same compiled recipe. Saving the view alone retains its snapshot, not a live connection to an external source.

TREE.WALK(view, {order: :preorder}) returns a bounded List of records containing payload, zero-based occurrence path, and root-relative depth. Select :postorder to visit children before their parent. TREE.FOLD always runs bottom up. Its second callback argument is the ordered List of completed child results; leaves receive an empty List. Callbacks receive the ordinary array carrier used by MAP and reducers. SUM retains its existing floating-point semantics; use exact scalar arithmetic when the accumulator must preserve exact integers. Results must retain one structural type, including record fields and scalar tags. Empty lists specialize to their observed element type. Callback errors or exhausted limits publish no partial result.

adjusted = TREE.MAP_VALUES(view) WITH node DO
  {cost: node.cost * 2}
END
nested = TREE.MATERIALIZE(adjusted)

Mapping preserves the exact parent relation and child order. A direct-field adapter refuses a mapper that introduces its child field. TREE.MATERIALIZE rebuilds nested native Records and Lists only for that validated adapter; computed selectors, Graph views and native syntax inspection have no inferred inverse and return GRID_HIERARCHY_ADAPTER. Use TRANSFORM and TREE.APPLY for grammatical changes and their derivations.

A resident Graph can supply the hierarchy without converting its topology to JSON or constructing a text grammar:

organization = TREE.VIEW(reporting,
  "chief" AS root, "ordinal" AS order, 100000 AS max_nodes)
payroll = TREE.FOLD(organization, (node, totals) =>
  node.properties.salary + SUM(totals))

Select the intended edges before admission. The Graph must be directed, connected and acyclic with the exact root, one parent per nonroot node, and a nonnegative integral order property on every child edge. Sibling order values must be distinct. Missing order, ties, disconnected nodes, multiple parents and cycles refuse; a DAG is never implicitly unfolded. Payloads contain exact id and properties fields. A source fingerprint identifies the snapshot for inspection; it grants no authority. Graph-backed views use Graph dialect v9 metadata, and changing the source or root invalidates the dependent view.

TREE.WALK, TREE.FOLD and TREE.MAP_VALUES also inspect native ground Trees through the same iterative traversal. Their data payload contains kind, name (constructor or symbol), and value (terminal text or blank). Mapping returns an ordinary data view and cannot rewrite the original syntax authority. Pattern holes and qualified symbolic variables retain their existing Tree APIs.

Ordinary callbacks, named definitions, partials and captured values use the existing compiled callable frames. Admission permits bounded scalar arithmetic, field/index access, conditionals, record construction and reductions such as SUM. Immutable local bindings use existing DO/LET semantics, with a conservative bound for composed value growth. It refuses exponentiation, I/O, recursive callbacks, nested dynamic HOFs and binding sequences that exceed the admitted work bound. The same restrictions apply to source-free compiled models; purity alone does not establish a resource bound.

Data operations accept maxNodes, maxDepth, work, and bytes options. Defaults are 100000 occurrences, depth 64, 1000000 work units and 64 MiB of cumulative charged storage. Hard maxima are 100000, 256, 10000000 and 64 MiB respectively. The Graph overload retains depth 64 and intersects its work/storage limits with the graph operation budget. Existing source-carrier depth limits still apply. Callback inputs, outputs and a conservative invocation envelope share the same operation meter; child calls never reset it. Final publication checks cancellation. Stable failures use GRID_HIERARCHY_NODES, _DEPTH, _WORK, _BYTES, _INTERRUPTED, _TOPOLOGY, _CALLBACK_PURITY, _CHILD_EDIT and _RESULT_TYPE.

Local Boolean policy library

shared/boolean-policy.gs is a local source library, contract 1.0.0. It exports syntax (grammar), laws (THEORY) and simplify (TRANSFORM). The default compiler resolver embeds it on explicit import. In a model using the asset resolver, place the module at shared/boolean-policy.gs in that model's source assets and retain those exact bytes when moving the model. Asset resolvers do not silently fall back to an embedded replacement. No Registry installation or published package coordinate is involved.

USE "shared/boolean-policy.gs" AS policy

policy_parser = policy.syntax USING LALR(max_input_bytes = 65536, max_nodes = 10000, max_steps = 1000000)
original = "(eligible and stock_ready) or (eligible and not stock_ready)"
parsed = PARSE(original, policy_parser)
result = PARSE_RESULT_OK(parsed) THEN policy.simplify(PARSE_RESULT_TREE(parsed)) ELSE BLANK
summary = PARSE_RESULT_OK(parsed) THEN (result.status = "complete" THEN TREE.RENDER(result.tree) ELSE "No completed simplification") ELSE "Policy text is invalid"

The completed tree is the single proposition eligible; source rendering may retain parentheses used for safe substitution. Retain original, parsed and the whole result, not just summary. WHY inspects the resident result's applied laws and native derivation. Rendering and explanation do not apply the transform again. TREE.APPLY(policy.simplify, tree) and policy.simplify(tree) use the same native operation. Only "complete" supplies a completed replacement; a work limit, interruption or invalid input retains its native diagnostic and, where available, a checkpoint. A checkpoint is not a completed simplified policy.

This is total two-valued Boolean logic. Names matching [a-z][a-z0-9_]* are case-sensitive opaque propositions, excluding true, false, not, and and or. They do not read Grid variables. not binds before and, then or; and and or parse left-associatively. Parentheses group expressions. Unknown operators, function calls, comparisons, property access and interpolation are rejected. Blank, errors, unknown values, SQL NULL, Predicate evidence and side-effecting expressions are outside this domain. Simplification never edits or authorizes a live policy; using its result is a separate caller decision.

The 29 ordered assumed laws remove constants, double negation, duplicate terms, complements and absorbed branches, and factor four symmetric placements of a common conjunct. The strategy is bottom-up, repeated, with smaller_tree orientation: every accepted law strictly decreases native logical node count. There is no distributive expansion, SAT/BDD solver, globally minimal form or equivalent API. The laws remain :assumed; test coverage does not turn them into :proved theorems. Equivalent inputs need not have identical output text.

The transform pins 1,000,000 native work units, including native explanation work. The parser in this example separately caps input at 64 KiB, parsing at 1,000,000 steps, and parse nodes at 10,000. Those parser limits belong to the selected parser, not to the grammar export; callers creating another parser must choose their limits explicitly. Existing native ceilings still apply: 1,024 tree depth, 1,000,000 tree nodes, 8 MiB metadata/render output and 64 MiB portable-tree/replay retention, with separate native derivation limits. These are individual engine ceilings, not an aggregate 64 MiB memory guarantee. There is no separate library-only atom-count, depth-128 or 8 MiB result cap. A limit can prevent completion even for an input within the parser's size bounds.

Native transforms reject non-shrinking equality directions before constructing their replacement. Selected operations reuse one materialized owner and prepare bounded recognizers while retaining exact render/reparse validation. The fixed 1,000-node policy example now completes within the same work limit; this is not a guarantee for every policy of that size. New transform results use native evidence schema 6. Current readers retain exact replay of schemas 3 and 5; older readers that do not support schema 6 reject new results.

See the complete model, selected-export reuse and inline control. The inline model authors the identical grammar/laws/strategy that the import saves you from repeating. The library is loaded only when explicitly imported; ordinary models start no worker, timer, package fetch or Boolean-policy evaluator.