protected Stream<AssemblyGeneratedPrototype> applyRemainingOperands(GeneratorContext parentGc,
			List<GeneratorContext> childGcs, AssemblyResolvedPatterns fromLeft,
			AssemblyConstructorSemantic sem, List<AssemblyParseTreeNode> opOrdered,
			List<AbstractAssemblyState> children) {
		Constructor cons = sem.getConstructor();
		int opIdx = children.size();
		if (opIdx == cons.getNumOperands()) {
			// We're done!
			return Stream.of(new AssemblyGeneratedPrototype(
				new AssemblyConstructState(resolver, parentGc.path, parentGc.shift, sem, children),
				fromLeft));
		}
		AssemblyParseTreeNode opNode = opOrdered.get(opIdx);
		OperandSymbol opSym = cons.getOperand(opIdx);
		int offset = opSym.getRelativeOffset();
		int offsetBase = opSym.getOffsetBase();
		if (-1 != offsetBase) {
			int baseShift = childGcs.get(offsetBase).shift;
			int baseLength = children.get(offsetBase).getLength();
			offset += baseShift - parentGc.shift + baseLength;
		}

		AbstractAssemblyStateGenerator<?> opGen =
			resolver.getStateGenerator(opSym, opNode, fromLeft);
		GeneratorContext opGc = parentGc.push(sem, offset);
		childGcs.set(opIdx, opGc);
		return opGen.generate(opGc).flatMap(prot -> {
			return applyRemainingOperands(parentGc, new ArrayList<>(childGcs), prot.patterns, sem,
				opOrdered, AsmUtil.extendList(children, prot.state));
		});
	}
A recursive function for generating child operand prototypes and constructing the parent(s)

<p>
The implementation generates states for the left-most node not yet considered. It knows which
is next by examining the length of {@code children}, which records the generated state for
each child already considered. It then appends the result to {@code children} and recurses,
using the resulting patterns as {@code fromLeft}. Given that multiple prototypes can be
generated, {@link Stream#flatMap(java.util.function.Function)} makes the recursive invocation
somewhat fluent. The base case occurs when all children have states generated. It constructs
the state for this node, storing the generated children with it.

<p>
This routine is also operative in computing shifts, since the offset of each operand is
incorporated here. Two accessors are needed to compute the offset:
{@link OperandSymbol#getOffsetBase()} and {@link OperandSymbol#getRelativeOffset()}. The
former identifies which operand's end (exclusive) byte is the base of the offset. The latter
specifies an additional number of bytes to the right. Consider an operand consisting of three
operands, each consuming a 1-byte token.

<pre>
+-----+-----+-----+
| op0 | op1 | op2 |
+-----+-----+-----+
 ^-1   ^0    ^1    ^2
</pre>

<p>
A base offset of 0 would indicate that the overall offset is the end of op0 (relative to the
parent op) plus the relative offset. A base offset of -1 is special, but is easy to
conceptualize from the diagram. It indicates the beginning byte of the parent op. Thus every
child operand boundary is numbered. The offset base must always refer to an operand to the
left.

@param parentGc the generator context for this node
@param childGcs a list to collect the generator context for each child operand. The root
           invocation should pass a fixed-length mutable list of nulls, one for each child.
@param fromLeft the accumulated patterns from the left sibling. The root invocation should
           pass the patterns accumulated after context changes.
@param sem the selected SLEIGH constructor, whose operands to generate
@param opOrdered the parsed children ordered as the constructor's operands
@param children the list of children generated so far. The root invocation should pass the
           empty list.
@return the stream of generated (sub) prototypes
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/assembler/sleigh/sem/AssemblyConstructStateGenerator.java