Skip to content

Commit 13e5296

Browse files
Unparse Expr values to strings (#1164)
* Unparse Expr values to strings * ExprToString test case
1 parent 3da6139 commit 13e5296

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

cel/io.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ func AstToParsedExpr(a *Ast) (*exprpb.ParsedExpr, error) {
9999
// Note, the conversion may not be an exact replica of the original expression, but will produce
100100
// a string that is semantically equivalent and whose textual representation is stable.
101101
func AstToString(a *Ast) (string, error) {
102-
return parser.Unparse(a.NativeRep().Expr(), a.NativeRep().SourceInfo())
102+
return ExprToString(a.NativeRep().Expr(), a.NativeRep().SourceInfo())
103+
}
104+
105+
// ExprToString converts an AST Expr node back to a string using macro call tracking metadata from
106+
// source info if any macros are encountered within the expression.
107+
func ExprToString(e ast.Expr, info *ast.SourceInfo) (string, error) {
108+
return parser.Unparse(e, info)
103109
}
104110

105111
// RefValueToValue converts between ref.Val and google.api.expr.v1alpha1.Value.

cel/io_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"google.golang.org/protobuf/proto"
2424

2525
"github.com/google/cel-go/checker/decls"
26+
celast "github.com/google/cel-go/common/ast"
27+
"github.com/google/cel-go/common/operators"
2628
"github.com/google/cel-go/common/types"
2729

2830
proto3pb "github.com/google/cel-go/test/proto3pb"
@@ -145,6 +147,52 @@ func TestAstToString(t *testing.T) {
145147
}
146148
}
147149

150+
func TestExprToString(t *testing.T) {
151+
stdEnv, err := NewEnv(EnableMacroCallTracking())
152+
if err != nil {
153+
t.Fatalf("NewEnv() failed: %v", err)
154+
}
155+
in := "[a, b].filter(i, (i > 0) ? (-i + 4) : i)"
156+
ast, iss := stdEnv.Parse(in)
157+
if iss.Err() != nil {
158+
t.Fatalf("stdEnv.Parse(%q) failed: %v", in, iss.Err())
159+
}
160+
expr, err := ExprToString(ast.NativeRep().Expr(), ast.NativeRep().SourceInfo())
161+
if err != nil {
162+
t.Fatalf("ExprToString(ast) failed: %v", err)
163+
}
164+
if expr != in {
165+
t.Errorf("got %v, wanted %v", expr, in)
166+
}
167+
168+
// Test sub-expression unparsing.
169+
navExpr := celast.NavigateAST(ast.NativeRep())
170+
condExpr := celast.MatchDescendants(navExpr, celast.FunctionMatcher(operators.Conditional))[0]
171+
want := `(i > 0) ? (-i + 4) : i`
172+
expr, err = ExprToString(condExpr, ast.NativeRep().SourceInfo())
173+
if err != nil {
174+
t.Fatalf("ExprToString(ast) failed: %v", err)
175+
}
176+
if expr != want {
177+
t.Errorf("got %v, wanted %v", expr, want)
178+
}
179+
180+
// Also passes with a nil source info, but only because the sub-expr doesn't contain macro calls.
181+
expr, err = ExprToString(condExpr, nil)
182+
if err != nil {
183+
t.Fatalf("ExprToString(ast) failed: %v", err)
184+
}
185+
if expr != want {
186+
t.Errorf("got %v, wanted %v", expr, want)
187+
}
188+
189+
// Fails do to missing macro information.
190+
_, err = ExprToString(ast.NativeRep().Expr(), nil)
191+
if err == nil {
192+
t.Error("ExprToString() succeeded, wanted error")
193+
}
194+
}
195+
148196
func TestAstToStringNil(t *testing.T) {
149197
expr, err := AstToString(nil)
150198
if err == nil || !strings.Contains(err.Error(), "unsupported expr") {

0 commit comments

Comments
 (0)