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 }