11package chomp
22
33import (
4- "fmt"
54 "strings"
65)
76
8- // Tag must match a series of characters at the beginning of the input text,
7+ // Tag must match a series of characters at the beginning of the input text
98// in the exact order and case provided.
109//
1110// chomp.Tag("Hello")("Hello, World!")
@@ -20,9 +19,8 @@ func Tag(str string) Combinator[string] {
2019 }
2120}
2221
23- // Any must match at least one character at the beginning of the input text,
24- // from the provided sequence. Parsing immediately stops upon the first
25- // unmatched character.
22+ // Any must match at least one character from the provided sequence at the
23+ // beginning of the input text. Parsing stops upon the first unmatched character.
2624//
2725// chomp.Any("eH")("Hello, World!")
2826// // ("llo, World!", "He", nil)
@@ -50,9 +48,8 @@ func Any(str string) Combinator[string] {
5048 }
5149}
5250
53- // Not must not match at least one character at the beginning of the input
54- // text from the provided sequence. Parsing immediately stops upon the
55- // first matched character.
51+ // Not must not match at least one character at the beginning of the input text
52+ // from the provided sequence. Parsing stops upon the first matched character.
5653//
5754// chomp.Not("ol")("Hello, World!")
5855// // ("llo, World!", "He", nil)
@@ -79,23 +76,8 @@ func Not(str string) Combinator[string] {
7976 }
8077}
8178
82- // Crlf must match either a CR or CRLF line ending.
83- //
84- // chomp.Crlf()("\r\nHello")
85- // // ("Hello", "\r\n", nil)
86- func Crlf () Combinator [string ] {
87- return func (s string ) (string , string , error ) {
88- idx := strings .Index (s , "\n " )
89- if idx == 0 || (idx == 1 && s [0 ] == '\r' ) {
90- return s [idx + 1 :], s [:idx + 1 ], nil
91- }
92-
93- return s , "" , CombinatorParseError {Text : s , Type : "crlf" }
94- }
95- }
96-
97- // OneOf must match a single character at the beginning of the text from the
98- // provided sequence.
79+ // OneOf must match a single character at the beginning of the text from
80+ // the provided sequence.
9981//
10082// chomp.OneOf("!,eH")("Hello, World!")
10183// // ("ello, World!", "H", nil)
@@ -114,8 +96,8 @@ func OneOf(str string) Combinator[string] {
11496 }
11597}
11698
117- // NoneOf must not match a single character at the beginning of the text from
118- // the provided sequence.
99+ // NoneOf must not match a single character at the beginning of the text
100+ // from the provided sequence.
119101//
120102// chomp.NoneOf("loWrd!e")("Hello, World!")
121103// // ("ello, World!", "H", nil)
@@ -150,132 +132,3 @@ func Until(str string) Combinator[string] {
150132 return s , "" , CombinatorParseError {Input : str , Text : s , Type : "until" }
151133 }
152134}
153-
154- // Opt allows a combinator to be optional. Any error returned by the underlying
155- // combinator will be swallowed. The parsed text will not be modified if the
156- // underlying combinator did not run.
157- //
158- // chomp.Opt(chomp.Tag("Hey"))("Hello, World!")
159- // // ("Hello, World!", "", nil)
160- func Opt [T Result ](c Combinator [T ]) Combinator [T ] {
161- return func (s string ) (string , T , error ) {
162- rem , out , _ := c (s )
163- return rem , out , nil
164- }
165- }
166-
167- // S wraps the result of the inner combinator within a string slice.
168- // Combinators of differing return types can be successfully chained
169- // together while using this conversion combinator.
170- //
171- // chomp.S(chomp.Until(","))("Hello, World!")
172- // // (", World!", []string{"Hello"}, nil)
173- func S (c Combinator [string ]) Combinator [[]string ] {
174- return func (s string ) (string , []string , error ) {
175- rem , ext , err := c (s )
176- if err != nil {
177- return rem , nil , err
178- }
179-
180- return rem , []string {ext }, err
181- }
182- }
183-
184- // I extracts and returns a single string from the result of the inner combinator.
185- // Combinators of differing return types can be successfully chained together while
186- // using this conversion combinator.
187- //
188- // chomp.I(chomp.SepPair(
189- // chomp.Tag("Hello"),
190- // chomp.Tag(", "),
191- // chomp.Tag("World")), 1)("Hello, World!")
192- // // ("!", "World", nil)
193- func I (c Combinator [[]string ], i int ) Combinator [string ] {
194- return func (s string ) (string , string , error ) {
195- rem , ext , err := c (s )
196- if err != nil {
197- return rem , "" , err
198- }
199-
200- if i < 0 || i >= len (ext ) {
201- return rem , "" , ParserError {
202- Err : fmt .Errorf ("index %d is out of bounds within string slice of %d elements" , i , len (ext )),
203- Type : "i" ,
204- }
205- }
206-
207- return rem , ext [i ], nil
208- }
209- }
210-
211- // Prefixed will firstly scan the input text for a defined prefix and discard it.
212- // The remaining input text will be matched against the [Combinator] and returned
213- // if successful. Both combinators must match.
214- //
215- // chomp.Prefixed(
216- // chomp.Tag("Hello"),
217- // chomp.Tag(`"`))(`"Hello, World!"`)
218- // // (`, World!"`, "Hello", nil)
219- func Prefixed (c , pre Combinator [string ]) Combinator [string ] {
220- return func (s string ) (string , string , error ) {
221- rem , _ , err := pre (s )
222- if err != nil {
223- return rem , "" , err
224- }
225-
226- return c (rem )
227- }
228- }
229-
230- // Suffixed will firstly scan the input text and match it against the [Combinator].
231- // The remaining text will be scanned for a defined suffix and discarded. Both
232- // combinators must match.
233- //
234- // chomp.Suffixed(
235- // chomp.Tag("Hello"),
236- // chomp.Tag(", "))("Hello, World!")
237- // // ("World!", "Hello", nil)
238- func Suffixed (c , suf Combinator [string ]) Combinator [string ] {
239- return func (s string ) (string , string , error ) {
240- rem , ext , err := c (s )
241- if err != nil {
242- return rem , "" , err
243- }
244-
245- rem , _ , err = suf (rem )
246- if err != nil {
247- return rem , "" , err
248- }
249-
250- return rem , ext , nil
251- }
252- }
253-
254- // Eol will scan the text until it encounters any ASCII line ending characters
255- // identified by the [IsLineEnding] predicate. All text before the line ending
256- // will be returned. The line ending, if detected, will be discarded.
257- //
258- // chomp.Eol()(`Hello, World!\nIt's a great day!`)
259- // // ("It's a great day!", "Hello, World!", nil)
260- func Eol () Combinator [string ] {
261- return func (s string ) (string , string , error ) {
262- return Suffixed (WhileNotN (IsLineEnding , 0 ), Opt (Crlf ()))(s )
263- }
264- }
265-
266- // Peek will scan the text and apply the parser without consuming any of the input.
267- // Useful if you need to lookahead.
268- //
269- // chomp.Peek(chomp.Tag("Hello"))("Hello, World!")
270- // // ("Hello, World!", "Hello", nil)
271- //
272- // chomp.Peek(
273- // chomp.Many(chomp.Suffixed(chomp.Tag(" "), chomp.Until(" "))),
274- // )("Hello and Good Morning!")
275- // // ("Hello and Good Morning!", []string{"Hello", "and", "Good"}, nil)
276- func Peek [T Result ](c Combinator [T ]) Combinator [T ] {
277- return func (s string ) (string , T , error ) {
278- _ , ext , err := c (s )
279- return s , ext , err
280- }
281- }
0 commit comments