github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/parser.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package jsonpath 18 19 import ( 20 "fmt" 21 "regexp" 22 "strconv" 23 "strings" 24 "unicode" 25 "unicode/utf8" 26 ) 27 28 const eof = -1 29 30 const ( 31 leftDelim = "{" 32 rightDelim = "}" 33 ) 34 35 type Parser struct { 36 Name string 37 Root *ListNode 38 input string 39 cur *ListNode 40 pos int 41 start int 42 width int 43 } 44 45 // Parse parsed the given text and return a node Parser. 46 // If an error is encountered, parsing stops and an empty 47 // Parser is returned with the error 48 func Parse(name, text string) (*Parser, error) { 49 p := NewParser(name) 50 err := p.Parse(text) 51 if err != nil { 52 p = nil 53 } 54 return p, err 55 } 56 57 func NewParser(name string) *Parser { 58 return &Parser{ 59 Name: name, 60 } 61 } 62 63 // parseAction parsed the expression inside delimiter 64 func parseAction(name, text string) (*Parser, error) { 65 p, err := Parse(name, fmt.Sprintf("%s%s%s", leftDelim, text, rightDelim)) 66 // when error happens, p will be nil, so we need to return here 67 if err != nil { 68 return p, err 69 } 70 p.Root = p.Root.Nodes[0].(*ListNode) 71 return p, nil 72 } 73 74 func (p *Parser) Parse(text string) error { 75 p.input = text 76 p.Root = newList() 77 p.pos = 0 78 return p.parseText(p.Root) 79 } 80 81 // consumeText return the parsed text since last cosumeText 82 func (p *Parser) consumeText() string { 83 value := p.input[p.start:p.pos] 84 p.start = p.pos 85 return value 86 } 87 88 // next returns the next rune in the input. 89 func (p *Parser) next() rune { 90 if int(p.pos) >= len(p.input) { 91 p.width = 0 92 return eof 93 } 94 r, w := utf8.DecodeRuneInString(p.input[p.pos:]) 95 p.width = w 96 p.pos += p.width 97 return r 98 } 99 100 // peek returns but does not consume the next rune in the input. 101 func (p *Parser) peek() rune { 102 r := p.next() 103 p.backup() 104 return r 105 } 106 107 // backup steps back one rune. Can only be called once per call of next. 108 func (p *Parser) backup() { 109 p.pos -= p.width 110 } 111 112 func (p *Parser) parseText(cur *ListNode) error { 113 for { 114 if strings.HasPrefix(p.input[p.pos:], leftDelim) { 115 if p.pos > p.start { 116 cur.append(newText(p.consumeText())) 117 } 118 return p.parseLeftDelim(cur) 119 } 120 if p.next() == eof { 121 break 122 } 123 } 124 // Correctly reached EOF. 125 if p.pos > p.start { 126 cur.append(newText(p.consumeText())) 127 } 128 return nil 129 } 130 131 // parseLeftDelim scans the left delimiter, which is known to be present. 132 func (p *Parser) parseLeftDelim(cur *ListNode) error { 133 p.pos += len(leftDelim) 134 p.consumeText() 135 newNode := newList() 136 cur.append(newNode) 137 cur = newNode 138 return p.parseInsideAction(cur) 139 } 140 141 func (p *Parser) parseInsideAction(cur *ListNode) error { 142 prefixMap := map[string]func(*ListNode) error{ 143 rightDelim: p.parseRightDelim, 144 "[?(": p.parseFilter, 145 "..": p.parseRecursive, 146 } 147 for prefix, parseFunc := range prefixMap { 148 if strings.HasPrefix(p.input[p.pos:], prefix) { 149 return parseFunc(cur) 150 } 151 } 152 153 switch r := p.next(); { 154 case r == eof || isEndOfLine(r): 155 return fmt.Errorf("unclosed action") 156 case r == ' ': 157 p.consumeText() 158 case r == '@' || r == '$': //the current object, just pass it 159 p.consumeText() 160 case r == '[': 161 return p.parseArray(cur) 162 case r == '"': 163 return p.parseQuote(cur) 164 case r == '.': 165 return p.parseField(cur) 166 case r == '+' || r == '-' || unicode.IsDigit(r): 167 p.backup() 168 return p.parseNumber(cur) 169 case isAlphaNumeric(r): 170 p.backup() 171 return p.parseIdentifier(cur) 172 default: 173 return fmt.Errorf("unrecognized character in action: %#U", r) 174 } 175 return p.parseInsideAction(cur) 176 } 177 178 // parseRightDelim scans the right delimiter, which is known to be present. 179 func (p *Parser) parseRightDelim(cur *ListNode) error { 180 p.pos += len(rightDelim) 181 p.consumeText() 182 cur = p.Root 183 return p.parseText(cur) 184 } 185 186 // parseIdentifier scans build-in keywords, like "range" "end" 187 func (p *Parser) parseIdentifier(cur *ListNode) error { 188 var r rune 189 for { 190 r = p.next() 191 if isTerminator(r) { 192 p.backup() 193 break 194 } 195 } 196 value := p.consumeText() 197 cur.append(newIdentifier(value)) 198 return p.parseInsideAction(cur) 199 } 200 201 // parseRecursive scans the recursive desent operator .. 202 func (p *Parser) parseRecursive(cur *ListNode) error { 203 p.pos += len("..") 204 p.consumeText() 205 cur.append(newRecursive()) 206 if r := p.peek(); isAlphaNumeric(r) { 207 return p.parseField(cur) 208 } 209 return p.parseInsideAction(cur) 210 } 211 212 // parseNumber scans number 213 func (p *Parser) parseNumber(cur *ListNode) error { 214 r := p.peek() 215 if r == '+' || r == '-' { 216 r = p.next() 217 } 218 for { 219 r = p.next() 220 if r != '.' && !unicode.IsDigit(r) { 221 p.backup() 222 break 223 } 224 } 225 value := p.consumeText() 226 i, err := strconv.Atoi(value) 227 if err == nil { 228 cur.append(newInt(i)) 229 return p.parseInsideAction(cur) 230 } 231 d, err := strconv.ParseFloat(value, 64) 232 if err == nil { 233 cur.append(newFloat(d)) 234 return p.parseInsideAction(cur) 235 } 236 return fmt.Errorf("cannot parse number %s", value) 237 } 238 239 // parseArray scans array index selection 240 func (p *Parser) parseArray(cur *ListNode) error { 241 Loop: 242 for { 243 switch p.next() { 244 case eof, '\n': 245 return fmt.Errorf("unterminated array") 246 case ']': 247 break Loop 248 } 249 } 250 text := p.consumeText() 251 text = string(text[1 : len(text)-1]) 252 if text == "*" { 253 text = ":" 254 } 255 256 //union operator 257 strs := strings.Split(text, ",") 258 if len(strs) > 1 { 259 union := []*ListNode{} 260 for _, str := range strs { 261 parser, err := parseAction("union", fmt.Sprintf("[%s]", strings.Trim(str, " "))) 262 if err != nil { 263 return err 264 } 265 union = append(union, parser.Root) 266 } 267 cur.append(newUnion(union)) 268 return p.parseInsideAction(cur) 269 } 270 271 // dict key 272 reg := regexp.MustCompile(`^'([^']*)'$`) 273 value := reg.FindStringSubmatch(text) 274 if value != nil { 275 parser, err := parseAction("arraydict", fmt.Sprintf(".%s", value[1])) 276 if err != nil { 277 return err 278 } 279 for _, node := range parser.Root.Nodes { 280 cur.append(node) 281 } 282 return p.parseInsideAction(cur) 283 } 284 285 //slice operator 286 reg = regexp.MustCompile(`^(-?[\d]*)(:-?[\d]*)?(:[\d]*)?$`) 287 value = reg.FindStringSubmatch(text) 288 if value == nil { 289 return fmt.Errorf("invalid array index %s", text) 290 } 291 value = value[1:] 292 params := [3]ParamsEntry{} 293 for i := 0; i < 3; i++ { 294 if value[i] != "" { 295 if i > 0 { 296 value[i] = value[i][1:] 297 } 298 if i > 0 && value[i] == "" { 299 params[i].Known = false 300 } else { 301 var err error 302 params[i].Known = true 303 params[i].Value, err = strconv.Atoi(value[i]) 304 if err != nil { 305 return fmt.Errorf("array index %s is not a number", value[i]) 306 } 307 } 308 } else { 309 if i == 1 { 310 params[i].Known = true 311 params[i].Value = params[0].Value + 1 312 } else { 313 params[i].Known = false 314 params[i].Value = 0 315 } 316 } 317 } 318 cur.append(newArray(params)) 319 return p.parseInsideAction(cur) 320 } 321 322 // parseFilter scans filter inside array selection 323 func (p *Parser) parseFilter(cur *ListNode) error { 324 p.pos += len("[?(") 325 p.consumeText() 326 Loop: 327 for { 328 switch p.next() { 329 case eof, '\n': 330 return fmt.Errorf("unterminated filter") 331 case ')': 332 break Loop 333 } 334 } 335 if p.next() != ']' { 336 return fmt.Errorf("unclosed array expect ]") 337 } 338 reg := regexp.MustCompile(`^([^!<>=]+)([!<>=]+)(.+?)$`) 339 text := p.consumeText() 340 text = string(text[:len(text)-2]) 341 value := reg.FindStringSubmatch(text) 342 if value == nil { 343 parser, err := parseAction("text", text) 344 if err != nil { 345 return err 346 } 347 cur.append(newFilter(parser.Root, newList(), "exists")) 348 } else { 349 leftParser, err := parseAction("left", value[1]) 350 if err != nil { 351 return err 352 } 353 rightParser, err := parseAction("right", value[3]) 354 if err != nil { 355 return err 356 } 357 cur.append(newFilter(leftParser.Root, rightParser.Root, value[2])) 358 } 359 return p.parseInsideAction(cur) 360 } 361 362 // parseQuote unquotes string inside double quote 363 func (p *Parser) parseQuote(cur *ListNode) error { 364 Loop: 365 for { 366 switch p.next() { 367 case eof, '\n': 368 return fmt.Errorf("unterminated quoted string") 369 case '"': 370 break Loop 371 } 372 } 373 value := p.consumeText() 374 s, err := strconv.Unquote(value) 375 if err != nil { 376 return fmt.Errorf("unquote string %s error %v", value, err) 377 } 378 cur.append(newText(s)) 379 return p.parseInsideAction(cur) 380 } 381 382 // parseField scans a field until a terminator 383 func (p *Parser) parseField(cur *ListNode) error { 384 p.consumeText() 385 var r rune 386 for { 387 r = p.next() 388 if isTerminator(r) { 389 p.backup() 390 break 391 } 392 } 393 value := p.consumeText() 394 if value == "*" { 395 cur.append(newWildcard()) 396 } else { 397 cur.append(newField(value)) 398 } 399 return p.parseInsideAction(cur) 400 } 401 402 // isTerminator reports whether the input is at valid termination character to appear after an identifier. 403 func isTerminator(r rune) bool { 404 if isSpace(r) || isEndOfLine(r) { 405 return true 406 } 407 switch r { 408 case eof, '.', ',', '[', ']', '$', '@', '{', '}': 409 return true 410 } 411 return false 412 } 413 414 // isSpace reports whether r is a space character. 415 func isSpace(r rune) bool { 416 return r == ' ' || r == '\t' 417 } 418 419 // isEndOfLine reports whether r is an end-of-line character. 420 func isEndOfLine(r rune) bool { 421 return r == '\r' || r == '\n' 422 } 423 424 // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. 425 func isAlphaNumeric(r rune) bool { 426 return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) 427 }