koi-reference-compiler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +76 -0
  4. data/Rakefile +42 -0
  5. data/VERSION +1 -0
  6. data/lib/koi-reference-compiler.rb +10 -0
  7. data/lib/koi-reference-compiler/exceptions.rb +2 -0
  8. data/lib/koi-reference-compiler/koi-reference-compiler.rb +23 -0
  9. data/lib/koi-reference-compiler/node_extensions/assignment/assignment.rb +12 -0
  10. data/lib/koi-reference-compiler/node_extensions/blocks/block.rb +13 -0
  11. data/lib/koi-reference-compiler/node_extensions/control_flow/if.rb +14 -0
  12. data/lib/koi-reference-compiler/node_extensions/control_flow/unless.rb +14 -0
  13. data/lib/koi-reference-compiler/node_extensions/expressions/additive_expression.rb +13 -0
  14. data/lib/koi-reference-compiler/node_extensions/expressions/comparative_expression.rb +13 -0
  15. data/lib/koi-reference-compiler/node_extensions/expressions/expression.rb +9 -0
  16. data/lib/koi-reference-compiler/node_extensions/expressions/multitive_expression.rb +13 -0
  17. data/lib/koi-reference-compiler/node_extensions/functions/function_call.rb +54 -0
  18. data/lib/koi-reference-compiler/node_extensions/functions/function_definition.rb +19 -0
  19. data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_access.rb +12 -0
  20. data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_accessor.rb +15 -0
  21. data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_accessor_list.rb +26 -0
  22. data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_assignment.rb +17 -0
  23. data/lib/koi-reference-compiler/node_extensions/hash_literals/hash_literal.rb +11 -0
  24. data/lib/koi-reference-compiler/node_extensions/hash_literals/key_value.rb +13 -0
  25. data/lib/koi-reference-compiler/node_extensions/hash_literals/key_value_list.rb +13 -0
  26. data/lib/koi-reference-compiler/node_extensions/identifiers/identifier.rb +31 -0
  27. data/lib/koi-reference-compiler/node_extensions/literals/false_literal.rb +9 -0
  28. data/lib/koi-reference-compiler/node_extensions/literals/float_literal.rb +9 -0
  29. data/lib/koi-reference-compiler/node_extensions/literals/integer_literal.rb +9 -0
  30. data/lib/koi-reference-compiler/node_extensions/literals/nil_literal.rb +9 -0
  31. data/lib/koi-reference-compiler/node_extensions/literals/string_literal.rb +9 -0
  32. data/lib/koi-reference-compiler/node_extensions/literals/true_literal.rb +9 -0
  33. data/lib/koi-reference-compiler/node_extensions/operators/addition_operator.rb +9 -0
  34. data/lib/koi-reference-compiler/node_extensions/operators/assignment_operator.rb +6 -0
  35. data/lib/koi-reference-compiler/node_extensions/operators/division_operator.rb +9 -0
  36. data/lib/koi-reference-compiler/node_extensions/operators/equality_operator.rb +9 -0
  37. data/lib/koi-reference-compiler/node_extensions/operators/great_than_operator.rb +9 -0
  38. data/lib/koi-reference-compiler/node_extensions/operators/inequality_operator.rb +9 -0
  39. data/lib/koi-reference-compiler/node_extensions/operators/less_than_operator.rb +9 -0
  40. data/lib/koi-reference-compiler/node_extensions/operators/multiplication_operator.rb +9 -0
  41. data/lib/koi-reference-compiler/node_extensions/operators/subtraction_operator.rb +9 -0
  42. data/lib/koi-reference-compiler/node_extensions/statements/statement.rb +9 -0
  43. data/lib/koi-reference-compiler/syntax_node.rb +18 -0
  44. data/lib/koi-reference-compiler/vm_opcodes.rb +70 -0
  45. data/test/compiler/unit/ast_hash_loader/ast_hash_loader_test.rb +46 -0
  46. data/test/compiler/unit/node_extensions/assignment/assignment_test.rb +22 -0
  47. data/test/compiler/unit/node_extensions/blocks/block_test.rb +37 -0
  48. data/test/compiler/unit/node_extensions/control_flow/if_test.rb +39 -0
  49. data/test/compiler/unit/node_extensions/control_flow/unless_test.rb +40 -0
  50. data/test/compiler/unit/node_extensions/expressions/additive_expression_test.rb +21 -0
  51. data/test/compiler/unit/node_extensions/expressions/comparative_expression_test.rb +21 -0
  52. data/test/compiler/unit/node_extensions/expressions/expression_test.rb +17 -0
  53. data/test/compiler/unit/node_extensions/expressions/multitive_expression_test.rb +21 -0
  54. data/test/compiler/unit/node_extensions/functions/function_call_test.rb +296 -0
  55. data/test/compiler/unit/node_extensions/functions/function_definition_test.rb +37 -0
  56. data/test/compiler/unit/node_extensions/hash_literals/hash_accessor_list_test.rb +83 -0
  57. data/test/compiler/unit/node_extensions/hash_literals/hash_accessor_test.rb +33 -0
  58. data/test/compiler/unit/node_extensions/hash_literals/hash_literal_test.rb +29 -0
  59. data/test/compiler/unit/node_extensions/hash_literals/key_value_list_test.rb +37 -0
  60. data/test/compiler/unit/node_extensions/hash_literals/key_value_test.rb +24 -0
  61. data/test/compiler/unit/node_extensions/identifiers/identifier_test.rb +103 -0
  62. data/test/compiler/unit/node_extensions/literals/false_literal_test.rb +13 -0
  63. data/test/compiler/unit/node_extensions/literals/float_literal_test.rb +13 -0
  64. data/test/compiler/unit/node_extensions/literals/integer_literal_test.rb +13 -0
  65. data/test/compiler/unit/node_extensions/literals/nil_literal_test.rb +13 -0
  66. data/test/compiler/unit/node_extensions/literals/string_literal_test.rb +13 -0
  67. data/test/compiler/unit/node_extensions/literals/true_literal_test.rb +13 -0
  68. data/test/compiler/unit/node_extensions/operators/addition_operator_test.rb +13 -0
  69. data/test/compiler/unit/node_extensions/operators/division_operator_test.rb +13 -0
  70. data/test/compiler/unit/node_extensions/operators/equality_operator_test.rb +13 -0
  71. data/test/compiler/unit/node_extensions/operators/greater_than_operator_test.rb +13 -0
  72. data/test/compiler/unit/node_extensions/operators/inequality_operator_test.rb +13 -0
  73. data/test/compiler/unit/node_extensions/operators/less_than_operator_test.rb +13 -0
  74. data/test/compiler/unit/node_extensions/operators/multiplication_operator_test.rb +13 -0
  75. data/test/compiler/unit/node_extensions/operators/subtraction_operator_test.rb +13 -0
  76. data/test/compiler/unit/node_extensions/statements/statement_test.rb +24 -0
  77. data/test/setup/test_unit_extensions.rb +21 -0
  78. data/test/test_helper.rb +10 -0
  79. metadata +177 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ *.gemspec
3
+ *.swp
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,2 @@
1
+ class CompileError < Exception
2
+ 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,12 @@
1
+ module KoiReferenceCompiler
2
+
3
+ class Assignment < SyntaxNode
4
+ def compile
5
+ bytecode = []
6
+ bytecode.concat( self.elements[2].compile )
7
+ bytecode.concat( self.elements[0].compile )
8
+ return bytecode
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,13 @@
1
+ module KoiReferenceCompiler
2
+
3
+ class Block < SyntaxNode
4
+ def compile
5
+ bytecode = []
6
+ self.elements.each do |element|
7
+ bytecode.concat( element.compile )
8
+ end
9
+ return bytecode
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ module KoiReferenceCompiler
2
+
3
+ class If < SyntaxNode
4
+ def compile
5
+ bytecode = self.elements[0].compile
6
+ block = self.elements[1].compile
7
+ bytecode.concat([
8
+ JUMP_UNLESS, block.length + 2
9
+ ])
10
+ bytecode.concat( block )
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,14 @@
1
+ module KoiReferenceCompiler
2
+
3
+ class Unless < SyntaxNode
4
+ def compile
5
+ bytecode = self.elements[0].compile
6
+ block = self.elements[1].compile
7
+ bytecode.concat([
8
+ JUMP_IF, block.length + 2
9
+ ])
10
+ bytecode.concat( block )
11
+ end
12
+ end
13
+
14
+ 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,9 @@
1
+ module KoiReferenceCompiler
2
+
3
+ class Expression < SyntaxNode
4
+ def compile
5
+ return self.elements.first.compile
6
+ end
7
+ end
8
+
9
+ 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,12 @@
1
+ module KoiReferenceCompiler
2
+
3
+ class HashAccess < SyntaxNode
4
+ def compile
5
+ bytecode = []
6
+ bytecode.concat( self.elements[0].compile )
7
+ bytecode.concat( self.elements[1].compile )
8
+ return bytecode
9
+ end
10
+ end
11
+
12
+ 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