2015-02-16 09:02:49 +00:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
2015-02-16 13:36:23 +00:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2015-02-16 09:02:49 +00:00
|
|
|
"github.com/localhots/punk/lexer"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2015-02-17 15:47:22 +00:00
|
|
|
// Holds the state of parser
|
2015-02-16 09:02:49 +00:00
|
|
|
Parser struct {
|
2015-02-17 15:47:22 +00:00
|
|
|
lex *lexer.Lexer
|
|
|
|
ctx *context
|
|
|
|
sels map[string]*context
|
|
|
|
res map[string][]lexer.Item
|
2015-02-16 09:02:49 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
// Creates a new parser
|
|
|
|
func New(b []byte, sels []string) *Parser {
|
|
|
|
return &Parser{
|
|
|
|
lex: lexer.New(string(b)),
|
|
|
|
ctx: &context{
|
|
|
|
exps: []expectation{},
|
|
|
|
},
|
|
|
|
sels: parseSelectors(sels),
|
|
|
|
res: map[string][]lexer.Item{},
|
2015-02-16 13:36:23 +00:00
|
|
|
}
|
2015-02-16 17:06:33 +00:00
|
|
|
}
|
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
// Starts parsing
|
|
|
|
func (p *Parser) Parse() map[string][]interface{} {
|
2015-02-16 17:06:33 +00:00
|
|
|
go p.lex.Run()
|
|
|
|
p.parseValue(p.next())
|
2015-02-16 13:36:23 +00:00
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
out := map[string][]interface{}{}
|
|
|
|
for sel, res := range p.res {
|
|
|
|
for _, item := range res {
|
|
|
|
if val, err := castValue(item); err == nil {
|
|
|
|
out[sel] = append(out[sel], val)
|
|
|
|
} else {
|
|
|
|
out[sel] = append(out[sel], err)
|
|
|
|
}
|
|
|
|
}
|
2015-02-16 17:06:33 +00:00
|
|
|
}
|
2015-02-16 13:36:23 +00:00
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) parseValue(item lexer.Item) {
|
2015-02-16 17:06:33 +00:00
|
|
|
switch item.Token {
|
|
|
|
case lexer.Null, lexer.Bool, lexer.Number, lexer.String:
|
2015-02-17 15:47:22 +00:00
|
|
|
p.pushValue(item)
|
|
|
|
case lexer.BraceOpen:
|
|
|
|
p.ctx.push(object)
|
|
|
|
p.parseObject()
|
|
|
|
p.ctx.pop()
|
|
|
|
case lexer.BracketOpen:
|
|
|
|
p.ctx.push(array)
|
|
|
|
p.parseArray(0)
|
|
|
|
p.ctx.pop()
|
2015-02-16 17:06:33 +00:00
|
|
|
default:
|
2015-02-17 15:47:22 +00:00
|
|
|
unexpected(item)
|
2015-02-16 13:36:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
// Parses array recursively part by part
|
|
|
|
// Is called after '[' and ',' tokens
|
|
|
|
// Expects a value followed by ']' or ',' tokens
|
|
|
|
func (p *Parser) parseArray(i int64) {
|
|
|
|
p.ctx.setIndex(i)
|
2015-02-16 17:06:33 +00:00
|
|
|
item := p.next()
|
|
|
|
if item.Token == lexer.BracketClose {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
p.parseValue(item)
|
|
|
|
|
|
|
|
switch item := p.next(); item.Token {
|
|
|
|
case lexer.BracketClose:
|
|
|
|
return
|
|
|
|
case lexer.Comma:
|
|
|
|
p.parseArray(i + 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) parseObject() {
|
|
|
|
item := p.next()
|
|
|
|
switch item.Token {
|
|
|
|
case lexer.BraceClose:
|
|
|
|
return
|
|
|
|
case lexer.String:
|
2015-02-17 15:47:22 +00:00
|
|
|
p.ctx.setKey(item.Val)
|
2015-02-16 17:06:33 +00:00
|
|
|
default:
|
|
|
|
unexpected(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
if item := p.next(); item.Token != lexer.Colon {
|
|
|
|
unexpected(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.parseValue(p.next())
|
|
|
|
|
|
|
|
switch item := p.next(); item.Token {
|
|
|
|
case lexer.BraceClose:
|
|
|
|
return
|
|
|
|
case lexer.Comma:
|
|
|
|
p.parseObject()
|
|
|
|
default:
|
|
|
|
unexpected(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
func (p *Parser) pushValue(item lexer.Item) {
|
|
|
|
for sel, exp := range p.sels {
|
|
|
|
if ok := exp.compare(p.ctx); ok {
|
|
|
|
p.res[sel] = append(p.res[sel], item)
|
|
|
|
return
|
2015-02-16 17:06:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) next() lexer.Item {
|
|
|
|
if item, ok := p.lex.NextItem(); ok {
|
|
|
|
fmt.Println(item)
|
|
|
|
return item
|
|
|
|
} else {
|
|
|
|
panic("EOF reached")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
func castValue(item lexer.Item) (val interface{}, err error) {
|
|
|
|
switch item.Token {
|
|
|
|
case lexer.Null:
|
|
|
|
val = nil
|
|
|
|
case lexer.Bool:
|
|
|
|
val = (item.Val == "true")
|
|
|
|
case lexer.String:
|
|
|
|
val = item.Val
|
|
|
|
case lexer.Number:
|
|
|
|
if strings.Index(item.Val, ".") > -1 {
|
|
|
|
val, err = strconv.ParseFloat(item.Val, 64)
|
|
|
|
} else {
|
|
|
|
val, err = strconv.ParseInt(item.Val, 10, 64)
|
2015-02-16 13:36:23 +00:00
|
|
|
}
|
2015-02-16 09:02:49 +00:00
|
|
|
}
|
2015-02-17 15:47:22 +00:00
|
|
|
return
|
2015-02-16 09:02:49 +00:00
|
|
|
}
|
2015-02-16 17:06:33 +00:00
|
|
|
|
2015-02-17 15:47:22 +00:00
|
|
|
func unexpected(item lexer.Item) {
|
|
|
|
panic(fmt.Errorf("Unexpected token: %s", item.String()))
|
2015-02-16 17:06:33 +00:00
|
|
|
}
|