2020-10-08 23:59:03 +00:00
|
|
|
package treeops
|
2020-09-17 11:58:01 +00:00
|
|
|
|
|
|
|
import (
|
2020-09-17 12:12:56 +00:00
|
|
|
"strconv"
|
2020-09-17 11:58:01 +00:00
|
|
|
|
|
|
|
lex "github.com/timtadh/lexmachine"
|
|
|
|
"github.com/timtadh/lexmachine/machines"
|
|
|
|
)
|
|
|
|
|
2020-10-11 00:24:22 +00:00
|
|
|
func skip(*lex.Scanner, *machines.Match) (interface{}, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2020-09-17 11:58:01 +00:00
|
|
|
|
2020-10-11 00:24:22 +00:00
|
|
|
type Token struct {
|
|
|
|
PathElementType PathElementType
|
2020-10-11 23:44:33 +00:00
|
|
|
OperationType *OperationType
|
2020-10-11 00:24:22 +00:00
|
|
|
Value interface{}
|
|
|
|
StringValue string
|
2020-10-11 23:09:13 +00:00
|
|
|
PrefixSelf bool
|
2020-09-20 12:40:09 +00:00
|
|
|
|
2020-10-11 00:24:22 +00:00
|
|
|
CheckForPreTraverse bool // this token can sometimes have the traverse '.' missing in frnot of it
|
|
|
|
// e.g. a[1] should really be a.[1]
|
|
|
|
CheckForPostTraverse bool // samething but for post, e.g. [1]cat should really be [1].cat
|
2020-09-17 11:58:01 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 00:24:22 +00:00
|
|
|
func pathToken(wrapped bool) lex.Action {
|
2020-09-17 11:58:01 +00:00
|
|
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
2020-10-11 00:24:22 +00:00
|
|
|
value := string(m.Bytes)
|
|
|
|
if wrapped {
|
|
|
|
value = unwrap(value)
|
|
|
|
}
|
|
|
|
return &Token{PathElementType: PathKey, OperationType: None, Value: value, StringValue: value}, nil
|
2020-09-17 11:58:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-11 23:44:33 +00:00
|
|
|
func opToken(op *OperationType, againstSelf bool) lex.Action {
|
2020-10-11 00:24:22 +00:00
|
|
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
|
|
|
value := string(m.Bytes)
|
2020-10-12 01:24:59 +00:00
|
|
|
return &Token{PathElementType: Operation, OperationType: op, Value: op.Type, StringValue: value, PrefixSelf: againstSelf}, nil
|
2020-10-11 00:24:22 +00:00
|
|
|
}
|
2020-09-17 12:12:56 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 23:09:13 +00:00
|
|
|
func literalToken(pType PathElementType, literal string, checkForPre bool, checkForPost bool, againstSelf bool) lex.Action {
|
2020-09-17 12:12:56 +00:00
|
|
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
2020-10-11 23:44:33 +00:00
|
|
|
return &Token{PathElementType: pType, OperationType: None, Value: literal, StringValue: literal, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost, PrefixSelf: againstSelf}, nil
|
2020-09-17 12:12:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-11 00:24:22 +00:00
|
|
|
func unwrap(value string) string {
|
|
|
|
return value[1 : len(value)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func arrayIndextoken(wrapped bool, checkForPre bool, checkForPost bool) lex.Action {
|
2020-09-17 12:12:56 +00:00
|
|
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
|
|
|
var numberString = string(m.Bytes)
|
|
|
|
if wrapped {
|
|
|
|
numberString = unwrap(numberString)
|
|
|
|
}
|
|
|
|
var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint
|
|
|
|
if errParsingInt != nil {
|
|
|
|
return nil, errParsingInt
|
|
|
|
}
|
2020-10-11 23:44:33 +00:00
|
|
|
return &Token{PathElementType: ArrayIndex, OperationType: None, Value: number, StringValue: numberString, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost}, nil
|
2020-09-17 12:12:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-17 11:58:01 +00:00
|
|
|
// Creates the lexer object and compiles the NFA.
|
|
|
|
func initLexer() (*lex.Lexer, error) {
|
|
|
|
lexer := lex.NewLexer()
|
2020-10-11 23:09:13 +00:00
|
|
|
lexer.Add([]byte(`\(`), literalToken(OpenBracket, "(", true, false, false))
|
|
|
|
lexer.Add([]byte(`\)`), literalToken(CloseBracket, ")", false, true, false))
|
|
|
|
lexer.Add([]byte(`\.\s*\)`), literalToken(CloseBracket, ")", false, true, true))
|
2020-10-11 00:24:22 +00:00
|
|
|
|
2020-10-11 23:09:13 +00:00
|
|
|
lexer.Add([]byte(`\[\+\]`), literalToken(PathKey, "[+]", true, true, false))
|
|
|
|
lexer.Add([]byte(`\[\*\]`), literalToken(PathKey, "[*]", true, true, false))
|
|
|
|
lexer.Add([]byte(`\*\*`), literalToken(PathKey, "**", false, false, false))
|
2020-10-11 00:24:22 +00:00
|
|
|
|
|
|
|
lexer.Add([]byte(`([Oo][Rr])`), opToken(Or, false))
|
|
|
|
lexer.Add([]byte(`([Aa][Nn][Dd])`), opToken(And, false))
|
2020-10-12 01:24:59 +00:00
|
|
|
lexer.Add([]byte(`([Cc][Oo][Uu][Nn][Tt])`), opToken(Count, false))
|
2020-10-13 03:04:21 +00:00
|
|
|
lexer.Add([]byte(`([Cc][Oo][Ll][Ll][Ee][Cc][Tt])`), opToken(Collect, false))
|
2020-10-11 00:24:22 +00:00
|
|
|
|
|
|
|
lexer.Add([]byte(`\.\s*==\s*`), opToken(Equals, true))
|
|
|
|
lexer.Add([]byte(`\s*==\s*`), opToken(Equals, false))
|
|
|
|
|
|
|
|
lexer.Add([]byte(`\.\s*.-\s*`), opToken(DeleteChild, true))
|
|
|
|
lexer.Add([]byte(`\s*.-\s*`), opToken(DeleteChild, false))
|
|
|
|
|
|
|
|
lexer.Add([]byte(`\.\s*:=\s*`), opToken(Assign, true))
|
|
|
|
lexer.Add([]byte(`\s*:=\s*`), opToken(Assign, false))
|
|
|
|
|
|
|
|
lexer.Add([]byte(`\[-?[0-9]+\]`), arrayIndextoken(true, true, true))
|
|
|
|
lexer.Add([]byte(`-?[0-9]+`), arrayIndextoken(false, false, false))
|
2020-09-17 11:58:01 +00:00
|
|
|
lexer.Add([]byte("( |\t|\n|\r)+"), skip)
|
2020-10-11 00:24:22 +00:00
|
|
|
|
|
|
|
lexer.Add([]byte(`"[^ "]+"`), pathToken(true))
|
|
|
|
lexer.Add([]byte(`[^ \.\[\(\)=]+`), pathToken(false))
|
|
|
|
|
|
|
|
lexer.Add([]byte(`\.`), opToken(Traverse, false))
|
2020-09-17 11:58:01 +00:00
|
|
|
err := lexer.Compile()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return lexer, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type PathTokeniser interface {
|
2020-10-11 00:24:22 +00:00
|
|
|
Tokenise(path string) ([]*Token, error)
|
2020-09-17 11:58:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type pathTokeniser struct {
|
|
|
|
lexer *lex.Lexer
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewPathTokeniser() PathTokeniser {
|
|
|
|
var lexer, err = initLexer()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return &pathTokeniser{lexer}
|
|
|
|
}
|
|
|
|
|
2020-10-11 00:24:22 +00:00
|
|
|
func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) {
|
2020-09-17 11:58:01 +00:00
|
|
|
scanner, err := p.lexer.Scanner([]byte(path))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-11 00:24:22 +00:00
|
|
|
var tokens []*Token
|
2020-09-17 11:58:01 +00:00
|
|
|
for tok, err, eof := scanner.Next(); !eof; tok, err, eof = scanner.Next() {
|
|
|
|
|
|
|
|
if tok != nil {
|
2020-10-11 00:24:22 +00:00
|
|
|
token := tok.(*Token)
|
2020-10-11 23:44:33 +00:00
|
|
|
log.Debugf("Tokenising %v - %v", token.Value, token.OperationType.Type)
|
2020-09-17 11:58:01 +00:00
|
|
|
tokens = append(tokens, token)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2020-10-11 00:24:22 +00:00
|
|
|
var postProcessedTokens = make([]*Token, 0)
|
2020-09-24 00:52:45 +00:00
|
|
|
|
|
|
|
for index, token := range tokens {
|
2020-10-11 00:24:22 +00:00
|
|
|
if index > 0 && token.CheckForPreTraverse &&
|
|
|
|
(tokens[index-1].PathElementType == PathKey || tokens[index-1].PathElementType == CloseBracket) {
|
|
|
|
postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: Operation, OperationType: Traverse, Value: "."})
|
|
|
|
}
|
2020-10-11 23:09:13 +00:00
|
|
|
if token.PrefixSelf {
|
2020-10-11 00:24:22 +00:00
|
|
|
postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: SelfReference, Value: "SELF"})
|
2020-09-24 00:52:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
postProcessedTokens = append(postProcessedTokens, token)
|
2020-10-11 00:24:22 +00:00
|
|
|
|
|
|
|
if index != len(tokens)-1 && token.CheckForPostTraverse &&
|
|
|
|
tokens[index+1].PathElementType == PathKey {
|
|
|
|
postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: Operation, OperationType: Traverse, Value: "."})
|
2020-09-24 00:52:45 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-17 11:58:01 +00:00
|
|
|
|
2020-09-24 00:52:45 +00:00
|
|
|
return postProcessedTokens, nil
|
2020-09-17 11:58:01 +00:00
|
|
|
}
|