aboutsummaryrefslogtreecommitdiff
path: root/src/parcom.cr
diff options
context:
space:
mode:
authorMatthew Hall <hallmatthew314@gmail.com>2023-03-11 15:22:46 +1300
committerMatthew Hall <hallmatthew314@gmail.com>2023-03-11 15:22:46 +1300
commit9951495a7b897b167a86c1c656b4f2418fd44e96 (patch)
tree6f1d61be1ced8d3717975b2e018ab3325dd4391e /src/parcom.cr
parent3fab598b4873fda69183de21b650d041cc95411c (diff)
Start of documentation + refactoring
Diffstat (limited to 'src/parcom.cr')
-rw-r--r--src/parcom.cr72
1 files changed, 26 insertions, 46 deletions
diff --git a/src/parcom.cr b/src/parcom.cr
index d13fce7..50f6e7e 100644
--- a/src/parcom.cr
+++ b/src/parcom.cr
@@ -1,16 +1,37 @@
+require "./parcom/*"
+
module Parcom
VERSION = "0.1.0"
+ # A ParserFail exception should be raised by `Parser#parse` when
+ # a parse attempt is unsuccessful.
+ # Raising this exception in the `#parse` method of a Parser "Foo"
+ # usually follows this pattern to allow for error tracing:
+ #
+ # ```
+ # class Foo(T, V) < Parser(T, V)
+ # def parse(tokens : Tokens(T)) : Result(T, V)
+ # helper.parse(tokens)
+ # rescue ex : ParserFail
+ # raise ParserFail.new("Foo: #{ex.message}")
+ # end
+ # ```
class ParserFail < Exception
end
+ # `Tokens` is an `Array` wrapper struct to store the input
+ # stream of one or more `Parser` objects.
+ # A `Tokens` can be created from any `Iterable`, along with
+ # `String` objects using a special constructor.
struct Tokens(T)
getter tokens
+ # Constructs a `Tokens(Char)` from a `String`.
def self.from_string(s : String) : Tokens(Char)
Tokens.new(s.chars)
end
+ # Constructs a `Tokens` from an `Iterable`.
def initialize(ts : Iterable(T))
if ts.responds_to?(:to_a)
@tokens = ts.to_a
@@ -20,24 +41,29 @@ module Parcom
end
end
+ # Exposes `Array#[](Int)`.
def [](index : Int) : T
@tokens[index]
end
+ # Exposes `Array#[](Int, Int)`, but wraps the returned array in a new `Tokens`.
def [](start : Int, count : Int) : Tokens(T)
Tokens.new(@tokens[start, count])
end
+ # Exposes `Array#[](Range)`, but wraps the returned array in a new `Tokens`.
def [](range : Range) : Tokens(T)
Tokens.new(@tokens[range])
end
+ # Like `#[]`, but returns `nil` instead of raising an `IndexError`.
def []?(*args)
self.[](*args)
rescue IndexError
nil
end
+ # Exposes `Array#empty?`.
def empty? : Bool
@tokens.empty?
end
@@ -50,52 +76,6 @@ module Parcom
end
end
- abstract class Parser(T, V)
- abstract def parse(tokens : Tokens(T)) : Result(T, V)
-
- def parse?(tokens : Tokens(T)) : Result(T, V)?
- self.parse(tokens)
- rescue ParserFail
- return nil
- end
-
- def |(other : Parser(T, V)) : Alt(T, V)
- Alt.new(self, other)
- end
-
- def +(other : Parser(T, U)) : Plus(T, V, U) forall U
- Plus.new(self, other)
- end
-
- def <<(other : Parser(T, U)) : Left(T, V, U) forall U
- Left.new(self, other)
- end
-
- def >>(other : Parser(T, U)) : Right(T, V, U) forall U
- Right.new(self, other)
- end
-
- def assert(f : V -> Bool)
- Assert.new(self, &f)
- end
-
- def assert(&block : V -> Bool)
- Assert.new(self, &block)
- end
-
- def map(&block : V -> U) : Map(T, V, U) forall U
- Map.new(self, &block)
- end
-
- def map(f : V -> U) : Map(T, V, U) forall U
- Map.new(self, &f)
- end
-
- def recover(default : V) : Recover(T, V)
- Recover.new(self, default)
- end
- end
-
class Flunk(T, V) < Parser(T, V)
def parse(tokens) : Result(T, V)
raise ParserFail.new("Flunk: parsing failed")