github.com/solo-io/cue@v0.4.7/internal/core/adt/binop.go (about)

     1  // Copyright 2020 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package adt
    16  
    17  import (
    18  	"bytes"
    19  	"strings"
    20  )
    21  
    22  // BinOp handles all operations except AndOp and OrOp. This includes processing
    23  // unary comparators such as '<4' and '=~"foo"'.
    24  //
    25  // BinOp returns nil if not both left and right are concrete.
    26  func BinOp(c *OpContext, op Op, left, right Value) Value {
    27  	leftKind := left.Kind()
    28  	rightKind := right.Kind()
    29  
    30  	const msg = "non-concrete value '%v' to operation '%s'"
    31  	if left.Concreteness() > Concrete {
    32  		return &Bottom{
    33  			Code: IncompleteError,
    34  			Err:  c.Newf(msg, left, op),
    35  		}
    36  	}
    37  	if right.Concreteness() > Concrete {
    38  		return &Bottom{
    39  			Code: IncompleteError,
    40  			Err:  c.Newf(msg, right, op),
    41  		}
    42  	}
    43  
    44  	if err := CombineErrors(c.src, left, right); err != nil {
    45  		return err
    46  	}
    47  
    48  	switch op {
    49  	case EqualOp:
    50  		switch {
    51  		case leftKind == NullKind && rightKind == NullKind:
    52  			return c.newBool(true)
    53  
    54  		case leftKind == NullKind || rightKind == NullKind:
    55  			return c.newBool(false)
    56  
    57  		case leftKind == BoolKind:
    58  			return c.newBool(c.BoolValue(left) == c.BoolValue(right))
    59  
    60  		case leftKind == StringKind:
    61  			// normalize?
    62  			return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right)))
    63  
    64  		case leftKind == BytesKind:
    65  			return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op)))
    66  
    67  		case leftKind&NumKind != 0 && rightKind&NumKind != 0:
    68  			// n := c.newNum()
    69  			return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X))
    70  
    71  		case leftKind == ListKind && rightKind == ListKind:
    72  			x := c.Elems(left)
    73  			y := c.Elems(right)
    74  			if len(x) != len(y) {
    75  				return c.newBool(false)
    76  			}
    77  			for i, e := range x {
    78  				a, _ := c.Concrete(nil, e, op)
    79  				b, _ := c.Concrete(nil, y[i], op)
    80  				if !test(c, EqualOp, a, b) {
    81  					return c.newBool(false)
    82  				}
    83  			}
    84  			return c.newBool(true)
    85  		}
    86  
    87  	case NotEqualOp:
    88  		switch {
    89  		case leftKind == NullKind && rightKind == NullKind:
    90  			return c.newBool(false)
    91  
    92  		case leftKind == NullKind || rightKind == NullKind:
    93  			return c.newBool(true)
    94  
    95  		case leftKind == BoolKind:
    96  			return c.newBool(c.boolValue(left, op) != c.boolValue(right, op))
    97  
    98  		case leftKind == StringKind:
    99  			// normalize?
   100  			return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right)))
   101  
   102  		case leftKind == BytesKind:
   103  			return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op)))
   104  
   105  		case leftKind&NumKind != 0 && rightKind&NumKind != 0:
   106  			// n := c.newNum()
   107  			return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X))
   108  
   109  		case leftKind == ListKind && rightKind == ListKind:
   110  			x := c.Elems(left)
   111  			y := c.Elems(right)
   112  			if len(x) != len(y) {
   113  				return c.newBool(false)
   114  			}
   115  			for i, e := range x {
   116  				a, _ := c.Concrete(nil, e, op)
   117  				b, _ := c.Concrete(nil, y[i], op)
   118  				if !test(c, EqualOp, a, b) {
   119  					return c.newBool(true)
   120  				}
   121  			}
   122  			return c.newBool(false)
   123  		}
   124  
   125  	case LessThanOp, LessEqualOp, GreaterEqualOp, GreaterThanOp:
   126  		switch {
   127  		case leftKind == StringKind && rightKind == StringKind:
   128  			// normalize?
   129  			return cmpTonode(c, op, strings.Compare(c.stringValue(left, op), c.stringValue(right, op)))
   130  
   131  		case leftKind == BytesKind && rightKind == BytesKind:
   132  			return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op)))
   133  
   134  		case leftKind&NumKind != 0 && rightKind&NumKind != 0:
   135  			// n := c.newNum(left, right)
   136  			return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X))
   137  		}
   138  
   139  	case BoolAndOp:
   140  		return c.newBool(c.boolValue(left, op) && c.boolValue(right, op))
   141  
   142  	case BoolOrOp:
   143  		return c.newBool(c.boolValue(left, op) || c.boolValue(right, op))
   144  
   145  	case MatchOp:
   146  		// if y.re == nil {
   147  		// 	// This really should not happen, but leave in for safety.
   148  		// 	b, err := Regexp.MatchString(str, x.str)
   149  		// 	if err != nil {
   150  		// 		return c.Errf(Src, "error parsing Regexp: %v", err)
   151  		// 	}
   152  		// 	return boolTonode(Src, b)
   153  		// }
   154  		return c.newBool(c.regexp(right).MatchString(c.stringValue(left, op)))
   155  
   156  	case NotMatchOp:
   157  		return c.newBool(!c.regexp(right).MatchString(c.stringValue(left, op)))
   158  
   159  	case AddOp:
   160  		switch {
   161  		case leftKind&NumKind != 0 && rightKind&NumKind != 0:
   162  			return c.Add(c.Num(left, op), c.Num(right, op))
   163  
   164  		case leftKind == StringKind && rightKind == StringKind:
   165  			return c.NewString(c.StringValue(left) + c.StringValue(right))
   166  
   167  		case leftKind == BytesKind && rightKind == BytesKind:
   168  			ba := c.bytesValue(left, op)
   169  			bb := c.bytesValue(right, op)
   170  			b := make([]byte, len(ba)+len(bb))
   171  			copy(b, ba)
   172  			copy(b[len(ba):], bb)
   173  			return c.newBytes(b)
   174  
   175  		case leftKind == ListKind && rightKind == ListKind:
   176  			// TODO: get rid of list addition. Semantically it is somewhat
   177  			// unclear and, as it turns out, it is also hard to get right.
   178  			// Simulate addition with comprehensions now.
   179  			if err := c.Err(); err != nil {
   180  				return err
   181  			}
   182  
   183  			x := MakeIdentLabel(c, "x", "")
   184  
   185  			forClause := func(src Expr) *ForClause {
   186  				return &ForClause{
   187  					Value: x,
   188  					Src:   src,
   189  					Dst: &ValueClause{&StructLit{Decls: []Decl{
   190  						&FieldReference{UpCount: 1, Label: x},
   191  					}}},
   192  				}
   193  			}
   194  
   195  			list := &ListLit{
   196  				Elems: []Elem{
   197  					forClause(left),
   198  					forClause(right),
   199  				},
   200  			}
   201  
   202  			n := &Vertex{}
   203  			n.AddConjunct(MakeRootConjunct(c.Env(0), list))
   204  			n.Finalize(c)
   205  
   206  			return n
   207  		}
   208  
   209  	case SubtractOp:
   210  		return c.Sub(c.Num(left, op), c.Num(right, op))
   211  
   212  	case MultiplyOp:
   213  		switch {
   214  		// float
   215  		case leftKind&NumKind != 0 && rightKind&NumKind != 0:
   216  			return c.Mul(c.Num(left, op), c.Num(right, op))
   217  
   218  		case leftKind == StringKind && rightKind == IntKind:
   219  			const as = "string multiplication"
   220  			return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as))))
   221  
   222  		case leftKind == IntKind && rightKind == StringKind:
   223  			const as = "string multiplication"
   224  			return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as))))
   225  
   226  		case leftKind == BytesKind && rightKind == IntKind:
   227  			const as = "bytes multiplication"
   228  			return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as))))
   229  
   230  		case leftKind == IntKind && rightKind == BytesKind:
   231  			const as = "bytes multiplication"
   232  			return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as))))
   233  
   234  		case leftKind == ListKind && rightKind == IntKind:
   235  			left, right = right, left
   236  			fallthrough
   237  
   238  		case leftKind == IntKind && rightKind == ListKind:
   239  			// TODO: get rid of list multiplication.
   240  
   241  			list := &ListLit{}
   242  			x := MakeIdentLabel(c, "x", "")
   243  
   244  			for i := c.uint64(left, "list multiplier"); i > 0; i-- {
   245  				list.Elems = append(list.Elems,
   246  					&ForClause{
   247  						Value: x,
   248  						Src:   right,
   249  						Dst: &ValueClause{&StructLit{Decls: []Decl{
   250  							&FieldReference{UpCount: 1, Label: x},
   251  						}}},
   252  					},
   253  				)
   254  			}
   255  			if err := c.Err(); err != nil {
   256  				return err
   257  			}
   258  
   259  			n := &Vertex{}
   260  			n.AddConjunct(MakeRootConjunct(c.Env(0), list))
   261  			n.Finalize(c)
   262  
   263  			return n
   264  		}
   265  
   266  	case FloatQuotientOp:
   267  		if leftKind&NumKind != 0 && rightKind&NumKind != 0 {
   268  			return c.Quo(c.Num(left, op), c.Num(right, op))
   269  		}
   270  
   271  	case IntDivideOp:
   272  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   273  			return c.IntDiv(c.Num(left, op), c.Num(right, op))
   274  		}
   275  
   276  	case IntModuloOp:
   277  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   278  			return c.IntMod(c.Num(left, op), c.Num(right, op))
   279  		}
   280  
   281  	case IntQuotientOp:
   282  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   283  			return c.IntQuo(c.Num(left, op), c.Num(right, op))
   284  		}
   285  
   286  	case IntRemainderOp:
   287  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   288  			return c.IntRem(c.Num(left, op), c.Num(right, op))
   289  		}
   290  	}
   291  
   292  	return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)",
   293  		left, right, op, left.Kind(), right.Kind())
   294  }
   295  
   296  func cmpTonode(c *OpContext, op Op, r int) Value {
   297  	result := false
   298  	switch op {
   299  	case LessThanOp:
   300  		result = r == -1
   301  	case LessEqualOp:
   302  		result = r != 1
   303  	case EqualOp, AndOp:
   304  		result = r == 0
   305  	case NotEqualOp:
   306  		result = r != 0
   307  	case GreaterEqualOp:
   308  		result = r != -1
   309  	case GreaterThanOp:
   310  		result = r == 1
   311  	}
   312  	return c.newBool(result)
   313  }