141 lines
2.5 KiB
Go
141 lines
2.5 KiB
Go
package parser
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
context struct {
|
|
exps []expectation
|
|
}
|
|
|
|
// Context building block
|
|
expectation struct {
|
|
typ expectationType
|
|
greedy bool
|
|
key string // Object key
|
|
index int64 // Array index
|
|
}
|
|
|
|
// Type of expectation: object or array
|
|
expectationType byte
|
|
)
|
|
|
|
const (
|
|
unknown expectationType = iota
|
|
object
|
|
array
|
|
)
|
|
|
|
func (c *context) compare(c2 *context) bool {
|
|
if len(c.exps) != len(c2.exps) {
|
|
return false
|
|
}
|
|
|
|
for i, exp := range c.exps {
|
|
exp2 := c2.exps[i]
|
|
if exp.typ != exp2.typ {
|
|
return false
|
|
}
|
|
if exp.greedy || exp2.greedy {
|
|
continue
|
|
}
|
|
switch exp.typ {
|
|
case array:
|
|
if exp.index != exp2.index {
|
|
return false
|
|
}
|
|
case object:
|
|
if exp.key != exp2.key {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (c *context) push(typ expectationType) {
|
|
c.exps = append(c.exps, expectation{typ: typ})
|
|
}
|
|
|
|
func (c *context) pop() {
|
|
if len(c.exps) == 0 {
|
|
return
|
|
}
|
|
c.exps = c.exps[:len(c.exps)-1]
|
|
}
|
|
|
|
func (c *context) setKey(key string) {
|
|
c.exps[len(c.exps)-1].key = key
|
|
}
|
|
|
|
func (c *context) setIndex(i int64) {
|
|
c.exps[len(c.exps)-1].index = i
|
|
}
|
|
|
|
func parseSelectors(sels []string) map[string]*context {
|
|
ctxs := map[string]*context{}
|
|
for _, sel := range sels {
|
|
ctxs[sel] = &context{
|
|
exps: parseSelector(sel),
|
|
}
|
|
}
|
|
return ctxs
|
|
}
|
|
|
|
// Format: .bananas#0.weight
|
|
// There are two selector types:
|
|
// * object property: .prop
|
|
// * array index: #1
|
|
// Greedy selectors are supported for both types: .* and #*
|
|
func parseSelector(sel string) []expectation {
|
|
tmp := strings.Replace(sel, ".", "/.", -1) // "/.bananas#0/.weight"
|
|
tmp = strings.Replace(tmp, "#", "/#", -1) // "/.bananas/#0/.weight"
|
|
parts := strings.Split(tmp[1:], "/") // [".bananas", "#0", ".weight"]
|
|
|
|
exps := []expectation{}
|
|
for _, part := range parts {
|
|
c := expectation{}
|
|
|
|
if len(part) < 2 {
|
|
panic("Invalid selector: " + sel)
|
|
} else if part[:1] == "." {
|
|
c.typ = object
|
|
} else if part[:1] == "#" {
|
|
c.typ = array
|
|
} else {
|
|
panic("Invalid selector: " + sel)
|
|
}
|
|
|
|
if part[1:2] == "*" {
|
|
c.greedy = true
|
|
if len(part) > 2 {
|
|
panic("Invalid selector: " + sel)
|
|
}
|
|
} else if c.typ == object {
|
|
c.key = part[1:]
|
|
} else if i, err := strconv.ParseInt(part[1:], 10, 64); err == nil {
|
|
c.index = i
|
|
} else {
|
|
panic("Array index should be numeric: " + part)
|
|
}
|
|
|
|
exps = append(exps, c)
|
|
}
|
|
|
|
return exps
|
|
}
|
|
|
|
func (e expectationType) String() string {
|
|
switch e {
|
|
case array:
|
|
return "Index"
|
|
case object:
|
|
return "Key"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|