rubex 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
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