Class: Walrus::Grammar::ParsletSequence
- Inherits:
-
ParsletCombination
- Object
- ParsletCombination
- Walrus::Grammar::ParsletSequence
- Defined in:
- lib/walrus/grammar/parslet_sequence.rb
Direct Known Subclasses
Constant Summary collapse
- SKIP_FIRST =
true
- NO_SKIP =
false
Instance Attribute Summary collapse
-
#hash ⇒ Object
readonly
Returns the value of attribute hash.
Instance Method Summary collapse
-
#&(next_parslet) ⇒ Object
Override so that sequences are appended to an existing sequence: Consider the following example: A & B This constitutes a single sequence: (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 produces unwanted nesting in the results because instead of returning a one-dimensional an array of results: [a, b, c] It returns a nested array: [[a, b], c] The solution to this unwanted nesting is to allowing appending to an existing sequence by using the private “append” method.
- #eql?(other) ⇒ Boolean
-
#initialize(first, second, *others) ⇒ ParsletSequence
constructor
first and second may not be nil.
- #parse(string, options = {}) ⇒ Object
- #parse_common(skip_first, string, options = {}) ⇒ Object
- #parse_remainder(string, options = {}) ⇒ Object
-
#recurse(state) ⇒ Object
Left-recursion helper.
Methods inherited from ParsletCombination
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(first, second, *others) ⇒ ParsletSequence
first and second may not be nil.
25 26 27 28 29 30 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 25 def initialize(first, second, *others) raise ArgumentError if first.nil? raise ArgumentError if second.nil? @components = [first, second] + others update_hash end |
Instance Attribute Details
#hash ⇒ Object (readonly)
Returns the value of attribute hash.
22 23 24 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 22 def hash @hash end |
Instance Method Details
#&(next_parslet) ⇒ Object
Override so that sequences are appended to an existing sequence: Consider the following example:
A & B
This constitutes a single sequence:
(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 produces unwanted nesting in the results because instead of returning a one-dimensional an array of results:
[a, b, c]
It returns a nested array:
[[a, b], c]
The solution to this unwanted nesting is to allowing appending to an existing sequence by using the private “append” method. This ensures that:
A & B & C
Translates to a single sequence:
(A & B & C)
And a single, uni-dimensional results array:
[a, b, c]
53 54 55 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 53 def &(next_parslet) append(next_parslet) end |
#eql?(other) ⇒ Boolean
175 176 177 178 179 180 181 182 183 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 175 def eql?(other) return false if not other.instance_of? ParsletSequence other_components = other.components return false if @components.length != other_components.length for i in 0..(@components.length - 1) return false unless @components[i].eql? other_components[i] end true end |
#parse(string, options = {}) ⇒ Object
60 61 62 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 60 def parse(string, = {}) parse_common(NO_SKIP, string, ) end |
#parse_common(skip_first, string, options = {}) ⇒ Object
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 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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 68 def parse_common(skip_first, string, = {}) raise ArgumentError if string.nil? state = ParserState.new(string, ) last_caught = nil # keep track of the last kind of throw to be caught left_recursion = false # keep track of whether left recursion was detected @components.each_with_index do |parseable, index| if index == 0 # for first component only if skip_first next end begin check_left_recursion(parseable, ) rescue LeftRecursionException => e left_recursion = true continuation = nil # Ruby 1.9/2.0 will almost certainly not support continuations so need to come up with an alternative # for handling left recursion here value = callcc { |c| continuation = c } if value == continuation # first time that we're here e.continuation = continuation # pack continuation into exception raise e # and propagate else grammar = state.[:grammar] rule_name = state.[:rule_name] state.parsed(grammar.wrap(value, rule_name)) next end end end catch :ProcessNextComponent do catch :NotPredicateSuccess do catch :AndPredicateSuccess do catch :ZeroWidthParseSuccess do begin parsed = parseable.memoizing_parse(state.remainder, state.) state.parsed(parsed) rescue SkippedSubstringException => e state.skipped(e) rescue ParseError => e # failed, will try to skip; save original error in case skipping fails if .has_key?(:skipping_override) skipping_parslet = [:skipping_override] elsif .has_key?(:skipping) skipping_parslet = [:skipping] else skipping_parslet = nil end raise e if skipping_parslet.nil? # no skipper defined, raise original error begin parsed = skipping_parslet.memoizing_parse(state.remainder, state.) # guard against self references (possible infinite recursion) here? state.skipped(parsed) redo # skipping succeeded, try to redo rescue ParseError raise e # skipping didn't help either, raise original error end end last_caught = nil throw :ProcessNextComponent # can't use "next" here because it would only break out of innermost "do" rather than continuing the iteration end last_caught = :ZeroWidthParseSuccess throw :ProcessNextComponent end last_caught = :AndPredicateSuccess throw :ProcessNextComponent end last_caught = :NotPredicateSuccess end end if left_recursion results = recurse(state) else results = state.results end if skip_first return results end if results.respond_to? :empty? and results.empty? and last_caught throw last_caught else results end end |
#parse_remainder(string, options = {}) ⇒ Object
64 65 66 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 64 def parse_remainder(string, = {}) parse_common(SKIP_FIRST, string, ) end |
#recurse(state) ⇒ Object
Left-recursion helper
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/walrus/grammar/parslet_sequence.rb', line 159 def recurse(state) return state.results if state.remainder == '' # further recursion is not possible new_state = ParserState.new(state.remainder, state.) last_successful_result = nil while state.remainder != '' begin new_results = parse_remainder(new_state.remainder, new_state.) new_state.parsed(new_results) last_successful_result = ArrayResult[last_successful_result || state.results, new_results] rescue ParseError break end end last_successful_result || state.results end |