citrus 2.3.2 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
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