citrus 2.3.1 → 2.3.2
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/citrus.rb +79 -68
- data/test/input_test.rb +3 -89
- data/test/match_test.rb +49 -0
- data/test/memoized_input_test.rb +43 -0
- metadata +108 -109
data/lib/citrus.rb
CHANGED
@@ -8,23 +8,19 @@ require 'strscan'
|
|
8
8
|
module Citrus
|
9
9
|
autoload :File, 'citrus/file'
|
10
10
|
|
11
|
-
|
11
|
+
# The current version of Citrus as [major, minor, patch].
|
12
|
+
VERSION = [2, 3, 2]
|
12
13
|
|
13
|
-
#
|
14
|
-
def self.version
|
15
|
-
VERSION.join('.')
|
16
|
-
end
|
17
|
-
|
18
|
-
# A pattern to match any character, including \\n.
|
14
|
+
# A pattern to match any character, including newline.
|
19
15
|
DOT = /./m
|
20
16
|
|
21
17
|
Infinity = 1.0 / 0
|
22
18
|
|
23
19
|
CLOSE = -1
|
24
20
|
|
25
|
-
#
|
26
|
-
def self.
|
27
|
-
|
21
|
+
# Returns the current version of Citrus as a string.
|
22
|
+
def self.version
|
23
|
+
VERSION.join('.')
|
28
24
|
end
|
29
25
|
|
30
26
|
# Evaluates the given Citrus parsing expression grammar +code+ in the global
|
@@ -37,25 +33,31 @@ module Citrus
|
|
37
33
|
# end
|
38
34
|
# end
|
39
35
|
# CITRUS
|
36
|
+
# # => [MyGrammar]
|
40
37
|
#
|
41
|
-
def self.eval(code)
|
42
|
-
parse(code).value
|
38
|
+
def self.eval(code, options={})
|
39
|
+
File.parse(code, options).value
|
43
40
|
end
|
44
41
|
|
45
42
|
# Evaluates the given expression and creates a new Rule object from it.
|
46
43
|
#
|
47
44
|
# Citrus.rule('"a" | "b"')
|
45
|
+
# # => #<Citrus::Rule: ... >
|
48
46
|
#
|
49
|
-
def self.rule(expr)
|
50
|
-
parse(expr, :root => :rule_body).value
|
47
|
+
def self.rule(expr, options={})
|
48
|
+
File.parse(expr, options.merge(:root => :rule_body)).value
|
51
49
|
end
|
52
50
|
|
53
51
|
# Loads the grammar from the given +file+ into the global scope using #eval.
|
54
|
-
|
52
|
+
#
|
53
|
+
# Citrus.load('mygrammar')
|
54
|
+
# # => [MyGrammar]
|
55
|
+
#
|
56
|
+
def self.load(file, options={})
|
55
57
|
file << '.citrus' unless ::File.file?(file)
|
56
|
-
raise "Cannot find file #{file}" unless ::File.file?(file)
|
57
|
-
raise "Cannot read file #{file}" unless ::File.readable?(file)
|
58
|
-
eval(::File.read(file))
|
58
|
+
raise ArgumentError, "Cannot find file #{file}" unless ::File.file?(file)
|
59
|
+
raise ArgumentError, "Cannot read file #{file}" unless ::File.readable?(file)
|
60
|
+
eval(::File.read(file), options)
|
59
61
|
end
|
60
62
|
|
61
63
|
# A standard error class that all Citrus errors extend.
|
@@ -157,14 +159,19 @@ module Citrus
|
|
157
159
|
lines[line_index(pos)]
|
158
160
|
end
|
159
161
|
|
162
|
+
# Returns +true+ when using memoization to cache match results.
|
163
|
+
def memoized?
|
164
|
+
false
|
165
|
+
end
|
166
|
+
|
160
167
|
# Returns an array of events for the given +rule+ at the current pointer
|
161
168
|
# position. Objects in this array may be one of three types: a Rule,
|
162
169
|
# Citrus::CLOSE, or a length (integer).
|
163
170
|
def exec(rule, events=[])
|
164
|
-
index = events.size
|
165
171
|
position = pos
|
172
|
+
index = events.size
|
166
173
|
|
167
|
-
if rule
|
174
|
+
if apply_rule(rule, position, events).size > index
|
168
175
|
position += events[-1]
|
169
176
|
@max_offset = position if position > @max_offset
|
170
177
|
end
|
@@ -177,25 +184,27 @@ module Citrus
|
|
177
184
|
# Returns the length of a match for the given +rule+ at the current pointer
|
178
185
|
# position, +nil+ if none can be made.
|
179
186
|
def test(rule)
|
180
|
-
|
181
|
-
events = rule
|
182
|
-
self.pos =
|
187
|
+
position = pos
|
188
|
+
events = apply_rule(rule, position, [])
|
189
|
+
self.pos = position
|
183
190
|
events[-1]
|
184
191
|
end
|
185
192
|
|
186
|
-
|
187
|
-
|
188
|
-
|
193
|
+
private
|
194
|
+
|
195
|
+
# Appends all events for +rule+ at the given +position+ to +events+.
|
196
|
+
def apply_rule(rule, position, events)
|
197
|
+
rule.exec(self, events)
|
189
198
|
end
|
190
199
|
end
|
191
200
|
|
192
|
-
# A
|
201
|
+
# A MemoizedInput is an Input that caches segments of the event stream for
|
193
202
|
# particular rules in a parse. This technique (also known as "Packrat"
|
194
203
|
# parsing) guarantees parsers will operate in linear time but costs
|
195
204
|
# significantly more in terms of time and memory required to perform a parse.
|
196
205
|
# For more information, please read the paper on Packrat parsing at
|
197
206
|
# http://pdos.csail.mit.edu/~baford/packrat/icfp02/.
|
198
|
-
class
|
207
|
+
class MemoizedInput < Input
|
199
208
|
def initialize(string)
|
200
209
|
super(string)
|
201
210
|
@cache = {}
|
@@ -214,31 +223,30 @@ module Citrus
|
|
214
223
|
super
|
215
224
|
end
|
216
225
|
|
217
|
-
|
218
|
-
|
226
|
+
# Returns +true+ when using memoization to cache match results.
|
227
|
+
def memoized?
|
228
|
+
true
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def apply_rule(rule, position, events) # :nodoc:
|
219
234
|
memo = @cache[rule] ||= {}
|
220
235
|
|
221
236
|
if memo[position]
|
222
237
|
@cache_hits += 1
|
238
|
+
events.concat(memo[position])
|
223
239
|
else
|
224
|
-
|
225
|
-
|
240
|
+
index = events.size
|
241
|
+
rule.exec(self, events)
|
226
242
|
|
227
|
-
|
228
|
-
|
229
|
-
position
|
230
|
-
@max_offset = position if position > @max_offset
|
243
|
+
# Memoize the result so we can use it next time this same rule is
|
244
|
+
# executed at this position.
|
245
|
+
memo[position] = events.slice(index, events.size)
|
231
246
|
end
|
232
247
|
|
233
|
-
self.pos = position
|
234
|
-
|
235
248
|
events
|
236
249
|
end
|
237
|
-
|
238
|
-
# Returns +true+ when using memoization to cache match results.
|
239
|
-
def memoized?
|
240
|
-
true
|
241
|
-
end
|
242
250
|
end
|
243
251
|
|
244
252
|
# Inclusion of this module into another extends the receiver with the grammar
|
@@ -281,8 +289,9 @@ module Citrus
|
|
281
289
|
# option. Otherwise, all options are the same as in Rule#parse.
|
282
290
|
def parse(string, options={})
|
283
291
|
rule_name = options.delete(:root) || root
|
292
|
+
raise Error, "No root rule specified" unless rule_name
|
284
293
|
rule = rule(rule_name)
|
285
|
-
raise
|
294
|
+
raise Error, "No rule named \"#{rule_name}\"" unless rule
|
286
295
|
rule.parse(string, options)
|
287
296
|
end
|
288
297
|
|
@@ -511,7 +520,7 @@ module Citrus
|
|
511
520
|
mod = Module.new { define_method(:value, &mod) }
|
512
521
|
end
|
513
522
|
|
514
|
-
raise ArgumentError unless Module === mod
|
523
|
+
raise ArgumentError, "Extension must be a Module" unless Module === mod
|
515
524
|
|
516
525
|
@extension = mod
|
517
526
|
end
|
@@ -519,22 +528,29 @@ module Citrus
|
|
519
528
|
# The module this rule uses to extend new matches.
|
520
529
|
attr_reader :extension
|
521
530
|
|
531
|
+
# The default set of options to use when calling #parse or #test.
|
532
|
+
def default_options # :nodoc:
|
533
|
+
{ :consume => true,
|
534
|
+
:memoize => false,
|
535
|
+
:offset => 0
|
536
|
+
}
|
537
|
+
end
|
538
|
+
|
522
539
|
# Attempts to parse the given +string+ and return a Match if any can be
|
523
|
-
# made.
|
540
|
+
# made. +options+ may contain any of the following keys:
|
524
541
|
#
|
525
|
-
#
|
526
|
-
# to
|
542
|
+
# consume:: If this is +true+ a ParseError will be raised unless the
|
543
|
+
# entire input string is consumed. Defaults to +true+.
|
527
544
|
# memoize:: If this is +true+ the matches generated during a parse are
|
528
|
-
# memoized. See
|
545
|
+
# memoized. See MemoizedInput for more information. Defaults to
|
529
546
|
# +false+.
|
530
|
-
#
|
531
|
-
#
|
532
|
-
# +true+.
|
547
|
+
# offset:: The offset in +string+ at which to start parsing. Defaults
|
548
|
+
# to 0.
|
533
549
|
def parse(string, options={})
|
534
|
-
opts =
|
550
|
+
opts = default_options.merge(options)
|
535
551
|
|
536
552
|
input = if opts[:memoize]
|
537
|
-
|
553
|
+
MemoizedInput.new(string)
|
538
554
|
else
|
539
555
|
Input.new(string)
|
540
556
|
end
|
@@ -551,18 +567,13 @@ module Citrus
|
|
551
567
|
Match.new(string.slice(opts[:offset], length), events)
|
552
568
|
end
|
553
569
|
|
554
|
-
# The default set of options to use when parsing.
|
555
|
-
def default_parse_options # :nodoc:
|
556
|
-
{ :offset => 0,
|
557
|
-
:memoize => false,
|
558
|
-
:consume => true
|
559
|
-
}
|
560
|
-
end
|
561
|
-
|
562
570
|
# Tests whether or not this rule matches on the given +string+. Returns the
|
563
|
-
# length of the match if any can be made, +nil+ otherwise.
|
564
|
-
|
565
|
-
|
571
|
+
# length of the match if any can be made, +nil+ otherwise. Accepts the same
|
572
|
+
# +options+ as #parse.
|
573
|
+
def test(string, options={})
|
574
|
+
parse(string, options).length
|
575
|
+
rescue ParseError
|
576
|
+
nil
|
566
577
|
end
|
567
578
|
|
568
579
|
# Returns +true+ if this rule is a Terminal.
|
@@ -1027,7 +1038,7 @@ module Citrus
|
|
1027
1038
|
events << CLOSE
|
1028
1039
|
events << length
|
1029
1040
|
else
|
1030
|
-
events.slice!(start,
|
1041
|
+
events.slice!(start, index)
|
1031
1042
|
end
|
1032
1043
|
|
1033
1044
|
events
|
@@ -1088,7 +1099,7 @@ module Citrus
|
|
1088
1099
|
events << CLOSE
|
1089
1100
|
events << length
|
1090
1101
|
else
|
1091
|
-
events.slice!(start,
|
1102
|
+
events.slice!(start, index)
|
1092
1103
|
end
|
1093
1104
|
|
1094
1105
|
events
|
@@ -1380,6 +1391,6 @@ class Object
|
|
1380
1391
|
namespace = respond_to?(:const_set) ? self : Object
|
1381
1392
|
namespace.const_set(name, Citrus::Grammar.new(&block))
|
1382
1393
|
rescue NameError
|
1383
|
-
raise ArgumentError,
|
1394
|
+
raise ArgumentError, "Invalid grammar name: #{name}"
|
1384
1395
|
end
|
1385
1396
|
end
|
data/test/input_test.rb
CHANGED
@@ -2,8 +2,7 @@ require File.expand_path('../helper', __FILE__)
|
|
2
2
|
|
3
3
|
class InputTest < Test::Unit::TestCase
|
4
4
|
def test_memoized?
|
5
|
-
|
6
|
-
assert MemoizingInput.new('').memoized?
|
5
|
+
assert_equal(false, Input.new('').memoized?)
|
7
6
|
end
|
8
7
|
|
9
8
|
def test_offsets_new
|
@@ -23,7 +22,7 @@ class InputTest < Test::Unit::TestCase
|
|
23
22
|
assert_equal("def\n", input.line)
|
24
23
|
end
|
25
24
|
|
26
|
-
def
|
25
|
+
def test_exec
|
27
26
|
a = Rule.for('a')
|
28
27
|
b = Rule.for('b')
|
29
28
|
c = Rule.for('c')
|
@@ -56,7 +55,7 @@ class InputTest < Test::Unit::TestCase
|
|
56
55
|
assert_equal(expected_events, events)
|
57
56
|
end
|
58
57
|
|
59
|
-
def
|
58
|
+
def test_exec2
|
60
59
|
a = Rule.for('a')
|
61
60
|
b = Rule.for('b')
|
62
61
|
c = Choice.new([ a, b ])
|
@@ -100,89 +99,4 @@ class InputTest < Test::Unit::TestCase
|
|
100
99
|
|
101
100
|
assert_equal(expected_events, events)
|
102
101
|
end
|
103
|
-
|
104
|
-
grammar :LetterA do
|
105
|
-
rule :top do
|
106
|
-
any(:three_as, :two_as, :one_a)
|
107
|
-
end
|
108
|
-
|
109
|
-
rule :three_as do
|
110
|
-
rep(:one_a, 3, 3)
|
111
|
-
end
|
112
|
-
|
113
|
-
rule :two_as do
|
114
|
-
rep(:one_a, 2, 2)
|
115
|
-
end
|
116
|
-
|
117
|
-
rule :one_a do
|
118
|
-
"a"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def test_cache_hits1
|
123
|
-
input = MemoizingInput.new('a')
|
124
|
-
input.exec(LetterA.rule(:top))
|
125
|
-
assert_equal(3, input.cache_hits)
|
126
|
-
end
|
127
|
-
|
128
|
-
def test_cache_hits2
|
129
|
-
input = MemoizingInput.new('aa')
|
130
|
-
input.exec(LetterA.rule(:top))
|
131
|
-
assert_equal(2, input.cache_hits)
|
132
|
-
end
|
133
|
-
|
134
|
-
def test_cache_hits3
|
135
|
-
input = MemoizingInput.new('aaa')
|
136
|
-
input.exec(LetterA.rule(:top))
|
137
|
-
assert_equal(0, input.cache_hits)
|
138
|
-
end
|
139
|
-
|
140
|
-
grammar :Addition do
|
141
|
-
rule :additive do
|
142
|
-
all(:number, :plus, label(any(:additive, :number), 'term')) {
|
143
|
-
number.value + term.value
|
144
|
-
}
|
145
|
-
end
|
146
|
-
|
147
|
-
rule :number do
|
148
|
-
all(/[0-9]+/, :space) {
|
149
|
-
strip.to_i
|
150
|
-
}
|
151
|
-
end
|
152
|
-
|
153
|
-
rule :plus do
|
154
|
-
all('+', :space)
|
155
|
-
end
|
156
|
-
|
157
|
-
rule :space do
|
158
|
-
/[ \t]*/
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def test_match
|
163
|
-
match = Addition.parse('+', :root => :plus)
|
164
|
-
assert(match)
|
165
|
-
assert(match.matches)
|
166
|
-
assert_equal(2, match.matches.length)
|
167
|
-
|
168
|
-
match = Addition.parse('+ ', :root => :plus)
|
169
|
-
assert(match)
|
170
|
-
assert(match.matches)
|
171
|
-
assert_equal(2, match.matches.length)
|
172
|
-
|
173
|
-
match = Addition.parse('99', :root => :number)
|
174
|
-
assert(match)
|
175
|
-
assert(match.matches)
|
176
|
-
assert_equal(2, match.matches.length)
|
177
|
-
|
178
|
-
match = Addition.parse('99 ', :root => :number)
|
179
|
-
assert(match)
|
180
|
-
assert(match.matches)
|
181
|
-
assert_equal(2, match.matches.length)
|
182
|
-
|
183
|
-
match = Addition.parse('1+2')
|
184
|
-
assert(match)
|
185
|
-
assert(match.matches)
|
186
|
-
assert_equal(3, match.matches.length)
|
187
|
-
end
|
188
102
|
end
|
data/test/match_test.rb
CHANGED
@@ -67,4 +67,53 @@ class MatchTest < Test::Unit::TestCase
|
|
67
67
|
assert_equal(3, m.matches.length)
|
68
68
|
end
|
69
69
|
end
|
70
|
+
|
71
|
+
grammar :Addition do
|
72
|
+
rule :additive do
|
73
|
+
all(:number, :plus, label(any(:additive, :number), 'term')) {
|
74
|
+
number.value + term.value
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
rule :number do
|
79
|
+
all(/[0-9]+/, :space) {
|
80
|
+
strip.to_i
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
rule :plus do
|
85
|
+
all('+', :space)
|
86
|
+
end
|
87
|
+
|
88
|
+
rule :space do
|
89
|
+
/[ \t]*/
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_matches2
|
94
|
+
match = Addition.parse('+', :root => :plus)
|
95
|
+
assert(match)
|
96
|
+
assert(match.matches)
|
97
|
+
assert_equal(2, match.matches.length)
|
98
|
+
|
99
|
+
match = Addition.parse('+ ', :root => :plus)
|
100
|
+
assert(match)
|
101
|
+
assert(match.matches)
|
102
|
+
assert_equal(2, match.matches.length)
|
103
|
+
|
104
|
+
match = Addition.parse('99', :root => :number)
|
105
|
+
assert(match)
|
106
|
+
assert(match.matches)
|
107
|
+
assert_equal(2, match.matches.length)
|
108
|
+
|
109
|
+
match = Addition.parse('99 ', :root => :number)
|
110
|
+
assert(match)
|
111
|
+
assert(match.matches)
|
112
|
+
assert_equal(2, match.matches.length)
|
113
|
+
|
114
|
+
match = Addition.parse('1+2')
|
115
|
+
assert(match)
|
116
|
+
assert(match.matches)
|
117
|
+
assert_equal(3, match.matches.length)
|
118
|
+
end
|
70
119
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class MemoizedInputTest < Test::Unit::TestCase
|
4
|
+
def test_memoized?
|
5
|
+
assert MemoizedInput.new('').memoized?
|
6
|
+
end
|
7
|
+
|
8
|
+
grammar :LetterA do
|
9
|
+
rule :top do
|
10
|
+
any(:three_as, :two_as, :one_a)
|
11
|
+
end
|
12
|
+
|
13
|
+
rule :three_as do
|
14
|
+
rep(:one_a, 3, 3)
|
15
|
+
end
|
16
|
+
|
17
|
+
rule :two_as do
|
18
|
+
rep(:one_a, 2, 2)
|
19
|
+
end
|
20
|
+
|
21
|
+
rule :one_a do
|
22
|
+
"a"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_cache_hits1
|
27
|
+
input = MemoizedInput.new('a')
|
28
|
+
input.exec(LetterA.rule(:top))
|
29
|
+
assert_equal(3, input.cache_hits)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_cache_hits2
|
33
|
+
input = MemoizedInput.new('aa')
|
34
|
+
input.exec(LetterA.rule(:top))
|
35
|
+
assert_equal(2, input.cache_hits)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_cache_hits3
|
39
|
+
input = MemoizedInput.new('aaa')
|
40
|
+
input.exec(LetterA.rule(:top))
|
41
|
+
assert_equal(0, input.cache_hits)
|
42
|
+
end
|
43
|
+
end
|
metadata
CHANGED
@@ -3,33 +3,32 @@ name: citrus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
version: 2.3.
|
6
|
+
- 2
|
7
|
+
- 3
|
8
|
+
- 2
|
9
|
+
version: 2.3.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
|
-
- Michael Jackson
|
12
|
+
- Michael Jackson
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-02 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
version_requirements: *id001
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
33
32
|
description: Parsing Expressions for Ruby
|
34
33
|
email: mjijackson@gmail.com
|
35
34
|
executables: []
|
@@ -37,112 +36,112 @@ executables: []
|
|
37
36
|
extensions: []
|
38
37
|
|
39
38
|
extra_rdoc_files:
|
40
|
-
- README
|
39
|
+
- README
|
41
40
|
files:
|
42
|
-
- benchmark/seqpar.citrus
|
43
|
-
- benchmark/seqpar.gnuplot
|
44
|
-
- benchmark/seqpar.rb
|
45
|
-
- doc/background.markdown
|
46
|
-
- doc/example.markdown
|
47
|
-
- doc/extras.markdown
|
48
|
-
- doc/index.markdown
|
49
|
-
- doc/license.markdown
|
50
|
-
- doc/links.markdown
|
51
|
-
- doc/syntax.markdown
|
52
|
-
- doc/testing.markdown
|
53
|
-
- examples/calc.citrus
|
54
|
-
- examples/calc.rb
|
55
|
-
- examples/ip.citrus
|
56
|
-
- examples/ip.rb
|
57
|
-
- lib/citrus
|
58
|
-
- lib/citrus.rb
|
59
|
-
- test/
|
60
|
-
- test/
|
61
|
-
- test/
|
62
|
-
- test/
|
63
|
-
- test/
|
64
|
-
- test/
|
65
|
-
- test/
|
66
|
-
- test/
|
67
|
-
- test/
|
68
|
-
- test/
|
69
|
-
- test/
|
70
|
-
- test/
|
71
|
-
- test/
|
72
|
-
- test/
|
73
|
-
- test/
|
74
|
-
- test/
|
75
|
-
- test/
|
76
|
-
- test/
|
77
|
-
- test/
|
78
|
-
- test/
|
79
|
-
- test/
|
80
|
-
- test/
|
81
|
-
- test/
|
82
|
-
- test/
|
83
|
-
- test/
|
84
|
-
- test/
|
85
|
-
- test/
|
86
|
-
- test/
|
87
|
-
- test/
|
88
|
-
- citrus
|
89
|
-
-
|
90
|
-
-
|
41
|
+
- benchmark/seqpar.citrus
|
42
|
+
- benchmark/seqpar.gnuplot
|
43
|
+
- benchmark/seqpar.rb
|
44
|
+
- doc/background.markdown
|
45
|
+
- doc/example.markdown
|
46
|
+
- doc/extras.markdown
|
47
|
+
- doc/index.markdown
|
48
|
+
- doc/license.markdown
|
49
|
+
- doc/links.markdown
|
50
|
+
- doc/syntax.markdown
|
51
|
+
- doc/testing.markdown
|
52
|
+
- examples/calc.citrus
|
53
|
+
- examples/calc.rb
|
54
|
+
- examples/ip.citrus
|
55
|
+
- examples/ip.rb
|
56
|
+
- lib/citrus.rb
|
57
|
+
- lib/citrus/file.rb
|
58
|
+
- test/alias_test.rb
|
59
|
+
- test/and_predicate_test.rb
|
60
|
+
- test/but_predicate_test.rb
|
61
|
+
- test/calc_file_test.rb
|
62
|
+
- test/calc_test.rb
|
63
|
+
- test/choice_test.rb
|
64
|
+
- test/extension_test.rb
|
65
|
+
- test/file_test.rb
|
66
|
+
- test/grammar_test.rb
|
67
|
+
- test/helper.rb
|
68
|
+
- test/input_test.rb
|
69
|
+
- test/label_test.rb
|
70
|
+
- test/match_test.rb
|
71
|
+
- test/memoized_input_test.rb
|
72
|
+
- test/multibyte_test.rb
|
73
|
+
- test/not_predicate_test.rb
|
74
|
+
- test/parse_error_test.rb
|
75
|
+
- test/repeat_test.rb
|
76
|
+
- test/sequence_test.rb
|
77
|
+
- test/string_terminal_test.rb
|
78
|
+
- test/super_test.rb
|
79
|
+
- test/terminal_test.rb
|
80
|
+
- test/_files/alias.citrus
|
81
|
+
- test/_files/grammar1.citrus
|
82
|
+
- test/_files/grammar2.citrus
|
83
|
+
- test/_files/rule1.citrus
|
84
|
+
- test/_files/rule2.citrus
|
85
|
+
- test/_files/rule3.citrus
|
86
|
+
- test/_files/super.citrus
|
87
|
+
- test/_files/super2.citrus
|
88
|
+
- citrus.gemspec
|
89
|
+
- Rakefile
|
90
|
+
- README
|
91
91
|
has_rdoc: true
|
92
92
|
homepage: http://mjijackson.com/citrus
|
93
93
|
licenses: []
|
94
94
|
|
95
95
|
post_install_message:
|
96
96
|
rdoc_options:
|
97
|
-
- --line-numbers
|
98
|
-
- --inline-source
|
99
|
-
- --title
|
100
|
-
- Citrus
|
101
|
-
- --main
|
102
|
-
- Citrus
|
97
|
+
- --line-numbers
|
98
|
+
- --inline-source
|
99
|
+
- --title
|
100
|
+
- Citrus
|
101
|
+
- --main
|
102
|
+
- Citrus
|
103
103
|
require_paths:
|
104
|
-
- lib
|
104
|
+
- lib
|
105
105
|
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
106
|
requirements:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
113
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
113
|
requirements:
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
121
119
|
requirements: []
|
122
120
|
|
123
121
|
rubyforge_project:
|
124
|
-
rubygems_version: 1.3.
|
122
|
+
rubygems_version: 1.3.6
|
125
123
|
signing_key:
|
126
124
|
specification_version: 3
|
127
125
|
summary: Parsing Expressions for Ruby
|
128
126
|
test_files:
|
129
|
-
- test/alias_test.rb
|
130
|
-
- test/and_predicate_test.rb
|
131
|
-
- test/but_predicate_test.rb
|
132
|
-
- test/calc_file_test.rb
|
133
|
-
- test/calc_test.rb
|
134
|
-
- test/choice_test.rb
|
135
|
-
- test/extension_test.rb
|
136
|
-
- test/file_test.rb
|
137
|
-
- test/grammar_test.rb
|
138
|
-
- test/input_test.rb
|
139
|
-
- test/label_test.rb
|
140
|
-
- test/match_test.rb
|
141
|
-
- test/
|
142
|
-
- test/
|
143
|
-
- test/
|
144
|
-
- test/
|
145
|
-
- test/
|
146
|
-
- test/
|
147
|
-
- test/
|
148
|
-
- test/
|
127
|
+
- test/alias_test.rb
|
128
|
+
- test/and_predicate_test.rb
|
129
|
+
- test/but_predicate_test.rb
|
130
|
+
- test/calc_file_test.rb
|
131
|
+
- test/calc_test.rb
|
132
|
+
- test/choice_test.rb
|
133
|
+
- test/extension_test.rb
|
134
|
+
- test/file_test.rb
|
135
|
+
- test/grammar_test.rb
|
136
|
+
- test/input_test.rb
|
137
|
+
- test/label_test.rb
|
138
|
+
- test/match_test.rb
|
139
|
+
- test/memoized_input_test.rb
|
140
|
+
- test/multibyte_test.rb
|
141
|
+
- test/not_predicate_test.rb
|
142
|
+
- test/parse_error_test.rb
|
143
|
+
- test/repeat_test.rb
|
144
|
+
- test/sequence_test.rb
|
145
|
+
- test/string_terminal_test.rb
|
146
|
+
- test/super_test.rb
|
147
|
+
- test/terminal_test.rb
|