Class: MiniRuby::VM
Overview
MiniRuby stack based Virtual Machine. Executes a chunk of bytecode produced by the compiler.
Constant Summary collapse
- Func =
T.type_alias { T.proc.params(vm: VM, args: T::Array[Object]).returns(Object) }
Class Attribute Summary collapse
-
.functions ⇒ Object
readonly
: Hash[Symbol, NativeFunction].
Instance Attribute Summary collapse
-
#bytecode ⇒ Object
readonly
: BytecodeFunction.
-
#stdin ⇒ Object
readonly
: IO.
-
#stdout ⇒ Object
readonly
: IO.
Class Method Summary collapse
-
.define(name, param_count = 0, &func) ⇒ Object
: (Symbol name, ?Integer param_count) { (?) -> untyped } -> void.
-
.interpret(source, name: '<main>', filename: '<main>', stdout: $stdout, stdin: $stdin) ⇒ Object
: (String source, ?name: String, ?filename: String, ?stdout: IO, ?stdin: IO) -> Object.
-
.run(bytecode, stdout: $stdout, stdin: $stdin) ⇒ Object
: (BytecodeFunction bytecode, ?stdout: IO, ?stdin: IO) -> Object.
Instance Method Summary collapse
-
#initialize(bytecode, stdout: $stdout, stdin: $stdin) ⇒ VM
constructor
: (BytecodeFunction bytecode, ?stdout: IO, ?stdin: IO) -> void.
-
#inspect_stack ⇒ Object
: -> void.
-
#run ⇒ Object
: -> void.
-
#stack_top ⇒ Object
: -> Object.
Constructor Details
#initialize(bytecode, stdout: $stdout, stdin: $stdin) ⇒ VM
: (BytecodeFunction bytecode, ?stdout: IO, ?stdin: IO) -> void
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/miniruby/vm.rb', line 40 def initialize(bytecode, stdout: $stdout, stdin: $stdin) # the currently executed chunk of bytecode @bytecode = bytecode # standard output used by the VM @stdout = stdout # standard input used by the VM @stdin = stdin # The value stack @stack = [Object.new] #: Array[Object] # Instruction pointer -- points to the next bytecode instruction @ip = 0 #: Integer # Stack pointer -- points to the offset on the stack where the next value will be pushed to @sp = 1 #: Integer end |
Class Attribute Details
.functions ⇒ Object (readonly)
: Hash[Symbol, NativeFunction]
68 69 70 |
# File 'lib/miniruby/vm.rb', line 68 def functions @functions end |
Instance Attribute Details
#bytecode ⇒ Object (readonly)
: BytecodeFunction
31 32 33 |
# File 'lib/miniruby/vm.rb', line 31 def bytecode @bytecode end |
#stdin ⇒ Object (readonly)
: IO
37 38 39 |
# File 'lib/miniruby/vm.rb', line 37 def stdin @stdin end |
#stdout ⇒ Object (readonly)
: IO
34 35 36 |
# File 'lib/miniruby/vm.rb', line 34 def stdout @stdout end |
Class Method Details
.define(name, param_count = 0, &func) ⇒ Object
: (Symbol name, ?Integer param_count) { (?) -> untyped } -> void
63 64 65 |
# File 'lib/miniruby/vm.rb', line 63 def define(name, param_count = 0, &func) @functions[name] = NativeFunction.new(name:, param_count:, &func) end |
.interpret(source, name: '<main>', filename: '<main>', stdout: $stdout, stdin: $stdin) ⇒ Object
: (String source, ?name: String, ?filename: String, ?stdout: IO, ?stdin: IO) -> Object
24 25 26 27 |
# File 'lib/miniruby/vm.rb', line 24 def interpret(source, name: '<main>', filename: '<main>', stdout: $stdout, stdin: $stdin) bytecode = Compiler.compile_source(source, name:, filename:) run(bytecode, stdout:, stdin:) end |
.run(bytecode, stdout: $stdout, stdin: $stdin) ⇒ Object
: (BytecodeFunction bytecode, ?stdout: IO, ?stdin: IO) -> Object
17 18 19 20 21 |
# File 'lib/miniruby/vm.rb', line 17 def run(bytecode, stdout: $stdout, stdin: $stdin) vm = new(bytecode, stdout:, stdin:) vm.run vm.stack_top end |
Instance Method Details
#inspect_stack ⇒ Object
: -> void
185 186 187 |
# File 'lib/miniruby/vm.rb', line 185 def inspect_stack @stdout.print("#{@stack[...@sp].inspect}\n") end |
#run ⇒ Object
: -> void
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/miniruby/vm.rb', line 85 def run while true opcode = read_byte case opcode when Opcode::TRUE push(true) when Opcode::FALSE push(false) when Opcode::NIL push(nil) when Opcode::POP pop() when Opcode::DUP push(peek) when Opcode::INSPECT_STACK inspect_stack() when Opcode::ADD right = pop() left = pop() #: as untyped push(left + right) when Opcode::SUBTRACT right = pop left = pop #: as untyped push(left - right) when Opcode::MULTIPLY right = pop left = pop #: as untyped push(left * right) when Opcode::DIVIDE right = pop left = pop #: as untyped push(left / right) when Opcode::EQUAL right = pop left = pop #: as untyped push(left == right) when Opcode::GREATER right = pop left = pop #: as untyped push(left > right) when Opcode::GREATER_EQUAL right = pop left = pop #: as untyped push(left >= right) when Opcode::LESS right = pop left = pop #: as untyped push(left < right) when Opcode::LESS_EQUAL right = pop left = pop #: as untyped push(left <= right) when Opcode::NOT value = pop push(!value) when Opcode::NEGATE value = pop #: as untyped push(-value) when Opcode::LOAD_VALUE index = read_byte push(get_value(index)) when Opcode::SELF push_local(0) when Opcode::PREP_LOCALS @sp += read_byte when Opcode::GET_LOCAL push_local(read_byte) when Opcode::SET_LOCAL value = peek local_index = read_byte set_local(local_index, value) when Opcode::JUMP offset = read_byte @ip += offset when Opcode::LOOP offset = read_byte @ip -= offset when Opcode::JUMP_UNLESS condition = pop offset = read_byte next if condition @ip += offset when Opcode::CALL index = read_byte call_info = get_value(index) #: as CallInfo to_pop = call_info.arg_count + 1 args = @stack[(@sp - to_pop)...@sp] #: as !nil result = self.class.functions.fetch(call_info.name).call(self, args) args.each { pop } push(result) when Opcode::RETURN return else raise ArgumentError, "invalid opcode: #{opcode}" end end end |
#stack_top ⇒ Object
: -> Object
190 191 192 |
# File 'lib/miniruby/vm.rb', line 190 def stack_top @stack[@sp - 1] end |