Class StreamPipelineMachinery
None of it touches StreamStageDispatcher, which is what lets it be a plain collaborator
both sides hold. Anything here that DID need the dispatcher would have to live with the engine
instead - that dependency is what separates the two.
-
Field Summary
Fields inherited from class AbstractStreamStageGenerator
generators, listBufferCalls, symbolTypeOrException, valuePromoterFields inherited from class AbstractGenerator
stackContext, typeNameOrException, variableNameForIR -
Constructor Summary
ConstructorsConstructorDescriptionStreamPipelineMachinery(IRGenerationContext stackContext, GeneratorSet generators) -
Method Summary
Modifier and TypeMethodDescription(package private) ConditionAndPostLoopbuildStreamLoop(IteratorSetup iteratorSetup, List<StageState.Counter> demandBounds, String itemVar, ISymbol itemType, boolean rawIterator, org.antlr.v4.runtime.tree.ParseTree probeSite, StreamLoopSink sink, DebugInfo debugInfo) The unified iterator-driven stream while-loop skeleton: condition scope (hasNext(), with the demand bounds - the segment's heads, or the async window - folded in ahead of it byStreamDemand) + body scope (binditerator.next()then theStreamLoopSinkbody-tail) + theConditionAndPostLoopthe caller emits withemitStreamLoop(ConditionAndPostLoop, String, DebugInfo, List).(package private) ConditionAndPostLoopbuildUnprobedStreamLoop(IteratorSetup iteratorSetup, List<StageState.Counter> demandBounds, String itemVar, ISymbol itemType, boolean rawIterator, StreamLoopSink sink, DebugInfo debugInfo) buildStreamLoop(IteratorSetup, List, String, ISymbol, boolean, ParseTree, StreamLoopSink, DebugInfo)with NO loop-body coverage probe - the emission of a sorted bucket buffer (StreamEmissionEngine.bufferSortEmit), which has never carried one.closeSource(CallDetails closeCall, DebugInfo debugInfo) THE close of a drained stream source (the async executor), in its pipeline's finally region.(package private) booleanTrue if any stage is a pre-loop buffer-barrier (sort/group/split/tail) — these cannot be composed by the windowed async driver and (on an unbounded async) are rejected at phase 3.(package private) booleanTrue iffstagescontains a GROUP/SPLIT/ASYNC collection-buffer stage.(package private) StringdeclareElementSlot(List<IRInstr> target, boolean typed, String typeName, String scopeId, DebugInfo debugInfo) THE declaration of a pipeline's flowing ELEMENT slot - rebound every element (a source item, an emitted item, a drained bucket item, a current group): a compiler-made slot the declaring scope owns, which drops its LAST value at exit and on unwind (A10; EK9_IR_OWNERSHIP_MODEL.md §6), so nothing releases it after the loop.(package private) StringTHE declaration of a compiler-made, initialised SLOT - a counter or flag rebound as the pipeline runs (a head or skip counter, the async in-flight and submitted counts, the windowed-asyncmoreflag):REFERENCE,STOREof the initial value,RETAIN, then the slot registered in the current scope, which releases its LAST value on exit and on unwind (A10; EK9_IR_OWNERSHIP_MODEL.md §6).(package private) voiddeclareOwnedSlot(List<IRInstr> target, boolean typed, String slot, String typeName, String scopeId, DebugInfo debugInfo) declareElementSlot(List, boolean, String, String, DebugInfo)for a slot that already has its name - a stream's ownforloop variable.(package private) StreamDemanddemand()THE demand rule (headbounds the pull) shared by every loop this machinery and the engine build.(package private) DrainConditiondrainCondition(HelperSource source, List<StageState.Counter> heads, DebugInfo debugInfo) THE drain-loop condition:source.hasNext()evaluated in its own scope, bounded byheads(rule B):c < l AND ... source.hasNext(), the counters first.(package private) voiddrainNextIntoItem(List<IRInstr> body, HelperSource source, String castTypeName, String drainItemVar, DebugInfo debugInfo) THE drain-loop body preamble:raw = source.next()(typedAny), cast to the element type, then bound into the persistent drain item variable.(package private) IteratorSetupdrainSourceToIterator(HelperSource source, ISymbol bucketType, boolean keepsUnset, DebugInfo debugInfo, List<IRInstr> instructions) As above;keepsUnsetappends with+=so an UNSET element survives the buffer - a stage never drops one (KeepsUnsetElements).(package private) IteratorSetupdrainSourceToIterator(HelperSource source, ISymbol bucketType, DebugInfo debugInfo, List<IRInstr> instructions) MATERIALISE a buffering value source (a GROUP/SPLIT tracker's hasNextGroup/nextGroup, or an async executor's hasNext/next) into a fresh raw EK9_LIST buffer and return an IteratorSetup over it.(package private) ISymbolThe phase-3-resolved IN-edge (consumes) type of a stream stage, ornullif unresolved.(package private) ISymbolThe phase-3-resolved OUT-edge (produces) type of a stream stage, ornullif the stage has no recordedStreamCallSymbolor no produced type set.(package private) ISymboleffectivePostSortElementType(SortSetup sortSetup, List<EK9Parser.StreamPartContext> allStages, ISymbol sourceType) The element type the POST-sort pipeline (and terminal) observe.(package private) voidemitStreamLoop(ConditionAndPostLoop loop, String loopScopeId, DebugInfo debugInfo, List<IRInstr> instructions) THE emission of a built stream loop: its while (emitWhileLoop(ConditionCaseDetails, String, DebugInfo, List)) then its post-loop instructions (a stage that emits after the loop - tail's drain, join's result).(package private) voidemitWhileLoop(ConditionCaseDetails conditionCase, String loopScopeId, DebugInfo debugInfo, List<IRInstr> instructions) THE while loop of a stream loop:conditionCaseas a WHILE overloopScopeId, appended toinstructions.generateDynamicStageLimit(EK9Parser.StreamPartContext stageCtx, String limitVar, ISymbol integerType, String scopeId, DebugInfo debugInfo) Generate IR to evaluate a dynamic stage operand (function reference or call) and store the Integer result in the given limit variable.generatePipelineItemBinding(String iteratorVar, IAggregateSymbol iteratorType, String pipelineItemVar, ISymbol elementType, DebugInfo debugInfo, boolean rawIterator) Generate: pipelineItem = iterator.next() Follows ForInGenerator.generateLoopVariableBinding() pattern.(package private) StageSetupResultgenerateStageSetup(List<EK9Parser.StreamPartContext> stages, String scopeId, DebugInfo debugInfo) (package private) StageWrapResulthelperDrainBody(HelperSource source, String castTypeName, String itemVar, Supplier<StageWrapResult> downstream, DebugInfo debugInfo) THE body of a loop over a runtime helper: its own per-element scope,item = (castType) source.next()(drainNextIntoItem(List, HelperSource, String, String, DebugInfo)), then whatdownstreambuilds - generated once that scope is current, so what it makes per element is owned by the iteration (A11).(package private) ConditionAndPostLoophelperDrainLoop(HelperSource source, String castTypeName, String itemVar, List<StageState.Counter> heads, Supplier<StageWrapResult> downstream, DebugInfo debugInfo) THE loop over a runtime helper - the twin ofbuildStreamLoop(IteratorSetup, List, String, ISymbol, boolean, ParseTree, StreamLoopSink, DebugInfo)for anHelperSourcerather than an iterator: thedrainCondition(HelperSource, List, DebugInfo)(bounded byheads), then thehelperDrainBody(HelperSource, String, String, Supplier, DebugInfo).(package private) intIndex of the first GROUP/SPLIT/ASYNC collection-buffer stage instages, or -1.(package private) intIndex of the first SORT stage instages, or -1.(package private) PipelineVarPlanplanPipelineVars(List<EK9Parser.StreamPartContext> stages, String sourceVar, ISymbol sourceType, String scopeId, DebugInfo debugInfo, List<IRInstr> declarationsOut) Compute the per-boundary item-variable plan for an in-loop stage list, allocating a fresh variable (declared at the produced type) only where a stage changes the element type.(package private) IteratorSetuprawListIterator(String bufferVar, DebugInfo debugInfo, List<IRInstr> instructions) THE iterator over a stream's raworg.ek9.lang::Listbuffer:buffer.iterator()into a fresh managed temp, typed as the rawIteratorover the rawList- so the loops that read it cast eachnext()(anAny) to the element type.(package private) IteratorSetupsortBufferAndGetIterator(EK9Parser.StreamPartContext sortStage, String bufferVar, DebugInfo debugInfo, List<IRInstr> instructions) Sort a raw org.ek9.lang::List buffer in place and return an iterator over the sorted result.(package private) booleanTrue iff the NEXT buffering SORT instagesis reachable with no intervening collection-buffer (GROUP/SPLIT/ASYNC).(package private) voidwithPipelineScope(List<EK9Parser.StreamPartContext> stages, boolean typedItem, String itemTypeName, DebugInfo debugInfo, List<IRInstr> instructions, PipelineScopeBody body) THE pipeline scope frame: mint and enter a scope, declare the pipeline's element slot in it (declareElementSlot(List, boolean, String, String, DebugInfo)), emit the pre-loop state ofstages(generateStageSetup(List, String, DebugInfo)), runbody(the loop or drain), then exit the scope - which drops the slot's last value.Methods inherited from class AbstractStreamStageGenerator
addManagedCall, borrowedBy, concrete, counterBelowLimit, emitStageFunctionOperand, evaluateExpressionOperand, extractPrimitive, gate, generateCounterDelta, generateTerminalPipe, loadManagedLiteral, ownedBy, publishAndContinue, publishCallResultAndContinue, rebind, rebindToCallResult, rebindToCastCallResult, returnTypeOf, streamFunctionCall
-
Constructor Details
-
StreamPipelineMachinery
StreamPipelineMachinery(IRGenerationContext stackContext, GeneratorSet generators)
-
-
Method Details
-
demand
StreamDemand demand()THE demand rule (headbounds the pull) shared by every loop this machinery and the engine build. -
drainCondition
DrainCondition drainCondition(HelperSource source, List<StageState.Counter> heads, DebugInfo debugInfo) THE drain-loop condition:source.hasNext()evaluated in its own scope, bounded byheads(rule B):c < l AND ... source.hasNext(), the counters first. With no heads it is the plain call. The returnedhasNextVaris the primitive the loop branches on. Shared by every loop over a runtime helper (helperDrainLoop(HelperSource, String, String, List, Supplier, DebugInfo)). -
drainNextIntoItem
void drainNextIntoItem(List<IRInstr> body, HelperSource source, String castTypeName, String drainItemVar, DebugInfo debugInfo) THE drain-loop body preamble:raw = source.next()(typedAny), cast to the element type, then bound into the persistent drain item variable.The item variable is declared in the DRAIN scope, not the body scope, so whatever the body pipes into a buffer outlives the iteration that produced it. Must be called with the body scope already entered - the cast registers against the current scope.
-
helperDrainBody
StageWrapResult helperDrainBody(HelperSource source, String castTypeName, String itemVar, Supplier<StageWrapResult> downstream, DebugInfo debugInfo) THE body of a loop over a runtime helper: its own per-element scope,item = (castType) source.next()(drainNextIntoItem(List, HelperSource, String, String, DebugInfo)), then whatdownstreambuilds - generated once that scope is current, so what it makes per element is owned by the iteration (A11).- Returns:
- the body (scope enter and exit included) and the downstream's post-loop instructions
-
helperDrainLoop
ConditionAndPostLoop helperDrainLoop(HelperSource source, String castTypeName, String itemVar, List<StageState.Counter> heads, Supplier<StageWrapResult> downstream, DebugInfo debugInfo) THE loop over a runtime helper - the twin ofbuildStreamLoop(IteratorSetup, List, String, ISymbol, boolean, ParseTree, StreamLoopSink, DebugInfo)for anHelperSourcerather than an iterator: thedrainCondition(HelperSource, List, DebugInfo)(bounded byheads), then thehelperDrainBody(HelperSource, String, String, Supplier, DebugInfo). Every buffered emission (the async executor's results, a group/split's buckets, the tail buffer, the drain that materialises a buffer) is this loop; only the item, the heads and the downstream differ. The condition case names the helper's ownhasNext()result as its condition result. -
declareElementSlot
String declareElementSlot(List<IRInstr> target, boolean typed, String typeName, String scopeId, DebugInfo debugInfo) THE declaration of a pipeline's flowing ELEMENT slot - rebound every element (a source item, an emitted item, a drained bucket item, a current group): a compiler-made slot the declaring scope owns, which drops its LAST value at exit and on unwind (A10; EK9_IR_OWNERSHIP_MODEL.md §6), so nothing releases it after the loop. Named as a slot, never_temp: a native back end releases a registered_tempwith the value it held when registered, a slot with its current value.typedgives aTYPED_REFERENCE(a join's ASM stackmap).- Returns:
- the slot's name
-
declareOwnedSlot
-
declareInitialisedSlot
String declareInitialisedSlot(List<IRInstr> target, ISymbol type, String literal, DebugInfo debugInfo) THE declaration of a compiler-made, initialised SLOT - a counter or flag rebound as the pipeline runs (a head or skip counter, the async in-flight and submitted counts, the windowed-asyncmoreflag):REFERENCE,STOREof the initial value,RETAIN, then the slot registered in the current scope, which releases its LAST value on exit and on unwind (A10; EK9_IR_OWNERSHIP_MODEL.md §6). Each rebind (RELEASE/STORE/ RETAIN) releases the value it replaces. The initial value is theliteral, loaded first (AbstractStreamStageGenerator.loadManagedLiteral(List, String, ISymbol, DebugInfo)).- Returns:
- the slot's name
-
withPipelineScope
void withPipelineScope(List<EK9Parser.StreamPartContext> stages, boolean typedItem, String itemTypeName, DebugInfo debugInfo, List<IRInstr> instructions, PipelineScopeBody body) THE pipeline scope frame: mint and enter a scope, declare the pipeline's element slot in it (declareElementSlot(List, boolean, String, String, DebugInfo)), emit the pre-loop state ofstages(generateStageSetup(List, String, DebugInfo)), runbody(the loop or drain), then exit the scope - which drops the slot's last value. The streaming pipeline, the buffered for-range emission and its drains all open this frame; never hand-roll it. -
emitWhileLoop
void emitWhileLoop(ConditionCaseDetails conditionCase, String loopScopeId, DebugInfo debugInfo, List<IRInstr> instructions) THE while loop of a stream loop:conditionCaseas a WHILE overloopScopeId, appended toinstructions. Every stream drain, pull and emission loop is emitted through this (oremitStreamLoop(ConditionAndPostLoop, String, DebugInfo, List), which adds the loop's post-loop work) - never a hand-builtcreateWhileLoop+controlFlowChainGeneratorpair. -
emitStreamLoop
void emitStreamLoop(ConditionAndPostLoop loop, String loopScopeId, DebugInfo debugInfo, List<IRInstr> instructions) THE emission of a built stream loop: its while (emitWhileLoop(ConditionCaseDetails, String, DebugInfo, List)) then its post-loop instructions (a stage that emits after the loop - tail's drain, join's result). -
buildStreamLoop
ConditionAndPostLoop buildStreamLoop(IteratorSetup iteratorSetup, List<StageState.Counter> demandBounds, String itemVar, ISymbol itemType, boolean rawIterator, org.antlr.v4.runtime.tree.ParseTree probeSite, StreamLoopSink sink, DebugInfo debugInfo) The unified iterator-driven stream while-loop skeleton: condition scope (hasNext(), with the demand bounds - the segment's heads, or the async window - folded in ahead of it byStreamDemand) + body scope (binditerator.next()then theStreamLoopSinkbody-tail) + theConditionAndPostLoopthe caller emits withemitStreamLoop(ConditionAndPostLoop, String, DebugInfo, List). Every iterator-based stream loop (streaming cat/for, and — after migration — the sort/group/split drains) is this skeleton differing only in its demand bounds and sink.probeSiteis where the loop's LOOP_BODY coverage probe is: the pipeline's source as written (itscatorfor), whichever compiler-made loop of that pipeline this is - its id is hashed from that location, so two pipelines are two probes (defect D88). Never null: a probe with no location has no line, and every such probe in one function would share one id. -
buildUnprobedStreamLoop
ConditionAndPostLoop buildUnprobedStreamLoop(IteratorSetup iteratorSetup, List<StageState.Counter> demandBounds, String itemVar, ISymbol itemType, boolean rawIterator, StreamLoopSink sink, DebugInfo debugInfo) buildStreamLoop(IteratorSetup, List, String, ISymbol, boolean, ParseTree, StreamLoopSink, DebugInfo)with NO loop-body coverage probe - the emission of a sorted bucket buffer (StreamEmissionEngine.bufferSortEmit), which has never carried one. -
containsBufferBarrier
True if any stage is a pre-loop buffer-barrier (sort/group/split/tail) — these cannot be composed by the windowed async driver and (on an unbounded async) are rejected at phase 3. -
containsBufferingCollection
True iffstagescontains a GROUP/SPLIT/ASYNC collection-buffer stage. -
drainSourceToIterator
IteratorSetup drainSourceToIterator(HelperSource source, ISymbol bucketType, DebugInfo debugInfo, List<IRInstr> instructions) MATERIALISE a buffering value source (a GROUP/SPLIT tracker's hasNextGroup/nextGroup, or an async executor's hasNext/next) into a fresh raw EK9_LIST buffer and return an IteratorSetup over it. No pre-stages, no sort: each next() yields Any, is cast tobucketType, and piped into the buffer as-is. Nothing is closed here: an async executor is closed by its pipeline's finally region (StreamEmissionEngine). Used to hand a collection-buffer's emitted elements toStreamEmissionEngine.continueOverIteratorso a DOWNSTREAM buffer (group|split, group|split|sort, async|split, ...) can drive them — the basis of unlimited buffer-after-buffer composition. -
drainSourceToIterator
IteratorSetup drainSourceToIterator(HelperSource source, ISymbol bucketType, boolean keepsUnset, DebugInfo debugInfo, List<IRInstr> instructions) As above;keepsUnsetappends with+=so an UNSET element survives the buffer - a stage never drops one (KeepsUnsetElements). Anasyncresult may be unset and passes through like amap's (Steve, 2026-10-21); a group/split bucket is a non-empty List, never unset, so those callers keep|. -
rawListIterator
THE iterator over a stream's raworg.ek9.lang::Listbuffer:buffer.iterator()into a fresh managed temp, typed as the rawIteratorover the rawList- so the loops that read it cast eachnext()(anAny) to the element type. Every buffer a pipeline materialises (a drained helper, a sorted buffer, a for-range buffer) is iterated through this. -
edgeConsumes
The phase-3-resolved IN-edge (consumes) type of a stream stage, ornullif unresolved. Counterpart toedgeProduces(EK9Parser.StreamPartContext)— the element type ENTERING stage k. -
edgeProduces
The phase-3-resolved OUT-edge (produces) type of a stream stage, ornullif the stage has no recordedStreamCallSymbolor no produced type set. This is the SINGLE source of truth for "the element type leaving stage k": Phase 3 (StreamAssemblyOrError.streamPartOrError) stampssetProducesSymbolTypeon EVERY stage's StreamCallSymbol while threadingcurrentStreamType, so the whole typed-edge sequence already exists — read it here rather than re-deriving from the source type carried forward.edgeConsumes(EK9Parser.StreamPartContext)is the IN-edge counterpart. -
effectivePostSortElementType
ISymbol effectivePostSortElementType(SortSetup sortSetup, List<EK9Parser.StreamPartContext> allStages, ISymbol sourceType) The element type the POST-sort pipeline (and terminal) observe. SORT buffers and re-emits the type it consumes; when a type-changing stage (map/flatten/call) precedes SORT, that is the post-stage type, not the source type. Returns the SORT stage's resolved produced type when SORT is present, else the supplied source type unchanged (so non-sort pipelines and natural-type sorts are byte-identical). -
generateDynamicStageLimit
List<IRInstr> generateDynamicStageLimit(EK9Parser.StreamPartContext stageCtx, String limitVar, ISymbol integerType, String scopeId, DebugInfo debugInfo) Generate IR to evaluate a dynamic stage operand (function reference or call) and store the Integer result in the given limit variable.For function references (
head GetThree): creates a function instance and calls it. For function calls (head GetThree()): evaluates the call expression. For Integer variables (head someVar): loads the variable value. -
generatePipelineItemBinding
List<IRInstr> generatePipelineItemBinding(String iteratorVar, IAggregateSymbol iteratorType, String pipelineItemVar, ISymbol elementType, DebugInfo debugInfo, boolean rawIterator) Generate: pipelineItem = iterator.next() Follows ForInGenerator.generateLoopVariableBinding() pattern.- Parameters:
rawIterator- true when iterating a raw org.ek9.lang.Iterator (buffered for-range), where next() returns Any and requires a cast to elementType
-
generateStageSetup
StageSetupResult generateStageSetup(List<EK9Parser.StreamPartContext> stages, String scopeId, DebugInfo debugInfo) -
closeSource
THE close of a drained stream source (the async executor), in its pipeline's finally region.Ek9Async.close()hands back its report String at +1: it is named and dropped at once - discarded, nothing released it (the per-path verifier's discarded-result rule; Linux LSan: every async pipeline leaked one String per run). -
indexOfFirstCollectionBuffer
Index of the first GROUP/SPLIT/ASYNC collection-buffer stage instages, or -1. -
indexOfFirstSort
Index of the first SORT stage instages, or -1. -
planPipelineVars
PipelineVarPlan planPipelineVars(List<EK9Parser.StreamPartContext> stages, String sourceVar, ISymbol sourceType, String scopeId, DebugInfo debugInfo, List<IRInstr> declarationsOut) Compute the per-boundary item-variable plan for an in-loop stage list, allocating a fresh variable (declared at the produced type) only where a stage changes the element type. Fresh-variable declarations (REFERENCE + SCOPE_REGISTER) are appended todeclarationsOut; the caller emits them once before the loop body. Type-preserving stages reuse the incoming variable, so pipelines without a type-changing stage produce byte-identical IR to before. -
sortBufferAndGetIterator
IteratorSetup sortBufferAndGetIterator(EK9Parser.StreamPartContext sortStage, String bufferVar, DebugInfo debugInfo, List<IRInstr> instructions) Sort a raw org.ek9.lang::List buffer in place and return an iterator over the sorted result. Shared by both cat-pipeline and for-range pipeline sort paths. Uses Ek9Sort.sort(List) for natural sort or Ek9Sort.sort(List, Any) for comparator sort. No LOAD_FIELD delegate access — works correctly for both pre-built and generated types.- Parameters:
sortStage- the SORT stage parse context (may have pipelinePart for comparator)bufferVar- variable holding the raw org.ek9.lang::List bufferdebugInfo- debug information for generated instructionsinstructions- receives the sort call and the iterator creation- Returns:
- the iterator over the sorted buffer
-
sortReachableBeforeBuffer
True iff the NEXT buffering SORT instagesis reachable with no intervening collection-buffer (GROUP/SPLIT/ASYNC). Those three stages own the sort that follows them — they buffer and emit in their own pre-loop path, where a downstream SORT is handled separately — so the source-drain sort loop must NOT drain through them (it would treat the GROUP/SPLIT/ASYNC as a no-op pre-sort stage and corrupt the stream). A SORT before any such buffer IS the source-drain loop's to handle. Used as the multi-sort loop condition sosort | map | sortkeeps draining butsort | group | sortstops at the group (leaving the trailing sort on postSortStages for the group path).
-