citrus 1.7.0 → 1.8.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.
- 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
data/test/choice_test.rb
CHANGED
@@ -15,7 +15,7 @@ class ChoiceTest < Test::Unit::TestCase
|
|
15
15
|
|
16
16
|
match = rule.match(input('a'))
|
17
17
|
assert(match)
|
18
|
-
assert_equal('a', match
|
18
|
+
assert_equal('a', match)
|
19
19
|
assert_equal(1, match.length)
|
20
20
|
end
|
21
21
|
|
@@ -24,12 +24,12 @@ class ChoiceTest < Test::Unit::TestCase
|
|
24
24
|
|
25
25
|
match = rule.match(input('ab'))
|
26
26
|
assert(match)
|
27
|
-
assert_equal('a', match
|
27
|
+
assert_equal('a', match)
|
28
28
|
assert_equal(1, match.length)
|
29
29
|
|
30
30
|
match = rule.match(input('ba'))
|
31
31
|
assert(match)
|
32
|
-
assert_equal('b', match
|
32
|
+
assert_equal('b', match)
|
33
33
|
assert_equal(1, match.length)
|
34
34
|
end
|
35
35
|
|
@@ -38,12 +38,12 @@ class ChoiceTest < Test::Unit::TestCase
|
|
38
38
|
|
39
39
|
match = rule.match(input('1+'))
|
40
40
|
assert(match)
|
41
|
-
assert_equal('1', match
|
41
|
+
assert_equal('1', match)
|
42
42
|
assert_equal(1, match.length)
|
43
43
|
|
44
44
|
match = rule.match(input('+1'))
|
45
45
|
assert(match)
|
46
|
-
assert_equal('+', match
|
46
|
+
assert_equal('+', match)
|
47
47
|
assert_equal(1, match.length)
|
48
48
|
end
|
49
49
|
|
data/test/expression_test.rb
CHANGED
data/test/file_test.rb
CHANGED
@@ -93,7 +93,7 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
93
93
|
assert_instance_of(Choice, match.value)
|
94
94
|
assert_equal(1, match.find(:bar).length)
|
95
95
|
assert_equal(2, match.find(:regular_expression).length)
|
96
|
-
assert_equal(0, match.find(:
|
96
|
+
assert_equal(0, match.find(:dot).length)
|
97
97
|
|
98
98
|
match = grammar.parse('"" {}')
|
99
99
|
assert(match)
|
@@ -444,12 +444,12 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
444
444
|
assert_equal(/\A[\x26-\x29]/, match.value)
|
445
445
|
end
|
446
446
|
|
447
|
-
def
|
448
|
-
grammar = file(:
|
447
|
+
def test_dot
|
448
|
+
grammar = file(:dot)
|
449
449
|
|
450
450
|
match = grammar.parse('.')
|
451
451
|
assert(match)
|
452
|
-
assert_equal(
|
452
|
+
assert_equal(DOT, match.value)
|
453
453
|
end
|
454
454
|
|
455
455
|
def test_regular_expression
|
@@ -481,11 +481,11 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
481
481
|
|
482
482
|
match = grammar.parse('&')
|
483
483
|
assert(match)
|
484
|
-
assert_kind_of(Rule, match.
|
484
|
+
assert_kind_of(Rule, match.value(''))
|
485
485
|
|
486
486
|
match = grammar.parse('!')
|
487
487
|
assert(match)
|
488
|
-
assert_kind_of(Rule, match.
|
488
|
+
assert_kind_of(Rule, match.value(''))
|
489
489
|
end
|
490
490
|
|
491
491
|
def test_and
|
@@ -493,11 +493,11 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
493
493
|
|
494
494
|
match = grammar.parse('&')
|
495
495
|
assert(match)
|
496
|
-
assert_instance_of(AndPredicate, match.
|
496
|
+
assert_instance_of(AndPredicate, match.value(''))
|
497
497
|
|
498
498
|
match = grammar.parse('& ')
|
499
499
|
assert(match)
|
500
|
-
assert_instance_of(AndPredicate, match.
|
500
|
+
assert_instance_of(AndPredicate, match.value(''))
|
501
501
|
end
|
502
502
|
|
503
503
|
def test_not
|
@@ -505,11 +505,11 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
505
505
|
|
506
506
|
match = grammar.parse('!')
|
507
507
|
assert(match)
|
508
|
-
assert_instance_of(NotPredicate, match.
|
508
|
+
assert_instance_of(NotPredicate, match.value(''))
|
509
509
|
|
510
510
|
match = grammar.parse('! ')
|
511
511
|
assert(match)
|
512
|
-
assert_instance_of(NotPredicate, match.
|
512
|
+
assert_instance_of(NotPredicate, match.value(''))
|
513
513
|
end
|
514
514
|
|
515
515
|
def test_label
|
@@ -517,13 +517,15 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
517
517
|
|
518
518
|
match = grammar.parse('label:')
|
519
519
|
assert(match)
|
520
|
-
|
521
|
-
assert_instance_of(Label,
|
520
|
+
v = match.value('')
|
521
|
+
assert_instance_of(Label, v)
|
522
|
+
assert_equal(:label, v.label)
|
522
523
|
|
523
524
|
match = grammar.parse('a_label : ')
|
524
525
|
assert(match)
|
525
|
-
|
526
|
-
assert_instance_of(Label,
|
526
|
+
v = match.value('')
|
527
|
+
assert_instance_of(Label, v)
|
528
|
+
assert_equal(:a_label, v.label)
|
527
529
|
end
|
528
530
|
|
529
531
|
def test_tag
|
@@ -676,7 +678,7 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
676
678
|
|
677
679
|
match = grammar.parse('# A comment.')
|
678
680
|
assert(match)
|
679
|
-
assert_equal('# A comment.', match
|
681
|
+
assert_equal('# A comment.', match)
|
680
682
|
end
|
681
683
|
|
682
684
|
end
|
data/test/fixed_width_test.rb
CHANGED
@@ -11,7 +11,7 @@ class FixedWidthTest < Test::Unit::TestCase
|
|
11
11
|
rule = FixedWidth.new('abc')
|
12
12
|
match = rule.match(input('abc'))
|
13
13
|
assert(match)
|
14
|
-
assert_equal('abc', match
|
14
|
+
assert_equal('abc', match)
|
15
15
|
assert_equal(3, match.length)
|
16
16
|
end
|
17
17
|
|
@@ -25,7 +25,7 @@ class FixedWidthTest < Test::Unit::TestCase
|
|
25
25
|
rule = FixedWidth.new('abc')
|
26
26
|
match = rule.match(input('abcd'))
|
27
27
|
assert(match)
|
28
|
-
assert_equal('abc', match
|
28
|
+
assert_equal('abc', match)
|
29
29
|
assert_equal(3, match.length)
|
30
30
|
end
|
31
31
|
|
data/test/grammar_test.rb
CHANGED
@@ -42,7 +42,7 @@ class GrammarTest < Test::Unit::TestCase
|
|
42
42
|
}
|
43
43
|
match = grammar.parse('abc')
|
44
44
|
assert(match)
|
45
|
-
assert_equal('abc', match
|
45
|
+
assert_equal('abc', match)
|
46
46
|
assert_equal(3, match.length)
|
47
47
|
end
|
48
48
|
|
@@ -52,7 +52,7 @@ class GrammarTest < Test::Unit::TestCase
|
|
52
52
|
}
|
53
53
|
match = grammar.parse('abc')
|
54
54
|
assert(match)
|
55
|
-
assert_equal('abc', match
|
55
|
+
assert_equal('abc', match)
|
56
56
|
assert_equal(3, match.length)
|
57
57
|
end
|
58
58
|
|
@@ -62,7 +62,7 @@ class GrammarTest < Test::Unit::TestCase
|
|
62
62
|
}
|
63
63
|
match = grammar.parse('123')
|
64
64
|
assert(match)
|
65
|
-
assert_equal('123', match
|
65
|
+
assert_equal('123', match)
|
66
66
|
assert_equal(3, match.length)
|
67
67
|
end
|
68
68
|
|
@@ -90,12 +90,12 @@ class GrammarTest < Test::Unit::TestCase
|
|
90
90
|
|
91
91
|
match = grammar.parse('a')
|
92
92
|
assert(match)
|
93
|
-
assert_equal('a', match
|
93
|
+
assert_equal('a', match)
|
94
94
|
assert_equal(1, match.length)
|
95
95
|
|
96
96
|
match = grammar.parse('1')
|
97
97
|
assert(match)
|
98
|
-
assert_equal('1', match
|
98
|
+
assert_equal('1', match)
|
99
99
|
assert_equal(1, match.length)
|
100
100
|
end
|
101
101
|
|
@@ -115,18 +115,18 @@ class GrammarTest < Test::Unit::TestCase
|
|
115
115
|
|
116
116
|
match = grammar.parse('a')
|
117
117
|
assert(match)
|
118
|
-
assert_equal('a', match
|
118
|
+
assert_equal('a', match)
|
119
119
|
assert_equal(1, match.length)
|
120
120
|
|
121
121
|
match = grammar.parse('((a))')
|
122
122
|
assert(match)
|
123
|
-
assert_equal('((a))', match
|
123
|
+
assert_equal('((a))', match)
|
124
124
|
assert_equal(5, match.length)
|
125
125
|
|
126
126
|
str = ('(' * 200) + 'a' + (')' * 200)
|
127
127
|
match = grammar.parse(str)
|
128
128
|
assert(match)
|
129
|
-
assert_equal(str, match
|
129
|
+
assert_equal(str, match)
|
130
130
|
assert_equal(str.length, match.length)
|
131
131
|
end
|
132
132
|
|
data/test/helper.rb
CHANGED
@@ -19,11 +19,7 @@ class Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
rule :num do
|
22
|
-
ext(/[0-9]/) {
|
23
|
-
def value
|
24
|
-
text.to_i
|
25
|
-
end
|
26
|
-
}
|
22
|
+
ext(/[0-9]/) { to_i }
|
27
23
|
end
|
28
24
|
|
29
25
|
rule :alphanum do
|
@@ -49,7 +45,7 @@ class Test::Unit::TestCase
|
|
49
45
|
def do_test(expr)
|
50
46
|
match = Calc.parse(expr)
|
51
47
|
assert(match)
|
52
|
-
assert_equal(expr, match
|
48
|
+
assert_equal(expr, match)
|
53
49
|
assert_equal(expr.length, match.length)
|
54
50
|
assert_equal(eval(expr), match.value)
|
55
51
|
end
|
@@ -105,5 +101,57 @@ class Test::Unit::TestCase
|
|
105
101
|
def test_complex_spaced
|
106
102
|
do_test('7 * 4 + 3.5 * (4.5 / 3)')
|
107
103
|
end
|
104
|
+
|
105
|
+
def test_complex_with_underscores
|
106
|
+
do_test('(12_000 / 3) * 2.5')
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_modulo
|
110
|
+
do_test('3 % 2 + 4')
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_exponent
|
114
|
+
do_test('2**9')
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_exponent_float
|
118
|
+
do_test('2**2.2')
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_negative_exponent
|
122
|
+
do_test('2**-3')
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_exponent_exponent
|
126
|
+
do_test('2**2**2')
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_exponent_group
|
130
|
+
do_test('2**(3+1)')
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_negative
|
134
|
+
do_test('-5')
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_double_negative
|
138
|
+
do_test('--5')
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_complement
|
142
|
+
do_test('~4')
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_double_complement
|
146
|
+
do_test('~~4')
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_mixed_unary
|
150
|
+
do_test('~-4')
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_complex_with_negatives
|
154
|
+
do_test('4 * -7 / (8.0 + 1_2)**2')
|
155
|
+
end
|
108
156
|
end
|
109
157
|
end
|
data/test/label_test.rb
CHANGED
@@ -8,7 +8,7 @@ class LabelTest < Test::Unit::TestCase
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_match
|
11
|
-
rule = Label.new('
|
11
|
+
rule = Label.new('a', 'label')
|
12
12
|
|
13
13
|
match = rule.match(input('a'))
|
14
14
|
assert(match)
|
@@ -16,10 +16,10 @@ class LabelTest < Test::Unit::TestCase
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_to_s
|
19
|
-
rule = Label.new('
|
19
|
+
rule = Label.new('a', 'label')
|
20
20
|
assert_equal('label:"a"', rule.to_s)
|
21
21
|
|
22
|
-
rule = Label.new(
|
22
|
+
rule = Label.new(Sequence.new(%w< a b >), 'label')
|
23
23
|
assert_equal('label:("a" "b")', rule.to_s)
|
24
24
|
end
|
25
25
|
|
data/test/match_test.rb
CHANGED
@@ -23,7 +23,7 @@ class MatchTest < Test::Unit::TestCase
|
|
23
23
|
|
24
24
|
def test_string
|
25
25
|
match = Match.new('hello')
|
26
|
-
assert_equal('hello', match
|
26
|
+
assert_equal('hello', match)
|
27
27
|
assert_equal(5, match.length)
|
28
28
|
end
|
29
29
|
|
@@ -31,13 +31,13 @@ class MatchTest < Test::Unit::TestCase
|
|
31
31
|
match1 = Match.new('a')
|
32
32
|
match2 = Match.new('b')
|
33
33
|
match = Match.new([match1, match2])
|
34
|
-
assert_equal('ab', match
|
34
|
+
assert_equal('ab', match)
|
35
35
|
assert_equal(2, match.length)
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_match_data
|
39
39
|
match = Match.new('hello world'.match(/^(\w+) /))
|
40
|
-
assert_equal('hello ', match
|
40
|
+
assert_equal('hello ', match)
|
41
41
|
assert_equal(6, match.length)
|
42
42
|
end
|
43
43
|
|
@@ -45,7 +45,7 @@ class MatchTest < Test::Unit::TestCase
|
|
45
45
|
match1 = Match.new('hello')
|
46
46
|
match2 = Match.new(' world'.match(/.+/))
|
47
47
|
match = Match.new([match1, match2])
|
48
|
-
assert_equal('hello world', match
|
48
|
+
assert_equal('hello world', match)
|
49
49
|
assert_equal(11, match.length)
|
50
50
|
end
|
51
51
|
|
@@ -75,7 +75,7 @@ class MatchTest < Test::Unit::TestCase
|
|
75
75
|
|
76
76
|
num = match.first(:num)
|
77
77
|
assert(num)
|
78
|
-
assert_equal('4', num
|
78
|
+
assert_equal('4', num)
|
79
79
|
assert_equal(4, num.value)
|
80
80
|
end
|
81
81
|
|
data/test/not_predicate_test.rb
CHANGED
data/test/repeat_test.rb
CHANGED
@@ -8,90 +8,90 @@ class RepeatTest < Test::Unit::TestCase
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_match_zero_or_one
|
11
|
-
rule = Repeat.new(0, 1
|
11
|
+
rule = Repeat.new('a', 0, 1)
|
12
12
|
|
13
13
|
match = rule.match(input(''))
|
14
14
|
assert(match)
|
15
|
-
assert_equal('', match
|
15
|
+
assert_equal('', match)
|
16
16
|
assert_equal(0, match.length)
|
17
17
|
|
18
18
|
match = rule.match(input('a'))
|
19
19
|
assert(match)
|
20
|
-
assert_equal('a', match
|
20
|
+
assert_equal('a', match)
|
21
21
|
assert_equal(1, match.length)
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_match_one_or_more
|
25
|
-
rule = Repeat.new(1, Infinity
|
25
|
+
rule = Repeat.new('a', 1, Infinity)
|
26
26
|
|
27
27
|
match = rule.match(input(''))
|
28
28
|
assert_equal(nil, match)
|
29
29
|
|
30
30
|
match = rule.match(input('a'))
|
31
31
|
assert(match)
|
32
|
-
assert_equal('a', match
|
32
|
+
assert_equal('a', match)
|
33
33
|
assert_equal(1, match.length)
|
34
34
|
|
35
35
|
match = rule.match(input('a' * 200))
|
36
36
|
assert(match)
|
37
|
-
assert_equal('a' * 200, match
|
37
|
+
assert_equal('a' * 200, match)
|
38
38
|
assert_equal(200, match.length)
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_match_one
|
42
|
-
rule = Repeat.new(
|
42
|
+
rule = Repeat.new('a', 1, 1)
|
43
43
|
|
44
44
|
match = rule.match(input(''))
|
45
45
|
assert_equal(nil, match)
|
46
46
|
|
47
47
|
match = rule.match(input('a'))
|
48
48
|
assert(match)
|
49
|
-
assert_equal('a', match
|
49
|
+
assert_equal('a', match)
|
50
50
|
assert_equal(1, match.length)
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_operator
|
54
|
-
rule = Repeat.new(1, 2)
|
54
|
+
rule = Repeat.new('', 1, 2)
|
55
55
|
assert_equal('1*2', rule.operator)
|
56
56
|
end
|
57
57
|
|
58
58
|
def test_operator_empty
|
59
|
-
rule = Repeat.new(0, 0)
|
59
|
+
rule = Repeat.new('', 0, 0)
|
60
60
|
assert_equal('', rule.operator)
|
61
61
|
end
|
62
62
|
|
63
63
|
def test_operator_asterisk
|
64
|
-
rule = Repeat.new(0, Infinity)
|
64
|
+
rule = Repeat.new('', 0, Infinity)
|
65
65
|
assert_equal('*', rule.operator)
|
66
66
|
end
|
67
67
|
|
68
68
|
def test_operator_question_mark
|
69
|
-
rule = Repeat.new(0, 1)
|
69
|
+
rule = Repeat.new('', 0, 1)
|
70
70
|
assert_equal('?', rule.operator)
|
71
71
|
end
|
72
72
|
|
73
73
|
def test_operator_plus
|
74
|
-
rule = Repeat.new(1, Infinity)
|
74
|
+
rule = Repeat.new('', 1, Infinity)
|
75
75
|
assert_equal('+', rule.operator)
|
76
76
|
end
|
77
77
|
|
78
78
|
def test_to_s
|
79
|
-
rule = Repeat.new(1, 2
|
79
|
+
rule = Repeat.new(/a/, 1, 2)
|
80
80
|
assert_equal('/a/1*2', rule.to_s)
|
81
81
|
end
|
82
82
|
|
83
83
|
def test_to_s_asterisk
|
84
|
-
rule = Repeat.new(0, Infinity
|
84
|
+
rule = Repeat.new('a', 0, Infinity)
|
85
85
|
assert_equal('"a"*', rule.to_s)
|
86
86
|
end
|
87
87
|
|
88
88
|
def test_to_s_question_mark
|
89
|
-
rule = Repeat.new(0, 1
|
89
|
+
rule = Repeat.new('a', 0, 1)
|
90
90
|
assert_equal('"a"?', rule.to_s)
|
91
91
|
end
|
92
92
|
|
93
93
|
def test_to_s_plus
|
94
|
-
rule = Repeat.new(1, Infinity
|
94
|
+
rule = Repeat.new('a', 1, Infinity)
|
95
95
|
assert_equal('"a"+', rule.to_s)
|
96
96
|
end
|
97
97
|
|