citrus 2.3.2 → 2.3.3

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 CHANGED
@@ -206,15 +206,6 @@ levels of precedence is below.
206
206
 
207
207
  See [Choice](api/classes/Citrus/Choice.html) for more information.
208
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
-
218
209
  ## Labels
219
210
 
220
211
  Match objects may be referred to by a different name than the rule that
@@ -299,6 +290,15 @@ Operator | Name | Precedence
299
290
  e1 e2 | Sequence | 2
300
291
  e1 | e2 | Ordered choice | 1
301
292
 
293
+ ## Grouping
294
+
295
+ As is common in many programming languages, parentheses may be used to override
296
+ the normal binding order of operators. In the following example parentheses are
297
+ used to make the vertical bar between `'b'` and `'c'` bind tighter than the
298
+ space between `'a'` and `'b'`.
299
+
300
+ 'a' ('b' | 'c') # match "a", then "b" or "c"
301
+
302
302
 
303
303
  # Example
304
304
 
@@ -510,7 +510,7 @@ case that could be used to test that our grammar works properly.
510
510
  assert_equal('23 + 12', match)
511
511
  assert_equal(35, match.value)
512
512
  end
513
-
513
+
514
514
  def test_number
515
515
  match = Addition.parse('23', :root => :number)
516
516
  assert(match)
@@ -567,9 +567,29 @@ To install the [Vim](http://www.vim.org/) scripts, copy the files in
567
567
  [runtimepath](http://vimdoc.sourceforge.net/htmldoc/options.html#\'runtimepath\').
568
568
 
569
569
 
570
+ # Examples
571
+
572
+
573
+ The project source directory contains several example scripts that demonstrate
574
+ how grammars are to be constructed and used. Each Citrus file in the examples
575
+ directory has an accompanying Ruby file with the same name that contains a suite
576
+ of tests for that particular file.
577
+
578
+ The best way to run any of these examples is to pass the name of the Ruby file
579
+ directly to the Ruby interpreter on the command line, e.g.:
580
+
581
+ $ ruby -Ilib examples/calc.rb
582
+
583
+ This particular invocation uses the `-I` flag to ensure that you are using the
584
+ version of Citrus that was bundled with that particular example file (i.e. the
585
+ version that is contained in the `lib` directory).
586
+
587
+
570
588
  # Links
571
589
 
572
590
 
591
+ Discussion around Citrus happens on the [citrus-users Google group](http://groups.google.com/group/citrus-users).
592
+
573
593
  The primary resource for all things to do with parsing expressions can be found
574
594
  on the original [Packrat and Parsing Expression Grammars page](http://pdos.csail.mit.edu/~baford/packrat)
575
595
  at MIT.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ task :default => :test
6
6
  # TESTS #######################################################################
7
7
 
8
8
  Rake::TestTask.new(:test) do |t|
9
- t.test_files = FileList['test/*_test.rb']
9
+ t.test_files = FileList['test/*_test.rb'] + FileList['examples/*.rb']
10
10
  end
11
11
 
12
12
  # DOCS ########################################################################
@@ -1,6 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
2
2
 
3
- require 'citrus'
3
+ require 'citrus/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'citrus'
@@ -104,15 +104,6 @@ levels of precedence is below.
104
104
 
105
105
  See [Choice](api/classes/Citrus/Choice.html) for more information.
106
106
 
107
- ## Grouping
108
-
109
- As is common in many programming languages, parentheses may be used to override
110
- the normal binding order of operators. In the following example parentheses are
111
- used to make the vertical bar between `'b'` and `'c'` bind tighter than the
112
- space between `'a'` and `'b'`.
113
-
114
- 'a' ('b' | 'c') # match "a", then "b" or "c"
115
-
116
107
  ## Labels
117
108
 
118
109
  Match objects may be referred to by a different name than the rule that
@@ -196,3 +187,12 @@ Operator | Name | Precedence
196
187
  `:` | Label | 3
197
188
  `e1 e2` | Sequence | 2
198
189
  <code>e1 &#124; e2</code> | Ordered choice | 1
190
+
191
+ ## Grouping
192
+
193
+ As is common in many programming languages, parentheses may be used to override
194
+ the normal binding order of operators. In the following example parentheses are
195
+ used to make the vertical bar between `'b'` and `'c'` bind tighter than the
196
+ space between `'a'` and `'b'`.
197
+
198
+ 'a' ('b' | 'c') # match "a", then "b" or "c"
@@ -1,8 +1,7 @@
1
1
  # A grammar for mathematical formulas that apply basic mathematical operations
2
2
  # to all numbers, respecting operator precedence and grouping of expressions
3
- # while ignoring whitespace.
4
- #
5
- # An identical grammar that is written using pure Ruby can be found in calc.rb.
3
+ # while ignoring whitespace. This grammar should provide the same interpretation
4
+ # as Ruby for all mathematical expressions.
6
5
  grammar Calc
7
6
 
8
7
  ## Hierarchical syntax
@@ -1,114 +1,121 @@
1
+ # This file contains a suite of tests for the Calc grammar found in calc.citrus.
2
+
1
3
  require 'citrus'
4
+ Citrus.require File.expand_path('../calc', __FILE__)
5
+ require 'test/unit'
6
+
7
+ class CalcTest < Test::Unit::TestCase
8
+ # A helper method that tests the successful parsing and evaluation of the
9
+ # given mathematical expression.
10
+ def do_test(expr)
11
+ match = ::Calc.parse(expr)
12
+ assert(match)
13
+ assert_equal(expr, match)
14
+ assert_equal(expr.length, match.length)
15
+ assert_equal(eval(expr), match.value)
16
+ end
17
+
18
+ def test_int
19
+ do_test('3')
20
+ end
2
21
 
3
- # A grammar for mathematical formulas that apply basic mathematical operations
4
- # to all numbers, respecting operator precedence and grouping of expressions
5
- # while ignoring whitespace.
6
- #
7
- # An identical grammar that is written using Citrus' own grammar syntax can be
8
- # found in calc.citrus.
9
- grammar :Calc do
22
+ def test_float
23
+ do_test('1.5')
24
+ end
10
25
 
11
- ## Hierarchical syntax
26
+ def test_addition
27
+ do_test('1+2')
28
+ end
12
29
 
13
- rule :term do
14
- any(:additive, :factor)
30
+ def test_addition_multi
31
+ do_test('1+2+3')
15
32
  end
16
33
 
17
- rule :additive do
18
- all(:factor, :additive_operator, :term) {
19
- additive_operator.value(factor.value, term.value)
20
- }
34
+ def test_addition_float
35
+ do_test('1.5+3')
21
36
  end
22
37
 
23
- rule :factor do
24
- any(:multiplicative, :prefix)
38
+ def test_subtraction
39
+ do_test('3-2')
25
40
  end
26
41
 
27
- rule :multiplicative do
28
- all(:prefix, :multiplicative_operator, :factor) {
29
- multiplicative_operator.value(prefix.value, factor.value)
30
- }
42
+ def test_subtraction_float
43
+ do_test('4.5-3')
31
44
  end
32
45
 
33
- rule :prefix do
34
- any(:prefixed, :exponent)
46
+ def test_multiplication
47
+ do_test('2*5')
35
48
  end
36
49
 
37
- rule :prefixed do
38
- all(:unary_operator, :prefix) {
39
- unary_operator.value(prefix.value)
40
- }
50
+ def test_multiplication_float
51
+ do_test('1.5*3')
41
52
  end
42
53
 
43
- rule :exponent do
44
- any(:exponential, :primary)
54
+ def test_division
55
+ do_test('20/5')
45
56
  end
46
57
 
47
- rule :exponential do
48
- all(:primary, :exponential_operator, :prefix) {
49
- exponential_operator.value(primary.value, prefix.value)
50
- }
58
+ def test_division_float
59
+ do_test('4.5/3')
51
60
  end
52
61
 
53
- rule :primary do
54
- any(:group, :number)
62
+ def test_complex
63
+ do_test('7*4+3.5*(4.5/3)')
55
64
  end
56
65
 
57
- rule :group do
58
- all(:lparen, :term, :rparen) {
59
- term.value
60
- }
66
+ def test_complex_spaced
67
+ do_test('7 * 4 + 3.5 * (4.5 / 3)')
61
68
  end
62
69
 
63
- ## Lexical syntax
70
+ def test_complex_with_underscores
71
+ do_test('(12_000 / 3) * 2.5')
72
+ end
64
73
 
65
- rule :number do
66
- any(:float, :integer)
74
+ def test_modulo
75
+ do_test('3 % 2 + 4')
67
76
  end
68
77
 
69
- rule :float do
70
- all(:digits, '.', :digits, zero_or_more(:space)) {
71
- strip.to_f
72
- }
78
+ def test_exponent
79
+ do_test('2**9')
73
80
  end
74
81
 
75
- rule :integer do
76
- all(:digits, zero_or_more(:space)) {
77
- strip.to_i
78
- }
82
+ def test_exponent_float
83
+ do_test('2**2.2')
79
84
  end
80
85
 
81
- rule :digits do
82
- # Numbers may contain underscores in Ruby.
83
- /[0-9]+(?:_[0-9]+)*/
86
+ def test_negative_exponent
87
+ do_test('2**-3')
84
88
  end
85
89
 
86
- rule :additive_operator do
87
- all(any('+', '-'), zero_or_more(:space)) { |a, b|
88
- a.send(strip, b)
89
- }
90
+ def test_exponent_exponent
91
+ do_test('2**2**2')
90
92
  end
91
93
 
92
- rule :multiplicative_operator do
93
- all(any('*', '/', '%'), zero_or_more(:space)) { |a, b|
94
- a.send(strip, b)
95
- }
94
+ def test_exponent_group
95
+ do_test('2**(3+1)')
96
96
  end
97
97
 
98
- rule :exponential_operator do
99
- all('**', zero_or_more(:space)) { |a, b|
100
- a ** b
101
- }
98
+ def test_negative
99
+ do_test('-5')
102
100
  end
103
101
 
104
- rule :unary_operator do
105
- all(any('~', '+', '-'), zero_or_more(:space)) { |n|
106
- # Unary + and - require an @.
107
- n.send(strip == '~' ? strip : '%s@' % strip)
108
- }
102
+ def test_double_negative
103
+ do_test('--5')
109
104
  end
110
105
 
111
- rule :lparen, ['(', zero_or_more(:space)]
112
- rule :rparen, [')', zero_or_more(:space)]
113
- rule :space, /[ \t\n\r]/
106
+ def test_complement
107
+ do_test('~4')
108
+ end
109
+
110
+ def test_double_complement
111
+ do_test('~~4')
112
+ end
113
+
114
+ def test_mixed_unary
115
+ do_test('~-4')
116
+ end
117
+
118
+ def test_complex_with_negatives
119
+ do_test('4 * -7 / (8.0 + 1_2)**2')
120
+ end
114
121
  end
@@ -0,0 +1,16 @@
1
+ # The grammars in this file conform to the ABNF given in Appendix A of RFC 3986
2
+ # Uniform Resource Identifier (URI): Generic Syntax.
3
+ #
4
+ # See http://tools.ietf.org/html/rfc3986#appendix-A for more information.
5
+
6
+ require 'ipv4address'
7
+ require 'ipv6address'
8
+
9
+ grammar IPAddress
10
+ include IPv4Address
11
+ include IPv6Address
12
+
13
+ rule IPaddress
14
+ IPv4address | IPv6address
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ examples = File.expand_path('..', __FILE__)
2
+ $LOAD_PATH.unshift(examples) unless $LOAD_PATH.include?(examples)
3
+
4
+ # This file contains a suite of tests for the IPAddress grammar found in
5
+ # ipaddress.citrus.
6
+
7
+ require 'citrus'
8
+ Citrus.require 'ipaddress'
9
+ require 'test/unit'
10
+
11
+ class IPAddressTest < Test::Unit::TestCase
12
+ def test_v4
13
+ match = IPAddress.parse('1.2.3.4')
14
+ assert(match)
15
+ assert_equal(4, match.version)
16
+ end
17
+
18
+ def test_v6
19
+ match = IPAddress.parse('1:2:3:4::')
20
+ assert(match)
21
+ assert_equal(6, match.version)
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ grammar IPv4Address
2
+ # A host identified by an IPv4 literal address is represented in
3
+ # dotted-decimal notation (a sequence of four decimal numbers in the
4
+ # range 0 to 255, separated by "."), as described in [RFC1123] by
5
+ # reference to [RFC0952]. Note that other forms of dotted notation may
6
+ # be interpreted on some platforms, as described in Section 7.4, but
7
+ # only the dotted-decimal form of four octets is allowed by this
8
+ # grammar.
9
+ rule IPv4address
10
+ (dec-octet '.' dec-octet '.' dec-octet '.' dec-octet) {
11
+ def version; 4 end
12
+ }
13
+ end
14
+
15
+ rule dec-octet
16
+ '25' [0-5] # 250-255
17
+ | '2' [0-4] DIGIT # 200-249
18
+ | '1' DIGIT DIGIT # 100-199
19
+ | [1-9] DIGIT # 10-99
20
+ | DIGIT # 0-9
21
+ end
22
+
23
+ rule DIGIT
24
+ [0-9]
25
+ end
26
+ end
@@ -0,0 +1,49 @@
1
+ examples = File.expand_path('..', __FILE__)
2
+ $LOAD_PATH.unshift(examples) unless $LOAD_PATH.include?(examples)
3
+
4
+ # This file contains a suite of tests for the IPv4Address grammar found in
5
+ # ipv4address.citrus.
6
+
7
+ require 'citrus'
8
+ Citrus.require 'ipv4address'
9
+ require 'test/unit'
10
+
11
+ class IPv4AddressTest < Test::Unit::TestCase
12
+ def test_dec_octet
13
+ match = IPv4Address.parse('0', :root => :'dec-octet')
14
+ assert(match)
15
+
16
+ match = IPv4Address.parse('255', :root => :'dec-octet')
17
+ assert(match)
18
+ end
19
+
20
+ def test_1
21
+ match = IPv4Address.parse('0.0.0.0')
22
+ assert(match)
23
+ assert_equal(4, match.version)
24
+ end
25
+
26
+ def test_2
27
+ match = IPv4Address.parse('255.255.255.255')
28
+ assert(match)
29
+ assert_equal(4, match.version)
30
+ end
31
+
32
+ def test_invalid
33
+ assert_raise Citrus::ParseError do
34
+ IPv4Address.parse('255.255.255.256')
35
+ end
36
+ end
37
+
38
+ def test_invalid_short
39
+ assert_raise Citrus::ParseError do
40
+ IPv4Address.parse('255.255.255')
41
+ end
42
+ end
43
+
44
+ def test_invalid_long
45
+ assert_raise Citrus::ParseError do
46
+ IPv4Address.parse('255.255.255.255.255')
47
+ end
48
+ end
49
+ end
@@ -1,34 +1,4 @@
1
- # The grammars in this file conform to the ABNF given in Appendix A of RFC 3986
2
- # Uniform Resource Identifier (URI): Generic Syntax.
3
- #
4
- # See http://tools.ietf.org/html/rfc3986#appendix-A for more information.
5
-
6
- grammar IPv4Address
7
- # A host identified by an IPv4 literal address is represented in
8
- # dotted-decimal notation (a sequence of four decimal numbers in the
9
- # range 0 to 255, separated by "."), as described in [RFC1123] by
10
- # reference to [RFC0952]. Note that other forms of dotted notation may
11
- # be interpreted on some platforms, as described in Section 7.4, but
12
- # only the dotted-decimal form of four octets is allowed by this
13
- # grammar.
14
- rule IPv4address
15
- (dec-octet '.' dec-octet '.' dec-octet '.' dec-octet) {
16
- def version; 4 end
17
- }
18
- end
19
-
20
- rule dec-octet
21
- '25' [0-5] # 250-255
22
- | '2' [0-4] DIGIT # 200-249
23
- | '1' DIGIT DIGIT # 100-199
24
- | [1-9] DIGIT # 10-99
25
- | DIGIT # 0-9
26
- end
27
-
28
- rule DIGIT
29
- [0-9]
30
- end
31
- end
1
+ require 'ipv4address'
32
2
 
33
3
  grammar IPv6Address
34
4
  include IPv4Address
@@ -71,12 +41,3 @@ grammar IPv6Address
71
41
  DIGIT | [a-fA-F] # Hexadecimal should be case-insensitive.
72
42
  end
73
43
  end
74
-
75
- grammar IPAddress
76
- include IPv4Address
77
- include IPv6Address
78
-
79
- rule IPaddress
80
- IPv4address | IPv6address
81
- end
82
- end