CEL-GO 实践 - 练习
Hello World
本文测试基于Google的CEL-GP CodeLab课程,按照所有语言的入门规则,先从一个"hello world"开始。
配置环境
func exercise1() {
fmt.Println("=== Exercise 1: Hello World ===\n")
env, err := cel.NewEnv()
if err != nil {
glog.Exitf("Error creating CEL environment: %v", err)
}
}
CEL应用程序根据环境来评估表达式,使用env,err := cel.NewEnv()
来配置标准环境。
可以通过cel.EnvOption来订制环境,这些选项能够禁用宏、声明自定义变量和函数等。
一个标注的CEl环境支持语言规范[^1] 当中定义的所有类型、运算符、函数和宏。
解析并检查表达式
当配置好环境之后,就可以解析和检查表达式。
// Compile, eval, profit!
func exercise1() {
fmt.Println("=== Exercise 1: Hello World ===\n")
// 创建标准环境
env, err := cel.NewEnv()
if err != nil {
glog.Exitf("Error creating CEL environment: %v", err)
}
// 检查表达式是否编译,返回AST、Issues (Issues定义用于检查解析和检查调用的错误详细信息的方法。)
ast,iss := env.Parse(`"Hello, World!"`)
// 是否存在语法错误
if iss.Err() != nil {
glog.Exitf("Error parsing expression: %v", iss.Err())
}
// 检查表达式的正确性
checked,iss := env.Check(ast)
// 检查是否存在语法错误
if iss.Err() != nil {
glog.Exitf("Error checking expression: %v", iss.Err())
}
// 检查表达式结果类型是否为String
if !proto.Equal(checked.ResultType(),decls.String) {
glog.Exitf("Error: expected result type to be string, got %v", checked.ResultType())
}
// TODO
}
env.Parse
和env.Check
返回的iss对象的值可能是错误的问题列表,如果iss.Err()
不为nil
,那说明表达式的语法或者语义有错误,程序无法继续运行。当表达式格式良好时,这两个调用的结果是一个可执行的cel.Ast
在这个codelab中解析和检查阶段被打包到当前文件的compile()方法中,它可以用于协助练习其余部分。
// compile将根据给定的表达式expr进行解析和检查
// ‘env’确定表达式执行的环境
// ‘exprType’ 匹配输入表达式结果的ResultType
func compile(env *cel.Env, expr string, exprType *exprpb.Type) *cel.Ast {
ast, iss := env.Compile(expr)
if iss.Err() != nil {
glog.Exit(iss.Err())
}
if !proto.Equal(ast.ResultType(), exprType) {
glog.Exitf(
"Got %v, wanted %v result type", ast.ResultType(), exprType)
}
fmt.Printf("%s\n\n", strings.ReplaceAll(expr, "\t", " "))
return ast
}
对表达式求值
一单表达式被解析并检查通过返回一个cel.Ast
,代表它可以转换成一个可以求值的表达式程序,其参数绑定和求值模式可以使用cel.ProgramOption
选项进行自定义。请注意,也可以使用cel.CheckedExprToAst
或celParsedExprToAst
函数从proto
中读取cel.Ast
一旦设计好了cel.Program
,就可以通过调用Eval
对表达式进行求值,求值结果将包含结果、求值详情、错误状态等信息。
func exercise1() {
fmt.Println("=== Exercise 1: Hello World ===\n")
// 创建标准环境
env, err := cel.NewEnv()
if err != nil {
glog.Exitf("Error creating CEL environment: %v", err)
}
// 检查表达式是否编译,返回AST、Issues (Issues定义用于检查解析和检查调用的错误详细信息的方法。)
ast,iss := env.Parse(`"Hello, World!"`)
// 是否存在语法错误
if iss.Err() != nil {
glog.Exitf("Error parsing expression: %v", iss.Err())
}
// 检查表达式的正确性
checked,iss := env.Check(ast)
// 检查是否存在语法错误
if iss.Err() != nil {
glog.Exitf("Error checking expression: %v", iss.Err())
}
// 检查表达式结果类型是否为String
if !proto.Equal(checked.ResultType(),decls.String) {
glog.Exitf("Error: expected result type to be string, got %v", checked.ResultType())
}
// 程序生成一个AST的可计算实例
program, err := env.Program(checked)
if err != nil {
glog.Exitf("Error creating program: %v", err)
}
// 在没有任何附加参数的情况下执行程序
out,_,err := eval(program, cel.NoVars())
if err != nil {
glog.Exitf("Error evaluating expression: %v", err)
}
// 打印结果
fmt.Printf("Result: %v\n", out)
}
运行这段代码
run it.
输出:
=== Exercise 1: Hello World ===
------ input ------
(interpreter.emptyActivation)
------ result ------
value: Hello, World! (types.String)
Result: Hello, World!
在函数中使用变量
大多数CEL应用程序会声明可以在表达式中引用的变量。变量的声明指定了名称和类型。变量的类型可以是CEL内置的类型、协议缓冲区的知名类型,或者任何protobuf消息类型,只要它的描述符也被提供给CEL。
在下面的例子当中,compile
和eval
helpers方法被作为evn.Parse()/env.Check()/program.Eval()
的替身而包含。这些辅助函数是用来打印练习的输入和输出的。
添加功能
在此开始使用exercise2的代码
注解
[^1]语言规范: https://github.com/google/cel-spec/blob/master/doc/langdef.md