cuelang.org/go@v0.13.0/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  			Node: c.vertex,
    36  		}
    37  	}
    38  	if right.Concreteness() > Concrete {
    39  		return &Bottom{
    40  			Code: IncompleteError,
    41  			Err:  c.Newf(msg, right, op),
    42  			Node: c.vertex,
    43  		}
    44  	}
    45  
    46  	if err := CombineErrors(c.src, left, right); err != nil {
    47  		return err
    48  	}
    49  
    50  	switch op {
    51  	case EqualOp:
    52  		switch {
    53  		case leftKind == NullKind && rightKind == NullKind:
    54  			return c.newBool(true)
    55  
    56  		case leftKind == NullKind || rightKind == NullKind:
    57  			return c.newBool(false)
    58  
    59  		case leftKind == BoolKind:
    60  			return c.newBool(c.BoolValue(left) == c.BoolValue(right))
    61  
    62  		case leftKind == StringKind:
    63  			// normalize?
    64  			return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right)))
    65  
    66  		case leftKind == BytesKind:
    67  			return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op)))
    68  
    69  		case leftKind&NumberKind != 0 && rightKind&NumberKind != 0:
    70  			// n := c.newNum()
    71  			return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X))
    72  
    73  		case leftKind == ListKind && rightKind == ListKind:
    74  			x := c.Elems(left)
    75  			y := c.Elems(right)
    76  			if len(x) != len(y) {
    77  				return c.newBool(false)
    78  			}
    79  			for i, e := range x {
    80  				a, _ := c.concrete(nil, e, op)
    81  				b, _ := c.concrete(nil, y[i], op)
    82  				if !test(c, EqualOp, a, b) {
    83  					return c.newBool(false)
    84  				}
    85  			}
    86  			return c.newBool(true)
    87  		}
    88  
    89  	case NotEqualOp:
    90  		switch {
    91  		case leftKind == NullKind && rightKind == NullKind:
    92  			return c.newBool(false)
    93  
    94  		case leftKind == NullKind || rightKind == NullKind:
    95  			return c.newBool(true)
    96  
    97  		case leftKind == BoolKind:
    98  			return c.newBool(c.boolValue(left, op) != c.boolValue(right, op))
    99  
   100  		case leftKind == StringKind:
   101  			// normalize?
   102  			return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right)))
   103  
   104  		case leftKind == BytesKind:
   105  			return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op)))
   106  
   107  		case leftKind&NumberKind != 0 && rightKind&NumberKind != 0:
   108  			// n := c.newNum()
   109  			return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X))
   110  
   111  		case leftKind == ListKind && rightKind == ListKind:
   112  			x := c.Elems(left)
   113  			y := c.Elems(right)
   114  			if len(x) != len(y) {
   115  				return c.newBool(true)
   116  			}
   117  			for i, e := range x {
   118  				a, _ := c.concrete(nil, e, op)
   119  				b, _ := c.concrete(nil, y[i], op)
   120  				if !test(c, EqualOp, a, b) {
   121  					return c.newBool(true)
   122  				}
   123  			}
   124  			return c.newBool(false)
   125  		}
   126  
   127  	case LessThanOp, LessEqualOp, GreaterEqualOp, GreaterThanOp:
   128  		switch {
   129  		case leftKind == StringKind && rightKind == StringKind:
   130  			// normalize?
   131  			return cmpTonode(c, op, strings.Compare(c.stringValue(left, op), c.stringValue(right, op)))
   132  
   133  		case leftKind == BytesKind && rightKind == BytesKind:
   134  			return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op)))
   135  
   136  		case leftKind&NumberKind != 0 && rightKind&NumberKind != 0:
   137  			// n := c.newNum(left, right)
   138  			return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X))
   139  		}
   140  
   141  	case BoolAndOp:
   142  		return c.newBool(c.boolValue(left, op) && c.boolValue(right, op))
   143  
   144  	case BoolOrOp:
   145  		return c.newBool(c.boolValue(left, op) || c.boolValue(right, op))
   146  
   147  	case MatchOp:
   148  		// if y.re == nil {
   149  		// 	// This really should not happen, but leave in for safety.
   150  		// 	b, err := Regexp.MatchString(str, x.str)
   151  		// 	if err != nil {
   152  		// 		return c.Errf(Src, "error parsing Regexp: %v", err)
   153  		// 	}
   154  		// 	return boolTonode(Src, b)
   155  		// }
   156  		return c.newBool(c.regexp(right).MatchString(c.stringValue(left, op)))
   157  
   158  	case NotMatchOp:
   159  		return c.newBool(!c.regexp(right).MatchString(c.stringValue(left, op)))
   160  
   161  	case AddOp:
   162  		switch {
   163  		case leftKind&NumberKind != 0 && rightKind&NumberKind != 0:
   164  			return c.Add(c.Num(left, op), c.Num(right, op))
   165  
   166  		case leftKind == StringKind && rightKind == StringKind:
   167  			return c.NewString(c.StringValue(left) + c.StringValue(right))
   168  
   169  		case leftKind == BytesKind && rightKind == BytesKind:
   170  			ba := c.bytesValue(left, op)
   171  			bb := c.bytesValue(right, op)
   172  			b := make([]byte, len(ba)+len(bb))
   173  			copy(b, ba)
   174  			copy(b[len(ba):], bb)
   175  			return c.newBytes(b)
   176  
   177  		case leftKind == ListKind && rightKind == ListKind:
   178  			return c.NewErrf("Addition of lists is superseded by list.Concat; see https://cuelang.org/e/v0.11-list-arithmetic")
   179  		}
   180  
   181  	case SubtractOp:
   182  		return c.Sub(c.Num(left, op), c.Num(right, op))
   183  
   184  	case MultiplyOp:
   185  		switch {
   186  		// float
   187  		case leftKind&NumberKind != 0 && rightKind&NumberKind != 0:
   188  			return c.Mul(c.Num(left, op), c.Num(right, op))
   189  
   190  		case leftKind == StringKind && rightKind == IntKind:
   191  			const as = "string multiplication"
   192  			return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as))))
   193  
   194  		case leftKind == IntKind && rightKind == StringKind:
   195  			const as = "string multiplication"
   196  			return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as))))
   197  
   198  		case leftKind == BytesKind && rightKind == IntKind:
   199  			const as = "bytes multiplication"
   200  			return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as))))
   201  
   202  		case leftKind == IntKind && rightKind == BytesKind:
   203  			const as = "bytes multiplication"
   204  			return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as))))
   205  
   206  		case leftKind == IntKind && rightKind == ListKind:
   207  			fallthrough
   208  		case leftKind == ListKind && rightKind == IntKind:
   209  			return c.NewErrf("Multiplication of lists is superseded by list.Repeat; see https://cuelang.org/e/v0.11-list-arithmetic")
   210  		}
   211  
   212  	case FloatQuotientOp:
   213  		if leftKind&NumberKind != 0 && rightKind&NumberKind != 0 {
   214  			return c.Quo(c.Num(left, op), c.Num(right, op))
   215  		}
   216  
   217  	case IntDivideOp:
   218  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   219  			return c.IntDiv(c.Num(left, op), c.Num(right, op))
   220  		}
   221  
   222  	case IntModuloOp:
   223  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   224  			return c.IntMod(c.Num(left, op), c.Num(right, op))
   225  		}
   226  
   227  	case IntQuotientOp:
   228  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   229  			return c.IntQuo(c.Num(left, op), c.Num(right, op))
   230  		}
   231  
   232  	case IntRemainderOp:
   233  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   234  			return c.IntRem(c.Num(left, op), c.Num(right, op))
   235  		}
   236  	}
   237  
   238  	return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)",
   239  		left, right, op, left.Kind(), right.Kind())
   240  }
   241  
   242  func cmpTonode(c *OpContext, op Op, r int) Value {
   243  	result := false
   244  	switch op {
   245  	case LessThanOp:
   246  		result = r == -1
   247  	case LessEqualOp:
   248  		result = r != 1
   249  	case EqualOp, AndOp:
   250  		result = r == 0
   251  	case NotEqualOp:
   252  		result = r != 0
   253  	case GreaterEqualOp:
   254  		result = r != -1
   255  	case GreaterThanOp:
   256  		result = r == 1
   257  	}
   258  	return c.newBool(result)
   259  }