rltk 1.1.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/test/tc_parser.rb ADDED
@@ -0,0 +1,275 @@
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Ruby Language Toolkit
3
+ # Date: 2011/04/06
4
+ # Description: This file contains unit tests for the RLTK::Parser class.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Standard Library
11
+ require 'pp'
12
+ require 'test/unit'
13
+
14
+ # Ruby Language Toolkit
15
+ require 'rltk/lexer'
16
+ require 'rltk/parser'
17
+ require 'rltk/lexers/calculator'
18
+ require 'rltk/parsers/prefix_calc'
19
+ require 'rltk/parsers/infix_calc'
20
+ require 'rltk/parsers/postfix_calc'
21
+
22
+ #######################
23
+ # Classes and Modules #
24
+ #######################
25
+
26
+ class ABLexer < RLTK::Lexer
27
+ rule(/a/) { [:A, 1] }
28
+ rule(/b/) { [:B, 2] }
29
+
30
+ rule(/\s/)
31
+ end
32
+
33
+ class APlusBParser < RLTK::Parser
34
+ production(:a, 'A+ B') { |a, _| a.length }
35
+
36
+ finalize
37
+ end
38
+
39
+ class AQuestionBParser < RLTK::Parser
40
+ production(:a, 'A? B') { |a, _| a }
41
+
42
+ finalize
43
+ end
44
+
45
+ class AStarBParser < RLTK::Parser
46
+ production(:a, 'A* B') { |a, _| a.length }
47
+
48
+ finalize
49
+ end
50
+
51
+ class AmbiguousParser < RLTK::Parser
52
+ production(:e) do
53
+ clause('NUM') {|n| n}
54
+
55
+ clause('e PLS e') { |e0, _, e1| e0 + e1 }
56
+ clause('e SUB e') { |e0, _, e1| e0 - e1 }
57
+ clause('e MUL e') { |e0, _, e1| e0 * e1 }
58
+ clause('e DIV e') { |e0, _, e1| e0 / e1 }
59
+ end
60
+
61
+ finalize
62
+ end
63
+
64
+ class ArrayCalc < RLTK::Parser
65
+ array_args
66
+
67
+ production(:e) do
68
+ clause('NUM') { |v| v[0] }
69
+
70
+ clause('PLS e e') { |v| v[1] + v[2] }
71
+ clause('SUB e e') { |v| v[1] - v[2] }
72
+ clause('MUL e e') { |v| v[1] * v[2] }
73
+ clause('DIV e e') { |v| v[1] / v[2] }
74
+ end
75
+
76
+ finalize
77
+ end
78
+
79
+ class DummyError1 < Exception; end
80
+ class DummyError2 < Exception; end
81
+
82
+ class ErrorCalc < RLTK::Parser
83
+ production(:e) do
84
+ clause('NUM') {|n| n}
85
+
86
+ clause('e PLS e') { |e0, _, e1| e0 + e1 }
87
+ clause('e SUB e') { |e0, _, e1| e0 - e1 }
88
+ clause('e MUL e') { |e0, _, e1| e0 * e1 }
89
+ clause('e DIV e') { |e0, _, e1| e0 / e1 }
90
+
91
+ clause('e PLS ERROR') { |_, _, _| raise DummyError1 }
92
+ clause('e SUB ERROR') { |_, _, _| raise DummyError2 }
93
+ end
94
+
95
+ finalize
96
+ end
97
+
98
+ class ELLexer < RLTK::Lexer
99
+ rule(/\n/) { :NEWLINE }
100
+ rule(/;/) { :SEMI }
101
+
102
+ rule(/\s/)
103
+
104
+ rule(/[A-Za-z]+/) { |t| [:WORD, t] }
105
+ end
106
+
107
+ class ErrorLine < RLTK::Parser
108
+
109
+ production(:s, 'line*') { |l| l }
110
+
111
+ production(:line) do
112
+ clause('NEWLINE') { |_| nil }
113
+
114
+ clause('WORD+ SEMI NEWLINE') { |w, _, _| w }
115
+ clause('WORD+ ERROR NEWLINE') { |w, e, _| error(pos(1).line_number); w }
116
+ end
117
+
118
+ finalize
119
+ end
120
+
121
+ class RotatingCalc < RLTK::Parser
122
+ production(:e) do
123
+ clause('NUM') {|n| n}
124
+
125
+ clause('PLS e e') { |_, e0, e1| e0.send(get_op(:+), e1) }
126
+ clause('SUB e e') { |_, e0, e1| e0.send(get_op(:-), e1) }
127
+ clause('MUL e e') { |_, e0, e1| e0.send(get_op(:*), e1) }
128
+ clause('DIV e e') { |_, e0, e1| e0.send(get_op(:/), e1) }
129
+ end
130
+
131
+ class Environment < Environment
132
+ def initialize
133
+ @map = { :+ => 0, :- => 1, :* => 2, :/ => 3 }
134
+ @ops = [ :+, :-, :*, :/ ]
135
+ end
136
+
137
+ def get_op(orig_op)
138
+ new_op = @ops[@map[orig_op]]
139
+
140
+ @ops = @ops[1..-1] << @ops[0]
141
+
142
+ new_op
143
+ end
144
+ end
145
+
146
+ finalize
147
+ end
148
+
149
+ class ParserTester < Test::Unit::TestCase
150
+ def test_ambiguous_grammar
151
+ actual = AmbiguousParser.parse(RLTK::Lexers::Calculator.lex('1 + 2 * 3'), {:accept => :all})
152
+ assert_equal([7, 9], actual.sort)
153
+ end
154
+
155
+ def test_array_args
156
+ actual = ArrayCalc.parse(RLTK::Lexers::Calculator.lex('+ 1 2'))
157
+ assert_equal(3, actual)
158
+
159
+ actual = ArrayCalc.parse(RLTK::Lexers::Calculator.lex('+ 1 * 2 3'))
160
+ assert_equal(7, actual)
161
+
162
+ actual = ArrayCalc.parse(RLTK::Lexers::Calculator.lex('* + 1 2 3'))
163
+ assert_equal(9, actual)
164
+ end
165
+
166
+ def test_ebnf_parsing
167
+ ################
168
+ # APlusBParser #
169
+ ################
170
+
171
+ assert_raise(RLTK::NotInLanguage) { APlusBParser.parse(ABLexer.lex('b')) }
172
+ assert_equal(1, APlusBParser.parse(ABLexer.lex('ab')))
173
+ assert_equal(2, APlusBParser.parse(ABLexer.lex('aab')))
174
+ assert_equal(3, APlusBParser.parse(ABLexer.lex('aaab')))
175
+ assert_equal(4, APlusBParser.parse(ABLexer.lex('aaaab')))
176
+
177
+ ####################
178
+ # AQuestionBParser #
179
+ ####################
180
+
181
+ assert_raise(RLTK::NotInLanguage) { AQuestionBParser.parse(ABLexer.lex('aab')) }
182
+ assert_nil(AQuestionBParser.parse(ABLexer.lex('b')))
183
+ assert_not_nil(AQuestionBParser.parse(ABLexer.lex('ab')))
184
+
185
+ ################
186
+ # AStarBParser #
187
+ ################
188
+
189
+ assert_equal(0, AStarBParser.parse(ABLexer.lex('b')))
190
+ assert_equal(1, AStarBParser.parse(ABLexer.lex('ab')))
191
+ assert_equal(2, AStarBParser.parse(ABLexer.lex('aab')))
192
+ assert_equal(3, AStarBParser.parse(ABLexer.lex('aaab')))
193
+ assert_equal(4, AStarBParser.parse(ABLexer.lex('aaaab')))
194
+ end
195
+
196
+ def test_environment
197
+ actual = RotatingCalc.parse(RLTK::Lexers::Calculator.lex('+ 1 2'))
198
+ assert_equal(3, actual)
199
+
200
+ actual = RotatingCalc.parse(RLTK::Lexers::Calculator.lex('/ 1 * 2 3'))
201
+ assert_equal(7, actual)
202
+
203
+ actual = RotatingCalc.parse(RLTK::Lexers::Calculator.lex('- + 1 2 3'))
204
+ assert_equal(9, actual)
205
+
206
+ parser = RotatingCalc.new
207
+
208
+ actual = parser.parse(RLTK::Lexers::Calculator.lex('+ 1 2'))
209
+ assert_equal(3, actual)
210
+
211
+ actual = parser.parse(RLTK::Lexers::Calculator.lex('/ 1 2'))
212
+ assert_equal(3, actual)
213
+ end
214
+
215
+ def test_error_productions
216
+ assert_raise(DummyError1) { ErrorCalc.parse(RLTK::Lexers::Calculator.lex('1 + +')) }
217
+ assert_raise(DummyError2) { ErrorCalc.parse(RLTK::Lexers::Calculator.lex('1 - +')) }
218
+
219
+ test_string = "first line;\n"
220
+ test_string += "second line\n"
221
+ test_string += "third line;\n"
222
+ test_string += "fourth line\n"
223
+
224
+ assert_raise(RLTK::HandledError) { ErrorLine.parse(ELLexer.lex(test_string)) }
225
+
226
+ begin
227
+ ErrorLine.parse(ELLexer.lex(test_string))
228
+ rescue RLTK::HandledError => e
229
+ assert_equal(e.errors, [2,4])
230
+ end
231
+ end
232
+
233
+ def test_infix_calc
234
+ actual = RLTK::Parsers::InfixCalc.parse(RLTK::Lexers::Calculator.lex('1 + 2'))
235
+ assert_equal(3, actual)
236
+
237
+ actual = RLTK::Parsers::InfixCalc.parse(RLTK::Lexers::Calculator.lex('1 + 2 * 3'))
238
+ assert_equal(7, actual)
239
+
240
+ actual = RLTK::Parsers::InfixCalc.parse(RLTK::Lexers::Calculator.lex('(1 + 2) * 3'))
241
+ assert_equal(9, actual)
242
+
243
+ assert_raise(RLTK::NotInLanguage) { RLTK::Parsers::InfixCalc.parse(RLTK::Lexers::Calculator.lex('1 2 + 3 *')) }
244
+ end
245
+
246
+ def test_input
247
+ assert_raise(RLTK::BadToken) { RLTK::Parsers::InfixCalc.parse(RLTK::Lexers::EBNF.lex('A B C')) }
248
+ end
249
+
250
+ def test_postfix_calc
251
+ actual = RLTK::Parsers::PostfixCalc.parse(RLTK::Lexers::Calculator.lex('1 2 +'))
252
+ assert_equal(3, actual)
253
+
254
+ actual = RLTK::Parsers::PostfixCalc.parse(RLTK::Lexers::Calculator.lex('1 2 3 * +'))
255
+ assert_equal(7, actual)
256
+
257
+ actual = RLTK::Parsers::PostfixCalc.parse(RLTK::Lexers::Calculator.lex('1 2 + 3 *'))
258
+ assert_equal(9, actual)
259
+
260
+ assert_raise(RLTK::NotInLanguage) { RLTK::Parsers::InfixCalc.parse(RLTK::Lexers::Calculator.lex('* + 1 2 3')) }
261
+ end
262
+
263
+ def test_prefix_calc
264
+ actual = RLTK::Parsers::PrefixCalc.parse(RLTK::Lexers::Calculator.lex('+ 1 2'))
265
+ assert_equal(3, actual)
266
+
267
+ actual = RLTK::Parsers::PrefixCalc.parse(RLTK::Lexers::Calculator.lex('+ 1 * 2 3'))
268
+ assert_equal(7, actual)
269
+
270
+ actual = RLTK::Parsers::PrefixCalc.parse(RLTK::Lexers::Calculator.lex('* + 1 2 3'))
271
+ assert_equal(9, actual)
272
+
273
+ assert_raise(RLTK::NotInLanguage) { RLTK::Parsers::PrefixCalc.parse(RLTK::Lexers::Calculator.lex('1 + 2 * 3')) }
274
+ end
275
+ end
data/test/tc_token.rb ADDED
@@ -0,0 +1,34 @@
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Ruby Language Toolkit
3
+ # Date: 2011/04/06
4
+ # Description: This file contains unit tests for the RLTK::Token class.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Standard Library
11
+ require 'test/unit'
12
+
13
+ # Ruby Language Toolkit
14
+ require 'rltk/token'
15
+
16
+ #######################
17
+ # Classes and Modules #
18
+ #######################
19
+
20
+ class TokenTester < Test::Unit::TestCase
21
+ def test_equal
22
+ t0 = RLTK::Token.new(:FOO, 0)
23
+ t1 = RLTK::Token.new(:FOO, 0)
24
+ t2 = RLTK::Token.new(:FOO, 1)
25
+ t3 = RLTK::Token.new(:BAR, 0)
26
+ t4 = RLTK::Token.new(:BAR, 1)
27
+
28
+ assert_equal(t0, t1)
29
+
30
+ assert_not_equal(t0, t2)
31
+ assert_not_equal(t0, t3)
32
+ assert_not_equal(t0, t4)
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rltk
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ version: 1.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Chris Wailes
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-11-27 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: The Ruby Language Toolkit provides classes for creatingcontext-free grammars, lexers, parsers, and abstract syntax trees.
22
+ email: chris.wailes@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - LICENSE
31
+ - AUTHORS
32
+ - README
33
+ - Rakefile
34
+ - lib/rltk/cfg.rb
35
+ - lib/rltk/lexer.rb
36
+ - lib/rltk/lexers/ebnf.rb
37
+ - lib/rltk/lexers/calculator.rb
38
+ - lib/rltk/ast.rb
39
+ - lib/rltk/parser.rb
40
+ - lib/rltk/token.rb
41
+ - lib/rltk/parsers/postfix_calc.rb
42
+ - lib/rltk/parsers/infix_calc.rb
43
+ - lib/rltk/parsers/prefix_calc.rb
44
+ - test/tc_ast.rb
45
+ - test/tc_token.rb
46
+ - test/tc_cfg.rb
47
+ - test/tc_parser.rb
48
+ - test/tc_lexer.rb
49
+ homepage: http://github.com/chriswailes/RLTK
50
+ licenses:
51
+ - University of Illinois/NCSA Open Source License
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.8.10
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: The Ruby Language Toolkit
82
+ test_files:
83
+ - test/tc_ast.rb
84
+ - test/tc_token.rb
85
+ - test/tc_cfg.rb
86
+ - test/tc_parser.rb
87
+ - test/tc_lexer.rb