Class: Ibex::TableArtifact::Executor

Inherits:
Object
  • Object
show all
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

Constructor Details

#initialize(document) ⇒ Executor

Returns a new instance of Executor.

RBS:

  • (Document | Hash[String, ValidationSupport::json_value] document) -> void

Parameters:

  • document (Document, Hash[String, ValidationSupport::json_value])


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?

RBS:

  • (Integer row, Integer column) -> Integer?

Parameters:

  • row (Integer)
  • column (Integer)

Returns:

  • (Integer, nil)


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?

RBS:

  • (Hash[String, ValidationSupport::json_value] table, Integer row, Integer column, String value_key) -> Integer?

Parameters:

  • table (Hash[String, ValidationSupport::json_value])
  • row (Integer)
  • column (Integer)
  • value_key (String)

Returns:

  • (Integer, nil)


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

RBS:

  • (String | Symbol? requested) -> Integer

Parameters:

  • requested (String, Symbol, nil)

Returns:

  • (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

RBS:

  • (Array[Integer] input, Array[Integer] stack, Integer max_steps) -> Result

Parameters:

  • input (Array[Integer])
  • stack (Array[Integer])
  • max_steps (Integer)

Returns:



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?

RBS:

  • (Integer row, Integer column) -> Integer?

Parameters:

  • row (Integer)
  • column (Integer)

Returns:

  • (Integer, nil)


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?

RBS:

  • (Integer? code, Integer token_id, Integer step, Integer cursor) -> Result?

Parameters:

  • code (Integer, nil)
  • token_id (Integer)
  • step (Integer)
  • cursor (Integer)

Returns:



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

RBS:

  • (Array[Integer] token_ids, ?entry: String | Symbol?, ?max_steps: Integer) -> Result

Parameters:

  • token_ids (Array[Integer])
  • entry: (String, Symbol, nil) (defaults to: nil)
  • max_steps: (Integer) (defaults to: 1_000_000)

Returns:



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?

RBS:

  • (Array[Integer] stack, Integer code) -> String?

Parameters:

  • stack (Array[Integer])
  • code (Integer)

Returns:

  • (String, nil)


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

RBS:

  • (Symbol status, Integer steps, Integer consumed, String? reason) -> Result

Parameters:

  • status (Symbol)
  • steps (Integer)
  • consumed (Integer)
  • reason (String, nil)

Returns:



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]

RBS:

  • (Array[Integer] token_ids) -> Array[Integer]

Parameters:

  • token_ids (Array[Integer])

Returns:

  • (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