]> git.wincent.com - walrat.git/blob - lib/walrat/parslet_combining.rb
Initial import (extraction from Walrus repo, commit 0c9d44c)
[walrat.git] / lib / walrat / parslet_combining.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   # The ParsletCombining module, together with the ParsletCombination class and
27   # its subclasses, provides simple container classes for encapsulating
28   # relationships among Parslets. By storing this information outside of the
29   # Parslet objects themselves their design is kept clean and they can become
30   # immutable objects which are much more easily copied and shared among
31   # multiple rules in a Grammar.
32   module ParsletCombining
33     # Convenience method.
34     def memoizing_parse string, options = {}
35       self.to_parseable.memoizing_parse string, options
36     end
37
38     # Convenience method.
39     def parse string, options = {}
40       self.to_parseable.parse string, options
41     end
42
43     # Defines a sequence of Parslets (or ParsletCombinations).
44     # Returns a ParsletSequence instance.
45     def sequence first, second, *others
46       Walrat::ParsletSequence.new first.to_parseable,
47         second.to_parseable, *others
48     end
49
50     # Shorthand for ParsletCombining.sequence(first, second).
51     def &(next_parslet)
52       self.sequence self, next_parslet
53     end
54
55     # Defines a sequence of Parslets similar to the sequence method but with
56     # the difference that the contents of array results from the component
57     # parslets will be merged into a single array rather than being added as
58     # arrays. To illustrate:
59     #
60     #   'foo' & 'bar'.one_or_more   # returns results like ['foo', ['bar', 'bar', 'bar']]
61     #   'foo' >> 'bar'.one_or_more  # returns results like ['foo', 'bar', 'bar', 'bar']
62     #
63     def merge first, second, *others
64       Walrat::ParsletMerge.new first.to_parseable,
65         second.to_parseable, *others
66     end
67
68     # Shorthand for ParsletCombining.sequence(first, second)
69     def >>(next_parslet)
70       self.merge self, next_parslet
71     end
72
73     # Defines a choice of Parslets (or ParsletCombinations).
74     # Returns a ParsletChoice instance.
75     def choice left, right, *others
76       Walrat::ParsletChoice.new left.to_parseable,
77         right.to_parseable, *others
78     end
79
80     # Shorthand for ParsletCombining.choice(left, right)
81     def |(alternative_parslet)
82       self.choice self, alternative_parslet
83     end
84
85     # Defines a repetition of the supplied Parslet (or ParsletCombination).
86     # Returns a ParsletRepetition instance.
87     def repetition parslet, min, max
88       Walrat::ParsletRepetition.new parslet.to_parseable, min, max
89     end
90
91     # Shorthand for ParsletCombining.repetition.
92     def repeat min = nil, max = nil
93       self.repetition self, min, max
94     end
95
96     def repetition_with_default parslet, min, max, default
97       Walrat::ParsletRepetitionDefault.new parslet.to_parseable, min,
98         max, default
99     end
100
101     def repeat_with_default min = nil, max = nil, default = nil
102       self.repetition_with_default self, min, max, default
103     end
104
105     # Shorthand for ParsletCombining.repetition(0, 1).
106     # This method optionally takes a single parameter specifying what object
107     # should be returned as a placeholder when there are no matches; this is
108     # useful for packing into ASTs where it may be better to parse an empty
109     # Array rather than nil. The specified object is cloned and returned in the
110     # event that there are no matches. As a convenience, the specified object
111     # is automatically extended using the LocationTracking module (this is a
112     # convenience so that you can specify empty Arrays, "[]", rather than
113     # explicitly passing an "ArrayResult.new")
114     def optional default_return_value = NoParameterMarker.instance
115       if default_return_value == NoParameterMarker.instance
116         self.repeat 0, 1 # default behaviour
117       else
118         self.repeat_with_default 0, 1, default_return_value
119       end
120     end
121
122     # Alternative to optional.
123     def zero_or_one
124       self.optional
125     end
126
127     # possible synonym "star"
128     def zero_or_more default_return_value = NoParameterMarker.instance
129       if default_return_value == NoParameterMarker.instance
130         self.repeat 0 # default behaviour
131       else
132         self.repeat_with_default 0, nil, default_return_value
133       end
134     end
135
136     # possible synonym "plus"
137     def one_or_more
138       self.repeat 1
139     end
140
141     # Parsing Expression Grammar support.
142     # Succeeds if parslet succeeds but consumes no input (throws an
143     # :AndPredicateSuccess symbol).
144     def and_predicate parslet
145       Walrat::AndPredicate.new parslet.to_parseable
146     end
147
148     # Shorthand for and_predicate
149     # Strictly speaking, this shorthand breaks with established Ruby practice
150     # that "?" at the end of a method name should indicate a method that
151     # returns true or false.
152     def and?
153       self.and_predicate self
154     end
155
156     # Parsing Expression Grammar support.
157     # Succeeds if parslet fails (throws a :NotPredicateSuccess symbol).
158     # Fails if parslet succeeds (raise a ParseError).
159     # Consumes no output.
160     # This method will almost invariably be used in conjuntion with the &
161     # operator, like this:
162     #       rule :foo, :p1 & :p2.not_predicate
163     #       rule :foo, :p1 & :p2.not!
164     def not_predicate parslet
165       Walrat::NotPredicate.new parslet.to_parseable
166     end
167
168     # Shorthand for not_predicate.
169     # Strictly speaking, this shorthand breaks with established Ruby practice
170     # that "!" at the end of a method name should indicate a destructive
171     # behaviour on (mutation of) the receiver.
172     def not!
173       self.not_predicate self
174     end
175
176     # Succeeds if parsing succeeds, consuming the output, but doesn't actually
177     # return anything.
178     #
179     # This is for elements which are required but which shouldn't appear in the
180     # final AST.
181     def omission parslet
182       Walrat::ParsletOmission.new parslet.to_parseable
183     end
184
185     # Shorthand for ParsletCombining.omission
186     def skip
187       self.omission self
188     end
189   end # module ParsletCombining
190 end # module Walrat