From 467660e024bad8e2e084aa703686d0856a7e88b9 Mon Sep 17 00:00:00 2001 From: Matthew Hall Date: Sat, 18 Mar 2023 21:44:26 +1300 Subject: Sequence, +, and variants --- spec/parcom_spec.cr | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'spec') diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr index 6acd1b2..86ad93a 100644 --- a/spec/parcom_spec.cr +++ b/spec/parcom_spec.cr @@ -170,6 +170,52 @@ describe Parser do end end + describe "self.sequence" do + it "always succeeds with 0 parsers" do + p = Parser(Char, Char).sequence([] of Parser(Char, Char)) + tokens = Tokens.from_string("") + result = p.parse(tokens) + + result.value.empty?.should be_true + result.tokens.should eq(tokens) + end + + p = Parser(Char, Char).sequence([ + Parser(Char, Char).token('a'), + Parser(Char, Char).token('b'), + Parser(Char, Char).token('c'), + ]) + + it "runs each parser in sequence" do + tokens = Tokens.from_string("abcd") + result = p.parse(tokens) + + result.value.should eq("abc".chars) + result.tokens.should eq(tokens[3..]) + end + + it "fails if any of the parsers fail" do + "xbc axc abx".split.each do |s| + tokens = Tokens.from_string(s) + expect_raises(ParserFail) { p.parse(tokens) } + end + end + end + + # most testing should be able to be skipped, since it is already + # done for `Parser.sequence` + describe "self.token_sequence" do + p = Parser(Char, Char).token_sequence("abc".chars) + + it "parses the specified tokens in sequence" do + tokens = Tokens.from_string("abcd") + result = p.parse(tokens) + + result.value.should eq("abc".chars) + result.tokens.should eq(tokens[3..]) + end + end + describe "#assert" do p = Parser(Char, Char).any_token.assert { |c| c == 'a' } @@ -284,7 +330,55 @@ describe Parser do it "does not modify the input when recovering" do result2.tokens.should eq(tokens2) end + end + + describe "#+" do + a = Parser(Char, Char).token('a') + b = Parser(Char, Char).token('b') + p = a + b + + it "combines both success results if both parsers succeed" do + tokens = Tokens.from_string("abcd") + result = p.parse(tokens) + + result.value.should eq({'a', 'b'}) + result.tokens.should eq(tokens[2..]) + end + + it "fails if either parser fails" do + "aacd bbcd cccd".split.each do |s| + tokens = Tokens.from_string(s) + expect_raises(ParserFail) { p.parse(tokens) } + end + end + end + + # Should be able to skip some tests because they are already + # written for #+, which this is based on. + describe "#<<" do + a = Parser(Char, Char).token('a') + b = Parser(Char, Char).token('b') + p = a << b + tokens = Tokens.from_string("abcd") + result = p.parse(tokens) + + it "discards the second parser's value" do + result.value.should eq('a') + end + end + # Should be able to skip some tests because they are already + # written for #+, which this is based on. + describe "#>>" do + a = Parser(Char, Char).token('a') + b = Parser(Char, Char).token('b') + p = a >> b + tokens = Tokens.from_string("abcd") + result = p.parse(tokens) + + it "discards the second parser's value" do + result.value.should eq('b') + end end end -- cgit v1.2.1