diff options
| author | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-08 00:57:59 +1300 |
|---|---|---|
| committer | Matthew Hall <hallmatthew314@gmail.com> | 2023-03-08 00:57:59 +1300 |
| commit | de03594f5269d7554772c86aeb5d0bba0d2cb600 (patch) | |
| tree | d4935a61202e8e926fb4f0388342744532123d69 | |
| parent | f9148bb172e68e0f584c1040e92f34ea3959b94e (diff) | |
A bunch of stuff
| -rw-r--r-- | spec/parcom_spec.cr | 107 | ||||
| -rw-r--r-- | src/parcom.cr | 69 |
2 files changed, 173 insertions, 3 deletions
diff --git a/spec/parcom_spec.cr b/spec/parcom_spec.cr index a496a3f..ade9aff 100644 --- a/spec/parcom_spec.cr +++ b/spec/parcom_spec.cr @@ -18,6 +18,21 @@ describe Result do end describe Parser do + describe "#parse?" do + it "returns `nil` if the parser fails" do + result = AnyToken(Char).new.parse?([] of Char) + + result.should be_nil + end + + it "returns a `Result(T, V)` if the parser succeeds" do + tokens = "testing".chars + result = AnyToken(Char).new.parse(tokens) + + result.should be_a(Result(Char, Char)) + end + end + describe "#|" do it "creates an `Alt` instance from `self` and another `Parser(T, V)`" do p = AnyToken(Char).new | AnyToken(Char).new @@ -27,19 +42,29 @@ describe Parser do end describe "#assert" do - it "creates an `Assert` instance from self and a `Proc(T, Bool)`" do + it "creates an `Assert` instance from `self` and a `Proc(T, Bool)`" do f = ->(x : Char) { x == '#' } p = AnyToken(Char).new.assert(f) p.should be_a(Assert(Char, Char)) end end + + describe "#map" do + it "creates a `Map(T, V, U)` instance from `self` and a Proc(V, U)" do + f = ->(x : Char) { x.letter? } + p = AnyToken(Char).new.map(f) + + p.should be_a(Map(Char, Char, Bool)) + end + end end describe Flunk do describe "#parse" do it "always fails" do tokens = "testing".chars + expect_raises(ParserException) { Flunk(Char, Char).new.parse(tokens) } end end @@ -56,7 +81,9 @@ describe AnyToken do end it "fails when input is empty" do - expect_raises(ParserException) { AnyToken(Char).new.parse([] of Char) } + p = AnyToken(Char).new + + expect_raises(ParserException) { p.parse([] of Char) } end end end @@ -104,6 +131,7 @@ describe Assert do it "fails if the wrapped parser fails" do test = ->(x : Char) { true } # doesn't matter for this test p = Assert.new(AnyToken(Char).new, test) + expect_raises(ParserException) { p.parse([] of Char) } end @@ -111,6 +139,7 @@ describe Assert do tokens = "testing".chars test = ->(x : Char) { x == '$' } p = Assert.new(AnyToken(Char).new, test) + expect_raises(ParserException) { p.parse(tokens) } end @@ -160,7 +189,9 @@ end describe Token do describe "#parse" do it "fails if the input is empty" do - expect_raises(ParserException) { Token(Char).new('t').parse([] of Char) } + p = Token(Char).new('t') + + expect_raises(ParserException) { p.parse([] of Char) } end it "fails if the token is not the expected token" do @@ -181,3 +212,73 @@ describe Token do end end +describe Map do + describe "#parse" do + it "fails if the wrapped parser fails" do + id = ->(x : Char) { x } + p = Map.new(AnyToken(Char).new, id) + + expect_raises(ParserException) { p.parse([] of Char) } + end + + it "changes the result value via the provided proc" do + is_letter = ->(x : Char) { x.letter? } + p = Map.new(AnyToken(Char).new, is_letter) + + result = p.parse("testing".chars) + result.value.should be_true + + result = p.parse("_testing".chars) + result.value.should be_false + end + end +end + +pending Phrase do +end + +pending Plus do +end + +pending Recover do +end + +pending Optional do +end + +pending Tokens do +end + +pending Many do +end + +pending Some do +end + +pending Exactly do +end + +pending AtLeast do +end + +pending AtMost do +end + +pending Between do +end + +pending StopAt do +end + +pending StopAfter do +end + +pending StopIf do +end + +pending FirstOf do +end + +pending SepBy do +end + diff --git a/src/parcom.cr b/src/parcom.cr index 9063bf9..8835778 100644 --- a/src/parcom.cr +++ b/src/parcom.cr @@ -14,6 +14,12 @@ module Parcom abstract class Parser(T, V) abstract def parse(tokens : Array(T)) : Result(T, V) + def parse?(tokens : Array(T)) : Result(T, V)? + self.parse(tokens) + rescue + return nil + end + def assert(f : V -> Bool) Assert.new(self, f) end @@ -21,6 +27,11 @@ module Parcom def |(other : Parser(T, V)) : Parser(T, V) Alt.new(self, other) end + + # TODO: Find a way to annotate this method's type + def map(f) + Map.new(self, f) + end end class Flunk(T, V) < Parser(T, V) @@ -102,5 +113,63 @@ module Parcom @p2.parse(tokens) end end + + class Map(T, V, U) < Parser(T, U) + def initialize(@p : Parser(T, V), @f : V -> U) + end + + def parse(tokens : Array(T)) : Result(T, U) + result = @p.parse(tokens) + Result.new(result.tokens, @f.call(result.value)) + end + end +end + +class Phrase +end + +class Plus +end + +class Recover +end + +class Optional +end + +class Tokens +end + +class Many +end + +class Some +end + +class Exactly +end + +class AtLeast +end + +class AtMost +end + +class Between +end + +class StopAt +end + +class StopAfter +end + +class StopIf +end + +class FirstOf +end + +class SepBy end |
