Go标准库无eval,用go/parser+go/constant可安全求值纯数字四则表达式,但不支持变量、函数或逻辑运算;需前置校验输入合法性,支持变量时应改用antonmedv/expr等专用引擎。

Go 语言标准库没有内置表达式求值器,eval 类功能需手动实现或借助第三方包;直接用 go/parser + go/constant 可安全解析简单算术表达式,但不支持变量、函数调用或逻辑运算。
用 go/parser 和 go/constant 解析并计算纯数字表达式
这是最轻量、最安全的方案,适用于形如 "2 + 3 * 4" 或 "(10 - 2) / 2" 的整数/浮点数四则运算。它绕过字符串拼接执行,避免代码注入风险。
- 只支持常量表达式(不能含变量、标识符、函数调用)
- 需将输入包裹成合法 Go 表达式语句,例如加前缀
"const _ = " -
go/parser.ParseExpr解析 AST,再用go/constant.Int64Val或Float64Val提取结果 - 整数除法会自动截断,若需浮点结果,确保至少一个操作数带小数点(如
"6.0 / 4")
package mainimport ( "fmt" "go/ast" "go/parser" "go/token" "go/constant" )
func evalExpr(s string) (float64, error) { expr, err := parser.ParseExpr("const _ = " + s) if err != nil { return 0, err }
// 确保是二元运算或字面量 tv, err := ast.EvalExpr(token.NewFileSet(), nil, expr) if err != nil { return 0, err } switch tv.Kind() { case constant.Int: return constant.Int64Val(tv.Value), nil case constant.Float: return constant.Float64Val(tv.Value), nil default: return 0, fmt.Errorf("unsupported constant kind: %v", tv.Kind()) }}
func main() { result, _ := evalExpr("2 + 3 * 4") fmt.Println(result) // 14 }
遇到
invalid operation或undefined: xxx错误怎么办这类错误几乎都源于输入不是合法 Go 常量表达式:
立即学习“go语言免费学习笔记(深入)”;
-
undefined: x→ 表达式里写了变量名(如"x + 1"),而go/parser不做变量绑定 -
invalid operation: operator + not defined on string→ 输入含未加引号的字符串字面量(如"hello + world"),应只传数字和运算符 -
syntax error: unexpected newline→ 字符串含换行或注释,需提前清理(strings.TrimSpace+ 去掉//和/* */) - 空字符串或只有空格 →
ParseExpr直接返回语法错误,建议前置校验len(strings.TrimSpace(s)) == 0
需要支持变量时,别硬改 go/parser,换用 antonmedv/expr
go/parser 天然不支持运行时变量代入,强行在 AST 上做符号表替换极易出错且破坏类型安全。更现实的做法是引入轻量表达式引擎:
-
github.com/antonmedv/expr支持变量、布尔逻辑、比较、基础函数(len,round),语法接近 Go 但更宽松 - 它仍不执行任意代码(无
exec、无反射调用),安全性可控 - 性能比
go/parser略低,但对千次/秒以下的计算场景无感 - 注意:默认禁止访问结构体字段(如
user.Name),需显式启用expr.Env并传入允许的字段名列表
import (
"fmt"
"github.com/antonmedv/expr"
)
func main() {
env := map[string]interface{}{"a": 5, "b": 3}
code := "a * b + 2"
program, := expr.Compile(code)
output, := expr.Run(program, env)
fmt.Println(output) // 17
}
真正麻烦的不是写个计算器,而是决定「哪些东西必须禁止」:用户输入是否可能含恶意构造(如超深递归、超长数字、科学计数法溢出)?是否要限制执行时间?这些边界问题比语法解析本身更消耗精力。










