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.
- 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
|