goparsify/combinator.go

176 lines
3.9 KiB
Go
Raw Normal View History

2017-08-07 12:07:29 +02:00
package goparsify
2017-08-06 06:31:35 +02:00
2017-08-06 09:02:39 +02:00
import (
"bytes"
)
2017-08-09 13:18:14 +02:00
// Seq matches all of the given parsers in order and returns their nodes as .Child[n]
2017-08-09 11:35:15 +02:00
func Seq(parsers ...Parserish) Parser {
2017-08-06 15:32:10 +02:00
parserfied := ParsifyAll(parsers...)
2017-08-06 06:31:35 +02:00
2017-08-09 13:18:14 +02:00
return NewParser("Seq()", func(ps *State) Result {
result := Result{Child: make([]Result, len(parserfied))}
2017-08-06 15:32:10 +02:00
startpos := ps.Pos
2017-08-07 14:38:34 +02:00
for i, parser := range parserfied {
2017-08-08 15:11:47 +02:00
result.Child[i] = parser(ps)
2017-08-06 15:32:10 +02:00
if ps.Errored() {
ps.Pos = startpos
2017-08-07 14:38:34 +02:00
return result
2017-08-06 06:31:35 +02:00
}
}
2017-08-07 14:38:34 +02:00
return result
2017-08-07 12:07:29 +02:00
})
2017-08-06 06:31:35 +02:00
}
2017-08-09 13:18:14 +02:00
// NoAutoWS disables automatically ignoring whitespace between tokens for all parsers underneath
2017-08-07 13:20:30 +02:00
func NoAutoWS(parser Parserish) Parser {
parserfied := Parsify(parser)
2017-08-09 13:18:14 +02:00
return func(ps *State) Result {
2017-08-07 13:20:30 +02:00
ps.NoAutoWS = true
ret := parserfied(ps)
ps.NoAutoWS = false
return ret
}
}
2017-08-09 13:18:14 +02:00
// Any matches the first successful parser and returns its node
2017-08-06 06:31:35 +02:00
func Any(parsers ...Parserish) Parser {
2017-08-06 15:32:10 +02:00
parserfied := ParsifyAll(parsers...)
2017-08-06 06:31:35 +02:00
2017-08-09 13:18:14 +02:00
return NewParser("Any()", func(ps *State) Result {
2017-08-06 15:32:10 +02:00
longestError := Error{}
startpos := ps.Pos
for _, parser := range parserfied {
node := parser(ps)
if ps.Errored() {
if ps.Error.pos > longestError.pos {
longestError = ps.Error
}
2017-08-09 13:18:14 +02:00
ps.Recover()
2017-08-06 06:31:35 +02:00
continue
}
2017-08-06 15:32:10 +02:00
return node
2017-08-06 06:31:35 +02:00
}
2017-08-06 15:32:10 +02:00
ps.Error = longestError
ps.Pos = startpos
2017-08-09 13:18:14 +02:00
return Result{}
2017-08-07 12:07:29 +02:00
})
2017-08-06 06:31:35 +02:00
}
2017-08-09 13:18:14 +02:00
// Some matches one or more parsers and returns the value as .Child[n]
// an optional separator can be provided and that value will be consumed
// but not returned. Only one separator can be provided.
func Some(parser Parserish, separator ...Parserish) Parser {
return NewParser("Some()", manyImpl(0, parser, separator...))
2017-08-06 06:31:35 +02:00
}
2017-08-09 13:18:14 +02:00
// Many matches zero or more parsers and returns the value as .Child[n]
// an optional separator can be provided and that value will be consumed
// but not returned. Only one separator can be provided.
func Many(parser Parserish, separator ...Parserish) Parser {
return NewParser("Many()", manyImpl(1, parser, separator...))
2017-08-06 06:31:35 +02:00
}
2017-08-07 14:38:34 +02:00
func manyImpl(min int, op Parserish, sep ...Parserish) Parser {
var opParser = Parsify(op)
var sepParser Parser
2017-08-06 06:31:35 +02:00
if len(sep) > 0 {
sepParser = Parsify(sep[0])
}
2017-08-09 13:18:14 +02:00
return func(ps *State) Result {
var result Result
2017-08-06 15:32:10 +02:00
startpos := ps.Pos
2017-08-06 06:31:35 +02:00
for {
2017-08-07 14:38:34 +02:00
node := opParser(ps)
2017-08-06 15:32:10 +02:00
if ps.Errored() {
2017-08-08 15:11:47 +02:00
if len(result.Child) < min {
2017-08-06 15:32:10 +02:00
ps.Pos = startpos
2017-08-07 14:38:34 +02:00
return result
2017-08-06 06:31:35 +02:00
}
2017-08-09 13:18:14 +02:00
ps.Recover()
2017-08-07 14:38:34 +02:00
return result
2017-08-06 06:31:35 +02:00
}
2017-08-08 15:11:47 +02:00
result.Child = append(result.Child, node)
2017-08-06 15:32:10 +02:00
2017-08-07 14:38:34 +02:00
if sepParser != nil {
sepParser(ps)
if ps.Errored() {
2017-08-09 13:18:14 +02:00
ps.Recover()
2017-08-07 14:38:34 +02:00
return result
}
2017-08-06 06:31:35 +02:00
}
}
2017-08-06 09:02:39 +02:00
}
}
2017-08-09 13:18:14 +02:00
// Maybe will 0 or 1 of the parser
2017-08-06 09:02:39 +02:00
func Maybe(parser Parserish) Parser {
2017-08-06 15:32:10 +02:00
parserfied := Parsify(parser)
2017-08-06 09:02:39 +02:00
2017-08-09 13:18:14 +02:00
return NewParser("Maybe()", func(ps *State) Result {
2017-08-06 15:32:10 +02:00
node := parserfied(ps)
if ps.Errored() {
2017-08-09 13:18:14 +02:00
ps.Recover()
2017-08-06 09:02:39 +02:00
}
2017-08-06 15:32:10 +02:00
return node
2017-08-07 12:07:29 +02:00
})
2017-08-06 09:02:39 +02:00
}
2017-08-09 13:18:14 +02:00
// Bind will set the node .Result when the given parser matches
// This is useful for giving a value to keywords and constant literals
// like true and false. See the json parser for an example.
2017-08-07 14:57:06 +02:00
func Bind(parser Parserish, val interface{}) Parser {
p := Parsify(parser)
2017-08-09 13:18:14 +02:00
return func(ps *State) Result {
2017-08-07 14:57:06 +02:00
node := p(ps)
if ps.Errored() {
return node
}
node.Result = val
return node
}
}
2017-08-09 13:18:14 +02:00
// Map applies the callback if the parser matches. This is used to set the Result
// based on the matched result.
func Map(parser Parserish, f func(n Result) Result) Parser {
2017-08-06 09:02:39 +02:00
p := Parsify(parser)
2017-08-09 13:18:14 +02:00
return NewParser("Map()", func(ps *State) Result {
2017-08-06 15:32:10 +02:00
node := p(ps)
if ps.Errored() {
2017-08-07 14:38:34 +02:00
return node
2017-08-06 09:02:39 +02:00
}
2017-08-06 15:32:10 +02:00
return f(node)
2017-08-07 12:07:29 +02:00
})
2017-08-06 09:02:39 +02:00
}
2017-08-09 13:18:14 +02:00
func flatten(n Result) string {
2017-08-07 10:25:23 +02:00
if n.Token != "" {
return n.Token
2017-08-06 06:31:35 +02:00
}
2017-08-06 09:02:39 +02:00
2017-08-08 15:11:47 +02:00
if len(n.Child) > 0 {
2017-08-06 09:02:39 +02:00
sbuf := &bytes.Buffer{}
2017-08-08 15:11:47 +02:00
for _, node := range n.Child {
2017-08-07 10:25:23 +02:00
sbuf.WriteString(flatten(node))
2017-08-06 09:02:39 +02:00
}
return sbuf.String()
}
2017-08-07 10:25:23 +02:00
return ""
2017-08-06 09:02:39 +02:00
}
2017-08-09 13:18:14 +02:00
// Merge all child Tokens together recursively
2017-08-06 09:02:39 +02:00
func Merge(parser Parserish) Parser {
2017-08-09 13:18:14 +02:00
return NewParser("Merge()", Map(parser, func(n Result) Result {
return Result{Token: flatten(n)}
2017-08-07 12:07:29 +02:00
}))
2017-08-06 06:31:35 +02:00
}