All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.cel.common.navigation.CelNavigableExprVisitor Maven / Gradle / Ivy

// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dev.cel.common.navigation;

import com.google.common.collect.ImmutableList;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.CelCall;
import dev.cel.common.ast.CelExpr.CelComprehension;
import dev.cel.common.ast.CelExpr.CelCreateList;
import dev.cel.common.ast.CelExpr.CelCreateMap;
import dev.cel.common.ast.CelExpr.CelCreateStruct;
import dev.cel.common.ast.CelExpr.CelSelect;
import dev.cel.common.navigation.CelNavigableExpr.TraversalOrder;
import java.util.stream.Stream;

/** Visitor implementation to navigate an AST. */
final class CelNavigableExprVisitor {
  private static final int MAX_DESCENDANTS_RECURSION_DEPTH = 500;

  private final Stream.Builder streamBuilder;
  private final ExprHeightCalculator exprHeightCalculator;
  private final TraversalOrder traversalOrder;
  private final int maxDepth;

  private CelNavigableExprVisitor(
      int maxDepth, ExprHeightCalculator exprHeightCalculator, TraversalOrder traversalOrder) {
    this.maxDepth = maxDepth;
    this.exprHeightCalculator = exprHeightCalculator;
    this.traversalOrder = traversalOrder;
    this.streamBuilder = Stream.builder();
  }

  /**
   * Returns a stream containing all the nodes using the specified traversal order. Note that the
   * collection occurs eagerly.
   *
   * 
   * For example, given the following tree:
   *             a
   *          b     c
   *       d    e
   *
   * The collected nodes are as follows using pre-order traversal:
   *
   * maxDepth of -1: none
   * maxDepth of 0: a
   * maxDepth of 1: a, b, c
   * maxDepth of 2: a, b, d, e, c
   * 
*/ static Stream collect( CelNavigableExpr navigableExpr, TraversalOrder traversalOrder) { return collect(navigableExpr, MAX_DESCENDANTS_RECURSION_DEPTH, traversalOrder); } /** * Returns a stream containing all the nodes upto the maximum depth specified using the specified * traversal order. Note that the collection occurs eagerly. * *
   * For example, given the following tree:
   *             a
   *          b     c
   *       d    e
   *
   * The collected nodes are as follows using pre-order traversal:
   *
   * maxDepth of -1: none
   * maxDepth of 0: a
   * maxDepth of 1: a, b, c
   * maxDepth of 2: a, b, d, e, c
   * 
*/ static Stream collect( CelNavigableExpr navigableExpr, int maxDepth, TraversalOrder traversalOrder) { ExprHeightCalculator exprHeightCalculator = new ExprHeightCalculator(navigableExpr.expr()); CelNavigableExprVisitor visitor = new CelNavigableExprVisitor(maxDepth, exprHeightCalculator, traversalOrder); visitor.visit(navigableExpr); return visitor.streamBuilder.build(); } private void visit(CelNavigableExpr navigableExpr) { if (navigableExpr.depth() > MAX_DESCENDANTS_RECURSION_DEPTH - 1) { throw new IllegalStateException("Max recursion depth reached."); } boolean addToStream = navigableExpr.depth() <= maxDepth; if (addToStream && traversalOrder.equals(TraversalOrder.PRE_ORDER)) { streamBuilder.add(navigableExpr); } switch (navigableExpr.getKind()) { case CALL: visit(navigableExpr, navigableExpr.expr().call()); break; case CREATE_LIST: visit(navigableExpr, navigableExpr.expr().createList()); break; case SELECT: visit(navigableExpr, navigableExpr.expr().select()); break; case CREATE_STRUCT: visitStruct(navigableExpr, navigableExpr.expr().createStruct()); break; case CREATE_MAP: visitMap(navigableExpr, navigableExpr.expr().createMap()); break; case COMPREHENSION: visit(navigableExpr, navigableExpr.expr().comprehension()); break; default: break; } if (addToStream && traversalOrder.equals(TraversalOrder.POST_ORDER)) { streamBuilder.add(navigableExpr); } } private void visit(CelNavigableExpr navigableExpr, CelCall call) { if (call.target().isPresent()) { visit(newNavigableChild(navigableExpr, call.target().get())); } visitExprList(call.args(), navigableExpr); } private void visit(CelNavigableExpr navigableExpr, CelCreateList createList) { visitExprList(createList.elements(), navigableExpr); } private void visit(CelNavigableExpr navigableExpr, CelSelect selectExpr) { CelNavigableExpr operand = newNavigableChild(navigableExpr, selectExpr.operand()); 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 void visitStruct(CelNavigableExpr navigableExpr, CelCreateStruct struct) { for (CelCreateStruct.Entry entry : struct.entries()) { visit(newNavigableChild(navigableExpr, entry.value())); } } private void visitMap(CelNavigableExpr navigableExpr, CelCreateMap map) { for (CelCreateMap.Entry entry : map.entries()) { CelNavigableExpr key = newNavigableChild(navigableExpr, entry.key()); visit(key); CelNavigableExpr value = newNavigableChild(navigableExpr, entry.value()); visit(value); } } private void visitExprList(ImmutableList createListExpr, CelNavigableExpr parent) { for (CelExpr expr : createListExpr) { visit(newNavigableChild(parent, expr)); } } private CelNavigableExpr newNavigableChild(CelNavigableExpr parent, CelExpr expr) { CelNavigableExpr.Builder navigableExpr = CelNavigableExpr.builder() .setExpr(expr) .setDepth(parent.depth() + 1) .setHeight(exprHeightCalculator.getHeight(expr.id())) .setParent(parent); return navigableExpr.build(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy