koi-reference-compiler 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +76 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/koi-reference-compiler.rb +10 -0
- data/lib/koi-reference-compiler/exceptions.rb +2 -0
- data/lib/koi-reference-compiler/koi-reference-compiler.rb +23 -0
- data/lib/koi-reference-compiler/node_extensions/assignment/assignment.rb +12 -0
- data/lib/koi-reference-compiler/node_extensions/blocks/block.rb +13 -0
- data/lib/koi-reference-compiler/node_extensions/control_flow/if.rb +14 -0
- data/lib/koi-reference-compiler/node_extensions/control_flow/unless.rb +14 -0
- data/lib/koi-reference-compiler/node_extensions/expressions/additive_expression.rb +13 -0
- data/lib/koi-reference-compiler/node_extensions/expressions/comparative_expression.rb +13 -0
- data/lib/koi-reference-compiler/node_extensions/expressions/expression.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/expressions/multitive_expression.rb +13 -0
- data/lib/koi-reference-compiler/node_extensions/functions/function_call.rb +54 -0
- data/lib/koi-reference-compiler/node_extensions/functions/function_definition.rb +19 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_access.rb +12 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_accessor.rb +15 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_accessor_list.rb +26 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_assignment.rb +17 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_literal.rb +11 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/key_value.rb +13 -0
- data/lib/koi-reference-compiler/node_extensions/hash_literals/key_value_list.rb +13 -0
- data/lib/koi-reference-compiler/node_extensions/identifiers/identifier.rb +31 -0
- data/lib/koi-reference-compiler/node_extensions/literals/false_literal.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/literals/float_literal.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/literals/integer_literal.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/literals/nil_literal.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/literals/string_literal.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/literals/true_literal.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/addition_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/assignment_operator.rb +6 -0
- data/lib/koi-reference-compiler/node_extensions/operators/division_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/equality_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/great_than_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/inequality_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/less_than_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/multiplication_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/operators/subtraction_operator.rb +9 -0
- data/lib/koi-reference-compiler/node_extensions/statements/statement.rb +9 -0
- data/lib/koi-reference-compiler/syntax_node.rb +18 -0
- data/lib/koi-reference-compiler/vm_opcodes.rb +70 -0
- data/test/compiler/unit/ast_hash_loader/ast_hash_loader_test.rb +46 -0
- data/test/compiler/unit/node_extensions/assignment/assignment_test.rb +22 -0
- data/test/compiler/unit/node_extensions/blocks/block_test.rb +37 -0
- data/test/compiler/unit/node_extensions/control_flow/if_test.rb +39 -0
- data/test/compiler/unit/node_extensions/control_flow/unless_test.rb +40 -0
- data/test/compiler/unit/node_extensions/expressions/additive_expression_test.rb +21 -0
- data/test/compiler/unit/node_extensions/expressions/comparative_expression_test.rb +21 -0
- data/test/compiler/unit/node_extensions/expressions/expression_test.rb +17 -0
- data/test/compiler/unit/node_extensions/expressions/multitive_expression_test.rb +21 -0
- data/test/compiler/unit/node_extensions/functions/function_call_test.rb +296 -0
- data/test/compiler/unit/node_extensions/functions/function_definition_test.rb +37 -0
- data/test/compiler/unit/node_extensions/hash_literals/hash_accessor_list_test.rb +83 -0
- data/test/compiler/unit/node_extensions/hash_literals/hash_accessor_test.rb +33 -0
- data/test/compiler/unit/node_extensions/hash_literals/hash_literal_test.rb +29 -0
- data/test/compiler/unit/node_extensions/hash_literals/key_value_list_test.rb +37 -0
- data/test/compiler/unit/node_extensions/hash_literals/key_value_test.rb +24 -0
- data/test/compiler/unit/node_extensions/identifiers/identifier_test.rb +103 -0
- data/test/compiler/unit/node_extensions/literals/false_literal_test.rb +13 -0
- data/test/compiler/unit/node_extensions/literals/float_literal_test.rb +13 -0
- data/test/compiler/unit/node_extensions/literals/integer_literal_test.rb +13 -0
- data/test/compiler/unit/node_extensions/literals/nil_literal_test.rb +13 -0
- data/test/compiler/unit/node_extensions/literals/string_literal_test.rb +13 -0
- data/test/compiler/unit/node_extensions/literals/true_literal_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/addition_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/division_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/equality_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/greater_than_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/inequality_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/less_than_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/multiplication_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/operators/subtraction_operator_test.rb +13 -0
- data/test/compiler/unit/node_extensions/statements/statement_test.rb +24 -0
- data/test/setup/test_unit_extensions.rb +21 -0
- data/test/test_helper.rb +10 -0
- metadata +177 -0
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Aaron Gough (http://thingsaaronmade.com/)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
= koi-reference-compiler
|
2
|
+
|
3
|
+
This is the reference compiler implementation for the programming language {Koi}[http://github.com/aarongough/koi]. This is a very simple compiler that does no optimization, and because of this remains very easy to understand. The compiler is implemented using the {delegation pattern}[http://en.wikipedia.org/wiki/Delegation_pattern] in that a tree representing the program is built up out of subclasses of the SyntaxNode class, each of which knows how to compile it's own specific instruction and nothing more.
|
4
|
+
|
5
|
+
=== Example
|
6
|
+
|
7
|
+
Take, for example, a very simple (and incorrect!) program that just pushes an integer onto the stack. The {Abstract Syntax Tree (AST)}[http://en.wikipedia.org/wiki/Abstract_syntax_tree] for such a program might look like this:
|
8
|
+
|
9
|
+
<IntegerLiteral "1">
|
10
|
+
|
11
|
+
In order to compile this program we might create an instance method on the IntegerLiteral class that looks like this:
|
12
|
+
|
13
|
+
class IntegerLiteral < SyntaxNode
|
14
|
+
def compile
|
15
|
+
return [PUSH_INT, self.text_value.to_i]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
This method simply produces the opcode PUSH_INT and provides the integer value of the text_value of the syntax node, in this case "1". This is obviously a minimal case, but it demonstrates the principal in a straight-forward way.
|
20
|
+
|
21
|
+
=== Abstract Syntax Tree Format
|
22
|
+
|
23
|
+
The compiler expects the AST to be passed to it in the form of a nested hash which represents the program structure. The AST hash for the program:
|
24
|
+
|
25
|
+
test = 1
|
26
|
+
|
27
|
+
would look like so:
|
28
|
+
|
29
|
+
{
|
30
|
+
:name => "Statement",
|
31
|
+
:text_value => "test = 1",
|
32
|
+
:offset => 0,
|
33
|
+
:elements => [
|
34
|
+
{
|
35
|
+
:name => "AssignmentOperator",
|
36
|
+
:text_value => "=",
|
37
|
+
:offset => 5,
|
38
|
+
:elements => nil
|
39
|
+
},
|
40
|
+
{
|
41
|
+
:name => "Expression",
|
42
|
+
:text_value => "1",
|
43
|
+
:offset => 7,
|
44
|
+
:elements => [
|
45
|
+
{
|
46
|
+
:name => "IntegerLiteral",
|
47
|
+
:text_value => "1",
|
48
|
+
:offset => 7,
|
49
|
+
:elements => nil
|
50
|
+
}
|
51
|
+
]
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}
|
55
|
+
|
56
|
+
|
57
|
+
=== Installation
|
58
|
+
|
59
|
+
This compiler is normally installed as part of Koi's default toolchain. However if you would like to install it on it's own you can do so by installing the gem like so:
|
60
|
+
|
61
|
+
gem install koi-reference-compiler
|
62
|
+
|
63
|
+
=== Usage
|
64
|
+
|
65
|
+
require 'rubygems'
|
66
|
+
require 'koi-reference-compiler'
|
67
|
+
|
68
|
+
include KoiReferenceCompiler
|
69
|
+
|
70
|
+
bytecode = Compiler.compile( hash_representing_abstract_syntax_tree )
|
71
|
+
|
72
|
+
=== Author & Credits
|
73
|
+
|
74
|
+
Author:: {Aaron Gough}[mailto:aaron@aarongough.com]
|
75
|
+
|
76
|
+
Copyright (c) 2010 {Aaron Gough}[http://thingsaaronmade.com/] ({thingsaaronmade.com}[http://thingsaaronmade.com/]), released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'jeweler'
|
10
|
+
Jeweler::Tasks.new do |gemspec|
|
11
|
+
gemspec.name = "koi-reference-compiler"
|
12
|
+
gemspec.summary = "The reference compiler for the Koi language."
|
13
|
+
gemspec.description = "The reference compiler for the Koi language."
|
14
|
+
gemspec.email = "aaron@aarongough.com"
|
15
|
+
gemspec.homepage = "http://github.com/aarongough/min-koi"
|
16
|
+
gemspec.authors = ["Aaron Gough"]
|
17
|
+
gemspec.rdoc_options << '--line-numbers' << '--inline-source'
|
18
|
+
gemspec.extra_rdoc_files = ['README.rdoc', 'MIT-LICENSE']
|
19
|
+
end
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
desc 'Test koi-reference-compiler.'
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib/*.rb'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = true
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
desc 'Generate documentation for koi-reference-compiler.'
|
35
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
36
|
+
rdoc.rdoc_dir = 'rdoc'
|
37
|
+
rdoc.title = 'koi-reference-compiler'
|
38
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
39
|
+
rdoc.rdoc_files.include('README.rdoc')
|
40
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
41
|
+
rdoc.rdoc_files.include('app/**/*.rb')
|
42
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'koi-reference-compiler/syntax_node.rb'))
|
4
|
+
|
5
|
+
require_files = []
|
6
|
+
require_files.concat Dir[File.join(File.dirname(__FILE__), 'koi-reference-compiler', '**', '*.rb')]
|
7
|
+
|
8
|
+
require_files.each do |file|
|
9
|
+
require File.expand_path(file)
|
10
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class Compiler
|
4
|
+
|
5
|
+
def self.compile(ast_hash)
|
6
|
+
tree = self.load_ast_hash(ast_hash)
|
7
|
+
bytecode = tree.compile
|
8
|
+
return bytecode
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.load_ast_hash(hash)
|
12
|
+
unless(hash[:elements].nil?)
|
13
|
+
elements = hash[:elements].map {|x| self.load_ast_hash(x) }
|
14
|
+
else
|
15
|
+
elements = nil
|
16
|
+
end
|
17
|
+
tree = Kernel.const_get("KoiReferenceCompiler").const_get(hash[:name]).new(hash[:text_value], hash[:offset], elements)
|
18
|
+
return tree
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class AdditiveExpression < SyntaxNode
|
4
|
+
def compile
|
5
|
+
bytecode = []
|
6
|
+
bytecode.concat(self.elements[0].compile)
|
7
|
+
bytecode.concat(self.elements[2].compile)
|
8
|
+
bytecode.concat(self.elements[1].compile)
|
9
|
+
return bytecode
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class ComparativeExpression < SyntaxNode
|
4
|
+
def compile
|
5
|
+
bytecode = []
|
6
|
+
bytecode.concat(self.elements[0].compile)
|
7
|
+
bytecode.concat(self.elements[2].compile)
|
8
|
+
bytecode.concat(self.elements[1].compile)
|
9
|
+
return bytecode
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class MultitiveExpression < SyntaxNode
|
4
|
+
def compile
|
5
|
+
bytecode = []
|
6
|
+
bytecode.concat(self.elements[0].compile)
|
7
|
+
bytecode.concat(self.elements[2].compile)
|
8
|
+
bytecode.concat(self.elements[1].compile)
|
9
|
+
return bytecode
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class FunctionCall < SyntaxNode
|
4
|
+
def compile
|
5
|
+
number_of_arguments = self.elements.length - 1
|
6
|
+
|
7
|
+
bytecode = []
|
8
|
+
bytecode.concat( self.elements[2].compile ) if(number_of_arguments > 1)
|
9
|
+
bytecode.concat( self.elements[1].compile ) if(number_of_arguments > 0)
|
10
|
+
|
11
|
+
function_name = self.elements.first.text_value
|
12
|
+
|
13
|
+
if( function_name == "print")
|
14
|
+
raise_error unless(number_of_arguments == 1)
|
15
|
+
bytecode << PRINT
|
16
|
+
|
17
|
+
elsif( function_name == "gets")
|
18
|
+
raise_error unless(number_of_arguments == 0)
|
19
|
+
bytecode << GETS
|
20
|
+
|
21
|
+
elsif( function_name == "call")
|
22
|
+
raise_error unless(number_of_arguments == 2)
|
23
|
+
bytecode << CALL
|
24
|
+
|
25
|
+
elsif( function_name == "tailcall")
|
26
|
+
raise_error unless(number_of_arguments == 1 || number_of_arguments == 2)
|
27
|
+
bytecode << TAILCALL
|
28
|
+
|
29
|
+
elsif( function_name == "return")
|
30
|
+
raise_error unless(number_of_arguments == 0 || number_of_arguments == 1)
|
31
|
+
bytecode << RETURN
|
32
|
+
|
33
|
+
elsif( function_name == "to_string")
|
34
|
+
raise_error unless(number_of_arguments == 1)
|
35
|
+
bytecode << TO_STRING
|
36
|
+
|
37
|
+
elsif( function_name == "type_of")
|
38
|
+
raise_error unless(number_of_arguments == 1)
|
39
|
+
bytecode << TYPEOF
|
40
|
+
|
41
|
+
else
|
42
|
+
raise CompileError, "Unknown function: #{function_name} at offset: #{self.elements.first.offset}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def raise_error()
|
49
|
+
function_name = self.elements.first.text_value
|
50
|
+
raise CompileError, "Wrong number of arguments for: #{function_name}() at offset: #{self.elements.first.offset}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class FunctionDefinition < SyntaxNode
|
4
|
+
def compile
|
5
|
+
function_id = self.offset
|
6
|
+
bytecode = [
|
7
|
+
PUSH_FUNCTION, function_id
|
8
|
+
]
|
9
|
+
bytecode.concat( self.elements[0].compile )
|
10
|
+
bytecode.concat( self.elements[1].compile )
|
11
|
+
bytecode.concat([
|
12
|
+
RETURN,
|
13
|
+
END_FUNCTION, 0,
|
14
|
+
END_FUNCTION, function_id
|
15
|
+
])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class HashAccessor < SyntaxNode
|
4
|
+
def compile_get
|
5
|
+
bytecode = self.elements.first.compile
|
6
|
+
bytecode << GET_KEY
|
7
|
+
end
|
8
|
+
|
9
|
+
def compile_set
|
10
|
+
bytecode = self.elements.first.compile
|
11
|
+
bytecode << SET_KEY
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module KoiReferenceCompiler
|
2
|
+
|
3
|
+
class HashAccessorList < SyntaxNode
|
4
|
+
def compile
|
5
|
+
bytecode = []
|
6
|
+
if(self.parent.is_a?(HashAssignment))
|
7
|
+
self.elements.each_index do |x|
|
8
|
+
if(x == self.elements.length - 1)
|
9
|
+
bytecode.concat( self.elements[x].compile_set )
|
10
|
+
else
|
11
|
+
bytecode.concat( self.elements[x].compile_get )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
bytecode << POP
|
15
|
+
elsif(self.parent.is_a?(HashAccess))
|
16
|
+
self.elements.each do |x|
|
17
|
+
bytecode.concat( x.compile_get )
|
18
|
+
end
|
19
|
+
else
|
20
|
+
raise CompileError, "Unkown parent for HashAccessorList: #{self.parent.class}"
|
21
|
+
end
|
22
|
+
return bytecode
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|