2019-02-26 13:05:15 +04:00
|
|
|
package jsonpath
|
|
|
|
|
|
|
|
const (
|
|
|
|
exprError = iota
|
|
|
|
exprEOF
|
|
|
|
exprParenLeft
|
|
|
|
exprParenRight
|
|
|
|
exprNumber
|
|
|
|
exprPath
|
|
|
|
exprBool
|
|
|
|
exprNull
|
|
|
|
exprString
|
|
|
|
|
|
|
|
exprOpEq
|
|
|
|
exprOpNeq
|
|
|
|
exprOpNot
|
|
|
|
exprOpLt
|
|
|
|
exprOpLe
|
|
|
|
exprOpGt
|
|
|
|
exprOpGe
|
|
|
|
exprOpAnd
|
|
|
|
exprOpOr
|
|
|
|
exprOpPlus
|
|
|
|
exprOpPlusUn
|
|
|
|
exprOpMinus
|
|
|
|
exprOpMinusUn
|
|
|
|
exprOpSlash
|
|
|
|
exprOpStar
|
|
|
|
exprOpHat
|
|
|
|
exprOpPercent
|
|
|
|
exprOpExclam
|
|
|
|
)
|
|
|
|
|
|
|
|
var exprTokenNames = map[int]string{
|
|
|
|
exprError: "error",
|
|
|
|
exprEOF: "EOF",
|
|
|
|
|
|
|
|
exprParenLeft: "(",
|
|
|
|
exprParenRight: ")",
|
|
|
|
exprNumber: "number",
|
|
|
|
exprPath: "path",
|
|
|
|
exprBool: "bool",
|
|
|
|
exprNull: "null",
|
|
|
|
exprString: "string",
|
|
|
|
exprOpEq: "==",
|
|
|
|
exprOpNeq: "!=",
|
|
|
|
exprOpNot: "!",
|
|
|
|
exprOpLt: "<",
|
|
|
|
exprOpLe: "<=",
|
|
|
|
exprOpGt: ">",
|
|
|
|
exprOpGe: ">=",
|
|
|
|
exprOpAnd: "&&",
|
|
|
|
exprOpOr: "||",
|
|
|
|
exprOpPlus: "+",
|
|
|
|
exprOpPlusUn: "(+)",
|
|
|
|
exprOpMinus: "-",
|
|
|
|
exprOpMinusUn: "(-)",
|
|
|
|
exprOpSlash: "/",
|
|
|
|
exprOpStar: "*",
|
|
|
|
exprOpHat: "^",
|
|
|
|
exprOpPercent: "%",
|
|
|
|
exprOpExclam: "!",
|
|
|
|
}
|
|
|
|
|
|
|
|
var EXPRESSION = lexExprText
|
|
|
|
|
|
|
|
func lexExprText(l lexer, state *intStack) stateFn {
|
|
|
|
ignoreSpaceRun(l)
|
|
|
|
cur := l.peek()
|
|
|
|
var next stateFn
|
|
|
|
switch cur {
|
|
|
|
case '(':
|
|
|
|
l.take()
|
|
|
|
state.push(exprParenLeft)
|
|
|
|
l.emit(exprParenLeft)
|
|
|
|
next = lexExprText
|
|
|
|
case ')':
|
|
|
|
if top, ok := state.peek(); ok && top != exprParenLeft {
|
|
|
|
next = l.errorf("Received %#U but has no matching (", cur)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
state.pop()
|
|
|
|
l.take()
|
|
|
|
l.emit(exprParenRight)
|
|
|
|
|
|
|
|
next = lexOneValue
|
|
|
|
case '!':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpNot)
|
|
|
|
next = lexExprText
|
|
|
|
case '+':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpPlusUn)
|
|
|
|
next = lexExprText
|
|
|
|
case '-':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpMinusUn)
|
|
|
|
next = lexExprText
|
|
|
|
case '@': //, '$': // Only support current key location
|
|
|
|
l.take()
|
|
|
|
takePath(l)
|
|
|
|
l.emit(exprPath)
|
|
|
|
next = lexOneValue
|
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
|
|
takeNumeric(l)
|
|
|
|
l.emit(exprNumber)
|
|
|
|
next = lexOneValue
|
|
|
|
case 't':
|
|
|
|
takeExactSequence(l, bytesTrue)
|
|
|
|
l.emit(exprBool)
|
|
|
|
next = lexOneValue
|
|
|
|
case 'f':
|
|
|
|
takeExactSequence(l, bytesFalse)
|
|
|
|
l.emit(exprBool)
|
|
|
|
next = lexOneValue
|
|
|
|
case 'n':
|
|
|
|
takeExactSequence(l, bytesNull)
|
|
|
|
l.emit(exprNull)
|
|
|
|
next = lexOneValue
|
|
|
|
case '"':
|
|
|
|
err := l.takeString()
|
|
|
|
if err != nil {
|
|
|
|
return l.errorf("Could not take string because %q", err)
|
|
|
|
}
|
|
|
|
l.emit(exprString)
|
|
|
|
next = lexOneValue
|
|
|
|
case eof:
|
|
|
|
l.emit(exprEOF)
|
|
|
|
// next = nil
|
|
|
|
default:
|
|
|
|
return l.errorf("Unrecognized sequence in expression: %#U", cur)
|
|
|
|
}
|
|
|
|
return next
|
|
|
|
}
|
|
|
|
|
|
|
|
func lexOneValue(l lexer, state *intStack) stateFn {
|
|
|
|
var next stateFn
|
|
|
|
cur := l.peek()
|
|
|
|
switch cur {
|
|
|
|
case '+':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpPlus)
|
|
|
|
next = lexExprText
|
|
|
|
case '-':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpMinus)
|
|
|
|
next = lexExprText
|
|
|
|
case '*':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpStar)
|
|
|
|
next = lexExprText
|
|
|
|
case '/':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpSlash)
|
|
|
|
next = lexExprText
|
|
|
|
case '%':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpPercent)
|
|
|
|
next = lexExprText
|
|
|
|
case '^':
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpHat)
|
|
|
|
next = lexExprText
|
|
|
|
case '<':
|
|
|
|
l.take()
|
|
|
|
cur = l.peek()
|
|
|
|
if cur == '=' {
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpLe)
|
|
|
|
} else {
|
|
|
|
l.emit(exprOpLt)
|
|
|
|
}
|
|
|
|
next = lexExprText
|
|
|
|
case '>':
|
|
|
|
l.take()
|
|
|
|
cur = l.peek()
|
|
|
|
if cur == '=' {
|
|
|
|
l.take()
|
|
|
|
l.emit(exprOpGe)
|
|
|
|
} else {
|
|
|
|
l.emit(exprOpGt)
|
|
|
|
}
|
|
|
|
next = lexExprText
|
|
|
|
case '&':
|
|
|
|
l.take()
|
|
|
|
cur = l.take()
|
|
|
|
if cur != '&' {
|
|
|
|
return l.errorf("Expected double & instead of %#U", cur)
|
|
|
|
}
|
|
|
|
l.emit(exprOpAnd)
|
|
|
|
next = lexExprText
|
|
|
|
case '|':
|
|
|
|
l.take()
|
|
|
|
cur = l.take()
|
|
|
|
if cur != '|' {
|
|
|
|
return l.errorf("Expected double | instead of %#U", cur)
|
|
|
|
}
|
|
|
|
l.emit(exprOpOr)
|
|
|
|
next = lexExprText
|
|
|
|
case '=':
|
|
|
|
l.take()
|
|
|
|
cur = l.take()
|
|
|
|
if cur != '=' {
|
|
|
|
return l.errorf("Expected double = instead of %#U", cur)
|
|
|
|
}
|
|
|
|
l.emit(exprOpEq)
|
|
|
|
next = lexExprText
|
|
|
|
case '!':
|
|
|
|
l.take()
|
|
|
|
cur = l.take()
|
|
|
|
if cur != '=' {
|
|
|
|
return l.errorf("Expected = for != instead of %#U", cur)
|
|
|
|
}
|
|
|
|
l.emit(exprOpNeq)
|
|
|
|
next = lexExprText
|
|
|
|
case ')':
|
|
|
|
if top, ok := state.peek(); ok && top != exprParenLeft {
|
|
|
|
next = l.errorf("Received %#U but has no matching (", cur)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
state.pop()
|
|
|
|
l.take()
|
|
|
|
l.emit(exprParenRight)
|
|
|
|
|
|
|
|
next = lexOneValue
|
|
|
|
case eof:
|
|
|
|
l.emit(exprEOF)
|
|
|
|
default:
|
|
|
|
return l.errorf("Unrecognized sequence in expression: %#U", cur)
|
|
|
|
}
|
|
|
|
return next
|
|
|
|
}
|
|
|
|
|
|
|
|
func takeNumeric(l lexer) {
|
|
|
|
takeDigits(l)
|
|
|
|
if l.peek() == '.' {
|
|
|
|
l.take()
|
|
|
|
takeDigits(l)
|
|
|
|
}
|
|
|
|
if l.peek() == 'e' || l.peek() == 'E' {
|
|
|
|
l.take()
|
|
|
|
if l.peek() == '+' || l.peek() == '-' {
|
|
|
|
l.take()
|
|
|
|
takeDigits(l)
|
|
|
|
} else {
|
|
|
|
takeDigits(l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func takePath(l lexer) {
|
|
|
|
inQuotes := false
|
2019-10-19 01:00:37 +04:00
|
|
|
prev := 0
|
2019-02-26 13:05:15 +04:00
|
|
|
// capture until end of path - ugly
|
|
|
|
takeLoop:
|
|
|
|
for {
|
|
|
|
cur := l.peek()
|
|
|
|
switch cur {
|
|
|
|
case '"':
|
|
|
|
if prev != '\\' {
|
|
|
|
inQuotes = !inQuotes
|
|
|
|
}
|
|
|
|
l.take()
|
|
|
|
case ' ':
|
|
|
|
if !inQuotes {
|
|
|
|
break takeLoop
|
|
|
|
}
|
|
|
|
l.take()
|
|
|
|
case eof:
|
|
|
|
break takeLoop
|
|
|
|
default:
|
|
|
|
l.take()
|
|
|
|
}
|
|
|
|
|
|
|
|
prev = cur
|
|
|
|
}
|
|
|
|
}
|