citrus-compiler 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|