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:41:57 +02:00
|
|
|
// Seq matches all of the given parsers in order and returns their result 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-13 09:30:10 +02:00
|
|
|
return NewParser("Seq()", func(ps *State, node *Result) {
|
|
|
|
node.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-13 09:30:10 +02:00
|
|
|
parser(ps, &node.Child[i])
|
2017-08-06 15:32:10 +02:00
|
|
|
if ps.Errored() {
|
|
|
|
ps.Pos = startpos
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-06 06:31:35 +02:00
|
|
|
}
|
|
|
|
}
|
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-13 09:30:10 +02:00
|
|
|
return func(ps *State, node *Result) {
|
2017-08-13 11:27:41 +02:00
|
|
|
oldWS := ps.WS
|
2017-08-13 14:11:27 +02:00
|
|
|
ps.WS = NoWhitespace
|
2017-08-13 09:30:10 +02:00
|
|
|
parserfied(ps, node)
|
2017-08-13 11:27:41 +02:00
|
|
|
ps.WS = oldWS
|
2017-08-07 13:20:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-09 13:41:57 +02:00
|
|
|
// Any matches the first successful parser and returns its result
|
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-13 09:30:10 +02:00
|
|
|
// Records which parser was successful for each byte, and will use it first next time.
|
2017-08-06 06:31:35 +02:00
|
|
|
|
2017-08-13 09:30:10 +02:00
|
|
|
return NewParser("Any()", func(ps *State, node *Result) {
|
2017-08-13 13:20:41 +02:00
|
|
|
ps.WS(ps)
|
2017-08-13 07:28:43 +02:00
|
|
|
if ps.Pos >= len(ps.Input) {
|
|
|
|
ps.ErrorHere("!EOF")
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-13 07:28:43 +02:00
|
|
|
}
|
2017-08-06 15:32:10 +02:00
|
|
|
startpos := ps.Pos
|
2017-08-13 07:28:43 +02:00
|
|
|
|
2017-08-13 13:20:41 +02:00
|
|
|
longestError := ps.Error
|
2017-08-13 07:28:43 +02:00
|
|
|
if ps.Cut <= startpos {
|
|
|
|
ps.Recover()
|
|
|
|
} else {
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-13 07:28:43 +02:00
|
|
|
}
|
|
|
|
|
2018-06-11 03:54:35 +02:00
|
|
|
for _, parser := range parserfied {
|
2017-08-13 09:30:10 +02:00
|
|
|
parser(ps, node)
|
2017-08-06 15:32:10 +02:00
|
|
|
if ps.Errored() {
|
2017-08-13 07:28:43 +02:00
|
|
|
if ps.Error.pos >= longestError.pos {
|
2017-08-06 15:32:10 +02:00
|
|
|
longestError = ps.Error
|
|
|
|
}
|
2017-08-10 13:58:14 +02:00
|
|
|
if ps.Cut > startpos {
|
|
|
|
break
|
|
|
|
}
|
2017-08-09 13:18:14 +02:00
|
|
|
ps.Recover()
|
2017-08-06 06:31:35 +02:00
|
|
|
continue
|
|
|
|
}
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-06 06:31:35 +02:00
|
|
|
}
|
|
|
|
|
2017-08-06 15:32:10 +02:00
|
|
|
ps.Error = longestError
|
|
|
|
ps.Pos = startpos
|
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-13 09:30:10 +02:00
|
|
|
return func(ps *State, node *Result) {
|
2017-08-13 12:25:41 +02:00
|
|
|
node.Child = make([]Result, 0, 5)
|
2017-08-06 15:32:10 +02:00
|
|
|
startpos := ps.Pos
|
2017-08-06 06:31:35 +02:00
|
|
|
for {
|
2017-08-13 12:25:41 +02:00
|
|
|
node.Child = append(node.Child, Result{})
|
|
|
|
opParser(ps, &node.Child[len(node.Child)-1])
|
2017-08-06 15:32:10 +02:00
|
|
|
if ps.Errored() {
|
2017-08-13 12:25:41 +02:00
|
|
|
if len(node.Child)-1 < min || ps.Cut > ps.Pos {
|
2017-08-06 15:32:10 +02:00
|
|
|
ps.Pos = startpos
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-06 06:31:35 +02:00
|
|
|
}
|
2017-08-09 13:18:14 +02:00
|
|
|
ps.Recover()
|
2017-08-13 12:25:41 +02:00
|
|
|
node.Child = node.Child[0 : len(node.Child)-1]
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-06 06:31:35 +02:00
|
|
|
}
|
2017-08-06 15:32:10 +02:00
|
|
|
|
2017-08-07 14:38:34 +02:00
|
|
|
if sepParser != nil {
|
2017-08-13 09:30:10 +02:00
|
|
|
sepParser(ps, TrashResult)
|
2017-08-07 14:38:34 +02:00
|
|
|
if ps.Errored() {
|
2017-08-09 13:18:14 +02:00
|
|
|
ps.Recover()
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-07 14:38:34 +02:00
|
|
|
}
|
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-13 09:30:10 +02:00
|
|
|
return NewParser("Maybe()", func(ps *State, node *Result) {
|
2017-08-10 13:58:14 +02:00
|
|
|
startpos := ps.Pos
|
2017-08-13 09:30:10 +02:00
|
|
|
parserfied(ps, node)
|
2017-08-10 13:58:14 +02:00
|
|
|
if ps.Errored() && ps.Cut <= startpos {
|
2017-08-09 13:18:14 +02:00
|
|
|
ps.Recover()
|
2017-08-06 09:02:39 +02:00
|
|
|
}
|
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-13 09:30:10 +02:00
|
|
|
return func(ps *State, node *Result) {
|
|
|
|
p(ps, node)
|
2017-08-07 14:57:06 +02:00
|
|
|
if ps.Errored() {
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-07 14:57:06 +02:00
|
|
|
}
|
|
|
|
node.Result = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
|
2017-08-13 09:30:10 +02:00
|
|
|
func Map(parser Parserish, f func(n *Result)) Parser {
|
2017-08-06 09:02:39 +02:00
|
|
|
p := Parsify(parser)
|
|
|
|
|
2017-08-13 09:30:10 +02:00
|
|
|
return func(ps *State, node *Result) {
|
|
|
|
p(ps, node)
|
2017-08-06 15:32:10 +02:00
|
|
|
if ps.Errored() {
|
2017-08-13 09:30:10 +02:00
|
|
|
return
|
2017-08-06 09:02:39 +02:00
|
|
|
}
|
2017-08-13 09:30:10 +02:00
|
|
|
f(node)
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
2017-08-06 09:02:39 +02:00
|
|
|
}
|
|
|
|
|
2017-08-13 09:30:10 +02:00
|
|
|
func flatten(n *Result) {
|
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-13 09:30:10 +02:00
|
|
|
for _, child := range n.Child {
|
|
|
|
flatten(&child)
|
|
|
|
sbuf.WriteString(child.Token)
|
2017-08-06 09:02:39 +02:00
|
|
|
}
|
2017-08-13 09:30:10 +02:00
|
|
|
n.Token = sbuf.String()
|
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-13 09:30:10 +02:00
|
|
|
return Map(parser, flatten)
|
2017-08-06 06:31:35 +02:00
|
|
|
}
|