1
0
Fork 0
kifflom/parser/parser.go

204 lines
3.6 KiB
Go
Raw Normal View History

2015-02-16 09:02:49 +00:00
package parser
import (
2015-02-16 13:36:23 +00:00
"fmt"
"strconv"
"strings"
2015-02-26 11:02:50 +00:00
"github.com/localhots/kifflom/buffer"
"github.com/localhots/kifflom/lexer"
2015-02-16 09:02:49 +00:00
)
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-18 16:05:45 +00:00
lex *lexer.Lexer
ctx *context
sels map[string]*context
res chan Match
verbose bool
2015-02-18 15:04:26 +00:00
}
Match struct {
Sel string
Val interface{}
2015-02-16 09:02:49 +00:00
}
)
2015-02-17 15:47:22 +00:00
// Creates a new parser
2015-02-23 13:07:19 +00:00
func New(buf *buffer.Buffer, sels []string) *Parser {
2015-02-17 15:47:22 +00:00
return &Parser{
2015-02-18 13:26:53 +00:00
lex: lexer.New(buf),
2015-02-17 15:47:22 +00:00
ctx: &context{
exps: []expectation{},
},
sels: parseSelectors(sels),
2015-02-18 15:04:26 +00:00
res: make(chan Match),
2015-02-16 13:36:23 +00:00
}
2015-02-16 17:06:33 +00:00
}
2015-02-18 16:05:45 +00:00
func (p *Parser) Debug() {
p.verbose = true
}
2015-02-18 15:33:57 +00:00
// Parse all and return matches
2015-02-17 15:47:22 +00:00
func (p *Parser) Parse() map[string][]interface{} {
2015-02-18 15:04:26 +00:00
p.ParseStream()
2015-02-17 15:47:22 +00:00
out := map[string][]interface{}{}
2015-02-18 15:04:26 +00:00
for {
if m, ok := <-p.res; ok {
out[m.Sel] = append(out[m.Sel], m.Val)
} else {
break
2015-02-17 15:47:22 +00:00
}
2015-02-16 17:06:33 +00:00
}
2015-02-17 15:47:22 +00:00
return out
}
2015-02-18 15:33:57 +00:00
// Starts parsing
2015-02-18 15:04:26 +00:00
func (p *Parser) ParseStream() <-chan Match {
go p.lex.Run()
go func() {
defer func() {
if err := recover(); err != nil {
fmt.Println("\nParse error! Yay!")
fmt.Println(err)
}
close(p.res)
}()
for {
if item := p.next(); item.Token != lexer.EOF {
p.parseValue(item)
} else {
break
}
}
}()
return p.res
}
2015-02-17 15:47:22 +00:00
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) {
2015-02-16 17:06:33 +00:00
item := p.next()
if item.Token == lexer.BracketClose {
2015-02-17 16:00:09 +00:00
// Neither a bug nor a feature
// This allows an array to have a trailing comma
// [1, 2, 3, ]
2015-02-16 17:06:33 +00:00
return
}
2015-02-17 16:00:09 +00:00
p.ctx.setIndex(i)
2015-02-16 17:06:33 +00:00
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:
2015-02-17 16:00:09 +00:00
// Neither a bug nor a feature
// This allows an object to have a trailing comma
// {"foo": 1, "bar": 2, }
2015-02-16 17:06:33 +00:00
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 {
2015-02-18 15:04:26 +00:00
if val, err := castValue(item); err == nil {
p.res <- Match{
Sel: sel,
Val: val,
}
} else {
p.res <- Match{
Sel: sel,
Val: err,
}
}
2015-02-17 15:47:22 +00:00
return
2015-02-16 17:06:33 +00:00
}
}
}
func (p *Parser) next() lexer.Item {
if item, ok := p.lex.NextItem(); ok {
if item.Token == lexer.Error {
panic(item)
}
2015-02-18 16:05:45 +00:00
if p.verbose {
fmt.Println(item)
}
2015-02-16 17:06:33 +00:00
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
}