rubex 0.0.1 → 0.1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +101 -0
- data/HISTORY.md +3 -0
- data/README.md +112 -297
- data/REFERENCE.md +753 -0
- data/Rakefile +4 -1
- data/TUTORIAL.md +234 -0
- data/bin/rubex +1 -1
- data/docs/_config.yml +1 -0
- data/docs/index.html +1 -0
- data/examples/c_struct_interface/c_struct_interface.rb +6 -0
- data/examples/c_struct_interface/c_struct_interface.rubex +47 -0
- data/examples/linked_list/linked_list.rubex +39 -0
- data/examples/linked_list/rb_linked_list.rb +8 -0
- data/examples/rcsv wrapper/rcsv/README.md +1 -0
- data/examples/rcsv wrapper/rcsv/Rakefile +7 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/extconf.rb +3 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.c +302 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.rubex +124 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv.rb +8 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv.so +0 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv/version.rb +1 -0
- data/examples/rcsv wrapper/rcsv/rcsv.gemspec +27 -0
- data/examples/rcsv wrapper/rcsv/spec/rcsv.csv +5 -0
- data/examples/rcsv wrapper/rcsv/spec/rcsv_spec.rb +17 -0
- data/examples/rcsv wrapper/rcsv/spec/spec_helper.rb +6 -0
- data/{spec/fixtures/basic_ruby_method/Makefile → examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/Makefile } +20 -20
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.o +0 -0
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.so +0 -0
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/stage/lib/rcsv.so +0 -0
- data/lib/rubex.rb +6 -50
- data/lib/rubex/ast.rb +1 -3
- data/lib/rubex/ast/expression.rb +1257 -8
- data/lib/rubex/ast/node.rb +226 -28
- data/lib/rubex/ast/statement.rb +1162 -35
- data/lib/rubex/ast/top_statement.rb +815 -0
- data/lib/rubex/code_writer.rb +103 -26
- data/lib/rubex/compiler.rb +72 -0
- data/lib/rubex/compiler_config.rb +19 -0
- data/lib/rubex/constants.rb +145 -8
- data/lib/rubex/data_type.rb +667 -4
- data/lib/rubex/error.rb +15 -0
- data/lib/rubex/helpers.rb +154 -0
- data/lib/rubex/lexer.rex +186 -22
- data/lib/rubex/lexer.rex.rb +261 -35
- data/lib/rubex/parser.racc +876 -28
- data/lib/rubex/parser.racc.rb +2845 -90
- data/lib/rubex/rake_task.rb +34 -0
- data/lib/rubex/symbol_table/entry.rb +17 -3
- data/lib/rubex/symbol_table/scope.rb +298 -25
- data/lib/rubex/version.rb +1 -1
- data/rubex.gemspec +11 -3
- data/spec/basic_ruby_method_spec.rb +15 -21
- data/spec/binding_ptr_args_spec.rb +33 -0
- data/spec/bitwise_operators_spec.rb +40 -0
- data/spec/blocks_spec.rb +35 -0
- data/spec/c_bindings_spec.rb +36 -0
- data/spec/c_constants_spec.rb +33 -0
- data/spec/c_function_ptrs_spec.rb +38 -0
- data/spec/c_functions_spec.rb +35 -0
- data/spec/c_struct_interface_spec.rb +38 -0
- data/spec/call_by_reference_spec.rb +33 -0
- data/spec/class_methods_spec.rb +33 -0
- data/spec/class_spec.rb +40 -0
- data/spec/comments_spec.rb +33 -0
- data/spec/default_args_spec.rb +37 -0
- data/spec/error_handling_spec.rb +42 -0
- data/spec/examples_spec.rb +52 -0
- data/spec/expressions_spec.rb +33 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.rubex +2 -0
- data/spec/fixtures/binding_ptr_args/binding_ptr_args.rubex +30 -0
- data/spec/fixtures/bitwise_operators/bitwise_operators.rubex +40 -0
- data/spec/fixtures/blocks/blocks.rubex +11 -0
- data/spec/fixtures/c_bindings/c_bindings.rubex +58 -0
- data/spec/fixtures/c_constants/c_constants.rubex +7 -0
- data/spec/fixtures/c_function_ptrs/c_function_ptrs.rubex +52 -0
- data/spec/fixtures/c_functions/c_functions.rubex +25 -0
- data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +34 -0
- data/spec/fixtures/call_by_reference/call_by_reference.rubex +30 -0
- data/spec/fixtures/class/class.rubex +20 -0
- data/spec/fixtures/class_methods/class_methods.rubex +12 -0
- data/spec/fixtures/comments/comments.rubex +9 -0
- data/spec/fixtures/default_args/default_args.rubex +11 -0
- data/spec/fixtures/error_handling/error_handling.rubex +54 -0
- data/spec/fixtures/examples/array_to_hash.rubex +14 -0
- data/spec/fixtures/examples/rcsv.csv +5 -0
- data/spec/fixtures/examples/rcsv.rubex +329 -0
- data/spec/fixtures/expressions/expressions.rubex +10 -0
- data/spec/fixtures/if_else/if_else.rubex +77 -0
- data/spec/fixtures/implicit_lib_include/implicit_lib_include.rubex +15 -0
- data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +17 -0
- data/spec/fixtures/loops/loops.rubex +33 -0
- data/spec/fixtures/recursion/recursion.rubex +9 -0
- data/spec/fixtures/ruby_constant_method_calls/ruby_constant_method_calls.rubex +17 -0
- data/spec/fixtures/ruby_operators/ruby_operators.rubex +29 -0
- data/spec/fixtures/ruby_raise/ruby_raise.rubex +13 -0
- data/spec/fixtures/ruby_strings/ruby_strings.rubex +19 -0
- data/spec/fixtures/ruby_strings/string_blank_bm.rb +37 -0
- data/spec/fixtures/ruby_symbols/ruby_symbols.rubex +12 -0
- data/spec/fixtures/ruby_types/ruby_types.rubex +15 -0
- data/spec/fixtures/statement_expression/statement_expression.rubex +23 -0
- data/spec/fixtures/static_array/static_array.rubex +20 -0
- data/spec/fixtures/string_literals/string_literals.rubex +15 -0
- data/spec/fixtures/struct/struct.rubex +82 -0
- data/spec/fixtures/typecasting/typecasting.rubex +23 -0
- data/spec/fixtures/var_declarations/var_declarations.rubex +39 -0
- data/spec/if_else_spec.rb +39 -0
- data/spec/implicit_lib_include_spec.rb +33 -0
- data/spec/init_ruby_objects_with_literal_syntax_spec.rb +39 -0
- data/spec/loops_spec.rb +34 -0
- data/spec/recursion_spec.rb +35 -0
- data/spec/ruby_constant_method_calls_spec.rb +35 -0
- data/spec/ruby_operators_spec.rb +40 -0
- data/spec/ruby_raise_spec.rb +35 -0
- data/spec/ruby_strings_spec.rb +33 -0
- data/spec/ruby_symbols_spec.rb +37 -0
- data/spec/ruby_types_spec.rb +35 -0
- data/spec/spec_helper.rb +54 -1
- data/spec/statement_expression_spec.rb +34 -0
- data/spec/static_array_spec.rb +33 -0
- data/spec/string_literals_spec.rb +34 -0
- data/spec/struct_spec.rb +36 -0
- data/spec/typecasting_spec.rb +38 -0
- data/spec/var_declarions_spec.rb +35 -0
- metadata +255 -29
- data/lib/rubex/ast/argument_list.rb +0 -20
- data/lib/rubex/ast/c_base_type.rb +0 -11
- data/lib/rubex/ast/ruby_method_def.rb +0 -84
- data/spec/fixtures/basic_ruby_method/basic.rb +0 -3
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.c +0 -16
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.o +0 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.so +0 -0
- data/spec/fixtures/basic_ruby_method/extconf.rb +0 -3
data/lib/rubex/ast/node.rb
CHANGED
@@ -1,47 +1,206 @@
|
|
1
1
|
module Rubex
|
2
2
|
module AST
|
3
3
|
class Node
|
4
|
+
include Rubex::Helpers::Writers
|
4
5
|
attr_reader :statements
|
5
6
|
|
6
7
|
def initialize statements
|
7
|
-
@statements = statements.
|
8
|
-
end
|
9
|
-
|
10
|
-
def add_child child
|
11
|
-
@statements.concat child
|
8
|
+
@statements = statements.flatten
|
12
9
|
end
|
13
10
|
|
14
11
|
def process_statements target_name, code
|
15
|
-
@scope = Rubex::SymbolTable::Scope::Klass.new 'Object'
|
16
|
-
|
17
|
-
|
12
|
+
@scope = Rubex::SymbolTable::Scope::Klass.new 'Object', nil
|
13
|
+
add_top_statements_to_object_scope
|
14
|
+
analyse_statement
|
15
|
+
rescan_declarations @scope
|
18
16
|
generate_preamble code
|
19
17
|
generate_code code
|
20
18
|
generate_init_method target_name, code
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
# TODO
|
21
|
+
def == other
|
22
|
+
self.class == other.class
|
26
23
|
end
|
27
24
|
|
28
|
-
|
25
|
+
private
|
26
|
+
|
27
|
+
# Scan all the statements that do not belong to any particular class
|
28
|
+
# (meaning that they belong to Object) and add them to the Object class,
|
29
|
+
# which becomes the class from which all other classes will inherit from.
|
30
|
+
def add_top_statements_to_object_scope
|
31
|
+
temp = []
|
32
|
+
combined_statements = []
|
33
|
+
@statements.each do |stmt|
|
34
|
+
if stmt.is_a?(TopStatement::Klass) || stmt.is_a?(TopStatement::CBindings)
|
35
|
+
if !temp.empty?
|
36
|
+
object_klass = TopStatement::Klass.new('Object', @scope, temp)
|
37
|
+
combined_statements << object_klass
|
38
|
+
end
|
39
|
+
|
40
|
+
combined_statements << stmt
|
41
|
+
temp = []
|
42
|
+
else
|
43
|
+
temp << stmt
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if !temp.empty?
|
48
|
+
combined_statements << TopStatement::Klass.new('Object', @scope, temp)
|
49
|
+
end
|
50
|
+
|
51
|
+
@statements = combined_statements
|
52
|
+
end
|
29
53
|
|
30
54
|
def generate_preamble code
|
31
55
|
code << "#include <ruby.h>\n"
|
32
56
|
code << "#include <stdint.h>\n"
|
33
|
-
code << "
|
57
|
+
code << "#include <stdbool.h>\n"
|
58
|
+
@scope.include_files.each do |name|
|
59
|
+
if name[0] == '<' && name[-1] == '>'
|
60
|
+
code << "#include #{name}\n"
|
61
|
+
else
|
62
|
+
code << "#include \"#{name}\"\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
write_usability_macros code
|
66
|
+
@statements.grep(Rubex::AST::TopStatement::Klass).each do |klass|
|
67
|
+
declare_types code, klass.scope
|
68
|
+
end
|
69
|
+
write_user_klasses code
|
70
|
+
write_global_variable_declarations code
|
71
|
+
write_function_declarations code
|
72
|
+
write_usability_functions code
|
73
|
+
code.nl
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_global_variable_declarations code
|
77
|
+
@statements.each do |stmt|
|
78
|
+
if stmt.is_a?(TopStatement::Klass)
|
79
|
+
stmt.statements.each do |s|
|
80
|
+
if s.is_a?(TopStatement::MethodDef)
|
81
|
+
s.scope.global_entries.each do |g|
|
82
|
+
code << "static #{g.type} #{g.c_name};"
|
83
|
+
code.nl
|
84
|
+
end # .each
|
85
|
+
end # if
|
86
|
+
end # .each
|
87
|
+
end # if
|
88
|
+
end # .each
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_user_klasses code
|
92
|
+
code.nl
|
93
|
+
@scope.ruby_class_entries.each do |klass|
|
94
|
+
code << "VALUE #{klass.c_name};"
|
95
|
+
code.nl
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_usability_macros code
|
100
|
+
code.nl
|
101
|
+
code.c_macro Rubex::RUBEX_PREFIX + "INT2BOOL(arg) (arg ? Qtrue : Qfalse)"
|
102
|
+
code.nl
|
103
|
+
end
|
104
|
+
|
105
|
+
def write_usability_functions code
|
106
|
+
code.nl
|
107
|
+
write_char_2_ruby_str code
|
108
|
+
end
|
109
|
+
|
110
|
+
def write_char_2_ruby_str code
|
111
|
+
code << "VALUE #{Rubex::C_FUNC_CHAR2RUBYSTR}(char ch);"
|
112
|
+
code.nl
|
113
|
+
code << "VALUE #{Rubex::C_FUNC_CHAR2RUBYSTR}(char ch)"
|
114
|
+
code.block do
|
115
|
+
code << "char s[2];\n"
|
116
|
+
code << "s[0] = ch;\n"
|
117
|
+
code << "s[1] = '\\0';\n"
|
118
|
+
code << "return rb_str_new2(s);\n"
|
119
|
+
end
|
34
120
|
end
|
35
121
|
|
36
|
-
def
|
122
|
+
def write_function_declarations code
|
123
|
+
@statements.each do |stmt|
|
124
|
+
if stmt.is_a?(Rubex::AST::TopStatement::Klass)
|
125
|
+
stmt.scope.ruby_method_entries.each do |entry|
|
126
|
+
code.write_ruby_method_header(
|
127
|
+
type: entry.type.type.to_s, c_name: entry.c_name)
|
128
|
+
code.colon
|
129
|
+
end
|
130
|
+
|
131
|
+
stmt.scope.c_method_entries.each do |entry|
|
132
|
+
if !entry.extern?
|
133
|
+
code.write_c_method_header(
|
134
|
+
type: entry.type.type.to_s,
|
135
|
+
c_name: entry.c_name,
|
136
|
+
args: Helpers.create_arg_arrays(entry.type.arg_list))
|
137
|
+
code.colon
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def analyse_statement
|
145
|
+
create_symtab_entries_for_top_statements
|
146
|
+
@statements.each do |stat|
|
147
|
+
stat.analyse_statement @scope
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def create_symtab_entries_for_top_statements
|
37
152
|
@statements.each do |stat|
|
38
|
-
stat.
|
153
|
+
if stat.is_a? Rubex::AST::TopStatement::Klass
|
154
|
+
name = stat.name
|
155
|
+
# The top level scope in Ruby is Object. The Object class's immediate
|
156
|
+
# ancestor is also Object. Hence, it is important to set the class
|
157
|
+
# scope and ancestor scope of Object as Object, and make sure that
|
158
|
+
# the same scope object is used for 'Object' class every single time
|
159
|
+
# throughout the compilation process.
|
160
|
+
if name != 'Object'
|
161
|
+
ancestor_entry = @scope.find(stat.ancestor)
|
162
|
+
if !ancestor_entry && Rubex::DEFAULT_CLASS_MAPPINGS[stat.ancestor]
|
163
|
+
ancestor_c_name = Rubex::DEFAULT_CLASS_MAPPINGS[stat.ancestor]
|
164
|
+
ancestor_scope = object_or_stdlib_klass_scope stat.ancestor
|
165
|
+
@scope.add_ruby_class(name: stat.ancestor, c_name: ancestor_c_name,
|
166
|
+
scope: @scope, ancestor: nil, extern: true)
|
167
|
+
else
|
168
|
+
ancestor_scope = ancestor_entry&.type&.scope || @scope
|
169
|
+
end
|
170
|
+
klass_scope = Rubex::SymbolTable::Scope::Klass.new(
|
171
|
+
name, ancestor_scope)
|
172
|
+
else
|
173
|
+
ancestor_scope = @scope
|
174
|
+
klass_scope = @scope
|
175
|
+
end
|
176
|
+
c_name = c_name_for_class name
|
177
|
+
|
178
|
+
@scope.add_ruby_class(name: name, c_name: c_name, scope: klass_scope,
|
179
|
+
ancestor: ancestor_scope, extern: false)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def object_or_stdlib_klass_scope name
|
185
|
+
name != 'Object' ? Rubex::SymbolTable::Scope::Klass.new(name, nil) :
|
186
|
+
@scope
|
187
|
+
end
|
188
|
+
|
189
|
+
def c_name_for_class name
|
190
|
+
c_name =
|
191
|
+
if Rubex::DEFAULT_CLASS_MAPPINGS.has_key? name
|
192
|
+
Rubex::DEFAULT_CLASS_MAPPINGS[name]
|
193
|
+
else
|
194
|
+
Rubex::RUBY_CLASS_PREFIX + name
|
39
195
|
end
|
196
|
+
|
197
|
+
c_name
|
40
198
|
end
|
41
199
|
|
42
|
-
def
|
200
|
+
def rescan_declarations scope
|
43
201
|
@statements.each do |stat|
|
44
|
-
stat.
|
202
|
+
stat.respond_to?(:rescan_declarations) and
|
203
|
+
stat.rescan_declarations(@scope)
|
45
204
|
end
|
46
205
|
end
|
47
206
|
|
@@ -54,18 +213,57 @@ module Rubex
|
|
54
213
|
def generate_init_method target_name, code
|
55
214
|
name = "Init_#{target_name}"
|
56
215
|
code.new_line
|
57
|
-
code.write_func_declaration "void", name,
|
58
|
-
code.
|
216
|
+
code.write_func_declaration type: "void", c_name: name, args: [], static: false
|
217
|
+
code.write_c_method_header type: "void", c_name: name, args: [], static: false
|
218
|
+
code.block do
|
219
|
+
@statements.each do |top_stmt|
|
220
|
+
if top_stmt.is_a?(TopStatement::Klass) && top_stmt.name != 'Object'
|
221
|
+
entry = @scope.find top_stmt.name
|
222
|
+
code.declare_variable type: "VALUE", c_name: entry.c_name
|
223
|
+
end
|
224
|
+
end
|
225
|
+
code.nl
|
59
226
|
|
60
|
-
|
61
|
-
|
62
|
-
|
227
|
+
@statements.each do |top_stmt|
|
228
|
+
# define a class
|
229
|
+
if top_stmt.is_a?(TopStatement::Klass) && top_stmt.name != 'Object'
|
230
|
+
entry = top_stmt.entry
|
231
|
+
ancestor_entry = @scope.find top_stmt.ancestor.name
|
232
|
+
c_name = ancestor_entry ? ancestor_entry.c_name : 'rb_cObject'
|
233
|
+
rhs = "rb_define_class(\"#{entry.name}\", #{c_name})"
|
234
|
+
code.init_variable lhs: entry.c_name, rhs: rhs
|
235
|
+
end
|
236
|
+
|
237
|
+
# specify allocation method in case of attached class
|
238
|
+
if top_stmt.is_a?(TopStatement::AttachedKlass)
|
239
|
+
entry = top_stmt.entry
|
240
|
+
scope = top_stmt.scope
|
241
|
+
alloc = ""
|
242
|
+
alloc << "rb_define_alloc_func(#{entry.c_name}, "
|
243
|
+
alloc << "#{scope.find(TopStatement::AttachedKlass::ALLOC_FUNC_NAME).c_name});\n"
|
244
|
+
|
245
|
+
code << alloc
|
246
|
+
end
|
63
247
|
end
|
64
|
-
|
248
|
+
code.nl
|
65
249
|
|
66
|
-
|
67
|
-
|
250
|
+
@statements.each do |top_stmt|
|
251
|
+
if top_stmt.is_a? TopStatement::Klass
|
252
|
+
entry = @scope.find top_stmt.name
|
253
|
+
klass_scope = entry.type.scope
|
254
|
+
klass_scope.ruby_method_entries.each do |meth|
|
255
|
+
if meth.singleton?
|
256
|
+
code.write_singleton_method klass: entry.c_name,
|
257
|
+
method_name: meth.name, method_c_name: meth.c_name
|
258
|
+
else
|
259
|
+
code.write_instance_method klass: entry.c_name,
|
260
|
+
method_name: meth.name, method_c_name: meth.c_name
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
68
266
|
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
267
|
+
end # class Node
|
268
|
+
end # module AST
|
269
|
+
end # module Rubex
|
data/lib/rubex/ast/statement.rb
CHANGED
@@ -1,54 +1,1181 @@
|
|
1
|
+
include Rubex::DataType
|
2
|
+
|
1
3
|
module Rubex
|
2
4
|
module AST
|
3
|
-
|
4
|
-
class
|
5
|
-
|
5
|
+
module Statement
|
6
|
+
class Base
|
7
|
+
include Rubex::Helpers::NodeTypeMethods
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
# File name and line number of statement in "file_name:lineno" format.
|
10
|
+
attr_reader :location
|
11
|
+
|
12
|
+
def initialize location
|
13
|
+
@location = location
|
14
|
+
end
|
15
|
+
|
16
|
+
def statement?; true; end
|
17
|
+
|
18
|
+
def == other
|
19
|
+
self.class == other.class
|
20
|
+
end
|
21
|
+
|
22
|
+
def generate_code code, local_scope
|
23
|
+
code.write_location @location
|
24
|
+
end
|
25
|
+
end # class Base
|
26
|
+
|
27
|
+
class CBaseType < Base
|
28
|
+
attr_reader :type, :name, :value
|
29
|
+
|
30
|
+
def initialize type, name, value=nil
|
31
|
+
@type, @name, @value = type, name, value
|
9
32
|
end
|
10
33
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
34
|
+
def == other
|
35
|
+
self.class == other.class &&
|
36
|
+
self.type == other.class &&
|
37
|
+
self.name == other.name &&
|
38
|
+
self.value == other.value
|
39
|
+
end
|
40
|
+
|
41
|
+
def analyse_statement local_scope
|
42
|
+
@type = Rubex::Helpers.determine_dtype @type
|
43
|
+
end
|
44
|
+
end # class CBaseType
|
45
|
+
|
46
|
+
class VarDecl < Base
|
47
|
+
# The name with which this particular variable can be identified with
|
48
|
+
# in the symbol table.
|
49
|
+
attr_reader :name
|
50
|
+
attr_reader :type, :value
|
51
|
+
|
52
|
+
def initialize type, name, value, location
|
53
|
+
super(location)
|
54
|
+
@name, @value = name, value
|
55
|
+
@type = type
|
56
|
+
end
|
57
|
+
|
58
|
+
def analyse_statement local_scope, extern: false
|
59
|
+
# TODO: Have type checks for knowing if correct literal assignment
|
60
|
+
# is taking place. For example, a char should not be assigned a float.
|
61
|
+
@type = Helpers.determine_dtype @type, ""
|
62
|
+
c_name = extern ? @name : Rubex::VAR_PREFIX + @name
|
63
|
+
if @value
|
64
|
+
@value.analyse_for_target_type(@type, local_scope)
|
65
|
+
@value.allocate_temp local_scope, @value.type
|
66
|
+
@value = Helpers.to_lhs_type(self, @value)
|
67
|
+
@value.release_temp local_scope
|
16
68
|
end
|
17
69
|
|
18
|
-
|
19
|
-
|
70
|
+
local_scope.declare_var name: @name, c_name: c_name, type: @type,
|
71
|
+
value: @value, extern: extern
|
72
|
+
end
|
20
73
|
|
21
|
-
|
22
|
-
|
23
|
-
|
74
|
+
def rescan_declarations scope
|
75
|
+
if @type.is_a? String
|
76
|
+
@type = Rubex::CUSTOM_TYPES[@type]
|
77
|
+
scope[@name].type = @type
|
78
|
+
end
|
24
79
|
end
|
25
80
|
|
26
81
|
def generate_code code, local_scope
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
code << "("
|
34
|
-
code << local_scope[left].c_name
|
35
|
-
code << " + "
|
36
|
-
code << local_scope[right].c_name
|
37
|
-
code << ")"
|
38
|
-
code << ";\n"
|
82
|
+
if @value
|
83
|
+
@value.generate_evaluation_code code, local_scope
|
84
|
+
lhs = local_scope.find(@name).c_name
|
85
|
+
code << "#{lhs} = #{@value.c_code(local_scope)};"
|
86
|
+
code.nl
|
87
|
+
@value.generate_disposal_code code
|
39
88
|
end
|
40
89
|
end
|
90
|
+
end # class VarDecl
|
91
|
+
|
92
|
+
class CPtrDecl < Base
|
93
|
+
attr_reader :entry, :type
|
94
|
+
|
95
|
+
# type - Specifies the type of the pointer. Is a string in case of a
|
96
|
+
# normal pointer denoting the data type and pointer level (like `int`
|
97
|
+
# for a pointerto an integer). Can be a Hash in case of func pointer
|
98
|
+
# declaration.
|
99
|
+
# name [String] - name of the variable.
|
100
|
+
def initialize type, name, value, ptr_level, location
|
101
|
+
super(location)
|
102
|
+
@name, @type, @value, @ptr_level = name, type, value, ptr_level
|
103
|
+
end
|
104
|
+
|
105
|
+
def analyse_statement local_scope, extern: false
|
106
|
+
c_name = extern ? @name : Rubex::POINTER_PREFIX + @name
|
107
|
+
|
108
|
+
if @type.is_a?(Hash) # function ptr
|
109
|
+
ident = @type[:ident]
|
110
|
+
ident[:arg_list].analyse_statement(local_scope, inside_func_ptr: true)
|
111
|
+
@type = DataType::CFunction.new(
|
112
|
+
@name,
|
113
|
+
c_name,
|
114
|
+
ident[:arg_list],
|
115
|
+
Helpers.determine_dtype(@type[:dtype], ident[:return_ptr_level]),
|
116
|
+
nil
|
117
|
+
)
|
118
|
+
end
|
119
|
+
@type = Helpers.determine_dtype @type, @ptr_level
|
120
|
+
if @value
|
121
|
+
@value.analyse_for_target_type(@type, local_scope)
|
122
|
+
@value = Helpers.to_lhs_type(self, @value)
|
123
|
+
end
|
41
124
|
|
42
|
-
|
125
|
+
@entry = local_scope.declare_var name: @name, c_name: c_name,
|
126
|
+
type: @type, value: @value, extern: extern
|
127
|
+
end
|
43
128
|
|
44
|
-
|
45
|
-
|
129
|
+
# FIXME: This feels jugaadu. Try to scan all declarations before you
|
130
|
+
# scan individual statements.
|
131
|
+
def rescan_declarations local_scope
|
132
|
+
base_type = @entry.type.base_type
|
133
|
+
if base_type.is_a? String
|
134
|
+
type = Helpers.determine_dtype base_type, @ptr_level
|
135
|
+
local_scope[@name].type = type
|
136
|
+
end
|
137
|
+
end
|
46
138
|
|
47
|
-
|
48
|
-
|
139
|
+
def generate_code code, local_scope
|
140
|
+
if @value
|
141
|
+
@value.generate_evaluation_code code, local_scope
|
142
|
+
code << "#{local_scope.find(@name).c_name} = #{@value.c_code(local_scope)};"
|
143
|
+
code.nl
|
144
|
+
@value.generate_disposal_code code
|
49
145
|
end
|
50
146
|
end
|
51
147
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
148
|
+
|
149
|
+
class CArrayDecl < Base
|
150
|
+
attr_reader :type, :array_list, :name, :dimension
|
151
|
+
|
152
|
+
def initialize type, array_ref, array_list, location
|
153
|
+
super(location)
|
154
|
+
@name, @array_list = array_ref.name, array_list
|
155
|
+
@dimension = array_ref.pos
|
156
|
+
@type = Rubex::TYPE_MAPPINGS[type].new
|
157
|
+
end
|
158
|
+
|
159
|
+
def analyse_statement local_scope, extern: false
|
160
|
+
@dimension.analyse_statement local_scope
|
161
|
+
create_symbol_table_entry local_scope
|
162
|
+
return if @array_list.nil?
|
163
|
+
analyse_array_list local_scope
|
164
|
+
verify_array_list_types local_scope
|
165
|
+
end
|
166
|
+
|
167
|
+
def generate_code code, local_scope
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
def rescan_declarations local_scope
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def analyse_array_list local_scope
|
178
|
+
@array_list.each do |expr|
|
179
|
+
expr.analyse_statement(local_scope)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def verify_array_list_types local_scope
|
184
|
+
@array_list.all? do |expr|
|
185
|
+
return true if @type >= expr.type
|
186
|
+
raise "Specified type #{@type} but list contains #{expr.type}."
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def create_symbol_table_entry local_scope
|
191
|
+
local_scope.add_carray(name: @name, c_name: Rubex::ARRAY_PREFIX + @name,
|
192
|
+
dimension: @dimension, type: @type, value: @array_list)
|
193
|
+
end
|
194
|
+
end # class CArrayDecl
|
195
|
+
|
196
|
+
class CStructOrUnionDef < Base
|
197
|
+
attr_reader :name, :declarations, :type, :kind, :entry, :scope
|
198
|
+
|
199
|
+
def initialize kind, name, declarations, location
|
200
|
+
super(location)
|
201
|
+
@declarations = declarations
|
202
|
+
if /struct/.match kind
|
203
|
+
@kind = :struct
|
204
|
+
elsif /union/.match kind
|
205
|
+
@kind = :union
|
206
|
+
end
|
207
|
+
@name = name
|
208
|
+
end
|
209
|
+
|
210
|
+
def analyse_statement outer_scope, extern: false
|
211
|
+
@scope = Rubex::SymbolTable::Scope::StructOrUnion.new(
|
212
|
+
@name, outer_scope)
|
213
|
+
if extern
|
214
|
+
c_name = @kind.to_s + " " + @name
|
215
|
+
else
|
216
|
+
c_name = Rubex::TYPE_PREFIX + @scope.klass_name + "_" + @name
|
217
|
+
end
|
218
|
+
@type = Rubex::DataType::CStructOrUnion.new(@kind, @name, c_name,
|
219
|
+
@scope)
|
220
|
+
|
221
|
+
@declarations.each do |decl|
|
222
|
+
decl.analyse_statement @scope, extern: extern
|
223
|
+
end
|
224
|
+
Rubex::CUSTOM_TYPES[@name] = @type
|
225
|
+
@entry = outer_scope.declare_sue(name: @name, c_name: c_name,
|
226
|
+
type: @type, extern: extern)
|
227
|
+
end
|
228
|
+
|
229
|
+
def generate_code code, local_scope=nil
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
def rescan_declarations local_scope
|
234
|
+
@declarations.each do |decl|
|
235
|
+
decl.respond_to?(:rescan_declarations) and
|
236
|
+
decl.rescan_declarations(@scope)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class ForwardDecl < Base
|
242
|
+
attr_reader :kind, :name, :type, :c_name
|
243
|
+
|
244
|
+
def initialize kind, name, location
|
245
|
+
super(location)
|
246
|
+
@name = name
|
247
|
+
if /struct/.match kind
|
248
|
+
@kind = :struct
|
249
|
+
elsif /union/.match kind
|
250
|
+
@kind = :union
|
251
|
+
end
|
252
|
+
Rubex::CUSTOM_TYPES[@name] = @name
|
253
|
+
end
|
254
|
+
|
255
|
+
def analyse_statement local_scope, extern: false
|
256
|
+
@c_name = Rubex::TYPE_PREFIX + local_scope.klass_name + "_" + @name
|
257
|
+
@type = Rubex::DataType::TypeDef.new("#{@kind} #{@name}", @c_name, type)
|
258
|
+
local_scope.declare_type type: @type, extern: extern
|
259
|
+
end
|
260
|
+
|
261
|
+
def rescan_declarations local_scope
|
262
|
+
@type = Rubex::DataType::TypeDef.new("#{@kind} #{@name}", @c_name,
|
263
|
+
Rubex::CUSTOM_TYPES[@name])
|
264
|
+
end
|
265
|
+
|
266
|
+
def generate_code code, local_scope
|
267
|
+
|
268
|
+
end
|
269
|
+
end # class ForwardDecl
|
270
|
+
|
271
|
+
class Print < Base
|
272
|
+
# An Array containing expressions that are passed to the print statement.
|
273
|
+
# Can either contain a single string containing interpolated exprs or
|
274
|
+
# a set of comma separated exprs. For example, the print statement can
|
275
|
+
# either be of like:
|
276
|
+
# print "Hello #{a} world!"
|
277
|
+
# OR
|
278
|
+
# print "Hello", a, " world!"
|
279
|
+
attr_reader :expressions
|
280
|
+
|
281
|
+
def initialize expressions, location
|
282
|
+
super(location)
|
283
|
+
@expressions = expressions
|
284
|
+
end
|
285
|
+
|
286
|
+
def analyse_statement local_scope
|
287
|
+
@expressions.each do |expr|
|
288
|
+
expr.analyse_statement local_scope
|
289
|
+
expr.allocate_temps local_scope
|
290
|
+
expr.allocate_temp local_scope, expr.type
|
291
|
+
expr.release_temps local_scope
|
292
|
+
expr.release_temp local_scope
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def generate_code code, local_scope
|
297
|
+
super
|
298
|
+
@expressions.each do |expr|
|
299
|
+
expr.generate_evaluation_code code, local_scope
|
300
|
+
|
301
|
+
str = "printf("
|
302
|
+
str << "\"#{expr.type.p_formatter}\""
|
303
|
+
str << ", #{inspected_expr(expr, local_scope)}"
|
304
|
+
str << ");"
|
305
|
+
code << str
|
306
|
+
code.nl
|
307
|
+
|
308
|
+
expr.generate_disposal_code code
|
309
|
+
end
|
310
|
+
|
311
|
+
code.nl
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
315
|
+
|
316
|
+
def inspected_expr expr, local_scope
|
317
|
+
obj = expr.c_code(local_scope)
|
318
|
+
if expr.type.object?
|
319
|
+
"RSTRING_PTR(rb_funcall(#{obj}, rb_intern(\"inspect\"), 0, NULL))"
|
320
|
+
else
|
321
|
+
obj
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end # class Print
|
325
|
+
|
326
|
+
class Return < Base
|
327
|
+
attr_reader :expression, :type
|
328
|
+
|
329
|
+
def initialize expression, location
|
330
|
+
super(location)
|
331
|
+
@expression = expression
|
332
|
+
end
|
333
|
+
|
334
|
+
def analyse_statement local_scope
|
335
|
+
unless @expression
|
336
|
+
if local_scope.type.ruby_method?
|
337
|
+
@expression = Rubex::AST::Expression::Literal::Nil.new 'Qnil'
|
338
|
+
elsif local_scope.type.c_function?
|
339
|
+
@expression = Rubex::AST::Expression::Empty.new
|
340
|
+
end # FIXME: print a warning for type mismatch if none of above
|
341
|
+
end
|
342
|
+
|
343
|
+
@expression.analyse_statement local_scope
|
344
|
+
@expression.allocate_temps local_scope
|
345
|
+
@expression.allocate_temp local_scope, @expression.type
|
346
|
+
@expression.release_temps local_scope
|
347
|
+
@expression.release_temp local_scope
|
348
|
+
t = @expression.type
|
349
|
+
|
350
|
+
@type =
|
351
|
+
if t.c_function? || t.alias_type?
|
352
|
+
t.type
|
353
|
+
else
|
354
|
+
t
|
355
|
+
end
|
356
|
+
@expression = @expression.to_ruby_object if local_scope.type.type.object?
|
357
|
+
|
358
|
+
# TODO: Raise error if type as inferred from the
|
359
|
+
# is not compatible with the return statement type.
|
360
|
+
end
|
361
|
+
|
362
|
+
def generate_code code, local_scope
|
363
|
+
super
|
364
|
+
@expression.generate_evaluation_code code, local_scope
|
365
|
+
code << "return #{@expression.c_code(local_scope)};"
|
366
|
+
code.nl
|
367
|
+
end
|
368
|
+
end # class Return
|
369
|
+
|
370
|
+
class Assign < Base
|
371
|
+
attr_reader :lhs, :rhs
|
372
|
+
|
373
|
+
def initialize lhs, rhs, location
|
374
|
+
super(location)
|
375
|
+
@lhs, @rhs = lhs, rhs
|
376
|
+
end
|
377
|
+
|
378
|
+
def analyse_statement local_scope
|
379
|
+
if @lhs.is_a?(Rubex::AST::Expression::Name)
|
380
|
+
@lhs.analyse_declaration @rhs, local_scope
|
381
|
+
else
|
382
|
+
@lhs.analyse_statement(local_scope)
|
383
|
+
end
|
384
|
+
@lhs.allocate_temps local_scope
|
385
|
+
@lhs.allocate_temp local_scope, @lhs.type
|
386
|
+
|
387
|
+
@rhs.analyse_for_target_type(@lhs.type, local_scope)
|
388
|
+
@rhs = Helpers.to_lhs_type(@lhs, @rhs)
|
389
|
+
|
390
|
+
@rhs.allocate_temps local_scope
|
391
|
+
@rhs.allocate_temp local_scope, @rhs.type
|
392
|
+
|
393
|
+
@lhs.release_temps local_scope
|
394
|
+
@lhs.release_temp local_scope
|
395
|
+
@rhs.release_temps local_scope
|
396
|
+
@rhs.release_temp local_scope
|
397
|
+
end
|
398
|
+
|
399
|
+
def generate_code code, local_scope
|
400
|
+
super
|
401
|
+
@rhs.generate_evaluation_code code, local_scope
|
402
|
+
@lhs.generate_assignment_code @rhs, code, local_scope
|
403
|
+
@rhs.generate_disposal_code code
|
404
|
+
end
|
405
|
+
end # class Assign
|
406
|
+
|
407
|
+
class IfBlock < Base
|
408
|
+
module Helper
|
409
|
+
def analyse_statement local_scope
|
410
|
+
@statements.each do |stat|
|
411
|
+
stat.analyse_statement local_scope
|
412
|
+
end
|
413
|
+
|
414
|
+
@if_tail.analyse_statement(local_scope) if @if_tail
|
415
|
+
end
|
416
|
+
|
417
|
+
def generate_code_for_statement stat, code, local_scope, node
|
418
|
+
if stat != "else"
|
419
|
+
condition = node.expr.c_code(local_scope)
|
420
|
+
expr_condition = node.expr.type.object? ? "RTEST(#{condition})" : condition
|
421
|
+
code << "#{stat} (#{expr_condition}) "
|
422
|
+
else
|
423
|
+
code << "#{stat}"
|
424
|
+
end
|
425
|
+
|
426
|
+
code.block do
|
427
|
+
node.statements.each do |stat|
|
428
|
+
stat.generate_code code, local_scope
|
429
|
+
code.nl
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
if stat != "else"
|
434
|
+
node.if_tail.generate_code(code, local_scope) if node.if_tail
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end # module Helper
|
438
|
+
|
439
|
+
attr_reader :expr, :statements, :if_tail
|
440
|
+
include Rubex::AST::Statement::IfBlock::Helper
|
441
|
+
|
442
|
+
def initialize expr, statements, if_tail, location
|
443
|
+
super(location)
|
444
|
+
@expr, @statements, @if_tail = expr, statements, if_tail
|
445
|
+
end
|
446
|
+
|
447
|
+
def analyse_statement local_scope
|
448
|
+
@tail_exprs = if_tail_exprs
|
449
|
+
@tail_exprs.each do |tail|
|
450
|
+
tail.analyse_statement local_scope
|
451
|
+
tail.allocate_temps local_scope
|
452
|
+
tail.allocate_temp local_scope, tail.type
|
453
|
+
end
|
454
|
+
@tail_exprs.each do |tail|
|
455
|
+
tail.release_temps local_scope
|
456
|
+
tail.release_temp local_scope
|
457
|
+
end
|
458
|
+
super
|
459
|
+
end
|
460
|
+
|
461
|
+
def if_tail_exprs
|
462
|
+
tail_exprs = []
|
463
|
+
temp = self
|
464
|
+
while temp.respond_to?(:if_tail) &&
|
465
|
+
!temp.is_a?(Rubex::AST::Statement::IfBlock::Else)
|
466
|
+
tail_exprs << temp.expr
|
467
|
+
temp = temp.if_tail
|
468
|
+
end
|
469
|
+
|
470
|
+
tail_exprs
|
471
|
+
end
|
472
|
+
|
473
|
+
def generate_code code, local_scope
|
474
|
+
@tail_exprs.each do |tail|
|
475
|
+
tail.generate_evaluation_code(code, local_scope)
|
476
|
+
end
|
477
|
+
generate_code_for_statement "if", code, local_scope, self
|
478
|
+
|
479
|
+
tail = @if_tail
|
480
|
+
while tail
|
481
|
+
if tail.is_a?(Elsif)
|
482
|
+
generate_code_for_statement "else if", code, local_scope, tail
|
483
|
+
elsif tail.is_a?(Else)
|
484
|
+
generate_code_for_statement "else", code, local_scope, tail
|
485
|
+
end
|
486
|
+
tail = tail.if_tail
|
487
|
+
end
|
488
|
+
|
489
|
+
@tail_exprs.each do |tail|
|
490
|
+
tail.generate_disposal_code code
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
class Elsif < Base
|
495
|
+
attr_reader :expr, :statements, :if_tail
|
496
|
+
include Rubex::AST::Statement::IfBlock::Helper
|
497
|
+
|
498
|
+
def initialize expr, statements, if_tail, location
|
499
|
+
super(location)
|
500
|
+
@expr, @statements, @if_tail = expr, statements, if_tail
|
501
|
+
end
|
502
|
+
|
503
|
+
def generate_code code, local_scope
|
504
|
+
end
|
505
|
+
end # class Elsif
|
506
|
+
|
507
|
+
class Else < Base
|
508
|
+
attr_reader :statements
|
509
|
+
include Rubex::AST::Statement::IfBlock::Helper
|
510
|
+
|
511
|
+
def initialize statements, location
|
512
|
+
super(location)
|
513
|
+
@statements = statements
|
514
|
+
end
|
515
|
+
|
516
|
+
def analyse_statement local_scope
|
517
|
+
@statements.each do |stat|
|
518
|
+
stat.analyse_statement local_scope
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def generate_code code, local_scope
|
523
|
+
end
|
524
|
+
|
525
|
+
def if_tail; nil; end
|
526
|
+
end # class Else
|
527
|
+
end # class IfBlock
|
528
|
+
|
529
|
+
class For < Base
|
530
|
+
attr_reader :left_expr, :left_op, :middle, :right_op, :right_expr,
|
531
|
+
:statements, :order
|
532
|
+
|
533
|
+
def initialize left_expr, left_op, middle, right_op, right_expr,
|
534
|
+
statements, location
|
535
|
+
super(location)
|
536
|
+
@left_expr, @left_op, @middle, @right_op, @right_expr =
|
537
|
+
left_expr, left_op, middle, right_op, right_expr
|
538
|
+
@statements, @order = statements, order
|
539
|
+
end
|
540
|
+
|
541
|
+
def analyse_statement local_scope
|
542
|
+
@left_expr.analyse_statement local_scope
|
543
|
+
@right_expr.analyse_statement local_scope
|
544
|
+
|
545
|
+
[ @left_expr, @right_expr ].each do |e|
|
546
|
+
e.allocate_temps local_scope
|
547
|
+
e.allocate_temp local_scope, e.type
|
548
|
+
end
|
549
|
+
[ @left_expr, @right_expr ].each do |e|
|
550
|
+
e.release_temps local_scope
|
551
|
+
e.release_temp local_scope
|
552
|
+
end
|
553
|
+
|
554
|
+
@middle = local_scope[@middle] # middle will not be an expr.
|
555
|
+
@statements.each do |stat|
|
556
|
+
stat.analyse_statement local_scope
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
def generate_code code, local_scope
|
561
|
+
code << for_loop_header(code, local_scope)
|
562
|
+
code.block do
|
563
|
+
@statements.each do |stat|
|
564
|
+
stat.generate_code code, local_scope
|
565
|
+
end
|
566
|
+
end
|
567
|
+
@left_expr.generate_disposal_code code
|
568
|
+
@right_expr.generate_disposal_code code
|
569
|
+
end
|
570
|
+
|
571
|
+
private
|
572
|
+
|
573
|
+
def for_loop_header code, local_scope
|
574
|
+
@left_expr.generate_evaluation_code code, local_scope
|
575
|
+
@right_expr.generate_evaluation_code code, local_scope
|
576
|
+
for_stmt = ""
|
577
|
+
for_stmt << "for (#{@middle.c_name} = #{@left_expr.c_code(local_scope)}"
|
578
|
+
|
579
|
+
if @left_op == '<'
|
580
|
+
for_stmt << " + 1"
|
581
|
+
elsif @left_op == '>'
|
582
|
+
for_stmt << " - 1"
|
583
|
+
end
|
584
|
+
|
585
|
+
for_stmt << "; #{@middle.c_name} #{@right_op} #{@right_expr.c_code(local_scope)}; "
|
586
|
+
for_stmt << "#{@middle.c_name}"
|
587
|
+
|
588
|
+
if ['>', '>='].include? @right_op
|
589
|
+
for_stmt << "--"
|
590
|
+
elsif ['<', '<='].include? @right_op
|
591
|
+
for_stmt << "++"
|
592
|
+
end
|
593
|
+
|
594
|
+
for_stmt << ")"
|
595
|
+
|
596
|
+
for_stmt
|
597
|
+
end
|
598
|
+
end # class For
|
599
|
+
|
600
|
+
class While < Base
|
601
|
+
attr_reader :expr, :statements
|
602
|
+
|
603
|
+
def initialize expr, statements, location
|
604
|
+
super(location)
|
605
|
+
@expr, @statements = expr, statements
|
606
|
+
end
|
607
|
+
|
608
|
+
def analyse_statement local_scope
|
609
|
+
@expr.analyse_statement local_scope
|
610
|
+
@expr.allocate_temp local_scope, @expr.type
|
611
|
+
@expr.release_temp local_scope
|
612
|
+
@statements.each do |stat|
|
613
|
+
stat.analyse_statement local_scope
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
def generate_code code, local_scope
|
618
|
+
@expr.generate_evaluation_code code, local_scope
|
619
|
+
stmt = "while (#{@expr.c_code(local_scope)})"
|
620
|
+
code << stmt
|
621
|
+
code.block do
|
622
|
+
@statements.each do |stat|
|
623
|
+
stat.generate_code code, local_scope
|
624
|
+
end
|
625
|
+
end
|
626
|
+
@expr.generate_disposal_code code
|
627
|
+
end
|
628
|
+
end # class While
|
629
|
+
|
630
|
+
class Alias < Base
|
631
|
+
attr_reader :new_name, :type, :old_name
|
632
|
+
|
633
|
+
def initialize new_name, old_name, location
|
634
|
+
super(location)
|
635
|
+
@new_name, @old_name = new_name, old_name
|
636
|
+
Rubex::CUSTOM_TYPES[@new_name] = @new_name
|
637
|
+
end
|
638
|
+
|
639
|
+
def analyse_statement local_scope, extern: false
|
640
|
+
original = @old_name[:dtype].gsub("struct ", "").gsub("union ", "")
|
641
|
+
var = @old_name[:variables][0]
|
642
|
+
ident = var[:ident]
|
643
|
+
ptr_level = var[:ptr_level]
|
644
|
+
|
645
|
+
base_type =
|
646
|
+
if ident.is_a?(Hash) # function pointer
|
647
|
+
cfunc_return_type = Helpers.determine_dtype(original,
|
648
|
+
ident[:return_ptr_level])
|
649
|
+
arg_list = ident[:arg_list].analyse_statement(local_scope,
|
650
|
+
inside_func_ptr: true)
|
651
|
+
ptr_level = "*" if ptr_level.empty?
|
652
|
+
|
653
|
+
Helpers.determine_dtype(
|
654
|
+
DataType::CFunction.new(nil, nil, arg_list, cfunc_return_type, nil),
|
655
|
+
ptr_level)
|
656
|
+
else
|
657
|
+
Helpers.determine_dtype(original, ptr_level)
|
658
|
+
end
|
659
|
+
|
660
|
+
@type = Rubex::DataType::TypeDef.new(base_type, @new_name, base_type)
|
661
|
+
Rubex::CUSTOM_TYPES[@new_name] = @type
|
662
|
+
local_scope.declare_type(type: @type, extern: extern) if original != @new_name
|
663
|
+
end
|
664
|
+
|
665
|
+
def generate_code code, local_scope
|
666
|
+
|
667
|
+
end
|
668
|
+
end # class Alias
|
669
|
+
|
670
|
+
class Expression < Base
|
671
|
+
attr_reader :expr
|
672
|
+
attr_accessor :typecast
|
673
|
+
|
674
|
+
def initialize expr, location
|
675
|
+
super(location)
|
676
|
+
@expr = expr
|
677
|
+
end
|
678
|
+
|
679
|
+
def analyse_statement local_scope
|
680
|
+
@expr.analyse_statement local_scope
|
681
|
+
@expr.allocate_temps local_scope
|
682
|
+
@expr.allocate_temp local_scope, @expr.type
|
683
|
+
end
|
684
|
+
|
685
|
+
def generate_code code, local_scope
|
686
|
+
super
|
687
|
+
@expr.generate_evaluation_code code, local_scope
|
688
|
+
code << @expr.c_code(local_scope) + ";"
|
689
|
+
code.nl
|
690
|
+
@expr.generate_disposal_code code
|
691
|
+
end
|
692
|
+
end # class Expression
|
693
|
+
|
694
|
+
class CFunctionDecl < Base
|
695
|
+
attr_reader :entry
|
696
|
+
|
697
|
+
def initialize type, return_ptr_level, name, arg_list
|
698
|
+
@type, @return_ptr_level, @name, @arg_list = type, return_ptr_level,
|
699
|
+
name, arg_list
|
700
|
+
end
|
701
|
+
|
702
|
+
def analyse_statement local_scope, extern: false
|
703
|
+
@arg_list.analyse_statement(local_scope, extern: extern) if @arg_list
|
704
|
+
c_name = extern ? @name : (Rubex::C_FUNC_PREFIX + @name)
|
705
|
+
# type = Rubex::DataType::CFunction.new(@name, c_name, @arg_list,
|
706
|
+
# Helpers.determine_dtype(@type, @return_ptr_level), nil)
|
707
|
+
@entry = local_scope.add_c_method(
|
708
|
+
name: @name,
|
709
|
+
c_name: c_name,
|
710
|
+
return_type: Helpers.determine_dtype(@type, @return_ptr_level),
|
711
|
+
arg_list: @arg_list,
|
712
|
+
scope: nil,
|
713
|
+
extern: extern)
|
714
|
+
end
|
715
|
+
|
716
|
+
def generate_code code, local_scope
|
717
|
+
super
|
718
|
+
code << "/* C function #{@name} declared.*/" if @entry.extern?
|
719
|
+
end
|
720
|
+
end # class CFunctionDecl
|
721
|
+
|
722
|
+
# This node is used for both formal and actual arguments of functions/methods.
|
723
|
+
class ArgumentList < Base
|
724
|
+
include Enumerable
|
725
|
+
|
726
|
+
# args - [ArgDeclaration]
|
727
|
+
attr_reader :args
|
728
|
+
|
729
|
+
def each &block
|
730
|
+
@args.each(&block)
|
731
|
+
end
|
732
|
+
|
733
|
+
def map! &block
|
734
|
+
@args.map!(&block)
|
735
|
+
end
|
736
|
+
|
737
|
+
def pop
|
738
|
+
@args.pop
|
739
|
+
end
|
740
|
+
|
741
|
+
def initialize args
|
742
|
+
@args = args
|
743
|
+
end
|
744
|
+
|
745
|
+
# func_ptr - switch that determines if this ArgList is part of the
|
746
|
+
# argument list of an argument that is a function pointer.
|
747
|
+
# For eg -
|
748
|
+
# cfunc int foo(int (*bar)(int, float)).
|
749
|
+
# ^^^ This is an arg list inside a function.
|
750
|
+
def analyse_statement local_scope, inside_func_ptr: false, extern: false
|
751
|
+
@args.each do |arg|
|
752
|
+
arg.analyse_statement(local_scope, inside_func_ptr: inside_func_ptr,
|
753
|
+
extern: extern)
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
def push arg
|
758
|
+
@args << arg
|
759
|
+
end
|
760
|
+
|
761
|
+
def << arg
|
762
|
+
push arg
|
763
|
+
end
|
764
|
+
|
765
|
+
def == other
|
766
|
+
self.class == other.class && @args == other.args
|
767
|
+
end
|
768
|
+
|
769
|
+
def size
|
770
|
+
@args.size
|
771
|
+
end
|
772
|
+
|
773
|
+
def empty?
|
774
|
+
@args.empty?
|
775
|
+
end
|
776
|
+
|
777
|
+
def [] idx
|
778
|
+
@args[idx]
|
779
|
+
end
|
780
|
+
end # class ArgumentList
|
781
|
+
|
782
|
+
class ActualArgList < ArgumentList
|
783
|
+
def analyse_statement local_scope
|
784
|
+
@args.each do |arg|
|
785
|
+
arg.analyse_statement local_scope
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
def allocate_temps local_scope
|
790
|
+
@args.each { |a| a.allocate_temp(local_scope, a.type) }
|
791
|
+
end
|
792
|
+
|
793
|
+
def release_temps local_scope
|
794
|
+
@args.each { |a| a.release_temp(local_scope) }
|
795
|
+
end
|
796
|
+
|
797
|
+
def generate_evaluation_code code, local_scope
|
798
|
+
@args.each do |a|
|
799
|
+
a.generate_evaluation_code code, local_scope
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
def generate_disposal_code code
|
804
|
+
@args.each do |a|
|
805
|
+
a.generate_disposal_code code
|
806
|
+
end
|
807
|
+
end
|
808
|
+
end # class ActualArgList
|
809
|
+
|
810
|
+
class Raise < Base
|
811
|
+
def initialize args
|
812
|
+
@args = args
|
813
|
+
end
|
814
|
+
|
815
|
+
def analyse_statement local_scope
|
816
|
+
@args.analyse_statement local_scope
|
817
|
+
@args.allocate_temps local_scope
|
818
|
+
@args.release_temps local_scope
|
819
|
+
unless @args.empty? || @args[0].is_a?(AST::Expression::Name) ||
|
820
|
+
@args[0].is_a?(AST::Expression::Literal::StringLit)
|
821
|
+
raise Rubex::TypeMismatchError, "Wrong argument list #{@args.inspect} for raise."
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
def generate_code code, local_scope
|
826
|
+
@args.generate_evaluation_code code, local_scope
|
827
|
+
str = ""
|
828
|
+
str << "rb_raise("
|
829
|
+
|
830
|
+
if @args[0].is_a?(AST::Expression::Name)
|
831
|
+
str << @args[0].c_code(local_scope) + ','
|
832
|
+
args = @args[1..-1]
|
833
|
+
else
|
834
|
+
str << Rubex::DEFAULT_CLASS_MAPPINGS["RuntimeError"] + ','
|
835
|
+
args = @args
|
836
|
+
end
|
837
|
+
|
838
|
+
unless args.empty?
|
839
|
+
str << "\"#{prepare_format_string(args)}\" ,"
|
840
|
+
str << args.map { |arg| "#{inspected_expr(arg, local_scope)}" }.join(',')
|
841
|
+
else
|
842
|
+
str << "\"\""
|
843
|
+
end
|
844
|
+
str << ");"
|
845
|
+
code << str
|
846
|
+
code.nl
|
847
|
+
@args.generate_disposal_code code
|
848
|
+
end
|
849
|
+
|
850
|
+
private
|
851
|
+
|
852
|
+
def prepare_format_string args
|
853
|
+
format_string = ""
|
854
|
+
args.each do |expr|
|
855
|
+
format_string << expr.type.p_formatter
|
856
|
+
end
|
857
|
+
|
858
|
+
format_string
|
859
|
+
end
|
860
|
+
|
861
|
+
def inspected_expr expr, local_scope
|
862
|
+
obj = expr.c_code(local_scope)
|
863
|
+
if expr.type.object?
|
864
|
+
"RSTRING_PTR(rb_funcall(#{obj}, rb_intern(\"inspect\"), 0, NULL))"
|
865
|
+
else
|
866
|
+
obj
|
867
|
+
end
|
868
|
+
end
|
869
|
+
end # class Raise
|
870
|
+
|
871
|
+
class Break < Base
|
872
|
+
def analyse_statement local_scope
|
873
|
+
# TODO: figure whether this is a Ruby break or C break. For now
|
874
|
+
# assuming C break.
|
875
|
+
end
|
876
|
+
|
877
|
+
def generate_code code, local_scope
|
878
|
+
code.write_location @location
|
879
|
+
code << "break;"
|
880
|
+
code.nl
|
881
|
+
end
|
882
|
+
end # class Break
|
883
|
+
|
884
|
+
class Yield < Base
|
885
|
+
def initialize args
|
886
|
+
@args = args
|
887
|
+
end
|
888
|
+
|
889
|
+
def analyse_statement local_scope
|
890
|
+
@args = @args.map do |arg|
|
891
|
+
arg.analyse_statement local_scope
|
892
|
+
arg.allocate_temps local_scope
|
893
|
+
arg.allocate_temp local_scope, arg.type
|
894
|
+
arg.to_ruby_object
|
895
|
+
end
|
896
|
+
|
897
|
+
@args.each do |arg|
|
898
|
+
arg.release_temps local_scope
|
899
|
+
arg.release_temp local_scope
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
def generate_code code, local_scope
|
904
|
+
@args.each do |a|
|
905
|
+
a.generate_evaluation_code code, local_scope
|
906
|
+
end
|
907
|
+
|
908
|
+
if @args.size > 0
|
909
|
+
code << "rb_yield_values(#{@args.size}, "
|
910
|
+
code << "#{@args.map { |a| a.c_code(local_scope) }.join(',')}"
|
911
|
+
code << ");"
|
912
|
+
else
|
913
|
+
code << "rb_yield(Qnil);"
|
914
|
+
end
|
915
|
+
code.nl
|
916
|
+
|
917
|
+
@args.each do |a|
|
918
|
+
a.generate_disposal_code code
|
919
|
+
end
|
920
|
+
end
|
921
|
+
end # class Yield
|
922
|
+
|
923
|
+
module BeginBlock
|
924
|
+
class Base < Statement::Base
|
925
|
+
attr_reader :statements
|
926
|
+
|
927
|
+
def initialize statements, location
|
928
|
+
@statements = statements
|
929
|
+
super(location)
|
930
|
+
end
|
931
|
+
end # class Base
|
932
|
+
|
933
|
+
class Begin < Base
|
934
|
+
def initialize statements, tails, location
|
935
|
+
@tails = tails
|
936
|
+
super(statements, location)
|
937
|
+
end
|
938
|
+
|
939
|
+
def analyse_statement local_scope
|
940
|
+
local_scope.found_begin_block
|
941
|
+
declare_error_state_variable local_scope
|
942
|
+
declare_error_klass_variable local_scope
|
943
|
+
declare_unhandled_error_variable local_scope
|
944
|
+
@block_scope = Rubex::SymbolTable::Scope::BeginBlock.new(
|
945
|
+
block_name(local_scope), local_scope)
|
946
|
+
create_c_function_to_house_statements local_scope.outer_scope
|
947
|
+
analyse_tails local_scope
|
948
|
+
end
|
949
|
+
|
950
|
+
def generate_code code, local_scope
|
951
|
+
code.nl
|
952
|
+
code << "/* begin-rescue-else-ensure-end block begins: */"
|
953
|
+
code.nl
|
954
|
+
super
|
955
|
+
cb_c_name = local_scope.find(@begin_func.name).c_name
|
956
|
+
state_var = local_scope.find(@state_var_name).c_name
|
957
|
+
code << "#{state_var} = 0;\n"
|
958
|
+
code << "rb_protect(#{cb_c_name}, Qnil, &#{state_var});"
|
959
|
+
code.nl
|
960
|
+
generate_rescue_else_ensure code, local_scope
|
961
|
+
end
|
962
|
+
|
963
|
+
private
|
964
|
+
|
965
|
+
def generate_rescue_else_ensure code, local_scope
|
966
|
+
err_state_var = local_scope.find(@error_var_name).c_name
|
967
|
+
set_error_state_variable err_state_var, code, local_scope
|
968
|
+
set_unhandled_error_variable code, local_scope
|
969
|
+
generate_rescue_blocks err_state_var, code, local_scope
|
970
|
+
generate_else_block code, local_scope
|
971
|
+
generate_ensure_block code, local_scope
|
972
|
+
generate_rb_jump_tag err_state_var, code, local_scope
|
973
|
+
code << "rb_set_errinfo(Qnil);\n"
|
974
|
+
end
|
975
|
+
|
976
|
+
def declare_unhandled_error_variable local_scope
|
977
|
+
@unhandled_err_var_name = "begin_block_" + local_scope.begin_block_counter.to_s + "_unhandled_error"
|
978
|
+
local_scope.declare_var(
|
979
|
+
name: @unhandled_err_var_name,
|
980
|
+
c_name: Rubex::VAR_PREFIX + @unhandled_err_var_name,
|
981
|
+
type: DataType::Int.new
|
982
|
+
)
|
983
|
+
end
|
984
|
+
|
985
|
+
def set_unhandled_error_variable code, local_scope
|
986
|
+
n = local_scope.find(@unhandled_err_var_name).c_name
|
987
|
+
code << "#{n} = 0;"
|
988
|
+
code.nl
|
989
|
+
end
|
990
|
+
|
991
|
+
def generate_rb_jump_tag err_state_var, code, local_scope
|
992
|
+
state_var = local_scope.find(@state_var_name).c_name
|
993
|
+
code << "if (#{local_scope.find(@unhandled_err_var_name).c_name})"
|
994
|
+
code.block do
|
995
|
+
code << "rb_jump_tag(#{state_var});"
|
996
|
+
code.nl
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
1000
|
+
def generate_ensure_block code, local_scope
|
1001
|
+
ensure_block = @tails.select { |e| e.is_a?(Ensure) }[0]
|
1002
|
+
ensure_block.generate_code(code, local_scope) unless ensure_block.nil?
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
# We use a goto statement to jump to the ensure block so that when a
|
1006
|
+
# condition arises where no error is raised, the ensure statement will
|
1007
|
+
# be executed, after which rb_jump_tag() will be called.
|
1008
|
+
def generate_else_block code, local_scope
|
1009
|
+
else_block = @tails.select { |t| t.is_a?(Else) }[0]
|
1010
|
+
|
1011
|
+
code << "else"
|
1012
|
+
code.block do
|
1013
|
+
state_var = local_scope.find(@state_var_name).c_name
|
1014
|
+
code << "/* If exception not among those captured in raise */"
|
1015
|
+
code.nl
|
1016
|
+
|
1017
|
+
code << "if (#{state_var})"
|
1018
|
+
code.block do
|
1019
|
+
code << "#{local_scope.find(@unhandled_err_var_name).c_name} = 1;"
|
1020
|
+
code.nl
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
if else_block
|
1024
|
+
code << "else"
|
1025
|
+
code.block do
|
1026
|
+
else_block.generate_code code, local_scope
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
def set_error_state_variable err_state_var, code, local_scope
|
1033
|
+
code << "#{err_state_var} = rb_errinfo();"
|
1034
|
+
code.nl
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
def generate_rescue_blocks err_state_var, code, local_scope
|
1038
|
+
if @tails[0].is_a?(Rescue)
|
1039
|
+
generate_first_rescue_block err_state_var, code, local_scope
|
1040
|
+
|
1041
|
+
@tails[1..-1].grep(Rescue).each do |resc|
|
1042
|
+
else_if_cond = rescue_condition err_state_var, resc, code, local_scope
|
1043
|
+
code << "else if (#{else_if_cond})"
|
1044
|
+
code.block do
|
1045
|
+
resc.generate_code code, local_scope
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
else # no rescue blocks present
|
1049
|
+
code << "if (0) {}\n"
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
def generate_first_rescue_block err_state_var, code, local_scope
|
1054
|
+
code << "if (#{rescue_condition(err_state_var, @tails[0], code, local_scope)})"
|
1055
|
+
code.block do
|
1056
|
+
@tails[0].generate_code code, local_scope
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def rescue_condition err_state_var, resc, code, local_scope
|
1061
|
+
resc.error_klass.generate_evaluation_code code, local_scope
|
1062
|
+
cond = "RTEST(rb_funcall(#{err_state_var}, rb_intern(\"kind_of?\")"
|
1063
|
+
cond << ", 1, #{resc.error_klass.c_code(local_scope)}))"
|
1064
|
+
|
1065
|
+
cond
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def declare_error_state_variable local_scope
|
1069
|
+
@state_var_name = "begin_block_" + local_scope.begin_block_counter.to_s + "_state"
|
1070
|
+
local_scope.declare_var(
|
1071
|
+
name: @state_var_name,
|
1072
|
+
c_name: Rubex::VAR_PREFIX + @state_var_name,
|
1073
|
+
type: DataType::Int.new
|
1074
|
+
)
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def declare_error_klass_variable local_scope
|
1078
|
+
@error_var_name = "begin_block_" + local_scope.begin_block_counter.to_s + "_exec"
|
1079
|
+
local_scope.declare_var(
|
1080
|
+
name: @error_var_name,
|
1081
|
+
c_name: Rubex::VAR_PREFIX + @error_var_name,
|
1082
|
+
type: DataType::RubyObject.new
|
1083
|
+
)
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
def block_name local_scope
|
1087
|
+
"begin_block_" + local_scope.klass_name + "_" + local_scope.name + "_" +
|
1088
|
+
local_scope.begin_block_counter.to_s
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
def create_c_function_to_house_statements scope
|
1092
|
+
func_name = @block_scope.name
|
1093
|
+
arg_list = Statement::ArgumentList.new([
|
1094
|
+
AST::Expression::ArgDeclaration.new(
|
1095
|
+
{ dtype:'object', variables: [{ident: 'dummy'}]})
|
1096
|
+
])
|
1097
|
+
@begin_func = TopStatement::CFunctionDef.new(
|
1098
|
+
'object', '', func_name, arg_list, @statements)
|
1099
|
+
arg_list.analyse_statement @block_scope
|
1100
|
+
add_to_symbol_table @begin_func.name, arg_list, scope
|
1101
|
+
@begin_func.analyse_statement @block_scope
|
1102
|
+
@block_scope.upgrade_symbols_to_global
|
1103
|
+
scope.add_begin_block_callback @begin_func
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
def add_to_symbol_table func_name, arg_list, scope
|
1107
|
+
c_name = Rubex::C_FUNC_PREFIX + func_name
|
1108
|
+
scope.add_c_method(
|
1109
|
+
name: func_name,
|
1110
|
+
c_name: c_name,
|
1111
|
+
extern: false,
|
1112
|
+
return_type: DataType::RubyObject.new,
|
1113
|
+
arg_list: arg_list,
|
1114
|
+
scope: @block_scope
|
1115
|
+
)
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
def analyse_tails local_scope
|
1119
|
+
@tails.each do |tail|
|
1120
|
+
tail.analyse_statement local_scope
|
1121
|
+
end
|
1122
|
+
end
|
1123
|
+
end # class Begin
|
1124
|
+
|
1125
|
+
class Else < Base
|
1126
|
+
def analyse_statement local_scope
|
1127
|
+
@statements.each do |stmt|
|
1128
|
+
stmt.analyse_statement local_scope
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
def generate_code code, local_scope
|
1133
|
+
@statements.each do |stmt|
|
1134
|
+
stmt.generate_code code, local_scope
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
end # class Else
|
1138
|
+
|
1139
|
+
class Rescue < Base
|
1140
|
+
attr_reader :error_klass
|
1141
|
+
|
1142
|
+
def initialize error_klass, error_obj, statements, location
|
1143
|
+
super(statements, location)
|
1144
|
+
@error_klass, @error_obj = error_klass, error_obj
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
def analyse_statement local_scope
|
1148
|
+
@error_klass.analyse_statement local_scope
|
1149
|
+
if !@error_klass.name.type.ruby_constant?
|
1150
|
+
raise "Must pass an error class to raise. Location #{@location}."
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
@statements.each do |stmt|
|
1154
|
+
stmt.analyse_statement local_scope
|
1155
|
+
end
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
def generate_code code, local_scope
|
1159
|
+
@statements.each do |stmt|
|
1160
|
+
stmt.generate_code code, local_scope
|
1161
|
+
end
|
1162
|
+
end
|
1163
|
+
end # class Rescue
|
1164
|
+
|
1165
|
+
class Ensure < Base
|
1166
|
+
def analyse_statement local_scope
|
1167
|
+
@statements.each do |stmt|
|
1168
|
+
stmt.analyse_statement local_scope
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
def generate_code code, local_scope
|
1173
|
+
@statements.each do |stmt|
|
1174
|
+
stmt.generate_code code, local_scope
|
1175
|
+
end
|
1176
|
+
end
|
1177
|
+
end # class Ensure
|
1178
|
+
end # module BeginBlock
|
1179
|
+
end # module Statement
|
1180
|
+
end # module AST
|
1181
|
+
end # module Rubex
|