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
|
require "./parcom/*"
module Parcom
VERSION = "0.2.0"
# A ParserFail exception should be raised by `Parser#parse` when
# a parse attempt is unsuccessful.
class ParserFail < Exception
end
# Provides a more convenient syntax for combining parsers via `Parser#and_then`.
# The first and second arguments are types used for the parser's type.
# The thirs argument is a string literal used for the name of the parser.
# This is 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 Char, String, "two words",
# {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(t, u, name, *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
end
|