koi-vm-ruby 0.0.4

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.
Files changed (111) hide show
  1. data/.gitignore +2 -0
  2. data/README.rdoc +64 -0
  3. data/Rakefile +42 -0
  4. data/VERSION +1 -0
  5. data/architecture.rdoc +112 -0
  6. data/examples/blastoff.rb +20 -0
  7. data/examples/hello_world.rb +8 -0
  8. data/koi-vm.gemspec +198 -0
  9. data/lib/.gitkeep +0 -0
  10. data/lib/koi-vm-ruby.rb +10 -0
  11. data/lib/koi-vm-ruby/accessors.rb +63 -0
  12. data/lib/koi-vm-ruby/core.rb +48 -0
  13. data/lib/koi-vm-ruby/exceptions.rb +8 -0
  14. data/lib/koi-vm-ruby/helpers.rb +5 -0
  15. data/lib/koi-vm-ruby/opcodes/_opcode_constants.rb +68 -0
  16. data/lib/koi-vm-ruby/opcodes/_value_constants.rb +11 -0
  17. data/lib/koi-vm-ruby/opcodes/comparative_operations/equal.rb +11 -0
  18. data/lib/koi-vm-ruby/opcodes/comparative_operations/greater_than.rb +14 -0
  19. data/lib/koi-vm-ruby/opcodes/comparative_operations/invert.rb +12 -0
  20. data/lib/koi-vm-ruby/opcodes/comparative_operations/less_than.rb +14 -0
  21. data/lib/koi-vm-ruby/opcodes/control_operations/exit.rb +10 -0
  22. data/lib/koi-vm-ruby/opcodes/control_operations/no_op.rb +9 -0
  23. data/lib/koi-vm-ruby/opcodes/flow_control_operations/jump.rb +11 -0
  24. data/lib/koi-vm-ruby/opcodes/flow_control_operations/jump_if.rb +16 -0
  25. data/lib/koi-vm-ruby/opcodes/flow_control_operations/jump_unless.rb +16 -0
  26. data/lib/koi-vm-ruby/opcodes/function_operations/call.rb +13 -0
  27. data/lib/koi-vm-ruby/opcodes/function_operations/push_function.rb +19 -0
  28. data/lib/koi-vm-ruby/opcodes/function_operations/return.rb +11 -0
  29. data/lib/koi-vm-ruby/opcodes/function_operations/tailcall.rb +12 -0
  30. data/lib/koi-vm-ruby/opcodes/hash_operations/get_key.rb +14 -0
  31. data/lib/koi-vm-ruby/opcodes/hash_operations/has_key.rb +18 -0
  32. data/lib/koi-vm-ruby/opcodes/hash_operations/length.rb +19 -0
  33. data/lib/koi-vm-ruby/opcodes/hash_operations/num_pairs.rb +12 -0
  34. data/lib/koi-vm-ruby/opcodes/hash_operations/pairs.rb +22 -0
  35. data/lib/koi-vm-ruby/opcodes/hash_operations/push_hash.rb +10 -0
  36. data/lib/koi-vm-ruby/opcodes/hash_operations/set_key.rb +14 -0
  37. data/lib/koi-vm-ruby/opcodes/io_operations/gets.rb +10 -0
  38. data/lib/koi-vm-ruby/opcodes/io_operations/print.rb +13 -0
  39. data/lib/koi-vm-ruby/opcodes/math_operations/add.rb +14 -0
  40. data/lib/koi-vm-ruby/opcodes/math_operations/divide.rb +14 -0
  41. data/lib/koi-vm-ruby/opcodes/math_operations/multiply.rb +14 -0
  42. data/lib/koi-vm-ruby/opcodes/math_operations/subtract.rb +14 -0
  43. data/lib/koi-vm-ruby/opcodes/push_operations/push_bool.rb +12 -0
  44. data/lib/koi-vm-ruby/opcodes/push_operations/push_float.rb +12 -0
  45. data/lib/koi-vm-ruby/opcodes/push_operations/push_int.rb +12 -0
  46. data/lib/koi-vm-ruby/opcodes/push_operations/push_nil.rb +10 -0
  47. data/lib/koi-vm-ruby/opcodes/push_operations/push_string.rb +12 -0
  48. data/lib/koi-vm-ruby/opcodes/stack_operations/dup.rb +11 -0
  49. data/lib/koi-vm-ruby/opcodes/stack_operations/pop.rb +11 -0
  50. data/lib/koi-vm-ruby/opcodes/stack_operations/stksize.rb +10 -0
  51. data/lib/koi-vm-ruby/opcodes/stack_operations/swap.rb +11 -0
  52. data/lib/koi-vm-ruby/opcodes/stack_operations/top.rb +13 -0
  53. data/lib/koi-vm-ruby/opcodes/stack_operations/typeof.rb +18 -0
  54. data/lib/koi-vm-ruby/opcodes/string_operations/concat.rb +12 -0
  55. data/lib/koi-vm-ruby/opcodes/string_operations/strlen.rb +12 -0
  56. data/lib/koi-vm-ruby/opcodes/string_operations/to_string.rb +16 -0
  57. data/lib/koi-vm-ruby/opcodes/variable_operations/get_global.rb +10 -0
  58. data/lib/koi-vm-ruby/opcodes/variable_operations/get_local.rb +10 -0
  59. data/lib/koi-vm-ruby/opcodes/variable_operations/set_global.rb +11 -0
  60. data/lib/koi-vm-ruby/opcodes/variable_operations/set_local.rb +11 -0
  61. data/test/.gitkeep +0 -0
  62. data/test/performance/.gitignore +1 -0
  63. data/test/performance/simple_benchmark.rb +87 -0
  64. data/test/setup/test_unit_extensions.rb +21 -0
  65. data/test/test_helper.rb +13 -0
  66. data/test/unit/opcodes/comparative_operations/equal_test.rb +116 -0
  67. data/test/unit/opcodes/comparative_operations/greater_than_test.rb +53 -0
  68. data/test/unit/opcodes/comparative_operations/invert_test.rb +44 -0
  69. data/test/unit/opcodes/comparative_operations/less_than_test.rb +53 -0
  70. data/test/unit/opcodes/control_operations/exit_test.rb +20 -0
  71. data/test/unit/opcodes/control_operations/no_op_test.rb +17 -0
  72. data/test/unit/opcodes/flow_control_operations/jump_if_test.rb +50 -0
  73. data/test/unit/opcodes/flow_control_operations/jump_test.rb +36 -0
  74. data/test/unit/opcodes/flow_control_operations/jump_unless_test.rb +50 -0
  75. data/test/unit/opcodes/function_operations/call_test.rb +39 -0
  76. data/test/unit/opcodes/function_operations/push_function_test.rb +41 -0
  77. data/test/unit/opcodes/function_operations/return_test.rb +31 -0
  78. data/test/unit/opcodes/function_operations/tailcall_test.rb +41 -0
  79. data/test/unit/opcodes/hash_operations/get_key_test.rb +56 -0
  80. data/test/unit/opcodes/hash_operations/has_key_test.rb +56 -0
  81. data/test/unit/opcodes/hash_operations/length_test.rb +46 -0
  82. data/test/unit/opcodes/hash_operations/num_pairs_test.rb +46 -0
  83. data/test/unit/opcodes/hash_operations/pairs_test.rb +67 -0
  84. data/test/unit/opcodes/hash_operations/push_hash_test.rb +16 -0
  85. data/test/unit/opcodes/hash_operations/set_key_test.rb +56 -0
  86. data/test/unit/opcodes/io_operations/gets_test.rb +38 -0
  87. data/test/unit/opcodes/io_operations/print_test.rb +42 -0
  88. data/test/unit/opcodes/math_operations/add_test.rb +62 -0
  89. data/test/unit/opcodes/math_operations/divide_test.rb +62 -0
  90. data/test/unit/opcodes/math_operations/multiply_test.rb +62 -0
  91. data/test/unit/opcodes/math_operations/subtract_test.rb +62 -0
  92. data/test/unit/opcodes/push_operations/push_bool_test.rb +36 -0
  93. data/test/unit/opcodes/push_operations/push_float_test.rb +27 -0
  94. data/test/unit/opcodes/push_operations/push_int_test.rb +27 -0
  95. data/test/unit/opcodes/push_operations/push_nil_test.rb +16 -0
  96. data/test/unit/opcodes/push_operations/push_string_test.rb +27 -0
  97. data/test/unit/opcodes/stack_operations/dup_test.rb +25 -0
  98. data/test/unit/opcodes/stack_operations/pop_test.rb +28 -0
  99. data/test/unit/opcodes/stack_operations/stksize_test.rb +20 -0
  100. data/test/unit/opcodes/stack_operations/swap_test.rb +36 -0
  101. data/test/unit/opcodes/stack_operations/top_test.rb +45 -0
  102. data/test/unit/opcodes/stack_operations/typeof_test.rb +79 -0
  103. data/test/unit/opcodes/string_operations/concat_test.rb +65 -0
  104. data/test/unit/opcodes/string_operations/strlen_test.rb +44 -0
  105. data/test/unit/opcodes/string_operations/to_string_test.rb +70 -0
  106. data/test/unit/opcodes/variable_operations/get_global_test.rb +18 -0
  107. data/test/unit/opcodes/variable_operations/get_local_test.rb +18 -0
  108. data/test/unit/opcodes/variable_operations/set_global_test.rb +27 -0
  109. data/test/unit/opcodes/variable_operations/set_local_test.rb +27 -0
  110. data/test/unit/vm_initialization_test.rb +86 -0
  111. metadata +224 -0
File without changes
@@ -0,0 +1,10 @@
1
+ require_files = []
2
+
3
+ require_files << File.join(File.dirname(__FILE__), "koi-vm-ruby", "core.rb")
4
+ require_files << File.join(File.dirname(__FILE__), "koi-vm-ruby", "helpers.rb")
5
+ require_files << File.join(File.dirname(__FILE__), "koi-vm-ruby", "exceptions.rb")
6
+ require_files.concat Dir[File.join(File.dirname(__FILE__), 'koi-vm-ruby', '**', '*.rb')]
7
+
8
+ require_files.each do |file|
9
+ require File.expand_path(file)
10
+ end
@@ -0,0 +1,63 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ def data_stack
5
+ return @state[:fiber][:data_stack]
6
+ end
7
+
8
+ def data_stack=(stack)
9
+ @state[:fiber][:data_stack] = stack
10
+ end
11
+
12
+ def return_stack
13
+ return @state[:fiber][:return_stack]
14
+ end
15
+
16
+ def return_stack=(stack)
17
+ @state[:fiber][:return_stack] = stack
18
+ end
19
+
20
+ def instruction_pointer
21
+ return @state[:fiber][:instruction_pointer]
22
+ end
23
+
24
+ def instruction_pointer=(ip)
25
+ @state[:fiber][:instruction_pointer] = ip
26
+ end
27
+
28
+ def opcodes
29
+ return @state[:opcodes]
30
+ end
31
+
32
+ def locals
33
+ return @state[:fiber][:locals][@state[:fiber][:level]]
34
+ end
35
+
36
+ def globals
37
+ return @state[:globals]
38
+ end
39
+
40
+ def increment_scope
41
+ @state[:fiber][:level] += 1
42
+ @state[:fiber][:locals][@state[:fiber][:level]] = @state[:fiber][:locals][@state[:fiber][:level] - 1].dup
43
+ end
44
+
45
+ def decrement_scope
46
+ @state[:fiber][:locals].delete_at(@state[:fiber][:level])
47
+ @state[:fiber][:level] -= 1
48
+ end
49
+
50
+ def reload_scope
51
+ @state[:fiber][:locals][@state[:fiber][:level]] = {}
52
+ end
53
+
54
+ def level
55
+ return @state[:fiber][:level]
56
+ end
57
+
58
+ def level=(level)
59
+ @state[:fiber][:level] = level
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,48 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction = []
5
+
6
+ attr_accessor :state, :files
7
+
8
+ def initialize(state = {}, opcodes = [])
9
+ default_state = {
10
+ :opcodes => opcodes,
11
+ :globals => {},
12
+ :fiber => {
13
+ :data_stack => [],
14
+ :return_stack => [],
15
+ :locals => [{}],
16
+ :instruction_pointer => 0,
17
+ :level => 0
18
+ }
19
+ }
20
+ @state = default_state.merge(state)
21
+ @files = {}
22
+ raise ArgumentError, "state[:fibers][x] must be a hash" unless(@state[:fiber].is_a?(Hash))
23
+ raise ArgumentError, "state[:fibers][x][:stack] must be an Array" unless(@state[:fiber][:data_stack].is_a?(Array))
24
+ raise ArgumentError, "state[:fibers][x][:stack] must be an Array" unless(@state[:fiber][:return_stack].is_a?(Array))
25
+ raise ArgumentError, "state[:fibers][x][:instruction_pointer] must be an Integer" unless(@state[:fiber][:instruction_pointer].is_a?(Integer))
26
+ raise ArgumentError, "state[:fibers][x][:level] must be an Integer" unless(@state[:fiber][:level].is_a?(Integer))
27
+ raise ArgumentError, "state[:globals] must be a hash" unless(@state[:globals].is_a?(Hash))
28
+ raise ArgumentError, "state[:opcodes] must be an array" unless(@state[:opcodes].is_a?(Array))
29
+ end
30
+
31
+ def run(opcodes = [])
32
+ @state[:opcodes].concat(opcodes)
33
+ opcode_size = @state[:opcodes].size
34
+ while (@state[:fiber][:instruction_pointer] < opcode_size)
35
+ begin
36
+ break if(@@instruction[@state[:opcodes][@state[:fiber][:instruction_pointer]]].call(self) == true)
37
+ rescue NoMethodError
38
+ @state.delete(:opcodes)
39
+ puts "\n\n" + @state.inspect + "\n\n" unless(defined?($test) && $test == true)
40
+ raise
41
+ end
42
+ end
43
+
44
+ return @state
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,8 @@
1
+ class StackError < RuntimeError
2
+ end
3
+
4
+ class ReturnStackError < RuntimeError
5
+ end
6
+
7
+ class OperandError < RuntimeError
8
+ end
@@ -0,0 +1,5 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ end
5
+ end
@@ -0,0 +1,68 @@
1
+ module KoiVMRuby
2
+ # 0
3
+ PUSH_NIL = 0
4
+ PUSH_BOOL = 1
5
+ PUSH_INT = 2
6
+ PUSH_FLOAT = 3
7
+ PUSH_STRING = 4
8
+
9
+ # 20
10
+ JUMP = 20
11
+ JUMP_IF = 21
12
+ JUMP_UNLESS = 22
13
+
14
+ # 40
15
+ POP = 40
16
+ SWAP = 41
17
+ DUP = 42
18
+ STKSIZE = 43
19
+ TYPEOF = 44
20
+ TOP = 45
21
+
22
+ # 60
23
+ ADD = 60
24
+ SUBTRACT = 61
25
+ MULTIPLY = 62
26
+ DIVIDE = 63
27
+
28
+ # 80
29
+ CONCAT = 80
30
+ STRLEN = 81
31
+ TO_STRING = 82
32
+
33
+ # 100
34
+ EQUAL = 100
35
+ LESS_THAN = 101
36
+ GREATER_THAN = 102
37
+ INVERT = 103
38
+
39
+ # 120
40
+ PRINT = 120
41
+ GETS = 121
42
+
43
+ # 140
44
+ SET_LOCAL = 140
45
+ GET_LOCAL = 141
46
+ SET_GLOBAL = 142
47
+ GET_GLOBAL = 143
48
+
49
+ # 160
50
+ PUSH_FUNCTION = 160
51
+ END_FUNCTION = 161
52
+ CALL = 162
53
+ RETURN = 163
54
+ TAILCALL = 164
55
+
56
+ # 180
57
+ PUSH_HASH = 180
58
+ SET_KEY = 181
59
+ GET_KEY = 182
60
+ HAS_KEY = 183
61
+ LENGTH = 184
62
+ NUM_PAIRS = 185
63
+ PAIRS = 186
64
+
65
+ # 240
66
+ NO_OP = 240
67
+ EXIT = 255
68
+ end
@@ -0,0 +1,11 @@
1
+ module KoiVMRuby
2
+ NIL_ = 0
3
+ BOOL_ = 1
4
+ INTEGER_ = 2
5
+ FLOAT_ = 3
6
+ STRING_ = 4
7
+ HASH_ = 5
8
+
9
+
10
+ FUNCTION_ = 16
11
+ end
@@ -0,0 +1,11 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[EQUAL] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least two items on the data stack" unless(vm.data_stack.length > 1)
6
+ vm.data_stack.push([BOOL_, vm.data_stack.pop == vm.data_stack.pop])
7
+ vm.instruction_pointer = vm.instruction_pointer + 1
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[GREATER_THAN] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least two items on the data stack" unless(vm.data_stack.length > 1)
6
+ result = [BOOL_, vm.data_stack[-2][1] > vm.data_stack[-1][1]]
7
+ vm.data_stack.pop
8
+ vm.data_stack.pop
9
+ vm.data_stack.push(result)
10
+ vm.instruction_pointer = vm.instruction_pointer + 1
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[INVERT] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least one item on the data stack" unless(vm.data_stack.length > 0)
6
+ raise StackError, "Expecting boolean value" unless(vm.data_stack[-1][0] == BOOL_)
7
+ vm.data_stack[-1][1] = !vm.data_stack[-1][1]
8
+ vm.instruction_pointer = vm.instruction_pointer + 1
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[LESS_THAN] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least two items on the data stack" unless(vm.data_stack.length > 1)
6
+ result = [BOOL_, vm.data_stack[-2][1] < vm.data_stack[-1][1]]
7
+ vm.data_stack.pop
8
+ vm.data_stack.pop
9
+ vm.data_stack.push(result)
10
+ vm.instruction_pointer = vm.instruction_pointer + 1
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[EXIT] = Proc.new() do |vm|
5
+ vm.instruction_pointer = vm.instruction_pointer + 1
6
+ true
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[NO_OP] = Proc.new() do |vm|
5
+ vm.instruction_pointer = vm.instruction_pointer + 1
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[JUMP] = Proc.new() do |vm|
5
+ operand = vm.opcodes[vm.instruction_pointer + 1]
6
+ raise OperandError, "Expecting integer value but got #{operand.inspect}" unless(operand.is_a?(Integer))
7
+ vm.instruction_pointer = vm.instruction_pointer + operand
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[JUMP_IF] = Proc.new() do |vm|
5
+ operand = vm.opcodes[vm.instruction_pointer + 1]
6
+ raise OperandError, "Expecting integer value but got #{operand.inspect}" unless(operand.is_a?(Integer))
7
+ raise StackError, "Expecting at least one item on the data stack" unless(vm.data_stack.length > 0)
8
+ if(vm.data_stack.pop[1])
9
+ vm.instruction_pointer = vm.instruction_pointer + operand
10
+ else
11
+ vm.instruction_pointer = vm.instruction_pointer + 2
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[JUMP_UNLESS] = Proc.new() do |vm|
5
+ operand = vm.opcodes[vm.instruction_pointer + 1]
6
+ raise OperandError, "Expecting integer value but got #{operand.inspect}" unless(operand.is_a?(Integer))
7
+ raise StackError, "Expecting at least one item on the data stack" unless(vm.data_stack.length > 0)
8
+ unless(vm.data_stack.pop[1])
9
+ vm.instruction_pointer = vm.instruction_pointer + operand
10
+ else
11
+ vm.instruction_pointer = vm.instruction_pointer + 2
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[CALL] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least one item on the data stack" unless(vm.data_stack.length > 0)
6
+ raise StackError, "Expecting function" unless(vm.data_stack[-1][0] == FUNCTION_)
7
+ vm.return_stack.push( vm.instruction_pointer + 1 )
8
+ vm.instruction_pointer = vm.data_stack.pop[1]
9
+ vm.increment_scope
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[PUSH_FUNCTION] = Proc.new() do |vm|
5
+ vm.data_stack.push([FUNCTION_, vm.instruction_pointer + 2])
6
+ function_id = vm.opcodes[vm.instruction_pointer + 1]
7
+ while(true)
8
+ vm.instruction_pointer = vm.opcodes.slice(vm.instruction_pointer, vm.opcodes.length).index(END_FUNCTION) + vm.instruction_pointer
9
+ if(vm.opcodes.slice(vm.instruction_pointer, 4) == [END_FUNCTION, 0, END_FUNCTION, function_id])
10
+ break
11
+ else
12
+ vm.instruction_pointer = vm.instruction_pointer + 1
13
+ end
14
+ end
15
+ vm.instruction_pointer = vm.instruction_pointer + 4
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[RETURN] = Proc.new() do |vm|
5
+ raise ReturnStackError, "Expecting at least one item on the return stack" unless(vm.return_stack.length > 0)
6
+ vm.instruction_pointer = vm.return_stack.pop
7
+ vm.decrement_scope
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[TAILCALL] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least one item on the data stack" unless(vm.data_stack.length > 0)
6
+ raise StackError, "Expecting function" unless(vm.data_stack[-1][0] == FUNCTION_)
7
+ vm.instruction_pointer = vm.data_stack.pop[1]
8
+ vm.reload_scope
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[GET_KEY] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least two items on the data stack" unless(vm.data_stack.length > 1)
6
+ raise StackError, "Expecting a hash" unless(vm.data_stack[-2][0] == HASH_)
7
+ key = vm.data_stack.pop
8
+ vm.data_stack.push( vm.data_stack.pop[1][key] )
9
+ vm.data_stack[-1] = [NIL_, nil] if(vm.data_stack[-1] == nil)
10
+ vm.instruction_pointer = vm.instruction_pointer + 1
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module KoiVMRuby
2
+ class VM
3
+
4
+ @@instruction[HAS_KEY] = Proc.new() do |vm|
5
+ raise StackError, "Expecting at least two items on the data stack" unless(vm.data_stack.length > 1)
6
+ raise StackError, "Expecting a hash" unless(vm.data_stack[-2][0] == HASH_)
7
+ key = vm.data_stack.pop
8
+ hash = vm.data_stack.pop[1]
9
+ if(hash.has_key?(key))
10
+ vm.data_stack.push([BOOL_, true])
11
+ else
12
+ vm.data_stack.push([BOOL_, false])
13
+ end
14
+ vm.instruction_pointer = vm.instruction_pointer + 1
15
+ end
16
+
17
+ end
18
+ end