rltk 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Ruby Language Toolkit
3
+ # Date: 2011/03/04
4
+ # Description: This file contains a parser for a simple infix calculator.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Ruby Language Toolkit
11
+ require 'rltk/parser'
12
+
13
+ #######################
14
+ # Classes and Modules #
15
+ #######################
16
+
17
+ module RLTK # :nodoc:
18
+
19
+ # The RLTK::Parsers module contains the parsers that are included as part
20
+ # of the RLKT project.
21
+ module Parsers
22
+
23
+ # A parser for a simple infix calculator.
24
+ class InfixCalc < Parser
25
+
26
+ left :PLS, :SUB
27
+ right :MUL, :DIV
28
+
29
+ production(:e) do
30
+ clause('NUM') { |n| n }
31
+
32
+ clause('LPAREN e RPAREN') { |_, e, _| e }
33
+
34
+ clause('e PLS e') { |e0, _, e1| e0 + e1 }
35
+ clause('e SUB e') { |e0, _, e1| e0 - e1 }
36
+ clause('e MUL e') { |e0, _, e1| e0 * e1 }
37
+ clause('e DIV e') { |e0, _, e1| e0 / e1 }
38
+ end
39
+
40
+ finalize
41
+ end
42
+ end
43
+ end
@@ -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 a parser for a simple postfix calculator.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Ruby Language Toolkit
11
+ require 'rltk/parser'
12
+
13
+ #######################
14
+ # Classes and Modules #
15
+ #######################
16
+
17
+ module RLTK # :nodoc:
18
+ module Parsers # :nodoc:
19
+
20
+ # A parser for a simple post-fix calculator.
21
+ class PostfixCalc < Parser
22
+ production(:e) do
23
+ clause('NUM') { |n| n }
24
+
25
+ clause('e e PLS') { |e0, e1, _| e0 + e1 }
26
+ clause('e e SUB') { |e0, e1, _| e0 - e1 }
27
+ clause('e e MUL') { |e0, e1, _| e0 * e1 }
28
+ clause('e e DIV') { |e0, e1, _| e0 / e1 }
29
+ end
30
+
31
+ finalize
32
+ end
33
+ end
34
+ end
@@ -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 a parser for a simple prefix calculator.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Ruby Language Toolkit
11
+ require 'rltk/parser'
12
+
13
+ #######################
14
+ # Classes and Modules #
15
+ #######################
16
+
17
+ module RLTK # :nodoc:
18
+ module Parsers # :nodoc:
19
+
20
+ # A parser for a simple prefix calculator.
21
+ class PrefixCalc < Parser
22
+ production(:e) do
23
+ clause('NUM') { |n| n }
24
+
25
+ clause('PLS e e') { |_, e0, e1| e0 + e1 }
26
+ clause('SUB e e') { |_, e0, e1| e0 - e1 }
27
+ clause('MUL e e') { |_, e0, e1| e0 * e1 }
28
+ clause('DIV e e') { |_, e0, e1| e0 / e1 }
29
+ end
30
+
31
+ finalize
32
+ end
33
+ end
34
+ end
data/lib/rltk/token.rb ADDED
@@ -0,0 +1,66 @@
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Ruby Language Toolkit
3
+ # Date: 2011/01/17
4
+ # Description: This file contains code having to do with tokens.
5
+
6
+ #######################
7
+ # Classes and Modules #
8
+ #######################
9
+
10
+ module RLTK # :nodoc:
11
+
12
+ # The StreamPosition class is used to indicate the position of a token or
13
+ # other text inside a stream.
14
+ class StreamPosition
15
+ attr_accessor :stream_offset
16
+ attr_accessor :line_number
17
+ attr_accessor :line_offset
18
+ attr_accessor :length
19
+
20
+ attr_accessor :file_name
21
+
22
+ alias :start :line_offset
23
+
24
+ # Instantiates a new StreamPosition object with the values specified.
25
+ def initialize(stream_offset = 0, line_number = 0, line_offset = 0, length = 0, file_name = nil)
26
+ @stream_offset = stream_offset
27
+ @line_number = line_number
28
+ @line_offset = line_offset
29
+ @length = length
30
+ @file_name = file_name
31
+ end
32
+ end
33
+
34
+ # The Token class is used to represent the output of a RLTK::Lexer and the
35
+ # input of a RLTK::Parser.
36
+ class Token
37
+ attr_reader :type
38
+ attr_reader :value
39
+
40
+ # The StreamPosition object associated with this token.
41
+ attr_reader :position
42
+
43
+ # Instantiates a new Token object with the values specified.
44
+ def initialize(type, value = nil, position = nil)
45
+ @type = type
46
+ @value = value
47
+
48
+ @position = position
49
+ end
50
+
51
+ # Compares one token to another. This only tests the token's _type_
52
+ # and _value_ and not the location of the token in its source.
53
+ def ==(other)
54
+ self.type == other.type and self.value == other.value
55
+ end
56
+
57
+ # Returns a string representing the tokens _type_ and _value_.
58
+ def to_s
59
+ if value
60
+ "#{self.type}(#{self.value})"
61
+ else
62
+ self.type.to_s
63
+ end
64
+ end
65
+ end
66
+ end
data/test/tc_ast.rb ADDED
@@ -0,0 +1,85 @@
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::ASTNode class.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Standard Library
11
+ require 'test/unit'
12
+ require 'pp'
13
+
14
+ # Ruby Language Toolkit
15
+ require 'rltk/ast'
16
+
17
+ #######################
18
+ # Classes and Modules #
19
+ #######################
20
+
21
+ class ANode < RLTK::ASTNode
22
+ child :left, ANode
23
+ child :right, ANode
24
+ end
25
+
26
+ class BNode < ANode; end
27
+ class CNode < ANode; end
28
+
29
+ class DNode < RLTK::ASTNode; end
30
+
31
+ class ASTNodeTester < Test::Unit::TestCase
32
+ def setup
33
+ @leaf0 = CNode.new(nil, nil)
34
+ @tree0 = ANode.new(BNode.new(@leaf0, nil), BNode.new(nil, nil))
35
+
36
+ @tree1 = ANode.new(BNode.new(CNode.new(nil, nil), nil), BNode.new(nil, nil))
37
+ @tree2 = ANode.new(BNode.new(nil, nil), BNode.new(CNode.new(nil, nil), nil))
38
+ end
39
+
40
+ def test_children
41
+ node = ANode.new(nil, nil)
42
+
43
+ assert_equal(node.children, [nil, nil])
44
+
45
+ node.children = (expected_children = [BNode.new(nil, nil), CNode.new(nil, nil)])
46
+
47
+ assert_equal(node.children, expected_children)
48
+
49
+ node.map do |child|
50
+ if child.is_a?(BNode)
51
+ CNode.new(nil, nil)
52
+ else
53
+ BNode.new(nil, nil)
54
+ end
55
+ end
56
+
57
+ assert_equal(node.children, expected_children.reverse)
58
+ end
59
+
60
+ def test_equal
61
+ assert_equal(@tree0, @tree1)
62
+ assert_not_equal(@tree0, @tree2)
63
+ end
64
+
65
+ def test_initialize
66
+ assert_raise(Exception) { RLTK::ASTNode.new }
67
+ assert_nothing_raised(Exception) { ANode.new(nil, nil) }
68
+ end
69
+
70
+ def test_notes
71
+ node = ANode.new(nil, nil)
72
+
73
+ assert_nil(node[:a])
74
+ assert_equal(node[:a] = :b, :b)
75
+ assert_equal(node.note?(:a), true)
76
+ assert_equal(node.note?(:b), false)
77
+ assert_equal(node.delete_note(:a), :b)
78
+ assert_nil(node[:a])
79
+ end
80
+
81
+ def test_root
82
+ assert_same(@tree0, @tree0.root)
83
+ assert_same(@tree0, @leaf0.root)
84
+ end
85
+ end
data/test/tc_cfg.rb ADDED
@@ -0,0 +1,149 @@
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::CFG class.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Standard Library
11
+ require 'test/unit'
12
+
13
+ # Ruby Language Toolkit
14
+ require 'rltk/cfg'
15
+
16
+ #######################
17
+ # Classes and Modules #
18
+ #######################
19
+
20
+ class CFGTester < Test::Unit::TestCase
21
+ def setup
22
+ @grammar = RLTK::CFG.new
23
+
24
+ @grammar.production(:s) do
25
+ clause('A G D')
26
+ clause('A a C')
27
+ clause('B a D')
28
+ clause('B G C')
29
+ end
30
+
31
+ @grammar.production(:a, 'b')
32
+ @grammar.production(:b, 'G')
33
+ end
34
+
35
+ def test_callback
36
+ grammar = RLTK::CFG.new
37
+
38
+ first = true
39
+ grammar.callback do |p, type, num|
40
+ assert_not_nil(p)
41
+ assert_equal(type, :'?')
42
+
43
+ if first
44
+ assert_equal(num, :first)
45
+ first = false
46
+ else
47
+ assert_equal(num, :second)
48
+ end
49
+ end
50
+
51
+ grammar.production(:a, 'A?') { |a| a }
52
+
53
+ first = true
54
+ grammar.callback do |p, type, num|
55
+ assert_not_nil(p)
56
+ assert_equal(type, :*)
57
+
58
+ if first
59
+ assert_equal(num, :first)
60
+ first = false
61
+ else
62
+ assert_equal(num, :second)
63
+ end
64
+ end
65
+
66
+ grammar.production(:a, 'A*') { |a| a }
67
+
68
+ first = true
69
+ grammar.callback do |p, type, num|
70
+ assert_not_nil(p)
71
+ assert_equal(type, :+)
72
+
73
+ if first
74
+ assert_equal(num, :first)
75
+ first = false
76
+ else
77
+ assert_equal(num, :second)
78
+ end
79
+ end
80
+
81
+ grammar.production(:a, 'A+') { |a| a }
82
+ end
83
+
84
+ def test_first_set
85
+ @grammar.first_set(:s).each do |sym|
86
+ assert([:A, :B].include?(sym))
87
+ end
88
+
89
+ assert_equal(@grammar.first_set(:b), [:G])
90
+ assert_equal(@grammar.first_set(:a), [:G])
91
+ end
92
+
93
+ def test_follow_set
94
+ assert_equal(@grammar.follow_set(:s), [:EOS])
95
+
96
+ @grammar.follow_set(:a).each do |sym|
97
+ assert([:C, :D].include?(sym))
98
+ end
99
+
100
+ @grammar.follow_set(:b).each do |sym|
101
+ assert([:C, :D].include?(sym))
102
+ end
103
+ end
104
+
105
+ def test_is_nonterminal
106
+ assert_equal(RLTK::CFG::is_nonterminal?(:lowercase), true)
107
+ assert_equal(RLTK::CFG::is_nonterminal?(:UPERCASE), false)
108
+ end
109
+
110
+ def test_is_terminal
111
+ assert_equal(RLTK::CFG::is_terminal?(:lowercase), false)
112
+ assert_equal(RLTK::CFG::is_terminal?(:UPERCASE), true)
113
+ end
114
+
115
+ def test_item
116
+ i0 = RLTK::CFG::Item.new(0, 0, :a, [:b, :C, :D, :e])
117
+ i1 = i0.copy
118
+
119
+ assert_equal(i0, i1)
120
+ assert_equal(i0.at_end?, false)
121
+ assert_equal(i0.next_symbol, :b)
122
+
123
+ i0.advance
124
+
125
+ assert_not_equal(i0, i1)
126
+ assert_equal(i0.at_end?, false)
127
+ assert_equal(i0.next_symbol, :C)
128
+
129
+ i0.advance
130
+ assert_equal(i0.at_end?, false)
131
+ assert_equal(i0.next_symbol, :D)
132
+
133
+ i0.advance
134
+ assert_equal(i0.at_end?, false)
135
+ assert_equal(i0.next_symbol, :e)
136
+
137
+ i0.advance
138
+ assert_equal(i0.at_end?, true)
139
+ assert_nil(i0.next_symbol)
140
+ end
141
+
142
+ def test_production
143
+ p0 = RLTK::CFG::Production.new(0, :a, [:b, :C, :D, :e])
144
+ p1 = p0.copy
145
+
146
+ assert_equal(p0, p1)
147
+ assert_equal(p0.last_terminal, :D)
148
+ end
149
+ end
data/test/tc_lexer.rb ADDED
@@ -0,0 +1,217 @@
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::Lexer class.
5
+
6
+ ############
7
+ # Requires #
8
+ ############
9
+
10
+ # Standard Library
11
+ require 'test/unit'
12
+
13
+ # Ruby Language Toolkit
14
+ require 'rltk/token'
15
+ require 'rltk/lexer'
16
+ require 'rltk/lexers/calculator'
17
+ require 'rltk/lexers/ebnf'
18
+
19
+ #######################
20
+ # Classes and Modules #
21
+ #######################
22
+
23
+ class ABLongest < RLTK::Lexer
24
+ rule(/a+/) { :APLUS }
25
+ rule(/b+/) { :BPLUS }
26
+
27
+ rule(/a+b+/) { :APLUSBPLUS }
28
+ end
29
+
30
+ class ABFirst < RLTK::Lexer
31
+ match_first
32
+
33
+ rule(/a+/) { :APLUS }
34
+ rule(/b+/) { :BPLUS }
35
+
36
+ rule(/a+b+/) { :APLUSBPLUS }
37
+ end
38
+
39
+ class ENVLexer < RLTK::Lexer
40
+ rule(/a/) { [:A, next_value] }
41
+
42
+ class Environment < Environment
43
+ def initialize(*args)
44
+ super(*args)
45
+ @value = -1
46
+ end
47
+
48
+ def next_value
49
+ @value += 1
50
+ end
51
+ end
52
+ end
53
+
54
+ class FlagLexer < RLTK::Lexer
55
+ rule(/a/) { set_flag(:a); :A }
56
+ rule(/\s/)
57
+
58
+ rule(/b/, :default, [:a]) { set_flag(:b); :B }
59
+ rule(/c/, :default, [:a, :b]) { :C }
60
+ end
61
+
62
+ class StateLexer < RLTK::Lexer
63
+ rule(/a/) { :A }
64
+ rule(/\s/)
65
+
66
+ rule(/\(\*/) { push_state(:comment) }
67
+
68
+ rule(/\(\*/, :comment) { push_state(:comment) }
69
+ rule(/\*\)/, :comment) { pop_state }
70
+ rule(/./, :comment)
71
+ end
72
+
73
+ class LexerTester < Test::Unit::TestCase
74
+ def test_calc
75
+ expected =
76
+ [
77
+ RLTK::Token.new(:NUM, 1),
78
+
79
+ RLTK::Token.new(:PLS),
80
+ RLTK::Token.new(:SUB),
81
+ RLTK::Token.new(:MUL),
82
+ RLTK::Token.new(:DIV),
83
+
84
+ RLTK::Token.new(:LPAREN),
85
+ RLTK::Token.new(:RPAREN),
86
+ RLTK::Token.new(:EOS)
87
+ ]
88
+
89
+ actual = RLTK::Lexers::Calculator.lex('1 + - * / ( )')
90
+
91
+ assert_equal(expected, actual)
92
+ end
93
+
94
+ def test_ebnf
95
+ expected =
96
+ [
97
+ RLTK::Token.new(:NONTERM, :aaa),
98
+ RLTK::Token.new(:TERM, :BBB),
99
+
100
+ RLTK::Token.new(:*),
101
+ RLTK::Token.new(:+),
102
+ RLTK::Token.new(:'?'),
103
+ RLTK::Token.new(:EOS)
104
+ ]
105
+
106
+ actual = RLTK::Lexers::EBNF.lex('aaa BBB * + ?')
107
+
108
+ assert_equal(expected, actual)
109
+ end
110
+
111
+ def test_environment
112
+ expected =
113
+ [
114
+ RLTK::Token.new(:A, 0),
115
+ RLTK::Token.new(:A, 1),
116
+ RLTK::Token.new(:A, 2),
117
+ RLTK::Token.new(:EOS)
118
+ ]
119
+
120
+ actual = ENVLexer.lex('aaa')
121
+
122
+ assert_equal(expected, actual)
123
+
124
+ lexer = ENVLexer.new
125
+
126
+ assert_equal(expected, lexer.lex('aaa'))
127
+
128
+ expected =
129
+ [
130
+ RLTK::Token.new(:A, 3),
131
+ RLTK::Token.new(:A, 4),
132
+ RLTK::Token.new(:A, 5),
133
+ RLTK::Token.new(:EOS)
134
+ ]
135
+
136
+ assert_equal(expected, lexer.lex('aaa'))
137
+ end
138
+
139
+ def test_first_match
140
+ expected =
141
+ [
142
+ RLTK::Token.new(:APLUS),
143
+ RLTK::Token.new(:BPLUS),
144
+ RLTK::Token.new(:EOS)
145
+ ]
146
+
147
+ actual = ABFirst.lex('aaabbb')
148
+
149
+ assert_equal(expected, actual)
150
+ end
151
+
152
+ def test_flags
153
+
154
+ assert_raise(RLTK::LexingError) { FlagLexer.lex('b') }
155
+ assert_raise(RLTK::LexingError) { FlagLexer.lex('ac') }
156
+
157
+ expected =
158
+ [
159
+ RLTK::Token.new(:A),
160
+ RLTK::Token.new(:B),
161
+ RLTK::Token.new(:C),
162
+ RLTK::Token.new(:EOS)
163
+ ]
164
+
165
+ actual = FlagLexer.lex('abc')
166
+ assert_equal(expected, actual)
167
+
168
+ expected =
169
+ [
170
+ RLTK::Token.new(:A),
171
+ RLTK::Token.new(:B),
172
+ RLTK::Token.new(:C),
173
+ RLTK::Token.new(:A),
174
+ RLTK::Token.new(:B),
175
+ RLTK::Token.new(:C),
176
+ RLTK::Token.new(:EOS)
177
+ ]
178
+
179
+ actual = FlagLexer.lex('abcabc')
180
+ assert_equal(expected, actual)
181
+ end
182
+
183
+ def test_lex
184
+ assert_raise(RLTK::LexingError) { ABFirst.lex('aaabbbCCC') }
185
+ assert_raise(RLTK::LexingError) { ABLongest.lex('aaabbbCCC') }
186
+
187
+ assert_nothing_raised(RLTK::LexingError) { ABFirst.lex('aaabbb') }
188
+ assert_nothing_raised(RLTK::LexingError) { ABLongest.lex('aaabbb') }
189
+ end
190
+
191
+ def test_longest_match
192
+ expected =
193
+ [
194
+ RLTK::Token.new(:APLUSBPLUS),
195
+ RLTK::Token.new(:EOS)
196
+ ]
197
+
198
+ actual = ABLongest.lex('aaabbb')
199
+
200
+ assert_equal(expected, actual)
201
+ end
202
+
203
+ def test_state
204
+ expected =
205
+ [
206
+ RLTK::Token.new(:A),
207
+ RLTK::Token.new(:A),
208
+ RLTK::Token.new(:EOS)
209
+ ]
210
+
211
+ actual = StateLexer.lex('a (* bbb *) a')
212
+ assert_equal(expected, actual)
213
+
214
+ actual = StateLexer.lex('a (* b (* ccc *) b *) a')
215
+ assert_equal(expected, actual)
216
+ end
217
+ end