github.com/GuanceCloud/cliutils@v1.1.21/filter/parse.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 package filter 7 8 import ( 9 "errors" 10 "fmt" 11 "os" 12 "reflect" 13 "regexp" 14 "runtime" 15 "strconv" 16 "strings" 17 "sync" 18 19 "github.com/GuanceCloud/cliutils/logger" 20 "github.com/prometheus/prometheus/util/strutil" 21 ) 22 23 var ( 24 parserPool = sync.Pool{ 25 New: func() interface{} { 26 return &parser{} 27 }, 28 } 29 30 log = logger.DefaultSLogger("filter-parser") 31 ) 32 33 type parser struct { 34 lex Lexer 35 yyParser yyParserImpl 36 37 parseResult interface{} 38 lastClosing Pos 39 errs ParseErrors 40 warns ParseErrors 41 42 inject ItemType 43 injecting bool 44 } 45 46 func GetConds(input string) (WhereConditions, error) { 47 var err error 48 p := newParser(input) 49 defer parserPool.Put(p) 50 defer p.recover(&err) 51 52 p.doParse() 53 54 if len(p.errs) > 0 { 55 return nil, &p.errs[0] 56 } 57 58 return p.parseResult.(WhereConditions), nil 59 } 60 61 func newParser(input string) *parser { 62 p, ok := parserPool.Get().(*parser) 63 if !ok { 64 log.Fatal("parserPool: should not been here") 65 } 66 67 // reset parser fields 68 p.injecting = false 69 p.errs = nil 70 p.warns = nil 71 p.parseResult = nil 72 p.lex = Lexer{ 73 input: input, 74 state: lexStatements, 75 } 76 77 return p 78 } 79 80 var errUnexpected = errors.New("unexpected error") 81 82 func (p *parser) unexpected(context string, expected string) { 83 var errMsg strings.Builder 84 85 if p.yyParser.lval.item.Typ == ERROR { // do not report lex error twice 86 return 87 } 88 89 errMsg.WriteString("unexpected: ") 90 errMsg.WriteString(p.yyParser.lval.item.desc()) 91 92 if context != "" { 93 errMsg.WriteString(" in: ") 94 errMsg.WriteString(context) 95 } 96 97 if expected != "" { 98 errMsg.WriteString(", expected: ") 99 errMsg.WriteString(expected) 100 } 101 102 p.addParseErr(p.yyParser.lval.item.PositionRange(), errors.New(errMsg.String())) 103 } 104 105 func (p *parser) recover(errp *error) { 106 e := recover() //nolint:ifshort 107 if _, ok := e.(runtime.Error); ok { 108 buf := make([]byte, 64<<10) // 64k 109 buf = buf[:runtime.Stack(buf, false)] 110 fmt.Fprintf(os.Stderr, "parser panic: %v\n%s", e, buf) 111 *errp = errUnexpected 112 } else if e != nil { 113 *errp = e.(error) //nolint:forcetypeassert 114 } 115 } 116 117 func (p *parser) addParseErr(pr *PositionRange, err error) { 118 p.errs = append(p.errs, ParseError{ 119 Pos: pr, 120 Err: err, 121 Query: p.lex.input, 122 }) 123 } 124 125 func (p *parser) addParseWarn(pr *PositionRange, err error) { 126 p.warns = append(p.warns, ParseError{ 127 Pos: pr, 128 Err: err, 129 Query: p.lex.input, 130 }) 131 } 132 133 func (p *parser) addParseErrf(pr *PositionRange, format string, args ...interface{}) { 134 p.addParseErr(pr, fmt.Errorf(format, args...)) 135 } 136 137 func (p *parser) addParseWarnf(pr *PositionRange, format string, args ...interface{}) { 138 p.addParseWarn(pr, fmt.Errorf(format, args...)) 139 } 140 141 // impl Lex interface. 142 func (p *parser) Lex(lval *yySymType) int { 143 var typ ItemType 144 145 if p.injecting { 146 p.injecting = false 147 return int(p.inject) 148 } 149 150 for { // skip comment 151 p.lex.NextItem(&lval.item) 152 typ = lval.item.Typ 153 if typ != COMMENT { 154 break 155 } 156 } 157 158 switch typ { 159 case ERROR: 160 pos := PositionRange{ 161 Start: p.lex.start, 162 End: Pos(len(p.lex.input)), 163 } 164 165 p.addParseErr(&pos, errors.New(p.yyParser.lval.item.Val)) 166 return 0 // tell yacc it's the end of input 167 168 case EOF: 169 lval.item.Typ = EOF 170 p.InjectItem(0) 171 case RIGHT_BRACE, RIGHT_PAREN, RIGHT_BRACKET, DURATION: 172 p.lastClosing = lval.item.Pos + Pos(len(lval.item.Val)) 173 } 174 return int(typ) 175 } 176 177 func (p *parser) Error(e string) {} 178 179 func (p *parser) unquoteString(s string) string { 180 unq, err := strutil.Unquote(s) 181 if err != nil { 182 p.addParseErrf(p.yyParser.lval.item.PositionRange(), 183 "error unquoting string %q: %s", s, err) 184 } 185 return unq 186 } 187 188 func (p *parser) doParse() { 189 p.InjectItem(START_WHERE_CONDITION) 190 p.yyParser.Parse(p) 191 } 192 193 func (p *parser) InjectItem(typ ItemType) { 194 if p.injecting { 195 log.Warnf("current inject is %v, new inject is %v", p.inject, typ) 196 panic("cannot inject multiple Items into the token stream") 197 } 198 199 if typ != 0 && (typ <= startSymbolsStart || typ >= startSymbolsEnd) { 200 log.Warnf("current inject is %v", typ) 201 panic("cannot inject symbol that isn't start symbol") 202 } 203 p.inject = typ 204 p.injecting = true 205 } 206 207 func (p *parser) number(v string) *NumberLiteral { 208 nl := &NumberLiteral{} 209 210 n, err := strconv.ParseInt(v, 0, 64) 211 if err != nil { 212 f, err := strconv.ParseFloat(v, 64) 213 if err != nil { 214 p.addParseErrf(p.yyParser.lval.item.PositionRange(), 215 "error parsing number: %s", err) 216 } 217 nl.Float = f 218 } else { 219 nl.IsInt = true 220 nl.Int = n 221 } 222 223 return nl 224 } 225 226 func doNewRegex(s string) (*Regex, error) { 227 re, err := regexp.Compile(s) 228 if err != nil { 229 return nil, err 230 } 231 return &Regex{ 232 Regex: s, 233 Re: re, 234 }, nil 235 } 236 237 func (p *parser) newRegex(s string) *Regex { 238 if x, err := doNewRegex(s); err != nil { 239 p.addParseWarnf(p.yyParser.lval.item.PositionRange(), 240 "invalid regex: %s: %s, ignored", err.Error(), s) 241 return nil 242 } else { 243 return x 244 } 245 } 246 247 func (p *parser) newBinExpr(l, r Node, op Item) *BinaryExpr { 248 switch op.Typ { 249 case DIV, MOD: 250 rightNumber, ok := r.(*NumberLiteral) 251 if ok { 252 if rightNumber.IsInt && rightNumber.Int == 0 || 253 !rightNumber.IsInt && rightNumber.Float == 0 { 254 p.addParseErrf(p.yyParser.lval.item.PositionRange(), "division or modulo by zero") 255 return nil 256 } 257 } 258 259 case MATCH, NOT_MATCH: // convert rhs into regex list 260 switch nl := r.(type) { 261 case NodeList: 262 // convert elems in @n into Regex node, used in CONTAIN/NOTCONTAIN 263 var regexArr NodeList 264 for _, elem := range nl { 265 switch x := elem.(type) { 266 case *StringLiteral: 267 if re := p.newRegex(x.Val); re != nil { 268 regexArr = append(regexArr, re) 269 } 270 271 default: 272 p.addParseErrf(p.yyParser.lval.item.PositionRange(), 273 "invalid element type in CONTAIN/NOT_CONTAIN list: %s", reflect.TypeOf(elem).String()) 274 } 275 } 276 return &BinaryExpr{LHS: l, RHS: regexArr, Op: op.Typ} 277 278 default: 279 p.addParseErrf(p.yyParser.lval.item.PositionRange(), 280 "invalid type in CONTAIN/NOT_CONTAIN list: %s(%s)", reflect.TypeOf(l).String(), l.String()) 281 } 282 } 283 284 return &BinaryExpr{RHS: r, LHS: l, Op: op.Typ} 285 } 286 287 func (p *parser) newFunc(fname string, args []Node) *FuncExpr { 288 agg := &FuncExpr{ 289 Name: strings.ToLower(fname), 290 Param: args, 291 } 292 return agg 293 } 294 295 // end of yylex.(*parser).newXXXX 296 297 type ParseErrors []ParseError 298 299 type ParseError struct { 300 Pos *PositionRange 301 Err error 302 Query string 303 LineOffset int 304 } 305 306 func (e *ParseError) Error() string { 307 if e.Pos == nil { 308 return fmt.Sprintf("%s", e.Err) 309 } 310 311 pos := int(e.Pos.Start) 312 lastLineBrk := -1 313 ln := e.LineOffset + 1 314 var posStr string 315 316 if pos < 0 || pos > len(e.Query) { 317 posStr = "invalid position:" 318 } else { 319 for i, c := range e.Query[:pos] { 320 if c == '\n' { 321 lastLineBrk = i 322 ln++ 323 } 324 } 325 326 col := pos - lastLineBrk 327 posStr = fmt.Sprintf("%d:%d", ln, col) 328 } 329 330 return fmt.Sprintf("%s parse error: %s", posStr, e.Err) 331 } 332 333 // Error impl Error() interface. 334 func (errs ParseErrors) Error() string { 335 var errArray []string 336 for _, err := range errs { 337 errStr := err.Error() 338 if errStr != "" { 339 errArray = append(errArray, errStr) 340 } 341 } 342 343 return strings.Join(errArray, "\n") 344 } 345 346 type PositionRange struct { 347 Start, End Pos 348 } 349 350 func (p *parser) newWhereConditions(conditions []Node) *WhereCondition { 351 return &WhereCondition{ 352 conditions: conditions, 353 } 354 } 355 356 func Init() { 357 log = logger.SLogger("filter-parser") 358 }