walrat 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/walrat.rb +70 -0
- data/lib/walrat/additions/proc.rb +32 -0
- data/lib/walrat/additions/regexp.rb +33 -0
- data/lib/walrat/additions/string.rb +99 -0
- data/lib/walrat/additions/symbol.rb +42 -0
- data/lib/walrat/and_predicate.rb +49 -0
- data/lib/walrat/array_result.rb +29 -0
- data/lib/walrat/continuation_wrapper_exception.rb +35 -0
- data/lib/walrat/grammar.rb +259 -0
- data/lib/walrat/left_recursion_exception.rb +34 -0
- data/lib/walrat/location_tracking.rb +126 -0
- data/lib/walrat/match_data_wrapper.rb +84 -0
- data/lib/walrat/memoizing.rb +55 -0
- data/lib/walrat/memoizing_cache.rb +126 -0
- data/lib/walrat/no_parameter_marker.rb +30 -0
- data/lib/walrat/node.rb +63 -0
- data/lib/walrat/not_predicate.rb +49 -0
- data/lib/walrat/parse_error.rb +48 -0
- data/lib/walrat/parser_state.rb +205 -0
- data/lib/walrat/parslet.rb +38 -0
- data/lib/walrat/parslet_choice.rb +155 -0
- data/lib/walrat/parslet_combination.rb +34 -0
- data/lib/walrat/parslet_combining.rb +190 -0
- data/lib/walrat/parslet_merge.rb +96 -0
- data/lib/walrat/parslet_omission.rb +74 -0
- data/lib/walrat/parslet_repetition.rb +114 -0
- data/lib/walrat/parslet_repetition_default.rb +77 -0
- data/lib/walrat/parslet_sequence.rb +241 -0
- data/lib/walrat/predicate.rb +68 -0
- data/lib/walrat/proc_parslet.rb +60 -0
- data/lib/walrat/regexp_parslet.rb +84 -0
- data/lib/walrat/skipped_substring_exception.rb +46 -0
- data/lib/walrat/string_enumerator.rb +47 -0
- data/lib/walrat/string_parslet.rb +89 -0
- data/lib/walrat/string_result.rb +34 -0
- data/lib/walrat/symbol_parslet.rb +82 -0
- data/lib/walrat/version.rb +26 -0
- metadata +110 -0
@@ -0,0 +1,34 @@
|
|
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 LeftRecursionException < Exception
|
27
|
+
attr_accessor :continuation
|
28
|
+
|
29
|
+
def initialize continuation = nil
|
30
|
+
super self.class.to_s
|
31
|
+
@continuation = continuation
|
32
|
+
end
|
33
|
+
end # class LeftRecursionException
|
34
|
+
end # module Walrat
|
@@ -0,0 +1,126 @@
|
|
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
|
+
# Methods for embedding location information in objects returned (or
|
27
|
+
# exceptions raised) from parse methods.
|
28
|
+
module LocationTracking
|
29
|
+
attr_reader :source_text
|
30
|
+
|
31
|
+
# For occasions where a single item must serve as a carrier for array-like
|
32
|
+
# information (that is, its own start, end and source_text, as well as the
|
33
|
+
# "outer" equivalents). This can happen where a single node appears in a
|
34
|
+
# list context surrounded only by skipped content.
|
35
|
+
attr_accessor :outer_start, :outer_end, :outer_source_text
|
36
|
+
|
37
|
+
def source_text=(string)
|
38
|
+
@source_text = string.to_s.clone
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets @column_start to col.
|
42
|
+
# Sets @column_start to 0 if passed nil (for ease of use, users of classes
|
43
|
+
# that mix-in this module don't have to worry about special casing nil
|
44
|
+
# values).
|
45
|
+
def column_start=(column_start)
|
46
|
+
@column_start = column_start.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns 0 if @column_start is nil (for ease of use, users of classes that
|
50
|
+
# mix-in this module don't have to worry about special casing nil values).
|
51
|
+
def column_start
|
52
|
+
@column_start || 0
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sets @line_start to line.
|
56
|
+
# Sets @line_start to 0 if passed nil (for ease of use, users of classes
|
57
|
+
# that mix-in this module don't have to worry about special casing nil
|
58
|
+
# values).
|
59
|
+
def line_start=(line_start)
|
60
|
+
@line_start = line_start.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns 0 if @line_start is nil (for ease of use, users of classes that
|
64
|
+
# mix-in this module don't have to worry about special casing nil values).
|
65
|
+
def line_start
|
66
|
+
@line_start || 0
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convenience method for getting both line_start and column_start at once.
|
70
|
+
def start
|
71
|
+
[self.line_start, self.column_start]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convenience method for setting both line_start and column_start at once.
|
75
|
+
def start=(array)
|
76
|
+
raise ArgumentError if array.nil?
|
77
|
+
raise ArgumentError if array.length != 2
|
78
|
+
self.line_start = array[0]
|
79
|
+
self.column_start = array[1]
|
80
|
+
end
|
81
|
+
|
82
|
+
def line_end=(line_end)
|
83
|
+
@line_end = line_end.to_i
|
84
|
+
end
|
85
|
+
|
86
|
+
def line_end
|
87
|
+
@line_end || 0
|
88
|
+
end
|
89
|
+
|
90
|
+
def column_end=(column_end)
|
91
|
+
@column_end = column_end.to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
def column_end
|
95
|
+
@column_end || 0
|
96
|
+
end
|
97
|
+
|
98
|
+
# Convenience method for getting both line_end and column_end at once.
|
99
|
+
def end
|
100
|
+
[self.line_end, self.column_end]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Convenience method for setting both line_end and column_end at once.
|
104
|
+
def end=(array)
|
105
|
+
raise ArgumentError if array.nil?
|
106
|
+
raise ArgumentError if array.length != 2
|
107
|
+
self.line_end = array[0]
|
108
|
+
self.column_end = array[1]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Given another object that responds to column_end and line_end, returns
|
112
|
+
# true if the receiver is rightmost or equal.
|
113
|
+
# If the other object is farther to the right returns false.
|
114
|
+
def rightmost? other
|
115
|
+
if self.line_end > other.line_end
|
116
|
+
true
|
117
|
+
elsif other.line_end > self.line_end
|
118
|
+
false
|
119
|
+
elsif self.column_end >= other.column_end
|
120
|
+
true
|
121
|
+
else
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end # module LocationTracking
|
126
|
+
end # module Walrat
|
@@ -0,0 +1,84 @@
|
|
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
|
+
# Simple wrapper for MatchData objects that implements length, to_s and
|
27
|
+
# to_str methods.
|
28
|
+
#
|
29
|
+
# By implementing to_str, MatchDataWrappers can be directly compared with
|
30
|
+
# Strings using the == method. The original MatchData instance can be
|
31
|
+
# obtained using the match_data accessor. Upon creation a clone of the passed
|
32
|
+
# in MatchData object is stored; this means that the $~ global variable can
|
33
|
+
# be conveniently wrapped without having to worry that subsequent operations
|
34
|
+
# will alter the contents of the variable.
|
35
|
+
class MatchDataWrapper
|
36
|
+
include Walrat::LocationTracking
|
37
|
+
|
38
|
+
attr_reader :match_data
|
39
|
+
|
40
|
+
# Raises if data is nil.
|
41
|
+
def initialize data
|
42
|
+
raise ArgumentError, 'nil data' if data.nil?
|
43
|
+
self.match_data = data
|
44
|
+
end
|
45
|
+
|
46
|
+
# The definition of this method, in conjunction with the == method, allows
|
47
|
+
# automatic comparisons with String objects using the == method.
|
48
|
+
# This is because in a parser matches essentially are Strings (just like
|
49
|
+
# Exceptions and Pathnames); it's just that this class encapsulates a
|
50
|
+
# little more information (the match data) for those who want it.
|
51
|
+
def to_str
|
52
|
+
self.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
# Although this method explicitly allows for MatchDataWrapper to
|
56
|
+
# MatchDataWrapper comparisons, note that all such comparisons will return
|
57
|
+
# false except for those between instances which were initialized with
|
58
|
+
# exactly the same match data instance; this is because the MatchData class
|
59
|
+
# itself always returns false when compared with other MatchData instances.
|
60
|
+
def ==(other)
|
61
|
+
if other.kind_of? MatchDataWrapper
|
62
|
+
self.match_data == other.match_data
|
63
|
+
elsif other.respond_to? :to_str
|
64
|
+
self.to_str == other.to_str
|
65
|
+
else
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
@match_data[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
def jlength
|
75
|
+
self.to_s.jlength
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def match_data=(data)
|
81
|
+
@match_data = (data.clone rescue data)
|
82
|
+
end
|
83
|
+
end # class MatchDataWrapper
|
84
|
+
end # module Walrat
|
@@ -0,0 +1,55 @@
|
|
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
|
+
module Memoizing
|
27
|
+
# This method provides a clean, optional implementation of memoizing by
|
28
|
+
# serving as a wrapper for all parse invocations. Rather than calling the
|
29
|
+
# parse methods directly, this method should be called; if it is
|
30
|
+
# appropriate to use a memoizer then it will be invoked, otherwise control
|
31
|
+
# will fall through to the real parse method. Turning off memoizing is as
|
32
|
+
# simple as not passing a value with the :memoizer key in the options hash.
|
33
|
+
# This method defined is in a separate module so that it can easily be
|
34
|
+
# mixed in with all Parslets, ParsletCombinations and Predicates.
|
35
|
+
def memoizing_parse(string, options = {})
|
36
|
+
# will use memoizer if available and not instructed to ignore it
|
37
|
+
if options.has_key?(:memoizer) and not
|
38
|
+
(options.has_key?(:ignore_memoizer) and options[:ignore_memoizer])
|
39
|
+
options[:parseable] = self
|
40
|
+
options[:memoizer].parse string, options
|
41
|
+
else # otherwise will proceed as normal
|
42
|
+
options[:ignore_memoizer] = false
|
43
|
+
parse string, options
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Can only check for left recursion if memoizing is turned on (the help of
|
48
|
+
# the memoizer is needed).
|
49
|
+
def check_left_recursion parseable, options = {}
|
50
|
+
return unless options.has_key?(:memoizer)
|
51
|
+
options[:memoizer].check_left_recursion parseable, options
|
52
|
+
end
|
53
|
+
end # module Memoizing
|
54
|
+
end # module Walrat
|
55
|
+
|
@@ -0,0 +1,126 @@
|
|
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 MemoizingCache class memoizes the outcomes of parse operations. The
|
27
|
+
# functionality is implemented as a separate class so as to minimize the
|
28
|
+
# amount of "contamination" of other classes by memoizing code, and to allow
|
29
|
+
# memoizing to be cleanly turned on or off at will. If a MemoizingCache is
|
30
|
+
# passed to a Parslet, ParsletCombination or Predicate as a value for the
|
31
|
+
# :memoizer key in the options hash passed to a parse method, the class
|
32
|
+
# implementing that method will call the parse method on the cache rather
|
33
|
+
# than proceeding normally. The cache will either propagate the previously
|
34
|
+
# memoized result, or will defer back to the original class to obtain the
|
35
|
+
# result. A circular dependency is avoided by setting the :skip_memoizer flag
|
36
|
+
# in the options dictionary. If no MemoizingCache is passed then normal
|
37
|
+
# program flow takes place.
|
38
|
+
class MemoizingCache
|
39
|
+
# Singleton class that serves as a default value for unset keys in a Hash.
|
40
|
+
class NoValueForKey
|
41
|
+
require 'singleton'
|
42
|
+
include Singleton
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
# The results of parse operations are stored (memoized) in a cache, keyed
|
47
|
+
# on a unique identifier comprising the Parslet, ParsletCombination or
|
48
|
+
# Predicate used in the parse operation, the location of the operation
|
49
|
+
# (the line_start and column_start), and the skipping override (if any).
|
50
|
+
# The values may be:
|
51
|
+
#
|
52
|
+
# - ParseErrors raised during parsing
|
53
|
+
# - SkippedSubstringExceptions raised during parsing
|
54
|
+
# - :ZeroWidthParseSuccess symbols thrown during parsing
|
55
|
+
# - :AndPredicateSuccess symbols thrown during parsing
|
56
|
+
# - :NotPredicateSuccess symbols thrown during parsing
|
57
|
+
# - String instances returned as parse results
|
58
|
+
# - MatchDataWrapper instance returned as parse results
|
59
|
+
# - Array instances containing ordered collections of parse results
|
60
|
+
# - Node subclass instances containing AST productions
|
61
|
+
@cache = Hash.new NoValueForKey.instance
|
62
|
+
end
|
63
|
+
|
64
|
+
# The receiver checks whether there is already a stored result
|
65
|
+
# corresponding to that a unique identifier that specifies the
|
66
|
+
# "coordinates" of a parsing operation (location, parseable, skipping
|
67
|
+
# override). If found propogates the result directly to the caller rather
|
68
|
+
# than performing the parse method all over again. Here "propagation" means
|
69
|
+
# re-raising parse errors, re-throwing symbols, and returning object
|
70
|
+
# references. If not found, performs the parsing operation and stores the
|
71
|
+
# result in the cache before propagating it.
|
72
|
+
def parse string, options = {}
|
73
|
+
raise ArgumentError if string.nil?
|
74
|
+
|
75
|
+
# construct a unique identifier
|
76
|
+
identifier = [options[:parseable], options[:line_start], options[:column_start]]
|
77
|
+
identifier << options[:origin] if options.has_key? :origin
|
78
|
+
identifier << options[:skipping_override] if options.has_key? :skipping_override
|
79
|
+
|
80
|
+
if (result = @cache[identifier]) != NoValueForKey.instance
|
81
|
+
if result.kind_of? Symbol
|
82
|
+
throw result
|
83
|
+
elsif result.kind_of? Exception
|
84
|
+
raise result
|
85
|
+
else
|
86
|
+
return result
|
87
|
+
end
|
88
|
+
else
|
89
|
+
# first time for this parseable/location/skipping_override (etc)
|
90
|
+
# combination; capture result and propagate
|
91
|
+
catch :NotPredicateSuccess do
|
92
|
+
catch :AndPredicateSuccess do
|
93
|
+
catch :ZeroWidthParseSuccess do
|
94
|
+
begin
|
95
|
+
options[:ignore_memoizer] = true
|
96
|
+
|
97
|
+
# short-circuit left recursion here rather than infinite
|
98
|
+
# looping
|
99
|
+
if options[:parseable].kind_of? SymbolParslet
|
100
|
+
check_left_recursion(options[:parseable], options)
|
101
|
+
@last_seen_symbol_parslet = options[:parseable]
|
102
|
+
@last_seen_symbol_parslet_location = [options[:line_start], options[:column_start]]
|
103
|
+
end
|
104
|
+
|
105
|
+
return @cache[identifier] = options[:parseable].memoizing_parse(string, options) # store and return
|
106
|
+
rescue Exception => e
|
107
|
+
raise @cache[identifier] = e # store and re-raise
|
108
|
+
end
|
109
|
+
end
|
110
|
+
throw @cache[identifier] = :ZeroWidthParseSuccess # store and re-throw
|
111
|
+
end
|
112
|
+
throw @cache[identifier] = :AndPredicateSuccess # store and re-throw
|
113
|
+
end
|
114
|
+
throw @cache[identifier] = :NotPredicateSuccess # store and re-throw
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_left_recursion parseable, options = {}
|
119
|
+
if parseable.kind_of? SymbolParslet and
|
120
|
+
@last_seen_symbol_parslet == parseable and
|
121
|
+
@last_seen_symbol_parslet_location == [options[:line_start], options[:column_start]]
|
122
|
+
raise LeftRecursionException
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end # class MemoizingCache
|
126
|
+
end # module Walrat
|
@@ -0,0 +1,30 @@
|
|
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 NoParameterMarker
|
27
|
+
require 'singleton'
|
28
|
+
include Singleton
|
29
|
+
end # class NoParameterMarker
|
30
|
+
end # module Walrat
|