walrat 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|