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
|
// 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.
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
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
|
// 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]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -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{}
|
||||||
|
Loading…
Reference in New Issue
Block a user