citrus-compiler 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +32 -0
- data/README.rdoc +63 -0
- data/bin/citrus.rb +30 -0
- data/example/factorial.ct +14 -0
- data/example/simple.ct +69 -0
- data/lib/citrus.rb +37 -0
- data/lib/citrus/compiler.rb +65 -0
- data/lib/citrus/compiler/block.rb +29 -0
- data/lib/citrus/compiler/function.rb +79 -0
- data/lib/citrus/compiler/generator.rb +303 -0
- data/lib/citrus/compiler/global_strings.rb +19 -0
- data/lib/citrus/compiler/variable.rb +65 -0
- data/lib/citrus/core.rb +31 -0
- data/lib/citrus/exceptions.rb +33 -0
- data/lib/citrus/nodes.rb +238 -0
- data/lib/citrus/runtime.rb +32 -0
- metadata +112 -0
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
|
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
|
data/lib/citrus/core.rb
ADDED
@@ -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
|
data/lib/citrus/nodes.rb
ADDED
@@ -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
|
+
|