mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
156 lines
2.7 KiB
Go
156 lines
2.7 KiB
Go
package query
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"log"
|
|
"pilosa/db"
|
|
"github.com/davecgh/go-spew/spew"
|
|
)
|
|
|
|
const (
|
|
TYPE_FUNC = iota
|
|
TYPE_LP = iota
|
|
TYPE_RP = iota
|
|
TYPE_ID = iota
|
|
)
|
|
|
|
type Token struct {
|
|
Text string
|
|
Type int
|
|
}
|
|
|
|
type statefn func(lexer *Lexer) statefn
|
|
|
|
type Lexer struct {
|
|
text string
|
|
pos int
|
|
start int
|
|
state int
|
|
ch chan Token
|
|
}
|
|
|
|
func (lexer *Lexer) emit(typ int) {
|
|
lexer.ch <- Token{lexer.text[lexer.start:lexer.pos], typ}
|
|
lexer.start = lexer.pos
|
|
}
|
|
|
|
func (lexer *Lexer) accept(char uint8) error {
|
|
for {
|
|
if lexer.text[lexer.pos] == char {
|
|
return nil
|
|
}
|
|
lexer.pos += 1
|
|
if lexer.pos > len(lexer.text) {
|
|
return errors.New("Parse error, expecting " + string(char))
|
|
}
|
|
}
|
|
}
|
|
|
|
func stateFunc(lexer *Lexer) statefn {
|
|
err := lexer.accept('(')
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
lexer.emit(TYPE_FUNC)
|
|
return stateLP
|
|
}
|
|
|
|
func stateLP(lexer *Lexer) statefn {
|
|
lexer.pos += 1
|
|
lexer.emit(TYPE_LP)
|
|
return stateID
|
|
}
|
|
|
|
func stateID(lexer *Lexer) statefn {
|
|
err := lexer.accept(')')
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
lexer.emit(TYPE_ID)
|
|
return stateRP
|
|
}
|
|
|
|
func stateRP(lexer *Lexer) statefn {
|
|
lexer.pos += 1
|
|
lexer.emit(TYPE_RP)
|
|
close(lexer.ch)
|
|
return nil
|
|
}
|
|
|
|
func (lexer *Lexer) Lex() []Token{
|
|
tokens := make([]Token, 0)
|
|
state := stateFunc
|
|
go func () {
|
|
for {
|
|
state = state(lexer)
|
|
if state == nil {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
for t := range lexer.ch {
|
|
spew.Dump(t)
|
|
tokens = append(tokens, t)
|
|
}
|
|
return tokens
|
|
}
|
|
|
|
func Lex(input string) []Token {
|
|
lexer := Lexer{input, 0, 0, TYPE_FUNC, make(chan Token)}
|
|
return lexer.Lex()
|
|
}
|
|
|
|
var InvalidQueryError = errors.New("Invalid query format.")
|
|
|
|
type QueryParser struct {
|
|
QueryString string
|
|
}
|
|
|
|
func (q *QueryParser) Walk(data interface{}) (*Query, error) {
|
|
query := new(Query)
|
|
|
|
slice, ok := data.([]interface{})
|
|
if !ok {
|
|
return nil, InvalidQueryError
|
|
}
|
|
operation, ok := slice[0].(string)
|
|
|
|
if !ok {
|
|
return nil, InvalidQueryError
|
|
}
|
|
if operation == "union" || operation == "intersect" {
|
|
query.Operation = operation
|
|
inputs := slice[1:]
|
|
query.Inputs = make([]QueryInput, len(inputs))
|
|
for idx, input := range inputs {
|
|
subquery, err := q.Walk(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
query.Inputs[idx] = subquery
|
|
}
|
|
} else if operation == "bitmap" {
|
|
query.Operation = "get"
|
|
frame, ok := slice[1].(string)
|
|
if !ok {
|
|
return nil, InvalidQueryError
|
|
}
|
|
id, ok := slice[2].(float64)
|
|
if !ok {
|
|
return nil, InvalidQueryError
|
|
}
|
|
id_int := int(id)
|
|
query.Inputs = []QueryInput{db.Bitmap{frame, id_int}}
|
|
}
|
|
|
|
return query, nil
|
|
}
|
|
|
|
func (q *QueryParser) Parse() (*Query, error) {
|
|
var data interface{}
|
|
if err := json.Unmarshal([]byte(q.QueryString), &data); err != nil {
|
|
return nil, err
|
|
}
|
|
return q.Walk(data)
|
|
}
|