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
@@ -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