blob: 3deda8a23325245ac667acc4b3234f69e64e89cf [file] [log] [blame] [edit]
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package parser declares an expression parser with support for macro
// expansion.
package parser
import (
"fmt"
"reflect"
"strconv"
"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/parser/gen"
structpb "github.com/golang/protobuf/ptypes/struct"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// Parse converts a source input a parsed expression.
// This function calls ParseWithMacros with AllMacros.
func Parse(source common.Source) (*exprpb.ParsedExpr, *common.Errors) {
return ParseWithMacros(source, AllMacros)
}
// ParseWithMacros converts a source input and macros set to a parsed expression.
func ParseWithMacros(source common.Source, macros []Macro) (*exprpb.ParsedExpr, *common.Errors) {
macroMap := make(map[string]Macro)
for _, m := range macros {
macroMap[m.MacroKey()] = m
}
p := parser{
errors: &parseErrors{common.NewErrors(source)},
helper: newParserHelper(source),
macros: macroMap,
}
e := p.parse(source.Content())
return &exprpb.ParsedExpr{
Expr: e,
SourceInfo: p.helper.getSourceInfo(),
}, p.errors.Errors
}
type parser struct {
gen.BaseCELVisitor
errors *parseErrors
helper *parserHelper
macros map[string]Macro
}
var _ gen.CELVisitor = (*parser)(nil)
func (p *parser) parse(expression string) *exprpb.Expr {
stream := antlr.NewInputStream(expression)
lexer := gen.NewCELLexer(stream)
prsr := gen.NewCELParser(antlr.NewCommonTokenStream(lexer, 0))
lexer.RemoveErrorListeners()
prsr.RemoveErrorListeners()
lexer.AddErrorListener(p)
prsr.AddErrorListener(p)
return p.Visit(prsr.Start()).(*exprpb.Expr)
}
// Visitor implementations.
func (p *parser) Visit(tree antlr.ParseTree) interface{} {
switch tree.(type) {
case *gen.StartContext:
return p.VisitStart(tree.(*gen.StartContext))
case *gen.ExprContext:
return p.VisitExpr(tree.(*gen.ExprContext))
case *gen.ConditionalAndContext:
return p.VisitConditionalAnd(tree.(*gen.ConditionalAndContext))
case *gen.ConditionalOrContext:
return p.VisitConditionalOr(tree.(*gen.ConditionalOrContext))
case *gen.RelationContext:
return p.VisitRelation(tree.(*gen.RelationContext))
case *gen.CalcContext:
return p.VisitCalc(tree.(*gen.CalcContext))
case *gen.LogicalNotContext:
return p.VisitLogicalNot(tree.(*gen.LogicalNotContext))
case *gen.MemberExprContext:
return p.VisitMemberExpr(tree.(*gen.MemberExprContext))
case *gen.PrimaryExprContext:
return p.VisitPrimaryExpr(tree.(*gen.PrimaryExprContext))
case *gen.SelectOrCallContext:
return p.VisitSelectOrCall(tree.(*gen.SelectOrCallContext))
case *gen.MapInitializerListContext:
return p.VisitMapInitializerList(tree.(*gen.MapInitializerListContext))
case *gen.NegateContext:
return p.VisitNegate(tree.(*gen.NegateContext))
case *gen.IndexContext:
return p.VisitIndex(tree.(*gen.IndexContext))
case *gen.UnaryContext:
return p.VisitUnary(tree.(*gen.UnaryContext))
case *gen.CreateListContext:
return p.VisitCreateList(tree.(*gen.CreateListContext))
case *gen.CreateMessageContext:
return p.VisitCreateMessage(tree.(*gen.CreateMessageContext))
case *gen.CreateStructContext:
return p.VisitCreateStruct(tree.(*gen.CreateStructContext))
}
text := "<<nil>>"
if tree != nil {
text = tree.GetText()
}
panic(fmt.Sprintf("unknown parsetree type: '%+v': %+v [%s]", reflect.TypeOf(tree), tree, text))
}
// Visit a parse tree produced by CELParser#start.
func (p *parser) VisitStart(ctx *gen.StartContext) interface{} {
return p.Visit(ctx.Expr())
}
// Visit a parse tree produced by CELParser#expr.
func (p *parser) VisitExpr(ctx *gen.ExprContext) interface{} {
result := p.Visit(ctx.GetE()).(*exprpb.Expr)
if ctx.GetOp() == nil {
return result
}
opID := p.helper.id(ctx.GetOp())
ifTrue := p.Visit(ctx.GetE1()).(*exprpb.Expr)
ifFalse := p.Visit(ctx.GetE2()).(*exprpb.Expr)
return p.globalCallOrMacro(opID, operators.Conditional, result, ifTrue, ifFalse)
}
// Visit a parse tree produced by CELParser#conditionalOr.
func (p *parser) VisitConditionalOr(ctx *gen.ConditionalOrContext) interface{} {
result := p.Visit(ctx.GetE()).(*exprpb.Expr)
if ctx.GetOps() == nil {
return result
}
b := newBalancer(p.helper, operators.LogicalOr, result)
for i, op := range ctx.GetOps() {
next := p.Visit(ctx.GetE1()[i]).(*exprpb.Expr)
opID := p.helper.id(op)
b.addTerm(opID, next)
}
return b.balance()
}
// Visit a parse tree produced by CELParser#conditionalAnd.
func (p *parser) VisitConditionalAnd(ctx *gen.ConditionalAndContext) interface{} {
result := p.Visit(ctx.GetE()).(*exprpb.Expr)
if ctx.GetOps() == nil {
return result
}
b := newBalancer(p.helper, operators.LogicalAnd, result)
for i, op := range ctx.GetOps() {
next := p.Visit(ctx.GetE1()[i]).(*exprpb.Expr)
opID := p.helper.id(op)
b.addTerm(opID, next)
}
return b.balance()
}
// Visit a parse tree produced by CELParser#relation.
func (p *parser) VisitRelation(ctx *gen.RelationContext) interface{} {
if ctx.Calc() != nil {
return p.Visit(ctx.Calc())
}
opText := ""
if ctx.GetOp() != nil {
opText = ctx.GetOp().GetText()
}
if op, found := operators.Find(opText); found {
lhs := p.Visit(ctx.Relation(0)).(*exprpb.Expr)
opID := p.helper.id(ctx.GetOp())
rhs := p.Visit(ctx.Relation(1)).(*exprpb.Expr)
return p.globalCallOrMacro(opID, op, lhs, rhs)
}
return p.reportError(ctx, "operator not found")
}
// Visit a parse tree produced by CELParser#calc.
func (p *parser) VisitCalc(ctx *gen.CalcContext) interface{} {
if ctx.Unary() != nil {
return p.Visit(ctx.Unary())
}
opText := ""
if ctx.GetOp() != nil {
opText = ctx.GetOp().GetText()
}
if op, found := operators.Find(opText); found {
lhs := p.Visit(ctx.Calc(0)).(*exprpb.Expr)
opID := p.helper.id(ctx.GetOp())
rhs := p.Visit(ctx.Calc(1)).(*exprpb.Expr)
return p.globalCallOrMacro(opID, op, lhs, rhs)
}
return p.reportError(ctx, "operator not found")
}
func (p *parser) VisitUnary(ctx *gen.UnaryContext) interface{} {
return p.helper.newLiteralString(ctx, "<<error>>")
}
// Visit a parse tree produced by CELParser#MemberExpr.
func (p *parser) VisitMemberExpr(ctx *gen.MemberExprContext) interface{} {
switch ctx.Member().(type) {
case *gen.PrimaryExprContext:
return p.VisitPrimaryExpr(ctx.Member().(*gen.PrimaryExprContext))
case *gen.SelectOrCallContext:
return p.VisitSelectOrCall(ctx.Member().(*gen.SelectOrCallContext))
case *gen.IndexContext:
return p.VisitIndex(ctx.Member().(*gen.IndexContext))
case *gen.CreateMessageContext:
return p.VisitCreateMessage(ctx.Member().(*gen.CreateMessageContext))
}
return p.reportError(ctx, "unsupported simple expression")
}
// Visit a parse tree produced by CELParser#LogicalNot.
func (p *parser) VisitLogicalNot(ctx *gen.LogicalNotContext) interface{} {
if len(ctx.GetOps())%2 == 0 {
return p.Visit(ctx.Member())
}
opID := p.helper.id(ctx.GetOps()[0])
target := p.Visit(ctx.Member()).(*exprpb.Expr)
return p.globalCallOrMacro(opID, operators.LogicalNot, target)
}
func (p *parser) VisitNegate(ctx *gen.NegateContext) interface{} {
if len(ctx.GetOps())%2 == 0 {
return p.Visit(ctx.Member())
}
opID := p.helper.id(ctx.GetOps()[0])
target := p.Visit(ctx.Member()).(*exprpb.Expr)
return p.globalCallOrMacro(opID, operators.Negate, target)
}
// Visit a parse tree produced by CELParser#SelectOrCall.
func (p *parser) VisitSelectOrCall(ctx *gen.SelectOrCallContext) interface{} {
operand := p.Visit(ctx.Member()).(*exprpb.Expr)
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil {
return p.helper.newExpr(ctx)
}
id := ctx.GetId().GetText()
if ctx.GetOpen() != nil {
opID := p.helper.id(ctx.GetOpen())
return p.receiverCallOrMacro(opID, id, operand, p.visitList(ctx.GetArgs())...)
}
return p.helper.newSelect(ctx.GetOp(), operand, id)
}
// Visit a parse tree produced by CELParser#PrimaryExpr.
func (p *parser) VisitPrimaryExpr(ctx *gen.PrimaryExprContext) interface{} {
switch ctx.Primary().(type) {
case *gen.NestedContext:
return p.VisitNested(ctx.Primary().(*gen.NestedContext))
case *gen.IdentOrGlobalCallContext:
return p.VisitIdentOrGlobalCall(ctx.Primary().(*gen.IdentOrGlobalCallContext))
case *gen.CreateListContext:
return p.VisitCreateList(ctx.Primary().(*gen.CreateListContext))
case *gen.CreateStructContext:
return p.VisitCreateStruct(ctx.Primary().(*gen.CreateStructContext))
case *gen.ConstantLiteralContext:
return p.VisitConstantLiteral(ctx.Primary().(*gen.ConstantLiteralContext))
}
return p.reportError(ctx, "invalid primary expression")
}
// Visit a parse tree produced by CELParser#Index.
func (p *parser) VisitIndex(ctx *gen.IndexContext) interface{} {
target := p.Visit(ctx.Member()).(*exprpb.Expr)
opID := p.helper.id(ctx.GetOp())
index := p.Visit(ctx.GetIndex()).(*exprpb.Expr)
return p.globalCallOrMacro(opID, operators.Index, target, index)
}
// Visit a parse tree produced by CELParser#CreateMessage.
func (p *parser) VisitCreateMessage(ctx *gen.CreateMessageContext) interface{} {
target := p.Visit(ctx.Member()).(*exprpb.Expr)
objID := p.helper.id(ctx.GetOp())
if messageName, found := p.extractQualifiedName(target); found {
entries := p.VisitIFieldInitializerList(ctx.GetEntries()).([]*exprpb.Expr_CreateStruct_Entry)
return p.helper.newObject(objID, messageName, entries...)
}
return p.helper.newExpr(objID)
}
// Visit a parse tree of field initializers.
func (p *parser) VisitIFieldInitializerList(ctx gen.IFieldInitializerListContext) interface{} {
if ctx == nil || ctx.GetFields() == nil {
return []*exprpb.Expr_CreateStruct_Entry{}
}
result := make([]*exprpb.Expr_CreateStruct_Entry, len(ctx.GetFields()))
for i, f := range ctx.GetFields() {
initID := p.helper.id(ctx.GetCols()[i])
value := p.Visit(ctx.GetValues()[i]).(*exprpb.Expr)
field := p.helper.newObjectField(initID, f.GetText(), value)
result[i] = field
}
return result
}
// Visit a parse tree produced by CELParser#IdentOrGlobalCall.
func (p *parser) VisitIdentOrGlobalCall(ctx *gen.IdentOrGlobalCallContext) interface{} {
identName := ""
if ctx.GetLeadingDot() != nil {
identName = "."
}
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil {
return p.helper.newExpr(ctx)
}
identName += ctx.GetId().GetText()
if ctx.GetOp() != nil {
opID := p.helper.id(ctx.GetOp())
return p.globalCallOrMacro(opID, identName, p.visitList(ctx.GetArgs())...)
}
return p.helper.newIdent(ctx.GetId(), identName)
}
// Visit a parse tree produced by CELParser#Nested.
func (p *parser) VisitNested(ctx *gen.NestedContext) interface{} {
return p.Visit(ctx.GetE())
}
// Visit a parse tree produced by CELParser#CreateList.
func (p *parser) VisitCreateList(ctx *gen.CreateListContext) interface{} {
listID := p.helper.id(ctx.GetOp())
return p.helper.newList(listID, p.visitList(ctx.GetElems())...)
}
// Visit a parse tree produced by CELParser#CreateStruct.
func (p *parser) VisitCreateStruct(ctx *gen.CreateStructContext) interface{} {
structID := p.helper.id(ctx.GetOp())
entries := []*exprpb.Expr_CreateStruct_Entry{}
if ctx.GetEntries() != nil {
entries = p.Visit(ctx.GetEntries()).([]*exprpb.Expr_CreateStruct_Entry)
}
return p.helper.newMap(structID, entries...)
}
// Visit a parse tree produced by CELParser#ConstantLiteral.
func (p *parser) VisitConstantLiteral(ctx *gen.ConstantLiteralContext) interface{} {
switch ctx.Literal().(type) {
case *gen.IntContext:
return p.VisitInt(ctx.Literal().(*gen.IntContext))
case *gen.UintContext:
return p.VisitUint(ctx.Literal().(*gen.UintContext))
case *gen.DoubleContext:
return p.VisitDouble(ctx.Literal().(*gen.DoubleContext))
case *gen.StringContext:
return p.VisitString(ctx.Literal().(*gen.StringContext))
case *gen.BytesContext:
return p.VisitBytes(ctx.Literal().(*gen.BytesContext))
case *gen.BoolFalseContext:
return p.VisitBoolFalse(ctx.Literal().(*gen.BoolFalseContext))
case *gen.BoolTrueContext:
return p.VisitBoolTrue(ctx.Literal().(*gen.BoolTrueContext))
case *gen.NullContext:
return p.VisitNull(ctx.Literal().(*gen.NullContext))
}
return p.reportError(ctx, "invalid literal")
}
// Visit a parse tree produced by CELParser#exprList.
func (p *parser) VisitExprList(ctx *gen.ExprListContext) interface{} {
if ctx == nil || ctx.GetE() == nil {
return []*exprpb.Expr{}
}
result := make([]*exprpb.Expr, len(ctx.GetE()))
for i, e := range ctx.GetE() {
exp := p.Visit(e).(*exprpb.Expr)
result[i] = exp
}
return result
}
// Visit a parse tree produced by CELParser#mapInitializerList.
func (p *parser) VisitMapInitializerList(ctx *gen.MapInitializerListContext) interface{} {
if ctx == nil || ctx.GetKeys() == nil {
return []*exprpb.Expr_CreateStruct_Entry{}
}
result := make([]*exprpb.Expr_CreateStruct_Entry, len(ctx.GetCols()))
for i, col := range ctx.GetCols() {
colID := p.helper.id(col)
key := p.Visit(ctx.GetKeys()[i]).(*exprpb.Expr)
value := p.Visit(ctx.GetValues()[i]).(*exprpb.Expr)
entry := p.helper.newMapEntry(colID, key, value)
result[i] = entry
}
return result
}
// Visit a parse tree produced by CELParser#Int.
func (p *parser) VisitInt(ctx *gen.IntContext) interface{} {
text := ctx.GetTok().GetText()
if ctx.GetSign() != nil {
text = ctx.GetSign().GetText() + text
}
i, err := strconv.ParseInt(text, 10, 64)
if err != nil {
return p.reportError(ctx, "invalid int literal")
}
return p.helper.newLiteralInt(ctx, i)
}
// Visit a parse tree produced by CELParser#Uint.
func (p *parser) VisitUint(ctx *gen.UintContext) interface{} {
text := ctx.GetTok().GetText()
// trim the 'u' designator included in the uint literal.
text = text[:len(text)-1]
i, err := strconv.ParseUint(text, 10, 64)
if err != nil {
return p.reportError(ctx, "invalid uint literal")
}
return p.helper.newLiteralUint(ctx, i)
}
// Visit a parse tree produced by CELParser#Double.
func (p *parser) VisitDouble(ctx *gen.DoubleContext) interface{} {
txt := ctx.GetTok().GetText()
if ctx.GetSign() != nil {
txt = ctx.GetSign().GetText() + txt
}
f, err := strconv.ParseFloat(txt, 64)
if err != nil {
return p.reportError(ctx, "invalid double literal")
}
return p.helper.newLiteralDouble(ctx, f)
}
// Visit a parse tree produced by CELParser#String.
func (p *parser) VisitString(ctx *gen.StringContext) interface{} {
s := p.unquote(ctx, ctx.GetText())
return p.helper.newLiteralString(ctx, s)
}
// Visit a parse tree produced by CELParser#Bytes.
func (p *parser) VisitBytes(ctx *gen.BytesContext) interface{} {
// TODO(ozben): Not sure if this is the right encoding.
b := []byte(p.unquote(ctx, ctx.GetTok().GetText()[1:]))
return p.helper.newLiteralBytes(ctx, b)
}
// Visit a parse tree produced by CELParser#BoolTrue.
func (p *parser) VisitBoolTrue(ctx *gen.BoolTrueContext) interface{} {
return p.helper.newLiteralBool(ctx, true)
}
// Visit a parse tree produced by CELParser#BoolFalse.
func (p *parser) VisitBoolFalse(ctx *gen.BoolFalseContext) interface{} {
return p.helper.newLiteralBool(ctx, false)
}
// Visit a parse tree produced by CELParser#Null.
func (p *parser) VisitNull(ctx *gen.NullContext) interface{} {
return p.helper.newLiteral(ctx,
&exprpb.Constant{
ConstantKind: &exprpb.Constant_NullValue{
NullValue: structpb.NullValue_NULL_VALUE}})
}
func (p *parser) visitList(ctx gen.IExprListContext) []*exprpb.Expr {
if ctx == nil {
return []*exprpb.Expr{}
}
return p.visitSlice(ctx.GetE())
}
func (p *parser) visitSlice(expressions []gen.IExprContext) []*exprpb.Expr {
if expressions == nil {
return []*exprpb.Expr{}
}
result := make([]*exprpb.Expr, len(expressions))
for i, e := range expressions {
ex := p.Visit(e).(*exprpb.Expr)
result[i] = ex
}
return result
}
func (p *parser) extractQualifiedName(e *exprpb.Expr) (string, bool) {
if e == nil {
return "", false
}
switch e.ExprKind.(type) {
case *exprpb.Expr_IdentExpr:
return e.GetIdentExpr().Name, true
case *exprpb.Expr_SelectExpr:
s := e.GetSelectExpr()
if prefix, found := p.extractQualifiedName(s.Operand); found {
return prefix + "." + s.Field, true
}
}
// TODO: Add a method to Source to get location from character offset.
location := p.helper.getLocation(e.Id)
p.reportError(location, "expected a qualified name")
return "", false
}
func (p *parser) unquote(ctx interface{}, value string) string {
text, err := unescape(value)
if err != nil {
p.reportError(ctx, err.Error())
return value
}
return text
}
func (p *parser) reportError(ctx interface{}, format string, args ...interface{}) *exprpb.Expr {
var location common.Location
switch ctx.(type) {
case common.Location:
location = ctx.(common.Location)
case antlr.Token, antlr.ParserRuleContext:
err := p.helper.newExpr(ctx)
location = p.helper.getLocation(err.Id)
}
err := p.helper.newExpr(ctx)
// Provide arguments to the report error.
p.errors.ReportError(location, format, args...)
return err
}
// ANTLR Parse listener implementations
func (p *parser) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
// TODO: Snippet
l := common.NewLocation(line, column)
p.errors.syntaxError(l, msg)
}
func (p *parser) ReportAmbiguity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, exact bool, ambigAlts *antlr.BitSet, configs antlr.ATNConfigSet) {
// Intentional
}
func (p *parser) ReportAttemptingFullContext(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, conflictingAlts *antlr.BitSet, configs antlr.ATNConfigSet) {
// Intentional
}
func (p *parser) ReportContextSensitivity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex, prediction int, configs antlr.ATNConfigSet) {
// Intentional
}
func (p *parser) globalCallOrMacro(exprID int64, function string, args ...*exprpb.Expr) *exprpb.Expr {
if expr, found := p.expandMacro(exprID, function, nil, args...); found {
return expr
}
return p.helper.newGlobalCall(exprID, function, args...)
}
func (p *parser) receiverCallOrMacro(exprID int64, function string, target *exprpb.Expr, args ...*exprpb.Expr) *exprpb.Expr {
if expr, found := p.expandMacro(exprID, function, target, args...); found {
return expr
}
return p.helper.newReceiverCall(exprID, function, target, args...)
}
func (p *parser) expandMacro(exprID int64, function string, target *exprpb.Expr, args ...*exprpb.Expr) (*exprpb.Expr, bool) {
macro, found := p.macros[makeMacroKey(function, len(args), target != nil)]
if !found {
macro, found = p.macros[makeVarArgMacroKey(function, target != nil)]
if !found {
return nil, false
}
}
eh := exprHelperPool.Get().(*exprHelper)
defer exprHelperPool.Put(eh)
eh.parserHelper = p.helper
eh.id = exprID
expr, err := macro.Expander()(eh, target, args)
if err != nil {
if err.Location != nil {
return p.reportError(err.Location, err.Message), true
}
return p.reportError(p.helper.getLocation(exprID), err.Message), true
}
return expr, true
}