-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodifier.go
123 lines (110 loc) · 3.47 KB
/
modifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package chomp
import (
"fmt"
"strings"
)
// MappedCombinator is a function capable of converting the output from a [Combinator]
// into any given type. Upon success, it will return the unparsed text, along with the
// mapped value. All combinators are strict and must parse its input. Any failure to do
// so should raise a [CombinatorParseError]. It is designed for exclusive use by the
// [Map] function
type MappedCombinator[S any, T Result] func(string) (string, S, error)
// Map the result of a [Combinator] to any other type
//
// chomp.Map(
// chomp.While(chomp.IsDigit),
// func (in string) int { return len(in) })("123456")
// // ("", 6, nil)
func Map[S any, T Result](c Combinator[T], mapper func(in T) S) MappedCombinator[S, T] {
return func(s string) (string, S, error) {
var mapped S
rem, out, err := c(s)
if err != nil {
return rem, mapped, err
}
mapped = mapper(out)
return rem, mapped, nil
}
}
// Opt allows a [Combinator] to be optional by discarding its returned
// error and not modifying the input text upon failure.
//
// chomp.Opt(chomp.Tag("Hey"))("Hello, World!")
// // ("Hello, World!", "", nil)
func Opt[T Result](c Combinator[T]) Combinator[T] {
return func(s string) (string, T, error) {
rem, out, _ := c(s)
return rem, out, nil
}
}
// S wraps the result of the inner [Combinator] within a string slice.
// Combinators of differing return types can be successfully chained
// together while using this conversion combinator.
//
// chomp.S(chomp.Until(","))("Hello, World!")
// // (", World!", []string{"Hello"}, nil)
func S(c Combinator[string]) Combinator[[]string] {
return func(s string) (string, []string, error) {
rem, ext, err := c(s)
if err != nil {
return rem, nil, err
}
return rem, []string{ext}, err
}
}
// I extracts and returns a single string from the result of the inner
// [Combinator]. Combinators of differing return types can be successfully
// chained together while using this conversion combinator.
//
// chomp.I(chomp.SepPair(
// chomp.Tag("Hello"),
// chomp.Tag(", "),
// chomp.Tag("World")), 1)("Hello, World!")
// // ("!", "World", nil)
func I(c Combinator[[]string], i int) Combinator[string] {
return func(s string) (string, string, error) {
rem, ext, err := c(s)
if err != nil {
return rem, "", err
}
if i < 0 || i >= len(ext) {
return rem, "", ParserError{
Err: fmt.Errorf("index %d is out of bounds within string slice of %d elements", i, len(ext)),
Type: "i",
}
}
return rem, ext[i], nil
}
}
// Peek will scan the text and apply the [Combinator] without consuming
// any input. Useful if you need to look ahead.
//
// chomp.Peek(chomp.Tag("Hello"))("Hello, World!")
// // ("Hello, World!", "Hello", nil)
//
// chomp.Peek(
// chomp.Many(chomp.Suffixed(chomp.Tag(" "), chomp.Until(" "))),
// )("Hello and Good Morning!")
// // ("Hello and Good Morning!", []string{"Hello", "and", "Good"}, nil)
func Peek[T Result](c Combinator[T]) Combinator[T] {
return func(s string) (string, T, error) {
_, ext, err := c(s)
return s, ext, err
}
}
// Flatten the output from a [Combinator] by joining all extracted values
// into a string.
//
// chomp.Flatten(
// chomp.Many(chomp.Parentheses()),
// )("(H)(el)(lo), World!")
// // (", World!", "Hello", nil)
func Flatten(c Combinator[[]string]) Combinator[string] {
return func(s string) (string, string, error) {
rem, ext, err := c(s)
if err != nil {
return rem, "", ParserError{Err: err, Type: "flatten"}
}
return rem, strings.Join(ext, ""), nil
}
}