class GenText::VM
Constants
- RescuePoint
Attributes
@return [IO]
@return [Integer]
Public Class Methods
@param program Array of [:method_id, *args]
. @return [Boolean] true if program
may result in calling
{IO#pos=} and false otherwise.
# File lib/gen_text/vm.rb, line 9 def self.may_set_out_pos?(program) program.any? do |instruction| [:rescue_, :capture].include? instruction.first end end
Public Instance Methods
@param [Integer] addr @return [void]
# File lib/gen_text/vm.rb, line 244 def call(addr) @stack.push(@pc + 1) @pc = addr end
-
p := {#pop};
-
binding_
.eval(set variable namedname
to a substring of {#out} from p to current position); -
if
discard_output
is true then set {IO#pos} of {#out} to p.
@param [Binding] binding_ @param [String] name @param [Boolean] discard_output @return [void]
# File lib/gen_text/vm.rb, line 180 def capture(binding_, name, discard_output) capture_start_pos = @stack.pop capture_end_pos = @out.pos captured_string = begin @out.pos = capture_start_pos @out.read(capture_end_pos - capture_start_pos) end @out.pos = if discard_output then capture_start_pos else capture_end_pos end binding_.eval("#{name} = #{captured_string.inspect}") @pc += 1 end
@return [void]
# File lib/gen_text/vm.rb, line 126 def dec @stack[-1] -= 1 @pc += 1 end
{#push}(eval(ruby_code
, file
, line
))
@param [Binding] binding_ @param [String] ruby_code @param [String] file original file of ruby_code
. @param [Integer] line original line of ruby_code
. @return [void]
# File lib/gen_text/vm.rb, line 204 def eval_ruby_code(binding_, ruby_code, file, line) @stack.push binding_.eval(ruby_code, file, line) @pc += 1 end
Writes {#pop} to {#out}.
@return [void]
# File lib/gen_text/vm.rb, line 166 def gen @out.write @stack.pop @pc += 1 end
NOP
@return [void]
# File lib/gen_text/vm.rb, line 76 def generated_from(*args) @pc += 1 end
@param [Integer] addr @return [void]
# File lib/gen_text/vm.rb, line 159 def goto(addr) @pc = addr end
If {#pop} is true then {#pc} := addr
.
@param [Integer] addr @return [void]
# File lib/gen_text/vm.rb, line 117 def goto_if(addr) if @stack.pop then @pc = addr else @pc += 1 end end
If the value on the stack != 0 then {#goto}(+addr).
@param [Integer] addr @return [void]
# File lib/gen_text/vm.rb, line 135 def goto_if_not_0(addr) if @stack.last != 0 then @pc += 1 else @pc = addr end end
If rand > v
then {#goto}(addr)
@param [Numeric] v @param [Integer] addr @return [void]
# File lib/gen_text/vm.rb, line 149 def goto_if_rand_gt(v, addr) if rand > v then @pc = addr else @pc += 1 end end
Sets {#halted?} to true.
@return [void]
# File lib/gen_text/vm.rb, line 69 def halt @halted = true end
@return [Boolean]
# File lib/gen_text/vm.rb, line 62 def halted? @halted end
Pops the value from the stack.
@return [Object] the popped value.
# File lib/gen_text/vm.rb, line 108 def pop @stack.pop @pc += 1 end
Pushes o
to the stack.
@param [Object] o @return [void]
# File lib/gen_text/vm.rb, line 84 def push(o) @stack.push o @pc += 1 end
{#push}(o.dup)
@param [Object] o @return [void]
# File lib/gen_text/vm.rb, line 93 def push_dup(o) push(o.dup) end
{#push}({#out}'s {IO#pos})
@return [void]
# File lib/gen_text/vm.rb, line 212 def push_pos @stack.push(@out.pos) @pc += 1 end
{#push}(rand(r
) if r
is specified; rand() otherwise)
@param [Object, nil] r @return [void]
# File lib/gen_text/vm.rb, line 101 def push_rand(r = nil) push(if r then rand(r) else rand end) end
{#push}({#out}'s {IO#pos}, {#pc} as {RescuePoint})
@param [Integer, nil] pc if specified then it is pushed instead of {#pc}. @return [void]
# File lib/gen_text/vm.rb, line 221 def push_rescue_point(pc = nil) @stack.push RescuePoint[(pc or @pc), @out.pos] @pc += 1 end
{#pop}s until a {RescuePoint} is found then restore {#out} and {#pc} from the {RescuePoint}.
@param [Proc] on_failure is called if no {RescuePoint} is found @return [void]
# File lib/gen_text/vm.rb, line 231 def rescue_(on_failure) @stack.pop until @stack.empty? or @stack.last.is_a? RescuePoint if @stack.empty? then on_failure.() else rescue_point = @stack.pop @pc = rescue_point.pc @out.pos = rescue_point.out_pos end end
@return [void]
# File lib/gen_text/vm.rb, line 250 def ret @pc = @stack.pop end
Executes program
.
If program
may result in calling {IO#pos=} (see {VM::may_set_out_pos?} then after the execution the out
may contain garbage after its {IO#pos}. It is up to the caller to truncate the garbage or to copy the useful data.
@param program Array of [:method_id, *args]
. @param [IO] out @param [Boolean] do_not_run if true then program
will not be run
(some checks and initializations will be performed only).
@return [void]
# File lib/gen_text/vm.rb, line 26 def run(program, out, do_not_run = false) # if $DEBUG STDERR.puts "PROGRAM:" program.each_with_index do |instruction, addr| STDERR.puts " #{addr}: #{inspect_instruction(instruction)}" end end # return if do_not_run # Init. @stack = [] @out = out @pc = 0 @halted = false # Run. STDERR.puts "RUN TRACE:" if $DEBUG until halted? instruction = program[@pc] method_id, *args = *instruction STDERR.puts " #{@pc}: #{inspect_instruction(instruction)}" if $DEBUG self.__send__(method_id, *args) if $DEBUG then STDERR.puts " PC: #{@pc}" STDERR.puts " STACK: #{@stack.inspect}" end end end
Let stack contains wa
= [[weight1, address1], [weight2, address2], …]. This function:
-
Picks a random address from
wa
(the more weight the address has, the more often it is picked); -
Deletes the chosen address from
wa
; -
If there was the only address in
wa
then it does {#push}(nil); otherwise it does {#push_rescue_point}; -
{#goto}(the chosen address).
@return [void]
# File lib/gen_text/vm.rb, line 265 def weighed_choice weights_and_addresses = @stack.last # If no alternatives left... if weights_and_addresses.size == 1 then _, address = *weights_and_addresses.first @stack.push nil @pc = address # If there are alternatives... else chosen_weight_and_address = sample_weighed(weights_and_addresses) weights_and_addresses.delete chosen_weight_and_address _, chosen_address = *chosen_weight_and_address push_rescue_point @pc = chosen_address end end
Private Instance Methods
# File lib/gen_text/vm.rb, line 294 def inspect_instruction(instruction) method_id, *args = *instruction "#{method_id} #{args.map(&:inspect).join(", ")}" end
@param [Array<Array<(Numeric, Object)>>] weights_and_items @return [Array<(Numeric, Object)>]
# File lib/gen_text/vm.rb, line 288 def sample_weighed(weights_and_items) # The algorithm is described in # http://utopia.duth.gr/~pefraimi/research/data/2007EncOfAlg.pdf weights_and_items.max_by { |weight, item| rand ** (1.0 / weight) } end