github.com/ndau/noms@v1.0.5/go/types/path.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package types 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "math" 12 "regexp" 13 "strconv" 14 "strings" 15 16 "github.com/ndau/noms/go/d" 17 "github.com/ndau/noms/go/hash" 18 ) 19 20 // For an annotation like @type, 1st capture group is the annotation. 21 // For @at(42), 1st capture group is the annotation and 3rd is the parameter. 22 // Note, @at() is valid under this regexp, code should deal with the error. 23 var annotationRe = regexp.MustCompile(`^([a-z]+)(\(([\w\-"']*)\))?`) 24 25 // A Path locates a value in Noms relative to some other value. For locating 26 // values absolutely within a database, see AbsolutePath. To locate values 27 // globally, see Spec. 28 // 29 // For more details, see: 30 // https://github.com/ndau/noms/blob/master/doc/spelling.md. 31 type Path []PathPart 32 33 type PathPart interface { 34 Resolve(v Value, vr ValueReader) Value 35 String() string 36 } 37 38 // ParsePath parses str into a Path, or returns an error if parsing failed. 39 func ParsePath(str string) (Path, error) { 40 if str == "" { 41 return Path{}, errors.New("Empty path") 42 } 43 return constructPath(Path{}, str) 44 } 45 46 // MustParsePath parses str into a Path, or panics if parsing failed. 47 func MustParsePath(str string) Path { 48 p, err := ParsePath(str) 49 if err != nil { 50 panic(err) 51 } 52 return p 53 } 54 55 type keyIndexable interface { 56 setIntoKey(v bool) keyIndexable 57 } 58 59 func constructPath(p Path, str string) (Path, error) { 60 if len(str) == 0 { 61 return p, nil 62 } 63 64 op, tail := str[0], str[1:] 65 66 switch op { 67 case '.': 68 idx := fieldNameComponentRe.FindIndex([]byte(tail)) 69 if idx == nil { 70 return Path{}, errors.New("Invalid field: " + tail) 71 } 72 p = append(p, FieldPath{tail[:idx[1]]}) 73 return constructPath(p, tail[idx[1]:]) 74 75 case '[': 76 if len(tail) == 0 { 77 return Path{}, errors.New("Path ends in [") 78 } 79 80 idx, h, rem, err := ParsePathIndex(tail) 81 if err != nil { 82 return Path{}, err 83 } 84 if !strings.HasPrefix(rem, "]") { 85 return Path{}, errors.New("[ is missing closing ]") 86 } 87 d.PanicIfTrue(idx == nil && h.IsEmpty()) 88 d.PanicIfTrue(idx != nil && !h.IsEmpty()) 89 90 if idx != nil { 91 p = append(p, NewIndexPath(idx)) 92 } else { 93 p = append(p, NewHashIndexPath(h)) 94 } 95 return constructPath(p, rem[1:]) 96 97 case '@': 98 ann, hasArg, arg, rem := getAnnotation(tail) 99 100 switch ann { 101 case "at": 102 if arg == "" { 103 return Path{}, fmt.Errorf("@at annotation requires a position argument") 104 } 105 idx, err := strconv.ParseInt(arg, 10, 64) 106 if err != nil { 107 return Path{}, fmt.Errorf("Invalid position: %s", arg) 108 } 109 return constructPath(append(p, NewAtAnnotation(idx)), rem) 110 111 case "key": 112 if hasArg { 113 return Path{}, fmt.Errorf("@key annotation does not support arguments") 114 } 115 if len(p) == 0 { 116 return Path{}, fmt.Errorf("Cannot use @key annotation at beginning of path") 117 } 118 lastPart := p[len(p)-1] 119 if ki, ok := lastPart.(keyIndexable); ok { 120 p[len(p)-1] = ki.setIntoKey(true).(PathPart) 121 return constructPath(p, rem) 122 } 123 return Path{}, fmt.Errorf("Cannot use @key annotation on: %s", lastPart.String()) 124 125 case "target": 126 if hasArg { 127 return Path{}, fmt.Errorf("@target annotation does not support arguments") 128 } 129 return constructPath(append(p, TargetAnnotation{}), rem) 130 131 case "type": 132 if hasArg { 133 return Path{}, fmt.Errorf("@type annotation does not support arguments") 134 } 135 return constructPath(append(p, TypeAnnotation{}), rem) 136 137 default: 138 return Path{}, fmt.Errorf("Unsupported annotation: @%s", ann) 139 } 140 141 case ']': 142 return Path{}, errors.New("] is missing opening [") 143 144 default: 145 return Path{}, fmt.Errorf("Invalid operator: %c", op) 146 } 147 } 148 149 // Resolve resolves a path relative to some value. 150 // A ValueReader is required to resolve paths that contain the @target annotation. 151 func (p Path) Resolve(v Value, vr ValueReader) (resolved Value) { 152 resolved = v 153 for _, part := range p { 154 if resolved == nil { 155 break 156 } 157 resolved = part.Resolve(resolved, vr) 158 } 159 160 return 161 } 162 163 func (p Path) Equals(o Path) bool { 164 if len(p) != len(o) { 165 return false 166 } 167 for i, pp := range p { 168 if pp != o[i] { 169 return false 170 } 171 } 172 return true 173 } 174 175 // Append makes a copy of a p and appends the PathPart 'pp' to it. 176 func (p Path) Append(pp PathPart) Path { 177 p1 := make(Path, len(p), len(p)+1) 178 copy(p1, p) 179 return append(p1, pp) 180 } 181 182 func (p Path) String() string { 183 strs := make([]string, 0, len(p)) 184 for _, part := range p { 185 strs = append(strs, part.String()) 186 } 187 return strings.Join(strs, "") 188 } 189 190 func (p Path) IsEmpty() bool { 191 return len(p) == 0 192 } 193 194 // FieldPath references Struct field values by name. 195 type FieldPath struct { 196 // The name of the field, e.g. `.Name`. 197 Name string 198 } 199 200 func NewFieldPath(name string) FieldPath { 201 return FieldPath{name} 202 } 203 204 func (fp FieldPath) Resolve(v Value, vr ValueReader) Value { 205 switch v := v.(type) { 206 case Struct: 207 if sv, ok := v.MaybeGet(fp.Name); ok { 208 return sv 209 } 210 case *Type: 211 if desc, ok := v.Desc.(StructDesc); ok { 212 if df, _ := desc.Field(fp.Name); df != nil { 213 return df 214 } 215 } 216 } 217 return nil 218 } 219 220 func (fp FieldPath) String() string { 221 return fmt.Sprintf(".%s", fp.Name) 222 } 223 224 // IndexPath ndexes into Maps and Lists by key or index. 225 type IndexPath struct { 226 // The value of the index, e.g. `[42]` or `["value"]`. If Index is a negative 227 // number and the path is resolved in a List, it means index from the back. 228 Index Value 229 // Whether this index should resolve to the key of a map, given by a `@key` 230 // annotation. Typically IntoKey is false, and indices would resolve to the 231 // values. E.g. given `{a: 42}` then `["a"]` resolves to `42`. If IntoKey is 232 // true, then it resolves to `"a"`. For IndexPath this isn't particularly 233 // useful - it's mostly provided for consistency with HashIndexPath - but 234 // note that given `{a: 42}` then `["b"]` resolves to nil, not `"b"`. 235 IntoKey bool 236 } 237 238 func NewIndexPath(idx Value) IndexPath { 239 return newIndexPath(idx, false) 240 } 241 242 func NewIndexIntoKeyPath(idx Value) IndexPath { 243 return newIndexPath(idx, true) 244 } 245 246 func ValueCanBePathIndex(v Value) bool { 247 k := v.Kind() 248 return k == StringKind || k == BoolKind || k == NumberKind 249 } 250 251 func newIndexPath(idx Value, intoKey bool) IndexPath { 252 d.PanicIfFalse(ValueCanBePathIndex(idx)) 253 return IndexPath{idx, intoKey} 254 } 255 256 func (ip IndexPath) Resolve(v Value, vr ValueReader) Value { 257 seqIndex := func(getter func(i uint64) Value) Value { 258 n, ok := ip.Index.(Number) 259 if !ok { 260 return nil 261 } 262 f := float64(n) 263 if f != math.Trunc(f) { 264 return nil 265 } 266 ai, ok := getAbsoluteIndex(v, int64(f)) 267 if !ok { 268 return nil 269 } 270 if ip.IntoKey { 271 return Number(ai) 272 } 273 return getter(ai) 274 } 275 276 switch v := v.(type) { 277 case List: 278 return seqIndex(func(i uint64) Value { return v.Get(i) }) 279 case *Type: 280 if cd, ok := v.Desc.(CompoundDesc); ok { 281 return seqIndex(func(i uint64) Value { return cd.ElemTypes[i] }) 282 } 283 case Map: 284 if !ip.IntoKey { 285 return v.Get(ip.Index) 286 } 287 if v.Has(ip.Index) { 288 return ip.Index 289 } 290 } 291 292 return nil 293 } 294 295 func (ip IndexPath) String() (str string) { 296 str = fmt.Sprintf("[%s]", EncodedIndexValue(ip.Index)) 297 if ip.IntoKey { 298 str += "@key" 299 } 300 return 301 } 302 303 func (ip IndexPath) setIntoKey(v bool) keyIndexable { 304 ip.IntoKey = v 305 return ip 306 } 307 308 // Indexes into Maps by the hash of a key, or a Set by the hash of a value. 309 type HashIndexPath struct { 310 // The hash of the key or value to search for. Maps and Set are ordered, so 311 // this in O(log(size)). 312 Hash hash.Hash 313 // Whether this index should resolve to the key of a map, given by a `@key` 314 // annotation. Typically IntoKey is false, and indices would resolve to the 315 // values. E.g. given `{a: 42}` and if the hash of `"a"` is `#abcd`, then 316 // `[#abcd]` resolves to `42`. If IntoKey is true, then it resolves to `"a"`. 317 // This is useful for when Map keys aren't primitive values, e.g. a struct, 318 // since struct literals can't be spelled using a Path. 319 IntoKey bool 320 } 321 322 func NewHashIndexPath(h hash.Hash) HashIndexPath { 323 return newHashIndexPath(h, false) 324 } 325 326 func NewHashIndexIntoKeyPath(h hash.Hash) HashIndexPath { 327 return newHashIndexPath(h, true) 328 } 329 330 func newHashIndexPath(h hash.Hash, intoKey bool) HashIndexPath { 331 d.PanicIfTrue(h.IsEmpty()) 332 return HashIndexPath{h, intoKey} 333 } 334 335 func (hip HashIndexPath) Resolve(v Value, vr ValueReader) (res Value) { 336 var seq orderedSequence 337 var getCurrentValue func(cur *sequenceCursor) Value 338 339 switch v := v.(type) { 340 case Set: 341 // Unclear what the behavior should be if |hip.IntoKey| is true, but ignoring it for sets is arguably correct. 342 seq = v.orderedSequence 343 getCurrentValue = func(cur *sequenceCursor) Value { return cur.current().(Value) } 344 case Map: 345 seq = v.orderedSequence 346 if hip.IntoKey { 347 getCurrentValue = func(cur *sequenceCursor) Value { return cur.current().(mapEntry).key } 348 } else { 349 getCurrentValue = func(cur *sequenceCursor) Value { return cur.current().(mapEntry).value } 350 } 351 default: 352 return nil 353 } 354 355 cur := newCursorAt(seq, orderedKeyFromHash(hip.Hash), false, false) 356 if !cur.valid() { 357 return nil 358 } 359 360 if getCurrentKey(cur).h != hip.Hash { 361 return nil 362 } 363 364 return getCurrentValue(cur) 365 } 366 367 func (hip HashIndexPath) String() (str string) { 368 str = fmt.Sprintf("[#%s]", hip.Hash.String()) 369 if hip.IntoKey { 370 str += "@key" 371 } 372 return 373 } 374 375 func (hip HashIndexPath) setIntoKey(v bool) keyIndexable { 376 hip.IntoKey = v 377 return hip 378 } 379 380 // Parse a Noms value from the path index syntax. 381 // 4 -> types.Number 382 // "4" -> types.String 383 // true|false -> types.Boolean 384 // #<chars> -> hash.Hash 385 func ParsePathIndex(str string) (idx Value, h hash.Hash, rem string, err error) { 386 Switch: 387 switch str[0] { 388 case '"': 389 // String is complicated because ] might be quoted, and " or \ might be escaped. 390 stringBuf := bytes.Buffer{} 391 i := 1 392 393 for ; i < len(str); i++ { 394 c := str[i] 395 if c == '"' { 396 i++ 397 break 398 } 399 if c == '\\' && i < len(str)-1 { 400 i++ 401 c = str[i] 402 if c != '\\' && c != '"' { 403 err = errors.New(`Only " and \ can be escaped`) 404 break Switch 405 } 406 } 407 stringBuf.WriteByte(c) 408 } 409 410 idx = String(stringBuf.String()) 411 rem = str[i:] 412 413 default: 414 idxStr := str 415 sepIdx := strings.Index(str, "]") 416 if sepIdx >= 0 { 417 idxStr = str[:sepIdx] 418 rem = str[sepIdx:] 419 } 420 if len(idxStr) == 0 { 421 err = errors.New("Empty index value") 422 } else if idxStr[0] == '#' { 423 hashStr := idxStr[1:] 424 h, _ = hash.MaybeParse(hashStr) 425 if h.IsEmpty() { 426 err = errors.New("Invalid hash: " + hashStr) 427 } 428 } else if idxStr == "true" { 429 idx = Bool(true) 430 } else if idxStr == "false" { 431 idx = Bool(false) 432 } else if i, err2 := strconv.ParseFloat(idxStr, 64); err2 == nil { 433 // Should we be more strict here? ParseFloat allows leading and trailing dots, and exponents. 434 idx = Number(i) 435 } else { 436 err = errors.New("Invalid index: " + idxStr) 437 } 438 } 439 440 return 441 } 442 443 // TypeAnnotation is a PathPart annotation to resolve to the type of the value 444 // it's resolved in. 445 type TypeAnnotation struct { 446 } 447 448 func (ann TypeAnnotation) Resolve(v Value, vr ValueReader) Value { 449 return TypeOf(v) 450 } 451 452 func (ann TypeAnnotation) String() string { 453 return "@type" 454 } 455 456 // TargetAnnotation is a PathPart annotation to resolve to the targetValue of the Ref it is resolved on. 457 type TargetAnnotation struct { 458 } 459 460 func (ann TargetAnnotation) Resolve(v Value, vr ValueReader) Value { 461 if vr == nil { 462 d.Panic("@target annotation requires a database to resolve against") 463 } 464 if r, ok := v.(Ref); ok { 465 return r.TargetValue(vr) 466 } else { 467 return nil 468 } 469 } 470 471 func (ann TargetAnnotation) String() string { 472 return "@target" 473 } 474 475 // AtAnnotation is a PathPart annotation that gets the value of a collection at 476 // a position, rather than a key. This is equivalent to IndexPath for lists, 477 // but different for sets and maps. 478 type AtAnnotation struct { 479 // Index is the position to resolve at. If negative, it means an index 480 // relative to the end of the collection. 481 Index int64 482 // IntoKey see IndexPath.IntoKey. 483 IntoKey bool 484 } 485 486 func NewAtAnnotation(idx int64) AtAnnotation { 487 return AtAnnotation{idx, false} 488 } 489 490 func NewAtAnnotationIntoKeyPath(idx int64) AtAnnotation { 491 return AtAnnotation{idx, true} 492 } 493 494 func (ann AtAnnotation) Resolve(v Value, vr ValueReader) Value { 495 ai, ok := getAbsoluteIndex(v, ann.Index) 496 if !ok { 497 return nil 498 } 499 500 switch v := v.(type) { 501 case List: 502 if !ann.IntoKey { 503 return v.Get(ai) 504 } 505 case Set: 506 return v.At(ai) 507 case Map: 508 k, mapv := v.At(ai) 509 if ann.IntoKey { 510 return k 511 } 512 return mapv 513 case *Type: 514 if cd, ok := v.Desc.(CompoundDesc); ok { 515 return cd.ElemTypes[ai] 516 } 517 } 518 519 return nil 520 } 521 522 func (ann AtAnnotation) String() (str string) { 523 str = fmt.Sprintf("@at(%d)", ann.Index) 524 if ann.IntoKey { 525 str += "@key" 526 } 527 return 528 } 529 530 func (ann AtAnnotation) setIntoKey(v bool) keyIndexable { 531 ann.IntoKey = v 532 return ann 533 } 534 535 func getAnnotation(str string) (ann string, hasArg bool, arg, rem string) { 536 parts := annotationRe.FindStringSubmatch(str) 537 if parts == nil { 538 return 539 } 540 541 ann = parts[1] 542 hasArg = parts[2] != "" 543 arg = parts[3] 544 rem = str[len(parts[0]):] 545 return 546 } 547 548 func getAbsoluteIndex(v Value, relIdx int64) (absIdx uint64, ok bool) { 549 var l uint64 550 switch v := v.(type) { 551 case Collection: 552 l = v.Len() 553 case *Type: 554 if cd, cdOK := v.Desc.(CompoundDesc); cdOK { 555 l = uint64(len(cd.ElemTypes)) 556 } else { 557 return 558 } 559 default: 560 return 561 } 562 563 if relIdx < 0 { 564 if uint64(-relIdx) > l { 565 return 566 } 567 absIdx = l - uint64(-relIdx) 568 } else { 569 if uint64(relIdx) >= l { 570 return 571 } 572 absIdx = uint64(relIdx) 573 } 574 575 ok = true 576 return 577 }