Compiler tools

Compiler tools and syntax values

Compiler tools and syntax values

Grid programs can inspect, construct, and transform expressions as data. The Rust compiler supplies parsing, source analysis, checked rename, and compilation diagnostics. Generated source passes through the ordinary compiler and LIR runtime.

In the Code workspace

Open Compiler tools in the Code toolbar. The panel makes no compiler requests until you choose an action.

  • Inspect syntax opens a source-linked tree. Select a node to reveal its source, attributes, and byte range. A named node can prepare a checked rename.
  • Select an expression and choose Explain expression for its binding, contextual type, known effects, direct source dependencies, and available units, bounds, and proofs. Each fact distinguishes established, partial, and unknown information. Go to definition follows a resolved source binding.
  • In Edit expression, prepare a replacement or extract a named function. Select a function declaration for Change function signature: rename or reorder its parameters, remove unused ones, or supply arguments for new ones. Preview each source change alongside the resulting compiler diagnostics. Staging rechecks the transaction against the original draft and applies all its edits together; a changed draft or invalid proposal cannot be staged.
  • Check source reports diagnostics and named dimension, bound, and proof facts without executing the model.
  • Choose one of the six examples to open a separate scratchpad. Run outputs evaluates only the named outputs in a fresh authoring model. Generate preview evaluates the generator output, checks the generated source, and optionally evaluates its named outputs in another fresh model.
  • Inspect expansion reads an AST.EXPAND output and opens a viewer linking generated nodes and compiler findings to their template and inserted inputs. It checks declaration source without running the generated model. An expression requiring surrounding declarations is clearly labeled as such.
  • Review the proposed replacement and Stage replacement for review. This pauses automatic synchronization; save explicitly to apply it. A changed model draft invalidates the preview.

Editor completion, hover, and signature help include AST.QUOTE and dotted AST.* names. The guide and all examples are bundled for offline reading.

Authoring requests accept up to 64 KiB of source and 32 selected output names. A fresh Rust worker handles each request, with a 15-second wall deadline, a 2 MiB report limit, and at most two workers per host. Unix workers also have a CPU limit; Linux adds an address-space limit. The worker initializes no live model, connector, or import resolver. This is a deterministic language admission boundary, not an operating-system sandbox. Closing the panel cancels its response; an already-started server worker remains bounded by its deadline.

Quotation and reflection

formula = AST.QUOTE(quantity * unit_price)
original = AST.SOURCE(formula)
dependencies = AST.FREE_NAMES(formula)
 
price(quantity) = quantity * 7
definition = AST.DEFINITION(price)
definition_source = AST.SOURCE(definition)

Quotation captures a normalized expression without evaluating its references or calls. AST.DEFINITION reads a local function, named assignment, or cell definition, including a definition written later in the source. It returns the declaration; quoting price returns only a reference. Imported definitions and computed targets are refused. Existing BODY, ARGS, and FUNCTION keep their existing record formats and callable metadata.

Reflected definitions depend on source. A formula edit in a model containing AST.DEFINITION requires ordinary whole-source compilation; partial formula splicing refuses rather than retaining an obsolete definition. Changing an input value does not change its authored definition.

Public data contract

A syntax value has schema: "grid.syntax.v1", root, and optional source. Every node has kind, attributes, children, and optional span. Spans are half-open UTF-8 byte offsets into that exact source. Paths are zero-based child indices; they identify occurrences in this tree, not persistent compiler IDs.

AST.WALK returns flat rows with paths, kinds, attributes, spans, and child counts. AST.AT(tree, path) selects a subtree. AST.ROOT exposes the root record; AST.CHILDREN returns detached child syntax values. Construction and substitution clear source spans: generated syntax must not claim an authored location it lacks.

The schema represents literals, references, operators, calls, lexical lambda and let nodes, arrays, objects, and declarations. A source module also retains the entire original source, including comments and directives. Its structureComplete: false flag means that metadata-owned declarations are not all semantically projected. Unprojected regions remain source-linked declaration fragments, so the inspector does not silently omit them. Ordinary named-assignment modules report complete structure and can render; use the original source for faithful reproduction, including comments.

Whole-source projection version 2 distinguishes structured inspection from source emission. Function definitions include their structured signature, effects, captures, dependencies, and body. Assignments retain modifiers and typed tensor shapes; their first child is the value and an optional second declaration child with compiler type AssignSchedule carries scheduling. A rule's first child has compiler type RuleTriggerAst, followed by its RuleActionAst children. These records, and their nested targets, qualifiers, write modes, rate limits and execution/retention policies, use the generated compilerDeclaration fields described below. For example, a When trigger's fields.execution links to its policy record, whose fields.concurrency is a number and fields.retention links to the retention variant. RESET and CLEAR expose fields.expr: null; their internal placeholder is never inspected. Solve declarations retain their goal or optimization direction.

New rule/schedule inspections replace the previous flattened ruleTrigger, ruleAction and schedule forms. Queries should select compiler type and follow local field links instead of old childRoles or flattened attributes. Previously serialized forms remain readable. The compiler records retain normalized addresses and default policies; exact authored spelling is in the source. Action spans are absent because the compiler does not retain them. Try Code’s Inspect rule policies example or inspect-rule.grid.

Graph schemas and predicate schemas expose their kinds, fields, types, and cardinality. Views expose their authored outline, named roles, and independently parsed expression children with source-relative byte spans. Contract-dependent phrases, action bodies, and other unclassified parts remain source-linked parts with structureComplete: false; they are not guessed into expression trees. Hidden assignments emitted during View lowering are not shown as additional authored declarations. Comments and formatting remain in the separate, exact source text; canonical rendering is not a formatting-preserving round trip.

structureComplete: true does not imply that rendering or substitution is supported. Use AST.CAPABILITIES for that answer. A module's incompleteFamilies also lists metadata families still awaiting projection, including clauses that share an assignment's source span. This prevents missing FORMAT or VALIDATE structure from being mistaken for complete coverage. Other declaration families remain incomplete; the full language syntax model is still being extended. The compiler's own program fields now own this inventory: a newly added field must declare its source coverage. Authored model headers, dimensional policy, unit-scale policy and coercion policy are also reported while their structured syntax remains incomplete.

Graph paths, partitions, chain patterns and fixed-point declarations now expose their retained compiler structure. Their enclosing declaration has form: "binding" and keeps assignment modifiers. Its first child has form: "compilerDeclaration", a compiler type such as PathBindingDecl, an optional enum variant, and a fields object. Named fields keep their compiler names; tuple variant fields use "0", "1", and so on. Scalars are values, absent options are null, and lists retain order. A nested record or expression is represented by {"child": n}, where n selects a child of that same node. The link never contains a compiler arena ID. External syntax validation rejects dangling, duplicate and unreferenced children.

These nodes derive from the same types used by the compiler. New supported fields are included automatically; source locations and derived lowering state have explicit roles. Internal GRAPH_PATH_QUERY, GRAPH_PARTITION_NODES, GRAPH_PATTERN_MATCH and GRAPH_PROPAGATE helper calls are replaced by the retained declaration structure. Locations remain in span, so detaching a tree removes them consistently. Inspection does not grant rendering or substitution support: ask AST.CAPABILITIES before transforming. Exact spelling, comments and formatting remain in the retained source; compiler defaults are not evidence that every option was explicitly authored. See inspect-graph.grid.

Views, library items, graph schemas and predicate schemas use the same generated compilerDeclaration representation directly as module children. This replaces the older view, viewItem, graphSchema and predicateSchema inspection forms. For example, SurfaceDeclaration.fields.items is an ordered list of local child links; a SurfaceItem has a kind child whose variant identifies the compiler's outline construct. Name records carry their own source locations. Nested arrangement rows retain their list-of-lists shape, and predicate multiplicity retains its ordered identity fields.

Span fields on the compiler records declare whether they contain embedded Grid expressions or retained fragments. Both become sourcePart nodes with a field role and source location. Only embedded Grid expressions are parsed into children. Unclassified phrases, parameter fragments and foreign/action bodies remain explicit fragments; their incompleteness propagates through generated parents. Error variants retain the compiler diagnostic. Layout-only data stays in the separate source text. New supported fields and variants need no View or schema visitor update. See inspect-view.grid.

Limits are 65,536 nodes, 128 node levels, and a 1 MiB source/attribute budget. Whole-source parsing applies the node bound to retained authored items as well as projected nodes, including declarations carried in opaque source fragments. Serialized syntax has a 2 MiB input bound and the JSON decoder's additional nesting bound. Each operation checks its combined input budget before copying values. AST.LITERAL preserves matrix rows and columns and refuses native values that cannot be represented by ordinary literal syntax. Unknown schemas, malformed nodes, invalid names, and budget excess return errors. AST.FROM_JSON(text) reads this portable data format; it grants no execution authority.

Construction and transformation

Checked source transactions

source = "price(quantity) = quantity * 7\nanswer = price(6)"
parsed = AST.PARSE(source)
proposal = AST.EDIT(source, {
  revision: parsed.sourceRevision,
  operation: {
    kind: "changeSignature",
    function: "price",
    parameters: [{ name: "items", from: "quantity" }]
  }
})
updated_source = AST.APPLY(source, proposal)

AST.EDIT(source, request) prepares a grid.syntax.transaction.v1 report. request contains revision and one operation:

Kind Fields Behavior
replaceExpression span, replacement Replace one exact retained expression with parsed expression text, parenthesized to preserve its surrounding precedence.
extractFunction span, name Move the selected expression into a function before its owning declaration, passing free value and lexical callable bindings explicitly.
changeSignature function, parameters Edit one local function and every resolved direct caller, including nested calls.

Each signature parameter has name and exactly one of from (an old parameter) or argument (a new expression inserted at every caller). Existing arguments follow their old parameter identities when reordered. Parameter reads respect sequential LET and LAMBDA shadowing; capture and removal of a used parameter are refused. New argument text is interpreted in each caller's lexical scope. Untouched source stays byte-identical. Comments inside an extracted body move with that body; retained argument comments move with their argument. Deliberately removed expressions or parameters can remove comments within their edited ranges.

The report carries its request, original and updated source revisions, updatedSource, sorted non-overlapping edits, an explanation, focused preview entries, ok, resulting diagnostics, and validation. Invalid compiled proposals remain reviewable with ok: false. AST.APPLY reconstructs the proposal, verifies its edits, source identities, explanation and focused preview, and compiles it again. It returns updated source text; it does not mutate a model. External success flags and diagnostic fields never authorize an application.

Compilation checks validity; it does not prove equivalent behavior. Extraction, argument reordering/removal, and newly supplied arguments can change evaluation order, effects, or reflection results. Every transaction explicitly reports that equivalence is not established.

Current refactorings require retained lexical scope. Imported definitions, partial/reflection/adapter uses of a changed function, and qualified, positional, or grammar expressions in an extracted or signature-edited body are refused. Expression replacement can still target an exact retained expression of those forms. The source must parse before preparation. Limits include 128 parameters, 4,096 edits/callers, 1 MiB source or replacement text, existing expression node/depth limits, and a 2 MiB report. These are explicit supported-operation limits, not claims that the entire language is refactorable.

Constructing syntax

expression = AST.BINARY("*", AST.REFERENCE("quantity"), AST.LITERAL(7))
definition = AST.FUNCTION("price", ["quantity"], expression)
answer = AST.ASSIGN("total", AST.CALL("price", [AST.LITERAL(6)]))
generated_source = AST.RENDER(AST.MODULE([definition, answer]))
check = AST.CHECK(generated_source)

AST.NODE(kind, attributes, children) constructs other validated node shapes. AST.SUBSTITUTE(tree, replacements) simultaneously replaces free names. It renames lambda and sequential LET binders, function parameters, and generated declaration names when necessary to prevent capture. Plain AST.ASSIGN, AST.FUNCTION, and AST.MODULE constructions share this support. Module names bind throughout the module, including forward and recursive references. Inserted expressions are never substituted again; unused or shadowed replacement keys do not rename declarations.

Declaration hygiene supports unqualified named bindings. Names containing digits must include an underscore to stay outside the compiler's spreadsheet-address syntax. Duplicate module names, qualified names, annotations, incomplete source projections, and other declaration families require compiler binding analysis. AST.CAPABILITIES reports these limits before transformation. Each replacement must be an expression, and combined input and expanded-name sizes are bounded.

Review generated declaration names before joining the result to other source: preventing capture can rename a module binding. Generated trees have no authored source spans. Use AST.RENAME or checked transactions for existing source declarations, preserving their annotations, comments, and formatting. Explicit templates extend this hygiene with named holes, list splicing, and traced origins.

Quoted templates and expansion origins

formula = AST.QUOTE(SUM(AST.SPLICE("prices")) * quantity + AST.HOLE("fee"))
expansion = AST.EXPAND(formula, {
  prices: [AST.QUOTE(3 + 4), AST.LITERAL(0)],
  fee: AST.QUOTE(5)
})
generated_source = expansion.generatedSource

AST.HOLE("name") denotes one expression, and AST.SPLICE("name") denotes a list of expressions in a call's arguments or an array row. They are captured unevaluated inside AST.QUOTE; calling them directly constructs the same marker syntax for use with AST.CALL, AST.FUNCTION, and other constructors. Bindings map case-sensitive labels of 1–128 UTF-8 bytes to one syntax value or a list of syntax values, respectively. Empty lists remove that list occurrence. Array row boundaries are retained. Missing, unused, mismatched, and misplaced bindings are refused. Fixed-arity operator operands, record keys, parameter names, and module declaration lists are not expression-splice positions.

Expansion is simultaneous. It does not recursively expand marker calls supplied inside an input. Temporary hole names cannot collide with names in either the template or its inputs. The shared scope engine renames supported template binders to keep inserted free names free, including constructed module declarations, forward references, recursive calls, parameters, and sequential local bindings. The source-only and incomplete-scope restrictions above remain applicable.

The grid.syntax.expansion.v1 report contains template, bindings, detached syntax, generatedSource, and origins. Each origin identifies a generated node path and UTF-8 generatedSpan, its template path and optional authored span, and, for inserted nodes, its binding label, optional zero-based list item, and input path/span. Repeated inputs have distinct generated occurrences. Original source stays in the retained template or binding, separate from generated syntax; generated nodes never claim authored source spans. Constructors detach child locations, so composed templates can expose a structural origin without authored text. Inputs made with quotation retain their exact input text and locations.

AST.EXPAND neither compiles nor runs generated code. In Code, choose the Quoted template example and Inspect expansion. Select a node or follow a compiler finding to compare its template, input, and generated text. Renamed bindings are shown explicitly. Declaration source is checked by the ordinary compiler; standalone expressions report that context is required. Successful checking does not establish runtime success or equivalent behavior. Inspection reconstructs the expansion from retained inputs instead of trusting supplied generated text, syntax, or origin claims. Generate preview also accepts an expansion report and applies the existing deterministic admission before optional execution.

Existing syntax depth/node budgets apply. Combined inputs are bounded to twice the syntax budget; output syntax and rendered source retain their 1 MiB limit. Origin allocation is bounded during construction, and the complete serialized expansion report, including retained inputs, is limited to 2 MiB. The editor worker's 64 KiB source and 15-second limits still apply. No automatic expansion is added to ordinary model compilation. Whole-declaration splices, origins preserved through every constructor, and expansion histories remain further work.

Bulk selection and structural rewrites

formula = AST.QUOTE(unit_price * quantity + delivery_fee)
references = AST.SELECT(formula, { kinds: ["reference"] })
support = AST.CAPABILITIES(formula, [])
 
# Paths refer to occurrences in the original formula.
revised = AST.REWRITE(formula, [
  { path: [0, 0], replacement: AST.LITERAL(7) },
  { path: [1], replacement: AST.LITERAL(5) }
])
revised_source = AST.RENDER(revised)

AST.SELECT(syntax, selector) returns flat rows in source-tree preorder, with the same path, kind, attributes, span, and childCount fields as AST.WALK. A selector can specify kinds, exact attributes, a within path, and maxDepth relative to that subtree. Omitted filters match everything; maxDepth: 0 selects only the subtree root. Returned paths remain absolute in the original tree. Unknown selector fields are errors. A report exceeding the 1 MiB logical output budget is refused; narrow the selector to inspect a large tree in bounded sections.

AST.REWRITE(syntax, edits) applies a batch of {path, replacement} records simultaneously. Input order does not matter; replacements are not revisited. Duplicate and ancestor/descendant paths are refused, as are nonexistent paths, invalid parent shapes, and combined or resulting budget excess. The original tree stays unchanged. The result has no retained source or authored spans. This is structural rewriting: it can change bindings and behavior. Use AST.SUBSTITUTE for capture-avoiding free-name substitution and compiler-checked source edits for preserving surrounding comments and formatting.

AST.CAPABILITIES(syntax, path) reports render, freeNames, and substitute support for that subtree. Each has supported and, on refusal, a reason and the absolute path of the first unsupported node. select and rewrite report structural operation support; sourceSpan is the retained authored range, if any. Support describes node forms, not unlimited resources or the validity of a future replacement: normal argument, scope, and output limits still apply. Capability discovery uses the renderer and scope policies without attempting to render or substitute every subtree.

These bulk operations validate external syntax once per request. Traversal does not validate the whole tree again for each selected node. Rust tooling can retain a ValidatedSyntax value for repeated immutable queries; it cannot mutate the admitted tree through that wrapper.

AST.RENDER emits supported expression forms, ordinary parsed named assignments, and constructed assignments, functions, and modules. It refuses source-only declarations or qualified references whose modifiers or spelling it cannot safely reproduce. AST.SOURCE returns retained text; it does not render an edited tree. This distinction preserves scheduling, types, comments, and source forms outside the emitter's supported subset. Rendering is not compilation or semantic-equivalence proof. String emission uses Grid’s own escape rules, preserving Unicode and control characters when reparsed. Object keys use Grid’s separate raw-key convention. Keys containing quotes or backslashes are explicitly refused by rendering and capability discovery because the emitter cannot reproduce them safely.

Compiler services

Function Result
AST.EXPRESSION(text) Syntax parsed by Grid's Rust expression parser
AST.PARSE(text) ok, normalized source syntax, and diagnostics
AST.ANALYZE(text) Source symbols, completeness flags, function types, effects, contracts, and captures
AST.CHECK(text) Ordinary source-to-LIR diagnostics, dimensions, bounds, validation proofs, and compilation summary
AST.MEANING(text, selection) Revision-bound expression binding, contextual type, effects, direct dependencies, and available compiled facts
AST.RENAME(text, byte_offset, new_name) Original source, updated source, and checked edits

Rename preserves untouched text and refuses ambiguous or unsupported scope, collisions, and capture. Reports expose completeness rather than claiming every source construct is covered. Compilation checks do not execute the model. The check report's sorted namedFacts rows associate inferred dimensions, bounds, and validation proofs with source names. A missing fact is null; inference has not established it. Raw namedWires identifiers are local to that compilation and must not be stored as stable source identities.

Expression meaning and source revisions

source = "price = 7\ntotal = price * 6"
parsed = AST.PARSE(source)
expressions = AST.SELECT(parsed.syntax, { kinds: ["binary"] })
selected = INDEX(expressions, 1)
meaning = AST.MEANING(source, {
  revision: parsed.sourceRevision,
  span: selected.span
})

Parse, analysis, and check reports include sourceRevision, a sha256: identity of the exact UTF-8 source bytes, including comments and whitespace. AST.MEANING requires that revision plus the exact nonempty byte range of one retained expression. A changed source returns AST/STALE; invalid UTF-8 boundaries, fractional offsets, ambiguous ranges, and declaration selections are refused. Revisions identify content, not compiler versions or permission to mutate a model. Tree paths and compiler arena IDs are not revision identities.

Meaning reports have schema: "grid.syntax.meaning.v1", sourceRevision, span, selected source, ordinary compilation diagnostics, and ok. Their binding, type, effects, dependencies, units, bounds, and proofs fields carry status and value. Status is known, partial, unknown, or notApplicable; unknown facts include a reason.

Binding queries use resolved callable targets and the source index, including function parameters and sequential lexical scopes. Contextual type queries use the existing inferencer and finalized function schemes. They retain unresolved variables and any as partial information. Enclosing higher-order constraints, nominal/process obligations, and some lowered scopes are not reconstructed. There is no inference from the current runtime value.

Effects include classified expression effects and named-callee effects. An empty partial list does not establish purity. Dependencies are resolved direct source references; this is not a transitive runtime dependency graph. Unit, bound, and validation-proof facts currently attach only to an exact, unique named-assignment value for which the ordinary compiler retained that fact. A fact about a whole formula is never attributed to one of its child expressions. Failed compilation withholds semantic type/effect/proof facts.

The Code inspector requests these facts only when Explain expression is selected. It preserves the syntax tree, labels old-draft reports, and disables navigation until current source is inspected again. All work remains in the bounded authoring worker; explaining an expression does not execute it.

These services require a host that installs the compiler-tools service. Grid's Rust runtime host installs its existing compiler at startup. Bare embedded LIR runtimes can use syntax data operations and must explicitly install that service to parse or check source; otherwise those calls return AST/UNAVAILABLE. Requests contain source, never model handles, import resolvers, files, network credentials, or mutation grants. Source imports that require a resolver fail through ordinary compiler diagnostics.

Grid-authored analyzers

An analyzer is an ordinary one-argument Grid function receiving model source as text. It returns {sourceRevision, diagnostics}. Each diagnostic has code, severity (error, warning, or info), message, a UTF-8 byte span, and optional fixes. A fix contains title and an operation accepted by AST.EDIT.

AST.DIAGNOSTICS(source, report) validates and normalizes this data against the exact source revision. Its schema is grid.analyzer.diagnostics.v1. It does not claim compiler validity, establish proofs, execute the inspected model, or apply suggested changes. Unknown fields, stale revisions, invalid UTF-8 ranges, and unsupported fix requests are refused. Diagnostics may use an empty range; expression-edit ranges must be nonempty.

The effects analyzer parses the source once, examines structured function definitions, and flags classified effects. Its suggested blank placeholder changes behavior and must be reviewed. An empty effect list or a clean analyzer report does not establish purity or completeness of compiler analysis.

In Code, open Compiler tools → Analyze the model with Grid, load the example or enter an analyzer, and choose Run analyzer. The analyzer program is kept separately from the model draft. Findings appear in the normal Problems list and editor markers with their analyzer origin. Review fix asks the compiler to prepare a checked transaction and opens its focused preview. Staging rechecks that transaction and the unchanged draft; saving remains explicit. Changing the model hides old findings, and canceled or old-draft responses cannot supply locations or edits for the current source. Findings do not control Save.

Runs are explicit and source-only. Each source and analyzer is limited to 64 KiB at the worker boundary; the existing deterministic staging admission, two-worker cap, and 15-second deadline apply. The driver supplies fresh bindings for source-as-data and the result, normalizes ordinary Grid records through the native builtin, and independently validates the report before attaching the analyzer's entry name and source revision. There is no filesystem, import resolver, network credential, or live-model mutation grant.

Reports allow at most 256 diagnostics, 8 fixes per diagnostic and 256 fixes in total, within the 2 MiB report budget. Codes use 1–64 ASCII letters, digits, underscores, hyphens or dots; messages have at most 4,096 bytes and fix titles 256 bytes. Suggested operations remain inert until explicit review. This version runs one selected analyzer against one source revision; it does not provide analyzer dependency scheduling or cross-module fact caching.

Working examples

The repository's examples/compiler-tools/ directory contains six executable Grid models and one reusable analyzer:

  • Effects analyzer supplies source-linked findings and a reviewable edit suggestion.
  • Model auditor lists function signatures, separates pure and effectful functions, and inspects formula references without executing the inspected model.
  • Checked refactoring renames a binding while preserving comments and demonstrates lexical substitution that avoids capture.
  • Invoice language parses buy 6 at 7 using an authored grammar, reads its native TREE terminals, and constructs source that computes invoice_total = 42.
  • Generate prices constructs a function and call yielding total = 42.
  • Model family generates three calculations yielding 42, 54, and 66.
  • Quoted template fills a formula's expression hole and argument splice, traces its inputs, and generates a calculation yielding 47.

The invoice example uses the grammar's existing native TREE authority. It explicitly translates terminal data into Grid syntax. An AST record cannot forge a native TREE value, grammar authority, rewrite certificate, or proof that a Grid program preserves meaning. General Grid AST-to-TREE conversion and automatic compiler rewrite hooks are not part of this interface.

Explicit staged generation

The maintained Rust example provides a concrete two-stage authoring tool:

cargo run --manifest-path rust/Cargo.toml -p grid-runtime-core \
  --example compiler_tools -- generate \
  examples/compiler-tools/model-family.grid generated_source total_7 total_9 total_11

It executes the generator through LIR, reads its generated_source result, checks the emitted source with the same compiler, and executes the explicitly requested symbols in a fresh LIR model. The JSON report includes source text, both source hashes, compilation size, and the results. Omit the trailing symbol names to compile without executing the generated model.

The example admits a documented subset of deterministic named calculations, local functions, collection helpers, and AST operations. It rejects external effects, addressed/model state, unsupported calls, and more than 4,096 expression nodes or 64 KiB of source at either stage. Its allowlist is in rust/grid-compiler-tools/src/staging.rs; a rejection does not mean the construct is unavailable in ordinary Grid models.

An explicit authoring command or panel action starts a worker process. The parent refuses output after 15 seconds or a worker failure. GRID_COMPILER_TOOLS_TIMEOUT_MS can lower that bound to between 1 and 15,000 milliseconds. Unix workers also install a 10-second CPU limit; Linux additionally installs a 4 GiB address-space limit. macOS does not support that address-space limit. Other platforms have the parent wall-time bound. These limits do not constitute an operating-system security sandbox. No output is accepted until both stages succeed, and generated code gains no import resolver, external effects, live-model state, or mutation authority. This is a runnable authoring example, not automatic macro expansion during ordinary compilation. Application hosts can compose the same compiler and LIR APIs at their own explicitly authorized staging boundary.