d-parse 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +104 -0
- data/Guardfile +3 -0
- data/LICENSE +19 -0
- data/NEWS.md +0 -0
- data/README.md +137 -0
- data/Rakefile +14 -0
- data/d-parse.gemspec +26 -0
- data/lib/d-parse.rb +10 -0
- data/lib/d-parse/dsl.rb +71 -0
- data/lib/d-parse/failure.rb +46 -0
- data/lib/d-parse/parser.rb +102 -0
- data/lib/d-parse/parsers.rb +26 -0
- data/lib/d-parse/parsers/combinators/alt.rb +32 -0
- data/lib/d-parse/parsers/combinators/repeat.rb +42 -0
- data/lib/d-parse/parsers/combinators/seq.rb +49 -0
- data/lib/d-parse/parsers/highlevel/char_in.rb +13 -0
- data/lib/d-parse/parsers/highlevel/intersperse.rb +18 -0
- data/lib/d-parse/parsers/highlevel/json.rb +237 -0
- data/lib/d-parse/parsers/highlevel/opt.rb +16 -0
- data/lib/d-parse/parsers/highlevel/string.rb +13 -0
- data/lib/d-parse/parsers/highlevel/whitespace_char.rb +15 -0
- data/lib/d-parse/parsers/modifiers/capturing.rb +13 -0
- data/lib/d-parse/parsers/modifiers/describe.rb +28 -0
- data/lib/d-parse/parsers/modifiers/ignore.rb +17 -0
- data/lib/d-parse/parsers/modifiers/lazy.rb +18 -0
- data/lib/d-parse/parsers/modifiers/map.rb +24 -0
- data/lib/d-parse/parsers/primitives/any.rb +22 -0
- data/lib/d-parse/parsers/primitives/bind.rb +25 -0
- data/lib/d-parse/parsers/primitives/char.rb +27 -0
- data/lib/d-parse/parsers/primitives/char_not.rb +27 -0
- data/lib/d-parse/parsers/primitives/char_not_in.rb +30 -0
- data/lib/d-parse/parsers/primitives/eof.rb +21 -0
- data/lib/d-parse/parsers/primitives/except.rb +33 -0
- data/lib/d-parse/parsers/primitives/fail.rb +17 -0
- data/lib/d-parse/parsers/primitives/succeed.rb +13 -0
- data/lib/d-parse/position.rb +31 -0
- data/lib/d-parse/success.rb +35 -0
- data/lib/d-parse/version.rb +3 -0
- data/samples/parse-bind +25 -0
- data/samples/parse-csv +19 -0
- data/samples/parse-errortest +45 -0
- data/samples/parse-fun +61 -0
- data/samples/parse-json +18 -0
- data/samples/parse-readme +27 -0
- data/spec/d-parse/failure_spec.rb +36 -0
- data/spec/d-parse/parser_spec.rb +77 -0
- data/spec/d-parse/parsers/alt_spec.rb +48 -0
- data/spec/d-parse/parsers/any_spec.rb +15 -0
- data/spec/d-parse/parsers/bind_spec.rb +31 -0
- data/spec/d-parse/parsers/capture_spec.rb +11 -0
- data/spec/d-parse/parsers/char_in_spec.rb +22 -0
- data/spec/d-parse/parsers/char_not_in_spec.rb +23 -0
- data/spec/d-parse/parsers/char_not_spec.rb +16 -0
- data/spec/d-parse/parsers/char_spec.rb +22 -0
- data/spec/d-parse/parsers/describe_spec.rb +22 -0
- data/spec/d-parse/parsers/end_of_input_spec.rb +20 -0
- data/spec/d-parse/parsers/except_spec.rb +20 -0
- data/spec/d-parse/parsers/fail_spec.rb +12 -0
- data/spec/d-parse/parsers/intersperse_spec.rb +18 -0
- data/spec/d-parse/parsers/json_spec.rb +69 -0
- data/spec/d-parse/parsers/lazy_spec.rb +16 -0
- data/spec/d-parse/parsers/map_spec.rb +54 -0
- data/spec/d-parse/parsers/optional_spec.rb +16 -0
- data/spec/d-parse/parsers/or_spec.rb +26 -0
- data/spec/d-parse/parsers/repeat_spec.rb +40 -0
- data/spec/d-parse/parsers/sequence_spec.rb +52 -0
- data/spec/d-parse/parsers/string_spec.rb +19 -0
- data/spec/d-parse/parsers/succeed_spec.rb +12 -0
- data/spec/d-parse/parsers/whitespace_char_spec.rb +14 -0
- data/spec/spec_helper.rb +97 -0
- metadata +140 -0
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Opt < DParse::Parser
         | 
| 4 | 
            +
                  def self.new(parser)
         | 
| 5 | 
            +
                    DParse::Parsers::Alt.new(
         | 
| 6 | 
            +
                      parser,
         | 
| 7 | 
            +
                      DParse::Parsers::Succeed.new,
         | 
| 8 | 
            +
                    )
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(*)
         | 
| 12 | 
            +
                    raise ArgumentError, "#{self.class} is not supposed to be initialized"
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class String < DParse::Parser
         | 
| 4 | 
            +
                  def self.new(string)
         | 
| 5 | 
            +
                    DParse::Parsers::Seq.new(*string.chars.map { |c| DParse::Parsers::Char.new(c) })
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(*)
         | 
| 9 | 
            +
                    raise ArgumentError, "#{self.class} is not supposed to be initialized"
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class WhitespaceChar < DParse::Parser
         | 
| 4 | 
            +
                  WS = [' ', "\t"].freeze
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def self.new
         | 
| 7 | 
            +
                    DParse::Parsers::CharIn.new(WS)
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def initialize(*)
         | 
| 11 | 
            +
                    raise ArgumentError, "#{self.class} is not supposed to be initialized"
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Capturing < DParse::Parser
         | 
| 4 | 
            +
                  def self.new(parser)
         | 
| 5 | 
            +
                    parser.map { |_data, result, old_pos| result.input[old_pos.index...result.pos.index] }
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(*)
         | 
| 9 | 
            +
                    raise ArgumentError, "#{self.class} is not supposed to be initialized"
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Describe < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(parser, name)
         | 
| 5 | 
            +
                    @parser = parser
         | 
| 6 | 
            +
                    @name = name
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    res = @parser.read(input, pos)
         | 
| 11 | 
            +
                    case res
         | 
| 12 | 
            +
                    when DParse::Success
         | 
| 13 | 
            +
                      res
         | 
| 14 | 
            +
                    when DParse::Failure
         | 
| 15 | 
            +
                      Failure.new(res.input, res.pos, origin: self)
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def inspect
         | 
| 20 | 
            +
                    "#{@name}()"
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def expectation_message
         | 
| 24 | 
            +
                    @name
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Ignore < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(parser)
         | 
| 5 | 
            +
                    @parser = parser
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def read(input, pos)
         | 
| 9 | 
            +
                    @parser.read(input, pos).map { |_| nil }
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def inspect
         | 
| 13 | 
            +
                    "ignore(#{@parser.inspect})"
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Lazy < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(&block)
         | 
| 5 | 
            +
                    raise ArgumentError, 'Expected block' unless block_given?
         | 
| 6 | 
            +
                    @block = block
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    @block.call.read(input, pos)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def inspect
         | 
| 14 | 
            +
                    'lazy(?)'
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Map < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(parser, &block)
         | 
| 5 | 
            +
                    @parser = parser
         | 
| 6 | 
            +
                    @block = block
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    res = @parser.read(input, pos)
         | 
| 11 | 
            +
                    case res
         | 
| 12 | 
            +
                    when Success
         | 
| 13 | 
            +
                      Success.new(input, res.pos, data: @block.call(res.data, res, pos), best_failure: res.best_failure)
         | 
| 14 | 
            +
                    when Failure
         | 
| 15 | 
            +
                      res
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def inspect
         | 
| 20 | 
            +
                    "map(#{@parser}, <proc>)"
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Any < DParse::Parser
         | 
| 4 | 
            +
                  def read(input, pos)
         | 
| 5 | 
            +
                    char = input[pos.index]
         | 
| 6 | 
            +
                    if char
         | 
| 7 | 
            +
                      Success.new(input, pos.advance(char))
         | 
| 8 | 
            +
                    else
         | 
| 9 | 
            +
                      Failure.new(input, pos, origin: self)
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def inspect
         | 
| 14 | 
            +
                    'any()'
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def expectation_message
         | 
| 18 | 
            +
                    'any character except end of file'
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Bind < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(parser, &block)
         | 
| 5 | 
            +
                    @parser = parser
         | 
| 6 | 
            +
                    @block = block
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    res = @parser.read(input, pos)
         | 
| 11 | 
            +
                    case res
         | 
| 12 | 
            +
                    when Success
         | 
| 13 | 
            +
                      other_parser = @block.call(res.data)
         | 
| 14 | 
            +
                      other_parser.read(input, res.pos)
         | 
| 15 | 
            +
                    when Failure
         | 
| 16 | 
            +
                      res
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def inspect
         | 
| 21 | 
            +
                    "bind(#{@parser}, <proc>)"
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Char < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(char)
         | 
| 5 | 
            +
                    raise ArgumentError, 'Expected input to have one char' unless char.length == 1
         | 
| 6 | 
            +
                    @char = char
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    char = input[pos.index]
         | 
| 11 | 
            +
                    if char == @char
         | 
| 12 | 
            +
                      Success.new(input, pos.advance(char))
         | 
| 13 | 
            +
                    else
         | 
| 14 | 
            +
                      Failure.new(input, pos, origin: self)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def inspect
         | 
| 19 | 
            +
                    "char(#{@char.inspect})"
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def expectation_message
         | 
| 23 | 
            +
                    display(@char)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class CharNot < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(char)
         | 
| 5 | 
            +
                    raise ArgumentError, 'Expected input to have one char' unless char.length == 1
         | 
| 6 | 
            +
                    @char = char
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    char = input[pos.index]
         | 
| 11 | 
            +
                    if char != @char && char
         | 
| 12 | 
            +
                      Success.new(input, pos.advance(char))
         | 
| 13 | 
            +
                    else
         | 
| 14 | 
            +
                      Failure.new(input, pos, origin: self)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def inspect
         | 
| 19 | 
            +
                    "char_not(#{@char.inspect})"
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def expectation_message
         | 
| 23 | 
            +
                    "any character not equal to #{display(@char)}"
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class CharNotIn < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(chars)
         | 
| 5 | 
            +
                    unless chars.all? { |char| char.length == 1 }
         | 
| 6 | 
            +
                      raise ArgumentError, 'Expected input to have one char'
         | 
| 7 | 
            +
                    end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    @chars = chars
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def read(input, pos)
         | 
| 13 | 
            +
                    char = input[pos.index]
         | 
| 14 | 
            +
                    if char && @chars.all? { |c| char != c }
         | 
| 15 | 
            +
                      Success.new(input, pos.advance(char))
         | 
| 16 | 
            +
                    else
         | 
| 17 | 
            +
                      Failure.new(input, pos, origin: self)
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def inspect
         | 
| 22 | 
            +
                    "char_not_in(#{@chars.inspect})"
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def expectation_message
         | 
| 26 | 
            +
                    "any character not in #{@chars.map { |c| display(c) }.join(', ')}"
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class EOF < DParse::Parser
         | 
| 4 | 
            +
                  def read(input, pos)
         | 
| 5 | 
            +
                    if input.size == pos.index
         | 
| 6 | 
            +
                      Success.new(input, pos)
         | 
| 7 | 
            +
                    else
         | 
| 8 | 
            +
                      Failure.new(input, pos, origin: self)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def inspect
         | 
| 13 | 
            +
                    'eof()'
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def expectation_message
         | 
| 17 | 
            +
                    'end of input'
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Except < DParse::Parser
         | 
| 4 | 
            +
                  def initialize(parser, bad_parser)
         | 
| 5 | 
            +
                    @parser = parser
         | 
| 6 | 
            +
                    @bad_parser = bad_parser
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def read(input, pos)
         | 
| 10 | 
            +
                    res = @parser.read(input, pos)
         | 
| 11 | 
            +
                    case res
         | 
| 12 | 
            +
                    when Success
         | 
| 13 | 
            +
                      bad_res = @bad_parser.read(input, pos)
         | 
| 14 | 
            +
                      if bad_res.is_a?(Success) && bad_res.pos.index == res.pos.index
         | 
| 15 | 
            +
                        Failure.new(input, pos, origin: self)
         | 
| 16 | 
            +
                      else
         | 
| 17 | 
            +
                        res
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
                    when Failure
         | 
| 20 | 
            +
                      res
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def inspect
         | 
| 25 | 
            +
                    "except(#{@parser.inspect}, #{@bad_parser.inspect})"
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def expectation_message
         | 
| 29 | 
            +
                    @parser.expectation_message + ', not ' + @bad_parser.expectation_message
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              module Parsers
         | 
| 3 | 
            +
                class Fail < DParse::Parser
         | 
| 4 | 
            +
                  def read(input, pos)
         | 
| 5 | 
            +
                    Failure.new(input, pos, origin: self)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def inspect
         | 
| 9 | 
            +
                    'fail()'
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def expectation_message
         | 
| 13 | 
            +
                    'nothing (always fail)'
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              class Position
         | 
| 3 | 
            +
                attr_reader :index
         | 
| 4 | 
            +
                attr_reader :line
         | 
| 5 | 
            +
                attr_reader :column
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(index: 0, line: 0, column: 0)
         | 
| 8 | 
            +
                  @index = index
         | 
| 9 | 
            +
                  @line = line
         | 
| 10 | 
            +
                  @column = column
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                FAR_BEHIND = new(index: -1, line: -1, column: -1)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def advance(char)
         | 
| 16 | 
            +
                  Position.new(
         | 
| 17 | 
            +
                    index: @index + 1,
         | 
| 18 | 
            +
                    line: char == "\n" ? @line + 1 : @line,
         | 
| 19 | 
            +
                    column: char == "\n" ? 0 : @column + 1,
         | 
| 20 | 
            +
                  )
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def to_s
         | 
| 24 | 
            +
                  "line #{@line + 1}, column #{@column + 1}"
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def inspect
         | 
| 28 | 
            +
                  "Pos(#{@index}; #{@line}:#{@column})"
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module DParse
         | 
| 2 | 
            +
              class Success
         | 
| 3 | 
            +
                attr_reader :input
         | 
| 4 | 
            +
                attr_reader :pos
         | 
| 5 | 
            +
                attr_reader :data
         | 
| 6 | 
            +
                attr_reader :best_failure
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(input, pos, data: nil, best_failure: nil)
         | 
| 9 | 
            +
                  @input = input
         | 
| 10 | 
            +
                  @pos = pos
         | 
| 11 | 
            +
                  @data = data
         | 
| 12 | 
            +
                  @best_failure = best_failure
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def map
         | 
| 16 | 
            +
                  self.class.new(@input, @pos, data: yield(@data), best_failure: @best_failure)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def with_best_failure(failure)
         | 
| 20 | 
            +
                  self.class.new(@input, @pos, data: @data, best_failure: failure)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def to_s
         | 
| 24 | 
            +
                  "Success(#{@pos}; #{@data}#{@best_failure ? '; best failure = ' + best_failure.inspect : ''})"
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def success?
         | 
| 28 | 
            +
                  true
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def inspect
         | 
| 32 | 
            +
                  to_s
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         |