Make until only work on string terminators
This commit is contained in:
parent
ddb61bdfc0
commit
0f854720ca
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
|
25
parser.go
25
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]
|
||||
})
|
||||
}
|
||||
|
@ -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{}
|
||||
|
Loading…
Reference in New Issue
Block a user