citrus-compiler 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +4 -4
- data/example/simple.ct +4 -0
- data/lib/citrus/compiler.rb +1 -0
- data/lib/citrus/compiler/array.rb +29 -0
- data/lib/citrus/compiler/generator.rb +24 -11
- data/lib/citrus/compiler/variable.rb +56 -10
- data/lib/citrus/core.rb +2 -2
- data/lib/citrus/nodes.rb +6 -0
- metadata +5 -20
data/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Author:: Mac Malone
|
4
4
|
License:: See LICENSE
|
5
|
-
Copyright:: Copyright (c)
|
5
|
+
Copyright:: Copyright (c) 2011 Mac Malone
|
6
6
|
|
7
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
8
|
|
@@ -10,7 +10,7 @@ Being linear, it could be called very c-like, but it has been written to have a
|
|
10
10
|
|
11
11
|
* Dynamically typed
|
12
12
|
* Extremely ruby-like syntax
|
13
|
-
* Integer, Float, String, Boolean, and
|
13
|
+
* Integer, Float, String, Boolean, Array, and Range types
|
14
14
|
* Easy integration of C functions into the API
|
15
15
|
|
16
16
|
It's prime examples are in the example directory, but here is an excerpt from factorial.ct:
|
@@ -37,9 +37,9 @@ Citrus requires the following libraries:
|
|
37
37
|
* On Linux, there are usually pre-compiled packages
|
38
38
|
* Ruby
|
39
39
|
* Treetop
|
40
|
-
* Ruby LLVM
|
40
|
+
* Ruby LLVM (currently building the source from Jeremy's next branch is required)
|
41
41
|
|
42
|
-
Everything except LLVM is installed when running `gem install citrus`
|
42
|
+
Everything except LLVM is installed when running `gem install citrus-compiler`
|
43
43
|
|
44
44
|
== Running
|
45
45
|
|
data/example/simple.ct
CHANGED
data/lib/citrus/compiler.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Citrus
|
2
|
+
class Array
|
3
|
+
|
4
|
+
attr_accessor :pointer
|
5
|
+
|
6
|
+
def self.create(values, builder)
|
7
|
+
ary = builder.alloca(LLVM::Array(LLVM::Type(values.first), values.size))
|
8
|
+
for index in 0...values.size
|
9
|
+
ptr = builder.gep(ary, [INT.from_i(0), INT.from_i(index)])
|
10
|
+
builder.store(values[index], ptr)
|
11
|
+
end
|
12
|
+
self.new(ary)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(pointer, props={})
|
16
|
+
@pointer = pointer
|
17
|
+
@length = props[:length]
|
18
|
+
end
|
19
|
+
|
20
|
+
def length
|
21
|
+
unless @length.nil?
|
22
|
+
return @length
|
23
|
+
else
|
24
|
+
return INT.from_i(LLVM::C.LLVMGetArrayLength(LLVM::Type(@pointer).element_type))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -18,12 +18,26 @@ module Citrus
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def array(values)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
return Array.create(values, @builder)
|
22
|
+
end
|
23
|
+
|
24
|
+
def range(first, last, full)
|
25
|
+
ival = INT.from_i(-1)
|
26
|
+
ary = @builder.alloca(LLVM::Array(INT, 0))
|
27
|
+
iteration = builder.alloca(INT)
|
28
|
+
builder.store(first, iteration)
|
29
|
+
index = builder.alloca(INT)
|
30
|
+
builder.store(INT.from_i(0), index)
|
31
|
+
self.preploop(:while)
|
32
|
+
self.while(self.compare(full ? :<= : :<, @builder.load(iteration), last)) do |gw|
|
33
|
+
ival = gw.builder.load(index)
|
34
|
+
val = gw.builder.load(iteration)
|
35
|
+
ptr = gw.builder.gep(ary, [INT.from_i(0), ival])
|
36
|
+
gw.builder.store(val, ptr)
|
37
|
+
gw.builder.store(gw.equate(:+, val, gw.number(1)), iteration)
|
38
|
+
gw.builder.store(gw.equate(:+, ival, gw.number(1)), index)
|
25
39
|
end
|
26
|
-
return ary
|
40
|
+
return Array.new(ary, :length => self.equate(:+, ival, self.number(1)))
|
27
41
|
end
|
28
42
|
|
29
43
|
def string(value)
|
@@ -102,7 +116,7 @@ module Citrus
|
|
102
116
|
end
|
103
117
|
|
104
118
|
def assign_index(name, index, value)
|
105
|
-
ary =
|
119
|
+
ary = self.load(name).pointer
|
106
120
|
ptr = @builder.gep(ary, [INT.from_i(0), index])
|
107
121
|
@builder.store(value, ptr)
|
108
122
|
end
|
@@ -163,11 +177,11 @@ module Citrus
|
|
163
177
|
end
|
164
178
|
|
165
179
|
def load_index(ary, index)
|
166
|
-
@builder.load(@builder.gep(ary, [INT.from_i(0), index]))
|
180
|
+
@builder.load(@builder.gep(ary.pointer, [INT.from_i(0), index]))
|
167
181
|
end
|
168
182
|
|
169
183
|
def function(name, args)
|
170
|
-
GlobalFunctions.add(name, args) { |g| yield g }
|
184
|
+
return GlobalFunctions.add(name, args) { |g| yield g }
|
171
185
|
end
|
172
186
|
|
173
187
|
def declare(name, args, ret, varargs = false)
|
@@ -177,7 +191,7 @@ module Citrus
|
|
177
191
|
end
|
178
192
|
|
179
193
|
def block
|
180
|
-
Block.new(@module, @function, self) { |g| yield g if block_given? }
|
194
|
+
return Block.new(@module, @function, self) { |g| yield g if block_given? }
|
181
195
|
end
|
182
196
|
|
183
197
|
def condition(cond, thenblock, elseblock, elsifs=[])
|
@@ -276,8 +290,7 @@ module Citrus
|
|
276
290
|
@builder.position_at_end(@basic_block)
|
277
291
|
self.resolve_conflict("for", generator, self)
|
278
292
|
@basic_block = self.block.bb
|
279
|
-
|
280
|
-
cond = self.compare(:<, self.load("for"), self.number(size))
|
293
|
+
cond = self.compare(:<, self.load("for"), indices.length)
|
281
294
|
@builder.cond(cond, generator.basic_block, @basic_block)
|
282
295
|
@builder.position_at_end(@basic_block)
|
283
296
|
end
|
@@ -2,15 +2,48 @@ module Citrus
|
|
2
2
|
class Variable
|
3
3
|
|
4
4
|
attr_reader :type
|
5
|
-
attr_reader :pointer
|
5
|
+
#attr_reader :pointer
|
6
6
|
|
7
7
|
def initialize(value, builder)
|
8
|
-
@
|
8
|
+
@value = nil
|
9
|
+
if value.is_a?(Citrus::Array)
|
10
|
+
@value = value
|
11
|
+
@type = LLVM::Type(value.pointer)
|
12
|
+
else
|
13
|
+
@type = LLVM::Type(value)
|
14
|
+
end
|
15
|
+
build_initialize(value, builder)
|
16
|
+
end
|
17
|
+
|
18
|
+
def assign(value, builder)
|
19
|
+
if value.is_a?(Citrus::Array)
|
20
|
+
@value = value
|
21
|
+
@type = LLVM::Type(value.pointer)
|
22
|
+
end
|
23
|
+
build_assign(value, builder)
|
24
|
+
end
|
25
|
+
|
26
|
+
def value(builder)
|
27
|
+
val = build_load(builder)
|
28
|
+
return @value.nil? ? val : @value
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def build_initialize(value, builder)
|
34
|
+
if value.is_a?(Citrus::Array)
|
35
|
+
@pointer = value.pointer
|
36
|
+
return
|
37
|
+
end
|
9
38
|
@pointer = builder.alloca(@type)
|
10
39
|
builder.store(value, @pointer)
|
11
40
|
end
|
12
41
|
|
13
|
-
def
|
42
|
+
def build_assign(value, builder)
|
43
|
+
if value.is_a?(Citrus::Array)
|
44
|
+
@pointer = value.pointer
|
45
|
+
return
|
46
|
+
end
|
14
47
|
type = LLVM::Type(value)
|
15
48
|
unless type == @type
|
16
49
|
@type = type
|
@@ -19,7 +52,7 @@ module Citrus
|
|
19
52
|
builder.store(value, @pointer)
|
20
53
|
end
|
21
54
|
|
22
|
-
def
|
55
|
+
def build_load(builder)
|
23
56
|
builder.load(@pointer)
|
24
57
|
end
|
25
58
|
|
@@ -30,17 +63,30 @@ module Citrus
|
|
30
63
|
def initialize(name, value, mod, builder)
|
31
64
|
@name = name
|
32
65
|
@module = mod
|
33
|
-
|
66
|
+
super(value, builder)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def build_initialize(value, builder)
|
34
72
|
@pointer = @module.globals.add(@type, @name)
|
35
|
-
|
73
|
+
if value.is_a?(Citrus::Array)
|
74
|
+
@pointer.initializer = value.pointer
|
75
|
+
else
|
76
|
+
@pointer.initializer = value
|
77
|
+
end
|
36
78
|
end
|
37
79
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
80
|
+
def build_assign(value, builder)
|
81
|
+
if value.is_a?(Citrus::Array)
|
82
|
+
builder.store(value.pointer, @pointer)
|
83
|
+
else
|
84
|
+
@type = LLVM::Type(value)
|
85
|
+
builder.store(value, @pointer)
|
86
|
+
end
|
41
87
|
end
|
42
88
|
|
43
|
-
def
|
89
|
+
def build_load(builder)
|
44
90
|
builder.load(@module.globals.named(@name))
|
45
91
|
end
|
46
92
|
|
data/lib/citrus/core.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
|
3
|
-
|
3
|
+
if File.directory?("#{File.dirname(__FILE__)}/../llvm-2.9") # Custom LLVM Loading (for me only)
|
4
4
|
require "llvm/load"
|
5
5
|
LLVM.load("#{File.dirname(__FILE__)}/../llvm-2.9")
|
6
|
-
|
6
|
+
end
|
7
7
|
|
8
8
|
require 'llvm/core'
|
9
9
|
require 'llvm/execution_engine'
|
data/lib/citrus/nodes.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: citrus-compiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 61
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 0.8.
|
9
|
+
- 1
|
10
|
+
version: 0.8.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Mac Malone
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-05-
|
18
|
+
date: 2011-05-03 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: treetop
|
@@ -33,22 +33,6 @@ dependencies:
|
|
33
33
|
version: 1.4.9
|
34
34
|
type: :runtime
|
35
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
36
|
description:
|
53
37
|
email:
|
54
38
|
executables: []
|
@@ -59,6 +43,7 @@ extra_rdoc_files:
|
|
59
43
|
- README.rdoc
|
60
44
|
- LICENSE
|
61
45
|
files:
|
46
|
+
- lib/citrus/compiler/array.rb
|
62
47
|
- lib/citrus/compiler/block.rb
|
63
48
|
- lib/citrus/compiler/function.rb
|
64
49
|
- lib/citrus/compiler/generator.rb
|