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