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