citrus 2.2.2 → 2.3.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 +160 -98
- data/doc/background.markdown +16 -17
- data/doc/example.markdown +86 -46
- data/doc/syntax.markdown +59 -36
- data/examples/calc.citrus +9 -3
- data/examples/calc.rb +9 -3
- data/examples/ip.rb +2 -0
- data/lib/citrus.rb +576 -473
- data/lib/citrus/file.rb +80 -71
- data/test/alias_test.rb +8 -2
- data/test/and_predicate_test.rb +13 -2
- data/test/but_predicate_test.rb +9 -3
- data/test/calc_file_test.rb +9 -4
- data/test/calc_test.rb +4 -4
- data/test/choice_test.rb +11 -5
- data/test/extension_test.rb +2 -12
- data/test/file_test.rb +215 -175
- data/test/grammar_test.rb +1 -1
- data/test/helper.rb +2 -2
- data/test/input_test.rb +44 -48
- data/test/label_test.rb +14 -17
- data/test/match_test.rb +21 -63
- data/test/not_predicate_test.rb +13 -2
- data/test/repeat_test.rb +20 -20
- data/test/sequence_test.rb +22 -8
- data/test/string_terminal_test.rb +10 -5
- data/test/super_test.rb +19 -16
- data/test/terminal_test.rb +7 -2
- metadata +21 -12
- data/benchmark/after.dat +0 -192
- data/benchmark/before.dat +0 -192
- data/test/_files/grammar3.citrus +0 -112
data/test/grammar_test.rb
CHANGED
@@ -63,7 +63,7 @@ class GrammarTest < Test::Unit::TestCase
|
|
63
63
|
grammar = Grammar.new {
|
64
64
|
rule(:num) { all(1, 2, 3) }
|
65
65
|
}
|
66
|
-
match = grammar.parse('1234')
|
66
|
+
match = grammar.parse('1234', :consume => false)
|
67
67
|
assert(match)
|
68
68
|
assert_equal('123', match)
|
69
69
|
assert_equal(3, match.length)
|
data/test/helper.rb
CHANGED
@@ -48,8 +48,8 @@ class Test::Unit::TestCase
|
|
48
48
|
module CalcTestMethods
|
49
49
|
# A helper method that tests the successful parsing and evaluation of the
|
50
50
|
# given mathematical expression.
|
51
|
-
def do_test(expr)
|
52
|
-
match =
|
51
|
+
def do_test(expr, grammar)
|
52
|
+
match = grammar.parse(expr)
|
53
53
|
assert(match)
|
54
54
|
assert_equal(expr, match)
|
55
55
|
assert_equal(expr.length, match.length)
|
data/test/input_test.rb
CHANGED
@@ -2,9 +2,8 @@ require File.expand_path('../helper', __FILE__)
|
|
2
2
|
|
3
3
|
class InputTest < Test::Unit::TestCase
|
4
4
|
def test_memoized?
|
5
|
-
|
6
|
-
|
7
|
-
assert(input.memoized?)
|
5
|
+
assert !Input.new('').memoized?
|
6
|
+
assert MemoizingInput.new('').memoized?
|
8
7
|
end
|
9
8
|
|
10
9
|
def test_offsets_new
|
@@ -25,31 +24,31 @@ class InputTest < Test::Unit::TestCase
|
|
25
24
|
end
|
26
25
|
|
27
26
|
def test_events
|
28
|
-
a = Rule.
|
29
|
-
b = Rule.
|
30
|
-
c = Rule.
|
31
|
-
s = Rule.
|
27
|
+
a = Rule.for('a')
|
28
|
+
b = Rule.for('b')
|
29
|
+
c = Rule.for('c')
|
30
|
+
s = Rule.for([ a, b, c ])
|
32
31
|
r = Repeat.new(s, 0, Infinity)
|
33
32
|
|
34
33
|
input = Input.new("abcabcabc")
|
35
34
|
events = input.exec(r)
|
36
35
|
|
37
36
|
expected_events = [
|
38
|
-
r
|
39
|
-
s
|
40
|
-
a
|
41
|
-
b
|
42
|
-
c
|
37
|
+
r,
|
38
|
+
s,
|
39
|
+
a, CLOSE, 1,
|
40
|
+
b, CLOSE, 1,
|
41
|
+
c, CLOSE, 1,
|
43
42
|
CLOSE, 3,
|
44
|
-
s
|
45
|
-
a
|
46
|
-
b
|
47
|
-
c
|
43
|
+
s,
|
44
|
+
a, CLOSE, 1,
|
45
|
+
b, CLOSE, 1,
|
46
|
+
c, CLOSE, 1,
|
48
47
|
CLOSE, 3,
|
49
|
-
s
|
50
|
-
a
|
51
|
-
b
|
52
|
-
c
|
48
|
+
s,
|
49
|
+
a, CLOSE, 1,
|
50
|
+
b, CLOSE, 1,
|
51
|
+
c, CLOSE, 1,
|
53
52
|
CLOSE, 3,
|
54
53
|
CLOSE, 9
|
55
54
|
]
|
@@ -58,42 +57,42 @@ class InputTest < Test::Unit::TestCase
|
|
58
57
|
end
|
59
58
|
|
60
59
|
def test_events2
|
61
|
-
a = Rule.
|
62
|
-
b = Rule.
|
60
|
+
a = Rule.for('a')
|
61
|
+
b = Rule.for('b')
|
63
62
|
c = Choice.new([ a, b ])
|
64
63
|
r = Repeat.new(c, 0, Infinity)
|
65
|
-
s = Rule.
|
64
|
+
s = Rule.for([ a, r ])
|
66
65
|
|
67
66
|
input = Input.new('abbababba')
|
68
67
|
events = input.exec(s)
|
69
68
|
|
70
69
|
expected_events = [
|
71
|
-
s
|
72
|
-
a
|
73
|
-
r
|
74
|
-
c
|
75
|
-
b
|
70
|
+
s,
|
71
|
+
a, CLOSE, 1,
|
72
|
+
r,
|
73
|
+
c,
|
74
|
+
b, CLOSE, 1,
|
76
75
|
CLOSE, 1,
|
77
|
-
c
|
78
|
-
b
|
76
|
+
c,
|
77
|
+
b, CLOSE, 1,
|
79
78
|
CLOSE, 1,
|
80
|
-
c
|
81
|
-
a
|
79
|
+
c,
|
80
|
+
a, CLOSE, 1,
|
82
81
|
CLOSE, 1,
|
83
|
-
c
|
84
|
-
b
|
82
|
+
c,
|
83
|
+
b, CLOSE, 1,
|
85
84
|
CLOSE, 1,
|
86
|
-
c
|
87
|
-
a
|
85
|
+
c,
|
86
|
+
a, CLOSE, 1,
|
88
87
|
CLOSE, 1,
|
89
|
-
c
|
90
|
-
b
|
88
|
+
c,
|
89
|
+
b, CLOSE, 1,
|
91
90
|
CLOSE, 1,
|
92
|
-
c
|
93
|
-
b
|
91
|
+
c,
|
92
|
+
b, CLOSE, 1,
|
94
93
|
CLOSE, 1,
|
95
|
-
c
|
96
|
-
a
|
94
|
+
c,
|
95
|
+
a, CLOSE, 1,
|
97
96
|
CLOSE, 1,
|
98
97
|
CLOSE, 8,
|
99
98
|
CLOSE, 9
|
@@ -121,22 +120,19 @@ class InputTest < Test::Unit::TestCase
|
|
121
120
|
end
|
122
121
|
|
123
122
|
def test_cache_hits1
|
124
|
-
input =
|
125
|
-
input.memoize!
|
123
|
+
input = MemoizingInput.new('a')
|
126
124
|
input.exec(LetterA.rule(:top))
|
127
125
|
assert_equal(3, input.cache_hits)
|
128
126
|
end
|
129
127
|
|
130
128
|
def test_cache_hits2
|
131
|
-
input =
|
132
|
-
input.memoize!
|
129
|
+
input = MemoizingInput.new('aa')
|
133
130
|
input.exec(LetterA.rule(:top))
|
134
131
|
assert_equal(2, input.cache_hits)
|
135
132
|
end
|
136
133
|
|
137
134
|
def test_cache_hits3
|
138
|
-
input =
|
139
|
-
input.memoize!
|
135
|
+
input = MemoizingInput.new('aaa')
|
140
136
|
input.exec(LetterA.rule(:top))
|
141
137
|
assert_equal(0, input.cache_hits)
|
142
138
|
end
|
data/test/label_test.rb
CHANGED
@@ -1,26 +1,23 @@
|
|
1
1
|
require File.expand_path('../helper', __FILE__)
|
2
2
|
|
3
3
|
class LabelTest < Test::Unit::TestCase
|
4
|
-
def
|
5
|
-
rule =
|
6
|
-
|
4
|
+
def test_to_s
|
5
|
+
rule = Rule.for('a')
|
6
|
+
rule.label = 'a_label'
|
7
|
+
assert_equal('a_label:"a"', rule.to_s)
|
7
8
|
end
|
8
9
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
label.name = 'label'
|
14
|
-
match = label.parse('abc')
|
15
|
-
assert(match)
|
16
|
-
assert_equal([:abc, :a_label, :label], match.names)
|
10
|
+
def test_to_s_sequence
|
11
|
+
rule = Sequence.new(%w< a b >)
|
12
|
+
rule.label = 's_label'
|
13
|
+
assert_equal('s_label:("a" "b")', rule.to_s)
|
17
14
|
end
|
18
15
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
rule =
|
24
|
-
assert_equal('
|
16
|
+
def test_to_s_embedded
|
17
|
+
a = Rule.for('a')
|
18
|
+
a.label = 'a_label'
|
19
|
+
rule = Sequence.new([ a, 'b' ])
|
20
|
+
rule.label = 's_label'
|
21
|
+
assert_equal('s_label:(a_label:"a" "b")', rule.to_s)
|
25
22
|
end
|
26
23
|
end
|
data/test/match_test.rb
CHANGED
@@ -20,71 +20,30 @@ class MatchTest < Test::Unit::TestCase
|
|
20
20
|
assert_equal(false, match2 == match1)
|
21
21
|
end
|
22
22
|
|
23
|
-
def test_names
|
24
|
-
a = Rule.new('a')
|
25
|
-
a.name = 'a'
|
26
|
-
b = Rule.new('b')
|
27
|
-
b.name = 'b'
|
28
|
-
c = Rule.new('c')
|
29
|
-
c.name = 'c'
|
30
|
-
s = Rule.new([ a, b, c ])
|
31
|
-
s.name = 's'
|
32
|
-
r = Repeat.new(s, 0, Infinity)
|
33
|
-
r.name = 'r'
|
34
|
-
|
35
|
-
events = [
|
36
|
-
r.id,
|
37
|
-
s.id,
|
38
|
-
a.id, CLOSE, 1,
|
39
|
-
b.id, CLOSE, 1,
|
40
|
-
c.id, CLOSE, 1,
|
41
|
-
CLOSE, 3,
|
42
|
-
s.id,
|
43
|
-
a.id, CLOSE, 1,
|
44
|
-
b.id, CLOSE, 1,
|
45
|
-
c.id, CLOSE, 1,
|
46
|
-
CLOSE, 3,
|
47
|
-
s.id,
|
48
|
-
a.id, CLOSE, 1,
|
49
|
-
b.id, CLOSE, 1,
|
50
|
-
c.id, CLOSE, 1,
|
51
|
-
CLOSE, 3,
|
52
|
-
CLOSE, 9
|
53
|
-
]
|
54
|
-
|
55
|
-
match = Match.new("abcabcabc", events)
|
56
|
-
assert(match.names)
|
57
|
-
assert_equal([:r], match.names)
|
58
|
-
|
59
|
-
match.matches.each do |m|
|
60
|
-
assert_equal([:s], m.names)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
23
|
def test_matches
|
65
|
-
a = Rule.
|
66
|
-
b = Rule.
|
67
|
-
c = Rule.
|
68
|
-
s = Rule.
|
24
|
+
a = Rule.for('a')
|
25
|
+
b = Rule.for('b')
|
26
|
+
c = Rule.for('c')
|
27
|
+
s = Rule.for([ a, b, c ])
|
69
28
|
s.name = 's'
|
70
29
|
r = Repeat.new(s, 0, Infinity)
|
71
30
|
|
72
31
|
events = [
|
73
|
-
r
|
74
|
-
s
|
75
|
-
a
|
76
|
-
b
|
77
|
-
c
|
32
|
+
r,
|
33
|
+
s,
|
34
|
+
a, CLOSE, 1,
|
35
|
+
b, CLOSE, 1,
|
36
|
+
c, CLOSE, 1,
|
78
37
|
CLOSE, 3,
|
79
|
-
s
|
80
|
-
a
|
81
|
-
b
|
82
|
-
c
|
38
|
+
s,
|
39
|
+
a, CLOSE, 1,
|
40
|
+
b, CLOSE, 1,
|
41
|
+
c, CLOSE, 1,
|
83
42
|
CLOSE, 3,
|
84
|
-
s
|
85
|
-
a
|
86
|
-
b
|
87
|
-
c
|
43
|
+
s,
|
44
|
+
a, CLOSE, 1,
|
45
|
+
b, CLOSE, 1,
|
46
|
+
c, CLOSE, 1,
|
88
47
|
CLOSE, 3,
|
89
48
|
CLOSE, 9
|
90
49
|
]
|
@@ -94,16 +53,15 @@ class MatchTest < Test::Unit::TestCase
|
|
94
53
|
assert_equal(3, match.matches.length)
|
95
54
|
|
96
55
|
sub_events = [
|
97
|
-
s
|
98
|
-
a
|
99
|
-
b
|
100
|
-
c
|
56
|
+
s,
|
57
|
+
a, CLOSE, 1,
|
58
|
+
b, CLOSE, 1,
|
59
|
+
c, CLOSE, 1,
|
101
60
|
CLOSE, 3
|
102
61
|
]
|
103
62
|
|
104
63
|
match.matches.each do |m|
|
105
64
|
assert_equal(sub_events, m.events)
|
106
|
-
assert_equal(:s, m.name)
|
107
65
|
assert_equal("abc", m)
|
108
66
|
assert(m.matches)
|
109
67
|
assert_equal(3, m.matches.length)
|
data/test/not_predicate_test.rb
CHANGED
@@ -9,7 +9,7 @@ class NotPredicateTest < Test::Unit::TestCase
|
|
9
9
|
def test_exec
|
10
10
|
rule = NotPredicate.new('abc')
|
11
11
|
events = rule.exec(Input.new('def'))
|
12
|
-
assert_equal([rule
|
12
|
+
assert_equal([rule, CLOSE, 0], events)
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_exec_miss
|
@@ -19,7 +19,12 @@ class NotPredicateTest < Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_consumption
|
22
|
-
rule = NotPredicate.new('
|
22
|
+
rule = NotPredicate.new(Sequence.new(['a', 'b', 'c']))
|
23
|
+
|
24
|
+
input = Input.new('abc')
|
25
|
+
events = rule.exec(input)
|
26
|
+
assert_equal(0, input.pos)
|
27
|
+
|
23
28
|
input = Input.new('def')
|
24
29
|
events = rule.exec(input)
|
25
30
|
assert_equal(0, input.pos)
|
@@ -29,4 +34,10 @@ class NotPredicateTest < Test::Unit::TestCase
|
|
29
34
|
rule = NotPredicate.new('a')
|
30
35
|
assert_equal('!"a"', rule.to_s)
|
31
36
|
end
|
37
|
+
|
38
|
+
def test_to_s_with_label
|
39
|
+
rule = NotPredicate.new('a')
|
40
|
+
rule.label = 'a_label'
|
41
|
+
assert_equal('a_label:!"a"', rule.to_s)
|
42
|
+
end
|
32
43
|
end
|
data/test/repeat_test.rb
CHANGED
@@ -7,34 +7,34 @@ class RepeatTest < Test::Unit::TestCase
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_exec_zero_or_one
|
10
|
-
abc = Rule.
|
10
|
+
abc = Rule.for('abc')
|
11
11
|
rule = Repeat.new(abc, 0, 1)
|
12
12
|
|
13
13
|
events = rule.exec(Input.new(''))
|
14
|
-
assert_equal([rule
|
14
|
+
assert_equal([rule, CLOSE, 0], events)
|
15
15
|
|
16
16
|
events = rule.exec(Input.new('abc'))
|
17
|
-
assert_equal([rule
|
17
|
+
assert_equal([rule, abc, CLOSE, 3, CLOSE, 3], events)
|
18
18
|
|
19
19
|
events = rule.exec(Input.new('abc' * 3))
|
20
|
-
assert_equal([rule
|
20
|
+
assert_equal([rule, abc, CLOSE, 3, CLOSE, 3], events)
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_exec_zero_or_more
|
24
|
-
abc = Rule.
|
24
|
+
abc = Rule.for('abc')
|
25
25
|
rule = Repeat.new(abc, 0, Infinity)
|
26
26
|
|
27
27
|
events = rule.exec(Input.new(''))
|
28
|
-
assert_equal([rule
|
28
|
+
assert_equal([rule, CLOSE, 0], events)
|
29
29
|
|
30
30
|
events = rule.exec(Input.new('abc'))
|
31
|
-
assert_equal([rule
|
31
|
+
assert_equal([rule, abc, CLOSE, 3, CLOSE, 3], events)
|
32
32
|
|
33
33
|
expected_events = [
|
34
|
-
rule
|
35
|
-
abc
|
36
|
-
abc
|
37
|
-
abc
|
34
|
+
rule,
|
35
|
+
abc, CLOSE, 3,
|
36
|
+
abc, CLOSE, 3,
|
37
|
+
abc, CLOSE, 3,
|
38
38
|
CLOSE, 9
|
39
39
|
]
|
40
40
|
|
@@ -43,20 +43,20 @@ class RepeatTest < Test::Unit::TestCase
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def test_exec_one_or_more
|
46
|
-
abc = Rule.
|
46
|
+
abc = Rule.for('abc')
|
47
47
|
rule = Repeat.new(abc, 1, Infinity)
|
48
48
|
|
49
49
|
events = rule.exec(Input.new(''))
|
50
50
|
assert_equal([], events)
|
51
51
|
|
52
52
|
events = rule.exec(Input.new('abc'))
|
53
|
-
assert_equal([rule
|
53
|
+
assert_equal([rule, abc, CLOSE, 3, CLOSE, 3], events)
|
54
54
|
|
55
55
|
expected_events = [
|
56
|
-
rule
|
57
|
-
abc
|
58
|
-
abc
|
59
|
-
abc
|
56
|
+
rule,
|
57
|
+
abc, CLOSE, 3,
|
58
|
+
abc, CLOSE, 3,
|
59
|
+
abc, CLOSE, 3,
|
60
60
|
CLOSE, 9
|
61
61
|
]
|
62
62
|
|
@@ -65,17 +65,17 @@ class RepeatTest < Test::Unit::TestCase
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def test_exec_one
|
68
|
-
abc = Rule.
|
68
|
+
abc = Rule.for('abc')
|
69
69
|
rule = Repeat.new(abc, 1, 1)
|
70
70
|
|
71
71
|
events = rule.exec(Input.new(''))
|
72
72
|
assert_equal([], events)
|
73
73
|
|
74
74
|
events = rule.exec(Input.new('abc'))
|
75
|
-
assert_equal([rule
|
75
|
+
assert_equal([rule, abc, CLOSE, 3, CLOSE, 3], events)
|
76
76
|
|
77
77
|
events = rule.exec(Input.new('abc' * 3))
|
78
|
-
assert_equal([rule
|
78
|
+
assert_equal([rule, abc, CLOSE, 3, CLOSE, 3], events)
|
79
79
|
end
|
80
80
|
|
81
81
|
def test_operator
|