Class: Walrus::Grammar::ParsletChoice

Inherits:
ParsletCombination show all
Defined in:
lib/walrus/grammar/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)


26
27
28
29
30
31
# File 'lib/walrus/grammar/parslet_choice.rb', line 26

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.



22
23
24
# File 'lib/walrus/grammar/parslet_choice.rb', line 22

def hash
  @hash
end

Instance Method Details

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
98
99
100
101
102
# File 'lib/walrus/grammar/parslet_choice.rb', line 94

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)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/walrus/grammar/parslet_choice.rb', line 51

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 Walrus-generated parsers?
      
    rescue ParseError => e
      if error.nil?
        error = e
      else
        error = e unless error.rightmost?(e)
      end
    end
  end
  raise ParseError.new('no valid alternatives while parsing "%s" (%s)' % [string, error.to_s],
                       :line_end => error.line_end, :column_end => error.column_end)                  # should generally report the rightmost error
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.



46
47
48
# File 'lib/walrus/grammar/parslet_choice.rb', line 46

def |(next_parslet)
  append(next_parslet)
end