citrus 2.2.2 → 2.3.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.
- 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.
|