summaryrefslogtreecommitdiff
path: root/internal/mapr/token.go
blob: b8be4dacf1b1be94c0882712e5d3eb0dfd76b277 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package mapr

import (
	"strings"
)

var keywords = [...]string{"select", "from", "where", "group", "rorder", "order", "interval", "limit", "outfile"}

// Represents a parsed token, used to parse the mapr query.
type token struct {
	str        string
	isBareword bool
}

func (t token) isKeyword() bool {
	if !t.isBareword {
		return false
	}

	for _, keyword := range keywords {
		if strings.ToLower(t.str) == keyword {
			return true
		}
	}
	return false
}

func (t token) String() string {
	return t.str
}

func tokenize(queryStr string) []token {
	var tokens []token

	for i, part := range strings.Split(queryStr, "\"") {
		// Even i, means that it is not a quoted string
		if i%2 == 0 {
			commasStripped := strings.Replace(part, ",", " ", -1)
			for _, tokenStr := range strings.Fields(commasStripped) {
				token := token{
					str:        tokenStr,
					isBareword: true,
				}
				tokens = append(tokens, token)
			}
			continue
		}
		// Add whole quoted string as a token
		token := token{
			str:        part,
			isBareword: false,
		}
		tokens = append(tokens, token)
	}

	return tokens
}

func tokensConsume(tokens []token) ([]token, []token) {
	//logger.Trace("=====================")
	var consumed []token

	for i, t := range tokens {
		if t.isKeyword() {
			//logger.Trace("keyword", t)
			return tokens[i:], consumed
		}
		// strip escapes, such as ` from `foo`, this allows to use keywords as field names
		length := len(t.str)
		if length == 0 {
			continue
		}
		if t.str[0] == '`' && t.str[length-1] == '`' {
			stripped := t.str[1 : length-1]
			//logger.Trace("stripped", stripped)
			t := token{
				str:        stripped,
				isBareword: t.isBareword,
			}
			consumed = append(consumed, t)
			continue
		}
		//logger.Trace("bare", token)
		consumed = append(consumed, t)
	}

	//logger.Trace("result", consumed)
	return nil, consumed
}

func tokensConsumeStr(tokens []token) ([]token, []string) {
	var strings []string
	tokens, found := tokensConsume(tokens)
	for _, token := range found {
		strings = append(strings, token.str)
	}
	return tokens, strings
}

func tokensConsumeOptional(tokens []token, optional string) []token {
	if len(tokens) < 1 {
		return tokens
	}
	if strings.ToLower(tokens[0].str) == strings.ToLower(optional) {
		return tokens[1:]
	}
	return tokens
}