= Example
Below is an example of a simple grammar that is able to parse strings of
integers separated by any amount of white space and a + symbol.
grammar Addition
rule additive
number plus (additive | number)
end
rule number
[0-9]+ space
end
rule plus
'+' space
end
rule space
[ \t]*
end
end
Several things to note about the above example:
* Grammar and rule declarations end with the end keyword
* A Sequence of rules is created by separating expressions with a space
* Likewise, ordered choice is represented with a vertical bar
* Parentheses may be used to override the natural binding order
* Rules may refer to other rules in their own definitions simply by using the
other rule's name
* Any expression may be followed by a quantifier
== Interpretation
The grammar above is able to parse simple mathematical expressions such as "1+2"
and "1 + 2+3", but it does not have enough semantic information to be able to
actually interpret these expressions.
At this point, when the grammar parses a string it generates a tree of Match[link:api/classes/Citrus/Match.html]
objects. Each match is created by a rule. A match will know what text it
contains, its offset in the original input, and what submatches it contains.
Submatches are created whenever a rule contains another rule. For example, in
the grammar above the number rule matches a string of digits followed by white
space. Thus, a match generated by the number rule will contain two submatches.
We can use Ruby's block syntax to create a module that will be attached to these
matches when they are created and is used to lazily extend them when we want to
interpret them. The following example shows one way to do this.
grammar Addition
rule additive
(number plus term:(additive | number)) {
def value
number.value + term.value
end
}
end
rule number
([0-9]+ space) {
def value
text.strip.to_i
end
}
end
rule plus
'+' space
end
rule space
[ \t]*
end
end
In this version of the grammar we have added two semantic blocks, one each for
the +additive+ and +number+ rules. These blocks contain methods that will be present
on all match objects that result from matches of those particular rules. It's
easiest to explain what is going on here by starting with the lowest level
block, which is defined within the +number+ rule.
The semantic block associated with the +number+ rule defines one method, +value+.
Inside this method, we can see that the value of a number match is determined to
be its text value, stripped of white space and converted to an integer.
The +additive+ rule also extends its matches with a +value+ method. Notice the use
of the "term" label within the rule definition. This label allows the match that
is created by either the +additive+ or the +number+ rule to be retrieved using the
"term" label. The value of an additive is determined to be the values of its
number and term matches added together using Ruby's addition operator.
Since additive is the first rule defined in the grammar, any match that results
from parsing a string with this grammar will have a value method that can be
used to recursively calculate the collective value of the entire match tree.
To give it a try, save the code for the Addition grammar in a file called
addition.citrus. Next, assuming you have the Citrus gem installed, try the
following sequence of commands in a terminal.
$ irb
> require 'citrus'
=> true
> Citrus.load 'addition'
=> [Addition]
> m = Addition.parse '1 + 2 + 3'
=> # m.value
=> 6
Congratulations! You just ran your first piece of Citrus code.
Take a look at examples/calc.citrus[http://github.com/mjijackson/citrus/blob/master/examples/calc.citrus] for an example of a calculator that is able
to parse and evaluate more complex mathematical expressions.