Skip to content

Commit 67c5a8a

Browse files
committed
refactor: clarify Scope internals, extract stepsOut() method and document depth-capture mechanism
1 parent f172704 commit 67c5a8a

1 file changed

Lines changed: 25 additions & 13 deletions

File tree

core/src/main/java/io/substrait/expression/LambdaBuilder.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ public Expression.Lambda lambda(List<Type> paramTypes, Function<Scope, Expressio
4545
Type.Struct params = Type.Struct.builder().nullable(false).addAllFields(paramTypes).build();
4646
pushLambdaContext(params);
4747
try {
48-
int index = lambdaContext.size() - 1;
49-
Scope scope = new Scope(index);
48+
Scope scope = new Scope(params);
5049
Expression body = bodyFn.apply(scope);
5150
return ImmutableExpression.Lambda.builder().parameters(params).body(body).build();
5251
} finally {
@@ -83,14 +82,14 @@ public Expression.Lambda lambdaFromStruct(
8382
* @throws IllegalArgumentException if stepsOut exceeds the current nesting depth
8483
*/
8584
public Type.Struct resolveParams(int stepsOut) {
86-
int index = lambdaContext.size() - 1 - stepsOut;
87-
if (index < 0 || index >= lambdaContext.size()) {
85+
int targetDepth = lambdaContext.size() - stepsOut;
86+
if (targetDepth <= 0 || targetDepth > lambdaContext.size()) {
8887
throw new IllegalArgumentException(
8988
String.format(
9089
"Lambda parameter reference with stepsOut=%d is invalid (current depth: %d)",
9190
stepsOut, lambdaContext.size()));
9291
}
93-
return lambdaContext.get(index);
92+
return lambdaContext.get(targetDepth - 1);
9493
}
9594

9695
/**
@@ -113,26 +112,39 @@ private void popLambdaContext() {
113112
/**
114113
* A handle to a particular lambda's parameter scope. Use {@link #ref} to create validated
115114
* parameter references.
115+
*
116+
* <p>Each Scope captures the depth of the lambdaContext stack at the time it was created. When
117+
* {@link #ref} is called, the Substrait {@code stepsOut} value is computed as the difference
118+
* between the current stack depth and the captured depth. This means the same Scope produces
119+
* different stepsOut values depending on the nesting level at the time of the call, which is what
120+
* allows outer.ref(0) to produce stepsOut=1 when called inside a nested lambda.
116121
*/
117122
public class Scope {
118-
private final int index;
123+
private final Type.Struct params;
124+
private final int depth;
125+
126+
private Scope(Type.Struct params) {
127+
this.params = params;
128+
this.depth = lambdaContext.size();
129+
}
119130

120-
private Scope(int index) {
121-
this.index = index;
131+
/**
132+
* Computes the number of lambda boundaries between this scope and the current innermost scope.
133+
* This value changes dynamically as nested lambdas are built.
134+
*/
135+
private int stepsOut() {
136+
return lambdaContext.size() - depth;
122137
}
123138

124139
/**
125-
* Creates a validated reference to a parameter of this lambda. The correct {@code stepsOut}
126-
* value is computed automatically.
140+
* Creates a validated reference to a parameter of this lambda.
127141
*
128142
* @param paramIndex index of the parameter within this lambda's parameter struct
129143
* @return a {@link FieldReference} pointing to the specified parameter
130144
* @throws IndexOutOfBoundsException if paramIndex is out of bounds
131145
*/
132146
public FieldReference ref(int paramIndex) {
133-
int stepsOut = lambdaContext.size() - 1 - index;
134-
return FieldReference.newLambdaParameterReference(
135-
paramIndex, lambdaContext.get(index), stepsOut);
147+
return FieldReference.newLambdaParameterReference(paramIndex, params, stepsOut());
136148
}
137149
}
138150
}

0 commit comments

Comments
 (0)