4. Surface Grammar and Statement Forms
4.1 Scope
Scope:
- top-level and block-level statement forms
- the atomic statement set
- local binding and assignment
- constructor/init surface boundaries
markers(...),stream(...), anddata_schema(...)
Boundary:
- These forms are the source authoring surface.
- Canonical lowering targets and runtime truth remain defined by Chapters 5-9.
- Historical design notes are not authoring entrypoints.
4.2 Source Context and Statement Matrix
| Authoring context | Statement unit | Source shape (summary) | Key components | Result / effect |
|---|---|---|---|---|
| top-level source file | source_include_decl | include "path.culs"; | path | source file dependency declaration |
| top-level source file | library_import_decl | import Module; | module name | importable library module binding |
| top-level source file | protocol_decl | protocol name(...) { ... [return ...;] } | protocol name, params, body with optional tail return | callable protocol definition |
| top-level entry source | script_stmt | any executable statement form valid in a protocol body | statement-specific components | script entry statement for the current run |
| protocol body direct form | protocol_ref_stmt | Module.Protocol(arg0, k = v, ...); | module name, protocol name, positional/named args | plan-time protocol expansion |
| protocol body direct form | include_stmt | include ProtocolName; | protocol name | include visible protocol body |
| protocol body direct form | let_stmt | let x = expr; | local name, expression | establish local binding |
| protocol body direct form | return_stmt | return expr; / return out = expr, ...; | return expression or named return bindings | protocol return contract; runtime projection is defined in Chapter 9 |
| protocol body direct form | assign_stmt | x = expr; / data.result.field = expr; | existing local or limited member path, expression | update local binding |
| protocol body direct form | step_call | op_name(k = v, ...); | operator name, args | invoke operator surface |
| protocol body direct form | markers_decl | let p = markers([CD3, CD19]); | marker list | declare controlled marker set |
| protocol body direct form | stream_call | let s = stream(sample = x, unit = single_cell, [panel = p]); | sample, unit, optional panel | create unit stream |
| protocol body direct form | data_schema_decl | let s = data_schema(label = "X", fields = [...]); | label, fields | declare structured result schema |
| protocol body direct form | container_ctor_expr | let c = tube(..., [load = [content_spec:Q, ...]]); | container family, named args, optional load | initialize container binding |
| protocol body direct form | mutation_stmt | target << [sources ...]; | target, sources | container material mutation / collection |
| protocol body block / scope form | with_env_stmt | with env(...) { [hold(target_expr);] ... } | env args, optional hold(...) target declarations, body | environment-scoped block |
| protocol body block / scope form | with_constraint_stmt | with constraint(...) { ... } / stmt with constraint(...); | requirements/options, body | execution-requirement scope |
| protocol body control form | repeat_stmt | repeat expr { ... } | statically decidable count or schedule(...) | repeated execution |
| protocol body control form | if_stmt | if expr { ... } else { ... } | statically decidable condition | branch execution |
| protocol body control form | continue_stmt | continue; | none | continue nearest repeat iteration |
| protocol body control form | break_stmt | break; | none | break nearest repeat |
Notes:
- General
step_call_with_runspecis not part of the source surface contract. mutation_stmt (<<)is its own source form; it is not authored through legacyTransfer(...)/Mix(...)calls.sep/fracretainprogram = ...as their mechanism-bearing field.img/ecp/phyusequantity = ...and optionalschema_ref.assign_stmtupdates protocol-local bindings only; it does not mutate material state.- In this table,
top-level source filemeans declarations outside any protocol body. top-level entry sourceexecutable statements form the script entry for the current run; top-level executable statements parsed from dependency files are omitted from that entry and are not executed.
For repeat ... in schedule(...), mode has the closed type ScheduleMode: ScheduleMode.DISCRETE or ScheduleMode.CONTINUOUS. Omission selects DISCRETE. The lowercase tokens and strings remain compatible forms. Selecting an enum member does not change the existing discrete-point or continuous-window rules; mode and dependent schedule arguments must be validated together after binding.
4.3 Atomic Statement Set
The atomic statement set is:
mutation_stmt (<<)agitsepfracimgecpphywith envstreammarkersdata_schemaconstraint
Contract notes:
mutation_stmt (<<)is a statement-first material action.agitis a statement-level operator with its own argument constraints.sep/fracare program-bearing operators.img/ecp/phyare quantity-bearing readout operators.constraintis available both as block form (with constraint(...) { ... }) and as trailing sugar over a single simple statement. Standard requirements may use qualified members such asConstraintRequirement.GENTLE; existing bare names retain the same meaning. Requirement items remain static declarations, not dynamically evaluated variable expressions. Installed domain-specific extension members must be referenced by their qualified names and declare their applicable operations, scope and conflicts. Custom context predicates are not supported in this baseline and must not be accepted as executable restrictions. Their wire spelling alone is not an extension declaration. Existing scope, conflict and customized/schema checks apply unchanged. Stored extension requirements retain stable domain identity and version; execution requires explicit driver support and must reject unknown or unsupported identities before performing the action. Requirement meanings, applicable actions, usage scenarios and compatibility checks are defined in the Constraint Contract.- Canonical env target declaration is positional
hold(target_expr)insidewith env(...); legacyhold(sample = ...)is a compatibility spelling, not preferred authoring.
4.4 Local Binding and Assignment
Forms:
let x = expr;x = expr;
Rules:
letcreates a new protocol-local binding.assignupdates an existing protocol-local binding.assigndoes not create new names.assigndoes not mutate the material store.
Assignment surface:
- scalar/text/bool/quantity locals
- limited member assignment paths under
data_ref.result.*anddata_group_ref.result.*
Excluded assignment forms:
- direct reassignment of
container_ref/ group handles - direct reassignment of
data_ref/data_group_ref - index assignment such as
x[i] = ... - compound assignment sugar such as
+=
4.5 Constructor and Init Boundary
4.5.1 Container Families
Source-level constructor families:
container(...)tube(...)well(...)chamber(...)surface(...)plate(...)
Examples:
let t = tube(label = "DNA", capacity = 1.5mL);
let w = well(label = "PCR_A1", capacity = 50uL, carrier_id = "PCRPlate1", carrier_position = "A1");
let p = plate(label = "PlateA", format = PlateFormat.WELL_96, carrier_id = "PlateA");Container-side args may include:
kindspeccarrier_kindcarrier_idcarrier_positioncapacityopenlabelbarcodeload
Rules:
surface(...)must not provide volumecapacity.plate(...)declares a plate carrier, distinct from a volume-bearing container and from an ordered group of wells. Each executing declaration establishes its own carrier identity, including declarations in separate protocol calls or allocating iterations. Aliases, protocol arguments and returned carrier values preserve that identity; equal labels or external identifiers do not merge carriers. Identity does not require a layout or display label to be known at declaration time. Its standardformattype isPlateFormat, withWELL_6,WELL_12,WELL_24,WELL_48,WELL_96, andWELL_384corresponding respectively to 2×3, 3×4, 4×6, 6×8, 8×12, and 16×24 layouts. Existing strings such as"96well"remain compatible. Explicit custom rows/cols remain separate from the standard format vocabulary. Actual protocol arguments are bound before selector bounds and per-well capacity are determined. Explicit arguments take precedence over parameter defaults. Logical coordinates may be retained before binding, but well allocation requires a valid, fully determined layout; an out-of-bounds selector prevents an executable plan.- When
plate(...)declarescapacity, that value is the per-well capacity inherited by every concretewellsynthesized from a selector on that plate. It is not a total capacity for the plate. load = [...]is only valid in constructor/init position.- Every load item is
content_spec:Q[Volume|Mass|Count];cellsis the count unit for cellular content.
Example with independent cellular count and carrier volume:
let cells = tube(load = [
content(kind = bio_cellular, type = cell_line, code = "RPE1"): 100000cells,
content(kind = formulation, type = medium, code = "DMEM_F12"): 300uL
]);After all authored load items are applied, runtime finalizes the container's material relationships. Explicit carrier volume is preferred. A count-only free cellular population uses the reference runtime's recorded default concentration policy to materialize an implicit carrier; adherent or otherwise immobilized cells retain count without an implicit carrier.
4.5.1.1 Container Target-View Accessors
Container expressions expose a narrow dot-accessor surface for target views:
container_expr.contents
container_expr.structure.top
container_expr.structure.bottom
container_expr.structure.sidewallRules:
container_expr.contentsselects the material contents carried by a logical container.container_expr.structure.topselects the top structural facet of the container. It is closure-agnostic: on a closed vessel it may correspond to the cap/seal-facing structure; on an open vessel it corresponds to the upper rim/top structural facet, not air or liquid surface.container_expr.structure.bottomselects the bottom structural facet of the container.container_expr.structure.sidewallselects the sidewall structural facet of the container.container_expr.structureby itself is a target-view namespace, not a complete target view.- The structure-facet member set is closed:
top,bottom, andsidewall. - These accessors are part of the container expression/type system, not a
hold(...)-only spelling rule. - The accessor is read-only and creates no new container, content identity, partition, region, or device component.
- A target-view expression may be bound with
letand passed to parameters that explicitly acceptContainerTargetVieworHoldTarget. - Target-view expressions are not material containers. They must not be used as mutation sources/targets or as
sampleinputs to ordinary material/readout operators unless that operator contract explicitly accepts the view type. - Grouped target views are expressed by grouping the view expressions, for example
group([tube_a.structure.top, tube_b.structure.top]);group_ref.structureis not a broadcast form. - No deeper structure path is defined. Forms such as
tube.structure.top.inner,tube.lid, andtube.boundaryare not container target-view accessors.
4.5.2 Content-Spec Families
Source-level content-spec families:
content(...)blood(...)reagent(...)buffer(...)
Typical usage:
let sample_tube = tube(
label = "Sample",
load = [blood(type = whole_blood): 200uL]
);The content taxonomy contract for kind, type, code, name, and attrs is defined with the operator vocabulary in Chapter 6. The source forms in this section carry those fields.
4.5.3 Constructor Authoring Rules
Rules:
content_specdescribes initial material inside constructorload = [...].- Standalone
content(...),blood(...),reagent(...), andbuffer(...)are not part of the execution-flow authoring contract. - Examples may use family constructors for ergonomic compatibility, but canonical taxonomy examples should use
content(kind = ..., type = ..., attrs = ...)when the exact canonicalkind/typematters. - Lowering targets such as
AllocContainer(...),DefineContent(...), andLoadContent(...)are kernel-facing continuity forms, not preferred protocol-body authoring forms.
4.6 markers, stream, and data_schema
4.6.1 markers
Form:
let panel = markers([CD3, CD19]);Rules:
markers(...)declares a controlled marker set.- Marker names are not hardcoded by the core grammar.
- Downstream marker-aware authoring should only refer to previously declared marker names.
4.6.2 stream
Forms:
let cells = stream(sample = sample_tube, unit = single_cell, panel = panel);
let mols = stream(sample = prepared_tube, unit = molecule);Rules:
sampleis a container-facing input handle.unitnames the emitted observation unit kind. The standardObservationUnit.SINGLE_CELLmember accepts the legacysingle_celltoken or string as a compatibility spelling. Other historical text remains compatibility metadata and does not declare an installed extension. Extension packages may install domain-specific observation unit types; source expressions reference their members using ordinary binding rules. A member from another domain is invalid. Installed units retain stable domain identity, version and member names across planning and storage, independent of implementation class paths. Unknown stored extension identities fail restoration without loading code. Declaring a unit does not confer single-cell interpretation or new measurement capabilities; observation contents and panel association retain their existing semantics.panelis optional and associates a marker panel with the output stream.stream(...)returns aunit_stream_ref-like surface handle.
4.6.3 data_schema
Form:
let obs_schema = data_schema(label = "GelObservation", fields = [...]);Rules:
data_schema(...)declares structured readout/result shape.img/ecp/phymay reference that schema throughschema_refwhen the operator contract allows it.
4.7 Authoring Guidance
Examples should:
- prefer the source-level shapes in this chapter
- avoid exposing internal lowering targets unless explicitly marked as kernel-internal
- use
.culssource files - treat Chapter 6 as the detailed contract layer for operator args and return policy
4.8 Conformance Boundary
Parser implementations, parser tests, and examples must conform to these source forms; they do not define separate grammar behavior.
Lowering, validation, execution, and runtime consequences are specified in Chapters 5-9.
