oryx 0.2.1 → 0.3.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,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