citrus 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'citrus'
3
- s.version = '1.4.0'
4
- s.date = '2010-06-22'
3
+ s.version = '1.5.0'
4
+ s.date = '2010-07-24'
5
5
 
6
6
  s.summary = 'Parsing Expressions for Ruby'
7
7
  s.description = 'Parsing Expressions for Ruby'
@@ -11,14 +11,12 @@ Gem::Specification.new do |s|
11
11
 
12
12
  s.require_paths = %w< lib >
13
13
 
14
- s.files = Dir['benchmark/*.rb'] +
15
- Dir['benchmark/*.citrus'] +
16
- Dir['benchmark/*.gnuplot'] +
17
- Dir['doc/**/*'] +
18
- Dir['examples/**/*'] +
19
- Dir['extras/**/*'] +
14
+ s.files = Dir['benchmark/**'] +
15
+ Dir['doc/**'] +
16
+ Dir['examples/**'] +
17
+ Dir['extras/**'] +
20
18
  Dir['lib/**/*.rb'] +
21
- Dir['test/*.rb'] +
19
+ Dir['test/**/*'] +
22
20
  %w< citrus.gemspec Rakefile README >
23
21
 
24
22
  s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/ }
@@ -7,9 +7,9 @@ grammar Calc
7
7
  end
8
8
 
9
9
  rule additive
10
- (factor operator:additive_op term) {
10
+ (factor operator:(plus | minus) term) {
11
11
  def value
12
- operator.apply(factor.value, term.value)
12
+ operator.apply(factor, term)
13
13
  end
14
14
  }
15
15
  end
@@ -19,9 +19,9 @@ grammar Calc
19
19
  end
20
20
 
21
21
  rule multiplicative
22
- (primary operator:multiplicative_op factor) {
22
+ (primary operator:(star | slash) factor) {
23
23
  def value
24
- operator.apply(primary.value, factor.value)
24
+ operator.apply(primary, factor)
25
25
  end
26
26
  }
27
27
  end
@@ -38,22 +38,6 @@ grammar Calc
38
38
  }
39
39
  end
40
40
 
41
- rule additive_op
42
- (plus | minus) {
43
- def apply(factor, term)
44
- text.strip == '+' ? factor + term : factor - term
45
- end
46
- }
47
- end
48
-
49
- rule multiplicative_op
50
- (star | slash) {
51
- def apply(primary, factor)
52
- text.strip == '*' ? primary * factor : primary / factor
53
- end
54
- }
55
- end
56
-
57
41
  rule number
58
42
  float | integer
59
43
  end
@@ -74,14 +58,39 @@ grammar Calc
74
58
  }
75
59
  end
76
60
 
77
- rule lparen '(' space end
78
- rule rparen ')' space end
79
- rule plus '+' space end
80
- rule minus '-' space end
81
- rule star '*' space end
82
- rule slash '/' space end
61
+ rule plus
62
+ ('+' space) {
63
+ def apply(factor, term)
64
+ factor.value + term.value
65
+ end
66
+ }
67
+ end
68
+
69
+ rule minus
70
+ ('-' space) {
71
+ def apply(factor, term)
72
+ factor.value - term.value
73
+ end
74
+ }
75
+ end
76
+
77
+ rule star
78
+ ('*' space) {
79
+ def apply(primary, factor)
80
+ primary.value * factor.value
81
+ end
82
+ }
83
+ end
83
84
 
84
- rule space
85
- [ \t\n\r]*
85
+ rule slash
86
+ ('/' space) {
87
+ def apply(primary, factor)
88
+ primary.value / factor.value
89
+ end
90
+ }
86
91
  end
92
+
93
+ rule lparen '(' space end
94
+ rule rparen ')' space end
95
+ rule space [ \t\n\r]* end
87
96
  end
@@ -3,17 +3,15 @@ require 'citrus'
3
3
  # A grammar for mathematical formulas that apply the basic four operations to
4
4
  # non-negative numbers (integers and floats), respecting operator precedence and
5
5
  # ignoring whitespace.
6
- module Calc
7
- include Citrus::Grammar
8
-
6
+ grammar :Calc do
9
7
  rule :term do
10
8
  any(:additive, :factor)
11
9
  end
12
10
 
13
11
  rule :additive do
14
- all(:factor, label(:additive_op, :operator), :term) {
12
+ all(:factor, label(any(:plus, :minus), :operator), :term) {
15
13
  def value
16
- operator.apply(factor.value, term.value)
14
+ operator.apply(factor, term)
17
15
  end
18
16
  }
19
17
  end
@@ -23,9 +21,9 @@ module Calc
23
21
  end
24
22
 
25
23
  rule :multiplicative do
26
- all(:primary, label(:multiplicative_op, :operator), :factor) {
24
+ all(:primary, label(any(:star, :slash), :operator), :factor) {
27
25
  def value
28
- operator.apply(primary.value, factor.value)
26
+ operator.apply(primary, factor)
29
27
  end
30
28
  }
31
29
  end
@@ -42,22 +40,6 @@ module Calc
42
40
  }
43
41
  end
44
42
 
45
- rule :additive_op do
46
- any(:plus, :minus) {
47
- def apply(factor, term)
48
- text.strip == '+' ? factor + term : factor - term
49
- end
50
- }
51
- end
52
-
53
- rule :multiplicative_op do
54
- any(:star, :slash) {
55
- def apply(primary, factor)
56
- text.strip == '*' ? primary * factor : primary / factor
57
- end
58
- }
59
- end
60
-
61
43
  rule :number do
62
44
  any(:float, :integer)
63
45
  end
@@ -78,12 +60,39 @@ module Calc
78
60
  }
79
61
  end
80
62
 
63
+ rule :plus do
64
+ all('+', :space) {
65
+ def apply(factor, term)
66
+ factor.value + term.value
67
+ end
68
+ }
69
+ end
70
+
71
+ rule :minus do
72
+ all('-', :space) {
73
+ def apply(factor, term)
74
+ factor.value - term.value
75
+ end
76
+ }
77
+ end
78
+
79
+ rule :star do
80
+ all('*', :space) {
81
+ def apply(primary, factor)
82
+ primary.value * factor.value
83
+ end
84
+ }
85
+ end
86
+
87
+ rule :slash do
88
+ all('/', :space) {
89
+ def apply(primary, factor)
90
+ primary.value / factor.value
91
+ end
92
+ }
93
+ end
94
+
81
95
  rule :lparen, ['(', :space]
82
96
  rule :rparen, [')', :space]
83
- rule :plus, ['+', :space]
84
- rule :minus, ['-', :space]
85
- rule :star, ['*', :space]
86
- rule :slash, ['/', :space]
87
-
88
97
  rule :space, /[ \t\n\r]*/
89
98
  end
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # http://mjijackson.com/citrus
6
6
  module Citrus
7
- VERSION = [1, 4, 0]
7
+ VERSION = [1, 5, 0]
8
8
 
9
9
  Infinity = 1.0 / 0
10
10
 
@@ -86,6 +86,11 @@ module Citrus
86
86
 
87
87
  # Contains methods that are available to Grammar modules at the class level.
88
88
  module GrammarMethods
89
+ def self.extend_object(obj)
90
+ raise ArgumentError, "Grammars must be Ruby modules" unless Module === obj
91
+ super
92
+ end
93
+
89
94
  # Returns the name of this grammar as a string.
90
95
  def name
91
96
  super.to_s
@@ -236,7 +241,7 @@ module Citrus
236
241
  def ext(rule, mod=nil)
237
242
  rule = Rule.create(rule)
238
243
  mod = Proc.new if block_given?
239
- rule.ext = mod if mod
244
+ rule.extension = mod if mod
240
245
  rule
241
246
  end
242
247
 
@@ -387,13 +392,13 @@ module Citrus
387
392
  # Specifies a module that will be used to extend all Match objects that
388
393
  # result from this rule. If +mod+ is a Proc, it is used to create an
389
394
  # anonymous module.
390
- def ext=(mod)
395
+ def extension=(mod)
391
396
  mod = Module.new(&mod) if Proc === mod
392
- @ext = mod
397
+ @extension = mod
393
398
  end
394
399
 
395
400
  # The module this rule uses to extend new matches.
396
- attr_reader :ext
401
+ attr_reader :extension
397
402
 
398
403
  # Returns +true+ if this rule is a Terminal.
399
404
  def terminal?
@@ -419,7 +424,7 @@ module Citrus
419
424
  private
420
425
 
421
426
  def extend_match(match, name)
422
- match.extensions << ext if ext
427
+ match.extensions << extension if extension
423
428
  match.names << name if name
424
429
  match
425
430
  end
@@ -931,26 +936,51 @@ module Citrus
931
936
 
932
937
  alias eql? ==
933
938
 
934
- # Uses #match to allow sub-matches of this match to be called by name as
935
- # instance methods.
936
- def method_missing(sym, *args)
937
- # Extend this object only when needed and immediately redefine
938
- # #method_missing so that the new version is used on all future calls.
939
- extensions.each {|e| extend(e) } if @extensions
940
- redefine_method_missing!
941
- __send__(sym, *args)
942
- end
943
-
944
939
  private
945
940
 
946
941
  def redefine_method_missing! # :nodoc:
947
942
  instance_eval(<<-RUBY, __FILE__, __LINE__ + 1)
948
943
  def method_missing(sym, *args)
949
- m = first(sym)
950
- return m if m
951
- raise 'No match named "%s" in %s (%s)' % [sym, self, name]
944
+ if sym == :to_ary
945
+ original_method_missing(sym, *args)
946
+ else
947
+ m = first(sym)
948
+ return m if m
949
+ raise 'No match named "%s" in %s (%s)' % [sym, self, name]
950
+ end
952
951
  end
953
952
  RUBY
954
953
  end
954
+
955
+ alias original_method_missing method_missing
956
+
957
+ public
958
+
959
+ # Allows sub-matches of this match to be retrieved by name as instance
960
+ # methods.
961
+ def method_missing(sym, *args)
962
+ # Extend this object only when needed and immediately redefine
963
+ # #method_missing so that the new version is used on all future calls.
964
+ extensions.each {|e| extend(e) } if @extensions
965
+ redefine_method_missing!
966
+ __send__(sym, *args)
967
+ end
968
+ end
969
+ end
970
+
971
+ class Object
972
+ # A sugar method for creating grammars.
973
+ #
974
+ # grammar :Calc do
975
+ # end
976
+ #
977
+ # module MyModule
978
+ # grammar :Calc do
979
+ # end
980
+ # end
981
+ #
982
+ def grammar(name, &block)
983
+ obj = respond_to?(:const_set) ? self : Object
984
+ obj.const_set(name, Citrus::Grammar.new(&block))
955
985
  end
956
986
  end
@@ -1,11 +1,10 @@
1
1
  require 'citrus'
2
2
 
3
3
  module Citrus
4
- # A grammar for Citrus grammar files. This module is used in Citrus#eval to
4
+ # A grammar for Citrus grammar files. This grammar is used in Citrus#eval to
5
5
  # parse and evaluate Citrus grammars and serves as a prime example of how to
6
6
  # create a complex grammar complete with semantic interpretation in pure Ruby.
7
- module File
8
- include Grammar
7
+ File = Grammar.new do
9
8
 
10
9
  ## Hierarchical syntax
11
10
 
@@ -119,7 +118,7 @@ module Citrus
119
118
  def value
120
119
  rule = suffix.value
121
120
  extension = matches[1].first
122
- rule = extension.wrap(rule) if extension
121
+ extension.apply(rule) if extension
123
122
  rule
124
123
  end
125
124
  }
@@ -217,7 +216,7 @@ module Citrus
217
216
  rule :character_class do
218
217
  all(/\[(?:\\?.)*?\]/, :space) {
219
218
  def value
220
- Regexp.new(first.text)
219
+ Regexp.new('\A' + first.text)
221
220
  end
222
221
  }
223
222
  end
@@ -225,7 +224,7 @@ module Citrus
225
224
  rule :anything_symbol do
226
225
  all('.', :space) {
227
226
  def value
228
- /./m # The dot matches newlines
227
+ /./m # Match newlines
229
228
  end
230
229
  }
231
230
  end
@@ -272,9 +271,8 @@ module Citrus
272
271
 
273
272
  rule :extension do
274
273
  any(:tag, :block) {
275
- def wrap(rule)
276
- rule.ext = value
277
- rule
274
+ def apply(rule)
275
+ rule.extension = value
278
276
  end
279
277
  }
280
278
  end
@@ -0,0 +1,9 @@
1
+ grammar AliasOne
2
+ rule alias
3
+ value
4
+ end
5
+
6
+ rule value
7
+ 'a'
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ grammar Calc end
@@ -0,0 +1,25 @@
1
+ grammar Calc
2
+ rule number
3
+ (float | integer) {
4
+ def value
5
+ first.value
6
+ end
7
+ }
8
+ end
9
+
10
+ rule float
11
+ (integer '.' integer) {
12
+ def value
13
+ text.to_f
14
+ end
15
+ }
16
+ end
17
+
18
+ rule integer
19
+ [0-9]+ {
20
+ def value
21
+ text.to_i
22
+ end
23
+ }
24
+ end
25
+ end
@@ -0,0 +1,112 @@
1
+ grammar Calc
2
+ # If "additive" were not already the first rule declared in this grammar, we
3
+ # could use the following line to make it the root rule.
4
+ #root additive
5
+
6
+ rule additive
7
+ (multitive_additive | multitive) {
8
+ def value
9
+ first.value
10
+ end
11
+ }
12
+ end
13
+
14
+ rule multitive_additive
15
+ (multitive additive_op additive) {
16
+ def value
17
+ if additive_op == '+'
18
+ multitive.value + additive.value
19
+ else
20
+ multitive.value - additive.value
21
+ end
22
+ end
23
+ }
24
+ end
25
+
26
+ rule multitive
27
+ (primary_multitive | primary) {
28
+ def value
29
+ first.value
30
+ end
31
+ }
32
+ end
33
+
34
+ rule primary_multitive
35
+ (primary multitive_op multitive) {
36
+ def value
37
+ if multitive_op == '*'
38
+ primary.value * multitive.value
39
+ else
40
+ primary.value / multitive.value
41
+ end
42
+ end
43
+ }
44
+ end
45
+
46
+ rule primary
47
+ (additive_paren | number) {
48
+ def value
49
+ first.value
50
+ end
51
+ }
52
+ end
53
+
54
+ rule additive_paren
55
+ ('(' additive ')') {
56
+ def value
57
+ additive.value
58
+ end
59
+ }
60
+ end
61
+
62
+ rule additive_op
63
+ (plus | minus) {
64
+ def ==(other)
65
+ text.strip == other
66
+ end
67
+ }
68
+ end
69
+
70
+ rule multitive_op
71
+ (star | slash) {
72
+ def ==(other)
73
+ text.strip == other
74
+ end
75
+ }
76
+ end
77
+
78
+ rule number
79
+ (float | integer) {
80
+ def value
81
+ first.value
82
+ end
83
+ }
84
+ end
85
+
86
+ rule float
87
+ (integer '.' integer) {
88
+ def value
89
+ text.to_f
90
+ end
91
+ }
92
+ end
93
+
94
+ rule integer
95
+ [0-9]+ {
96
+ def value
97
+ text.to_i
98
+ end
99
+ }
100
+ end
101
+
102
+ rule lparen '(' space end
103
+ rule rparen ')' space end
104
+ rule plus '+' space end
105
+ rule minus '-' space end
106
+ rule star '*' space end
107
+ rule slash '/' space end
108
+
109
+ rule space
110
+ /[ \t\n\r]*/
111
+ end
112
+ end
@@ -0,0 +1 @@
1
+ rule int '' end
@@ -0,0 +1,3 @@
1
+ rule int
2
+ [0-9]+
3
+ end
@@ -0,0 +1,7 @@
1
+ rule int
2
+ [0-9]+ {
3
+ def value
4
+ text.to_i
5
+ end
6
+ }
7
+ end
@@ -0,0 +1,13 @@
1
+ grammar SuperOne
2
+ rule num
3
+ '1'
4
+ end
5
+ end
6
+
7
+ grammar SuperTwo
8
+ include SuperOne
9
+
10
+ rule num
11
+ '2' | super
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
  Citrus.load(File.dirname(__FILE__) + '/_files/alias')
3
3
 
4
4
  class AliasTest < Test::Unit::TestCase
@@ -16,8 +16,8 @@ class AliasTest < Test::Unit::TestCase
16
16
 
17
17
  match = grammar.parse('b')
18
18
  assert(match)
19
- assert('b', match.text)
20
- assert(1, match.length)
19
+ assert_equal('b', match.text)
20
+ assert_equal(1, match.length)
21
21
  end
22
22
 
23
23
  def test_match_renamed
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class AndPredicateTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  if defined?(Calc)
4
4
  Object.__send__(:remove_const, :Calc)
@@ -1,10 +1,10 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  if defined?(Calc)
4
4
  Object.__send__(:remove_const, :Calc)
5
5
  end
6
6
 
7
- require File.dirname(__FILE__) + '/../examples/calc'
7
+ require File.expand_path('../../examples/calc', __FILE__)
8
8
 
9
9
  class CalcTest < Test::Unit::TestCase
10
10
  include CalcTestMethods
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class ChoiceTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class ExpressionTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
  require 'citrus/file'
3
3
 
4
4
  class CitrusFileTest < Test::Unit::TestCase
@@ -233,17 +233,17 @@ class CitrusFileTest < Test::Unit::TestCase
233
233
  match = grammar.parse('"" <Module>')
234
234
  assert(match)
235
235
  assert_kind_of(Rule, match.value)
236
- assert_kind_of(Module, match.value.ext)
236
+ assert_kind_of(Module, match.value.extension)
237
237
 
238
238
  match = grammar.parse('"" {}')
239
239
  assert(match)
240
240
  assert_kind_of(Rule, match.value)
241
- assert_kind_of(Module, match.value.ext)
241
+ assert_kind_of(Module, match.value.extension)
242
242
 
243
243
  match = grammar.parse('"" {} ')
244
244
  assert(match)
245
245
  assert_kind_of(Rule, match.value)
246
- assert_kind_of(Module, match.value.ext)
246
+ assert_kind_of(Module, match.value.extension)
247
247
  end
248
248
 
249
249
  def test_suffix
@@ -408,19 +408,23 @@ class CitrusFileTest < Test::Unit::TestCase
408
408
 
409
409
  match = grammar.parse('[_]')
410
410
  assert(match)
411
- assert_equal(/[_]/, match.value)
411
+ assert_equal(/\A[_]/, match.value)
412
412
 
413
413
  match = grammar.parse('[a-z]')
414
414
  assert(match)
415
- assert_equal(/[a-z]/, match.value)
415
+ assert_equal(/\A[a-z]/, match.value)
416
416
 
417
417
  match = grammar.parse('[a-z0-9]')
418
418
  assert(match)
419
- assert_equal(/[a-z0-9]/, match.value)
419
+ assert_equal(/\A[a-z0-9]/, match.value)
420
+
421
+ match = grammar.parse('[\[-\]]')
422
+ assert(match)
423
+ assert_equal(/\A[\[-\]]/, match.value)
420
424
 
421
425
  match = grammar.parse('[\\x26-\\x29]')
422
426
  assert(match)
423
- assert_equal(/[\x26-\x29]/, match.value)
427
+ assert_equal(/\A[\x26-\x29]/, match.value)
424
428
  end
425
429
 
426
430
  def test_anything_symbol
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class FixedWidthTest < Test::Unit::TestCase
4
4
 
@@ -1,11 +1,16 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class GrammarTest < Test::Unit::TestCase
4
4
 
5
5
  def test_new
6
- Grammar.new do
7
- assert_kind_of(Module, self)
8
- assert(include?(Grammar))
6
+ g = Grammar.new
7
+ assert_kind_of(Module, g)
8
+ assert(g.include?(Grammar))
9
+ end
10
+
11
+ def test_non_module_fail
12
+ assert_raise ArgumentError do
13
+ ''.extend(GrammarMethods)
9
14
  end
10
15
  end
11
16
 
@@ -116,14 +121,14 @@ class GrammarTest < Test::Unit::TestCase
116
121
 
117
122
  match = grammar.parse('((a))')
118
123
  assert(match)
119
- assert('((a))', match.text)
120
- assert(5, match.length)
124
+ assert_equal('((a))', match.text)
125
+ assert_equal(5, match.length)
121
126
 
122
127
  str = ('(' * 200) + 'a' + (')' * 200)
123
128
  match = grammar.parse(str)
124
129
  assert(match)
125
- assert(str, match.text)
126
- assert(str.length, match.length)
130
+ assert_equal(str, match.text)
131
+ assert_equal(str.length, match.length)
127
132
  end
128
133
 
129
134
  end
@@ -44,100 +44,66 @@ class Test::Unit::TestCase
44
44
  end
45
45
 
46
46
  module CalcTestMethods
47
- def test_int
48
- match = Calc.parse('3')
47
+ # A helper method that tests the successful parsing and evaluation of the
48
+ # given mathematical expression.
49
+ def do_test(expr)
50
+ match = Calc.parse(expr)
49
51
  assert(match)
50
- assert_equal('3', match.text)
51
- assert_equal(1, match.length)
52
- assert_equal(3, match.value)
52
+ assert_equal(expr, match.text)
53
+ assert_equal(expr.length, match.length)
54
+ assert_equal(eval(expr), match.value)
55
+ end
56
+
57
+ def test_int
58
+ do_test('3')
53
59
  end
54
60
 
55
61
  def test_float
56
- match = Calc.parse('1.5')
57
- assert(match)
58
- assert_equal('1.5', match.text)
59
- assert_equal(3, match.length)
60
- assert_equal(1.5, match.value)
62
+ do_test('1.5')
61
63
  end
62
64
 
63
65
  def test_addition
64
- match = Calc.parse('1+2')
65
- assert(match)
66
- assert_equal('1+2', match.text)
67
- assert_equal(3, match.length)
68
- assert_equal(3, match.value)
66
+ do_test('1+2')
69
67
  end
70
68
 
71
69
  def test_addition_multi
72
- match = Calc.parse('1+2+3')
73
- assert(match)
74
- assert_equal('1+2+3', match.text)
75
- assert_equal(5, match.length)
76
- assert_equal(6, match.value)
70
+ do_test('1+2+3')
77
71
  end
78
72
 
79
73
  def test_addition_float
80
- match = Calc.parse('1.5+3')
81
- assert(match)
82
- assert_equal('1.5+3', match.text)
83
- assert_equal(5, match.length)
84
- assert_equal(4.5, match.value)
74
+ do_test('1.5+3')
85
75
  end
86
76
 
87
77
  def test_subtraction
88
- match = Calc.parse('3-2')
89
- assert(match)
90
- assert_equal(1, match.value)
78
+ do_test('3-2')
91
79
  end
92
80
 
93
81
  def test_subtraction_float
94
- match = Calc.parse('4.5-3')
95
- assert(match)
96
- assert_equal('4.5-3', match.text)
97
- assert_equal(5, match.length)
98
- assert_equal(1.5, match.value)
82
+ do_test('4.5-3')
99
83
  end
100
84
 
101
85
  def test_multiplication
102
- match = Calc.parse('2*5')
103
- assert(match)
104
- assert_equal(10, match.value)
86
+ do_test('2*5')
105
87
  end
106
88
 
107
89
  def test_multiplication_float
108
- match = Calc.parse('1.5*3')
109
- assert(match)
110
- assert_equal('1.5*3', match.text)
111
- assert_equal(5, match.length)
112
- assert_equal(4.5, match.value)
90
+ do_test('1.5*3')
113
91
  end
114
92
 
115
93
  def test_division
116
- match = Calc.parse('20/5')
117
- assert(match)
118
- assert_equal(4, match.value)
94
+ do_test('20/5')
119
95
  end
120
96
 
121
97
  def test_division_float
122
- match = Calc.parse('4.5/3')
123
- assert(match)
124
- assert_equal('4.5/3', match.text)
125
- assert_equal(5, match.length)
126
- assert_equal(1.5, match.value)
98
+ do_test('4.5/3')
127
99
  end
128
100
 
129
101
  def test_complex
130
- match = Calc.parse('7*4+3.5*(4.5/3)')
131
- assert(match)
132
- assert_equal('7*4+3.5*(4.5/3)', match.text)
133
- assert_equal(33.25, match.value)
102
+ do_test('7*4+3.5*(4.5/3)')
134
103
  end
135
104
 
136
105
  def test_complex_spaced
137
- match = Calc.parse('7 * 4 + 3.5 * (4.5 / 3)')
138
- assert(match)
139
- assert_equal(33.25, match.value)
106
+ do_test('7 * 4 + 3.5 * (4.5 / 3)')
140
107
  end
141
108
  end
142
-
143
109
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class LabelTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class MatchTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class NotPredicateTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class RepeatTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class RuleTest < Test::Unit::TestCase
4
4
 
@@ -22,7 +22,7 @@ class RuleTest < Test::Unit::TestCase
22
22
 
23
23
  def test_match_module
24
24
  rule = EqualRule.new('a')
25
- rule.ext = MatchModule
25
+ rule.extension = MatchModule
26
26
  match = rule.match(input('a'))
27
27
  assert(match)
28
28
  assert_equal(:test, match.a_test)
@@ -30,7 +30,7 @@ class RuleTest < Test::Unit::TestCase
30
30
 
31
31
  def test_numeric_proc
32
32
  rule = EqualRule.new(1)
33
- rule.ext = NumericProc
33
+ rule.extension = NumericProc
34
34
  match = rule.match(input('1'))
35
35
  assert(match)
36
36
  assert_equal(1, match.to_i)
@@ -39,7 +39,7 @@ class RuleTest < Test::Unit::TestCase
39
39
 
40
40
  def test_numeric_module
41
41
  rule = EqualRule.new(1)
42
- rule.ext = NumericModule
42
+ rule.extension = NumericModule
43
43
  match = rule.match(input('1'))
44
44
  assert(match)
45
45
  assert_equal(1, match.to_i)
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class SequenceTest < Test::Unit::TestCase
4
4
 
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
  Citrus.load(File.dirname(__FILE__) + '/_files/super')
3
3
 
4
4
  class SuperTest < Test::Unit::TestCase
@@ -20,13 +20,13 @@ class SuperTest < Test::Unit::TestCase
20
20
 
21
21
  match = grammar2.parse('b')
22
22
  assert(match)
23
- assert('b', match.text)
24
- assert(1, match.length)
23
+ assert_equal('b', match.text)
24
+ assert_equal(1, match.length)
25
25
 
26
26
  match = grammar2.parse('a')
27
27
  assert(match)
28
- assert('a', match.text)
29
- assert(1, match.length)
28
+ assert_equal('a', match.text)
29
+ assert_equal(1, match.length)
30
30
  end
31
31
 
32
32
  def test_peg
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: citrus
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 3
4
5
  prerelease: false
5
6
  segments:
6
7
  - 1
7
- - 4
8
+ - 5
8
9
  - 0
9
- version: 1.4.0
10
+ version: 1.5.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Michael Jackson
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-06-22 00:00:00 -06:00
18
+ date: 2010-07-24 00:00:00 -06:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: builder
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
@@ -33,9 +36,11 @@ dependencies:
33
36
  name: rake
34
37
  prerelease: false
35
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
36
40
  requirements:
37
41
  - - ">="
38
42
  - !ruby/object:Gem::Version
43
+ hash: 3
39
44
  segments:
40
45
  - 0
41
46
  version: "0"
@@ -50,9 +55,9 @@ extensions: []
50
55
  extra_rdoc_files:
51
56
  - README
52
57
  files:
53
- - benchmark/seqpar.rb
54
58
  - benchmark/seqpar.citrus
55
59
  - benchmark/seqpar.gnuplot
60
+ - benchmark/seqpar.rb
56
61
  - doc/background.rdoc
57
62
  - doc/example.rdoc
58
63
  - doc/index.rdoc
@@ -61,16 +66,21 @@ files:
61
66
  - doc/syntax.rdoc
62
67
  - examples/calc.citrus
63
68
  - examples/calc.rb
64
- - examples/calc_sugar.rb
65
69
  - extras/citrus.vim
66
70
  - lib/citrus/debug.rb
67
71
  - lib/citrus/file.rb
68
- - lib/citrus/sugar.rb
69
72
  - lib/citrus.rb
73
+ - test/_files/alias.citrus
74
+ - test/_files/grammar1.citrus
75
+ - test/_files/grammar2.citrus
76
+ - test/_files/grammar3.citrus
77
+ - test/_files/rule1.citrus
78
+ - test/_files/rule2.citrus
79
+ - test/_files/rule3.citrus
80
+ - test/_files/super.citrus
70
81
  - test/alias_test.rb
71
82
  - test/and_predicate_test.rb
72
83
  - test/calc_file_test.rb
73
- - test/calc_sugar_test.rb
74
84
  - test/calc_test.rb
75
85
  - test/choice_test.rb
76
86
  - test/expression_test.rb
@@ -103,23 +113,27 @@ rdoc_options:
103
113
  require_paths:
104
114
  - lib
105
115
  required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
106
117
  requirements:
107
118
  - - ">="
108
119
  - !ruby/object:Gem::Version
120
+ hash: 3
109
121
  segments:
110
122
  - 0
111
123
  version: "0"
112
124
  required_rubygems_version: !ruby/object:Gem::Requirement
125
+ none: false
113
126
  requirements:
114
127
  - - ">="
115
128
  - !ruby/object:Gem::Version
129
+ hash: 3
116
130
  segments:
117
131
  - 0
118
132
  version: "0"
119
133
  requirements: []
120
134
 
121
135
  rubyforge_project:
122
- rubygems_version: 1.3.6
136
+ rubygems_version: 1.3.7
123
137
  signing_key:
124
138
  specification_version: 3
125
139
  summary: Parsing Expressions for Ruby
@@ -127,7 +141,6 @@ test_files:
127
141
  - test/alias_test.rb
128
142
  - test/and_predicate_test.rb
129
143
  - test/calc_file_test.rb
130
- - test/calc_sugar_test.rb
131
144
  - test/calc_test.rb
132
145
  - test/choice_test.rb
133
146
  - test/expression_test.rb
@@ -1,87 +0,0 @@
1
- require 'citrus/sugar'
2
-
3
- # A grammar for mathematical formulas that apply the basic four operations to
4
- # non-negative numbers (integers and floats), respecting operator precedence and
5
- # ignoring whitespace.
6
- Calc = Citrus::Grammar.new {
7
- rule term do
8
- any(additive, factor)
9
- end
10
-
11
- rule additive do
12
- all(factor, label(additive_op, operator), term) {
13
- def value
14
- operator.apply(factor.value, term.value)
15
- end
16
- }
17
- end
18
-
19
- rule factor do
20
- any(multiplicative, primary)
21
- end
22
-
23
- rule multiplicative do
24
- all(primary, label(multiplicative_op, operator), factor) {
25
- def value
26
- operator.apply(primary.value, factor.value)
27
- end
28
- }
29
- end
30
-
31
- rule primary do
32
- any(term_paren, number)
33
- end
34
-
35
- rule term_paren do
36
- all(lparen, term, rparen) {
37
- def value
38
- term.value
39
- end
40
- }
41
- end
42
-
43
- rule additive_op do
44
- any(plus, minus) {
45
- def apply(factor, term)
46
- text.strip == '+' ? factor + term : factor - term
47
- end
48
- }
49
- end
50
-
51
- rule multiplicative_op do
52
- any(star, slash) {
53
- def apply(primary, factor)
54
- text.strip == '*' ? primary * factor : primary / factor
55
- end
56
- }
57
- end
58
-
59
- rule number do
60
- any(float, integer)
61
- end
62
-
63
- rule float do
64
- all(/[0-9]+/, '.', /[0-9]+/, space) {
65
- def value
66
- text.strip.to_f
67
- end
68
- }
69
- end
70
-
71
- rule integer do
72
- all(/[0-9]+/, space) {
73
- def value
74
- text.strip.to_i
75
- end
76
- }
77
- end
78
-
79
- rule lparen, ['(', space]
80
- rule rparen, [')', space]
81
- rule plus, ['+', space]
82
- rule minus, ['-', space]
83
- rule star, ['*', space]
84
- rule slash, ['/', space]
85
-
86
- rule space, /[ \t\n\r]*/
87
- }
@@ -1,25 +0,0 @@
1
- require 'citrus'
2
-
3
- module Citrus
4
- module GrammarMethods
5
- # Permits creation of aliases within rule definitions in Ruby grammars using
6
- # the bare name of another rule instead of a Symbol, e.g.:
7
- #
8
- # rule :value do
9
- # any(:alpha, :num)
10
- # end
11
- #
12
- # can now be written as
13
- #
14
- # rule value do
15
- # any(alpha, num)
16
- # end
17
- #
18
- # The only caveat is that since this hack uses +method_missing+ you must
19
- # still use symbols for rules that have the same name as any of the methods
20
- # in GrammarMethods (root, rule, rules, etc.)
21
- def method_missing(sym, *args)
22
- sym
23
- end
24
- end
25
- end
@@ -1,11 +0,0 @@
1
- require File.dirname(__FILE__) + '/helper'
2
-
3
- if defined?(Calc)
4
- Object.__send__(:remove_const, :Calc)
5
- end
6
-
7
- require File.dirname(__FILE__) + '/../examples/calc_sugar'
8
-
9
- class CalcSugarTest < Test::Unit::TestCase
10
- include CalcTestMethods
11
- end