citrus 2.2.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +160 -98
- data/doc/background.markdown +16 -17
- data/doc/example.markdown +86 -46
- data/doc/syntax.markdown +59 -36
- data/examples/calc.citrus +9 -3
- data/examples/calc.rb +9 -3
- data/examples/ip.rb +2 -0
- data/lib/citrus.rb +576 -473
- data/lib/citrus/file.rb +80 -71
- data/test/alias_test.rb +8 -2
- data/test/and_predicate_test.rb +13 -2
- data/test/but_predicate_test.rb +9 -3
- data/test/calc_file_test.rb +9 -4
- data/test/calc_test.rb +4 -4
- data/test/choice_test.rb +11 -5
- data/test/extension_test.rb +2 -12
- data/test/file_test.rb +215 -175
- data/test/grammar_test.rb +1 -1
- data/test/helper.rb +2 -2
- data/test/input_test.rb +44 -48
- data/test/label_test.rb +14 -17
- data/test/match_test.rb +21 -63
- data/test/not_predicate_test.rb +13 -2
- data/test/repeat_test.rb +20 -20
- data/test/sequence_test.rb +22 -8
- data/test/string_terminal_test.rb +10 -5
- data/test/super_test.rb +19 -16
- data/test/terminal_test.rb +7 -2
- metadata +21 -12
- data/benchmark/after.dat +0 -192
- data/benchmark/before.dat +0 -192
- data/test/_files/grammar3.citrus +0 -112
data/README
CHANGED
@@ -70,31 +70,30 @@ of Ruby modules. Rules use these modules to extend the matches they create.
|
|
70
70
|
|
71
71
|
## Grammars
|
72
72
|
|
73
|
-
A
|
74
|
-
form a complete specification for some
|
75
|
-
thereof.
|
73
|
+
A [Grammar](api/classes/Citrus/Grammar.html) is a container for rules. Usually
|
74
|
+
the rules in a grammar collectively form a complete specification for some
|
75
|
+
language, or a well-defined subset thereof.
|
76
76
|
|
77
77
|
A Citrus grammar is really just a souped-up Ruby
|
78
78
|
[module](http://ruby-doc.org/core/classes/Module.html). These modules may be
|
79
79
|
included in other grammar modules in the same way that Ruby modules are normally
|
80
80
|
used. This property allows you to divide a complex grammar into more manageable,
|
81
|
-
reusable pieces that may be combined at runtime. Any
|
82
|
-
|
83
|
-
|
81
|
+
reusable pieces that may be combined at runtime. Any rule with the same name as
|
82
|
+
a rule in an included grammar may access that rule with a mechanism similar to
|
83
|
+
Ruby's `super` keyword.
|
84
84
|
|
85
85
|
## Matches
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
[String](http://ruby-doc.org/core/classes/String.html) object with some extra
|
90
|
-
information attached such as the name(s) of the rule(s) from which it was
|
91
|
-
generated and any submatches it may contain.
|
87
|
+
A [Match](api/classes/Citrus/Match.html) object represents a successful
|
88
|
+
recognition of some piece of the input. Matches are created by rule objects during a parse.
|
92
89
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
match
|
97
|
-
|
90
|
+
Matches are arranged in a tree structure where any match may contain any number
|
91
|
+
of other matches. Each match contains information about its own subtree. The
|
92
|
+
structure of the tree is determined by the way in which the rule that generated
|
93
|
+
each match is used in the grammar. For example, a match that is created from a
|
94
|
+
nonterminal rule that contains several other terminals will likewise contain
|
95
|
+
several matches, one for each terminal. However, this is an implementation
|
96
|
+
detail and should be relatively transparent to the user.
|
98
97
|
|
99
98
|
Match objects may be extended with semantic information in the form of methods.
|
100
99
|
These methods should provide various interpretations for the semantic value of a
|
@@ -130,6 +129,9 @@ match in a case-insensitive manner.
|
|
130
129
|
|
131
130
|
`abc` # match "abc" in any case
|
132
131
|
|
132
|
+
Besides case sensitivity, case-insensitive strings have the same behavior as
|
133
|
+
double quoted strings.
|
134
|
+
|
133
135
|
See [Terminal](api/classes/Citrus/Terminal.html) and
|
134
136
|
[StringTerminal](api/classes/Citrus/StringTerminal.html) for more information.
|
135
137
|
|
@@ -172,6 +174,9 @@ that does not match a given expression.
|
|
172
174
|
~'a' # match all characters until an "a"
|
173
175
|
~/xyz/ # match all characters until /xyz/ matches
|
174
176
|
|
177
|
+
When using this operator (the tilde), at least one character must be consumed
|
178
|
+
for the rule to succeed.
|
179
|
+
|
175
180
|
See [AndPredicate](api/classes/Citrus/AndPredicate.html),
|
176
181
|
[NotPredicate](api/classes/Citrus/NotPredicate.html), and
|
177
182
|
[ButPredicate](api/classes/Citrus/ButPredicate.html) for more information.
|
@@ -201,25 +206,25 @@ levels of precedence is below.
|
|
201
206
|
|
202
207
|
See [Choice](api/classes/Citrus/Choice.html) for more information.
|
203
208
|
|
209
|
+
## Grouping
|
210
|
+
|
211
|
+
As is common in many programming languages, parentheses may be used to override
|
212
|
+
the normal binding order of operators. In the following example parentheses are
|
213
|
+
used to make the vertical bar between `'b'` and `'c'` bind tighter than the
|
214
|
+
space between `'a'` and `'b'`.
|
215
|
+
|
216
|
+
'a' ('b' | 'c') # match "a", then "b" or "c"
|
217
|
+
|
204
218
|
## Labels
|
205
219
|
|
206
220
|
Match objects may be referred to by a different name than the rule that
|
207
|
-
originally generated them. Labels are
|
221
|
+
originally generated them. Labels are added by placing the label and a colon
|
208
222
|
immediately preceding any expression.
|
209
223
|
|
210
224
|
chars:/[a-z]+/ # the characters matched by the regular expression
|
211
225
|
# may be referred to as "chars" in an extension
|
212
226
|
# method
|
213
227
|
|
214
|
-
See [Label](api/classes/Citrus/Label.html) for more information.
|
215
|
-
|
216
|
-
## Grouping
|
217
|
-
|
218
|
-
As is common in many programming languages, parentheses may be used to override
|
219
|
-
the normal binding order of operators.
|
220
|
-
|
221
|
-
'a' ('b' | 'c') # match "a", then "b" or "c"
|
222
|
-
|
223
228
|
## Extensions
|
224
229
|
|
225
230
|
Extensions may be specified using either "module" or "block" syntax. When using
|
@@ -231,17 +236,16 @@ in between less than and greater than symbols.
|
|
231
236
|
# times and extend the match with the
|
232
237
|
# CouponCode module
|
233
238
|
|
234
|
-
Additionally, extensions may be specified inline using curly braces.
|
235
|
-
|
236
|
-
|
239
|
+
Additionally, extensions may be specified inline using curly braces. When using
|
240
|
+
this method, the code inside the curly braces may be invoked by calling the
|
241
|
+
`value` method on the match object.
|
237
242
|
|
238
|
-
# match any digit and return its integer value when
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
}
|
243
|
+
[0-9] { to_i } # match any digit and return its integer value when
|
244
|
+
# calling the #value method on the match object
|
245
|
+
|
246
|
+
Note that when using the inline block method you may also specify arguments in
|
247
|
+
between vertical bars immediately following the opening curly brace, just like
|
248
|
+
in Ruby blocks.
|
245
249
|
|
246
250
|
## Super
|
247
251
|
|
@@ -249,6 +253,24 @@ When including a grammar inside another, all rules in the child that have the
|
|
249
253
|
same name as a rule in the parent also have access to the `super` keyword to
|
250
254
|
invoke the parent rule.
|
251
255
|
|
256
|
+
grammar Number
|
257
|
+
def number
|
258
|
+
[0-9]+
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
grammar FloatingPoint
|
263
|
+
include Number
|
264
|
+
|
265
|
+
rule number
|
266
|
+
super ('.' super)?
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
In the example above, the `FloatingPoint` grammar includes `Number`. Both have a
|
271
|
+
rule named `number`, so `FloatingPoint#number` has access to `Number#number` by
|
272
|
+
means of using `super`.
|
273
|
+
|
252
274
|
See [Super](api/classes/Citrus/Super.html) for more information.
|
253
275
|
|
254
276
|
## Precedence
|
@@ -258,22 +280,22 @@ their precedence. A higher precedence indicates tighter binding.
|
|
258
280
|
|
259
281
|
Operator | Name | Precedence
|
260
282
|
--------- | ------------------------- | ----------
|
261
|
-
'' | String (single quoted) |
|
262
|
-
"" | String (double quoted) |
|
263
|
-
`` | String (case insensitive) |
|
264
|
-
[] | Character class |
|
265
|
-
. | Dot (any character) |
|
266
|
-
// | Regular expression |
|
267
|
-
() | Grouping |
|
268
|
-
* | Repetition (arbitrary) |
|
269
|
-
+ | Repetition (one or more) |
|
270
|
-
? | Repetition (zero or one) |
|
271
|
-
& | And predicate |
|
272
|
-
! | Not predicate |
|
273
|
-
~ | But predicate |
|
274
|
-
|
275
|
-
|
276
|
-
|
283
|
+
'' | String (single quoted) | 7
|
284
|
+
"" | String (double quoted) | 7
|
285
|
+
`` | String (case insensitive) | 7
|
286
|
+
[] | Character class | 7
|
287
|
+
. | Dot (any character) | 7
|
288
|
+
// | Regular expression | 7
|
289
|
+
() | Grouping | 7
|
290
|
+
* | Repetition (arbitrary) | 6
|
291
|
+
+ | Repetition (one or more) | 6
|
292
|
+
? | Repetition (zero or one) | 6
|
293
|
+
& | And predicate | 5
|
294
|
+
! | Not predicate | 5
|
295
|
+
~ | But predicate | 5
|
296
|
+
<> | Extension (module name) | 4
|
297
|
+
{} | Extension (literal) | 4
|
298
|
+
: | Label | 3
|
277
299
|
e1 e2 | Sequence | 2
|
278
300
|
e1 | e2 | Ordered choice | 1
|
279
301
|
|
@@ -288,15 +310,15 @@ integers separated by any amount of white space and a `+` symbol.
|
|
288
310
|
rule additive
|
289
311
|
number plus (additive | number)
|
290
312
|
end
|
291
|
-
|
313
|
+
|
292
314
|
rule number
|
293
315
|
[0-9]+ space
|
294
316
|
end
|
295
|
-
|
317
|
+
|
296
318
|
rule plus
|
297
319
|
'+' space
|
298
320
|
end
|
299
|
-
|
321
|
+
|
300
322
|
rule space
|
301
323
|
[ \t]*
|
302
324
|
end
|
@@ -305,7 +327,7 @@ integers separated by any amount of white space and a `+` symbol.
|
|
305
327
|
Several things to note about the above example:
|
306
328
|
|
307
329
|
* Grammar and rule declarations end with the `end` keyword
|
308
|
-
* A
|
330
|
+
* A sequence of rules is created by separating expressions with a space
|
309
331
|
* Likewise, ordered choice is represented with a vertical bar
|
310
332
|
* Parentheses may be used to override the natural binding order
|
311
333
|
* Rules may refer to other rules in their own definitions simply by using the
|
@@ -326,58 +348,54 @@ Submatches are created whenever a rule contains another rule. For example, in
|
|
326
348
|
the grammar above `number` matches a string of digits followed by white space.
|
327
349
|
Thus, a match generated by this rule will contain two submatches.
|
328
350
|
|
329
|
-
We can define
|
330
|
-
|
351
|
+
We can define a method inside a set of curly braces that will be used to extend
|
352
|
+
a particular rule's matches. This works in similar fashion to using Ruby's
|
331
353
|
blocks. Let's extend the `Addition` grammar using this technique.
|
332
354
|
|
333
355
|
grammar Addition
|
334
356
|
rule additive
|
335
357
|
(number plus term:(additive | number)) {
|
336
|
-
|
337
|
-
number.value + term.value
|
338
|
-
end
|
358
|
+
number.value + term.value
|
339
359
|
}
|
340
360
|
end
|
341
|
-
|
361
|
+
|
342
362
|
rule number
|
343
363
|
([0-9]+ space) {
|
344
|
-
|
345
|
-
strip.to_i
|
346
|
-
end
|
364
|
+
to_i
|
347
365
|
}
|
348
366
|
end
|
349
|
-
|
367
|
+
|
350
368
|
rule plus
|
351
369
|
'+' space
|
352
370
|
end
|
353
|
-
|
371
|
+
|
354
372
|
rule space
|
355
373
|
[ \t]*
|
356
374
|
end
|
357
375
|
end
|
358
376
|
|
359
377
|
In this version of the grammar we have added two semantic blocks, one each for
|
360
|
-
the additive and number rules. These blocks contain
|
361
|
-
on
|
378
|
+
the `additive` and `number` rules. These blocks contain code that we can
|
379
|
+
execute by calling `value` on match objects that result from those rules. It's
|
362
380
|
easiest to explain what is going on here by starting with the lowest level
|
363
|
-
block, which is defined within
|
381
|
+
block, which is defined within `number`.
|
364
382
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
method in this case is actually
|
370
|
-
[String#strip](http://ruby-doc.org/core/classes/String.html#M000820).
|
383
|
+
Inside this block we see a call to another method, namely `to_i`. When called in
|
384
|
+
the context of a match object, methods that are not defined may be called on a
|
385
|
+
match's internal string object via `method_missing`. Thus, the call to `to_i`
|
386
|
+
should return the integer value of the match.
|
371
387
|
|
372
|
-
|
373
|
-
use of the `term` label within the rule definition. This label allows the
|
374
|
-
that is created by
|
375
|
-
the `term`
|
376
|
-
`number` and `term` matches added together using Ruby's
|
388
|
+
Similarly, matches created by `additive` will also have a `value` method. Notice
|
389
|
+
the use of the `term` label within the rule definition. This label allows the
|
390
|
+
match that is created by the choice between `additive` and `number` to be
|
391
|
+
retrieved using the `term` method. The value of an additive match is determined
|
392
|
+
to be the values of its `number` and `term` matches added together using Ruby's
|
393
|
+
addition operator.
|
377
394
|
|
378
|
-
Since additive is the first rule defined in the grammar, any match that
|
379
|
-
from parsing a string with this grammar will have a `value` method that
|
380
|
-
used to recursively calculate the collective value of the entire match
|
395
|
+
Since `additive` is the first rule defined in the grammar, any match that
|
396
|
+
results from parsing a string with this grammar will have a `value` method that
|
397
|
+
can be used to recursively calculate the collective value of the entire match
|
398
|
+
tree.
|
381
399
|
|
382
400
|
To give it a try, save the code for the `Addition` grammar in a file called
|
383
401
|
addition.citrus. Next, assuming you have the Citrus
|
@@ -408,29 +426,73 @@ Take a look at
|
|
408
426
|
for an example of a calculator that is able to parse and evaluate more complex
|
409
427
|
mathematical expressions.
|
410
428
|
|
411
|
-
##
|
429
|
+
## Additional Methods
|
412
430
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
`
|
418
|
-
|
431
|
+
If you need more than just a `value` method on your match object, you can attach
|
432
|
+
additional methods as well. There are two ways to do this. The first lets you
|
433
|
+
define additional methods inline in your semantic block. This block will be used
|
434
|
+
to create a new Module using [Module#new](http://ruby-doc.org/core/classes/Module.html#M001682). Using the
|
435
|
+
`Addition` example above, we might refactor the `additive` rule to look like
|
436
|
+
this:
|
419
437
|
|
420
438
|
rule additive
|
421
439
|
(number plus term:(additive | number)) {
|
422
|
-
|
440
|
+
def lhs
|
441
|
+
number.value
|
442
|
+
end
|
443
|
+
|
444
|
+
def rhs
|
445
|
+
term.value
|
446
|
+
end
|
447
|
+
|
448
|
+
def value
|
449
|
+
lhs + rhs
|
450
|
+
end
|
423
451
|
}
|
424
452
|
end
|
425
453
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
454
|
+
Now, in addition to having a `value` method, matches that result from the
|
455
|
+
`additive` rule will have a `lhs` and a `rhs` method as well. Although not
|
456
|
+
particularly useful in this example, this technique can be useful when unit
|
457
|
+
testing more complex rules. For example, using this method you might make the
|
458
|
+
following assertions in a unit test:
|
459
|
+
|
460
|
+
match = Addition.parse('1 + 4')
|
461
|
+
assert_equal(1, match.lhs)
|
462
|
+
assert_equal(4, match.rhs)
|
463
|
+
assert_equal(5, match.value)
|
464
|
+
|
465
|
+
If you would like to abstract away the code in a semantic block, simply create
|
466
|
+
a separate Ruby module (in another file) that contains the extension methods you
|
467
|
+
want and use the angle bracket notation to indicate that a rule should use that
|
468
|
+
module when extending matches.
|
469
|
+
|
470
|
+
To demonstrate this method with the above example, in a Ruby file you would
|
471
|
+
define the following module.
|
472
|
+
|
473
|
+
module Additive
|
474
|
+
def lhs
|
475
|
+
number.value
|
476
|
+
end
|
477
|
+
|
478
|
+
def rhs
|
479
|
+
term.value
|
480
|
+
end
|
481
|
+
|
482
|
+
def value
|
483
|
+
lhs + rhs
|
484
|
+
end
|
430
485
|
end
|
431
486
|
|
432
|
-
|
433
|
-
|
487
|
+
Then, in your Citrus grammar file the rule definition would look like this:
|
488
|
+
|
489
|
+
rule additive
|
490
|
+
(number plus term:(additive | number)) <Additive>
|
491
|
+
end
|
492
|
+
|
493
|
+
This method of defining extensions can help keep your grammar files cleaner.
|
494
|
+
However, you do need to make sure that your extension modules are already loaded
|
495
|
+
before using `Citrus.load` to load your grammar file.
|
434
496
|
|
435
497
|
|
436
498
|
# Testing
|
data/doc/background.markdown
CHANGED
@@ -43,31 +43,30 @@ of Ruby modules. Rules use these modules to extend the matches they create.
|
|
43
43
|
|
44
44
|
## Grammars
|
45
45
|
|
46
|
-
A
|
47
|
-
form a complete specification for some
|
48
|
-
thereof.
|
46
|
+
A [Grammar](api/classes/Citrus/Grammar.html) is a container for rules. Usually
|
47
|
+
the rules in a grammar collectively form a complete specification for some
|
48
|
+
language, or a well-defined subset thereof.
|
49
49
|
|
50
50
|
A Citrus grammar is really just a souped-up Ruby
|
51
51
|
[module](http://ruby-doc.org/core/classes/Module.html). These modules may be
|
52
52
|
included in other grammar modules in the same way that Ruby modules are normally
|
53
53
|
used. This property allows you to divide a complex grammar into more manageable,
|
54
|
-
reusable pieces that may be combined at runtime. Any
|
55
|
-
|
56
|
-
|
54
|
+
reusable pieces that may be combined at runtime. Any rule with the same name as
|
55
|
+
a rule in an included grammar may access that rule with a mechanism similar to
|
56
|
+
Ruby's `super` keyword.
|
57
57
|
|
58
58
|
## Matches
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
terminals will likewise contain several matches, one for each terminal.
|
60
|
+
A [Match](api/classes/Citrus/Match.html) object represents a successful
|
61
|
+
recognition of some piece of the input. Matches are created by rule objects during a parse.
|
62
|
+
|
63
|
+
Matches are arranged in a tree structure where any match may contain any number
|
64
|
+
of other matches. Each match contains information about its own subtree. The
|
65
|
+
structure of the tree is determined by the way in which the rule that generated
|
66
|
+
each match is used in the grammar. For example, a match that is created from a
|
67
|
+
nonterminal rule that contains several other terminals will likewise contain
|
68
|
+
several matches, one for each terminal. However, this is an implementation
|
69
|
+
detail and should be relatively transparent to the user.
|
71
70
|
|
72
71
|
Match objects may be extended with semantic information in the form of methods.
|
73
72
|
These methods should provide various interpretations for the semantic value of a
|
data/doc/example.markdown
CHANGED
@@ -8,15 +8,15 @@ integers separated by any amount of white space and a `+` symbol.
|
|
8
8
|
rule additive
|
9
9
|
number plus (additive | number)
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
rule number
|
13
13
|
[0-9]+ space
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
rule plus
|
17
17
|
'+' space
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
rule space
|
21
21
|
[ \t]*
|
22
22
|
end
|
@@ -25,7 +25,7 @@ integers separated by any amount of white space and a `+` symbol.
|
|
25
25
|
Several things to note about the above example:
|
26
26
|
|
27
27
|
* Grammar and rule declarations end with the `end` keyword
|
28
|
-
* A
|
28
|
+
* A sequence of rules is created by separating expressions with a space
|
29
29
|
* Likewise, ordered choice is represented with a vertical bar
|
30
30
|
* Parentheses may be used to override the natural binding order
|
31
31
|
* Rules may refer to other rules in their own definitions simply by using the
|
@@ -46,58 +46,54 @@ Submatches are created whenever a rule contains another rule. For example, in
|
|
46
46
|
the grammar above `number` matches a string of digits followed by white space.
|
47
47
|
Thus, a match generated by this rule will contain two submatches.
|
48
48
|
|
49
|
-
We can define
|
50
|
-
|
49
|
+
We can define a method inside a set of curly braces that will be used to extend
|
50
|
+
a particular rule's matches. This works in similar fashion to using Ruby's
|
51
51
|
blocks. Let's extend the `Addition` grammar using this technique.
|
52
52
|
|
53
53
|
grammar Addition
|
54
54
|
rule additive
|
55
55
|
(number plus term:(additive | number)) {
|
56
|
-
|
57
|
-
number.value + term.value
|
58
|
-
end
|
56
|
+
number.value + term.value
|
59
57
|
}
|
60
58
|
end
|
61
|
-
|
59
|
+
|
62
60
|
rule number
|
63
61
|
([0-9]+ space) {
|
64
|
-
|
65
|
-
strip.to_i
|
66
|
-
end
|
62
|
+
to_i
|
67
63
|
}
|
68
64
|
end
|
69
|
-
|
65
|
+
|
70
66
|
rule plus
|
71
67
|
'+' space
|
72
68
|
end
|
73
|
-
|
69
|
+
|
74
70
|
rule space
|
75
71
|
[ \t]*
|
76
72
|
end
|
77
73
|
end
|
78
74
|
|
79
75
|
In this version of the grammar we have added two semantic blocks, one each for
|
80
|
-
the additive and number rules. These blocks contain
|
81
|
-
on
|
76
|
+
the `additive` and `number` rules. These blocks contain code that we can
|
77
|
+
execute by calling `value` on match objects that result from those rules. It's
|
82
78
|
easiest to explain what is going on here by starting with the lowest level
|
83
|
-
block, which is defined within
|
79
|
+
block, which is defined within `number`.
|
84
80
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
method in this case is actually
|
90
|
-
[String#strip](http://ruby-doc.org/core/classes/String.html#M000820).
|
81
|
+
Inside this block we see a call to another method, namely `to_i`. When called in
|
82
|
+
the context of a match object, methods that are not defined may be called on a
|
83
|
+
match's internal string object via `method_missing`. Thus, the call to `to_i`
|
84
|
+
should return the integer value of the match.
|
91
85
|
|
92
|
-
|
93
|
-
use of the `term` label within the rule definition. This label allows the
|
94
|
-
that is created by
|
95
|
-
the `term`
|
96
|
-
`number` and `term` matches added together using Ruby's
|
86
|
+
Similarly, matches created by `additive` will also have a `value` method. Notice
|
87
|
+
the use of the `term` label within the rule definition. This label allows the
|
88
|
+
match that is created by the choice between `additive` and `number` to be
|
89
|
+
retrieved using the `term` method. The value of an additive match is determined
|
90
|
+
to be the values of its `number` and `term` matches added together using Ruby's
|
91
|
+
addition operator.
|
97
92
|
|
98
|
-
Since additive is the first rule defined in the grammar, any match that
|
99
|
-
from parsing a string with this grammar will have a `value` method that
|
100
|
-
used to recursively calculate the collective value of the entire match
|
93
|
+
Since `additive` is the first rule defined in the grammar, any match that
|
94
|
+
results from parsing a string with this grammar will have a `value` method that
|
95
|
+
can be used to recursively calculate the collective value of the entire match
|
96
|
+
tree.
|
101
97
|
|
102
98
|
To give it a try, save the code for the `Addition` grammar in a file called
|
103
99
|
addition.citrus. Next, assuming you have the Citrus
|
@@ -128,26 +124,70 @@ Take a look at
|
|
128
124
|
for an example of a calculator that is able to parse and evaluate more complex
|
129
125
|
mathematical expressions.
|
130
126
|
|
131
|
-
##
|
127
|
+
## Additional Methods
|
132
128
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
`
|
138
|
-
|
129
|
+
If you need more than just a `value` method on your match object, you can attach
|
130
|
+
additional methods as well. There are two ways to do this. The first lets you
|
131
|
+
define additional methods inline in your semantic block. This block will be used
|
132
|
+
to create a new Module using [Module#new](http://ruby-doc.org/core/classes/Module.html#M001682). Using the
|
133
|
+
`Addition` example above, we might refactor the `additive` rule to look like
|
134
|
+
this:
|
139
135
|
|
140
136
|
rule additive
|
141
137
|
(number plus term:(additive | number)) {
|
142
|
-
|
138
|
+
def lhs
|
139
|
+
number.value
|
140
|
+
end
|
141
|
+
|
142
|
+
def rhs
|
143
|
+
term.value
|
144
|
+
end
|
145
|
+
|
146
|
+
def value
|
147
|
+
lhs + rhs
|
148
|
+
end
|
143
149
|
}
|
144
150
|
end
|
145
151
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
152
|
+
Now, in addition to having a `value` method, matches that result from the
|
153
|
+
`additive` rule will have a `lhs` and a `rhs` method as well. Although not
|
154
|
+
particularly useful in this example, this technique can be useful when unit
|
155
|
+
testing more complex rules. For example, using this method you might make the
|
156
|
+
following assertions in a unit test:
|
157
|
+
|
158
|
+
match = Addition.parse('1 + 4')
|
159
|
+
assert_equal(1, match.lhs)
|
160
|
+
assert_equal(4, match.rhs)
|
161
|
+
assert_equal(5, match.value)
|
162
|
+
|
163
|
+
If you would like to abstract away the code in a semantic block, simply create
|
164
|
+
a separate Ruby module (in another file) that contains the extension methods you
|
165
|
+
want and use the angle bracket notation to indicate that a rule should use that
|
166
|
+
module when extending matches.
|
167
|
+
|
168
|
+
To demonstrate this method with the above example, in a Ruby file you would
|
169
|
+
define the following module.
|
170
|
+
|
171
|
+
module Additive
|
172
|
+
def lhs
|
173
|
+
number.value
|
174
|
+
end
|
175
|
+
|
176
|
+
def rhs
|
177
|
+
term.value
|
178
|
+
end
|
179
|
+
|
180
|
+
def value
|
181
|
+
lhs + rhs
|
182
|
+
end
|
150
183
|
end
|
151
184
|
|
152
|
-
|
153
|
-
|
185
|
+
Then, in your Citrus grammar file the rule definition would look like this:
|
186
|
+
|
187
|
+
rule additive
|
188
|
+
(number plus term:(additive | number)) <Additive>
|
189
|
+
end
|
190
|
+
|
191
|
+
This method of defining extensions can help keep your grammar files cleaner.
|
192
|
+
However, you do need to make sure that your extension modules are already loaded
|
193
|
+
before using `Citrus.load` to load your grammar file.
|