From 9951495a7b897b167a86c1c656b4f2418fd44e96 Mon Sep 17 00:00:00 2001 From: Matthew Hall Date: Sat, 11 Mar 2023 15:22:46 +1300 Subject: Start of documentation + refactoring --- src/parcom.cr | 72 +++++++++++++++++++++-------------------------------------- 1 file changed, 26 insertions(+), 46 deletions(-) (limited to 'src/parcom.cr') 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") -- cgit v1.2.1