Line data Source code
1 : // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 : // of this source code is governed by a BSD-style license that can be found in 3 : // the LICENSE file. 4 : 5 : package dsl 6 : 7 : import ( 8 : "fmt" 9 : "go/token" 10 : "strconv" 11 : "strings" 12 : "sync/atomic" 13 : 14 : "github.com/cockroachdb/errors" 15 : ) 16 : 17 : // Predicate encodes conditional logic that yields a boolean. 18 : type Predicate[E any] interface { 19 : Evaluate(E) bool 20 : String() string 21 : } 22 : 23 : // Not returns a Predicate that negates the provided predicate. 24 0 : func Not[E any](p Predicate[E]) Predicate[E] { return not[E]{Predicate: p} } 25 : 26 : // And returns a Predicate that evaluates to true if all its operands evaluate 27 : // to true. 28 1 : func And[E any](preds ...Predicate[E]) Predicate[E] { return and[E](preds) } 29 : 30 : // Or returns a Predicate that evaluates to true if any of its operands evaluate 31 : // true. 32 0 : func Or[E any](preds ...Predicate[E]) Predicate[E] { return or[E](preds) } 33 : 34 : // OnIndex returns a Predicate that evaluates to true on its N-th call. 35 0 : func OnIndex[E any](n int32) *Index[E] { 36 0 : p := new(Index[E]) 37 0 : p.Int32.Store(n) 38 0 : return p 39 0 : } 40 : 41 : // Index is a Predicate that evaluates to true only on its N-th invocation. 42 : type Index[E any] struct { 43 : atomic.Int32 44 : } 45 : 46 : // String implements fmt.Stringer. 47 0 : func (p *Index[E]) String() string { 48 0 : return fmt.Sprintf("(OnIndex %d)", p.Int32.Load()) 49 0 : } 50 : 51 : // Evaluate implements Predicate. 52 0 : func (p *Index[E]) Evaluate(E) bool { return p.Int32.Add(-1) == -1 } 53 : 54 : type not[E any] struct { 55 : Predicate[E] 56 : } 57 : 58 0 : func (p not[E]) String() string { return fmt.Sprintf("(Not %s)", p.Predicate.String()) } 59 0 : func (p not[E]) Evaluate(e E) bool { return !p.Predicate.Evaluate(e) } 60 : 61 : type and[E any] []Predicate[E] 62 : 63 0 : func (p and[E]) String() string { 64 0 : var sb strings.Builder 65 0 : sb.WriteString("(And") 66 0 : for i := 0; i < len(p); i++ { 67 0 : sb.WriteRune(' ') 68 0 : sb.WriteString(p[i].String()) 69 0 : } 70 0 : sb.WriteRune(')') 71 0 : return sb.String() 72 : } 73 : 74 1 : func (p and[E]) Evaluate(e E) bool { 75 1 : ok := true 76 1 : for i := range p { 77 1 : ok = ok && p[i].Evaluate(e) 78 1 : } 79 1 : return ok 80 : } 81 : 82 : type or[E any] []Predicate[E] 83 : 84 0 : func (p or[E]) String() string { 85 0 : var sb strings.Builder 86 0 : sb.WriteString("(Or") 87 0 : for i := 0; i < len(p); i++ { 88 0 : sb.WriteRune(' ') 89 0 : sb.WriteString(p[i].String()) 90 0 : } 91 0 : sb.WriteRune(')') 92 0 : return sb.String() 93 : } 94 : 95 0 : func (p or[E]) Evaluate(e E) bool { 96 0 : ok := false 97 0 : for i := range p { 98 0 : ok = ok || p[i].Evaluate(e) 99 0 : } 100 0 : return ok 101 : } 102 : 103 0 : func parseNot[E any](p *Parser[Predicate[E]], s *Scanner) Predicate[E] { 104 0 : preds := parseVariadicPredicate(p, s) 105 0 : if len(preds) != 1 { 106 0 : panic(errors.Newf("dsl: not accepts exactly 1 argument, given %d", len(preds))) 107 : } 108 0 : return not[E]{Predicate: preds[0]} 109 : } 110 : 111 0 : func parseAnd[E any](p *Parser[Predicate[E]], s *Scanner) Predicate[E] { 112 0 : return And[E](parseVariadicPredicate[E](p, s)...) 113 0 : } 114 : 115 0 : func parseOr[E any](p *Parser[Predicate[E]], s *Scanner) Predicate[E] { 116 0 : return Or[E](parseVariadicPredicate[E](p, s)...) 117 0 : } 118 : 119 0 : func parseOnIndex[E any](p *Parser[Predicate[E]], s *Scanner) Predicate[E] { 120 0 : i, err := strconv.ParseInt(s.Consume(token.INT).Lit, 10, 32) 121 0 : if err != nil { 122 0 : panic(err) 123 : } 124 0 : s.Consume(token.RPAREN) 125 0 : return OnIndex[E](int32(i)) 126 : } 127 : 128 0 : func parseVariadicPredicate[E any](p *Parser[Predicate[E]], s *Scanner) (ret []Predicate[E]) { 129 0 : tok := s.Scan() 130 0 : for tok.Kind == token.LPAREN || tok.Kind == token.IDENT { 131 0 : ret = append(ret, p.ParseFromPos(s, tok)) 132 0 : tok = s.Scan() 133 0 : } 134 0 : assertTok(tok, token.RPAREN) 135 0 : return ret 136 : }