Source File
parser.go
Belonging Package
github.com/jmespath/go-jmespath
package jmespath
import (
)
type astNodeType int
const (
ASTEmpty astNodeType = iota
ASTComparator
ASTCurrentNode
ASTExpRef
ASTFunctionExpression
ASTField
ASTFilterProjection
ASTFlatten
ASTIdentity
ASTIndex
ASTIndexExpression
ASTKeyValPair
ASTLiteral
ASTMultiSelectHash
ASTMultiSelectList
ASTOrExpression
ASTAndExpression
ASTNotExpression
ASTPipe
ASTProjection
ASTSubexpression
ASTSlice
ASTValueProjection
)
type ASTNode struct {
nodeType astNodeType
value interface{}
children []ASTNode
}
func ( ASTNode) () string {
return .PrettyPrint(0)
}
+= fmt.Sprintf("%svalue: %s\n", strings.Repeat(" ", ), .String())
} else {
+= fmt.Sprintf("%svalue: %#v\n", strings.Repeat(" ", ), .value)
}
}
:= len(.children)
if > 0 {
+= fmt.Sprintf("%schildren: {\n", strings.Repeat(" ", ))
:= + 2
for , := range .children {
+= .()
}
}
+= fmt.Sprintf("%s}\n", )
return
}
var bindingPowers = map[tokType]int{
tEOF: 0,
tUnquotedIdentifier: 0,
tQuotedIdentifier: 0,
tRbracket: 0,
tRparen: 0,
tComma: 0,
tRbrace: 0,
tNumber: 0,
tCurrent: 0,
tExpref: 0,
tColon: 0,
tPipe: 1,
tOr: 2,
tAnd: 3,
tEQ: 5,
tLT: 5,
tLTE: 5,
tGT: 5,
tGTE: 5,
tNE: 5,
tFlatten: 9,
tStar: 20,
tFilter: 21,
tDot: 40,
tNot: 45,
tLbrace: 50,
tLbracket: 55,
tLparen: 60,
}
func ( *Parser) ( string) (ASTNode, error) {
:= NewLexer()
.expression =
.index = 0
, := .tokenize()
if != nil {
return ASTNode{},
}
.tokens =
, := .parseExpression(0)
if != nil {
return ASTNode{},
}
if .current() != tEOF {
return ASTNode{}, .syntaxError(fmt.Sprintf(
"Unexpected token at the end of the expression: %s", .current()))
}
return , nil
}
func ( *Parser) ( int) (ASTNode, error) {
var error
:= .lookaheadToken(0)
.advance()
, := .nud()
if != nil {
return ASTNode{},
}
:= .current()
for < bindingPowers[] {
.advance()
, = .led(, )
if != nil {
return ASTNode{},
}
= .current()
}
return , nil
}
func ( *Parser) () (ASTNode, error) {
if .lookahead(0) == tColon || .lookahead(1) == tColon {
return .parseSliceExpression()
}
:= .lookaheadToken(0).value
, := strconv.Atoi()
if != nil {
return ASTNode{},
}
:= ASTNode{nodeType: ASTIndex, value: }
.advance()
if := .match(tRbracket); != nil {
return ASTNode{},
}
return , nil
}
func ( *Parser) () (ASTNode, error) {
:= []*int{nil, nil, nil}
:= 0
:= .current()
for != tRbracket && < 3 {
if == tColon {
++
.advance()
} else if == tNumber {
, := strconv.Atoi(.lookaheadToken(0).value)
if != nil {
return ASTNode{},
}
[] = &
.advance()
} else {
return ASTNode{}, .syntaxError(
"Expected tColon or tNumber" + ", received: " + .current().String())
}
= .current()
}
if := .match(tRbracket); != nil {
return ASTNode{},
}
return ASTNode{
nodeType: ASTSlice,
value: ,
}, nil
}
func ( *Parser) ( tokType) error {
if .current() == {
.advance()
return nil
}
return .syntaxError("Expected " + .String() + ", received: " + .current().String())
}
func ( *Parser) ( tokType, ASTNode) (ASTNode, error) {
switch {
case tDot:
if .current() != tStar {
, := .parseDotRHS(bindingPowers[tDot])
return ASTNode{
nodeType: ASTSubexpression,
children: []ASTNode{, },
},
}
.advance()
, := .parseProjectionRHS(bindingPowers[tDot])
return ASTNode{
nodeType: ASTValueProjection,
children: []ASTNode{, },
},
case tPipe:
, := .parseExpression(bindingPowers[tPipe])
return ASTNode{nodeType: ASTPipe, children: []ASTNode{, }},
case tOr:
, := .parseExpression(bindingPowers[tOr])
return ASTNode{nodeType: ASTOrExpression, children: []ASTNode{, }},
case tAnd:
, := .parseExpression(bindingPowers[tAnd])
return ASTNode{nodeType: ASTAndExpression, children: []ASTNode{, }},
case tLparen:
:= .value
var []ASTNode
for .current() != tRparen {
, := .parseExpression(0)
if != nil {
return ASTNode{},
}
if .current() == tComma {
if := .match(tComma); != nil {
return ASTNode{},
}
}
= append(, )
}
if := .match(tRparen); != nil {
return ASTNode{},
}
return ASTNode{
nodeType: ASTFunctionExpression,
value: ,
children: ,
}, nil
case tFilter:
return .parseFilter()
case tFlatten:
:= ASTNode{nodeType: ASTFlatten, children: []ASTNode{}}
, := .parseProjectionRHS(bindingPowers[tFlatten])
return ASTNode{
nodeType: ASTProjection,
children: []ASTNode{, },
},
case tEQ, tNE, tGT, tGTE, tLT, tLTE:
, := .parseExpression(bindingPowers[])
if != nil {
return ASTNode{},
}
return ASTNode{
nodeType: ASTComparator,
value: ,
children: []ASTNode{, },
}, nil
case tLbracket:
:= .current()
var ASTNode
var error
if == tNumber || == tColon {
, = .parseIndexExpression()
if != nil {
return ASTNode{},
}
return .projectIfSlice(, )
if := .match(tStar); != nil {
return ASTNode{},
}
if := .match(tRbracket); != nil {
return ASTNode{},
}
, = .parseProjectionRHS(bindingPowers[tStar])
if != nil {
return ASTNode{},
}
return ASTNode{
nodeType: ASTProjection,
children: []ASTNode{, },
}, nil
}
return ASTNode{}, .syntaxError("Unexpected token: " + .String())
}
func ( *Parser) ( token) (ASTNode, error) {
switch .tokenType {
case tJSONLiteral:
var interface{}
:= json.Unmarshal([]byte(.value), &)
if != nil {
return ASTNode{},
}
return ASTNode{nodeType: ASTLiteral, value: }, nil
case tStringLiteral:
return ASTNode{nodeType: ASTLiteral, value: .value}, nil
case tUnquotedIdentifier:
return ASTNode{
nodeType: ASTField,
value: .value,
}, nil
case tQuotedIdentifier:
:= ASTNode{nodeType: ASTField, value: .value}
if .current() == tLparen {
return ASTNode{}, .syntaxErrorToken("Can't have quoted identifier as function name.", )
}
return , nil
case tStar:
:= ASTNode{nodeType: ASTIdentity}
var ASTNode
var error
if .current() == tRbracket {
= ASTNode{nodeType: ASTIdentity}
} else {
, = .parseProjectionRHS(bindingPowers[tStar])
}
return ASTNode{nodeType: ASTValueProjection, children: []ASTNode{, }},
case tFilter:
return .parseFilter(ASTNode{nodeType: ASTIdentity})
case tLbrace:
return .parseMultiSelectHash()
case tFlatten:
:= ASTNode{
nodeType: ASTFlatten,
children: []ASTNode{{nodeType: ASTIdentity}},
}
, := .parseProjectionRHS(bindingPowers[tFlatten])
if != nil {
return ASTNode{},
}
return ASTNode{nodeType: ASTProjection, children: []ASTNode{, }}, nil
case tLbracket:
if == tNumber || == tColon {
, := .parseIndexExpression()
if != nil {
return ASTNode{}, nil
}
return .projectIfSlice(ASTNode{nodeType: ASTIdentity}, )
} else if == tStar && .lookahead(1) == tRbracket {
.advance()
.advance()
, := .parseProjectionRHS(bindingPowers[tStar])
if != nil {
return ASTNode{},
}
return ASTNode{
nodeType: ASTProjection,
children: []ASTNode{{nodeType: ASTIdentity}, },
}, nil
} else {
return .parseMultiSelectList()
}
case tCurrent:
return ASTNode{nodeType: ASTCurrentNode}, nil
case tExpref:
, := .parseExpression(bindingPowers[tExpref])
if != nil {
return ASTNode{},
}
return ASTNode{nodeType: ASTExpRef, children: []ASTNode{}}, nil
case tNot:
, := .parseExpression(bindingPowers[tNot])
if != nil {
return ASTNode{},
}
return ASTNode{nodeType: ASTNotExpression, children: []ASTNode{}}, nil
case tLparen:
, := .parseExpression(0)
if != nil {
return ASTNode{},
}
if := .match(tRparen); != nil {
return ASTNode{},
}
return , nil
case tEOF:
return ASTNode{}, .syntaxErrorToken("Incomplete expression", )
}
return ASTNode{}, .syntaxErrorToken("Invalid token: "+.tokenType.String(), )
}
func ( *Parser) () (ASTNode, error) {
var []ASTNode
for {
, := .parseExpression(0)
if != nil {
return ASTNode{},
}
= append(, )
if .current() == tRbracket {
break
}
= .match(tComma)
if != nil {
return ASTNode{},
}
}
:= .match(tRbracket)
if != nil {
return ASTNode{},
}
return ASTNode{
nodeType: ASTMultiSelectList,
children: ,
}, nil
}
func ( *Parser) () (ASTNode, error) {
var []ASTNode
for {
:= .lookaheadToken(0)
if := .match(tUnquotedIdentifier); != nil {
if := .match(tQuotedIdentifier); != nil {
return ASTNode{}, .syntaxError("Expected tQuotedIdentifier or tUnquotedIdentifier")
}
}
:= .value
:= .match(tColon)
if != nil {
return ASTNode{},
}
, := .parseExpression(0)
if != nil {
return ASTNode{},
}
:= ASTNode{
nodeType: ASTKeyValPair,
value: ,
children: []ASTNode{},
}
= append(, )
if .current() == tComma {
:= .match(tComma)
if != nil {
return ASTNode{}, nil
}
} else if .current() == tRbrace {
:= .match(tRbrace)
if != nil {
return ASTNode{}, nil
}
break
}
}
return ASTNode{
nodeType: ASTMultiSelectHash,
children: ,
}, nil
}
func ( *Parser) ( ASTNode, ASTNode) (ASTNode, error) {
:= ASTNode{
nodeType: ASTIndexExpression,
children: []ASTNode{, },
}
if .nodeType == ASTSlice {
, := .parseProjectionRHS(bindingPowers[tStar])
return ASTNode{
nodeType: ASTProjection,
children: []ASTNode{, },
},
}
return , nil
}
func ( *Parser) ( ASTNode) (ASTNode, error) {
var , ASTNode
var error
, = .parseExpression(0)
if != nil {
return ASTNode{},
}
if := .match(tRbracket); != nil {
return ASTNode{},
}
if .current() == tFlatten {
= ASTNode{nodeType: ASTIdentity}
} else {
, = .parseProjectionRHS(bindingPowers[tFilter])
if != nil {
return ASTNode{},
}
}
return ASTNode{
nodeType: ASTFilterProjection,
children: []ASTNode{, , },
}, nil
}
func ( *Parser) ( int) (ASTNode, error) {
:= .current()
if tokensOneOf([]tokType{tQuotedIdentifier, tUnquotedIdentifier, tStar}, ) {
return .parseExpression()
} else if == tLbracket {
if := .match(tLbracket); != nil {
return ASTNode{},
}
return .parseMultiSelectList()
} else if == tLbrace {
if := .match(tLbrace); != nil {
return ASTNode{},
}
return .parseMultiSelectHash()
}
return ASTNode{}, .syntaxError("Expected identifier, lbracket, or lbrace")
}
func ( *Parser) ( int) (ASTNode, error) {
:= .current()
if bindingPowers[] < 10 {
return ASTNode{nodeType: ASTIdentity}, nil
} else if == tLbracket {
return .parseExpression()
} else if == tFilter {
return .parseExpression()
} else if == tDot {
:= .match(tDot)
if != nil {
return ASTNode{},
}
return .parseDotRHS()
} else {
return ASTNode{}, .syntaxError("Error")
}
}
func ( *Parser) ( int) tokType {
return .lookaheadToken().tokenType
}
func ( *Parser) () tokType {
return .lookahead(0)
}
func ( *Parser) ( int) token {
return .tokens[.index+]
}
func ( *Parser) () {
.index++
}
func ( []tokType, tokType) bool {
for , := range {
if == {
return true
}
}
return false
}
func ( *Parser) ( string) SyntaxError {
return SyntaxError{
msg: ,
Expression: .expression,
Offset: .lookaheadToken(0).position,
}
}
func ( *Parser) ( string, token) SyntaxError {
return SyntaxError{
msg: ,
Expression: .expression,
Offset: .position,
}
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |