rly 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/assets/ply_dump.erb +15 -0
- data/lib/rly.rb +2 -0
- data/lib/rly/lex.rb +54 -25
- data/lib/rly/lex_token.rb +8 -0
- data/lib/rly/parse/grammar.rb +211 -0
- data/lib/rly/parse/lr_item.rb +32 -0
- data/lib/rly/parse/lr_table.rb +529 -0
- data/lib/rly/parse/ply_dump.rb +52 -0
- data/lib/rly/parse/production.rb +38 -0
- data/lib/rly/parse/rule_parser.rb +68 -0
- data/lib/rly/parse/yacc_production.rb +11 -0
- data/lib/rly/parse/yacc_symbol.rb +6 -0
- data/lib/rly/version.rb +2 -1
- data/lib/rly/yacc.rb +355 -0
- data/spec/lex/{lexer_spec.rb → lex_spec.rb} +45 -24
- data/spec/parse/calc_spec.rb +95 -0
- data/spec/parse/grammar_spec.rb +239 -0
- data/spec/parse/lr_table_spec.rb +212 -0
- data/spec/parse/production_spec.rb +18 -0
- data/spec/parse/rule_parser_spec.rb +20 -0
- data/spec/parse/yacc_spec.rb +57 -0
- data/spec/spec_helper.rb +5 -0
- metadata +26 -4
@@ -0,0 +1,212 @@
|
|
1
|
+
require "rly"
|
2
|
+
require "rly/parse/grammar"
|
3
|
+
require "rly/parse/lr_table"
|
4
|
+
|
5
|
+
describe Rly::LRTable do
|
6
|
+
before :each do
|
7
|
+
g = Rly::Grammar.new([:NUMBER])
|
8
|
+
|
9
|
+
g.set_precedence('+', :left, 1)
|
10
|
+
g.set_precedence('-', :left, 1)
|
11
|
+
|
12
|
+
g.add_production(:statement, [:expression])
|
13
|
+
g.add_production(:expression, [:expression, '+', :expression])
|
14
|
+
g.add_production(:expression, [:expression, '-', :expression])
|
15
|
+
g.add_production(:expression, [:NUMBER])
|
16
|
+
|
17
|
+
g.set_start
|
18
|
+
|
19
|
+
g.build_lritems
|
20
|
+
|
21
|
+
@t = Rly::LRTable.new(g)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "computes the LR(0) closure operation on I, where I is a set of LR(0) items" do
|
25
|
+
@t.instance_eval do
|
26
|
+
lr0_c = lr0_closure([@grammar.productions[0].lr_next])
|
27
|
+
|
28
|
+
lr0_c.length.should == 5
|
29
|
+
lr0_c.length.times do |i|
|
30
|
+
lr0_c[i].should == @grammar.productions[i].lr_next
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "computes the LR(0) goto function goto(I,X) where I is a set of LR(0) items and X is a grammar symbol" do
|
36
|
+
@t.instance_eval do
|
37
|
+
lr0_c = lr0_closure([@grammar.productions[0].lr_next])
|
38
|
+
|
39
|
+
lr0_g = lr0_goto(lr0_c, :statement)
|
40
|
+
|
41
|
+
lr0_g.length.should == 1
|
42
|
+
lr0_g[0].name.should == :"S'"
|
43
|
+
lr0_g[0].prod.should == [:statement, :'.']
|
44
|
+
|
45
|
+
lr0_g = lr0_goto(lr0_c, :expression)
|
46
|
+
|
47
|
+
lr0_g.length.should == 3
|
48
|
+
lr0_g[0].name.should == :statement
|
49
|
+
lr0_g[0].prod.should == [:expression, :'.']
|
50
|
+
lr0_g[1].name.should == :expression
|
51
|
+
lr0_g[1].prod.should == [:expression, :'.', '+', :expression]
|
52
|
+
lr0_g[2].name.should == :expression
|
53
|
+
lr0_g[2].prod.should == [:expression, :'.', '-', :expression]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "computes the LR(0) sets of item function" do
|
58
|
+
@t.instance_eval do
|
59
|
+
lr0_i = lr0_items
|
60
|
+
|
61
|
+
reflist = Set.new(["S' -> . statement|statement -> . expression|expression -> . expression + expression|expression -> . expression - expression|expression -> . NUMBER",
|
62
|
+
"S' -> statement .",
|
63
|
+
"statement -> expression .|expression -> expression . + expression|expression -> expression . - expression",
|
64
|
+
"expression -> NUMBER .",
|
65
|
+
"expression -> expression + . expression|expression -> . expression + expression|expression -> . expression - expression|expression -> . NUMBER",
|
66
|
+
"expression -> expression - . expression|expression -> . expression + expression|expression -> . expression - expression|expression -> . NUMBER",
|
67
|
+
"expression -> expression + expression .|expression -> expression . + expression|expression -> expression . - expression",
|
68
|
+
"expression -> expression - expression .|expression -> expression . + expression|expression -> expression . - expression"])
|
69
|
+
|
70
|
+
lr0_i.length.should == reflist.length
|
71
|
+
Set.new(lr0_i.map { |a| a.map { |k| k.to_s } .join('|') }).inspect .should == reflist.inspect
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "creates a dictionary containing all of the non-terminals that might produce an empty production." do
|
76
|
+
# TODO: write a better spec
|
77
|
+
@t.instance_eval do
|
78
|
+
compute_nullable_nonterminals.should == {}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "finds all of the non-terminal transitions" do
|
83
|
+
@t.instance_eval do
|
84
|
+
find_nonterminal_transitions(lr0_items).should == [[0, :statement], [0, :expression], [4, :expression], [5, :expression]]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "computes the DR(p,A) relationships for non-terminal transitions" do
|
89
|
+
@t.instance_eval do
|
90
|
+
lr0_i = lr0_items
|
91
|
+
nullable = compute_nullable_nonterminals
|
92
|
+
trans = find_nonterminal_transitions(lr0_i)
|
93
|
+
|
94
|
+
dr_relation(lr0_i, trans[0], nullable).should == [:'$end']
|
95
|
+
dr_relation(lr0_i, trans[1], nullable).should == ['+', '-']
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "computes the READS() relation (p,A) READS (t,C)" do
|
100
|
+
# TODO: write a better spec
|
101
|
+
@t.instance_eval do
|
102
|
+
lr0_i = lr0_items
|
103
|
+
nullable = compute_nullable_nonterminals
|
104
|
+
trans = find_nonterminal_transitions(lr0_i)
|
105
|
+
|
106
|
+
reads_relation(lr0_i, trans[0], nullable).should == []
|
107
|
+
reads_relation(lr0_i, trans[1], nullable).should == []
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "computes the read sets given a set of LR(0) items" do
|
112
|
+
@t.instance_eval do
|
113
|
+
lr0_i = lr0_items
|
114
|
+
nullable = compute_nullable_nonterminals
|
115
|
+
trans = find_nonterminal_transitions(lr0_i)
|
116
|
+
|
117
|
+
compute_read_sets(lr0_i, trans, nullable).should == {
|
118
|
+
[0, :statement] => [:'$end'],
|
119
|
+
[5, :expression] => ['+', '-'],
|
120
|
+
[4, :expression] => ['+', '-'],
|
121
|
+
[0, :expression] => ['+', '-']
|
122
|
+
}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it "determines the lookback and includes relations" do
|
127
|
+
@t.instance_eval do
|
128
|
+
lr0_i = lr0_items
|
129
|
+
nullable = compute_nullable_nonterminals
|
130
|
+
trans = find_nonterminal_transitions(lr0_i)
|
131
|
+
|
132
|
+
lookd, included = compute_lookback_includes(lr0_i, trans, nullable)
|
133
|
+
|
134
|
+
included.should == {
|
135
|
+
[5, :expression] => [ [0, :expression], [4, :expression], [5, :expression], [5, :expression] ],
|
136
|
+
[4, :expression] => [ [0, :expression], [4, :expression], [4, :expression], [5, :expression] ],
|
137
|
+
[0, :expression] => [ [0, :statement] ]
|
138
|
+
}
|
139
|
+
|
140
|
+
lookd = lookd.each_with_object({}) { |(k, v), h| h[k] = v.map { |n,i| [n, i.to_s] } }
|
141
|
+
|
142
|
+
# NOTE: this one goes not map 1-1 to pry as we have differencies in lr0_items order. Looks valid though.
|
143
|
+
expected = {
|
144
|
+
[0, :statement] => [ [2, "statement -> expression ."] ],
|
145
|
+
[0, :expression]=> [
|
146
|
+
[6, "expression -> expression + expression ."],
|
147
|
+
[6, "expression -> expression . + expression"],
|
148
|
+
[6, "expression -> expression . - expression"],
|
149
|
+
[7, "expression -> expression - expression ."],
|
150
|
+
[7, "expression -> expression . + expression"],
|
151
|
+
[7, "expression -> expression . - expression"],
|
152
|
+
[3, "expression -> NUMBER ."]
|
153
|
+
],
|
154
|
+
[4, :expression] => [
|
155
|
+
[6, "expression -> expression + expression ."],
|
156
|
+
[6, "expression -> expression . + expression"],
|
157
|
+
[6, "expression -> expression . - expression"],
|
158
|
+
[7, "expression -> expression - expression ."],
|
159
|
+
[7, "expression -> expression . + expression"],
|
160
|
+
[7, "expression -> expression . - expression"],
|
161
|
+
[3, "expression -> NUMBER ."]
|
162
|
+
],
|
163
|
+
[5, :expression] => [
|
164
|
+
[6, "expression -> expression + expression ."],
|
165
|
+
[6, "expression -> expression . + expression"],
|
166
|
+
[6, "expression -> expression . - expression"],
|
167
|
+
[7, "expression -> expression - expression ."],
|
168
|
+
[7, "expression -> expression . + expression"],
|
169
|
+
[7, "expression -> expression . - expression"],
|
170
|
+
[3, "expression -> NUMBER ."]
|
171
|
+
]}
|
172
|
+
|
173
|
+
lookd.should == expected
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it "computes the follow sets given a set of LR(0) items, a set of non-terminal transitions, a readset, and an include set" do
|
179
|
+
@t.instance_eval do
|
180
|
+
lr0_i = lr0_items
|
181
|
+
nullable = compute_nullable_nonterminals
|
182
|
+
trans = find_nonterminal_transitions(lr0_i)
|
183
|
+
readsets = compute_read_sets(lr0_i, trans, nullable)
|
184
|
+
lookd, included = compute_lookback_includes(lr0_i, trans, nullable)
|
185
|
+
|
186
|
+
compute_follow_sets(trans, readsets, included).should == {
|
187
|
+
[0, :statement] => [:'$end'],
|
188
|
+
[5, :expression] => ['+', '-', :'$end'],
|
189
|
+
[4, :expression] => ['+', '-', :'$end'],
|
190
|
+
[0, :expression] => ['+', '-', :'$end']
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
it "attaches the lookahead symbols to grammar rules" do
|
196
|
+
pending "verify that values in LRItem#lookaheads are meaningful"
|
197
|
+
@t.instance_eval do
|
198
|
+
lr0_i = lr0_items
|
199
|
+
nullable = compute_nullable_nonterminals
|
200
|
+
trans = find_nonterminal_transitions(lr0_i)
|
201
|
+
readsets = compute_read_sets(lr0_i, trans, nullable)
|
202
|
+
lookd, included = compute_lookback_includes(lr0_i, trans, nullable)
|
203
|
+
followsets = compute_follow_sets(trans, readsets, included)
|
204
|
+
|
205
|
+
add_lookaheads(lookd, followsets)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "parses the table" do
|
210
|
+
expect { @t.parse_table } .not_to raise_error
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "rly/parse/production"
|
2
|
+
|
3
|
+
describe Rly::Production do
|
4
|
+
it "has a length same as length of its symbols" do
|
5
|
+
p = Rly::Production.new(1, 'test', ['test', '+', 'test'])
|
6
|
+
p.length.should == 3
|
7
|
+
end
|
8
|
+
|
9
|
+
it "converts to_s as source -> symbols" do
|
10
|
+
p = Rly::Production.new(1, 'test', ['test', '+', 'test'])
|
11
|
+
p.to_s.should == 'test -> test + test'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "builds a list of unique symbols" do
|
15
|
+
p = Rly::Production.new(1, 'test', ['test', '+', 'test'])
|
16
|
+
p.usyms.should == ['test', '+']
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rly"
|
2
|
+
require "rly/parse/rule_parser"
|
3
|
+
|
4
|
+
describe Rly::RuleParser do
|
5
|
+
it "parses a simple rule string" do
|
6
|
+
s = 'expression : expression "+" expression
|
7
|
+
| expression "-" expression
|
8
|
+
| expression "*" expression
|
9
|
+
| expression "/" expression'
|
10
|
+
p = Rly::RuleParser.new
|
11
|
+
|
12
|
+
productions = p.parse(s)
|
13
|
+
|
14
|
+
productions.length.should == 4
|
15
|
+
productions[0].should == [:expression, [:expression, '+', :expression]]
|
16
|
+
productions[1].should == [:expression, [:expression, '-', :expression]]
|
17
|
+
productions[2].should == [:expression, [:expression, '*', :expression]]
|
18
|
+
productions[3].should == [:expression, [:expression, '/', :expression]]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "rly"
|
2
|
+
require "rly/parse/grammar"
|
3
|
+
|
4
|
+
describe Rly::Yacc do
|
5
|
+
it "acceps a set of rules" do
|
6
|
+
expect {
|
7
|
+
Class.new(Rly::Yacc) do
|
8
|
+
rule 'statement : expression' do |e|
|
9
|
+
@val = e
|
10
|
+
end
|
11
|
+
end
|
12
|
+
} .not_to raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "accepts an instance of lexer as an argument" do
|
16
|
+
testParser = Class.new(Rly::Yacc) do
|
17
|
+
rule 'statement : VALUE' do |v|
|
18
|
+
@val = v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
testLexer = Class.new(Rly::Lex) do
|
23
|
+
token :VALUE, /[a-z]+/
|
24
|
+
end
|
25
|
+
m = testLexer.new
|
26
|
+
|
27
|
+
expect {
|
28
|
+
p = testParser.new(m)
|
29
|
+
p.lex.should == m
|
30
|
+
} .not_to raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it "can use built in lexer if one is defined" do
|
34
|
+
testParser = Class.new(Rly::Yacc) do
|
35
|
+
lexer do
|
36
|
+
token :VALUE, /[a-z]+/
|
37
|
+
end
|
38
|
+
|
39
|
+
rule 'statement : VALUE' do |v|
|
40
|
+
@val = v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
p = testParser.new
|
45
|
+
p.lex.should be_kind_of(Rly::Lex)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "raises error if no lexer is built in and no given" do
|
49
|
+
testParser = Class.new(Rly::Yacc) do
|
50
|
+
rule 'statement : VALUE' do |v|
|
51
|
+
@val = v
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
expect { testParser.new } .to raise_error(ArgumentError)
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -41,12 +41,28 @@ files:
|
|
41
41
|
- LICENSE.txt
|
42
42
|
- README.md
|
43
43
|
- Rakefile
|
44
|
+
- assets/ply_dump.erb
|
44
45
|
- lib/rly.rb
|
45
46
|
- lib/rly/lex.rb
|
46
47
|
- lib/rly/lex_token.rb
|
48
|
+
- lib/rly/parse/grammar.rb
|
49
|
+
- lib/rly/parse/lr_item.rb
|
50
|
+
- lib/rly/parse/lr_table.rb
|
51
|
+
- lib/rly/parse/ply_dump.rb
|
52
|
+
- lib/rly/parse/production.rb
|
53
|
+
- lib/rly/parse/rule_parser.rb
|
54
|
+
- lib/rly/parse/yacc_production.rb
|
55
|
+
- lib/rly/parse/yacc_symbol.rb
|
47
56
|
- lib/rly/version.rb
|
57
|
+
- lib/rly/yacc.rb
|
48
58
|
- rly.gemspec
|
49
|
-
- spec/lex/
|
59
|
+
- spec/lex/lex_spec.rb
|
60
|
+
- spec/parse/calc_spec.rb
|
61
|
+
- spec/parse/grammar_spec.rb
|
62
|
+
- spec/parse/lr_table_spec.rb
|
63
|
+
- spec/parse/production_spec.rb
|
64
|
+
- spec/parse/rule_parser_spec.rb
|
65
|
+
- spec/parse/yacc_spec.rb
|
50
66
|
- spec/spec_helper.rb
|
51
67
|
homepage: ''
|
52
68
|
licenses: []
|
@@ -73,6 +89,12 @@ signing_key:
|
|
73
89
|
specification_version: 3
|
74
90
|
summary: A simple ruby implementation of lex and yacc, based on Python's ply
|
75
91
|
test_files:
|
76
|
-
- spec/lex/
|
92
|
+
- spec/lex/lex_spec.rb
|
93
|
+
- spec/parse/calc_spec.rb
|
94
|
+
- spec/parse/grammar_spec.rb
|
95
|
+
- spec/parse/lr_table_spec.rb
|
96
|
+
- spec/parse/production_spec.rb
|
97
|
+
- spec/parse/rule_parser_spec.rb
|
98
|
+
- spec/parse/yacc_spec.rb
|
77
99
|
- spec/spec_helper.rb
|
78
100
|
has_rdoc:
|