rubex 0.1.1 → 0.1.2

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