summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Scarr <adam@vektah.net>2017-08-15 18:57:00 +1000
committerAdam Scarr <adam@vektah.net>2017-08-15 18:57:00 +1000
commit0f854720ca2bad30246020bb01cdb903a5f9406d (patch)
treef4acedaa6928300fecd7791a68cc493c37938e5e
parentddb61bdfc034a683f755dec6ddc80627d2d43522 (diff)
Make until only work on string terminators
-rw-r--r--combinator.go30
-rw-r--r--combinator_test.go14
-rw-r--r--parser.go25
-rw-r--r--parser_test.go16
4 files changed, 40 insertions, 45 deletions
diff --git a/combinator.go b/combinator.go
index 4e7e4a5..38145ee 100644
--- a/combinator.go
+++ b/combinator.go
@@ -145,36 +145,6 @@ 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 e792c47..c1eedb3 100644
--- a/combinator_test.go
+++ b/combinator_test.go
@@ -181,20 +181,6 @@ 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/parser.go b/parser.go
index fecf635..a612857 100644
--- a/parser.go
+++ b/parser.go
@@ -200,7 +200,7 @@ func Chars(matcher string, repetition ...int) Parser {
}
// NotChars accepts the full range of input from Chars, but it will stop when any
-// character matches.
+// character matches. If you need to match until you see a sequence use Until instead
func NotChars(matcher string, repetition ...int) Parser {
return NewParser("!["+matcher+"]", charsImpl(matcher, true, repetition...))
}
@@ -244,3 +244,26 @@ func charsImpl(matcher string, stopOn bool, repetition ...int) Parser {
ps.Advance(matched)
}
}
+
+// Until will consume all input until one of the given terminator sequences is found. If you want to stop when seeing
+// single characters see NotChars instead
+func Until(terminators ...string) Parser {
+
+ return NewParser("Until", func(ps *State, node *Result) {
+ startPos := ps.Pos
+ loop:
+ for ps.Pos < len(ps.Input) {
+ for _, terminator := range terminators {
+ if ps.Pos+len(terminator) <= len(ps.Input) && ps.Input[ps.Pos:ps.Pos+len(terminator)] == terminator {
+ break loop
+ }
+ }
+ ps.Pos++
+ }
+
+ if ps.Pos == startPos {
+ ps.ErrorHere("something")
+ }
+ node.Token = ps.Input[startPos:ps.Pos]
+ })
+}
diff --git a/parser_test.go b/parser_test.go
index ce5f3e7..aaed25f 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -214,6 +214,22 @@ func TestAutoWS(t *testing.T) {
})
}
+func TestUntil(t *testing.T) {
+ parser := Until("world", ".")
+
+ t.Run("success", func(t *testing.T) {
+ result, ps := runParser("this is the end of the world", parser)
+ require.Equal(t, "this is the end of the ", result.Token)
+ require.Equal(t, "world", ps.Get())
+ })
+
+ t.Run("eof", func(t *testing.T) {
+ result, ps := runParser("this is the end of it all", parser)
+ require.Equal(t, "this is the end of it all", result.Token)
+ require.Equal(t, "", ps.Get())
+ })
+}
+
func runParser(input string, parser Parser) (Result, *State) {
ps := NewState(input)
result := Result{}