citrus 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +217 -154
- data/doc/{background.rdoc → background.markdown} +35 -32
- data/doc/example.markdown +145 -0
- data/doc/index.markdown +18 -0
- data/doc/{license.rdoc → license.markdown} +2 -1
- data/doc/links.markdown +13 -0
- data/doc/syntax.markdown +129 -0
- data/examples/calc.citrus +55 -49
- data/examples/calc.rb +55 -49
- data/examples/ip.rb +1 -1
- data/lib/citrus.rb +118 -89
- data/lib/citrus/debug.rb +1 -1
- data/lib/citrus/file.rb +75 -154
- data/test/alias_test.rb +2 -4
- data/test/and_predicate_test.rb +1 -1
- data/test/but_predicate_test.rb +36 -0
- data/test/choice_test.rb +5 -5
- data/test/expression_test.rb +1 -1
- data/test/file_test.rb +17 -15
- data/test/fixed_width_test.rb +2 -2
- data/test/grammar_test.rb +8 -8
- data/test/helper.rb +54 -6
- data/test/label_test.rb +3 -3
- data/test/match_test.rb +5 -5
- data/test/not_predicate_test.rb +1 -1
- data/test/repeat_test.rb +17 -17
- data/test/rule_test.rb +5 -9
- data/test/sequence_test.rb +3 -3
- data/test/super_test.rb +2 -2
- metadata +11 -9
- data/doc/example.rdoc +0 -115
- data/doc/index.rdoc +0 -15
- data/doc/links.rdoc +0 -18
- data/doc/syntax.rdoc +0 -96
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# Background
|
2
|
+
|
2
3
|
|
3
4
|
In order to be able to use Citrus effectively, you must first understand the
|
4
5
|
difference between syntax and semantics. Syntax is a set of rules that govern
|
@@ -9,8 +10,8 @@ sentences should end with a period.
|
|
9
10
|
Semantics are the rules by which meaning may be derived in a language. For
|
10
11
|
example, as you read a book you are able to make some sense of the particular
|
11
12
|
way in which words on a page are combined to form thoughts and express ideas
|
12
|
-
because you understand what the words themselves mean and you
|
13
|
-
|
13
|
+
because you understand what the words themselves mean and you understand what
|
14
|
+
they mean collectively.
|
14
15
|
|
15
16
|
Computers use a similar process when interpreting code. First, the code must be
|
16
17
|
parsed into recognizable symbols or tokens. These tokens may then be passed to
|
@@ -22,14 +23,15 @@ powerful parsers that are simple to understand and easy to create and maintain.
|
|
22
23
|
|
23
24
|
In Citrus, there are three main types of objects: rules, grammars, and matches.
|
24
25
|
|
25
|
-
|
26
|
+
## Rules
|
26
27
|
|
27
|
-
A Rule
|
28
|
-
two types of rules: terminals and non-terminals.
|
29
|
-
strings or regular expressions that specify some
|
30
|
-
terminal created from the string "end" would
|
31
|
-
characters "e", "n", and "d", in that order. A
|
32
|
-
expression uses Ruby's regular expression engine
|
28
|
+
A [Rule](api/classes/Citrus/Rule.html) is an object that specifies some matching
|
29
|
+
behavior on a string. There are two types of rules: terminals and non-terminals.
|
30
|
+
Terminals can be either Ruby strings or regular expressions that specify some
|
31
|
+
input to match. For example, a terminal created from the string "end" would
|
32
|
+
match any sequence of the characters "e", "n", and "d", in that order. A
|
33
|
+
terminal created from a regular expression uses Ruby's regular expression engine
|
34
|
+
to attempt to create a match.
|
33
35
|
|
34
36
|
Non-terminals are rules that may contain other rules but do not themselves match
|
35
37
|
directly on the input. For example, a Repeat is a non-terminal that may contain
|
@@ -37,34 +39,35 @@ one other rule that will try and match a certain number of times. Several other
|
|
37
39
|
types of non-terminals are available that will be discussed later.
|
38
40
|
|
39
41
|
Rule objects may also have semantic information associated with them in the form
|
40
|
-
of Ruby modules.
|
41
|
-
match objects created by the rule with which they are associated.
|
42
|
+
of Ruby modules. Rules use these modules to extend the matches they create.
|
42
43
|
|
43
|
-
|
44
|
+
## Grammars
|
44
45
|
|
45
|
-
A
|
46
|
+
A grammar is a container for rules. Usually the rules in a grammar collectively
|
46
47
|
form a complete specification for some language, or a well-defined subset
|
47
48
|
thereof.
|
48
49
|
|
49
|
-
A Citrus grammar is really just a souped-up Ruby
|
50
|
+
A Citrus grammar is really just a souped-up Ruby
|
51
|
+
[module](http://ruby-doc.org/core/classes/Module.html). These modules may be
|
50
52
|
included in other grammar modules in the same way that Ruby modules are normally
|
51
|
-
used. This property allows you to divide a complex grammar into
|
52
|
-
that may be combined
|
53
|
-
as a rule in an included grammar may access that rule with a mechanism
|
54
|
-
to Ruby's super keyword.
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
Matches are created by rule objects when they match on the input. A
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
terminal
|
53
|
+
used. This property allows you to divide a complex grammar into more manageable,
|
54
|
+
reusable pieces that may be combined at runtime. Any grammar rule with the same
|
55
|
+
name as a rule in an included grammar may access that rule with a mechanism
|
56
|
+
similar to Ruby's super keyword.
|
57
|
+
|
58
|
+
## Matches
|
59
|
+
|
60
|
+
Matches are created by rule objects when they match on the input. A
|
61
|
+
[Match](api/classes/Citrus/Match.html) in Citrus is actually a
|
62
|
+
[String](http://ruby-doc.org/core/classes/String.html) with some extra
|
63
|
+
information attached such as the name(s) of the rule(s) which generated the
|
64
|
+
match as well as its offset in the original input string.
|
65
|
+
|
66
|
+
During a parse, matches are arranged in a tree structure where any match may
|
67
|
+
contain any number of other matches. This structure is determined by the way in
|
68
|
+
which the rule that generated each match is used in the grammar. For example, a
|
69
|
+
match that is created from a non-terminal rule that contains several other
|
70
|
+
terminals will likewise contain several matches, one for each terminal.
|
68
71
|
|
69
72
|
Match objects may be extended with semantic information in the form of methods.
|
70
73
|
These methods can interpret the text of a match using the wealth of information
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# Example
|
2
|
+
|
3
|
+
|
4
|
+
Below is an example of a simple grammar that is able to parse strings of
|
5
|
+
integers separated by any amount of white space and a `+` symbol.
|
6
|
+
|
7
|
+
grammar Addition
|
8
|
+
rule additive
|
9
|
+
number plus (additive | number)
|
10
|
+
end
|
11
|
+
|
12
|
+
rule number
|
13
|
+
[0-9]+ space
|
14
|
+
end
|
15
|
+
|
16
|
+
rule plus
|
17
|
+
'+' space
|
18
|
+
end
|
19
|
+
|
20
|
+
rule space
|
21
|
+
[ \t]*
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Several things to note about the above example:
|
26
|
+
|
27
|
+
* Grammar and rule declarations end with the `end` keyword
|
28
|
+
* A Sequence of rules is created by separating expressions with a space
|
29
|
+
* Likewise, ordered choice is represented with a vertical bar
|
30
|
+
* Parentheses may be used to override the natural binding order
|
31
|
+
* Rules may refer to other rules in their own definitions simply by using the
|
32
|
+
other rule's name
|
33
|
+
* Any expression may be followed by a quantifier
|
34
|
+
|
35
|
+
## Interpretation
|
36
|
+
|
37
|
+
The grammar above is able to parse simple mathematical expressions such as "1+2"
|
38
|
+
and "1 + 2+3", but it does not have enough semantic information to be able to
|
39
|
+
actually interpret these expressions.
|
40
|
+
|
41
|
+
At this point, when the grammar parses a string it generates a tree of
|
42
|
+
[Match](api/classes/Citrus/Match.html) objects. Each match is created by a rule.
|
43
|
+
A match knows what text it contains, its offset in the original input, and what
|
44
|
+
submatches it contains.
|
45
|
+
|
46
|
+
Submatches are created whenever a rule contains another rule. For example, in
|
47
|
+
the grammar above the number rule matches a string of digits followed by white
|
48
|
+
space. Thus, a match generated by the number rule will contain two submatches.
|
49
|
+
|
50
|
+
We can use Ruby's block syntax to create a module that will be attached to these
|
51
|
+
matches when they are created and is used to lazily extend them when we want to
|
52
|
+
interpret them. The following example shows one way to do this.
|
53
|
+
|
54
|
+
grammar Addition
|
55
|
+
rule additive
|
56
|
+
(number plus term:(additive | number)) {
|
57
|
+
def value
|
58
|
+
number.value + term.value
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
rule number
|
64
|
+
([0-9]+ space) {
|
65
|
+
def value
|
66
|
+
strip.to_i
|
67
|
+
end
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
rule plus
|
72
|
+
'+' space
|
73
|
+
end
|
74
|
+
|
75
|
+
rule space
|
76
|
+
[ \t]*
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
In this version of the grammar we have added two semantic blocks, one each for
|
81
|
+
the additive and number rules. These blocks contain methods that will be present
|
82
|
+
on all match objects that result from matches of those particular rules. It's
|
83
|
+
easiest to explain what is going on here by starting with the lowest level
|
84
|
+
block, which is defined within the number rule.
|
85
|
+
|
86
|
+
The semantic block associated with the number rule defines one method, value.
|
87
|
+
Inside this method, we can see that the value of a number match is determined to
|
88
|
+
be its text value, stripped of white space and converted to an integer. Remember
|
89
|
+
that matches are simply strings, so the `strip` method in this case is actually
|
90
|
+
`String#strip`.
|
91
|
+
|
92
|
+
The `additive` rule also extends its matches with a value method. Notice the use
|
93
|
+
of the `term` label within the rule definition. This label allows the match that
|
94
|
+
is created by either the additive or the number rule to be retrieved using the
|
95
|
+
`term` label. The value of an additive is determined to be the values of its
|
96
|
+
`number` and `term` matches added together using Ruby's addition operator.
|
97
|
+
|
98
|
+
Since additive is the first rule defined in the grammar, any match that results
|
99
|
+
from parsing a string with this grammar will have a `value` method that can be
|
100
|
+
used to recursively calculate the collective value of the entire match tree.
|
101
|
+
|
102
|
+
To give it a try, save the code for the Addition grammar in a file called
|
103
|
+
addition.citrus. Next, assuming you have the Citrus gem installed, try the
|
104
|
+
following sequence of commands in a terminal.
|
105
|
+
|
106
|
+
$ irb
|
107
|
+
> require 'citrus'
|
108
|
+
=> true
|
109
|
+
> Citrus.load 'addition'
|
110
|
+
=> [Addition]
|
111
|
+
> m = Addition.parse '1 + 2 + 3'
|
112
|
+
=> #<Citrus::Match ...
|
113
|
+
> m.value
|
114
|
+
=> 6
|
115
|
+
|
116
|
+
Congratulations! You just ran your first piece of Citrus code.
|
117
|
+
|
118
|
+
Take a look at
|
119
|
+
[examples/calc.citrus](http://github.com/mjijackson/citrus/blob/master/examples/calc.citrus)
|
120
|
+
for an example of a calculator that is able to parse and evaluate more complex
|
121
|
+
mathematical expressions.
|
122
|
+
|
123
|
+
## Implicit Value
|
124
|
+
|
125
|
+
It is very common for a grammar to only have one interpretation for a given
|
126
|
+
symbol. For this reason, you may find yourself writing a `value` method for
|
127
|
+
every rule in your grammar. Because this can be tedious, Citrus allows you to
|
128
|
+
omit defining such a method if you choose. For example, the `additive` and
|
129
|
+
`number` rules from the simple calculator example above could also be written
|
130
|
+
as:
|
131
|
+
|
132
|
+
rule additive
|
133
|
+
(number plus term:(additive | number)) {
|
134
|
+
number.value + term.value
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
rule number
|
139
|
+
([0-9]+ space) {
|
140
|
+
strip.to_i
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
Since no method name is explicitly specified in the semantic blocks, they may be
|
145
|
+
called using the `value` method.
|
data/doc/index.markdown
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Citrus is a compact and powerful parsing library for
|
2
|
+
[Ruby](http://ruby-lang.org/) that combines the elegance and expressiveness of
|
3
|
+
the language with the simplicity and power of
|
4
|
+
[parsing expressions](http://en.wikipedia.org/wiki/Parsing_expression_grammar).
|
5
|
+
|
6
|
+
|
7
|
+
# Installation
|
8
|
+
|
9
|
+
|
10
|
+
Via [RubyGems](http://rubygems.org/):
|
11
|
+
|
12
|
+
$ sudo gem install citrus
|
13
|
+
|
14
|
+
From a local copy:
|
15
|
+
|
16
|
+
$ git clone git://github.com/mjijackson/citrus.git
|
17
|
+
$ cd citrus
|
18
|
+
$ rake package && sudo rake install
|
data/doc/links.markdown
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Links
|
2
|
+
|
3
|
+
|
4
|
+
The primary resource for all things to do with parsing expressions can be found
|
5
|
+
on the original [Packrat and Parsing Expression Grammars page](http://pdos.csail.mit.edu/~baford/packrat) at MIT.
|
6
|
+
|
7
|
+
Also, a useful summary of parsing expression grammars can be found on
|
8
|
+
[Wikipedia](http://en.wikipedia.org/wiki/Parsing_expression_grammar).
|
9
|
+
|
10
|
+
Citrus draws inspiration from another Ruby library for writing parsing
|
11
|
+
expression grammars, Treetop. While Citrus' syntax is similar to that of
|
12
|
+
[Treetop](http://treetop.rubyforge.org), it's not identical. The link is
|
13
|
+
included here for those who may wish toexplore an alternative implementation.
|
data/doc/syntax.markdown
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# Syntax
|
2
|
+
|
3
|
+
|
4
|
+
The most straightforward way to compose a Citrus grammar is to use Citrus' own
|
5
|
+
custom grammar syntax. This syntax borrows heavily from Ruby, so it should
|
6
|
+
already be familiar to Ruby programmers.
|
7
|
+
|
8
|
+
## Terminals
|
9
|
+
|
10
|
+
Terminals may be represented by a string or a regular expression. Both follow
|
11
|
+
the same rules as Ruby string and regular expression literals.
|
12
|
+
|
13
|
+
'abc'
|
14
|
+
"abc\n"
|
15
|
+
/\xFF/
|
16
|
+
|
17
|
+
Character classes and the dot (match anything) symbol are supported as well for
|
18
|
+
compatibility with other parsing expression implementations.
|
19
|
+
|
20
|
+
[a-z0-9] # match any lowercase letter or digit
|
21
|
+
[\x00-\xFF] # match any octet
|
22
|
+
. # match anything, even new lines
|
23
|
+
|
24
|
+
See [FixedWidth](api/classes/Citrus/FixedWidth.html) and
|
25
|
+
[Expression](api/classes/Citrus/Expression.html) for more information.
|
26
|
+
|
27
|
+
## Repetition
|
28
|
+
|
29
|
+
Quantifiers may be used after any expression to specify a number of times it
|
30
|
+
must match. The universal form of a quantifier is N*M where N is the minimum and
|
31
|
+
M is the maximum number of times the expression may match.
|
32
|
+
|
33
|
+
'abc'1*2 # match "abc" a minimum of one, maximum
|
34
|
+
# of two times
|
35
|
+
'abc'1* # match "abc" at least once
|
36
|
+
'abc'*2 # match "abc" a maximum of twice
|
37
|
+
|
38
|
+
The + and ? operators are supported as well for the common cases of 1* and *1
|
39
|
+
respectively.
|
40
|
+
|
41
|
+
'abc'+ # match "abc" at least once
|
42
|
+
'abc'? # match "abc" a maximum of once
|
43
|
+
|
44
|
+
See [Repeat](api/classes/Citrus/Repeat.html) for more information.
|
45
|
+
|
46
|
+
## Lookahead
|
47
|
+
|
48
|
+
Both positive and negative lookahead are supported in Citrus. Use the & and !
|
49
|
+
operators to indicate that an expression either should or should not match. In
|
50
|
+
neither case is any input consumed.
|
51
|
+
|
52
|
+
&'a' 'b' # match a "b" preceded by an "a"
|
53
|
+
!'a' 'b' # match a "b" that is not preceded by an "a"
|
54
|
+
!'a' . # match any character except for "a"
|
55
|
+
|
56
|
+
A special form of lookahead is also supported which will match any character
|
57
|
+
that does not match a given expression.
|
58
|
+
|
59
|
+
~'a' # match all characters until an "a"
|
60
|
+
~/xyz/ # match all characters until /xyz/ matches
|
61
|
+
|
62
|
+
See [AndPredicate](api/classes/Citrus/AndPredicate.html),
|
63
|
+
[NotPredicate](api/classes/Citrus/NotPredicate.html), and
|
64
|
+
[ButPredicate](api/classes/Citrus/ButPredicate.html) for more information.
|
65
|
+
|
66
|
+
## Sequences
|
67
|
+
|
68
|
+
Sequences of expressions may be separated by a space to indicate that the rules
|
69
|
+
should match in that order.
|
70
|
+
|
71
|
+
'a' 'b' 'c' # match "a", then "b", then "c"
|
72
|
+
'a' [0-9] # match "a", then a numeric digit
|
73
|
+
|
74
|
+
See [Sequence](api/classes/Citrus/Sequence.html) for more information.
|
75
|
+
|
76
|
+
## Choices
|
77
|
+
|
78
|
+
Ordered choice is indicated by a vertical bar that separates two expressions.
|
79
|
+
Note that any operator binds more tightly than the bar.
|
80
|
+
|
81
|
+
'a' | 'b' # match "a" or "b"
|
82
|
+
'a' 'b' | 'c' # match "a" then "b" (in sequence), or "c"
|
83
|
+
|
84
|
+
See [Choice](api/classes/Citrus/Choice.html) for more information.
|
85
|
+
|
86
|
+
## Super
|
87
|
+
|
88
|
+
When including a grammar inside another, all rules in the child that have the
|
89
|
+
same name as a rule in the parent also have access to the "super" keyword to
|
90
|
+
invoke the parent rule.
|
91
|
+
|
92
|
+
See [Super](api/classes/Citrus/Super.html) for more information.
|
93
|
+
|
94
|
+
## Labels
|
95
|
+
|
96
|
+
Match objects may be referred to by a different name than the rule that
|
97
|
+
originally generated them. Labels are created by placing the label and a colon
|
98
|
+
immediately preceding any expression.
|
99
|
+
|
100
|
+
chars:/[a-z]+/ # the characters matched by the regular
|
101
|
+
# expression may be referred to as "chars"
|
102
|
+
# in a block method
|
103
|
+
|
104
|
+
See [Label](api/classes/Citrus/Label.html) for more information.
|
105
|
+
|
106
|
+
## Precedence
|
107
|
+
|
108
|
+
The following table contains a list of all Citrus operators and their
|
109
|
+
precedence. A higher precedence indicates tighter binding.
|
110
|
+
|
111
|
+
| Operator | Name | Precedence
|
112
|
+
| ------------ | ------------------------- | ----------
|
113
|
+
| '' | String (single quoted) | 6
|
114
|
+
| "" | String (double quoted) | 6
|
115
|
+
| [] | Character class | 6
|
116
|
+
| . | Dot (any character) | 6
|
117
|
+
| // | Regular expression | 6
|
118
|
+
| () | Grouping | 6
|
119
|
+
| * | Repetition (arbitrary) | 5
|
120
|
+
| + | Repetition (one or more) | 5
|
121
|
+
| ? | Repetition (zero or one) | 5
|
122
|
+
| & | And predicate | 4
|
123
|
+
| ! | Not predicate | 4
|
124
|
+
| ~ | But predicate | 4
|
125
|
+
| : | Label | 4
|
126
|
+
| <> | Extension (module name) | 3
|
127
|
+
| {} | Extension (literal) | 3
|
128
|
+
| e1 e2 | Sequence | 2
|
129
|
+
| e1 | e2 | Ordered choice | 1
|
data/examples/calc.citrus
CHANGED
@@ -1,94 +1,100 @@
|
|
1
|
-
# A grammar for mathematical formulas that apply
|
2
|
-
#
|
3
|
-
# ignoring whitespace.
|
1
|
+
# A grammar for mathematical formulas that apply basic mathematical operations
|
2
|
+
# to all numbers, respecting operator precedence and grouping of expressions
|
3
|
+
# while ignoring whitespace.
|
4
4
|
#
|
5
5
|
# An identical grammar that is written using pure Ruby can be found in calc.rb.
|
6
6
|
grammar Calc
|
7
|
+
|
8
|
+
## Hierarchy
|
9
|
+
|
7
10
|
rule term
|
8
11
|
additive | factor
|
9
12
|
end
|
10
13
|
|
11
14
|
rule additive
|
12
|
-
(factor
|
13
|
-
|
14
|
-
operator.apply(factor, term)
|
15
|
-
end
|
15
|
+
(factor additive_operator term) {
|
16
|
+
additive_operator.value(factor.value, term.value)
|
16
17
|
}
|
17
18
|
end
|
18
19
|
|
19
20
|
rule factor
|
20
|
-
multiplicative |
|
21
|
+
multiplicative | prefix
|
21
22
|
end
|
22
23
|
|
23
24
|
rule multiplicative
|
24
|
-
(
|
25
|
-
|
26
|
-
operator.apply(primary, factor)
|
27
|
-
end
|
25
|
+
(prefix multiplicative_operator factor) {
|
26
|
+
multiplicative_operator.value(prefix.value, factor.value)
|
28
27
|
}
|
29
28
|
end
|
30
29
|
|
31
|
-
rule
|
32
|
-
|
30
|
+
rule prefix
|
31
|
+
prefixed | exponent
|
33
32
|
end
|
34
33
|
|
35
|
-
rule
|
36
|
-
(
|
37
|
-
|
38
|
-
term.value
|
39
|
-
end
|
34
|
+
rule prefixed
|
35
|
+
(unary_operator prefix) {
|
36
|
+
unary_operator.value(prefix.value)
|
40
37
|
}
|
41
38
|
end
|
42
39
|
|
40
|
+
rule exponent
|
41
|
+
exponential | primary
|
42
|
+
end
|
43
|
+
|
44
|
+
rule exponential
|
45
|
+
(primary exponential_operator prefix) {
|
46
|
+
exponential_operator.value(primary.value, prefix.value)
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
rule primary
|
51
|
+
group | number
|
52
|
+
end
|
53
|
+
|
54
|
+
rule group
|
55
|
+
(lparen term rparen) { term.value }
|
56
|
+
end
|
57
|
+
|
58
|
+
## Syntax
|
59
|
+
|
43
60
|
rule number
|
44
61
|
float | integer
|
45
62
|
end
|
46
63
|
|
47
64
|
rule float
|
48
|
-
(
|
49
|
-
def value
|
50
|
-
text.strip.to_f
|
51
|
-
end
|
52
|
-
}
|
65
|
+
(digits '.' digits space) { strip.to_f }
|
53
66
|
end
|
54
67
|
|
55
68
|
rule integer
|
56
|
-
(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
69
|
+
(digits space) { strip.to_i }
|
70
|
+
end
|
71
|
+
|
72
|
+
rule digits
|
73
|
+
[0-9]+ ('_' [0-9]+)*
|
61
74
|
end
|
62
75
|
|
63
|
-
rule
|
64
|
-
('+' space) {
|
65
|
-
|
66
|
-
factor.value + term.value
|
67
|
-
end
|
76
|
+
rule additive_operator
|
77
|
+
(('+' | '-') space) { |a, b|
|
78
|
+
a.send(strip, b)
|
68
79
|
}
|
69
80
|
end
|
70
81
|
|
71
|
-
rule
|
72
|
-
('
|
73
|
-
|
74
|
-
factor.value - term.value
|
75
|
-
end
|
82
|
+
rule multiplicative_operator
|
83
|
+
(('*' | '/' | '%') space) { |a, b|
|
84
|
+
a.send(strip, b)
|
76
85
|
}
|
77
86
|
end
|
78
87
|
|
79
|
-
rule
|
80
|
-
('
|
81
|
-
|
82
|
-
primary.value * factor.value
|
83
|
-
end
|
88
|
+
rule exponential_operator
|
89
|
+
('**' space) { |a, b|
|
90
|
+
a ** b
|
84
91
|
}
|
85
92
|
end
|
86
93
|
|
87
|
-
rule
|
88
|
-
('
|
89
|
-
|
90
|
-
|
91
|
-
end
|
94
|
+
rule unary_operator
|
95
|
+
(('~' | '+' | '-') space) { |n|
|
96
|
+
# Unary + and - require an @.
|
97
|
+
n.send(strip == '~' ? strip : '%s@' % strip)
|
92
98
|
}
|
93
99
|
end
|
94
100
|
|