1
0
Fork 0
kifflom/parser/parser.go

166 lines
3.0 KiB
Go

package parser
import (
"fmt"
"strconv"
"strings"
"github.com/localhots/punk/buffer"
"github.com/localhots/punk/lexer"
)
type (
// Holds the state of parser
Parser struct {
lex *lexer.Lexer
ctx *context
sels map[string]*context
res map[string][]lexer.Item
}
)
// Creates a new parser
func New(buf buffer.Bufferer, sels []string) *Parser {
return &Parser{
lex: lexer.New(buf),
ctx: &context{
exps: []expectation{},
},
sels: parseSelectors(sels),
res: map[string][]lexer.Item{},
}
}
// Starts parsing
func (p *Parser) Parse() map[string][]interface{} {
go p.lex.Run()
p.parseValue(p.next())
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)
}
}
}
return out
}
func (p *Parser) parseValue(item lexer.Item) {
switch item.Token {
case lexer.Null, lexer.Bool, lexer.Number, lexer.String:
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()
default:
unexpected(item)
}
}
// Parses array recursively part by part
// Is called after '[' and ',' tokens
// Expects a value followed by ']' or ',' tokens
func (p *Parser) parseArray(i int64) {
item := p.next()
if item.Token == lexer.BracketClose {
// Neither a bug nor a feature
// This allows an array to have a trailing comma
// [1, 2, 3, ]
return
}
p.ctx.setIndex(i)
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:
// Neither a bug nor a feature
// This allows an object to have a trailing comma
// {"foo": 1, "bar": 2, }
return
case lexer.String:
p.ctx.setKey(item.Val)
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)
}
}
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
}
}
}
func (p *Parser) next() lexer.Item {
if item, ok := p.lex.NextItem(); ok {
if item.Token == lexer.Error {
panic(item)
}
fmt.Println(item)
return item
} else {
panic("EOF reached")
}
}
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)
}
}
return
}
func unexpected(item lexer.Item) {
panic(fmt.Errorf("Unexpected token: %s", item.String()))
}