citrus 1.4.0 → 1.5.0

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.
@@ -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