rly 0.1.0 → 0.2.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.
@@ -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: