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
@@ -0,0 +1,815 @@
1
+ module Rubex
2
+ module AST
3
+ module TopStatement
4
+ class CBindings
5
+ attr_reader :lib, :declarations, :location
6
+
7
+ def initialize lib, comp_opts, declarations, location
8
+ @lib, @comp_opts, @declarations, @location = lib, comp_opts,
9
+ declarations, location
10
+ end
11
+
12
+ def analyse_statement local_scope
13
+ unless @declarations
14
+ @declarations = []
15
+ load_predecided_declarations
16
+ end
17
+ @declarations.each do |stat|
18
+ stat.analyse_statement local_scope, extern: true
19
+ end
20
+ local_scope.include_files.push @lib
21
+ update_compiler_config
22
+ end
23
+
24
+ def generate_code code
25
+
26
+ end
27
+
28
+ private
29
+ def update_compiler_config
30
+ @comp_opts.each do |h|
31
+ if h[:link]
32
+ Rubex::Compiler::CONFIG.add_link h[:link]
33
+ end
34
+ end
35
+ end
36
+
37
+ def load_predecided_declarations
38
+ if @lib == 'rubex/ruby'
39
+ load_ruby_functions_and_types
40
+ @lib = '<ruby.h>'
41
+ elsif @lib == 'rubex/ruby/encoding'
42
+ load_ruby_encoding_functions_and_type
43
+ @lib = '<ruby/encoding.h>'
44
+ elsif @lib == 'rubex/stdlib'
45
+ load_stdlib_functions_and_types
46
+ @lib = '<stdlib.h>'
47
+ else
48
+ raise Rubex::LibraryNotFoundError, "Cannot find #{@lib}."
49
+ end
50
+ end
51
+
52
+ def load_ruby_functions_and_types
53
+ @declarations << xmalloc
54
+ @declarations << xfree
55
+ @declarations << type_get
56
+ @declarations.concat type_identifiers
57
+ @declarations << rb_str_new
58
+ @declarations << rb_ary_includes
59
+ end
60
+
61
+ def load_ruby_encoding_functions_and_type
62
+ @declarations << rb_enc_associate_index
63
+ @declarations << rb_enc_find_index
64
+ end
65
+
66
+ def load_stdlib_functions_and_types
67
+ @declarations.concat atox_functions
68
+ end
69
+
70
+ def rb_ary_includes
71
+ cfunc_decl('object', '', 'rb_ary_includes',
72
+ arg_list([arg('object', '','ary'), arg('object', '','item')]))
73
+ end
74
+
75
+ def atox_functions
76
+ [
77
+ ['int', 'atoi'], ['long', 'atol'], ['long long', 'atoll'],
78
+ ['double', 'atof']
79
+ ].map do |type, ident|
80
+ cfunc_decl(type, '', ident, arg_list([arg('char', '*', 'str')]))
81
+ end
82
+ end
83
+
84
+ def rb_enc_find_index
85
+ cfunc_decl('int', '', 'rb_enc_find_index',
86
+ arg_list([arg('char', '*', 'enc')]))
87
+ end
88
+
89
+ def rb_enc_associate_index
90
+ args = arg_list([arg('object', '', 'string'), arg('int', '', 'enc')])
91
+ cfunc_decl('object', '', 'rb_enc_associate_index', args)
92
+ end
93
+
94
+ def rb_str_new
95
+ args = arg_list([arg('char', '*', 'str'), arg('long', '', 'length')])
96
+ cfunc_decl('object', '', 'rb_str_new', args)
97
+ end
98
+
99
+ def type_get
100
+ cfunc_decl('int', '', 'TYPE', arg_list([arg('object', '', 'dummy')]))
101
+ end
102
+
103
+ def type_identifiers
104
+ stmts = [
105
+ 'T_ARRAY', 'T_NIL', 'T_TRUE', 'T_FALSE', 'T_FLOAT', 'T_FIXNUM',
106
+ 'T_BIGNUM', 'T_REGEXP', 'T_STRING'
107
+ ].map do |ident|
108
+ Statement::VarDecl.new('int', ident, nil, @location)
109
+ end
110
+
111
+ stmts
112
+ end
113
+
114
+ def xmalloc
115
+ args = Statement::ArgumentList.new([
116
+ Expression::ArgDeclaration.new({
117
+ dtype: 'size_t', variables: [{ident: 'dummy'}] })
118
+ ])
119
+ Statement::CFunctionDecl.new('void', '*', 'xmalloc', args)
120
+ end
121
+
122
+ def xfree
123
+ cfunc_decl 'void', '', 'xfree', arg_list([arg('void', '*', 'dummy')])
124
+ end
125
+
126
+ private
127
+
128
+ def arg type, ptr_level, ident
129
+ Expression::ArgDeclaration.new({
130
+ dtype: type, variables: [{ ident: ident, ptr_level: ptr_level }]
131
+ })
132
+ end
133
+
134
+ def cfunc_decl return_type, return_ptr_level, ident, args
135
+ Statement::CFunctionDecl.new(return_type, return_ptr_level, ident, args)
136
+ end
137
+
138
+ def arg_list args
139
+ Statement::ArgumentList.new args
140
+ end
141
+ end # class CBindings
142
+
143
+ class MethodDef
144
+ include Rubex::Helpers::Writers
145
+ # Ruby name of the method.
146
+ attr_reader :name
147
+ # Method arguments. Accessor because arguments need to be modified in
148
+ # case of auxillary C functions of attach classes.
149
+ attr_accessor :arg_list
150
+ # The statments/expressions contained within the method.
151
+ attr_reader :statements
152
+ # Symbol Table entry.
153
+ attr_reader :entry
154
+ # Instance of Scope::Local for this method.
155
+ attr_reader :scope
156
+ # Variable name that identifies 'self'
157
+ attr_reader :self_name
158
+
159
+ def initialize name, arg_list, statements
160
+ @name, @arg_list, @statements = name, arg_list, statements
161
+ @self_name = Rubex::ARG_PREFIX + "self"
162
+ end
163
+
164
+ def analyse_statement outer_scope
165
+ @entry = outer_scope.find @name
166
+ @scope = @entry.type.scope
167
+ @scope.type = @entry.type
168
+ @scope.self_name = @self_name
169
+ @arg_list = @entry.type.arg_list
170
+ @statements.each do |stat|
171
+ stat.analyse_statement @scope
172
+ end
173
+ end
174
+
175
+ # Option c_function - When set to true, certain code that is not required
176
+ # for Ruby methods will be generated too.
177
+ def generate_code code, c_function: false
178
+ code.block do
179
+ generate_function_definition code, c_function: c_function
180
+ end
181
+ end
182
+
183
+ def rescan_declarations scope
184
+ @statements.each do |stat|
185
+ stat.respond_to?(:rescan_declarations) and
186
+ stat.rescan_declarations(@scope)
187
+ end
188
+ end
189
+
190
+ private
191
+ def generate_function_definition code, c_function:
192
+ declare_types code, @scope
193
+ declare_args code unless c_function
194
+ declare_vars code, @scope
195
+ declare_carrays code, @scope
196
+ declare_ruby_objects code, @scope
197
+ declare_temps code, @scope
198
+ generate_arg_checking code unless c_function
199
+ init_args code unless c_function
200
+ declare_carrays_using_init_var_value code
201
+ generate_statements code
202
+ end
203
+
204
+ def declare_args code
205
+ @scope.arg_entries.each do |arg|
206
+ code.declare_variable type: arg.type.to_s, c_name: arg.c_name
207
+ end
208
+ end
209
+
210
+ def generate_statements code
211
+ @statements.each do |stat|
212
+ stat.generate_code code, @scope
213
+ end
214
+ end
215
+
216
+ def init_args code
217
+ @scope.arg_entries.each_with_index do |arg, i|
218
+ code << arg.c_name + '=' + arg.type.from_ruby_object("argv[#{i}]") + ';'
219
+ code.nl
220
+ end
221
+ end
222
+
223
+ def declare_carrays_using_init_var_value code
224
+ @scope.carray_entries.select { |s|
225
+ !s.type.dimension.is_a?(Rubex::AST::Expression::Literal::Base)
226
+ }. each do |arr|
227
+ type = arr.type.type.to_s
228
+ c_name = arr.c_name
229
+ dimension = arr.type.dimension.c_code(@scope)
230
+ value = arr.value.map { |a| a.c_code(@scope) } if arr.value
231
+ code.declare_carray(type: type, c_name: c_name, dimension: dimension,
232
+ value: value)
233
+ end
234
+ end
235
+
236
+ def generate_arg_checking code
237
+ code << 'if (argc < ' + @scope.arg_entries.size.to_s + ")"
238
+ code.block do
239
+ code << %Q{rb_raise(rb_eArgError, "Need #{@scope.arg_entries.size} args, not %d", argc);\n}
240
+ end
241
+ end
242
+ end
243
+
244
+ class RubyMethodDef < MethodDef
245
+ attr_reader :singleton
246
+
247
+ def initialize name, arg_list, statements, singleton: false
248
+ super(name, arg_list, statements)
249
+ @singleton = singleton
250
+ end
251
+
252
+ def analyse_statement local_scope
253
+ super
254
+ @entry.singleton = @singleton
255
+ end
256
+
257
+ def generate_code code
258
+ code.write_ruby_method_header(type: @entry.type.type.to_s,
259
+ c_name: @entry.c_name)
260
+ super
261
+ end
262
+
263
+ def == other
264
+ self.class == other.class && @name == other.name &&
265
+ @c_name == other.c_name && @arg_list == other.arg_list &&
266
+ @statements == other.statements && @entry == other.entry &&
267
+ @type == other.type
268
+ end
269
+ end # class RubyMethodDef
270
+
271
+ class CFunctionDef < MethodDef
272
+ attr_reader :type, :return_ptr_level
273
+
274
+ def initialize type, return_ptr_level, name, arg_list, statements
275
+ super(name, arg_list, statements)
276
+ @type = type
277
+ @return_ptr_level = return_ptr_level
278
+ end
279
+
280
+ def analyse_statement outer_scope, extern: false
281
+ super(outer_scope)
282
+ end
283
+
284
+ def generate_code code
285
+ code.write_c_method_header(type: @entry.type.type.to_s,
286
+ c_name: @entry.c_name, args: Helpers.create_arg_arrays(@arg_list))
287
+ super code, c_function: true
288
+ end
289
+ end # class CFunctionDef
290
+
291
+ class Klass
292
+ include Rubex::Helpers::Writers
293
+ # Stores the scope of the class. Rubex::SymbolTable::Scope::Klass.
294
+ attr_reader :scope
295
+
296
+ attr_reader :name
297
+
298
+ attr_reader :ancestor
299
+
300
+ attr_reader :statements
301
+
302
+ attr_reader :entry
303
+
304
+ # Name of the class. Ancestor can be Scope::Klass or String object
305
+ # depending on whether invoker is another higher level scope or
306
+ # the parser. Statements are the statements inside the class.
307
+ def initialize name, ancestor, statements
308
+ @name, @ancestor, @statements = name, ancestor, statements
309
+ @ancestor = 'Object' if @ancestor.nil?
310
+ end
311
+
312
+ def analyse_statement local_scope, attach_klass: false
313
+ @entry = local_scope.find(@name)
314
+ @scope = @entry.type.scope
315
+ @ancestor = @entry.type.ancestor
316
+ add_statement_symbols_to_symbol_table
317
+ if !attach_klass
318
+ @statements.each do |stat|
319
+ stat.analyse_statement @scope
320
+ end
321
+ end
322
+ end
323
+
324
+ def rescan_declarations local_scope
325
+ @statements.each do |stat|
326
+ stat&.rescan_declarations(@scope)
327
+ end
328
+ end
329
+
330
+ def generate_code code
331
+ @scope.begin_block_callbacks.each do |cb|
332
+ cb.generate_code code
333
+ end
334
+
335
+ @statements.each do |stat|
336
+ stat.generate_code code
337
+ end
338
+ end
339
+
340
+ protected
341
+
342
+ def add_statement_symbols_to_symbol_table
343
+ @statements.each do |stmt|
344
+ if ruby_method_or_c_function?(stmt)
345
+ f_name, f_scope = prepare_name_and_scope_of_functions(stmt)
346
+ if !auxillary_c_function_for_attached_klass?(f_name)
347
+ stmt.arg_list.analyse_statement(f_scope)
348
+ if stmt.is_a? Rubex::AST::TopStatement::RubyMethodDef
349
+ add_ruby_method_to_scope f_name, f_scope, stmt.arg_list
350
+ elsif stmt.is_a? Rubex::AST::TopStatement::CFunctionDef
351
+ return_type = Helpers.determine_dtype(stmt.type, stmt.return_ptr_level)
352
+ add_c_function_to_scope f_name, f_scope, stmt.arg_list, return_type
353
+ end
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ def auxillary_c_function_for_attached_klass? f_name
360
+ self.is_a?(AttachedKlass) and [
361
+ Rubex::ALLOC_FUNC_NAME, Rubex::DEALLOC_FUNC_NAME,
362
+ Rubex::MEMCOUNT_FUNC_NAME, Rubex::GET_STRUCT_FUNC_NAME
363
+ ].include?(f_name)
364
+ end
365
+
366
+ def add_c_function_to_scope f_name, f_scope, arg_list, return_type
367
+ c_name = c_func_c_name(f_name)
368
+ arg_list.each do |arg|
369
+ if arg.entry.value
370
+ e = arg.entry
371
+ e.value = Rubex::Helpers.to_lhs_type(e, e.value)
372
+ end
373
+ end
374
+ @scope.add_c_method(
375
+ name: f_name,
376
+ c_name: c_name,
377
+ extern: false,
378
+ return_type: return_type,
379
+ arg_list: arg_list,
380
+ scope: f_scope
381
+ )
382
+ end
383
+
384
+ def add_ruby_method_to_scope f_name, f_scope, arg_list
385
+ c_name = Rubex::RUBY_FUNC_PREFIX + @name + "_" +
386
+ f_name.gsub("?", "_qmark").gsub("!", "_bang")
387
+ @scope.add_ruby_method(
388
+ name: f_name,
389
+ c_name: c_name,
390
+ scope: f_scope,
391
+ arg_list: arg_list
392
+ )
393
+ end
394
+
395
+ def prepare_name_and_scope_of_functions stmt
396
+ f_name = stmt.name
397
+ f_scope = Rubex::SymbolTable::Scope::Local.new f_name, @scope
398
+ [f_name, f_scope]
399
+ end
400
+
401
+ def ruby_method_or_c_function? stmt
402
+ stmt.is_a?(Rubex::AST::TopStatement::RubyMethodDef) ||
403
+ stmt.is_a?(Rubex::AST::TopStatement::CFunctionDef)
404
+ end
405
+
406
+ def c_func_c_name name
407
+ Rubex::C_FUNC_PREFIX + @name + "_" + name
408
+ end
409
+ end # class Klass
410
+
411
+ class AttachedKlass < Klass
412
+
413
+ attr_reader :attached_type
414
+
415
+ ALLOC_FUNC_NAME = Rubex::ALLOC_FUNC_NAME
416
+ DEALLOC_FUNC_NAME = Rubex::DEALLOC_FUNC_NAME
417
+ MEMCOUNT_FUNC_NAME = Rubex::MEMCOUNT_FUNC_NAME
418
+ GET_STRUCT_FUNC_NAME = Rubex::GET_STRUCT_FUNC_NAME
419
+
420
+ def initialize name, attached_type, ancestor, statements, location
421
+ @attached_type = attached_type
422
+ @location = location
423
+ super(name, ancestor, statements)
424
+ end
425
+
426
+ def analyse_statement outer_scope
427
+ super(outer_scope, attach_klass: true)
428
+ prepare_data_holding_struct
429
+ prepare_rb_data_type_t_struct
430
+ detach_and_modify_auxillary_c_functions_from_statements
431
+ add_auxillary_functions_to_klass_scope
432
+ prepare_auxillary_c_functions
433
+ @statements[1..-1].each do |stmt| # 0th stmt is the data struct
434
+ if ruby_method_or_c_func?(stmt)
435
+ rewrite_method_with_data_fetching stmt
436
+ end
437
+ stmt.analyse_statement @scope
438
+ end
439
+ analyse_auxillary_c_functions
440
+ end
441
+
442
+ def generate_code code
443
+ write_auxillary_c_functions code
444
+ write_data_type_t_struct code
445
+ write_get_struct_c_function code
446
+ write_alloc_c_function code
447
+ super
448
+ end
449
+
450
+ private
451
+
452
+ # Since auxillary functions are detached from the klass statements,
453
+ # analyse their arg_list separately and add function names to the
454
+ # class scope so that their statements can be analysed properly later.
455
+ def add_auxillary_functions_to_klass_scope
456
+ @auxillary_c_functions.each_value do |func|
457
+ f_name, f_scope = prepare_name_and_scope_of_functions func
458
+ func.arg_list.analyse_statement(f_scope)
459
+ return_type = Helpers.determine_dtype(func.type, func.return_ptr_level)
460
+ add_c_function_to_scope f_name, f_scope, func.arg_list, return_type
461
+ end
462
+ end
463
+
464
+ def analyse_auxillary_c_functions
465
+ @auxillary_c_functions.each_value do |func|
466
+ func.analyse_statement(@scope)
467
+ end
468
+ end
469
+
470
+ # Detach the user-supplied auxlically C functions from the class and
471
+ # store them in Hash @auxillary_c_functions. Make modifications to
472
+ # them if necessary and also set an ivar that indicates if an
473
+ # auxillary function is user-supplied or not.
474
+ def detach_and_modify_auxillary_c_functions_from_statements
475
+ @auxillary_c_functions = {}
476
+
477
+ indexes = []
478
+ @statements.each_with_index do |stmt, idx|
479
+ if stmt.is_a?(CFunctionDef)
480
+ if stmt.name == ALLOC_FUNC_NAME
481
+ @auxillary_c_functions[ALLOC_FUNC_NAME] = stmt
482
+ @user_defined_alloc = true
483
+ indexes << idx
484
+ elsif stmt.name == DEALLOC_FUNC_NAME
485
+ @auxillary_c_functions[DEALLOC_FUNC_NAME] = stmt
486
+ @user_defined_dealloc = true
487
+ modify_dealloc_func stmt
488
+ indexes << idx
489
+ elsif stmt.name == MEMCOUNT_FUNC_NAME
490
+ @auxillary_c_functions[MEMCOUNT_FUNC_NAME] = stmt
491
+ @user_defined_memcount = true
492
+ indexes << idx
493
+ elsif stmt.name == GET_STRUCT_FUNC_NAME
494
+ @auxillary_c_functions[GET_STRUCT_FUNC_NAME] = stmt
495
+ @user_defined_get_struct = true
496
+ indexes << idx
497
+ end
498
+ end
499
+ end
500
+
501
+ indexes.each do |idx|
502
+ @statements.delete_at idx
503
+ end
504
+ end
505
+
506
+ def write_auxillary_c_functions code
507
+ write_dealloc_c_function code
508
+ write_memcount_c_function code
509
+ end
510
+
511
+ # Actually write the alloc function into C code.
512
+ def write_alloc_c_function code
513
+ if user_defined_alloc?
514
+ @auxillary_c_functions[ALLOC_FUNC_NAME].generate_code code
515
+ else
516
+ code.write_c_method_header(
517
+ type: @alloc_c_func.type.type.to_s,
518
+ c_name: @alloc_c_func.c_name,
519
+ args: Helpers.create_arg_arrays(@alloc_c_func.type.arg_list))
520
+ code.block do
521
+ lines = ""
522
+ lines << "#{@data_struct.entry.c_name} *data;\n\n"
523
+
524
+ lines << "data = (#{@data_struct.entry.c_name}*)xmalloc("
525
+ lines << "sizeof(#{@data_struct.entry.c_name}));\n"
526
+
527
+ lines << member_struct_allocations
528
+
529
+ lines << "return TypedData_Wrap_Struct("
530
+ lines << "#{@alloc_c_func.type.arg_list[0].entry.c_name},"
531
+ lines << "&#{@data_type_t}, data);\n"
532
+
533
+ code << lines
534
+ end
535
+ end
536
+ end
537
+
538
+ #TODO: modify for supporting inheritance
539
+ def member_struct_allocations
540
+ c_name = @scope.find(@attached_type).c_name
541
+ "data->#{Rubex::POINTER_PREFIX + @attached_type} = (#{c_name}*)xmalloc(sizeof(#{c_name}));\n"
542
+ end
543
+
544
+ # Actually write the dealloc function into C code.
545
+ def write_dealloc_c_function code
546
+ if user_defined_dealloc?
547
+ @auxillary_c_functions[DEALLOC_FUNC_NAME].generate_code code
548
+ else
549
+ # TODO: define dealloc if user hasnt supplied.
550
+ end
551
+ end
552
+
553
+ # Actually write the memcount function into C code.
554
+ def write_memcount_c_function code
555
+ if user_defined_memcount?
556
+ @auxillary_c_functions[MEMCOUNT_FUNC_NAME].generate_code code
557
+ else
558
+ code.write_c_method_header(
559
+ type: @memcount_c_func.type.type.to_s,
560
+ c_name: @memcount_c_func.c_name,
561
+ args: Helpers.create_arg_arrays(@memcount_c_func.type.arg_list))
562
+ code.block do
563
+ code << "return sizeof(#{@memcount_c_func.type.arg_list[0].entry.c_name})"
564
+ code.colon
565
+ code.nl
566
+ end
567
+ end
568
+ end
569
+
570
+ # Actually write the get_struct function into C code.
571
+ def write_get_struct_c_function code
572
+ if user_defined_get_struct?
573
+ @auxillary_c_functions[GET_STRUCT_FUNC_NAME].generate_code code
574
+ else
575
+ code.write_c_method_header(
576
+ type: @get_struct_c_func.type.type.to_s,
577
+ c_name: @get_struct_c_func.c_name,
578
+ args: Helpers.create_arg_arrays(@get_struct_c_func.type.arg_list))
579
+ code.block do
580
+ lines = ""
581
+ lines << "#{@data_struct.entry.c_name} *data;\n\n"
582
+ lines << "TypedData_Get_Struct("
583
+ lines << "#{@get_struct_c_func.type.arg_list[0].entry.c_name}, "
584
+ lines << "#{@data_struct.entry.c_name}, &#{@data_type_t}, data);\n"
585
+ lines << "return data;\n"
586
+
587
+ code << lines
588
+ end
589
+ end
590
+ end
591
+
592
+ def write_data_type_t_struct code
593
+ decl = ""
594
+ decl << "static const rb_data_type_t #{@data_type_t} = {\n"
595
+ decl << " \"#{@name}\",\n"
596
+ decl << " {0, #{@dealloc_c_func.c_name}, #{@memcount_c_func.c_name},\n"
597
+ decl << " 0}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY\n"
598
+ decl << "};\n"
599
+
600
+ code << decl
601
+ code.new_line
602
+ end
603
+
604
+ # Prepare the data holding struct 'data' that will hold a pointer to the
605
+ # struct that is attached to this class.
606
+ def prepare_data_holding_struct
607
+ struct_name = @name + "_data_struct"
608
+ declarations = declarations_for_data_struct
609
+ @data_struct = Statement::CStructOrUnionDef.new(
610
+ :struct, struct_name, declarations, @location)
611
+ @data_struct.analyse_statement(@scope)
612
+ @statements.unshift @data_struct
613
+ end
614
+
615
+ # TODO: support inherited attached structs.
616
+ def declarations_for_data_struct
617
+ stmts = []
618
+ stmts << Statement::CPtrDecl.new(@attached_type, @attached_type, nil,
619
+ "*", @location)
620
+
621
+ stmts
622
+ end
623
+
624
+ def prepare_rb_data_type_t_struct
625
+ @data_type_t = Rubex::ATTACH_CLASS_PREFIX + "_" + @name + "_data_type_t"
626
+ end
627
+
628
+ # Prepare auxillary function in case they have not been supplied by user
629
+ # and create ivars for their Symbol Table entries for easy accesss later.
630
+ def prepare_auxillary_c_functions
631
+ prepare_alloc_c_function
632
+ prepare_memcount_c_function
633
+ prepare_deallocation_c_function
634
+ prepare_get_struct_c_function
635
+ end
636
+
637
+ def ruby_method_or_c_func? stmt
638
+ stmt.is_a?(RubyMethodDef) || stmt.is_a?(CFunctionDef)
639
+ end
640
+
641
+ # Rewrite method `stmt` so that the `data` variable becomes available
642
+ # inside the scope of the method.
643
+ def rewrite_method_with_data_fetching stmt
644
+ data_stmt = Statement::CPtrDecl.new(@data_struct.name, 'data',
645
+ get_struct_func_call(stmt), "*", @location)
646
+ stmt.statements.unshift data_stmt
647
+ end
648
+
649
+ def get_struct_func_call stmt
650
+ Expression::CommandCall.new(nil, @get_struct_c_func.name,
651
+ Statement::ArgumentList.new([]))
652
+ end
653
+
654
+ # Create an alloc function if it is not supplied by user.
655
+ def prepare_alloc_c_function
656
+ if user_defined_alloc?
657
+ @alloc_c_func = @scope.find(ALLOC_FUNC_NAME)
658
+ else
659
+ c_name = c_func_c_name(ALLOC_FUNC_NAME)
660
+ scope = Rubex::SymbolTable::Scope::Local.new(ALLOC_FUNC_NAME, @scope)
661
+ arg_list = Statement::ArgumentList.new([
662
+ Expression::ArgDeclaration.new({
663
+ dtype: 'object',
664
+ variables: [
665
+ {
666
+ ident: 'self'
667
+ }
668
+ ]
669
+ })
670
+ ])
671
+ arg_list.analyse_statement(scope)
672
+ @alloc_c_func = @scope.add_c_method(
673
+ name: ALLOC_FUNC_NAME,
674
+ c_name: c_name,
675
+ arg_list: arg_list,
676
+ return_type: DataType::RubyObject.new,
677
+ scope: scope)
678
+ end
679
+ end
680
+
681
+ # Create a memcount function if it is not supplied by user.
682
+ def prepare_memcount_c_function
683
+ if user_defined_memcount?
684
+ @memcount_c_func = @scope.find(MEMCOUNT_FUNC_NAME)
685
+ else
686
+ c_name = c_func_c_name(MEMCOUNT_FUNC_NAME)
687
+ scope = Rubex::SymbolTable::Scope::Local.new(MEMCOUNT_FUNC_NAME, @scope)
688
+ arg_list = Statement::ArgumentList.new([
689
+ Expression::ArgDeclaration.new({
690
+ dtype: "void",
691
+ variables: [
692
+ {
693
+ ptr_level: "*",
694
+ ident: "raw_data"
695
+ }
696
+ ]
697
+ })
698
+ ])
699
+ arg_list.analyse_statement(scope)
700
+ @memcount_c_func = @scope.add_c_method(
701
+ name: MEMCOUNT_FUNC_NAME,
702
+ c_name: c_name,
703
+ arg_list: arg_list,
704
+ return_type: DataType::Size_t.new,
705
+ scope: scope)
706
+ end
707
+ end
708
+
709
+ # Create a deallocate function if it is not supplied by user.
710
+ def prepare_deallocation_c_function
711
+ if user_defined_dealloc?
712
+ @dealloc_c_func = @scope.find(DEALLOC_FUNC_NAME)
713
+ else
714
+ c_name = c_func_c_name(DEALLOC_FUNC_NAME)
715
+ scope = Rubex::SymbolTable::Scope::Local.new(DEALLOC_FUNC_NAME, @scope)
716
+ arg_list = Statement::ArgumentList.new([
717
+ Expression::ArgDeclaration.new({
718
+ dtype: "void",
719
+ variables: [
720
+ {
721
+ ptr_level: "*",
722
+ ident: "raw_data"
723
+ }
724
+ ]
725
+ })
726
+ ])
727
+ arg_list.analyse_statement(scope)
728
+ @dealloc_c_func = @scope.add_c_method(
729
+ name: DEALLOC_FUNC_NAME,
730
+ c_name: c_name,
731
+ return_type: DataType::Void.new,
732
+ scope: scope,
733
+ arg_list: arg_list)
734
+ end
735
+ end
736
+
737
+ # Create a get_struct function if it is not supplied by user.
738
+ def prepare_get_struct_c_function
739
+ if user_defined_get_struct?
740
+ @get_struct_c_func = @scope.find(GET_STRUCT_FUNC_NAME)
741
+ else
742
+ c_name = c_func_c_name(GET_STRUCT_FUNC_NAME)
743
+ scope = Rubex::SymbolTable::Scope::Local.new(
744
+ GET_STRUCT_FUNC_NAME, @scope)
745
+ arg_list = Statement::ArgumentList.new([
746
+ Expression::ArgDeclaration.new({
747
+ dtype: "object",
748
+ variables: [
749
+ {
750
+ ident: "obj"
751
+ }
752
+ ]
753
+ })
754
+ ])
755
+ arg_list.analyse_statement(scope)
756
+ return_type = DataType::CPtr.new(
757
+ DataType::CStructOrUnion.new(
758
+ :struct, @data_struct.name, @data_struct.entry.c_name, nil)
759
+ )
760
+ @get_struct_c_func = @scope.add_c_method(
761
+ name: GET_STRUCT_FUNC_NAME,
762
+ c_name: c_name,
763
+ return_type: return_type,
764
+ arg_list: arg_list,
765
+ scope: scope
766
+ )
767
+ end
768
+ end
769
+
770
+ # Modify the dealloc function by adding an argument of type void* so
771
+ # that it is compatible with what Ruby expects. This is done so that
772
+ # the user is not burdened with additional knowledge of knowing the
773
+ # the correct argument for deallocate.
774
+ def modify_dealloc_func func
775
+ func.arg_list = Statement::ArgumentList.new([
776
+ Expression::ArgDeclaration.new({
777
+ dtype: "void",
778
+ variables: [
779
+ {
780
+ ptr_level: "*",
781
+ ident: "raw_data"
782
+ }
783
+ ]
784
+ })
785
+ ])
786
+ value = Expression::Name.new('raw_data')
787
+ value.typecast = Expression::Typecast.new(@data_struct.name, "*")
788
+ data_var = Statement::CPtrDecl.new(@data_struct.name, 'data', value,
789
+ "*", @location)
790
+ xfree = Expression::CommandCall.new(nil, 'xfree',
791
+ [Expression::Name.new('data')])
792
+ data_xfree = Statement::Expression.new xfree, @location
793
+ func.statements.unshift data_var
794
+ func.statements.push data_xfree
795
+ end
796
+
797
+ def user_defined_dealloc?
798
+ @user_defined_dealloc
799
+ end
800
+
801
+ def user_defined_alloc?
802
+ @user_defined_alloc
803
+ end
804
+
805
+ def user_defined_memcount?
806
+ @user_defined_memcount
807
+ end
808
+
809
+ def user_defined_get_struct?
810
+ @user_defined_get_struct
811
+ end
812
+ end # class AttachedKlass
813
+ end # module TopStatement
814
+ end # module AST
815
+ end # module Rubex