1
0
mirror of https://github.com/openshift/installer.git synced 2026-02-06 00:48:45 +01:00
Files
Sid Shukla 345e855c90 Update Nutanix terraform version to 1.5.0 for installer
The updated versions include bugfixes for various components.
2022-05-23 11:52:08 +02:00

205 lines
4.4 KiB
Go

package jsonpath
import (
"context"
"fmt"
"math"
"text/scanner"
"github.com/PaesslerAG/gval"
)
type parser struct {
*gval.Parser
path path
}
func parseRootPath(ctx context.Context, gParser *gval.Parser) (r gval.Evaluable, err error) {
p := newParser(gParser)
return p.parse(ctx)
}
func parseCurrentPath(ctx context.Context, gParser *gval.Parser) (r gval.Evaluable, err error) {
p := newParser(gParser)
p.appendPlainSelector(currentElementSelector())
return p.parse(ctx)
}
func newParser(p *gval.Parser) *parser {
return &parser{Parser: p, path: plainPath{}}
}
func (p *parser) parse(c context.Context) (r gval.Evaluable, err error) {
err = p.parsePath(c)
if err != nil {
return nil, err
}
return p.path.evaluate, nil
}
func (p *parser) parsePath(c context.Context) error {
switch p.Scan() {
case '.':
return p.parseSelect(c)
case '[':
keys, seperator, err := p.parseBracket(c)
if err != nil {
return err
}
switch seperator {
case ':':
if len(keys) > 3 {
return fmt.Errorf("range query has at least the parameter [min:max:step]")
}
keys = append(keys, []gval.Evaluable{
p.Const(0), p.Const(float64(math.MaxInt32)), p.Const(1)}[len(keys):]...)
p.appendAmbiguousSelector(rangeSelector(keys[0], keys[1], keys[2]))
case '?':
if len(keys) != 1 {
return fmt.Errorf("filter needs exactly one key")
}
p.appendAmbiguousSelector(filterSelector(keys[0]))
default:
if len(keys) == 1 {
p.appendPlainSelector(directSelector(keys[0]))
} else {
p.appendAmbiguousSelector(multiSelector(keys))
}
}
return p.parsePath(c)
case '(':
return p.parseScript(c)
default:
p.Camouflage("jsonpath", '.', '[', '(')
return nil
}
}
func (p *parser) parseSelect(c context.Context) error {
scan := p.Scan()
switch scan {
case scanner.Ident:
p.appendPlainSelector(directSelector(p.Const(p.TokenText())))
return p.parsePath(c)
case '.':
p.appendAmbiguousSelector(mapperSelector())
return p.parseMapper(c)
case '*':
p.appendAmbiguousSelector(starSelector())
return p.parsePath(c)
default:
return p.Expected("JSON select", scanner.Ident, '.', '*')
}
}
func (p *parser) parseBracket(c context.Context) (keys []gval.Evaluable, seperator rune, err error) {
for {
scan := p.Scan()
skipScan := false
switch scan {
case '?':
skipScan = true
case ':':
i := float64(0)
if len(keys) == 1 {
i = math.MaxInt32
}
keys = append(keys, p.Const(i))
skipScan = true
case '*':
if p.Scan() != ']' {
return nil, 0, p.Expected("JSON bracket star", ']')
}
return []gval.Evaluable{}, 0, nil
case ']':
if seperator == ':' {
skipScan = true
break
}
fallthrough
default:
p.Camouflage("jsonpath brackets")
key, err := p.ParseExpression(c)
if err != nil {
return nil, 0, err
}
keys = append(keys, key)
}
if !skipScan {
scan = p.Scan()
}
if seperator == 0 {
seperator = scan
}
switch scan {
case ':', ',':
case ']':
return
case '?':
if len(keys) != 0 {
return nil, 0, p.Expected("JSON filter", ']')
}
default:
return nil, 0, p.Expected("JSON bracket separator", ':', ',')
}
if seperator != scan {
return nil, 0, fmt.Errorf("mixed %v and %v in JSON bracket", seperator, scan)
}
}
}
func (p *parser) parseMapper(c context.Context) error {
scan := p.Scan()
switch scan {
case scanner.Ident:
p.appendPlainSelector(directSelector(p.Const(p.TokenText())))
case '[':
keys, seperator, err := p.parseBracket(c)
if err != nil {
return err
}
switch seperator {
case ':':
return fmt.Errorf("mapper can not be combined with range query")
case '?':
if len(keys) != 1 {
return fmt.Errorf("filter needs exactly one key")
}
p.appendAmbiguousSelector(filterSelector(keys[0]))
default:
p.appendAmbiguousSelector(multiSelector(keys))
}
case '*':
p.appendAmbiguousSelector(starSelector())
case '(':
return p.parseScript(c)
default:
return p.Expected("JSON mapper", '[', scanner.Ident, '*')
}
return p.parsePath(c)
}
func (p *parser) parseScript(c context.Context) error {
script, err := p.ParseExpression(c)
if err != nil {
return err
}
if p.Scan() != ')' {
return p.Expected("jsnopath script", ')')
}
p.appendPlainSelector(newScript(script))
return p.parsePath(c)
}
func (p *parser) appendPlainSelector(next plainSelector) {
p.path = p.path.withPlainSelector(next)
}
func (p *parser) appendAmbiguousSelector(next ambiguousSelector) {
p.path = p.path.withAmbiguousSelector(next)
}