LCOV - code coverage report
Current view: top level - pebble/metamorphic - parser.go (source / functions) Hit Total Coverage
Test: 2023-12-19 08:16Z 5c5ad7ed - meta test only.lcov Lines: 364 431 84.5 %
Date: 2023-12-19 08:16:43 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
       2             : // of this source code is governed by a BSD-style license that can be found in
       3             : // the LICENSE file.
       4             : 
       5             : package metamorphic
       6             : 
       7             : import (
       8             :         "fmt"
       9             :         "go/scanner"
      10             :         "go/token"
      11             :         "reflect"
      12             :         "strconv"
      13             :         "strings"
      14             : 
      15             :         "github.com/cockroachdb/errors"
      16             :         "github.com/cockroachdb/pebble"
      17             : )
      18             : 
      19             : type methodInfo struct {
      20             :         constructor func() op
      21             :         validTags   uint32
      22             : }
      23             : 
      24           1 : func makeMethod(i interface{}, tags ...objTag) *methodInfo {
      25           1 :         var validTags uint32
      26           1 :         for _, tag := range tags {
      27           1 :                 validTags |= 1 << tag
      28           1 :         }
      29             : 
      30           1 :         t := reflect.TypeOf(i)
      31           1 :         return &methodInfo{
      32           1 :                 constructor: func() op {
      33           1 :                         return reflect.New(t).Interface().(op)
      34           1 :                 },
      35             :                 validTags: validTags,
      36             :         }
      37             : }
      38             : 
      39             : // args returns the receiverID, targetID and arguments for the op. The
      40             : // receiverID is the ID of the object the op will be applied to. The targetID
      41             : // is the ID of the object for assignment. If the method does not return a new
      42             : // object, then targetID will be nil. The argument list is just what it sounds
      43             : // like: the list of arguments for the operation.
      44           1 : func opArgs(op op) (receiverID *objID, targetID *objID, args []interface{}) {
      45           1 :         switch t := op.(type) {
      46           1 :         case *applyOp:
      47           1 :                 return &t.writerID, nil, []interface{}{&t.batchID}
      48           1 :         case *checkpointOp:
      49           1 :                 return &t.dbID, nil, []interface{}{&t.spans}
      50           1 :         case *closeOp:
      51           1 :                 return &t.objID, nil, nil
      52           1 :         case *compactOp:
      53           1 :                 return &t.dbID, nil, []interface{}{&t.start, &t.end, &t.parallelize}
      54           1 :         case *batchCommitOp:
      55           1 :                 return &t.batchID, nil, nil
      56           1 :         case *dbRatchetFormatMajorVersionOp:
      57           1 :                 return &t.dbID, nil, []interface{}{&t.vers}
      58           1 :         case *dbRestartOp:
      59           1 :                 return &t.dbID, nil, nil
      60           1 :         case *deleteOp:
      61           1 :                 return &t.writerID, nil, []interface{}{&t.key}
      62           1 :         case *deleteRangeOp:
      63           1 :                 return &t.writerID, nil, []interface{}{&t.start, &t.end}
      64           1 :         case *iterFirstOp:
      65           1 :                 return &t.iterID, nil, nil
      66           1 :         case *flushOp:
      67           1 :                 return &t.db, nil, nil
      68           1 :         case *getOp:
      69           1 :                 return &t.readerID, nil, []interface{}{&t.key}
      70           1 :         case *ingestOp:
      71           1 :                 return &t.dbID, nil, []interface{}{&t.batchIDs}
      72           1 :         case *ingestAndExciseOp:
      73           1 :                 return &t.dbID, nil, []interface{}{&t.batchID, &t.exciseStart, &t.exciseEnd}
      74           1 :         case *initOp:
      75           1 :                 return nil, nil, []interface{}{&t.dbSlots, &t.batchSlots, &t.iterSlots, &t.snapshotSlots}
      76           1 :         case *iterLastOp:
      77           1 :                 return &t.iterID, nil, nil
      78           1 :         case *mergeOp:
      79           1 :                 return &t.writerID, nil, []interface{}{&t.key, &t.value}
      80           1 :         case *newBatchOp:
      81           1 :                 return &t.dbID, &t.batchID, nil
      82           1 :         case *newIndexedBatchOp:
      83           1 :                 return &t.dbID, &t.batchID, nil
      84           1 :         case *newIterOp:
      85           1 :                 return &t.readerID, &t.iterID, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
      86           1 :         case *newIterUsingCloneOp:
      87           1 :                 return &t.existingIterID, &t.iterID, []interface{}{&t.refreshBatch, &t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
      88           1 :         case *newSnapshotOp:
      89           1 :                 return &t.dbID, &t.snapID, []interface{}{&t.bounds}
      90           1 :         case *iterNextOp:
      91           1 :                 return &t.iterID, nil, []interface{}{&t.limit}
      92           1 :         case *iterNextPrefixOp:
      93           1 :                 return &t.iterID, nil, nil
      94           1 :         case *iterCanSingleDelOp:
      95           1 :                 return &t.iterID, nil, []interface{}{}
      96           1 :         case *iterPrevOp:
      97           1 :                 return &t.iterID, nil, []interface{}{&t.limit}
      98           1 :         case *iterSeekLTOp:
      99           1 :                 return &t.iterID, nil, []interface{}{&t.key, &t.limit}
     100           1 :         case *iterSeekGEOp:
     101           1 :                 return &t.iterID, nil, []interface{}{&t.key, &t.limit}
     102           1 :         case *iterSeekPrefixGEOp:
     103           1 :                 return &t.iterID, nil, []interface{}{&t.key}
     104           1 :         case *setOp:
     105           1 :                 return &t.writerID, nil, []interface{}{&t.key, &t.value}
     106           1 :         case *iterSetBoundsOp:
     107           1 :                 return &t.iterID, nil, []interface{}{&t.lower, &t.upper}
     108           1 :         case *iterSetOptionsOp:
     109           1 :                 return &t.iterID, nil, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
     110           1 :         case *singleDeleteOp:
     111           1 :                 return &t.writerID, nil, []interface{}{&t.key, &t.maybeReplaceDelete}
     112           1 :         case *rangeKeyDeleteOp:
     113           1 :                 return &t.writerID, nil, []interface{}{&t.start, &t.end}
     114           1 :         case *rangeKeySetOp:
     115           1 :                 return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix, &t.value}
     116           1 :         case *rangeKeyUnsetOp:
     117           1 :                 return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix}
     118           1 :         case *replicateOp:
     119           1 :                 return &t.source, nil, []interface{}{&t.dest, &t.start, &t.end}
     120             :         }
     121           0 :         panic(fmt.Sprintf("unsupported op type: %T", op))
     122             : }
     123             : 
     124             : var methods = map[string]*methodInfo{
     125             :         "Apply":                     makeMethod(applyOp{}, dbTag, batchTag),
     126             :         "Checkpoint":                makeMethod(checkpointOp{}, dbTag),
     127             :         "Clone":                     makeMethod(newIterUsingCloneOp{}, iterTag),
     128             :         "Close":                     makeMethod(closeOp{}, dbTag, batchTag, iterTag, snapTag),
     129             :         "Commit":                    makeMethod(batchCommitOp{}, batchTag),
     130             :         "Compact":                   makeMethod(compactOp{}, dbTag),
     131             :         "Delete":                    makeMethod(deleteOp{}, dbTag, batchTag),
     132             :         "DeleteRange":               makeMethod(deleteRangeOp{}, dbTag, batchTag),
     133             :         "First":                     makeMethod(iterFirstOp{}, iterTag),
     134             :         "Flush":                     makeMethod(flushOp{}, dbTag),
     135             :         "Get":                       makeMethod(getOp{}, dbTag, batchTag, snapTag),
     136             :         "Ingest":                    makeMethod(ingestOp{}, dbTag),
     137             :         "IngestAndExcise":           makeMethod(ingestAndExciseOp{}, dbTag),
     138             :         "Init":                      makeMethod(initOp{}, dbTag),
     139             :         "Last":                      makeMethod(iterLastOp{}, iterTag),
     140             :         "Merge":                     makeMethod(mergeOp{}, dbTag, batchTag),
     141             :         "NewBatch":                  makeMethod(newBatchOp{}, dbTag),
     142             :         "NewIndexedBatch":           makeMethod(newIndexedBatchOp{}, dbTag),
     143             :         "NewIter":                   makeMethod(newIterOp{}, dbTag, batchTag, snapTag),
     144             :         "NewSnapshot":               makeMethod(newSnapshotOp{}, dbTag),
     145             :         "Next":                      makeMethod(iterNextOp{}, iterTag),
     146             :         "NextPrefix":                makeMethod(iterNextPrefixOp{}, iterTag),
     147             :         "InternalNext":              makeMethod(iterCanSingleDelOp{}, iterTag),
     148             :         "Prev":                      makeMethod(iterPrevOp{}, iterTag),
     149             :         "RangeKeyDelete":            makeMethod(rangeKeyDeleteOp{}, dbTag, batchTag),
     150             :         "RangeKeySet":               makeMethod(rangeKeySetOp{}, dbTag, batchTag),
     151             :         "RangeKeyUnset":             makeMethod(rangeKeyUnsetOp{}, dbTag, batchTag),
     152             :         "RatchetFormatMajorVersion": makeMethod(dbRatchetFormatMajorVersionOp{}, dbTag),
     153             :         "Replicate":                 makeMethod(replicateOp{}, dbTag),
     154             :         "Restart":                   makeMethod(dbRestartOp{}, dbTag),
     155             :         "SeekGE":                    makeMethod(iterSeekGEOp{}, iterTag),
     156             :         "SeekLT":                    makeMethod(iterSeekLTOp{}, iterTag),
     157             :         "SeekPrefixGE":              makeMethod(iterSeekPrefixGEOp{}, iterTag),
     158             :         "Set":                       makeMethod(setOp{}, dbTag, batchTag),
     159             :         "SetBounds":                 makeMethod(iterSetBoundsOp{}, iterTag),
     160             :         "SetOptions":                makeMethod(iterSetOptionsOp{}, iterTag),
     161             :         "SingleDelete":              makeMethod(singleDeleteOp{}, dbTag, batchTag),
     162             : }
     163             : 
     164             : type parser struct {
     165             :         opts parserOpts
     166             :         fset *token.FileSet
     167             :         s    scanner.Scanner
     168             :         objs map[objID]bool
     169             : }
     170             : 
     171             : type parserOpts struct {
     172             :         allowUndefinedObjs bool
     173             : }
     174             : 
     175           1 : func parse(src []byte, opts parserOpts) (_ []op, err error) {
     176           1 :         // Various bits of magic incantation to set up a scanner for Go compatible
     177           1 :         // syntax. We arranged for the textual format of ops (e.g. op.String()) to
     178           1 :         // look like Go which allows us to use the Go scanner for parsing.
     179           1 :         p := &parser{
     180           1 :                 opts: opts,
     181           1 :                 fset: token.NewFileSet(),
     182           1 :                 objs: map[objID]bool{makeObjID(dbTag, 1): true, makeObjID(dbTag, 2): true},
     183           1 :         }
     184           1 :         file := p.fset.AddFile("", -1, len(src))
     185           1 :         p.s.Init(file, src, nil /* no error handler */, 0)
     186           1 :         return p.parse()
     187           1 : }
     188             : 
     189           1 : func (p *parser) parse() (_ []op, err error) {
     190           1 :         defer func() {
     191           1 :                 if r := recover(); r != nil {
     192           0 :                         var ok bool
     193           0 :                         if err, ok = r.(error); ok {
     194           0 :                                 return
     195           0 :                         }
     196           0 :                         err = errors.Errorf("%v", r)
     197             :                 }
     198             :         }()
     199             : 
     200           1 :         var ops []op
     201           1 :         for {
     202           1 :                 op := p.parseOp()
     203           1 :                 if op == nil {
     204           1 :                         computeDerivedFields(ops)
     205           1 :                         return ops, nil
     206           1 :                 }
     207           1 :                 ops = append(ops, op)
     208             :         }
     209             : }
     210             : 
     211           1 : func (p *parser) parseOp() op {
     212           1 :         destPos, destTok, destLit := p.s.Scan()
     213           1 :         if destTok == token.EOF {
     214           1 :                 return nil
     215           1 :         }
     216           1 :         if destTok != token.IDENT {
     217           0 :                 panic(p.errorf(destPos, "unexpected token: %s %q", destTok, destLit))
     218             :         }
     219           1 :         if destLit == "Init" {
     220           1 :                 // <op>(<args>)
     221           1 :                 return p.makeOp(destLit, makeObjID(dbTag, 1), 0, destPos)
     222           1 :         }
     223             : 
     224           1 :         destID := p.parseObjID(destPos, destLit)
     225           1 : 
     226           1 :         pos, tok, lit := p.s.Scan()
     227           1 :         switch tok {
     228           1 :         case token.PERIOD:
     229           1 :                 // <obj>.<op>(<args>)
     230           1 :                 if !p.objs[destID] {
     231           0 :                         if p.opts.allowUndefinedObjs {
     232           0 :                                 p.objs[destID] = true
     233           0 :                         } else {
     234           0 :                                 panic(p.errorf(destPos, "unknown object: %s", destID))
     235             :                         }
     236             :                 }
     237           1 :                 _, methodLit := p.scanToken(token.IDENT)
     238           1 :                 return p.makeOp(methodLit, destID, 0, destPos)
     239             : 
     240           1 :         case token.ASSIGN:
     241           1 :                 // <obj> = <obj>.<op>(<args>)
     242           1 :                 srcPos, srcLit := p.scanToken(token.IDENT)
     243           1 :                 srcID := p.parseObjID(srcPos, srcLit)
     244           1 :                 if !p.objs[srcID] {
     245           0 :                         if p.opts.allowUndefinedObjs {
     246           0 :                                 p.objs[srcID] = true
     247           0 :                         } else {
     248           0 :                                 panic(p.errorf(srcPos, "unknown object %q", srcLit))
     249             :                         }
     250             :                 }
     251           1 :                 p.scanToken(token.PERIOD)
     252           1 :                 _, methodLit := p.scanToken(token.IDENT)
     253           1 :                 p.objs[destID] = true
     254           1 :                 return p.makeOp(methodLit, srcID, destID, srcPos)
     255             :         }
     256           0 :         panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     257             : }
     258             : 
     259           1 : func parseObjID(str string) (objID, error) {
     260           1 :         var tag objTag
     261           1 :         switch {
     262           1 :         case strings.HasPrefix(str, "db"):
     263           1 :                 tag, str = dbTag, str[2:]
     264           1 :                 if str == "" {
     265           0 :                         str = "1"
     266           0 :                 }
     267           1 :         case strings.HasPrefix(str, "batch"):
     268           1 :                 tag, str = batchTag, str[5:]
     269           1 :         case strings.HasPrefix(str, "iter"):
     270           1 :                 tag, str = iterTag, str[4:]
     271           1 :         case strings.HasPrefix(str, "snap"):
     272           1 :                 tag, str = snapTag, str[4:]
     273           0 :         default:
     274           0 :                 return 0, errors.Newf("unable to parse objectID: %q", str)
     275             :         }
     276           1 :         id, err := strconv.ParseInt(str, 10, 32)
     277           1 :         if err != nil {
     278           0 :                 return 0, err
     279           0 :         }
     280           1 :         return makeObjID(tag, uint32(id)), nil
     281             : }
     282             : 
     283           1 : func (p *parser) parseObjID(pos token.Pos, str string) objID {
     284           1 :         id, err := parseObjID(str)
     285           1 :         if err != nil {
     286           0 :                 panic(p.errorf(pos, "%s", err))
     287             :         }
     288           1 :         return id
     289             : }
     290             : 
     291           1 : func unquoteBytes(lit string) []byte {
     292           1 :         s, err := strconv.Unquote(lit)
     293           1 :         if err != nil {
     294           0 :                 panic(err)
     295             :         }
     296           1 :         if len(s) == 0 {
     297           1 :                 return nil
     298           1 :         }
     299           1 :         return []byte(s)
     300             : }
     301             : 
     302           1 : func (p *parser) parseArgs(op op, methodName string, args []interface{}) {
     303           1 :         pos, _ := p.scanToken(token.LPAREN)
     304           1 :         for i := range args {
     305           1 :                 if i > 0 {
     306           1 :                         pos, _ = p.scanToken(token.COMMA)
     307           1 :                 }
     308             : 
     309           1 :                 switch t := args[i].(type) {
     310           1 :                 case *uint32:
     311           1 :                         _, lit := p.scanToken(token.INT)
     312           1 :                         val, err := strconv.ParseUint(lit, 10, 32)
     313           1 :                         if err != nil {
     314           0 :                                 panic(err)
     315             :                         }
     316           1 :                         *t = uint32(val)
     317             : 
     318           1 :                 case *uint64:
     319           1 :                         _, lit := p.scanToken(token.INT)
     320           1 :                         val, err := strconv.ParseUint(lit, 10, 64)
     321           1 :                         if err != nil {
     322           0 :                                 panic(err)
     323             :                         }
     324           1 :                         *t = uint64(val)
     325             : 
     326           1 :                 case *[]byte:
     327           1 :                         _, lit := p.scanToken(token.STRING)
     328           1 :                         *t = unquoteBytes(lit)
     329             : 
     330           1 :                 case *bool:
     331           1 :                         _, lit := p.scanToken(token.IDENT)
     332           1 :                         b, err := strconv.ParseBool(lit)
     333           1 :                         if err != nil {
     334           0 :                                 panic(err)
     335             :                         }
     336           1 :                         *t = b
     337             : 
     338           1 :                 case *objID:
     339           1 :                         pos, lit := p.scanToken(token.IDENT)
     340           1 :                         *t = p.parseObjID(pos, lit)
     341             : 
     342           1 :                 case *[]pebble.KeyRange:
     343           1 :                         var pending pebble.KeyRange
     344           1 :                         for {
     345           1 :                                 pos, tok, lit := p.s.Scan()
     346           1 :                                 switch tok {
     347           1 :                                 case token.STRING:
     348           1 :                                         x := unquoteBytes(lit)
     349           1 :                                         if pending.Start == nil {
     350           1 :                                                 pending.Start = x
     351           1 :                                         } else {
     352           1 :                                                 pending.End = x
     353           1 :                                                 *t = append(*t, pending)
     354           1 :                                                 pending = pebble.KeyRange{}
     355           1 :                                         }
     356           1 :                                         pos, tok, lit := p.s.Scan()
     357           1 :                                         switch tok {
     358           1 :                                         case token.COMMA:
     359           1 :                                                 continue
     360           1 :                                         case token.RPAREN:
     361           1 :                                                 p.scanToken(token.SEMICOLON)
     362           1 :                                                 return
     363           0 :                                         default:
     364           0 :                                                 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     365             :                                         }
     366           0 :                                 case token.RPAREN:
     367           0 :                                         p.scanToken(token.SEMICOLON)
     368           0 :                                         return
     369           0 :                                 default:
     370           0 :                                         panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     371             :                                 }
     372             :                         }
     373             : 
     374           1 :                 case *[]objID:
     375           1 :                         for {
     376           1 :                                 pos, tok, lit := p.s.Scan()
     377           1 :                                 switch tok {
     378           1 :                                 case token.IDENT:
     379           1 :                                         *t = append(*t, p.parseObjID(pos, lit))
     380           1 :                                         pos, tok, lit := p.s.Scan()
     381           1 :                                         switch tok {
     382           1 :                                         case token.COMMA:
     383           1 :                                                 continue
     384           1 :                                         case token.RPAREN:
     385           1 :                                                 p.scanToken(token.SEMICOLON)
     386           1 :                                                 return
     387           0 :                                         default:
     388           0 :                                                 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     389             :                                         }
     390           0 :                                 case token.RPAREN:
     391           0 :                                         p.scanToken(token.SEMICOLON)
     392           0 :                                         return
     393           0 :                                 default:
     394           0 :                                         panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     395             :                                 }
     396             :                         }
     397             : 
     398           1 :                 case *[]pebble.CheckpointSpan:
     399           1 :                         pos, tok, lit := p.s.Scan()
     400           1 :                         switch tok {
     401           1 :                         case token.RPAREN:
     402           1 :                                 // No spans.
     403           1 :                                 *t = nil
     404           1 :                                 p.scanToken(token.SEMICOLON)
     405           1 :                                 return
     406             : 
     407           1 :                         case token.STRING:
     408           1 :                                 var keys [][]byte
     409           1 :                                 for {
     410           1 :                                         s, err := strconv.Unquote(lit)
     411           1 :                                         if err != nil {
     412           0 :                                                 panic(p.errorf(pos, "unquoting %q: %v", lit, err))
     413             :                                         }
     414           1 :                                         keys = append(keys, []byte(s))
     415           1 : 
     416           1 :                                         pos, tok, lit = p.s.Scan()
     417           1 :                                         switch tok {
     418           1 :                                         case token.COMMA:
     419           1 :                                                 pos, tok, lit = p.s.Scan()
     420           1 :                                                 if tok != token.STRING {
     421           0 :                                                         panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     422             :                                                 }
     423           1 :                                                 continue
     424             : 
     425           1 :                                         case token.RPAREN:
     426           1 :                                                 p.scanToken(token.SEMICOLON)
     427           1 :                                                 if len(keys)%2 == 1 {
     428           0 :                                                         panic(p.errorf(pos, "expected even number of keys"))
     429             :                                                 }
     430           1 :                                                 *t = make([]pebble.CheckpointSpan, len(keys)/2)
     431           1 :                                                 for i := range *t {
     432           1 :                                                         (*t)[i] = pebble.CheckpointSpan{
     433           1 :                                                                 Start: keys[i*2],
     434           1 :                                                                 End:   keys[i*2+1],
     435           1 :                                                         }
     436           1 :                                                 }
     437           1 :                                                 return
     438             : 
     439           0 :                                         default:
     440           0 :                                                 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     441             :                                         }
     442             :                                 }
     443             : 
     444           0 :                         default:
     445           0 :                                 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     446             :                         }
     447             : 
     448           1 :                 case *pebble.FormatMajorVersion:
     449           1 :                         _, lit := p.scanToken(token.INT)
     450           1 :                         val, err := strconv.ParseUint(lit, 10, 64)
     451           1 :                         if err != nil {
     452           0 :                                 panic(err)
     453             :                         }
     454           1 :                         *t = pebble.FormatMajorVersion(val)
     455             : 
     456           0 :                 default:
     457           0 :                         panic(p.errorf(pos, "%s: unsupported arg[%d] type: %T", methodName, i, args[i]))
     458             :                 }
     459             :         }
     460           1 :         p.scanToken(token.RPAREN)
     461           1 :         p.scanToken(token.SEMICOLON)
     462             : }
     463             : 
     464           1 : func (p *parser) scanToken(expected token.Token) (pos token.Pos, lit string) {
     465           1 :         pos, tok, lit := p.s.Scan()
     466           1 :         if tok != expected {
     467           0 :                 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
     468             :         }
     469           1 :         return pos, lit
     470             : }
     471             : 
     472           1 : func (p *parser) makeOp(methodName string, receiverID, targetID objID, pos token.Pos) op {
     473           1 :         info := methods[methodName]
     474           1 :         if info == nil {
     475           0 :                 panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName))
     476             :         }
     477           1 :         if info.validTags&(1<<receiverID.tag()) == 0 {
     478           0 :                 panic(p.errorf(pos, "%s.%s: %s is not a method on %s",
     479           0 :                         receiverID, methodName, methodName, receiverID))
     480             :         }
     481             : 
     482           1 :         op := info.constructor()
     483           1 :         receiver, target, args := opArgs(op)
     484           1 : 
     485           1 :         // The form of an operation is:
     486           1 :         //   [target =] receiver.method(args)
     487           1 :         //
     488           1 :         // The receiver is the object the operation will be called on, which can be
     489           1 :         // any valid ID. Certain operations such as Ingest are only valid on the DB
     490           1 :         // object. That is indicated by opArgs returning a nil receiver.
     491           1 :         if receiver != nil {
     492           1 :                 *receiver = receiverID
     493           1 :         } else if receiverID.tag() != dbTag {
     494           0 :                 panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName))
     495             :         }
     496             : 
     497             :         // The target is the object that will be assigned the result of an object
     498             :         // creation operation such as newBatchOp or newIterOp.
     499           1 :         if target != nil {
     500           1 :                 // It is invalid to not have a targetID for a method which generates a new
     501           1 :                 // object.
     502           1 :                 if targetID == 0 {
     503           0 :                         panic(p.errorf(pos, "assignment expected for %s.%s", receiverID, methodName))
     504             :                 }
     505             :                 // It is invalid to try to assign to the DB object.
     506           1 :                 if targetID.tag() == dbTag {
     507           0 :                         panic(p.errorf(pos, "cannot use %s as target of assignment", targetID))
     508             :                 }
     509           1 :                 *target = targetID
     510           1 :         } else if targetID != 0 {
     511           0 :                 panic(p.errorf(pos, "cannot use %s.%s in assignment", receiverID, methodName))
     512             :         }
     513             : 
     514           1 :         p.parseArgs(op, methodName, args)
     515           1 :         return op
     516             : }
     517             : 
     518           0 : func (p *parser) tokenf(tok token.Token, lit string) string {
     519           0 :         if tok.IsLiteral() {
     520           0 :                 return lit
     521           0 :         }
     522           0 :         return tok.String()
     523             : }
     524             : 
     525           0 : func (p *parser) errorf(pos token.Pos, format string, args ...interface{}) error {
     526           0 :         return errors.New(p.fset.Position(pos).String() + ": " + fmt.Sprintf(format, args...))
     527           0 : }
     528             : 
     529             : // computeDerivedFields makes one pass through the provided operations, filling
     530             : // any derived fields. This pass must happen before execution because concurrent
     531             : // execution depends on these fields.
     532           1 : func computeDerivedFields(ops []op) {
     533           1 :         iterToReader := make(map[objID]objID)
     534           1 :         objToDB := make(map[objID]objID)
     535           1 :         for i := range ops {
     536           1 :                 switch v := ops[i].(type) {
     537           1 :                 case *newSnapshotOp:
     538           1 :                         objToDB[v.snapID] = v.dbID
     539           1 :                 case *newIterOp:
     540           1 :                         iterToReader[v.iterID] = v.readerID
     541           1 :                         dbReaderID := v.readerID
     542           1 :                         if dbReaderID.tag() != dbTag {
     543           1 :                                 dbReaderID = objToDB[dbReaderID]
     544           1 :                         }
     545           1 :                         objToDB[v.iterID] = dbReaderID
     546           1 :                         v.derivedDBID = dbReaderID
     547           1 :                 case *newIterUsingCloneOp:
     548           1 :                         v.derivedReaderID = iterToReader[v.existingIterID]
     549           1 :                         iterToReader[v.iterID] = v.derivedReaderID
     550           1 :                         objToDB[v.iterID] = objToDB[v.existingIterID]
     551           1 :                 case *iterSetOptionsOp:
     552           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     553           1 :                 case *iterFirstOp:
     554           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     555           1 :                 case *iterLastOp:
     556           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     557           1 :                 case *iterSeekGEOp:
     558           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     559           1 :                 case *iterSeekPrefixGEOp:
     560           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     561           1 :                 case *iterSeekLTOp:
     562           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     563           1 :                 case *iterNextOp:
     564           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     565           1 :                 case *iterNextPrefixOp:
     566           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     567           1 :                 case *iterCanSingleDelOp:
     568           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     569           1 :                 case *iterPrevOp:
     570           1 :                         v.derivedReaderID = iterToReader[v.iterID]
     571           1 :                 case *newBatchOp:
     572           1 :                         objToDB[v.batchID] = v.dbID
     573           1 :                 case *newIndexedBatchOp:
     574           1 :                         objToDB[v.batchID] = v.dbID
     575           1 :                 case *applyOp:
     576           1 :                         if derivedDBID, ok := objToDB[v.batchID]; ok && v.writerID.tag() != dbTag {
     577           1 :                                 objToDB[v.writerID] = derivedDBID
     578           1 :                         }
     579           1 :                 case *getOp:
     580           1 :                         if derivedDBID, ok := objToDB[v.readerID]; ok {
     581           1 :                                 v.derivedDBID = derivedDBID
     582           1 :                         }
     583           1 :                 case *batchCommitOp:
     584           1 :                         v.dbID = objToDB[v.batchID]
     585           1 :                 case *closeOp:
     586           1 :                         if derivedDBID, ok := objToDB[v.objID]; ok && v.objID.tag() != dbTag {
     587           1 :                                 v.derivedDBID = derivedDBID
     588           1 :                         }
     589           1 :                 case *ingestOp:
     590           1 :                         v.derivedDBIDs = make([]objID, len(v.batchIDs))
     591           1 :                         for i := range v.batchIDs {
     592           1 :                                 v.derivedDBIDs[i] = objToDB[v.batchIDs[i]]
     593           1 :                         }
     594           1 :                 case *ingestAndExciseOp:
     595           1 :                         v.derivedDBID = objToDB[v.batchID]
     596           1 :                 case *deleteOp:
     597           1 :                         derivedDBID := v.writerID
     598           1 :                         if v.writerID.tag() != dbTag {
     599           1 :                                 derivedDBID = objToDB[v.writerID]
     600           1 :                         }
     601           1 :                         v.derivedDBID = derivedDBID
     602             :                 }
     603             :         }
     604             : }

Generated by: LCOV version 1.14