github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/metamorphic/parser.go (about) 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 ) 17 18 type methodInfo struct { 19 constructor func() op 20 validTags uint32 21 } 22 23 func makeMethod(i interface{}, tags ...objTag) *methodInfo { 24 var validTags uint32 25 for _, tag := range tags { 26 validTags |= 1 << tag 27 } 28 29 t := reflect.TypeOf(i) 30 return &methodInfo{ 31 constructor: func() op { 32 return reflect.New(t).Interface().(op) 33 }, 34 validTags: validTags, 35 } 36 } 37 38 // args returns the receiverID, targetID and arguments for the op. The 39 // receiverID is the ID of the object the op will be applied to. The targetID 40 // is the ID of the object for assignment. If the method does not return a new 41 // object, then targetID will be nil. The argument list is just what it sounds 42 // like: the list of arguments for the operation. 43 func opArgs(op op) (receiverID *objID, targetID *objID, args []interface{}) { 44 switch t := op.(type) { 45 case *applyOp: 46 return &t.writerID, nil, []interface{}{&t.batchID} 47 case *checkpointOp: 48 return nil, nil, nil 49 case *closeOp: 50 return &t.objID, nil, nil 51 case *compactOp: 52 return nil, nil, []interface{}{&t.start, &t.end, &t.parallelize} 53 case *batchCommitOp: 54 return &t.batchID, nil, nil 55 case *dbRestartOp: 56 return nil, nil, nil 57 case *deleteOp: 58 return &t.writerID, nil, []interface{}{&t.key} 59 case *deleteRangeOp: 60 return &t.writerID, nil, []interface{}{&t.start, &t.end} 61 case *iterFirstOp: 62 return &t.iterID, nil, nil 63 case *flushOp: 64 return nil, nil, nil 65 case *getOp: 66 return &t.readerID, nil, []interface{}{&t.key} 67 case *ingestOp: 68 return nil, nil, []interface{}{&t.batchIDs} 69 case *initOp: 70 return nil, nil, []interface{}{&t.batchSlots, &t.iterSlots, &t.snapshotSlots} 71 case *iterLastOp: 72 return &t.iterID, nil, nil 73 case *mergeOp: 74 return &t.writerID, nil, []interface{}{&t.key, &t.value} 75 case *newBatchOp: 76 return nil, &t.batchID, nil 77 case *newIndexedBatchOp: 78 return nil, &t.batchID, nil 79 case *newIterOp: 80 return &t.readerID, &t.iterID, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.maskSuffix} 81 case *newIterUsingCloneOp: 82 return &t.existingIterID, &t.iterID, []interface{}{&t.refreshBatch, &t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.maskSuffix} 83 case *newSnapshotOp: 84 return nil, &t.snapID, nil 85 case *iterNextOp: 86 return &t.iterID, nil, []interface{}{&t.limit} 87 case *iterPrevOp: 88 return &t.iterID, nil, []interface{}{&t.limit} 89 case *iterSeekLTOp: 90 return &t.iterID, nil, []interface{}{&t.key, &t.limit} 91 case *iterSeekGEOp: 92 return &t.iterID, nil, []interface{}{&t.key, &t.limit} 93 case *iterSeekPrefixGEOp: 94 return &t.iterID, nil, []interface{}{&t.key} 95 case *setOp: 96 return &t.writerID, nil, []interface{}{&t.key, &t.value} 97 case *iterSetBoundsOp: 98 return &t.iterID, nil, []interface{}{&t.lower, &t.upper} 99 case *iterSetOptionsOp: 100 return &t.iterID, nil, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.maskSuffix} 101 case *singleDeleteOp: 102 return &t.writerID, nil, []interface{}{&t.key, &t.maybeReplaceDelete} 103 case *rangeKeyDeleteOp: 104 return &t.writerID, nil, []interface{}{&t.start, &t.end} 105 case *rangeKeySetOp: 106 return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix, &t.value} 107 case *rangeKeyUnsetOp: 108 return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix} 109 } 110 panic(fmt.Sprintf("unsupported op type: %T", op)) 111 } 112 113 var methods = map[string]*methodInfo{ 114 "Apply": makeMethod(applyOp{}, dbTag, batchTag), 115 "Checkpoint": makeMethod(checkpointOp{}, dbTag), 116 "Clone": makeMethod(newIterUsingCloneOp{}, iterTag), 117 "Close": makeMethod(closeOp{}, dbTag, batchTag, iterTag, snapTag), 118 "Commit": makeMethod(batchCommitOp{}, batchTag), 119 "Compact": makeMethod(compactOp{}, dbTag), 120 "Delete": makeMethod(deleteOp{}, dbTag, batchTag), 121 "DeleteRange": makeMethod(deleteRangeOp{}, dbTag, batchTag), 122 "First": makeMethod(iterFirstOp{}, iterTag), 123 "Flush": makeMethod(flushOp{}, dbTag), 124 "Get": makeMethod(getOp{}, dbTag, batchTag, snapTag), 125 "Ingest": makeMethod(ingestOp{}, dbTag), 126 "Init": makeMethod(initOp{}, dbTag), 127 "Last": makeMethod(iterLastOp{}, iterTag), 128 "Merge": makeMethod(mergeOp{}, dbTag, batchTag), 129 "NewBatch": makeMethod(newBatchOp{}, dbTag), 130 "NewIndexedBatch": makeMethod(newIndexedBatchOp{}, dbTag), 131 "NewIter": makeMethod(newIterOp{}, dbTag, batchTag, snapTag), 132 "NewSnapshot": makeMethod(newSnapshotOp{}, dbTag), 133 "Next": makeMethod(iterNextOp{}, iterTag), 134 "Prev": makeMethod(iterPrevOp{}, iterTag), 135 "RangeKeyDelete": makeMethod(rangeKeyDeleteOp{}, dbTag, batchTag), 136 "RangeKeySet": makeMethod(rangeKeySetOp{}, dbTag, batchTag), 137 "RangeKeyUnset": makeMethod(rangeKeyUnsetOp{}, dbTag, batchTag), 138 "Restart": makeMethod(dbRestartOp{}, dbTag), 139 "SeekGE": makeMethod(iterSeekGEOp{}, iterTag), 140 "SeekLT": makeMethod(iterSeekLTOp{}, iterTag), 141 "SeekPrefixGE": makeMethod(iterSeekPrefixGEOp{}, iterTag), 142 "Set": makeMethod(setOp{}, dbTag, batchTag), 143 "SetBounds": makeMethod(iterSetBoundsOp{}, iterTag), 144 "SetOptions": makeMethod(iterSetOptionsOp{}, iterTag), 145 "SingleDelete": makeMethod(singleDeleteOp{}, dbTag, batchTag), 146 } 147 148 type parser struct { 149 fset *token.FileSet 150 s scanner.Scanner 151 objs map[objID]bool 152 } 153 154 func parse(src []byte) (_ []op, err error) { 155 // Various bits of magic incantation to set up a scanner for Go compatible 156 // syntax. We arranged for the textual format of ops (e.g. op.String()) to 157 // look like Go which allows us to use the Go scanner for parsing. 158 p := &parser{ 159 fset: token.NewFileSet(), 160 objs: map[objID]bool{makeObjID(dbTag, 0): true}, 161 } 162 file := p.fset.AddFile("", -1, len(src)) 163 p.s.Init(file, src, nil /* no error handler */, 0) 164 return p.parse() 165 } 166 167 func (p *parser) parse() (_ []op, err error) { 168 defer func() { 169 if r := recover(); r != nil { 170 var ok bool 171 if err, ok = r.(error); ok { 172 return 173 } 174 err = errors.Errorf("%v", r) 175 } 176 }() 177 178 var ops []op 179 for { 180 op := p.parseOp() 181 if op == nil { 182 return ops, nil 183 } 184 ops = append(ops, op) 185 } 186 } 187 188 func (p *parser) parseOp() op { 189 destPos, destTok, destLit := p.s.Scan() 190 if destTok == token.EOF { 191 return nil 192 } 193 if destTok != token.IDENT { 194 panic(p.errorf(destPos, "unexpected token: %s %q", destTok, destLit)) 195 } 196 if destLit == "Init" { 197 // <op>(<args>) 198 return p.makeOp(destLit, makeObjID(dbTag, 0), 0, destPos) 199 } 200 201 destID := p.parseObjID(destPos, destLit) 202 203 pos, tok, lit := p.s.Scan() 204 switch tok { 205 case token.PERIOD: 206 // <obj>.<op>(<args>) 207 if !p.objs[destID] { 208 panic(p.errorf(destPos, "unknown object: %s", destID)) 209 } 210 _, methodLit := p.scanToken(token.IDENT) 211 return p.makeOp(methodLit, destID, 0, destPos) 212 213 case token.ASSIGN: 214 // <obj> = <obj>.<op>(<args>) 215 srcPos, srcLit := p.scanToken(token.IDENT) 216 srcID := p.parseObjID(srcPos, srcLit) 217 if !p.objs[srcID] { 218 panic(p.errorf(srcPos, "unknown object %q", srcLit)) 219 } 220 p.scanToken(token.PERIOD) 221 _, methodLit := p.scanToken(token.IDENT) 222 p.objs[destID] = true 223 return p.makeOp(methodLit, srcID, destID, srcPos) 224 } 225 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit))) 226 } 227 228 func (p *parser) parseObjID(pos token.Pos, str string) objID { 229 var tag objTag 230 switch { 231 case str == "db": 232 return makeObjID(dbTag, 0) 233 case strings.HasPrefix(str, "batch"): 234 tag, str = batchTag, str[5:] 235 case strings.HasPrefix(str, "iter"): 236 tag, str = iterTag, str[4:] 237 case strings.HasPrefix(str, "snap"): 238 tag, str = snapTag, str[4:] 239 default: 240 panic(p.errorf(pos, "unable to parse objectID: %q", str)) 241 } 242 id, err := strconv.ParseInt(str, 10, 32) 243 if err != nil { 244 panic(p.errorf(pos, "%s", err)) 245 } 246 return makeObjID(tag, uint32(id)) 247 } 248 249 func (p *parser) parseArgs(op op, methodName string, args []interface{}) { 250 pos, _ := p.scanToken(token.LPAREN) 251 for i := range args { 252 if i > 0 { 253 pos, _ = p.scanToken(token.COMMA) 254 } 255 256 switch t := args[i].(type) { 257 case *uint32: 258 _, lit := p.scanToken(token.INT) 259 val, err := strconv.ParseUint(lit, 0, 32) 260 if err != nil { 261 panic(err) 262 } 263 *t = uint32(val) 264 265 case *uint64: 266 _, lit := p.scanToken(token.INT) 267 val, err := strconv.ParseUint(lit, 0, 64) 268 if err != nil { 269 panic(err) 270 } 271 *t = uint64(val) 272 273 case *[]byte: 274 _, lit := p.scanToken(token.STRING) 275 s, err := strconv.Unquote(lit) 276 if err != nil { 277 panic(err) 278 } 279 if len(s) == 0 { 280 *t = nil 281 } else { 282 *t = []byte(s) 283 } 284 285 case *bool: 286 _, lit := p.scanToken(token.IDENT) 287 b, err := strconv.ParseBool(lit) 288 if err != nil { 289 panic(err) 290 } 291 *t = b 292 293 case *objID: 294 pos, lit := p.scanToken(token.IDENT) 295 *t = p.parseObjID(pos, lit) 296 297 case *[]objID: 298 for { 299 pos, tok, lit := p.s.Scan() 300 switch tok { 301 case token.IDENT: 302 *t = append(*t, p.parseObjID(pos, lit)) 303 pos, tok, lit := p.s.Scan() 304 switch tok { 305 case token.COMMA: 306 continue 307 case token.RPAREN: 308 p.scanToken(token.SEMICOLON) 309 return 310 default: 311 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit))) 312 } 313 case token.RPAREN: 314 p.scanToken(token.SEMICOLON) 315 return 316 default: 317 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit))) 318 } 319 } 320 321 default: 322 panic(p.errorf(pos, "%s: unsupported arg[%d] type: %T", methodName, i, args[i])) 323 } 324 } 325 p.scanToken(token.RPAREN) 326 p.scanToken(token.SEMICOLON) 327 } 328 329 func (p *parser) scanToken(expected token.Token) (pos token.Pos, lit string) { 330 pos, tok, lit := p.s.Scan() 331 if tok != expected { 332 panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit))) 333 } 334 return pos, lit 335 } 336 337 func (p *parser) makeOp(methodName string, receiverID, targetID objID, pos token.Pos) op { 338 info := methods[methodName] 339 if info == nil { 340 panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName)) 341 } 342 if info.validTags&(1<<receiverID.tag()) == 0 { 343 panic(p.errorf(pos, "%s.%s: %s is not a method on %s", 344 receiverID, methodName, methodName, receiverID)) 345 } 346 347 op := info.constructor() 348 receiver, target, args := opArgs(op) 349 350 // The form of an operation is: 351 // [target =] receiver.method(args) 352 // 353 // The receiver is the object the operation will be called on, which can be 354 // any valid ID. Certain operations such as Ingest are only valid on the DB 355 // object. That is indicated by opArgs returning a nil receiver. 356 if receiver != nil { 357 *receiver = receiverID 358 } else if receiverID.tag() != dbTag { 359 panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName)) 360 } 361 362 // The target is the object that will be assigned the result of an object 363 // creation operation such as newBatchOp or newIterOp. 364 if target != nil { 365 // It is invalid to not have a targetID for a method which generates a new 366 // object. 367 if targetID == 0 { 368 panic(p.errorf(pos, "assignment expected for %s.%s", receiverID, methodName)) 369 } 370 // It is invalid to try to assign to the DB object. 371 if targetID.tag() == dbTag { 372 panic(p.errorf(pos, "cannot use %s as target of assignment", targetID)) 373 } 374 *target = targetID 375 } else if targetID != 0 { 376 panic(p.errorf(pos, "cannot use %s.%s in assignment", receiverID, methodName)) 377 } 378 379 p.parseArgs(op, methodName, args) 380 return op 381 } 382 383 func (p *parser) tokenf(tok token.Token, lit string) string { 384 if tok.IsLiteral() { 385 return lit 386 } 387 return tok.String() 388 } 389 390 func (p *parser) errorf(pos token.Pos, format string, args ...interface{}) error { 391 return errors.New(p.fset.Position(pos).String() + ": " + fmt.Sprintf(format, args...)) 392 }