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  }