oryx 0.2.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,185 @@
1
+ require_relative "../oryx"
2
+
3
+ require "rltk/cg/contractor"
4
+ require "rltk/cg/llvm"
5
+ require "rltk/cg/module"
6
+
7
+ RLTK::CG::LLVM.init(:x86)
8
+
9
+ module Oryx
10
+
11
+ ZERO = RLTK::CG::NativeInt.new(0)
12
+
13
+ class Contractor < RLTK::CG::Contractor
14
+ attr_reader :module, :st
15
+
16
+ def initialize
17
+ super
18
+ @module = RLTK::CG::Module.new('Oryx JIT')
19
+ @st = SymbolTable.new
20
+ end
21
+
22
+ def begin ast
23
+ ast.each {|a| dispatch a}
24
+ end
25
+
26
+ on Function do |node|
27
+ fun = nil
28
+ if fun = @module.functions[node.i]
29
+ raise GenerationError, "Redefinition of function #{node.i}."
30
+ else
31
+ st.enter_scope
32
+ param_types = parameter_types node.params
33
+ return_type = visit node.return_type
34
+ fun = @module.functions.add(node.i, return_type, param_types)
35
+ node.params.params.each {|p| visit p}
36
+ param_names = parameter_names node.params
37
+ param_names.each_with_index do |name, i|
38
+ st.update name.to_sym, fun.params[i]
39
+ end
40
+ end
41
+
42
+ ret (visit node.body, at: fun.blocks.append('entry'))
43
+ st.exit_scope
44
+
45
+ returning(fun) { fun.verify }
46
+ end
47
+
48
+ on ParamList do |node|
49
+ end
50
+
51
+ on Int do |node|
52
+ RLTK::CG::NativeIntType
53
+ end
54
+
55
+ on Variable do |node|
56
+ begin
57
+ st.lookup node.name.to_sym
58
+ rescue SymbolTableError => e
59
+ STDERR.puts e.message, "Attempting to continue without this value."
60
+ end
61
+ end
62
+
63
+ on CodeBlock do |node|
64
+ a = node.statements.map {|s| visit s}
65
+ a.first
66
+ end
67
+
68
+ on Return do |node|
69
+ result = visit node.right
70
+ result
71
+ end
72
+
73
+ on Binary do |node|
74
+ left = visit node.left
75
+ right = visit node.right
76
+
77
+ case node
78
+ when Add then add(left, right, 'addtmp')
79
+ when Sub then sub(left, right, 'subtmp')
80
+ when Mul then mul(left, right, 'multmp')
81
+ when Div then sdiv(left, right, 'divtmp')
82
+ when GE then integer_cast(icmp(:sgt, left, right), RLTK::CG::NativeIntType, 'booltmp')
83
+ when GEQ then integer_cast(icmp(:sge, left, right), RLTK::CG::NativeIntType, 'booltmp')
84
+ when LE then integer_cast(icmp(:slt, left, right), RLTK::CG::NativeIntType, 'booltmp')
85
+ when LEQ then integer_cast(icmp(:sle, left, right), RLTK::CG::NativeIntType, 'booltmp')
86
+ when EQ then integer_cast(icmp(:eq, left, right), RLTK::CG::NativeIntType, 'booltmp')
87
+ when NEQ then integer_cast(icmp(:ne, left, right), RLTK::CG::NativeIntType, 'booltmp')
88
+ end
89
+ end
90
+
91
+ on Number do |node|
92
+ RLTK::CG::NativeInt.new(node.value)
93
+ end
94
+
95
+ on GInitialization do |node|
96
+ name = node.name.to_sym
97
+ value = visit node.right
98
+ begin
99
+ st.insert(name, value)
100
+ rescue SymbolTableError => e
101
+ STDERR.puts e.message, "Continuing processing without modifying the symbol table"
102
+ end
103
+ end
104
+
105
+ on GDeclaration do |node|
106
+ name = node.name.to_sym
107
+ begin
108
+ st.insert name
109
+ rescue SymbolTableError => e
110
+ STDERR.puts e.message, "Continuing processing without modifying the symbol table"
111
+ end
112
+ end
113
+
114
+ on Declaration do |node|
115
+ name = node.name.to_sym
116
+ begin
117
+ st.insert name
118
+ rescue SymbolTableError => e
119
+ STDERR.puts e.message, "Continuing processing without modifying the symbol table"
120
+ end
121
+ end
122
+
123
+ on Assign do |node|
124
+ value = visit node.right
125
+ name = node.name.to_sym
126
+ begin
127
+ st.update name, value
128
+ rescue SymbolTableError => e
129
+ STDERR.puts e.message, "Continuing processing witout modifying the symbol table"
130
+ end
131
+ end
132
+
133
+ on Call do |node|
134
+ callee = @module.functions[node.name]
135
+ raise GenerationError, "Unknown function referenced" unless callee
136
+
137
+ raise GenerationError, "Function #{node.name} expected #{callee.params.size} argument(s) but was called with #{node.args.args.length}." unless callee.params.size == node.args.args.length
138
+ args = node.args.args.map { |a| visit a }
139
+ call callee, *args.push('calltmp')
140
+ end
141
+
142
+ on If do |node|
143
+ cond_val = icmp :ne, (visit node.cond), ZERO, 'ifcond'
144
+ start_bb = current_block
145
+ fun = start_bb.parent
146
+ then_bb = fun.blocks.append('then')
147
+ then_val, new_then_bb = visit node.then, at: then_bb, rcb: true
148
+
149
+ else_bb = fun.blocks.append('else')
150
+ else_val, new_else_bb = visit node.else, at: else_bb, rcb: true
151
+
152
+ merge_bb = fun.blocks.append('merge', self)
153
+ phi_inst = build(merge_bb) { phi RLTK::CG::NativeIntType, {new_then_bb => then_val, new_else_bb => else_val}, 'iftmp' }
154
+
155
+ build(start_bb) { cond cond_val, then_bb, else_bb }
156
+ build(new_then_bb) { br merge_bb }
157
+ build(new_else_bb) { br merge_bb }
158
+ returning(phi_inst) { target merge_bb }
159
+ end
160
+
161
+
162
+ private
163
+ def dispatch node
164
+ case node
165
+ when Function then visit node
166
+ when GInitialization then visit node
167
+ when GDeclaration then visit node
168
+ when Call then visit node
169
+ else raise GenerationError "Unhandled node type #{node}"
170
+ end
171
+ end
172
+
173
+ def parameter_types node
174
+ node.params.map do |p|
175
+ t = visit p.type
176
+ end
177
+ end
178
+
179
+ def parameter_names node
180
+ node.params.map do |p|
181
+ t = p.name
182
+ end
183
+ end
184
+ end
185
+ end
data/lib/oryx/error.rb ADDED
@@ -0,0 +1,8 @@
1
+ module Oryx
2
+ class Error < StandardError; end
3
+
4
+ class LexError < Error; end
5
+ class ParseError < Error; end
6
+ class GenerationError < Error; end
7
+ class SymbolTableError < Error; end
8
+ end
data/lib/oryx/parser.rb CHANGED
@@ -27,15 +27,15 @@ module Oryx
27
27
  end
28
28
 
29
29
  production(:vdecl) do
30
- clause('type_spec IDENT SEMI') { |t, i, _| Variable.new i}
30
+ clause('type_spec IDENT SEMI') { |t, i, _| GDeclaration.new i, t}
31
31
  end
32
32
 
33
33
  production(:vinit) do
34
- clause('type_spec IDENT ASSIGN constant SEMI') { |t, i, _, c, _| Assign.new i, c}
34
+ clause('type_spec IDENT ASSIGN constant SEMI') { |t, i, _, c, _| GInitialization.new i, c, t }
35
35
  end
36
36
 
37
37
  production(:fdecl) do
38
- clause('type_spec IDENT LPAREN opt_param_list RPAREN code_block') { |t, i, _, opl, _, c| Function.new i, opl, c }
38
+ clause('type_spec IDENT LPAREN opt_param_list RPAREN code_block') { |t, i, _, opl, _, c| Function.new i, opl, c, t }
39
39
  end
40
40
 
41
41
  production(:opt_param_list) do
@@ -49,7 +49,21 @@ module Oryx
49
49
  end
50
50
 
51
51
  production(:param) do
52
- clause('type_spec IDENT') { |t, i| Variable.new i }
52
+ clause('type_spec IDENT') { |t, i| Declaration.new i, t }
53
+ end
54
+
55
+ production(:opt_arg_list) do
56
+ clause('') { || ArgList.new [] }
57
+ clause('arg_list') { |al| ArgList.new al}
58
+ end
59
+
60
+ production(:arg_list) do
61
+ clause('arg') { |a| [a] }
62
+ clause('arg COMMA arg_list') { |a, _, al| Array(a) + al }
63
+ end
64
+
65
+ production(:arg) do
66
+ clause('e') { |e| e }
53
67
  end
54
68
 
55
69
  production(:constant) do
@@ -80,12 +94,21 @@ module Oryx
80
94
  clause('e SEMI') { |e, _| e }
81
95
  clause('if_statement') { |i| i}
82
96
  clause('WHILE LPAREN e RPAREN code_block') {|_,_,e,_,c| While.new(e,c) }
97
+ clause('type_spec IDENT ASSIGN e SEMI') { |t, e0, _, e1, _| Initialization.new t, e0, e1 }
98
+ clause('type_spec IDENT SEMI') { |t, i, _ | Declaration.new i,t }
83
99
  end
84
100
 
85
101
  production(:if_statement) do
86
- clause('IF LPAREN e RPAREN statement') { |_, _, e, _, s| If.new(e, s, nil) }
87
- clause('IF LPAREN e RPAREN statement ELSE statement') { |_,_,e,_,ts,_,fs| If.new(e, ts, fs) }
88
- clause('IF LPAREN e RPAREN code_block ELSE code_block') {|_,_,e,_,tc,_,fc| If.new(e, tc, fc) }
102
+ clause('if_preamble statement') { |e, s| If.new(e, s, nil) }
103
+ clause('if_preamble code_block') { |e, c| If.new(e, c, nil) }
104
+ clause('if_preamble statement ELSE statement') { |e,ts,_,fs| If.new(e, ts, fs) }
105
+ clause('if_preamble code_block ELSE code_block') { |e,tc,_,fc| If.new(e, tc, fc) }
106
+ clause('if_preamble statement ELSE code_block') { |e,ts,_,fc| If.new(e, ts, fc) }
107
+ clause('if_preamble code_block ELSE statement') { |e,tc,_,fs| If.new(e, tc, fs) }
108
+ end
109
+
110
+ production(:if_preamble) do
111
+ clause('IF LPAREN e RPAREN') { |_, _, e, _| e }
89
112
  end
90
113
 
91
114
  production(:e) do
@@ -109,6 +132,7 @@ module Oryx
109
132
 
110
133
  clause('RETURN e') { |_, e| Return.new e }
111
134
 
135
+ clause('IDENT LPAREN opt_arg_list RPAREN') { |i, _, oal, _| Call.new(i, oal) }
112
136
  end
113
137
 
114
138
  finalize explain: 'explain.out'
data/lib/oryx/runner.rb CHANGED
@@ -20,7 +20,16 @@ module Oryx
20
20
  puts "complete".green
21
21
 
22
22
  p = Parser.new
23
- p.parse(l.lex_file(input_filename.to_s), parse_tree: 'tree.dot', verbose: 'parse.out')
23
+ ast = p.parse(l.lex_file(input_filename.to_s), parse_tree: 'tree.dot', verbose: 'parse.out')
24
+
25
+ c = Contractor.new
26
+ c.begin ast
27
+
28
+ output_ir c.module
29
+
30
+ translate_to_assembly
31
+ create_executable
32
+
24
33
  end
25
34
 
26
35
  private
@@ -38,6 +47,26 @@ module Oryx
38
47
  s
39
48
  end
40
49
 
50
+ def base_name
51
+ input_filename.to_s.split('.').first
52
+ end
53
+
54
+ def output_ir ir_module
55
+ ir_module.verify
56
+ orig_stderr = $stderr.clone
57
+ $stderr.reopen File.new("#{base_name}.ll", "w")
58
+ ir_module.dump
59
+ $stderr.reopen orig_stderr
60
+ end
61
+
62
+ def translate_to_assembly
63
+ `llc -disable-cfi #{base_name}.ll`
64
+ end
65
+
66
+ def create_executable
67
+ `gcc #{base_name}.s -o #{base_name}.out`
68
+ end
69
+
41
70
  def table_header
42
71
  s = center "TYPE", 10
43
72
  s += "|"
@@ -0,0 +1,48 @@
1
+ require_relative '../oryx'
2
+
3
+ module Oryx
4
+ class SymbolTable
5
+ def initialize
6
+ @values = [{}]
7
+ end
8
+
9
+ def enter_scope
10
+ values.push Hash.new
11
+ end
12
+
13
+ def exit_scope
14
+ raise SymbolTableError, "Cannot exit global scope." if current_scope == 0
15
+ values.pop
16
+ end
17
+
18
+ def lookup variable
19
+ return values[scope_level_of variable][variable.to_sym]
20
+ end
21
+
22
+ def update variable, value=nil
23
+ level = scope_level_of variable
24
+ values[level][variable.to_sym] = value
25
+ end
26
+
27
+ def insert variable, value=nil
28
+ raise SymbolTableError, "Re-definition of #{variable} not allowed" if values.last.include? variable.to_sym
29
+
30
+ values[current_scope][variable.to_sym] = value
31
+ end
32
+
33
+ def current_scope
34
+ values.length - 1
35
+ end
36
+
37
+ private
38
+ attr_accessor :values
39
+
40
+ def scope_level_of variable
41
+ values.reverse.each_with_index do |v, i|
42
+ return current_scope - i if v.include? variable.to_sym
43
+ end
44
+
45
+ raise SymbolTableError, "Variable #{variable} not found in symbol table."
46
+ end
47
+ end
48
+ end
data/lib/oryx/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Oryx
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.1"
3
3
  end
data/lib/oryx.rb CHANGED
@@ -4,6 +4,9 @@ require "oryx/runner"
4
4
  require "oryx/lexer"
5
5
  require "oryx/parser"
6
6
  require "oryx/ast"
7
+ require "oryx/symbol_table"
8
+ require "oryx/error"
9
+ require "oryx/contractor"
7
10
 
8
11
  module Oryx
9
12
  # Your code goes here...
data/test/data/add.c ADDED
@@ -0,0 +1,3 @@
1
+ int main(){
2
+ return 7 + 5;
3
+ }
data/test/data/div.c ADDED
@@ -0,0 +1,3 @@
1
+ int main(){
2
+ return 15/7;
3
+ }
data/test/data/eq.c ADDED
@@ -0,0 +1,6 @@
1
+ int main(){
2
+ if(5 == 5)
3
+ return 5;
4
+ else
5
+ return 4;
6
+ }
data/test/data/fib.c ADDED
@@ -0,0 +1,8 @@
1
+ int fib(int x) {
2
+ if (x < 2) return 1;
3
+ else return fib(x-1)+fib(x-2);
4
+ }
5
+
6
+ int main() {
7
+ return fib(8);
8
+ }
data/test/data/fun_1.c ADDED
@@ -0,0 +1,7 @@
1
+ int fun_1(){
2
+ return 1;
3
+ }
4
+
5
+ int main(){
6
+ return fun_1() + 8;
7
+ }
data/test/data/ge.c ADDED
@@ -0,0 +1,6 @@
1
+ int main(){
2
+ if( 15 > 8)
3
+ return 7;
4
+ else
5
+ return 8;
6
+ }
data/test/data/geq.c ADDED
@@ -0,0 +1,6 @@
1
+ int main(){
2
+ if(15 >= 16)
3
+ return 7;
4
+ else
5
+ return 9;
6
+ }
@@ -0,0 +1,5 @@
1
+ int a = 7;
2
+
3
+ int main(){
4
+ return a;
5
+ }
@@ -0,0 +1,6 @@
1
+ int a = 19;
2
+ int b = 23;
3
+
4
+ int main(){
5
+ return a+b;
6
+ }
@@ -0,0 +1,6 @@
1
+ int a;
2
+
3
+ int main(){
4
+ a = 15;
5
+ return a;
6
+ }
data/test/data/if.c ADDED
@@ -0,0 +1,15 @@
1
+ int a = 8;
2
+ int b = 13;
3
+
4
+ int main(){
5
+ if(a < b){
6
+ a = a + b;
7
+ b = 1;
8
+ if (b < a)
9
+ return b;
10
+ else
11
+ return a;
12
+ }
13
+ else
14
+ return a;
15
+ }
data/test/data/le.c ADDED
@@ -0,0 +1,6 @@
1
+ int main(){
2
+ if( 15 < 8 )
3
+ return 7;
4
+ else
5
+ return 8;
6
+ }
data/test/data/leq.c ADDED
@@ -0,0 +1,6 @@
1
+ int main(){
2
+ if( 15 <= 15 )
3
+ return 15;
4
+ else
5
+ return 3;
6
+ }
data/test/data/mul.c ADDED
@@ -0,0 +1,3 @@
1
+ int main(){
2
+ return 25*7;
3
+ }
data/test/data/neq.c ADDED
@@ -0,0 +1,6 @@
1
+ int main(){
2
+ if(5 != 5)
3
+ return 5;
4
+ else
5
+ return 4;
6
+ }
@@ -0,0 +1,3 @@
1
+ int main(){
2
+ return 42;
3
+ }
data/test/data/sub.c ADDED
@@ -0,0 +1,3 @@
1
+ int main(){
2
+ return 10-17;
3
+ }
@@ -0,0 +1,19 @@
1
+ require_relative '../../test_helper'
2
+
3
+ module Oryx
4
+ class TestError < Test::Unit::TestCase
5
+ context "Error Hierarchy" do
6
+ should "be subclasssed from standard error" do
7
+ assert Error.ancestors.include? StandardError
8
+ end
9
+
10
+ should "be namespaced" do
11
+ assert Error.name.include? "::"
12
+ end
13
+
14
+ should "have Oryx namespace" do
15
+ assert_equal Error.name.to_s.split("::").first, "Oryx"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../../test_helper'
2
+
3
+ module Oryx
4
+ class TestRunner < Test::Unit::TestCase
5
+ context "test runner with many different files" do
6
+ should_generate_toolchain_output("return", 42)
7
+ should_generate_toolchain_output("add", 12)
8
+ should_generate_toolchain_output("sub", (-7)%256)
9
+ should_generate_toolchain_output("mul", 175)
10
+ should_generate_toolchain_output("div", 2)
11
+ should_generate_toolchain_output("gvar_1", 7)
12
+ should_generate_toolchain_output("gvar_2", 42)
13
+ should_generate_toolchain_output("gvar_3", 15)
14
+ should_generate_toolchain_output("fun_1", 9)
15
+ should_generate_toolchain_output("ge", 7)
16
+ should_generate_toolchain_output("le", 8)
17
+ should_generate_toolchain_output("leq", 15)
18
+ should_generate_toolchain_output("geq", 9)
19
+ should_generate_toolchain_output("eq", 5)
20
+ should_generate_toolchain_output("neq", 4)
21
+ should_generate_toolchain_output("if", 21)
22
+ should_generate_toolchain_output("fib", 34)
23
+ end
24
+ end
25
+ end