citrus 1.7.0 → 1.8.0

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.
@@ -13,7 +13,7 @@ module Citrus
13
13
  xml.instruct!
14
14
  end
15
15
 
16
- attrs = { "names" => names.join(','), "text" => text, "offset" => offset }
16
+ attrs = { "names" => names.join(','), "text" => to_s, "offset" => offset }
17
17
 
18
18
  if matches.empty?
19
19
  xml.match(attrs)
@@ -10,31 +10,25 @@ module Citrus
10
10
 
11
11
  rule :file do
12
12
  all(:space, zero_or_more(any(:require, :grammar))) {
13
- def value
14
- find(:require).each {|r| require r.value }
15
- find(:grammar).map {|g| g.value }
16
- end
13
+ find(:require).each { |r| require r.value }
14
+ find(:grammar).map { |g| g.value }
17
15
  }
18
16
  end
19
17
 
20
18
  rule :grammar do
21
19
  all(:grammar_keyword, :module_name, :grammar_body, :end_keyword) {
22
- def modules
23
- find(:include).map {|inc| eval(inc.value, TOPLEVEL_BINDING) }
24
- end
25
-
26
- def root
27
- find(:root).last
28
- end
29
-
30
- def value
31
- code = '%s = Citrus::Grammar.new' % module_name.value
32
- grammar = eval(code, TOPLEVEL_BINDING)
33
- modules.each {|mod| grammar.include(mod) }
34
- grammar.root(root.value) if root
35
- find(:rule).each {|r| grammar.rule(r.rule_name.value, r.value) }
36
- grammar
37
- end
20
+ code = '%s = Citrus::Grammar.new' % module_name.value
21
+ grammar = eval(code, TOPLEVEL_BINDING)
22
+
23
+ modules = find(:include).map { |inc| eval(inc.value, TOPLEVEL_BINDING) }
24
+ modules.each { |mod| grammar.include(mod) }
25
+
26
+ root = find(:root).last
27
+ grammar.root(root.value) if root
28
+
29
+ find(:rule).each { |r| grammar.rule(r.rule_name.value, r.value) }
30
+
31
+ grammar
38
32
  }
39
33
  end
40
34
 
@@ -44,214 +38,157 @@ module Citrus
44
38
 
45
39
  rule :rule do
46
40
  all(:rule_keyword, :rule_name, :rule_body, :end_keyword) {
47
- def value
48
- rule_body.value
49
- end
41
+ rule_body.value
50
42
  }
51
43
  end
52
44
 
53
45
  rule :rule_body do
54
46
  all(:sequence, :choice) {
55
- def choices
56
- @choices ||= [ sequence ] + choice.sequences
57
- end
58
-
59
- def values
60
- choices.map {|c| c.value }
61
- end
62
-
63
- def value
64
- choices.length > 1 ? Choice.new(values) : values[0]
65
- end
47
+ @choices ||= [ sequence ] + choice.value
48
+ values = @choices.map { |c| c.value }
49
+ values.length > 1 ? Choice.new(values) : values[0]
66
50
  }
67
51
  end
68
52
 
69
53
  rule :choice do
70
54
  zero_or_more([ :bar, :sequence ]) {
71
- def sequences
72
- matches.map {|m| m.matches[1] }
73
- end
55
+ matches.map { |m| m.matches[1] }
74
56
  }
75
57
  end
76
58
 
77
59
  rule :sequence do
78
60
  zero_or_more(:appendix) {
79
- def values
80
- matches.map {|m| m.value }
81
- end
82
-
83
- def value
84
- matches.length > 1 ? Sequence.new(values) : values[0]
85
- end
61
+ values = matches.map { |m| m.value }
62
+ values.length > 1 ? Sequence.new(values) : values[0]
86
63
  }
87
64
  end
88
65
 
89
66
  rule :appendix do
90
67
  all(:prefix, zero_or_one(:extension)) {
91
- def value
92
- rule = prefix.value
93
- extension = matches[1].first
94
- rule.extension = extension.value if extension
95
- rule
96
- end
68
+ rule = prefix.value
69
+ extension = matches[1].first
70
+ rule.extension = extension.value if extension
71
+ rule
97
72
  }
98
73
  end
99
74
 
100
75
  rule :prefix do
101
76
  all(zero_or_one(:predicate), :suffix) {
102
- def value
103
- rule = suffix.value
104
- predicate = matches[0].first
105
- rule = predicate.wrap(rule) if predicate
106
- rule
107
- end
77
+ rule = suffix.value
78
+ predicate = matches[0].first
79
+ rule = predicate.value(rule) if predicate
80
+ rule
108
81
  }
109
82
  end
110
83
 
111
84
  rule :suffix do
112
85
  all(:primary, zero_or_one(:repeat)) {
113
- def value
114
- rule = primary.value
115
- repeat = matches[1].first
116
- rule = repeat.wrap(rule) if repeat
117
- rule
118
- end
86
+ rule = primary.value
87
+ repeat = matches[1].first
88
+ rule = repeat.value(rule) if repeat
89
+ rule
119
90
  }
120
91
  end
121
92
 
122
93
  rule :primary do
123
- any(:super, :alias, :grouping, :terminal)
94
+ any(:grouping, :proxy, :terminal)
124
95
  end
125
96
 
126
97
  rule :grouping do
127
- all(:lparen, :rule_body, :rparen) {
128
- def value
129
- rule_body.value
130
- end
131
- }
98
+ all(:lparen, :rule_body, :rparen) { rule_body.value }
132
99
  end
133
100
 
134
101
  ## Lexical syntax
135
102
 
136
103
  rule :require do
137
- all(:require_keyword, :quoted_string) {
138
- def value
139
- quoted_string.value
140
- end
141
- }
104
+ all(:require_keyword, :quoted_string) { quoted_string.value }
142
105
  end
143
106
 
144
107
  rule :include do
145
- all(:include_keyword, :module_name) {
146
- def value
147
- module_name.value
148
- end
149
- }
108
+ all(:include_keyword, :module_name) { module_name.value }
150
109
  end
151
110
 
152
111
  rule :root do
153
- all(:root_keyword, :rule_name) {
154
- def value
155
- rule_name.value
156
- end
157
- }
112
+ all(:root_keyword, :rule_name) { rule_name.value }
158
113
  end
159
114
 
160
115
  # Rule names may contain letters, numbers, underscores, and dashes. They
161
116
  # MUST start with a letter.
162
117
  rule :rule_name do
163
- all(/[a-zA-Z][a-zA-Z0-9_-]*/, :space) {
164
- def value
165
- first.text
166
- end
167
- }
118
+ all(/[a-zA-Z][a-zA-Z0-9_-]*/, :space) { first.to_s }
119
+ end
120
+
121
+ rule :proxy do
122
+ any(:super, :alias)
168
123
  end
169
124
 
170
125
  rule :super do
171
126
  all('super', :space) {
172
- def value
173
- Super.new
174
- end
127
+ Super.new
175
128
  }
176
129
  end
177
130
 
178
131
  rule :alias do
179
132
  all(notp(:end_keyword), :rule_name) {
180
- def value
181
- Alias.new(rule_name.value)
182
- end
133
+ Alias.new(rule_name.value)
183
134
  }
184
135
  end
185
136
 
186
137
  rule :terminal do
187
- any(:quoted_string, :character_class, :anything_symbol, :regular_expression) {
188
- def value
189
- Rule.new(super)
190
- end
138
+ any(:quoted_string, :character_class, :dot, :regular_expression) {
139
+ Rule.new(super())
191
140
  }
192
141
  end
193
142
 
194
143
  rule :quoted_string do
195
144
  all(/(["'])(?:\\?.)*?\1/, :space) {
196
- def value
197
- eval(first.text)
198
- end
145
+ eval(first.to_s)
199
146
  }
200
147
  end
201
148
 
202
149
  rule :character_class do
203
150
  all(/\[(?:\\?.)*?\]/, :space) {
204
- def value
205
- Regexp.new('\A' + first.text, nil, 'n')
206
- end
151
+ Regexp.new('\A' + first.to_s, nil, 'n')
207
152
  }
208
153
  end
209
154
 
210
- rule :anything_symbol do
155
+ rule :dot do
211
156
  all('.', :space) {
212
- def value
213
- /./m # Match newlines
214
- end
157
+ DOT
215
158
  }
216
159
  end
217
160
 
218
161
  rule :regular_expression do
219
162
  all(/\/(?:\\?.)*?\/[imxouesn]*/, :space) {
220
- def value
221
- eval(first.text)
222
- end
163
+ eval(first.to_s)
223
164
  }
224
165
  end
225
166
 
226
167
  rule :predicate do
227
- any(:and, :not, :label)
168
+ any(:and, :not, :but, :label)
228
169
  end
229
170
 
230
171
  rule :and do
231
- all('&', :space) {
232
- def wrap(rule)
233
- AndPredicate.new(rule)
234
- end
172
+ all('&', :space) { |rule|
173
+ AndPredicate.new(rule)
235
174
  }
236
175
  end
237
176
 
238
177
  rule :not do
239
- all('!', :space) {
240
- def wrap(rule)
241
- NotPredicate.new(rule)
242
- end
178
+ all('!', :space) { |rule|
179
+ NotPredicate.new(rule)
180
+ }
181
+ end
182
+
183
+ rule :but do
184
+ all('~', :space) { |rule|
185
+ ButPredicate.new(rule)
243
186
  }
244
187
  end
245
188
 
246
189
  rule :label do
247
- all(/[a-zA-Z0-9_]+/, :space, ':', :space) {
248
- def wrap(rule)
249
- Label.new(value, rule)
250
- end
251
-
252
- def value
253
- first.text
254
- end
190
+ all(/[a-zA-Z0-9_]+/, :space, ':', :space) { |rule|
191
+ Label.new(rule, first.to_s)
255
192
  }
256
193
  end
257
194
 
@@ -261,25 +198,19 @@ module Citrus
261
198
 
262
199
  rule :tag do
263
200
  all(:lt, :module_name, :gt) {
264
- def value
265
- eval(module_name.value, TOPLEVEL_BINDING)
266
- end
201
+ eval(module_name.value, TOPLEVEL_BINDING)
267
202
  }
268
203
  end
269
204
 
270
205
  rule :block do
271
- all(:lcurly, zero_or_more(any(:block, /[^{}]+/)), :rcurly) {
272
- def value
273
- eval('Proc.new ' + text)
274
- end
206
+ all(:lcurly, zero_or_more(any(:block, /[^}]+/)), :rcurly) {
207
+ eval('Proc.new ' + to_s)
275
208
  }
276
209
  end
277
210
 
278
211
  rule :repeat do
279
- any(:question, :plus, :star_quantity) {
280
- def wrap(rule)
281
- Repeat.new(min, max, rule)
282
- end
212
+ any(:question, :plus, :star) { |rule|
213
+ Repeat.new(rule, min, max)
283
214
  }
284
215
  end
285
216
 
@@ -297,30 +228,19 @@ module Citrus
297
228
  }
298
229
  end
299
230
 
300
- rule :star_quantity do
231
+ rule :star do
301
232
  all(/[0-9]*/, '*', /[0-9]*/, :space) {
302
- def min
303
- matches[0] == '' ? 0 : matches[0].text.to_i
304
- end
305
-
306
- def max
307
- matches[2] == '' ? Infinity : matches[2].text.to_i
308
- end
233
+ def min; matches[0] == '' ? 0 : matches[0].to_i end
234
+ def max; matches[2] == '' ? Infinity : matches[2].to_i end
309
235
  }
310
236
  end
311
237
 
312
238
  rule :module_name do
313
239
  all(one_or_more([ zero_or_one('::'), :constant ]), :space) {
314
- def value
315
- first.text
316
- end
240
+ first.to_s
317
241
  }
318
242
  end
319
243
 
320
- rule :constant do
321
- /[A-Z][a-zA-Z0-9_]*/
322
- end
323
-
324
244
  rule :require_keyword, [ 'require', :space ]
325
245
  rule :include_keyword, [ 'include', :space ]
326
246
  rule :grammar_keyword, [ 'grammar', :space ]
@@ -335,6 +255,7 @@ module Citrus
335
255
  rule :lt, [ '<', :space ]
336
256
  rule :gt, [ '>', :space ]
337
257
 
258
+ rule :constant, /[A-Z][a-zA-Z0-9_]*/
338
259
  rule :white, /[ \t\n\r]/
339
260
  rule :comment, /#.*/
340
261
  rule :space, zero_or_more(any(:white, :comment))
@@ -16,16 +16,14 @@ class AliasTest < Test::Unit::TestCase
16
16
 
17
17
  match = grammar.parse('b')
18
18
  assert(match)
19
- assert_equal('b', match.text)
19
+ assert_equal('b', match)
20
20
  assert_equal(1, match.length)
21
21
  end
22
22
 
23
23
  def test_match_renamed
24
24
  grammar = Grammar.new {
25
25
  rule :a, ext(:b) {
26
- def value
27
- 'a' + text
28
- end
26
+ 'a' + to_s
29
27
  }
30
28
  rule :b, 'b'
31
29
  }
@@ -15,7 +15,7 @@ class AndPredicateTest < Test::Unit::TestCase
15
15
 
16
16
  match = rule.match(input('a'))
17
17
  assert(match)
18
- assert_equal('', match.text)
18
+ assert_equal('', match)
19
19
  assert_equal(0, match.length)
20
20
  end
21
21
 
@@ -0,0 +1,36 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class ButPredicateTest < Test::Unit::TestCase
4
+
5
+ def test_terminal?
6
+ rule = ButPredicate.new
7
+ assert_equal(false, rule.terminal?)
8
+ end
9
+
10
+ def test_match
11
+ rule = ButPredicate.new('a')
12
+
13
+ match = rule.match(input('b'))
14
+ assert(match)
15
+ assert_equal('b', match)
16
+ assert_equal(1, match.length)
17
+
18
+ match = rule.match(input('bbba'))
19
+ assert(match)
20
+ assert_equal('bbb', match)
21
+ assert_equal(3, match.length)
22
+
23
+ match = rule.match(input('a'))
24
+ assert_equal(nil, match)
25
+
26
+ # ButPredicate must match at least one character.
27
+ match = rule.match(input(''))
28
+ assert_equal(nil, match)
29
+ end
30
+
31
+ def test_to_s
32
+ rule = ButPredicate.new('a')
33
+ assert_equal('~"a"', rule.to_s)
34
+ end
35
+
36
+ end