rly 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,5 +1,10 @@
1
1
  require "rubygems"
2
2
  require "bundler/setup"
3
+ begin
4
+ require "pry-nav"
5
+ rescue LoadError
6
+ #
7
+ end
3
8
 
4
9
  RSpec.configure do |config|
5
10
  config.treat_symbols_as_metadata_keys_with_true_values = true
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.1.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-09 00:00:00.000000000 Z
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/lexer_spec.rb
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/lexer_spec.rb
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: