asciigoat's core library
https://asciigoat.org/core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
2.7 KiB
124 lines
2.7 KiB
package runes |
|
|
|
import ( |
|
"unicode" |
|
) |
|
|
|
// Probe was borrowed from https://github.com/JamesOwenHall/json2.Scanner |
|
// |
|
|
|
// Probe is a func that returns a subset of the input and a success bool. |
|
type Probe func([]rune) ([]rune, bool) |
|
|
|
// If returns a probe that accepts the a rune if it satisfies the condition. |
|
func If(condition func(rune) bool) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
if len(input) > 0 && condition(input[0]) { |
|
return input[0:1], true |
|
} |
|
|
|
return nil, false |
|
} |
|
} |
|
|
|
// Rune returns a probe that accepts r. |
|
func Rune(r rune) Probe { |
|
return If(func(b rune) bool { |
|
return r == b |
|
}) |
|
} |
|
|
|
// Space returns a probe that accepts whitespace as defined in the unicode |
|
// package. |
|
func Space() Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
if len(input) > 0 && unicode.IsSpace(input[0]) { |
|
return input[0:1], true |
|
} |
|
|
|
return nil, false |
|
} |
|
} |
|
|
|
// And returns a probe that accepts all probes in sequence. |
|
func And(probes ...Probe) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
remaining := input |
|
accumulated := []rune{} |
|
|
|
for _, s := range probes { |
|
if read, ok := s(remaining); !ok { |
|
return nil, false |
|
} else { |
|
accumulated = append(accumulated, read...) |
|
remaining = remaining[len(read):] |
|
} |
|
} |
|
|
|
return accumulated, true |
|
} |
|
} |
|
|
|
// Or returns a probe that accepts the first successful probe in probes. |
|
func Or(probes ...Probe) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
for _, s := range probes { |
|
if read, ok := s(input); ok { |
|
return read, true |
|
} |
|
} |
|
|
|
return nil, false |
|
} |
|
} |
|
|
|
// Maybe runs probe and returns true regardless of the output. |
|
func Maybe(probe Probe) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
read, _ := probe(input) |
|
return read, true |
|
} |
|
} |
|
|
|
// Any returns a probe that accepts any number of occurrences of probe, |
|
// including zero. |
|
func Any(probe Probe) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
remaining := input |
|
accumulated := []rune{} |
|
|
|
for { |
|
if read, ok := probe(remaining); !ok { |
|
return accumulated, true |
|
} else { |
|
accumulated = append(accumulated, read...) |
|
remaining = remaining[len(read):] |
|
} |
|
} |
|
} |
|
} |
|
|
|
// N returns a probe that accepts probe exactly n times. |
|
func N(n int, probe Probe) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
probes := make([]Probe, n) |
|
for i := 0; i < n; i++ { |
|
probes[i] = probe |
|
} |
|
|
|
return And(probes...)(input) |
|
} |
|
} |
|
|
|
// AtLeast returns a probe that accepts probe at least n times. |
|
func AtLeast(n int, probe Probe) Probe { |
|
return func(input []rune) ([]rune, bool) { |
|
probes := make([]Probe, n, n+1) |
|
for i := range probes { |
|
probes[i] = probe |
|
} |
|
|
|
probes = append(probes, Any(probe)) |
|
return And(probes...)(input) |
|
} |
|
}
|
|
|