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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/CONTRIBUTING.md +73 -9
- data/HISTORY.md +19 -0
- data/README.md +53 -8
- data/REFERENCE.md +112 -44
- data/benchmarks/no_gil/no_gil.rb +24 -0
- data/benchmarks/no_gil/no_gil.rubex +22 -0
- data/bin/rubex +1 -1
- data/examples/c_struct_interface/c_struct_interface.rubex +1 -0
- data/lib/rubex.rb +1 -0
- data/lib/rubex/ast.rb +11 -7
- data/lib/rubex/ast/expression.rb +1 -1
- data/lib/rubex/ast/expression/actual_arg_list.rb +7 -0
- data/lib/rubex/ast/expression/analysed_element_ref/c_var_element_ref.rb +5 -2
- data/lib/rubex/ast/expression/analysed_element_ref/ruby_object_element_ref.rb +26 -9
- data/lib/rubex/ast/expression/binary/binary_boolean.rb +1 -1
- data/lib/rubex/ast/expression/binary/binary_expo.rb +34 -0
- data/lib/rubex/ast/expression/binary/colon2.rb +34 -0
- data/lib/rubex/ast/expression/binary/empty_classes.rb +0 -3
- data/lib/rubex/ast/expression/command_call.rb +24 -0
- data/lib/rubex/ast/{statement → expression/command_call}/print.rb +4 -5
- data/lib/rubex/ast/{statement → expression/command_call}/raise.rb +29 -24
- data/lib/rubex/ast/expression/command_call/require.rb +27 -0
- data/lib/rubex/ast/expression/command_call/yield.rb +36 -0
- data/lib/rubex/ast/expression/element_ref.rb +10 -5
- data/lib/rubex/ast/expression/instance_var.rb +33 -0
- data/lib/rubex/ast/expression/method_call.rb +4 -2
- data/lib/rubex/ast/expression/method_call/c_function_call.rb +4 -3
- data/lib/rubex/ast/expression/method_call/ruby_method_call.rb +7 -5
- data/lib/rubex/ast/expression/name.rb +10 -1
- data/lib/rubex/ast/expression/ruby_object_element_ref/ruby_array_element_ref.rb +5 -2
- data/lib/rubex/ast/expression/ruby_object_element_ref/ruby_hash_element_ref.rb +10 -2
- data/lib/rubex/ast/expression/to_ruby_object.rb +1 -0
- data/lib/rubex/ast/expression/unary.rb +7 -3
- data/lib/rubex/ast/expression/unary_base/ampersand.rb +5 -0
- data/lib/rubex/ast/node.rb +213 -185
- data/lib/rubex/ast/node/file_node.rb +25 -0
- data/lib/rubex/ast/node/main_node.rb +56 -0
- data/lib/rubex/ast/statement/begin_block/begin.rb +4 -3
- data/lib/rubex/ast/statement/c_array_decl.rb +1 -1
- data/lib/rubex/ast/statement/c_ptr_decl.rb +2 -0
- data/lib/rubex/ast/statement/no_gil_block.rb +70 -0
- data/lib/rubex/ast/statement/return.rb +1 -0
- data/lib/rubex/ast/top_statement.rb +1 -1
- data/lib/rubex/ast/top_statement/klass.rb +4 -0
- data/lib/rubex/ast/top_statement/klass/attached_klass.rb +88 -10
- data/lib/rubex/ast/top_statement/method_def.rb +2 -3
- data/lib/rubex/ast/top_statement/method_def/c_function_def.rb +10 -4
- data/lib/rubex/cli.rb +11 -6
- data/lib/rubex/code_supervisor.rb +49 -0
- data/lib/rubex/code_writer.rb +22 -1
- data/lib/rubex/compiler.rb +109 -30
- data/lib/rubex/compiler_config.rb +14 -1
- data/lib/rubex/constants.rb +3 -0
- data/lib/rubex/data_type.rb +2 -3
- data/lib/rubex/data_type/ruby_object/ruby_symbol.rb +0 -1
- data/lib/rubex/error.rb +4 -0
- data/lib/rubex/helpers/writers.rb +33 -4
- data/lib/rubex/lexer.rex +9 -1
- data/lib/rubex/lexer.rex.rb +15 -2
- data/lib/rubex/parser.racc +125 -49
- data/lib/rubex/parser.racc.rb +1526 -1376
- data/lib/rubex/rake_task.rb +42 -6
- data/lib/rubex/symbol_table/entry.rb +6 -0
- data/lib/rubex/symbol_table/scope.rb +28 -3
- data/lib/rubex/version.rb +1 -1
- data/rubex.gemspec +1 -0
- data/spec/basic_ruby_method_spec.rb +2 -2
- data/spec/blocks_spec.rb +2 -2
- data/spec/box_op_multi_args_spec.rb +34 -0
- data/spec/c_function_ptrs_spec.rb +2 -2
- data/spec/c_functions_spec.rb +2 -0
- data/spec/c_struct_interface_spec.rb +8 -3
- data/spec/default_args_spec.rb +2 -2
- data/spec/external_c_struct_spec.rb +33 -0
- data/spec/fixtures/api/consumer.rubex +0 -0
- data/spec/fixtures/api/implementation.rubex +0 -0
- data/spec/fixtures/api/implementation.rubexd +0 -0
- data/spec/fixtures/box_op_multi_args/box_op_multi_args.rubex +3 -0
- data/spec/fixtures/c_functions/c_functions.rubex +13 -0
- data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +28 -0
- data/spec/fixtures/class_methods/class_methods.rubex +1 -1
- data/spec/fixtures/error_handling/error_handling.rubex +2 -2
- data/spec/fixtures/external_c_struct/external_c_struct.rubex +16 -0
- data/spec/fixtures/if_else/if_else.rubex +1 -1
- data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +1 -1
- data/spec/fixtures/instance_variables/instance_variables.rubex +25 -0
- data/spec/fixtures/loops/loops.rubex +2 -2
- data/spec/fixtures/module/module.rubex +28 -0
- data/spec/fixtures/multi_file_programs/Rakefile +8 -0
- data/spec/fixtures/multi_file_programs/a.rubex +5 -0
- data/spec/fixtures/multi_file_programs/b.rubex +5 -0
- data/spec/fixtures/multi_file_programs/multi_file_programs.rubex +14 -0
- data/spec/fixtures/no_gil/no_gil.rubex +24 -0
- data/spec/fixtures/no_gil_attach_class/no_gil_attach_class.rubex +23 -0
- data/spec/fixtures/no_gil_compile_check/no_gil_compile_check.rubex +4 -0
- data/spec/fixtures/outside_stmts/outside_stmts.rubex +6 -0
- data/spec/fixtures/pow/pow.rubex +4 -0
- data/spec/fixtures/rake_task/single_file/test.rubex +3 -0
- data/spec/fixtures/recursion/recursion.rubex +1 -1
- data/spec/fixtures/ruby_constant_scoping/ruby_constant_scoping.rubex +7 -0
- data/spec/fixtures/ruby_operators/ruby_operators.rubex +1 -1
- data/spec/fixtures/ruby_raise/ruby_raise.rubex +2 -2
- data/spec/fixtures/ruby_types/ruby_types.rubex +4 -4
- data/spec/fixtures/statement_expression/statement_expression.rubex +2 -2
- data/spec/fixtures/static_array/static_array.rubex +3 -3
- data/spec/fixtures/string_literals/string_literals.rubex +12 -2
- data/spec/fixtures/struct/struct.rubex +1 -1
- data/spec/fixtures/var_declarations/var_declarations.rubex +1 -1
- data/spec/implicit_lib_include_spec.rb +2 -2
- data/spec/init_ruby_objects_with_literal_syntax_spec.rb +2 -2
- data/spec/instance_variables_spec.rb +33 -0
- data/spec/loops_spec.rb +2 -2
- data/spec/module_spec.rb +39 -0
- data/spec/multi_file_programs_spec.rb +41 -0
- data/spec/no_gil_attach_class_spec.rb +33 -0
- data/spec/no_gil_compile_check_spec.rb +25 -0
- data/spec/no_gil_spec.rb +36 -0
- data/spec/outside_stmts_spec.rb +34 -0
- data/spec/pow_spec.rb +33 -0
- data/spec/rake_task_spec.rb +142 -0
- data/spec/recursion_spec.rb +4 -4
- data/spec/ruby_constant_scoping_spec.rb +42 -0
- data/spec/ruby_raise_spec.rb +2 -2
- data/spec/ruby_symbols_spec.rb +2 -2
- data/spec/ruby_types_spec.rb +2 -2
- data/spec/spec_helper.rb +17 -3
- data/spec/string_literals_spec.rb +1 -0
- metadata +90 -6
- 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,
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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.
|
26
|
-
|
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
|
9
|
-
@
|
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
|
-
|
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 = "
|
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 << ")
|
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},
|
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},
|
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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'unary_base'
|
2
|
-
Dir[
|
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
|
data/lib/rubex/ast/node.rb
CHANGED
@@ -1,231 +1,228 @@
|
|
1
1
|
require 'rubex/helpers'
|
2
2
|
module Rubex
|
3
3
|
module AST
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
15
|
+
def ==(other)
|
16
|
+
self.class == other.class
|
17
|
+
end
|
25
18
|
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
49
|
-
combined_statements << TopStatement::Klass.new('Object', @scope, temp)
|
73
|
+
@statements = combined_statements
|
50
74
|
end
|
51
75
|
|
52
|
-
|
53
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
135
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
-
|
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
|