diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-29 23:34:13 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-29 23:34:13 +1300 |
| commit | ec5c32d902148ab958cd4aac82b473fc8242d5af (patch) | |
| tree | 2b7435d07b2301fb81fb468a395a9793cf527655 /src | |
| parent | 3d8af6e93dfedcf2fa4e37adbece58dccaba168a (diff) | |
Implement parser_chain macro
Diffstat (limited to 'src')
| -rw-r--r-- | src/parcom.cr | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/src/parcom.cr b/src/parcom.cr index cdbebdb..7f0e475 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -8,6 +8,50 @@ module Parcom class ParserFail < Exception end + # Provides a more convenient syntax for combining parsers via `Parser#and_then`. + # The first argument is a string literal used for the name of the parser. + # The second and third arguments are types used for the parser's type. + # These are followed by any number of 2-tuples containing a variable name and + # an expression resolving to a `Parser(t.Class, _)`, whose success value will + # be stored in the aformentioned variable. The `finally` named argument is an + # expression that resolves to a `Parser(t.class, u.class)`. + # + # Example: + # ``` + # any_word = Parser(Char, Char).satisfy(&.letter?).some.map(&.join) + # ws = Parser(Char, Array(Char)).satisfy(&.whitespace?).many + # two_of_same_word = parser_chain "two words", Char, String, + # {word, any_word}, + # {_, ws}, + # finally: Parser.token_sequence(word.chars).map(&.join) + # + # tokens = Tokens.from_string("foo foo") + # result = two_of_same_word.parse(tokens) + # result.value # => "foo" + # + # # The above definition of `two_of_same word` + # # is an alternative way of doing this: + # two_of_same_word = any_word.and_then do |word| + # ws.and_then do |_| + # Parser.token_sequence(word.chars).map(&.join) + # end + # end.named("two words") + # ``` + # + # This macro is based on Haskell's do-notation. + macro parser_chain(name, t, u, *steps, finally) + Parser({{t}}, {{u}}).new({{name}}) do |tokens| + {% for tup, index in steps %} + {{tup.last}}.and_then do |{{tup.first}}| + {% end %} + {{finally}} + {% for _, _ in steps %} + end + {% end %} + .parse(tokens) + end + end + # `Tokens` is an `Array` wrapper struct to store the input # stream of one or more `Parser` objects. # A `Tokens` can be created from any `Iterable`, along with |
