citrus 3.0.0 → 3.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.
- checksums.yaml +7 -0
- data/CHANGES +45 -0
- data/README.md +16 -14
- data/lib/citrus.rb +7 -16
- data/lib/citrus/file.rb +31 -15
- data/lib/citrus/version.rb +1 -1
- data/test/extension_test.rb +2 -2
- data/test/grammar_test.rb +7 -0
- data/test/match_test.rb +21 -2
- metadata +9 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2caf741a96f7499506f5bf450998aad817dbe9d4
|
4
|
+
data.tar.gz: 955838c4b13b2ae7c25be7b7d8e440a594105be8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3b02bb8198f6190575e3e4960be9d81adcca08d638919e32a6b1b1dc164e953007ce60b6ed86e0988a51a3c81b2ad490f40863e9ed61f06a58e50193a055e03f
|
7
|
+
data.tar.gz: 3a5b292f74e1849e01ebe3349c402a4ad3d7f9c1f0a024b21f830c3f10700063f9551ebd601155070992f9d636cca4e6609dd00e4e72faa8400c098d622510ad
|
data/CHANGES
CHANGED
@@ -1,9 +1,54 @@
|
|
1
|
+
= 3.0.1 / 2014-03-14
|
2
|
+
|
3
|
+
* Fixed bad 3.0.0 release.
|
4
|
+
|
1
5
|
= 3.0.0 / 2014-03-14
|
2
6
|
|
3
7
|
* Moved Object#grammar to citrus/core_ext.rb. Citrus no longer installs core
|
4
8
|
extensions by default. Use "require 'citrus/core_ext.rb'" instead of
|
5
9
|
"require 'citrus'" to keep the previous behavior.
|
6
10
|
|
11
|
+
* Removed Match#method_missing, added #capture(name) and #captures(name)
|
12
|
+
|
13
|
+
Match#method_missing is unsafe as illustrated in Github issue #41. In
|
14
|
+
particular, it makes composing a grammar with aribitrary gems unsafe (e.g.
|
15
|
+
when the latter make core extensions), leads to unexpected results with
|
16
|
+
labels match existing Kernel methods (e.g. `p`), and prevents Match from
|
17
|
+
getting new methods in a backward compatible way. This commit therefore
|
18
|
+
removes it.
|
19
|
+
|
20
|
+
In Citrus 2.x, method_missing allowed rule productions to denote captured
|
21
|
+
matches by label name:
|
22
|
+
|
23
|
+
rule pair
|
24
|
+
(foo ':' bar) {
|
25
|
+
[foo.value, bar.value]
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
Also, it allowed invoking String operators on the Match's text:
|
30
|
+
|
31
|
+
rule int
|
32
|
+
[0-9]+ { to_i }
|
33
|
+
end
|
34
|
+
|
35
|
+
Those two scenarios no longer work out of the box in Citrus 3.0. You must
|
36
|
+
use capture(label) for the former, and to_str for the latter:
|
37
|
+
|
38
|
+
rule pair
|
39
|
+
(foo ':' bar) {
|
40
|
+
[capture(:foo).value, capture(:bar).value]
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
rule int
|
45
|
+
[0-9]+ { to_str.to_i }
|
46
|
+
end
|
47
|
+
|
48
|
+
Match#captures now accepts an optional label name as first argument and
|
49
|
+
returns the corresponding array of matches for that label (useful in case
|
50
|
+
the label belongs to a repetition).
|
51
|
+
|
7
52
|
= 2.5.0 / 2014-03-13
|
8
53
|
|
9
54
|
* Inputs may be generated from many different sources, including Pathname and
|
data/README.md
CHANGED
@@ -232,8 +232,8 @@ Additionally, extensions may be specified inline using curly braces. When using
|
|
232
232
|
this method, the code inside the curly braces may be invoked by calling the
|
233
233
|
`value` method on the match object.
|
234
234
|
|
235
|
-
[0-9] { to_i }
|
236
|
-
|
235
|
+
[0-9] { to_str.to_i } # match any digit and return its integer value when
|
236
|
+
# calling the #value method on the match object
|
237
237
|
|
238
238
|
Note that when using the inline block method you may also specify arguments in
|
239
239
|
between vertical bars immediately following the opening curly brace, just like
|
@@ -358,13 +358,13 @@ blocks. Let's extend the `Addition` grammar using this technique.
|
|
358
358
|
grammar Addition
|
359
359
|
rule additive
|
360
360
|
(number plus term:(additive | number)) {
|
361
|
-
number.value + term.value
|
361
|
+
capture(:number).value + capture(:term).value
|
362
362
|
}
|
363
363
|
end
|
364
364
|
|
365
365
|
rule number
|
366
366
|
([0-9]+ space) {
|
367
|
-
to_i
|
367
|
+
to_str.to_i
|
368
368
|
}
|
369
369
|
end
|
370
370
|
|
@@ -383,17 +383,19 @@ execute by calling `value` on match objects that result from those rules. It's
|
|
383
383
|
easiest to explain what is going on here by starting with the lowest level
|
384
384
|
block, which is defined within `number`.
|
385
385
|
|
386
|
-
Inside this block we see a call to another method, namely `
|
387
|
-
the context of a match object,
|
388
|
-
|
389
|
-
|
386
|
+
Inside this block we see a call to another method, namely `to_str`. When called
|
387
|
+
in the context of a match object, this method returns the match's internal
|
388
|
+
string object. Thus, the call to `to_str.to_i` should return the integer value
|
389
|
+
of the match.
|
390
390
|
|
391
391
|
Similarly, matches created by `additive` will also have a `value` method. Notice
|
392
392
|
the use of the `term` label within the rule definition. This label allows the
|
393
393
|
match that is created by the choice between `additive` and `number` to be
|
394
|
-
retrieved using
|
394
|
+
retrieved using `capture(:term)`. The value of an additive match is determined
|
395
395
|
to be the values of its `number` and `term` matches added together using Ruby's
|
396
|
-
addition operator.
|
396
|
+
addition operator. Note that the plural form `captures(:term)` can be used to
|
397
|
+
get an array of matches for a given label (e.g. when the label belongs to a
|
398
|
+
repetition).
|
397
399
|
|
398
400
|
Since `additive` is the first rule defined in the grammar, any match that
|
399
401
|
results from parsing a string with this grammar will have a `value` method that
|
@@ -441,11 +443,11 @@ look like this:
|
|
441
443
|
rule additive
|
442
444
|
(number plus term:(additive | number)) {
|
443
445
|
def lhs
|
444
|
-
number.value
|
446
|
+
capture(:number).value
|
445
447
|
end
|
446
448
|
|
447
449
|
def rhs
|
448
|
-
term.value
|
450
|
+
capture(:term).value
|
449
451
|
end
|
450
452
|
|
451
453
|
def value
|
@@ -475,11 +477,11 @@ define the following module.
|
|
475
477
|
|
476
478
|
module Additive
|
477
479
|
def lhs
|
478
|
-
number.value
|
480
|
+
capture(:number).value
|
479
481
|
end
|
480
482
|
|
481
483
|
def rhs
|
482
|
-
term.value
|
484
|
+
capture(:term).value
|
483
485
|
end
|
484
486
|
|
485
487
|
def value
|
data/lib/citrus.rb
CHANGED
@@ -1326,9 +1326,14 @@ module Citrus
|
|
1326
1326
|
|
1327
1327
|
# Returns a hash of capture names to arrays of matches with that name,
|
1328
1328
|
# in the order they appeared in the input.
|
1329
|
-
def captures
|
1329
|
+
def captures(name = nil)
|
1330
1330
|
process_events! unless @captures
|
1331
|
-
@captures
|
1331
|
+
name ? @captures[name] : @captures
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
# Convenient method for captures[name].first.
|
1335
|
+
def capture(name)
|
1336
|
+
captures[name].first
|
1332
1337
|
end
|
1333
1338
|
|
1334
1339
|
# Returns an array of all immediate submatches of this match.
|
@@ -1342,20 +1347,6 @@ module Citrus
|
|
1342
1347
|
matches.first
|
1343
1348
|
end
|
1344
1349
|
|
1345
|
-
# Allows methods of this match's string to be called directly and provides
|
1346
|
-
# a convenient interface for retrieving the first match with a given name.
|
1347
|
-
def method_missing(sym, *args, &block)
|
1348
|
-
unless defined?(Citrus::METHOD_MISSING_WARNED)
|
1349
|
-
warn("[`#{sym}`] Citrus::Match#method_missing is unsafe and will be removed in 3.0. Use captures.")
|
1350
|
-
Citrus.send(:const_set, :METHOD_MISSING_WARNED, true)
|
1351
|
-
end
|
1352
|
-
if string.respond_to?(sym)
|
1353
|
-
string.__send__(sym, *args, &block)
|
1354
|
-
else
|
1355
|
-
captures[sym].first
|
1356
|
-
end
|
1357
|
-
end
|
1358
|
-
|
1359
1350
|
alias_method :to_s, :string
|
1360
1351
|
|
1361
1352
|
# This alias allows strings to be compared to the string value of Match
|
data/lib/citrus/file.rb
CHANGED
@@ -6,6 +6,10 @@ module Citrus
|
|
6
6
|
# Some helper methods for rules that alias +module_name+ and don't want to
|
7
7
|
# use +Kernel#eval+ to retrieve Module objects.
|
8
8
|
module ModuleNameHelpers #:nodoc:
|
9
|
+
def module_name
|
10
|
+
capture(:module_name)
|
11
|
+
end
|
12
|
+
|
9
13
|
def module_segments
|
10
14
|
@module_segments ||= module_name.value.split('::')
|
11
15
|
end
|
@@ -58,6 +62,7 @@ module Citrus
|
|
58
62
|
captures[:include].each {|inc| grammar.include(inc.value) }
|
59
63
|
captures[:rule].each {|r| grammar.rule(r.rule_name.value, r.value) }
|
60
64
|
|
65
|
+
root = capture(:root)
|
61
66
|
grammar.root(root.value) if root
|
62
67
|
|
63
68
|
grammar
|
@@ -66,10 +71,17 @@ module Citrus
|
|
66
71
|
end
|
67
72
|
|
68
73
|
rule :rule do
|
69
|
-
all(:rule_keyword, :rule_name, zero_or_one(:expression), :end_keyword)
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
mod all(:rule_keyword, :rule_name, zero_or_one(:expression), :end_keyword) do
|
75
|
+
def rule_name
|
76
|
+
capture(:rule_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def value
|
80
|
+
# An empty rule definition matches the empty string.
|
81
|
+
expr = capture(:expression)
|
82
|
+
expr ? expr.value : Rule.for('')
|
83
|
+
end
|
84
|
+
end
|
73
85
|
end
|
74
86
|
|
75
87
|
rule :expression do
|
@@ -88,7 +100,8 @@ module Citrus
|
|
88
100
|
|
89
101
|
rule :labelled do
|
90
102
|
all(zero_or_one(:label), :extended) {
|
91
|
-
|
103
|
+
label = capture(:label)
|
104
|
+
rule = capture(:extended).value
|
92
105
|
rule.label = label.value if label
|
93
106
|
rule
|
94
107
|
}
|
@@ -96,7 +109,8 @@ module Citrus
|
|
96
109
|
|
97
110
|
rule :extended do
|
98
111
|
all(:prefix, zero_or_one(:extension)) {
|
99
|
-
|
112
|
+
extension = capture(:extension)
|
113
|
+
rule = capture(:prefix).value
|
100
114
|
rule.extension = extension.value if extension
|
101
115
|
rule
|
102
116
|
}
|
@@ -104,7 +118,8 @@ module Citrus
|
|
104
118
|
|
105
119
|
rule :prefix do
|
106
120
|
all(zero_or_one(:predicate), :suffix) {
|
107
|
-
|
121
|
+
predicate = capture(:predicate)
|
122
|
+
rule = capture(:suffix).value
|
108
123
|
rule = predicate.value(rule) if predicate
|
109
124
|
rule
|
110
125
|
}
|
@@ -112,7 +127,8 @@ module Citrus
|
|
112
127
|
|
113
128
|
rule :suffix do
|
114
129
|
all(:primary, zero_or_one(:repeat)) {
|
115
|
-
|
130
|
+
repeat = capture(:repeat)
|
131
|
+
rule = capture(:primary).value
|
116
132
|
rule = repeat.value(rule) if repeat
|
117
133
|
rule
|
118
134
|
}
|
@@ -124,7 +140,7 @@ module Citrus
|
|
124
140
|
|
125
141
|
rule :grouping do
|
126
142
|
all(['(', zero_or_one(:space)], :expression, [')', zero_or_one(:space)]) {
|
127
|
-
expression.value
|
143
|
+
capture(:expression).value
|
128
144
|
}
|
129
145
|
end
|
130
146
|
|
@@ -132,7 +148,7 @@ module Citrus
|
|
132
148
|
|
133
149
|
rule :require do
|
134
150
|
all(:require_keyword, :quoted_string) {
|
135
|
-
quoted_string.value
|
151
|
+
capture(:quoted_string).value
|
136
152
|
}
|
137
153
|
end
|
138
154
|
|
@@ -148,7 +164,7 @@ module Citrus
|
|
148
164
|
|
149
165
|
rule :root do
|
150
166
|
all(:root_keyword, :rule_name) {
|
151
|
-
rule_name.value
|
167
|
+
capture(:rule_name).value
|
152
168
|
}
|
153
169
|
end
|
154
170
|
|
@@ -172,7 +188,7 @@ module Citrus
|
|
172
188
|
|
173
189
|
rule :alias do
|
174
190
|
all(notp(:end_keyword), :rule_name) {
|
175
|
-
Alias.new(rule_name.value)
|
191
|
+
Alias.new(capture(:rule_name).value)
|
176
192
|
}
|
177
193
|
end
|
178
194
|
|
@@ -232,7 +248,7 @@ module Citrus
|
|
232
248
|
|
233
249
|
rule :label do
|
234
250
|
all(/[a-zA-Z0-9_]+/, :space, ':', :space) {
|
235
|
-
first.to_sym
|
251
|
+
first.to_str.to_sym
|
236
252
|
}
|
237
253
|
end
|
238
254
|
|
@@ -312,8 +328,8 @@ module Citrus
|
|
312
328
|
|
313
329
|
rule :star do
|
314
330
|
all(/[0-9]*/, '*', /[0-9]*/, :space) { |rule|
|
315
|
-
min = captures[1] == '' ? 0 : captures[1].to_i
|
316
|
-
max = captures[3] == '' ? Infinity : captures[3].to_i
|
331
|
+
min = captures[1] == '' ? 0 : captures[1].to_str.to_i
|
332
|
+
max = captures[3] == '' ? Infinity : captures[3].to_str.to_i
|
317
333
|
Repeat.new(rule, min, max)
|
318
334
|
}
|
319
335
|
end
|
data/lib/citrus/version.rb
CHANGED
data/test/extension_test.rb
CHANGED
data/test/grammar_test.rb
CHANGED
@@ -149,6 +149,13 @@ class GrammarTest < Test::Unit::TestCase
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
+
def test_labeled_production
|
153
|
+
grammar = Grammar.new {
|
154
|
+
rule(:abc) { label('abc', :p){ capture(:p) } }
|
155
|
+
}
|
156
|
+
assert_equal('abc', grammar.parse('abc').value)
|
157
|
+
end
|
158
|
+
|
152
159
|
def test_global_grammar
|
153
160
|
assert_raise ArgumentError do
|
154
161
|
grammar(:abc)
|
data/test/match_test.rb
CHANGED
@@ -94,13 +94,13 @@ class MatchTest < Test::Unit::TestCase
|
|
94
94
|
grammar :Addition do
|
95
95
|
rule :additive do
|
96
96
|
all(:number, :plus, label(any(:additive, :number), 'term')) {
|
97
|
-
number.value + term.value
|
97
|
+
capture(:number).value + capture(:term).value
|
98
98
|
}
|
99
99
|
end
|
100
100
|
|
101
101
|
rule :number do
|
102
102
|
all(/[0-9]+/, :space) {
|
103
|
-
strip.to_i
|
103
|
+
to_str.strip.to_i
|
104
104
|
}
|
105
105
|
end
|
106
106
|
|
@@ -139,4 +139,23 @@ class MatchTest < Test::Unit::TestCase
|
|
139
139
|
assert(match.matches)
|
140
140
|
assert_equal(3, match.matches.length)
|
141
141
|
end
|
142
|
+
|
143
|
+
def test_capture
|
144
|
+
match = Addition.parse('1+2')
|
145
|
+
assert(match.capture(:number))
|
146
|
+
assert_equal(1, match.capture(:number).value)
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_captures
|
150
|
+
match = Addition.parse('1+2')
|
151
|
+
assert(match.captures)
|
152
|
+
assert_equal(7, match.captures.size)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_captures_with_a_name
|
156
|
+
match = Addition.parse('1+2')
|
157
|
+
assert(match.captures(:number))
|
158
|
+
assert_equal(2, match.captures(:number).size)
|
159
|
+
assert_equal([1, 2], match.captures(:number).map(&:value))
|
160
|
+
end
|
142
161
|
end
|
metadata
CHANGED
@@ -1,30 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: citrus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
5
|
-
prerelease:
|
4
|
+
version: 3.0.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Michael Jackson
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-15 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rake
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
description: Parsing Expressions for Ruby
|
@@ -97,6 +94,7 @@ files:
|
|
97
94
|
- CHANGES
|
98
95
|
homepage: http://mjijackson.com/citrus
|
99
96
|
licenses: []
|
97
|
+
metadata: {}
|
100
98
|
post_install_message:
|
101
99
|
rdoc_options:
|
102
100
|
- --line-numbers
|
@@ -108,22 +106,20 @@ rdoc_options:
|
|
108
106
|
require_paths:
|
109
107
|
- lib
|
110
108
|
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
-
none: false
|
112
109
|
requirements:
|
113
|
-
- -
|
110
|
+
- - '>='
|
114
111
|
- !ruby/object:Gem::Version
|
115
112
|
version: '0'
|
116
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
-
none: false
|
118
114
|
requirements:
|
119
|
-
- -
|
115
|
+
- - '>='
|
120
116
|
- !ruby/object:Gem::Version
|
121
117
|
version: '0'
|
122
118
|
requirements: []
|
123
119
|
rubyforge_project:
|
124
|
-
rubygems_version:
|
120
|
+
rubygems_version: 2.0.3
|
125
121
|
signing_key:
|
126
|
-
specification_version:
|
122
|
+
specification_version: 4
|
127
123
|
summary: Parsing Expressions for Ruby
|
128
124
|
test_files:
|
129
125
|
- test/alias_test.rb
|