rltk 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +610 -0
- data/Rakefile +156 -21
- data/lib/rltk/ast.rb +179 -127
- data/lib/rltk/cfg.rb +131 -38
- data/lib/rltk/cg/basic_block.rb +167 -0
- data/lib/rltk/cg/bindings.rb +136 -0
- data/lib/rltk/cg/builder.rb +1095 -0
- data/lib/rltk/cg/context.rb +51 -0
- data/lib/rltk/cg/execution_engine.rb +172 -0
- data/lib/rltk/cg/function.rb +224 -0
- data/lib/rltk/cg/generated_bindings.rb +6158 -0
- data/lib/rltk/cg/generated_extended_bindings.rb +43 -0
- data/lib/rltk/cg/generic_value.rb +98 -0
- data/lib/rltk/cg/instruction.rb +498 -0
- data/lib/rltk/cg/llvm.rb +51 -0
- data/lib/rltk/cg/memory_buffer.rb +62 -0
- data/lib/rltk/cg/module.rb +328 -0
- data/lib/rltk/cg/pass_manager.rb +201 -0
- data/lib/rltk/cg/support.rb +29 -0
- data/lib/rltk/cg/type.rb +510 -0
- data/lib/rltk/cg/value.rb +1207 -0
- data/lib/rltk/cg.rb +33 -0
- data/lib/rltk/lexer.rb +135 -85
- data/lib/rltk/lexers/calculator.rb +4 -1
- data/lib/rltk/lexers/ebnf.rb +1 -4
- data/lib/rltk/parser.rb +360 -196
- data/lib/rltk/token.rb +29 -4
- data/lib/rltk/util/abstract_class.rb +25 -0
- data/lib/rltk/util/monkeys.rb +125 -0
- data/lib/rltk/version.rb +5 -2
- data/test/tc_ast.rb +11 -11
- data/test/tc_lexer.rb +41 -41
- data/test/tc_parser.rb +123 -97
- data/test/ts_rltk.rb +50 -0
- metadata +181 -87
- data/README +0 -390
data/lib/rltk/token.rb
CHANGED
@@ -12,16 +12,30 @@ module RLTK # :nodoc:
|
|
12
12
|
# The StreamPosition class is used to indicate the position of a token or
|
13
13
|
# other text inside a stream.
|
14
14
|
class StreamPosition
|
15
|
+
# @return [Integer]
|
15
16
|
attr_accessor :stream_offset
|
17
|
+
|
18
|
+
# @return [Integer]
|
16
19
|
attr_accessor :line_number
|
20
|
+
|
21
|
+
# @return [Integer]
|
17
22
|
attr_accessor :line_offset
|
23
|
+
|
24
|
+
# @return [Integer]
|
18
25
|
attr_accessor :length
|
19
26
|
|
27
|
+
# @return [String]
|
20
28
|
attr_accessor :file_name
|
21
29
|
|
22
30
|
alias :start :line_offset
|
23
31
|
|
24
32
|
# Instantiates a new StreamPosition object with the values specified.
|
33
|
+
#
|
34
|
+
# @param [Integer] stream_offset The position from the beginning of the stream.
|
35
|
+
# @param [Integer] line_number The number of newlines since the beginning of the file.
|
36
|
+
# @param [Integer] line_offset The offset of this token from the beginning of the current line.
|
37
|
+
# @param [Integer] length The length of the text of the token.
|
38
|
+
# @param [String, nil] file_name The name of the file being lexed.
|
25
39
|
def initialize(stream_offset = 0, line_number = 0, line_offset = 0, length = 0, file_name = nil)
|
26
40
|
@stream_offset = stream_offset
|
27
41
|
@line_number = line_number
|
@@ -34,13 +48,20 @@ module RLTK # :nodoc:
|
|
34
48
|
# The Token class is used to represent the output of a RLTK::Lexer and the
|
35
49
|
# input of a RLTK::Parser.
|
36
50
|
class Token
|
51
|
+
# @return [Symbol]
|
37
52
|
attr_reader :type
|
53
|
+
|
54
|
+
# @return [Symbol]
|
38
55
|
attr_reader :value
|
39
56
|
|
40
|
-
#
|
57
|
+
# @return [StreamPosition] StreamPosition object associated with this token.
|
41
58
|
attr_reader :position
|
42
59
|
|
43
60
|
# Instantiates a new Token object with the values specified.
|
61
|
+
#
|
62
|
+
# @param [Symbol] type A symbol representing the type of this Token.
|
63
|
+
# @param [Object, nil] value A value associated with this token.
|
64
|
+
# @param [StreamPosition, nil] position The position of the token in a stream.
|
44
65
|
def initialize(type, value = nil, position = nil)
|
45
66
|
@type = type
|
46
67
|
@value = value
|
@@ -48,13 +69,17 @@ module RLTK # :nodoc:
|
|
48
69
|
@position = position
|
49
70
|
end
|
50
71
|
|
51
|
-
# Compares one token to another. This only tests the token's
|
52
|
-
# and
|
72
|
+
# Compares one token to another. This only tests the token's *type*
|
73
|
+
# and *value* and not the location of the token in its source.
|
74
|
+
#
|
75
|
+
# @param [Token] other Another Token to compare to.
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
53
78
|
def ==(other)
|
54
79
|
self.type == other.type and self.value == other.value
|
55
80
|
end
|
56
81
|
|
57
|
-
#
|
82
|
+
# @return [String] String representing the tokens *type* and *value*.
|
58
83
|
def to_s
|
59
84
|
if value
|
60
85
|
"#{self.type}(#{self.value})"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2012/04/20
|
4
|
+
# Description: This file holds an implementation of an AbstractClass module.
|
5
|
+
|
6
|
+
# A module used for making abstract classes. Any class that includes this
|
7
|
+
# module will not be able to be instantiated directly.
|
8
|
+
module AbstractClass
|
9
|
+
# Callback method for when this module is included.
|
10
|
+
#
|
11
|
+
# @param [Class] klass The Class object for the including class.
|
12
|
+
def self.included(klass)
|
13
|
+
klass.instance_exec do
|
14
|
+
@abstract_class = klass
|
15
|
+
|
16
|
+
def self.new(*args)
|
17
|
+
if self == @abstract_class
|
18
|
+
raise "Instantiating abstract class #{self} is not allowed."
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2012/03/08
|
4
|
+
# Description: This file holds various monkey patches that make coding easier.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
###########
|
11
|
+
# Methods #
|
12
|
+
###########
|
13
|
+
|
14
|
+
# A helper method for type checking Ruby values.
|
15
|
+
#
|
16
|
+
# @param [Object] o Object to type check.
|
17
|
+
# @param [Class] type Class the object should be an instance of.
|
18
|
+
# @param [String, nil] blame Variable name to blame for failed type checks.
|
19
|
+
# @param [Boolean] strict Strict or non-strict checking. Uses `instance_of?` and `is_a?` respectively.
|
20
|
+
#
|
21
|
+
# @return [Object] The object passed as parameter o.
|
22
|
+
def check_type(o, type, blame = nil, strict = false)
|
23
|
+
type_ok = if strict then o.instance_of?(type) else o.is_a?(type) end
|
24
|
+
|
25
|
+
if type_ok
|
26
|
+
o
|
27
|
+
else
|
28
|
+
if blame
|
29
|
+
raise "Parameter #{blame} must be an instance of the #{type.name} class. Received an instance of #{o.class.name}."
|
30
|
+
else
|
31
|
+
raise "Expected an object of type #{type.name}. Received an instance of #{o.class.name}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# A helper method for type checking Ruby array values.
|
37
|
+
#
|
38
|
+
# @param [Array<Object>] array Array of objects to type check.
|
39
|
+
# @param [Class] type Class the objects should be an instance of.
|
40
|
+
# @param [String, nil] blame Variable name to blame for failed type checks.
|
41
|
+
# @param [Boolean] strict Strict or non-strict checking. Uses `instance_of?` and `is_a?` respectively.
|
42
|
+
#
|
43
|
+
# @return [Object] The object passed in parameter o.
|
44
|
+
def check_array_type(array, type, blame = nil, strict = false)
|
45
|
+
array.each do |o|
|
46
|
+
type_ok = if strict then o.instance_of?(type) else o.is_a?(type) end
|
47
|
+
|
48
|
+
if not type_ok
|
49
|
+
if blame
|
50
|
+
raise "Parameter #{blame} must contain instances of the #{type.name} class."
|
51
|
+
else
|
52
|
+
raise "Expected an object of type #{type.name}."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#######################
|
59
|
+
# Classes and Modules #
|
60
|
+
#######################
|
61
|
+
|
62
|
+
# Monkey-patched Object class.
|
63
|
+
class Object
|
64
|
+
# Simple implementation of the Y combinator.
|
65
|
+
#
|
66
|
+
# @param [Object] value Value to be returned after executing the provided block.
|
67
|
+
#
|
68
|
+
# @return [Object] The object passed in parameter value.
|
69
|
+
def returning(value)
|
70
|
+
yield(value)
|
71
|
+
value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Monkey-patched Class class.
|
76
|
+
class Class
|
77
|
+
# Checks for module inclusion.
|
78
|
+
#
|
79
|
+
# @param [Module] mod Module to check the inclusion of.
|
80
|
+
def includes_module?(mod)
|
81
|
+
self.included_modules.include?(mod)
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [String] Name of class without the namespace.
|
85
|
+
def short_name
|
86
|
+
self.name.split('::').last
|
87
|
+
end
|
88
|
+
|
89
|
+
# Checks to see if a Class object is a subclass of the given class.
|
90
|
+
#
|
91
|
+
# @param [Class] klass Class we are checking if this is a subclass of.
|
92
|
+
def subclass_of?(klass)
|
93
|
+
raise 'The klass parameter must be an instance of Class.' if not klass.is_a?(Class)
|
94
|
+
|
95
|
+
if (superklass = self.superclass)
|
96
|
+
superklass == klass or superklass.subclass_of?(klass)
|
97
|
+
else
|
98
|
+
false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Monkey-patchec Integer class.
|
104
|
+
class Integer
|
105
|
+
# @return [Boolean] This Integer as a Boolean value.
|
106
|
+
def to_bool
|
107
|
+
self != 0
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Monkey-patched TrueClass class.
|
112
|
+
class TrueClass
|
113
|
+
# @return [1]
|
114
|
+
def to_i
|
115
|
+
1
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Monkey-patched FalseClass class.
|
120
|
+
class FalseClass
|
121
|
+
# @return [0]
|
122
|
+
def to_i
|
123
|
+
0
|
124
|
+
end
|
125
|
+
end
|
data/lib/rltk/version.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
# Date: 2012/03/08
|
4
4
|
# Description: This file specifies the version number of RLTK.
|
5
5
|
|
6
|
-
module RLTK
|
7
|
-
|
6
|
+
module RLTK # :nodoc:
|
7
|
+
# The version number of the RLTK library.
|
8
|
+
VERSION = '2.0.0'
|
9
|
+
# The version of LLVM targeted by RLTK.
|
10
|
+
LLVM_TARGET_VERSION = '3.0'
|
8
11
|
end
|
data/test/tc_ast.rb
CHANGED
@@ -18,17 +18,17 @@ require 'rltk/ast'
|
|
18
18
|
# Classes and Modules #
|
19
19
|
#######################
|
20
20
|
|
21
|
-
class
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
class BNode < ANode; end
|
27
|
-
class CNode < ANode; end
|
21
|
+
class ASTNodeTester < Test::Unit::TestCase
|
22
|
+
class ANode < RLTK::ASTNode
|
23
|
+
child :left, ANode
|
24
|
+
child :right, ANode
|
25
|
+
end
|
28
26
|
|
29
|
-
class
|
27
|
+
class BNode < ANode; end
|
28
|
+
class CNode < ANode; end
|
30
29
|
|
31
|
-
class
|
30
|
+
class DNode < RLTK::ASTNode; end
|
31
|
+
|
32
32
|
def setup
|
33
33
|
@leaf0 = CNode.new(nil, nil)
|
34
34
|
@tree0 = ANode.new(BNode.new(@leaf0, nil), BNode.new(nil, nil))
|
@@ -63,8 +63,8 @@ class ASTNodeTester < Test::Unit::TestCase
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def test_initialize
|
66
|
-
assert_raise(
|
67
|
-
assert_nothing_raised(
|
66
|
+
assert_raise(RuntimeError) { RLTK::ASTNode.new }
|
67
|
+
assert_nothing_raised(RuntimeError) { ANode.new(nil, nil) }
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_notes
|
data/test/tc_lexer.rb
CHANGED
@@ -20,61 +20,61 @@ require 'rltk/lexers/ebnf'
|
|
20
20
|
# Classes and Modules #
|
21
21
|
#######################
|
22
22
|
|
23
|
-
class
|
24
|
-
|
25
|
-
|
23
|
+
class LexerTester < Test::Unit::TestCase
|
24
|
+
class ABLongest < RLTK::Lexer
|
25
|
+
rule(/a+/) { :APLUS }
|
26
|
+
rule(/b+/) { :BPLUS }
|
26
27
|
|
27
|
-
|
28
|
-
end
|
28
|
+
rule(/a+b+/) { :APLUSBPLUS }
|
29
|
+
end
|
29
30
|
|
30
|
-
class ABFirst < RLTK::Lexer
|
31
|
-
|
31
|
+
class ABFirst < RLTK::Lexer
|
32
|
+
match_first
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
rule(/a+/) { :APLUS }
|
35
|
+
rule(/b+/) { :BPLUS }
|
35
36
|
|
36
|
-
|
37
|
-
end
|
37
|
+
rule(/a+b+/) { :APLUSBPLUS }
|
38
|
+
end
|
38
39
|
|
39
|
-
class ENVLexer < RLTK::Lexer
|
40
|
-
|
40
|
+
class ENVLexer < RLTK::Lexer
|
41
|
+
rule(/a/) { [:A, next_value] }
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
class Environment < Environment
|
44
|
+
def initialize(*args)
|
45
|
+
super(*args)
|
46
|
+
@value = -1
|
47
|
+
end
|
48
|
+
|
49
|
+
def next_value
|
50
|
+
@value += 1
|
51
|
+
end
|
50
52
|
end
|
51
53
|
end
|
52
|
-
end
|
53
54
|
|
54
|
-
class FlagLexer < RLTK::Lexer
|
55
|
-
|
56
|
-
|
55
|
+
class FlagLexer < RLTK::Lexer
|
56
|
+
rule(/a/) { set_flag(:a); :A }
|
57
|
+
rule(/\s/)
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
59
|
+
rule(/b/, :default, [:a]) { set_flag(:b); :B }
|
60
|
+
rule(/c/, :default, [:a, :b]) { :C }
|
61
|
+
end
|
61
62
|
|
62
|
-
class StateLexer < RLTK::Lexer
|
63
|
-
|
64
|
-
|
63
|
+
class StateLexer < RLTK::Lexer
|
64
|
+
rule(/a/) { :A }
|
65
|
+
rule(/\s/)
|
65
66
|
|
66
|
-
|
67
|
+
rule(/\(\*/) { push_state(:comment) }
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
class MatchDataLexer < RLTK::Lexer
|
74
|
-
rule(/a(b*)(c+)/) { [:FOO, match[1,2]] }
|
75
|
-
end
|
69
|
+
rule(/\(\*/, :comment) { push_state(:comment) }
|
70
|
+
rule(/\*\)/, :comment) { pop_state }
|
71
|
+
rule(/./, :comment)
|
72
|
+
end
|
76
73
|
|
77
|
-
class
|
74
|
+
class MatchDataLexer < RLTK::Lexer
|
75
|
+
rule(/a(b*)(c+)/) { [:FOO, match[1,2]] }
|
76
|
+
end
|
77
|
+
|
78
78
|
def test_calc
|
79
79
|
expected =
|
80
80
|
[
|
data/test/tc_parser.rb
CHANGED
@@ -8,8 +8,8 @@
|
|
8
8
|
############
|
9
9
|
|
10
10
|
# Standard Library
|
11
|
-
require 'pp'
|
12
11
|
require 'test/unit'
|
12
|
+
require 'tmpdir'
|
13
13
|
|
14
14
|
# Ruby Language Toolkit
|
15
15
|
require 'rltk/lexer'
|
@@ -23,130 +23,130 @@ require 'rltk/parsers/postfix_calc'
|
|
23
23
|
# Classes and Modules #
|
24
24
|
#######################
|
25
25
|
|
26
|
-
class
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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 }
|
26
|
+
class ParserTester < Test::Unit::TestCase
|
27
|
+
class ABLexer < RLTK::Lexer
|
28
|
+
rule(/a/) { [:A, 1] }
|
29
|
+
rule(/b/) { [:B, 2] }
|
30
|
+
|
31
|
+
rule(/\s/)
|
32
|
+
end
|
41
33
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
34
|
+
class APlusBParser < RLTK::Parser
|
35
|
+
production(:a, 'A+ B') { |a, _| a.length }
|
36
|
+
|
37
|
+
finalize
|
38
|
+
end
|
47
39
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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 }
|
40
|
+
class AQuestionBParser < RLTK::Parser
|
41
|
+
production(:a, 'A? B') { |a, _| a }
|
42
|
+
|
43
|
+
finalize
|
59
44
|
end
|
60
45
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
46
|
+
class AStarBParser < RLTK::Parser
|
47
|
+
production(:a, 'A* B') { |a, _| a.length }
|
48
|
+
|
49
|
+
finalize
|
50
|
+
end
|
66
51
|
|
67
|
-
|
68
|
-
|
52
|
+
class AmbiguousParser < RLTK::Parser
|
53
|
+
production(:e) do
|
54
|
+
clause('NUM') {|n| n}
|
55
|
+
|
56
|
+
clause('e PLS e') { |e0, _, e1| e0 + e1 }
|
57
|
+
clause('e SUB e') { |e0, _, e1| e0 - e1 }
|
58
|
+
clause('e MUL e') { |e0, _, e1| e0 * e1 }
|
59
|
+
clause('e DIV e') { |e0, _, e1| e0 / e1 }
|
60
|
+
end
|
69
61
|
|
70
|
-
|
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] }
|
62
|
+
finalize
|
74
63
|
end
|
75
64
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
class ErrorCalc < RLTK::Parser
|
83
|
-
production(:e) do
|
84
|
-
clause('NUM') {|n| n}
|
65
|
+
class ArrayCalc < RLTK::Parser
|
66
|
+
array_args
|
67
|
+
|
68
|
+
production(:e) do
|
69
|
+
clause('NUM') { |v| v[0] }
|
85
70
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
71
|
+
clause('PLS e e') { |v| v[1] + v[2] }
|
72
|
+
clause('SUB e e') { |v| v[1] - v[2] }
|
73
|
+
clause('MUL e e') { |v| v[1] * v[2] }
|
74
|
+
clause('DIV e e') { |v| v[1] / v[2] }
|
75
|
+
end
|
90
76
|
|
91
|
-
|
92
|
-
clause('e SUB ERROR') { |_, _, _| raise DummyError2 }
|
77
|
+
finalize
|
93
78
|
end
|
94
79
|
|
95
|
-
|
96
|
-
end
|
80
|
+
class DummyError1 < StandardError; end
|
81
|
+
class DummyError2 < StandardError; end
|
82
|
+
|
83
|
+
class ErrorCalc < RLTK::Parser
|
84
|
+
production(:e) do
|
85
|
+
clause('NUM') {|n| n}
|
86
|
+
|
87
|
+
clause('e PLS e') { |e0, _, e1| e0 + e1 }
|
88
|
+
clause('e SUB e') { |e0, _, e1| e0 - e1 }
|
89
|
+
clause('e MUL e') { |e0, _, e1| e0 * e1 }
|
90
|
+
clause('e DIV e') { |e0, _, e1| e0 / e1 }
|
91
|
+
|
92
|
+
clause('e PLS ERROR') { |_, _, _| raise DummyError1 }
|
93
|
+
clause('e SUB ERROR') { |_, _, _| raise DummyError2 }
|
94
|
+
end
|
95
|
+
|
96
|
+
finalize
|
97
|
+
end
|
97
98
|
|
98
|
-
class ELLexer < RLTK::Lexer
|
99
|
-
|
100
|
-
|
99
|
+
class ELLexer < RLTK::Lexer
|
100
|
+
rule(/\n/) { :NEWLINE }
|
101
|
+
rule(/;/) { :SEMI }
|
101
102
|
|
102
|
-
|
103
|
+
rule(/\s/)
|
103
104
|
|
104
|
-
|
105
|
-
end
|
105
|
+
rule(/[A-Za-z]+/) { |t| [:WORD, t] }
|
106
|
+
end
|
106
107
|
|
107
|
-
class ErrorLine < RLTK::Parser
|
108
|
+
class ErrorLine < RLTK::Parser
|
108
109
|
|
109
|
-
|
110
|
+
production(:s, 'line*') { |l| l }
|
110
111
|
|
111
|
-
|
112
|
-
|
112
|
+
production(:line) do
|
113
|
+
clause('NEWLINE') { |_| nil }
|
113
114
|
|
114
|
-
|
115
|
-
|
116
|
-
|
115
|
+
clause('WORD+ SEMI NEWLINE') { |w, _, _| w }
|
116
|
+
clause('WORD+ ERROR NEWLINE') { |w, e, _| error(pos(1).line_number); w }
|
117
|
+
end
|
117
118
|
|
118
|
-
|
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) }
|
119
|
+
finalize
|
129
120
|
end
|
130
|
-
|
131
|
-
class
|
132
|
-
|
133
|
-
|
134
|
-
|
121
|
+
|
122
|
+
class RotatingCalc < RLTK::Parser
|
123
|
+
production(:e) do
|
124
|
+
clause('NUM') {|n| n}
|
125
|
+
|
126
|
+
clause('PLS e e') { |_, e0, e1| e0.send(get_op(:+), e1) }
|
127
|
+
clause('SUB e e') { |_, e0, e1| e0.send(get_op(:-), e1) }
|
128
|
+
clause('MUL e e') { |_, e0, e1| e0.send(get_op(:*), e1) }
|
129
|
+
clause('DIV e e') { |_, e0, e1| e0.send(get_op(:/), e1) }
|
135
130
|
end
|
131
|
+
|
132
|
+
class Environment < Environment
|
133
|
+
def initialize
|
134
|
+
@map = { :+ => 0, :- => 1, :* => 2, :/ => 3 }
|
135
|
+
@ops = [ :+, :-, :*, :/ ]
|
136
|
+
end
|
136
137
|
|
137
|
-
|
138
|
-
|
138
|
+
def get_op(orig_op)
|
139
|
+
new_op = @ops[@map[orig_op]]
|
139
140
|
|
140
|
-
|
141
|
+
@ops = @ops[1..-1] << @ops[0]
|
141
142
|
|
142
|
-
|
143
|
+
new_op
|
144
|
+
end
|
143
145
|
end
|
146
|
+
|
147
|
+
finalize
|
144
148
|
end
|
145
149
|
|
146
|
-
finalize
|
147
|
-
end
|
148
|
-
|
149
|
-
class ParserTester < Test::Unit::TestCase
|
150
150
|
def test_ambiguous_grammar
|
151
151
|
actual = AmbiguousParser.parse(RLTK::Lexers::Calculator.lex('1 + 2 * 3'), {:accept => :all})
|
152
152
|
assert_equal([7, 9], actual.sort)
|
@@ -272,4 +272,30 @@ class ParserTester < Test::Unit::TestCase
|
|
272
272
|
|
273
273
|
assert_raise(RLTK::NotInLanguage) { RLTK::Parsers::PrefixCalc.parse(RLTK::Lexers::Calculator.lex('1 + 2 * 3')) }
|
274
274
|
end
|
275
|
+
|
276
|
+
def test_use
|
277
|
+
tmpfile = File.join(Dir.tmpdir, 'usetest')
|
278
|
+
|
279
|
+
parser0 = Class.new(RLTK::Parser) do
|
280
|
+
production(:a, 'A+') { |a| a.length }
|
281
|
+
|
282
|
+
finalize :use => tmpfile
|
283
|
+
end
|
284
|
+
|
285
|
+
result0 = parser0.parse(ABLexer.lex('a'))
|
286
|
+
|
287
|
+
assert(File.exist?(tmpfile), 'Serialized parser file not found.')
|
288
|
+
|
289
|
+
parser1 = Class.new(RLTK::Parser) do
|
290
|
+
production(:a, 'A+') { |a| a.length }
|
291
|
+
|
292
|
+
finalize :use => tmpfile
|
293
|
+
end
|
294
|
+
|
295
|
+
result1 = parser1.parse(ABLexer.lex('a'))
|
296
|
+
|
297
|
+
assert_equal(result0, result1)
|
298
|
+
|
299
|
+
File.unlink(tmpfile)
|
300
|
+
end
|
275
301
|
end
|
data/test/ts_rltk.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2011/04/06
|
4
|
+
# Description: This file contains the test suit for RLTK. It requires the
|
5
|
+
# individual tests from their respective files.
|
6
|
+
|
7
|
+
############
|
8
|
+
# Requires #
|
9
|
+
############
|
10
|
+
|
11
|
+
if RUBY_VERSION[0..2] == '1.9'
|
12
|
+
begin
|
13
|
+
require 'simplecov'
|
14
|
+
SimpleCov.start do
|
15
|
+
add_filter 'tc_*'
|
16
|
+
add_filter 'generated*'
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
puts 'SimpleCov not installed. Continuing without it.'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Rubygems
|
25
|
+
require 'rubygems'
|
26
|
+
require 'ffi'
|
27
|
+
|
28
|
+
# Ruby Language Toolkit
|
29
|
+
require 'rltk/version'
|
30
|
+
|
31
|
+
# Test cases
|
32
|
+
require 'tc_token'
|
33
|
+
require 'tc_ast'
|
34
|
+
require 'tc_cfg'
|
35
|
+
require 'tc_lexer'
|
36
|
+
require 'tc_parser'
|
37
|
+
|
38
|
+
require 'util/ts_util'
|
39
|
+
|
40
|
+
begin
|
41
|
+
class Tester
|
42
|
+
extend FFI::Library
|
43
|
+
|
44
|
+
ffi_lib("LLVM-#{RLTK::LLVM_TARGET_VERSION}")
|
45
|
+
end
|
46
|
+
|
47
|
+
# The test suite for the LLVM bindings
|
48
|
+
require 'cg/ts_cg'
|
49
|
+
rescue
|
50
|
+
end
|