aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hall <hallmatthew314@gmail.com>2023-03-17 23:54:46 +1300
committerMatthew Hall <hallmatthew314@gmail.com>2023-03-17 23:54:46 +1300
commit658876fa4db1bb8363a03135fd22ad61b25050b7 (patch)
tree79f99c99ceac77abe761903346a662c3aa788763
parent72236e9bcaf7bff4a2bfbeb50ddf5d0261f74927 (diff)
recover and optional
-rw-r--r--spec/parcom_spec.cr47
-rw-r--r--src/parcom/parser.cr21
2 files changed, 65 insertions, 3 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr
index 14e232e..6acd1b2 100644
--- a/spec/parcom_spec.cr
+++ b/spec/parcom_spec.cr
@@ -239,5 +239,52 @@ describe Parser do
expect_raises(ParserFail) { p.parse(tokens) }
end
end
+
+ describe "#recover" do
+ p = Parser(Char, Char).token('a').recover('z')
+
+ it "behaves as normal on success" do
+ tokens = Tokens.from_string("abcd")
+ result = p.parse(tokens)
+
+ result.value.should eq('a')
+ result.tokens.should eq(tokens[1..])
+ end
+
+ tokens2 = Tokens.from_string("bbcd")
+ result2 = p.parse(tokens2)
+
+ it "returns the default value instead of failing" do
+ result2.value.should eq('z')
+ end
+
+ it "does not modify the input when recovering" do
+ result2.tokens.should eq(tokens2)
+ end
+ end
+
+ describe "#optional" do
+ p = Parser(Char, Char).token('a').optional
+
+ it "behaves as normal on success" do
+ tokens = Tokens.from_string("abcd")
+ result = p.parse(tokens)
+
+ result.value.should eq('a')
+ result.tokens.should eq(tokens[1..])
+ end
+
+ tokens2 = Tokens.from_string("bbcd")
+ result2 = p.parse(tokens2)
+
+ it "returns nil instead of failing" do
+ result2.value.should be_nil
+ end
+
+ it "does not modify the input when recovering" do
+ result2.tokens.should eq(tokens2)
+ end
+
+ end
end
diff --git a/src/parcom/parser.cr b/src/parcom/parser.cr
index 3098080..b78ab77 100644
--- a/src/parcom/parser.cr
+++ b/src/parcom/parser.cr
@@ -169,9 +169,24 @@ module Parcom
# Creates a new parser that is the same as the parser object it is
# called from, but it will return a default value without consuming
# any input instead of failing.
- #def recover(default : U) : Parser(T, U)
- # nil
- #end
+ def recover(default : U) : Parser(T, U)
+ r = Parser(T, U).pure(default)
+ (self | r).named("#{@name} (recover <#{default}>)")
+ end
+
+ # Creates a new parser that is the same as the parser object it is
+ # called from, but it will return `nil` without consuming any input
+ # instead of returning.
+ def optional : Parser(T, U?)
+ p = self
+ Parser(T, U?).new("#{p.name} (optional)") do |tokens|
+ # We have to do it this way due to how type unions work.
+ r = p.parse?(tokens)
+ new_tokens = r.nil? ? tokens : r.tokens
+ new_value = r.nil? ? nil : r.value
+ Result.new(new_tokens, new_value)
+ end
+ end
end
end