...

Source file src/cuelang.org/go/internal/core/adt/binop.go

Documentation: cuelang.org/go/internal/core/adt

     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(true)
   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  			// for x in expr { x }
   186  			forClause := func(src Expr) *Comprehension {
   187  				s := &StructLit{Decls: []Decl{
   188  					&FieldReference{UpCount: 1, Label: x},
   189  				}}
   190  				return &Comprehension{
   191  					Clauses: []Yielder{
   192  						&ForClause{
   193  							Value: x,
   194  							Src:   src,
   195  						},
   196  					},
   197  					Value: s,
   198  				}
   199  			}
   200  
   201  			list := &ListLit{
   202  				Elems: []Elem{
   203  					forClause(left),
   204  					forClause(right),
   205  				},
   206  			}
   207  
   208  			n := c.newInlineVertex(nil, nil, MakeConjunct(c.Env(0), list, c.ci))
   209  			n.CompleteArcs(c)
   210  
   211  			return n
   212  		}
   213  
   214  	case SubtractOp:
   215  		return c.Sub(c.Num(left, op), c.Num(right, op))
   216  
   217  	case MultiplyOp:
   218  		switch {
   219  		// float
   220  		case leftKind&NumKind != 0 && rightKind&NumKind != 0:
   221  			return c.Mul(c.Num(left, op), c.Num(right, op))
   222  
   223  		case leftKind == StringKind && rightKind == IntKind:
   224  			const as = "string multiplication"
   225  			return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as))))
   226  
   227  		case leftKind == IntKind && rightKind == StringKind:
   228  			const as = "string multiplication"
   229  			return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as))))
   230  
   231  		case leftKind == BytesKind && rightKind == IntKind:
   232  			const as = "bytes multiplication"
   233  			return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as))))
   234  
   235  		case leftKind == IntKind && rightKind == BytesKind:
   236  			const as = "bytes multiplication"
   237  			return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as))))
   238  
   239  		case leftKind == ListKind && rightKind == IntKind:
   240  			left, right = right, left
   241  			fallthrough
   242  
   243  		case leftKind == IntKind && rightKind == ListKind:
   244  			// TODO: get rid of list multiplication.
   245  
   246  			list := &ListLit{}
   247  			x := MakeIdentLabel(c, "x", "")
   248  
   249  			for i := c.uint64(left, "list multiplier"); i > 0; i-- {
   250  				st := &StructLit{Decls: []Decl{
   251  					&FieldReference{UpCount: 1, Label: x},
   252  				}}
   253  				list.Elems = append(list.Elems,
   254  					&Comprehension{
   255  						Clauses: []Yielder{
   256  							&ForClause{
   257  								Value: x,
   258  								Src:   right,
   259  							},
   260  						},
   261  						Value: st,
   262  					},
   263  				)
   264  			}
   265  			if err := c.Err(); err != nil {
   266  				return err
   267  			}
   268  
   269  			n := c.newInlineVertex(nil, nil, MakeConjunct(c.Env(0), list, c.ci))
   270  			n.CompleteArcs(c)
   271  
   272  			return n
   273  		}
   274  
   275  	case FloatQuotientOp:
   276  		if leftKind&NumKind != 0 && rightKind&NumKind != 0 {
   277  			return c.Quo(c.Num(left, op), c.Num(right, op))
   278  		}
   279  
   280  	case IntDivideOp:
   281  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   282  			return c.IntDiv(c.Num(left, op), c.Num(right, op))
   283  		}
   284  
   285  	case IntModuloOp:
   286  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   287  			return c.IntMod(c.Num(left, op), c.Num(right, op))
   288  		}
   289  
   290  	case IntQuotientOp:
   291  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   292  			return c.IntQuo(c.Num(left, op), c.Num(right, op))
   293  		}
   294  
   295  	case IntRemainderOp:
   296  		if leftKind&IntKind != 0 && rightKind&IntKind != 0 {
   297  			return c.IntRem(c.Num(left, op), c.Num(right, op))
   298  		}
   299  	}
   300  
   301  	return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)",
   302  		left, right, op, left.Kind(), right.Kind())
   303  }
   304  
   305  func cmpTonode(c *OpContext, op Op, r int) Value {
   306  	result := false
   307  	switch op {
   308  	case LessThanOp:
   309  		result = r == -1
   310  	case LessEqualOp:
   311  		result = r != 1
   312  	case EqualOp, AndOp:
   313  		result = r == 0
   314  	case NotEqualOp:
   315  		result = r != 0
   316  	case GreaterEqualOp:
   317  		result = r != -1
   318  	case GreaterThanOp:
   319  		result = r == 1
   320  	}
   321  	return c.newBool(result)
   322  }
   323  

View as plain text