Class: Ibex::TableArtifact::Executor
- Inherits:
-
Object
- Object
- Ibex::TableArtifact::Executor
- Defined in:
- lib/ibex/table_artifact/executor.rb,
sig/ibex/table_artifact/executor.rbs
Overview
Recognition-only driver over internal token ids; it never loads wrapper code.
Defined Under Namespace
Classes: Result
Instance Method Summary collapse
- #action(row, column) ⇒ Integer?
- #displacement_lookup(table, row, column, value_key) ⇒ Integer?
- #entry_state(requested) ⇒ Integer
- #execute(input, stack, max_steps) ⇒ Result
- #goto_state(row, column) ⇒ Integer?
- #immediate_result(code, token_id, step, cursor) ⇒ Result?
-
#initialize(document) ⇒ Executor
constructor
A new instance of Executor.
- #recognize(token_ids, entry: nil, max_steps: 1_000_000) ⇒ Result
- #reduce_stack(stack, code) ⇒ String?
- #result(status, steps, consumed, reason) ⇒ Result
- #validate_input(token_ids) ⇒ Array[Integer]
Constructor Details
#initialize(document) ⇒ Executor
Returns a new instance of Executor.
31 32 33 34 35 36 37 38 39 |
# File 'lib/ibex/table_artifact/executor.rb', line 31 def initialize(document) @payload = document.is_a?(Document) ? document.payload : Document.new(document).payload @tables = @payload.fetch("tables") #: Hash[String, ValidationSupport::json_value] @productions = @payload.fetch("productions") #: Array[Hash[String, ValidationSupport::json_value]] tokens = @payload.fetch("tokens") #: Array[Hash[String, ValidationSupport::json_value]] @terminal_ids = tokens.map do |token| token.fetch("id") #: Integer end end |
Instance Method Details
#action(row, column) ⇒ Integer?
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/ibex/table_artifact/executor.rb', line 111 def action(row, column) table = @tables.fetch("actions") #: Hash[String, ValidationSupport::json_value] code = if table.fetch("encoding") == "signed-sparse-rows-v1" rows = table.fetch("rows") #: Array[Array[Hash[String, ValidationSupport::json_value]]] cells = rows.fetch(row) cell = cells.bsearch do |candidate| token_id = candidate.fetch("token_id") #: Integer token_id >= column end if cell token_id = cell.fetch("token_id") #: Integer if token_id == column cell.fetch("code") #: Integer end end else displacement_lookup(table, row, column, "codes") end defaults = @tables.fetch("default_actions") #: Array[Integer?] code.nil? ? defaults.fetch(row) : code end |
#displacement_lookup(table, row, column, value_key) ⇒ Integer?
156 157 158 159 160 161 162 163 164 |
# File 'lib/ibex/table_artifact/executor.rb', line 156 def displacement_lookup(table, row, column, value_key) offsets = table.fetch("offsets") #: Array[Integer] index = offsets.fetch(row) + column checks = table.fetch("checks") #: Array[Integer?] return if index.negative? || index >= checks.length || checks[index] != row values = table.fetch(value_key) #: Array[Integer?] values.fetch(index) end |
#entry_state(requested) ⇒ Integer
99 100 101 102 103 104 105 106 107 108 |
# File 'lib/ibex/table_artifact/executor.rb', line 99 def entry_state(requested) entries = @payload.fetch("entry_states") #: Array[Hash[String, ValidationSupport::json_value]] default_state = entries.first.fetch("state") #: Integer return default_state unless requested entry = entries.find { |candidate| candidate.fetch("name") == requested.to_s } raise ArgumentError, "unknown parser entry #{requested.inspect}" unless entry entry.fetch("state") #: Integer end |
#execute(input, stack, max_steps) ⇒ Result
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/ibex/table_artifact/executor.rb', line 54 def execute(input, stack, max_steps) cursor = 0 max_steps.times do |step| return result(:rejected, step + 1, cursor, "parser shifted past end of input") if cursor >= input.length code = action(stack.last, input.fetch(cursor)) immediate = immediate_result(code, input.fetch(cursor), step, cursor) return immediate if immediate code ||= -1 if code.positive? stack << (code - 1) cursor += 1 next end reason = reduce_stack(stack, code) return result(:rejected, step + 1, cursor, reason) if reason end result(:exhausted, max_steps, cursor, "max_steps exceeded") end |
#goto_state(row, column) ⇒ Integer?
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/ibex/table_artifact/executor.rb', line 134 def goto_state(row, column) table = @tables.fetch("gotos") #: Hash[String, ValidationSupport::json_value] if table.fetch("encoding") == "sparse-rows-v1" rows = table.fetch("rows") #: Array[Array[Hash[String, ValidationSupport::json_value]]] cells = rows.fetch(row) cell = cells.bsearch do |candidate| symbol_id = candidate.fetch("symbol_id") #: Integer symbol_id >= column end if cell symbol_id = cell.fetch("symbol_id") #: Integer if symbol_id == column cell.fetch("state") #: Integer end end else displacement_lookup(table, row, column, "values") end end |
#immediate_result(code, token_id, step, cursor) ⇒ Result?
77 78 79 80 81 82 83 |
# File 'lib/ibex/table_artifact/executor.rb', line 77 def immediate_result(code, token_id, step, cursor) return result(:rejected, step + 1, cursor, "no parser action") if code.nil? || code == -1 return unless code.zero? return result(:rejected, step + 1, cursor, "accept action before end of input") unless token_id.zero? result(:accepted, step + 1, cursor, nil) end |
#recognize(token_ids, entry: nil, max_steps: 1_000_000) ⇒ Result
42 43 44 45 46 47 48 49 |
# File 'lib/ibex/table_artifact/executor.rb', line 42 def recognize(token_ids, entry: nil, max_steps: 1_000_000) raise ArgumentError, "max_steps must be positive" unless max_steps.is_a?(Integer) && max_steps.positive? input = validate_input(token_ids) input << 0 unless input.last&.zero? stack = [entry_state(entry)] execute(input, stack, max_steps) end |
#reduce_stack(stack, code) ⇒ String?
167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/ibex/table_artifact/executor.rb', line 167 def reduce_stack(stack, code) production = @productions.fetch(-2 - code) length = production.fetch("rhs_length") #: Integer return "reduction underflow" if length >= stack.length stack.pop(length) lhs = production.fetch("lhs") #: Integer state = goto_state(stack.last, lhs) return "missing goto after reduction" unless state stack << state nil end |
#result(status, steps, consumed, reason) ⇒ Result
182 183 184 |
# File 'lib/ibex/table_artifact/executor.rb', line 182 def result(status, steps, consumed, reason) Result.new(status: status, steps: steps, consumed_tokens: consumed, reason: reason) end |
#validate_input(token_ids) ⇒ Array[Integer]
86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ibex/table_artifact/executor.rb', line 86 def validate_input(token_ids) raise ArgumentError, "token_ids must be an array" unless token_ids.is_a?(Array) token_ids.map.with_index do |token_id, index| raise ArgumentError, "token_ids[#{index}] must be an internal terminal id" unless token_id.is_a?(Integer) && @terminal_ids.include?(token_id) raise ArgumentError, "$eof may only appear at the end" if token_id.zero? && index != token_ids.length - 1 token_id end end |