diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/parcom/parser.cr | 44 |
1 files changed, 42 insertions, 2 deletions
diff --git a/src/parcom/parser.cr b/src/parcom/parser.cr index adf0bba..3098080 100644 --- a/src/parcom/parser.cr +++ b/src/parcom/parser.cr @@ -80,9 +80,19 @@ module Parcom satisfy(block) end + # Creates a parser that parses the fist token in the input stream + # if that token matches the provided token. + def self.token(token : T) : Parser(T, T) + Parser(T, T).satisfy { |x| x == token }.named("Token <#{token}>") + end + + # Creates a new parser from a `Proc`. + # The `Proc` should have the properties outlined above. def initialize(@name : String, @f : Tokens(T) -> Result(T, U)) end + # Creates a new parser from a block. + # The block should have the properties outline above. def initialize(@name : String, &block : Tokens(T) -> Result(T, U)) @f = block end @@ -97,16 +107,24 @@ module Parcom self end + # Tries to parse some kind of data from the given input stream. + # This method calls the `Proc` or block this parser object was + # initialized with. def parse(tokens : Tokens(T)) : Result(T, U) @f.call(tokens) end + # Same as `#parse(Tokens(T)) : Result(T, U)`, but returns `nil` + # instead of raising an exception if parsing fails. def parse?(tokens : Tokens(T)) : Result(T, U)? parse(tokens) rescue nil end + # Creates a new parser that is the same as the parser object it is + # called from, but tests the result against a given predicate. + # If the value does not pass the test, the parser fails. def assert(f : U -> Bool) : Parser(T, U) p = self Parser(T, U).new("#{p.name} (assertion)") do |tokens| @@ -118,20 +136,42 @@ module Parcom end end + # :ditto: def assert(&block : U -> Bool) : Parser(T, U) assert(block) end - def map(f : U -> T) : Parser(T, V) forall V + # Creates a new parser that is the same as the parser object it is + # called from, but transforms the result into something else via a + # given function. + # The function in question should not introduce side effects. + def map(f : U -> V) : Parser(T, V) forall V p = self Parser(T, V).new("#{p.name} (mapped)") do |tokens| p.parse(tokens).map(f) end end - def map(&block : U -> T) : Parser(T, V) forall V + # :ditto: + def map(&block : U -> V) : Parser(T, V) forall V map(block) end + + def |(p2 : Parser(T, U)) : Parser(T, U) + p1 = self + Parser(T, U).new("#{p1.name} or #{p2.name}") do |tokens| + p1.parse(tokens) + rescue ParserFail + p2.parse(tokens) + end + end + + # 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 end end |
