Class: Rjq::VM
- Inherits:
-
Object
show all
- Defined in:
- lib/rjq/vm.rb
Defined Under Namespace
Classes: InstructionBudget, RecordingInputQueue
Instance Method Summary
collapse
-
#execute_block(instructions, input, context) ⇒ Object
-
#initialize(program, opts = {}, instruction_budget: nil) ⇒ VM
constructor
-
#paths_for_block(instructions, input, context) ⇒ Object
-
#replacement_filters(block, captured_context) ⇒ Object
-
#run(input_value) ⇒ Object
-
#source_all?(instructions, input, context) ⇒ Boolean
-
#source_any?(instructions, input, context) ⇒ Boolean
-
#stream_block(block, input, context) ⇒ Object
-
#take_block(instructions, input, context, count) ⇒ Object
Constructor Details
#initialize(program, opts = {}, instruction_budget: nil) ⇒ VM
Returns a new instance of VM.
54
55
56
57
58
|
# File 'lib/rjq/vm.rb', line 54
def initialize(program, opts = {}, instruction_budget: nil)
@program = program
@opts = opts
@instruction_budget = instruction_budget || InstructionBudget.new(opts[:max_instructions])
end
|
Instance Method Details
#execute_block(instructions, input, context) ⇒ Object
84
85
86
87
88
89
90
91
92
|
# File 'lib/rjq/vm.rb', line 84
def execute_block(instructions, input, context)
output = []
each_block(instructions, input, context) { |value| output << value }
output
rescue Rjq::RuntimeError => e
raise e.prepend_outputs(output)
rescue BreakSignal => e
raise BreakSignal.new(e.label, e.value, outputs: output + Array(e.outputs))
end
|
#paths_for_block(instructions, input, context) ⇒ Object
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
# File 'lib/rjq/vm.rb', line 100
def paths_for_block(instructions, input, context)
stack = []
instructions.each_with_index do |instruction, index|
execute_path_instruction(instruction, stack, input, context)
rescue Rjq::RuntimeError => e
remaining = instructions[(index + 1)..].to_a
raise if remaining.empty?
if e.is_a?(InvalidPathError)
raise InvalidPathError.new(invalid_path_message(remaining, e.result, input, context), e.result,
outputs: e.outputs)
end
raise
end
stack.pop || []
end
|
#replacement_filters(block, captured_context) ⇒ Object
125
126
127
128
129
130
131
132
133
|
# File 'lib/rjq/vm.rb', line 125
def replacement_filters(block, captured_context)
if block.instructions.last&.op == :append
left = BytecodeBlock.new(instructions: block.instructions[0...-1])
right = block.instructions.last.arg1
return replacement_filters(left, captured_context) + replacement_filters(right, captured_context)
end
[BytecodeFilter.new(self, block, captured_context: captured_context)]
end
|
#run(input_value) ⇒ Object
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
# File 'lib/rjq/vm.rb', line 60
def run(input_value)
Enumerator.new do |yielder|
@call_depth = 0
Value.validate!(input_value)
@opts.fetch(:variables, {}).each_value { |value| Value.validate!(value) }
context = context_with_definitions(base_context)
output_count = 0
begin
each_block(@program.program.instructions, input_value, context).each do |value|
output_count += 1
max_outputs = @opts[:max_outputs]
raise RuntimeError, "output limit exceeded (#{max_outputs})" if max_outputs && output_count > max_outputs
yielder << value
end
rescue ErrorValue => e
Array(e.outputs).each { |value| yielder << value }
raise
rescue SystemStackError
raise ResourceLimitError, 'execution recursion exceeded the host stack limit'
end
end
end
|
#source_all?(instructions, input, context) ⇒ Boolean
121
122
123
|
# File 'lib/rjq/vm.rb', line 121
def source_all?(instructions, input, context, &)
each_block(instructions, input, context).all?(&)
end
|
#source_any?(instructions, input, context) ⇒ Boolean
117
118
119
|
# File 'lib/rjq/vm.rb', line 117
def source_any?(instructions, input, context, &)
each_block(instructions, input, context).any?(&)
end
|
#stream_block(block, input, context) ⇒ Object
135
136
137
|
# File 'lib/rjq/vm.rb', line 135
def stream_block(block, input, context)
each_block(block.instructions, input, context)
end
|
#take_block(instructions, input, context, count) ⇒ Object
94
95
96
97
98
|
# File 'lib/rjq/vm.rb', line 94
def take_block(instructions, input, context, count)
return [] if count <= 0
each_block(instructions, input, context).take(count)
end
|