gofmt pass
This commit is contained in:
+109
-109
@@ -6,8 +6,8 @@
|
|||||||
package jpath
|
package jpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/pelletier/go-toml"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
. "github.com/pelletier/go-toml"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -27,42 +27,42 @@ const (
|
|||||||
const (
|
const (
|
||||||
tokenError tokenType = iota
|
tokenError tokenType = iota
|
||||||
tokenEOF
|
tokenEOF
|
||||||
tokenKey
|
tokenKey
|
||||||
tokenString
|
tokenString
|
||||||
tokenFloat
|
tokenFloat
|
||||||
tokenInteger
|
tokenInteger
|
||||||
tokenAtCost
|
tokenAtCost
|
||||||
tokenDollar
|
tokenDollar
|
||||||
tokenLBracket
|
tokenLBracket
|
||||||
tokenRBracket
|
tokenRBracket
|
||||||
tokenDot
|
tokenDot
|
||||||
tokenDotDot
|
tokenDotDot
|
||||||
tokenStar
|
tokenStar
|
||||||
tokenComma
|
tokenComma
|
||||||
tokenColon
|
tokenColon
|
||||||
tokenQuestion
|
tokenQuestion
|
||||||
tokenLParen
|
tokenLParen
|
||||||
tokenRParen
|
tokenRParen
|
||||||
)
|
)
|
||||||
|
|
||||||
var tokenTypeNames = []string{
|
var tokenTypeNames = []string{
|
||||||
"EOF",
|
"EOF",
|
||||||
"Key",
|
"Key",
|
||||||
"String",
|
"String",
|
||||||
"Float",
|
"Float",
|
||||||
"Integer",
|
"Integer",
|
||||||
"@",
|
"@",
|
||||||
"$",
|
"$",
|
||||||
"[",
|
"[",
|
||||||
"]",
|
"]",
|
||||||
".",
|
".",
|
||||||
"..",
|
"..",
|
||||||
"*",
|
"*",
|
||||||
",",
|
",",
|
||||||
":",
|
":",
|
||||||
"?",
|
"?",
|
||||||
"(",
|
"(",
|
||||||
")",
|
")",
|
||||||
}
|
}
|
||||||
|
|
||||||
type token struct {
|
type token struct {
|
||||||
@@ -126,7 +126,7 @@ type lexer struct {
|
|||||||
depth int
|
depth int
|
||||||
line int
|
line int
|
||||||
col int
|
col int
|
||||||
stringTerm string
|
stringTerm string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lexer) run() {
|
func (l *lexer) run() {
|
||||||
@@ -221,67 +221,67 @@ func (l *lexer) follow(next string) bool {
|
|||||||
type stateFn func(*lexer) stateFn
|
type stateFn func(*lexer) stateFn
|
||||||
|
|
||||||
func lexVoid(l *lexer) stateFn {
|
func lexVoid(l *lexer) stateFn {
|
||||||
for {
|
for {
|
||||||
next := l.peek()
|
next := l.peek()
|
||||||
switch next {
|
switch next {
|
||||||
case '$':
|
case '$':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenDollar)
|
l.emit(tokenDollar)
|
||||||
continue
|
continue
|
||||||
case '.':
|
case '.':
|
||||||
if l.follow("..") {
|
if l.follow("..") {
|
||||||
l.pos += 2
|
l.pos += 2
|
||||||
l.emit(tokenDotDot)
|
l.emit(tokenDotDot)
|
||||||
} else {
|
} else {
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenDot)
|
l.emit(tokenDot)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
case '@':
|
case '@':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenAtCost)
|
l.emit(tokenAtCost)
|
||||||
continue
|
continue
|
||||||
case '[':
|
case '[':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenLBracket)
|
l.emit(tokenLBracket)
|
||||||
continue
|
continue
|
||||||
case ']':
|
case ']':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenRBracket)
|
l.emit(tokenRBracket)
|
||||||
continue
|
continue
|
||||||
case ',':
|
case ',':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenComma)
|
l.emit(tokenComma)
|
||||||
continue
|
continue
|
||||||
case '*':
|
case '*':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenStar)
|
l.emit(tokenStar)
|
||||||
continue
|
continue
|
||||||
case '(':
|
case '(':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenLParen)
|
l.emit(tokenLParen)
|
||||||
continue
|
continue
|
||||||
case ')':
|
case ')':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenRParen)
|
l.emit(tokenRParen)
|
||||||
continue
|
continue
|
||||||
case '?':
|
case '?':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenQuestion)
|
l.emit(tokenQuestion)
|
||||||
continue
|
continue
|
||||||
case ':':
|
case ':':
|
||||||
l.pos++
|
l.pos++
|
||||||
l.emit(tokenColon)
|
l.emit(tokenColon)
|
||||||
continue
|
continue
|
||||||
case '\'':
|
case '\'':
|
||||||
l.ignore()
|
l.ignore()
|
||||||
l.stringTerm = string(next)
|
l.stringTerm = string(next)
|
||||||
return lexString
|
return lexString
|
||||||
case '"':
|
case '"':
|
||||||
l.ignore()
|
l.ignore()
|
||||||
l.stringTerm = string(next)
|
l.stringTerm = string(next)
|
||||||
return lexString
|
return lexString
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAlphanumeric(next) {
|
if isAlphanumeric(next) {
|
||||||
return lexKey
|
return lexKey
|
||||||
@@ -291,7 +291,7 @@ func lexVoid(l *lexer) stateFn {
|
|||||||
return lexNumber
|
return lexNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAlphanumeric(next) {
|
if isAlphanumeric(next) {
|
||||||
return lexKey
|
return lexKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,28 +299,28 @@ func lexVoid(l *lexer) stateFn {
|
|||||||
l.ignore()
|
l.ignore()
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.next() == eof {
|
if l.next() == eof {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.errorf("unexpected char: '%v'", next)
|
return l.errorf("unexpected char: '%v'", next)
|
||||||
}
|
}
|
||||||
l.emit(tokenEOF)
|
l.emit(tokenEOF)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexKey(l *lexer) stateFn {
|
func lexKey(l *lexer) stateFn {
|
||||||
for {
|
for {
|
||||||
next := l.peek()
|
next := l.peek()
|
||||||
if !isAlphanumeric(next) {
|
if !isAlphanumeric(next) {
|
||||||
l.emit(tokenKey)
|
l.emit(tokenKey)
|
||||||
return lexVoid
|
return lexVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.next() == eof {
|
if l.next() == eof {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l.emit(tokenEOF)
|
l.emit(tokenEOF)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -1,9 +1,8 @@
|
|||||||
|
|
||||||
package jpath
|
package jpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/pelletier/go-toml"
|
. "github.com/pelletier/go-toml"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testFlow(t *testing.T, input string, expectedFlow []token) {
|
func testFlow(t *testing.T, input string, expectedFlow []token) {
|
||||||
|
|||||||
+78
-79
@@ -1,7 +1,7 @@
|
|||||||
package jpath
|
package jpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/pelletier/go-toml"
|
. "github.com/pelletier/go-toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QueryPath []PathFn
|
type QueryPath []PathFn
|
||||||
@@ -9,107 +9,106 @@ type QueryPath []PathFn
|
|||||||
type PathFn func(context interface{}, next QueryPath)
|
type PathFn func(context interface{}, next QueryPath)
|
||||||
|
|
||||||
func (path QueryPath) Fn(context interface{}) {
|
func (path QueryPath) Fn(context interface{}) {
|
||||||
path[0](context, path[1:])
|
path[0](context, path[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func treeValue(tree *TomlTree, key string) interface{} {
|
func treeValue(tree *TomlTree, key string) interface{} {
|
||||||
return tree.GetPath([]string{key})
|
return tree.GetPath([]string{key})
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchKeyFn(name string) PathFn {
|
func matchKeyFn(name string) PathFn {
|
||||||
return func(context interface{}, next QueryPath) {
|
return func(context interface{}, next QueryPath) {
|
||||||
if tree, ok := context.(*TomlTree); ok {
|
if tree, ok := context.(*TomlTree); ok {
|
||||||
item := treeValue(tree, name)
|
item := treeValue(tree, name)
|
||||||
if item != nil {
|
if item != nil {
|
||||||
next.Fn(item)
|
next.Fn(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchIndexFn(idx int) PathFn {
|
func matchIndexFn(idx int) PathFn {
|
||||||
return func(context interface{}, next QueryPath) {
|
return func(context interface{}, next QueryPath) {
|
||||||
if arr, ok := context.([]interface{}); ok {
|
if arr, ok := context.([]interface{}); ok {
|
||||||
if idx < len(arr) && idx >= 0 {
|
if idx < len(arr) && idx >= 0 {
|
||||||
next.Fn(arr[idx])
|
next.Fn(arr[idx])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchSliceFn(start, end, step int) PathFn {
|
func matchSliceFn(start, end, step int) PathFn {
|
||||||
return func(context interface{}, next QueryPath) {
|
return func(context interface{}, next QueryPath) {
|
||||||
if arr, ok := context.([]interface{}); ok {
|
if arr, ok := context.([]interface{}); ok {
|
||||||
// adjust indexes for negative values, reverse ordering
|
// adjust indexes for negative values, reverse ordering
|
||||||
realStart, realEnd := start, end
|
realStart, realEnd := start, end
|
||||||
if realStart < 0 {
|
if realStart < 0 {
|
||||||
realStart = len(arr) + realStart
|
realStart = len(arr) + realStart
|
||||||
}
|
}
|
||||||
if realEnd < 0 {
|
if realEnd < 0 {
|
||||||
realEnd = len(arr) + realEnd
|
realEnd = len(arr) + realEnd
|
||||||
}
|
}
|
||||||
if realEnd < realStart {
|
if realEnd < realStart {
|
||||||
realEnd, realStart = realStart, realEnd // swap
|
realEnd, realStart = realStart, realEnd // swap
|
||||||
}
|
}
|
||||||
// loop and gather
|
// loop and gather
|
||||||
for idx := realStart; idx < realEnd; idx += step {
|
for idx := realStart; idx < realEnd; idx += step {
|
||||||
next.Fn(arr[idx])
|
next.Fn(arr[idx])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchAnyFn() PathFn {
|
func matchAnyFn() PathFn {
|
||||||
return func(context interface{}, next QueryPath) {
|
return func(context interface{}, next QueryPath) {
|
||||||
if tree, ok := context.(*TomlTree); ok {
|
if tree, ok := context.(*TomlTree); ok {
|
||||||
for _, key := range tree.Keys() {
|
for _, key := range tree.Keys() {
|
||||||
item := treeValue(tree, key)
|
item := treeValue(tree, key)
|
||||||
next.Fn(item)
|
next.Fn(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchUnionFn(union QueryPath) PathFn {
|
func matchUnionFn(union QueryPath) PathFn {
|
||||||
return func(context interface{}, next QueryPath) {
|
return func(context interface{}, next QueryPath) {
|
||||||
for _, fn := range union {
|
for _, fn := range union {
|
||||||
fn(context, next)
|
fn(context, next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchRecurseFn() PathFn {
|
func matchRecurseFn() PathFn {
|
||||||
return func(context interface{}, next QueryPath) {
|
return func(context interface{}, next QueryPath) {
|
||||||
if tree, ok := context.(*TomlTree); ok {
|
if tree, ok := context.(*TomlTree); ok {
|
||||||
var visit func(tree *TomlTree)
|
var visit func(tree *TomlTree)
|
||||||
visit = func(tree *TomlTree) {
|
visit = func(tree *TomlTree) {
|
||||||
for _, key := range tree.Keys() {
|
for _, key := range tree.Keys() {
|
||||||
item := treeValue(tree, key)
|
item := treeValue(tree, key)
|
||||||
next.Fn(item)
|
next.Fn(item)
|
||||||
switch node := item.(type) {
|
switch node := item.(type) {
|
||||||
case *TomlTree:
|
case *TomlTree:
|
||||||
visit(node)
|
visit(node)
|
||||||
case []*TomlTree:
|
case []*TomlTree:
|
||||||
for _, subtree := range node {
|
for _, subtree := range node {
|
||||||
visit(subtree)
|
visit(subtree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
visit(tree)
|
visit(tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func processPath(path QueryPath, context interface{}) []interface{} {
|
func processPath(path QueryPath, context interface{}) []interface{} {
|
||||||
// terminate the path with a collection funciton
|
// terminate the path with a collection funciton
|
||||||
result := []interface{}{}
|
result := []interface{}{}
|
||||||
newPath := append(path, func(context interface{}, next QueryPath) {
|
newPath := append(path, func(context interface{}, next QueryPath) {
|
||||||
result = append(result, context)
|
result = append(result, context)
|
||||||
})
|
})
|
||||||
|
|
||||||
// execute the path
|
// execute the path
|
||||||
newPath.Fn(context)
|
newPath.Fn(context)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
+106
-107
@@ -2,14 +2,14 @@ package jpath
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
flow chan token
|
flow chan token
|
||||||
tokensBuffer []token
|
tokensBuffer []token
|
||||||
path []PathFn
|
path []PathFn
|
||||||
}
|
}
|
||||||
|
|
||||||
type parserStateFn func(*parser) parserStateFn
|
type parserStateFn func(*parser) parserStateFn
|
||||||
@@ -38,7 +38,7 @@ func (p *parser) peek() *token {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p.backup(&tok)
|
p.backup(&tok)
|
||||||
return &tok
|
return &tok
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +55,8 @@ func (p *parser) getToken() *token {
|
|||||||
return &tok
|
return &tok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) appendPath(fn PathFn) {
|
func (p *parser) appendPath(fn PathFn) {
|
||||||
p.path = append(p.path, fn)
|
p.path = append(p.path, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseStart(p *parser) parserStateFn {
|
func parseStart(p *parser) parserStateFn {
|
||||||
@@ -67,29 +66,29 @@ func parseStart(p *parser) parserStateFn {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if tok.typ != tokenDollar {
|
if tok.typ != tokenDollar {
|
||||||
p.raiseError(tok, "Expected '$' at start of expression")
|
p.raiseError(tok, "Expected '$' at start of expression")
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMatchExpr(p *parser) parserStateFn {
|
func parseMatchExpr(p *parser) parserStateFn {
|
||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenDot:
|
case tokenDot:
|
||||||
p.appendPath(matchKeyFn(tok.val))
|
p.appendPath(matchKeyFn(tok.val))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
case tokenDotDot:
|
case tokenDotDot:
|
||||||
p.appendPath(matchRecurseFn())
|
p.appendPath(matchRecurseFn())
|
||||||
return parseSimpleMatchExpr
|
return parseSimpleMatchExpr
|
||||||
case tokenLBracket:
|
case tokenLBracket:
|
||||||
return parseBracketExpr
|
return parseBracketExpr
|
||||||
case tokenStar:
|
case tokenStar:
|
||||||
p.appendPath(matchAnyFn())
|
p.appendPath(matchAnyFn())
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
case tokenEOF:
|
case tokenEOF:
|
||||||
return nil // allow EOF at this stage
|
return nil // allow EOF at this stage
|
||||||
}
|
}
|
||||||
p.raiseError(tok, "expected match expression")
|
p.raiseError(tok, "expected match expression")
|
||||||
return nil
|
return nil
|
||||||
@@ -101,124 +100,124 @@ func parseSimpleMatchExpr(p *parser) parserStateFn {
|
|||||||
case tokenLBracket:
|
case tokenLBracket:
|
||||||
return parseBracketExpr
|
return parseBracketExpr
|
||||||
case tokenKey:
|
case tokenKey:
|
||||||
p.appendPath(matchKeyFn(tok.val))
|
p.appendPath(matchKeyFn(tok.val))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
case tokenStar:
|
case tokenStar:
|
||||||
p.appendPath(matchAnyFn())
|
p.appendPath(matchAnyFn())
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
p.raiseError(tok, "expected match expression")
|
p.raiseError(tok, "expected match expression")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseBracketExpr(p *parser) parserStateFn {
|
func parseBracketExpr(p *parser) parserStateFn {
|
||||||
tok := p.peek()
|
tok := p.peek()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenInteger:
|
case tokenInteger:
|
||||||
// look ahead for a ':'
|
// look ahead for a ':'
|
||||||
p.getToken()
|
p.getToken()
|
||||||
next := p.peek()
|
next := p.peek()
|
||||||
p.backup(tok)
|
p.backup(tok)
|
||||||
if next.typ == tokenColon {
|
if next.typ == tokenColon {
|
||||||
return parseSliceExpr
|
return parseSliceExpr
|
||||||
}
|
}
|
||||||
return parseUnionExpr
|
return parseUnionExpr
|
||||||
case tokenColon:
|
case tokenColon:
|
||||||
return parseSliceExpr
|
return parseSliceExpr
|
||||||
}
|
}
|
||||||
return parseUnionExpr
|
return parseUnionExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUnionExpr(p *parser) parserStateFn {
|
func parseUnionExpr(p *parser) parserStateFn {
|
||||||
union := []PathFn{}
|
union := []PathFn{}
|
||||||
for {
|
for {
|
||||||
// parse sub expression
|
// parse sub expression
|
||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenInteger:
|
case tokenInteger:
|
||||||
idx, _ := strconv.Atoi(tok.val)
|
idx, _ := strconv.Atoi(tok.val)
|
||||||
union = append(union, matchIndexFn(idx))
|
union = append(union, matchIndexFn(idx))
|
||||||
case tokenKey:
|
case tokenKey:
|
||||||
union = append(union, matchKeyFn(tok.val))
|
union = append(union, matchKeyFn(tok.val))
|
||||||
case tokenQuestion:
|
case tokenQuestion:
|
||||||
return parseFilterExpr
|
return parseFilterExpr
|
||||||
case tokenLParen:
|
case tokenLParen:
|
||||||
return parseScriptExpr
|
return parseScriptExpr
|
||||||
default:
|
default:
|
||||||
p.raiseError(tok, "expected union sub expression")
|
p.raiseError(tok, "expected union sub expression")
|
||||||
}
|
}
|
||||||
// parse delimiter or terminator
|
// parse delimiter or terminator
|
||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenComma:
|
case tokenComma:
|
||||||
continue
|
continue
|
||||||
case tokenRBracket:
|
case tokenRBracket:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
p.raiseError(tok, "expected ',' or ']'")
|
p.raiseError(tok, "expected ',' or ']'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.appendPath(matchUnionFn(union))
|
p.appendPath(matchUnionFn(union))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSliceExpr(p *parser) parserStateFn {
|
func parseSliceExpr(p *parser) parserStateFn {
|
||||||
// init slice to grab all elements
|
// init slice to grab all elements
|
||||||
start, end, step := 0, math.MaxInt64, 1
|
start, end, step := 0, math.MaxInt64, 1
|
||||||
|
|
||||||
// parse optional start
|
// parse optional start
|
||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
if tok.typ == tokenInteger {
|
if tok.typ == tokenInteger {
|
||||||
start, _ = strconv.Atoi(tok.val)
|
start, _ = strconv.Atoi(tok.val)
|
||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
}
|
}
|
||||||
if tok.typ != tokenColon {
|
if tok.typ != tokenColon {
|
||||||
p.raiseError(tok, "expected ':'")
|
p.raiseError(tok, "expected ':'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse optional end
|
// parse optional end
|
||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
if tok.typ == tokenInteger {
|
if tok.typ == tokenInteger {
|
||||||
end, _ = strconv.Atoi(tok.val)
|
end, _ = strconv.Atoi(tok.val)
|
||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
}
|
}
|
||||||
if tok.typ != tokenColon || tok.typ != tokenRBracket {
|
if tok.typ != tokenColon || tok.typ != tokenRBracket {
|
||||||
p.raiseError(tok, "expected ']' or ':'")
|
p.raiseError(tok, "expected ']' or ':'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse optional step
|
// parse optional step
|
||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
if tok.typ == tokenInteger {
|
if tok.typ == tokenInteger {
|
||||||
step, _ = strconv.Atoi(tok.val)
|
step, _ = strconv.Atoi(tok.val)
|
||||||
if step < 0 {
|
if step < 0 {
|
||||||
p.raiseError(tok, "step must be a positive value")
|
p.raiseError(tok, "step must be a positive value")
|
||||||
}
|
}
|
||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
}
|
}
|
||||||
if tok.typ != tokenRBracket {
|
if tok.typ != tokenRBracket {
|
||||||
p.raiseError(tok, "expected ']'")
|
p.raiseError(tok, "expected ']'")
|
||||||
}
|
}
|
||||||
|
|
||||||
p.appendPath(matchSliceFn(start, end, step))
|
p.appendPath(matchSliceFn(start, end, step))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFilterExpr(p *parser) parserStateFn {
|
func parseFilterExpr(p *parser) parserStateFn {
|
||||||
p.raiseError(p.peek(), "filter expressions are unsupported")
|
p.raiseError(p.peek(), "filter expressions are unsupported")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseScriptExpr(p *parser) parserStateFn {
|
func parseScriptExpr(p *parser) parserStateFn {
|
||||||
p.raiseError(p.peek(), "script expressions are unsupported")
|
p.raiseError(p.peek(), "script expressions are unsupported")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(flow chan token) []PathFn {
|
func parse(flow chan token) []PathFn {
|
||||||
result := []PathFn{}
|
result := []PathFn{}
|
||||||
parser := &parser{
|
parser := &parser{
|
||||||
flow: flow,
|
flow: flow,
|
||||||
tokensBuffer: []token{},
|
tokensBuffer: []token{},
|
||||||
path: result,
|
path: result,
|
||||||
}
|
}
|
||||||
parser.run()
|
parser.run()
|
||||||
return result
|
return result
|
||||||
|
|||||||
+55
-55
@@ -2,15 +2,15 @@ package jpath
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
. "github.com/pelletier/go-toml"
|
||||||
"testing"
|
"testing"
|
||||||
. "github.com/pelletier/go-toml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func assertQuery(t *testing.T, toml, query string, ref []interface{}) {
|
func assertQuery(t *testing.T, toml, query string, ref []interface{}) {
|
||||||
tree, err := Load(toml)
|
tree, err := Load(toml)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Non-nil toml parse error: %v", err)
|
t.Errorf("Non-nil toml parse error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, flow := lex(query)
|
_, flow := lex(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -18,65 +18,65 @@ func assertQuery(t *testing.T, toml, query string, ref []interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
path := parse(flow)
|
path := parse(flow)
|
||||||
result := processPath(path, tree)
|
result := processPath(path, tree)
|
||||||
assertValue(t, result, ref, "")
|
assertValue(t, result, ref, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertValue(t *testing.T, result, ref interface{}, location string) {
|
func assertValue(t *testing.T, result, ref interface{}, location string) {
|
||||||
switch node := ref.(type) {
|
switch node := ref.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
if resultNode, ok := result.([]interface{}); !ok {
|
if resultNode, ok := result.([]interface{}); !ok {
|
||||||
t.Errorf("{%s} result value not of type %T: %T",
|
t.Errorf("{%s} result value not of type %T: %T",
|
||||||
location, node, resultNode)
|
location, node, resultNode)
|
||||||
} else {
|
} else {
|
||||||
for i, v := range node {
|
for i, v := range node {
|
||||||
assertValue(t, resultNode[i], v, fmt.Sprintf("%s[%d]", location, i))
|
assertValue(t, resultNode[i], v, fmt.Sprintf("%s[%d]", location, i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
if resultNode, ok := result.(*TomlTree); !ok {
|
if resultNode, ok := result.(*TomlTree); !ok {
|
||||||
t.Errorf("{%s} result value not of type %T: %T",
|
t.Errorf("{%s} result value not of type %T: %T",
|
||||||
location, node, resultNode)
|
location, node, resultNode)
|
||||||
} else {
|
} else {
|
||||||
for k, v := range node {
|
for k, v := range node {
|
||||||
assertValue(t, resultNode.GetPath([]string{k}), v, location + "." + k)
|
assertValue(t, resultNode.GetPath([]string{k}), v, location+"."+k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case int64:
|
case int64:
|
||||||
if resultNode, ok := result.(int64); !ok {
|
if resultNode, ok := result.(int64); !ok {
|
||||||
t.Errorf("{%s} result value not of type %T: %T",
|
t.Errorf("{%s} result value not of type %T: %T",
|
||||||
location, node, resultNode)
|
location, node, resultNode)
|
||||||
} else {
|
} else {
|
||||||
if node != resultNode {
|
if node != resultNode {
|
||||||
t.Errorf("{%s} result value does not match", location)
|
t.Errorf("{%s} result value does not match", location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case string:
|
case string:
|
||||||
if resultNode, ok := result.(string); !ok {
|
if resultNode, ok := result.(string); !ok {
|
||||||
t.Errorf("{%s} result value not of type %T: %T",
|
t.Errorf("{%s} result value not of type %T: %T",
|
||||||
location, node, resultNode)
|
location, node, resultNode)
|
||||||
} else {
|
} else {
|
||||||
if node != resultNode {
|
if node != resultNode {
|
||||||
t.Errorf("{%s} result value does not match", location)
|
t.Errorf("{%s} result value does not match", location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if fmt.Sprintf("%v", node) != fmt.Sprintf("%v", ref) {
|
if fmt.Sprintf("%v", node) != fmt.Sprintf("%v", ref) {
|
||||||
t.Errorf("{%s} result value does not match: %v != %v",
|
t.Errorf("{%s} result value does not match: %v != %v",
|
||||||
location, node, ref)
|
location, node, ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQueryRoot(t *testing.T) {
|
func TestQueryRoot(t *testing.T) {
|
||||||
assertQuery(t,
|
assertQuery(t,
|
||||||
"a = 42",
|
"a = 42",
|
||||||
"$",
|
"$",
|
||||||
[]interface{}{
|
[]interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"a": int64(42),
|
"a": int64(42),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user