rubex 0.0.1
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 +7 -0
- data/.gitignore +50 -0
- data/CONTRIBUTING.md +0 -0
- data/Gemfile +2 -0
- data/HISTORY.md +0 -0
- data/LICENSE +23 -0
- data/README.md +343 -0
- data/Rakefile +18 -0
- data/bin/rubex +10 -0
- data/lib/rubex.rb +56 -0
- data/lib/rubex/ast.rb +6 -0
- data/lib/rubex/ast/argument_list.rb +20 -0
- data/lib/rubex/ast/c_base_type.rb +11 -0
- data/lib/rubex/ast/expression.rb +13 -0
- data/lib/rubex/ast/node.rb +71 -0
- data/lib/rubex/ast/ruby_method_def.rb +84 -0
- data/lib/rubex/ast/statement.rb +54 -0
- data/lib/rubex/code_writer.rb +67 -0
- data/lib/rubex/constants.rb +17 -0
- data/lib/rubex/data_type.rb +15 -0
- data/lib/rubex/lexer.rex +50 -0
- data/lib/rubex/lexer.rex.rb +129 -0
- data/lib/rubex/parser.racc +67 -0
- data/lib/rubex/parser.racc.rb +251 -0
- data/lib/rubex/symbol_table.rb +2 -0
- data/lib/rubex/symbol_table/entry.rb +16 -0
- data/lib/rubex/symbol_table/scope.rb +57 -0
- data/lib/rubex/version.rb +3 -0
- data/rubex.gemspec +30 -0
- data/spec/basic_ruby_method_spec.rb +40 -0
- data/spec/fixtures/basic_ruby_method/Makefile +260 -0
- data/spec/fixtures/basic_ruby_method/basic.rb +3 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.c +16 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.o +0 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.rubex +3 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.so +0 -0
- data/spec/fixtures/basic_ruby_method/extconf.rb +3 -0
- data/spec/spec_helper.rb +7 -0
- metadata +163 -0
data/bin/rubex
ADDED
data/lib/rubex.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubex/code_writer'
|
2
|
+
require 'rubex/data_type'
|
3
|
+
require 'rubex/constants'
|
4
|
+
require 'rubex/ast'
|
5
|
+
require 'rubex/symbol_table'
|
6
|
+
require 'rubex/parser.racc.rb'
|
7
|
+
|
8
|
+
module Rubex
|
9
|
+
class << self
|
10
|
+
def compile path, test=false
|
11
|
+
tree = ast path
|
12
|
+
target_name = extract_target_name path
|
13
|
+
code = generate_code tree, target_name
|
14
|
+
ext = extconf target_name
|
15
|
+
|
16
|
+
return [code, ext] if test
|
17
|
+
write_files target_name, code, ext
|
18
|
+
end
|
19
|
+
|
20
|
+
def ast path
|
21
|
+
parser = Rubex::Parser.new
|
22
|
+
parser.parse(path)
|
23
|
+
parser.do_parse
|
24
|
+
end
|
25
|
+
|
26
|
+
def extconf target_name, dir=nil
|
27
|
+
extconf = ""
|
28
|
+
extconf << "require 'mkmf'\n"
|
29
|
+
extconf << "create_makefile('#{target_name}/#{target_name}')\n"
|
30
|
+
extconf
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_code tree, target_name
|
34
|
+
code = Rubex::CodeWriter.new target_name
|
35
|
+
raise "Must be a Rubex::AST::Node, not #{tree.class}" unless
|
36
|
+
tree.is_a? Rubex::AST::Node
|
37
|
+
tree.process_statements target_name, code
|
38
|
+
code
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_target_name path
|
42
|
+
File.basename(path).split('.')[0]
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_files target_name, code, ext
|
46
|
+
Dir.mkdir(target_name)
|
47
|
+
code_file = File.new "#{target_name}/#{target_name}.c", "w+"
|
48
|
+
code_file.puts code.to_s
|
49
|
+
code_file.close
|
50
|
+
|
51
|
+
extconf_file = File.new "#{target_name}/extconf.rb", "w+"
|
52
|
+
extconf_file.puts ext
|
53
|
+
extconf_file.close
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/rubex/ast.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Rubex
|
2
|
+
module AST
|
3
|
+
class Node
|
4
|
+
attr_reader :statements
|
5
|
+
|
6
|
+
def initialize statements
|
7
|
+
@statements = statements.is_a?(Array) ? statements : [statements]
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_child child
|
11
|
+
@statements.concat child
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_statements target_name, code
|
15
|
+
@scope = Rubex::SymbolTable::Scope::Klass.new 'Object'
|
16
|
+
generate_symbol_table_entries
|
17
|
+
analyse_expressions
|
18
|
+
generate_preamble code
|
19
|
+
generate_code code
|
20
|
+
generate_init_method target_name, code
|
21
|
+
end
|
22
|
+
|
23
|
+
# Pretty print the AST
|
24
|
+
def pp
|
25
|
+
# TODO
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def generate_preamble code
|
31
|
+
code << "#include <ruby.h>\n"
|
32
|
+
code << "#include <stdint.h>\n"
|
33
|
+
code << "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_symbol_table_entries
|
37
|
+
@statements.each do |stat|
|
38
|
+
stat.generate_symbol_table_entries @scope
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def analyse_expressions
|
43
|
+
@statements.each do |stat|
|
44
|
+
stat.analyse_expressions @scope
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_code code
|
49
|
+
@statements.each do |stat|
|
50
|
+
stat.generate_code code
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_init_method target_name, code
|
55
|
+
name = "Init_#{target_name}"
|
56
|
+
code.new_line
|
57
|
+
code.write_func_declaration "void", name, "void"
|
58
|
+
code.write_func_definition_header "void", name, "void"
|
59
|
+
|
60
|
+
@statements.each do |stat|
|
61
|
+
if stat.is_a? Rubex::AST::RubyMethodDef
|
62
|
+
code.define_instance_method_under @scope, stat.name, stat.c_name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
code << "\n"
|
67
|
+
code << "}\n"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Rubex
|
2
|
+
module AST
|
3
|
+
class RubyMethodDef
|
4
|
+
# Ruby name of the method.
|
5
|
+
attr_reader :name
|
6
|
+
# The equivalent C name of the method.
|
7
|
+
attr_reader :c_name
|
8
|
+
# Method arguments.
|
9
|
+
attr_reader :args
|
10
|
+
# The statments/expressions contained within the method.
|
11
|
+
attr_reader :statements
|
12
|
+
# Symbol Table entry.
|
13
|
+
attr_reader :entry
|
14
|
+
# Return type of the function.
|
15
|
+
attr_reader :return_type
|
16
|
+
|
17
|
+
def initialize name, args
|
18
|
+
@name, @args = name, args
|
19
|
+
@c_name = Rubex::FUNC_PREFIX + name
|
20
|
+
@statements = []
|
21
|
+
@return_type = Rubex::DataType::RubyObject.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_statements statements
|
25
|
+
statements.each { |s| @statements << s }
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate_symbol_table_entries outer_scope
|
29
|
+
@scope = Rubex::SymbolTable::Scope::Local.new
|
30
|
+
@scope.outer_scope = outer_scope
|
31
|
+
@scope.return_type = @return_type.dup
|
32
|
+
@scope.declare_args @args
|
33
|
+
end
|
34
|
+
|
35
|
+
def analyse_expressions outer_scope
|
36
|
+
@statements.each do |stat|
|
37
|
+
stat.analyse_expression @scope
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_code code
|
42
|
+
code.write_func_declaration @return_type.to_s, @c_name
|
43
|
+
code.write_func_definition_header @return_type.to_s, @c_name
|
44
|
+
generate_function_definition code
|
45
|
+
code << "}"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def generate_function_definition code
|
51
|
+
declare_args code
|
52
|
+
generate_arg_checking code
|
53
|
+
init_args code
|
54
|
+
generate_statements code
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_statements code
|
58
|
+
@statements.each do |stat|
|
59
|
+
stat.generate_code code, @scope
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def declare_args code
|
64
|
+
@scope.arg_entries.each do |arg|
|
65
|
+
code.declare_variable arg
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def init_args code
|
70
|
+
@scope.arg_entries.each_with_index do |arg, i|
|
71
|
+
code << arg.c_name + '=' + arg.type.from_ruby_function + '(' +
|
72
|
+
'argv[' + i.to_s + ']);' + "\n"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_arg_checking code
|
77
|
+
code << 'if (argc != ' + @scope.arg_entries.size.to_s + ")\n"
|
78
|
+
code << "{\n"
|
79
|
+
code << %Q{rb_raise(rb_eArgError, "Need #{@scope.arg_entries.size} args, not %d", argc);\n}
|
80
|
+
code << "}\n"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rubex
|
2
|
+
module AST
|
3
|
+
class Statement
|
4
|
+
class Return
|
5
|
+
attr_reader :expression, :return_type
|
6
|
+
|
7
|
+
def initialize expression
|
8
|
+
@expression = expression
|
9
|
+
end
|
10
|
+
|
11
|
+
def analyse_expression local_scope
|
12
|
+
case @expression
|
13
|
+
when Rubex::AST::Expression::Addition
|
14
|
+
left = @expression.left
|
15
|
+
right = @expression.right
|
16
|
+
end
|
17
|
+
|
18
|
+
left_type = local_scope[left].type
|
19
|
+
right_type = local_scope[right].type
|
20
|
+
|
21
|
+
@return_type = result_type_for left_type, right_type
|
22
|
+
# TODO: Raise error if return_type is not compatible with the return
|
23
|
+
# type of the function.
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_code code, local_scope
|
27
|
+
code << "return "
|
28
|
+
case @expression
|
29
|
+
when Rubex::AST::Expression::Addition
|
30
|
+
left = @expression.left
|
31
|
+
right = @expression.right
|
32
|
+
code << @return_type.to_ruby_function
|
33
|
+
code << "("
|
34
|
+
code << local_scope[left].c_name
|
35
|
+
code << " + "
|
36
|
+
code << local_scope[right].c_name
|
37
|
+
code << ")"
|
38
|
+
code << ";\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def result_type_for left_type, right_type
|
45
|
+
dtype = Rubex::DataType
|
46
|
+
|
47
|
+
if left_type.is_a?(dtype::CInt32) && right_type.is_a?(dtype::CInt32)
|
48
|
+
return dtype::CInt32.new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Rubex
|
2
|
+
class CodeWriter
|
3
|
+
attr_reader :code
|
4
|
+
|
5
|
+
def initialize target_name
|
6
|
+
@code = "/* C extension for #{target_name}.\n"\
|
7
|
+
"This file in generated by Rubex. Do not change!\n"\
|
8
|
+
"*/\n"
|
9
|
+
@indent = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def write_func_declaration return_type, c_name, args=""
|
13
|
+
write_func_prototype return_type, c_name, args
|
14
|
+
@code << ";"
|
15
|
+
new_line
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_func_definition_header return_type, c_name, args=""
|
19
|
+
write_func_prototype return_type, c_name, args
|
20
|
+
@code << "\n"
|
21
|
+
@code << "{\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def declare_variable var
|
25
|
+
@code << "#{var.type.to_s} #{var.c_name};\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def << s
|
29
|
+
@code << s
|
30
|
+
end
|
31
|
+
|
32
|
+
def new_line
|
33
|
+
@code << "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
def indent
|
37
|
+
@indent += 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def dedent
|
41
|
+
raise "Cannot dedent, already 0." if @indent == 0
|
42
|
+
@indent -= 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def define_instance_method_under scope, name, c_name
|
46
|
+
@code << "rb_define_method(" + scope.c_name + " ,\"" + name + "\", " +
|
47
|
+
c_name + ", -1);\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
@code
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def write_func_prototype return_type, c_name, args
|
57
|
+
@code << "#{return_type} #{c_name} "
|
58
|
+
@code << "("
|
59
|
+
if args.empty?
|
60
|
+
@code << "int argc, VALUE* argv, VALUE #{Rubex::ARG_PREFIX}self"
|
61
|
+
else
|
62
|
+
@code << args
|
63
|
+
end
|
64
|
+
@code << ")"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rubex
|
2
|
+
RUBEX_PREFIX = "__rubex_"
|
3
|
+
|
4
|
+
FUNC_PREFIX = RUBEX_PREFIX + "f_"
|
5
|
+
VAR_PREFIX = RUBEX_PREFIX + "v_"
|
6
|
+
CLASS_PREFIX = RUBEX_PREFIX + "c_"
|
7
|
+
ARG_PREFIX = RUBEX_PREFIX + "arg_"
|
8
|
+
|
9
|
+
TYPE_MAPPINGS = {
|
10
|
+
'i32' => Rubex::DataType::CInt32,
|
11
|
+
'object' => Rubex::DataType::RubyObject
|
12
|
+
}
|
13
|
+
|
14
|
+
CLASS_MAPPINGS = {
|
15
|
+
'Object' => 'rb_cObject'
|
16
|
+
}
|
17
|
+
end
|
data/lib/rubex/lexer.rex
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
class Rubex::Lexer
|
2
|
+
macros
|
3
|
+
DEF /def/
|
4
|
+
RETURN /return/
|
5
|
+
|
6
|
+
IDENTIFIER /[a-z_][a-zA-Z_0-9]*/
|
7
|
+
LPAREN /\(/
|
8
|
+
RPAREN /\)/
|
9
|
+
NL /\n/
|
10
|
+
COMMA /,/
|
11
|
+
|
12
|
+
# operators
|
13
|
+
EXPO /\*\*/
|
14
|
+
MULTIPLY /\*/
|
15
|
+
DIVIDE /\//
|
16
|
+
PLUS /\+/
|
17
|
+
MINUS /\-/
|
18
|
+
MODULUS /%/
|
19
|
+
EQUAL /=/
|
20
|
+
rules
|
21
|
+
/#{DEF}/ { [:kDEF, text] }
|
22
|
+
/end/ { [:kEND, text] }
|
23
|
+
/#{RETURN}/ { [:kRETURN, text] }
|
24
|
+
|
25
|
+
/i32/ { [:kDTYPE_I32, text] }
|
26
|
+
|
27
|
+
/#{IDENTIFIER}/ { [:tIDENTIFIER, text] }
|
28
|
+
/#{LPAREN}/ { [:tLPAREN, text] }
|
29
|
+
/#{RPAREN}/ { [:tRPAREN, text] }
|
30
|
+
/#{NL}/ { [:tNL, text] }
|
31
|
+
/#{COMMA}/ { [:tCOMMA, text] }
|
32
|
+
|
33
|
+
# operators
|
34
|
+
|
35
|
+
/#{PLUS}/ { [:tPLUS, text]}
|
36
|
+
/#{MINUS}/ { [:tMINUS, text]}
|
37
|
+
/#{MULTIPLY}/ { [:tMULTIPLY, text]}
|
38
|
+
/#{DIVIDE}/ { [:tDIVIDE, text]}
|
39
|
+
/#{EXPO}/ { [:tEXPO, text]}
|
40
|
+
/#{MODULUS}/ { [:tMODULUS, text]}
|
41
|
+
/#{EXPO}/ { [:tEXPO, text]}
|
42
|
+
|
43
|
+
# whitespace
|
44
|
+
|
45
|
+
/ / {}
|
46
|
+
inner
|
47
|
+
def do_parse
|
48
|
+
# this is a stub since oedipus lex uses this internally.
|
49
|
+
end
|
50
|
+
end # Rubex::Lexer
|