rubex 0.1 → 0.1.1

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