oryx 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/.travis.yml +15 -2
- data/README.md +13 -1
- data/doc/ast.md +110 -0
- data/doc/cflat.md +71 -0
- data/doc/conclusion.md +0 -0
- data/doc/fibonacci.md +15 -0
- data/doc/img/fib_parse.jpg +0 -0
- data/doc/intermediate_lang.md +0 -0
- data/doc/intro.md +89 -0
- data/doc/lexer.md +33 -0
- data/doc/parser.md +11 -0
- data/doc/symbol_table.md +23 -0
- data/doc/tools.md +9 -0
- data/doc/x86_translation.md +0 -0
- data/lib/oryx/ast.rb +34 -8
- data/lib/oryx/contractor.rb +185 -0
- data/lib/oryx/error.rb +8 -0
- data/lib/oryx/parser.rb +31 -7
- data/lib/oryx/runner.rb +30 -1
- data/lib/oryx/symbol_table.rb +48 -0
- data/lib/oryx/version.rb +1 -1
- data/lib/oryx.rb +3 -0
- data/test/data/add.c +3 -0
- data/test/data/div.c +3 -0
- data/test/data/eq.c +6 -0
- data/test/data/fib.c +8 -0
- data/test/data/fun_1.c +7 -0
- data/test/data/ge.c +6 -0
- data/test/data/geq.c +6 -0
- data/test/data/gvar_1.c +5 -0
- data/test/data/gvar_2.c +6 -0
- data/test/data/gvar_3.c +6 -0
- data/test/data/if.c +15 -0
- data/test/data/le.c +6 -0
- data/test/data/leq.c +6 -0
- data/test/data/mul.c +3 -0
- data/test/data/neq.c +6 -0
- data/test/data/return.c +3 -0
- data/test/data/sub.c +3 -0
- data/test/lib/oryx/error_test.rb +19 -0
- data/test/lib/oryx/runner_test.rb +25 -0
- data/test/lib/oryx/symbol_table_test.rb +147 -0
- data/test/shoulda_macros/runner.rb +58 -0
- data/test/test_helper.rb +1 -0
- metadata +61 -4
@@ -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
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, _|
|
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, _|
|
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|
|
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('
|
87
|
-
clause('
|
88
|
-
clause('
|
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
data/lib/oryx.rb
CHANGED
data/test/data/add.c
ADDED
data/test/data/div.c
ADDED
data/test/data/eq.c
ADDED
data/test/data/fib.c
ADDED
data/test/data/fun_1.c
ADDED
data/test/data/ge.c
ADDED
data/test/data/geq.c
ADDED
data/test/data/gvar_1.c
ADDED
data/test/data/gvar_2.c
ADDED
data/test/data/gvar_3.c
ADDED
data/test/data/if.c
ADDED
data/test/data/le.c
ADDED
data/test/data/leq.c
ADDED
data/test/data/mul.c
ADDED
data/test/data/neq.c
ADDED
data/test/data/return.c
ADDED
data/test/data/sub.c
ADDED
@@ -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
|