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  }