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…
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 ruleto 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.HIGHLIGHTnames 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 = expressionfor 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 nullaryBUILDS 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.