rubex 0.1.1 → 0.1.2

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 (131) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/CONTRIBUTING.md +73 -9
  4. data/HISTORY.md +19 -0
  5. data/README.md +53 -8
  6. data/REFERENCE.md +112 -44
  7. data/benchmarks/no_gil/no_gil.rb +24 -0
  8. data/benchmarks/no_gil/no_gil.rubex +22 -0
  9. data/bin/rubex +1 -1
  10. data/examples/c_struct_interface/c_struct_interface.rubex +1 -0
  11. data/lib/rubex.rb +1 -0
  12. data/lib/rubex/ast.rb +11 -7
  13. data/lib/rubex/ast/expression.rb +1 -1
  14. data/lib/rubex/ast/expression/actual_arg_list.rb +7 -0
  15. data/lib/rubex/ast/expression/analysed_element_ref/c_var_element_ref.rb +5 -2
  16. data/lib/rubex/ast/expression/analysed_element_ref/ruby_object_element_ref.rb +26 -9
  17. data/lib/rubex/ast/expression/binary/binary_boolean.rb +1 -1
  18. data/lib/rubex/ast/expression/binary/binary_expo.rb +34 -0
  19. data/lib/rubex/ast/expression/binary/colon2.rb +34 -0
  20. data/lib/rubex/ast/expression/binary/empty_classes.rb +0 -3
  21. data/lib/rubex/ast/expression/command_call.rb +24 -0
  22. data/lib/rubex/ast/{statement → expression/command_call}/print.rb +4 -5
  23. data/lib/rubex/ast/{statement → expression/command_call}/raise.rb +29 -24
  24. data/lib/rubex/ast/expression/command_call/require.rb +27 -0
  25. data/lib/rubex/ast/expression/command_call/yield.rb +36 -0
  26. data/lib/rubex/ast/expression/element_ref.rb +10 -5
  27. data/lib/rubex/ast/expression/instance_var.rb +33 -0
  28. data/lib/rubex/ast/expression/method_call.rb +4 -2
  29. data/lib/rubex/ast/expression/method_call/c_function_call.rb +4 -3
  30. data/lib/rubex/ast/expression/method_call/ruby_method_call.rb +7 -5
  31. data/lib/rubex/ast/expression/name.rb +10 -1
  32. data/lib/rubex/ast/expression/ruby_object_element_ref/ruby_array_element_ref.rb +5 -2
  33. data/lib/rubex/ast/expression/ruby_object_element_ref/ruby_hash_element_ref.rb +10 -2
  34. data/lib/rubex/ast/expression/to_ruby_object.rb +1 -0
  35. data/lib/rubex/ast/expression/unary.rb +7 -3
  36. data/lib/rubex/ast/expression/unary_base/ampersand.rb +5 -0
  37. data/lib/rubex/ast/node.rb +213 -185
  38. data/lib/rubex/ast/node/file_node.rb +25 -0
  39. data/lib/rubex/ast/node/main_node.rb +56 -0
  40. data/lib/rubex/ast/statement/begin_block/begin.rb +4 -3
  41. data/lib/rubex/ast/statement/c_array_decl.rb +1 -1
  42. data/lib/rubex/ast/statement/c_ptr_decl.rb +2 -0
  43. data/lib/rubex/ast/statement/no_gil_block.rb +70 -0
  44. data/lib/rubex/ast/statement/return.rb +1 -0
  45. data/lib/rubex/ast/top_statement.rb +1 -1
  46. data/lib/rubex/ast/top_statement/klass.rb +4 -0
  47. data/lib/rubex/ast/top_statement/klass/attached_klass.rb +88 -10
  48. data/lib/rubex/ast/top_statement/method_def.rb +2 -3
  49. data/lib/rubex/ast/top_statement/method_def/c_function_def.rb +10 -4
  50. data/lib/rubex/cli.rb +11 -6
  51. data/lib/rubex/code_supervisor.rb +49 -0
  52. data/lib/rubex/code_writer.rb +22 -1
  53. data/lib/rubex/compiler.rb +109 -30
  54. data/lib/rubex/compiler_config.rb +14 -1
  55. data/lib/rubex/constants.rb +3 -0
  56. data/lib/rubex/data_type.rb +2 -3
  57. data/lib/rubex/data_type/ruby_object/ruby_symbol.rb +0 -1
  58. data/lib/rubex/error.rb +4 -0
  59. data/lib/rubex/helpers/writers.rb +33 -4
  60. data/lib/rubex/lexer.rex +9 -1
  61. data/lib/rubex/lexer.rex.rb +15 -2
  62. data/lib/rubex/parser.racc +125 -49
  63. data/lib/rubex/parser.racc.rb +1526 -1376
  64. data/lib/rubex/rake_task.rb +42 -6
  65. data/lib/rubex/symbol_table/entry.rb +6 -0
  66. data/lib/rubex/symbol_table/scope.rb +28 -3
  67. data/lib/rubex/version.rb +1 -1
  68. data/rubex.gemspec +1 -0
  69. data/spec/basic_ruby_method_spec.rb +2 -2
  70. data/spec/blocks_spec.rb +2 -2
  71. data/spec/box_op_multi_args_spec.rb +34 -0
  72. data/spec/c_function_ptrs_spec.rb +2 -2
  73. data/spec/c_functions_spec.rb +2 -0
  74. data/spec/c_struct_interface_spec.rb +8 -3
  75. data/spec/default_args_spec.rb +2 -2
  76. data/spec/external_c_struct_spec.rb +33 -0
  77. data/spec/fixtures/api/consumer.rubex +0 -0
  78. data/spec/fixtures/api/implementation.rubex +0 -0
  79. data/spec/fixtures/api/implementation.rubexd +0 -0
  80. data/spec/fixtures/box_op_multi_args/box_op_multi_args.rubex +3 -0
  81. data/spec/fixtures/c_functions/c_functions.rubex +13 -0
  82. data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +28 -0
  83. data/spec/fixtures/class_methods/class_methods.rubex +1 -1
  84. data/spec/fixtures/error_handling/error_handling.rubex +2 -2
  85. data/spec/fixtures/external_c_struct/external_c_struct.rubex +16 -0
  86. data/spec/fixtures/if_else/if_else.rubex +1 -1
  87. data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +1 -1
  88. data/spec/fixtures/instance_variables/instance_variables.rubex +25 -0
  89. data/spec/fixtures/loops/loops.rubex +2 -2
  90. data/spec/fixtures/module/module.rubex +28 -0
  91. data/spec/fixtures/multi_file_programs/Rakefile +8 -0
  92. data/spec/fixtures/multi_file_programs/a.rubex +5 -0
  93. data/spec/fixtures/multi_file_programs/b.rubex +5 -0
  94. data/spec/fixtures/multi_file_programs/multi_file_programs.rubex +14 -0
  95. data/spec/fixtures/no_gil/no_gil.rubex +24 -0
  96. data/spec/fixtures/no_gil_attach_class/no_gil_attach_class.rubex +23 -0
  97. data/spec/fixtures/no_gil_compile_check/no_gil_compile_check.rubex +4 -0
  98. data/spec/fixtures/outside_stmts/outside_stmts.rubex +6 -0
  99. data/spec/fixtures/pow/pow.rubex +4 -0
  100. data/spec/fixtures/rake_task/single_file/test.rubex +3 -0
  101. data/spec/fixtures/recursion/recursion.rubex +1 -1
  102. data/spec/fixtures/ruby_constant_scoping/ruby_constant_scoping.rubex +7 -0
  103. data/spec/fixtures/ruby_operators/ruby_operators.rubex +1 -1
  104. data/spec/fixtures/ruby_raise/ruby_raise.rubex +2 -2
  105. data/spec/fixtures/ruby_types/ruby_types.rubex +4 -4
  106. data/spec/fixtures/statement_expression/statement_expression.rubex +2 -2
  107. data/spec/fixtures/static_array/static_array.rubex +3 -3
  108. data/spec/fixtures/string_literals/string_literals.rubex +12 -2
  109. data/spec/fixtures/struct/struct.rubex +1 -1
  110. data/spec/fixtures/var_declarations/var_declarations.rubex +1 -1
  111. data/spec/implicit_lib_include_spec.rb +2 -2
  112. data/spec/init_ruby_objects_with_literal_syntax_spec.rb +2 -2
  113. data/spec/instance_variables_spec.rb +33 -0
  114. data/spec/loops_spec.rb +2 -2
  115. data/spec/module_spec.rb +39 -0
  116. data/spec/multi_file_programs_spec.rb +41 -0
  117. data/spec/no_gil_attach_class_spec.rb +33 -0
  118. data/spec/no_gil_compile_check_spec.rb +25 -0
  119. data/spec/no_gil_spec.rb +36 -0
  120. data/spec/outside_stmts_spec.rb +34 -0
  121. data/spec/pow_spec.rb +33 -0
  122. data/spec/rake_task_spec.rb +142 -0
  123. data/spec/recursion_spec.rb +4 -4
  124. data/spec/ruby_constant_scoping_spec.rb +42 -0
  125. data/spec/ruby_raise_spec.rb +2 -2
  126. data/spec/ruby_symbols_spec.rb +2 -2
  127. data/spec/ruby_types_spec.rb +2 -2
  128. data/spec/spec_helper.rb +17 -3
  129. data/spec/string_literals_spec.rb +1 -0
  130. metadata +90 -6
  131. data/lib/rubex/ast/statement/yield.rb +0 -41
@@ -0,0 +1,36 @@
1
+ module Rubex
2
+ module AST
3
+ module Expression
4
+ class Yield < Base
5
+ def initialize(args)
6
+ @args = args
7
+ end
8
+
9
+ def analyse_types(local_scope)
10
+ @args = @args.map do |arg|
11
+ arg.analyse_types local_scope
12
+ arg.allocate_temps local_scope
13
+ arg.to_ruby_object
14
+ end
15
+ @args.each do |arg|
16
+ arg.release_temps local_scope
17
+ end
18
+ @subexprs = @args
19
+ end
20
+
21
+ def generate_evaluation_code(code, local_scope)
22
+ generate_and_dispose_subexprs(code, local_scope) do
23
+ if !@args.empty?
24
+ code << "rb_yield_values(#{@args.size}, "
25
+ code << (@args.map { |a| a.c_code(local_scope) }.join(',')).to_s
26
+ code << ');'
27
+ else
28
+ code << 'rb_yield(Qnil);'
29
+ end
30
+ code.nl
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -5,12 +5,17 @@ module Rubex
5
5
  # Readers needed for accessing elements due to delegator classes.
6
6
  attr_reader :entry, :pos, :name, :subexprs
7
7
  extend Forwardable
8
- def_delegators :@element_ref, :generate_disposal_code, :generate_evaluation_code,
9
- :analyse_statement, :generate_element_ref_code,
10
- :generate_assignment_code, :has_temp, :c_code,
11
- :allocate_temps, :release_temps, :to_ruby_object,
12
- :from_ruby_object
8
+ def_delegators :@element_ref, :generate_disposal_code,
9
+ :generate_evaluation_code,
10
+ :analyse_statement, :generate_element_ref_code,
11
+ :generate_assignment_code, :has_temp, :c_code,
12
+ :allocate_temps, :release_temps, :to_ruby_object,
13
+ :from_ruby_object
13
14
 
15
+ # Initialize an array_ref. Can be in an expression or variable decl.
16
+ #
17
+ # @param name [String] The name in the code of the variable.
18
+ # @param pos [Expression::ActualArgList] Position information.
14
19
  def initialize(name, pos)
15
20
  @name = name
16
21
  @pos = pos
@@ -0,0 +1,33 @@
1
+ module Rubex
2
+ module AST
3
+ module Expression
4
+ class InstanceVar < Base
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def analyse_types(local_scope)
10
+ @type = DataType::RubyObject.new
11
+ @has_temp = true
12
+ end
13
+
14
+ def generate_evaluation_code(code, local_scope)
15
+ code << "#{@c_code} = rb_iv_get(#{local_scope.self_name}, \"#{@name}\");"
16
+ code.nl
17
+ end
18
+
19
+ def generate_assignment_code(rhs, code, local_scope)
20
+ code << "rb_iv_set(#{local_scope.self_name}, \"#{@name}\","
21
+ code << "#{rhs.c_code(local_scope)});"
22
+ code.nl
23
+ end
24
+
25
+ def c_code(local_scope)
26
+ code = super
27
+ code << @c_code
28
+ code
29
+ end
30
+ end # class InstanceVar
31
+ end # module Expression
32
+ end # module AST
33
+ end # module Rubex
@@ -22,8 +22,10 @@ module Rubex
22
22
  private
23
23
 
24
24
  def type_check_arg_types(entry)
25
- @arg_list.map!.with_index do |arg, idx|
26
- Helpers.to_lhs_type(entry.type.base_type.arg_list[idx], arg)
25
+ unless @arg_list.empty?
26
+ @arg_list.map!.with_index do |arg, idx|
27
+ Helpers.to_lhs_type(entry.type.base_type.arg_list[idx], arg)
28
+ end
27
29
  end
28
30
  end
29
31
 
@@ -5,11 +5,12 @@ module Rubex
5
5
  def analyse_types(local_scope)
6
6
  @entry = local_scope.find(@method_name)
7
7
  super
8
- append_self_argument unless @entry.extern?
9
- @arg_list.analyse_types local_scope
8
+ append_self_argument if !@entry.extern? && !@entry.no_gil
9
+ @type = @entry.type.base_type
10
+ @arg_list.analyse_for_target_type @type.arg_list, local_scope
10
11
  @arg_list.allocate_temps local_scope
11
12
  @arg_list.release_temps local_scope
12
- @type = @entry.type.base_type
13
+
13
14
  type_check_arg_types @entry
14
15
  end
15
16
 
@@ -6,6 +6,7 @@ module Rubex
6
6
  @entry = local_scope.find(@method_name)
7
7
  add_as_ruby_method_to_symtab(local_scope) unless @entry
8
8
  super
9
+ @entry.times_called += 1
9
10
  @arg_list.analyse_types local_scope
10
11
  @type = Rubex::DataType::RubyObject.new
11
12
  @arg_list.map! { |a| a.to_ruby_object }
@@ -37,7 +38,7 @@ module Rubex
37
38
  end
38
39
 
39
40
  def prepare_arg_list(local_scope)
40
- @arg_list_var = @entry.c_name + Rubex::ACTUAL_ARGS_SUFFIX
41
+ @arg_list_var = @entry.c_name + "_#{@entry.times_called}_" + Rubex::ACTUAL_ARGS_SUFFIX
41
42
  args_size = @entry.type.arg_list&.size || 0
42
43
  local_scope.add_carray(name: @arg_list_var, c_name: @arg_list_var,
43
44
  dimension: Literal::Int.new(args_size.to_s),
@@ -45,25 +46,26 @@ module Rubex
45
46
  end
46
47
 
47
48
  def code_for_ruby_method_call(code, local_scope)
48
- str = "#{@c_code} = "
49
+ str = ""
49
50
  if @entry.extern?
50
- str << "rb_funcall(#{@invoker.c_code(local_scope)}, "
51
+ str << "#{@c_code} = rb_funcall(#{@invoker.c_code(local_scope)}, "
51
52
  str << "rb_intern(\"#{@method_name}\"), "
52
53
  str << @arg_list.size.to_s
53
54
  @arg_list.each do |arg|
54
55
  str << " ,#{arg.c_code(local_scope)}"
55
56
  end
56
57
  str << ', NULL' if @arg_list.empty?
57
- str << ");\n"
58
+ str << ");"
58
59
  else
59
60
  str << populate_method_args_into_value_array(local_scope)
60
61
  str << actual_ruby_method_call(local_scope)
61
62
  end
63
+ str << "\n"
62
64
  code << str
63
65
  end
64
66
 
65
67
  def actual_ruby_method_call(local_scope)
66
- str = "#{@entry.c_name}(#{@arg_list.size}, #{@arg_list_var || 'NULL'},"
68
+ str = "#{@c_code} = #{@entry.c_name}(#{@arg_list.size}, #{@arg_list_var || 'NULL'},"
67
69
  str << "#{local_scope.self_name});"
68
70
  end
69
71
 
@@ -47,8 +47,9 @@ module Rubex
47
47
  add_as_ruby_method_to_scope local_scope
48
48
  end
49
49
  end
50
- analyse_as_ruby_method(local_scope) if @entry.type.ruby_method?
51
50
  assign_type_based_on_whether_wrapped_type
51
+ analyse_as_ruby_method(local_scope) if @entry.type.ruby_method?
52
+ analyse_as_c_function(local_scope) if @entry.type.c_function?
52
53
  if @name.is_a?(Expression::Base)
53
54
  @name.allocate_temps local_scope
54
55
  @name.release_temps local_scope
@@ -56,6 +57,14 @@ module Rubex
56
57
  super
57
58
  end
58
59
 
60
+ def analyse_as_c_function(local_scope)
61
+ @name = Rubex::AST::Expression::CFunctionCall.new(
62
+ Expression::Self.new, @name,
63
+ Expression::ActualArgList.new([])
64
+ )
65
+ @name.analyse_types(local_scope)
66
+ end
67
+
59
68
  def generate_evaluation_code(code, local_scope)
60
69
  if @name.respond_to? :generate_evaluation_code
61
70
  @name.generate_evaluation_code code, local_scope
@@ -7,10 +7,13 @@ module Rubex
7
7
  @pos.analyse_types local_scope
8
8
  @subexprs << @pos
9
9
  end
10
-
10
+
11
+ # FIXME: If there are multiple arguments specified for array_ref,
12
+ # we cannot use RARRAY_REF.
11
13
  def generate_evaluation_code(code, local_scope)
12
14
  generate_and_dispose_subexprs(code, local_scope) do
13
- code << "#{@c_code} = RARRAY_AREF(#{@entry.c_name}, #{@pos.c_code(local_scope)});"
15
+ code << "#{@c_code} = RARRAY_AREF(#{@entry.c_name},"
16
+ code << "#{@pos[0].c_code(local_scope)});"
14
17
  code.nl
15
18
  end
16
19
  end
@@ -2,16 +2,24 @@ module Rubex
2
2
  module AST
3
3
  module Expression
4
4
  class RubyHashElementRef < RubyObjectElementRef
5
+ def analyse_types(local_scope)
6
+ if @pos.size > 1
7
+ raise "Ruby Hash#[] expects 1 argument. Not #{@pos.size}."
8
+ end
9
+ super
10
+ end
11
+
5
12
  def generate_evaluation_code(code, local_scope)
6
13
  generate_and_dispose_subexprs(code, local_scope) do
7
- code << "#{@c_code} = rb_hash_aref(#{@entry.c_name}, #{@pos.c_code(local_scope)});"
14
+ code << "#{@c_code} = rb_hash_aref(#{@entry.c_name}, "
15
+ code << "#{@pos[0].c_code(local_scope)});"
8
16
  code.nl
9
17
  end
10
18
  end
11
19
 
12
20
  def generate_assignment_code(rhs, code, local_scope)
13
21
  generate_and_dispose_subexprs(code, local_scope) do
14
- code << "rb_hash_aset(#{@entry.c_name}, #{@pos.c_code(local_scope)},"
22
+ code << "rb_hash_aset(#{@entry.c_name}, #{@pos[0].c_code(local_scope)},"
15
23
  code << "#{rhs.c_code(local_scope)});"
16
24
  code.nl
17
25
  end
@@ -3,6 +3,7 @@ module Rubex
3
3
  module Expression
4
4
  # internal node for converting to ruby object.
5
5
  class ToRubyObject < CoerceObject
6
+ #attr_reader :expr
6
7
  attr_reader :type
7
8
 
8
9
  def initialize(expr)
@@ -1,5 +1,5 @@
1
1
  require_relative 'unary_base'
2
- Dir['./lib/rubex/ast/expression/unary_base/*.rb'].each { |f| require f }
2
+ Dir[File.join(File.dirname(File.dirname(__FILE__)), "expression", "unary_base", "*.rb" )].each { |f| require f }
3
3
  module Rubex
4
4
  module AST
5
5
  module Expression
@@ -13,16 +13,20 @@ module Rubex
13
13
 
14
14
  def initialize(operator, expr)
15
15
  @operator = operator
16
- @expr = expr
16
+ @expr = OP_CLASS_MAP[@operator].new(expr)
17
17
  end
18
18
 
19
19
  def analyse_types(local_scope)
20
- @expr = OP_CLASS_MAP[@operator].new(@expr)
21
20
  @expr.analyse_types local_scope
22
21
  @type = @expr.type
23
22
  super
24
23
  end
25
24
 
25
+ def analyse_for_target_type(target_type, local_scope)
26
+ @expr.analyse_for_target_type(target_type, local_scope)
27
+ @type = @expr.type
28
+ end
29
+
26
30
  def generate_evaluation_code(code, local_scope)
27
31
  @expr.generate_evaluation_code code, local_scope
28
32
  end
@@ -7,6 +7,11 @@ module Rubex
7
7
  @type = DataType::CPtr.new @expr.type
8
8
  end
9
9
 
10
+ def analyse_for_target_type(target_type, local_scope)
11
+ @expr.analyse_for_target_type(target_type, local_scope)
12
+ @type = DataType::CPtr.new @expr.type
13
+ end
14
+
10
15
  def c_code(local_scope)
11
16
  "&#{@expr.c_code(local_scope)}"
12
17
  end
@@ -1,231 +1,228 @@
1
1
  require 'rubex/helpers'
2
2
  module Rubex
3
3
  module AST
4
- class Node
5
- include Rubex::Helpers::Writers
6
- attr_reader :statements
7
-
8
- def initialize(statements)
9
- @statements = statements.flatten
10
- end
11
-
12
- def process_statements(target_name, code)
13
- @scope = Rubex::SymbolTable::Scope::Klass.new('Object', nil)
14
- add_top_statements_to_object_scope
15
- analyse_statement
16
- rescan_declarations @scope
17
- generate_preamble code
18
- generate_code code
19
- generate_init_method target_name, code
20
- end
4
+ module Node
5
+ class Base
6
+ include Rubex::Helpers::Writers
7
+ attr_reader :statements, :file_name
8
+
9
+ def initialize(statements, file_name)
10
+ @statements = statements.flatten
11
+ @file_name = file_name
12
+ @dependent_files = []
13
+ end
21
14
 
22
- def ==(other)
23
- self.class == other.class
24
- end
15
+ def ==(other)
16
+ self.class == other.class
17
+ end
25
18
 
26
- private
19
+ def analyse_statement
20
+ @statements.each do |stat|
21
+ @dependent_files << stat.file_name if stat.is_a?(Node::FileNode)
22
+ stat.analyse_statement @scope
23
+ end
24
+ @outside_statements.each do |stmt|
25
+ stmt.analyse_statement @scope
26
+ end
27
+ end
28
+
29
+ # Scan all the statements that do not belong to any particular class
30
+ # (meaning that they belong to Object) and add them to the Object class,
31
+ # which becomes the class from which all other classes will inherit from.
32
+ #
33
+ # Top-level statements that do not belong inside classes and which do not
34
+ # have any relevance inside a class at the level of a C extension are
35
+ # added to a different array and are analysed differently. For example
36
+ # 'require' statements that are top level statements but cannot be 'defined'
37
+ # inside the Init_ method the way a ruby class or method can be. These are
38
+ # stored in @outside_statements.
39
+ #
40
+ # If the user defines multiple Object classes, they will share the same @scope
41
+ # object and will therefore have accessible members between each other. Since
42
+ # Ruby also allows users to open classes whenever and wherever they want, this
43
+ # behaviour is conformant with actual Ruby behaviour. It also implies that a
44
+ # FileNode can basically create an Object class of its own and the scope will
45
+ # shared between FileNode and MainNode and other FileNodes as long as the FileNode
46
+ # shares the same @scope object.
47
+ def add_top_statements_to_object_scope
48
+ temp = []
49
+ combined_statements = []
50
+ @outside_statements = []
51
+ @statements.each do |stmt|
52
+ if stmt.is_a?(TopStatement::Klass) || stmt.is_a?(TopStatement::CBindings)
53
+ if !temp.empty?
54
+ object_klass = TopStatement::Klass.new('Object', @scope, temp)
55
+ combined_statements << object_klass
56
+ end
27
57
 
28
- # Scan all the statements that do not belong to any particular class
29
- # (meaning that they belong to Object) and add them to the Object class,
30
- # which becomes the class from which all other classes will inherit from.
31
- def add_top_statements_to_object_scope
32
- temp = []
33
- combined_statements = []
34
- @statements.each do |stmt|
35
- if stmt.is_a?(TopStatement::Klass) || stmt.is_a?(TopStatement::CBindings)
36
- unless temp.empty?
37
- object_klass = TopStatement::Klass.new('Object', @scope, temp)
38
- combined_statements << object_klass
58
+ combined_statements << stmt
59
+ temp = []
60
+ elsif outside_statement?(stmt)
61
+ @outside_statements << stmt
62
+ elsif stmt.is_a?(Node::FileNode)
63
+ combined_statements << stmt
64
+ else
65
+ temp << stmt
39
66
  end
67
+ end
40
68
 
41
- combined_statements << stmt
42
- temp = []
43
- else
44
- temp << stmt
69
+ unless temp.empty?
70
+ combined_statements << TopStatement::Klass.new('Object', @scope, temp)
45
71
  end
46
- end
47
72
 
48
- unless temp.empty?
49
- combined_statements << TopStatement::Klass.new('Object', @scope, temp)
73
+ @statements = combined_statements
50
74
  end
51
75
 
52
- @statements = combined_statements
53
- end
76
+ # TODO: accomodate all sorts of outside stmts like if blocks/while/for etc
77
+ def outside_statement?(stmt)
78
+ stmt.is_a?(Statement::Expression)
79
+ end
54
80
 
55
- def generate_preamble(code)
56
- code << "#include <ruby.h>\n"
57
- code << "#include <stdint.h>\n"
58
- code << "#include <stdbool.h>\n"
59
- @scope.include_files.each do |name|
60
- code <<
61
- if name[0] == '<' && name[-1] == '>'
62
- "#include #{name}\n"
63
- else
64
- "#include \"#{name}\"\n"
65
- end
81
+ def declare_unique_types header
82
+ types = []
83
+ @statements.grep(Rubex::AST::TopStatement::Klass).each do |klass|
84
+ types.concat klass.scope.type_entries
85
+ end
86
+ types.uniq!
87
+ declare_types header, types
66
88
  end
67
- write_usability_macros code
68
- @statements.grep(Rubex::AST::TopStatement::Klass).each do |klass|
69
- declare_types code, klass.scope
89
+
90
+ def generate_header_file(target_name, header)
91
+ header_def_name = target_name.gsub("/", "_").gsub(".", "_").upcase + "_H"
92
+ header.in_header_guard(header_def_name) do
93
+ header.write_include Rubex::COMMON_UTILS_FILE
94
+ @scope.include_files.each do |name|
95
+ header <<
96
+ if name[0] == '<' && name[-1] == '>'
97
+ "#include #{name}\n"
98
+ else
99
+ "#include \"#{name}\"\n"
100
+ end
101
+ end
102
+ declare_unique_types header
103
+ write_user_klasses header
104
+ write_global_variable_declarations header
105
+ write_function_declarations header
106
+ header.write_func_declaration type: 'void', c_name: init_function,
107
+ args: [], static: false
108
+ end
70
109
  end
71
- write_user_klasses code
72
- write_global_variable_declarations code
73
- write_function_declarations code
74
- write_usability_functions code
75
- code.nl
76
- end
77
110
 
78
- def write_global_variable_declarations(code)
79
- @statements.each do |stmt|
80
- next unless stmt.is_a?(TopStatement::Klass)
81
- stmt.statements.each do |s|
82
- next unless s.is_a?(TopStatement::MethodDef)
83
- s.scope.global_entries.each do |g|
84
- code << "static #{g.type} #{g.c_name};"
85
- code.nl
111
+ def write_global_variable_declarations(code)
112
+ @statements.each do |stmt|
113
+ next unless stmt.is_a?(TopStatement::Klass)
114
+ stmt.statements.each do |s|
115
+ next unless s.is_a?(TopStatement::MethodDef)
116
+ s.scope.global_entries.each do |g|
117
+ code << "static #{g.type} #{g.c_name};"
118
+ code.nl
119
+ end
86
120
  end
87
121
  end
88
122
  end
89
- end
90
123
 
91
- def write_user_klasses(code)
92
- code.nl
93
- @scope.ruby_class_entries.each do |klass|
94
- unless Rubex::DEFAULT_CLASS_MAPPINGS.has_key?(klass.name)
95
- code << "VALUE #{klass.c_name};"
96
- code.nl
124
+ def write_user_klasses(code)
125
+ code.nl
126
+ @scope.ruby_class_entries.each do |klass|
127
+ unless Rubex::DEFAULT_CLASS_MAPPINGS.has_key?(klass.name)
128
+ code << "VALUE #{klass.c_name};"
129
+ code.nl
130
+ end
97
131
  end
98
132
  end
99
- end
100
-
101
- def write_usability_macros(code)
102
- code.nl
103
- code.c_macro Rubex::RUBEX_PREFIX + 'INT2BOOL(arg) (arg ? Qtrue : Qfalse)'
104
- code.nl
105
- end
106
133
 
107
- def write_usability_functions(code)
108
- code.nl
109
- write_char_2_ruby_str code
110
- end
134
+ def write_function_declarations(code)
135
+ @statements.each do |stmt|
136
+ next unless stmt.is_a?(Rubex::AST::TopStatement::Klass)
137
+ stmt.scope.ruby_method_entries.each do |entry|
138
+ code.write_ruby_method_header(
139
+ type: entry.type.type.to_s, c_name: entry.c_name
140
+ )
141
+ code.colon
142
+ end
111
143
 
112
- def write_char_2_ruby_str(code)
113
- code << "VALUE #{Rubex::C_FUNC_CHAR2RUBYSTR}(char ch);"
114
- code.nl
115
- code << "VALUE #{Rubex::C_FUNC_CHAR2RUBYSTR}(char ch)"
116
- code.block do
117
- code << "char s[2];\n"
118
- code << "s[0] = ch;\n"
119
- code << "s[1] = '\\0';\n"
120
- code << "return rb_str_new2(s);\n"
144
+ stmt.scope.c_method_entries.each do |entry|
145
+ next if entry.extern?
146
+ code.write_c_method_header(
147
+ type: entry.type.type.to_s,
148
+ c_name: entry.c_name,
149
+ args: Helpers.create_arg_arrays(entry.type.arg_list)
150
+ )
151
+ code.colon
152
+ end
153
+ end
121
154
  end
122
- end
123
155
 
124
- def write_function_declarations(code)
125
- @statements.each do |stmt|
126
- next unless stmt.is_a?(Rubex::AST::TopStatement::Klass)
127
- stmt.scope.ruby_method_entries.each do |entry|
128
- code.write_ruby_method_header(
129
- type: entry.type.type.to_s, c_name: entry.c_name
130
- )
131
- code.colon
132
- end
156
+ def create_symtab_entries_for_top_statements(s)
157
+ @scope = s
158
+ @statements.each do |stat|
159
+ stat.create_symtab_entries_for_top_statements(@scope) if stat.is_a?(Node::FileNode)
160
+ next unless stat.is_a? Rubex::AST::TopStatement::Klass
161
+ name = stat.name
162
+ # The top level scope in Ruby is Object. The Object class's immediate
163
+ # ancestor is also Object. Hence, it is important to set the class
164
+ # scope and ancestor scope of Object as Object, and make sure that
165
+ # the same scope object is used for 'Object' class every single time
166
+ # throughout the compilation process.
167
+ if name != 'Object'
168
+ ancestor_entry = @scope.find(stat.ancestor)
169
+ if !ancestor_entry && Rubex::DEFAULT_CLASS_MAPPINGS[stat.ancestor]
170
+ ancestor_c_name = Rubex::DEFAULT_CLASS_MAPPINGS[stat.ancestor]
171
+ ancestor_scope = object_or_stdlib_klass_scope stat.ancestor
172
+ @scope.add_ruby_class(name: stat.ancestor, c_name: ancestor_c_name,
173
+ scope: @scope, ancestor: nil, extern: true)
174
+ else
175
+ ancestor_scope = ancestor_entry&.type&.scope || @scope
176
+ end
177
+ klass_scope = Rubex::SymbolTable::Scope::Klass.new(
178
+ name, ancestor_scope
179
+ )
180
+ else
181
+ ancestor_scope = @scope
182
+ klass_scope = @scope
183
+ end
184
+ c_name = c_name_for_class name
133
185
 
134
- stmt.scope.c_method_entries.each do |entry|
135
- next if entry.extern?
136
- code.write_c_method_header(
137
- type: entry.type.type.to_s,
138
- c_name: entry.c_name,
139
- args: Helpers.create_arg_arrays(entry.type.arg_list)
140
- )
141
- code.colon
186
+ @scope.add_ruby_class(name: name, c_name: c_name, scope: klass_scope,
187
+ ancestor: ancestor_scope, extern: false)
142
188
  end
143
189
  end
144
- end
145
190
 
146
- def analyse_statement
147
- create_symtab_entries_for_top_statements
148
- @statements.each do |stat|
149
- stat.analyse_statement @scope
191
+ def object_or_stdlib_klass_scope(name)
192
+ name != 'Object' ? Rubex::SymbolTable::Scope::Klass.new(name, nil) :
193
+ @scope
150
194
  end
151
- end
152
195
 
153
- def create_symtab_entries_for_top_statements
154
- @statements.each do |stat|
155
- next unless stat.is_a? Rubex::AST::TopStatement::Klass
156
- name = stat.name
157
- # The top level scope in Ruby is Object. The Object class's immediate
158
- # ancestor is also Object. Hence, it is important to set the class
159
- # scope and ancestor scope of Object as Object, and make sure that
160
- # the same scope object is used for 'Object' class every single time
161
- # throughout the compilation process.
162
- if name != 'Object'
163
- ancestor_entry = @scope.find(stat.ancestor)
164
- if !ancestor_entry && Rubex::DEFAULT_CLASS_MAPPINGS[stat.ancestor]
165
- ancestor_c_name = Rubex::DEFAULT_CLASS_MAPPINGS[stat.ancestor]
166
- ancestor_scope = object_or_stdlib_klass_scope stat.ancestor
167
- @scope.add_ruby_class(name: stat.ancestor, c_name: ancestor_c_name,
168
- scope: @scope, ancestor: nil, extern: true)
196
+ def c_name_for_class(name)
197
+ c_name =
198
+ if Rubex::DEFAULT_CLASS_MAPPINGS.key? name
199
+ Rubex::DEFAULT_CLASS_MAPPINGS[name]
169
200
  else
170
- ancestor_scope = ancestor_entry&.type&.scope || @scope
201
+ Rubex::RUBY_CLASS_PREFIX + name
171
202
  end
172
- klass_scope = Rubex::SymbolTable::Scope::Klass.new(
173
- name, ancestor_scope
174
- )
175
- else
176
- ancestor_scope = @scope
177
- klass_scope = @scope
178
- end
179
- c_name = c_name_for_class name
180
203
 
181
- @scope.add_ruby_class(name: name, c_name: c_name, scope: klass_scope,
182
- ancestor: ancestor_scope, extern: false)
204
+ c_name
183
205
  end
184
- end
185
-
186
- def object_or_stdlib_klass_scope(name)
187
- name != 'Object' ? Rubex::SymbolTable::Scope::Klass.new(name, nil) :
188
- @scope
189
- end
190
206
 
191
- def c_name_for_class(name)
192
- c_name =
193
- if Rubex::DEFAULT_CLASS_MAPPINGS.key? name
194
- Rubex::DEFAULT_CLASS_MAPPINGS[name]
195
- else
196
- Rubex::RUBY_CLASS_PREFIX + name
207
+ def rescan_declarations(_scope)
208
+ @statements.each do |stat|
209
+ stat.respond_to?(:rescan_declarations) &&
210
+ stat.rescan_declarations(@scope)
197
211
  end
198
-
199
- c_name
200
- end
201
-
202
- def rescan_declarations(_scope)
203
- @statements.each do |stat|
204
- stat.respond_to?(:rescan_declarations) &&
205
- stat.rescan_declarations(@scope)
206
212
  end
207
- end
208
213
 
209
- def generate_code(code)
210
- @statements.each do |stat|
211
- stat.generate_code code
212
- end
213
- end
214
-
215
- def generate_init_method(target_name, code)
216
- name = "Init_#{target_name}"
217
- code.new_line
218
- code.write_func_declaration type: 'void', c_name: name, args: [], static: false
219
- code.write_c_method_header type: 'void', c_name: name, args: [], static: false
220
- code.block do
221
- @statements.each do |top_stmt|
222
- if top_stmt.is_a?(TopStatement::Klass) && top_stmt.name != 'Object'
223
- entry = @scope.find top_stmt.name
224
- code.declare_variable type: 'VALUE', c_name: entry.c_name
214
+ # FIXME: Find a way to eradicate the if statement.
215
+ def generate_code(supervisor)
216
+ @statements.each do |stat|
217
+ if stat.is_a?(Rubex::AST::Node::FileNode)
218
+ stat.generate_code supervisor
219
+ else
220
+ stat.generate_code supervisor.code(@file_name)
225
221
  end
226
222
  end
227
- code.nl
223
+ end
228
224
 
225
+ def define_classes(code)
229
226
  @statements.each do |top_stmt|
230
227
  # define a class
231
228
  if top_stmt.is_a?(TopStatement::Klass) && top_stmt.name != 'Object'
@@ -247,7 +244,9 @@ module Rubex
247
244
  code << alloc
248
245
  end
249
246
  code.nl
247
+ end
250
248
 
249
+ def define_instance_and_singleton_methods_for_all_classes(code)
251
250
  @statements.each do |top_stmt|
252
251
  next unless top_stmt.is_a? TopStatement::Klass
253
252
  entry = @scope.find top_stmt.name
@@ -261,6 +260,35 @@ module Rubex
261
260
  method_name: meth.name, method_c_name: meth.c_name
262
261
  end
263
262
  end
263
+ end
264
+ end
265
+
266
+ def write_outside_statements(code)
267
+ @outside_statements.each do |stmt|
268
+ stmt.generate_code(code, @scope)
269
+ end
270
+ end
271
+
272
+ def init_function
273
+ "Init_#{@file_name}"
274
+ end
275
+
276
+ def call_dependent_init_methods(code)
277
+ @dependent_files.each do |dep|
278
+ code << "Init_#{dep}();"
279
+ code.nl
280
+ end
281
+ end
282
+
283
+ def generate_init_method(code)
284
+ name = init_function
285
+ code.new_line
286
+ code.write_c_method_header type: 'void', c_name: name, args: [], static: false
287
+ code.block do
288
+ call_dependent_init_methods(code)
289
+ write_outside_statements(code)
290
+ define_classes(code)
291
+ define_instance_and_singleton_methods_for_all_classes(code)
264
292
  end
265
293
  end
266
294
  end