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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubex'
4
+
5
+ file_name = ARGV[0]
6
+ if File.exists? file_name
7
+ file_name = Dir.pwd + "/#{file_name}"
8
+ end
9
+
10
+ Rubex.compile file_name
@@ -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
@@ -0,0 +1,6 @@
1
+ require 'rubex/ast/node'
2
+ require 'rubex/ast/ruby_method_def'
3
+ require 'rubex/ast/c_base_type'
4
+ require 'rubex/ast/argument_list'
5
+ require 'rubex/ast/statement'
6
+ require 'rubex/ast/expression'
@@ -0,0 +1,20 @@
1
+ module Rubex
2
+ module AST
3
+ class ArgumentList
4
+ include Enumerable
5
+ attr_reader :args
6
+
7
+ def each &block
8
+ @args.each(&block)
9
+ end
10
+
11
+ def initialize
12
+ @args = []
13
+ end
14
+
15
+ def push arg
16
+ @args << arg
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module Rubex
2
+ module AST
3
+ class CBaseType
4
+ attr_reader :type, :name
5
+
6
+ def initialize type, name
7
+ @type, @name = type, name
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Rubex
2
+ module AST
3
+ class Expression
4
+ class Addition
5
+ attr_reader :left, :right
6
+
7
+ def initialize left, right
8
+ @left, @right = left, right
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
@@ -0,0 +1,15 @@
1
+ module Rubex
2
+ module DataType
3
+ class RubyObject
4
+ def to_s; "VALUE"; end
5
+ end
6
+
7
+ class CInt32
8
+ def to_s; "int32_t"; end
9
+
10
+ def to_ruby_function; "INT2NUM"; end
11
+
12
+ def from_ruby_function; "NUM2INT"; end
13
+ end
14
+ end
15
+ end
@@ -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