]> git.wincent.com - walrat.git/blob - lib/walrat/parslet_merge.rb
Initial import (extraction from Walrus repo, commit 0c9d44c)
[walrat.git] / lib / walrat / parslet_merge.rb
1 # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
2 # Redistribution and use in source and binary forms, with or without
3 # modification, are permitted provided that the following conditions are met:
4 #
5 # 1. Redistributions of source code must retain the above copyright notice,
6 #    this list of conditions and the following disclaimer.
7 # 2. Redistributions in binary form must reproduce the above copyright notice,
8 #    this list of conditions and the following disclaimer in the documentation
9 #    and/or other materials provided with the distribution.
10 #
11 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
12 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
15 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
16 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
19 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
20 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 # POSSIBILITY OF SUCH DAMAGE.
22
23 require 'walrat'
24
25 module Walrat
26   class ParsletMerge < ParsletSequence
27     def parse string, options = {}
28       raise ArgumentError if string.nil?
29       state       = ParserState.new string, options
30       last_caught = nil # keep track of the last kind of throw to be caught
31       @components.each do |parseable|
32         catch :ProcessNextComponent do
33           catch :NotPredicateSuccess do
34             catch :AndPredicateSuccess do
35               catch :ZeroWidthParseSuccess do
36                 begin
37                   parsed = parseable.memoizing_parse state.remainder, state.options
38                   if parsed.respond_to? :each
39                     parsed.each { |element| state.parsed element }
40                   else
41                     state.parsed(parsed)
42                   end
43                 rescue SkippedSubstringException => e
44                   state.skipped(e)
45                 # rescue ParseError => e # failed, will try to skip; save original error in case skipping fails
46                 #   if options.has_key?(:skipping_override) : skipping_parslet = options[:skipping_override]
47                 #   elsif options.has_key?(:skipping)       : skipping_parslet = options[:skipping]
48                 #   else                                      skipping_parslet = nil
49                 #   end
50                 #   raise e if skipping_parslet.nil?        # no skipper defined, raise original error
51                 #   begin
52                 #     # guard against self references (possible infinite recursion) here?
53                 #     parsed = skipping_parslet.memoizing_parse(state.remainder, state.options)
54                 #     state.skipped(parsed)
55                 #     redo              # skipping succeeded, try to redo
56                 #   rescue ParseError
57                 #     raise e           # skipping didn't help either, raise original error
58                 #   end
59                 end
60                 last_caught = nil
61                 throw :ProcessNextComponent # can't use "next" here because it will only break out of innermost "do"
62               end
63               last_caught = :ZeroWidthParseSuccess
64               throw :ProcessNextComponent
65             end
66             last_caught = :AndPredicateSuccess
67             throw :ProcessNextComponent
68           end
69           last_caught = :NotPredicateSuccess
70         end
71       end
72
73       if state.results.respond_to? :empty? and state.results.empty? and
74        throw last_caught
75       else
76         state.results
77       end
78     end
79
80     def eql?(other)
81       return false if not other.instance_of? ParsletMerge
82       other_components = other.components
83       return false if @components.length != other_components.length
84       for i in 0..(@components.length - 1)
85         return false unless @components[i].eql? other_components[i]
86       end
87       true
88     end
89
90   private
91
92     def hash_offset
93       53
94     end
95   end # class ParsletMerge
96 end # module Walrat