Make until only work on string terminators

This commit is contained in:
Adam Scarr 2017-08-15 18:57:00 +10:00
parent ddb61bdfc0
commit 0f854720ca
4 changed files with 40 additions and 45 deletions

View File

@ -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 // Bind will set the node .Result when the given parser matches
// This is useful for giving a value to keywords and constant literals // This is useful for giving a value to keywords and constant literals
// like true and false. See the json parser for an example. // like true and false. See the json parser for an example.

View File

@ -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) { func TestBind(t *testing.T) {
parser := Bind("true", true) parser := Bind("true", true)

View File

@ -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 // 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 { func NotChars(matcher string, repetition ...int) Parser {
return NewParser("!["+matcher+"]", charsImpl(matcher, true, repetition...)) return NewParser("!["+matcher+"]", charsImpl(matcher, true, repetition...))
} }
@ -244,3 +244,26 @@ func charsImpl(matcher string, stopOn bool, repetition ...int) Parser {
ps.Advance(matched) 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]
})
}

View File

@ -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) { func runParser(input string, parser Parser) (Result, *State) {
ps := NewState(input) ps := NewState(input)
result := Result{} result := Result{}