citrus-compiler 0.8.0
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/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
|
+
|