summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--combinator.go32
-rw-r--r--combinator_test.go14
-rw-r--r--state.go4
3 files changed, 49 insertions, 1 deletions
diff --git a/combinator.go b/combinator.go
index d7be3f9..4e7e4a5 100644
--- a/combinator.go
+++ b/combinator.go
@@ -26,7 +26,7 @@ func NoAutoWS(parser Parserish) Parser {
parserfied := Parsify(parser)
return func(ps *State, node *Result) {
oldWS := ps.WS
- ps.WS = func(ps *State) {}
+ ps.WS = NoWhitespace
parserfied(ps, node)
ps.WS = oldWS
}
@@ -145,6 +145,36 @@ func Maybe(parser Parserish) Parser {
})
}
+// Until will consume all input until one of the given parsers match. This is running every parser over every byte,
+// so its probably going to be slow.
+func Until(parsers ...Parserish) Parser {
+ parserfied := ParsifyAll(parsers...)
+ return NewParser("Until()", func(ps *State, node *Result) {
+ ws := ps.WS
+ ps.WS = NoWhitespace
+ defer func() {
+ ps.WS = ws
+ }()
+ startPos := ps.Pos
+ for ps.Pos < len(ps.Input) {
+ endPos := ps.Pos
+ for _, p := range parserfied {
+ ps.Pos = endPos
+
+ p(ps, node)
+
+ if !ps.Errored() {
+ node.Token = ps.Input[startPos:endPos]
+ return
+ }
+ ps.Recover()
+ }
+ ps.Pos++
+ }
+ node.Token = ps.Input[startPos:ps.Pos]
+ })
+}
+
// 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.
diff --git a/combinator_test.go b/combinator_test.go
index c1eedb3..e792c47 100644
--- a/combinator_test.go
+++ b/combinator_test.go
@@ -181,6 +181,20 @@ func TestMap(t *testing.T) {
})
}
+func TestUntil(t *testing.T) {
+ parser := Until("world", ".")
+
+ t.Run("success", func(t *testing.T) {
+ result, _ := runParser("this is the end of the world", parser)
+ require.Equal(t, "this is the end of the ", result.Token)
+ })
+
+ t.Run("eof", func(t *testing.T) {
+ result, _ := runParser("this is the end of it all", parser)
+ require.Equal(t, "this is the end of it all", result.Token)
+ })
+}
+
func TestBind(t *testing.T) {
parser := Bind("true", true)
diff --git a/state.go b/state.go
index 5d35ac8..9d081cf 100644
--- a/state.go
+++ b/state.go
@@ -44,6 +44,10 @@ func UnicodeWhitespace(s *State) {
}
s.Pos += w
}
+}
+
+// NoWhitespace disables automatic whitespace matching
+func NoWhitespace(s *State) {
}