...

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

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

     1  // Copyright 2023 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  	"cuelang.org/go/cue/ast"
    19  	"cuelang.org/go/cue/token"
    20  )
    21  
    22  var (
    23  	handleExpr              *runner
    24  	handleResolver          *runner
    25  	handleDynamic           *runner
    26  	handlePatternConstraint *runner
    27  	handleComprehension     *runner
    28  	handleListLit           *runner
    29  	handleListVertex        *runner
    30  	handleDisjunction       *runner
    31  )
    32  
    33  // Use init to avoid a (spurious?) cyclic dependency in Go.
    34  func init() {
    35  	handleExpr = &runner{
    36  		name:      "Expr",
    37  		f:         processExpr,
    38  		completes: genericConjunct,
    39  	}
    40  	handleResolver = &runner{
    41  		name:      "Resolver",
    42  		f:         processResolver,
    43  		completes: genericConjunct,
    44  	}
    45  	handleDynamic = &runner{
    46  		name:      "Dynamic",
    47  		f:         processDynamic,
    48  		completes: fieldConjunct,
    49  	}
    50  	handlePatternConstraint = &runner{
    51  		name:      "PatternConstraint",
    52  		f:         processPatternConstraint,
    53  		completes: allTasksCompleted | fieldConjunctsKnown,
    54  	}
    55  	handleComprehension = &runner{
    56  		name:      "Comprehension",
    57  		f:         processComprehension,
    58  		completes: valueKnown | allTasksCompleted | fieldConjunctsKnown,
    59  	}
    60  	handleListLit = &runner{
    61  		name:      "ListLit",
    62  		f:         processListLit,
    63  		completes: fieldConjunct,
    64  		needs:     listTypeKnown,
    65  	}
    66  	handleListVertex = &runner{
    67  		name:      "ListVertex",
    68  		f:         processListVertex,
    69  		completes: fieldConjunct,
    70  		needs:     listTypeKnown,
    71  	}
    72  }
    73  
    74  // This file contains task runners (func(ctx *OpContext, t *task, mode runMode)).
    75  
    76  func processExpr(ctx *OpContext, t *task, mode runMode) {
    77  	x := t.x.(Expr)
    78  
    79  	state := combineMode(concreteKnown, mode)
    80  	v := ctx.evalState(x, state)
    81  	t.node.insertValueConjunct(t.env, v, t.id)
    82  }
    83  
    84  func processResolver(ctx *OpContext, t *task, mode runMode) {
    85  	r := t.x.(Resolver)
    86  
    87  	arc := r.resolve(ctx, oldOnly(0))
    88  	if arc == nil {
    89  		// TODO: yield instead?
    90  		return
    91  	}
    92  	// A reference that points to itself indicates equality. In that case
    93  	// we are done computing and we can return the arc as is.
    94  	ci, skip := t.node.markCycle(arc, t.env, r, t.id)
    95  	if skip {
    96  		return
    97  	}
    98  
    99  	c := MakeConjunct(t.env, t.x, ci)
   100  	t.node.scheduleVertexConjuncts(c, arc, ci)
   101  }
   102  
   103  func processDynamic(ctx *OpContext, t *task, mode runMode) {
   104  	n := t.node
   105  
   106  	field := t.x.(*DynamicField)
   107  
   108  	v := ctx.scalarValue(t, field.Key)
   109  	if v == nil {
   110  		return
   111  	}
   112  
   113  	if v.Concreteness() != Concrete {
   114  		n.addBottom(&Bottom{
   115  			Code: IncompleteError,
   116  			Err: ctx.NewPosf(pos(field.Key),
   117  				"key value of dynamic field must be concrete, found %v", v),
   118  		})
   119  		return
   120  	}
   121  
   122  	f := ctx.Label(field.Key, v)
   123  	// TODO: remove this restriction.
   124  	if f.IsInt() {
   125  		n.addErr(ctx.NewPosf(pos(field.Key), "integer fields not supported"))
   126  		return
   127  	}
   128  
   129  	c := MakeConjunct(t.env, field, t.id)
   130  	c.CloseInfo.cc = nil
   131  	n.insertArc(f, field.ArcType, c, t.id, true)
   132  }
   133  
   134  func processPatternConstraint(ctx *OpContext, t *task, mode runMode) {
   135  	n := t.node
   136  
   137  	field := t.x.(*BulkOptionalField)
   138  
   139  	// Note that the result may be a disjunction. Be sure to not take the
   140  	// default value as we want to retain the options of the disjunction.
   141  	v := ctx.evalState(field.Filter, require(0, scalarKnown))
   142  	if v == nil {
   143  		return
   144  	}
   145  
   146  	n.insertPattern(v, MakeConjunct(t.env, t.x, t.id))
   147  }
   148  
   149  func processComprehension(ctx *OpContext, t *task, mode runMode) {
   150  	n := t.node
   151  
   152  	y := &envYield{
   153  		envComprehension: t.comp,
   154  		leaf:             t.leaf,
   155  		env:              t.env,
   156  		id:               t.id,
   157  		expr:             t.x,
   158  	}
   159  
   160  	err := n.processComprehension(y, 0)
   161  	t.err = CombineErrors(nil, t.err, err)
   162  	t.comp.vertex.state.addBottom(err)
   163  }
   164  
   165  func processListLit(c *OpContext, t *task, mode runMode) {
   166  	n := t.node
   167  
   168  	l := t.x.(*ListLit)
   169  
   170  	n.updateCyclicStatus(t.id)
   171  
   172  	var ellipsis Node
   173  
   174  	index := int64(0)
   175  	hasComprehension := false
   176  	for j, elem := range l.Elems {
   177  		// TODO: Terminate early in case of runaway comprehension.
   178  
   179  		switch x := elem.(type) {
   180  		case *Comprehension:
   181  			err := c.yield(nil, t.env, x, 0, func(e *Environment) {
   182  				label, err := MakeLabel(x.Source(), index, IntLabel)
   183  				n.addErr(err)
   184  				index++
   185  				c := MakeConjunct(e, x.Value, t.id)
   186  				n.insertArc(label, ArcMember, c, t.id, true)
   187  			})
   188  			hasComprehension = true
   189  			if err != nil {
   190  				n.addBottom(err)
   191  				return
   192  			}
   193  
   194  		case *Ellipsis:
   195  			if j != len(l.Elems)-1 {
   196  				n.addErr(c.Newf("ellipsis must be last element in list"))
   197  				return
   198  			}
   199  
   200  			elem := x.Value
   201  			if elem == nil {
   202  				elem = &Top{}
   203  			}
   204  
   205  			c := MakeConjunct(t.env, elem, t.id)
   206  			pat := &BoundValue{
   207  				Op:    GreaterEqualOp,
   208  				Value: n.ctx.NewInt64(index, x),
   209  			}
   210  			n.insertPattern(pat, c)
   211  			ellipsis = x
   212  
   213  		default:
   214  			label, err := MakeLabel(x.Source(), index, IntLabel)
   215  			n.addErr(err)
   216  			index++
   217  			c := MakeConjunct(t.env, x, t.id)
   218  			n.insertArc(label, ArcMember, c, t.id, true)
   219  		}
   220  
   221  		if max := n.maxListLen; n.listIsClosed && int(index) > max {
   222  			n.invalidListLength(max, len(l.Elems), n.maxNode, l)
   223  			return
   224  		}
   225  	}
   226  
   227  	isClosed := ellipsis == nil
   228  
   229  	switch max := n.maxListLen; {
   230  	case int(index) < max:
   231  		if isClosed {
   232  			n.invalidListLength(int(index), max, l, n.maxNode)
   233  			return
   234  		}
   235  
   236  	case int(index) > max,
   237  		isClosed && !n.listIsClosed,
   238  		(isClosed == n.listIsClosed) && !hasComprehension:
   239  		n.maxListLen = int(index)
   240  		n.maxNode = l
   241  		n.listIsClosed = isClosed
   242  	}
   243  
   244  	n.updateListType(l, t.id, isClosed, ellipsis)
   245  }
   246  
   247  func processListVertex(c *OpContext, t *task, mode runMode) {
   248  	n := t.node
   249  
   250  	l := t.x.(*Vertex)
   251  
   252  	elems := l.Elems()
   253  	isClosed := l.IsClosedList()
   254  
   255  	// TODO: Share with code above.
   256  	switch max := n.maxListLen; {
   257  	case len(elems) < max:
   258  		if isClosed {
   259  			n.invalidListLength(len(elems), max, l, n.maxNode)
   260  			return
   261  		}
   262  
   263  	case len(elems) > max:
   264  		if n.listIsClosed {
   265  			n.invalidListLength(max, len(elems), n.maxNode, l)
   266  			return
   267  		}
   268  		n.listIsClosed = isClosed
   269  		n.maxListLen = len(elems)
   270  		n.maxNode = l
   271  
   272  	case isClosed:
   273  		n.listIsClosed = true
   274  		n.maxNode = l
   275  	}
   276  
   277  	for _, a := range elems {
   278  		if a.Conjuncts == nil {
   279  			c := MakeRootConjunct(nil, a)
   280  			n.insertArc(a.Label, ArcMember, c, CloseInfo{}, true)
   281  			continue
   282  		}
   283  		for _, c := range a.Conjuncts {
   284  			c.CloseInfo.cc = t.id.cc
   285  			n.insertArc(a.Label, ArcMember, c, t.id, true)
   286  		}
   287  	}
   288  
   289  	n.updateListType(l, t.id, isClosed, nil)
   290  }
   291  
   292  func (n *nodeContext) updateListType(list Expr, id CloseInfo, isClosed bool, ellipsis Node) {
   293  	m, ok := n.node.BaseValue.(*ListMarker)
   294  	if !ok {
   295  		m = &ListMarker{
   296  			IsOpen: true,
   297  		}
   298  		n.node.setValue(n.ctx, partial, m)
   299  	}
   300  	m.IsOpen = m.IsOpen && !isClosed
   301  
   302  	if ellipsis != nil {
   303  		if src, _ := ellipsis.Source().(ast.Expr); src != nil {
   304  			if m.Src == nil {
   305  				m.Src = src
   306  			} else {
   307  				m.Src = ast.NewBinExpr(token.AND, m.Src, src)
   308  			}
   309  		}
   310  	}
   311  
   312  	if n.kind != ListKind {
   313  		n.updateNodeType(ListKind, list, id)
   314  	}
   315  }
   316  

View as plain text