You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cel-python遍历CEL AST并构建有序条件栈的方法

问题描述

我正在使用cel-python库中的celparser.py解析CEL表达式并生成AST。解析完成后,我需要遍历该AST并构建一个以最内层条件为栈顶的条件栈。例如,给定以下CEL表达式:

(a.name == 'Jon' && (c.role == 'lead' || c.region == 'US')) ? b.salary : c.bonus

我期望得到的栈内容如下:

c.role == 'lead'
c.region == 'US'
c.role == 'lead' || c.region == 'US'
a.name == 'Jon' && (c.role == 'lead' || c.region == 'US')

请问实现该AST遍历与栈构建的最优方法是什么?

最优实现方案

核心逻辑:深度优先后序遍历

要让最内层条件处于栈顶,核心是先处理所有子表达式,再处理当前表达式。深度优先遍历的后序遍历正好匹配这个需求:递归遍历完所有子节点后,再将当前表达式加入栈,这样最内层的原子条件会被最先压入栈,随后是逐层向外的组合条件,最终栈的顺序完全符合预期。

具体实现步骤

  1. 节点类型识别:cel-python生成的AST会为不同表达式分配对应节点类,重点关注三类节点:
    • 原子条件:如a.name == 'Jon'这类比较表达式
    • 组合逻辑条件:&&、||这类二元逻辑运算表达式
    • 条件表达式?::仅处理其前置的布尔判断部分(即?:左侧的表达式)
  2. 递归遍历与压栈:
    • 遇到组合逻辑表达式(&&/||):先递归遍历左、右子节点,最后将当前组合表达式的字符串形式压入栈。
    • 遇到原子条件表达式:直接将其字符串形式压入栈。
    • 遇到条件表达式:仅递归遍历其条件判断节点,忽略分支结果部分。
  3. AST节点转字符串:实现辅助函数,将AST节点还原为可读的CEL表达式字符串,比如把属性访问节点转成a.name,字面量转成带引号的字符串等。

代码示例

from celparser import parse  # 按cel-python实际导入方式调整

# 根据cel-python实际AST节点结构适配,以下为模拟定义
class ASTNodeTypes:
    BinaryExpr = 'BinaryExpr'
    ConditionalExpr = 'ConditionalExpr'
    Attribute = 'Attribute'
    Identifier = 'Identifier'
    Literal = 'Literal'

class ASTWalker:
    def __init__(self):
        self.condition_stack = []

    def traverse(self, node):
        node_type = type(node).__name__
        # 处理条件表达式,仅遍历判断部分
        if node_type == ASTNodeTypes.ConditionalExpr:
            self.traverse(node.condition)
        # 处理二元逻辑表达式(&&、||)
        elif node_type == ASTNodeTypes.BinaryExpr and node.op in ('&&', '||'):
            self.traverse(node.left)
            self.traverse(node.right)
            expr_str = f"({self.node_to_str(node.left)} {node.op} {self.node_to_str(node.right)})"
            self.condition_stack.append(expr_str)
        # 处理原子比较条件(==、!=、>等)
        elif node_type == ASTNodeTypes.BinaryExpr and node.op in ('==', '!=', '>', '<', '>=', '<='):
            expr_str = f"{self.node_to_str(node.left)} {node.op} {self.node_to_str(node.right)}"
            self.condition_stack.append(expr_str)

    def node_to_str(self, node):
        node_type = type(node).__name__
        if node_type == ASTNodeTypes.Literal:
            return repr(node.value)  # 自动处理字符串引号、数字格式
        elif node_type == ASTNodeTypes.Attribute:
            return f"{self.node_to_str(node.obj)}.{node.attr}"
        elif node_type == ASTNodeTypes.Identifier:
            return node.name
        # 其他节点类型按需扩展
        else:
            return str(node)

# 使用示例
cel_expression = "(a.name == 'Jon' && (c.role == 'lead' || c.region == 'US')) ? b.salary : c.bonus"
ast_root = parse(cel_expression)
walker = ASTWalker()
walker.traverse(ast_root)

# 输出栈内容(顺序与预期一致)
for cond in walker.condition_stack:
    print(cond)

注意事项

  • 需根据cel-python库实际返回的AST节点结构,调整代码中的节点类型判断和属性访问方式(比如节点的op、left、right等属性名称可能略有不同)。
  • 若需严格匹配示例中不包含单独a.name == 'Jon'的结果,可在遍历原子条件时增加判断:仅当原子条件的父节点是组合表达式,且该组合表达式的另一个子节点也是组合表达式时才压栈。是否加入单独原子条件可根据实际需求调整。

内容的提问来源于stack exchange,提问作者Pradhiksha Kannan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:19:51