citrus-compiler 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ Copyright (c) 2011 Mac Malone
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions
7
+ are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the author nor the names of his contributors
17
+ may be used to endorse or promote products derived from this software
18
+ without specific prior written permission.
19
+
20
+ This license is subject to change without notice.
21
+
22
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
+ DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
26
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ POSSIBILITY OF SUCH DAMAGE.
data/README.rdoc ADDED
@@ -0,0 +1,63 @@
1
+ = Citrus
2
+
3
+ Author:: Mac Malone
4
+ License:: See LICENSE
5
+ Copyright:: Copyright (c) 2010 Mac Malone
6
+
7
+ Citrus is a simple dynamically typed linear language written in Ruby, Treetop, and LLVM. It is meant to be an example of how to write a basic compiler.
8
+
9
+ Being linear, it could be called very c-like, but it has been written to have a syntax closer to Ruby. Its current main features are:
10
+
11
+ * Dynamically typed
12
+ * Extremely ruby-like syntax
13
+ * Integer, Float, String, Boolean, and Array types
14
+ * Easy integration of C functions into the API
15
+
16
+ It's prime examples are in the example directory, but here is an excerpt from factorial.ct:
17
+
18
+ def factorial(num)
19
+ if num == 0
20
+ fac = 1
21
+ else
22
+ fac = num*factorial(num-1)
23
+ end
24
+ return fac
25
+ end
26
+
27
+ printf("factorial(%d): %d", 6, factorial(6))
28
+
29
+ As you can see, very ruby-like.
30
+
31
+ == Installing & Requirements
32
+
33
+ Citrus requires the following libraries:
34
+
35
+ * LLVM 2.8 compiled with shared libraries
36
+ * On Mac OS X, the quickest way is to use `brew install llvm --shared`
37
+ * On Linux, there are usually pre-compiled packages
38
+ * Ruby
39
+ * Treetop
40
+ * Ruby LLVM 2.9.1
41
+
42
+ Everything except LLVM is installed when running `gem install citrus`
43
+
44
+ == Running
45
+
46
+ Citrus is a pretty simple thing to run:
47
+
48
+ $ ruby bin/citrus [options] FILENAME
49
+ -O Doesn't run optimization
50
+ -i Dumps bitcode
51
+ -c NAME Compiles to the given file name
52
+ -h Prints help
53
+
54
+ If citrus is run without -c it JIT complies the code, running it in place.
55
+
56
+ == Problems & Todos
57
+
58
+ * Hashs
59
+ * Create a standard library
60
+ * Better dynamically typed arrays (using structures)
61
+ * Better exception handling (currently only works semi-effectively within begin blocks)
62
+ * Garbage Collection
63
+
data/bin/citrus.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby -s
2
+ $:.unshift File.dirname(__FILE__) + "/../lib"
3
+ require "citrus"
4
+
5
+ if $h
6
+ puts <<-EOS
7
+
8
+ ~= Citrus =~
9
+
10
+ To run a file
11
+ citrus fruit.ct
12
+
13
+ To compile to native application
14
+ citrus -c=lemon lime.ct
15
+
16
+ EOS
17
+ exit
18
+ end
19
+
20
+ file = ARGV.first
21
+ abort "Usage: citrus [-hiO] [-c=file.o] file.or" unless file
22
+
23
+ g = Citrus.compile_file(File.expand_path(file))
24
+ g.optimize unless $O
25
+
26
+ case
27
+ when $i: g.inspect
28
+ when $c: g.compile(TrueClass === $c ? File.basename(file, File.extname(file)) : $c)
29
+ else g.run
30
+ end
@@ -0,0 +1,14 @@
1
+ # Citrus factorials...
2
+
3
+ def factorial(num)
4
+ if num == 0
5
+ fac = 1
6
+ else
7
+ fac = num*factorial(num-1)
8
+ end
9
+ return fac
10
+ end
11
+
12
+ puts("== factorial ")
13
+ printf("factorial(%d): %d", 6, factorial(6))
14
+ puts("")
data/example/simple.ct ADDED
@@ -0,0 +1,69 @@
1
+ =begin
2
+ A simple test...
3
+ Included in Citrus source code
4
+ v1.0
5
+ =end
6
+
7
+ require 'factorial.ct'
8
+
9
+ $exitcode = 7
10
+
11
+ def input
12
+ buf = " "
13
+ read(0, buf, 1)
14
+ puts(buf)
15
+ end
16
+
17
+ def compare
18
+ num = 7
19
+ cond = (num == 4.5 + 2.5)
20
+ if cond
21
+ puts("7 = 4.5 + 2.5")
22
+ end
23
+ if num == 6 - 3
24
+ puts("6 - 3 = 7")
25
+ else
26
+ puts("6 - 3 != 7")
27
+ end
28
+ end
29
+
30
+ def iterate
31
+ iteration = 10
32
+ while iteration >= 0
33
+ printf("%d ", iteration)
34
+ iteration = iteration - 1
35
+ end
36
+ puts("")
37
+ array = ["lift", "off", "and", "away"]
38
+ for iteration in array
39
+ printf("%s ", iteration)
40
+ end
41
+ puts("")
42
+ end
43
+
44
+ def handle(err)
45
+ begin
46
+ puts("beginning exception handling...")
47
+ if err
48
+ raise(" - Erred! - ")
49
+ end
50
+ rescue
51
+ puts($!)
52
+ else
53
+ puts(" - No Error - ")
54
+ ensure
55
+ puts("...end of exception handling")
56
+ end
57
+ end
58
+
59
+ puts("== input ")
60
+ input
61
+ puts("== compare ")
62
+ compare
63
+ puts("== iterate ")
64
+ iterate
65
+ puts("== handle ")
66
+ handle(true)
67
+ handle(false)
68
+ puts("== exit ")
69
+ exit($exitcode)
data/lib/citrus.rb ADDED
@@ -0,0 +1,37 @@
1
+ require "rubygems"
2
+ require "treetop"
3
+
4
+ require "citrus/core"
5
+ require "citrus/runtime"
6
+ require "citrus/compiler"
7
+ require "citrus/exceptions"
8
+ require "citrus/nodes"
9
+
10
+ if File.file?(File.dirname(__FILE__) + "/citrus/grammar.rb")
11
+ # Take compiled one
12
+ require "citrus/grammar"
13
+ else
14
+ Treetop.load File.dirname(__FILE__) + "/citrus/grammar.tt"
15
+ end
16
+
17
+ module Citrus
18
+
19
+ def self.compile(code)
20
+ $compiler = Citrus::Compiler.new
21
+ $parser = CitrusParser.new
22
+
23
+ if node = $parser.parse(code)
24
+ node.compile($compiler)
25
+ else
26
+ error(SyntaxError.new)
27
+ end
28
+
29
+ $compiler
30
+ end
31
+
32
+ def self.compile_file(file)
33
+ $file = file
34
+ self.compile(File.read(file))
35
+ end
36
+
37
+ end
@@ -0,0 +1,65 @@
1
+ require "citrus/compiler/generator"
2
+ require "citrus/compiler/block"
3
+ require "citrus/compiler/function"
4
+ require "citrus/compiler/variable"
5
+ require "citrus/compiler/global_strings"
6
+
7
+ require "tempfile"
8
+
9
+ module Citrus
10
+ class Compiler
11
+
12
+ attr_reader :generator
13
+ attr_reader :module
14
+
15
+ def initialize
16
+ @module = LLVM::Module.create("Citrus")
17
+ GlobalVariables.init(@module)
18
+ GlobalFunctions.init(@module)
19
+ @function = @module.functions.add("main", LLVM::Type.function([INT, LLVM::Type.pointer(PCHAR)], INT))
20
+ @generator = Generator.new(@module, @function)
21
+ GlobalStrings.init(@generator.builder)
22
+ end
23
+
24
+ def preamble
25
+ Runtime.build_lib(@generator)
26
+ end
27
+
28
+ def finish
29
+ @generator.builder.ret(INT.from_i(0))
30
+ @generator.finish
31
+ end
32
+
33
+ def run
34
+ @engine = LLVM::ExecutionEngine.create_jit_compiler(@module)
35
+ @engine.run_function(@function, 0, 0)
36
+ end
37
+
38
+ def to_file(file)
39
+ @module.write_bitcode(file)
40
+ end
41
+
42
+ def compile(file)
43
+ #bc = Tempfile.new("#{file}.bc", "/tmp")
44
+ #as = Tempfile.new("#{file}.s", "/tmp")
45
+ to_file("#{file}.ctc.bc")
46
+ tool = LLVM.bin_path.empty? ? "llc" : File.join(LLVM.bin_path, "llc")
47
+ %x[#{tool} #{file}.ctc.bc -o #{file}.ctc.s]
48
+ %x[gcc #{file}.ctc.s -o #{file}]
49
+ File.delete("#{file}.ctc.bc")
50
+ File.delete("#{file}.ctc.s")
51
+ #bc.close!
52
+ #as.close!
53
+ end
54
+
55
+ def optimize
56
+ PassManager.new(@engine).run(@module) unless @engine.nil?
57
+ end
58
+
59
+ def inspect
60
+ @module.dump
61
+ end
62
+
63
+ end
64
+ end
65
+
@@ -0,0 +1,29 @@
1
+ module Citrus
2
+ class Block
3
+
4
+ def initialize(mod, function, parent)
5
+ @module = mod
6
+ @function = function
7
+ @parent = parent
8
+ build_block { |g| yield g if block_given? }
9
+ end
10
+
11
+ def basic_block
12
+ return @generator.basic_block
13
+ end
14
+ alias :bb :basic_block
15
+
16
+ def locals
17
+ return @generator.locals
18
+ end
19
+
20
+ private
21
+
22
+ def build_block
23
+ @generator = Generator.new(@module, @function, @parent)
24
+ yield @generator if block_given?
25
+ @generator.finish
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,79 @@
1
+ module Citrus
2
+ class Function
3
+
4
+ attr_reader :varargs
5
+ attr_reader :generator
6
+
7
+ def initialize(name, args, mod)
8
+ @name = name
9
+ @module = mod
10
+ @rtype = LLVM::Type.opaque
11
+ @atypes = args.map{LLVM::Type.opaque}
12
+ @args = args.map{|arg| arg.class.to_s == "String" ? arg.to_a : arg}
13
+ @varargs = @args.empty? ? false : (@args.last[0].slice(0, 1) == "*")
14
+ @func = @module.functions.add(name, LLVM::Type.function(@atypes, @rtype, :varargs => @vaargs))
15
+ build_function { |g| yield g if block_given? }
16
+ end
17
+
18
+ def args
19
+ return @args.map{|arg| arg[0]}
20
+ end
21
+
22
+ def return(value, builder)
23
+ @rtype.refine(LLVM::Type(value)) if @rtype.kind == :opaque
24
+ builder.ret(value)
25
+ end
26
+
27
+ def call(args, builder)
28
+ for i in 0...@atypes.size
29
+ @atypes[i].refine(LLVM::Type(args[i])) if @atypes[i].kind == :opaque
30
+ end
31
+ builder.call(@func, *args)
32
+ end
33
+
34
+ def force_types(args, ret=nil)
35
+ unless args.empty?
36
+ for i in 0...@atypes.size
37
+ @atypes[i].refine(LLVM::Type(args[i])) if @atypes[i].kind == :opaque
38
+ end
39
+ end
40
+ unless ret.nil?
41
+ @rtype.refine(ret) if @rtype.kind == :opaque
42
+ end
43
+ end
44
+
45
+ def method_missing(symbol, *args, &block)
46
+ @func.send(symbol, *args, &block)
47
+ end
48
+
49
+ private
50
+
51
+ def build_function
52
+ @generator = Generator.new(@module, self)
53
+ @args.each do |arg|
54
+ @generator.assign(arg[0], @func.params[@args.index(arg)])
55
+ end
56
+ yield generator
57
+ @generator.return
58
+ @generator.finish
59
+ end
60
+
61
+ end
62
+
63
+ class GlobalFunctions
64
+
65
+ def self.init(mod)
66
+ @module ||= mod
67
+ @functions ||= {}
68
+ end
69
+
70
+ def self.add(name, args)
71
+ return @functions[name] = Function.new(name, args, @module) { |g| yield g if block_given? }
72
+ end
73
+
74
+ def self.named(name)
75
+ return @functions[name]
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,303 @@
1
+ module Citrus
2
+ class Generator
3
+
4
+ attr_reader :basic_block
5
+ attr_reader :module
6
+ attr_reader :builder
7
+ attr_reader :locals
8
+ attr_reader :parent
9
+
10
+ def initialize(mod, function, parent=nil)
11
+ @module = mod
12
+ @locals = {}
13
+ @parent = parent
14
+ @function = function
15
+ @basic_block = @function.basic_blocks.append
16
+ @builder = LLVM::Builder.create
17
+ @builder.position_at_end(@basic_block)
18
+ end
19
+
20
+ def array(values)
21
+ ary = @builder.alloca(LLVM::Array(LLVM::Type(values.first), values.size))
22
+ for index in 0...values.size
23
+ ptr = @builder.gep(ary, [INT.from_i(0), INT.from_i(index)])
24
+ @builder.store(values[index], ptr)
25
+ end
26
+ return ary
27
+ end
28
+
29
+ def string(value)
30
+ ptr = GlobalStrings.pointer(value)
31
+ end
32
+
33
+ def float(value)
34
+ FLOAT.from_f(value)
35
+ end
36
+
37
+ def number(value)
38
+ INT.from_i(value)
39
+ end
40
+
41
+ def bool(value)
42
+ BOOL.from_i(value ? 1 : 0)
43
+ end
44
+
45
+ def not(value)
46
+ @builder.not(value)
47
+ end
48
+
49
+ def call(func, *args)
50
+ begin
51
+ GlobalFunctions.named(func).call(args, @builder)
52
+ rescue NoMethodError # for C methods
53
+ f = @module.functions.named(func)
54
+ raise NoMethodError if f.nil?
55
+ @builder.call(f, *args)
56
+ end
57
+ end
58
+
59
+ def resolve_conflicts(*gens)
60
+ locals = []
61
+ gens.each{|g| locals += g.locals.keys}
62
+ stat = locals.inject(Hash.new(0)){|h, e| h[e]+=1; h}
63
+ stat.select{|k, v| v > 1}.collect{|a| a[0]}.each do |k|
64
+ resolve_conflict(k, *gens)
65
+ end
66
+ end
67
+
68
+ def resolve_conflict(name, *gens)
69
+ ty = INT
70
+ nodes = {}
71
+ gens.select{|g| g.locals.has_key?(name)}.each do |g|
72
+ bb = g.basic_block
73
+ var = g.locals[name]
74
+ builder = LLVM::Builder.create
75
+ if bb == @builder.insert_block && !nodes.has_key?(bb.previous)
76
+ builder.position_before(bb.previous.instructions.last)
77
+ nodes[bb.previous] = var.value(builder)
78
+ else
79
+ builder.position_before(bb.instructions.last)
80
+ nodes[bb] = var.value(builder)
81
+ end
82
+ builder.dispose
83
+ end
84
+ ptr = @builder.phi(ty, nodes)
85
+ self.assign(name, ptr)
86
+ end
87
+
88
+ def assign(name, value)
89
+ unless @locals.has_key?(name)
90
+ @locals[name] = Variable.new(value, @builder)
91
+ else
92
+ @locals[name].assign(value, @builder)
93
+ end
94
+ end
95
+
96
+ def assign_global(name, value)
97
+ unless GlobalVariables.named(name)
98
+ GlobalVariables.add(name, value, @builder)
99
+ else
100
+ GlobalVariables.named(name).assign(value, @builder)
101
+ end
102
+ end
103
+
104
+ def assign_index(name, index, value)
105
+ ary = @locals[name].value(@builder)
106
+ ptr = @builder.gep(ary, [INT.from_i(0), index])
107
+ @builder.store(value, ptr)
108
+ end
109
+
110
+ def compare(op, lval, rval)
111
+ case op.to_s
112
+ when *CMP_MAPPING.keys
113
+ if LLVM::Type(lval) == FLOAT.type || LLVM::Type(rval) == FLOAT.type
114
+ symbol = "o#{CMP_MAPPING[op.to_s].to_s}".to_sym
115
+ if LLVM::Type(lval) != FLOAT.type
116
+ lval = @builder.ui2fp(lval, FLOAT.type)
117
+ elsif LLVM::Type(rval) != FLOAT.type
118
+ rval = @builder.ui2fp(rval, FLOAT.type)
119
+ end
120
+ @builder.fcmp(symbol, lval, rval)
121
+ else
122
+ symbol = CMP_MAPPING[op.to_s]
123
+ symbol = "s#{symbol.to_s}".to_sym unless symbol == :eq || symbol == :ne
124
+ @builder.icmp(symbol, lval, rval)
125
+ end
126
+ when "&&", "and"
127
+ @builder.and(lval, rval)
128
+ when "||", "or"
129
+ @builder.or(lval, rval)
130
+ end
131
+ end
132
+
133
+ def equate(op, lval, rval)
134
+ if LLVM::Type(lval) == FLOAT.type || LLVM::Type(rval) == FLOAT.type
135
+ symbol = "f#{EQU_MAPPING[op.to_s].to_s}".to_sym
136
+ if LLVM::Type(lval) != FLOAT.type
137
+ lval = @builder.ui2fp(lval, FLOAT.type)
138
+ elsif LLVM::Type(rval) != FLOAT.type
139
+ rval = @builder.ui2fp(rval, FLOAT.type)
140
+ end
141
+ @builder.send(symbol, lval, rval)
142
+ else
143
+ symbol = EQU_MAPPING[op.to_s]
144
+ symbol = :sdiv if symbol == :div
145
+ @builder.send(symbol, lval, rval)
146
+ end
147
+ end
148
+
149
+ def vars
150
+ return @parent.nil? ? @locals : @locals.merge(@parent.vars)
151
+ end
152
+
153
+ def load(name)
154
+ if @locals.has_key?(name)
155
+ @locals[name].value(@builder)
156
+ else
157
+ self.vars[name].value(@builder)
158
+ end
159
+ end
160
+
161
+ def load_global(name)
162
+ GlobalVariables.named(name).value(@builder)
163
+ end
164
+
165
+ def load_index(ary, index)
166
+ @builder.load(@builder.gep(ary, [INT.from_i(0), index]))
167
+ end
168
+
169
+ def function(name, args)
170
+ GlobalFunctions.add(name, args) { |g| yield g }
171
+ end
172
+
173
+ def declare(name, args, ret, varargs = false)
174
+ rtype = DEC_MAPPING[ret.to_sym]
175
+ atypes = args.map{|arg| DEC_MAPPING[arg.to_sym]}
176
+ @module.functions.add(name.to_s, LLVM::Type.function(atypes, rtype, :varargs => varargs))
177
+ end
178
+
179
+ def block
180
+ Block.new(@module, @function, self) { |g| yield g if block_given? }
181
+ end
182
+
183
+ def condition(cond, thenblock, elseblock, elsifs=[])
184
+ @basic_block = self.block.bb
185
+ eb = elsifs.empty? ? elseblock : self.block
186
+ efbs = []
187
+ @builder.cond(cond, thenblock.bb, eb.bb)
188
+ for i in 0...elsifs.length
189
+ efbs += eb
190
+ @builder.position_at_end(eb.bb)
191
+ neb = i+1 == elsifs.length ? elseblock : self.block
192
+ @builder.cond(elsifs[i][0], elsifs[i][1].bb, eb.bb)
193
+ @builder.position_at_end(elsifs[i][1].bb)
194
+ @builder.br(@basic_block)
195
+ eb = neb
196
+ end
197
+ @builder.position_at_end(thenblock.bb)
198
+ @builder.br(@basic_block)
199
+ @builder.position_at_end(elseblock.bb)
200
+ @builder.br(@basic_block)
201
+ @builder.position_at_end(@basic_block)
202
+ self.resolve_conflicts(thenblock, elseblock, *efbs)
203
+ end
204
+
205
+ def case(val, cases, elseblock)
206
+ ncases = {}
207
+ for pair in cases
208
+ ncases[pair[0].bb] = pair[1]
209
+ end
210
+ switch = @builder.switch(val, elseblock.bb, ncases)
211
+ @basic_block = self.block.bb
212
+ @builder.position_at_end(elseblock.bb)
213
+ @builder.br(@basic_block)
214
+ ncases.each_value do |c|
215
+ @builder.position_at_end(c[1])
216
+ @builder.br(@basic_block)
217
+ end
218
+ @builder.position_at_end(@basic_block)
219
+ self.resolve_conflicts(elseblock, *cases.keys)
220
+ end
221
+
222
+ def begin(rblock, elblock, enblock)
223
+ cond = self.compare(:==, self.load_global(STS_GLOBAL), self.number(1))
224
+ @builder.cond(cond, rblock.bb, elblock.bb)
225
+ @basic_block = self.block.bb
226
+ @builder.position_before(rblock.bb.instructions.first)
227
+ self.assign_global(STS_GLOBAL, self.number(0))
228
+ @builder.position_at_end(rblock.bb)
229
+ @builder.br(enblock.bb)
230
+ @builder.position_at_end(elblock.bb)
231
+ @builder.br(enblock.bb)
232
+ @builder.position_before(enblock.bb.instructions.first)
233
+ self.resolve_conflicts(rblock, elblock)
234
+ @builder.position_at_end(enblock.bb)
235
+ @builder.br(@basic_block)
236
+ @builder.position_at_end(@basic_block)
237
+ @locals.merge!(enblock.locals)
238
+ end
239
+
240
+ def unwind
241
+ @builder.unwind
242
+ end
243
+
244
+ # Needs to be called before running any loop command
245
+ # (specifically before calculating the conditions for the loop)
246
+ def preploop(looptype=nil, *args)
247
+ if looptype == :for
248
+ self.assign("for", INT.from_i(0))
249
+ self.assign(args[0], self.load_index(args[1], self.number(0)))
250
+ end
251
+ @basic_block = self.block.bb
252
+ @builder.br(@basic_block)
253
+ @builder.position_at_end(@basic_block)
254
+ end
255
+
256
+ def while(cond)
257
+ @builder.position_before(@basic_block.instructions.first)
258
+ generator = Generator.new(@module, @function, self)
259
+ yield generator
260
+ generator.break(@basic_block)
261
+ generator.finish
262
+ self.resolve_conflicts(generator, self)
263
+ @builder.position_at_end(@basic_block)
264
+ @basic_block = self.block.bb
265
+ @builder.cond(cond, generator.basic_block, @basic_block)
266
+ @builder.position_at_end(@basic_block)
267
+ end
268
+
269
+ def for(var, indices)
270
+ generator = Generator.new(@module, @function, self)
271
+ generator.assign(var, generator.load_index(indices, generator.load("for")))
272
+ yield generator
273
+ generator.assign("for", generator.equate(:+, generator.load("for"), generator.number(1)))
274
+ generator.break(@basic_block)
275
+ generator.finish
276
+ @builder.position_at_end(@basic_block)
277
+ self.resolve_conflict("for", generator, self)
278
+ @basic_block = self.block.bb
279
+ size = LLVM::C.LLVMGetArrayLength(LLVM::Type(indices).element_type)
280
+ cond = self.compare(:<, self.load("for"), self.number(size))
281
+ @builder.cond(cond, generator.basic_block, @basic_block)
282
+ @builder.position_at_end(@basic_block)
283
+ end
284
+
285
+ def return(value=self.number(0))
286
+ unless @finished
287
+ @function.return(value, @builder)
288
+ @finished = true
289
+ end
290
+ end
291
+
292
+ def break(block)
293
+ unless @finished
294
+ @builder.br(block)
295
+ end
296
+ end
297
+
298
+ def finish
299
+ @builder.dispose
300
+ end
301
+
302
+ end
303
+ end
@@ -0,0 +1,19 @@
1
+ module Citrus
2
+ class GlobalStrings
3
+
4
+ def self.init(builder)
5
+ @builder ||= builder
6
+ @pointers ||= {}
7
+ end
8
+
9
+ def self.pointer(value)
10
+ if @pointers.has_key?(value)
11
+ return @pointers[value]
12
+ else
13
+ @pointers[value] = @builder.global_string_pointer(value)
14
+ return @pointers[value]
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,65 @@
1
+ module Citrus
2
+ class Variable
3
+
4
+ attr_reader :type
5
+ attr_reader :pointer
6
+
7
+ def initialize(value, builder)
8
+ @type = LLVM::Type(value)
9
+ @pointer = builder.alloca(@type)
10
+ builder.store(value, @pointer)
11
+ end
12
+
13
+ def assign(value, builder)
14
+ type = LLVM::Type(value)
15
+ unless type == @type
16
+ @type = type
17
+ @pointer = builder.alloca(type)
18
+ end
19
+ builder.store(value, @pointer)
20
+ end
21
+
22
+ def value(builder)
23
+ builder.load(@pointer)
24
+ end
25
+
26
+ end
27
+
28
+ class GlobalVariable < Variable
29
+
30
+ def initialize(name, value, mod, builder)
31
+ @name = name
32
+ @module = mod
33
+ @type = LLVM::Type(value)
34
+ @pointer = @module.globals.add(@type, @name)
35
+ @pointer.initializer = value
36
+ end
37
+
38
+ def assign(value, builder)
39
+ @type = LLVM::Type(value)
40
+ builder.store(value, @pointer)
41
+ end
42
+
43
+ def value(builder)
44
+ builder.load(@module.globals.named(@name))
45
+ end
46
+
47
+ end
48
+
49
+ class GlobalVariables
50
+
51
+ def self.init(mod)
52
+ @module ||= mod
53
+ @variables ||= {}
54
+ end
55
+
56
+ def self.add(name, value, builder)
57
+ return @variables[name] = GlobalVariable.new(name, value, @module, builder)
58
+ end
59
+
60
+ def self.named(name)
61
+ return @variables[name]
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,31 @@
1
+ require "rubygems"
2
+
3
+ =begin # Custom LLVM Loading (for me only)
4
+ require "llvm/load"
5
+ LLVM.load("#{File.dirname(__FILE__)}/../llvm-2.9")
6
+ =end
7
+
8
+ require 'llvm/core'
9
+ require 'llvm/execution_engine'
10
+ require 'llvm/transforms/scalar'
11
+
12
+ module Citrus
13
+
14
+ LLVM.init_x86
15
+
16
+ PCHAR = LLVM::Type.pointer(LLVM::Int8)
17
+ FLOAT = LLVM::Double
18
+ INT = LLVM::Int
19
+ BOOL = LLVM::Int1
20
+
21
+ CMP_MAPPING = { "==" => :eq, "!=" => :ne, ">" => :gt,
22
+ ">=" => :ge, "<" => :lt, "<=" => :le }
23
+
24
+ EQU_MAPPING = { "+" => :add, "-" => :sub, "/" => :div, "*" => :mul }
25
+
26
+ DEC_MAPPING = { :bool => BOOL, :float => FLOAT, :int => INT, :string => PCHAR }
27
+
28
+ ERR_GLOBAL = "$!"
29
+ STS_GLOBAL = "$?"
30
+
31
+ end
@@ -0,0 +1,33 @@
1
+ module Citrus
2
+ class SyntaxError < RuntimeError
3
+ def initialize
4
+ line = $parser.failure_line
5
+ col = $parser.failure_column
6
+ str = $parser.input.lines.to_a[line-1]
7
+ str = str.slice(col-1, str.length).delete($/)
8
+ str = "file end" if str.empty?
9
+ puts $parser.failure_reason
10
+ super("Unexpected #{str} at #{line}:#{col}.")
11
+ end
12
+ end
13
+
14
+ class NameError < RuntimeError
15
+ def initialize(name, index=nil, ff=false)
16
+ index ||= $parser.input.index(name)
17
+ line = $parser.input.line_of(index)
18
+ col = $parser.input.column_of(index)
19
+ super("Undefined #{ff ? "" : "local variable or "}function '#{name}' at #{line}:#{col}.")
20
+ end
21
+ end
22
+
23
+ class NotFoundError < StandardError
24
+ def initialize(file)
25
+ super("No such file or directory - #{file}.")
26
+ end
27
+ end
28
+
29
+ def self.error(error)
30
+ puts("#{error.message} (#{error.class.to_s.split('::').last})")
31
+ exit(1)
32
+ end
33
+ end
@@ -0,0 +1,238 @@
1
+ module Citrus
2
+ class ::Treetop::Runtime::SyntaxNode
3
+ def value
4
+ text_value
5
+ end
6
+
7
+ def codegen(context)
8
+ end
9
+ end
10
+ Node = Treetop::Runtime::SyntaxNode
11
+
12
+ class Script < Node
13
+ def compile(compiler)
14
+ compiler.preamble
15
+ expressions.each { |e| e.codegen(compiler.generator) }
16
+ compiler.finish
17
+ end
18
+ end
19
+
20
+ class Expression < Node
21
+ def codegen(g)
22
+ statements.map { |s| s.codegen(g) }
23
+ end
24
+ end
25
+
26
+ class Assign < Node
27
+ def codegen(g)
28
+ g.assign(var.value, expression.codegen(g).last)
29
+ end
30
+ end
31
+
32
+ class GlobalEq < Node
33
+ def codegen(g)
34
+ g.assign_global(globalvar.value, expression.codegen(g).last)
35
+ end
36
+ end
37
+
38
+ class IndexEq < Node
39
+ def codegen(g)
40
+ g.assign_index(index.var.value, index.expression.codegen(g).last, expression.codegen(g).last)
41
+ end
42
+ end
43
+
44
+ class Cmp < Node
45
+ def codegen(g)
46
+ g.compare(op.value, object.codegen(g), expression.codegen(g).last)
47
+ end
48
+ end
49
+
50
+ class Equation < Node
51
+ def codegen(g)
52
+ g.equate(op.value, object.codegen(g), expression.codegen(g).last)
53
+ end
54
+ end
55
+
56
+ class Require < Node
57
+ def codegen(g)
58
+ file = string.value
59
+ file = File.join(File.dirname($file), file) unless $file.nil?
60
+ error(NotFoundError.new(file)) unless File.exists?(file)
61
+ if node = $parser.parse(File.read(file))
62
+ node.expressions.each { |e| e.codegen(g) }
63
+ else
64
+ error(SyntaxError.new)
65
+ end
66
+ end
67
+ end
68
+
69
+ class Call < Node
70
+ def codegen(g)
71
+ arg_values = calllist.args.map { |arg| arg.codegen(g) }
72
+ begin
73
+ g.call(func.value, *arg_values)
74
+ rescue
75
+ Citrus.error(NameError.new(func.value, self.interval.first, true))
76
+ end
77
+ end
78
+ end
79
+
80
+ class Def < Node
81
+ def codegen(g)
82
+ args = arglist.args.map do |arg|
83
+ [arg.var.value, arg.default.nil? ? nil : arg.default.codegen(g)]
84
+ end
85
+ g.function(func.value, args) do |gf|
86
+ expressions.each { |e| e.codegen(gf) }
87
+ end
88
+ end
89
+ end
90
+
91
+ class Return < Node
92
+ def codegen(g)
93
+ g.return(expression.codegen(g).last)
94
+ end
95
+ end
96
+
97
+ class Begin < Node
98
+ def codegen(g)
99
+ expressions.each { |e| e.codegen(g) }
100
+ rb = g.block do |gb|
101
+ rescue_expressions.each { |e| e.codegen(gb) }
102
+ end
103
+ elb = g.block do |gb|
104
+ else_expressions.each { |e| e.codegen(gb) }
105
+ end
106
+ enb = g.block do |gb|
107
+ ensure_expressions.each { |e| e.codegen(gb) }
108
+ end
109
+ g.begin(rb, elb, enb)
110
+ end
111
+ end
112
+
113
+ class If < Node
114
+ def codegen(g)
115
+ tb = g.block do |gb|
116
+ expressions.each { |e| e.codegen(gb) }
117
+ end
118
+ elfs = elsifs.each do |elf|
119
+ [elf.condition.codegen(g).last,
120
+ g.block do |gb|
121
+ elf.elements.each { |e| e.expression.codegen(gb) }
122
+ end]
123
+ end
124
+ fb = g.block do |gb|
125
+ else_expressions.each { |e| e.codegen(gb) }
126
+ end
127
+ g.condition(condition.codegen(g).last, tb, fb, elfs)
128
+ end
129
+ end
130
+
131
+ class Unless < Node
132
+ def codegen(g)
133
+ tb = g.block do |gb|
134
+ expressions.each { |e| e.codegen(gb) }
135
+ end
136
+ fb = g.block do |gb|
137
+ else_expressions.each { |e| e.codegen(gb) }
138
+ end
139
+ g.condition(g.not(condition.codegen(g).last), tb, fb)
140
+ end
141
+ end
142
+
143
+ class Case < Node
144
+ def codegen(g)
145
+ cases = {}
146
+ whens.each do |w|
147
+ cases[w.val.codegen(g).last] = g.block do |gb|
148
+ w.elements.each { |e| e.expression.codegen(gb) }
149
+ end
150
+ end
151
+ eb = g.block do |gb|
152
+ else_expressions.each { |e| e.codegen(gb) }
153
+ end
154
+ g.case(switch.codegen(g).last, cases, eb)
155
+ end
156
+ end
157
+
158
+ class While < Node
159
+ def codegen(g)
160
+ g.preploop(:while)
161
+ g.while(condition.codegen(g).last) do |gw|
162
+ expressions.each { |e| e.codegen(gw) }
163
+ end
164
+ end
165
+ end
166
+
167
+ class For < Node
168
+ def codegen(g)
169
+ ary = indices.codegen(g)
170
+ g.preploop(:for, var.value, ary)
171
+ g.for(var.value, ary) do |gf|
172
+ expressions.each { |e| e.codegen(gf) }
173
+ end
174
+ end
175
+ end
176
+
177
+ class Index < Node
178
+ def codegen(g)
179
+ g.load_index(var.codegen(g), expression.codegen(g).last)
180
+ end
181
+ end
182
+
183
+ class GlobalVar < Node
184
+ def codegen(g)
185
+ g.load_global(value)
186
+ end
187
+ end
188
+
189
+ class Var < Node
190
+ def codegen(g)
191
+ begin
192
+ g.load(value)
193
+ rescue NoMethodError
194
+ begin
195
+ g.call(value)
196
+ rescue NoMethodError
197
+ Citrus.error(NameError.new(value, self.interval.first))
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ class ArrayNode < Node
204
+ def codegen(g)
205
+ g.array(value.map{ |v| v.codegen(g).last })
206
+ end
207
+ end
208
+
209
+ class StringNode < Node
210
+ def codegen(g)
211
+ g.string(value)
212
+ end
213
+ end
214
+
215
+ class SymbolNode < Node
216
+ def codegen(g)
217
+ g.string(value)
218
+ end
219
+ end
220
+
221
+ class FloatNode < Node
222
+ def codegen(g)
223
+ g.float(value)
224
+ end
225
+ end
226
+
227
+ class NumberNode < Node
228
+ def codegen(g)
229
+ g.number(value)
230
+ end
231
+ end
232
+
233
+ class BoolNode < Node
234
+ def codegen(g)
235
+ g.bool(value)
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,32 @@
1
+ module Citrus
2
+ module Runtime
3
+
4
+ def self.build_lib(generator)
5
+ @ct = generator
6
+ build_clib
7
+ build_globals
8
+ build_raise
9
+ end
10
+
11
+ def self.build_clib
12
+ @ct.declare(:printf, [:string], :int, true)
13
+ @ct.declare(:puts, [:string], :int)
14
+ @ct.declare(:read, [:int, :string, :int], :int)
15
+ @ct.declare(:exit, [:int], :int)
16
+ end
17
+
18
+ def self.build_globals
19
+ @ct.assign_global(ERR_GLOBAL, @ct.string(""))
20
+ @ct.assign_global(STS_GLOBAL, @ct.number(0))
21
+ end
22
+
23
+ def self.build_raise
24
+ func = @ct.function("raise", ["msg"]) do |gf|
25
+ gf.assign_global(ERR_GLOBAL, gf.load("msg"))
26
+ gf.assign_global(STS_GLOBAL, gf.number(1))
27
+ end
28
+ func.force_types([PCHAR], INT)
29
+ end
30
+
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: citrus-compiler
3
+ version: !ruby/object:Gem::Version
4
+ hash: 63
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 8
9
+ - 0
10
+ version: 0.8.0
11
+ platform: ruby
12
+ authors:
13
+ - Mac Malone
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-02 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: treetop
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 21
29
+ segments:
30
+ - 1
31
+ - 4
32
+ - 9
33
+ version: 1.4.9
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: ruby-llvm
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 41
45
+ segments:
46
+ - 2
47
+ - 9
48
+ - 1
49
+ version: 2.9.1
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description:
53
+ email:
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files:
59
+ - README.rdoc
60
+ - LICENSE
61
+ files:
62
+ - lib/citrus/compiler/block.rb
63
+ - lib/citrus/compiler/function.rb
64
+ - lib/citrus/compiler/generator.rb
65
+ - lib/citrus/compiler/global_strings.rb
66
+ - lib/citrus/compiler/variable.rb
67
+ - lib/citrus/compiler.rb
68
+ - lib/citrus/core.rb
69
+ - lib/citrus/exceptions.rb
70
+ - lib/citrus/nodes.rb
71
+ - lib/citrus/runtime.rb
72
+ - lib/citrus.rb
73
+ - bin/citrus.rb
74
+ - example/factorial.ct
75
+ - example/simple.ct
76
+ - README.rdoc
77
+ - LICENSE
78
+ homepage: http://github.com/tophat/citrus
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements:
105
+ - LLVM v2.9
106
+ rubyforge_project:
107
+ rubygems_version: 1.7.2
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Linear compiler written in Ruby
111
+ test_files: []
112
+