go: 如何高效的执行字符串算术表达式

2023-07-20 20:22:24 浏览数 (1)

数据系统常常需要在某个流节点执行简单的数据处理操作,例如单位的转换。

假设传入的值为input,在这一节点,输出的结果定义为 input * 8 / 1024, 为了方便运维同事修改,这个算式并未固化在代码中,而是使用string的方式定义在配置文件中。

如:

代码语言:javascript复制
rules:
    rule1:  "input * 8 / 1024"

要在go中动态的载入string的算式并计算,一种简单的方式是使用语法解析树。生成这个算式对应的解析树,然后执行它。

代码语言:javascript复制
import (
    "fmt"
    "go/parser"
    "go/token"
    "testing"
)
func TestMyAst(t *testing.T) {
    expression := "input*8/1024"

    // Create a file set for Go syntax tree
    fset := token.NewFileSet()

    // Parse the expression
    expr, err := parser.ParseExprFrom(fset, "", expression, 0)
    if err != nil {
        fmt.Printf("Failed to parse the expression: %vn", err)
        return
    }

    // Create the context environment for evaluation
    data := 169661
    context := map[string]int64{
        "input": int64(data),
    }
    // Evaluate the expression
    result := eval(expr, context)
    logrus.Infof("result:%v %v", data, result)
}

此时,将传入参数放在context中,然后调用eval执行。

parser.ParseExprFrom的结果可以缓存下来,因为它只和算式有关。以提高性能。

eval的代码如下,通过完善eval,可以利用解析树执行更复杂的功能。下面的eval函数可以方便的执行二元运算,满足 input * 8 / 1024 的计算需求。

代码语言:javascript复制
func eval(expr ast.Expr, context map[string]int64) int64 {
    switch e := expr.(type) {
    case *ast.Ident:
        // Handle identifiers (variables)
        value, ok := context[e.Name]
        if !ok {
            fmt.Printf("Undefined variable: %sn", e.Name)
            return 0
        }
        return value
    case *ast.BasicLit:
        // Handle basic literals (constants)
        value, _ := strconv.ParseInt(e.Value, 0, 64)
        return value
    case *ast.BinaryExpr:
        // Handle binary expressions
        left := eval(e.X, context)
        right := eval(e.Y, context)
        switch e.Op {
        case token.ADD:
            return left   right
        case token.SUB:
            return left - right
        case token.MUL:
            return left * right
        case token.QUO:
            return left / right
        default:
            fmt.Printf("Unsupported operator: %sn", e.Op)
            return 0
        }
    default:
        fmt.Printf("Unsupported expression type: %Tn", e)
        return 0
    }
}

0 人点赞