Class: Walrat::ParsletChoice

Inherits:
ParsletCombination show all
Defined in:
lib/walrat/parslet_choice.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from ParsletCombination

#to_parseable

Methods included from Memoizing

#check_left_recursion, #memoizing_parse

Methods included from ParsletCombining

#&, #>>, #and?, #and_predicate, #choice, #memoizing_parse, #merge, #not!, #not_predicate, #omission, #one_or_more, #optional, #repeat, #repeat_with_default, #repetition, #repetition_with_default, #sequence, #skip, #zero_or_more, #zero_or_one

Constructor Details

#initialize(left, right, *others) ⇒ ParsletChoice

Either parameter may be a Parslet or a ParsletCombination. Neither parmeter may be nil.

Raises:

  • (ArgumentError)


31
32
33
34
35
36
# File 'lib/walrat/parslet_choice.rb', line 31

def initialize left, right, *others
  raise ArgumentError if left.nil?
  raise ArgumentError if right.nil?
  @alternatives = [left, right] + others
  update_hash
end

Instance Attribute Details

#hashObject (readonly)

Returns the value of attribute hash.



27
28
29
# File 'lib/walrat/parslet_choice.rb', line 27

def hash
  @hash
end

Instance Method Details

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
123
124
125
126
127
128
# File 'lib/walrat/parslet_choice.rb', line 120

def eql? other
  return false if not other.instance_of? ParsletChoice
  other_alternatives = other.alternatives
  return false if @alternatives.length != other_alternatives.length
  for i in 0..(@alternatives.length - 1)
    return false unless @alternatives[i].eql? other_alternatives[i]
  end
  true
end

#parse(string, options = {}) ⇒ Object

First tries to parse the left option, falling back and trying the right option and then the any subsequent options in the others instance variable on failure. If no options successfully complete parsing then an ParseError is raised. Any zero-width parse successes thrown by alternative parsers will flow on to a higher level.

Raises:

  • (ArgumentError)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/walrat/parslet_choice.rb', line 71

def parse string, options = {}
  raise ArgumentError if string.nil?
  error           = nil # for error reporting purposes will track which parseable gets farthest to the right before failing
  left_recursion  = nil # will also track any left recursion that we detect
  @alternatives.each do |parseable|
    begin
      result = parseable.memoizing_parse(string, options) # successful parse
      if left_recursion and left_recursion.continuation   # and we have a continuation
        continuation = left_recursion.continuation        # continuations are once-only, one-way tickets
        left_recursion = nil                              # set this to nil so as not to call it again without meaning to
        continuation.call(result)                         # so jump back to where we were before
      end
      return result
    rescue LeftRecursionException => e
      left_recursion = e

      # TODO:
      # it's not enough to just catch this kind of exception and remember
      # the last one
      # may need to accumulate these in an array
      # consider the example rule:
      #   :a, :a & :b | :a & :c | :a & :d | :b
      # the first option will raise a LeftRecursionException
      # the next option will raise for the same reason
      # the third likewise
      # finally we get to the fourth option, the first which might succeed
      # at that point we should have three continuations
      # we should try the first, falling back to the second and third if
      # necessary
      # on successfully retrying, need to start all over again and try all
      # the options again, just in case further recursion is possible
      # so it is quite complicated
      # the question is, is it more complicated than the other ways of
      # getting right-associativity into Walrat-generated parsers?
    rescue ParseError => e
      if error.nil?
        error = e
      else
        error = e unless error.rightmost?(e)
      end
    end
  end

  # should generally report the rightmost error
  raise ParseError.new('no valid alternatives while parsing "%s" (%s)' % [string, error.to_s],
                       :line_end => error.line_end,
                       :column_end => error.column_end)
end

#|(next_parslet) ⇒ Object

Override so that alternatives are appended to an existing sequence: Consider the following example:

A | B

This constitutes a single choice:

(A | B)

If we then make this a three-element sequence:

A | B | C

We are effectively creating an nested sequence containing the original sequence and an additional element:

((A | B) | C)

Although such a nested sequence is correctly parsed it is not as architecturally clean as a single sequence without nesting:

(A | B | C)

This method allows us to use the architecturally cleaner format.



62
63
64
# File 'lib/walrat/parslet_choice.rb', line 62

def |(next_parslet)
  append next_parslet
end