github.com/attic-labs/noms@v0.0.0-20210827224422-e5fa29d95e8b/samples/go/nomdex/parser.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 main
     6  
     7  import (
     8  	"fmt"
     9  	"strconv"
    10  	"strings"
    11  	"text/scanner"
    12  	"unicode"
    13  
    14  	"github.com/attic-labs/noms/go/d"
    15  	"github.com/attic-labs/noms/go/datas"
    16  	"github.com/attic-labs/noms/go/types"
    17  )
    18  
    19  /**** Query language BNF
    20    query := expr
    21    expr := expr boolOp compExpr | group
    22    compExpr := indexToken compOp value
    23    group := '(' expr ')' | compExpr
    24    boolOp := 'and' | 'or'
    25    compOp := '=' | '<' | '<=' | '>' | '>=' | !=
    26    value := "<string>" | number
    27    number := '-' digits | digits
    28    digits := int | float
    29  
    30  */
    31  
    32  type compOp string
    33  type boolOp string
    34  
    35  type indexManager struct {
    36  	db      datas.Database
    37  	indexes map[string]types.Map
    38  }
    39  
    40  const (
    41  	equals compOp = "="
    42  	gt     compOp = ">"
    43  	gte    compOp = ">="
    44  	lt     compOp = "<"
    45  	lte    compOp = "<="
    46  	ne     compOp = "!="
    47  	openP         = "("
    48  	closeP        = ")"
    49  	and    boolOp = "and"
    50  	or     boolOp = "or"
    51  )
    52  
    53  var (
    54  	compOps = []compOp{equals, gt, gte, lt, lte, ne}
    55  	boolOps = []boolOp{and, or}
    56  )
    57  
    58  type qScanner struct {
    59  	s           scanner.Scanner
    60  	peekedToken rune
    61  	peekedText  string
    62  	peeked      bool
    63  }
    64  
    65  func (qs *qScanner) Scan() rune {
    66  	var r rune
    67  	if qs.peeked {
    68  		r = qs.peekedToken
    69  		qs.peeked = false
    70  	} else {
    71  		r = qs.s.Scan()
    72  	}
    73  	return r
    74  }
    75  
    76  func (qs *qScanner) Peek() rune {
    77  	var r rune
    78  
    79  	if !qs.peeked {
    80  		qs.peekedToken = qs.s.Scan()
    81  		qs.peekedText = qs.s.TokenText()
    82  		qs.peeked = true
    83  	}
    84  	r = qs.peekedToken
    85  	return r
    86  }
    87  
    88  func (qs *qScanner) TokenText() string {
    89  	var text string
    90  	if qs.peeked {
    91  		text = qs.peekedText
    92  	} else {
    93  		text = qs.s.TokenText()
    94  	}
    95  	return text
    96  }
    97  
    98  func (qs *qScanner) Pos() scanner.Position {
    99  	return qs.s.Pos()
   100  }
   101  
   102  func parseQuery(q string, im *indexManager) (expr, error) {
   103  	s := NewQueryScanner(q)
   104  	var expr expr
   105  	err := d.Try(func() {
   106  		expr = s.parseExpr(0, im)
   107  	})
   108  	return expr, err
   109  }
   110  
   111  func NewQueryScanner(query string) *qScanner {
   112  	isIdentRune := func(r rune, i int) bool {
   113  		identChars := ":/.>=-"
   114  		startIdentChars := "!><"
   115  		if i == 0 {
   116  			return unicode.IsLetter(r) || strings.ContainsRune(startIdentChars, r)
   117  		}
   118  		return unicode.IsLetter(r) || unicode.IsDigit(r) || strings.ContainsRune(identChars, r)
   119  	}
   120  
   121  	errorFunc := func(s *scanner.Scanner, msg string) {
   122  		d.PanicIfError(fmt.Errorf("%s, pos: %s\n", msg, s.Pos()))
   123  	}
   124  
   125  	var s scanner.Scanner
   126  	s.Mode = scanner.ScanIdents | scanner.ScanFloats | scanner.ScanStrings | scanner.SkipComments
   127  	s.Init(strings.NewReader(query))
   128  	s.IsIdentRune = isIdentRune
   129  	s.Error = errorFunc
   130  	qs := qScanner{s: s}
   131  	return &qs
   132  }
   133  
   134  func (qs *qScanner) parseExpr(level int, im *indexManager) expr {
   135  	tok := qs.Scan()
   136  	switch tok {
   137  	case '(':
   138  		expr1 := qs.parseExpr(level+1, im)
   139  		tok := qs.Scan()
   140  		if tok != ')' {
   141  			d.PanicIfError(fmt.Errorf("missing ending paren for expr"))
   142  		} else {
   143  			tok = qs.Peek()
   144  			if tok == ')' {
   145  				return expr1
   146  			}
   147  			tok = qs.Scan()
   148  			text := qs.TokenText()
   149  			switch {
   150  			case tok == scanner.Ident && isBoolOp(text):
   151  				op := boolOp(text)
   152  				expr2 := qs.parseExpr(level+1, im)
   153  				return logExpr{op: op, expr1: expr1, expr2: expr2, idxName: idxNameIfSame(expr1, expr2)}
   154  			case tok == scanner.EOF:
   155  				return expr1
   156  			default:
   157  				d.PanicIfError(fmt.Errorf("extra text found at end of expr, tok: %d, text: %s", int(tok), qs.TokenText()))
   158  			}
   159  		}
   160  	case scanner.Ident:
   161  		err := openIndex(qs.TokenText(), im)
   162  		d.PanicIfError(err)
   163  		expr1 := qs.parseCompExpr(level+1, qs.TokenText(), im)
   164  		tok := qs.Peek()
   165  		switch tok {
   166  		case ')':
   167  			return expr1
   168  		case rune(scanner.Ident):
   169  			_ = qs.Scan()
   170  			text := qs.TokenText()
   171  			if isBoolOp(text) {
   172  				op := boolOp(text)
   173  				expr2 := qs.parseExpr(level+1, im)
   174  				return logExpr{op: op, expr1: expr1, expr2: expr2, idxName: idxNameIfSame(expr1, expr2)}
   175  			} else {
   176  				d.PanicIfError(fmt.Errorf("expected boolean op, found: %s, level: %d", text, level))
   177  			}
   178  		case rune(scanner.EOF):
   179  			return expr1
   180  		default:
   181  			_ = qs.Scan()
   182  		}
   183  	default:
   184  		d.PanicIfError(fmt.Errorf("unexpected token in expr: %s, %d", qs.TokenText(), tok))
   185  	}
   186  	return logExpr{}
   187  }
   188  
   189  func (qs *qScanner) parseCompExpr(level int, indexName string, im *indexManager) compExpr {
   190  	qs.Scan()
   191  	text := qs.TokenText()
   192  	if !isCompOp(text) {
   193  		d.PanicIfError(fmt.Errorf("expected relop token but found: '%s'", text))
   194  	}
   195  	op := compOp(text)
   196  	value := qs.parseValExpr()
   197  	return compExpr{indexName, op, value}
   198  }
   199  
   200  func (qs *qScanner) parseValExpr() types.Value {
   201  	tok := qs.Scan()
   202  	text := qs.TokenText()
   203  	isNeg := false
   204  	if tok == '-' {
   205  		isNeg = true
   206  		tok = qs.Scan()
   207  		text = qs.TokenText()
   208  	}
   209  	switch tok {
   210  	case scanner.String:
   211  		if isNeg {
   212  			d.PanicIfError(fmt.Errorf("expected number after '-', found string: %s", text))
   213  		}
   214  		return valueFromString(text)
   215  	case scanner.Float:
   216  		f, _ := strconv.ParseFloat(text, 64)
   217  		if isNeg {
   218  			f = -f
   219  		}
   220  		return types.Number(f)
   221  	case scanner.Int:
   222  		i, _ := strconv.ParseInt(text, 10, 64)
   223  		if isNeg {
   224  			i = -i
   225  		}
   226  		return types.Number(i)
   227  	}
   228  	d.PanicIfError(fmt.Errorf("expected value token, found: '%s'", text))
   229  	return nil // for compiler
   230  }
   231  
   232  func valueFromString(t string) types.Value {
   233  	l := len(t)
   234  	if l < 2 && t[0] == '"' && t[l-1] == '"' {
   235  		d.PanicIfError(fmt.Errorf("Unable to get value from token: %s", t))
   236  	}
   237  	return types.String(t[1 : l-1])
   238  }
   239  
   240  func isCompOp(s string) bool {
   241  	for _, op := range compOps {
   242  		if s == string(op) {
   243  			return true
   244  		}
   245  	}
   246  	return false
   247  }
   248  
   249  func isBoolOp(s string) bool {
   250  	for _, op := range boolOps {
   251  		if s == string(op) {
   252  			return true
   253  		}
   254  	}
   255  	return false
   256  }
   257  
   258  func idxNameIfSame(expr1, expr2 expr) string {
   259  	if expr1.indexName() == expr2.indexName() {
   260  		return expr1.indexName()
   261  	}
   262  	return ""
   263  }