Skip to content

Commit 3b46abb

Browse files
committed
adding COUNT
1 parent fcbec67 commit 3b46abb

File tree

4 files changed

+176
-163
lines changed

4 files changed

+176
-163
lines changed

parser.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ const (
1616
type AggregateMethod string
1717

1818
const (
19-
AGG_SUM = "SUM"
19+
AGG_SUM = "SUM"
20+
AGG_COUNT = "COUNT"
2021
)
2122

2223
type IField interface {
@@ -167,9 +168,15 @@ func tokenToField(tok Token, lit string) (*Field, error) {
167168

168169
func (p *Parser) parseField(stmt IStatement) (IField, error) {
169170
tok, field := p.scanIgnoreWhitespace()
170-
if tok == SUM {
171+
if tok == SUM || tok == COUNT {
172+
var m AggregateMethod
173+
if tok == SUM {
174+
m = AGG_SUM
175+
} else if tok == COUNT {
176+
m = AGG_COUNT
177+
}
171178
if targetField, err := p.parseField(stmt); err == nil {
172-
return &Aggregator{Field: Field{Name: targetField.GetName()}, Method: AGG_SUM, Target: targetField}, nil
179+
return &Aggregator{Field: Field{Name: targetField.GetName()}, Method: m, Target: targetField}, nil
173180
} else {
174181
return nil, err
175182
}

query.go

+2
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ func evalField(event_json map[string]interface{}, field IField) interface{} {
187187
}
188188
}
189189
return sum
190+
case AGG_COUNT:
191+
return len(collection)
190192
default:
191193
panic(fmt.Sprintf("No aggregate method found for %d", agg.Method))
192194
}

scanner.go

+120-116
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,146 @@
11
package main
22

33
import (
4-
"bufio"
5-
"bytes"
6-
"io"
7-
"regexp"
8-
"strings"
4+
"bufio"
5+
"bytes"
6+
"io"
7+
"regexp"
8+
"strings"
99
)
1010

1111
// Scanner represents a lexical scanner.
1212
type Scanner struct {
13-
r *bufio.Reader
13+
r *bufio.Reader
1414
}
1515

1616
// NewScanner returns a new instance of Scanner.
1717
func NewScanner(r io.Reader) *Scanner {
18-
return &Scanner{r: bufio.NewReader(r)}
18+
return &Scanner{r: bufio.NewReader(r)}
1919
}
2020

2121
// Scan returns the next token and literal value.
2222
func (s *Scanner) Scan() (tok Token, lit string) {
23-
// Read the next rune.
24-
ch := s.read()
25-
26-
// If we see whitespace then consume all contiguous whitespace.
27-
// If we see a letter then consume as an ident or reserved word.
28-
// If we see a digit then consume as a number.
29-
if isWhitespace(ch) {
30-
s.unread()
31-
return s.scanWhitespace()
32-
} else if (ch == eof) {
33-
return EOF, ""
34-
} else if (ch == ',') {
35-
return COMMA, string(ch)
36-
} else if (isValidCh(ch)) {
37-
s.unread()
38-
return s.scanToken()
39-
}
40-
41-
return ILLEGAL, string(ch)
23+
// Read the next rune.
24+
ch := s.read()
25+
26+
// If we see whitespace then consume all contiguous whitespace.
27+
// If we see a letter then consume as an ident or reserved word.
28+
// If we see a digit then consume as a number.
29+
if isWhitespace(ch) {
30+
s.unread()
31+
return s.scanWhitespace()
32+
} else if ch == eof {
33+
return EOF, ""
34+
} else if ch == ',' {
35+
return COMMA, string(ch)
36+
} else if isValidCh(ch) {
37+
s.unread()
38+
return s.scanToken()
39+
}
40+
41+
return ILLEGAL, string(ch)
4242
}
4343

4444
// scanWhitespace consumes the current rune and all contiguous whitespace.
4545
func (s *Scanner) scanWhitespace() (tok Token, lit string) {
46-
// Create a buffer and read the current character into it.
47-
var buf bytes.Buffer
48-
buf.WriteRune(s.read())
49-
50-
// Read every subsequent whitespace character into the buffer.
51-
// Non-whitespace characters and EOF will cause the loop to exit.
52-
for {
53-
if ch := s.read(); ch == eof {
54-
break
55-
} else if !isWhitespace(ch) {
56-
s.unread()
57-
break
58-
} else {
59-
buf.WriteRune(ch)
60-
}
61-
}
62-
63-
return WS, buf.String()
46+
// Create a buffer and read the current character into it.
47+
var buf bytes.Buffer
48+
buf.WriteRune(s.read())
49+
50+
// Read every subsequent whitespace character into the buffer.
51+
// Non-whitespace characters and EOF will cause the loop to exit.
52+
for {
53+
if ch := s.read(); ch == eof {
54+
break
55+
} else if !isWhitespace(ch) {
56+
s.unread()
57+
break
58+
} else {
59+
buf.WriteRune(ch)
60+
}
61+
}
62+
63+
return WS, buf.String()
6464
}
6565

6666
// scanToken consumes the current rune and all contiguous ident runes.
6767
func (s *Scanner) scanToken() (tok Token, lit string) {
68-
// Create a buffer and read the current character into it.
69-
var buf bytes.Buffer
70-
buf.WriteRune(s.read())
71-
72-
// Read every subsequent ident character into the buffer.
73-
// Non-ident characters and EOF will cause the loop to exit.
74-
for {
75-
if ch := s.read(); ch == eof {
76-
break
77-
} else if isWhitespace(ch) || !isValidCh(ch) {
78-
s.unread()
79-
break
80-
} else {
81-
_, _ = buf.WriteRune(ch)
82-
}
83-
}
84-
85-
// If the string matches a keyword then return that keyword.
86-
switch strings.ToUpper(buf.String()) {
87-
case "MAP":
88-
return MAP, buf.String()
89-
case "REDUCE":
90-
return REDUCE, buf.String()
91-
case "ON":
92-
return ON, buf.String()
93-
case "WHERE":
94-
return WHERE, buf.String()
95-
case "AND":
96-
return AND, buf.String()
97-
case "IN":
98-
return IN, buf.String()
99-
case "SUM":
100-
return SUM, buf.String()
101-
}
102-
103-
// Match operators
104-
switch buf.String() {
105-
case "<":
106-
return LT, buf.String()
107-
case "<=":
108-
return LTE, buf.String()
109-
case ">":
110-
return GT, buf.String()
111-
case ">=":
112-
return GTE, buf.String()
113-
case "=":
114-
return EQ, buf.String()
115-
case "!=":
116-
return NOT_EQ, buf.String()
117-
}
118-
119-
// Check for string literal or ident
120-
var str string = buf.String()
121-
if str[0] == '"' && str[len(str)-1] == '"' {
122-
str = str[1:len(str)-1]
123-
return STRING, str
124-
}
125-
126-
// Check for number
127-
if match, _ := regexp.MatchString("([0-9.]+)", buf.String()); match {
128-
return NUMBER, buf.String()
129-
}
130-
131-
return IDENT, buf.String()
68+
// Create a buffer and read the current character into it.
69+
var buf bytes.Buffer
70+
buf.WriteRune(s.read())
71+
72+
// Read every subsequent ident character into the buffer.
73+
// Non-ident characters and EOF will cause the loop to exit.
74+
for {
75+
if ch := s.read(); ch == eof {
76+
break
77+
} else if isWhitespace(ch) || !isValidCh(ch) {
78+
s.unread()
79+
break
80+
} else {
81+
_, _ = buf.WriteRune(ch)
82+
}
83+
}
84+
85+
// If the string matches a keyword then return that keyword.
86+
switch strings.ToUpper(buf.String()) {
87+
case "MAP":
88+
return MAP, buf.String()
89+
case "REDUCE":
90+
return REDUCE, buf.String()
91+
case "ON":
92+
return ON, buf.String()
93+
case "WHERE":
94+
return WHERE, buf.String()
95+
case "AND":
96+
return AND, buf.String()
97+
case "IN":
98+
return IN, buf.String()
99+
case "SUM":
100+
return SUM, buf.String()
101+
case "COUNT":
102+
return COUNT, buf.String()
103+
}
104+
105+
// Match operators
106+
switch buf.String() {
107+
case "<":
108+
return LT, buf.String()
109+
case "<=":
110+
return LTE, buf.String()
111+
case ">":
112+
return GT, buf.String()
113+
case ">=":
114+
return GTE, buf.String()
115+
case "=":
116+
return EQ, buf.String()
117+
case "!=":
118+
return NOT_EQ, buf.String()
119+
}
120+
121+
// Check for string literal or ident
122+
var str string = buf.String()
123+
if str[0] == '"' && str[len(str)-1] == '"' {
124+
str = str[1 : len(str)-1]
125+
return STRING, str
126+
}
127+
128+
// Check for number
129+
if match, _ := regexp.MatchString("([0-9.]+)", buf.String()); match {
130+
return NUMBER, buf.String()
131+
}
132+
133+
return IDENT, buf.String()
132134
}
133135

134136
// read reads the next rune from the bufferred reader.
135137
// Returns the rune(0) if an error occurs (or io.EOF is returned).
136138
func (s *Scanner) read() rune {
137-
ch, _, err := s.r.ReadRune()
138-
if err != nil {
139-
return eof
140-
}
141-
return ch
139+
ch, _, err := s.r.ReadRune()
140+
if err != nil {
141+
return eof
142+
}
143+
return ch
142144
}
143145

144146
// unread places the previously read rune back on the reader.
@@ -153,10 +155,12 @@ func isLetter(ch rune) bool { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && c
153155
// isDigit returns true if the rune is a digit.
154156
func isDigit(ch rune) bool { return (ch >= '0' && ch <= '9') }
155157

156-
func isOperator(ch rune) bool { return (ch == '<' || ch == '>' || ch == '=' || ch == '+' || ch == '-' || ch == '/' || ch == '*') }
158+
func isOperator(ch rune) bool {
159+
return (ch == '<' || ch == '>' || ch == '=' || ch == '+' || ch == '-' || ch == '/' || ch == '*')
160+
}
157161

158-
func isValidCh(ch rune) bool {
159-
return isWhitespace(ch) || isLetter(ch) || isDigit(ch) || isOperator(ch) || ch == '_' || ch == '"' || ch == '.' || ch == '@' || ch == '!'
162+
func isValidCh(ch rune) bool {
163+
return isWhitespace(ch) || isLetter(ch) || isDigit(ch) || isOperator(ch) || ch == '_' || ch == '"' || ch == '.' || ch == '@' || ch == '!'
160164
}
161165

162166
// eof represents a marker rune for the end of the reader.

0 commit comments

Comments
 (0)