this repo has no description
0
fork

Configure Feed

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

cue: add package

excludes builtin support

Change-Id: Ifad5a13b0db11e54ea2ffc64117e499b12c8e00e

+12428
+561
cue/ast.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "strconv" 20 + 21 + "cuelang.org/go/cue/ast" 22 + "cuelang.org/go/cue/build" 23 + "cuelang.org/go/cue/token" 24 + ) 25 + 26 + // insertFile inserts the given file at the root of the instance. 27 + // 28 + // The contents will be merged (unified) with any pre-existing value. In this 29 + // case an error may be reported, but only if the merge failed at the top-level. 30 + // Other errors will be recorded at the respective values in the tree. 31 + // 32 + // There should be no unresolved identifiers in file, meaning the Node field 33 + // of all identifiers should be set to a non-nil value. 34 + func (inst *Instance) insertFile(f *ast.File) error { 35 + // TODO: insert by converting to value first so that the trim command can 36 + // also remove top-level fields. 37 + // First process single file. 38 + v := newVisitor(inst.index, inst.inst, inst.rootStruct, inst.scope) 39 + v.astState.astMap[f] = inst.rootStruct 40 + result := v.walk(f) 41 + if isBottom(result) { 42 + return result.(*bottom) 43 + } 44 + 45 + for _, c := range v.comprehensions { 46 + inst.rootValue = mkBin(v.ctx(), token.NoPos, opUnify, inst.rootValue, c) 47 + } 48 + 49 + return nil 50 + } 51 + 52 + type astVisitor struct { 53 + *astState 54 + object *structLit 55 + comprehensions []*structComprehension 56 + 57 + inSelector int 58 + } 59 + 60 + func (v *astVisitor) ctx() *context { 61 + return v.astState.ctx 62 + } 63 + 64 + type astState struct { 65 + ctx *context 66 + *index 67 + inst *build.Instance 68 + 69 + litParser *litParser 70 + resolveRoot *structLit 71 + 72 + // make unique per level to avoid reuse of structs being an issue. 73 + astMap map[ast.Node]scope 74 + } 75 + 76 + func (s *astState) mapScope(n ast.Node) (m scope) { 77 + if m = s.astMap[n]; m == nil { 78 + m = newStruct(newNode(n)) 79 + s.astMap[n] = m 80 + } 81 + return m 82 + } 83 + 84 + func (s *astState) setScope(n ast.Node, v scope) { 85 + if m, ok := s.astMap[n]; ok && m != v { 86 + panic("already defined") 87 + } 88 + s.astMap[n] = v 89 + } 90 + 91 + func newVisitor(idx *index, inst *build.Instance, obj, resolveRoot *structLit) *astVisitor { 92 + ctx := idx.newContext() 93 + v := &astVisitor{ 94 + object: obj, 95 + } 96 + v.astState = &astState{ 97 + ctx: ctx, 98 + index: ctx.index, 99 + inst: inst, 100 + litParser: &litParser{ctx: ctx}, 101 + resolveRoot: resolveRoot, 102 + astMap: map[ast.Node]scope{}, 103 + } 104 + return v 105 + } 106 + 107 + func (v *astVisitor) error(n ast.Node, args ...interface{}) value { 108 + return v.mkErr(newNode(n), args...) 109 + } 110 + 111 + func (v *astVisitor) resolve(n *ast.Ident) value { 112 + ctx := v.ctx() 113 + label := v.label(n.Name, true) 114 + if r := v.resolveRoot; r != nil { 115 + if value, _ := r.lookup(v.ctx(), label); value != nil { 116 + return &selectorExpr{newExpr(n), 117 + &nodeRef{baseValue: newExpr(n), node: r}, label} 118 + } 119 + if v.inSelector > 0 { 120 + if p := getBuiltinShorthandPkg(ctx, n.Name); p != nil { 121 + return &nodeRef{baseValue: newExpr(n), node: p} 122 + } 123 + } 124 + } 125 + return nil 126 + } 127 + 128 + func (v *astVisitor) loadImport(imp *ast.ImportSpec) evaluated { 129 + ctx := v.ctx() 130 + val := lookupBuiltinPkg(ctx, imp) 131 + if !isBottom(val) { 132 + return val 133 + } 134 + path, err := strconv.Unquote(imp.Path.Value) 135 + if err != nil { 136 + return ctx.mkErr(newNode(imp), "illformed import spec") 137 + } 138 + bimp := v.inst.LookupImport(path) 139 + if bimp == nil { 140 + return ctx.mkErr(newNode(imp), "package %q not found", path) 141 + } 142 + impInst := v.index.loadInstance(bimp) 143 + return impInst.rootValue.evalPartial(ctx) 144 + } 145 + 146 + // We probably don't need to call Walk.s 147 + func (v *astVisitor) walk(astNode ast.Node) (value value) { 148 + switch n := astNode.(type) { 149 + case *ast.File: 150 + obj := v.object 151 + v1 := &astVisitor{ 152 + astState: v.astState, 153 + object: obj, 154 + } 155 + for _, e := range n.Decls { 156 + switch x := e.(type) { 157 + case *ast.EmitDecl: 158 + if v1.object.emit == nil { 159 + v1.object.emit = v1.walk(x.Expr) 160 + } else { 161 + v1.object.emit = mkBin(v.ctx(), token.NoPos, opUnify, v1.object.emit, v1.walk(x.Expr)) 162 + } 163 + default: 164 + v1.walk(e) 165 + } 166 + } 167 + for _, c := range v1.comprehensions { 168 + v.comprehensions = append(v.comprehensions, c) 169 + } 170 + value = obj 171 + 172 + case *ast.ImportDecl: 173 + for _, s := range n.Specs { 174 + v.walk(s) 175 + } 176 + 177 + case *ast.ImportSpec: 178 + val := v.loadImport(n) 179 + if !isBottom(val) { 180 + v.setScope(n, val.(*structLit)) 181 + } 182 + 183 + case *ast.StructLit: 184 + obj := v.mapScope(n).(*structLit) 185 + v1 := &astVisitor{ 186 + astState: v.astState, 187 + object: obj, 188 + } 189 + for _, e := range n.Elts { 190 + switch x := e.(type) { 191 + case *ast.EmitDecl: 192 + // Only allowed at top-level. 193 + v1.error(x, "emitting values is only allowed at top level") 194 + case *ast.Field, *ast.Alias: 195 + v1.walk(e) 196 + case *ast.ComprehensionDecl: 197 + v1.walk(x) 198 + } 199 + } 200 + value = obj 201 + for i, c := range v1.comprehensions { 202 + if i == 0 && obj.template == nil && len(obj.arcs) == 0 { 203 + value = c 204 + continue 205 + } 206 + value = mkBin(v.ctx(), token.NoPos, opUnify, value, c) 207 + } 208 + 209 + case *ast.ComprehensionDecl: 210 + yielder := &yield{baseValue: newExpr(n.Field.Value)} 211 + sc := &structComprehension{ 212 + baseValue: newDecl(n), 213 + clauses: wrapClauses(v, yielder, n.Clauses), 214 + } 215 + field := n.Field 216 + switch x := field.Label.(type) { 217 + case *ast.Interpolation: 218 + yielder.key = v.walk(x) 219 + yielder.value = v.walk(field.Value) 220 + 221 + case *ast.ExprLabel: 222 + yielder.key = v.walk(x.Label) 223 + yielder.value = v.walk(field.Value) 224 + 225 + case *ast.TemplateLabel: 226 + f := v.label(x.Ident.Name, true) 227 + 228 + sig := &params{} 229 + sig.add(f, &basicType{newNode(field.Label), stringKind}) 230 + template := &lambdaExpr{newExpr(field.Value), sig, nil} 231 + 232 + v.setScope(field, template) 233 + template.value = v.walk(field.Value) 234 + yielder.value = template 235 + sc.isTemplate = true 236 + 237 + case *ast.BasicLit, *ast.Ident: 238 + name, ok := ast.LabelName(x) 239 + if !ok { 240 + return v.error(x, "invalid field name: %v", x) 241 + } 242 + if name != "" { 243 + yielder.key = &stringLit{newNode(x), name} 244 + yielder.value = v.walk(field.Value) 245 + } 246 + 247 + default: 248 + panic("cue: unknown label type") 249 + } 250 + // yielder.key = v.walk(n.Field.Label) 251 + // yielder.value = v.walk(n.Field.Value) 252 + v.comprehensions = append(v.comprehensions, sc) 253 + 254 + case *ast.Field: 255 + switch x := n.Label.(type) { 256 + case *ast.Interpolation: 257 + yielder := &yield{baseValue: newNode(x)} 258 + sc := &structComprehension{ 259 + baseValue: newDecl(n), 260 + clauses: yielder, 261 + } 262 + yielder.key = v.walk(x) 263 + yielder.value = v.walk(n.Value) 264 + v.comprehensions = append(v.comprehensions, sc) 265 + 266 + case *ast.ExprLabel: 267 + yielder := &yield{baseValue: newNode(x.Label)} 268 + sc := &structComprehension{ 269 + baseValue: newDecl(n), 270 + clauses: yielder, 271 + } 272 + yielder.key = v.walk(x.Label) 273 + yielder.value = v.walk(n.Value) 274 + v.comprehensions = append(v.comprehensions, sc) 275 + 276 + case *ast.TemplateLabel: 277 + f := v.label(x.Ident.Name, true) 278 + 279 + sig := &params{} 280 + sig.add(f, &basicType{newNode(n.Label), stringKind}) 281 + template := &lambdaExpr{newExpr(n.Value), sig, nil} 282 + 283 + v.setScope(n, template) 284 + template.value = v.walk(n.Value) 285 + 286 + if v.object.template == nil { 287 + v.object.template = template 288 + } else { 289 + v.object.template = mkBin(v.ctx(), token.NoPos, opUnify, v.object.template, template) 290 + } 291 + 292 + case *ast.BasicLit, *ast.Ident: 293 + f, ok := v.nodeLabel(x) 294 + if !ok { 295 + return v.error(n.Label, "invalid field name: %v", n.Label) 296 + } 297 + if f != 0 { 298 + v.object.insertValue(v.ctx(), f, v.walk(n.Value)) 299 + } 300 + 301 + default: 302 + panic("cue: unknown label type") 303 + } 304 + 305 + case *ast.Alias: 306 + // parsed verbatim at reference. 307 + 308 + case *ast.LambdaExpr: 309 + sig := &params{} 310 + lambda := &lambdaExpr{newExpr(n), sig, nil} 311 + v.setScope(n, lambda) 312 + 313 + for _, p := range n.Params { 314 + f, _ := v.nodeLabel(p.Label) 315 + if p.Value != nil { 316 + sig.add(f, v.walk(p.Value)) 317 + } else { 318 + src := &ast.Ident{NamePos: p.Pos(), Name: "_"} 319 + sig.add(f, &top{baseValue: newExpr(src)}) 320 + } 321 + } 322 + lambda.value = v.walk(n.Expr) 323 + return lambda 324 + 325 + case *ast.ListComprehension: 326 + yielder := &yield{baseValue: newExpr(n.Expr)} 327 + lc := &listComprehension{ 328 + newExpr(n), 329 + wrapClauses(v, yielder, n.Clauses), 330 + } 331 + // we don't support key for lists (yet?) 332 + yielder.value = v.walk(n.Expr) 333 + return lc 334 + 335 + case *ast.ExprLabel: 336 + 337 + // Expressions 338 + case *ast.Ident: 339 + if n.Node == nil { 340 + if value = v.resolve(n); value != nil { 341 + break 342 + } 343 + 344 + switch n.Name { 345 + case "_": 346 + return &top{newExpr(n)} 347 + case "string": 348 + return &basicType{newExpr(n), stringKind} 349 + case "bytes": 350 + return &basicType{newExpr(n), bytesKind} 351 + case "bool": 352 + return &basicType{newExpr(n), boolKind} 353 + case "int": 354 + return &basicType{newExpr(n), intKind} 355 + case "float": 356 + return &basicType{newExpr(n), floatKind} 357 + case "number": 358 + return &basicType{newExpr(n), numKind} 359 + case "duration": 360 + return &basicType{newExpr(n), durationKind} 361 + 362 + case "len": 363 + return lenBuiltin 364 + } 365 + if r, ok := predefinedRanges[n.Name]; ok { 366 + return r 367 + } 368 + 369 + value = v.error(n, "reference %q not found", n.Name) 370 + break 371 + } 372 + 373 + if a, ok := n.Node.(*ast.Alias); ok { 374 + value = v.walk(a.Expr) 375 + break 376 + } 377 + 378 + label := v.label(n.Name, true) 379 + if n.Scope != nil { 380 + n2 := v.mapScope(n.Scope) 381 + value = &nodeRef{baseValue: newExpr(n), node: n2} 382 + value = &selectorExpr{newExpr(n), value, label} 383 + } else { 384 + n2 := v.mapScope(n.Node) 385 + value = &nodeRef{baseValue: newExpr(n), node: n2} 386 + } 387 + 388 + case *ast.BottomLit: 389 + value = v.error(n, "from source") 390 + 391 + case *ast.BadDecl: 392 + // nothing to do 393 + 394 + case *ast.BadExpr: 395 + value = v.error(n, "invalid expression") 396 + 397 + case *ast.BasicLit: 398 + value = v.litParser.parse(n) 399 + 400 + case *ast.Interpolation: 401 + if len(n.Elts) == 0 { 402 + return v.error(n, "invalid interpolation") 403 + } 404 + first, ok1 := n.Elts[0].(*ast.BasicLit) 405 + last, ok2 := n.Elts[len(n.Elts)-1].(*ast.BasicLit) 406 + if !ok1 || !ok2 { 407 + return v.error(n, "invalid interpolation") 408 + } 409 + if len(n.Elts) == 1 { 410 + value = v.walk(n.Elts[0]) 411 + break 412 + } 413 + lit := &interpolation{baseValue: newExpr(n), k: stringKind} 414 + value = lit 415 + quote, err := stringType(first.Value) 416 + if err != nil { 417 + return v.error(n, "invalid interpolation: %v", err) 418 + } 419 + if quote[0] == '\'' { 420 + return v.error(n, "interpolation not implemented for bytes: %v", err) 421 + } 422 + ws, err := wsPrefix(last.Value, quote) 423 + if err != nil { 424 + return v.error(n, "invalid interpolation: %v", err) 425 + } 426 + prefix := quote 427 + multi := len(quote) == 3 428 + p := v.litParser 429 + for i := 0; i < len(n.Elts); i += 2 { 430 + l, ok := n.Elts[i].(*ast.BasicLit) 431 + if !ok { 432 + return v.error(n, "invalid interpolation") 433 + } 434 + if err := p.init(l); err != nil { 435 + return v.error(n, "invalid interpolation: %v", err) 436 + } 437 + if i+1 < len(n.Elts) { 438 + x := p.parseString(prefix, `\(`, ws, multi, quote[0]) 439 + lit.parts = append(lit.parts, x, v.walk(n.Elts[i+1])) 440 + } else { 441 + x := p.parseString(prefix, quote, ws, multi, quote[0]) 442 + lit.parts = append(lit.parts, x) 443 + } 444 + prefix = ")" 445 + } 446 + 447 + case *ast.ListLit: 448 + list := &list{baseValue: newExpr(n)} 449 + for _, e := range n.Elts { 450 + list.a = append(list.a, v.walk(e)) 451 + } 452 + list.initLit() 453 + if n.Ellipsis != token.NoPos || n.Type != nil { 454 + list.len = &rangeLit{list.baseValue, list.len, &top{list.baseValue}} 455 + if n.Type != nil { 456 + list.typ = v.walk(n.Type) 457 + } 458 + } 459 + value = list 460 + 461 + case *ast.ParenExpr: 462 + value = v.walk(n.X) 463 + 464 + case *ast.SelectorExpr: 465 + v.inSelector++ 466 + value = &selectorExpr{ 467 + newExpr(n), 468 + v.walk(n.X), 469 + v.label(n.Sel.Name, true), 470 + } 471 + v.inSelector-- 472 + 473 + case *ast.IndexExpr: 474 + value = &indexExpr{newExpr(n), v.walk(n.X), v.walk(n.Index)} 475 + 476 + case *ast.SliceExpr: 477 + slice := &sliceExpr{baseValue: newExpr(n), x: v.walk(n.X)} 478 + if n.Low != nil { 479 + slice.lo = v.walk(n.Low) 480 + } 481 + if n.High != nil { 482 + slice.hi = v.walk(n.High) 483 + } 484 + value = slice 485 + 486 + case *ast.CallExpr: 487 + call := &callExpr{baseValue: newExpr(n), x: v.walk(n.Fun)} 488 + for _, a := range n.Args { 489 + call.args = append(call.args, v.walk(a)) 490 + } 491 + value = call 492 + 493 + case *ast.UnaryExpr: 494 + value = &unaryExpr{ 495 + newExpr(n), 496 + tokenMap[n.Op], 497 + v.walk(n.X), 498 + } 499 + 500 + case *ast.BinaryExpr: 501 + switch n.Op { 502 + case token.DISJUNCTION: 503 + value = makeDisjunction(v.ctx(), n, v.walk(n.X), v.walk(n.Y)) 504 + case token.RANGE: 505 + value = &rangeLit{ 506 + newExpr(n), 507 + v.walk(n.X), // from 508 + v.walk(n.Y), // to 509 + } 510 + default: 511 + value = &binaryExpr{ 512 + newExpr(n), 513 + tokenMap[n.Op], // op 514 + v.walk(n.X), // left 515 + v.walk(n.Y), // right 516 + } 517 + } 518 + 519 + // nothing to do 520 + // case *syntax.EmitDecl: 521 + default: 522 + // TODO: unhandled node. 523 + // value = ctx.mkErr(n, "unknown node type %T", n) 524 + panic(fmt.Sprintf("unimplemented %T", n)) 525 + 526 + } 527 + return value 528 + } 529 + 530 + func wrapClauses(v *astVisitor, y yielder, clauses []ast.Clause) yielder { 531 + for _, c := range clauses { 532 + if n, ok := c.(*ast.ForClause); ok { 533 + params := &params{} 534 + fn := &lambdaExpr{newExpr(n.Source), params, nil} 535 + v.setScope(n, fn) 536 + } 537 + } 538 + for i := len(clauses) - 1; i >= 0; i-- { 539 + switch n := clauses[i].(type) { 540 + case *ast.ForClause: 541 + fn := v.mapScope(n).(*lambdaExpr) 542 + fn.value = y 543 + 544 + key := "_" 545 + if n.Key != nil { 546 + key = n.Key.Name 547 + } 548 + f := v.label(key, true) 549 + fn.add(f, &basicType{newExpr(n.Key), stringKind | intKind}) 550 + 551 + f = v.label(n.Value.Name, true) 552 + fn.add(f, &top{}) 553 + 554 + y = &feed{newExpr(n.Source), v.walk(n.Source), fn} 555 + 556 + case *ast.IfClause: 557 + y = &guard{newExpr(n.Condition), v.walk(n.Condition), y} 558 + } 559 + } 560 + return y 561 + }
+289
cue/ast_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "testing" 20 + 21 + "cuelang.org/go/cue/errors" 22 + ) 23 + 24 + func TestCompile(t *testing.T) { 25 + testCases := []struct { 26 + in string 27 + out string 28 + }{{ 29 + in: `{ 30 + foo: 1, 31 + }`, 32 + out: "<0>{}", // emitted value, but no top-level fields 33 + }, { 34 + in: ` 35 + foo: 1 36 + `, 37 + out: "<0>{foo: 1}", 38 + }, { 39 + in: ` 40 + a: true 41 + b: 2K 42 + c: 4_5 43 + d: "abc" 44 + e: 3e2 // 3h1m2ss 45 + `, 46 + out: "<0>{a: true, b: 2000, c: 45, d: \"abc\", e: 3e+2}", 47 + }, { 48 + in: ` 49 + a: null 50 + b: true 51 + c: false 52 + `, 53 + out: "<0>{a: null, b: true, c: false}", 54 + }, { 55 + in: "" + 56 + `a: "\(4)", 57 + b: "one \(a) two \( a + c )", 58 + c: "one"`, 59 + out: `<0>{a: ""+4+"", b: "one "+<0>.a+" two "+(<0>.a + <0>.c)+"", c: "one"}`, 60 + }, { 61 + in: "" + 62 + `a: """ 63 + multi 64 + """, 65 + b: ''' 66 + hello world 67 + goodbye globe 68 + welcome back planet 69 + '''`, 70 + out: `<0>{a: "multi", b: 'hello world\ngoodbye globe\nwelcome back planet'}`, 71 + }, { 72 + in: "" + 73 + `a: """ 74 + multi \(4) 75 + """, 76 + b: """ 77 + hello \("world") 78 + goodbye \("globe") 79 + welcome back \("planet") 80 + """`, 81 + out: `<0>{a: "multi "+4+"", b: "hello "+"world"+"\ngoodbye "+"globe"+"\nwelcome back "+"planet"+""}`, 82 + }, { 83 + in: ` 84 + a: _ 85 + b: int 86 + c: float 87 + d: bool 88 + e: duration 89 + f: string 90 + `, 91 + out: "<0>{a: _, b: int, c: float, d: bool, e: duration, f: string}", 92 + }, { 93 + in: ` 94 + a: null 95 + b: true 96 + c: false 97 + `, 98 + out: "<0>{a: null, b: true, c: false}", 99 + }, { 100 + in: ` 101 + null: null 102 + true: true 103 + false: false 104 + `, 105 + out: "<0>{null: null, true: true, false: false}", 106 + }, { 107 + in: ` 108 + a: 1 + 2 109 + b: -2 - 3 110 + c: !d 111 + d: true 112 + `, 113 + out: "<0>{a: (1 + 2), b: (-2 - 3), c: !<0>.d, d: true}", 114 + }, { 115 + in: ` 116 + l0: 3*[int] 117 + l0: [1, 2, 3] 118 + l1: (0..5)*[string] 119 + l1: ["a", "b"] 120 + l2: (0..5)*[{ a: int }] 121 + l2: [{a: 1}, {a: 2, b: 3}] 122 + l3: (0..10)*[int] 123 + l3: [1, 2, 3, ...] 124 + l4: [1, 2, ...] 125 + l4: [...int] 126 + l5: [1, ...int] 127 + 128 + s1: ((0..6)*[int])[2:3] 129 + s2: [0,2,3][1:2] 130 + 131 + e0: (2..5)*[{}] 132 + e0: [{}] 133 + `, 134 + out: `<0>{l0: ((3 * [int]) & [1,2,3]), l1: (((0..5) * [string]) & ["a","b"]), l2: (((0..5) * [<1>{a: int}]) & [<2>{a: 1},<3>{a: 2, b: 3}]), l3: (((0..10) * [int]) & [1,2,3, ...]), l4: ([1,2, ...] & [, ...int]), l5: [1, ...int], s1: ((0..6) * [int])[2:3], s2: [0,2,3][1:2], e0: (((2..5) * [<4>{}]) & [<5>{}])}`, 135 + }, { 136 + in: ` 137 + a: 5 | "a" | true 138 + b c: { 139 + cc: { ccc: 3 } 140 + } 141 + d: true 142 + `, 143 + out: "<0>{a: (5 | \"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}", 144 + }, { 145 + in: ` 146 + a a: { b: a } // referencing ancestor nodes is legal. 147 + a b: a.a // do lookup before merging of nodes 148 + b: a.a // different node as a.a.b, as first node counts 149 + c: a // same node as b, as first node counts 150 + d: a["a"] 151 + `, 152 + out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`, 153 + }, { 154 + // bunch of aliases 155 + in: ` 156 + a1 = a2 157 + a2 = 5 158 + b: a1 159 + a3 = d 160 + c: { 161 + d: { 162 + r: a3 163 + } 164 + r: a3 165 + } 166 + d: { e: 4 } 167 + `, 168 + out: `<0>{b: 5, c: <1>{d: <2>{r: <0>.d}, r: <0>.d}, d: <3>{e: 4}}`, 169 + }, { 170 + // aliases with errors 171 + in: ` 172 + e1 = 1 173 + e1 = 2 174 + e1v: e1 175 + e2: "a" 176 + e2 = "a" 177 + `, 178 + out: "cannot have two aliases with the same name in the same scope:\n" + 179 + " test:3:3\n" + 180 + "cannot have alias and non-alias with the same name:\n" + 181 + " test:6:3\n" + 182 + "<0>{}", 183 + }, { 184 + in: ` 185 + a = b 186 + b: { 187 + c: a // reference to own root. 188 + } 189 + `, 190 + out: `<0>{b: <1>{c: <0>.b}}`, 191 + }, { 192 + in: ` 193 + a: { 194 + <name>: { n: name } 195 + k: 1 196 + } 197 + b: { 198 + <x>: { x: 0, y: 1 } 199 + v: {} 200 + } 201 + `, 202 + out: `<0>{a: <1>{<>: <2>(name: string)-><3>{n: <2>.name}, k: 1}, b: <4>{<>: <5>(x: string)-><6>{x: 0, y: 1}, v: <7>{}}}`, 203 + }, { 204 + in: ` 205 + a: { [k]: v for k, v in b if b.a < k } 206 + b: { 207 + a: 1 208 + b: 2 209 + c: 3 210 + } 211 + `, 212 + out: `<0>{a: { <1>for k, v in <0>.b if (<0>.b.a < <1>.k) yield (<1>.k): <1>.v }, b: <2>{a: 1, b: 2, c: 3}}`, 213 + }, { 214 + in: ` 215 + a: { [v]: v for k, v in b } 216 + b: { a: "aa", b: "bb", c: "cc" } 217 + `, 218 + out: `<0>{a: { <1>for k, v in <0>.b yield (<1>.v): <1>.v }, b: <2>{a: "aa", b: "bb", c: "cc"}}`, 219 + }, { 220 + in: ` 221 + a: [ v for _, v in b ] 222 + b: { a: 1, b: 2, c: 3 } 223 + `, 224 + out: `<0>{a: [ <1>for _, v in <0>.b yield (*nil*): <1>.v ], b: <2>{a: 1, b: 2, c: 3}}`, 225 + }, { 226 + in: ` 227 + a: 1..2 228 + b: 1..2..3 229 + c: "a".."b" 230 + d: (2+3)..(4+5) 231 + `, 232 + out: `<0>{a: (1..2), b: ((1..2)..3), c: ("a".."b"), d: ((2 + 3)..(4 + 5))}`, 233 + }} 234 + for _, tc := range testCases { 235 + t.Run("", func(t *testing.T) { 236 + ctx, root, errs := compileFileWithErrors(t, tc.in) 237 + buf := &bytes.Buffer{} 238 + if len(errs) > 0 { 239 + errors.Print(buf, errs) 240 + } 241 + buf.WriteString(debugStr(ctx, root)) 242 + got := buf.String() 243 + if got != tc.out { 244 + t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out) 245 + } 246 + }) 247 + } 248 + } 249 + 250 + func TestEmit(t *testing.T) { 251 + testCases := []struct { 252 + in string 253 + out string 254 + rw rewriteMode 255 + }{{ 256 + in: `"\(hello), \(world)!"` + ` 257 + hello: "Hello" 258 + world: "World" 259 + `, 260 + out: `""+<0>.hello+", "+<0>.world+"!"`, 261 + rw: evalRaw, 262 + }, { 263 + in: `"\(hello), \(world)!"` + ` 264 + hello: "Hello" 265 + world: "World" 266 + `, 267 + out: `"Hello, World!"`, 268 + rw: evalPartial, 269 + }, { 270 + // Ambiguous disjunction must cary over to emit value. 271 + in: `baz 272 + 273 + baz: { 274 + a: 8000 | 7080 275 + a: 7080 | int 276 + }`, 277 + out: `<0>{a: _|_((8000! | 7080! | 7080):ambiguous disjunction)}`, 278 + rw: evalFull, 279 + }} 280 + for _, tc := range testCases { 281 + t.Run("", func(t *testing.T) { 282 + ctx, root := compileFile(t, tc.in) 283 + v := testResolve(ctx, root.emit, tc.rw) 284 + if got := debugStr(ctx, v); got != tc.out { 285 + t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out) 286 + } 287 + }) 288 + } 289 + }
+975
cue/binop.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "math/big" 20 + "sort" 21 + "strings" 22 + "time" 23 + 24 + "cuelang.org/go/cue/token" 25 + "github.com/cockroachdb/apd" 26 + ) 27 + 28 + // binSrc returns a baseValue representing a binary expression of the given 29 + // values. 30 + func binSrc(pos token.Pos, op op, a, b value) baseValue { 31 + return baseValue{&computedSource{pos, op, a, b}} 32 + } 33 + 34 + type binFunc func(ctx *context, src source, op op, left, right evaluated) evaluated 35 + 36 + func binSwap(ctx *context, src source, op op, x, y evaluated) evaluated { 37 + return binOp(ctx, src, op, y, x) 38 + } 39 + 40 + func unify(ctx *context, src source, left, right evaluated) evaluated { 41 + return binOp(ctx, src, opUnify, left, right) 42 + } 43 + 44 + func binOp(ctx *context, src source, op op, left, right evaluated) (result evaluated) { 45 + if err := firstBottom(left, right); err != nil { 46 + return err 47 + } 48 + 49 + leftKind := left.kind() 50 + rightKind := right.kind() 51 + kind, invert := matchBinOpKind(op, leftKind, rightKind) 52 + if kind == bottomKind { 53 + return ctx.mkIncompatible(src, op, left, right) 54 + } 55 + if kind.hasReferences() { 56 + panic("unexpected references in expression") 57 + } 58 + if invert { 59 + left, right = right, left 60 + } 61 + if op != opUnify { 62 + v := left.binOp(ctx, src, op, right) // may return incomplete 63 + return v 64 + } 65 + 66 + // op == opUnify 67 + 68 + // Evaluated and recompose disjunctions. 69 + 70 + if dl, ok := left.(*disjunction); ok { 71 + if dr, ok := right.(*disjunction); ok { 72 + return dl.cross(ctx, src, op, dr) 73 + } 74 + return dl.expand(ctx, src, op, right, binOp) 75 + } else if dr, ok := right.(*disjunction); ok { 76 + return dr.expand(ctx, src, op, left, binSwap) 77 + } 78 + 79 + // TODO: unify type masks. 80 + if left == right { 81 + return left 82 + } 83 + if isTop(left) { 84 + return right 85 + } 86 + if isTop(right) { 87 + return left 88 + } 89 + 90 + // TODO: value may be incomplete if there is a cycle. Instead of an error 91 + // schedule an assert and return the atomic value, if applicable. 92 + v := left.binOp(ctx, src, op, right) 93 + if isBottom(v) { 94 + v := right.binOp(ctx, src, op, left) 95 + // Return the original failure if both fail, as this will result in 96 + // better error messages. 97 + if !isBottom(v) { 98 + return v 99 + } 100 + } 101 + return v 102 + } 103 + 104 + func (x *disjunction) expand(ctx *context, src source, op op, y evaluated, fn binFunc) evaluated { 105 + // disjunction & single value 106 + dn := disjunction{src.base(), make([]dValue, 0, len(x.values))} 107 + changed := false 108 + for _, v := range x.values { 109 + e := fn(ctx, src, op, v.val.(evaluated), y) 110 + changed = changed || e != v.val 111 + dn.add(ctx, e, v.ambiguous) 112 + } 113 + if !changed { 114 + return x 115 + } 116 + return dn.simplify(ctx, binSrc(src.Pos(), op, x, y)).(evaluated) 117 + } 118 + 119 + func (x *disjunction) cross(ctx *context, src source, op op, y *disjunction) evaluated { 120 + vr := []dValue{} 121 + idx := make([]int, 0, len(y.values)) 122 + for _, va := range x.values { 123 + for j, vb := range y.values { 124 + e := binOp(ctx, src, op, va.val.(evaluated), vb.val.(evaluated)) 125 + if isBottom(e) { 126 + continue 127 + } 128 + 129 + // TODO: filter using subsumption. 130 + 131 + // Mark crossing values as ambiguous. 132 + ambiguous := va.ambiguous || vb.ambiguous 133 + for xi, x := range idx { 134 + if x > j { 135 + vr[xi].ambiguous = true 136 + ambiguous = true 137 + } 138 + } 139 + vr = append(vr, dValue{e, ambiguous}) 140 + idx = append(idx, j) 141 + } 142 + } 143 + 144 + d := &disjunction{baseValue: src.base(), values: vr} 145 + return d.simplify(ctx, binSrc(src.Pos(), op, x, y)).(evaluated) 146 + } 147 + 148 + func (x *disjunction) binOp(ctx *context, src source, op op, other evaluated) evaluated { 149 + panic("unreachable: special-cased") 150 + } 151 + 152 + func (x *bottom) binOp(ctx *context, src source, op op, other evaluated) evaluated { 153 + panic("unreachable: special-cased") 154 + } 155 + 156 + func (x *top) binOp(ctx *context, src source, op op, other evaluated) evaluated { 157 + switch op { 158 + case opUnify: 159 + return other 160 + } 161 + src = mkBin(ctx, src.Pos(), op, x, other) 162 + return ctx.mkErr(src, "binary operation on non-ground top value") 163 + } 164 + 165 + func (x *basicType) binOp(ctx *context, src source, op op, other evaluated) evaluated { 166 + k := unifyType(x.kind(), other.kind()) 167 + switch y := other.(type) { 168 + case *basicType: 169 + switch op { 170 + // TODO: other types. 171 + case opUnify: 172 + if k&typeKinds != bottomKind { 173 + return &basicType{binSrc(src.Pos(), op, x, other), k & typeKinds} 174 + } 175 + } 176 + case *rangeLit: 177 + src = mkBin(ctx, src.Pos(), op, x, other) 178 + return ctx.mkErr(src, codeIncomplete, "%s with incomplete values", op) 179 + case *numLit: 180 + if op == opUnify { 181 + if k == y.k { 182 + return y 183 + } 184 + i := *y 185 + i.k = k 186 + return &i 187 + } 188 + src = mkBin(ctx, src.Pos(), op, x, other) 189 + return ctx.mkErr(src, codeIncomplete, "%s with incomplete values", op) 190 + default: 191 + if k&typeKinds != bottomKind { 192 + return other 193 + } 194 + } 195 + return ctx.mkIncompatible(src, op, x, other) 196 + } 197 + 198 + // unifyFrom determines the maximum value of a and b. 199 + func unifyFrom(ctx *context, src source, a, b evaluated) evaluated { 200 + if a.kind().isGround() && b.kind().isGround() { 201 + if leq(ctx, src, a, b) { 202 + return b 203 + } 204 + return a 205 + } 206 + if isTop(a) { 207 + return b 208 + } 209 + if isTop(b) { 210 + return a 211 + } 212 + if x, ok := a.(*rangeLit); ok { 213 + return unifyFrom(ctx, src, x.from.(evaluated), b) 214 + } 215 + if x, ok := b.(*rangeLit); ok { 216 + return unifyFrom(ctx, src, a, x.from.(evaluated)) 217 + } 218 + src = mkBin(ctx, src.Pos(), opUnify, a, b) 219 + return ctx.mkErr(src, "incompatible types %v and %v", a.kind(), b.kind()) 220 + } 221 + 222 + // unifyTo determines the minimum value of a and b. 223 + func unifyTo(ctx *context, src source, a, b evaluated) evaluated { 224 + if a.kind().isGround() && b.kind().isGround() { 225 + if leq(ctx, src, a, b) { 226 + return a 227 + } 228 + return b 229 + } 230 + if isTop(a) { 231 + return b 232 + } 233 + if isTop(b) { 234 + return a 235 + } 236 + if x, ok := a.(*rangeLit); ok { 237 + return unifyTo(ctx, src, x.to.(evaluated), b) 238 + } 239 + if x, ok := b.(*rangeLit); ok { 240 + return unifyTo(ctx, src, a, x.to.(evaluated)) 241 + } 242 + src = mkBin(ctx, src.Pos(), opUnify, a, b) 243 + return ctx.mkErr(src, "incompatible types %v and %v", a.kind(), b.kind()) 244 + } 245 + 246 + func errInRange(ctx *context, pos token.Pos, r *rangeLit, v evaluated) *bottom { 247 + if pos == token.NoPos { 248 + pos = r.Pos() 249 + } 250 + const msgInRange = "value %v not in range %v" 251 + e := mkBin(ctx, pos, opUnify, r, v) 252 + return ctx.mkErr(e, msgInRange, v.strValue(), debugStr(ctx, r)) 253 + } 254 + 255 + func (x *rangeLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 256 + combine := func(x, y evaluated) evaluated { 257 + if _, ok := x.(*numLit); !ok { 258 + return x 259 + } 260 + if _, ok := y.(*numLit); !ok { 261 + return y 262 + } 263 + return binOp(ctx, src, op, x, y) 264 + } 265 + from := x.from.(evaluated) 266 + to := x.to.(evaluated) 267 + newSrc := mkBin(ctx, src.Pos(), op, x, other) 268 + switch op { 269 + case opUnify: 270 + k := unifyType(x.kind(), other.kind()) 271 + if k&comparableKind != bottomKind { 272 + switch y := other.(type) { 273 + case *basicType: 274 + from := unify(ctx, src, x.from.(evaluated), y) 275 + to := unify(ctx, src, x.to.(evaluated), y) 276 + if from == x.from && to == x.to { 277 + return x 278 + } 279 + return &rangeLit{newSrc.base(), from, to} 280 + case *rangeLit: 281 + from := unifyFrom(ctx, src, x.from.(evaluated), y.from.(evaluated)) 282 + to := unifyTo(ctx, src, x.to.(evaluated), y.to.(evaluated)) 283 + if from.kind().isGround() && to.kind().isGround() && !leq(ctx, src, from, to) { 284 + r1 := debugStr(ctx, x) 285 + r2 := debugStr(ctx, y) 286 + return ctx.mkErr(newSrc, "non-overlapping ranges %s and %s", r1, r2) 287 + } 288 + return ctx.manifest(&rangeLit{newSrc.base(), from, to}) 289 + 290 + case *numLit: 291 + if !leq(ctx, src, x.from.(evaluated), y) || !leq(ctx, src, y, x.to.(evaluated)) { 292 + return errInRange(ctx, src.Pos(), x, y) 293 + } 294 + if y.k != k { 295 + n := *y 296 + n.k = k 297 + return &n 298 + } 299 + return other 300 + 301 + case *durationLit, *stringLit: 302 + if !leq(ctx, src, x.from.(evaluated), y) || !leq(ctx, src, y, x.to.(evaluated)) { 303 + return errInRange(ctx, src.Pos(), x, y) 304 + } 305 + return other 306 + } 307 + } 308 + // See https://en.wikipedia.org/wiki/Interval_arithmetic. 309 + case opAdd: 310 + switch x.kind() & typeKinds { 311 + case stringKind: 312 + if !x.from.kind().isGround() || !x.to.kind().isGround() { 313 + // TODO: return regexp 314 + return ctx.mkErr(newSrc, codeIncomplete, "cannot add incomplete values") 315 + } 316 + combine := func(x, y evaluated) evaluated { 317 + if _, ok := x.(*basicType); ok { 318 + return ctx.mkErr(newSrc, "adding string to non-concrete type") 319 + } 320 + if _, ok := y.(*basicType); ok { 321 + return x 322 + } 323 + return binOp(ctx, src, opAdd, x, y) 324 + } 325 + return &rangeLit{ 326 + baseValue: binSrc(src.Pos(), op, x, other), 327 + from: combine(minNum(from), minNum(other)), 328 + to: combine(maxNum(to), maxNum(other)), 329 + } 330 + 331 + case intKind, numKind, floatKind: 332 + return &rangeLit{ 333 + baseValue: binSrc(src.Pos(), op, x, other), 334 + from: combine(minNum(from), minNum(other)), 335 + to: combine(maxNum(to), maxNum(other)), 336 + } 337 + 338 + default: 339 + return ctx.mkErrUnify(src, x, other) 340 + } 341 + 342 + case opSub: 343 + return &rangeLit{ 344 + baseValue: binSrc(src.Pos(), op, x, other), 345 + from: combine(minNum(from), maxNum(other)), 346 + to: combine(maxNum(to), minNum(other)), 347 + } 348 + 349 + case opQuo: 350 + // See https://en.wikipedia.org/wiki/Interval_arithmetic. 351 + // TODO: all this is strictly not correct. To do it right we need to 352 + // have non-inclusive ranges at the least. So for now we just do this. 353 + var from, to evaluated 354 + if max := maxNum(other); !max.kind().isGround() { 355 + from = newNum(other, max.kind()) // 1/infinity is 0 356 + } else if num, ok := max.(*numLit); ok && num.v.IsZero() { 357 + from = &basicType{num.baseValue, num.kind()} // div by 0 358 + } else { 359 + one := newNum(other, max.kind()) 360 + one.v.SetInt64(1) 361 + from = combine(one, max) 362 + } 363 + 364 + if _, ok := other.(*rangeLit); !ok { 365 + other = from 366 + } else { 367 + if min := minNum(other); !min.kind().isGround() { 368 + to = newNum(other, min.kind()) // 1/infinity is 0 369 + } else if num, ok := min.(*numLit); ok && num.v.IsZero() { 370 + to = &basicType{num.baseValue, num.kind()} // div by 0 371 + } else { 372 + one := newNum(other, min.kind()) 373 + one.v.SetInt64(1) 374 + to = combine(one, min) 375 + } 376 + 377 + if !from.kind().isGround() && !to.kind().isGround() { 378 + other = from 379 + } else if leq(ctx, src, from, to) && leq(ctx, src, to, from) { 380 + other = from 381 + } else { 382 + other = &rangeLit{newSrc.base(), from, to} 383 + } 384 + } 385 + fallthrough 386 + 387 + case opMul: 388 + xMin, xMax := minNum(from), maxNum(to) 389 + yMin, yMax := minNum(other), maxNum(other) 390 + 391 + var from, to evaluated 392 + negMax := func(from, to *evaluated, val, sign evaluated) { 393 + if !val.kind().isGround() { 394 + *from = val 395 + if num, ok := sign.(*numLit); ok && num.v.Negative { 396 + *to = val 397 + } 398 + } 399 + } 400 + negMax(&from, &to, yMin, xMax) 401 + negMax(&to, &from, yMax, xMin) 402 + negMax(&from, &to, xMin, yMax) 403 + negMax(&to, &from, xMax, yMin) 404 + if from != nil && to != nil { 405 + return binOp(ctx, src, opUnify, from, to) 406 + } 407 + 408 + values := []evaluated{} 409 + add := func(a, b evaluated) { 410 + if a.kind().isGround() && b.kind().isGround() { 411 + values = append(values, combine(a, b)) 412 + } 413 + } 414 + add(xMin, yMin) 415 + add(xMax, yMin) 416 + add(xMin, yMax) 417 + add(xMax, yMax) 418 + sort.Slice(values, func(i, j int) bool { 419 + return !leq(ctx, src, values[j], values[i]) 420 + }) 421 + 422 + r := &rangeLit{baseValue: binSrc(src.Pos(), op, x, other), from: from, to: to} 423 + if from == nil { 424 + r.from = values[0] 425 + } 426 + if to == nil { 427 + r.to = values[len(values)-1] 428 + } 429 + return r 430 + } 431 + return ctx.mkIncompatible(src, op, x, other) 432 + } 433 + 434 + func evalLambda(ctx *context, a value) (l *lambdaExpr, err evaluated) { 435 + if a == nil { 436 + return nil, nil 437 + } 438 + // NOTE: the values of a lambda might still be a disjunction 439 + e := ctx.manifest(a) 440 + if isBottom(e) { 441 + return nil, e 442 + } 443 + l, ok := e.(*lambdaExpr) 444 + if !ok { 445 + return nil, ctx.mkErr(a, "value must be lambda") 446 + } 447 + return ctx.deref(l).(*lambdaExpr), nil 448 + } 449 + 450 + func (x *structLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 451 + y, ok := other.(*structLit) 452 + if !ok || op != opUnify { 453 + return ctx.mkIncompatible(src, op, x, other) 454 + } 455 + 456 + // TODO: unify emit 457 + 458 + x = ctx.deref(x).(*structLit) 459 + y = ctx.deref(y).(*structLit) 460 + if x == y { 461 + return x 462 + } 463 + arcs := make(arcs, 0, len(x.arcs)+len(y.arcs)) 464 + obj := &structLit{binSrc(src.Pos(), op, x, other), x.emit, nil, arcs} 465 + defer ctx.pushForwards(x, obj, y, obj).popForwards() 466 + 467 + tx, ex := evalLambda(ctx, x.template) 468 + ty, ey := evalLambda(ctx, y.template) 469 + 470 + var t *lambdaExpr 471 + switch { 472 + case ex != nil: 473 + return ex 474 + case ey != nil: 475 + return ey 476 + case tx != nil: 477 + t = tx 478 + case ty != nil: 479 + t = ty 480 + } 481 + if tx != ty && tx != nil && ty != nil { 482 + v := binOp(ctx, src, opUnify, tx, ty) 483 + if isBottom(v) { 484 + return v 485 + } 486 + t = v.(*lambdaExpr) 487 + } 488 + if t != nil { 489 + obj.template = ctx.copy(t).(*lambdaExpr) 490 + } 491 + 492 + for _, a := range x.arcs { 493 + cp := ctx.copy(a.v) 494 + obj.arcs = append(obj.arcs, arc{a.feature, cp, nil}) 495 + } 496 + outer: 497 + for _, a := range y.arcs { 498 + v := ctx.copy(a.v) 499 + for i, b := range obj.arcs { 500 + if a.feature == b.feature && a.v != b.v { 501 + v = mkBin(ctx, src.Pos(), opUnify, b.v, v) 502 + obj.arcs[i].v = v 503 + continue outer 504 + } 505 + } 506 + obj.arcs = append(obj.arcs, arc{feature: a.feature, v: v}) 507 + } 508 + sort.Stable(obj) 509 + 510 + return obj 511 + } 512 + 513 + func (x *nullLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 514 + // TODO: consider using binSrc instead of src.base() for better traceability. 515 + switch op { 516 + case opEql: 517 + return &boolLit{baseValue: src.base(), b: true} 518 + case opNeq: 519 + return &boolLit{baseValue: src.base(), b: false} 520 + case opUnify: 521 + return x 522 + default: 523 + panic("unimplemented") 524 + } 525 + } 526 + 527 + func (x *boolLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 528 + switch y := other.(type) { 529 + case *basicType: 530 + // range math 531 + return x 532 + 533 + case *boolLit: 534 + switch op { 535 + case opUnify: 536 + if x.b != y.b { 537 + return ctx.mkErr(x, "failed to unify: %v != %v", x.b, y.b) 538 + } 539 + return x 540 + case opLand: 541 + return boolTonode(src, x.b && y.b) 542 + case opLor: 543 + return boolTonode(src, x.b || y.b) 544 + case opEql: 545 + return boolTonode(src, x.b == y.b) 546 + case opNeq: 547 + return boolTonode(src, x.b != y.b) 548 + } 549 + } 550 + return ctx.mkIncompatible(src, op, x, other) 551 + } 552 + 553 + func (x *stringLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 554 + switch other.(type) { 555 + // case *basicType: 556 + // return x 557 + 558 + // TODO: rangelit 559 + 560 + case *stringLit: 561 + str := other.strValue() 562 + switch op { 563 + case opUnify: 564 + str := other.strValue() 565 + if x.str != str { 566 + src := mkBin(ctx, src.Pos(), op, x, other) 567 + return ctx.mkErr(src, "failed to unify: %v != %v", x.str, str) 568 + } 569 + return x 570 + case opLss, opLeq, opEql, opNeq, opGeq, opGtr: 571 + return cmpTonode(src, op, strings.Compare(string(x.str), string(str))) 572 + case opAdd: 573 + return &stringLit{binSrc(src.Pos(), op, x, other), x.str + str} 574 + } 575 + } 576 + return ctx.mkIncompatible(src, op, x, other) 577 + } 578 + 579 + func (x *bytesLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 580 + switch y := other.(type) { 581 + // case *basicType: 582 + // return x 583 + 584 + // TODO: rangelit 585 + 586 + case *bytesLit: 587 + b := y.b 588 + switch op { 589 + case opUnify: 590 + if !bytes.Equal(x.b, b) { 591 + return ctx.mkErr(x, "failed to unify: %v != %v", x.b, b) 592 + } 593 + return x 594 + case opLss, opLeq, opEql, opNeq, opGeq, opGtr: 595 + return cmpTonode(src, op, bytes.Compare(x.b, b)) 596 + case opAdd: 597 + copy := append([]byte(nil), x.b...) 598 + copy = append(copy, b...) 599 + return &bytesLit{binSrc(src.Pos(), op, x, other), copy} 600 + } 601 + } 602 + return ctx.mkIncompatible(src, op, x, other) 603 + } 604 + 605 + func leq(ctx *context, src source, a, b evaluated) bool { 606 + if isTop(a) || isTop(b) { 607 + return true 608 + } 609 + v := binOp(ctx, src, opLeq, a, b) 610 + if isBottom(v) { 611 + return false 612 + } 613 + return v.(*boolLit).b 614 + } 615 + 616 + func maxNum(v evaluated) evaluated { 617 + switch x := v.(type) { 618 + case *numLit: 619 + return x 620 + case *rangeLit: 621 + return maxNum(x.to.(evaluated)) 622 + } 623 + return v 624 + } 625 + 626 + func minNum(v evaluated) evaluated { 627 + switch x := v.(type) { 628 + case *numLit: 629 + return x 630 + case *rangeLit: 631 + return minNum(x.from.(evaluated)) 632 + } 633 + return v 634 + } 635 + 636 + func maxNumRaw(v value) value { 637 + switch x := v.(type) { 638 + case *numLit: 639 + return x 640 + case *rangeLit: 641 + return maxNumRaw(x.to) 642 + } 643 + return v 644 + } 645 + 646 + func minNumRaw(v value) value { 647 + switch x := v.(type) { 648 + case *numLit: 649 + return x 650 + case *rangeLit: 651 + return minNumRaw(x.from) 652 + } 653 + return v 654 + } 655 + 656 + func cmpTonode(src source, op op, r int) evaluated { 657 + result := false 658 + switch op { 659 + case opLss: 660 + result = r == -1 661 + case opLeq: 662 + result = r != 1 663 + case opEql, opUnify: 664 + result = r == 0 665 + case opNeq: 666 + result = r != 0 667 + case opGeq: 668 + result = r != -1 669 + case opGtr: 670 + result = r == 1 671 + } 672 + return boolTonode(src, result) 673 + } 674 + 675 + func (x *numLit) updateNumInfo(a, b *numLit) { 676 + x.numInfo = unifyNuminfo(a.numInfo, b.numInfo) 677 + } 678 + 679 + func (x *numLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 680 + switch y := other.(type) { 681 + case *basicType: 682 + if op == opUnify { 683 + return y.binOp(ctx, src, op, x) 684 + } 685 + // infinity math 686 + // 4 * int = int 687 + case *rangeLit: 688 + if op == opUnify { 689 + return y.binOp(ctx, src, op, x) 690 + } 691 + // 5..7 - 8 = -3..4 692 + case *numLit: 693 + k := unifyType(x.kind(), y.kind()) 694 + n := newNumBin(k, x, y) 695 + switch op { 696 + case opUnify: 697 + if x.v.Cmp(&y.v) != 0 { 698 + src = mkBin(ctx, src.Pos(), op, x, other) 699 + return ctx.mkErr(src, "cannot unify numbers %v and %v", x.strValue(), y.strValue()) 700 + } 701 + if k != x.k { 702 + n.v = x.v 703 + return n 704 + } 705 + return x 706 + case opLss, opLeq, opEql, opNeq, opGeq, opGtr: 707 + return cmpTonode(src, op, x.v.Cmp(&y.v)) 708 + case opAdd: 709 + ctx.Add(&n.v, &x.v, &y.v) 710 + case opSub: 711 + ctx.Sub(&n.v, &x.v, &y.v) 712 + case opMul: 713 + ctx.Mul(&n.v, &x.v, &y.v) 714 + case opQuo: 715 + ctx.Quo(&n.v, &x.v, &y.v) 716 + ctx.Reduce(&n.v, &n.v) 717 + n.k = floatKind 718 + case opRem: 719 + ctx.Rem(&n.v, &x.v, &y.v) 720 + n.k = floatKind 721 + case opIDiv: 722 + intOp(ctx, n, (*big.Int).Div, x, y) 723 + case opIMod: 724 + intOp(ctx, n, (*big.Int).Mod, x, y) 725 + case opIQuo: 726 + intOp(ctx, n, (*big.Int).Quo, x, y) 727 + case opIRem: 728 + intOp(ctx, n, (*big.Int).Rem, x, y) 729 + } 730 + return n 731 + 732 + case *durationLit: 733 + if op == opMul { 734 + fd := float64(y.d) 735 + // TODO: check range 736 + f, _ := x.v.Float64() 737 + d := time.Duration(f * fd) 738 + return &durationLit{binSrc(src.Pos(), op, x, other), d} 739 + } 740 + } 741 + return ctx.mkIncompatible(src, op, x, other) 742 + } 743 + 744 + type intFunc func(z, x, y *big.Int) *big.Int 745 + 746 + func intOp(ctx *context, n *numLit, fn intFunc, a, b *numLit) { 747 + var x, y apd.Decimal 748 + ctx.RoundToIntegralValue(&x, &a.v) 749 + if x.Negative { 750 + x.Coeff.Neg(&x.Coeff) 751 + } 752 + ctx.RoundToIntegralValue(&y, &b.v) 753 + if y.Negative { 754 + y.Coeff.Neg(&y.Coeff) 755 + } 756 + fn(&n.v.Coeff, &x.Coeff, &y.Coeff) 757 + if n.v.Coeff.Sign() < 0 { 758 + n.v.Coeff.Neg(&n.v.Coeff) 759 + n.v.Negative = true 760 + } 761 + n.k = intKind 762 + } 763 + 764 + // TODO: check overflow 765 + 766 + func (x *durationLit) binOp(ctx *context, src source, op op, other evaluated) evaluated { 767 + switch y := other.(type) { 768 + case *basicType: 769 + // infinity math 770 + 771 + case *durationLit: 772 + switch op { 773 + case opUnify: 774 + if x.d != y.d { 775 + return ctx.mkIncompatible(src, op, x, other) 776 + } 777 + return other 778 + case opLss: 779 + return boolTonode(src, x.d < y.d) 780 + case opLeq: 781 + return boolTonode(src, x.d <= y.d) 782 + case opEql: 783 + return boolTonode(src, x.d == y.d) 784 + case opNeq: 785 + return boolTonode(src, x.d != y.d) 786 + case opGeq: 787 + return boolTonode(src, x.d >= y.d) 788 + case opGtr: 789 + return boolTonode(src, x.d > y.d) 790 + case opAdd: 791 + return &durationLit{binSrc(src.Pos(), op, x, other), x.d + y.d} 792 + case opSub: 793 + return &durationLit{binSrc(src.Pos(), op, x, other), x.d - y.d} 794 + case opQuo: 795 + n := &numLit{ 796 + numBase: newNumBase(nil, newNumInfo(floatKind, 0, 10, false)), 797 + } 798 + n.v.SetInt64(int64(x.d)) 799 + d := apd.New(int64(y.d), 0) 800 + ctx.Quo(&n.v, &n.v, d) 801 + return n 802 + case opRem: 803 + n := &numLit{ 804 + numBase: newNumBase(nil, newNumInfo(intKind, 0, 10, false)), 805 + } 806 + n.v.SetInt64(int64(x.d % y.d)) 807 + n.v.Exponent = -9 808 + return n 809 + } 810 + 811 + case *numLit: 812 + switch op { 813 + case opMul: 814 + // TODO: check range 815 + f, _ := y.v.Float64() 816 + d := time.Duration(float64(x.d) * f) 817 + return &durationLit{binSrc(src.Pos(), op, x, other), d} 818 + case opQuo: 819 + // TODO: check range 820 + f, _ := y.v.Float64() 821 + d := time.Duration(float64(x.d) * f) 822 + return &durationLit{binSrc(src.Pos(), op, x, other), d} 823 + case opRem: 824 + d := x.d % time.Duration(y.intValue(ctx)) 825 + return &durationLit{binSrc(src.Pos(), op, x, other), d} 826 + } 827 + } 828 + return ctx.mkIncompatible(src, op, x, other) 829 + } 830 + 831 + func (x *list) binOp(ctx *context, src source, op op, other evaluated) evaluated { 832 + switch op { 833 + case opUnify: 834 + y, ok := other.(*list) 835 + if !ok { 836 + break 837 + } 838 + n := unify(ctx, src, x.len.(evaluated), y.len.(evaluated)) 839 + if isBottom(n) { 840 + src = mkBin(ctx, src.Pos(), op, x, other) 841 + return ctx.mkErr(src, "incompatible list lengths: %v", n) 842 + } 843 + var a, rest []value 844 + var rtyp value 845 + nx, ny := len(x.a), len(y.a) 846 + if nx < ny { 847 + a = make([]value, nx, ny) 848 + rest = y.a[nx:] 849 + rtyp = x.typ 850 + 851 + } else { 852 + a = make([]value, ny, nx) 853 + rest = x.a[ny:] 854 + rtyp = y.typ 855 + } 856 + typ := x.typ 857 + max, ok := n.(*numLit) 858 + if !ok || len(a)+len(rest) < max.intValue(ctx) { 859 + typ = unify(ctx, src, x.typ.(evaluated), y.typ.(evaluated)) 860 + if isBottom(typ) { 861 + src = mkBin(ctx, src.Pos(), op, x, other) 862 + return ctx.mkErr(src, "incompatible list types: %v: ", typ) 863 + } 864 + } 865 + 866 + for i := range a { 867 + ai := unify(ctx, src, x.at(ctx, i).evalPartial(ctx), y.at(ctx, i).evalPartial(ctx)) 868 + if isBottom(ai) { 869 + return ai 870 + } 871 + a[i] = ai 872 + } 873 + for _, n := range rest { 874 + an := unify(ctx, src, n.evalPartial(ctx), rtyp.(evaluated)) 875 + if isBottom(an) { 876 + return an 877 + } 878 + a = append(a, an) 879 + } 880 + return &list{baseValue: binSrc(src.Pos(), op, x, other), a: a, typ: typ, len: n} 881 + 882 + case opMul: 883 + k := other.kind() 884 + if !k.isAnyOf(intKind) { 885 + panic("multiplication must be int type") 886 + } 887 + typ := x.typ.(evaluated) 888 + ln := x.len.(evaluated) 889 + n := &list{baseValue: binSrc(src.Pos(), op, x, other), typ: x.typ} 890 + switch len(x.a) { 891 + case 0: 892 + case 1: 893 + n.typ = binOp(ctx, src, opUnify, typ, x.a[0].evalPartial(ctx)) 894 + default: 895 + if !k.isGround() { 896 + return x 897 + } 898 + if ln := other.(*numLit).intValue(ctx); ln > 0 { 899 + // TODO: check error 900 + for i := 0; i < ln; i++ { 901 + // TODO: copy values 902 + n.a = append(n.a, x.a...) 903 + } 904 + } 905 + } 906 + switch v := x.len.(type) { 907 + case *top, *basicType: 908 + n.len = other 909 + case *numLit: 910 + switch v.intValue(ctx) { 911 + case 0: 912 + n.len = x.len 913 + case 1: 914 + n.len = other 915 + default: 916 + n.len = binOp(ctx, src, opMul, ln, other) 917 + } 918 + default: 919 + n.len = binOp(ctx, src, opMul, ln, other) 920 + } 921 + return n 922 + } 923 + return ctx.mkIncompatible(src, op, x, other) 924 + } 925 + 926 + func (x *lambdaExpr) binOp(ctx *context, src source, op op, other evaluated) evaluated { 927 + if y, ok := other.(*lambdaExpr); ok && op == opUnify { 928 + x = ctx.deref(x).(*lambdaExpr) 929 + y = ctx.deref(y).(*lambdaExpr) 930 + n, m := len(x.params.arcs), len(y.params.arcs) 931 + if n != m { 932 + src = mkBin(ctx, src.Pos(), op, x, other) 933 + return ctx.mkErr(src, "number of params of params should match in unification (%d != %d)", n, m) 934 + } 935 + arcs := make([]arc, len(x.arcs)) 936 + lambda := &lambdaExpr{binSrc(src.Pos(), op, x, other), &params{arcs}, nil} 937 + defer ctx.pushForwards(x, lambda, y, lambda).popForwards() 938 + 939 + xVal := ctx.copy(x.value) 940 + yVal := ctx.copy(y.value) 941 + lambda.value = mkBin(ctx, src.Pos(), opUnify, xVal, yVal) 942 + 943 + for i := range arcs { 944 + xArg := ctx.copy(x.at(ctx, i)).(evaluated) 945 + yArg := ctx.copy(y.at(ctx, i)).(evaluated) 946 + v := binOp(ctx, src, op, xArg, yArg) 947 + if isBottom(v) { 948 + return v 949 + } 950 + arcs[i] = arc{x.arcs[i].feature, v, nil} 951 + } 952 + 953 + return lambda 954 + } 955 + return ctx.mkIncompatible(src, op, x, other) 956 + } 957 + 958 + func (x *builtin) binOp(ctx *context, src source, op op, other evaluated) evaluated { 959 + if op == opUnify && evaluated(x) == other { 960 + return x 961 + } 962 + return ctx.mkIncompatible(src, op, x, other) 963 + } 964 + 965 + func (x *feed) binOp(ctx *context, src source, op op, other evaluated) evaluated { 966 + return ctx.mkIncompatible(src, op, x, other) 967 + } 968 + 969 + func (x *guard) binOp(ctx *context, src source, op op, other evaluated) evaluated { 970 + return ctx.mkIncompatible(src, op, x, other) 971 + } 972 + 973 + func (x *yield) binOp(ctx *context, src source, op op, other evaluated) evaluated { 974 + return ctx.mkIncompatible(src, op, x, other) 975 + }
+245
cue/build.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "path" 19 + "strconv" 20 + 21 + "cuelang.org/go/cue/ast" 22 + build "cuelang.org/go/cue/build" 23 + "cuelang.org/go/cue/token" 24 + ) 25 + 26 + // Build creates one Instance for each build.Instance. A returned Instance 27 + // may be incomplete, in which case its Err field is set. 28 + // 29 + // Example: 30 + // inst := cue.Build(load.Load(args)) 31 + // 32 + func Build(instances []*build.Instance) []*Instance { 33 + if len(instances) == 0 { 34 + panic("cue: list of instances must not be empty") 35 + } 36 + index := newIndex(instances[0].Context().FileSet()) 37 + 38 + loaded := []*Instance{} 39 + 40 + for _, p := range instances { 41 + p.Complete() 42 + 43 + loaded = append(loaded, index.loadInstance(p)) 44 + } 45 + // TODO: insert imports 46 + return loaded 47 + } 48 + 49 + // FromExpr creates anb instance from an expression. 50 + // Any references must be resolved beforehand. 51 + func FromExpr(fset *token.FileSet, expr ast.Expr) (*Instance, error) { 52 + i := newIndex(fset).NewInstance(nil) 53 + err := i.insertFile(&ast.File{ 54 + Decls: []ast.Decl{&ast.EmitDecl{Expr: expr}}, 55 + }) 56 + if err != nil { 57 + return nil, err 58 + } 59 + return i, nil 60 + } 61 + 62 + // index maps conversions from label names to internal codes. 63 + // 64 + // All instances belonging to the same package should share this index. 65 + type index struct { 66 + fset *token.FileSet 67 + 68 + labelMap map[string]label 69 + labels []string 70 + 71 + loaded map[*build.Instance]*Instance 72 + } 73 + 74 + // newIndex creates a new index. 75 + func newIndex(f *token.FileSet) *index { 76 + i := &index{ 77 + fset: f, 78 + labelMap: map[string]label{"": 0}, 79 + labels: []string{""}, 80 + loaded: map[*build.Instance]*Instance{}, 81 + } 82 + return i 83 + } 84 + 85 + func (idx *index) strLabel(str string) label { 86 + return idx.label(str, false) 87 + } 88 + 89 + func (idx *index) nodeLabel(n ast.Node) (f label, ok bool) { 90 + switch x := n.(type) { 91 + case *ast.BasicLit: 92 + name, ok := ast.LabelName(x) 93 + return idx.label(name, false), ok 94 + case *ast.Ident: 95 + return idx.label(x.Name, true), true 96 + } 97 + return 0, false 98 + } 99 + 100 + func (idx *index) label(s string, isIdent bool) label { 101 + f, ok := idx.labelMap[s] 102 + if !ok { 103 + f = label(len(idx.labelMap)) 104 + idx.labelMap[s] = f 105 + idx.labels = append(idx.labels, s) 106 + } 107 + f <<= 1 108 + if isIdent && s != "" && s[0] == '_' { 109 + f |= 1 110 + } 111 + return f 112 + } 113 + 114 + func (idx *index) labelStr(l label) string { 115 + return idx.labels[l>>1] 116 + } 117 + 118 + func (idx *index) loadInstance(p *build.Instance) *Instance { 119 + if inst := idx.loaded[p]; inst != nil { 120 + if !inst.complete { 121 + // cycles should be detected by the builder and it should not be 122 + // possible to construct a build.Instance that has them. 123 + panic("cue: cycle") 124 + } 125 + return inst 126 + } 127 + files := p.Files 128 + inst := idx.NewInstance(p) 129 + if inst.Err == nil { 130 + // inst.instance.index.state = s 131 + // inst.instance.inst = p 132 + inst.Err = resolveFiles(idx, p) 133 + for _, f := range files { 134 + inst.insertFile(f) 135 + } 136 + } 137 + inst.complete = true 138 + return inst 139 + } 140 + 141 + func lineStr(idx *index, n ast.Node) string { 142 + return idx.fset.Position(n.Pos()).String() 143 + } 144 + 145 + func resolveFiles(idx *index, p *build.Instance) error { 146 + // Link top-level declarations. As top-level entries get unified, an entry 147 + // may be linked to any top-level entry of any of the files. 148 + allFields := map[string]ast.Node{} 149 + for _, file := range p.Files { 150 + for _, d := range file.Decls { 151 + if f, ok := d.(*ast.Field); ok && f.Value != nil { 152 + if ident, ok := f.Label.(*ast.Ident); ok { 153 + allFields[ident.Name] = f.Value 154 + } 155 + } 156 + } 157 + } 158 + for _, f := range p.Files { 159 + if err := resolveFile(idx, f, p, allFields); err != nil { 160 + return err 161 + } 162 + } 163 + return nil 164 + } 165 + 166 + func resolveFile(idx *index, f *ast.File, p *build.Instance, allFields map[string]ast.Node) error { 167 + type importInfo struct { 168 + node ast.Node 169 + inst *Instance 170 + used bool // TODO: use a more general unresolved value technique 171 + } 172 + index := map[string][]*ast.Ident{} 173 + for _, u := range f.Unresolved { 174 + index[u.Name] = append(index[u.Name], u) 175 + } 176 + fields := map[string]ast.Node{} 177 + for _, d := range f.Decls { 178 + if f, ok := d.(*ast.Field); ok && f.Value != nil { 179 + if ident, ok := f.Label.(*ast.Ident); ok { 180 + fields[ident.Name] = d 181 + } 182 + } 183 + } 184 + var errUnused error 185 + 186 + for _, spec := range f.Imports { 187 + id, err := strconv.Unquote(spec.Path.Value) 188 + if err != nil { 189 + continue // quietly ignore the error 190 + } 191 + name := path.Base(id) 192 + if _, ok := builtins[id]; ok { 193 + } else if imp := p.LookupImport(id); imp != nil { 194 + name = imp.PkgName 195 + if spec.Name != nil { 196 + name = spec.Name.Name 197 + } 198 + } else { 199 + // continue 200 + return idx.mkErr(newNode(spec), "package %q not found", id) 201 + } 202 + if n, ok := fields[name]; ok { 203 + return idx.mkErr(newNode(spec), 204 + "%s redeclared as imported package name\n"+ 205 + "\tprevious declaration at %v", name, lineStr(idx, n)) 206 + } 207 + used := false 208 + for _, u := range index[name] { 209 + used = true 210 + u.Node = spec 211 + } 212 + if !used { 213 + if spec.Name == nil { 214 + errUnused = idx.mkErr(newNode(spec), 215 + "imported and not used: %s", spec.Path.Value) 216 + } else { 217 + errUnused = idx.mkErr(newNode(spec), 218 + "imported and not used: %s as %s", spec.Path.Value, spec.Name) 219 + } 220 + } 221 + } 222 + i := 0 223 + for _, u := range f.Unresolved { 224 + if u.Node != nil { 225 + continue 226 + } 227 + if n, ok := allFields[u.Name]; ok { 228 + u.Node = n 229 + u.Scope = f 230 + continue 231 + } 232 + f.Unresolved[i] = u 233 + i++ 234 + } 235 + f.Unresolved = f.Unresolved[:i] 236 + // TODO: also need to resolve types. 237 + // if len(f.Unresolved) > 0 { 238 + // n := f.Unresolved[0] 239 + // return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name) 240 + // } 241 + if errUnused != nil { 242 + return errUnused 243 + } 244 + return nil 245 + }
+207
cue/build_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "strings" 20 + "testing" 21 + 22 + "cuelang.org/go/cue/ast" 23 + "cuelang.org/go/cue/build" 24 + "cuelang.org/go/cue/token" 25 + ) 26 + 27 + func TestFromExpr(t *testing.T) { 28 + testCases := []struct { 29 + expr ast.Expr 30 + out string 31 + }{{ 32 + expr: &ast.BasicLit{Kind: token.STRING, Value: `"Hello"`}, 33 + out: `"Hello"`, 34 + }, { 35 + expr: &ast.ListLit{Elts: []ast.Expr{ 36 + &ast.BasicLit{Kind: token.STRING, Value: `"Hello"`}, 37 + &ast.BasicLit{Kind: token.STRING, Value: `"World"`}, 38 + }}, 39 + out: `["Hello","World"]`, 40 + }} 41 + for _, tc := range testCases { 42 + t.Run("", func(t *testing.T) { 43 + inst, err := FromExpr(nil, tc.expr) 44 + if err != nil { 45 + t.Fatal(err) 46 + } 47 + ctx := inst.newContext() 48 + if got := debugStr(ctx, inst.eval(ctx)); got != tc.out { 49 + t.Errorf("\n got: %v; want %v", got, tc.out) 50 + } 51 + }) 52 + } 53 + } 54 + 55 + func TestBuild(t *testing.T) { 56 + files := func(s ...string) []string { return s } 57 + insts := func(i ...*bimport) []*bimport { return i } 58 + pkg1 := &bimport{ 59 + "pkg1", 60 + files(` 61 + package pkg1 62 + 63 + Object: "World" 64 + `), 65 + } 66 + pkg2 := &bimport{ 67 + "example.com/foo/pkg2", 68 + files(` 69 + package pkg 70 + 71 + Number: 12 72 + `), 73 + } 74 + insts(pkg1, pkg2) 75 + 76 + testCases := []struct { 77 + instances []*bimport 78 + emit string 79 + }{{ 80 + insts(&bimport{"", files(`test: "ok"`)}), 81 + `<0>{test: "ok"}`, 82 + // }, { 83 + // insts(pkg1, &bimport{"", 84 + // files( 85 + // `package test 86 + 87 + // import "math" 88 + 89 + // "Pi: \(math.Pi)!"`)}), 90 + // `"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`, 91 + }, { 92 + insts(pkg1, &bimport{"", 93 + files( 94 + `package test 95 + 96 + import "pkg1" 97 + 98 + "Hello \(pkg1.Object)!"`), 99 + }), 100 + `"Hello World!"`, 101 + }, { 102 + insts(pkg1, &bimport{"", 103 + files( 104 + `package test 105 + 106 + import "pkg1" 107 + pkg1: 1 108 + 109 + "Hello \(pkg1.Object)!"`), 110 + }), 111 + `pkg1 redeclared as imported package name 112 + previous declaration at file0.cue:4:5`, 113 + }, { 114 + insts(pkg1, &bimport{"", 115 + files( 116 + `package test 117 + 118 + import bar "pkg1" 119 + 120 + pkg1 Object: 3 121 + "Hello \(pkg1.Object)!"`), 122 + }), 123 + `imported and not used: "pkg1" as bar`, 124 + }, { 125 + insts(pkg2, &bimport{"", 126 + files( 127 + `package test 128 + 129 + import "example.com/foo/pkg2" 130 + 131 + "Hello \(pkg2.Number)!"`), 132 + }), 133 + `imported and not used: "example.com/foo/pkg2"`, 134 + // `file0.cue:5:14: unresolved reference pkg2`, 135 + }, { 136 + insts(pkg2, &bimport{"", 137 + files( 138 + `package test 139 + 140 + import "example.com/foo/pkg2" 141 + 142 + "Hello \(pkg.Number)!"`), 143 + }), 144 + `"Hello 12!"`, 145 + }} 146 + for _, tc := range testCases { 147 + t.Run("", func(t *testing.T) { 148 + insts := Build(makeInstances(tc.instances)) 149 + var got string 150 + if err := insts[0].Err; err != nil { 151 + got = err.Error() 152 + } else { 153 + got = strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value())) 154 + } 155 + if got != tc.emit { 156 + t.Errorf("\n got: %s\nwant: %s", got, tc.emit) 157 + } 158 + }) 159 + } 160 + } 161 + 162 + type builder struct { 163 + ctxt *build.Context 164 + imports map[string]*bimport 165 + } 166 + 167 + func (b *builder) load(path string) *build.Instance { 168 + p := b.ctxt.NewInstance(path, b.load) 169 + bi := b.imports[path] 170 + if bi == nil { 171 + return nil 172 + } 173 + buildInstance(b.imports[path], p) 174 + return p 175 + } 176 + 177 + type bimport struct { 178 + path string // "" means top-level 179 + files []string 180 + } 181 + 182 + func makeInstances(insts []*bimport) (instances []*build.Instance) { 183 + b := builder{ 184 + ctxt: build.NewContext(), 185 + imports: map[string]*bimport{}, 186 + } 187 + for _, bi := range insts { 188 + if bi.path != "" { 189 + b.imports[bi.path] = bi 190 + } 191 + } 192 + for _, bi := range insts { 193 + if bi.path == "" { 194 + p := b.ctxt.NewInstance("dir", b.load) 195 + buildInstance(bi, p) 196 + instances = append(instances, p) 197 + } 198 + } 199 + return 200 + } 201 + 202 + func buildInstance(bi *bimport, p *build.Instance) { 203 + for i, f := range bi.files { 204 + p.AddFile(fmt.Sprintf("file%d.cue", i), f) 205 + } 206 + p.Complete() 207 + }
+519
cue/builtin.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "io" 20 + "math/big" 21 + "path" 22 + "reflect" 23 + "sort" 24 + "strconv" 25 + "strings" 26 + 27 + "cuelang.org/go/cue/ast" 28 + "github.com/cockroachdb/apd" 29 + ) 30 + 31 + // A builtin is a builtin function or constant. 32 + // 33 + // A function may return and a constant may be any of the following types: 34 + // 35 + // error (translates to bottom) 36 + // nil (translates to null) 37 + // bool 38 + // int* 39 + // uint* 40 + // float64 41 + // string 42 + // *big.Float 43 + // *big.Int 44 + // 45 + // For any of the above, including interface{} and these types recursively: 46 + // []T 47 + // map[string]T 48 + // 49 + type builtin struct { 50 + baseValue 51 + Name string 52 + Params []kind 53 + Result kind 54 + Func func(c *callCtxt) 55 + // Const interface{} 56 + Const evaluated 57 + } 58 + 59 + var _ caller = &builtin{} 60 + 61 + var lenBuiltin = &builtin{ 62 + Name: "len", 63 + Params: []kind{stringKind | bytesKind | listKind | structKind}, 64 + Result: intKind, 65 + Func: func(c *callCtxt) { 66 + v := c.value(0) 67 + switch v.Kind() { 68 + case StructKind: 69 + s, _ := v.structVal(c.ctx) 70 + c.ret = s.Len() 71 + case ListKind: 72 + i := 0 73 + iter, _ := v.List() 74 + for ; iter.Next(); i++ { 75 + } 76 + c.ret = i 77 + case BytesKind: 78 + b, _ := v.Bytes() 79 + c.ret = len(b) 80 + case StringKind: 81 + s, _ := v.String() 82 + c.ret = len(s) 83 + } 84 + }, 85 + } 86 + 87 + func (x *builtin) kind() kind { 88 + if x.Const != nil { 89 + return x.Const.kind() 90 + } 91 + return lambdaKind 92 + } 93 + 94 + func (x *builtin) evalPartial(ctx *context) evaluated { 95 + if x.Const != nil { 96 + return x.Const 97 + } 98 + return x 99 + } 100 + 101 + func (x *builtin) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 102 + if y, ok := v.(*builtin); ok { 103 + return x == y 104 + } 105 + return false 106 + } 107 + 108 + func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) { 109 + if x.Func == nil { 110 + return ctx.mkErr(x, "Builtin %q is not a function", x.Name) 111 + } 112 + if len(x.Params) != len(args) { 113 + return ctx.mkErr(src, x, "number of arguments does not match (%d vs %d)", 114 + len(x.Params), len(args)) 115 + } 116 + for i, a := range args { 117 + if x.Params[i] != bottomKind { 118 + if unifyType(x.Params[i], a.kind()) == bottomKind { 119 + return ctx.mkErr(src, x, "argument %d requires type %v, found %v", i+1, x.Params[i], a.kind()) 120 + } 121 + } 122 + } 123 + call := callCtxt{src: src, ctx: ctx, args: args} 124 + defer func() { 125 + var errVal interface{} = call.err 126 + if err := recover(); err != nil { 127 + errVal = err 128 + } 129 + switch err := errVal.(type) { 130 + case nil: 131 + case *bottom: 132 + ret = err 133 + default: 134 + ret = ctx.mkErr(src, x, "call error: %v", err) 135 + } 136 + }() 137 + x.Func(&call) 138 + return convert(ctx, x, call.ret) 139 + } 140 + 141 + // callCtxt is passed to builtin implementations. 142 + type callCtxt struct { 143 + src source 144 + ctx *context 145 + args []evaluated 146 + err error 147 + ret interface{} 148 + } 149 + 150 + var builtins map[string][]*builtin 151 + 152 + func initBuiltins(pkgs map[string][]*builtin) { 153 + builtins = pkgs 154 + for k, b := range pkgs { 155 + builtins["-/"+path.Base(k)] = b 156 + } 157 + } 158 + 159 + func getBuiltinShorthandPkg(ctx *context, shorthand string) *structLit { 160 + return getBuiltinPkg(ctx, "-/"+shorthand) 161 + } 162 + 163 + func getBuiltinPkg(ctx *context, path string) *structLit { 164 + p, ok := builtins[path] 165 + if !ok { 166 + return nil 167 + } 168 + 169 + // TODO(perf): store in index 170 + 171 + obj := &structLit{} 172 + for _, b := range p { 173 + f := ctx.label(b.Name, false) // never starts with _ 174 + // n := &node{baseValue: newBase(imp.Path)} 175 + obj.arcs = append(obj.arcs, arc{feature: f, v: b}) 176 + } 177 + sort.Sort(obj) 178 + return obj 179 + } 180 + 181 + // do returns whether the call should be done. 182 + func (c *callCtxt) do() bool { 183 + return c.err == nil 184 + } 185 + 186 + func (c *callCtxt) value(i int) Value { 187 + return newValueRoot(c.ctx, c.args[i]) 188 + } 189 + 190 + func (c *callCtxt) int(i int) int { return int(c.intValue(i, 64)) } 191 + func (c *callCtxt) int8(i int) int8 { return int8(c.intValue(i, 8)) } 192 + func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16)) } 193 + func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32)) } 194 + func (c *callCtxt) rune(i int) rune { return rune(c.intValue(i, 32)) } 195 + func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64)) } 196 + 197 + func (c *callCtxt) intValue(i, bits int) int64 { 198 + x := newValueRoot(c.ctx, c.args[i]) 199 + n, err := x.Int(nil) 200 + if err != nil { 201 + c.err = c.ctx.mkErr(c.src, "argument %d must be in int, found number", i) 202 + return 0 203 + } 204 + if n.BitLen() > bits { 205 + c.err = c.ctx.mkErr(c.src, err, "argument %d out of range: has %d > %d bits", n.BitLen(), bits) 206 + } 207 + res, _ := x.Int64() 208 + return res 209 + } 210 + 211 + func (c *callCtxt) uint(i int) uint { return uint(c.uintValue(i, 64)) } 212 + func (c *callCtxt) uint8(i int) uint8 { return uint8(c.uintValue(i, 8)) } 213 + func (c *callCtxt) byte(i int) uint8 { return byte(c.uintValue(i, 8)) } 214 + func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16)) } 215 + func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32)) } 216 + func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64)) } 217 + 218 + func (c *callCtxt) uintValue(i, bits int) uint64 { 219 + x := newValueRoot(c.ctx, c.args[i]) 220 + n, err := x.Int(nil) 221 + if err != nil { 222 + c.err = c.ctx.mkErr(c.src, "argument %d must be an integer", i) 223 + return 0 224 + } 225 + if n.Sign() < 0 { 226 + c.err = c.ctx.mkErr(c.src, "argument %d must be a positive integer", i) 227 + return 0 228 + } 229 + if n.BitLen() > bits { 230 + c.err = c.ctx.mkErr(c.src, err, "argument %d out of range: has %d > %d bits", i, n.BitLen(), bits) 231 + } 232 + res, _ := x.Uint64() 233 + return res 234 + } 235 + 236 + func (c *callCtxt) float64(i int) float64 { 237 + x := newValueRoot(c.ctx, c.args[i]) 238 + res, err := x.Float64() 239 + if err != nil { 240 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 241 + return 0 242 + } 243 + return res 244 + } 245 + 246 + func (c *callCtxt) bigInt(i int) *big.Int { 247 + x := newValueRoot(c.ctx, c.args[i]) 248 + n, err := x.Int(nil) 249 + if err != nil { 250 + c.err = c.ctx.mkErr(c.src, "argument %d must be in int, found number", i) 251 + return nil 252 + } 253 + return n 254 + } 255 + 256 + func (c *callCtxt) bigFloat(i int) *big.Float { 257 + x := newValueRoot(c.ctx, c.args[i]) 258 + var mant big.Int 259 + exp, err := x.MantExp(&mant) 260 + if err != nil { 261 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 262 + return nil 263 + } 264 + f := &big.Float{} 265 + f.SetInt(&mant) 266 + if exp != 0 { 267 + var g big.Float 268 + e := big.NewInt(int64(exp)) 269 + f.Mul(f, g.SetInt(e.Exp(ten, e, nil))) 270 + } 271 + return f 272 + } 273 + 274 + func (c *callCtxt) string(i int) string { 275 + x := newValueRoot(c.ctx, c.args[i]) 276 + v, err := x.String() 277 + if err != nil { 278 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 279 + return "" 280 + } 281 + return v 282 + } 283 + 284 + func (c *callCtxt) bytes(i int) []byte { 285 + x := newValueRoot(c.ctx, c.args[i]) 286 + v, err := x.Bytes() 287 + if err != nil { 288 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 289 + return nil 290 + } 291 + return v 292 + } 293 + 294 + func (c *callCtxt) reader(i int) io.Reader { 295 + x := newValueRoot(c.ctx, c.args[i]) 296 + // TODO: optimize for string and bytes cases 297 + r, err := x.Reader() 298 + if err != nil { 299 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 300 + return nil 301 + } 302 + return r 303 + } 304 + 305 + func (c *callCtxt) bool(i int) bool { 306 + x := newValueRoot(c.ctx, c.args[i]) 307 + b, err := x.Bool() 308 + if err != nil { 309 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 310 + return false 311 + } 312 + return b 313 + } 314 + 315 + func (c *callCtxt) error(i int) error { 316 + x := newValueRoot(c.ctx, c.args[i]) 317 + return x.Err() 318 + } 319 + 320 + func (c *callCtxt) list(i int) (a Iterator) { 321 + x := newValueRoot(c.ctx, c.args[i]) 322 + v, err := x.List() 323 + if err != nil { 324 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 325 + return Iterator{ctx: c.ctx} 326 + } 327 + return v 328 + } 329 + 330 + func (c *callCtxt) strList(i int) (a []string) { 331 + x := newValueRoot(c.ctx, c.args[i]) 332 + v, err := x.List() 333 + if err != nil { 334 + c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) 335 + return nil 336 + } 337 + for i := 0; v.Next(); i++ { 338 + str, err := v.Value().String() 339 + if err != nil { 340 + c.err = c.ctx.mkErr(c.src, err, "list element %d: %v", i, err) 341 + } 342 + a = append(a, str) 343 + } 344 + return a 345 + } 346 + 347 + // lookupBuiltinPkg returns the builtin package for the given path if it exists. 348 + func lookupBuiltinPkg(ctx *context, imp *ast.ImportSpec) evaluated { 349 + path, err := strconv.Unquote(imp.Path.Value) 350 + if err != nil { 351 + return ctx.mkErr(newNode(imp), "illformed import spec") 352 + } 353 + 354 + p := getBuiltinPkg(ctx, path) 355 + if p == nil { 356 + return ctx.mkErr(newNode(imp), "package %q not found", path) 357 + } 358 + return p 359 + } 360 + 361 + func convert(ctx *context, src source, x interface{}) evaluated { 362 + switch v := x.(type) { 363 + case evaluated: 364 + return v 365 + case nil: 366 + return &nullLit{src.base()} 367 + case *Instance: 368 + return v.eval(ctx) 369 + case error: 370 + return ctx.mkErr(src, v.Error()) 371 + case bool: 372 + return &boolLit{src.base(), v} 373 + case string: 374 + return &stringLit{src.base(), v} 375 + case []byte: 376 + return &bytesLit{src.base(), v} 377 + case int: 378 + return toInt(ctx, src, int64(v)) 379 + case int8: 380 + return toInt(ctx, src, int64(v)) 381 + case int16: 382 + return toInt(ctx, src, int64(v)) 383 + case int32: 384 + return toInt(ctx, src, int64(v)) 385 + case int64: 386 + return toInt(ctx, src, int64(v)) 387 + case uint: 388 + return toUint(ctx, src, uint64(v)) 389 + case uint8: 390 + return toUint(ctx, src, uint64(v)) 391 + case uint16: 392 + return toUint(ctx, src, uint64(v)) 393 + case uint32: 394 + return toUint(ctx, src, uint64(v)) 395 + case uint64: 396 + return toUint(ctx, src, uint64(v)) 397 + case float64: 398 + r := newNum(src, floatKind) 399 + r.v.SetString(fmt.Sprintf("%g", v)) 400 + return r 401 + case *big.Int: 402 + n := newNum(src, intKind) 403 + n.v.Coeff.Set(v) 404 + if v.Sign() < 0 { 405 + n.v.Coeff.Neg(&n.v.Coeff) 406 + n.v.Negative = true 407 + } 408 + return n 409 + case *big.Rat: 410 + n := newNum(src, numKind) 411 + ctx.Quo(&n.v, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0)) 412 + if !v.IsInt() { 413 + n.k = floatKind 414 + } 415 + return n 416 + case *big.Float: 417 + n := newNum(src, floatKind) 418 + n.v.SetString(v.String()) 419 + return n 420 + case *apd.Decimal: 421 + n := newNum(src, floatKind|intKind) 422 + n.v.Set(v) 423 + if !n.isInt(ctx) { 424 + n.k = floatKind 425 + } 426 + return n 427 + case reflect.Value: 428 + if v.CanInterface() { 429 + return convert(ctx, src, v.Interface()) 430 + } 431 + 432 + default: 433 + value := reflect.ValueOf(v) 434 + switch value.Kind() { 435 + case reflect.Ptr: 436 + if value.IsNil() { 437 + return &nullLit{src.base()} 438 + } 439 + return convert(ctx, src, value.Elem().Interface()) 440 + case reflect.Struct: 441 + obj := newStruct(src) 442 + t := value.Type() 443 + for i := 0; i < value.NumField(); i++ { 444 + t := t.Field(i) 445 + if t.PkgPath != "" { 446 + continue 447 + } 448 + sub := convert(ctx, src, value.Field(i).Interface()) 449 + // leave errors like we do during normal evaluation or do we 450 + // want to return the error? 451 + name := t.Name 452 + for _, s := range []string{"cue", "json", "protobuf"} { 453 + if tag, ok := t.Tag.Lookup(s); ok { 454 + if p := strings.Index(tag, ","); p >= 0 { 455 + tag = tag[:p] 456 + } 457 + if tag != "" { 458 + name = tag 459 + break 460 + } 461 + } 462 + } 463 + f := ctx.strLabel(name) 464 + obj.arcs = append(obj.arcs, arc{feature: f, v: sub}) 465 + } 466 + sort.Sort(obj) 467 + return obj 468 + 469 + case reflect.Map: 470 + obj := newStruct(src) 471 + t := value.Type() 472 + if t.Key().Kind() != reflect.String { 473 + return ctx.mkErr(src, "builtin map key not a string, but unsupported type %s", t.Key().String()) 474 + } 475 + keys := []string{} 476 + for _, k := range value.MapKeys() { 477 + keys = append(keys, k.String()) 478 + } 479 + sort.Strings(keys) 480 + for _, k := range keys { 481 + sub := convert(ctx, src, value.MapIndex(reflect.ValueOf(k)).Interface()) 482 + // leave errors like we do during normal evaluation or do we 483 + // want to return the error? 484 + f := ctx.strLabel(k) 485 + obj.arcs = append(obj.arcs, arc{feature: f, v: sub}) 486 + } 487 + sort.Sort(obj) 488 + return obj 489 + 490 + case reflect.Slice, reflect.Array: 491 + list := &list{baseValue: src.base()} 492 + for i := 0; i < value.Len(); i++ { 493 + x := convert(ctx, src, value.Index(i).Interface()) 494 + if isBottom(x) { 495 + return x 496 + } 497 + list.a = append(list.a, x) 498 + } 499 + list.initLit() 500 + // There is no need to set the type of the list, as the list will 501 + // be of fixed size and all elements will already have a defined 502 + // value. 503 + return list 504 + } 505 + } 506 + return ctx.mkErr(src, "builtin returned unsupported type %T", x) 507 + } 508 + 509 + func toInt(ctx *context, src source, x int64) evaluated { 510 + n := newNum(src, intKind) 511 + n.v.SetInt64(x) 512 + return n 513 + } 514 + 515 + func toUint(ctx *context, src source, x uint64) evaluated { 516 + n := newNum(src, floatKind) 517 + n.v.Coeff.SetUint64(x) 518 + return n 519 + }
+127
cue/builtin_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "math/big" 19 + "reflect" 20 + "testing" 21 + 22 + "cuelang.org/go/cue/ast" 23 + "cuelang.org/go/cue/errors" 24 + ) 25 + 26 + func TestConvert(t *testing.T) { 27 + i34 := big.NewInt(34) 28 + d34 := mkBigInt(34) 29 + f34 := big.NewFloat(34.0000) 30 + testCases := []struct { 31 + goVal interface{} 32 + want string 33 + }{{ 34 + nil, "null", 35 + }, { 36 + true, "true", 37 + }, { 38 + false, "false", 39 + }, { 40 + errors.New("oh noes"), "_|_(oh noes)", 41 + }, { 42 + "foo", `"foo"`, 43 + }, { 44 + 3, "3", 45 + }, { 46 + uint(3), "3", 47 + }, { 48 + uint8(3), "3", 49 + }, { 50 + uint16(3), "3", 51 + }, { 52 + uint32(3), "3", 53 + }, { 54 + uint64(3), "3", 55 + }, { 56 + int8(-3), "-3", 57 + }, { 58 + int16(-3), "-3", 59 + }, { 60 + int32(-3), "-3", 61 + }, { 62 + int64(-3), "-3", 63 + }, { 64 + float64(3.1), "3.1", 65 + }, { 66 + &i34, "34", 67 + }, { 68 + &f34, "34", 69 + }, { 70 + &d34, "34", 71 + }, { 72 + []int{1, 2, 3, 4}, "[1,2,3,4]", 73 + }, { 74 + []interface{}{}, "[]", 75 + }, { 76 + map[string][]int{ 77 + "a": []int{1}, 78 + "b": []int{3, 4}, 79 + }, "<0>{a: [1], b: [3,4]}", 80 + }, { 81 + map[int]int{}, "_|_(builtin map key not a string, but unsupported type int)", 82 + }, { 83 + map[int]int{1: 2}, "_|_(builtin map key not a string, but unsupported type int)", 84 + }, { 85 + struct { 86 + a int 87 + b int 88 + }{3, 4}, 89 + "<0>{}", 90 + }, { 91 + struct { 92 + A int 93 + B int 94 + }{3, 4}, 95 + "<0>{A: 3, B: 4}", 96 + }, { 97 + struct { 98 + A int `json:"a"` 99 + B int `cue:"b"` 100 + }{3, 4}, 101 + "<0>{a: 3, b: 4}", 102 + }, { 103 + struct { 104 + A int `json:",bb" cue:"" protobuf:"aa"` 105 + B int `json:"cc" cue:"bb" protobuf:"aa"` 106 + }{3, 4}, 107 + "<0>{aa: 3, bb: 4}", 108 + }, { 109 + &struct{ A int }{3}, "<0>{A: 3}", 110 + }, { 111 + (*struct{ A int })(nil), "null", 112 + }, { 113 + reflect.ValueOf(3), "3", 114 + }} 115 + inst := getInstance(t, "foo") 116 + b := ast.NewIdent("dummy") 117 + for _, tc := range testCases { 118 + ctx := inst.newContext() 119 + t.Run("", func(t *testing.T) { 120 + v := convert(ctx, newNode(b), tc.goVal) 121 + got := debugStr(ctx, v) 122 + if got != tc.want { 123 + t.Errorf("got %q; want %q", got, tc.want) 124 + } 125 + }) 126 + } 127 + }
+105
cue/context.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "github.com/cockroachdb/apd" 19 + ) 20 + 21 + // context manages evaluation state. 22 + type context struct { 23 + *apd.Context 24 + 25 + *index 26 + 27 + forwardMap []scope // pairs 28 + oldSize []int 29 + 30 + // constraints are to be evaluated at the end values to be evaluated later. 31 + constraints []value 32 + noDelay bool 33 + 34 + // for debug strings 35 + nodeRefs map[scope]string 36 + 37 + // tracing 38 + trace bool 39 + level int 40 + } 41 + 42 + var baseContext apd.Context 43 + 44 + func init() { 45 + baseContext = apd.BaseContext 46 + baseContext.Precision = 24 47 + } 48 + 49 + // newContext returns a new evaluation context. 50 + func (idx *index) newContext() *context { 51 + c := &context{ 52 + Context: &baseContext, 53 + index: idx, 54 + } 55 + return c 56 + } 57 + 58 + // delayConstraint schedules constraint to be evaluated and returns ret. If 59 + // delaying constraints is currently not allowed, it returns an error instead. 60 + func (c *context) delayConstraint(ret evaluated, constraint value) evaluated { 61 + if c.noDelay { 62 + return c.mkErr(ret, "delayed constraint %v violated", debugStr(c, constraint)) 63 + } 64 + c.constraints = append(c.constraints, constraint) 65 + return ret 66 + } 67 + 68 + func (c *context) processDelayedConstraints() evaluated { 69 + cons := c.constraints 70 + c.constraints = c.constraints[:0] 71 + for _, dc := range cons { 72 + c.noDelay = true 73 + v := c.manifest(dc) 74 + c.noDelay = false 75 + if isBottom(v) { 76 + return c.mkErr(dc, "constraint violated: %v", debugStr(c, v)) 77 + } 78 + } 79 + return nil 80 + } 81 + 82 + func (c *context) deref(f scope) scope { 83 + outer: 84 + for { 85 + for i := 0; i < len(c.forwardMap); i += 2 { 86 + if c.forwardMap[i] == f { 87 + f = c.forwardMap[i+1] 88 + continue outer 89 + } 90 + } 91 + return f 92 + } 93 + } 94 + 95 + func (c *context) pushForwards(pairs ...scope) *context { 96 + c.oldSize = append(c.oldSize, len(c.forwardMap)) 97 + c.forwardMap = append(c.forwardMap, pairs...) 98 + return c 99 + } 100 + 101 + func (c *context) popForwards() { 102 + last := len(c.oldSize) - 1 103 + c.forwardMap = c.forwardMap[:c.oldSize[last]] 104 + c.oldSize = c.oldSize[:last] 105 + }
+66
cue/copy.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + func (c *context) copy(v value) value { 18 + // return v.copy(c) 19 + return rewrite(c, v, rewriteCopy) 20 + } 21 + 22 + func rewriteCopy(ctx *context, v value) (value, bool) { 23 + switch x := v.(type) { 24 + case *nodeRef: 25 + node := ctx.deref(x.node) 26 + if node == x.node { 27 + return x, false 28 + } 29 + return &nodeRef{x.baseValue, node}, false 30 + 31 + case *structLit: 32 + arcs := make(arcs, len(x.arcs)) 33 + emit := x.emit 34 + if emit != nil { 35 + emit = ctx.copy(x.emit) 36 + } 37 + t := x.template 38 + if t != nil { 39 + v := ctx.copy(t) 40 + if isBottom(v) { 41 + return t, false 42 + } 43 + t = v.(*lambdaExpr) 44 + } 45 + obj := &structLit{x.baseValue, emit, t, arcs} 46 + 47 + defer ctx.pushForwards(x, obj).popForwards() 48 + for i, a := range x.arcs { 49 + v := ctx.copy(a.v) 50 + arcs[i] = arc{a.feature, v, nil} 51 + } 52 + return obj, false 53 + 54 + case *lambdaExpr: 55 + arcs := make([]arc, len(x.arcs)) 56 + for i, a := range x.arcs { 57 + arcs[i] = arc{feature: a.feature, v: ctx.copy(a.v)} 58 + } 59 + lambda := &lambdaExpr{x.baseValue, &params{arcs}, nil} 60 + defer ctx.pushForwards(x, lambda).popForwards() 61 + 62 + lambda.value = ctx.copy(x.value) 63 + return lambda, false 64 + } 65 + return v, true 66 + }
+390
cue/debug.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "fmt" 20 + "strconv" 21 + "strings" 22 + ) 23 + 24 + func debugStr(ctx *context, v value) string { 25 + p := &printer{ 26 + ctx: ctx, 27 + w: &bytes.Buffer{}, 28 + } 29 + p.debugStr(v) 30 + return p.w.String() 31 + } 32 + 33 + func (c *context) ref(v scope) string { 34 + v = c.deref(v) 35 + if c.nodeRefs == nil { 36 + c.nodeRefs = map[scope]string{} 37 + } 38 + ref, ok := c.nodeRefs[v] 39 + if ok { 40 + return ref 41 + } 42 + ref = strconv.Itoa(len(c.nodeRefs)) 43 + c.nodeRefs[v] = ref 44 + return ref 45 + } 46 + 47 + func (c *context) indent() { 48 + fmt.Print(strings.Repeat(" ", c.level)) 49 + } 50 + 51 + func (c *context) debugPrint(args ...interface{}) { 52 + if c.trace { 53 + c.indent() 54 + c.println(args...) 55 + } 56 + } 57 + 58 + func (c *context) println(args ...interface{}) { 59 + for i, a := range args { 60 + if i != 0 { 61 + fmt.Print(" ") 62 + } 63 + switch x := a.(type) { 64 + case value: 65 + fmt.Print(debugStr(c, x)) 66 + default: 67 + fmt.Print(x) 68 + } 69 + } 70 + fmt.Println() 71 + } 72 + 73 + // func trace(c *context, r rewriter, n *node) (*context, rewriter, *node) { 74 + // n = derefNode(n) 75 + // name := "evaluate" 76 + // if r != nil { 77 + // name = fmt.Sprintf("%T", r) 78 + // } 79 + // c.debugPrint("---", name, c.ref(n)) 80 + // if n.obj != nil { 81 + // c.debugPrint("<<< node: ", debugStr(c, n.obj)) 82 + // } 83 + // if n.expr != nil { 84 + // c.debugPrint("<<< expr: ", debugStr(c, n.expr)) 85 + // } 86 + // if n.value != nil { 87 + // c.debugPrint("<<< value:", debugStr(c, n.value)) 88 + // } 89 + // c.level++ 90 + // return c, r, n 91 + // } 92 + 93 + // func un(c *context, r rewriter, n *node) { 94 + // n = derefNode(n) 95 + // c.level-- 96 + // if n.expr != nil { 97 + // c.debugPrint(">>> expr:", debugStr(c, n.expr)) 98 + // } 99 + // if n.value != nil { 100 + // c.debugPrint(">>> value:", debugStr(c, n.value)) 101 + // } 102 + // if n.obj != nil { 103 + // c.debugPrint(">>> node: ", debugStr(c, n.obj)) 104 + // } 105 + // } 106 + 107 + func indent(c *context, msg string, x value) (_ *context, m, v string) { 108 + str := debugStr(c, x) 109 + c.debugPrint("...", msg) 110 + c.level++ 111 + c.debugPrint("in:", str) 112 + return c, msg, str 113 + } 114 + 115 + func uni(c *context, msg, oldValue string) { 116 + c.debugPrint("was: ", oldValue) 117 + c.level-- 118 + c.debugPrint("...", msg) 119 + } 120 + 121 + func newPrinter(ctx *context) *printer { 122 + return &printer{ 123 + ctx: ctx, 124 + w: &bytes.Buffer{}, 125 + } 126 + } 127 + 128 + type printer struct { 129 + ctx *context 130 + w *bytes.Buffer 131 + } 132 + 133 + func (p *printer) str(v value) string { 134 + p.debugStr(v) 135 + str := p.w.String() 136 + p.w.Reset() 137 + return str 138 + } 139 + 140 + func (p *printer) label(f label) string { 141 + if p.ctx == nil { 142 + return strconv.Itoa(int(f)) 143 + } 144 + return p.ctx.labelStr(f) 145 + } 146 + 147 + func (p *printer) writef(format string, args ...interface{}) { 148 + fmt.Fprintf(p.w, format, args...) 149 + } 150 + 151 + func (p *printer) write(args ...interface{}) { 152 + fmt.Fprint(p.w, args...) 153 + } 154 + 155 + func (p *printer) debugStr(v interface{}) { 156 + writef := p.writef 157 + write := p.write 158 + switch x := v.(type) { 159 + case nil: 160 + write("*nil*") 161 + case string: 162 + write(x) 163 + case *builtin: 164 + write("builtin:") 165 + p.debugStr(x.Name) 166 + case *nodeRef: 167 + writef("<%s>", p.ctx.ref(x.node)) 168 + // p.debugStr(x.node) 169 + case *selectorExpr: 170 + p.debugStr(x.x) 171 + writef(".%v", p.label(x.feature)) 172 + case *indexExpr: 173 + p.debugStr(x.x) 174 + write("[") 175 + p.debugStr(x.index) 176 + write("]") 177 + case *sliceExpr: 178 + p.debugStr(x.x) 179 + write("[") 180 + if x.lo != nil { 181 + p.debugStr(x.lo) 182 + } 183 + write(":") 184 + if x.hi != nil { 185 + p.debugStr(x.hi) 186 + } 187 + write("]") 188 + case *callExpr: 189 + p.debugStr(x.x) 190 + write(" (") 191 + for i, a := range x.args { 192 + p.debugStr(a) 193 + if i < len(x.args)-1 { 194 + write(",") 195 + } 196 + } 197 + write(")") 198 + case *unaryExpr: 199 + write(x.op) 200 + p.debugStr(x.x) 201 + case *binaryExpr: 202 + write("(") 203 + p.debugStr(x.left) 204 + writef(" %v ", x.op) 205 + p.debugStr(x.right) 206 + write(")") 207 + case *disjunction: 208 + write("(") 209 + for i, v := range x.values { 210 + if i != 0 { 211 + writef(" | ") 212 + } 213 + p.debugStr(v.val) 214 + if v.ambiguous { 215 + writef("!") 216 + } 217 + } 218 + write(")") 219 + case *lambdaExpr: 220 + writef("<%s>(", p.ctx.ref(x)) 221 + p.debugStr(x.params.arcs) 222 + write(")->") 223 + p.debugStr(x.value) 224 + 225 + case *structLit: 226 + if x == nil { 227 + write("*nil node*") 228 + break 229 + } 230 + p.writef("<%s>", p.ctx.ref(x)) 231 + writef("{") 232 + if x.template != nil { 233 + write("<>: ") 234 + p.debugStr(x.template) 235 + write(", ") 236 + } 237 + p.debugStr(x.arcs) 238 + write("}") 239 + 240 + case []arc: 241 + for i, a := range x { 242 + p.debugStr(a) 243 + 244 + if i < len(x)-1 { 245 + p.write(", ") 246 + } 247 + } 248 + 249 + case arc: 250 + n := x.v 251 + orig := p.label(x.feature) 252 + str := strconv.Quote(orig) 253 + if len(orig)+2 == len(str) { 254 + str = str[1 : len(str)-1] 255 + } 256 + p.writef(str) 257 + p.write(": ") 258 + p.debugStr(n) 259 + 260 + case *structComprehension: 261 + writef("{") 262 + p.debugStr(x.clauses) 263 + write(" }") 264 + 265 + case *listComprehension: 266 + writef("[") 267 + p.debugStr(x.clauses) 268 + write(" ]") 269 + 270 + case *yield: 271 + writef(" yield ") 272 + writef("(") 273 + p.debugStr(x.key) 274 + writef("): ") 275 + p.debugStr(x.value) 276 + 277 + case *feed: 278 + writef(" <%s>for ", p.ctx.ref(x.fn)) 279 + a := x.fn.params.arcs[0] 280 + p.writef(p.label(a.feature)) 281 + writef(", ") 282 + a = x.fn.params.arcs[1] 283 + p.writef(p.label(a.feature)) 284 + writef(" in ") 285 + p.debugStr(x.source) 286 + p.debugStr(x.fn.value) 287 + 288 + case *guard: 289 + writef(" if ") 290 + p.debugStr(x.condition) 291 + p.debugStr(x.value) 292 + 293 + case *nullLit: 294 + write("null") 295 + case *boolLit: 296 + writef("%v", x.b) 297 + case *stringLit: 298 + writef("%q", x.str) 299 + case *bytesLit: 300 + str := strconv.Quote(string(x.b)) 301 + str = str[1 : len(str)-1] 302 + writef("'%s'", str) 303 + case *numLit: 304 + if x.k&intKind != 0 { 305 + write(x.v.Text('f')) // also render info 306 + } else { 307 + write(x.v.Text('g')) // also render info 308 + } 309 + case *durationLit: 310 + write(x.d.String()) 311 + case *rangeLit: 312 + write("(") 313 + p.debugStr(x.from) 314 + write("..") 315 + p.debugStr(x.to) 316 + write(")") 317 + case *interpolation: 318 + for i, e := range x.parts { 319 + if i != 0 { 320 + write("+") 321 + } 322 + p.debugStr(e) 323 + } 324 + case *list: 325 + // TODO: do not evaluate 326 + max := maxNum(x.len.evalPartial(p.ctx)) 327 + inCast := false 328 + ellipsis := false 329 + n, ok := max.(*numLit) 330 + if !ok { 331 + // TODO: do not evaluate 332 + min := minNum(x.len.evalPartial(p.ctx)) 333 + n, _ = min.(*numLit) 334 + } 335 + ln := 0 336 + if n != nil { 337 + x, _ := n.v.Int64() 338 + ln = int(x) 339 + } 340 + if !ok || ln > len(x.a) { 341 + if !isTop(max) && !isTop(x.typ) { 342 + p.debugStr(x.len) 343 + write("*[") 344 + p.debugStr(x.typ) 345 + write("]") 346 + if len(x.a) == 0 { 347 + break 348 + } 349 + write("(") 350 + inCast = true 351 + } 352 + ellipsis = true 353 + } 354 + write("[") 355 + for i, a := range x.a { 356 + p.debugStr(a) 357 + if i < len(x.a)-1 { 358 + write(",") 359 + } 360 + } 361 + if ellipsis { 362 + write(", ...") 363 + if !isTop(x.typ) { 364 + p.debugStr(x.typ) 365 + } 366 + } 367 + write("]") 368 + if inCast { 369 + write(")") 370 + } 371 + 372 + case *bottom: 373 + write("_|_") 374 + if x.value != nil || x.msg != "" { 375 + write("(") 376 + if x.value != nil { 377 + writef("%s:", debugStr(p.ctx, x.value)) 378 + } 379 + write(x.msg) 380 + write(")") 381 + } 382 + case *top: 383 + write("_") // ⊤ 384 + case *basicType: 385 + write(x.k.String()) 386 + 387 + default: 388 + panic(fmt.Sprintf("unimplemented type %T", x)) 389 + } 390 + }
+16
cue/doc.go
··· 1 + // Copyright 2018 The 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 cue creates, evaluates and manipulates CUE configurations. 16 + package cue // import "cuelang.org/go/cue"
+219
cue/errors.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "sort" 19 + 20 + "cuelang.org/go/cue/token" 21 + "golang.org/x/exp/errors" 22 + "golang.org/x/exp/errors/fmt" 23 + ) 24 + 25 + type errCode int 26 + 27 + const ( 28 + codeNone errCode = iota 29 + codeFatal 30 + codeNotExist 31 + codeTypeError 32 + codeIncomplete 33 + codeCycle 34 + ) 35 + 36 + func isIncomplete(v value) bool { 37 + if err, ok := v.(*bottom); ok { 38 + return err.code == codeIncomplete 39 + } 40 + return false 41 + } 42 + 43 + func recoverable(v value) bool { 44 + if err, ok := v.(*bottom); ok { 45 + switch err.code { 46 + case codeFatal, 47 + codeCycle: // only recoverable when explicitly handled and discarded 48 + return false 49 + } 50 + } 51 + return true 52 + } 53 + 54 + var errNotExists = &bottom{code: codeNotExist, msg: "undefined value"} 55 + 56 + func exists(v value) bool { 57 + if err, ok := v.(*bottom); ok { 58 + return err.code != codeNotExist 59 + } 60 + return true 61 + } 62 + 63 + // bottom is the bottom of the value lattice. It is subsumed by all values. 64 + type bottom struct { 65 + baseValue 66 + 67 + index *index 68 + code errCode 69 + value value 70 + offendingValue value 71 + replacement evaluated // for cycle resolution 72 + pos source 73 + msg string 74 + 75 + wrapped *bottom 76 + 77 + // TODO: file at which the error was generated in the code. 78 + // File positions of where the error occurred. 79 + } 80 + 81 + func (x *bottom) kind() kind { return bottomKind } 82 + 83 + func (x *bottom) Position() []token.Position { 84 + if x.index != nil && x.index.fset != nil { 85 + return appendPositions(nil, x.index.fset, x.pos) 86 + } 87 + return nil 88 + } 89 + 90 + func appendPositions(pos []token.Position, fset *token.FileSet, src source) []token.Position { 91 + if src != nil { 92 + if src.Pos() != token.NoPos { 93 + return append(pos, fset.Position(src.Pos())) 94 + } 95 + if c := src.computed(); c != nil { 96 + pos = appendPositions(pos, fset, c.x) 97 + pos = appendPositions(pos, fset, c.y) 98 + } 99 + } 100 + return pos 101 + } 102 + 103 + func (x *bottom) Error() string { return fmt.Sprint(x) } 104 + 105 + func (x *bottom) Format(p errors.Printer) error { 106 + p.Print(x.msg) 107 + if p.Detail() && x.index != nil && x.index.fset != nil { 108 + locs := appendLocations(nil, x.index.fset, x.pos) 109 + sort.Strings(locs) 110 + for _, l := range locs { 111 + p.Printf("%s\n", l) 112 + } 113 + } 114 + if x.wrapped != nil { 115 + return x.wrapped // nil interface 116 + } 117 + return nil 118 + } 119 + 120 + func appendLocations(locs []string, fset *token.FileSet, src source) []string { 121 + if src != nil { 122 + if src.Pos() != token.NoPos { 123 + return append(locs, fset.Position(src.Pos()).String()) 124 + } 125 + if c := src.computed(); c != nil { 126 + locs = appendLocations(locs, fset, c.x) 127 + locs = appendLocations(locs, fset, c.y) 128 + } 129 + } 130 + return locs 131 + } 132 + 133 + func cycleError(v evaluated) *bottom { 134 + if err, ok := v.(*bottom); ok && err.code == codeCycle { 135 + return err 136 + } 137 + return nil 138 + } 139 + 140 + // sentinel values used for signalling a type of error. 141 + var ( 142 + // errNonGround = // values are not compatible 143 + cycleSentinel = &bottom{ 144 + code: codeCycle, 145 + msg: "cycle detected", 146 + } 147 + 148 + // unifyNotSupported may be returned when a node implementation cannot 149 + // handle the other type. In this case it may still be the case that the 150 + // converse can be implemented. 151 + // unifyNotSupported node = &bottom{} 152 + ) 153 + 154 + func (idx *index) mkErrUnify(src source, a, b evaluated) evaluated { 155 + if err := firstBottom(a, b); err != nil { 156 + return err 157 + } 158 + e := binSrc(src.Pos(), opUnify, a, b) 159 + // TODO: show string of values and show location of both values. 160 + return idx.mkErr(e, "incompatible values &(%s, %s)", a.kind(), b.kind()) 161 + } 162 + 163 + func (c *context) mkIncompatible(src source, op op, a, b evaluated) evaluated { 164 + if err := firstBottom(a, b); err != nil { 165 + return err 166 + } 167 + e := mkBin(c, src.Pos(), op, a, b) 168 + return c.mkErr(e, "unsupported op %s(%s, %s)", op, a.kind(), b.kind()) 169 + } 170 + 171 + func (idx *index) mkErr(src source, args ...interface{}) *bottom { 172 + e := &bottom{baseValue: src.base(), index: idx, pos: src} 173 + 174 + if v, ok := src.(value); ok { 175 + e.value = v 176 + } 177 + for i, a := range args { 178 + switch x := a.(type) { 179 + case errCode: 180 + e.code = x 181 + case *bottom: 182 + e.wrapped = x 183 + e.offendingValue = x 184 + case value: 185 + e.offendingValue = x 186 + case op: 187 + panic("no longer using offending value and op") 188 + case string: 189 + e.msg += fmt.Sprintf(x, args[i+1:]...) 190 + return e 191 + } 192 + } 193 + if e.code == codeNone && e.wrapped != nil { 194 + e.code = e.wrapped.code 195 + } 196 + return e 197 + } 198 + 199 + func isBottom(n value) bool { 200 + return n.kind() == bottomKind 201 + } 202 + 203 + func firstBottom(v ...value) evaluated { 204 + for _, b := range v { 205 + if isBottom(b) { 206 + return b.(*bottom) 207 + } 208 + } 209 + return nil 210 + } 211 + 212 + func expectType(idx *index, t kind, n evaluated) value { 213 + if isBottom(n) { 214 + return n 215 + } 216 + return idx.mkErr(n, "value should of type %s, found %s", n.kind(), t) 217 + } 218 + 219 + // TODO: consider returning a type or subsuption error for op != opUnify
+490
cue/eval.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "sort" 20 + ) 21 + 22 + func eval(idx *index, v value) evaluated { 23 + ctx := idx.newContext() 24 + return v.evalPartial(ctx) 25 + } 26 + 27 + func (x *nodeRef) evalPartial(ctx *context) (result evaluated) { 28 + return x.node.evalPartial(ctx) 29 + } 30 + 31 + // Atoms 32 + 33 + func (x *top) evalPartial(ctx *context) evaluated { return x } 34 + func (x *bottom) evalPartial(ctx *context) evaluated { return x } 35 + 36 + func (x *basicType) evalPartial(ctx *context) evaluated { return x } 37 + func (x *nullLit) evalPartial(ctx *context) evaluated { return x } 38 + func (x *boolLit) evalPartial(ctx *context) evaluated { return x } 39 + func (x *stringLit) evalPartial(ctx *context) evaluated { return x } 40 + func (x *bytesLit) evalPartial(ctx *context) evaluated { return x } 41 + func (x *numLit) evalPartial(ctx *context) evaluated { return x } 42 + func (x *durationLit) evalPartial(ctx *context) evaluated { return x } 43 + 44 + func (x *lambdaExpr) evalPartial(ctx *context) evaluated { 45 + return ctx.deref(x).(*lambdaExpr) 46 + } 47 + 48 + func (x *selectorExpr) evalPartial(ctx *context) (result evaluated) { 49 + if ctx.trace { 50 + defer uni(indent(ctx, "selectorExpr", x)) 51 + defer func() { ctx.debugPrint("result:", result) }() 52 + } 53 + 54 + e := newEval(ctx, true) 55 + 56 + const msgType = "invalid operation: %[5]s (type %[3]s does not support selection)" 57 + v := e.eval(x.x, structKind|lambdaKind, msgType, x) 58 + 59 + if e.is(v, structKind|lambdaKind, "") { 60 + n, _ := v.(scope).lookup(ctx, x.feature) 61 + if n == nil { 62 + field := ctx.labelStr(x.feature) 63 + // m.foo undefined (type map[string]bool has no field or method foo) 64 + return ctx.mkErr(x, "undefined field %q", field) 65 + } 66 + return n.evalPartial(ctx) 67 + } 68 + return e.err(&selectorExpr{x.baseValue, v, x.feature}) 69 + } 70 + 71 + func (x *indexExpr) evalPartial(ctx *context) (result evaluated) { 72 + if ctx.trace { 73 + defer uni(indent(ctx, "indexExpr", x)) 74 + defer func() { ctx.debugPrint("result:", result) }() 75 + } 76 + 77 + e := newEval(ctx, true) 78 + 79 + const msgType = "invalid operation: %[5]s (type %[3]s does not support indexing)" 80 + const msgIndexType = "invalid %[5]s index %[1]s (type %[3]s)" 81 + 82 + val := e.eval(x.x, listKind|structKind|stringKind|bytesKind, msgType, x) 83 + k := val.kind() 84 + index := e.eval(x.index, stringKind|intKind, msgIndexType, k) 85 + 86 + switch v := val.(type) { 87 + case *structLit: 88 + if e.is(index, stringKind, msgIndexType, k) { 89 + s := index.strValue() 90 + // TODO: must lookup 91 + n, _ := v.lookup(ctx, ctx.strLabel(s)) 92 + if n == nil { 93 + return ctx.mkErr(x, index, "undefined field %q", s) 94 + } 95 + return n 96 + } 97 + case atter: 98 + if e.is(index, intKind, msgIndexType, k) { 99 + i := index.(*numLit).intValue(ctx) 100 + if i < 0 { 101 + const msg = "invalid %[4]s index %[1]s (index must be non-negative)" 102 + return e.mkErr(x.index, index, 0, k, msg) 103 + } 104 + return v.at(ctx, i) 105 + } 106 + } 107 + return e.err(&indexExpr{x.baseValue, val, index}) 108 + } 109 + 110 + // Composit 111 + 112 + func (x *sliceExpr) evalPartial(ctx *context) (result evaluated) { 113 + if ctx.trace { 114 + defer uni(indent(ctx, "sliceExpr", x)) 115 + defer func() { ctx.debugPrint("result:", result) }() 116 + } 117 + 118 + e := newEval(ctx, true) 119 + const msgType = "cannot slice %[2]s (type %[3]s)" 120 + const msgInvalidIndex = "invalid slice index %[1]s (type %[3]s)" 121 + val := e.eval(x.x, listKind|stringKind, msgType) 122 + lo := e.evalAllowNil(x.lo, intKind, msgInvalidIndex) 123 + hi := e.evalAllowNil(x.hi, intKind, msgInvalidIndex) 124 + var low, high *numLit 125 + if lo != nil && e.is(lo, intKind, msgInvalidIndex) { 126 + low = lo.(*numLit) 127 + } 128 + if hi != nil && e.is(hi, intKind, msgInvalidIndex) { 129 + high = hi.(*numLit) 130 + } 131 + if !e.hasErr() { 132 + switch x := val.(type) { 133 + case *list: 134 + return x.slice(ctx, low, high) 135 + case *stringLit: 136 + return x.slice(ctx, low, high) 137 + } 138 + } 139 + return e.err(&sliceExpr{x.baseValue, val, lo, hi}) 140 + } 141 + 142 + // TODO: make a callExpr a binary expression 143 + func (x *callExpr) evalPartial(ctx *context) (result evaluated) { 144 + if ctx.trace { 145 + defer uni(indent(ctx, "callExpr", x)) 146 + defer func() { 147 + ctx.debugPrint("result:", result) 148 + }() 149 + } 150 + 151 + e := newEval(ctx, true) 152 + 153 + fn := e.eval(x.x, lambdaKind, "cannot call non-function %[1]s (type %[3]s)") 154 + args := make([]evaluated, len(x.args)) 155 + for i, a := range x.args { 156 + args[i] = e.evalPartial(a, typeKinds, "never triggers") 157 + } 158 + if !e.hasErr() { 159 + // If we have a template expression, it is either already copied it as 160 + // result of a references, or it is a literal, in which case it is 161 + // trivially fully evaluated. 162 + return fn.(caller).call(ctx, x, args...).evalPartial(ctx) 163 + } 164 + // Construct a simplified call for reporting purposes. 165 + err := &callExpr{x.baseValue, fn, nil} 166 + for _, a := range args { 167 + err.args = append(err.args, a) 168 + } 169 + return e.err(err) 170 + } 171 + 172 + func (x *rangeLit) evalPartial(ctx *context) (result evaluated) { 173 + if ctx.trace { 174 + defer uni(indent(ctx, "rangeLit", x)) 175 + defer func() { ctx.debugPrint("result:", result) }() 176 + } 177 + rngFrom := x.from.evalPartial(ctx) 178 + rngTo := x.to.evalPartial(ctx) 179 + // rngFrom := ctx.manifest(x.from) 180 + // rngTo := ctx.manifest(x.to) 181 + // kind := unifyType(rngFrom.kind(), rngTo.kind()) 182 + // TODO: sufficient to do just this? 183 + kind, _ := matchBinOpKind(opLeq, rngFrom.kind(), rngTo.kind()) 184 + if kind&comparableKind == bottomKind { 185 + return ctx.mkErr(x, "invalid range: must be defined for strings or numbers") 186 + } 187 + // Collapse evaluated nested ranges 188 + if from, ok := rngFrom.(*rangeLit); ok { 189 + rngFrom = from.from.(evaluated) 190 + } 191 + if to, ok := rngTo.(*rangeLit); ok { 192 + rngTo = to.to.(evaluated) 193 + } 194 + rng := &rangeLit{x.baseValue, rngFrom, rngTo} 195 + if !rngFrom.kind().isGround() || !rngTo.kind().isGround() { 196 + return rng 197 + } 198 + // validate range 199 + comp := binOp(ctx, x, opLeq, rngFrom, rngTo) 200 + if isBottom(comp) { 201 + return ctx.mkErr(comp, "invalid range") 202 + } 203 + if !comp.(*boolLit).b { 204 + return ctx.mkErr(x, "for ranges from <= to, found %v > %v", rngFrom, rngTo) 205 + } 206 + if binOp(ctx, x, opEql, rngFrom, rngTo).(*boolLit).b { 207 + return rngFrom 208 + } 209 + return rng 210 + } 211 + 212 + func (x *interpolation) evalPartial(ctx *context) (result evaluated) { 213 + if ctx.trace { 214 + defer uni(indent(ctx, "interpolation", x)) 215 + defer func() { ctx.debugPrint("result:", result) }() 216 + } 217 + buf := bytes.Buffer{} 218 + for _, v := range x.parts { 219 + switch e := ctx.manifest(v).(type) { 220 + case *bottom: 221 + return e 222 + case *stringLit, *numLit, *durationLit: 223 + buf.WriteString(e.strValue()) 224 + default: 225 + k := e.kind() 226 + if k&stringableKind == bottomKind { 227 + return ctx.mkErr(e, "expression in interpolation must evaluate to a number kind or string (found %v)", k) 228 + } 229 + if !k.isGround() { 230 + return ctx.mkErr(e, codeIncomplete, "incomplete") 231 + } 232 + } 233 + } 234 + return &stringLit{x.baseValue, buf.String()} 235 + } 236 + 237 + func (x *list) evalPartial(ctx *context) (result evaluated) { 238 + if ctx.trace { 239 + defer uni(indent(ctx, "list", x)) 240 + defer func() { ctx.debugPrint("result:", result) }() 241 + } 242 + n := x.len.evalPartial(ctx) 243 + t := x.typ.evalPartial(ctx) 244 + if err := firstBottom(n, t); err != nil { 245 + return err 246 + } 247 + a := make([]value, len(x.a)) 248 + changed := false 249 + for i, v := range x.a { 250 + // TODO: don't evaluate now. List elements may refer to other list 251 + // elements. Evaluating them here will cause a cycle evaluating the 252 + // struct field. 253 + e := v.evalPartial(ctx) 254 + changed = changed || e != v 255 + switch e.(type) { 256 + case *bottom: 257 + return e 258 + case value: 259 + a[i] = e 260 + } 261 + } 262 + if !changed && n == x.len && t == x.typ { 263 + return x 264 + } 265 + return &list{x.baseValue, a, t, n} 266 + } 267 + 268 + func (x *structComprehension) evalPartial(ctx *context) evaluated { 269 + obj := &structLit{baseValue: x.baseValue} 270 + result := x.clauses.yield(ctx, func(k, v evaluated) *bottom { 271 + if !k.kind().isAnyOf(stringKind) { 272 + return ctx.mkErr(k, "key must be of type string") 273 + } 274 + if x.isTemplate { 275 + if obj.template == nil { 276 + obj.template = v 277 + } else { 278 + obj.template = mkBin(ctx, x.Pos(), opUnify, obj.template, v) 279 + } 280 + return nil 281 + } 282 + // TODO: improve big O 283 + f := ctx.label(k.strValue(), true) 284 + for i, a := range obj.arcs { 285 + if a.feature == f { 286 + obj.arcs[i].v = mkBin(ctx, x.Pos(), opUnify, a.v, v) 287 + return nil 288 + } 289 + } 290 + obj.arcs = append(obj.arcs, arc{feature: f, v: v}) 291 + return nil 292 + }) 293 + switch { 294 + case result == nil: 295 + case isBottom(result): 296 + return result 297 + default: 298 + panic("should not happen") 299 + } 300 + sort.Stable(obj) 301 + return obj 302 + } 303 + 304 + func (x *listComprehension) evalPartial(ctx *context) evaluated { 305 + list := &list{baseValue: x.baseValue} 306 + result := x.clauses.yield(ctx, func(k, v evaluated) *bottom { 307 + if !k.kind().isAnyOf(intKind) { 308 + return ctx.mkErr(k, "key must be of type int") 309 + } 310 + list.a = append(list.a, v.evalPartial(ctx)) 311 + return nil 312 + }) 313 + switch { 314 + case result == nil: 315 + case isBottom(result): 316 + return result 317 + default: 318 + panic("should not happen") 319 + } 320 + list.initLit() 321 + return list 322 + } 323 + 324 + func (x *feed) evalPartial(ctx *context) evaluated { return x } 325 + func (x *guard) evalPartial(ctx *context) evaluated { return x } 326 + func (x *yield) evalPartial(ctx *context) evaluated { return x } 327 + 328 + func (x *structLit) evalPartial(ctx *context) (result evaluated) { 329 + if ctx.trace { 330 + defer uni(indent(ctx, "struct eval", x)) 331 + defer func() { ctx.debugPrint("result:", result) }() 332 + } 333 + x = ctx.deref(x).(*structLit) 334 + 335 + // TODO: Handle cycle? 336 + return x 337 + } 338 + 339 + func (x *disjunction) evalPartial(ctx *context) (result evaluated) { 340 + if ctx.trace { 341 + defer uni(indent(ctx, "disjunction", x)) 342 + defer func() { ctx.debugPrint("result:", result) }() 343 + } 344 + 345 + dn := &disjunction{x.baseValue, make([]dValue, 0, len(x.values))} 346 + changed := false 347 + for _, v := range x.values { 348 + n := v.val.evalPartial(ctx) 349 + changed = changed || n != v.val 350 + dn.add(ctx, n, v.ambiguous) 351 + } 352 + // TODO: move to evaluator 353 + if !changed { 354 + return x 355 + } 356 + return dn.simplify(ctx, x).(evaluated) 357 + } 358 + 359 + func (x *disjunction) manifest(ctx *context) (result evaluated) { 360 + switch len(x.values) { 361 + case 0: 362 + return x.simplify(ctx, x).(evaluated) // force error 363 + case 1: 364 + return x.values[0].val.(evaluated) 365 + default: 366 + for _, d := range x.values { 367 + if validate(ctx, d.val) != nil { 368 + continue 369 + } 370 + if d.ambiguous { 371 + // better error 372 + return ctx.mkErr(x, "ambiguous disjunction") 373 + } 374 + return d.val.(evaluated) 375 + } 376 + return ctx.mkErr(x, "empty disjunction after evaluation") 377 + } 378 + } 379 + 380 + func (x *binaryExpr) evalPartial(ctx *context) (result evaluated) { 381 + if ctx.trace { 382 + defer uni(indent(ctx, "binaryExpr", x)) 383 + defer func() { ctx.debugPrint("result:", result) }() 384 + } 385 + var left, right evaluated 386 + 387 + if x.op != opUnify { 388 + left = ctx.manifest(x.left) 389 + right = ctx.manifest(x.right) 390 + 391 + // TODO: allow comparing to a literal bottom only. Find something more 392 + // principled perhaps. One should especially take care that two values 393 + // evaluating to bottom don't evaluate to true. For now we check for 394 + // bottom here and require that one of the values be a bottom literal. 395 + if l, r := isBottom(x.left), isBottom(x.right); l || r { 396 + leftBottom := isBottom(left) 397 + rightBottom := isBottom(right) 398 + switch x.op { 399 + case opEql: 400 + return &boolLit{x.baseValue, leftBottom == rightBottom} 401 + case opNeq: 402 + return &boolLit{x.baseValue, leftBottom != rightBottom} 403 + } 404 + } 405 + } else { 406 + left = x.left.evalPartial(ctx) 407 + right = x.right.evalPartial(ctx) 408 + 409 + if err := cycleError(left); err != nil && right.kind().isAtom() { 410 + return ctx.delayConstraint(right, 411 + mkBin(ctx, x.Pos(), opUnify, x.left, right)) 412 + } 413 + if err := cycleError(right); err != nil && left.kind().isAtom() { 414 + return ctx.delayConstraint(left, 415 + mkBin(ctx, x.Pos(), opUnify, left, x.right)) 416 + } 417 + 418 + // check if it is a cycle that can be unwrapped. 419 + // If other value is a cycle or list, return the original forwarded, 420 + // but ensure the value is not cached. Object/list error? 421 + } 422 + return binOp(ctx, x, x.op, left, right) 423 + } 424 + 425 + func (x *unaryExpr) evalPartial(ctx *context) (result evaluated) { 426 + if ctx.trace { 427 + defer uni(indent(ctx, "unaryExpr", x)) 428 + defer func() { ctx.debugPrint("result:", result) }() 429 + } 430 + 431 + return evalUnary(ctx, x, x.op, x.x) 432 + } 433 + 434 + func evalUnary(ctx *context, src source, op op, x value) evaluated { 435 + v := ctx.manifest(x) 436 + 437 + const numeric = numKind | durationKind 438 + kind := v.kind() 439 + switch op { 440 + case opSub: 441 + if kind&numeric == bottomKind { 442 + return ctx.mkErr(src, "unary '-' requires numeric value, found %s", kind) 443 + } 444 + switch v := v.(type) { 445 + case *numLit: 446 + f := *v 447 + f.v.Neg(&v.v) 448 + return &f 449 + case *durationLit: 450 + d := *v 451 + d.d = -d.d 452 + return &d 453 + } 454 + fallthrough 455 + 456 + case opAdd: 457 + if kind&numeric == bottomKind { 458 + return ctx.mkErr(src, "unary '+' requires numeric value, found %s", kind) 459 + } 460 + if kind&^(numeric|nonGround|referenceKind) == bottomKind { 461 + return v 462 + } 463 + switch v := v.(type) { 464 + case *numLit, *durationLit: 465 + return v 466 + case *top: 467 + return &basicType{v.baseValue, numeric | nonGround} 468 + case *basicType: 469 + return &basicType{v.baseValue, (v.k & numeric) | nonGround} 470 + case *rangeLit: 471 + from := evalUnary(ctx, src, op, v.from) 472 + to := evalUnary(ctx, src, op, v.to) 473 + return &rangeLit{src.base(), from, to} 474 + } 475 + 476 + case opNot: 477 + if kind&boolKind == bottomKind { 478 + return ctx.mkErr(src, "unary '!' requires bool value, found %s", kind) 479 + } 480 + switch v := v.(type) { 481 + case *top: 482 + return &basicType{v.baseValue, boolKind | nonGround} 483 + case *basicType: 484 + return v 485 + case *boolLit: 486 + return &boolLit{src.base(), !v.b} 487 + } 488 + } 489 + return ctx.mkErr(src, "invalid operand type %v for unary operator %v", v, op) 490 + }
+140
cue/evaluator.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + func (c *context) manifest(v value) evaluated { 18 + evaluated := v.evalPartial(c) 19 + for { 20 + x, ok := evaluated.(*disjunction) 21 + if !ok { 22 + break 23 + } 24 + evaluated = x.manifest(c) 25 + } 26 + return evaluated 27 + } 28 + 29 + type evaluator struct { 30 + ctx *context 31 + bottom []*bottom 32 + } 33 + 34 + const ( 35 + // (fmt, evaluated, orig, gotKind, wantKind) 36 + // "invalid [string index] -1 (index must be non-negative)" 37 + // "invalid operation: %[1]s (type %[3] does not support indexing)" 38 + // msgType = "invalid %s %s (must be type %s)" 39 + msgGround = "invalid non-ground value %[1]s (must be concrete %[4]s)" 40 + ) 41 + 42 + func newEval(ctx *context, manifest bool) evaluator { 43 + return evaluator{ctx: ctx} 44 + } 45 + 46 + func (e *evaluator) hasErr() bool { 47 + return len(e.bottom) > 0 48 + } 49 + 50 + func (e *evaluator) mkErr(orig, eval value, code errCode, want kind, desc string, args ...interface{}) (err *bottom) { 51 + args = append([]interface{}{ 52 + eval, 53 + code, 54 + desc, // format string 55 + eval, // 1 56 + orig, // 2 57 + eval.kind(), // 3 58 + want}, // 4 59 + args...) 60 + for i := 3; i < len(args); i++ { 61 + switch v := args[i].(type) { 62 + case value: 63 + args[i] = debugStr(e.ctx, v) 64 + } 65 + } 66 + err = e.ctx.mkErr(orig, args...) 67 + // TODO: maybe replace with more specific type error. 68 + for i, old := range e.bottom { 69 + if old == eval { 70 + e.bottom[i] = err 71 + return err 72 + } 73 + } 74 + e.bottom = append(e.bottom, err) 75 + return err 76 + } 77 + 78 + func (e *evaluator) eval(v value, want kind, desc string, extraArgs ...interface{}) evaluated { 79 + eval := e.ctx.manifest(v) 80 + 81 + if isBottom(eval) { 82 + e.bottom = append(e.bottom, eval.(*bottom)) 83 + return eval 84 + } 85 + got := eval.kind() 86 + if got&want == bottomKind { 87 + return e.mkErr(v, eval, codeTypeError, want, desc, extraArgs...) 88 + } 89 + if !got.isGround() { 90 + return e.mkErr(v, eval, codeIncomplete, want, msgGround, extraArgs...) 91 + } 92 + return eval 93 + } 94 + 95 + func (e *evaluator) evalPartial(v value, want kind, desc string, extraArgs ...interface{}) evaluated { 96 + eval := v.evalPartial(e.ctx) 97 + if isBottom(eval) { 98 + // handle incomplete errors separately? 99 + e.bottom = append(e.bottom, eval.(*bottom)) 100 + return eval 101 + } 102 + got := eval.kind() 103 + if got&want == bottomKind { 104 + return e.mkErr(v, eval, codeTypeError, want, desc, extraArgs...) 105 + } 106 + return eval 107 + } 108 + 109 + func (e *evaluator) evalAllowNil(v value, want kind, desc string, extraArgs ...interface{}) evaluated { 110 + if v == nil { 111 + return nil 112 + } 113 + return e.eval(v, want, desc, extraArgs...) 114 + } 115 + 116 + func (e *evaluator) is(v value, want kind, desc string, args ...interface{}) bool { 117 + if isBottom(v) { 118 + // Even though errors are ground, we treat them as not allowed. 119 + return false 120 + } 121 + got := v.kind() 122 + if got&want == bottomKind { 123 + e.mkErr(v, v, codeTypeError, want, desc, args...) 124 + return false 125 + } 126 + // groundness must already have been checked. 127 + return true 128 + } 129 + 130 + func (e *evaluator) err(v value) evaluated { 131 + // if bottom is a fatal (not incomplete) error, return that. 132 + // otherwise, try to extract a fatal error from the given value. 133 + // otherwise return an incomplete error with the given value as offending. 134 + for _, b := range e.bottom { 135 + if b.code != codeIncomplete { 136 + return b 137 + } 138 + } 139 + return e.ctx.mkErr(v, codeIncomplete, e.bottom[0]) 140 + }
+403
cue/export.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "strconv" 20 + "strings" 21 + "unicode/utf8" 22 + 23 + "cuelang.org/go/cue/ast" 24 + "cuelang.org/go/cue/token" 25 + ) 26 + 27 + func export(ctx *context, v value) ast.Expr { 28 + e := exporter{ctx} 29 + return e.expr(v) 30 + } 31 + 32 + type exporter struct { 33 + ctx *context 34 + } 35 + 36 + func (p *exporter) label(f label) *ast.Ident { 37 + orig := p.ctx.labelStr(f) 38 + str := strconv.Quote(orig) 39 + if len(orig)+2 == len(str) { 40 + str = str[1 : len(str)-1] 41 + } 42 + return &ast.Ident{Name: str} 43 + } 44 + 45 + func (p *exporter) ident(str string) *ast.Ident { 46 + return &ast.Ident{Name: str} 47 + } 48 + 49 + func (p *exporter) clause(v value) (n ast.Clause, next yielder) { 50 + switch x := v.(type) { 51 + case *feed: 52 + feed := &ast.ForClause{ 53 + Value: p.label(x.fn.params.arcs[1].feature), 54 + Source: p.expr(x.source), 55 + } 56 + key := x.fn.params.arcs[0] 57 + if p.ctx.labelStr(key.feature) != "_" { 58 + feed.Key = p.label(key.feature) 59 + } 60 + return feed, x.fn.value.(yielder) 61 + 62 + case *guard: 63 + return &ast.IfClause{Condition: p.expr(x.condition)}, x.value 64 + } 65 + panic(fmt.Sprintf("unsupported clause type %T", v)) 66 + } 67 + 68 + func (p *exporter) expr(v value) ast.Expr { 69 + // TODO: also add position information. 70 + switch x := v.(type) { 71 + case *builtin: 72 + return &ast.Ident{Name: x.Name} 73 + case *nodeRef: 74 + return nil 75 + case *selectorExpr: 76 + n := p.expr(x.x) 77 + if n == nil { 78 + return p.label(x.feature) 79 + } 80 + return &ast.SelectorExpr{X: n, Sel: p.label(x.feature)} 81 + case *indexExpr: 82 + return &ast.IndexExpr{X: p.expr(x.x), Index: p.expr(x.index)} 83 + case *sliceExpr: 84 + return &ast.SliceExpr{ 85 + X: p.expr(x.x), 86 + Low: p.expr(x.lo), 87 + High: p.expr(x.hi), 88 + } 89 + case *callExpr: 90 + call := &ast.CallExpr{Fun: p.expr(x.x)} 91 + for _, a := range x.args { 92 + call.Args = append(call.Args, p.expr(a)) 93 + } 94 + return call 95 + case *unaryExpr: 96 + return &ast.UnaryExpr{Op: opMap[x.op], X: p.expr(x.x)} 97 + case *binaryExpr: 98 + return &ast.BinaryExpr{ 99 + X: p.expr(x.left), 100 + Op: opMap[x.op], Y: p.expr(x.right), 101 + } 102 + case *disjunction: 103 + if len(x.values) == 1 { 104 + return p.expr(x.values[0].val) 105 + } 106 + bin := &ast.BinaryExpr{ 107 + X: p.expr(x.values[0].val), 108 + Op: token.DISJUNCTION, 109 + Y: p.expr(x.values[1].val), 110 + } 111 + for _, v := range x.values[2:] { 112 + bin = &ast.BinaryExpr{X: bin, Op: token.DISJUNCTION, Y: p.expr(v.val)} 113 + } 114 + return bin 115 + // case *lambdaExpr: 116 + 117 + // p.debugStr(x.params.arcs) 118 + // write(")->") 119 + // p.debugStr(x.value) 120 + 121 + case *structLit: 122 + obj := &ast.StructLit{} 123 + if x.emit != nil { 124 + obj.Elts = append(obj.Elts, &ast.EmitDecl{Expr: p.expr(x.emit)}) 125 + } 126 + for _, a := range x.arcs { 127 + obj.Elts = append(obj.Elts, &ast.Field{ 128 + Label: p.label(a.feature), 129 + Value: p.expr(a.v), 130 + }) 131 + } 132 + return obj 133 + 134 + case *structComprehension: 135 + var clauses []ast.Clause 136 + for y, next := p.clause(x.clauses); ; y, next = p.clause(next) { 137 + clauses = append(clauses, y) 138 + if yield, ok := next.(*yield); ok { 139 + return &ast.StructLit{Elts: []ast.Decl{ 140 + &ast.ComprehensionDecl{ 141 + Field: &ast.Field{ 142 + Label: p.expr(yield.key).(ast.Label), 143 + Value: p.expr(yield.value), 144 + }, 145 + Clauses: clauses, 146 + }, 147 + }} 148 + } 149 + } 150 + 151 + case *listComprehension: 152 + var clauses []ast.Clause 153 + for y, next := p.clause(x.clauses); ; y, next = p.clause(next) { 154 + clauses = append(clauses, y) 155 + if yield, ok := next.(*yield); ok { 156 + return &ast.ListComprehension{ 157 + Expr: p.expr(yield.value), 158 + Clauses: clauses, 159 + } 160 + } 161 + } 162 + 163 + case *nullLit: 164 + return p.ident("null") 165 + 166 + case *boolLit: 167 + return p.ident(fmt.Sprint(x.b)) 168 + 169 + case *stringLit: 170 + return &ast.BasicLit{ 171 + Kind: token.STRING, 172 + Value: quote(x.str, '"'), 173 + } 174 + 175 + case *bytesLit: 176 + return &ast.BasicLit{ 177 + Kind: token.STRING, 178 + Value: quote(string(x.b), '\''), 179 + } 180 + 181 + case *numLit: 182 + if x.k&intKind != 0 { 183 + return &ast.BasicLit{ 184 + Kind: token.INT, 185 + Value: x.v.Text('f'), 186 + } 187 + } 188 + return &ast.BasicLit{ 189 + Kind: token.FLOAT, 190 + Value: x.v.Text('g'), 191 + } 192 + 193 + case *durationLit: 194 + panic("unimplemented") 195 + 196 + case *rangeLit: 197 + return &ast.BinaryExpr{ 198 + X: p.expr(x.from), 199 + Op: token.RANGE, 200 + Y: p.expr(x.to), 201 + } 202 + 203 + case *interpolation: 204 + t := &ast.Interpolation{} 205 + multiline := false 206 + // TODO: mark formatting in interpolation itself. 207 + for i := 0; i < len(x.parts); i += 2 { 208 + str := x.parts[i].(*stringLit).str 209 + if strings.IndexByte(str, '\n') >= 0 { 210 + multiline = true 211 + break 212 + } 213 + } 214 + quote := `"` 215 + if multiline { 216 + quote = `"""` 217 + } 218 + prefix := quote 219 + suffix := `\(` 220 + for i, e := range x.parts { 221 + if i%2 == 1 { 222 + t.Elts = append(t.Elts, p.expr(e)) 223 + } else { 224 + buf := []byte(prefix) 225 + if i == len(x.parts)-1 { 226 + suffix = quote 227 + } 228 + str := e.(*stringLit).str 229 + if multiline { 230 + buf = appendEscapeMulti(buf, str, '"') 231 + } else { 232 + buf = appendEscaped(buf, str, '"', true) 233 + } 234 + buf = append(buf, suffix...) 235 + t.Elts = append(t.Elts, &ast.BasicLit{ 236 + Kind: token.STRING, 237 + Value: string(buf), 238 + }) 239 + } 240 + prefix = ")" 241 + } 242 + return t 243 + 244 + case *list: 245 + list := &ast.ListLit{} 246 + var expr ast.Expr = list 247 + for _, e := range x.a { 248 + list.Elts = append(list.Elts, p.expr(e)) 249 + } 250 + max := maxNumRaw(x.len) 251 + num, ok := max.(*numLit) 252 + if !ok { 253 + min := minNumRaw(x.len) 254 + num, _ = min.(*numLit) 255 + } 256 + ln := 0 257 + if num != nil { 258 + x, _ := num.v.Int64() 259 + ln = int(x) 260 + } 261 + if !ok || ln > len(x.a) { 262 + list.Type = p.expr(x.typ) 263 + if !isTop(max) && !isTop(x.typ) { 264 + expr = &ast.BinaryExpr{ 265 + X: &ast.BinaryExpr{ 266 + X: p.expr(x.len), 267 + Op: token.MUL, 268 + Y: &ast.ListLit{Elts: []ast.Expr{ 269 + p.expr(x.typ), 270 + }}, 271 + }, 272 + Op: token.UNIFY, 273 + Y: list, 274 + } 275 + 276 + } 277 + } 278 + return expr 279 + 280 + case *bottom: 281 + err := &ast.BottomLit{} 282 + comment := &ast.Comment{Text: "// " + x.msg} 283 + err.AddComment(&ast.CommentGroup{ 284 + Line: true, 285 + Position: 1, 286 + List: []*ast.Comment{comment}, 287 + }) 288 + return err 289 + 290 + case *top: 291 + return p.ident("_") 292 + 293 + case *basicType: 294 + return p.ident(x.k.String()) 295 + 296 + case *lambdaExpr: 297 + return p.ident("TODO: LAMBDA") 298 + 299 + default: 300 + panic(fmt.Sprintf("unimplemented type %T", x)) 301 + } 302 + } 303 + 304 + // quote quotes the given string. 305 + func quote(str string, quote byte) string { 306 + if strings.IndexByte(str, '\n') < 0 { 307 + buf := []byte{quote} 308 + buf = appendEscaped(buf, str, quote, true) 309 + buf = append(buf, quote) 310 + return string(buf) 311 + } 312 + buf := []byte{quote, quote, quote} 313 + buf = append(buf, multiSep...) 314 + buf = appendEscapeMulti(buf, str, quote) 315 + buf = append(buf, quote, quote, quote) 316 + return string(buf) 317 + } 318 + 319 + // TODO: consider the best indent strategy. 320 + const multiSep = "\n " 321 + 322 + func appendEscapeMulti(buf []byte, str string, quote byte) []byte { 323 + // TODO(perf) 324 + a := strings.Split(str, "\n") 325 + for _, s := range a { 326 + buf = appendEscaped(buf, s, quote, true) 327 + buf = append(buf, multiSep...) 328 + } 329 + return buf 330 + } 331 + 332 + const lowerhex = "0123456789abcdef" 333 + 334 + func appendEscaped(buf []byte, s string, quote byte, graphicOnly bool) []byte { 335 + for width := 0; len(s) > 0; s = s[width:] { 336 + r := rune(s[0]) 337 + width = 1 338 + if r >= utf8.RuneSelf { 339 + r, width = utf8.DecodeRuneInString(s) 340 + } 341 + if width == 1 && r == utf8.RuneError { 342 + buf = append(buf, `\x`...) 343 + buf = append(buf, lowerhex[s[0]>>4]) 344 + buf = append(buf, lowerhex[s[0]&0xF]) 345 + continue 346 + } 347 + buf = appendEscapedRune(buf, r, quote, graphicOnly) 348 + } 349 + return buf 350 + } 351 + 352 + func appendEscapedRune(buf []byte, r rune, quote byte, graphicOnly bool) []byte { 353 + var runeTmp [utf8.UTFMax]byte 354 + if r == rune(quote) || r == '\\' { // always backslashed 355 + buf = append(buf, '\\') 356 + buf = append(buf, byte(r)) 357 + return buf 358 + } 359 + // TODO(perf): IsGraphic calls IsPrint. 360 + if strconv.IsPrint(r) || graphicOnly && strconv.IsGraphic(r) { 361 + n := utf8.EncodeRune(runeTmp[:], r) 362 + buf = append(buf, runeTmp[:n]...) 363 + return buf 364 + } 365 + switch r { 366 + case '\a': 367 + buf = append(buf, `\a`...) 368 + case '\b': 369 + buf = append(buf, `\b`...) 370 + case '\f': 371 + buf = append(buf, `\f`...) 372 + case '\n': 373 + buf = append(buf, `\n`...) 374 + case '\r': 375 + buf = append(buf, `\r`...) 376 + case '\t': 377 + buf = append(buf, `\t`...) 378 + case '\v': 379 + buf = append(buf, `\v`...) 380 + default: 381 + switch { 382 + case r < ' ': 383 + // Invalid for strings, only bytes. 384 + buf = append(buf, `\x`...) 385 + buf = append(buf, lowerhex[byte(r)>>4]) 386 + buf = append(buf, lowerhex[byte(r)&0xF]) 387 + case r > utf8.MaxRune: 388 + r = 0xFFFD 389 + fallthrough 390 + case r < 0x10000: 391 + buf = append(buf, `\u`...) 392 + for s := 12; s >= 0; s -= 4 { 393 + buf = append(buf, lowerhex[r>>uint(s)&0xF]) 394 + } 395 + default: 396 + buf = append(buf, `\U`...) 397 + for s := 28; s >= 0; s -= 4 { 398 + buf = append(buf, lowerhex[r>>uint(s)&0xF]) 399 + } 400 + } 401 + } 402 + return buf 403 + }
+188
cue/export_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "fmt" 20 + "log" 21 + "strings" 22 + "testing" 23 + 24 + "cuelang.org/go/cue/format" 25 + ) 26 + 27 + func TestExport(t *testing.T) { 28 + testCases := []struct { 29 + raw bool 30 + in, out string 31 + }{{ 32 + in: `"hello"`, 33 + out: `"hello"`, 34 + }, { 35 + in: `'hello'`, 36 + out: `'hello'`, 37 + }, { 38 + in: `'hello\nworld'`, 39 + out: "'''" + 40 + multiSep + "hello" + 41 + multiSep + "world" + 42 + multiSep + "'''", 43 + }, { 44 + in: `"hello\nworld"`, 45 + out: `"""` + 46 + multiSep + "hello" + 47 + multiSep + "world" + 48 + multiSep + `"""`, 49 + }, { 50 + in: "{ a: 1, b: a + 2, c: null, d: true, e: _, f: string }", 51 + out: unindent(` 52 + { 53 + a: 1 54 + b: 3 55 + c: null 56 + d: true 57 + e: _ 58 + f: string 59 + }`), 60 + }, { 61 + in: `{ a: { b: 2.0, s: "abc" }, b: a.b, c: a.c, d: a["d"], e: a.t[2:3] }`, 62 + out: unindent(` 63 + { 64 + a: { 65 + b: 2.0 66 + s: "abc" 67 + } 68 + b: 2.0 69 + c: _|_ // undefined field "c" 70 + d: _|_ // undefined field "d" 71 + e: _|_ // undefined field "t" 72 + }`), 73 + }, { 74 + in: `{ 75 + a: 5*[int] 76 + a: [1, 2, ...] 77 + b: 0..5*[int] 78 + b: [1, 2, ...] 79 + c: 3..5*[int] 80 + c: [1, 2, ...] 81 + d: 2.._*[int] 82 + d: [1, 2, ...] 83 + e: [...int] 84 + e: [1, 2, ...] 85 + f: [1, 2, ...] 86 + }`, 87 + out: unindent(` 88 + { 89 + a: 5*[int] & [1, 2, ...int] 90 + b: 2..5*[int] & [1, 2, ...int] 91 + c: 3..5*[int] & [1, 2, ...int] 92 + d: [1, 2, ...int] 93 + e: [1, 2, ...int] 94 + f: [1, 2, ...] 95 + }`), 96 + }, { 97 + in: `{ 98 + a: (0.._)*[int] 99 + a: (0.._)*[...int] 100 + }`, 101 + out: unindent(` 102 + { 103 + a: [...int] 104 + }`), 105 + }, { 106 + raw: true, 107 + in: `{ a: { b: [] }, c: a.b, d: a["b"] }`, 108 + out: unindent(` 109 + { 110 + a b: [] 111 + c: a.b 112 + d: a["b"] 113 + }`), 114 + }, { 115 + raw: true, 116 + in: `{ a: "foo" | "bar" | string, b: a[2:3] }`, 117 + out: unindent(` 118 + { 119 + a: "foo" | "bar" | string 120 + b: a[2:3] 121 + }`), 122 + }, { 123 + raw: true, 124 + in: `{ a: [1, 2], b: { "\(k)": v for k, v in a if a > 1 } }`, 125 + out: unindent(` 126 + { 127 + a: [1, 2] 128 + b: { 129 + "\(k)": v for k, v in a if a > 1 130 + } 131 + }`), 132 + }, { 133 + raw: true, 134 + in: `{ a: [1, 2], b: [ v for k, v in a ] }`, 135 + out: unindent(` 136 + { 137 + a: [1, 2] 138 + b: [ v for k, v in a ] 139 + }`), 140 + }, { 141 + raw: true, 142 + in: `{ a: 0..10, b: "Count: \(a) times" }`, 143 + out: unindent(` 144 + { 145 + a: 0..10 146 + b: "Count: \(a) times" 147 + }`), 148 + }} 149 + for _, tc := range testCases { 150 + t.Run("", func(t *testing.T) { 151 + body := fmt.Sprintf("Test: %s", tc.in) 152 + ctx, obj := compileFile(t, body) 153 + ctx.trace = *traceOn 154 + var root value = obj 155 + if !tc.raw { 156 + root = testResolve(ctx, obj, evalFull) 157 + } 158 + t.Log(debugStr(ctx, root)) 159 + 160 + n := root.(*structLit).arcs[0].v 161 + v := newValueRoot(ctx, n) 162 + 163 + buf := &bytes.Buffer{} 164 + err := format.Node(buf, export(ctx, v.eval(ctx))) 165 + if err != nil { 166 + log.Fatal(err) 167 + } 168 + if got := buf.String(); got != tc.out { 169 + t.Errorf("\ngot %v;\nwant %v", got, tc.out) 170 + } 171 + }) 172 + } 173 + } 174 + 175 + func unindent(s string) string { 176 + lines := strings.Split(s, "\n")[1:] 177 + ws := lines[0][:len(lines[0])-len(strings.TrimLeft(lines[0], " \t"))] 178 + for i, s := range lines { 179 + if s == "" { 180 + continue 181 + } 182 + if !strings.HasPrefix(s, ws) { 183 + panic("invalid indentation") 184 + } 185 + lines[i] = lines[i][len(ws):] 186 + } 187 + return strings.Join(lines, "\n") 188 + }
+251
cue/instance.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "cuelang.org/go/cue/ast" 19 + "cuelang.org/go/cue/build" 20 + "cuelang.org/go/cue/token" 21 + "cuelang.org/go/internal" 22 + ) 23 + 24 + // An Instance defines a single configuration based on a collection of 25 + // underlying CUE files. 26 + type Instance struct { 27 + *index 28 + 29 + rootStruct *structLit // the struct to insert root values into 30 + rootValue value // the value to evaluate: may add comprehensions 31 + 32 + // scope is used as an additional top-level scope between the package scope 33 + // and the predeclared identifiers. 34 + scope *structLit 35 + 36 + ImportPath string 37 + Dir string 38 + Name string 39 + 40 + Incomplete bool // true if Pkg and all its dependencies are free of errors 41 + Err error // non-nil if the package had errors 42 + 43 + inst *build.Instance 44 + 45 + complete bool // for cycle detection 46 + } 47 + 48 + // NewInstance creates a new instance. Use Insert to populate the instance. 49 + func (x *index) NewInstance(p *build.Instance) *Instance { 50 + st := &structLit{baseValue: baseValue{nil}} 51 + i := &Instance{ 52 + index: x, 53 + rootStruct: st, 54 + rootValue: st, 55 + inst: p, 56 + } 57 + if p != nil { 58 + i.ImportPath = p.ImportPath 59 + i.Dir = p.Dir 60 + i.Name = p.PkgName 61 + if p.Err != nil { 62 + i.setError(p.Err) 63 + } 64 + } 65 + return i 66 + } 67 + 68 + func (inst *Instance) setError(err error) { 69 + inst.Err = err 70 + inst.Incomplete = true 71 + } 72 + 73 + func (inst *Instance) eval(ctx *context) evaluated { 74 + v := ctx.manifest(inst.rootValue) 75 + if s, ok := v.(*structLit); ok && s.emit != nil { 76 + v = ctx.manifest(s.emit) 77 + } 78 + // manifest to benefit from validation step. 79 + // TODO: consider not doing manifest. 80 + return ctx.manifest(v) 81 + } 82 + 83 + func init() { 84 + internal.EvalExpr = func(value, expr interface{}) interface{} { 85 + v := value.(Value) 86 + e := expr.(ast.Expr) 87 + ctx := v.idx.newContext() 88 + return newValueRoot(ctx, evalExpr(v.idx, v.eval(ctx), e)) 89 + } 90 + } 91 + 92 + func evalExpr(idx *index, x value, expr ast.Expr) evaluated { 93 + if isBottom(x) { 94 + return idx.mkErr(x, "error evaluating instance: %v", x) 95 + } 96 + obj, ok := x.(*structLit) 97 + if !ok { 98 + return idx.mkErr(obj, "instance is not a struct") 99 + } 100 + 101 + v := newVisitor(idx, nil, nil, obj) 102 + return eval(idx, v.walk(expr)) 103 + } 104 + 105 + func (inst *Instance) evalExpr(ctx *context, expr ast.Expr) evaluated { 106 + root := inst.eval(ctx) 107 + if isBottom(root) { 108 + return ctx.mkErr(root, "error evaluating instance") 109 + } 110 + obj, ok := root.(*structLit) 111 + if !ok { 112 + return ctx.mkErr(obj, "instance is not a struct") 113 + } 114 + v := newVisitor(ctx.index, inst.inst, nil, obj) 115 + return v.walk(expr).evalPartial(ctx) 116 + } 117 + 118 + // Value returns the root value of the configuration. If the configuration 119 + // defines in emit value, it will be that value. Otherwise it will be all 120 + // top-level values. 121 + func (inst *Instance) Value() Value { 122 + ctx := inst.newContext() 123 + return newValueRoot(ctx, inst.eval(ctx)) 124 + } 125 + 126 + // Eval evaluates an expression within an existing instance. 127 + // 128 + // Expressions may refer to builtin packages if they can be uniquely identified. 129 + func (inst *Instance) Eval(expr ast.Expr) Value { 130 + ctx := inst.newContext() 131 + result := inst.evalExpr(ctx, expr) 132 + return newValueRoot(ctx, result) 133 + } 134 + 135 + // Merge unifies the given instances into a single one. 136 + // 137 + // Errors regarding conflicts are included in the result, but not reported, so 138 + // that these will only surface during manifestation. This allows 139 + func Merge(inst ...*Instance) *Instance { 140 + switch len(inst) { 141 + case 0: 142 + return nil 143 + case 1: 144 + return inst[0] 145 + } 146 + 147 + for _, i := range inst { 148 + if i.Err != nil { 149 + return i 150 + } 151 + } 152 + 153 + ctx := inst[0].newContext() 154 + 155 + merged := stripTemplates(ctx, inst[0].rootValue) 156 + 157 + for _, i := range inst[1:] { 158 + x := stripTemplates(ctx, i.rootValue) 159 + merged = mkBin(ctx, token.NoPos, opUnify, merged, x) 160 + } 161 + 162 + st, ok := ctx.manifest(merged).(*structLit) 163 + if !ok { 164 + return nil 165 + } 166 + 167 + p := &Instance{ 168 + rootStruct: st, 169 + rootValue: merged, 170 + index: ctx.index, 171 + complete: true, 172 + } 173 + return p 174 + } 175 + 176 + // Build creates a new instance from the build instances, allowing unbound 177 + // identifier to bind to the top-level field in inst. The top-level fields in 178 + // inst take precedence over predeclared identifier and builtin functions. 179 + func (inst *Instance) Build(p *build.Instance) *Instance { 180 + p.Complete() 181 + 182 + idx := inst.index 183 + 184 + i := idx.NewInstance(p) 185 + if i.Err != nil { 186 + return i 187 + } 188 + 189 + ctx := inst.newContext() 190 + v, err := newValueRoot(ctx, inst.rootValue).structVal(ctx) 191 + if err != nil { 192 + i.setError(err) 193 + return i 194 + } 195 + i.scope = v.n 196 + 197 + i.Err = resolveFiles(idx, p) 198 + for _, f := range p.Files { 199 + i.insertFile(f) 200 + } 201 + i.complete = true 202 + 203 + return i 204 + } 205 + 206 + // Lookup reports the value starting from the top level struct (not the emitted 207 + // value), or an error if the path is not found. 208 + // The empty path returns the top-level configuration struct, regardless of 209 + // whether an emit value was specified. 210 + func (inst *Instance) Lookup(path ...string) Value { 211 + idx := inst.index 212 + ctx := idx.newContext() 213 + v := newValueRoot(ctx, inst.rootValue) 214 + for _, k := range path { 215 + obj, err := v.structVal(ctx) 216 + if err != nil { 217 + v := err.(*bottom) 218 + return Value{idx, &valueData{v: v, raw: v}} 219 + } 220 + v = obj.Lookup(k) 221 + } 222 + return v 223 + } 224 + 225 + // Fill creates a new instance with the values of the old instance unified with 226 + // the given value. It is not possible to update the emit value. 227 + func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) { 228 + ctx := inst.newContext() 229 + root := ctx.manifest(inst.rootValue) 230 + for i := len(path) - 1; i >= 0; i-- { 231 + x = map[string]interface{}{path[i]: x} 232 + } 233 + value := convert(ctx, root, x) 234 + eval := binOp(ctx, baseValue{}, opUnify, root, value) 235 + // TODO: validate recursively? 236 + st := eval.(*structLit) 237 + inst = &Instance{ 238 + rootStruct: st, 239 + rootValue: st, 240 + index: inst.index, 241 + inst: nil, 242 + 243 + ImportPath: inst.ImportPath, 244 + Name: inst.Name, 245 + Incomplete: inst.Incomplete, 246 + Err: inst.Err, 247 + 248 + complete: true, 249 + } 250 + return inst, nil 251 + }
+148
cue/instance_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "strings" 19 + "testing" 20 + 21 + "cuelang.org/go/cue/build" 22 + ) 23 + 24 + func toString(t *testing.T, v Value) string { 25 + t.Helper() 26 + 27 + b, err := v.MarshalJSON() 28 + if err != nil { 29 + t.Fatal(err) 30 + } 31 + 32 + return strings.Replace(string(b), `"`, "", -1) 33 + } 34 + 35 + func TestMerge(t *testing.T) { 36 + insts := func(s ...string) []string { return s } 37 + testCases := []struct { 38 + desc string 39 + instances []string 40 + out string 41 + isErr bool 42 + }{{ 43 + desc: "single", 44 + instances: insts(`a: 1, b: 2`), 45 + out: `{a:1,b:2}`, 46 + }, { 47 + desc: "multiple", 48 + instances: insts( 49 + `a: 1`, 50 + `b: 2`, 51 + `a: int`, 52 + ), 53 + out: `{a:1,b:2}`, 54 + }, { 55 + desc: "templates", 56 + instances: insts(` 57 + obj <X>: { a: "A" } 58 + obj alpha: { b: 2 } 59 + `, 60 + ` 61 + obj <X>: { a: "B" } 62 + obj beta: { b: 3 } 63 + `, 64 + ), 65 + out: `{obj:{alpha:{a:A,b:2},beta:{a:B,b:3}}}`, 66 + }, { 67 + // Structs that are shared in templates may have conflicting results. 68 + // However, this is not an issue as long as these value are not 69 + // referenced during evaluation. For generating JSON this is not an 70 + // issue as such fields are typically hidden. 71 + desc: "shared struct", 72 + instances: insts(` 73 + _shared: { a: "A" } 74 + obj <X>: _shared & {} 75 + obj alpha: { b: 2 } 76 + `, 77 + ` 78 + _shared: { a: "B" } 79 + obj <X>: _shared & {} 80 + obj beta: { b: 3 } 81 + `, 82 + ), 83 + out: `{obj:{alpha:{a:A,b:2},beta:{a:B,b:3}}}`, 84 + }, { 85 + desc: "error", 86 + instances: insts(`a:`), 87 + out: `{}`, 88 + isErr: true, 89 + }} 90 + 91 + for _, tc := range testCases { 92 + t.Run(tc.desc, func(t *testing.T) { 93 + ctx := build.NewContext() 94 + in := []*build.Instance{} 95 + for _, str := range tc.instances { 96 + bi := ctx.NewInstance("dir", nil) // no packages 97 + bi.AddFile("file", str) 98 + in = append(in, bi) 99 + } 100 + merged := Merge(Build(in)...) 101 + if err := merged.Err; err != nil { 102 + if !tc.isErr { 103 + t.Fatal(err) 104 + } 105 + } 106 + 107 + if got := toString(t, merged.Value()); got != tc.out { 108 + t.Errorf("\n got: %s\nwant: %s", got, tc.out) 109 + } 110 + }) 111 + } 112 + } 113 + 114 + func TestInstance_Build(t *testing.T) { 115 + testCases := []struct { 116 + desc string 117 + instance string 118 + overlay string 119 + out string 120 + }{{ 121 + desc: "single", 122 + instance: `a: {b: 1, c: 2}`, 123 + overlay: `res: a`, 124 + out: `{res:{b:1,c:2}}`, 125 + }} 126 + 127 + for _, tc := range testCases { 128 + t.Run(tc.desc, func(t *testing.T) { 129 + ctx := build.NewContext() 130 + 131 + bi := ctx.NewInstance("main", nil) // no packages 132 + bi.AddFile("file", tc.instance) 133 + main := Build([]*build.Instance{bi}) 134 + if err := main[0].Err; err != nil { 135 + t.Fatal(err) 136 + } 137 + 138 + bi = ctx.NewInstance("overlay", nil) // no packages 139 + bi.AddFile("file", tc.overlay) 140 + 141 + overlay := main[0].Build(bi) 142 + 143 + if got := toString(t, overlay.Value()); got != tc.out { 144 + t.Errorf("\n got: %s\nwant: %s", got, tc.out) 145 + } 146 + }) 147 + } 148 + }
+281
cue/kind.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + 20 + "cuelang.org/go/cue/token" 21 + ) 22 + 23 + func unifyType(a, b kind) kind { 24 + const mask = topKind 25 + isRef := (a &^ mask) | (b &^ mask) 26 + return isRef | (a & b) 27 + } 28 + 29 + type kind uint16 30 + 31 + const ( 32 + unknownKind kind = (1 << iota) 33 + nullKind 34 + boolKind 35 + intKind 36 + floatKind 37 + stringKind 38 + bytesKind 39 + durationKind 40 + listKind 41 + structKind 42 + 43 + lambdaKind 44 + // customKind 45 + 46 + // nonGround means that a value is not specific enough to be emitted. 47 + // Structs and lists are indicated as ground even when their values are not. 48 + nonGround 49 + 50 + // TODO: distinguish beteween nonGround and disjunctions? 51 + 52 + // a referenceKind is typically top and nonGround, but is indicated with an 53 + // additional bit. If reference is set and nonGround is not, it is possible 54 + // to move the reference to an assertion clause. 55 + referenceKind 56 + 57 + atomKind = (listKind - 1) 58 + concreteKind = (lambdaKind - 1) 59 + 60 + // doneKind indicates a value can not further develop on its own (i.e. not a 61 + // reference). If doneKind is not set, but the result is ground, it 62 + // typically possible to hoist the reference out of a unification operation. 63 + 64 + // For rational numbers, typically both intKind and floatKind are set, 65 + // unless the range is restricted by a root type. 66 + numKind = intKind | floatKind 67 + 68 + comparableKind = listKind - 1 69 + stringableKind = scalarKinds | stringKind 70 + topKind = (referenceKind - 1) // all kinds, but not references 71 + typeKinds = nonGround - 1 72 + okKinds = typeKinds &^ bottomKind 73 + fixedKinds = okKinds &^ (structKind | lambdaKind) 74 + scalarKinds = numKind | durationKind 75 + 76 + bottomKind = 0 77 + ) 78 + 79 + func isTop(v value) bool { 80 + return v.kind() == topKind 81 + } 82 + 83 + // isDone means that the value will not evaluate further. 84 + func (k kind) isDone() bool { return k&referenceKind == bottomKind } 85 + func (k kind) hasReferences() bool { return k&referenceKind != bottomKind } 86 + func (k kind) isGround() bool { return k&nonGround == bottomKind } 87 + func (k kind) isAtom() bool { return k.isGround() && k&atomKind != bottomKind } 88 + func (k kind) isAnyOf(of kind) bool { 89 + return k&of != bottomKind 90 + } 91 + func (k kind) stringable() bool { 92 + return k.isGround() && k&stringKind|scalarKinds != bottomKind 93 + } 94 + 95 + func (k kind) String() string { 96 + str := "" 97 + if k&topKind == topKind { 98 + str = "_" 99 + goto finalize 100 + } 101 + for i := kind(1); i < referenceKind; i <<= 1 { 102 + t := "" 103 + switch k & i { 104 + case bottomKind: 105 + continue 106 + case nullKind: 107 + t = "null" 108 + case boolKind: 109 + t = "bool" 110 + case intKind: 111 + if k&floatKind != 0 { 112 + t = "number" 113 + } else { 114 + t = "int" 115 + } 116 + case floatKind: 117 + if k&intKind != 0 { 118 + continue 119 + } 120 + t = "float" 121 + case stringKind: 122 + t = "string" 123 + case bytesKind: 124 + t = "bytes" 125 + case durationKind: 126 + t = "duration" 127 + case listKind: 128 + t = "list" 129 + case structKind: 130 + t = "struct" 131 + case lambdaKind: 132 + t = "lambda" 133 + case nonGround, referenceKind: 134 + continue 135 + default: 136 + t = fmt.Sprintf("<unknown> %x", int(i)) 137 + } 138 + if str != "" { 139 + str += "|" 140 + } 141 + str += t 142 + } 143 + finalize: 144 + if str == "" { 145 + return "_|_" 146 + } 147 + if k&nonGround != 0 { 148 + str = "(" + str + ")*" 149 + } 150 + if k&referenceKind != 0 { 151 + str = "&" + str 152 + } 153 + return str 154 + } 155 + 156 + type kindInfo struct { 157 + kind 158 + subsumes []*kindInfo 159 + subsumedBy []*kindInfo 160 + unary []token.Token 161 + binary []token.Token 162 + } 163 + 164 + var toKindInfo = map[kind]*kindInfo{} 165 + 166 + // matchBinOpKind reports whether the given op is possible for the given kinds 167 + // and what the result will be. Evaluating binary expressions uses this to 168 + // - fail incompatible operations early, even if the concrete types are 169 + // not known, 170 + // - check the result type of unification, 171 + // 172 + // Secondary goals: 173 + // - keep type compatibility mapped at a central place 174 + // - reduce the amount op type switching. 175 + // - simplifies testing 176 + func matchBinOpKind(op op, a, b kind) (k kind, invert bool) { 177 + if op == opDisjunction { 178 + return a | b, false 179 + } 180 + u := unifyType(a, b) 181 + valBits := u & typeKinds 182 + catBits := u &^ typeKinds 183 + a = a & typeKinds 184 + b = b & typeKinds 185 + if valBits == bottomKind { 186 + if op == opEql || op == opNeq { 187 + return bottomKind, false 188 + } 189 + if a.isAnyOf(listKind) && b.isAnyOf(intKind) { 190 + return a, false 191 + } 192 + if b.isAnyOf(listKind) && a.isAnyOf(intKind) { 193 + return b, true 194 + } 195 + // non-overlapping types 196 + if a&scalarKinds == 0 || b&scalarKinds == 0 { 197 + return bottomKind, false 198 + } 199 + // a and b have different numeric types. 200 + switch { 201 + case b.isAnyOf(durationKind): 202 + // a must be a numeric, non-duration type. 203 + if op == opMul { 204 + return durationKind | catBits, true 205 + } 206 + case a.isAnyOf(durationKind): 207 + if opIn(op, opMul, opQuo, opRem) { 208 + return durationKind | catBits, false 209 + } 210 + case op.isCmp(): 211 + return boolKind, false 212 + case op.allowImplicitNumCast(): 213 + if a.isAnyOf(intKind) || 214 + (a.isAnyOf(floatKind) && !b.isAnyOf(intKind)) { 215 + return b | catBits, false 216 + } 217 + return a | catBits, false 218 + } 219 + return bottomKind, false 220 + } 221 + switch { 222 + case a&nonGround == 0 && b&nonGround == 0: 223 + // both ground values: nothing to do 224 + 225 + case op != opUnify && op != opLand && op != opLor: 226 + 227 + default: 228 + invert = a&nonGround == 0 && b&nonGround != 0 229 + } 230 + // a and b have overlapping types. 231 + switch op { 232 + case opUnify: 233 + // Increase likelihood of unification succeeding on first try. 234 + return u, invert 235 + 236 + case opLand, opLor: 237 + if u.isAnyOf(boolKind) { 238 + return boolKind | catBits, invert 239 + } 240 + case opEql, opNeq: 241 + if u.isAnyOf(fixedKinds) { 242 + return boolKind | catBits, false 243 + } 244 + case opLss, opLeq, opGeq, opGtr: 245 + if u.isAnyOf(fixedKinds) { 246 + return boolKind | catBits, false 247 + } 248 + case opAdd: 249 + if u.isAnyOf(atomKind) { 250 + return u&(atomKind) | catBits, false 251 + // u&(durationKind|intKind) 252 + } 253 + case opSub: 254 + if u.isAnyOf(scalarKinds) { 255 + return u&scalarKinds | catBits, false 256 + } 257 + case opRem: 258 + if u.isAnyOf(durationKind | intKind) { 259 + return u&(durationKind|intKind) | catBits, false 260 + } 261 + case opQuo: 262 + if u.isAnyOf(durationKind | numKind) { 263 + return floatKind | catBits, false 264 + } 265 + case opIRem, opIMod: 266 + if u.isAnyOf(intKind) { 267 + return u&(intKind) | catBits, false 268 + } 269 + case opIQuo, opIDiv: 270 + if u.isAnyOf(intKind) { 271 + return intKind | catBits, false 272 + } 273 + case opMul: 274 + if u.isAnyOf(numKind) { 275 + return u&numKind | catBits, false 276 + } 277 + default: 278 + panic("unimplemented") 279 + } 280 + return bottomKind, false 281 + }
+53
cue/kind_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "testing" 20 + ) 21 + 22 + func TestMatchBinOpKind(t *testing.T) { 23 + testCases := []struct { 24 + op op 25 + a kind 26 + b kind 27 + want kind 28 + }{{ 29 + op: opMul, 30 + a: floatKind, 31 + b: intKind, 32 + want: floatKind, 33 + }, { 34 + op: opMul, 35 + a: listKind, 36 + b: intKind, 37 + want: listKind, 38 + }, { 39 + op: opMul, 40 + a: intKind, 41 + b: listKind, 42 + want: listKind, 43 + }} 44 + for _, tc := range testCases { 45 + key := fmt.Sprintf("%s(%v, %v)", tc.op, tc.a, tc.b) 46 + t.Run(key, func(t *testing.T) { 47 + got, _ := matchBinOpKind(tc.op, tc.a, tc.b) 48 + if got != tc.want { 49 + t.Errorf("got %v, want %v", got, tc.want) 50 + } 51 + }) 52 + } 53 + }
+687
cue/lit.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "math/big" 19 + "strings" 20 + "unicode" 21 + "unicode/utf8" 22 + 23 + "cuelang.org/go/cue/ast" 24 + "cuelang.org/go/cue/errors" 25 + "github.com/cockroachdb/apd" 26 + ) 27 + 28 + // errRange indicates that a value is out of range for the target type. 29 + var errRange = errors.New("value out of range") 30 + 31 + // errSyntax indicates that a value does not have the right syntax for the 32 + // target type. 33 + var errSyntax = errors.New("invalid syntax") 34 + 35 + var errInvalidString = errors.New("invalid string") 36 + 37 + // Unquote interprets s as a single-quoted, double-quoted, or backquoted CUE 38 + // string literal, returning the string value that s quotes. 39 + func Unquote(s string) (string, error) { 40 + quote, err := stringType(s) 41 + if err != nil { 42 + return "", err 43 + } 44 + prefix, err := wsPrefix(s, quote) 45 + if err != nil { 46 + return "", err 47 + } 48 + s = s[len(quote) : len(s)-len(quote)] 49 + return unquote(quote[0], len(quote) == 3, true, prefix, s) 50 + } 51 + 52 + // unquote interprets s as a CUE string, where quote identifies the string type: 53 + // s: Unicode string (normal double quoted strings) 54 + // b: Binary strings: allows escape sequences that may result in invalid 55 + // Unicode. 56 + // r: raw strings. 57 + // 58 + // quote indicates the quote used. This is relevant for raw strings, as they 59 + // may not contain the quoting character itself. 60 + func unquote(quote byte, multiline, first bool, wsPrefix, s string) (string, error) { 61 + if quote == '`' { 62 + if contains(s, quote) { 63 + return "", errSyntax 64 + } 65 + if contains(s, '\r') { 66 + // -1 because we know there is at least one \r to remove. 67 + buf := make([]byte, 0, len(s)-1) 68 + for i := 0; i < len(s); i++ { 69 + if s[i] != '\r' { 70 + buf = append(buf, s[i]) 71 + } 72 + } 73 + return string(buf), nil 74 + } 75 + return s, nil 76 + } 77 + if !multiline { 78 + if contains(s, '\n') { 79 + return "", errSyntax 80 + } 81 + // Is it trivial? Avoid allocation. 82 + if !contains(s, '\\') && !contains(s, quote) { 83 + return s, nil 84 + } 85 + } 86 + 87 + var runeTmp [utf8.UTFMax]byte 88 + buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. 89 + for len(s) > 0 { 90 + switch s[0] { 91 + case '\r': 92 + s = s[1:] 93 + continue 94 + case '\n': 95 + switch { 96 + case !multiline: 97 + fallthrough 98 + default: 99 + return "", errSyntax 100 + case strings.HasPrefix(s[1:], wsPrefix): 101 + s = s[1+len(wsPrefix):] 102 + case strings.HasPrefix(s[1:], "\n"): 103 + s = s[1:] 104 + } 105 + if !first && len(s) > 0 { 106 + buf = append(buf, '\n') 107 + } 108 + first = false 109 + continue 110 + } 111 + c, multibyte, ss, err := unquoteChar(s, quote) 112 + if err != nil { 113 + return "", err 114 + } 115 + // TODO: handle surrogates: if we have a left-surrogate, expect the 116 + // next value to be a right surrogate. Otherwise this is an error. 117 + s = ss 118 + if c < utf8.RuneSelf || !multibyte { 119 + buf = append(buf, byte(c)) 120 + } else { 121 + n := utf8.EncodeRune(runeTmp[:], c) 122 + buf = append(buf, runeTmp[:n]...) 123 + } 124 + } 125 + return string(buf), nil 126 + } 127 + 128 + // contains reports whether the string contains the byte c. 129 + func contains(s string, c byte) bool { 130 + for i := 0; i < len(s); i++ { 131 + if s[i] == c { 132 + return true 133 + } 134 + } 135 + return false 136 + } 137 + 138 + // unquoteChar decodes the first character or byte in the escaped string. 139 + // It returns four values: 140 + // 141 + // 1) value, the decoded Unicode code point or byte value; 142 + // 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation; 143 + // 3) tail, the remainder of the string after the character; and 144 + // 4) an error that will be nil if the character is syntactically valid. 145 + // 146 + // The second argument, kind, specifies the type of literal being parsed 147 + // and therefore which kind of escape sequences are permitted. 148 + // For kind 's' only JSON escapes and \u{ are permitted. 149 + // For kind 'b' also hexadecimal and octal escape sequences are permitted. 150 + // 151 + // The third argument, quote, specifies that an ASCII quoting character that 152 + // is not permitted in the output. 153 + func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) { 154 + // easy cases 155 + switch c := s[0]; { 156 + case c == quote && quote != 0: 157 + err = errSyntax 158 + return 159 + case c >= utf8.RuneSelf: 160 + r, size := utf8.DecodeRuneInString(s) 161 + return r, true, s[size:], nil 162 + case c != '\\': 163 + return rune(s[0]), false, s[1:], nil 164 + } 165 + 166 + // hard case: c is backslash 167 + if len(s) <= 1 { 168 + err = errSyntax 169 + return 170 + } 171 + c := s[1] 172 + s = s[2:] 173 + 174 + switch c { 175 + case 'a': 176 + value = '\a' 177 + case 'b': 178 + value = '\b' 179 + case 'f': 180 + value = '\f' 181 + case 'n': 182 + value = '\n' 183 + case 'r': 184 + value = '\r' 185 + case 't': 186 + value = '\t' 187 + case 'v': 188 + value = '\v' 189 + case 'x', 'u', 'U': 190 + n := 0 191 + switch c { 192 + case 'x': 193 + n = 2 194 + case 'u': 195 + n = 4 196 + case 'U': 197 + n = 8 198 + } 199 + var v rune 200 + if len(s) < n { 201 + err = errSyntax 202 + return 203 + } 204 + for j := 0; j < n; j++ { 205 + x, ok := unhex(s[j]) 206 + if !ok { 207 + err = errSyntax 208 + return 209 + } 210 + v = v<<4 | x 211 + } 212 + s = s[n:] 213 + if c == 'x' { 214 + if quote == '"' { 215 + err = errSyntax 216 + return 217 + } 218 + // single-byte string, possibly not UTF-8 219 + value = v 220 + break 221 + } 222 + if v > utf8.MaxRune { 223 + err = errSyntax 224 + return 225 + } 226 + value = v 227 + multibyte = true 228 + case '0', '1', '2', '3', '4', '5', '6', '7': 229 + if quote == '"' { 230 + err = errSyntax 231 + return 232 + } 233 + v := rune(c) - '0' 234 + if len(s) < 2 { 235 + err = errSyntax 236 + return 237 + } 238 + for j := 0; j < 2; j++ { // one digit already; two more 239 + x := rune(s[j]) - '0' 240 + if x < 0 || x > 7 { 241 + err = errSyntax 242 + return 243 + } 244 + v = (v << 3) | x 245 + } 246 + s = s[2:] 247 + if v > 255 { 248 + err = errSyntax 249 + return 250 + } 251 + value = v 252 + case '\\': 253 + value = '\\' 254 + case '\'', '"': 255 + // TODO: should we allow escaping of quotes regardless? 256 + if c != quote { 257 + err = errSyntax 258 + return 259 + } 260 + value = rune(c) 261 + default: 262 + err = errSyntax 263 + return 264 + } 265 + tail = s 266 + return 267 + } 268 + 269 + func unhex(b byte) (v rune, ok bool) { 270 + c := rune(b) 271 + switch { 272 + case '0' <= c && c <= '9': 273 + return c - '0', true 274 + case 'a' <= c && c <= 'f': 275 + return c - 'a' + 10, true 276 + case 'A' <= c && c <= 'F': 277 + return c - 'A' + 10, true 278 + } 279 + return 280 + } 281 + 282 + type numInfo struct { 283 + rep multiplier 284 + k kind 285 + } 286 + 287 + func newNumInfo(k kind, m multiplier, base int, sep bool) numInfo { 288 + switch base { 289 + case 10: 290 + m |= base10 291 + case 2: 292 + m |= base2 293 + k = intKind 294 + case 8: 295 + m |= base8 296 + k = intKind 297 + case 16: 298 + m |= base16 299 + k = intKind 300 + } 301 + if sep { 302 + m |= hasSeparators 303 + } 304 + return numInfo{m, k} 305 + } 306 + 307 + func unifyNuminfo(a, b numInfo) numInfo { 308 + k := unifyType(a.k, b.k) 309 + return numInfo{a.rep | b.rep, k} 310 + } 311 + 312 + func (n numInfo) isValid() bool { return n.k != bottomKind } 313 + func (n numInfo) multiplier() multiplier { return n.rep & (hasSeparators - 1) } 314 + 315 + type multiplier uint16 316 + 317 + const ( 318 + mul1 multiplier = 1 << iota 319 + mul2 320 + mul3 321 + mul4 322 + mul5 323 + mul6 324 + mul7 325 + mul8 326 + 327 + mulBin 328 + mulDec 329 + 330 + // _ 3 for dec, 4 for hex. Maybe support first and rest, like CLDR. 331 + hasSeparators 332 + 333 + base2 334 + base8 335 + base10 336 + base16 337 + 338 + mulK = mulDec | mul1 339 + mulM = mulDec | mul2 340 + mulG = mulDec | mul3 341 + mulT = mulDec | mul4 342 + mulP = mulDec | mul5 343 + mulE = mulDec | mul6 344 + mulZ = mulDec | mul7 345 + mulY = mulDec | mul8 346 + 347 + mulKi = mulBin | mul1 348 + mulMi = mulBin | mul2 349 + mulGi = mulBin | mul3 350 + mulTi = mulBin | mul4 351 + mulPi = mulBin | mul5 352 + mulEi = mulBin | mul6 353 + mulZi = mulBin | mul7 354 + mulYi = mulBin | mul8 355 + ) 356 + 357 + type litParser struct { 358 + ctx *context 359 + node *ast.BasicLit 360 + src string 361 + p int 362 + // pDot int // first position after the dot, if any 363 + ch byte 364 + useSep bool 365 + buf []byte 366 + err value 367 + } 368 + 369 + func (p *litParser) error(l ast.Node, args ...interface{}) value { 370 + return p.ctx.mkErr(newNode(l), args...) 371 + } 372 + 373 + func (p *litParser) next() bool { 374 + if p.p >= len(p.src) { 375 + p.ch = 0 376 + return false 377 + } 378 + p.ch = p.src[p.p] 379 + p.p++ 380 + if p.ch == '.' { 381 + p.buf = append(p.buf, '.') 382 + 383 + } 384 + return true 385 + } 386 + 387 + func (p *litParser) init(l *ast.BasicLit) (err value) { 388 + s := l.Value 389 + b := p.buf 390 + *p = litParser{ctx: p.ctx, node: l, src: s} 391 + p.buf = b[:0] 392 + if !p.next() { 393 + return p.error(l, "invalid literal %q", s) 394 + } 395 + return nil 396 + } 397 + 398 + func (p *litParser) parse(l *ast.BasicLit) (n value) { 399 + s := l.Value 400 + switch s { 401 + case "null": 402 + return &nullLit{newExpr(l)} 403 + case "true": 404 + return &boolLit{newExpr(l), true} 405 + case "false": 406 + return &boolLit{newExpr(l), false} 407 + } 408 + if err := p.init(l); err != nil { 409 + return err 410 + } 411 + switch p.ch { 412 + case '"', '\'', '`': 413 + quote, err := stringType(l.Value) 414 + if err != nil { 415 + return p.error(l, err.Error()) 416 + } 417 + ws, err := wsPrefix(l.Value, quote) 418 + if err != nil { 419 + return p.error(l, err.Error()) 420 + } 421 + return p.parseString(quote, quote, ws, len(quote) == 3, quote[0]) 422 + case '.': 423 + p.next() 424 + n = p.scanNumber(true) 425 + default: 426 + n = p.scanNumber(false) 427 + } 428 + if p.err != nil { 429 + return p.err 430 + } 431 + if p.p < len(p.src) { 432 + return p.error(l, "invalid number") 433 + } 434 + return n 435 + } 436 + 437 + var ( 438 + errStringTooShort = errors.New("invalid string: too short") 439 + errMissingNewline = errors.New( 440 + "invalid string: opening quote of multiline string must be followed by newline") 441 + errUnmatchedQuote = errors.New("invalid string: unmatched quote") 442 + ) 443 + 444 + // stringType reports the type of quoting used, being ther a ", ', """, or ''', 445 + // or `. 446 + func stringType(s string) (quote string, err error) { 447 + if len(s) < 2 { 448 + return "", errStringTooShort 449 + } 450 + switch s[0] { 451 + case '"', '\'': 452 + if len(s) > 3 && s[1] == s[0] && s[2] == s[0] { 453 + if s[3] != '\n' { 454 + return "", errMissingNewline 455 + } 456 + return s[:3], nil 457 + } 458 + case '`': 459 + default: 460 + return "", errSyntax 461 + } 462 + return s[:1], nil 463 + } 464 + 465 + func wsPrefix(s, quote string) (ws string, err error) { 466 + for i := 0; i < len(quote); i++ { 467 + if j := len(s) - i - 1; j < 0 || quote[i] != s[j] { 468 + return "", errUnmatchedQuote 469 + } 470 + } 471 + i := len(s) - len(quote) 472 + for i > 0 { 473 + r, size := utf8.DecodeLastRuneInString(s[:i]) 474 + if r == '\n' || !unicode.IsSpace(r) { 475 + break 476 + } 477 + i -= size 478 + } 479 + return s[i : len(s)-len(quote)], nil 480 + } 481 + 482 + func (p *litParser) parseString(prefix, suffix, ws string, multi bool, quote byte) (n value) { 483 + if len(p.src) < len(prefix)+len(suffix) { 484 + return p.error(p.node, "invalid string: too short") 485 + } 486 + for _, r := range prefix { 487 + if byte(r) != p.ch { 488 + return p.error(p.node, "invalid interpolation: expected %q", prefix) 489 + } 490 + p.next() 491 + } 492 + if !strings.HasSuffix(p.src, suffix) { 493 + return p.error(p.node, "invalid interpolation: unmatched ')'", suffix) 494 + } 495 + start, end := len(prefix), len(p.src)-len(suffix) 496 + str, err := unquote(quote, multi, len(prefix) == 3, ws, p.src[start:end]) 497 + if err != nil { 498 + return p.error(p.node, err, "invalid string: %v", err) 499 + } 500 + if quote == '"' { 501 + return &stringLit{newExpr(p.node), str} 502 + } 503 + return &bytesLit{newExpr(p.node), []byte(str)} 504 + } 505 + 506 + func (p *litParser) digitVal(ch byte) (d int) { 507 + switch { 508 + case '0' <= ch && ch <= '9': 509 + d = int(ch - '0') 510 + case ch == '_': 511 + p.useSep = true 512 + return 0 513 + case 'a' <= ch && ch <= 'f': 514 + d = int(ch - 'a' + 10) 515 + case 'A' <= ch && ch <= 'F': 516 + d = int(ch - 'A' + 10) 517 + default: 518 + return 16 // larger than any legal digit val 519 + } 520 + return d 521 + } 522 + 523 + func (p *litParser) scanMantissa(base int) { 524 + var last byte 525 + for p.digitVal(p.ch) < base { 526 + if p.ch != '_' { 527 + p.buf = append(p.buf, p.ch) 528 + } 529 + last = p.ch 530 + p.next() 531 + } 532 + if last == '_' { 533 + p.err = p.error(p.node, "illegal '_' in number") 534 + } 535 + } 536 + 537 + func (p *litParser) scanNumber(seenDecimalPoint bool) value { 538 + // digitVal(s.ch) < 10 539 + isFloat := false 540 + base := 10 541 + 542 + if seenDecimalPoint { 543 + isFloat = true 544 + p.scanMantissa(10) 545 + goto exponent 546 + } 547 + 548 + if p.ch == '0' { 549 + // int or float 550 + p.next() 551 + if p.ch == 'x' || p.ch == 'X' { 552 + base = 16 553 + // hexadecimal int 554 + p.next() 555 + p.scanMantissa(16) 556 + if p.p <= 2 { 557 + // only scanned "0x" or "0X" 558 + return p.error(p.node, "illegal hexadecimal number %q", p.src) 559 + } 560 + } else if p.ch == 'b' || p.ch == 'B' { 561 + base = 2 562 + // binary int 563 + p.next() 564 + p.scanMantissa(2) 565 + if p.p <= 2 { 566 + // only scanned "0b" or "0B" 567 + return p.error(p.node, "illegal binary number %q", p.src) 568 + } 569 + } else { 570 + base = 8 571 + // octal int or float 572 + seenDecimalDigit := false 573 + p.scanMantissa(8) 574 + if p.ch == '8' || p.ch == '9' { 575 + // illegal octal int or float 576 + seenDecimalDigit = true 577 + p.scanMantissa(10) 578 + } 579 + // TODO: disallow complex 580 + if p.ch == '.' || p.ch == 'e' { 581 + goto fraction 582 + } 583 + // octal int 584 + if seenDecimalDigit { 585 + return p.error(p.node, "illegal octal number %q", p.src) 586 + } 587 + } 588 + goto exit 589 + } 590 + 591 + // decimal int or float 592 + p.scanMantissa(10) 593 + 594 + // TODO: allow 3h4s, etc. 595 + // switch p.ch { 596 + // case 'h', 'm', 's', "µ"[0], 'u', 'n': 597 + // } 598 + 599 + fraction: 600 + if p.ch == '.' { 601 + isFloat = true 602 + p.next() 603 + p.scanMantissa(10) 604 + } 605 + 606 + exponent: 607 + switch p.ch { 608 + case 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y': 609 + mul := charToMul[p.ch] 610 + p.next() 611 + if p.ch == 'i' { 612 + mul |= mulBin 613 + p.next() 614 + } else { 615 + mul |= mulDec 616 + } 617 + n := &numLit{ 618 + numBase: newNumBase(p.node, newNumInfo(numKind, mul, 10, p.useSep)), 619 + } 620 + n.v.UnmarshalText(p.buf) 621 + p.ctx.Mul(&n.v, &n.v, mulToRat[mul]) 622 + cond, _ := p.ctx.RoundToIntegralExact(&n.v, &n.v) 623 + if cond.Inexact() { 624 + return p.error(p.node, "number cannot be represented as int") 625 + } 626 + return n 627 + 628 + case 'e': 629 + isFloat = true 630 + p.next() 631 + p.buf = append(p.buf, 'e') 632 + if p.ch == '-' || p.ch == '+' { 633 + p.buf = append(p.buf, p.ch) 634 + p.next() 635 + } 636 + p.scanMantissa(10) 637 + } 638 + 639 + exit: 640 + if isFloat { 641 + f := &numLit{ 642 + numBase: newNumBase(p.node, newNumInfo(floatKind, 0, 10, p.useSep)), 643 + } 644 + f.v.UnmarshalText(p.buf) 645 + return f 646 + } 647 + i := &numLit{numBase: newNumBase(p.node, newNumInfo(numKind, 0, base, p.useSep))} 648 + i.v.Coeff.SetString(string(p.buf), base) 649 + return i 650 + } 651 + 652 + type mulInfo struct { 653 + fact *big.Rat 654 + mul multiplier 655 + } 656 + 657 + var charToMul = map[byte]multiplier{ 658 + 'K': mul1, 659 + 'M': mul2, 660 + 'G': mul3, 661 + 'T': mul4, 662 + 'P': mul5, 663 + 'E': mul6, 664 + 'Z': mul7, 665 + 'Y': mul8, 666 + } 667 + 668 + var mulToRat = map[multiplier]*apd.Decimal{} 669 + 670 + func init() { 671 + d := apd.New(1, 0) 672 + b := apd.New(1, 0) 673 + dm := apd.New(1000, 0) 674 + bm := apd.New(1024, 0) 675 + 676 + c := apd.BaseContext 677 + for i := uint(0); int(i) < len(charToMul); i++ { 678 + // TODO: may we write to one of the sources? 679 + var bn, dn apd.Decimal 680 + c.Mul(&dn, d, dm) 681 + d = &dn 682 + c.Mul(&bn, b, bm) 683 + b = &bn 684 + mulToRat[mulDec|1<<i] = d 685 + mulToRat[mulBin|1<<i] = b 686 + } 687 + }
+228
cue/lit_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "math/big" 20 + "testing" 21 + 22 + "cuelang.org/go/cue/ast" 23 + "github.com/cockroachdb/apd" 24 + "github.com/google/go-cmp/cmp" 25 + "github.com/google/go-cmp/cmp/cmpopts" 26 + ) 27 + 28 + func TestUnquote(t *testing.T) { 29 + testCases := []struct { 30 + in, out string 31 + err error 32 + }{ 33 + {`"Hello"`, "Hello", nil}, 34 + {`'Hello'`, "Hello", nil}, 35 + {"`Hello`", "Hello", nil}, 36 + {`"""` + "\n\t\tHello\n\t\t" + `"""`, "Hello", nil}, 37 + {"'''\n\t\tHello\n\t\t'''", "Hello", nil}, 38 + {"'''\n\t\tHello\n\n\t\t'''", "Hello\n", nil}, 39 + {"'''\n\n\t\tHello\n\t\t'''", "\nHello", nil}, 40 + {"'''\n\n\n\n\t\t'''", "\n\n", nil}, 41 + {"'''\n\t\t'''", "", nil}, 42 + {"`\n\tHello`", "\n\tHello", nil}, 43 + {"`aaa\n\rbbb`", "aaa\nbbb", nil}, 44 + {`"""` + "\n\raaa\n\rbbb\n\r" + `"""`, "aaa\nbbb", nil}, 45 + 46 + {"```", "", errSyntax}, 47 + {"Hello", "", errSyntax}, 48 + {`"Hello`, "", errUnmatchedQuote}, 49 + {`"""Hello"""`, "", errMissingNewline}, 50 + {`"Hello""`, "", errSyntax}, 51 + } 52 + for _, tc := range testCases { 53 + t.Run(tc.in, func(t *testing.T) { 54 + if got, err := Unquote(tc.in); err != tc.err { 55 + t.Errorf("error: got %q; want %q", err, tc.err) 56 + } else if got != tc.out { 57 + t.Errorf("value: got %q; want %q", got, tc.out) 58 + } 59 + }) 60 + } 61 + } 62 + 63 + var defIntBase = newNumBase(&ast.BasicLit{}, newNumInfo(numKind, 0, 10, false)) 64 + var defRatBase = newNumBase(&ast.BasicLit{}, newNumInfo(floatKind, 0, 10, false)) 65 + 66 + func mkInt(a int64) *numLit { 67 + x := &numLit{numBase: defIntBase} 68 + x.v.SetInt64(a) 69 + return x 70 + } 71 + func mkIntString(a string) *numLit { 72 + x := &numLit{numBase: defIntBase} 73 + x.v.SetString(a) 74 + return x 75 + } 76 + func mkFloat(a string) *numLit { 77 + x := &numLit{numBase: defRatBase} 78 + x.v.SetString(a) 79 + return x 80 + } 81 + func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return } 82 + 83 + func mkBigFloat(a string) (v apd.Decimal) { v.SetString(a); return } 84 + 85 + var diffOpts = []cmp.Option{ 86 + cmp.Comparer(func(x, y big.Rat) bool { 87 + return x.String() == y.String() 88 + }), 89 + cmp.Comparer(func(x, y big.Int) bool { 90 + return x.String() == y.String() 91 + }), 92 + cmp.AllowUnexported( 93 + nullLit{}, 94 + boolLit{}, 95 + stringLit{}, 96 + bytesLit{}, 97 + numLit{}, 98 + numBase{}, 99 + numInfo{}, 100 + ), 101 + cmpopts.IgnoreUnexported( 102 + bottom{}, 103 + baseValue{}, 104 + baseValue{}, 105 + ), 106 + } 107 + 108 + var ( 109 + nullSentinel = &nullLit{} 110 + trueSentinel = &boolLit{b: true} 111 + falseSentinel = &boolLit{b: false} 112 + ) 113 + 114 + func TestLiterals(t *testing.T) { 115 + mkMul := func(x int64, m multiplier, base int) *numLit { 116 + return &numLit{ 117 + newNumBase(&ast.BasicLit{}, newNumInfo(numKind, m, base, false)), 118 + mkBigInt(x), 119 + } 120 + } 121 + hk := &numLit{ 122 + newNumBase(&ast.BasicLit{}, newNumInfo(numKind, 0, 10, true)), 123 + mkBigInt(100000), 124 + } 125 + testCases := []struct { 126 + lit string 127 + node value 128 + }{ 129 + {"null", nullSentinel}, 130 + {"true", trueSentinel}, 131 + {"false", falseSentinel}, 132 + {"fls", &bottom{}}, 133 + {`"foo"`, &stringLit{str: "foo"}}, 134 + {`"\"foo\""`, &stringLit{str: `"foo"`}}, 135 + {`"foo\u0032"`, &stringLit{str: `foo2`}}, 136 + {`"foo\U00000033"`, &stringLit{str: `foo3`}}, 137 + {`"foo\U0001f499"`, &stringLit{str: `foo💙`}}, 138 + {`"\a\b\f\n\r\t\v"`, &stringLit{str: "\a\b\f\n\r\t\v"}}, 139 + {`""" 140 + """`, &stringLit{str: ""}}, 141 + {`""" 142 + abc 143 + """`, &stringLit{str: "abc"}}, 144 + {`""" 145 + abc 146 + def 147 + """`, &stringLit{str: "abc\ndef"}}, 148 + {`""" 149 + abc 150 + def 151 + """`, &stringLit{str: "abc\n\tdef"}}, 152 + {`'\xff'`, &bytesLit{b: []byte("\xff")}}, 153 + {"1", mkInt(1)}, 154 + {"100_000", hk}, 155 + {"1.", mkFloat("1")}, 156 + {".0", mkFloat(".0")}, 157 + {"1K", mkMul(1000, mulK, 10)}, 158 + {"1Mi", mkMul(1024*1024, mulMi, 10)}, 159 + {"1.5Mi", mkMul((1024+512)*1024, mulMi, 10)}, 160 + {"1.3Mi", &bottom{}}, // Cannot be accurately represented. 161 + {"1.3G", mkMul(1300000000, mulG, 10)}, 162 + {"1.3e+20", mkFloat("1.3e+20")}, 163 + {"1.3e20", mkFloat("1.3e+20")}, 164 + {"1.3e-5", mkFloat("1.3e-5")}, 165 + {"0x1234", mkMul(0x1234, 0, 16)}, 166 + {"0xABCD", mkMul(0xABCD, 0, 16)}, 167 + {"0b11001000", mkMul(0xc8, 0, 2)}, 168 + {"0b1", mkMul(1, 0, 2)}, 169 + {"0755", mkMul(0755, 0, 8)}, 170 + } 171 + p := litParser{ 172 + ctx: &context{Context: &apd.BaseContext}, 173 + } 174 + for i, tc := range testCases { 175 + t.Run(fmt.Sprintf("%d/%+q", i, tc.lit), func(t *testing.T) { 176 + got := p.parse(&ast.BasicLit{Value: tc.lit}) 177 + if !cmp.Equal(got, tc.node, diffOpts...) { 178 + t.Error(cmp.Diff(got, tc.node, diffOpts...)) 179 + t.Errorf("%#v, %#v\n", got, tc.node) 180 + } 181 + }) 182 + } 183 + } 184 + 185 + func TestLiteralErrors(t *testing.T) { 186 + testCases := []struct { 187 + lit string 188 + }{ 189 + {`"foo\u"`}, 190 + {`"foo\u003"`}, 191 + {`"foo\U1234567"`}, 192 + {`"foo\U12345678"`}, 193 + {`"foo\Ug"`}, 194 + {`"\xff"`}, 195 + // not allowed in string literal, only binary 196 + {`"foo\x00"`}, 197 + {`0x`}, 198 + {`09`}, 199 + {`0_`}, 200 + {``}, 201 + {`"`}, 202 + {`"a`}, 203 + // wrong indentation 204 + {`""" 205 + abc 206 + def 207 + """`}, 208 + // non-matching quotes 209 + {`""" 210 + abc 211 + '''`}, 212 + {`""" 213 + abc 214 + "`}, 215 + {`"abc \( foo "`}, 216 + } 217 + p := litParser{ 218 + ctx: &context{Context: &apd.BaseContext}, 219 + } 220 + for _, tc := range testCases { 221 + t.Run(fmt.Sprintf("%+q", tc.lit), func(t *testing.T) { 222 + got := p.parse(&ast.BasicLit{Value: tc.lit}) 223 + if _, ok := got.(*bottom); !ok { 224 + t.Fatalf("expected error but found none") 225 + } 226 + }) 227 + } 228 + }
+144
cue/op.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import "cuelang.org/go/cue/token" 18 + 19 + func opIn(op op, anyOf ...op) bool { 20 + for _, o := range anyOf { 21 + if o == op { 22 + return true 23 + } 24 + } 25 + return false 26 + } 27 + 28 + // isCmp reports whether an op is a comparator. 29 + func (op op) isCmp() bool { 30 + return opEql <= op && op <= opGeq 31 + } 32 + 33 + // allowImplicitNumCast returns whether an operator is allowed between two 34 + // different kind of numeric types without an explicit cast. 35 + // TODO: remove 36 + func (op op) allowImplicitNumCast() bool { 37 + return opAdd <= op && op <= opQuo 38 + } 39 + 40 + type op uint16 41 + 42 + const ( 43 + opUnknown op = iota 44 + 45 + opUnify 46 + opDisjunction 47 + 48 + opLand 49 + opLor 50 + opNot 51 + 52 + opEql 53 + opNeq 54 + 55 + opLss 56 + opGtr 57 + opLeq 58 + opGeq 59 + 60 + opAdd 61 + opSub 62 + opMul 63 + opQuo 64 + opRem 65 + 66 + opIDiv 67 + opIMod 68 + opIQuo 69 + opIRem 70 + 71 + opRange // Used in computedSource 72 + ) 73 + 74 + var opStrings = []string{ 75 + opUnknown: "??", 76 + 77 + opUnify: "&", 78 + opDisjunction: "|", 79 + 80 + opLand: "&&", 81 + opLor: "||", 82 + opNot: "!", 83 + 84 + opEql: "==", 85 + opNeq: "!=", 86 + 87 + opLss: "<", 88 + opGtr: ">", 89 + opLeq: "<=", 90 + opGeq: ">=", 91 + 92 + opAdd: "+", 93 + opSub: "-", 94 + opMul: "*", 95 + opQuo: "/", 96 + opRem: "%", 97 + 98 + opIDiv: "div", 99 + opIMod: "mod", 100 + opIQuo: "quo", 101 + opIRem: "rem", 102 + 103 + opRange: "..", 104 + } 105 + 106 + func (op op) String() string { return opStrings[op] } 107 + 108 + var tokenMap = map[token.Token]op{ 109 + token.DISJUNCTION: opDisjunction, // | 110 + token.UNIFY: opUnify, // & 111 + 112 + token.ADD: opAdd, // + 113 + token.SUB: opSub, // - 114 + token.MUL: opMul, // * 115 + token.QUO: opQuo, // / 116 + token.REM: opRem, // % 117 + 118 + token.IDIV: opIDiv, // div 119 + token.IMOD: opIMod, // mod 120 + token.IQUO: opIQuo, // quo 121 + token.IREM: opIRem, // rem 122 + 123 + token.LAND: opLand, // && 124 + token.LOR: opLor, // || 125 + 126 + token.EQL: opEql, // == 127 + token.LSS: opLss, // < 128 + token.GTR: opGtr, // > 129 + token.NOT: opNot, // ! 130 + 131 + token.NEQ: opNeq, // != 132 + token.LEQ: opLeq, // <= 133 + token.GEQ: opGeq, // >= 134 + 135 + token.RANGE: opRange, // .. 136 + } 137 + 138 + var opMap = map[op]token.Token{} 139 + 140 + func init() { 141 + for t, o := range tokenMap { 142 + opMap[o] = t 143 + } 144 + }
+1074
cue/resolve_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "flag" 19 + "fmt" 20 + "testing" 21 + 22 + "cuelang.org/go/cue/errors" 23 + "cuelang.org/go/cue/parser" 24 + "cuelang.org/go/cue/token" 25 + ) 26 + 27 + var traceOn = flag.Bool("debug", false, "enable tracing") 28 + 29 + func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, errors.List) { 30 + t.Helper() 31 + ctx, inst, errs := compileInstance(t, body) 32 + return ctx, inst.rootValue.evalPartial(ctx).(*structLit), errs 33 + } 34 + 35 + func compileFile(t *testing.T, body string) (*context, *structLit) { 36 + t.Helper() 37 + ctx, inst, errs := compileInstance(t, body) 38 + if errs != nil { 39 + t.Fatal(errs) 40 + } 41 + return ctx, inst.rootValue.evalPartial(ctx).(*structLit) 42 + } 43 + 44 + func compileInstance(t *testing.T, body string) (*context, *Instance, errors.List) { 45 + t.Helper() 46 + 47 + fset := token.NewFileSet() 48 + x := newIndex(fset).NewInstance(nil) 49 + f, err := parser.ParseFile(fset, "test", body, parser.ParseLambdas) 50 + ctx := x.newContext() 51 + 52 + switch errs := err.(type) { 53 + case nil: 54 + x.insertFile(f) 55 + case errors.List: 56 + return ctx, x, errs 57 + default: 58 + t.Fatal(err) 59 + } 60 + return ctx, x, nil 61 + } 62 + 63 + func rewriteHelper(t *testing.T, cases []testCase, r rewriteMode) { 64 + t.Helper() 65 + for _, tc := range cases { 66 + t.Run(tc.desc, func(t *testing.T) { 67 + t.Helper() 68 + ctx, obj := compileFile(t, tc.in) 69 + ctx.trace = *traceOn 70 + root := testResolve(ctx, obj, r) 71 + 72 + got := debugStr(ctx, root) 73 + if v := ctx.processDelayedConstraints(); v != nil { 74 + got += fmt.Sprintf("\n%s", debugStr(ctx, v)) 75 + } 76 + 77 + // Copy the result 78 + if got != tc.out { 79 + fn := t.Errorf 80 + if tc.skip { 81 + fn = t.Skipf 82 + } 83 + fn("output differs:\ngot %s\nwant %s", got, tc.out) 84 + } 85 + }) 86 + } 87 + } 88 + 89 + type testCase struct { 90 + desc string 91 + in string 92 + out string 93 + skip bool 94 + } 95 + 96 + func TestBasicRewrite(t *testing.T) { 97 + testCases := []testCase{{ 98 + desc: "errors", 99 + in: ` 100 + a: _|_ & _|_ 101 + b: null & _|_ 102 + c: b.a == _|_ 103 + d: _|_ != b.a 104 + e: _|_ == _|_ 105 + `, 106 + out: `<0>{a: _|_(from source), b: _|_(from source), c: true, d: false, e: true}`, 107 + }, { 108 + desc: "arithmetic", 109 + in: ` 110 + sum: -1 + +2 // 1 111 + str: "foo" + "bar" // "foobar" 112 + div1: 2.0 / 3 * 6 // 4 113 + div2: 2 / 3 * 6 // 4 114 + rem: 2 % 3 // 2 115 + e: 2 + "a" // _|_: unsupported op +(int, string)) 116 + b: 1 != 4 117 + `, 118 + out: `<0>{sum: 1, str: "foobar", div1: 4.00000000000000000000000, div2: 4.00000000000000000000000, rem: 2, e: _|_((2 + "a"):unsupported op +(number, string)), b: true}`, 119 + }, { 120 + desc: "integer-specific arithmetic", 121 + in: ` 122 + q1: 5 quo 2 // 2 123 + q2: 5 quo -2 // -2 124 + q3: -5 quo 2 // -2 125 + q4: -5 quo -2 // 2 126 + qe1: 2.0 quo 1 127 + qe2: 2 quo 1.0 128 + 129 + r1: 5 rem 2 // 1 130 + r2: 5 rem -2 // 1 131 + r3: -5 rem 2 // -1 132 + r4: -5 rem -2 // -1 133 + re1: 2.0 rem 1 134 + re2: 2 rem 1.0 135 + 136 + d1: 5 div 2 // 2 137 + d2: 5 div -2 // -2 138 + d3: -5 div 2 // -3 139 + d4: -5 div -2 // 3 140 + de1: 2.0 div 1 141 + de2: 2 div 1.0 142 + 143 + m1: 5 mod 2 // 1 144 + m2: 5 mod -2 // 1 145 + m3: -5 mod 2 // 1 146 + m4: -5 mod -2 // 1 147 + me1: 2.0 mod 1 148 + me2: 2 mod 1.0 149 + 150 + // TODO: handle divide by zero 151 + `, 152 + out: `<0>{q1: 2, q2: -2, q3: -2, q4: 2, ` + 153 + `qe1: _|_((2.0 quo 1):unsupported op quo(float, number)), ` + 154 + `qe2: _|_((2 quo 1.0):unsupported op quo(number, float)), ` + 155 + `r1: 1, r2: 1, r3: -1, r4: -1, re1: ` + 156 + `_|_((2.0 rem 1):unsupported op rem(float, number)), ` + 157 + `re2: _|_((2 rem 1.0):unsupported op rem(number, float)), ` + 158 + `d1: 2, d2: -2, d3: -3, d4: 3, ` + 159 + `de1: _|_((2.0 div 1):unsupported op div(float, number)), ` + 160 + `de2: _|_((2 div 1.0):unsupported op div(number, float)), ` + 161 + `m1: 1, m2: 1, m3: 1, m4: 1, ` + 162 + `me1: _|_((2.0 mod 1):unsupported op mod(float, number)), ` + 163 + `me2: _|_((2 mod 1.0):unsupported op mod(number, float))}`, 164 + }, { 165 + desc: "booleans", 166 + in: ` 167 + t: true 168 + t: !false 169 + f: false 170 + f: !t 171 + e: true 172 + e: !true 173 + `, 174 + out: "<0>{t: true, f: false, e: _|_(true:failed to unify: true != false)}", 175 + }, { 176 + desc: "boolean arithmetic", 177 + in: ` 178 + a: true && true 179 + b: true || false 180 + c: false == true 181 + d: false != true 182 + e: true & true 183 + f: true & false 184 + `, 185 + out: "<0>{a: true, b: true, c: false, d: true, e: true, f: _|_(true:failed to unify: true != false)}", 186 + }, { 187 + desc: "basic type", 188 + in: ` 189 + a: 1 & int 190 + b: number & 1 191 + c: 1.0 192 + c: float 193 + d: int & float // _|_ 194 + e: "4" & string 195 + f: true 196 + f: bool 197 + `, 198 + out: `<0>{a: 1, b: 1, c: 1.0, d: _|_((int & float):unsupported op &((int)*, (float)*)), e: "4", f: true}`, 199 + }, { 200 + desc: "escaping", 201 + 202 + in: ` 203 + a: "foo\nbar", 204 + b: a, 205 + 206 + // TODO: mimic http://exploringjs.com/es6/ch_template-literals.html#sec_introduction-template-literals 207 + `, 208 + out: `<0>{a: "foo\nbar", b: "foo\nbar"}`, 209 + // out: `<0>{a: "foo\nbar", b: <0>.a}`, 210 + }, { 211 + desc: "reference", 212 + in: ` 213 + a: b 214 + b: 2 215 + d: { 216 + d: 3 217 + e: d 218 + } 219 + e: { 220 + e: { 221 + v: 1 222 + } 223 + f: { 224 + v: e.v 225 + } 226 + } 227 + `, 228 + out: "<0>{a: 2, b: 2, d: <1>{d: 3, e: 3}, e: <2>{e: <3>{v: 1}, f: <4>{v: 1}}}", 229 + }, { 230 + desc: "lists", 231 + in: ` 232 + list: [1,2,3] 233 + index: [1,2,3][1] 234 + unify: [1,2,3] & [_,2,3] 235 + e: [] & 4 236 + e2: [3]["d"] 237 + e3: [3][-1] 238 + e4: [1, 2, ...4..5] & [1, 2, 4, 8] 239 + e5: [1, 2, 4, 8] & [1, 2, ...4..5] 240 + `, 241 + out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):unsupported op &(list, number)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: _|_(((4..5) & 8):value 8 not in range (4..5)), e5: _|_(((4..5) & 8):value 8 not in range (4..5))}`, 242 + }, { 243 + desc: "selecting", 244 + in: ` 245 + obj: {a: 1, b: 2} 246 + index: {a: 1, b: 2}["b"] 247 + mulidx: {a: 1, b: {a:1, b: 3}}["b"]["b"] 248 + e: {a: 1}[4] 249 + f: {a: 1}.b 250 + g: {a: 1}["b"] 251 + h: [3].b 252 + `, 253 + out: `<0>{obj: <1>{a: 1, b: 2}, index: 2, mulidx: 3, e: _|_(4:invalid struct index 4 (type number)), f: _|_(<2>{a: 1}.b:undefined field "b"), g: _|_(<3>{a: 1}["b"]:undefined field "b"), h: _|_([3]:invalid operation: [3].b (type list does not support selection))}`, 254 + }, { 255 + desc: "obj unify", 256 + in: ` 257 + o1: {a: 1 } & { b: 2} // {a:1,b:2} 258 + o2: {a: 1, b:2 } & { b: 2} // {a:1,b:2} 259 + o3: {a: 1 } & { a:1, b: 2} // {a:1,b:2} 260 + o4: {a: 1 } & { b: 2} // {a:1,b:2} 261 + o4: {a: 1, b:2 } & { b: 2} 262 + o4: {a: 1 } & { a:1, b: 2} 263 + e: 1 // 1 & {a:3} 264 + e: {a:3} 265 + `, 266 + out: "<0>{o1: <1>{a: 1, b: 2}, o2: <2>{a: 1, b: 2}, o3: <3>{a: 1, b: 2}, o4: <4>{a: 1, b: 2}, e: _|_((1 & <5>{a: 3}):unsupported op &(number, struct))}", 267 + }, { 268 + desc: "disjunctions", 269 + in: ` 270 + o1: 1 | 2 | 3 271 + o2: (1 | 2 | 3) & 1 272 + o3: 2 & (1 | 2 | 3) 273 + o4: (1 | 2 | 3) & (1 | 2 | 3) 274 + o5: (1 | 2 | 3) & (3 | 2 | 1) 275 + o6: (1 | 2 | 3) & (3 | 1 | 2) 276 + o7: (1 | 2 | 3) & (2 | 3) 277 + o8: (1 | 2 | 3) & (3 | 2) 278 + o9: (2 | 3) & (1 | 2 | 3) 279 + o10: (3 | 2) & (1 | 2 | 3) 280 + 281 + // All errors are treated the same as per the unification model. 282 + i1: [1, 2][3] | "c" 283 + `, 284 + out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | 2 | 3), o5: (1! | 2! | 3!), o6: (1! | 2! | 3!), o7: (2 | 3), o8: (2! | 3!), o9: (2 | 3), o10: (3! | 2!), i1: "c"}`, 285 + }, { 286 + desc: "lambda", 287 + in: ` 288 + o1(A:1, B:2) -> { a: A, b: B } 289 + oe() -> { a: 1, b: 2 } 290 + l1: (A:1, B:2) -> { a: A, b: B } 291 + c1: ((A:int, B:int) -> {a:A, b:B})(1, 2) 292 + `, 293 + // TODO(P1): don't let values refer to themselves. 294 + out: "<0>{o1: <1>(A: 1, B: 2)-><2>{a: <1>.A, b: <1>.B}, oe: <3>()-><4>{a: 1, b: 2}, l1: <5>(A: 1, B: 2)-><6>{a: <5>.A, b: <5>.B}, c1: <7>{a: 1, b: 2}}", 295 + }, { 296 + desc: "types", 297 + in: ` 298 + i: int 299 + j: int & 3 300 + s: string 301 + t: "s" & string 302 + e: int & string 303 + e2: 1 & string 304 + b: !int 305 + p: +true 306 + m: -false 307 + `, 308 + out: `<0>{i: int, j: 3, s: string, t: "s", e: _|_((int & string):unsupported op &((int)*, (string)*)), e2: _|_((1 & string):unsupported op &(number, (string)*)), b: _|_(!int:unary '!' requires bool value, found (int)*), p: _|_(+true:unary '+' requires numeric value, found bool), m: _|_(-false:unary '-' requires numeric value, found bool)}`, 309 + }, { 310 + desc: "comparisson", 311 + in: ` 312 + lss: 1 < 2 313 + leq: 1 <= 1.0 314 + leq: 2.0 <= 3 315 + eql: 1 == 1.0 316 + neq: 1.0 == 1 317 + gtr: !(2 > 3) 318 + geq: 2.0 >= 2 319 + seq: "a" + "b" == "ab" 320 + err: 2 == "s" 321 + `, 322 + out: `<0>{lss: true, leq: true, eql: true, neq: true, gtr: true, geq: true, seq: true, err: _|_((2 == "s"):unsupported op ==(number, string))}`, 323 + }, { 324 + desc: "null", 325 + in: ` 326 + eql: null == null 327 + neq: null != null 328 + unf: null & null 329 + 330 + // errors 331 + eqe1: null == 1 332 + eqe2: 1 == null 333 + nee1: "s" != null 334 + call: null() 335 + `, 336 + out: `<0>{eql: true, neq: false, unf: null, eqe1: _|_((null == 1):unsupported op ==(null, number)), eqe2: _|_((1 == null):unsupported op ==(number, null)), nee1: _|_(("s" != null):unsupported op !=(string, null)), call: _|_(null:cannot call non-function null (type null))}`, 337 + }, { 338 + desc: "self-reference cycles", 339 + in: ` 340 + a: b - 100 341 + b: a + 100 342 + 343 + c: [c[1], c[0]] 344 + `, 345 + out: `<0>{a: _|_(cycle detected), b: _|_(cycle detected), c: _|_(cycle detected)}`, 346 + // }, { 347 + // desc: "resolved self-reference cycles", 348 + // in: ` 349 + // a: b - 100 350 + // b: a + 100 351 + // b: 200 352 + 353 + // c: [c[1], a] // TODO: should be allowed 354 + 355 + // s1: s2 & {a: 1} 356 + // s2: s3 & {b: 2} 357 + // s3: s1 & {c: 3} 358 + // `, 359 + // out: `<0>{a: 100, b: 200, c: _|_(cycle detected)}`, 360 + }, { 361 + desc: "delayed constraint failure", 362 + in: ` 363 + a: b - 100 364 + b: a + 110 365 + b: 200 366 + `, 367 + out: `<0>{a: 100, b: 200} 368 + _|_(((<1>.a + 110) & 200):constraint violated: _|_((210 & 200):cannot unify numbers 210 and 200))`, 369 + // TODO: find a way to mark error in data. 370 + }} 371 + rewriteHelper(t, testCases, evalPartial) 372 + } 373 + 374 + func TestChooseFirst(t *testing.T) { 375 + testCases := []testCase{{ 376 + desc: "pick first", 377 + in: ` 378 + a: 5 | "a" | true 379 + b c: { 380 + a: 2 381 + } | { 382 + a : 3 383 + } 384 + `, 385 + out: "<0>{a: 5, b: <1>{c: <2>{a: 2}}}", 386 + }, { 387 + desc: "simple disambiguation conflict", 388 + in: ` 389 + a: "a" | "b" 390 + b: "b" | "a" 391 + c: a & b 392 + `, 393 + out: `<0>{a: "a", b: "b", c: _|_(("a"! | "b"!):ambiguous disjunction)}`, 394 + }, { 395 + desc: "disambiguation non-conflict", 396 + in: ` 397 + a: "a" | ("b" | "c") 398 + b: ("a" | "b") | "c" 399 + c: a & b 400 + `, 401 + out: `<0>{a: "a", b: "a", c: "a"}`, 402 + }} 403 + rewriteHelper(t, testCases, evalFull) 404 + } 405 + 406 + func TestResolve(t *testing.T) { 407 + testCases := []testCase{{ 408 + in: ` 409 + a: b.c.d 410 + b c: { d: 3 } 411 + c: { c: d.d, } 412 + d: { d: 2 } 413 + `, 414 + out: "<0>{a: 3, b: <1>{c: <2>{d: 3}}, c: <3>{c: 2}, d: <4>{d: 2}}", 415 + }, { 416 + in: ` 417 + a: _ 418 + b: a 419 + a: { d: 1, d: _ } 420 + b: _ 421 + `, 422 + out: `<0>{a: <1>{d: 1}, b: <2>{d: 1}}`, 423 + }, { 424 + desc: "JSON", 425 + in: ` 426 + "a": 3 427 + b: a 428 + o: { "a\nb": 2 } // TODO: use $ for root? 429 + c: o["a\nb"] 430 + `, 431 + out: `<0>{a: 3, b: 3, o: <1>{"a\nb": 2}, c: 2}`, 432 + }, { 433 + desc: "arithmetic", 434 + in: ` 435 + v1: 1.0T/2.0 // 436 + v2: 2.0 == 2 437 + i1: 1 438 + v5: 2.0 / i1 // TODO: should probably fail 439 + e1: 2.0 % 3 440 + e2: int & 4.0/2.0 441 + `, 442 + out: `<0>{v1: 5e+11, v2: true, i1: 1, v5: 2, e1: _|_((2.0 % 3):unsupported op %(float, number)), e2: _|_((int & 2):unsupported op &((int)*, float))}`, 443 + // }, { 444 + // desc: "null coalescing", 445 + // in: ` 446 + // a: null 447 + // b: a.x 448 + // c: a["x"] 449 + // `, 450 + // out: ``, 451 + }, { 452 + desc: "call", 453 + in: ` 454 + a: { a: (P, Q) -> {p:P, q:Q} } 455 + b: a // reference different nodes 456 + c: a.a(1, 2) 457 + `, 458 + out: "<0>{a: <1>{a: <2>(P: _, Q: _)-><3>{p: <2>.P, q: <2>.Q}}, b: <4>{a: <2>(P: _, Q: _)-><3>{p: <2>.P, q: <2>.Q}}, c: <5>{p: 1, q: 2}}", 459 + }, { 460 + desc: "call of lambda", 461 + in: ` 462 + a(P, Q) -> {p:P, q:Q} 463 + a(P, Q) -> {p:P, q:Q} 464 + ai(P, Q) -> {p:Q, q:P} 465 + b: a(1,2) 466 + c: (a | b)(1) 467 + d: ([] | (a) -> 3)(2) 468 + e1: a(1) 469 + `, 470 + out: "<1>{a: <2>(P: _, Q: _)->(<3>{p: <2>.P, q: <2>.Q} & <4>{p: <2>.P, q: <2>.Q}), ai: <5>(P: _, Q: _)-><6>{p: <5>.Q, q: <5>.P}, b: <7>{p: 1, q: 2}, c: _|_((<8>.a | <8>.b) (1):number of arguments does not match (2 vs 1)), d: _|_(([] | <0>(a: _)->3):cannot call non-function [] (type list)), e1: _|_(<8>.a (1):number of arguments does not match (2 vs 1))}", 471 + }, { 472 + desc: "reference across tuples and back", 473 + // Tests that it is okay to partially evaluate structs. 474 + in: ` 475 + a: { c: b.e, d: b.f } 476 + b: { e: 3, f: a.c } 477 + `, 478 + out: "<0>{a: <1>{c: 3, d: 3}, b: <2>{e: 3, f: 3}}", 479 + }, { 480 + desc: "index", 481 + in: ` 482 + a: [2][0] 483 + b: {foo:"bar"}["foo"] 484 + c: (l|{"3":3})["3"] 485 + d: ([]|[1])[0] 486 + l: [] 487 + e1: [2][""] 488 + e2: 2[2] 489 + e3: [][true] 490 + e4: [1,2,3][3] 491 + e5: [1,2,3][-1] 492 + e6: ([]|{})[1] 493 + `, 494 + out: `<0>{a: 2, b: "bar", c: _|_("3":invalid list index "3" (type string)), l: [], d: _|_([]:index 0 out of bounds), e1: _|_("":invalid list index "" (type string)), e2: _|_(2:invalid operation: 2[2] (type number does not support indexing)), e3: _|_(true:invalid list index true (type bool)), e4: _|_([1,2,3]:index 3 out of bounds), e5: _|_(-1:invalid list index -1 (index must be non-negative)), e6: _|_([]:index 1 out of bounds)}`, 495 + }, { 496 + desc: "string index", 497 + in: ` 498 + a0: "abc"[0] 499 + a1: "abc"[1] 500 + a2: "abc"[2] 501 + a3: "abc"[3] 502 + a4: "abc"[-1] 503 + 504 + b: "zoëven"[2] 505 + `, 506 + out: `<0>{a0: "a", a1: "b", a2: "c", a3: _|_("abc":index 3 out of bounds), a4: _|_(-1:invalid string index -1 (index must be non-negative)), b: "ë"}`, 507 + }, { 508 + desc: "disjunctions of lists", 509 + in: ` 510 + l: [ int, int ] | [ string, string ] 511 + 512 + l1: [ "a", "b" ] 513 + l2: l & [ "c", "d" ] 514 + `, 515 + out: `<0>{l: ([int,int] | [string,string]), l1: ["a","b"], l2: ["c","d"]}`, 516 + }, { 517 + desc: "slice", 518 + in: ` 519 + a: [2][0:0] 520 + b: [0][1:1] 521 + e1: [][1:1] 522 + e2: [0][-1:0] 523 + e3: [0][1:0] 524 + e4: [0][1:2] 525 + e5: 4[1:2] 526 + e6: [2]["":] 527 + e7: [2][:"9"] 528 + 529 + `, 530 + out: `<0>{a: [], b: [], e1: _|_(1:slice bounds out of range), e2: _|_([0]:negative slice index), e3: _|_([0]:invalid slice index: 1 > 0), e4: _|_(2:slice bounds out of range), e5: _|_(4:cannot slice 4 (type number)), e6: _|_("":invalid slice index "" (type string)), e7: _|_("9":invalid slice index "9" (type string))}`, 531 + }, { 532 + desc: "string slice", 533 + in: ` 534 + a0: ""[0:0] 535 + a1: ""[:] 536 + a2: ""[0:] 537 + a3: ""[:0] 538 + b0: "abc"[0:0] 539 + b1: "abc"[0:1] 540 + b2: "abc"[0:2] 541 + b3: "abc"[0:3] 542 + b4: "abc"[3:3] 543 + b5: "abc"[1:] 544 + b6: "abc"[:2] 545 + 546 + // TODO: supported extended graphemes, instead of just runes. 547 + u: "Spaß"[3:4] 548 + `, 549 + out: `<0>{a0: "", a1: "", a2: "", a3: "", b0: "", b1: "a", b2: "ab", b3: "abc", b4: "", b5: "bc", b6: "ab", u: "ß"}`, 550 + }, { 551 + desc: "list types", 552 + in: ` 553 + l0: 3*[int] 554 + l0: [1, 2, 3] 555 + l1:(0..5)*[string] 556 + l1: ["a", "b"] 557 + l2: (0..5)*[{ a: int }] 558 + l2: [{a: 1}, {a: 2, b: 3}] 559 + l3: (0..10)*[int] 560 + l3: [1, 2, 3, ...] 561 + 562 + s1: ((0..6)*[int])[2:3] // TODO: simplify 1*[int] to [int] 563 + s2: [0,2,3][1:2] 564 + 565 + i1: ((0..6)*[int])[2] 566 + i2: [0,2,3][2] 567 + 568 + t0: [...{a: 8}] 569 + t0: [{}] 570 + 571 + e0: (2..5)*[{}] 572 + e0: [{}] 573 + 574 + e1: 0.._*[...int] 575 + `, 576 + out: `<0>{l0: [1,2,3], l1: ["a","b"], l2: [<1>{a: 1},<2>{a: 2, b: 3}], l3: (3..10)*[int]([1,2,3, ...int]), s1: 1*[int], s2: [2], i1: int, i2: 3, t0: [<3>{a: 8}], e0: _|_(((2..5)*[<4>{}] & [<5>{}]):incompatible list lengths: value 1 not in range (2..5)), e1: [, ...int]}`, 577 + }, { 578 + desc: "list arithmetic", 579 + in: ` 580 + l0: 3*[1, 2, 3] 581 + l1: 0*[1, 2, 3] 582 + l2: 10*[] 583 + l3: (0..2)*[] 584 + l4: (0..2)*[int] 585 + l5: (0..2)*(int*[int]) 586 + l6: 3*((3..4)*[int]) 587 + `, 588 + out: `<0>{l0: [1,2,3,1,2,3,1,2,3], l1: [], l2: [], l3: [], l4: (0..2)*[int], l5: (0..2)*[int], l6: (9..12)*[int]}`, 589 + }, { 590 + desc: "correct error messages", 591 + // Tests that it is okay to partially evaluate structs. 592 + in: ` 593 + a: "a" & 1 594 + `, 595 + out: `<0>{a: _|_(("a" & 1):unsupported op &(string, number))}`, 596 + }, { 597 + desc: "structs", 598 + in: ` 599 + a: t & { c: 5 } // {c:5,d:15} 600 + b: ti & { c: 7 } // {c:7,d:21} 601 + t: { c: number, d: c * 3 } // {c:number,d:number*3} 602 + ti: t & { c: int } 603 + `, 604 + out: `<0>{a: <1>{c: 5, d: 15}, t: <2>{c: number, d: (<3>.c * 3)}, b: <4>{c: 7, d: 21}, ti: <5>{c: int, d: (<6>.c * 3)}}`, 605 + }, { 606 + desc: "reference to root", 607 + in: ` 608 + a: { b: int } 609 + c: a & { 610 + b: 100 611 + d: a.b + 3 // do not resolve as c != a. 612 + 613 + // TODO(crash) 614 + // e: int; e < 100 // where clause can be different. 615 + } 616 + x: { 617 + b: int 618 + c: b + 5 619 + } 620 + y: x & { 621 + b: 100 622 + // c should resolve to 105 623 + } 624 + v: { 625 + b: int 626 + c: v.b + 5 // reference starting from copied node. 627 + } 628 + w: v & { b: 100 } 629 + wp: v & { b: 100 } 630 + `, 631 + out: `<0>{a: <1>{b: int}, c: <2>{b: 100, d: (<3>.a.b + 3)}, x: <4>{b: int, c: (<5>.b + 5)}, y: <6>{b: 100, c: 105}, v: <7>{b: int, c: (<8>.b + 5)}, w: <9>{b: 100, c: 105}, wp: <10>{b: 100, c: 105}}`, 632 + }, { 633 + desc: "references from template to concrete", 634 + in: ` 635 + res: [t] 636 + t <X>: { 637 + a: c + b.str 638 + b str: string 639 + c: "X" 640 + } 641 + t x: { b str: "DDDD" } 642 + `, 643 + out: `<0>{res: [<1>{<>: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <5>{a: "XDDDD", c: "X", b: <6>{str: "DDDD"}}}], t: <7>{<>: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <8>{a: "XDDDD", c: "X", b: <9>{str: "DDDD"}}}}`, 644 + }, { 645 + desc: "interpolation", 646 + in: ` 647 + a: "\(4)" 648 + b: "one \(a) two \( a + c )" 649 + c: "one" 650 + d: "\(r)" 651 + u: "\(_)" 652 + r: _ 653 + e: "\([])"`, 654 + out: `<0>{a: "4", b: "one 4 two 4one", c: "one", d: ""+<1>.r+"", r: _, u: ""+_+"", e: _|_([]:expression in interpolation must evaluate to a number kind or string (found list))}`, 655 + }, { 656 + desc: "diamond-shaped constraints", 657 + in: ` 658 + S: { 659 + A: { 660 + a: 1, 661 + }, 662 + B: A & { 663 + b: 2, 664 + } 665 + }, 666 + T: S & { // S == { A: { a:1 }, B: { a:1, b:2 } } 667 + A: { 668 + c: 3, 669 + }, 670 + B: { // S.B & A 671 + d: 4, // Combines constraints S.A, S.B, T.A, and T.B 672 + } 673 + }`, 674 + out: "<0>{S: <1>{A: <2>{a: 1}, B: <3>{a: 1, b: 2}}, T: <4>{A: <5>{a: 1, c: 3}, B: <6>{a: 1, b: 2, c: 3, d: 4}}}", 675 + }, { 676 + desc: "field templates", 677 + in: ` 678 + a: { 679 + <name>: int 680 + k: 1 681 + } 682 + b: { 683 + <x>: { x: 0, y: 1 | int } 684 + v: {} 685 + w: { x: 0 } 686 + } 687 + b: { <y>: {} } // TODO: allow different name 688 + c: { 689 + <Name>: { name: Name, y: 1 } 690 + foo: {} 691 + bar: _ 692 + } 693 + `, 694 + out: `<0>{a: <1>{<>: <2>(name: string)->int, k: 1}, b: <3>{<>: <4>(x: string)->(<5>{x: 0, y: (1 | int)} & <6>{}), v: <7>{x: 0, y: (1 | int)}, w: <8>{x: 0, y: (1 | int)}}, c: <9>{<>: <10>(Name: string)-><11>{name: <10>.Name, y: 1}, foo: <12>{name: "foo", y: 1}, bar: <13>{name: "bar", y: 1}}}`, 695 + }, { 696 + desc: "simple ranges", 697 + in: ` 698 + a: 1..2 699 + c: "a".."b" 700 + d: (2+3)..(4+5) // 5..9 701 + 702 + s1: 1..1 // 1 703 + s2: 1..2..3 // simplify (1..2)..3 to 1..3 704 + s3: (1..10)..5 // This is okay! 705 + s4: 5..(1..10) // This is okay! 706 + s5: (0..(5..6))..(1..10) 707 + `, 708 + out: `<0>{a: (1..2), c: ("a".."b"), d: (5..9), s1: 1, s2: (1..3), s3: (1..5), s4: (5..10), s5: (0..10)}`, 709 + }, { 710 + desc: "range unification", 711 + in: ` 712 + // with concrete values 713 + a1: 1..5 & 3 714 + a2: 1..5 & 1 715 + a3: 1..5 & 5 716 + a4: 1..5 & 6 717 + a5: 1..5 & 0 718 + 719 + a6: 3 & 1..5 720 + a7: 1 & 1..5 721 + a8: 5 & 1..5 722 + a9: 6 & 1..5 723 + a10: 0 & 1..5 724 + 725 + // with ranges 726 + b1: 1..5 & 1..5 727 + b2: 1..5 & 1..1 728 + b3: 1..5 & 5..5 729 + b4: 1..5 & 2..3 730 + b5: 1..5 & 3..9 731 + b6: 1..5 & 5..9 732 + b7: 1..5 & 6..9 733 + 734 + b8: 1..5 & 1..5 735 + b9: 1..1 & 1..5 736 + b10: 5..5 & 1..5 737 + b11: 2..3 & 1..5 738 + b12: 3..9 & 1..5 739 + b13: 5..9 & 1..5 740 + b14: 6..9 & 1..5 741 + 742 + // ranges with more general types 743 + c1: int & 1..5 744 + c2: 1..5 & int 745 + c3: string & 1..5 746 + c4: 1..5 & string 747 + 748 + // other types 749 + s1: "d" .. "z" & "e" 750 + s2: "d" .. "z" & "ee" 751 + 752 + n1: number & 1..2 753 + n2: int & 1.1 .. 1.3 754 + n3: 1.0..3.0 & 2 755 + n4: 0.0..0.1 & 0.09999 756 + n5: 1..5 & 2.5 757 + `, 758 + out: `<0>{a1: 3, a2: 1, a3: 5, a4: _|_(((1..5) & 6):value 6 not in range (1..5)), a5: _|_(((1..5) & 0):value 0 not in range (1..5)), a6: 3, a7: 1, a8: 5, a9: _|_(((1..5) & 6):value 6 not in range (1..5)), a10: _|_(((1..5) & 0):value 0 not in range (1..5)), b1: (1..5), b2: 1, b3: 5, b4: (2..3), b5: (3..5), b6: 5, b7: _|_(((1..5) & (6..9)):non-overlapping ranges (1..5) and (6..9)), b8: (1..5), b9: 1, b10: 5, b11: (2..3), b12: (3..5), b13: 5, b14: _|_(((6..9) & (1..5)):non-overlapping ranges (6..9) and (1..5)), c1: (1..5), c2: (1..5), c3: _|_((string & (1..5)):unsupported op &((string)*, (number)*)), c4: _|_(((1..5) & string):unsupported op &((number)*, (string)*)), s1: "e", s2: "ee", n1: (1..2), n2: _|_((int & (1.1..1.3)):unsupported op &((int)*, (float)*)), n3: 2, n4: 0.09999, n5: 2.5}`, 759 + }, { 760 + desc: "range arithmetic", 761 + in: ` 762 + r0: (1..2) * (4..5) 763 + r1: (1..2) * (-1..2) 764 + r2: (1.0..2.0) * (-0.5..1.0) 765 + r3: (1..2) + (4..5) 766 + 767 + i0: (1..2) * 2 768 + i1: (2..3) * -2 769 + i2: (1..2) * 2 770 + i3: (2..3) * -2 771 + 772 + t0: int * (1..2) // TODO: should be int 773 + t1: (1..2) * int 774 + t2: (1..2) * (0..int) 775 + t3: (1..int) * (0..2) 776 + t4: (1..int) * (-1..2) 777 + t5: _ * (1..2) // TODO: should be int 778 + 779 + s0: (1..2) - (3..5) 780 + s1: (1..2) - 1 781 + 782 + str0: ("ab".."cd") + "ef" 783 + str1: ("ab".."cd") + ("ef".."gh") 784 + str2: ("ab".."cd") + string 785 + 786 + `, 787 + out: `<0>{r0: (4..10), r1: (-2..4), r2: (-1.00..2.00), r3: (5..7), i0: (2..4), i1: (-6..-4), i2: (2..4), i3: (-6..-4), t0: (int * (1..2)), t1: int, t2: (0..int), t3: (0..int), t4: int, t5: _|_((_ * (1..2)):binary operation on non-ground top value), s0: (-4..-1), s1: (0..1), str0: ("abef".."cdef"), str1: ("abef".."cdgh"), str2: ("ab".."cd")}`, 788 + }, { 789 + desc: "predefined ranges", 790 + in: ` 791 + k1: int8 792 + k1: 44 793 + 794 + k2: int64 795 + k2: -8_000_000_000 796 + 797 + e1: int16 798 + e1: 100_000 799 + `, 800 + out: `<0>{k1: 44, k2: -8000000000, ` + 801 + `e1: _|_(((-32768..32767) & 100000):value 100000 not in range (-32768..32767))}`, 802 + // TODO(P3): if two fields are evaluating to the same field, their 803 + // values could be bound. 804 + // nodes: use a shared node 805 + // other: change to where clause 806 + // unknown: change to where clause. 807 + // in: ` 808 + // a: b 809 + // b: a 810 + // a: { d: 1 } 811 + // `, 812 + // out: `<0>{a: {d:1}, b: {d:1}}`, 813 + 814 + // TODO(P2): circular references in expressions can be resolved when 815 + // unified with complete values. 816 + // in: ` 817 + // a: 20 818 + // a: b + 10 // 20 & b+10 ==> 20; where a == b+10 819 + // b: a - 10 // 10-10 = 10 ==> 20 == 10+10 820 + // ` 821 + // out: `<0>{a_0:20, b_1:10}` 822 + }} 823 + rewriteHelper(t, testCases, evalPartial) 824 + } 825 + 826 + func TestFullEval(t *testing.T) { 827 + testCases := []testCase{{ 828 + desc: "detect conflicting value", 829 + in: ` 830 + a: 8000.9 831 + a: 7080 | int`, 832 + out: `<0>{a: _|_(empty disjunction after evaluation)}`, 833 + }, { 834 + desc: "resolve all disjunctions", 835 + in: ` 836 + service <Name>: { 837 + name: Name | string 838 + port: 7080 | int 839 + } 840 + service foo: _ 841 + service bar: { port: 8000 } 842 + service baz: { name: "foobar" } 843 + `, 844 + out: `<0>{service: <1>{<>: <2>(Name: string)-><3>{name: (<2>.Name | string), port: (7080 | int)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`, 845 + }, { 846 + desc: "field templates", 847 + in: ` 848 + a: { 849 + <name>: int 850 + k: 1 851 + } 852 + b: { 853 + <x>: { x: 0, y: 1 | int } 854 + v: {} 855 + w: { y: 0 } 856 + } 857 + b: { <y>: {} } // TODO: allow different name 858 + c: { 859 + <Name>: { name: Name, y: 1 } 860 + foo: {} 861 + bar: _ 862 + } 863 + `, 864 + out: `<0>{a: <1>{<>: <2>(name: string)->int, k: 1}, b: <3>{<>: <4>(x: string)->(<5>{x: 0, y: (1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{<>: <10>(Name: string)-><11>{name: <10>.Name, y: 1}, foo: <12>{name: "foo", y: 1}, bar: <13>{name: "bar", y: 1}}}`, 865 + }, { 866 + desc: "field comprehension", 867 + in: ` 868 + a: { "\(k)": v for k, v in b if k < "d" if v > b.a } 869 + b: { 870 + a: 1 871 + b: 2 872 + c: 3 873 + d: 4 874 + } 875 + c: { 876 + "\(k)": v <- 877 + for k, v in b 878 + if k < "d" 879 + if v > b.a 880 + } 881 + // TODO: Propagate error: 882 + // e: { "\(k)": v for k, v in b if k < "d" if v > b.a } 883 + `, 884 + out: `<0>{a: <1>{b: 2, c: 3}, b: <2>{a: 1, b: 2, c: 3, d: 4}, c: <3>{b: 2, c: 3}}`, 885 + }, { 886 + desc: "nested templates in one field", 887 + in: ` 888 + a <A> b <B>: { 889 + name: A 890 + kind: B 891 + } 892 + a "A" b "B": _ 893 + a "C" b "D": _ 894 + a "E" b "F": { c: "bar" } 895 + `, 896 + out: `<0>{a: <1>{<>: <2>(A: string)-><3>{b: <4>{<>: <5>(B: string)-><6>{name: <2>.A, kind: <5>.B}, }}, A: <7>{b: <8>{<>: <9>(B: string)-><10>{name: <11>.A, kind: <9>.B}, B: <12>{name: "A", kind: "B"}}}, C: <13>{b: <14>{<>: <15>(B: string)-><16>{name: <17>.A, kind: <15>.B}, D: <18>{name: "C", kind: "D"}}}, E: <19>{b: <20>{<>: <21>(B: string)-><22>{name: <23>.A, kind: <21>.B}, F: <24>{name: "E", kind: "F", c: "bar"}}}}}`, 897 + }, { 898 + desc: "template unification within one struct", 899 + in: ` 900 + a: { 901 + <A>: { name: A } 902 + <A>: { kind: A } 903 + } 904 + a "A": _ 905 + a "C": _ 906 + a "E": { c: "bar" } 907 + `, 908 + out: `<0>{a: <1>{<>: <2>(A: string)->(<3>{name: <2>.A} & <4>{kind: <2>.A}), ` + 909 + `A: <5>{name: "A", kind: "A"}, ` + 910 + `C: <6>{name: "C", kind: "C"}, ` + 911 + `E: <7>{name: "E", kind: "E", c: "bar"}}}`, 912 + }, { 913 + desc: "field comprehensions with multiple keys", 914 + in: ` 915 + a "\(x.a)" b "\(x.b)": x for x in [ 916 + {a: "A", b: "B" }, 917 + {a: "C", b: "D" }, 918 + {a: "E", b: "F" }, 919 + ] 920 + 921 + "\(x.a)" "\(x.b)": x for x in [ 922 + {a: "A", b: "B" }, 923 + {a: "C", b: "D" }, 924 + {a: "E", b: "F" }, 925 + ]`, 926 + out: `<0>{a: <1>{` + 927 + `A: <2>{b: <3>{B: <4>{a: "A", b: "B"}}}, ` + 928 + `C: <5>{b: <6>{D: <7>{a: "C", b: "D"}}}, ` + 929 + `E: <8>{b: <9>{F: <10>{a: "E", b: "F"}}}}, ` + 930 + `A: <11>{B: <12>{a: "A", b: "B"}}, ` + 931 + `C: <13>{D: <14>{a: "C", b: "D"}}, ` + 932 + `E: <15>{F: <16>{a: "E", b: "F"}}}`, 933 + }, { 934 + desc: "field comprehensions with templates", 935 + in: ` 936 + num: 1 937 + a: { 938 + <A> <B>: { 939 + name: A 940 + kind: B 941 + } if num < 5 942 + 943 + } 944 + a b c d: "bar" 945 + `, 946 + out: `<0>{num: 1, a: <1>{<>: <2>(A: string)-><3>{<>: <4>(B: string)-><5>{name: <2>.A, kind: <4>.B}, }, ` + 947 + `b: <6>{<>: <7>(B: string)-><8>{name: <9>.A, kind: <7>.B}, ` + 948 + `c: <10>{name: "b", kind: "c", ` + 949 + `d: "bar"}}}}`, 950 + }, { 951 + desc: "disjunctions of lists", 952 + in: ` 953 + l: [ int, int ] | [ string, string ] 954 + 955 + l1: [ "a", "b" ] 956 + l2: l & [ "c", "d" ] 957 + `, 958 + out: `<0>{l: [int,int], l1: ["a","b"], l2: ["c","d"]}`, 959 + }, { 960 + desc: "list comprehension", 961 + in: ` 962 + // a: [ k for k: v in b if k < "d" if v > b.a ] // TODO test error using common iso colon 963 + a: [ k for k, v in b if k < "d" if v > b.a ] 964 + b: { 965 + a: 1 966 + b: 2 967 + c: 3 968 + d: 4 969 + } 970 + c: [ x for _, x in b for _, y in b if x < y ] 971 + d: [ x for x, _ in a ] 972 + `, 973 + out: `<0>{a: ["b","c"], b: <1>{a: 1, b: 2, c: 3, d: 4}, c: [1,1,1,2,2,3], d: [0,1]}`, 974 + }, { 975 + desc: "struct comprehension with template", 976 + in: ` 977 + result: [ v for _, v in service ] 978 + 979 + service <Name>: { 980 + type: "service" 981 + name: Name | string 982 + port: 7080 | int 983 + } 984 + service foo: {} 985 + service bar: { port: 8000 } 986 + service baz: { name: "foobar" } 987 + `, 988 + out: `<0>{result: [` + 989 + `<1>{type: "service", name: "foo", port: 7080},` + 990 + `<2>{type: "service", name: "bar", port: 8000},` + 991 + `<3>{type: "service", name: "foobar", port: 7080}], ` + 992 + 993 + `service: <4>{` + 994 + `<>: <5>(Name: string)-><6>{type: "service", name: (<5>.Name | string), port: (7080 | int)}, ` + 995 + `foo: <7>{type: "service", name: "foo", port: 7080}, ` + 996 + `bar: <8>{type: "service", name: "bar", port: 8000}, ` + 997 + `baz: <9>{type: "service", name: "foobar", port: 7080}}}`, 998 + }, { 999 + desc: "resolutions in struct comprehension keys", 1000 + in: ` 1001 + a: { [b + "."]: "a" for _, b in ["c"] } 1002 + `, 1003 + out: `<0>{a: <1>{c.: "a"}}`, 1004 + }, { 1005 + desc: "recursive evaluation within list", 1006 + in: ` 1007 + l: [a] 1008 + a: b & { c: "t" } 1009 + b: { 1010 + d: c 1011 + c: string 1012 + } 1013 + l1: [a1] 1014 + a1: b1 & { c: "t" } 1015 + b1: { 1016 + d: "s" + c 1017 + c: string 1018 + } 1019 + `, 1020 + out: `<0>{l: [<1>{c: "t", d: "t"}], a: <2>{c: "t", d: "t"}, b: <3>{c: string, d: string}, l1: [<4>{c: "t", d: "st"}], a1: <5>{c: "t", d: "st"}, b1: <6>{c: string, d: _|_(("s" + string):unsupported op +(string, (string)*))}}`, 1021 + }, { 1022 + desc: "ips", 1023 + in: ` 1024 + IP: 4*[ 0..255 ] 1025 + 1026 + Private: 1027 + [ 192, 168, 0..255, 0..255 ] | 1028 + [ 10, 0..255, 0..255, 0..255] | 1029 + [ 172, 16..32, 0..255, 0..255 ] 1030 + 1031 + Inst: Private & [ _, 10, ... ] 1032 + 1033 + MyIP: Inst & [_, _, 10, 10 ] 1034 + `, 1035 + out: `<0>{IP: 4*[(0..255)], Private: [192,168,(0..255),(0..255)], Inst: [10,10,(0..255),(0..255)], MyIP: [10,10,10,10]}`, 1036 + }, { 1037 + desc: "complex interaction of groundness", 1038 + in: ` 1039 + res: [ y & { d: "b" } for x in a for y in x ] 1040 + res: [ a.b.c & { d: "b" } ] 1041 + 1042 + a b <C>: { d: string, s: "a" + d } 1043 + a b c d: string 1044 + `, 1045 + // TODO(perf): unification should catch shared node. 1046 + out: `<0>{res: [<1>{d: "b", s: "ab"}], a: <2>{b: <3>{<>: <4>(C: string)-><5>{d: string, s: ("a" + <5>.d)}, c: <6>{d: string, s: _|_(("a" + string):unsupported op +(string, (string)*))}}}}`, 1047 + }, { 1048 + desc: "complex groundness 2", 1049 + in: ` 1050 + r1: f1 & { y: "c" } 1051 + 1052 + f1: { y: string, res: a.b.c & { d: y } } 1053 + 1054 + a b c: { d: string, s: "a" + d } 1055 + a b <C>: { d: string, s: "a" + d } 1056 + a b c d: string 1057 + `, 1058 + out: `<0>{r1: <1>{y: "c", res: <2>{d: "c", s: "ac"}}, f1: <3>{y: string, res: <4>{d: string, s: _|_(("a" + string):unsupported op +(string, (string)*))}}, a: <5>{b: <6>{<>: <7>(C: string)-><8>{d: string, s: ("a" + <8>.d)}, c: <9>{d: string, s: _|_(("a" + string):unsupported op +(string, (string)*))}}}}`, 1059 + }, { 1060 + desc: "references from template to concrete", 1061 + in: ` 1062 + res: [t] 1063 + t <X>: { 1064 + a: c + b.str 1065 + b str: string 1066 + c: "X" 1067 + } 1068 + t x: { b str: "DDDD" } 1069 + `, 1070 + out: `<0>{res: [<1>{<>: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <5>{a: "XDDDD", c: "X", b: <6>{str: "DDDD"}}}], ` + 1071 + `t: <7>{<>: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <8>{a: "XDDDD", c: "X", b: <9>{str: "DDDD"}}}}`, 1072 + }} 1073 + rewriteHelper(t, testCases, evalFull) 1074 + }
+238
cue/rewrite.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + // TODO: nodeRefs are currently not updated if the structs they point to are 18 + // updated. Handing this in uses of rewrite is tedious and hard to get correct. 19 + // Make this a general mechanism. This can be done using a Tomabechi-like 20 + // approach of associating copies with nodes in one pass, and then make a 21 + // complete copy in a second. 22 + 23 + type rewriteFunc func(ctx *context, v value) (value, bool) 24 + 25 + func rewrite(ctx *context, v value, fn rewriteFunc) value { 26 + v, descend := fn(ctx, v) 27 + if !descend { 28 + return v 29 + } 30 + return v.rewrite(ctx, fn) 31 + } 32 + 33 + func (x *nodeRef) rewrite(ctx *context, fn rewriteFunc) value { 34 + return x 35 + } 36 + 37 + func (x *structLit) rewrite(ctx *context, fn rewriteFunc) value { 38 + emit := x.emit 39 + if emit != nil { 40 + emit = rewrite(ctx, x.emit, fn) 41 + } 42 + arcs := make(arcs, len(x.arcs)) 43 + obj := &structLit{baseValue: x.baseValue, emit: emit, arcs: arcs} 44 + changed := emit == x.emit 45 + for i, a := range x.arcs { 46 + v := rewrite(ctx, a.v, fn) 47 + arcs[i] = arc{a.feature, v, nil} 48 + changed = changed || arcs[i].v != v 49 + } 50 + if !changed { 51 + return x 52 + } 53 + return obj 54 + } 55 + 56 + func (x *selectorExpr) rewrite(ctx *context, fn rewriteFunc) value { 57 + v := rewrite(ctx, x.x, fn) 58 + if v == x.x { 59 + return x 60 + } 61 + return &selectorExpr{x.baseValue, v, x.feature} 62 + } 63 + 64 + func (x *indexExpr) rewrite(ctx *context, fn rewriteFunc) value { 65 + v := rewrite(ctx, x.x, fn) 66 + index := rewrite(ctx, x.index, fn) 67 + if v == x.x && index == x.index { 68 + return x 69 + } 70 + return &indexExpr{x.baseValue, v, index} 71 + } 72 + 73 + // Even more boring stuff below. 74 + 75 + func (x *builtin) rewrite(ctx *context, fn rewriteFunc) value { return x } 76 + func (x *top) rewrite(ctx *context, fn rewriteFunc) value { return x } 77 + func (x *bottom) rewrite(ctx *context, fn rewriteFunc) value { return x } 78 + func (x *basicType) rewrite(ctx *context, fn rewriteFunc) value { return x } 79 + func (x *nullLit) rewrite(ctx *context, fn rewriteFunc) value { return x } 80 + func (x *boolLit) rewrite(ctx *context, fn rewriteFunc) value { return x } 81 + func (x *stringLit) rewrite(ctx *context, fn rewriteFunc) value { return x } 82 + func (x *bytesLit) rewrite(ctx *context, fn rewriteFunc) value { return x } 83 + func (x *numLit) rewrite(ctx *context, fn rewriteFunc) value { return x } 84 + func (x *durationLit) rewrite(ctx *context, fn rewriteFunc) value { return x } 85 + 86 + func (x *rangeLit) rewrite(ctx *context, fn rewriteFunc) value { 87 + from := rewrite(ctx, x.from, fn) 88 + to := rewrite(ctx, x.to, fn) 89 + if from == x.from && to == x.to { 90 + return x 91 + } 92 + return &rangeLit{x.baseValue, from, to} 93 + } 94 + 95 + func (x *interpolation) rewrite(ctx *context, fn rewriteFunc) value { 96 + parts := make([]value, len(x.parts)) 97 + changed := false 98 + for i, p := range x.parts { 99 + parts[i] = rewrite(ctx, p, fn) 100 + changed = changed || parts[i] != p 101 + } 102 + if !changed { 103 + return x 104 + } 105 + return &interpolation{x.baseValue, x.k, parts} 106 + } 107 + 108 + func (x *list) rewrite(ctx *context, fn rewriteFunc) value { 109 + a := make([]value, len(x.a)) 110 + changed := false 111 + for i, e := range x.a { 112 + a[i] = rewrite(ctx, e, fn) 113 + changed = changed || a[i] != e 114 + } 115 + typ := rewrite(ctx, x.typ, fn) 116 + len := rewrite(ctx, x.len, fn) 117 + if !changed && typ == x.typ && len == x.len { 118 + return x 119 + } 120 + return &list{x.baseValue, a, typ, len} 121 + } 122 + 123 + func (x *sliceExpr) rewrite(ctx *context, fn rewriteFunc) value { 124 + v := rewrite(ctx, x.x, fn) 125 + lo := rewrite(ctx, x.lo, fn) 126 + hi := rewrite(ctx, x.hi, fn) 127 + if v == x.x && lo == x.lo && hi == x.hi { 128 + return x 129 + } 130 + return &sliceExpr{x.baseValue, v, lo, hi} 131 + } 132 + 133 + func (x *callExpr) rewrite(ctx *context, fn rewriteFunc) value { 134 + args := make([]value, len(x.args)) 135 + changed := false 136 + for i, a := range x.args { 137 + v := rewrite(ctx, a, fn) 138 + args[i] = v 139 + changed = changed || v != a 140 + } 141 + v := rewrite(ctx, x.x, fn) 142 + if !changed && v == x.x { 143 + return x 144 + } 145 + return &callExpr{baseValue: x.baseValue, x: v, args: args} 146 + } 147 + 148 + func (x *lambdaExpr) rewrite(ctx *context, fn rewriteFunc) value { 149 + arcs := make([]arc, len(x.arcs)) 150 + changed := false 151 + for i, a := range x.arcs { 152 + v := rewrite(ctx, a.v, fn) 153 + arcs[i] = arc{feature: a.feature, v: v} 154 + changed = changed || v != a.v 155 + } 156 + value := rewrite(ctx, x.value, fn) 157 + if !changed && value == x.value { 158 + return x 159 + } 160 + return &lambdaExpr{x.baseValue, &params{arcs}, value} 161 + } 162 + 163 + func (x *unaryExpr) rewrite(ctx *context, fn rewriteFunc) value { 164 + v := rewrite(ctx, x.x, fn) 165 + if v == x.x { 166 + return x 167 + } 168 + return &unaryExpr{x.baseValue, x.op, v} 169 + } 170 + 171 + func (x *binaryExpr) rewrite(ctx *context, fn rewriteFunc) value { 172 + left := rewrite(ctx, x.left, fn) 173 + right := rewrite(ctx, x.right, fn) 174 + if left == x.left && right == x.right { 175 + return x 176 + } 177 + return &binaryExpr{x.baseValue, x.op, left, right} 178 + } 179 + 180 + func (x *disjunction) rewrite(ctx *context, fn rewriteFunc) value { 181 + values := make([]dValue, len(x.values)) 182 + changed := false 183 + for i, d := range x.values { 184 + v := rewrite(ctx, d.val, fn) 185 + values[i] = dValue{v, d.ambiguous} 186 + changed = changed || v != d.val 187 + } 188 + if !changed { 189 + return x 190 + } 191 + return &disjunction{x.baseValue, values} 192 + } 193 + 194 + func (x *listComprehension) rewrite(ctx *context, fn rewriteFunc) value { 195 + clauses := rewrite(ctx, x.clauses, fn).(yielder) 196 + if clauses == x.clauses { 197 + return x 198 + } 199 + return &listComprehension{x.baseValue, clauses} 200 + } 201 + 202 + func (x *structComprehension) rewrite(ctx *context, fn rewriteFunc) value { 203 + clauses := rewrite(ctx, x.clauses, fn).(yielder) 204 + if clauses == x.clauses { 205 + return x 206 + } 207 + return &structComprehension{x.baseValue, clauses, x.isTemplate} 208 + } 209 + 210 + func (x *yield) rewrite(ctx *context, fn rewriteFunc) value { 211 + key := x.key 212 + if key != nil { 213 + key = rewrite(ctx, x.key, fn) 214 + } 215 + value := rewrite(ctx, x.value, fn) 216 + if key == x.key && value == x.value { 217 + return x 218 + } 219 + return &yield{x.baseValue, key, value} 220 + } 221 + 222 + func (x *guard) rewrite(ctx *context, fn rewriteFunc) value { 223 + condition := rewrite(ctx, x.condition, fn) 224 + value := rewrite(ctx, x.value, fn).(yielder) 225 + if condition == x.condition && value == x.value { 226 + return x 227 + } 228 + return &guard{x.baseValue, condition, value} 229 + } 230 + 231 + func (x *feed) rewrite(ctx *context, fn rewriteFunc) value { 232 + source := rewrite(ctx, x.source, fn) 233 + lambda := rewrite(ctx, x.fn, fn).(*lambdaExpr) 234 + if source == x.source && lambda == x.fn { 235 + return x 236 + } 237 + return &feed{x.baseValue, source, lambda} 238 + }
+104
cue/rewrite_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "strings" 20 + ) 21 + 22 + type rewriteMode int 23 + 24 + const ( 25 + evalRaw rewriteMode = iota 26 + evalSimplify 27 + evalPartial // all but disjunctions 28 + evalFull 29 + ) 30 + 31 + // testResolve recursively 32 + func testResolve(ctx *context, v value, m rewriteMode) (result value) { 33 + if m == evalRaw || v == nil { 34 + return v 35 + } 36 + return rewriteRec(ctx, v, v.evalPartial(ctx), m) 37 + } 38 + 39 + var indentLevel int 40 + 41 + func (c *context) ind() int { 42 + old := indentLevel 43 + indentLevel += 2 44 + return old 45 + } 46 + 47 + func (c *context) unindent(old int) { 48 + indentLevel = old 49 + } 50 + 51 + func (c *context) printIndent(args ...interface{}) { 52 + fmt.Print(strings.Repeat(" ", indentLevel)) 53 + c.println(args...) 54 + } 55 + 56 + func rewriteRec(ctx *context, raw value, eval evaluated, m rewriteMode) (result value) { 57 + if m >= evalPartial { 58 + if isIncomplete(eval) { 59 + return raw 60 + } 61 + if m == evalFull { 62 + eval = ctx.manifest(eval) 63 + if isBottom(eval) { 64 + return eval 65 + } 66 + } 67 + } 68 + switch x := eval.(type) { 69 + case *structLit: 70 + if m == evalFull { 71 + e := ctx.manifest(x) 72 + if isBottom(e) { 73 + return e 74 + } 75 + x = e.(*structLit) 76 + } 77 + arcs := make(arcs, len(x.arcs)) 78 + for i, a := range x.arcs { 79 + v := x.at(ctx, i) 80 + arcs[i] = arc{a.feature, rewriteRec(ctx, a.v, v, m), nil} 81 + } 82 + t := x.template 83 + if t != nil { 84 + v := rewriteRec(ctx, t, t.evalPartial(ctx), m) 85 + if isBottom(v) { 86 + return v 87 + } 88 + t = v.(*lambdaExpr) 89 + } 90 + emit := testResolve(ctx, x.emit, m) 91 + obj := &structLit{x.baseValue, emit, t, arcs} 92 + return obj 93 + case *list: 94 + a := make([]value, len(x.a)) 95 + for i := range x.a { 96 + a[i] = rewriteRec(ctx, x.a[i], x.at(ctx, i), m) 97 + } 98 + len := rewriteRec(ctx, x.len, x.len.(evaluated), m) 99 + typ := rewriteRec(ctx, x.typ, x.typ.(evaluated), m) 100 + return &list{x.baseValue, a, typ, len} 101 + default: 102 + return eval 103 + } 104 + }
+43
cue/strip.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + // This file defines a rewriter that strips a fully evaluated value of its 18 + // template. 19 + 20 + // TODO: currently strip templates does a full evaluation as it is hard to keep 21 + // nodeRef and copied structs in sync. This is far from efficient, but it is the 22 + // easiest to get correct. 23 + 24 + // stripTemplates evaluates v and strips the result of templates. 25 + func stripTemplates(ctx *context, v value) value { 26 + return rewrite(ctx, v, stripRewriter) 27 + } 28 + 29 + func stripRewriter(ctx *context, v value) (value, bool) { 30 + eval := ctx.manifest(v) 31 + switch x := eval.(type) { 32 + case *structLit: 33 + if x.template != nil { 34 + arcs := make(arcs, len(x.arcs)) 35 + for i, a := range x.arcs { 36 + v := rewrite(ctx, x.at(ctx, i), stripRewriter) 37 + arcs[i] = arc{a.feature, v, nil} 38 + } 39 + return &structLit{x.baseValue, x.emit, nil, arcs}, false 40 + } 41 + } 42 + return eval, true 43 + }
+61
cue/strip_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import "testing" 18 + 19 + func TestStripTemplates(t *testing.T) { 20 + testCases := []testCase{{ 21 + desc: "basic", 22 + in: ` 23 + foo <Name>: { name: Name } 24 + foo bar: { units: 5 } 25 + `, 26 + out: `<0>{foo: <1>{bar: <2>{name: "bar", units: 5}}}`, 27 + }, { 28 + desc: "top-level template", 29 + in: ` 30 + <Name>: { name: Name } 31 + bar: { units: 5 } 32 + `, 33 + out: `<0>{bar: <1>{name: "bar", units: 5}}`, 34 + }, { 35 + desc: "with reference", 36 + in: ` 37 + before: foo.bar 38 + foo <Name>: { name: Name } 39 + foo bar: { units: 5 } 40 + after: foo.bar 41 + `, 42 + out: `<0>{before: <1>{name: "bar", units: 5}, foo: <2>{bar: <3>{name: "bar", units: 5}}, after: <4>{name: "bar", units: 5}}`, 43 + }, { 44 + desc: "nested", 45 + in: ` 46 + <X1> foo <X2> bar <X3>: { name: X1+X2+X3 } 47 + a foo b bar c: { morning: true } 48 + `, 49 + out: `<0>{a: <1>{foo: <2>{b: <3>{bar: <4>{c: <5>{name: "abc", morning: true}}}}}}`}} 50 + for _, tc := range testCases { 51 + t.Run("", func(t *testing.T) { 52 + ctx, obj := compileFile(t, tc.in) 53 + 54 + v := stripTemplates(ctx, obj) 55 + 56 + if got := debugStr(ctx, v); got != tc.out { 57 + t.Errorf("output differs:\ngot %s\nwant %s", got, tc.out) 58 + } 59 + }) 60 + } 61 + }
+370
cue/subsume.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + ) 20 + 21 + type valueSubsumer interface { 22 + subsumesImpl(ctx *context, v value) bool 23 + } 24 + 25 + type subsumeMode int 26 + 27 + const ( 28 + // subChoose ensures values are elected before doing a subsumption. This 29 + // feature is on the conservative side and may result in false negatives. 30 + subChoose subsumeMode = 1 << iota 31 + ) 32 + 33 + // subsumes checks gt subsumes lt. If any of the values contains references or 34 + // unevaluated expressions, structural subsumption is performed. This means 35 + // subsumption is conservative; it may return false when a guarantee for 36 + // subsumption could be proven. For concreted values it returns the exact 37 + // relation. It never returns a false positive. 38 + func subsumes(ctx *context, gt, lt value, mode subsumeMode) bool { 39 + var v, w value 40 + if mode&subChoose == 0 { 41 + v = gt.evalPartial(ctx) 42 + w = lt.evalPartial(ctx) 43 + } else { 44 + v = ctx.manifest(gt) 45 + w = ctx.manifest(lt) 46 + } 47 + if !isIncomplete(v) && !isIncomplete(w) { 48 + gt = v 49 + lt = w 50 + } 51 + a := gt.kind() 52 + b := lt.kind() 53 + switch { 54 + case b == bottomKind: 55 + return true 56 + case b&^(a&b) != 0: 57 + // a does not have strictly more bits. This implies any ground kind 58 + // subsuming a non-ground type. 59 + return false 60 + // TODO: consider not supporting references. 61 + // case (a|b)&(referenceKind) != 0: 62 + // // no resolution if references are in play. 63 + // return false, false 64 + } 65 + return gt.subsumesImpl(ctx, lt, mode) 66 + } 67 + 68 + func (x *structLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 69 + if o, ok := v.(*structLit); ok { 70 + // all arcs in n must exist in v and its values must subsume. 71 + for _, a := range x.arcs { 72 + b, _ := o.lookup(ctx, a.feature) 73 + if b == nil || !subsumes(ctx, a.v, b, mode) { 74 + return false 75 + } 76 + } 77 + } 78 + return !isBottom(v) 79 + } 80 + 81 + func (*top) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 82 + return true 83 + } 84 + 85 + func (x *bottom) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 86 + // never called. 87 + return v.kind() == bottomKind 88 + } 89 + 90 + func (x *basicType) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 91 + return true 92 + } 93 + 94 + func (x *rangeLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 95 + k := unifyType(x.from.kind(), x.to.kind()) 96 + if k.isDone() && k.isGround() { 97 + switch y := v.(type) { 98 + case *rangeLit: 99 + // structural equivalence 100 + return subsumes(ctx, x.from, y.from, 0) && subsumes(ctx, x.to, y.to, 0) 101 + case *numLit, *stringLit, *durationLit: 102 + kv := v.kind() 103 + k, _ := matchBinOpKind(opAdd, k, kv) 104 + if k != kv { 105 + return false 106 + } 107 + v := v.(evaluated) 108 + if x.from != nil { 109 + from := minNumRaw(x.from) 110 + if !from.kind().isGround() { 111 + return subsumes(ctx, from, v, 0) // false negative is okay 112 + } 113 + return leq(ctx, x, x.from.(evaluated), v) 114 + } 115 + if x.to != nil { 116 + to := minNumRaw(x.to) 117 + if !to.kind().isGround() { 118 + return subsumes(ctx, to, v, 0) // false negative is okay 119 + } 120 + return leq(ctx, x, v, x.to.(evaluated)) 121 + } 122 + return true 123 + } 124 + } 125 + return isBottom(v) 126 + } 127 + 128 + func (x *nullLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 129 + return true 130 + } 131 + 132 + func (x *boolLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 133 + return x.b == v.(*boolLit).b 134 + } 135 + 136 + func (x *stringLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 137 + return x.str == v.(*stringLit).str 138 + } 139 + 140 + func (x *bytesLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 141 + return bytes.Equal(x.b, v.(*bytesLit).b) 142 + } 143 + 144 + func (x *numLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 145 + b := v.(*numLit) 146 + return x.v.Cmp(&b.v) == 0 147 + } 148 + 149 + func (x *durationLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 150 + return x.d == v.(*durationLit).d 151 + } 152 + 153 + func (x *list) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 154 + switch y := v.(type) { 155 + case *list: 156 + if !subsumes(ctx, x.len, y.len, mode) { 157 + return false 158 + } 159 + n := len(x.a) 160 + if len(y.a) < n { 161 + n = len(y.a) 162 + } 163 + for i, a := range x.a[:n] { 164 + if !subsumes(ctx, a, y.a[i], mode) { 165 + return false 166 + } 167 + } 168 + if y.isOpen() { 169 + return subsumes(ctx, x.typ, y.typ, 0) 170 + } 171 + for i := range y.a[n:] { 172 + if !subsumes(ctx, x.typ, y.a[i], mode) { 173 + return false 174 + } 175 + } 176 + return true 177 + } 178 + return isBottom(v) 179 + } 180 + 181 + func (x *params) subsumes(ctx *context, y *params, mode subsumeMode) bool { 182 + // structural equivalence 183 + // TODO: make agnostic to argument names. 184 + if len(y.arcs) != len(x.arcs) { 185 + return false 186 + } 187 + for i, a := range x.arcs { 188 + if !subsumes(ctx, a.v, y.arcs[i].v, 0) { 189 + return false 190 + } 191 + } 192 + return true 193 + } 194 + 195 + func (x *lambdaExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 196 + // structural equivalence 197 + if y, ok := v.(*lambdaExpr); ok { 198 + return x.params.subsumes(ctx, y.params, 0) && 199 + subsumes(ctx, x.value, y.value, 0) 200 + } 201 + return isBottom(v) 202 + } 203 + 204 + // subsumes for disjunction is logically precise. However, just like with 205 + // structural subsumption, it should not have to be called after evaluation. 206 + func (x *disjunction) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 207 + if d, ok := v.(*disjunction); ok { 208 + // TODO: the result of subsuming a value by a disjunction is logically 209 + // the subsumed value. However, since we have default semantics, we 210 + // should mark a resulting subsumed value as ambiguous if necessary. 211 + // Also, prove that the returned subsumed single value is always the 212 + // left-most matching value. 213 + return false 214 + // at least one value in x should subsume each value in d. 215 + for _, vd := range d.values { 216 + if !subsumes(ctx, x, vd.val, 0) { 217 + return false 218 + } 219 + } 220 + return true 221 + } 222 + // v is subsumed if any value in x subsumes v. 223 + for _, vx := range x.values { 224 + if subsumes(ctx, vx.val, v, 0) { 225 + return true 226 + } 227 + } 228 + return false 229 + } 230 + 231 + // Structural subsumption operations. Should never have to be called after 232 + // evaluation. 233 + 234 + // structural equivalence 235 + func (x *nodeRef) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 236 + if r, ok := v.(*nodeRef); ok { 237 + return x.node == r.node 238 + } 239 + return isBottom(v) 240 + } 241 + 242 + // structural equivalence 243 + func (x *selectorExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 244 + if r, ok := v.(*selectorExpr); ok { 245 + return x.feature == r.feature && subsumes(ctx, x.x, r.x, subChoose) 246 + } 247 + return isBottom(v) 248 + } 249 + 250 + func (x *interpolation) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 251 + switch v := v.(type) { 252 + case *stringLit: 253 + // Be conservative if not ground. 254 + return false 255 + 256 + case *interpolation: 257 + // structural equivalence 258 + if len(x.parts) != len(v.parts) { 259 + return false 260 + } 261 + for i, p := range x.parts { 262 + if !subsumes(ctx, p, v.parts[i], 0) { 263 + return false 264 + } 265 + } 266 + return true 267 + } 268 + return false 269 + } 270 + 271 + // structural equivalence 272 + func (x *indexExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 273 + // TODO: what does it mean to subsume if the index value is not known? 274 + if r, ok := v.(*indexExpr); ok { 275 + // TODO: could be narrowed down if we know the exact value of the index 276 + // and referenced value. 277 + return subsumes(ctx, x.x, r.x, mode) && subsumes(ctx, x.index, r.index, 0) 278 + } 279 + return isBottom(v) 280 + } 281 + 282 + // structural equivalence 283 + func (x *sliceExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 284 + // TODO: what does it mean to subsume if the index value is not known? 285 + if r, ok := v.(*sliceExpr); ok { 286 + // TODO: could be narrowed down if we know the exact value of the index 287 + // and referenced value. 288 + return subsumes(ctx, x.x, r.x, 0) && 289 + subsumes(ctx, x.lo, r.lo, 0) && 290 + subsumes(ctx, x.hi, r.hi, 0) 291 + } 292 + return isBottom(v) 293 + } 294 + 295 + // structural equivalence 296 + func (x *callExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 297 + if c, ok := v.(*callExpr); ok { 298 + if len(x.args) != len(c.args) { 299 + return false 300 + } 301 + for i, a := range x.args { 302 + if !subsumes(ctx, a, c.args[i], 0) { 303 + return false 304 + } 305 + } 306 + return subsumes(ctx, x.x, c.x, 0) 307 + } 308 + return isBottom(v) 309 + } 310 + 311 + // structural equivalence 312 + func (x *unaryExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 313 + if b, ok := v.(*unaryExpr); ok { 314 + return x.op == b.op && subsumes(ctx, x.x, b.x, 0) 315 + } 316 + return isBottom(v) 317 + } 318 + 319 + // structural equivalence 320 + func (x *binaryExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 321 + if b, ok := v.(*binaryExpr); ok { 322 + return x.op == b.op && 323 + subsumes(ctx, x.left, b.left, 0) && 324 + subsumes(ctx, x.right, b.right, 0) 325 + } 326 + return isBottom(v) 327 + } 328 + 329 + // structural equivalence 330 + func (x *listComprehension) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 331 + if b, ok := v.(*listComprehension); ok { 332 + return subsumes(ctx, x.clauses, b.clauses, 0) 333 + } 334 + return isBottom(v) 335 + } 336 + 337 + // structural equivalence 338 + func (x *structComprehension) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 339 + if b, ok := v.(*structComprehension); ok { 340 + return subsumes(ctx, x.clauses, b.clauses, 0) 341 + } 342 + return isBottom(v) 343 + } 344 + 345 + // structural equivalence 346 + func (x *yield) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 347 + if b, ok := v.(*yield); ok { 348 + return subsumes(ctx, x.key, b.key, 0) && 349 + subsumes(ctx, x.value, b.value, 0) 350 + } 351 + return isBottom(v) 352 + } 353 + 354 + // structural equivalence 355 + func (x *feed) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 356 + if b, ok := v.(*feed); ok { 357 + return subsumes(ctx, x.source, b.source, 0) && 358 + subsumes(ctx, x.fn, b.fn, 0) 359 + } 360 + return isBottom(v) 361 + } 362 + 363 + // structural equivalence 364 + func (x *guard) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { 365 + if b, ok := v.(*guard); ok { 366 + return subsumes(ctx, x.condition, b.condition, 0) && 367 + subsumes(ctx, x.value, b.value, 0) 368 + } 369 + return isBottom(v) 370 + }
+350
cue/subsume_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "regexp" 19 + "strconv" 20 + "strings" 21 + "testing" 22 + ) 23 + 24 + func TestSubsume(t *testing.T) { 25 + testCases := []struct { 26 + // the result of a ⊑ b, where a and b are defined in "in" 27 + subsumes bool 28 + in string 29 + mode subsumeMode 30 + }{ 31 + // Top subsumes everything 32 + 0: {subsumes: true, in: `a: _, b: _ `}, 33 + 1: {subsumes: true, in: `a: _, b: null `}, 34 + 2: {subsumes: true, in: `a: _, b: int `}, 35 + 3: {subsumes: true, in: `a: _, b: 1 `}, 36 + 4: {subsumes: true, in: `a: _, b: float `}, 37 + 5: {subsumes: true, in: `a: _, b: "s" `}, 38 + 6: {subsumes: true, in: `a: _, b: {} `}, 39 + 7: {subsumes: true, in: `a: _, b: []`}, 40 + 8: {subsumes: true, in: `a: _, b: _|_ `}, 41 + 42 + // Nothing besides top subsumed top 43 + 9: {subsumes: false, in: `a: null, b: _`}, 44 + 10: {subsumes: false, in: `a: int, b: _`}, 45 + 11: {subsumes: false, in: `a: 1, b: _`}, 46 + 12: {subsumes: false, in: `a: float, b: _`}, 47 + 13: {subsumes: false, in: `a: "s", b: _`}, 48 + 14: {subsumes: false, in: `a: {}, b: _`}, 49 + 15: {subsumes: false, in: `a: [], b: _`}, 50 + 16: {subsumes: false, in: `a: _|_ , b: _`}, 51 + 52 + // Bottom subsumes nothing except bottom itself. 53 + 17: {subsumes: false, in: `a: _|_, b: null `}, 54 + 18: {subsumes: false, in: `a: _|_, b: int `}, 55 + 19: {subsumes: false, in: `a: _|_, b: 1 `}, 56 + 20: {subsumes: false, in: `a: _|_, b: float `}, 57 + 21: {subsumes: false, in: `a: _|_, b: "s" `}, 58 + 22: {subsumes: false, in: `a: _|_, b: {} `}, 59 + 23: {subsumes: false, in: `a: _|_, b: [] `}, 60 + 24: {subsumes: true, in: ` a: _|_, b: _|_ `}, 61 + 62 + // All values subsume bottom 63 + 25: {subsumes: true, in: `a: null, b: _|_`}, 64 + 26: {subsumes: true, in: `a: int, b: _|_`}, 65 + 27: {subsumes: true, in: `a: 1, b: _|_`}, 66 + 28: {subsumes: true, in: `a: float, b: _|_`}, 67 + 29: {subsumes: true, in: `a: "s", b: _|_`}, 68 + 30: {subsumes: true, in: `a: {}, b: _|_`}, 69 + 31: {subsumes: true, in: `a: [], b: _|_`}, 70 + 32: {subsumes: true, in: `a: true, b: _|_`}, 71 + 33: {subsumes: true, in: `a: _|_, b: _|_`}, 72 + 73 + // null subsumes only null 74 + 34: {subsumes: true, in: ` a: null, b: null `}, 75 + 35: {subsumes: false, in: `a: null, b: 1 `}, 76 + 36: {subsumes: false, in: `a: 1, b: null `}, 77 + 78 + 37: {subsumes: true, in: ` a: true, b: true `}, 79 + 38: {subsumes: false, in: `a: true, b: false `}, 80 + 81 + 39: {subsumes: true, in: ` a: "a", b: "a" `}, 82 + 40: {subsumes: false, in: `a: "a", b: "b" `}, 83 + 41: {subsumes: true, in: ` a: string, b: "a" `}, 84 + 42: {subsumes: false, in: `a: "a", b: string `}, 85 + 86 + // Number typing (TODO) 87 + // 88 + // In principle, an "int" cannot assume an untyped "1", as "1" may 89 + // still by typed as a float. They are two different type aspects. When 90 + // considering, keep in mind that: 91 + // Key requirement: if A subsumes B, it must not be possible to 92 + // specialize B further such that A does not subsume B. HOWEVER, 93 + // The type conversion rules for conversion are INDEPENDENT of the 94 + // rules for subsumption! 95 + // Consider: 96 + // - only having number, but allowing user-defined types. 97 + // Subsumption would still work the same, but it may be somewhat 98 + // less weird. 99 + // - making 1 always an int and 1.0 always a float. 100 + // - the int type would subsume any derived type from int. 101 + // - arithmetic would allow implicit conversions, but maybe not for 102 + // types. 103 + // 104 + // TODO: irrational numbers: allow untyped, but require explicit 105 + // trunking when assigning to float. 106 + // 107 + // a: number; cue.IsInteger(a) && a > 0 108 + // t: (x) -> number; cue.IsInteger(a) && a > 0 109 + // type x number: cue.IsInteger(x) && x > 0 110 + // x: typeOf(number); cue.IsInteger(x) && x > 0 111 + 43: {subsumes: true, in: `a: 1, b: 1 `}, 112 + 44: {subsumes: true, in: `a: 1.0, b: 1.0 `}, 113 + 45: {subsumes: true, in: `a: 3.0, b: 3.0 `}, 114 + 46: {subsumes: false, in: `a: 1.0, b: 1 `}, 115 + 47: {subsumes: true, in: `a: 1, b: 1.0 `}, 116 + 48: {subsumes: true, in: `a: 3, b: 3.0`}, 117 + 49: {subsumes: false, in: `a: int, b: 1`}, 118 + 50: {subsumes: true, in: `a: int, b: int & 1`}, 119 + 51: {subsumes: true, in: `a: float, b: 1.0`}, 120 + 52: {subsumes: false, in: `a: float, b: 1`}, 121 + 53: {subsumes: false, in: `a: int, b: 1.0`}, 122 + 54: {subsumes: true, in: `a: int, b: int`}, 123 + 55: {subsumes: true, in: `a: number, b: int`}, 124 + 125 + // Lists 126 + 56: {subsumes: true, in: `a: [], b: [] `}, 127 + 57: {subsumes: true, in: `a: [1], b: [1] `}, 128 + 58: {subsumes: false, in: `a: [1], b: [2] `}, 129 + 59: {subsumes: false, in: `a: [1], b: [2, 3] `}, 130 + 60: {subsumes: true, in: `a: [{b: string}], b: [{b: "foo"}] `}, 131 + 61: {subsumes: true, in: `a: [...{b: string}], b: [{b: "foo"}] `}, 132 + 62: {subsumes: false, in: `a: [{b: "foo"}], b: [{b: string}] `}, 133 + 63: {subsumes: false, in: `a: [{b: string}], b: [{b: "foo"}, ...{b: "foo"}] `}, 134 + 135 + // Structs 136 + 64: {subsumes: true, in: `a: {}, b: {}`}, 137 + 65: {subsumes: true, in: `a: {}, b: {a: 1}`}, 138 + 66: {subsumes: true, in: `a: {a:1}, b: {a:1, b:1}`}, 139 + 67: {subsumes: true, in: `a: {s: { a:1} }, b: { s: { a:1, b:2 }}`}, 140 + // TODO: allow subsumption of unevaluated values? 141 + 68: {subsumes: true, in: `a: {}, b: c(), c: () -> {}`}, 142 + // TODO: allow subsumption of unevaluated values? 143 + // ref not yet evaluated and not structurally equivalent 144 + 69: {subsumes: true, in: `a: {}, b: {} & c, c: {}`}, 145 + 146 + 70: {subsumes: false, in: `a: {a:1}, b: {}`}, 147 + 71: {subsumes: false, in: `a: {a:1, b:1}, b: {a:1}`}, 148 + 72: {subsumes: false, in: `a: {s: { a:1} }, b: { s: {}}`}, 149 + 150 + // Lambda 151 + 73: {subsumes: true, in: `a: (x: _) -> {}, b: (x: _) -> {}`}, 152 + 74: {subsumes: true, in: `a: (x: int) -> {}, b: (x: int) -> {}`}, 153 + 75: {subsumes: true, in: `a: (x: {}) -> {}, b: (x: {}) -> {}`}, 154 + 76: {subsumes: false, in: `a: (x: _) -> {}, b: (x: _, y:_) -> {}`}, 155 + 77: {subsumes: true, in: `a: (x: _) -> {}, b: (x: 1) -> { a: 1 }`}, 156 + 78: {subsumes: false, in: `a: (x: 1) -> {}, b: (x: _) -> {}`}, 157 + 79: {subsumes: false, in: `a: (x: _) -> {}, b: () -> {}`}, 158 + 159 + 80: {subsumes: true, in: `a: (x: _) -> {}, b: (y: _) -> {}`}, 160 + 81: {subsumes: true, in: `a: (x) -> {}, b: (y) -> {}`}, 161 + 162 + 82: {subsumes: true, in: `a: (x: {a:1}) -> {f:2}, b: (x: {a:1, b:1}) -> {f:2, g:3}`}, 163 + 83: {subsumes: false, in: `a: (x: {a:1, b:1}) -> {f:2}, b: (x: {a:1}) -> {f:2, g:3}`}, 164 + 165 + // Disjunction TODO: for now these two are false: unifying may result in 166 + // an ambiguity that we are currently not handling, so safer to not 167 + // unify. 168 + 84: {subsumes: false, in: `a: 1 | 2, b: 2 | 1`}, 169 + 85: {subsumes: false, in: `a: 1 | 2, b: 1 | 2`}, 170 + 171 + 86: {subsumes: true, in: `a: number, b: 2 | 1`}, 172 + 87: {subsumes: true, in: `a: number, b: 2 | 1`}, 173 + 88: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`}, 174 + 175 + // Disjunction TODO: for now these two are false: unifying may result in 176 + // an ambiguity that we are currently not handling, so safer to not 177 + // unify. 178 + 89: {subsumes: false, in: `a: float | number, b: 1 | 2 | 3.1`}, 179 + 180 + 90: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`}, 181 + 91: {subsumes: true, in: `a: 1 | 2, b: 1`}, 182 + 92: {subsumes: true, in: `a: 1 | 2, b: 2`}, 183 + 93: {subsumes: false, in: `a: 1 | 2, b: 3`}, 184 + 185 + // Structural 186 + 94: {subsumes: false, in: `a: int + int, b: int`}, 187 + 95: {subsumes: true, in: `a: int + int, b: int + int`}, 188 + 96: {subsumes: true, in: `a: int + number, b: int + int`}, 189 + 97: {subsumes: true, in: `a: number + number, b: int + int`}, 190 + // TODO: allow subsumption of unevaluated values? 191 + // TODO: may be false if we allow arithmetic on incomplete values. 192 + 98: {subsumes: true, in: `a: int + int, b: int * int`}, 193 + 194 + 99: {subsumes: true, in: `a: !int, b: !int`}, 195 + 100: {subsumes: true, in: `a: !number, b: !int`}, 196 + // TODO: allow subsumption of unevaluated values? 197 + // true because both evaluate to bottom 198 + 101: {subsumes: true, in: `a: !int, b: !number`}, 199 + // TODO: allow subsumption of unevaluated values? 200 + // true because both evaluate to bottom 201 + 102: {subsumes: true, in: `a: int + int, b: !number`}, 202 + // TODO: allow subsumption of unevaluated values? 203 + // true because both evaluate to bool 204 + 103: {subsumes: true, in: `a: !bool, b: bool`}, 205 + 206 + 104: {subsumes: true, in: ` 207 + a: () -> 2 208 + b: () -> 2`}, 209 + 105: {subsumes: true, in: ` 210 + a: () -> number 211 + b: () -> 2`}, 212 + 106: {subsumes: true, in: ` 213 + a: (a: number) -> 2 214 + b: (a: number) -> 2`}, 215 + 107: {subsumes: true, in: ` 216 + a: (a: number) -> 2 217 + b: (a: 2) -> 2`}, 218 + 108: {subsumes: false, in: ` 219 + a: (a: 2) -> 2 220 + b: (a: 2, b: 2) -> 2`}, 221 + 109: {subsumes: false, in: ` 222 + a: (a: number) -> 2, 223 + b: (a: number, b: number) -> 2`}, 224 + 110: {subsumes: false, in: ` 225 + a: () -> 3 226 + b: () -> number`}, 227 + 111: {subsumes: false, in: ` 228 + a: (a: 3) -> 2 229 + b: (a: number) -> 2`}, 230 + 112: {subsumes: false, in: ` 231 + a: (a: 3, b: 3) -> 2 232 + b: (a: 3) -> 2`}, 233 + 234 + // Call 235 + 113: {subsumes: true, in: ` 236 + a: (() -> 2)(), 237 + b: (() -> 2)()`, 238 + }, 239 + // TODO: allow subsumption of unevaluated values? 240 + 114: {subsumes: true, in: ` 241 + a: (() -> 2)(), 242 + b: ((a) -> 2)(1)`, 243 + }, 244 + 115: {subsumes: true, in: ` 245 + a: ((a: number) -> [2])(2) 246 + b: ((a: number) -> [2])(2)`, 247 + }, 248 + // TODO: allow subsumption of unevaluated values? 249 + 116: {subsumes: true, in: ` 250 + a: ((a: number) -> [2])(number) 251 + b: ((a: number) -> [2])(2)`, 252 + }, 253 + // TODO: allow subsumption of unevaluated values? 254 + 117: {subsumes: true, in: ` 255 + a: ((a: number) -> [2])(2) 256 + b: ((a: number) -> [2])(number)`, 257 + }, 258 + 118: {subsumes: true, in: ` 259 + a: ((a) -> number)(2) 260 + b: ((a) -> 2)(2)`, 261 + }, 262 + 119: {subsumes: false, in: ` 263 + a: ((a) -> 2)(2) 264 + b: ((a) -> number)(2)`, 265 + }, 266 + // purely structural: 267 + // TODO: allow subsumption of unevaluated values? 268 + 120: {subsumes: true, in: ` 269 + a: ((a) -> int)(2) 270 + b: int`, 271 + }, 272 + 273 + // TODO: allow subsumption of unevaluated values? 274 + // TODO: okay, but why false? 275 + 121: {subsumes: false, in: `a: c + d, b: int, c: int, d: int`}, 276 + // TODO: allow subsumption of unevaluated values? 277 + 122: {subsumes: true, in: `a: {}, b: c & {}, c: {}`}, 278 + 279 + // references 280 + 123: {subsumes: true, in: `a: c, b: c, c: {}`}, 281 + // TODO: allow subsumption of unevaluated values? 282 + 124: {subsumes: true, in: `a: c, b: d, c: {}, d: {}`}, 283 + 125: {subsumes: false, in: `a: c, b: d, c: {a:1}, d: {}`}, 284 + // TODO: allow subsumption of unevaluated values? 285 + 126: {subsumes: true, in: `a: c, b: d, c: {a:1}, d: c & {b:1}`}, 286 + 127: {subsumes: false, in: `a: d, b: c, c: {a:1}, d: c & {b:1}`}, 287 + 128: {subsumes: false, in: `a: c.c, b: c, c: { d: number}`}, 288 + 289 + // type unification catches a reference error. 290 + 129: {subsumes: false, in: `a: c, b: d, c: 1, d: 2`}, 291 + 292 + 130: {subsumes: true, in: ` a: [1][1], b: [1][1]`}, 293 + 131: {subsumes: true, in: ` a: [1][number], b: [1][1]`}, 294 + 132: {subsumes: true, in: ` a: [number][1], b: [1][1]`}, 295 + 133: {subsumes: true, in: ` a: [number][number], b: [1][1]`}, 296 + 134: {subsumes: false, in: ` a: [1][0], b: [1][number]`}, 297 + 135: {subsumes: false, in: ` a: [1][0], b: [number][0]`}, 298 + 136: {subsumes: true, in: ` a: [number][number], b: [1][number]`}, 299 + 137: {subsumes: true, in: ` a: [number][number], b: [number][1]`}, 300 + // purely structural: 301 + 138: {subsumes: false, in: ` a: [number][number], b: number`}, 302 + 303 + // interpolations 304 + 139: {subsumes: true, in: ` a: "\(d)", b: "\(d)", d: _`}, 305 + // TODO: allow subsumption of unevaluated values? 306 + 140: {subsumes: true, in: ` a: "\(d)", b: "\(e)", d: _, e: _`}, 307 + 308 + 141: {subsumes: true, in: ` a: "\(string)", b: "\("foo")"`}, 309 + // TODO: allow subsumption of unevaluated values? 310 + 142: {subsumes: true, in: ` a: "\(string)", b: "\(d)", d: "foo"`}, 311 + 143: {subsumes: true, in: ` a: "\("foo")", b: "\("foo")"`}, 312 + 144: {subsumes: false, in: ` a: "\("foo")", b: "\(1) \(2)"`}, 313 + 314 + 145: {subsumes: false, in: ` a: "s \(d) e", b: "s a e", d: _`}, 315 + 146: {subsumes: false, in: ` a: "s \(d)m\(d) e", b: "s a e", d: _`}, 316 + 317 + 147: {subsumes: true, in: ` a: 7080, b: 7080 | int`, mode: subChoose}, 318 + } 319 + 320 + re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`) 321 + for i, tc := range testCases { 322 + m := re.FindStringSubmatch(strings.Join(strings.Split(tc.in, "\n"), "")) 323 + const cutset = "\n ," 324 + key := strings.Trim(m[1], cutset) + " ⊑ " + strings.Trim(m[2], cutset) 325 + 326 + t.Run(strconv.Itoa(i)+"/"+key, func(t *testing.T) { 327 + ctx, root := compileFile(t, tc.in) 328 + 329 + // Use low-level lookup to avoid evaluation. 330 + var a, b value 331 + for _, arc := range root.arcs { 332 + switch arc.feature { 333 + case ctx.strLabel("a"): 334 + a = arc.v 335 + case ctx.strLabel("b"): 336 + b = arc.v 337 + } 338 + } 339 + if got := subsumes(ctx, a, b, tc.mode); got != tc.subsumes { 340 + t.Errorf("got %v; want %v (%v vs %v)", got, tc.subsumes, a.kind(), b.kind()) 341 + } 342 + }) 343 + } 344 + } 345 + 346 + func TestTouchBottom(t *testing.T) { 347 + // Just call this function to mark coverage. It is otherwise never called. 348 + var x bottom 349 + x.subsumesImpl(nil, &bottom{}, 0) 350 + }
+1034
cue/types.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "encoding/json" 20 + "fmt" 21 + "io" 22 + "math" 23 + "math/big" 24 + "strings" 25 + 26 + "cuelang.org/go/cue/ast" 27 + "cuelang.org/go/cue/errors" 28 + "cuelang.org/go/cue/token" 29 + "github.com/cockroachdb/apd" 30 + ) 31 + 32 + // Kind determines the underlying type of a Value. 33 + type Kind int 34 + 35 + const ( 36 + // BottomKind is the error value. 37 + BottomKind Kind = 1 << iota 38 + 39 + // NullKind indicates a null value. 40 + NullKind 41 + 42 + // BoolKind indicates a boolean value. 43 + BoolKind 44 + 45 + // NumberKind represents any kind of number. 46 + NumberKind 47 + 48 + // StringKind indicates any kind of string. 49 + StringKind 50 + 51 + // BytesKind is a blob of data. 52 + BytesKind 53 + 54 + // StructKind is a kev-value map. 55 + StructKind 56 + 57 + // ListKind indicates a list of values. 58 + ListKind 59 + 60 + nextKind 61 + ) 62 + 63 + // An structValue represents a JSON object. 64 + // 65 + // TODO: remove 66 + type structValue struct { 67 + ctx *context 68 + path *valueData 69 + n *structLit 70 + } 71 + 72 + // Len reports the number of fields in this struct. 73 + func (o *structValue) Len() int { 74 + return len(o.n.arcs) 75 + } 76 + 77 + // At reports the key and value of the ith field, i < o.Len(). 78 + func (o *structValue) At(i int) (key string, v Value) { 79 + a := o.n.arcs[i] 80 + v = newChildValue(o, i) 81 + return o.ctx.labelStr(a.feature), v 82 + } 83 + 84 + // Lookup reports the field for the given key. The returned Value is invalid 85 + // if it does not exist. 86 + func (o *structValue) Lookup(key string) Value { 87 + f := o.ctx.strLabel(key) 88 + i := 0 89 + for ; i < len(o.n.arcs); i++ { 90 + if o.n.arcs[i].feature == f { 91 + break 92 + } 93 + } 94 + if i == len(o.n.arcs) { 95 + // TODO: better message. 96 + return newValueRoot(o.ctx, o.ctx.mkErr(o.n, codeNotExist, 97 + "value %q not found", key)) 98 + } 99 + // v, _ := o.n.lookup(o.ctx, f) 100 + // v = o.ctx.manifest(v) 101 + return newChildValue(o, i) 102 + } 103 + 104 + // MarshalJSON returns a valid JSON encoding or reports an error if any of the 105 + // fields is invalid. 106 + func (o *structValue) MarshalJSON() (b []byte, err error) { 107 + b = append(b, '{') 108 + n := o.Len() 109 + for i := 0; i < n; i++ { 110 + k, v := o.At(i) 111 + s, err := json.Marshal(k) 112 + if err != nil { 113 + return nil, err 114 + } 115 + b = append(b, s...) 116 + b = append(b, ':') 117 + bb, err := json.Marshal(v) 118 + if err != nil { 119 + return nil, err 120 + } 121 + b = append(b, bb...) 122 + if i < n-1 { 123 + b = append(b, ',') 124 + } 125 + } 126 + b = append(b, '}') 127 + return b, nil 128 + } 129 + 130 + // An Iterator iterates over values. 131 + // 132 + type Iterator struct { 133 + val Value 134 + ctx *context 135 + iter iterAtter 136 + len int 137 + p int 138 + cur Value 139 + f label 140 + } 141 + 142 + // Next advances the iterator to the next value and reports whether there was 143 + // any. It must be called before the first call to Value or Key. 144 + func (i *Iterator) Next() bool { 145 + if i.p >= i.len { 146 + i.cur = Value{} 147 + return false 148 + } 149 + eval, orig, f := i.iter.iterAt(i.ctx, i.p) 150 + i.cur = i.val.makeChild(i.ctx, uint32(i.p), f, eval, orig) 151 + i.f = f 152 + i.p++ 153 + return true 154 + } 155 + 156 + // Value returns the current value in the list. It will panic if Next advanced 157 + // past the last entry. 158 + func (i *Iterator) Value() Value { 159 + return i.cur 160 + } 161 + 162 + // Label reports the label of the value if i iterates over struct fields and 163 + // "" otherwise. 164 + func (i *Iterator) Label() string { 165 + if i.f == 0 { 166 + return "" 167 + } 168 + return i.ctx.labelStr(i.f) 169 + } 170 + 171 + // IsHidden reports if a field is hidden from the data model. This may only 172 + // be true if the field was obtained using AllFields. 173 + func (i *Iterator) IsHidden() bool { 174 + return i.f&hidden != 0 175 + } 176 + 177 + // marshalJSON iterates over the list and generates JSON output. HasNext 178 + // will return false after this operation. 179 + func marshalList(l *Iterator) (b []byte, err error) { 180 + b = append(b, '[') 181 + if l.Next() { 182 + for { 183 + x, err := json.Marshal(l.Value()) 184 + if err != nil { 185 + return nil, err 186 + } 187 + b = append(b, x...) 188 + if !l.Next() { 189 + break 190 + } 191 + b = append(b, ',') 192 + } 193 + } 194 + b = append(b, ']') 195 + return b, nil 196 + } 197 + 198 + func (v Value) getNum(k kind) (*numLit, error) { 199 + if err := v.checkKind(v.ctx(), k); err != nil { 200 + return nil, err 201 + } 202 + n, _ := v.path.v.(*numLit) 203 + return n, nil 204 + } 205 + 206 + // MantExp breaks x into its mantissa and exponent components and returns the 207 + // exponent. If a non-nil mant argument is provided its value is set to the 208 + // mantissa of x. The components satisfy x == mant × 10**exp. It returns an 209 + // error if v is not a number. 210 + // 211 + // The components are not normalized. For instance, 2.00 is represented mant == 212 + // 200 and exp == -2. Calling MantExp with a nil argument is an efficient way to 213 + // get the exponent of the receiver. 214 + func (v Value) MantExp(mant *big.Int) (exp int, err error) { 215 + n, err := v.getNum(numKind) 216 + if err != nil { 217 + return 0, err 218 + } 219 + if n.v.Form != 0 { 220 + return 0, ErrInfinite 221 + } 222 + if mant != nil { 223 + mant.Set(&n.v.Coeff) 224 + if n.v.Negative { 225 + mant.Neg(mant) 226 + } 227 + } 228 + return int(n.v.Exponent), nil 229 + } 230 + 231 + // AppendInt appends the string representation of x in the given base to buf and 232 + // returns the extended buffer, or an error if the underlying number was not 233 + // an integer. 234 + func (v Value) AppendInt(buf []byte, base int) ([]byte, error) { 235 + i, err := v.Int(nil) 236 + if err != nil { 237 + return nil, err 238 + } 239 + return i.Append(buf, base), nil 240 + } 241 + 242 + // AppendFloat appends to buf the string form of the floating-point number x. 243 + // It returns an error if v is not a number. 244 + func (v Value) AppendFloat(buf []byte, fmt byte, prec int) ([]byte, error) { 245 + n, err := v.getNum(numKind) 246 + if err != nil { 247 + return nil, err 248 + } 249 + ctx := apd.BaseContext 250 + nd := int(apd.NumDigits(&n.v.Coeff)) + int(n.v.Exponent) 251 + if n.v.Form == apd.Infinite { 252 + if n.v.Negative { 253 + buf = append(buf, '-') 254 + } 255 + return append(buf, string('∞')...), nil 256 + } 257 + if fmt == 'f' && nd > 0 { 258 + ctx.Precision = uint32(nd + prec) 259 + } else { 260 + ctx.Precision = uint32(prec) 261 + } 262 + var d apd.Decimal 263 + ctx.Round(&d, &n.v) 264 + return d.Append(buf, fmt), nil 265 + } 266 + 267 + var ( 268 + // ErrBelow indicates that a value was rounded down in a conversion. 269 + ErrBelow = errors.New("cue: value was rounded down") 270 + 271 + // ErrAbove indicates that a value was rounded up in a conversion. 272 + ErrAbove = errors.New("cue: value was rounded up") 273 + 274 + // ErrInfinite indicates that a value is infinite. 275 + ErrInfinite = errors.New("cue: infinite") 276 + ) 277 + 278 + // IsInt reports whether n is a integral number type. 279 + func (v Value) IsInt() bool { 280 + _, err := v.getNum(intKind) // TODO: make more efficient. 281 + return err == nil 282 + } 283 + 284 + // IsUint reports whether n is a positive integral number type. 285 + func (v Value) IsUint() bool { 286 + if !v.IsInt() { 287 + return false 288 + } 289 + n, _ := v.path.v.(*numLit) 290 + return n.v.Sign() >= 0 291 + } 292 + 293 + // Int converts the underlying integral number to an big.Int. It reports an 294 + // error if the underlying value is not an integer type. If a non-nil *Int 295 + // argument z is provided, Int stores the result in z instead of allocating a 296 + // new Int. 297 + func (v Value) Int(z *big.Int) (*big.Int, error) { 298 + n, err := v.getNum(intKind) 299 + if err != nil { 300 + return nil, err 301 + } 302 + if z == nil { 303 + z = &big.Int{} 304 + } 305 + if n.v.Exponent != 0 { 306 + panic("cue: exponent should always be nil for integer types") 307 + } 308 + z.Set(&n.v.Coeff) 309 + if n.v.Negative { 310 + z.Neg(z) 311 + } 312 + return z, nil 313 + } 314 + 315 + // Int64 converts the underlying integral number to int64. It reports an 316 + // error if the underlying value is not an integer type or cannot be represented 317 + // as an int64. The result is (math.MinInt64, ErrAbove) for x < math.MinInt64, 318 + // and (math.MaxInt64, ErrBelow) for x > math.MaxInt64. 319 + func (v Value) Int64() (int64, error) { 320 + n, err := v.getNum(intKind) 321 + if err != nil { 322 + return 0, err 323 + } 324 + if !n.v.Coeff.IsInt64() { 325 + if n.v.Negative { 326 + return math.MinInt64, ErrAbove 327 + } 328 + return math.MaxInt64, ErrBelow 329 + } 330 + i := n.v.Coeff.Int64() 331 + if n.v.Negative { 332 + i = -i 333 + } 334 + return i, nil 335 + } 336 + 337 + // Uint64 converts the underlying integral number to uint64. It reports an 338 + // error if the underlying value is not an integer type or cannot be represented 339 + // as a uint64. The result is (0, ErrAbove) for x < 0, and 340 + // (math.MaxUint64, ErrBelow) for x > math.MaxUint64. 341 + func (v Value) Uint64() (uint64, error) { 342 + n, err := v.getNum(intKind) 343 + if err != nil { 344 + return 0, err 345 + } 346 + if n.v.Negative { 347 + return 0, ErrAbove 348 + } 349 + if !n.v.Coeff.IsUint64() { 350 + return math.MaxUint64, ErrBelow 351 + } 352 + i := n.v.Coeff.Uint64() 353 + return i, nil 354 + } 355 + 356 + // trimZeros trims 0's for better JSON respresentations. 357 + func trimZeros(s string) string { 358 + n1 := len(s) 359 + s2 := strings.TrimRight(s, "0") 360 + n2 := len(s2) 361 + if p := strings.IndexByte(s2, '.'); p != -1 { 362 + if p == n2-1 { 363 + return s[:len(s2)+1] 364 + } 365 + return s2 366 + } 367 + if n1-n2 <= 4 { 368 + return s 369 + } 370 + return fmt.Sprint(s2, "e+", n1-n2) 371 + } 372 + 373 + var ( 374 + smallestPosFloat64 *apd.Decimal 375 + smallestNegFloat64 *apd.Decimal 376 + maxPosFloat64 *apd.Decimal 377 + maxNegFloat64 *apd.Decimal 378 + ) 379 + 380 + func init() { 381 + const ( 382 + // math.SmallestNonzeroFloat64: 1 / 2**(1023 - 1 + 52) 383 + smallest = "4.940656458412465441765687928682213723651e-324" 384 + // math.MaxFloat64: 2**1023 * (2**53 - 1) / 2**52 385 + max = "1.797693134862315708145274237317043567981e+308" 386 + ) 387 + ctx := apd.BaseContext 388 + ctx.Precision = 40 389 + 390 + var err error 391 + smallestPosFloat64, _, err = ctx.NewFromString(smallest) 392 + if err != nil { 393 + panic(err) 394 + } 395 + smallestNegFloat64, _, err = ctx.NewFromString("-" + smallest) 396 + if err != nil { 397 + panic(err) 398 + } 399 + maxPosFloat64, _, err = ctx.NewFromString(max) 400 + if err != nil { 401 + panic(err) 402 + } 403 + maxNegFloat64, _, err = ctx.NewFromString("-" + max) 404 + if err != nil { 405 + panic(err) 406 + } 407 + } 408 + 409 + // Float64 returns the float64 value nearest to x. It reports an error if v is 410 + // not a number. If x is too small to be represented by a float64 (|x| < 411 + // math.SmallestNonzeroFloat64), the result is (0, ErrBelow) or (-0, ErrAbove), 412 + // respectively, depending on the sign of x. If x is too large to be represented 413 + // by a float64 (|x| > math.MaxFloat64), the result is (+Inf, ErrAbove) or 414 + // (-Inf, ErrBelow), depending on the sign of x. 415 + func (v Value) Float64() (float64, error) { 416 + n, err := v.getNum(numKind) 417 + if err != nil { 418 + return 0, err 419 + } 420 + if n.v.Negative { 421 + if n.v.Cmp(smallestNegFloat64) == 1 { 422 + return -0, ErrAbove 423 + } 424 + if n.v.Cmp(maxNegFloat64) == -1 { 425 + return math.Inf(-1), ErrBelow 426 + } 427 + } else { 428 + if n.v.Cmp(smallestPosFloat64) == -1 { 429 + return 0, ErrBelow 430 + } 431 + if n.v.Cmp(maxPosFloat64) == 1 { 432 + return math.Inf(1), ErrAbove 433 + } 434 + } 435 + f, _ := n.v.Float64() 436 + return f, nil 437 + } 438 + 439 + type valueData struct { 440 + parent *valueData 441 + feature label 442 + index uint32 443 + v evaluated 444 + raw value 445 + } 446 + 447 + // Value holds any value, which may be a Boolean, Error, List, Null, Number, 448 + // Struct, or String. 449 + type Value struct { 450 + idx *index 451 + path *valueData 452 + } 453 + 454 + func newValueRoot(ctx *context, x value) Value { 455 + v := x.evalPartial(ctx) 456 + return Value{ctx.index, &valueData{nil, 0, 0, v, x}} 457 + } 458 + 459 + func newChildValue(obj *structValue, i int) Value { 460 + eval := obj.ctx.manifest(obj.n.at(obj.ctx, i)) 461 + a := obj.n.arcs[i] 462 + return Value{obj.ctx.index, &valueData{obj.path, a.feature, uint32(i), eval, a.v}} 463 + } 464 + 465 + func (v Value) ctx() *context { 466 + return v.idx.newContext() 467 + } 468 + 469 + func (v Value) makeChild(ctx *context, i uint32, f label, eval evaluated, raw value) Value { 470 + eval = ctx.manifest(eval) 471 + return Value{v.idx, &valueData{v.path, f, i, eval, raw}} 472 + } 473 + 474 + func (v Value) eval(ctx *context) value { 475 + if v.path == nil || v.path.v == nil { 476 + panic("undefined value") 477 + } 478 + return ctx.manifest(v.path.v) 479 + } 480 + 481 + // Default returs v if v.Exists or a value converted from x otherwise. 482 + func (v Value) Default(x interface{}) Value { 483 + return v 484 + } 485 + 486 + // Label reports he label used to obtain this value from the enclosing struct. 487 + // 488 + // TODO: get rid of this somehow. Maybe by passing it to walk 489 + func (v Value) Label() (string, bool) { 490 + if v.path.feature == 0 { 491 + return "", false 492 + } 493 + return v.idx.labelStr(v.path.feature), true 494 + } 495 + 496 + // Kind returns the kind of value. It returns BottomKind for atomic values that 497 + // are not concrete. For instance, it will return BottomKind for the range 498 + // 0..5. 499 + func (v Value) Kind() Kind { 500 + k := v.eval(v.ctx()).kind() 501 + if k.isGround() { 502 + switch { 503 + case k.isAnyOf(nullKind): 504 + return NullKind 505 + case k.isAnyOf(boolKind): 506 + return BoolKind 507 + case k.isAnyOf(numKind): 508 + return NumberKind 509 + case k.isAnyOf(bytesKind): 510 + return BytesKind 511 + case k.isAnyOf(stringKind): 512 + return StringKind 513 + case k.isAnyOf(structKind): 514 + return StructKind 515 + case k.isAnyOf(listKind): 516 + return ListKind 517 + } 518 + } 519 + return BottomKind 520 + } 521 + 522 + // IncompleteKind returns a mask of all kinds that this value may be. 523 + func (v Value) IncompleteKind() Kind { 524 + k := v.eval(v.ctx()).kind() 525 + vk := BottomKind // Everything is a bottom kind. 526 + for i := kind(1); i < nonGround; i <<= 1 { 527 + if k&i != 0 { 528 + switch i { 529 + case nullKind: 530 + vk |= NullKind 531 + case boolKind: 532 + vk |= BoolKind 533 + case intKind, floatKind: 534 + vk |= NumberKind 535 + case stringKind: 536 + vk |= StringKind 537 + case bytesKind: 538 + vk |= BytesKind 539 + case structKind: 540 + vk |= StructKind 541 + case listKind: 542 + vk |= ListKind 543 + } 544 + } 545 + } 546 + return vk 547 + } 548 + 549 + // MarshalJSON marshalls this value into valid JSON. 550 + func (v Value) MarshalJSON() (b []byte, err error) { 551 + if v.path == nil { 552 + return json.Marshal(nil) 553 + } 554 + ctx := v.idx.newContext() 555 + x := v.eval(ctx) 556 + // TODO: implement marshalles in value. 557 + switch k := x.kind(); k { 558 + case nullKind: 559 + return json.Marshal(nil) 560 + case boolKind: 561 + return json.Marshal(x.(*boolLit).b) 562 + case intKind, floatKind, numKind: 563 + return x.(*numLit).v.MarshalText() 564 + case stringKind: 565 + return json.Marshal(x.(*stringLit).str) 566 + case bytesKind: 567 + return json.Marshal(x.(*bytesLit).b) 568 + case listKind: 569 + l := x.(*list) 570 + i := Iterator{ctx: ctx, val: v, iter: l, len: len(l.a)} 571 + return marshalList(&i) 572 + case structKind: 573 + obj, _ := v.structVal(ctx) 574 + return obj.MarshalJSON() 575 + case bottomKind: 576 + return nil, x.(*bottom) 577 + default: 578 + if k.hasReferences() { 579 + return nil, v.idx.mkErr(x, "value %q contains unresolved references", debugStr(ctx, x)) 580 + } 581 + if !k.isGround() { 582 + return nil, v.idx.mkErr(x, "cannot convert incomplete value %q to JSON", debugStr(ctx, x)) 583 + } 584 + return nil, v.idx.mkErr(x, "cannot convert value %q of type %T to JSON", debugStr(ctx, x), x) 585 + } 586 + } 587 + 588 + // Syntax converts the possibly partially evaluated value into syntax. This 589 + // can use used to print the value with package format. 590 + func (v Value) Syntax() ast.Expr { 591 + if v.path == nil || v.path.v == nil { 592 + return nil 593 + } 594 + ctx := v.ctx() 595 + return export(ctx, v.eval(ctx)) 596 + } 597 + 598 + // Decode initializes x with Value v. If x is a struct, it will validate the 599 + // constraints specified in the field tags. 600 + func (v Value) Decode(x interface{}) error { 601 + // TODO: optimize 602 + b, err := v.MarshalJSON() 603 + if err != nil { 604 + return err 605 + } 606 + return json.Unmarshal(b, x) 607 + } 608 + 609 + // // EncodeJSON generates JSON for the given value. 610 + // func (v Value) EncodeJSON(w io.Writer, v Value) error { 611 + // return nil 612 + // } 613 + 614 + // Split returns a list of values from which v originated such that 615 + // the unification of all these values equals v and for all returned values 616 + // Source returns a non-nil value. 617 + func (v Value) Split() []Value { 618 + if v.path == nil { 619 + return nil 620 + } 621 + ctx := v.ctx() 622 + a := []Value{} 623 + for _, x := range separate(v.path.raw) { 624 + path := *v.path 625 + path.v = x.evalPartial(ctx) 626 + path.raw = x 627 + a = append(a, Value{v.idx, &path}) 628 + } 629 + return a 630 + } 631 + 632 + func separate(v value) (a []value) { 633 + c := v.computed() 634 + if c == nil { 635 + return []value{v} 636 + } 637 + if c.x != nil { 638 + a = append(a, separate(c.x)...) 639 + } 640 + if c.y != nil { 641 + a = append(a, separate(c.y)...) 642 + } 643 + return a 644 + } 645 + 646 + // Source returns the original node for this value. The return value may not 647 + // be a syntax.Expr. For instance, a struct kind may be represented by a 648 + // struct literal, a field comprehension, or a file. It returns nil for 649 + // computed nodes. Use Split to get all source values that apply to a field. 650 + func (v Value) Source() ast.Node { 651 + if v.path == nil { 652 + return nil 653 + } 654 + return v.path.raw.syntax() 655 + } 656 + 657 + // Err returns the error represented by v or nil v is not an error. 658 + func (v Value) Err() error { 659 + if err := v.checkKind(v.ctx(), bottomKind); err != nil { 660 + return err 661 + } 662 + return nil 663 + } 664 + 665 + // Pos returns position information. 666 + func (v Value) Pos() token.Position { 667 + if v.path == nil || v.Source() == nil { 668 + return token.Position{} 669 + } 670 + pos := v.Source().Pos() 671 + return v.idx.fset.Position(pos) 672 + } 673 + 674 + // IsIncomplete indicates that the value cannot be fully evaluated due to 675 + // insufficient information. 676 + func (v Value) IsIncomplete() bool { 677 + x := v.eval(v.ctx()) 678 + if x.kind().hasReferences() || !x.kind().isGround() { 679 + return true 680 + } 681 + return isIncomplete(x) 682 + } 683 + 684 + // IsValid reports whether this value is defined and evaluates to something 685 + // other than an error. 686 + func (v Value) IsValid() bool { 687 + if v.path == nil || v.path.v == nil { 688 + return false 689 + } 690 + k := v.eval(v.ctx()).kind() 691 + return k != bottomKind && !v.IsIncomplete() 692 + } 693 + 694 + // Exists reports whether this value existed in the configuration. 695 + func (v Value) Exists() bool { 696 + if v.path == nil { 697 + return false 698 + } 699 + return exists(v.eval(v.ctx())) 700 + } 701 + 702 + func (v Value) checkKind(ctx *context, want kind) *bottom { 703 + if v.path == nil { 704 + return errNotExists 705 + } 706 + // TODO: use checkKind 707 + x := v.eval(ctx) 708 + if b, ok := x.(*bottom); ok { 709 + return b 710 + } 711 + got := x.kind() 712 + if got&want&concreteKind == bottomKind && want != bottomKind { 713 + return ctx.mkErr(x, "not of right kind (%v vs %v)", got, want) 714 + } 715 + if !got.isGround() { 716 + return ctx.mkErr(x, codeIncomplete, 717 + "non-concrete value %v when evaluating config file", got) 718 + } 719 + return nil 720 + } 721 + 722 + // List creates an iterator over the values of a list or reports an error if 723 + // v is not a list. 724 + func (v Value) List() (Iterator, error) { 725 + ctx := v.ctx() 726 + if err := v.checkKind(ctx, listKind); err != nil { 727 + return Iterator{ctx: ctx}, err 728 + } 729 + l := v.eval(ctx).(*list) 730 + return Iterator{ctx: ctx, val: v, iter: l, len: len(l.a)}, nil 731 + } 732 + 733 + // Null reports an error if v is not null. 734 + func (v Value) Null() error { 735 + if err := v.checkKind(v.ctx(), nullKind); err != nil { 736 + return err 737 + } 738 + return nil 739 + } 740 + 741 + // IsNull reports whether v is null. 742 + func (v Value) IsNull() bool { 743 + return v.Null() == nil 744 + } 745 + 746 + // Bool returns the bool value of v or false and an error if v is not a boolean. 747 + func (v Value) Bool() (bool, error) { 748 + ctx := v.ctx() 749 + if err := v.checkKind(ctx, boolKind); err != nil { 750 + return false, err 751 + } 752 + return v.eval(ctx).(*boolLit).b, nil 753 + } 754 + 755 + // String returns the string value if v is a string or an error otherwise. 756 + func (v Value) String() (string, error) { 757 + ctx := v.ctx() 758 + if err := v.checkKind(ctx, stringKind); err != nil { 759 + return "", err 760 + } 761 + return v.eval(ctx).(*stringLit).str, nil 762 + } 763 + 764 + // Bytes returns a byte slice if v represents a list of bytes or an error 765 + // otherwise. 766 + func (v Value) Bytes() ([]byte, error) { 767 + ctx := v.ctx() 768 + switch x := v.eval(ctx).(type) { 769 + case *bytesLit: 770 + return append([]byte(nil), x.b...), nil 771 + case *stringLit: 772 + return []byte(x.str), nil 773 + } 774 + return nil, v.checkKind(ctx, bytesKind|stringKind) 775 + } 776 + 777 + // Reader returns a new Reader if v is a string or bytes type and an error 778 + // otherwise. 779 + func (v Value) Reader() (io.Reader, error) { 780 + ctx := v.ctx() 781 + switch x := v.eval(ctx).(type) { 782 + case *bytesLit: 783 + return bytes.NewReader(x.b), nil 784 + case *stringLit: 785 + return strings.NewReader(x.str), nil 786 + } 787 + return nil, v.checkKind(ctx, stringKind|bytesKind) 788 + } 789 + 790 + // structVal returns an structVal or an error if v is not a struct. 791 + func (v Value) structVal(ctx *context) (structValue, error) { 792 + if err := v.checkKind(ctx, structKind); err != nil { 793 + return structValue{}, err 794 + } 795 + obj := v.eval(ctx).(*structLit) 796 + 797 + // check if any labels are hidden 798 + f := label(0) 799 + for _, a := range obj.arcs { 800 + f |= a.feature 801 + } 802 + 803 + if f&hidden != 0 { 804 + arcs := make([]arc, len(obj.arcs)) 805 + k := 0 806 + for _, a := range obj.arcs { 807 + if a.feature&hidden == 0 { 808 + arcs[k] = a 809 + k++ 810 + } 811 + } 812 + arcs = arcs[:k] 813 + obj = &structLit{ 814 + obj.baseValue, 815 + obj.emit, 816 + obj.template, 817 + arcs, 818 + } 819 + 820 + } 821 + return structValue{ctx, v.path, obj}, nil 822 + } 823 + 824 + func (v Value) structValWithHidden(ctx *context) (structValue, error) { 825 + if err := v.checkKind(ctx, structKind); err != nil { 826 + return structValue{}, err 827 + } 828 + obj := v.eval(ctx).(*structLit) 829 + 830 + return structValue{ctx, v.path, obj}, nil 831 + } 832 + 833 + // Fields creates an iterator over v's fields if v is a struct or an error 834 + // otherwise. 835 + func (v Value) Fields() (Iterator, error) { 836 + ctx := v.ctx() 837 + obj, err := v.structVal(ctx) 838 + if err != nil { 839 + return Iterator{ctx: ctx}, err 840 + } 841 + return Iterator{ctx: ctx, val: v, iter: obj.n, len: len(obj.n.arcs)}, nil 842 + } 843 + 844 + // AllFields creates an iterator over all of v's fields, including the hidden 845 + // ones, if v is a struct or an error otherwise. 846 + func (v Value) AllFields() (Iterator, error) { 847 + ctx := v.ctx() 848 + obj, err := v.structValWithHidden(ctx) 849 + if err != nil { 850 + return Iterator{ctx: ctx}, err 851 + } 852 + return Iterator{ctx: ctx, val: v, iter: obj.n, len: len(obj.n.arcs)}, nil 853 + } 854 + 855 + // Lookup reports the value starting from v, or an error if the path is not 856 + // found. The empty path returns v itself. 857 + // 858 + // Lookup cannot be used to look up hidden fields. 859 + func (v Value) Lookup(path ...string) Value { 860 + ctx := v.ctx() 861 + for _, k := range path { 862 + obj, err := v.structVal(ctx) 863 + if err != nil { 864 + return newValueRoot(ctx, err.(*bottom)) 865 + } 866 + v = obj.Lookup(k) 867 + } 868 + return v 869 + } 870 + 871 + // Template returns a function that represents the template definition for a 872 + // struct in a configuration file. It returns nil if v is not a struct kind or 873 + // if there is no template associated with the struct. 874 + // 875 + // The returned function returns the value that would be unified with field 876 + // given its name. 877 + func (v Value) Template() func(label string) Value { 878 + ctx := v.ctx() 879 + x, ok := v.path.v.(*structLit) 880 + if !ok || x.template == nil { 881 + return nil 882 + } 883 + fn, ok := ctx.manifest(x.template).(*lambdaExpr) 884 + if !ok { 885 + return nil 886 + } 887 + return func(label string) Value { 888 + arg := &stringLit{x.baseValue, label} 889 + y := fn.call(ctx, x, arg) 890 + return newValueRoot(ctx, y) 891 + } 892 + } 893 + 894 + // Subsumes reports whether w is an instance of v. 895 + // 896 + // Value v and w must be obtained from the same build. 897 + // TODO: remove this requirement. 898 + func (v Value) Subsumes(w Value) bool { 899 + ctx := v.ctx() 900 + return subsumes(ctx, v.eval(ctx), w.eval(ctx), subChoose) 901 + } 902 + 903 + // Unify reports the greatest lower bound of v and w. 904 + // 905 + // Value v and w must be obtained from the same build. 906 + // TODO: remove this requirement. 907 + func (v Value) Unify(w Value) Value { 908 + ctx := v.ctx() 909 + if v.path == nil { 910 + return w 911 + } 912 + if w.path == nil { 913 + return v 914 + } 915 + a := v.eval(ctx) 916 + b := w.eval(ctx) 917 + val := ctx.manifest(mkBin(ctx, token.NoPos, opUnify, a, b)) 918 + if err := validate(ctx, val); err != nil { 919 + val = err 920 + } 921 + return newValueRoot(ctx, val) 922 + } 923 + 924 + // Format prints a debug version of a value. 925 + func (v Value) Format(state fmt.State, verb rune) { 926 + ctx := v.ctx() 927 + if v.path == nil { 928 + fmt.Fprint(state, "<nil>") 929 + return 930 + } 931 + io.WriteString(state, debugStr(ctx, v.path.v)) 932 + } 933 + 934 + // References reports all references used to evaluate this value. It does not 935 + // report references for sub fields if v is a struct. 936 + func (v Value) References() [][]string { 937 + ctx := v.ctx() 938 + pf := pathFinder{up: v.path} 939 + raw := v.path.raw 940 + if raw == nil { 941 + return nil 942 + } 943 + rewrite(ctx, raw, pf.find) 944 + return pf.paths 945 + } 946 + 947 + type pathFinder struct { 948 + paths [][]string 949 + stack []string 950 + up *valueData 951 + } 952 + 953 + func (p *pathFinder) find(ctx *context, v value) (value, bool) { 954 + switch x := v.(type) { 955 + case *selectorExpr: 956 + i := len(p.stack) 957 + p.stack = append(p.stack, ctx.labelStr(x.feature)) 958 + rewrite(ctx, x.x, p.find) 959 + p.stack = p.stack[:i] 960 + return v, false 961 + case *nodeRef: 962 + i := len(p.stack) 963 + up := p.up 964 + for ; up != nil && up.v != x.node.(value); up = up.parent { 965 + } 966 + for ; up != nil && up.feature > 0; up = up.parent { 967 + p.stack = append(p.stack, ctx.labelStr(up.feature)) 968 + } 969 + path := make([]string, len(p.stack)) 970 + for i, v := range p.stack { 971 + path[len(path)-1-i] = v 972 + } 973 + p.paths = append(p.paths, path) 974 + p.stack = p.stack[:i] 975 + return v, false 976 + case *structLit: // handled in sub fields 977 + return v, false 978 + } 979 + return v, true 980 + } 981 + 982 + // Validate reports any errors, recursively. The returned error may be an 983 + // errors.List reporting multiple errors, where the total number of errors 984 + // reported may be less than the actual number. 985 + func (v Value) Validate() error { 986 + list := errors.List{} 987 + v.Walk(func(v Value) bool { 988 + if err := v.Err(); err != nil { 989 + list.Add(err) 990 + if len(list) > 50 { 991 + return false // mostly to avoid some hypothetical cycle issue 992 + } 993 + } 994 + return true 995 + }, nil) 996 + if len(list) > 0 { 997 + list.Sort() 998 + // list.RemoveMultiples() // TODO: use RemoveMultiples when it is fixed 999 + return list 1000 + } 1001 + return nil 1002 + } 1003 + 1004 + // Walk descends into all values of v, calling f. If f returns false, Walk 1005 + // will not descent further. 1006 + func (v Value) Walk(before func(Value) bool, after func(Value)) { 1007 + ctx := v.ctx() 1008 + switch v.Kind() { 1009 + case StructKind: 1010 + if before != nil && !before(v) { 1011 + return 1012 + } 1013 + obj, _ := v.structVal(ctx) 1014 + for i := 0; i < obj.Len(); i++ { 1015 + _, v := obj.At(i) 1016 + v.Walk(before, after) 1017 + } 1018 + case ListKind: 1019 + if before != nil && !before(v) { 1020 + return 1021 + } 1022 + list, _ := v.List() 1023 + for list.Next() { 1024 + list.Value().Walk(before, after) 1025 + } 1026 + default: 1027 + if before != nil { 1028 + before(v) 1029 + } 1030 + } 1031 + if after != nil { 1032 + after(v) 1033 + } 1034 + }
+1228
cue/types_test.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "bytes" 19 + "fmt" 20 + "io/ioutil" 21 + "math" 22 + "math/big" 23 + "reflect" 24 + "strings" 25 + "testing" 26 + 27 + "github.com/google/go-cmp/cmp" 28 + ) 29 + 30 + func getInstance(t *testing.T, body ...string) *Instance { 31 + t.Helper() 32 + 33 + insts := Build(makeInstances([]*bimport{{files: body}})) 34 + if insts[0].Err != nil { 35 + t.Fatalf("unexpected parse error: %v", insts[0].Err) 36 + } 37 + return insts[0] 38 + } 39 + 40 + func TestValueType(t *testing.T) { 41 + testCases := []struct { 42 + value string 43 + kind Kind 44 + incompleteKind Kind 45 + json string 46 + valid bool 47 + // pos token.Pos 48 + }{{ // Not a concrete value. 49 + value: `_`, 50 + kind: BottomKind, 51 + incompleteKind: nextKind - 1, 52 + }, { 53 + value: `_|_`, 54 + kind: BottomKind, 55 + incompleteKind: BottomKind, 56 + }, { 57 + value: `1&2`, 58 + kind: BottomKind, 59 + incompleteKind: BottomKind, 60 + }, { // TODO: should be error{ 61 + value: `b`, 62 + kind: BottomKind, 63 + incompleteKind: BottomKind, 64 + }, { 65 + value: `(b[a])`, 66 + kind: BottomKind, 67 + incompleteKind: BottomKind, 68 + }, { // TODO: should be error{ 69 + value: `(b) 70 + b: bool`, 71 + kind: BottomKind, 72 + incompleteKind: BoolKind, 73 + }, { 74 + value: `([][b])`, 75 + kind: BottomKind, 76 + incompleteKind: BottomKind, 77 + }, { 78 + value: `null`, 79 + kind: NullKind, 80 + incompleteKind: NullKind, 81 + }, { 82 + value: `true`, 83 + kind: BoolKind, 84 + incompleteKind: BoolKind, 85 + }, { 86 + value: `false`, 87 + kind: BoolKind, 88 + incompleteKind: BoolKind, 89 + }, { 90 + value: `bool`, 91 + kind: BottomKind, 92 + incompleteKind: BoolKind, 93 + }, { 94 + value: `2`, 95 + kind: NumberKind, 96 + incompleteKind: NumberKind, 97 + }, { 98 + value: `2.0`, 99 + kind: NumberKind, 100 + incompleteKind: NumberKind, 101 + }, { 102 + value: `2.0Mi`, 103 + kind: NumberKind, 104 + incompleteKind: NumberKind, 105 + }, { 106 + value: `14_000`, 107 + kind: NumberKind, 108 + incompleteKind: NumberKind, 109 + }, { 110 + value: `0..5`, 111 + kind: BottomKind, 112 + incompleteKind: NumberKind, 113 + }, { 114 + value: `float`, 115 + kind: BottomKind, 116 + incompleteKind: NumberKind, 117 + }, { 118 + value: `"str"`, 119 + kind: StringKind, 120 + incompleteKind: StringKind, 121 + }, { 122 + value: "'''\n'''", 123 + kind: BytesKind, 124 + incompleteKind: BytesKind, 125 + }, { 126 + value: "string", 127 + kind: BottomKind, 128 + incompleteKind: StringKind, 129 + }, { 130 + value: `{}`, 131 + kind: StructKind, 132 + incompleteKind: StructKind, 133 + }, { 134 + value: `[]`, 135 + kind: ListKind, 136 + incompleteKind: ListKind, 137 + }} 138 + for _, tc := range testCases { 139 + t.Run(tc.value, func(t *testing.T) { 140 + inst := getInstance(t, tc.value) 141 + v := inst.Value() 142 + if got := v.Kind(); got != tc.kind { 143 + t.Errorf("Kind: got %x; want %x", got, tc.kind) 144 + } 145 + want := tc.incompleteKind | BottomKind 146 + if got := v.IncompleteKind(); got != want { 147 + t.Errorf("IncompleteKind: got %x; want %x", got, want) 148 + } 149 + incomplete := tc.incompleteKind != tc.kind 150 + if got := v.IsIncomplete(); got != incomplete { 151 + t.Errorf("IsIncomplete: got %v; want %v", got, incomplete) 152 + } 153 + invalid := tc.kind == BottomKind 154 + if got := v.IsValid(); got != !invalid { 155 + t.Errorf("IsValid: got %v; want %v", got, !invalid) 156 + } 157 + // if got, want := v.Pos(), tc.pos+1; got != want { 158 + // t.Errorf("pos: got %v; want %v", got, want) 159 + // } 160 + }) 161 + } 162 + } 163 + 164 + func TestInt(t *testing.T) { 165 + testCases := []struct { 166 + value string 167 + int int64 168 + uint uint64 169 + base int 170 + err string 171 + errU string 172 + notInt bool 173 + }{{ 174 + value: "1", 175 + int: 1, 176 + uint: 1, 177 + }, { 178 + value: "-1", 179 + int: -1, 180 + uint: 0, 181 + errU: ErrAbove.Error(), 182 + }, { 183 + value: "-111222333444555666777888999000", 184 + int: math.MinInt64, 185 + uint: 0, 186 + err: ErrAbove.Error(), 187 + errU: ErrAbove.Error(), 188 + }, { 189 + value: "111222333444555666777888999000", 190 + int: math.MaxInt64, 191 + uint: math.MaxUint64, 192 + err: ErrBelow.Error(), 193 + errU: ErrBelow.Error(), 194 + }, { 195 + value: "1.0", 196 + err: "not of right kind (float vs int)", 197 + errU: "not of right kind (float vs int)", 198 + notInt: true, 199 + }, { 200 + value: "int", 201 + err: "non-concrete value (int)*", 202 + errU: "non-concrete value (int)*", 203 + notInt: true, 204 + }, { 205 + value: "_|_", 206 + err: "from source", 207 + errU: "from source", 208 + notInt: true, 209 + }} 210 + for _, tc := range testCases { 211 + t.Run(tc.value, func(t *testing.T) { 212 + n := getInstance(t, tc.value).Value() 213 + base := 10 214 + if tc.base > 0 { 215 + base = tc.base 216 + } 217 + b, err := n.AppendInt(nil, base) 218 + if checkFailed(t, err, tc.err, "append") { 219 + want := tc.value 220 + if got := string(b); got != want { 221 + t.Errorf("append: got %v; want %v", got, want) 222 + } 223 + } 224 + 225 + if got := n.IsInt(); got != !tc.notInt { 226 + t.Errorf("isInt: got %v; want %v", got, !tc.notInt) 227 + } 228 + 229 + isUint := !tc.notInt && tc.int >= 0 230 + if got := n.IsUint(); got != isUint { 231 + t.Errorf("isUint: got %v; want %v", got, isUint) 232 + } 233 + 234 + vi, err := n.Int64() 235 + checkErr(t, err, tc.err, "Int64") 236 + if vi != tc.int { 237 + t.Errorf("Int64: got %v; want %v", vi, tc.int) 238 + } 239 + 240 + vu, err := n.Uint64() 241 + checkErr(t, err, tc.errU, "Uint64") 242 + if vu != uint64(tc.uint) { 243 + t.Errorf("Uint64: got %v; want %v", vu, tc.uint) 244 + } 245 + }) 246 + } 247 + } 248 + 249 + func TestFloat(t *testing.T) { 250 + testCases := []struct { 251 + value string 252 + float string 253 + float64 float64 254 + mant string 255 + exp int 256 + fmt byte 257 + prec int 258 + err string 259 + }{{ 260 + value: "1", 261 + float: "1", 262 + mant: "1", 263 + exp: 0, 264 + float64: 1, 265 + fmt: 'g', 266 + }, { 267 + value: "-1", 268 + float: "-1", 269 + mant: "-1", 270 + exp: 0, 271 + float64: -1, 272 + fmt: 'g', 273 + }, { 274 + value: "1.0", 275 + float: "1.0", 276 + mant: "10", 277 + exp: -1, 278 + float64: 1.0, 279 + fmt: 'g', 280 + }, { 281 + value: "2.6", 282 + float: "2.6", 283 + mant: "26", 284 + exp: -1, 285 + float64: 2.6, 286 + fmt: 'g', 287 + }, { 288 + value: "20.600", 289 + float: "20.60", 290 + mant: "20600", 291 + exp: -3, 292 + float64: 20.60, 293 + prec: 2, 294 + fmt: 'f', 295 + }, { 296 + value: "1/0", 297 + float: "∞", 298 + float64: math.Inf(1), 299 + prec: 2, 300 + fmt: 'f', 301 + err: ErrAbove.Error(), 302 + }, { 303 + value: "-1/0", 304 + float: "-∞", 305 + float64: math.Inf(-1), 306 + prec: 2, 307 + fmt: 'f', 308 + err: ErrBelow.Error(), 309 + }, { 310 + value: "1.797693134862315708145274237317043567982e+308", 311 + float: "1.8e+308", 312 + mant: "1797693134862315708145274237317043567982", 313 + exp: 269, 314 + float64: math.Inf(1), 315 + prec: 2, 316 + fmt: 'g', 317 + err: ErrAbove.Error(), 318 + }, { 319 + value: "-1.797693134862315708145274237317043567982e+308", 320 + float: "-1.8e+308", 321 + mant: "-1797693134862315708145274237317043567982", 322 + exp: 269, 323 + float64: math.Inf(-1), 324 + prec: 2, 325 + fmt: 'g', 326 + err: ErrBelow.Error(), 327 + }, { 328 + value: "4.940656458412465441765687928682213723650e-324", 329 + float: "4.941e-324", 330 + mant: "4940656458412465441765687928682213723650", 331 + exp: -363, 332 + float64: 0, 333 + prec: 4, 334 + fmt: 'g', 335 + err: ErrBelow.Error(), 336 + }, { 337 + value: "-4.940656458412465441765687928682213723650e-324", 338 + float: "-4.940656458412465441765687928682213723650e-324", 339 + mant: "-4940656458412465441765687928682213723650", 340 + exp: -363, 341 + float64: 0, 342 + prec: -1, 343 + fmt: 'g', 344 + err: ErrAbove.Error(), 345 + }} 346 + for _, tc := range testCases { 347 + t.Run(tc.value, func(t *testing.T) { 348 + n := getInstance(t, tc.value).Value() 349 + if n.Kind() != NumberKind { 350 + t.Fatal("Not a number") 351 + } 352 + 353 + var mant big.Int 354 + exp, err := n.MantExp(&mant) 355 + mstr := "" 356 + if err == nil { 357 + mstr = mant.String() 358 + } 359 + if exp != tc.exp || mstr != tc.mant { 360 + t.Errorf("mantExp: got %s %d; want %s %d", mstr, exp, tc.mant, tc.exp) 361 + } 362 + 363 + b, err := n.AppendFloat(nil, tc.fmt, tc.prec) 364 + want := tc.float 365 + if got := string(b); got != want { 366 + t.Errorf("append: got %v; want %v", got, want) 367 + } 368 + 369 + f, err := n.Float64() 370 + checkErr(t, err, tc.err, "Float64") 371 + if f != tc.float64 { 372 + t.Errorf("Float64: got %v; want %v", f, tc.float64) 373 + } 374 + }) 375 + } 376 + } 377 + 378 + func TestString(t *testing.T) { 379 + testCases := []struct { 380 + value string 381 + str string 382 + err string 383 + }{{ 384 + value: `""`, 385 + str: ``, 386 + }, { 387 + value: `"Hello world!"`, 388 + str: `Hello world!`, 389 + }, { 390 + value: `"Hello \(world)!" 391 + world: "world"`, 392 + str: `Hello world!`, 393 + }, { 394 + value: `string`, 395 + err: "non-concrete value (string)*", 396 + }} 397 + for _, tc := range testCases { 398 + t.Run(tc.value, func(t *testing.T) { 399 + str, err := getInstance(t, tc.value).Value().String() 400 + checkFatal(t, err, tc.err, "init") 401 + if str != tc.str { 402 + t.Errorf("String: got %q; want %q", str, tc.str) 403 + } 404 + 405 + b, err := getInstance(t, tc.value).Value().Bytes() 406 + checkFatal(t, err, tc.err, "init") 407 + if got := string(b); got != tc.str { 408 + t.Errorf("Bytes: got %q; want %q", got, tc.str) 409 + } 410 + 411 + r, err := getInstance(t, tc.value).Value().Reader() 412 + checkFatal(t, err, tc.err, "init") 413 + b, _ = ioutil.ReadAll(r) 414 + if got := string(b); got != tc.str { 415 + t.Errorf("Reader: got %q; want %q", got, tc.str) 416 + } 417 + }) 418 + } 419 + } 420 + 421 + func TestError(t *testing.T) { 422 + testCases := []struct { 423 + value string 424 + err string 425 + }{{ 426 + value: `_|_`, 427 + err: "from source", 428 + }, { 429 + value: `"Hello world!"`, 430 + }, { 431 + value: `string`, 432 + err: "non-concrete value (string)*", 433 + }} 434 + for _, tc := range testCases { 435 + t.Run(tc.value, func(t *testing.T) { 436 + err := getInstance(t, tc.value).Value().Err() 437 + checkErr(t, err, tc.err, "init") 438 + }) 439 + } 440 + } 441 + 442 + func TestNull(t *testing.T) { 443 + testCases := []struct { 444 + value string 445 + err string 446 + }{{ 447 + value: `_|_`, 448 + err: "from source", 449 + }, { 450 + value: `"str"`, 451 + err: "not of right kind (string vs null)", 452 + }, { 453 + value: `null`, 454 + }, { 455 + value: `_`, 456 + err: "non-concrete value (_)*", 457 + }} 458 + for _, tc := range testCases { 459 + t.Run(tc.value, func(t *testing.T) { 460 + err := getInstance(t, tc.value).Value().Null() 461 + checkErr(t, err, tc.err, "init") 462 + }) 463 + } 464 + } 465 + 466 + func TestBool(t *testing.T) { 467 + testCases := []struct { 468 + value string 469 + bool bool 470 + err string 471 + }{{ 472 + value: `_|_`, 473 + err: "from source", 474 + }, { 475 + value: `"str"`, 476 + err: "not of right kind (string vs bool)", 477 + }, { 478 + value: `true`, 479 + bool: true, 480 + }, { 481 + value: `false`, 482 + }, { 483 + value: `bool`, 484 + err: "non-concrete value (bool)*", 485 + }} 486 + for _, tc := range testCases { 487 + t.Run(tc.value, func(t *testing.T) { 488 + got, err := getInstance(t, tc.value).Value().Bool() 489 + if checkErr(t, err, tc.err, "init") { 490 + if got != tc.bool { 491 + t.Errorf("got %v; want %v", got, tc.bool) 492 + } 493 + } 494 + }) 495 + } 496 + } 497 + 498 + func TestList(t *testing.T) { 499 + testCases := []struct { 500 + value string 501 + res string 502 + err string 503 + }{{ 504 + value: `_|_`, 505 + err: "from source", 506 + }, { 507 + value: `"str"`, 508 + err: "not of right kind (string vs list)", 509 + }, { 510 + value: `[]`, 511 + res: "[]", 512 + }, { 513 + value: `[1,2,3]`, 514 + res: "[1,2,3,]", 515 + }, { 516 + value: `(0..5)*[1,2,3, ...int]`, 517 + res: "[1,2,3,]", 518 + }, { 519 + value: `[x for x in y if x > 1] 520 + y: [1,2,3]`, 521 + res: "[2,3,]", 522 + }, { 523 + value: `[int]`, 524 + err: "cannot convert incomplete value", 525 + }} 526 + for _, tc := range testCases { 527 + t.Run(tc.value, func(t *testing.T) { 528 + l, err := getInstance(t, tc.value).Value().List() 529 + checkFatal(t, err, tc.err, "init") 530 + 531 + buf := []byte{'['} 532 + for l.Next() { 533 + b, err := l.Value().MarshalJSON() 534 + checkFatal(t, err, tc.err, "list.Value") 535 + buf = append(buf, b...) 536 + buf = append(buf, ',') 537 + } 538 + buf = append(buf, ']') 539 + if got := string(buf); got != tc.res { 540 + t.Errorf("got %v; want %v", got, tc.res) 541 + } 542 + }) 543 + } 544 + } 545 + 546 + func TestFields(t *testing.T) { 547 + testCases := []struct { 548 + value string 549 + res string 550 + err string 551 + }{{ 552 + value: `_|_`, 553 + err: "from source", 554 + }, { 555 + value: `"str"`, 556 + err: "not of right kind (string vs struct)", 557 + }, { 558 + value: `{}`, 559 + res: "{}", 560 + }, { 561 + value: `{a:1,b:2,c:3}`, 562 + res: "{a:1,b:2,c:3,}", 563 + }, { 564 + value: `{a:1,"_b":2,c:3,_d:4}`, 565 + res: "{a:1,_b:2,c:3,}", 566 + }, { 567 + value: `{_a:"a"}`, 568 + res: "{}", 569 + }, { 570 + value: `{[k]: v for k, v in y if v > 1} 571 + y: {a:1,b:2,c:3}`, 572 + res: "{b:2,c:3,}", 573 + }, { 574 + value: `{a:1,b:2,c:int}`, 575 + err: "cannot convert incomplete value", 576 + }} 577 + for _, tc := range testCases { 578 + t.Run(tc.value, func(t *testing.T) { 579 + obj := getInstance(t, tc.value).Value() 580 + 581 + iter, err := obj.Fields() 582 + checkFatal(t, err, tc.err, "init") 583 + 584 + buf := []byte{'{'} 585 + for iter.Next() { 586 + buf = append(buf, iter.Label()...) 587 + buf = append(buf, ':') 588 + b, err := iter.Value().MarshalJSON() 589 + checkFatal(t, err, tc.err, "Obj.At") 590 + buf = append(buf, b...) 591 + buf = append(buf, ',') 592 + } 593 + buf = append(buf, '}') 594 + if got := string(buf); got != tc.res { 595 + t.Errorf("got %v; want %v", got, tc.res) 596 + } 597 + 598 + iter, _ = obj.Fields() 599 + for iter.Next() { 600 + want, err := iter.Value().MarshalJSON() 601 + checkFatal(t, err, tc.err, "Obj.At2") 602 + 603 + got, err := obj.Lookup(iter.Label()).MarshalJSON() 604 + checkFatal(t, err, tc.err, "Obj.At2") 605 + 606 + if !bytes.Equal(got, want) { 607 + t.Errorf("Lookup: got %q; want %q", got, want) 608 + } 609 + } 610 + v := obj.Lookup("non-existing") 611 + checkErr(t, v.Err(), "not found", "non-existing") 612 + }) 613 + } 614 + } 615 + 616 + func TestAllFields(t *testing.T) { 617 + testCases := []struct { 618 + value string 619 + res string 620 + err string 621 + }{{ 622 + value: `{a:1,"_b":2,c:3,_d:4}`, 623 + res: "{a:1,_b:2,c:3,_d:4,}", 624 + }, { 625 + value: `{_a:"a"}`, 626 + res: `{_a:"a",}`, 627 + }} 628 + for _, tc := range testCases { 629 + t.Run(tc.value, func(t *testing.T) { 630 + obj := getInstance(t, tc.value).Value() 631 + 632 + iter, err := obj.AllFields() 633 + checkFatal(t, err, tc.err, "init") 634 + 635 + buf := []byte{'{'} 636 + for iter.Next() { 637 + buf = append(buf, iter.Label()...) 638 + buf = append(buf, ':') 639 + b, err := iter.Value().MarshalJSON() 640 + checkFatal(t, err, tc.err, "Obj.At") 641 + buf = append(buf, b...) 642 + buf = append(buf, ',') 643 + } 644 + buf = append(buf, '}') 645 + if got := string(buf); got != tc.res { 646 + t.Errorf("got %v; want %v", got, tc.res) 647 + } 648 + }) 649 + } 650 + } 651 + 652 + func TestTemplate(t *testing.T) { 653 + testCases := []struct { 654 + value string 655 + path []string 656 + want string 657 + }{{ 658 + value: ` 659 + a <Name>: Name 660 + `, 661 + path: []string{"a", ""}, 662 + want: `"label"`, 663 + }, { 664 + value: ` 665 + <Name>: { a: Name } 666 + `, 667 + path: []string{"", "a"}, 668 + want: `"label"`, 669 + }, { 670 + value: ` 671 + <Name>: { a: Name } 672 + `, 673 + path: []string{""}, 674 + want: `{"a":"label"}`, 675 + }, { 676 + value: ` 677 + a <Foo> <Bar>: { b: Foo+Bar } 678 + `, 679 + path: []string{"a", "", ""}, 680 + want: `{"b":"labellabel"}`, 681 + }, { 682 + value: ` 683 + a <Foo> b <Bar>: { c: Foo+Bar } 684 + a foo b <Bar>: { d: Bar } 685 + `, 686 + path: []string{"a", "foo", "b", ""}, 687 + want: `{"c":"foolabel","d":"label"}`, 688 + }} 689 + for _, tc := range testCases { 690 + t.Run("", func(t *testing.T) { 691 + v := getInstance(t, tc.value).Value() 692 + for _, p := range tc.path { 693 + if p == "" { 694 + v = v.Template()("label") 695 + } else { 696 + v = v.Lookup(p) 697 + } 698 + } 699 + b, err := v.MarshalJSON() 700 + if err != nil { 701 + t.Fatal(err) 702 + } 703 + if got := string(b); got != tc.want { 704 + t.Errorf("\n got: %q\nwant: %q", got, tc.want) 705 + } 706 + }) 707 + } 708 + } 709 + 710 + func TestSubsumes(t *testing.T) { 711 + a := []string{"a"} 712 + b := []string{"b"} 713 + testCases := []struct { 714 + value string 715 + pathA []string 716 + pathB []string 717 + want bool 718 + }{{ 719 + value: `4`, 720 + want: true, 721 + }, { 722 + value: `a: string, b: "foo"`, 723 + pathA: a, 724 + pathB: b, 725 + want: true, 726 + }, { 727 + value: `a: string, b: "foo"`, 728 + pathA: b, 729 + pathB: a, 730 + want: false, 731 + }, { 732 + value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`, 733 + pathA: a, 734 + pathB: b, 735 + want: true, 736 + }, { 737 + value: `a: [string, 4], b: ["foo", 4]`, 738 + pathA: a, 739 + pathB: b, 740 + want: true, 741 + }} 742 + for _, tc := range testCases { 743 + t.Run(tc.value, func(t *testing.T) { 744 + v := getInstance(t, tc.value) 745 + a := v.Lookup(tc.pathA...) 746 + b := v.Lookup(tc.pathB...) 747 + got := a.Subsumes(b) 748 + if got != tc.want { 749 + t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b) 750 + } 751 + }) 752 + } 753 + } 754 + 755 + func TestUnify(t *testing.T) { 756 + a := []string{"a"} 757 + b := []string{"b"} 758 + testCases := []struct { 759 + value string 760 + pathA []string 761 + pathB []string 762 + want string 763 + }{{ 764 + value: `4`, 765 + want: `4`, 766 + }, { 767 + value: `a: string, b: "foo"`, 768 + pathA: a, 769 + pathB: b, 770 + want: `"foo"`, 771 + }, { 772 + value: `a: string, b: "foo"`, 773 + pathA: b, 774 + pathB: a, 775 + want: `"foo"`, 776 + }, { 777 + value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`, 778 + pathA: a, 779 + pathB: b, 780 + want: `{"a":"foo","b":4}`, 781 + }, { 782 + value: `a: [string, 4], b: ["foo", 4]`, 783 + pathA: a, 784 + pathB: b, 785 + want: `["foo",4]`, 786 + }} 787 + for _, tc := range testCases { 788 + t.Run(tc.value, func(t *testing.T) { 789 + v := getInstance(t, tc.value).Value() 790 + x := v.Lookup(tc.pathA...) 791 + y := v.Lookup(tc.pathB...) 792 + b, err := x.Unify(y).MarshalJSON() 793 + if err != nil { 794 + t.Fatal(err) 795 + } 796 + if got := string(b); got != tc.want { 797 + t.Errorf("got %v; want %v", got, tc.want) 798 + } 799 + }) 800 + } 801 + } 802 + 803 + func TestDecode(t *testing.T) { 804 + type fields struct { 805 + A int `json:"A"` 806 + B int `json:"B"` 807 + C int `json:"C"` 808 + } 809 + intList := func(ints ...int) *[]int { 810 + ints = append([]int{}, ints...) 811 + return &ints 812 + } 813 + testCases := []struct { 814 + value string 815 + dst interface{} 816 + want interface{} 817 + err string 818 + }{{ 819 + value: `_|_`, 820 + err: "from source", 821 + }, { 822 + value: `"str"`, 823 + dst: "", 824 + want: "str", 825 + }, { 826 + value: `"str"`, 827 + dst: new(int), 828 + err: "cannot unmarshal string into Go value of type int", 829 + }, { 830 + value: `{}`, 831 + dst: &fields{}, 832 + want: &fields{}, 833 + }, { 834 + value: `{a:1,b:2,c:3}`, 835 + dst: &fields{}, 836 + want: &fields{A: 1, B: 2, C: 3}, 837 + }, { 838 + value: `{[k]: v for k, v in y if v > 1} 839 + y: {a:1,b:2,c:3}`, 840 + dst: &fields{}, 841 + want: &fields{B: 2, C: 3}, 842 + }, { 843 + value: `{a:1,b:2,c:int}`, 844 + dst: new(fields), 845 + err: "cannot convert incomplete value", 846 + }, { 847 + value: `[]`, 848 + dst: intList(), 849 + want: intList(), 850 + }, { 851 + value: `[1,2,3]`, 852 + dst: intList(), 853 + want: intList(1, 2, 3), 854 + }, { 855 + value: `[x for x in y if x > 1] 856 + y: [1,2,3]`, 857 + dst: intList(), 858 + want: intList(2, 3), 859 + }, { 860 + value: `[int]`, 861 + err: "cannot convert incomplete value", 862 + }} 863 + for _, tc := range testCases { 864 + t.Run(tc.value, func(t *testing.T) { 865 + err := getInstance(t, tc.value).Value().Decode(&tc.dst) 866 + checkFatal(t, err, tc.err, "init") 867 + 868 + if !cmp.Equal(tc.dst, tc.want) { 869 + t.Error(cmp.Diff(tc.dst, tc.want)) 870 + t.Errorf("\n%#v\n%#v", tc.dst, tc.want) 871 + } 872 + }) 873 + } 874 + } 875 + 876 + func TestValueLookup(t *testing.T) { 877 + config := ` 878 + a: { 879 + a: 0 880 + b: 1 881 + c: 2 882 + } 883 + b: { 884 + d: a.a 885 + e: int 886 + } 887 + ` 888 + 889 + strList := func(s ...string) []string { return s } 890 + 891 + testCases := []struct { 892 + config string 893 + path []string 894 + str string 895 + notExists bool 896 + }{{ 897 + config: "_|_", 898 + path: strList(""), 899 + str: "from source", 900 + }, { 901 + config: "_|_", 902 + path: strList("a"), 903 + str: "from source", 904 + }, { 905 + config: config, 906 + path: strList(), 907 + str: "<0>{a: <1>{a: 0, b: 1, c: 2}, b: <2>{d: <0>.a.a, e: int}", 908 + }, { 909 + config: config, 910 + path: strList("a", "a"), 911 + str: "0", 912 + }, { 913 + config: config, 914 + path: strList("a"), 915 + str: "<0>{a: 0, b: 1, c: 2}", 916 + }, { 917 + config: config, 918 + path: strList("b", "d"), 919 + str: "0", 920 + }, { 921 + config: config, 922 + path: strList("c", "non-existing"), 923 + str: "not found", 924 + notExists: true, 925 + }, { 926 + config: config, 927 + path: strList("b", "d", "lookup in non-struct"), 928 + str: "not of right kind (int vs struct)", 929 + }} 930 + for _, tc := range testCases { 931 + t.Run(tc.str, func(t *testing.T) { 932 + v := getInstance(t, tc.config).Value().Lookup(tc.path...) 933 + if got := !v.Exists(); got != tc.notExists { 934 + t.Errorf("exists: got %v; want %v", got, tc.notExists) 935 + } 936 + 937 + got := fmt.Sprint(v) 938 + if tc.str == "" { 939 + t.Fatalf("str empty, got %q", got) 940 + } 941 + if !strings.Contains(got, tc.str) { 942 + t.Errorf("\n got %v\nwant %v", got, tc.str) 943 + } 944 + }) 945 + } 946 + } 947 + 948 + func TestMashalJSON(t *testing.T) { 949 + testCases := []struct { 950 + value string 951 + json string 952 + err string 953 + }{{ 954 + value: `""`, 955 + json: `""`, 956 + }, { 957 + value: `null`, 958 + json: `null`, 959 + }, { 960 + value: `_|_`, 961 + err: "from source", 962 + }, { 963 + value: `(a.b) 964 + a: {}`, 965 + err: "undefined field", 966 + }, { 967 + value: `((a)->a)`, 968 + err: "cannot convert value", 969 + }, { 970 + value: `true`, 971 + json: `true`, 972 + }, { 973 + value: `false`, 974 + json: `false`, 975 + }, { 976 + value: `bool`, 977 + json: `bool`, 978 + err: "cannot convert incomplete value", 979 + }, { 980 + value: `"str"`, 981 + json: `"str"`, 982 + }, { 983 + value: `12_000`, 984 + json: `12000`, 985 + }, { 986 + value: `12.000`, 987 + json: `12.000`, 988 + }, { 989 + value: `12M`, 990 + json: `12000000`, 991 + }, { 992 + value: `3.0e100`, 993 + json: `3.0E+100`, 994 + }, { 995 + value: `[]`, 996 + json: `[]`, 997 + }, { 998 + value: `[1, 2, 3]`, 999 + json: `[1,2,3]`, 1000 + }, { 1001 + value: `[int]`, 1002 + err: `cannot convert incomplete value`, 1003 + }, { 1004 + value: `((0..3) * [1, 2])`, 1005 + json: `[1,2]`, 1006 + }, { 1007 + value: `{}`, 1008 + json: `{}`, 1009 + }, { 1010 + value: `{a: 2, b: 3, c: ["A", "B"]}`, 1011 + json: `{"a":2,"b":3,"c":["A","B"]}`, 1012 + }} 1013 + for i, tc := range testCases { 1014 + t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) { 1015 + inst := getInstance(t, tc.value) 1016 + b, err := inst.Value().MarshalJSON() 1017 + checkFatal(t, err, tc.err, "init") 1018 + 1019 + if got := string(b); got != tc.json { 1020 + t.Errorf("\n got %v;\nwant %v", got, tc.json) 1021 + } 1022 + }) 1023 + } 1024 + } 1025 + 1026 + func TestWalk(t *testing.T) { 1027 + testCases := []struct { 1028 + value string 1029 + out string 1030 + }{{ 1031 + value: `""`, 1032 + out: `""`, 1033 + }, { 1034 + value: `null`, 1035 + out: `null`, 1036 + }, { 1037 + value: `_|_`, 1038 + out: "_|_(from source)", 1039 + }, { 1040 + value: `(a.b) 1041 + a: {}`, 1042 + out: `_|_(<0>.a.b:undefined field "b")`, 1043 + }, { 1044 + value: `true`, 1045 + out: `true`, 1046 + }, { 1047 + value: `false`, 1048 + out: `false`, 1049 + }, { 1050 + value: `bool`, 1051 + out: "bool", 1052 + }, { 1053 + value: `"str"`, 1054 + out: `"str"`, 1055 + }, { 1056 + value: `12_000`, 1057 + out: `12000`, 1058 + }, { 1059 + value: `12.000`, 1060 + out: `12.000`, 1061 + }, { 1062 + value: `12M`, 1063 + out: `12000000`, 1064 + }, { 1065 + value: `3.0e100`, 1066 + out: `3.0e+100`, 1067 + }, { 1068 + value: `[]`, 1069 + out: `[]`, 1070 + }, { 1071 + value: `[1, 2, 3]`, 1072 + out: `[1,2,3]`, 1073 + }, { 1074 + value: `[int]`, 1075 + out: `[int]`, 1076 + }, { 1077 + value: `((0..3) * [1, 2])`, 1078 + out: `[1,2]`, 1079 + }, { 1080 + value: `{}`, 1081 + out: `{}`, 1082 + }, { 1083 + value: `{a: 2, b: 3, c: ["A", "B"]}`, 1084 + out: `{a:2,b:3,c:["A","B"]}`, 1085 + }} 1086 + for i, tc := range testCases { 1087 + t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) { 1088 + inst := getInstance(t, tc.value) 1089 + buf := []byte{} 1090 + stripComma := func() { 1091 + if n := len(buf) - 1; buf[n] == ',' { 1092 + buf = buf[:n] 1093 + } 1094 + } 1095 + inst.Value().Walk(func(v Value) bool { 1096 + if k, ok := v.Label(); ok { 1097 + buf = append(buf, k+":"...) 1098 + } 1099 + switch v.Kind() { 1100 + case StructKind: 1101 + buf = append(buf, '{') 1102 + case ListKind: 1103 + buf = append(buf, '[') 1104 + default: 1105 + buf = append(buf, fmt.Sprint(v, ",")...) 1106 + } 1107 + return true 1108 + }, func(v Value) { 1109 + switch v.Kind() { 1110 + case StructKind: 1111 + stripComma() 1112 + buf = append(buf, "},"...) 1113 + case ListKind: 1114 + stripComma() 1115 + buf = append(buf, "],"...) 1116 + } 1117 + }) 1118 + stripComma() 1119 + if got := string(buf); got != tc.out { 1120 + t.Errorf("\n got %v;\nwant %v", got, tc.out) 1121 + } 1122 + }) 1123 + } 1124 + } 1125 + 1126 + func TestTrimZeros(t *testing.T) { 1127 + testCases := []struct { 1128 + in string 1129 + out string 1130 + }{ 1131 + {"", ""}, 1132 + {"2", "2"}, 1133 + {"2.0", "2.0"}, 1134 + {"2.000000000000", "2.0"}, 1135 + {"2000000000000", "2e+12"}, 1136 + {"2000000", "2e+6"}, 1137 + } 1138 + for _, tc := range testCases { 1139 + t.Run(tc.in, func(t *testing.T) { 1140 + if got := trimZeros(tc.in); got != tc.out { 1141 + t.Errorf("got %q; want %q", got, tc.out) 1142 + } 1143 + }) 1144 + } 1145 + } 1146 + 1147 + func TestReferences(t *testing.T) { 1148 + config1 := ` 1149 + a: { 1150 + b: 3 1151 + } 1152 + c: { 1153 + d: a.b 1154 + e: c.d 1155 + f: a 1156 + } 1157 + ` 1158 + config2 := ` 1159 + a: { c: 3 } 1160 + b: { c: int, d: 4 } 1161 + r: (a & b).c 1162 + ` 1163 + testCases := []struct { 1164 + config string 1165 + in string 1166 + out string 1167 + }{ 1168 + {config1, "c.d", "a.b"}, 1169 + {config1, "c.e", "c.d"}, 1170 + {config1, "c.f", "a"}, 1171 + 1172 + {config2, "r", "a.c b.c"}, 1173 + } 1174 + for _, tc := range testCases { 1175 + t.Run(tc.in, func(t *testing.T) { 1176 + ctx, st := compileFile(t, tc.config) 1177 + v := newValueRoot(ctx, st) 1178 + for _, k := range strings.Split(tc.in, ".") { 1179 + obj, err := v.structVal(ctx) 1180 + if err != nil { 1181 + t.Fatal(err) 1182 + } 1183 + v = obj.Lookup(k) 1184 + } 1185 + got := []string{} 1186 + for _, r := range v.References() { 1187 + got = append(got, strings.Join(r, ".")) 1188 + } 1189 + want := strings.Split(tc.out, " ") 1190 + if !reflect.DeepEqual(got, want) { 1191 + t.Errorf("got %v; want %v", got, want) 1192 + } 1193 + }) 1194 + } 1195 + } 1196 + 1197 + func checkErr(t *testing.T, err error, str, name string) bool { 1198 + t.Helper() 1199 + if err == nil { 1200 + if str != "" { 1201 + t.Errorf(`err:%s: got ""; want %q`, name, str) 1202 + } 1203 + return true 1204 + } 1205 + return checkFailed(t, err, str, name) 1206 + } 1207 + 1208 + func checkFatal(t *testing.T, err error, str, name string) { 1209 + t.Helper() 1210 + if !checkFailed(t, err, str, name) { 1211 + t.SkipNow() 1212 + } 1213 + } 1214 + 1215 + func checkFailed(t *testing.T, err error, str, name string) bool { 1216 + t.Helper() 1217 + if err != nil { 1218 + got := err.Error() 1219 + if str == "" { 1220 + t.Fatalf(`err:%s: got %q; want ""`, name, got) 1221 + } 1222 + if !strings.Contains(got, str) { 1223 + t.Errorf(`err:%s: got %q; want %q`, name, got, str) 1224 + } 1225 + return false 1226 + } 1227 + return true 1228 + }
+38
cue/validate.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + // validate returns whether there is any error, recursively. 18 + func validate(ctx *context, v value) *bottom { 19 + eval := v.evalPartial(ctx) 20 + if err, ok := eval.(*bottom); ok && err.code != codeIncomplete { 21 + return eval.(*bottom) 22 + } 23 + switch x := eval.(type) { 24 + case *structLit: 25 + for i := range x.arcs { 26 + if err := validate(ctx, x.at(ctx, i)); err != nil { 27 + return err 28 + } 29 + } 30 + case *list: 31 + for _, v := range x.a { 32 + if err := validate(ctx, v); err != nil { 33 + return err 34 + } 35 + } 36 + } 37 + return nil 38 + }
+1145
cue/value.go
··· 1 + // Copyright 2018 The 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 cue 16 + 17 + import ( 18 + "fmt" 19 + "math/big" 20 + "sort" 21 + "strconv" 22 + "strings" 23 + "time" 24 + 25 + "cuelang.org/go/cue/ast" 26 + "cuelang.org/go/cue/token" 27 + "github.com/cockroachdb/apd" 28 + ) 29 + 30 + type value interface { 31 + source 32 + 33 + rewrite(*context, rewriteFunc) value 34 + 35 + // evalPartial evaluates a value without choosing default values. 36 + evalPartial(*context) evaluated 37 + 38 + kind() kind 39 + 40 + // subsumesImpl is only defined for non-reference types. 41 + // It should only be called by the subsumes function. 42 + subsumesImpl(*context, value, subsumeMode) bool 43 + } 44 + 45 + type evaluated interface { 46 + value 47 + binOp(*context, source, op, evaluated) evaluated 48 + strValue() string 49 + } 50 + 51 + type scope interface { 52 + value 53 + lookup(*context, label) (e evaluated, raw value) 54 + } 55 + 56 + type atter interface { 57 + // at returns the evaluated and its original value at the given position. 58 + // If the original could not be found, it returns an error and nil. 59 + at(*context, int) evaluated 60 + } 61 + 62 + type iterAtter interface { 63 + // at returns the evaluated and its original value at the given position. 64 + // If the original could not be found, it returns an error and nil. 65 + iterAt(*context, int) (evaluated, value, label) 66 + } 67 + 68 + // caller must be implemented by any concrete lambdaKind 69 + type caller interface { 70 + call(ctx *context, src source, args ...evaluated) value 71 + returnKind() kind 72 + } 73 + 74 + func checkKind(ctx *context, x value, want kind) *bottom { 75 + if b, ok := x.(*bottom); ok { 76 + return b 77 + } 78 + got := x.kind() 79 + if got&want&concreteKind == bottomKind && want != bottomKind { 80 + return ctx.mkErr(x, "not of right kind (%v vs %v)", got, want) 81 + } 82 + if !got.isGround() { 83 + return ctx.mkErr(x, codeIncomplete, 84 + "non-concrete value %v", got) 85 + } 86 + return nil 87 + } 88 + 89 + func newDecl(n ast.Decl) baseValue { 90 + if n == nil { 91 + panic("empty node") 92 + } 93 + return baseValue{n} 94 + } 95 + 96 + func newExpr(n ast.Expr) baseValue { 97 + if n == nil { 98 + panic("empty node") 99 + } 100 + return baseValue{n} 101 + } 102 + 103 + func newNode(n ast.Node) baseValue { 104 + if n == nil { 105 + panic("empty node") 106 + } 107 + return baseValue{n} 108 + } 109 + 110 + type source interface { 111 + // syntax returns the parsed file of the underlying node or a computed 112 + // node indicating that it is a computed binary expression. 113 + syntax() ast.Node 114 + computed() *computedSource 115 + Pos() token.Pos 116 + base() baseValue 117 + } 118 + 119 + type computedSource struct { 120 + pos token.Pos 121 + op op 122 + x value 123 + y value 124 + } 125 + 126 + func (s *computedSource) Pos() token.Pos { 127 + return s.pos 128 + } 129 + 130 + type posser interface { 131 + Pos() token.Pos 132 + } 133 + 134 + type baseValue struct { 135 + pos posser 136 + } 137 + 138 + func (b baseValue) Pos() token.Pos { 139 + if b.pos == nil { 140 + return token.NoPos 141 + } 142 + return b.pos.Pos() 143 + } 144 + 145 + func (b baseValue) computed() *computedSource { 146 + switch x := b.pos.(type) { 147 + case *computedSource: 148 + return x 149 + } 150 + return nil 151 + } 152 + 153 + func (b baseValue) syntax() ast.Node { 154 + switch x := b.pos.(type) { 155 + case ast.Node: 156 + return x 157 + } 158 + return nil 159 + } 160 + 161 + func (b baseValue) base() baseValue { 162 + return b 163 + } 164 + 165 + func (x baseValue) strValue() string { panic("unimplemented") } 166 + func (x baseValue) returnKind() kind { panic("unimplemented") } 167 + 168 + // top is the top of the value lattice. It subsumes all possible values. 169 + type top struct{ baseValue } 170 + 171 + func (x *top) kind() kind { return topKind } 172 + 173 + // basicType represents the root class of any specific type. 174 + type basicType struct { 175 + baseValue 176 + k kind 177 + } 178 + 179 + func (x *basicType) kind() kind { return x.k | nonGround } 180 + 181 + // Literals 182 + 183 + type nullLit struct{ baseValue } 184 + 185 + func (x *nullLit) kind() kind { return nullKind } 186 + 187 + type boolLit struct { 188 + baseValue 189 + b bool 190 + } 191 + 192 + func (x *boolLit) kind() kind { return boolKind } 193 + 194 + func boolTonode(src source, b bool) evaluated { 195 + return &boolLit{src.base(), b} 196 + } 197 + 198 + type bytesLit struct { 199 + baseValue 200 + b []byte 201 + // TODO: maintain extended grapheme index cache. 202 + } 203 + 204 + func (x *bytesLit) kind() kind { return bytesKind } 205 + func (x *bytesLit) strValue() string { return string(x.b) } 206 + 207 + func (x *bytesLit) iterAt(ctx *context, i int) (evaluated, value, label) { 208 + if i >= len(x.b) { 209 + return nil, nil, 0 210 + } 211 + v := x.at(ctx, i) 212 + return v, v, 0 213 + } 214 + 215 + func (x *bytesLit) at(ctx *context, i int) evaluated { 216 + if i < 0 || i >= len(x.b) { 217 + return ctx.mkErr(x, "index %d out of bounds", i) 218 + } 219 + // TODO: this is incorrect. 220 + n := newNum(x, intKind) 221 + n.v.SetInt64(int64(x.b[i])) 222 + return n 223 + } 224 + 225 + func (x *bytesLit) len() int { return len(x.b) } 226 + 227 + func (x *bytesLit) slice(ctx *context, lo, hi *numLit) evaluated { 228 + lox := 0 229 + hix := len(x.b) 230 + if lo != nil { 231 + lox = lo.intValue(ctx) 232 + } 233 + if hi != nil { 234 + hix = hi.intValue(ctx) 235 + } 236 + if lox < 0 { 237 + return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", lox) 238 + } 239 + if hix < 0 { 240 + return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", hix) 241 + } 242 + if hix < lox { 243 + return ctx.mkErr(x, "invalid slice index: %d > %d", lox, hix) 244 + } 245 + if len(x.b) < hix { 246 + return ctx.mkErr(hi, "slice bounds out of range") 247 + } 248 + return &bytesLit{x.baseValue, x.b[lox:hix]} 249 + } 250 + 251 + type stringLit struct { 252 + baseValue 253 + str string 254 + 255 + // TODO: maintain extended grapheme index cache. 256 + } 257 + 258 + func (x *stringLit) kind() kind { return stringKind } 259 + func (x *stringLit) strValue() string { return x.str } 260 + 261 + func (x *stringLit) iterAt(ctx *context, i int) (evaluated, value, label) { 262 + runes := []rune(x.str) 263 + if i >= len(runes) { 264 + return nil, nil, 0 265 + } 266 + v := x.at(ctx, i) 267 + return v, v, 0 268 + } 269 + 270 + func (x *stringLit) at(ctx *context, i int) evaluated { 271 + runes := []rune(x.str) 272 + if i < 0 || i >= len(runes) { 273 + return ctx.mkErr(x, "index %d out of bounds", i) 274 + } 275 + // TODO: this is incorrect. 276 + return &stringLit{x.baseValue, string(runes[i : i+1])} 277 + } 278 + func (x *stringLit) len() int { return len([]rune(x.str)) } 279 + 280 + func (x *stringLit) slice(ctx *context, lo, hi *numLit) evaluated { 281 + runes := []rune(x.str) 282 + lox := 0 283 + hix := len(runes) 284 + if lo != nil { 285 + lox = lo.intValue(ctx) 286 + } 287 + if hi != nil { 288 + hix = hi.intValue(ctx) 289 + } 290 + if lox < 0 { 291 + return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", lox) 292 + } 293 + if hix < 0 { 294 + return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", hix) 295 + } 296 + if hix < lox { 297 + return ctx.mkErr(x, "invalid slice index: %d > %d", lox, hix) 298 + } 299 + if len(runes) < hix { 300 + return ctx.mkErr(hi, "slice bounds out of range") 301 + } 302 + return &stringLit{x.baseValue, string(runes[lox:hix])} 303 + } 304 + 305 + type numBase struct { 306 + baseValue 307 + numInfo 308 + } 309 + 310 + func newNumBase(n ast.Expr, info numInfo) numBase { 311 + return numBase{newExpr(n), info} 312 + } 313 + 314 + func newNumBin(k kind, a, b *numLit) *numLit { 315 + n := &numLit{ 316 + numBase: numBase{ 317 + baseValue: a.baseValue, 318 + numInfo: unifyNuminfo(a.numInfo, b.numInfo), 319 + }, 320 + } 321 + return n 322 + } 323 + 324 + func resultNumBase(a, b numBase) numBase { 325 + return numBase{ 326 + baseValue: a.baseValue, 327 + numInfo: unifyNuminfo(a.numInfo, b.numInfo), 328 + } 329 + } 330 + 331 + type numLit struct { 332 + numBase 333 + v apd.Decimal 334 + } 335 + 336 + func parseInt(k kind, s string) *numLit { 337 + n := &ast.BasicLit{ 338 + Kind: token.INT, 339 + Value: s, 340 + } 341 + num := newNum(newExpr(n), k) 342 + _, _, err := num.v.SetString(s) 343 + if err != nil { 344 + panic(err) 345 + } 346 + return num 347 + } 348 + 349 + func newNum(src source, k kind) *numLit { 350 + n := &numLit{numBase: numBase{baseValue: src.base()}} 351 + n.k = k 352 + return n 353 + } 354 + 355 + var ten = big.NewInt(10) 356 + 357 + func (x *numLit) kind() kind { return x.k } 358 + func (x *numLit) strValue() string { return x.v.String() } 359 + 360 + func (x *numLit) isInt(ctx *context) bool { 361 + return x.kind()&intKind != 0 362 + } 363 + 364 + func (x *numLit) intValue(ctx *context) int { 365 + v, err := x.v.Int64() 366 + if err != nil { 367 + ctx.mkErr(x, "intValue: %v", err) 368 + return 0 369 + } 370 + return int(v) 371 + } 372 + 373 + func intFromGo(s string) *numLit { 374 + num := &numLit{} 375 + num.k = intKind 376 + var ok bool 377 + switch { 378 + case strings.HasPrefix(s, "0x"), strings.HasPrefix(s, "0X"): 379 + _, ok = num.v.Coeff.SetString(s, 16) 380 + case strings.HasPrefix(s, "0"): 381 + _, ok = num.v.Coeff.SetString(s, 16) 382 + default: 383 + _, cond, err := num.v.SetString(s) 384 + ok = cond == 0 && err == nil 385 + } 386 + if !ok { 387 + panic(fmt.Sprintf("could not parse number %q", s)) 388 + } 389 + return num 390 + } 391 + 392 + func floatFromGo(s string) *numLit { 393 + num := &numLit{} 394 + num.k = floatKind 395 + num.v.SetString(s) 396 + return num 397 + } 398 + 399 + type durationLit struct { 400 + baseValue 401 + d time.Duration 402 + } 403 + 404 + func (x *durationLit) kind() kind { return durationKind } 405 + func (x *durationLit) strValue() string { return x.d.String() } 406 + 407 + type rangeLit struct { 408 + baseValue 409 + from, to value 410 + } 411 + 412 + func (x *rangeLit) kind() kind { 413 + return unifyType(x.from.kind(), x.to.kind()) | nonGround 414 + } 415 + 416 + func mkIntRange(a, b string) *rangeLit { 417 + from := parseInt(intKind, a) 418 + to := parseInt(intKind, b) 419 + return &rangeLit{ 420 + binSrc(token.NoPos, opRange, from, to), 421 + from, 422 + to, 423 + } 424 + } 425 + 426 + var predefinedRanges = map[string]*rangeLit{ 427 + "rune": mkIntRange("0", strconv.Itoa(0x10FFFF)), 428 + "int8": mkIntRange("-128", "127"), 429 + "int16": mkIntRange("-32768", "32767"), 430 + "int32": mkIntRange("-2147483648", "2147483647"), 431 + "int64": mkIntRange("-9223372036854775808", "9223372036854775807"), 432 + "int128": mkIntRange( 433 + "-170141183460469231731687303715884105728", 434 + "170141183460469231731687303715884105727"), 435 + 436 + // Do not include an alias for "byte", as it would be too easily confused 437 + // with the builtin "bytes". 438 + "uint8": mkIntRange("0", "255"), 439 + "uint16": mkIntRange("0", "65535"), 440 + "uint32": mkIntRange("0", "4294967295"), 441 + "uint64": mkIntRange("0", "18446744073709551615"), 442 + "uint128": mkIntRange("0", "340282366920938463463374607431768211455"), 443 + } 444 + 445 + type interpolation struct { 446 + baseValue 447 + k kind // string or bytes 448 + parts []value // odd: strings, even expressions 449 + } 450 + 451 + func (x *interpolation) kind() kind { return x.k | nonGround } 452 + 453 + type list struct { 454 + baseValue 455 + // TODO: Elements in a list are nodes to allow for cycle detection. 456 + a []value // TODO: could be arc? 457 + 458 + typ value 459 + len value 460 + } 461 + 462 + // initLit initializes a literal list. 463 + func (x *list) initLit() { 464 + n := newNum(x, intKind) 465 + n.v.SetInt64(int64(len(x.a))) 466 + x.len = n 467 + x.typ = &top{x.baseValue} 468 + } 469 + 470 + func (x *list) kind() kind { 471 + // Any open list has a default manifestation and can thus always be 472 + // interpreted as ground (ignoring non-ground elements). 473 + return listKind 474 + } 475 + 476 + // at returns the evaluated and original value of position i. List x must 477 + // already have been evaluated. It returns an error and nil if there was an 478 + // issue evaluating the list itself. 479 + func (x *list) at(ctx *context, i int) evaluated { 480 + e, _, _ := x.iterAt(ctx, i) 481 + if e == nil { 482 + return ctx.mkErr(x, "index %d out of bounds", i) 483 + } 484 + return e 485 + } 486 + 487 + // iterAt returns the evaluated and original value of position i. List x must 488 + // already have been evaluated. It returns an error and nil if there was an 489 + // issue evaluating the list itself. 490 + func (x *list) iterAt(ctx *context, i int) (evaluated, value, label) { 491 + if i < 0 { 492 + return ctx.mkErr(x, "index %d out of bounds", i), nil, 0 493 + } 494 + if i < len(x.a) { 495 + return x.a[i].evalPartial(ctx), x.a[i], 0 496 + } 497 + max := maxNum(x.len.(evaluated)) 498 + if max.kind().isGround() { 499 + if max.kind()&intKind == bottomKind { 500 + return ctx.mkErr(max, "length indicator of list not of type int"), nil, 0 501 + } 502 + n := max.(*numLit).intValue(ctx) 503 + if i >= n { 504 + return nil, nil, 0 505 + } 506 + } 507 + return x.typ.(evaluated), x.typ, 0 508 + } 509 + 510 + func (x *list) isOpen() bool { 511 + return !x.len.kind().isGround() 512 + } 513 + 514 + // lo and hi must be nil or a ground integer. 515 + func (x *list) slice(ctx *context, lo, hi *numLit) evaluated { 516 + a := x.a 517 + max := maxNum(x.len.(evaluated)) 518 + if hi != nil { 519 + n := hi.intValue(ctx) 520 + if n < 0 { 521 + return ctx.mkErr(x, "negative slice index") 522 + } 523 + if max.kind().isGround() && !leq(ctx, hi, hi, max) { 524 + return ctx.mkErr(hi, "slice bounds out of range") 525 + } 526 + max = hi 527 + if n < len(a) { 528 + a = a[:n] 529 + } 530 + } 531 + 532 + if lo != nil { 533 + n := lo.intValue(ctx) 534 + if n < 0 { 535 + return ctx.mkErr(x, "negative slice index") 536 + } 537 + if n > 0 && max.kind().isGround() { 538 + if !leq(ctx, lo, lo, max) { 539 + max := max.(*numLit).intValue(ctx) 540 + return ctx.mkErr(x, "invalid slice index: %v > %v", n, max) 541 + } 542 + max = binOp(ctx, lo, opSub, max, lo) 543 + } 544 + if n < len(a) { 545 + a = a[n:] 546 + } else { 547 + a = []value{} 548 + } 549 + } 550 + return &list{baseValue: x.baseValue, a: a, typ: x.typ, len: max} 551 + } 552 + 553 + // An structLit is a single structLit in the configuration tree. 554 + // 555 + // An structLit may have multiple arcs. There may be only one arc per label. Use 556 + // insertRaw to insert arcs to ensure this invariant holds. 557 + type structLit struct { 558 + baseValue 559 + emit value // currently only supported at top level. 560 + 561 + template value 562 + // TODO: consider hoisting the template arc to its own value. 563 + arcs []arc 564 + } 565 + 566 + func newStruct(src source) *structLit { 567 + return &structLit{baseValue: src.base()} 568 + } 569 + 570 + func (x *structLit) kind() kind { return structKind } 571 + 572 + type arcs []arc 573 + 574 + func (x *structLit) Len() int { return len(x.arcs) } 575 + func (x *structLit) Less(i, j int) bool { return x.arcs[i].feature < x.arcs[j].feature } 576 + func (x *structLit) Swap(i, j int) { x.arcs[i], x.arcs[j] = x.arcs[j], x.arcs[i] } 577 + 578 + // lookup returns the node for the given label f, if present, or nil otherwise. 579 + func (x *structLit) lookup(ctx *context, f label) (v evaluated, raw value) { 580 + // Lookup is done by selector or index references. Either this is done on 581 + // literal nodes or nodes obtained from references. In the later case, 582 + // noderef will have ensured that the ancestors were evaluated. 583 + for i, a := range x.arcs { 584 + if a.feature == f { 585 + return x.at(ctx, i), a.v 586 + } 587 + } 588 + return nil, nil 589 + } 590 + 591 + func (x *structLit) iterAt(ctx *context, i int) (evaluated, value, label) { 592 + if i >= len(x.arcs) { 593 + return nil, nil, 0 594 + } 595 + v := x.at(ctx, i) 596 + return v, x.arcs[i].v, x.arcs[i].feature // TODO: return template & v for original? 597 + } 598 + 599 + // Cycle Handling 600 + // 601 + // Structs (and thus lists) 602 + // 603 + // Unresolved cycle 604 + // a: b // _|_ 605 + // b: a // _|_ 606 + // 607 + // a: b | {c:1} // _|_ 608 + // b: a | {d:1} // _|_ 609 + // 610 + // Resolved cycles 611 + // a: b // {c:1} : b -> a&{c:1} ->a&{c:1}[a:cycle error] -> {c:1} 612 + // b: a & { c: 1 } // {c:1} 613 + // 614 + // a: b & { d: 1 } // {c:1, d:1} 615 + // b: a & { c: 1 } // {c:1, d:1} 616 + // 617 + // To resolve cycles in disjunctions, the unification operations need to be 618 + // rewritten before that disjunction values are unified. This is optional for 619 + // CUE implementations. 620 + // a: b | {d:1} // {c:1} | {d:1} 621 + // b: a & {c:1} // b & {c:1} | {d:1}&{c:1} -> {c:1} | {c:1,d:1} 622 + // 623 + // 624 + // Atoms 625 + // a: b + 100 // _|_ 626 + // b: a - 100 // _|_ 627 + // 628 + // a: b + 100 // 200 629 + // b: a - 100 // 100 + check 100 & (a-100) 630 + // b: 100 631 + // 632 + // a: b+100 | b+50 // 200 | 150 633 + // b: a-100 | a-50 // 100 + check 100 & (a-100 | a-50) 634 + // b: 100 635 + 636 + // TODO: Question: defining an interpration of a constraint structure with no 637 + // default resolution applied versus one there is. And given a substitution that 638 + // is valid for both interpretation, resulting in a an b, respectively. Is there 639 + // any expression in which a an b can be substituted for which the fully 640 + // evaluated interpretation can yield different results? If so, can we define 641 + // disjunction in a way that it does not? 642 + // ANSWER: NO. But is it sufficient to only require this for direct references? 643 + // Maybe, but at least it holds in that case. 644 + // f1: a1 | ... | an 645 + // f2: f1 646 + // f2: b1 | ... | bm | ... | bn 647 + // -- Suppose the element that unifies with a1 in f2 is bm. 648 + // Is a1 & bm the same answer if f2 referencing f1 referred to the 649 + // disjunction? 650 + // Assume that some ax, x > 1 unifies with a by, y < m. 651 + // This would be an ambiguous disjunction, and by definition invalid. 652 + // The first valid disjunction is one where bm is the first to match 653 + // any of the values in f1. The result will therefore be the same. 654 + // 655 + // Case 1 656 + // replicas: 1 | int 657 + // 658 + // foo: 1 | 2 // interpretations must be equal or else the value crosses 659 + // foo: replicas 660 + // 661 + // Case 2 662 + // a: { 663 + // replicas: 1 | int 664 + // b: {chk: 0, num: 4 } | { chk: int, e: 5, f: int } | { chk: 1, e: 6, f: 7 } 665 + // b chk: replicas // { chk: 1, num: 5 } 666 + // c: 1/(replicas-1) | 0 // 0 667 + // } 668 + // 669 + // b: { 670 + // b chk f: 7 671 + // } 672 + // 673 + // This may be an issue, but by defining all expressions to be lazily evaluated, 674 + // this is not a surprise. We could have a cue.Choose() builtin to pick 675 + // the disjunctions recursively. Note that within a single struct, though, the 676 + // selection for a field is always unique. It may only vary if a substructure 677 + // is referred to in a new substructure. 678 + 679 + func (x *structLit) at(ctx *context, i int) evaluated { 680 + // Lookup is done by selector or index references. Either this is done on 681 + // literal nodes or nodes obtained from references. In the later case, 682 + // noderef will have ensured that the ancestors were evaluated. 683 + if x.arcs[i].cache == nil { 684 + 685 + // cycle detection 686 + x.arcs[i].cache = cycleSentinel 687 + v := x.arcs[i].v.evalPartial(ctx) 688 + 689 + if err := cycleError(v); err != nil { 690 + 691 + x.arcs[i].cache = nil 692 + return err 693 + } 694 + 695 + v = x.applyTemplate(ctx, i, v) 696 + x.arcs[i].cache = v 697 + } 698 + return x.arcs[i].cache 699 + } 700 + 701 + func (x *structLit) applyTemplate(ctx *context, i int, v evaluated) evaluated { 702 + if x.template != nil { 703 + e := ctx.manifest(x.template) 704 + if isBottom(e) { 705 + return e 706 + } 707 + 708 + fn, ok := e.(*lambdaExpr) 709 + if !ok { 710 + return ctx.mkErr(e, "expected lambda expression") 711 + } 712 + 713 + name := ctx.labelStr(x.arcs[i].feature) 714 + arg := &stringLit{x.baseValue, name} 715 + w := fn.call(ctx, x, arg).evalPartial(ctx) 716 + v = binOp(ctx, x, opUnify, v, w) 717 + } 718 + return v 719 + } 720 + 721 + // A label is a canonicalized feature name. 722 + type label uint32 723 + 724 + const hidden label = 0x01 // only set iff identifier starting with $ 725 + 726 + // An arc holds the label-value pair. 727 + // 728 + // A fully evaluated arc has either a node or a value. An unevaluated arc, 729 + // however, may have both. In this case, the value must ultimately evaluate 730 + // to a node, which will then be merged with the existing one. 731 + type arc struct { 732 + feature label 733 + 734 + v value 735 + cache evaluated // also used as newValue during unification. 736 + } 737 + 738 + type arcInfo struct { 739 + hidden bool 740 + tags []string // name:string 741 + } 742 + 743 + var hiddenArc = &arcInfo{hidden: true} 744 + 745 + // insertValue is used during initialization but never during evaluation. 746 + func (x *structLit) insertValue(ctx *context, f label, value value) { 747 + for i, p := range x.arcs { 748 + if f != p.feature { 749 + continue 750 + } 751 + x.arcs[i].v = mkBin(ctx, token.NoPos, opUnify, p.v, value) 752 + return 753 + } 754 + x.arcs = append(x.arcs, arc{feature: f, v: value}) 755 + sort.Stable(x) 756 + } 757 + 758 + // A nodeRef is a reference to a node. 759 + type nodeRef struct { 760 + baseValue 761 + node scope 762 + } 763 + 764 + func (x *nodeRef) kind() kind { 765 + // TODO(REWORK): no context available 766 + // n := x.node.deref(nil) 767 + n := x.node 768 + return n.kind() | nonGround | referenceKind 769 + } 770 + 771 + type selectorExpr struct { 772 + baseValue 773 + x value 774 + feature label 775 + } 776 + 777 + // TODO: could this be narrowed down? 778 + func (x *selectorExpr) kind() kind { 779 + isRef := x.x.kind() & referenceKind 780 + return topKind | isRef 781 + } 782 + 783 + type indexExpr struct { 784 + baseValue 785 + x value 786 + index value 787 + } 788 + 789 + // TODO: narrow this down when we have list types. 790 + func (x *indexExpr) kind() kind { return topKind | referenceKind } 791 + 792 + type sliceExpr struct { 793 + baseValue 794 + x value 795 + lo value 796 + hi value 797 + } 798 + 799 + // TODO: narrow this down when we have list types. 800 + func (x *sliceExpr) kind() kind { return topKind | referenceKind } 801 + 802 + type callExpr struct { 803 + baseValue 804 + x value 805 + args []value 806 + } 807 + 808 + func (x *callExpr) kind() kind { 809 + // TODO: could this be narrowed down? 810 + if l, ok := x.x.(*lambdaExpr); ok { 811 + return l.returnKind() | nonGround 812 + } 813 + return topKind | referenceKind 814 + } 815 + 816 + type params struct { 817 + arcs []arc 818 + } 819 + 820 + func (x *params) add(f label, v value) { 821 + if v == nil { 822 + panic("nil node") 823 + } 824 + x.arcs = append(x.arcs, arc{f, v, nil}) 825 + } 826 + 827 + func (x *params) iterAt(ctx *context, i int) (evaluated, value) { 828 + if i >= len(x.arcs) { 829 + return nil, nil 830 + } 831 + return x.at(ctx, i), x.arcs[i].v 832 + } 833 + 834 + // lookup returns the node for the given label f, if present, or nil otherwise. 835 + func (x *params) at(ctx *context, i int) evaluated { 836 + // Lookup is done by selector or index references. Either this is done on 837 + // literal nodes or nodes obtained from references. In the later case, 838 + // noderef will have ensured that the ancestors were evaluated. 839 + if x.arcs[i].cache == nil { 840 + x.arcs[i].cache = x.arcs[i].v.evalPartial(ctx) 841 + } 842 + return x.arcs[i].cache 843 + } 844 + 845 + // lookup returns the node for the given label f, if present, or nil otherwise. 846 + func (x *params) lookup(ctx *context, f label) (v evaluated, raw value) { 847 + // Lookup is done by selector or index references. Either this is done on 848 + // literal nodes or nodes obtained from references. In the later case, 849 + // noderef will have ensured that the ancestors were evaluated. 850 + for i, a := range x.arcs { 851 + if a.feature == f { 852 + return x.at(ctx, i), a.v 853 + } 854 + } 855 + return nil, nil 856 + } 857 + 858 + type lambdaExpr struct { 859 + baseValue 860 + *params 861 + value value 862 + } 863 + 864 + // TODO: could this be narrowed down? 865 + func (x *lambdaExpr) kind() kind { return lambdaKind } 866 + func (x *lambdaExpr) returnKind() kind { return x.value.kind() } 867 + 868 + // call calls and evaluates a lambda expression. It is assumed that x may be 869 + // destroyed, either because it is copied as a result of a reference or because 870 + // it is invoked as a literal. 871 + func (x *lambdaExpr) call(ctx *context, p source, args ...evaluated) value { 872 + // fully evaluated. 873 + if len(x.params.arcs) != len(args) { 874 + return ctx.mkErr(p, x, "number of arguments does not match (%d vs %d)", 875 + len(x.params.arcs), len(args)) 876 + } 877 + 878 + // force parameter substitution. It is important that the result stands on 879 + // its own and does not depend on its input parameters. 880 + arcs := make(arcs, len(x.arcs)) 881 + for i, a := range x.arcs { 882 + v := unify(ctx, p, a.v.evalPartial(ctx), args[i]) 883 + if isBottom(v) { 884 + return v 885 + } 886 + arcs[i] = arc{a.feature, v, v} 887 + } 888 + lambda := &lambdaExpr{x.baseValue, &params{arcs}, nil} 889 + defer ctx.pushForwards(x, lambda).popForwards() 890 + return ctx.copy(x.value) 891 + } 892 + 893 + // Operations 894 + 895 + type unaryExpr struct { 896 + baseValue 897 + op op 898 + x value 899 + } 900 + 901 + func (x *unaryExpr) kind() kind { return x.x.kind() } 902 + 903 + type binaryExpr struct { 904 + baseValue 905 + op op 906 + left value 907 + right value 908 + } 909 + 910 + func mkBin(ctx *context, pos token.Pos, op op, left, right value) value { 911 + if left == nil || right == nil { 912 + panic("operands may not be nil") 913 + } 914 + if op == opUnify { 915 + if left == right { 916 + return left 917 + } 918 + if _, ok := left.(*top); ok { 919 + return right 920 + } 921 + if _, ok := right.(*top); ok { 922 + return left 923 + } 924 + // TODO(perf): consider adding a subsumption filter. 925 + // if subsumes(ctx, left, right) { 926 + // return right 927 + // } 928 + // if subsumes(ctx, right, left) { 929 + // return left 930 + // } 931 + } 932 + return &binaryExpr{binSrc(pos, op, left, right), op, left, right} 933 + } 934 + 935 + func (x *binaryExpr) kind() kind { 936 + // TODO: cache results 937 + kind, _ := matchBinOpKind(x.op, x.left.kind(), x.right.kind()) 938 + return kind | nonGround 939 + } 940 + 941 + // TODO: make disjunction a binOp, but translate disjunctions into 942 + // arrays, or at least linked lists. 943 + 944 + type disjunction struct { 945 + baseValue 946 + 947 + values []dValue 948 + 949 + // bind is the node that a successful disjunction will bind to. This 950 + // allows other arcs to point to this node before the disjunction is 951 + // completed. For early failure, this node can be set to the glb of all 952 + // disjunctions. Otherwise top will suffice. 953 + // bind node 954 + } 955 + 956 + type dValue struct { 957 + val value 958 + ambiguous bool 959 + } 960 + 961 + // makeDisjunction constructs a disjunction linked list. 962 + func makeDisjunction(ctx *context, n ast.Expr, a, b value) *disjunction { 963 + d, ok := a.(*disjunction) 964 + if !ok { 965 + d = &disjunction{newExpr(n), []dValue{{val: a}}} 966 + } 967 + if o, ok := b.(*disjunction); ok { 968 + d.values = append(d.values, o.values...) 969 + } else { 970 + d.values = append(d.values, dValue{val: b}) 971 + } 972 + return d 973 + } 974 + 975 + func (x *disjunction) kind() kind { 976 + k := kind(0) 977 + for _, v := range x.values { 978 + k |= v.val.kind() 979 + } 980 + if k != bottomKind { 981 + k |= nonGround 982 + } 983 + return k 984 + } 985 + 986 + func (x *disjunction) Pos() token.Pos { return x.values[0].val.Pos() } 987 + 988 + // add add a value to the disjunction. It is assumed not to be a disjunction. 989 + func (x *disjunction) add(ctx *context, v value, ambiguous bool) { 990 + if !isBottom(v) { 991 + x.values = append(x.values, dValue{v, ambiguous}) 992 + } 993 + } 994 + 995 + // simplify unwraps the disjunction if necessary. 996 + func (x *disjunction) simplify(ctx *context, src source) value { 997 + switch len(x.values) { 998 + case 0: 999 + return ctx.mkErr(src, "empty disjunction after evaluation") 1000 + case 1: 1001 + return x.values[0].val 1002 + } 1003 + return x 1004 + } 1005 + 1006 + type listComprehension struct { 1007 + baseValue 1008 + clauses yielder 1009 + } 1010 + 1011 + func (x *listComprehension) kind() kind { 1012 + return listKind | nonGround | referenceKind 1013 + } 1014 + 1015 + // TODO: structComprehensions are a left-over from the past and are used to 1016 + // interpret field comprehensions. The resulting construction prohibits 1017 + // references in comprehension clauses to be able to reference values in 1018 + // the struct in which the field is defined. See the updater config in the 1019 + // kubernetes demo. 1020 + type structComprehension struct { 1021 + baseValue 1022 + clauses yielder 1023 + isTemplate bool 1024 + } 1025 + 1026 + func (x *structComprehension) kind() kind { 1027 + return structKind | nonGround | referenceKind 1028 + } 1029 + 1030 + type yieldFunc func(k, v evaluated) *bottom 1031 + 1032 + type yielder interface { 1033 + value 1034 + yield(*context, yieldFunc) evaluated 1035 + } 1036 + 1037 + type yield struct { 1038 + baseValue 1039 + key value 1040 + value value 1041 + } 1042 + 1043 + func (x *yield) kind() kind { return topKind | referenceKind } 1044 + 1045 + func (x *yield) yield(ctx *context, fn yieldFunc) evaluated { 1046 + var k evaluated 1047 + if x.key != nil { 1048 + k = ctx.manifest(x.key) 1049 + if isBottom(k) { 1050 + return k 1051 + } 1052 + } else { 1053 + k = &top{} 1054 + } 1055 + v := x.value.evalPartial(ctx) 1056 + if isBottom(v) { 1057 + return v 1058 + } 1059 + if err := fn(k, v); err != nil { 1060 + return err 1061 + } 1062 + return nil 1063 + } 1064 + 1065 + type guard struct { // rename to guard 1066 + baseValue 1067 + condition value 1068 + value yielder 1069 + } 1070 + 1071 + func (x *guard) kind() kind { return topKind | referenceKind } 1072 + 1073 + func (x *guard) yield(ctx *context, fn yieldFunc) evaluated { 1074 + filter := ctx.manifest(x.condition) 1075 + if isBottom(filter) { 1076 + return filter 1077 + } 1078 + if err := checkKind(ctx, filter, boolKind); err != nil { 1079 + return err 1080 + } 1081 + if filter.(*boolLit).b { 1082 + if err := x.value.yield(ctx, fn); err != nil { 1083 + return err 1084 + } 1085 + } 1086 + return nil 1087 + } 1088 + 1089 + type feed struct { 1090 + baseValue 1091 + source value 1092 + fn *lambdaExpr 1093 + } 1094 + 1095 + func (x *feed) kind() kind { return topKind | referenceKind } 1096 + 1097 + func (x *feed) yield(ctx *context, yfn yieldFunc) (result evaluated) { 1098 + if ctx.trace { 1099 + defer uni(indent(ctx, "feed", x)) 1100 + } 1101 + source := ctx.manifest(x.source) 1102 + fn := x.fn // no need to evaluate eval 1103 + 1104 + switch src := source.(type) { 1105 + case *structLit: 1106 + for i, a := range src.arcs { 1107 + key := &stringLit{ 1108 + x.baseValue, 1109 + ctx.labelStr(a.feature), 1110 + } 1111 + val := src.at(ctx, i) 1112 + v := fn.call(ctx, x, key, val) 1113 + if isBottom(v) { 1114 + return v.evalPartial(ctx) 1115 + } 1116 + if err := v.(yielder).yield(ctx, yfn); err != nil { 1117 + return err 1118 + } 1119 + } 1120 + return nil 1121 + 1122 + case *list: 1123 + for i := range src.a { 1124 + idx := newNum(x, intKind) 1125 + idx.v.SetInt64(int64(i)) 1126 + v := fn.call(ctx, x, idx, src.at(ctx, i)) 1127 + if isBottom(v) { 1128 + return v.evalPartial(ctx) 1129 + } 1130 + if err := v.(yielder).yield(ctx, yfn); err != nil { 1131 + return err 1132 + } 1133 + } 1134 + return nil 1135 + 1136 + default: 1137 + if isBottom(source) { 1138 + return source 1139 + } 1140 + if k := source.kind(); k&(structKind|listKind) == bottomKind { 1141 + return ctx.mkErr(x, x.source, "feed source must be list or struct, found %s", k) 1142 + } 1143 + return ctx.mkErr(x, "feed source not fully evaluated to struct or list") 1144 + } 1145 + }
+11
internal/internal.go
··· 18 18 19 19 // DebugStr prints a syntax node. 20 20 var DebugStr func(x interface{}) string 21 + 22 + // EvalExpr evaluates an expression within an existing struct value. 23 + // Identifiers only resolve to values defined within the struct. 24 + // 25 + // Expressions may refer to builtin packages if they can be uniquely identified 26 + // 27 + // Both value and result are of type cue.Value, but are an interface to prevent 28 + // cyclic dependencies. 29 + // 30 + // TODO: extract interface 31 + var EvalExpr func(value, expr interface{}) (result interface{})