rubex 0.1 → 0.1.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 (197) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -2
  3. data/.travis.yml +9 -1
  4. data/CONTRIBUTING.md +2 -2
  5. data/README.md +4 -1
  6. data/Rakefile +2 -2
  7. data/bin/rubex +4 -5
  8. data/lib/rubex.rb +4 -4
  9. data/lib/rubex/ast.rb +4 -1
  10. data/lib/rubex/ast/expression.rb +22 -1191
  11. data/lib/rubex/ast/expression/actual_arg_list.rb +40 -0
  12. data/lib/rubex/ast/expression/analysed_element_ref.rb +26 -0
  13. data/lib/rubex/ast/expression/analysed_element_ref/c_var_element_ref.rb +30 -0
  14. data/lib/rubex/ast/expression/analysed_element_ref/ruby_object_element_ref.rb +42 -0
  15. data/lib/rubex/ast/expression/arg_declaration.rb +43 -0
  16. data/lib/rubex/ast/expression/binary.rb +57 -0
  17. data/lib/rubex/ast/expression/binary/binary_boolean.rb +23 -0
  18. data/lib/rubex/ast/expression/binary/binary_boolean_special_op.rb +20 -0
  19. data/lib/rubex/ast/expression/binary/empty_classes.rb +62 -0
  20. data/lib/rubex/ast/expression/block_given.rb +15 -0
  21. data/lib/rubex/ast/expression/coerce_object.rb +15 -0
  22. data/lib/rubex/ast/expression/command_call.rb +74 -0
  23. data/lib/rubex/ast/expression/command_call/struct_or_union_member_call.rb +38 -0
  24. data/lib/rubex/ast/expression/element_ref.rb +64 -0
  25. data/lib/rubex/ast/expression/empty.rb +13 -0
  26. data/lib/rubex/ast/expression/from_ruby_object.rb +20 -0
  27. data/lib/rubex/ast/expression/func_ptr_arg_declaration.rb +21 -0
  28. data/lib/rubex/ast/expression/func_ptr_internal_arg_declaration.rb +13 -0
  29. data/lib/rubex/ast/expression/literal.rb +30 -0
  30. data/lib/rubex/ast/expression/literal/array_lit.rb +51 -0
  31. data/lib/rubex/ast/expression/literal/c_null.rb +15 -0
  32. data/lib/rubex/ast/expression/literal/char.rb +36 -0
  33. data/lib/rubex/ast/expression/literal/double.rb +14 -0
  34. data/lib/rubex/ast/expression/literal/false.rb +33 -0
  35. data/lib/rubex/ast/expression/literal/hash_lit.rb +45 -0
  36. data/lib/rubex/ast/expression/literal/int.rb +14 -0
  37. data/lib/rubex/ast/expression/literal/nil.rb +14 -0
  38. data/lib/rubex/ast/expression/literal/ruby_symbol.rb +22 -0
  39. data/lib/rubex/ast/expression/literal/string_lit.rb +45 -0
  40. data/lib/rubex/ast/expression/literal/true.rb +29 -0
  41. data/lib/rubex/ast/expression/method_call.rb +52 -0
  42. data/lib/rubex/ast/expression/method_call/c_function_call.rb +40 -0
  43. data/lib/rubex/ast/expression/method_call/ruby_method_call.rb +83 -0
  44. data/lib/rubex/ast/expression/name.rb +127 -0
  45. data/lib/rubex/ast/expression/ruby_constant.rb +25 -0
  46. data/lib/rubex/ast/expression/ruby_object_element_ref/ruby_array_element_ref.rb +20 -0
  47. data/lib/rubex/ast/expression/ruby_object_element_ref/ruby_hash_element_ref.rb +22 -0
  48. data/lib/rubex/ast/expression/self.rb +15 -0
  49. data/lib/rubex/ast/expression/size_of.rb +22 -0
  50. data/lib/rubex/ast/expression/struct_or_union_member_call/element_ref_member_call.rb +23 -0
  51. data/lib/rubex/ast/expression/to_ruby_object.rb +21 -0
  52. data/lib/rubex/ast/expression/typecast.rb +20 -0
  53. data/lib/rubex/ast/expression/typecast_to.rb +10 -0
  54. data/lib/rubex/ast/expression/unary.rb +37 -0
  55. data/lib/rubex/ast/expression/unary_base.rb +24 -0
  56. data/lib/rubex/ast/expression/unary_base/ampersand.rb +16 -0
  57. data/lib/rubex/ast/expression/unary_base/unary_bit_not.rb +18 -0
  58. data/lib/rubex/ast/expression/unary_base/unary_not.rb +18 -0
  59. data/lib/rubex/ast/expression/unary_base/unary_sub.rb +18 -0
  60. data/lib/rubex/ast/node.rb +111 -111
  61. data/lib/rubex/ast/statement.rb +9 -1160
  62. data/lib/rubex/ast/statement/alias.rb +43 -0
  63. data/lib/rubex/ast/statement/argument_list.rb +59 -0
  64. data/lib/rubex/ast/statement/assign.rb +35 -0
  65. data/lib/rubex/ast/statement/begin_block.rb +14 -0
  66. data/lib/rubex/ast/statement/begin_block/begin.rb +202 -0
  67. data/lib/rubex/ast/statement/begin_block/else.rb +21 -0
  68. data/lib/rubex/ast/statement/begin_block/ensure.rb +21 -0
  69. data/lib/rubex/ast/statement/begin_block/rescue.rb +34 -0
  70. data/lib/rubex/ast/statement/break.rb +18 -0
  71. data/lib/rubex/ast/statement/c_array_decl.rb +49 -0
  72. data/lib/rubex/ast/statement/c_base_type.rb +26 -0
  73. data/lib/rubex/ast/statement/c_function_decl.rb +30 -0
  74. data/lib/rubex/ast/statement/c_ptr_decl.rb +52 -0
  75. data/lib/rubex/ast/statement/c_ptr_decl/c_ptr_func_decl.rb +25 -0
  76. data/lib/rubex/ast/statement/c_struct_or_union_def.rb +49 -0
  77. data/lib/rubex/ast/statement/expression.rb +26 -0
  78. data/lib/rubex/ast/statement/for.rb +73 -0
  79. data/lib/rubex/ast/statement/forward_decl.rb +31 -0
  80. data/lib/rubex/ast/statement/if_block.rb +64 -0
  81. data/lib/rubex/ast/statement/if_block/else.rb +30 -0
  82. data/lib/rubex/ast/statement/if_block/elsif.rb +22 -0
  83. data/lib/rubex/ast/statement/if_block/helper.rb +38 -0
  84. data/lib/rubex/ast/statement/print.rb +49 -0
  85. data/lib/rubex/ast/statement/raise.rb +66 -0
  86. data/lib/rubex/ast/statement/return.rb +45 -0
  87. data/lib/rubex/ast/statement/var_decl.rb +49 -0
  88. data/lib/rubex/ast/statement/while.rb +34 -0
  89. data/lib/rubex/ast/statement/yield.rb +41 -0
  90. data/lib/rubex/ast/top_statement.rb +1 -815
  91. data/lib/rubex/ast/top_statement/c_bindings.rb +145 -0
  92. data/lib/rubex/ast/top_statement/klass.rb +125 -0
  93. data/lib/rubex/ast/top_statement/klass/attached_klass.rb +417 -0
  94. data/lib/rubex/ast/top_statement/method_def.rb +110 -0
  95. data/lib/rubex/ast/top_statement/method_def/c_function_def.rb +26 -0
  96. data/lib/rubex/ast/top_statement/method_def/ruby_method_def.rb +33 -0
  97. data/lib/rubex/cli.rb +26 -0
  98. data/lib/rubex/code_writer.rb +1 -1
  99. data/lib/rubex/compiler.rb +49 -28
  100. data/lib/rubex/compiler_config.rb +4 -2
  101. data/lib/rubex/constants.rb +71 -71
  102. data/lib/rubex/data_type.rb +9 -675
  103. data/lib/rubex/data_type/c_array.rb +33 -0
  104. data/lib/rubex/data_type/c_function.rb +23 -0
  105. data/lib/rubex/data_type/c_ptr.rb +71 -0
  106. data/lib/rubex/data_type/c_str.rb +23 -0
  107. data/lib/rubex/data_type/c_struct_or_union.rb +23 -0
  108. data/lib/rubex/data_type/char.rb +30 -0
  109. data/lib/rubex/data_type/f_32.rb +38 -0
  110. data/lib/rubex/data_type/f_64.rb +38 -0
  111. data/lib/rubex/data_type/int.rb +32 -0
  112. data/lib/rubex/data_type/int/c_boolean.rb +13 -0
  113. data/lib/rubex/data_type/int_16.rb +32 -0
  114. data/lib/rubex/data_type/int_32.rb +32 -0
  115. data/lib/rubex/data_type/int_64.rb +36 -0
  116. data/lib/rubex/data_type/int_8.rb +33 -0
  117. data/lib/rubex/data_type/l_int.rb +38 -0
  118. data/lib/rubex/data_type/l_l_int.rb +26 -0
  119. data/lib/rubex/data_type/ruby_method.rb +22 -0
  120. data/lib/rubex/data_type/ruby_object.rb +19 -0
  121. data/lib/rubex/data_type/ruby_object/boolean.rb +11 -0
  122. data/lib/rubex/data_type/ruby_object/boolean/false_type.rb +5 -0
  123. data/lib/rubex/data_type/ruby_object/boolean/true_type.rb +5 -0
  124. data/lib/rubex/data_type/ruby_object/nil_type.rb +9 -0
  125. data/lib/rubex/data_type/ruby_object/ruby_array.rb +10 -0
  126. data/lib/rubex/data_type/ruby_object/ruby_constant.rb +18 -0
  127. data/lib/rubex/data_type/ruby_object/ruby_constant/ruby_class.rb +18 -0
  128. data/lib/rubex/data_type/ruby_object/ruby_hash.rb +9 -0
  129. data/lib/rubex/data_type/ruby_object/ruby_string.rb +10 -0
  130. data/lib/rubex/data_type/ruby_object/ruby_symbol.rb +10 -0
  131. data/lib/rubex/data_type/type_def.rb +34 -0
  132. data/lib/rubex/data_type/u_char.rb +27 -0
  133. data/lib/rubex/data_type/u_int.rb +32 -0
  134. data/lib/rubex/data_type/u_int_16.rb +22 -0
  135. data/lib/rubex/data_type/u_int_32.rb +22 -0
  136. data/lib/rubex/data_type/u_int_64.rb +26 -0
  137. data/lib/rubex/data_type/u_int_8.rb +22 -0
  138. data/lib/rubex/data_type/u_l_int.rb +36 -0
  139. data/lib/rubex/data_type/u_l_int/size_t.rb +10 -0
  140. data/lib/rubex/data_type/u_l_l_int.rb +26 -0
  141. data/lib/rubex/data_type/void.rb +15 -0
  142. data/lib/rubex/data_type_helpers/float_helpers.rb +8 -0
  143. data/lib/rubex/data_type_helpers/helpers.rb +48 -0
  144. data/lib/rubex/data_type_helpers/int_helpers.rb +10 -0
  145. data/lib/rubex/data_type_helpers/u_int_helpers.rb +11 -0
  146. data/lib/rubex/helpers.rb +35 -118
  147. data/lib/rubex/helpers/node_type_methods.rb +9 -0
  148. data/lib/rubex/helpers/writers.rb +79 -0
  149. data/lib/rubex/parser.racc +83 -34
  150. data/lib/rubex/parser.racc.rb +233 -184
  151. data/lib/rubex/version.rb +2 -2
  152. data/rubex.gemspec +2 -0
  153. data/spec/basic_ruby_method_spec.rb +1 -1
  154. data/spec/binding_ptr_args_spec.rb +2 -2
  155. data/spec/bitwise_operators_spec.rb +1 -1
  156. data/spec/blocks_spec.rb +2 -2
  157. data/spec/c_bindings_spec.rb +1 -1
  158. data/spec/c_constants_spec.rb +1 -1
  159. data/spec/c_function_ptrs_spec.rb +1 -1
  160. data/spec/c_functions_spec.rb +2 -2
  161. data/spec/c_struct_interface_spec.rb +1 -1
  162. data/spec/call_by_reference_spec.rb +2 -2
  163. data/spec/class_methods_spec.rb +2 -2
  164. data/spec/class_spec.rb +4 -4
  165. data/spec/cli_spec.rb +43 -0
  166. data/spec/comments_spec.rb +2 -2
  167. data/spec/default_args_spec.rb +21 -23
  168. data/spec/error_handling_spec.rb +1 -1
  169. data/spec/examples_spec.rb +4 -4
  170. data/spec/expressions_spec.rb +1 -1
  171. data/spec/fixtures/cli/cli.rubex +3 -0
  172. data/spec/fixtures/examples/array_to_hash.rubex +1 -1
  173. data/spec/fixtures/examples/rcsv.rubex +10 -6
  174. data/spec/fixtures/loops/loops.rubex +1 -1
  175. data/spec/fixtures/ruby_strings/string_blank_bm.rb +7 -5
  176. data/spec/fixtures/struct/struct.rubex +7 -2
  177. data/spec/fixtures/temp_allocation/temp_allocation.rubex +8 -0
  178. data/spec/if_else_spec.rb +3 -7
  179. data/spec/implicit_lib_include_spec.rb +1 -1
  180. data/spec/init_ruby_objects_with_literal_syntax_spec.rb +1 -1
  181. data/spec/loops_spec.rb +1 -1
  182. data/spec/recursion_spec.rb +18 -21
  183. data/spec/ruby_constant_method_calls_spec.rb +4 -4
  184. data/spec/ruby_operators_spec.rb +1 -1
  185. data/spec/ruby_raise_spec.rb +1 -1
  186. data/spec/ruby_strings_spec.rb +3 -3
  187. data/spec/ruby_symbols_spec.rb +1 -1
  188. data/spec/ruby_types_spec.rb +2 -2
  189. data/spec/spec_helper.rb +42 -10
  190. data/spec/statement_expression_spec.rb +3 -3
  191. data/spec/static_array_spec.rb +3 -3
  192. data/spec/string_literals_spec.rb +2 -2
  193. data/spec/struct_spec.rb +4 -4
  194. data/spec/temp_allocation_spec.rb +35 -0
  195. data/spec/typecasting_spec.rb +2 -2
  196. data/spec/var_declarions_spec.rb +2 -2
  197. metadata +168 -3
@@ -0,0 +1,145 @@
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 = lib
9
+ @comp_opts = comp_opts
10
+ @declarations = declarations
11
+ @location = location
12
+ end
13
+
14
+ def analyse_statement(local_scope)
15
+ unless @declarations
16
+ @declarations = []
17
+ load_predecided_declarations
18
+ end
19
+ @declarations.each do |stat|
20
+ stat.analyse_statement local_scope, extern: true
21
+ end
22
+ local_scope.include_files.push @lib
23
+ update_compiler_config
24
+ end
25
+
26
+ def generate_code(code); end
27
+
28
+ private
29
+
30
+ def update_compiler_config
31
+ @comp_opts.each do |h|
32
+ Rubex::Compiler::CONFIG.add_link h[:link] if h[:link]
33
+ end
34
+ end
35
+
36
+ def load_predecided_declarations
37
+ if @lib == 'rubex/ruby'
38
+ load_ruby_functions_and_types
39
+ @lib = '<ruby.h>'
40
+ elsif @lib == 'rubex/ruby/encoding'
41
+ load_ruby_encoding_functions_and_type
42
+ @lib = '<ruby/encoding.h>'
43
+ elsif @lib == 'rubex/stdlib'
44
+ load_stdlib_functions_and_types
45
+ @lib = '<stdlib.h>'
46
+ else
47
+ raise Rubex::LibraryNotFoundError, "Cannot find #{@lib}."
48
+ end
49
+ end
50
+
51
+ def load_ruby_functions_and_types
52
+ @declarations << xmalloc
53
+ @declarations << xfree
54
+ @declarations << type_get
55
+ @declarations.concat type_identifiers
56
+ @declarations << rb_str_new
57
+ @declarations << rb_ary_includes
58
+ end
59
+
60
+ def load_ruby_encoding_functions_and_type
61
+ @declarations << rb_enc_associate_index
62
+ @declarations << rb_enc_find_index
63
+ end
64
+
65
+ def load_stdlib_functions_and_types
66
+ @declarations.concat atox_functions
67
+ end
68
+
69
+ def rb_ary_includes
70
+ cfunc_decl('object', '', 'rb_ary_includes',
71
+ arg_list([arg('object', '', 'ary'), arg('object', '', 'item')]))
72
+ end
73
+
74
+ def atox_functions
75
+ [
76
+ %w[int atoi], %w[long atol], ['long long', 'atoll'],
77
+ %w[double atof]
78
+ ].map do |type, ident|
79
+ cfunc_decl(type, '', ident, arg_list([arg('char', '*', 'str')]))
80
+ end
81
+ end
82
+
83
+ def rb_enc_find_index
84
+ cfunc_decl('int', '', 'rb_enc_find_index',
85
+ arg_list([arg('char', '*', 'enc')]))
86
+ end
87
+
88
+ def rb_enc_associate_index
89
+ args = arg_list([arg('object', '', 'string'), arg('int', '', 'enc')])
90
+ cfunc_decl('object', '', 'rb_enc_associate_index', args)
91
+ end
92
+
93
+ def rb_str_new
94
+ args = arg_list([arg('char', '*', 'str'), arg('long', '', 'length')])
95
+ cfunc_decl('object', '', 'rb_str_new', args)
96
+ end
97
+
98
+ def type_get
99
+ cfunc_decl('int', '', 'TYPE', arg_list([arg('object', '', 'dummy')]))
100
+ end
101
+
102
+ def type_identifiers
103
+ stmts = %w[
104
+ T_ARRAY T_NIL T_TRUE T_FALSE T_FLOAT T_FIXNUM
105
+ T_BIGNUM T_REGEXP T_STRING
106
+ ].map do |ident|
107
+ Statement::VarDecl.new('int', ident, nil, @location)
108
+ end
109
+
110
+ stmts
111
+ end
112
+
113
+ def xmalloc
114
+ args = Statement::ArgumentList.new([
115
+ Expression::ArgDeclaration.new(
116
+ dtype: 'size_t', variables: [{ ident: 'dummy' }]
117
+ )
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
142
+
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,125 @@
1
+ module Rubex
2
+ module AST
3
+ module TopStatement
4
+ class Klass
5
+ include Rubex::Helpers::Writers
6
+ # Stores the scope of the class. Rubex::SymbolTable::Scope::Klass.
7
+ attr_reader :scope
8
+
9
+ attr_reader :name
10
+
11
+ attr_reader :ancestor
12
+
13
+ attr_reader :statements
14
+
15
+ attr_reader :entry
16
+
17
+ # Name of the class. Ancestor can be Scope::Klass or String object
18
+ # depending on whether invoker is another higher level scope or
19
+ # the parser. Statements are the statements inside the class.
20
+ def initialize(name, ancestor, statements)
21
+ @name = name
22
+ @ancestor = ancestor
23
+ @statements = statements
24
+ @ancestor = 'Object' if @ancestor.nil?
25
+ end
26
+
27
+ def analyse_statement(local_scope, attach_klass: false)
28
+ @entry = local_scope.find(@name)
29
+ @scope = @entry.type.scope
30
+ @ancestor = @entry.type.ancestor
31
+ add_statement_symbols_to_symbol_table
32
+ unless attach_klass
33
+ @statements.each do |stat|
34
+ stat.analyse_statement @scope
35
+ end
36
+ end
37
+ end
38
+
39
+ def rescan_declarations(_local_scope)
40
+ @statements.each do |stat|
41
+ stat&.rescan_declarations(@scope)
42
+ end
43
+ end
44
+
45
+ def generate_code(code)
46
+ @scope.begin_block_callbacks.each do |cb|
47
+ cb.generate_code code
48
+ end
49
+
50
+ @statements.each do |stat|
51
+ stat.generate_code code
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def add_statement_symbols_to_symbol_table
58
+ @statements.each do |stmt|
59
+ next unless ruby_method_or_c_function?(stmt)
60
+ f_name, f_scope = prepare_name_and_scope_of_functions(stmt)
61
+ next if auxillary_c_function_for_attached_klass?(f_name)
62
+ stmt.arg_list.analyse_statement(f_scope)
63
+ if stmt.is_a? Rubex::AST::TopStatement::RubyMethodDef
64
+ add_ruby_method_to_scope f_name, f_scope, stmt.arg_list
65
+ elsif stmt.is_a? Rubex::AST::TopStatement::CFunctionDef
66
+ return_type = Helpers.determine_dtype(stmt.type, stmt.return_ptr_level)
67
+ add_c_function_to_scope f_name, f_scope, stmt.arg_list, return_type
68
+ end
69
+ end
70
+ end
71
+
72
+ def auxillary_c_function_for_attached_klass?(f_name)
73
+ is_a?(AttachedKlass) && [
74
+ Rubex::ALLOC_FUNC_NAME, Rubex::DEALLOC_FUNC_NAME,
75
+ Rubex::MEMCOUNT_FUNC_NAME, Rubex::GET_STRUCT_FUNC_NAME
76
+ ].include?(f_name)
77
+ end
78
+
79
+ def add_c_function_to_scope(f_name, f_scope, arg_list, return_type)
80
+ c_name = c_func_c_name(f_name)
81
+ arg_list.each do |arg|
82
+ if arg.entry&.value
83
+ e = arg.entry
84
+ e.value = Rubex::Helpers.to_lhs_type(e, e.value)
85
+ end
86
+ end
87
+ @scope.add_c_method(
88
+ name: f_name,
89
+ c_name: c_name,
90
+ extern: false,
91
+ return_type: return_type,
92
+ arg_list: arg_list,
93
+ scope: f_scope
94
+ )
95
+ end
96
+
97
+ def add_ruby_method_to_scope(f_name, f_scope, arg_list)
98
+ c_name = Rubex::RUBY_FUNC_PREFIX + @name + '_' +
99
+ f_name.gsub('?', '_qmark').gsub('!', '_bang')
100
+ @scope.add_ruby_method(
101
+ name: f_name,
102
+ c_name: c_name,
103
+ scope: f_scope,
104
+ arg_list: arg_list
105
+ )
106
+ end
107
+
108
+ def prepare_name_and_scope_of_functions(stmt)
109
+ f_name = stmt.name
110
+ f_scope = Rubex::SymbolTable::Scope::Local.new f_name, @scope
111
+ [f_name, f_scope]
112
+ end
113
+
114
+ def ruby_method_or_c_function?(stmt)
115
+ stmt.is_a?(Rubex::AST::TopStatement::RubyMethodDef) ||
116
+ stmt.is_a?(Rubex::AST::TopStatement::CFunctionDef)
117
+ end
118
+
119
+ def c_func_c_name(name)
120
+ Rubex::C_FUNC_PREFIX + @name + '_' + name
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,417 @@
1
+ module Rubex
2
+ module AST
3
+ module TopStatement
4
+ class AttachedKlass < Klass
5
+ attr_reader :attached_type
6
+
7
+ ALLOC_FUNC_NAME = Rubex::ALLOC_FUNC_NAME
8
+ DEALLOC_FUNC_NAME = Rubex::DEALLOC_FUNC_NAME
9
+ MEMCOUNT_FUNC_NAME = Rubex::MEMCOUNT_FUNC_NAME
10
+ GET_STRUCT_FUNC_NAME = Rubex::GET_STRUCT_FUNC_NAME
11
+
12
+ def initialize(name, attached_type, ancestor, statements, location)
13
+ @attached_type = attached_type
14
+ @location = location
15
+ super(name, ancestor, statements)
16
+ end
17
+
18
+ def analyse_statement(outer_scope)
19
+ super(outer_scope, attach_klass: true)
20
+ prepare_data_holding_struct
21
+ prepare_rb_data_type_t_struct
22
+ detach_and_modify_auxillary_c_functions_from_statements
23
+ add_auxillary_functions_to_klass_scope
24
+ prepare_auxillary_c_functions
25
+ @statements[1..-1].each do |stmt| # 0th stmt is the data struct
26
+ if ruby_method_or_c_func?(stmt)
27
+ rewrite_method_with_data_fetching stmt
28
+ end
29
+ stmt.analyse_statement @scope
30
+ end
31
+ analyse_auxillary_c_functions
32
+ end
33
+
34
+ def generate_code(code)
35
+ write_auxillary_c_functions code
36
+ write_data_type_t_struct code
37
+ write_get_struct_c_function code
38
+ write_alloc_c_function code
39
+ super
40
+ end
41
+
42
+ private
43
+
44
+ # Since auxillary functions are detached from the klass statements,
45
+ # analyse their arg_list separately and add function names to the
46
+ # class scope so that their statements can be analysed properly later.
47
+ def add_auxillary_functions_to_klass_scope
48
+ @auxillary_c_functions.each_value do |func|
49
+ f_name, f_scope = prepare_name_and_scope_of_functions func
50
+ func.arg_list.analyse_statement(f_scope)
51
+ return_type = Helpers.determine_dtype(func.type, func.return_ptr_level)
52
+ add_c_function_to_scope f_name, f_scope, func.arg_list, return_type
53
+ end
54
+ end
55
+
56
+ def analyse_auxillary_c_functions
57
+ @auxillary_c_functions.each_value do |func|
58
+ func.analyse_statement(@scope)
59
+ end
60
+ end
61
+
62
+ # Detach the user-supplied auxlically C functions from the class and
63
+ # store them in Hash @auxillary_c_functions. Make modifications to
64
+ # them if necessary and also set an ivar that indicates if an
65
+ # auxillary function is user-supplied or not.
66
+ def detach_and_modify_auxillary_c_functions_from_statements
67
+ @auxillary_c_functions = {}
68
+
69
+ indexes = []
70
+ @statements.each_with_index do |stmt, idx|
71
+ if stmt.is_a?(CFunctionDef)
72
+ if stmt.name == ALLOC_FUNC_NAME
73
+ @auxillary_c_functions[ALLOC_FUNC_NAME] = stmt
74
+ @user_defined_alloc = true
75
+ indexes << idx
76
+ elsif stmt.name == DEALLOC_FUNC_NAME
77
+ @auxillary_c_functions[DEALLOC_FUNC_NAME] = stmt
78
+ @user_defined_dealloc = true
79
+ modify_dealloc_func stmt
80
+ indexes << idx
81
+ elsif stmt.name == MEMCOUNT_FUNC_NAME
82
+ @auxillary_c_functions[MEMCOUNT_FUNC_NAME] = stmt
83
+ @user_defined_memcount = true
84
+ indexes << idx
85
+ elsif stmt.name == GET_STRUCT_FUNC_NAME
86
+ @auxillary_c_functions[GET_STRUCT_FUNC_NAME] = stmt
87
+ @user_defined_get_struct = true
88
+ indexes << idx
89
+ end
90
+ end
91
+ end
92
+
93
+ indexes.each do |idx|
94
+ @statements.delete_at idx
95
+ end
96
+ end
97
+
98
+ def write_auxillary_c_functions(code)
99
+ write_dealloc_c_function code
100
+ write_memcount_c_function code
101
+ end
102
+
103
+ # Actually write the alloc function into C code.
104
+ def write_alloc_c_function(code)
105
+ if user_defined_alloc?
106
+ @auxillary_c_functions[ALLOC_FUNC_NAME].generate_code code
107
+ else
108
+ code.write_c_method_header(
109
+ type: @alloc_c_func.type.type.to_s,
110
+ c_name: @alloc_c_func.c_name,
111
+ args: Helpers.create_arg_arrays(@alloc_c_func.type.arg_list)
112
+ )
113
+ code.block do
114
+ lines = ''
115
+ lines << "#{@data_struct.entry.c_name} *data;\n\n"
116
+
117
+ lines << "data = (#{@data_struct.entry.c_name}*)xmalloc("
118
+ lines << "sizeof(#{@data_struct.entry.c_name}));\n"
119
+
120
+ lines << member_struct_allocations
121
+
122
+ lines << 'return TypedData_Wrap_Struct('
123
+ lines << "#{@alloc_c_func.type.arg_list[0].entry.c_name},"
124
+ lines << "&#{@data_type_t}, data);\n"
125
+
126
+ code << lines
127
+ end
128
+ end
129
+ end
130
+
131
+ # TODO: modify for supporting inheritance
132
+ def member_struct_allocations
133
+ c_name = @scope.find(@attached_type).c_name
134
+ "data->#{Rubex::POINTER_PREFIX + @attached_type} = (#{c_name}*)xmalloc(sizeof(#{c_name}));\n"
135
+ end
136
+
137
+ # Actually write the dealloc function into C code.
138
+ def write_dealloc_c_function(code)
139
+ if user_defined_dealloc?
140
+ @auxillary_c_functions[DEALLOC_FUNC_NAME].generate_code code
141
+ else
142
+ # TODO: define dealloc if user hasnt supplied.
143
+ end
144
+ end
145
+
146
+ # Actually write the memcount function into C code.
147
+ def write_memcount_c_function(code)
148
+ if user_defined_memcount?
149
+ @auxillary_c_functions[MEMCOUNT_FUNC_NAME].generate_code code
150
+ else
151
+ code.write_c_method_header(
152
+ type: @memcount_c_func.type.type.to_s,
153
+ c_name: @memcount_c_func.c_name,
154
+ args: Helpers.create_arg_arrays(@memcount_c_func.type.arg_list)
155
+ )
156
+ code.block do
157
+ code << "return sizeof(#{@memcount_c_func.type.arg_list[0].entry.c_name})"
158
+ code.colon
159
+ code.nl
160
+ end
161
+ end
162
+ end
163
+
164
+ # Actually write the get_struct function into C code.
165
+ def write_get_struct_c_function(code)
166
+ if user_defined_get_struct?
167
+ @auxillary_c_functions[GET_STRUCT_FUNC_NAME].generate_code code
168
+ else
169
+ code.write_c_method_header(
170
+ type: @get_struct_c_func.type.type.to_s,
171
+ c_name: @get_struct_c_func.c_name,
172
+ args: Helpers.create_arg_arrays(@get_struct_c_func.type.arg_list)
173
+ )
174
+ code.block do
175
+ lines = ''
176
+ lines << "#{@data_struct.entry.c_name} *data;\n\n"
177
+ lines << 'TypedData_Get_Struct('
178
+ lines << "#{@get_struct_c_func.type.arg_list[0].entry.c_name}, "
179
+ lines << "#{@data_struct.entry.c_name}, &#{@data_type_t}, data);\n"
180
+ lines << "return data;\n"
181
+
182
+ code << lines
183
+ end
184
+ end
185
+ end
186
+
187
+ def write_data_type_t_struct(code)
188
+ decl = ''
189
+ decl << "static const rb_data_type_t #{@data_type_t} = {\n"
190
+ decl << " \"#{@name}\",\n"
191
+ decl << " {0, #{@dealloc_c_func.c_name}, #{@memcount_c_func.c_name},\n"
192
+ decl << " 0}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY\n"
193
+ decl << "};\n"
194
+
195
+ code << decl
196
+ code.new_line
197
+ end
198
+
199
+ # Prepare the data holding struct 'data' that will hold a pointer to the
200
+ # struct that is attached to this class.
201
+ def prepare_data_holding_struct
202
+ struct_name = @name + '_data_struct'
203
+ declarations = declarations_for_data_struct
204
+ @data_struct = Statement::CStructOrUnionDef.new(
205
+ :struct, struct_name, declarations, @location
206
+ )
207
+ @data_struct.analyse_statement(@scope)
208
+ @statements.unshift @data_struct
209
+ end
210
+
211
+ # TODO: support inherited attached structs.
212
+ def declarations_for_data_struct
213
+ stmts = []
214
+ stmts << Statement::CPtrDecl.new(@attached_type, @attached_type, nil,
215
+ '*', @location)
216
+
217
+ stmts
218
+ end
219
+
220
+ def prepare_rb_data_type_t_struct
221
+ @data_type_t = Rubex::ATTACH_CLASS_PREFIX + '_' + @name + '_data_type_t'
222
+ end
223
+
224
+ # Prepare auxillary function in case they have not been supplied by user
225
+ # and create ivars for their Symbol Table entries for easy accesss later.
226
+ def prepare_auxillary_c_functions
227
+ prepare_alloc_c_function
228
+ prepare_memcount_c_function
229
+ prepare_deallocation_c_function
230
+ prepare_get_struct_c_function
231
+ end
232
+
233
+ def ruby_method_or_c_func?(stmt)
234
+ stmt.is_a?(RubyMethodDef) || stmt.is_a?(CFunctionDef)
235
+ end
236
+
237
+ # Rewrite method `stmt` so that the `data` variable becomes available
238
+ # inside the scope of the method.
239
+ def rewrite_method_with_data_fetching(stmt)
240
+ data_stmt = Statement::CPtrDecl.new(@data_struct.name, 'data',
241
+ get_struct_func_call(stmt), '*', @location)
242
+ stmt.statements.unshift data_stmt
243
+ end
244
+
245
+ def get_struct_func_call(_stmt)
246
+ Expression::CommandCall.new(nil, @get_struct_c_func.name,
247
+ Expression::ActualArgList.new([]))
248
+ end
249
+
250
+ # Create an alloc function if it is not supplied by user.
251
+ def prepare_alloc_c_function
252
+ if user_defined_alloc?
253
+ @alloc_c_func = @scope.find(ALLOC_FUNC_NAME)
254
+ else
255
+ c_name = c_func_c_name(ALLOC_FUNC_NAME)
256
+ scope = Rubex::SymbolTable::Scope::Local.new(ALLOC_FUNC_NAME, @scope)
257
+ arg_list = Statement::ArgumentList.new([
258
+ Expression::ArgDeclaration.new(
259
+ dtype: 'object',
260
+ variables: [
261
+ {
262
+ ident: 'self'
263
+ }
264
+ ]
265
+ )
266
+ ])
267
+ arg_list.analyse_statement(scope)
268
+ @alloc_c_func = @scope.add_c_method(
269
+ name: ALLOC_FUNC_NAME,
270
+ c_name: c_name,
271
+ arg_list: arg_list,
272
+ return_type: DataType::RubyObject.new,
273
+ scope: scope
274
+ )
275
+ end
276
+ end
277
+
278
+ # Create a memcount function if it is not supplied by user.
279
+ def prepare_memcount_c_function
280
+ if user_defined_memcount?
281
+ @memcount_c_func = @scope.find(MEMCOUNT_FUNC_NAME)
282
+ else
283
+ c_name = c_func_c_name(MEMCOUNT_FUNC_NAME)
284
+ scope = Rubex::SymbolTable::Scope::Local.new(MEMCOUNT_FUNC_NAME, @scope)
285
+ arg_list = Statement::ArgumentList.new([
286
+ Expression::ArgDeclaration.new(
287
+ dtype: 'void',
288
+ variables: [
289
+ {
290
+ ptr_level: '*',
291
+ ident: 'raw_data'
292
+ }
293
+ ]
294
+ )
295
+ ])
296
+ arg_list.analyse_statement(scope)
297
+ @memcount_c_func = @scope.add_c_method(
298
+ name: MEMCOUNT_FUNC_NAME,
299
+ c_name: c_name,
300
+ arg_list: arg_list,
301
+ return_type: DataType::Size_t.new,
302
+ scope: scope
303
+ )
304
+ end
305
+ end
306
+
307
+ # Create a deallocate function if it is not supplied by user.
308
+ def prepare_deallocation_c_function
309
+ if user_defined_dealloc?
310
+ @dealloc_c_func = @scope.find(DEALLOC_FUNC_NAME)
311
+ else
312
+ c_name = c_func_c_name(DEALLOC_FUNC_NAME)
313
+ scope = Rubex::SymbolTable::Scope::Local.new(DEALLOC_FUNC_NAME, @scope)
314
+ arg_list = Statement::ArgumentList.new([
315
+ Expression::ArgDeclaration.new(
316
+ dtype: 'void',
317
+ variables: [
318
+ {
319
+ ptr_level: '*',
320
+ ident: 'raw_data'
321
+ }
322
+ ]
323
+ )
324
+ ])
325
+ arg_list.analyse_statement(scope)
326
+ @dealloc_c_func = @scope.add_c_method(
327
+ name: DEALLOC_FUNC_NAME,
328
+ c_name: c_name,
329
+ return_type: DataType::Void.new,
330
+ scope: scope,
331
+ arg_list: arg_list
332
+ )
333
+ end
334
+ end
335
+
336
+ # Create a get_struct function if it is not supplied by user.
337
+ def prepare_get_struct_c_function
338
+ if user_defined_get_struct?
339
+ @get_struct_c_func = @scope.find(GET_STRUCT_FUNC_NAME)
340
+ else
341
+ c_name = c_func_c_name(GET_STRUCT_FUNC_NAME)
342
+ scope = Rubex::SymbolTable::Scope::Local.new(
343
+ GET_STRUCT_FUNC_NAME, @scope
344
+ )
345
+ arg_list = Statement::ArgumentList.new([
346
+ Expression::ArgDeclaration.new(
347
+ dtype: 'object',
348
+ variables: [
349
+ {
350
+ ident: 'obj'
351
+ }
352
+ ]
353
+ )
354
+ ])
355
+ arg_list.analyse_statement(scope)
356
+ return_type = DataType::CPtr.new(
357
+ DataType::CStructOrUnion.new(
358
+ :struct, @data_struct.name, @data_struct.entry.c_name, nil
359
+ )
360
+ )
361
+ @get_struct_c_func = @scope.add_c_method(
362
+ name: GET_STRUCT_FUNC_NAME,
363
+ c_name: c_name,
364
+ return_type: return_type,
365
+ arg_list: arg_list,
366
+ scope: scope
367
+ )
368
+ end
369
+ end
370
+
371
+ # Modify the dealloc function by adding an argument of type void* so
372
+ # that it is compatible with what Ruby expects. This is done so that
373
+ # the user is not burdened with additional knowledge of knowing the
374
+ # the correct argument for deallocate.
375
+ def modify_dealloc_func(func)
376
+ func.arg_list = Statement::ArgumentList.new([
377
+ Expression::ArgDeclaration.new(
378
+ dtype: 'void',
379
+ variables: [
380
+ {
381
+ ptr_level: '*',
382
+ ident: 'raw_data'
383
+ }
384
+ ]
385
+ )
386
+ ])
387
+ value = Expression::Name.new('raw_data')
388
+ value.typecast = Expression::Typecast.new(@data_struct.name, '*')
389
+ data_var = Statement::CPtrDecl.new(@data_struct.name, 'data', value,
390
+ '*', @location)
391
+ xfree = Expression::CommandCall.new(nil, 'xfree',
392
+ Expression::ActualArgList.new(
393
+ [Expression::Name.new('data')]))
394
+ data_xfree = Statement::Expression.new xfree, @location
395
+ func.statements.unshift data_var
396
+ func.statements.push data_xfree
397
+ end
398
+
399
+ def user_defined_dealloc?
400
+ @user_defined_dealloc
401
+ end
402
+
403
+ def user_defined_alloc?
404
+ @user_defined_alloc
405
+ end
406
+
407
+ def user_defined_memcount?
408
+ @user_defined_memcount
409
+ end
410
+
411
+ def user_defined_get_struct?
412
+ @user_defined_get_struct
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end