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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +14 -0
  4. data/CONTRIBUTING.md +101 -0
  5. data/HISTORY.md +3 -0
  6. data/README.md +112 -297
  7. data/REFERENCE.md +753 -0
  8. data/Rakefile +4 -1
  9. data/TUTORIAL.md +234 -0
  10. data/bin/rubex +1 -1
  11. data/docs/_config.yml +1 -0
  12. data/docs/index.html +1 -0
  13. data/examples/c_struct_interface/c_struct_interface.rb +6 -0
  14. data/examples/c_struct_interface/c_struct_interface.rubex +47 -0
  15. data/examples/linked_list/linked_list.rubex +39 -0
  16. data/examples/linked_list/rb_linked_list.rb +8 -0
  17. data/examples/rcsv wrapper/rcsv/README.md +1 -0
  18. data/examples/rcsv wrapper/rcsv/Rakefile +7 -0
  19. data/examples/rcsv wrapper/rcsv/ext/rcsv/extconf.rb +3 -0
  20. data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.c +302 -0
  21. data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.rubex +124 -0
  22. data/examples/rcsv wrapper/rcsv/lib/rcsv.rb +8 -0
  23. data/examples/rcsv wrapper/rcsv/lib/rcsv.so +0 -0
  24. data/examples/rcsv wrapper/rcsv/lib/rcsv/version.rb +1 -0
  25. data/examples/rcsv wrapper/rcsv/rcsv.gemspec +27 -0
  26. data/examples/rcsv wrapper/rcsv/spec/rcsv.csv +5 -0
  27. data/examples/rcsv wrapper/rcsv/spec/rcsv_spec.rb +17 -0
  28. data/examples/rcsv wrapper/rcsv/spec/spec_helper.rb +6 -0
  29. data/{spec/fixtures/basic_ruby_method/Makefile → examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/Makefile } +20 -20
  30. data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.o +0 -0
  31. data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.so +0 -0
  32. data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/stage/lib/rcsv.so +0 -0
  33. data/lib/rubex.rb +6 -50
  34. data/lib/rubex/ast.rb +1 -3
  35. data/lib/rubex/ast/expression.rb +1257 -8
  36. data/lib/rubex/ast/node.rb +226 -28
  37. data/lib/rubex/ast/statement.rb +1162 -35
  38. data/lib/rubex/ast/top_statement.rb +815 -0
  39. data/lib/rubex/code_writer.rb +103 -26
  40. data/lib/rubex/compiler.rb +72 -0
  41. data/lib/rubex/compiler_config.rb +19 -0
  42. data/lib/rubex/constants.rb +145 -8
  43. data/lib/rubex/data_type.rb +667 -4
  44. data/lib/rubex/error.rb +15 -0
  45. data/lib/rubex/helpers.rb +154 -0
  46. data/lib/rubex/lexer.rex +186 -22
  47. data/lib/rubex/lexer.rex.rb +261 -35
  48. data/lib/rubex/parser.racc +876 -28
  49. data/lib/rubex/parser.racc.rb +2845 -90
  50. data/lib/rubex/rake_task.rb +34 -0
  51. data/lib/rubex/symbol_table/entry.rb +17 -3
  52. data/lib/rubex/symbol_table/scope.rb +298 -25
  53. data/lib/rubex/version.rb +1 -1
  54. data/rubex.gemspec +11 -3
  55. data/spec/basic_ruby_method_spec.rb +15 -21
  56. data/spec/binding_ptr_args_spec.rb +33 -0
  57. data/spec/bitwise_operators_spec.rb +40 -0
  58. data/spec/blocks_spec.rb +35 -0
  59. data/spec/c_bindings_spec.rb +36 -0
  60. data/spec/c_constants_spec.rb +33 -0
  61. data/spec/c_function_ptrs_spec.rb +38 -0
  62. data/spec/c_functions_spec.rb +35 -0
  63. data/spec/c_struct_interface_spec.rb +38 -0
  64. data/spec/call_by_reference_spec.rb +33 -0
  65. data/spec/class_methods_spec.rb +33 -0
  66. data/spec/class_spec.rb +40 -0
  67. data/spec/comments_spec.rb +33 -0
  68. data/spec/default_args_spec.rb +37 -0
  69. data/spec/error_handling_spec.rb +42 -0
  70. data/spec/examples_spec.rb +52 -0
  71. data/spec/expressions_spec.rb +33 -0
  72. data/spec/fixtures/basic_ruby_method/basic_ruby_method.rubex +2 -0
  73. data/spec/fixtures/binding_ptr_args/binding_ptr_args.rubex +30 -0
  74. data/spec/fixtures/bitwise_operators/bitwise_operators.rubex +40 -0
  75. data/spec/fixtures/blocks/blocks.rubex +11 -0
  76. data/spec/fixtures/c_bindings/c_bindings.rubex +58 -0
  77. data/spec/fixtures/c_constants/c_constants.rubex +7 -0
  78. data/spec/fixtures/c_function_ptrs/c_function_ptrs.rubex +52 -0
  79. data/spec/fixtures/c_functions/c_functions.rubex +25 -0
  80. data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +34 -0
  81. data/spec/fixtures/call_by_reference/call_by_reference.rubex +30 -0
  82. data/spec/fixtures/class/class.rubex +20 -0
  83. data/spec/fixtures/class_methods/class_methods.rubex +12 -0
  84. data/spec/fixtures/comments/comments.rubex +9 -0
  85. data/spec/fixtures/default_args/default_args.rubex +11 -0
  86. data/spec/fixtures/error_handling/error_handling.rubex +54 -0
  87. data/spec/fixtures/examples/array_to_hash.rubex +14 -0
  88. data/spec/fixtures/examples/rcsv.csv +5 -0
  89. data/spec/fixtures/examples/rcsv.rubex +329 -0
  90. data/spec/fixtures/expressions/expressions.rubex +10 -0
  91. data/spec/fixtures/if_else/if_else.rubex +77 -0
  92. data/spec/fixtures/implicit_lib_include/implicit_lib_include.rubex +15 -0
  93. data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +17 -0
  94. data/spec/fixtures/loops/loops.rubex +33 -0
  95. data/spec/fixtures/recursion/recursion.rubex +9 -0
  96. data/spec/fixtures/ruby_constant_method_calls/ruby_constant_method_calls.rubex +17 -0
  97. data/spec/fixtures/ruby_operators/ruby_operators.rubex +29 -0
  98. data/spec/fixtures/ruby_raise/ruby_raise.rubex +13 -0
  99. data/spec/fixtures/ruby_strings/ruby_strings.rubex +19 -0
  100. data/spec/fixtures/ruby_strings/string_blank_bm.rb +37 -0
  101. data/spec/fixtures/ruby_symbols/ruby_symbols.rubex +12 -0
  102. data/spec/fixtures/ruby_types/ruby_types.rubex +15 -0
  103. data/spec/fixtures/statement_expression/statement_expression.rubex +23 -0
  104. data/spec/fixtures/static_array/static_array.rubex +20 -0
  105. data/spec/fixtures/string_literals/string_literals.rubex +15 -0
  106. data/spec/fixtures/struct/struct.rubex +82 -0
  107. data/spec/fixtures/typecasting/typecasting.rubex +23 -0
  108. data/spec/fixtures/var_declarations/var_declarations.rubex +39 -0
  109. data/spec/if_else_spec.rb +39 -0
  110. data/spec/implicit_lib_include_spec.rb +33 -0
  111. data/spec/init_ruby_objects_with_literal_syntax_spec.rb +39 -0
  112. data/spec/loops_spec.rb +34 -0
  113. data/spec/recursion_spec.rb +35 -0
  114. data/spec/ruby_constant_method_calls_spec.rb +35 -0
  115. data/spec/ruby_operators_spec.rb +40 -0
  116. data/spec/ruby_raise_spec.rb +35 -0
  117. data/spec/ruby_strings_spec.rb +33 -0
  118. data/spec/ruby_symbols_spec.rb +37 -0
  119. data/spec/ruby_types_spec.rb +35 -0
  120. data/spec/spec_helper.rb +54 -1
  121. data/spec/statement_expression_spec.rb +34 -0
  122. data/spec/static_array_spec.rb +33 -0
  123. data/spec/string_literals_spec.rb +34 -0
  124. data/spec/struct_spec.rb +36 -0
  125. data/spec/typecasting_spec.rb +38 -0
  126. data/spec/var_declarions_spec.rb +35 -0
  127. metadata +255 -29
  128. data/lib/rubex/ast/argument_list.rb +0 -20
  129. data/lib/rubex/ast/c_base_type.rb +0 -11
  130. data/lib/rubex/ast/ruby_method_def.rb +0 -84
  131. data/spec/fixtures/basic_ruby_method/basic.rb +0 -3
  132. data/spec/fixtures/basic_ruby_method/basic_ruby_method.c +0 -16
  133. data/spec/fixtures/basic_ruby_method/basic_ruby_method.o +0 -0
  134. data/spec/fixtures/basic_ruby_method/basic_ruby_method.so +0 -0
  135. data/spec/fixtures/basic_ruby_method/extconf.rb +0 -3
@@ -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.is_a?(Array) ? 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
- generate_symbol_table_entries
17
- analyse_expressions
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
- # Pretty print the AST
24
- def pp
25
- # TODO
21
+ def == other
22
+ self.class == other.class
26
23
  end
27
24
 
28
- private
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 << "\n"
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 generate_symbol_table_entries
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.generate_symbol_table_entries @scope
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 analyse_expressions
200
+ def rescan_declarations scope
43
201
  @statements.each do |stat|
44
- stat.analyse_expressions @scope
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, "void"
58
- code.write_func_definition_header "void", name, "void"
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
- @statements.each do |stat|
61
- if stat.is_a? Rubex::AST::RubyMethodDef
62
- code.define_instance_method_under @scope, stat.name, stat.c_name
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
- end
248
+ code.nl
65
249
 
66
- code << "\n"
67
- code << "}\n"
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
@@ -1,54 +1,1181 @@
1
+ include Rubex::DataType
2
+
1
3
  module Rubex
2
4
  module AST
3
- class Statement
4
- class Return
5
- attr_reader :expression, :return_type
5
+ module Statement
6
+ class Base
7
+ include Rubex::Helpers::NodeTypeMethods
6
8
 
7
- def initialize expression
8
- @expression = expression
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 analyse_expression local_scope
12
- case @expression
13
- when Rubex::AST::Expression::Addition
14
- left = @expression.left
15
- right = @expression.right
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
- left_type = local_scope[left].type
19
- right_type = local_scope[right].type
70
+ local_scope.declare_var name: @name, c_name: c_name, type: @type,
71
+ value: @value, extern: extern
72
+ end
20
73
 
21
- @return_type = result_type_for left_type, right_type
22
- # TODO: Raise error if return_type is not compatible with the return
23
- # type of the function.
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
- code << "return "
28
- case @expression
29
- when Rubex::AST::Expression::Addition
30
- left = @expression.left
31
- right = @expression.right
32
- code << @return_type.to_ruby_function
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
- private
125
+ @entry = local_scope.declare_var name: @name, c_name: c_name,
126
+ type: @type, value: @value, extern: extern
127
+ end
43
128
 
44
- def result_type_for left_type, right_type
45
- dtype = Rubex::DataType
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
- if left_type.is_a?(dtype::CInt32) && right_type.is_a?(dtype::CInt32)
48
- return dtype::CInt32.new
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
- end
53
- end
54
- end
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