Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/src/main/java/dev/cel/common/navigation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ java_library(
"//:auto_value",
"//common",
"//common/ast",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package dev.cel.common.navigation;

import com.google.auto.value.AutoValue;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.CelComprehension;
import dev.cel.common.ast.CelExpr.ExprKind;
Expand Down Expand Up @@ -59,6 +61,12 @@ public long id() {
/** Represents the count of transitive parents. Depth of an AST's root is 0. */
public abstract int depth();

/**
* Represents the maximum count of children from any of its branches. Height of a leaf node is 0.
* For example, the height of the call node 'func' in expression `(1 + 2 + 3).func(4 + 5)` is 3.
*/
public abstract int height();

/** Constructs a new instance of {@link CelNavigableExpr} from {@link CelExpr}. */
public static CelNavigableExpr fromExpr(CelExpr expr) {
return CelNavigableExpr.builder().setExpr(expr).build();
Expand Down Expand Up @@ -93,7 +101,8 @@ public Stream<CelNavigableExpr> descendants() {
* the specified traversal order.
*/
public Stream<CelNavigableExpr> descendants(TraversalOrder traversalOrder) {
return CelNavigableExprVisitor.collect(this, traversalOrder).filter(node -> !node.equals(this));
return CelNavigableExprVisitor.collect(this, traversalOrder)
.filter(node -> node.depth() > this.depth());
}

/**
Expand All @@ -110,7 +119,7 @@ public Stream<CelNavigableExpr> children() {
*/
public Stream<CelNavigableExpr> children(TraversalOrder traversalOrder) {
return CelNavigableExprVisitor.collect(this, this.depth() + 1, traversalOrder)
.filter(node -> !node.equals(this));
.filter(node -> node.depth() > this.depth());
}

/** Returns the underlying kind of the {@link CelExpr}. */
Expand All @@ -120,21 +129,47 @@ public ExprKind.Kind getKind() {

/** Create a new builder to construct a {@link CelNavigableExpr} instance. */
public static Builder builder() {
return new AutoValue_CelNavigableExpr.Builder().setDepth(0);
return new AutoValue_CelNavigableExpr.Builder().setDepth(0).setHeight(0);
}

/** Builder to configure {@link CelNavigableExpr}. */
@AutoValue.Builder
public abstract static class Builder {

private Builder parentBuilder;

public abstract CelExpr expr();

public abstract int depth();

public ExprKind.Kind getKind() {
return expr().exprKind().getKind();
}

public abstract Builder setExpr(CelExpr value);

public abstract Builder setParent(CelNavigableExpr value);
abstract Builder setParent(CelNavigableExpr value);

@CanIgnoreReturnValue
public Builder setParentBuilder(CelNavigableExpr.Builder value) {
parentBuilder = value;
return this;
}

public abstract Builder setDepth(int value);

public abstract CelNavigableExpr build();
public abstract Builder setHeight(int value);

public abstract CelNavigableExpr autoBuild();

@CheckReturnValue
public CelNavigableExpr build() {
if (parentBuilder != null) {
setParent(parentBuilder.build());
}
return autoBuild();
}
}

public abstract Builder toBuilder();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package dev.cel.common.navigation;

import static java.lang.Math.max;

import com.google.common.collect.ImmutableList;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.CelCall;
Expand All @@ -29,7 +31,7 @@
final class CelNavigableExprVisitor {
private static final int MAX_DESCENDANTS_RECURSION_DEPTH = 500;

private final Stream.Builder<CelNavigableExpr> streamBuilder;
private final Stream.Builder<CelNavigableExpr.Builder> streamBuilder;
private final TraversalOrder traversalOrder;
private final int maxDepth;

Expand Down Expand Up @@ -84,105 +86,124 @@ static Stream<CelNavigableExpr> collect(
CelNavigableExpr navigableExpr, int maxDepth, TraversalOrder traversalOrder) {
CelNavigableExprVisitor visitor = new CelNavigableExprVisitor(maxDepth, traversalOrder);

visitor.visit(navigableExpr);
visitor.visit(navigableExpr.toBuilder());

return visitor.streamBuilder.build();
return visitor.streamBuilder.build().map(CelNavigableExpr.Builder::build);
}

private void visit(CelNavigableExpr navigableExpr) {
private int visit(CelNavigableExpr.Builder navigableExpr) {
if (navigableExpr.depth() > MAX_DESCENDANTS_RECURSION_DEPTH - 1) {
throw new IllegalStateException("Max recursion depth reached.");
}
if (navigableExpr.depth() > maxDepth) {
return;
return -1;
}
if (traversalOrder.equals(TraversalOrder.PRE_ORDER)) {
streamBuilder.add(navigableExpr);
}

int height = 1;
switch (navigableExpr.getKind()) {
case CALL:
visit(navigableExpr, navigableExpr.expr().call());
height += visit(navigableExpr, navigableExpr.expr().call());
break;
case CREATE_LIST:
visit(navigableExpr, navigableExpr.expr().createList());
height += visit(navigableExpr, navigableExpr.expr().createList());
break;
case SELECT:
visit(navigableExpr, navigableExpr.expr().select());
height += visit(navigableExpr, navigableExpr.expr().select());
break;
case CREATE_STRUCT:
visitStruct(navigableExpr, navigableExpr.expr().createStruct());
height += visitStruct(navigableExpr, navigableExpr.expr().createStruct());
break;
case CREATE_MAP:
visitMap(navigableExpr, navigableExpr.expr().createMap());
height += visitMap(navigableExpr, navigableExpr.expr().createMap());
break;
case COMPREHENSION:
visit(navigableExpr, navigableExpr.expr().comprehension());
height += visit(navigableExpr, navigableExpr.expr().comprehension());
break;
default:
// This is a leaf node
height = 0;
break;
}

navigableExpr.setHeight(height);
if (traversalOrder.equals(TraversalOrder.POST_ORDER)) {
streamBuilder.add(navigableExpr);
}

return height;
}

private void visit(CelNavigableExpr navigableExpr, CelCall call) {
private int visit(CelNavigableExpr.Builder navigableExpr, CelCall call) {
int targetHeight = 0;
if (call.target().isPresent()) {
CelNavigableExpr target = newNavigableChild(navigableExpr, call.target().get());
visit(target);
CelNavigableExpr.Builder target = newNavigableChild(navigableExpr, call.target().get());
targetHeight = visit(target);
}

visitExprList(call.args(), navigableExpr);
int argumentHeight = visitExprList(call.args(), navigableExpr);
return max(targetHeight, argumentHeight);
}

private void visit(CelNavigableExpr navigableExpr, CelCreateList createList) {
visitExprList(createList.elements(), navigableExpr);
private int visit(CelNavigableExpr.Builder navigableExpr, CelCreateList createList) {
return visitExprList(createList.elements(), navigableExpr);
}

private void visit(CelNavigableExpr navigableExpr, CelSelect selectExpr) {
CelNavigableExpr operand = newNavigableChild(navigableExpr, selectExpr.operand());
visit(operand);
private int visit(CelNavigableExpr.Builder navigableExpr, CelSelect selectExpr) {
CelNavigableExpr.Builder operand = newNavigableChild(navigableExpr, selectExpr.operand());
return visit(operand);
}

private void visit(CelNavigableExpr navigableExpr, CelComprehension comprehension) {
visit(newNavigableChild(navigableExpr, comprehension.iterRange()));
visit(newNavigableChild(navigableExpr, comprehension.accuInit()));
visit(newNavigableChild(navigableExpr, comprehension.loopCondition()));
visit(newNavigableChild(navigableExpr, comprehension.loopStep()));
visit(newNavigableChild(navigableExpr, comprehension.result()));
private int visit(CelNavigableExpr.Builder navigableExpr, CelComprehension comprehension) {
int maxHeight = 0;
maxHeight = max(visit(newNavigableChild(navigableExpr, comprehension.iterRange())), maxHeight);
maxHeight = max(visit(newNavigableChild(navigableExpr, comprehension.accuInit())), maxHeight);
maxHeight =
max(visit(newNavigableChild(navigableExpr, comprehension.loopCondition())), maxHeight);
maxHeight = max(visit(newNavigableChild(navigableExpr, comprehension.loopStep())), maxHeight);
maxHeight = max(visit(newNavigableChild(navigableExpr, comprehension.result())), maxHeight);

return maxHeight;
}

private void visitStruct(CelNavigableExpr navigableExpr, CelCreateStruct struct) {
private int visitStruct(CelNavigableExpr.Builder navigableExpr, CelCreateStruct struct) {
int maxHeight = 0;
for (CelCreateStruct.Entry entry : struct.entries()) {
CelNavigableExpr value = newNavigableChild(navigableExpr, entry.value());
visit(value);
CelNavigableExpr.Builder value = newNavigableChild(navigableExpr, entry.value());
maxHeight = max(visit(value), maxHeight);
}
return maxHeight;
}

private void visitMap(CelNavigableExpr navigableExpr, CelCreateMap map) {
private int visitMap(CelNavigableExpr.Builder navigableExpr, CelCreateMap map) {
int maxHeight = 0;
for (CelCreateMap.Entry entry : map.entries()) {
CelNavigableExpr key = newNavigableChild(navigableExpr, entry.key());
visit(key);
CelNavigableExpr.Builder key = newNavigableChild(navigableExpr, entry.key());
maxHeight = max(visit(key), maxHeight);

CelNavigableExpr value = newNavigableChild(navigableExpr, entry.value());
visit(value);
CelNavigableExpr.Builder value = newNavigableChild(navigableExpr, entry.value());
maxHeight = max(visit(value), maxHeight);
}
return 0;
}

private void visitExprList(ImmutableList<CelExpr> createListExpr, CelNavigableExpr parent) {
private int visitExprList(
ImmutableList<CelExpr> createListExpr, CelNavigableExpr.Builder parent) {
int maxHeight = 0;
for (CelExpr expr : createListExpr) {
CelNavigableExpr arg = newNavigableChild(parent, expr);
visit(arg);
CelNavigableExpr.Builder arg = newNavigableChild(parent, expr);
maxHeight = max(visit(arg), maxHeight);
}
return maxHeight;
}

private CelNavigableExpr newNavigableChild(CelNavigableExpr parent, CelExpr expr) {
private CelNavigableExpr.Builder newNavigableChild(
CelNavigableExpr.Builder parent, CelExpr expr) {
return CelNavigableExpr.builder()
.setExpr(expr)
.setDepth(parent.depth() + 1)
.setParent(parent)
.build();
.setParentBuilder(parent);
}
}
Loading