aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--spec/parser_chain_spec.cr23
-rw-r--r--spec/practical/json_spec.cr2
-rw-r--r--src/parcom.cr44
-rw-r--r--src/parcom/parser.cr4
4 files changed, 51 insertions, 22 deletions
diff --git a/spec/parser_chain_spec.cr b/spec/parser_chain_spec.cr
index 05156ac..9510897 100644
--- a/spec/parser_chain_spec.cr
+++ b/spec/parser_chain_spec.cr
@@ -4,7 +4,7 @@ include Parcom
describe "parser_chain" do
it "works with zero intermediate steps" do
- p = parser_chain Char, Char, "test", finally: Parser(Char, Char).pure('x')
+ p = parser_chain Char, Char, "test", pure: 'x'
tokens = Tokens.from_string("abcd")
result = p.parse(tokens)
@@ -15,7 +15,7 @@ describe "parser_chain" do
it "works with one intermediate step" do
p = parser_chain Char, Char, "test",
{c, Parser(Char, Char).any_token},
- finally: Parser(Char, Char).pure(c)
+ pure: c
tokens = Tokens.from_string("abcd")
result = p.parse(tokens)
@@ -30,7 +30,7 @@ describe "parser_chain" do
{front, digit.many},
{point, Parser.token('.').optional},
{back, digit.many},
- finally: case {front.empty?, point.nil?, back.empty?}
+ make: case {front.empty?, point.nil?, back.empty?}
when {false, _, true}
Parser(Char, Float64).pure(front.join.to_f64 * sign)
when {true, false, false}
@@ -64,7 +64,7 @@ describe "parser_chain" do
two_words = parser_chain Char, {String, String}, "two_words",
{word, any_word},
{_, ws},
- finally: Parser.token_sequence(word.chars).map_const({word, word})
+ make: Parser.token_sequence(word.chars).map_const({word, word})
tokens = Tokens.from_string("foo \n foo")
result = two_words.parse(tokens)
@@ -75,5 +75,20 @@ describe "parser_chain" do
tokens = Tokens.from_string("foofoo")
expect_raises(ParserFail) { two_words.parse(tokens) }
end
+
+ it "fails without being given either `make` or `pure`" do
+ expect_raises(ArgumentError) do
+ _ = parser_chain Char, Char, "fails", {c, Parser.token('t')}
+ end
+ end
+
+ it "fails when given both `make` and `pure` at the same time" do
+ expect_raises(ArgumentError) do
+ _ = parser_chain Char, Char, "fails",
+ {c, Parser.token('t')},
+ pure: c,
+ make: Parser(Char, Char).pure('s')
+ end
+ end
end
diff --git a/spec/practical/json_spec.cr b/spec/practical/json_spec.cr
index a0ce2db..cdd3425 100644
--- a/spec/practical/json_spec.cr
+++ b/spec/practical/json_spec.cr
@@ -47,7 +47,7 @@ describe "example: JSON parsing", tags: "example" do
{front, digits},
{p, point},
{back, digits},
- finally:
+ make:
case {front.empty?, p.nil?, back.empty?}
when {true, false, false}
Parser(Char, Float64).pure("0.#{back.join}".to_f64)
diff --git a/src/parcom.cr b/src/parcom.cr
index 0963184..60b8df0 100644
--- a/src/parcom.cr
+++ b/src/parcom.cr
@@ -12,9 +12,13 @@ module Parcom
# 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)`.
+ # an expression resolving to a `Parser(t.class, _)`, whose success value will
+ # be stored in the aformentioned variable. The `make` named argument is an
+ # expression that resolves to a `Parser(t.class, u.class)`. This parser is
+ # the final step of the parser chain. The `pure` named argument is an
+ # expression that resolves to a `u.class`. This expression is passed to a
+ # call to `Parser(t.class, u.class).pure`, which will be the final step
+ # of the parser chain.
#
# Example:
# ```
@@ -23,7 +27,7 @@ module Parcom
# two_of_same_word = parser_chain Char, String, "two words",
# {word, any_word},
# {_, ws},
- # finally: Parser.token_sequence(word.chars).map(&.join)
+ # make: Parser.token_sequence(word.chars).map(&.join)
#
# tokens = Tokens.from_string("foo foo")
# result = two_of_same_word.parse(tokens)
@@ -39,17 +43,27 @@ module Parcom
# ```
#
# 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
+ macro parser_chain(t, u, name, *steps, make = nil, pure = nil )
+ {% if make.nil? && pure.nil? %}
+ raise ArgumentError.new("Expected exactly one of 'make' and 'pure', but got neither")
+ {% elsif !make.nil? && !pure.nil? %}
+ raise ArgumentError.new("Expected exactly one of 'make' and 'pure', but got both")
+ {% else %}
+ Parser({{t}}, {{u}}).new({{name}}) do |tokens|
+ {% for tup, index in steps %}
+ {{tup.last}}.and_then do |{{tup.first}}|
+ {% end %}
+ {% if !make.nil? %}
+ {{make}}
+ {% else %}
+ Parser({{t}}, {{u}}).pure({{pure}})
+ {% end %}
+ {% for _, _ in steps %}
+ end
+ {% end %}
+ .parse(tokens)
+ end
+ {% end %}
end
end
diff --git a/src/parcom/parser.cr b/src/parcom/parser.cr
index efa9780..398c95d 100644
--- a/src/parcom/parser.cr
+++ b/src/parcom/parser.cr
@@ -255,7 +255,7 @@ module Parcom
parser_chain T, {U, V}, "#{@name} + #{other.name}",
{x, self},
{y, other},
- finally: Parser(T, {U, V}).pure({x, y})
+ make: Parser(T, {U, V}).pure({x, y})
end
# Same as `#+`, but discards the second parser's result.
@@ -440,7 +440,7 @@ module Parcom
parser_chain T, Array(U), "<#{@name}> sep by <#{sep.name}>",
{head, self},
{tail, (sep >> self).many},
- finally: Parser(T, Array(U)).pure(tail.unshift(head))
+ make: Parser(T, Array(U)).pure(tail.unshift(head))
end
end
end