this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 505 lines 16 kB view raw
1// Copyright 2024 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 15package toposort 16 17// Ultimately we need to build a graph of field names. Those field 18// names can come from different constructions, such as: 19// 20// 1. Within a struct 21// 22// x: {z: _, y: _} 23// 24// When considering x, there should be a edge from z to y (written 25// from now on as (z -> y)). 26// 27// 2. Explicit unification 28// 29// x: {z: _, y: _} & {x: _, w: _} 30// 31// When considering x, we want no edges between the arguments of the 32// explicit unification operator '&'. There should only be edges (z 33// -> y) and (x -> w). Through explicit unifications, cycles of field 34// names can be introduced, e.g.: 35// 36// x: {z: _, y: _} & {y: _, w: _, z: _} 37// 38// 3. Embeddings 39// 40// b: {x: _, w: _} 41// a: {z: _, y: _} 42// c: { a, b } 43// 44// Here, a and b are embedded within c, and the order is important, so 45// at a minimum we want edges (z -> y), (x -> w), and (y -> x). Other 46// edges which don't introduce cycles are also acceptable (e.g. (z -> 47// x), (y -> w) etc). 48// 49// 4. Implicit unification 50// 51// c: {z: _, y: _} 52// c: {x: _, w: _} 53// 54// Here, like with embeddings, we choose that the source order is 55// important, and so we must have a minimum of (z -> y), (x -> w) and 56// (y -> x). 57// 58// Currently, the evaluator does not always provide enough information 59// for us to be able to reliably identify all implicit unifications, 60// especially where the ordering is enforced via some intermediate 61// node. For example: 62// 63// a: { 64// d: z: _ 65// d: t: _ 66// e: {x: _, w: _} 67// } 68// c: a.d & a.e 69// 70// Here, the information we get when sorting the fields of c (post 71// evaluation), is insufficient to be able to establish the edge (z -> 72// t), but it is sufficient to establish (x -> w). So in this case, we 73// end up only with the edge (x -> w), and so the other field names 74// fall back to lexicographical sorting. 75// 76// 5. Duplicates 77// 78// a: {z: _, y: _, z: int} 79// 80// b: c: _ 81// b: d: _ 82// b: c: int 83// 84// For a, we want to try to avoid adding an edge (y -> z), and for b 85// we want to try to avoid adding an edge (d -> c). So within a 86// regular struct, we do not add any additional edges when revisiting 87// a declaration previously visited within the same struct. Similarly, 88// for implicit unifications within the same file, we do not add any 89// additional edges when revisiting a declaration. 90// 91// In order to get as close as possible to the desired ordering, we 92// range over the Vertex's StructInfos, maintaining a list of Features 93// which must come before any new Features, i.e. a frontier. For this 94// to work, we need to sort the Vertex's StructInfos. Two approaches 95// are used: 96// 97// 1. A topological sorting of a Vertex's StructInfos. This is 98// effective for embeddings, and the relationship between embeddings 99// and regular fields. For example: 100// 101// a: {y: _, x: _} 102// b: {z: _, a} 103// 104// For b, a topological analysis will find that we can't enter the 105// StructInfo containing y and x, until after we've processed the 106// declaration of z. 107// 108// 2. However, even after a topological analysis, we'll often have 109// many root StructInfos. We order these by source position (not the 110// soure position of the StructInfo's StructLit itself, but of the 111// references (if any) that resolved to the StructInfo's StructLit), 112// then group them. If several StructInfos share the same position, 113// then they are batched together and considered to be explictly 114// unified. Then, consecutive batches of explicitly unified 115// StructInfos are grouped together. 116// 117// The result is that explicit unification is correctly 118// identified. E.g.: 119// 120// a: {x: _} 121// b: {z: int} 122// c: {y: >10} 123// o: a & b & c 124// 125// for o, the StructInfos corresponding to a, b and c will all be 126// grouped together in a single batch and considered to be explicitly 127// unified. Also, structInfos that correspond to the same position 128// (including no position) will be treated as explicity unified, and 129// so no weight will be given to their relative position within the 130// Vertex's slice of StructInfos. 131// 132// TODO: Switch if possible to finding if a struct has been unified 133// with a definition and as much as possible taking order from the 134// definition. In order words, if a cycle is only created by edges 135// that come from non-definitions, then we ignore those edges, and 136// thus don't end up dealing with a cycle. 137 138import ( 139 "fmt" 140 "maps" 141 "slices" 142 143 "cuelang.org/go/cue/token" 144 "cuelang.org/go/internal/core/adt" 145) 146 147type structMeta struct { 148 structInfo adt.StructInfo 149 pos token.Pos 150 151 // Should this struct be considered to be part of an explicit 152 // unification (e.g. x & y)? 153 isExplicit bool 154} 155 156func (sMeta *structMeta) String() string { 157 sl := sMeta.structInfo.StructLit 158 return fmt.Sprintf("{%p sl:%p %v (explicit? %v)}", 159 sMeta, sl, sMeta.pos, sMeta.isExplicit) 160} 161 162func (sm *structMeta) hasDynamic(dynFieldsMap map[*adt.DynamicField][]adt.Feature) bool { 163 for _, decl := range sm.structInfo.Decls { 164 if dynField, ok := decl.(*adt.DynamicField); ok { 165 if _, found := dynFieldsMap[dynField]; found { 166 return true 167 } 168 } 169 } 170 return false 171} 172 173// We need to order a Vertex's StructInfos. To do that, we want a 174// filename+position for every StructInfo. 175// 176// We build a map from every StructInfo's StructLit and all its decls 177// to a *structMeta, using the structLit's position. 178// 179// The StructLit in a StructInfo may directly appear in the parent's 180// arc conjuncts. In this case, the StructLit's position is the 181// correct position to use. But the StructLit may have been reached 182// via a FieldReference, or SelectorExpr or something else. We want 183// the position of the reference, and not the StructLit itself. E.g. 184// 185// a: {x: 5} 186// b: {y: 7} 187// c: b 188// c: a 189// 190// If we're ordering the fields of c, we want the position of b and a 191// on lines 3 and 4, not the StructLits which declare a and b on lines 192// 1 and 2. To do this, we walk through the Vertex's Arc's 193// conjuncts. If a conjunct's Field has been reached via some 194// resolver, then the conjunct's Refs will record that, and will allow 195// us to update the Field's position (and hence the StructLit's 196// position) to that of the reference. 197// 198// Additionally, we need to discover whether each StructLit is 199// included as a result of explicit unification (c: a & b), implicit 200// unification: 201// 202// c: b 203// c: a 204// 205// or embedding: 206// 207// c: { 208// b 209// a 210// } 211// 212// Explicit unification needs treating specially so to avoid incorrect 213// edges between the fields of the lhs and rhs of the &. To do this, 214// we look at the vertex's conjuncts. If a conjunct is a binary 215// expression &, then we look up the structMeta for the arguments to 216// the binary expression, and mark them as explicit unification. 217func analyseStructs(v *adt.Vertex, builder *GraphBuilder) []structMeta { 218 structInfos := v.Structs 219 // Note that it's important that nodeToStructMetas avoids duplicate entries, 220 // which cause significant slowness for some large configs. 221 nodeToStructMetas := make(map[adt.Node]map[*structMeta]bool) 222 // structMetaMap is heplful as we can't insert into a map unless we make it. 223 structMetaMap := func(node adt.Node) map[*structMeta]bool { 224 if m := nodeToStructMetas[node]; m != nil { 225 return m 226 } 227 m := make(map[*structMeta]bool) 228 nodeToStructMetas[node] = m 229 return m 230 } 231 232 structMetas := make([]structMeta, len(structInfos)) 233 234 // Create all the structMetas and map to them from a StructInfo's 235 // StructLit, and all its internal Decls. Initial attempt at 236 // recording a position, which will be correct only for direct use 237 // of literal structs in the calculation of vertex v. 238 metaIdx := 0 239 for _, s := range structInfos { 240 sl := s.StructLit 241 sMeta := &structMetas[metaIdx] 242 metaIdx++ 243 sMeta.structInfo = s 244 sMeta.pos = adt.Pos(sl) 245 246 structMetaMap(sl)[sMeta] = true 247 for _, decl := range sl.Decls { 248 structMetaMap(decl)[sMeta] = true 249 } 250 } 251 252 // If an arc's conjunct's Field is a node we care about, and it has 253 // been reached via resolution, then unwind those resolutions to 254 // uncover the position of the earliest reference. 255 for _, arc := range v.Arcs { 256 builder.EnsureNode(arc.Label) 257 for c := range arc.LeafConjuncts() { 258 field := c.Field() 259 debug("self arc conjunct field %p :: %T, expr %p :: %T (%v)\n", 260 field, field, c.Expr(), c.Expr(), c.Expr().Source()) 261 sMetas, found := nodeToStructMetas[field] 262 if !found { 263 continue 264 } 265 if src := field.Source(); src != nil { 266 pos := src.Pos() 267 for sMeta := range sMetas { 268 sMeta.pos = pos 269 } 270 } 271 refs := c.CloseInfo.CycleInfo.Refs 272 if refs == nil { 273 continue 274 } 275 debug(" ref %p :: %T (%v)\n", 276 refs.Ref, refs.Ref, refs.Ref.Source().Pos()) 277 for refs.Next != nil { 278 refs = refs.Next 279 debug(" ref %p :: %T (%v)\n", 280 refs.Ref, refs.Ref, refs.Ref.Source().Pos()) 281 } 282 maps.Insert(structMetaMap(refs.Ref), maps.All(sMetas)) 283 if pos := refs.Ref.Source().Pos(); pos.IsValid() { 284 for sMeta := range nodeToStructMetas[refs.Ref] { 285 sMeta.pos = pos 286 } 287 } 288 } 289 } 290 291 // Explore our own conjuncts, and the decls from our StructList, to 292 // find explicit unifications, and mark structMetas accordingly. 293 var worklist []adt.Expr 294 for c := range v.LeafConjuncts() { 295 debug("self conjunct field %p :: %T, expr %p :: %T\n", 296 c.Field(), c.Field(), c.Expr(), c.Expr()) 297 worklist = append(worklist, c.Expr()) 298 } 299 for _, si := range structInfos { 300 for _, decl := range si.StructLit.Decls { 301 if expr, ok := decl.(adt.Expr); ok { 302 worklist = append(worklist, expr) 303 } 304 } 305 } 306 307 for len(worklist) != 0 { 308 expr := worklist[0] 309 worklist = worklist[1:] 310 311 binExpr, ok := expr.(*adt.BinaryExpr) 312 if !ok || binExpr.Op != adt.AndOp { 313 continue 314 } 315 for _, expr := range []adt.Expr{binExpr.X, binExpr.Y} { 316 for sMeta := range nodeToStructMetas[expr] { 317 sMeta.isExplicit = true 318 debug(" now explicit: %v\n", sMeta) 319 } 320 } 321 worklist = append(worklist, binExpr.X, binExpr.Y) 322 } 323 324 return structMetas 325} 326 327// Find all fields which have been created as a result of successful 328// evaluation of a dynamic field name. 329func dynamicFieldsFeatures(v *adt.Vertex) map[*adt.DynamicField][]adt.Feature { 330 var m map[*adt.DynamicField][]adt.Feature 331 for _, arc := range v.Arcs { 332 for c := range arc.LeafConjuncts() { 333 if dynField, ok := c.Field().(*adt.DynamicField); ok { 334 if m == nil { 335 m = make(map[*adt.DynamicField][]adt.Feature) 336 } 337 m[dynField] = append(m[dynField], arc.Label) 338 } 339 } 340 } 341 return m 342} 343 344type structMetaBatch []*structMeta 345 346func (batch structMetaBatch) isExplicit() bool { 347 return len(batch) > 1 || (len(batch) == 1 && batch[0].isExplicit) 348} 349 350type structMetaBatches []structMetaBatch 351 352func (batchesPtr *structMetaBatches) appendBatch(batch structMetaBatch) { 353 if len(batch) == 0 { 354 return 355 } 356 batches := *batchesPtr 357 if l := len(batches); l == 0 { 358 *batchesPtr = append(batches, batch) 359 } else if prevBatch := batches[l-1]; batch.isExplicit() && 360 prevBatch.isExplicit() && 361 batch[0].pos.Filename() == prevBatch[0].pos.Filename() { 362 batches[l-1] = append(batches[l-1], batch...) 363 } else { 364 *batchesPtr = append(batches, batch) 365 } 366} 367 368type vertexFeatures struct { 369 builder *GraphBuilder 370 dynFieldsMap map[*adt.DynamicField][]adt.Feature 371} 372 373func (vf *vertexFeatures) compareStructMeta(a, b structMeta) int { 374 if c := a.pos.Compare(b.pos); c != 0 { 375 return c 376 } 377 aHasDyn := a.hasDynamic(vf.dynFieldsMap) 378 bHasDyn := b.hasDynamic(vf.dynFieldsMap) 379 switch { 380 case aHasDyn == bHasDyn: 381 return 0 382 case aHasDyn: 383 return 1 // gather dynamic fields at the end 384 default: 385 return -1 386 } 387} 388 389func VertexFeatures(ctx *adt.OpContext, v *adt.Vertex) []adt.Feature { 390 debug("\n*** V (%s %v %p) ***\n", v.Label.SelectorString(ctx), v.Label, v) 391 392 builder := NewGraphBuilder(!ctx.Config.SortFields) 393 dynFieldsMap := dynamicFieldsFeatures(v) 394 roots := analyseStructs(v, builder) 395 396 vf := &vertexFeatures{ 397 builder: builder, 398 dynFieldsMap: dynFieldsMap, 399 } 400 401 slices.SortFunc(roots, vf.compareStructMeta) 402 debug("roots: %v\n", roots) 403 404 var batches structMetaBatches 405 var batch structMetaBatch 406 for i := range roots { 407 root := &roots[i] 408 for range 1 + root.structInfo.Repeats { 409 if len(batch) == 0 || 410 (batch[0].pos == root.pos && !root.hasDynamic(dynFieldsMap)) { 411 batch = append(batch, root) 412 } else { 413 batches.appendBatch(batch) 414 batch = structMetaBatch{root} 415 } 416 } 417 } 418 batches.appendBatch(batch) 419 debug("batches: %v\n", batches) 420 421 var previous, next []adt.Feature 422 var previousBatch structMetaBatch 423 for _, batch := range batches { 424 explicit := batch.isExplicit() 425 if len(previousBatch) != 0 && 426 previousBatch[0].pos.Filename() != batch[0].pos.Filename() { 427 previous = nil 428 } 429 for _, root := range batch { 430 root.isExplicit = explicit 431 debug("starting root. Explicit unification? %v\n", explicit) 432 next = append(next, vf.addEdges(previous, root)...) 433 } 434 previous = next 435 next = nil 436 previousBatch = batch 437 } 438 439 debug("edges: %v\n", builder.edgesSet) 440 return builder.Build().Sort(ctx) 441} 442 443func (vf *vertexFeatures) addEdges(previous []adt.Feature, sMeta *structMeta) []adt.Feature { 444 debug("--- S %p (sl: %p) (explicit? %v) ---\n", 445 sMeta, sMeta.structInfo.StructLit, sMeta.isExplicit) 446 debug(" previous: %v\n", previous) 447 var next []adt.Feature 448 449 filename := sMeta.pos.Filename() 450 debug(" filename: %s (%v)\n", filename, sMeta.pos) 451 452 for i, decl := range sMeta.structInfo.Decls { 453 debug(" %p / %d: d (%p :: %T)\n", sMeta, i, decl, decl) 454 if bin, ok := decl.(*adt.BinaryExpr); ok { 455 debug(" binary expr: %p :: %T %v %p :: %T\n", 456 bin.X, bin.X, bin.Op, bin.Y, bin.Y) 457 } 458 459 currentLabel := adt.InvalidLabel 460 switch decl := decl.(type) { 461 case *adt.Field: 462 currentLabel = decl.Label 463 debug(" value %p :: %T (%v)\n", decl.Value, decl.Value, decl.Value) 464 if src := decl.Value.Source(); src != nil { 465 debug(" field value source: %v\n", src.Pos()) 466 } 467 case *adt.DynamicField: 468 // This struct contains a dynamic field. If that dynamic 469 // field was successfully evaluated into a field, then insert 470 // that field into this chain. 471 if labels := vf.dynFieldsMap[decl]; len(labels) > 0 { 472 currentLabel = labels[0] 473 vf.dynFieldsMap[decl] = labels[1:] 474 } 475 } 476 if currentLabel != adt.InvalidLabel { 477 debug(" label %v\n", currentLabel) 478 479 node, exists := vf.builder.nodesByFeature[currentLabel] 480 if exists && node.structMeta == sMeta { 481 // same field within the same structLit 482 debug(" skipping 1\n") 483 484 } else if exists && !sMeta.isExplicit && sMeta.pos.IsValid() && 485 node.structMeta != nil && 486 node.structMeta.pos.Filename() == filename { 487 // same field within the same file during implicit unification 488 debug(" skipping 2\n") 489 490 } else { 491 debug(" %v %v\n", node, exists) 492 node = vf.builder.EnsureNode(currentLabel) 493 node.structMeta = sMeta 494 next = append(next, currentLabel) 495 for _, prevLabel := range previous { 496 vf.builder.AddEdge(prevLabel, currentLabel) 497 } 498 previous = next 499 next = nil 500 } 501 } 502 } 503 504 return previous 505}