Path: blob/main/components/proxy/plugins/logif/lang/lang.go
2501 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package lang56import (7"context"8"fmt"9"reflect"10"regexp"11"strconv"12"text/scanner"13"unicode"1415"github.com/PaesslerAG/gval"16)1718var Lang = gval.NewLanguage(19// Logic20gval.InfixShortCircuit("&&", func(lhs interface{}) (interface{}, bool) { return false, lhs == false }),21gval.InfixShortCircuit("||", func(lhs interface{}) (interface{}, bool) { return true, lhs == true }),2223gval.InfixBoolOperator("&&", func(lhs, rhs bool) (interface{}, error) { return lhs && rhs, nil }),24gval.InfixBoolOperator("||", func(lhs, rhs bool) (interface{}, error) { return lhs || rhs, nil }),2526gval.InfixBoolOperator("==", func(lhs, rhs bool) (interface{}, error) { return lhs == rhs, nil }),27gval.InfixBoolOperator("!=", func(lhs, rhs bool) (interface{}, error) { return lhs != rhs, nil }),2829// Arithmetic3031gval.InfixNumberOperator("==", func(lhs, rhs float64) (interface{}, error) { return lhs == rhs, nil }),32gval.InfixNumberOperator("!=", func(lhs, rhs float64) (interface{}, error) { return lhs != rhs, nil }),3334gval.InfixNumberOperator("<", func(lhs, rhs float64) (interface{}, error) { return lhs < rhs, nil }),35gval.InfixNumberOperator("<=", func(lhs, rhs float64) (interface{}, error) { return lhs <= rhs, nil }),36gval.InfixNumberOperator(">", func(lhs, rhs float64) (interface{}, error) { return lhs > rhs, nil }),37gval.InfixNumberOperator(">=", func(lhs, rhs float64) (interface{}, error) { return lhs >= rhs, nil }),3839// Text4041gval.InfixEvalOperator("~~", regEx),4243// Base4445gval.InfixOperator("==", func(lhs, rhs interface{}) (interface{}, error) { return reflect.DeepEqual(lhs, rhs), nil }),46gval.InfixOperator("!=", func(lhs, rhs interface{}) (interface{}, error) { return !reflect.DeepEqual(lhs, rhs), nil }),4748gval.PrefixExtension(scanner.Int, parseNumber),49gval.PrefixExtension(scanner.Float, parseNumber),50gval.PrefixExtension(scanner.RawString, parseString),5152gval.Constant("true", true),53gval.Constant("false", false),5455gval.Parentheses(),5657gval.Precedence("||", 20),58gval.Precedence("&&", 21),5960gval.Precedence("==", 40),61gval.Precedence("!=", 40),62gval.Precedence("~~", 40),6364gval.Precedence("<", 40),65gval.Precedence("<=", 40),66gval.Precedence(">", 40),67gval.Precedence(">=", 40),6869gval.PrefixMetaPrefix(scanner.Ident, parseIdent),70)7172var Fields []string7374func Compile(expression string) (gval.Evaluable, error) {75return Lang.NewEvaluable(expression)76}7778func Execute(eval gval.Evaluable, data map[string]interface{}) (interface{}, error) {79return eval(context.Background(), data)80}8182func parseString(c context.Context, p *gval.Parser) (gval.Evaluable, error) {83s, err := strconv.Unquote(p.TokenText())84if err != nil {85return nil, fmt.Errorf("could not parse string: %s", err)86}87return p.Const(s), nil88}8990func parseNumber(c context.Context, p *gval.Parser) (gval.Evaluable, error) {91n, err := strconv.ParseFloat(p.TokenText(), 64)92if err != nil {93return nil, err94}95return p.Const(n), nil96}9798func regEx(a, b gval.Evaluable) (gval.Evaluable, error) {99if !b.IsConst() {100return func(c context.Context, o interface{}) (interface{}, error) {101a, err := a.EvalString(c, o)102if err != nil {103return nil, err104}105b, err := b.EvalString(c, o)106if err != nil {107return nil, err108}109matched, err := regexp.MatchString(b, a)110return matched, err111}, nil112}113s, err := b.EvalString(context.TODO(), nil)114if err != nil {115return nil, err116}117regex, err := regexp.Compile(s)118if err != nil {119return nil, err120}121return func(c context.Context, v interface{}) (interface{}, error) {122s, err := a.EvalString(c, v)123if err != nil {124return nil, err125}126return regex.MatchString(s), nil127}, nil128}129130func parseIdent(c context.Context, p *gval.Parser) (call string, alternative func() (gval.Evaluable, error), err error) {131token := p.TokenText()132return token, func() (gval.Evaluable, error) {133tok := token134for {135scan := p.Scan()136curr := p.TokenText()137switch scan {138case '-':139fallthrough140case '>':141// Disambiguate greater than (>) operator to obtain correct parsing142r := p.Peek()143if unicode.IsLetter(r) || r == '_' || r == '-' || r == '[' || unicode.IsDigit(r) {144scan = p.Scan()145} else {146p.Camouflage("variable")147Fields = append(Fields, tok)148return p.Var(p.Const(tok)), nil149}150// Continue scanning the identifier151switch scan {152case scanner.Int:153fallthrough154case scanner.Ident:155fallthrough156case '[':157tok += curr + p.TokenText()158continue159default:160return nil, p.Expected("field", scanner.Ident)161}162case scanner.Int:163tok += p.TokenText()164165switch p.Scan() {166case ']':167tok += "]"168default:169return nil, p.Expected("array closing bracket", ']')170}171default:172p.Camouflage("variable", '>', '-')173Fields = append(Fields, tok)174return p.Var(p.Const(tok)), nil175}176}177}, nil178}179180181