diff options
author | Adam Scarr <adam@vektah.net> | 2017-08-06 14:31:35 +1000 |
---|---|---|
committer | Adam Scarr <adam@vektah.net> | 2017-08-06 14:31:35 +1000 |
commit | 68cde88125e1f016c5706ca8d0b3db6ba06624a2 (patch) | |
tree | cdd2de679892179f537b22ab05bfe3820dc7b4bf /combinator.go |
Initial commit
Diffstat (limited to 'combinator.go')
-rw-r--r-- | combinator.go | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/combinator.go b/combinator.go new file mode 100644 index 0000000..2b6b8a3 --- /dev/null +++ b/combinator.go @@ -0,0 +1,114 @@ +package parsec + +func Nil(p Pointer) (Node, Pointer) { + return nil, p +} + +func Never(p Pointer) (Node, Pointer) { + return Error{p.pos, "Never matches"}, p +} + +func And(parsers ...Parserish) Parser { + if len(parsers) == 0 { + return Nil + } + + ps := ParsifyAll(parsers...) + + return func(p Pointer) (Node, Pointer) { + var nodes = make([]Node, 0, len(ps)) + var node Node + newP := p + for _, parser := range ps { + node, newP = parser(newP) + if node == nil { + continue + } + if IsError(node) { + return node, p + } + nodes = append(nodes, node) + } + return NewSequence(p.pos, nodes...), newP + } +} + +func Any(parsers ...Parserish) Parser { + if len(parsers) == 0 { + return Nil + } + + ps := ParsifyAll(parsers...) + + return func(p Pointer) (Node, Pointer) { + errors := []Error{} + for _, parser := range ps { + node, newP := parser(p) + if err, isErr := node.(Error); isErr { + errors = append(errors, err) + continue + } + return node, newP + } + + longestError := errors[0] + for _, e := range errors[1:] { + if e.pos > longestError.pos { + longestError = e + } + } + + return longestError, p + } +} + +func Kleene(opScan Parserish, sepScan ...Parserish) Parser { + return manyImpl(0, opScan, Never, sepScan...) +} + +func KleeneUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser { + return manyImpl(0, opScan, untilScan, sepScan...) +} + +func Many(opScan Parserish, sepScan ...Parserish) Parser { + return manyImpl(1, opScan, Never, sepScan...) +} + +func ManyUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser { + return manyImpl(1, opScan, untilScan, sepScan...) +} + +func manyImpl(min int, op Parserish, until Parserish, sep ...Parserish) Parser { + opParser := Parsify(op) + untilParser := Parsify(until) + sepParser := Nil + if len(sep) > 0 { + sepParser = Parsify(sep[0]) + } + + return func(p Pointer) (Node, Pointer) { + var node Node + nodes := make([]Node, 0) + newP := p + for { + if node, _ := untilParser(newP); !IsError(node) { + if len(nodes) < min { + return NewError(newP.pos, "Unexpected input"), p + } + break + } + + if node, newP = opParser(newP); IsError(node) { + if len(nodes) < min { + return node, p + } + break + } + nodes = append(nodes, node) + if node, newP = sepParser(newP); IsError(node) { + break + } + } + return NewSequence(p.pos, nodes...), newP + } +} |