Class: Ibex::TableArtifact::TableValidator
- Inherits:
-
Object
- Object
- Ibex::TableArtifact::TableValidator
- Includes:
- ValidationSupport
- Defined in:
- lib/ibex/table_artifact/validator/tables.rb,
sig/ibex/table_artifact/validator/tables.rbs
Constant Summary
Constants included from ValidationSupport
Instance Method Summary collapse
-
#initialize(data, state_count:, production_count:, terminal_ids:, nonterminal_ids:, representation:) ⇒ TableValidator
constructor
A new instance of TableValidator.
- #validate! ⇒ void
- #validate_action_code(code, path) ⇒ void
- #validate_actions(table) ⇒ void
- #validate_canonical_displacement(table, rows, value_key:, width_key:, width:, dense:, path:) ⇒ void
- #validate_compact_actions(table) ⇒ void
- #validate_compact_gotos(table) ⇒ void
- #validate_defaults(defaults) ⇒ void
- #validate_displacement(table, value_key:, path:) {|arg0, arg1, arg2| ... } ⇒ Array[Hash[Integer, ValidationSupport::json_value]]
- #validate_gotos(table) ⇒ void
- #validate_row_count(table, path) ⇒ void
- #validate_sparse_actions(table) ⇒ void
- #validate_sparse_gotos(table) ⇒ void
- #validate_state(state, path) ⇒ void
- #validate_token_action(code, token_id, path) ⇒ void
Methods included from ValidationSupport
#array, #boolean, #digest, #enum, #integer, #invalid, #nullable_integer, #nullable_string, #record, #sorted_unique!, #string
Constructor Details
#initialize(data, state_count:, production_count:, terminal_ids:, nonterminal_ids:, representation:) ⇒ TableValidator
Returns a new instance of TableValidator.
16 17 18 19 20 21 22 23 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 16 def initialize(data, state_count:, production_count:, terminal_ids:, nonterminal_ids:, representation:) @data = data @state_count = state_count @production_count = production_count @terminal_ids = terminal_ids @nonterminal_ids = nonterminal_ids @representation = representation end |
Instance Method Details
#validate! ⇒ void
This method returns an undefined value.
26 27 28 29 30 31 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 26 def validate! record(@data, "$.payload.tables", %w[actions gotos default_actions]) validate_actions(@data.fetch("actions")) validate_gotos(@data.fetch("gotos")) validate_defaults(@data.fetch("default_actions")) end |
#validate_action_code(code, path) ⇒ void
This method returns an undefined value.
222 223 224 225 226 227 228 229 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 222 def validate_action_code(code, path) code = integer(code, path) return if [0, -1].include?(code) return validate_state(code - 1, path) if code.positive? production = -2 - code invalid(path, "references a missing production") unless production.between?(0, @production_count - 1) end |
#validate_actions(table) ⇒ void
This method returns an undefined value.
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 36 def validate_actions(table) invalid("$.payload.tables.actions", "must be an object") unless table.is_a?(Hash) table_data = table #: Hash[String, ValidationSupport::json_value] encoding = table_data["encoding"] expected = @representation == "compact" ? "signed-row-displacement-v1" : "signed-sparse-rows-v1" invalid("$.payload.tables.actions.encoding", "does not match table representation") unless encoding == expected case encoding when "signed-sparse-rows-v1" then validate_sparse_actions(table_data) when "signed-row-displacement-v1" then validate_compact_actions(table_data) else invalid("$.payload.tables.actions.encoding", "is unsupported") end end |
#validate_canonical_displacement(table, rows, value_key:, width_key:, width:, dense:, path:) ⇒ void
This method returns an undefined value.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 173 def validate_canonical_displacement(table, rows, value_key:, width_key:, width:, dense:, path:) canonical = Tables::Compact.build(rows, dense: dense) expected = [canonical.offsets, canonical.values, canonical.checks] actual = [table.fetch("offsets"), table.fetch(value_key), table.fetch("checks")] invalid(path, "must use the canonical minimal row-displacement layout") unless actual == expected expected_width = if width_key == "column_count" rows.flat_map(&:keys).max.to_i + 1 else canonical.dense_width end expected_width = nil if expected_width && (@state_count * expected_width) > Tables::Compact::DENSE_CELL_LIMIT invalid("#{path}.#{width_key}", "does not match the canonical table width") unless width == expected_width end |
#validate_compact_actions(table) ⇒ void
This method returns an undefined value.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 68 def validate_compact_actions(table) keys = %w[encoding row_count column_count offsets codes checks] record(table, "$.payload.tables.actions", keys) enum(table.fetch("encoding"), "$.payload.tables.actions.encoding", ["signed-row-displacement-v1"]) validate_row_count(table, "$.payload.tables.actions") column_count = nullable_integer(table.fetch("column_count"), "$.payload.tables.actions.column_count", minimum: 1) rows = validate_displacement(table, value_key: "codes", path: "$.payload.tables.actions") do |column, code, cell_path| invalid(cell_path, "column must reference a terminal") unless @terminal_ids.include?(column) invalid(cell_path, "column exceeds column_count") if column_count && column >= column_count validate_token_action(code, column, "#{cell_path}.code") end validate_canonical_displacement( table, rows, value_key: "codes", width_key: "column_count", width: column_count, dense: false, path: "$.payload.tables.actions" ) end |
#validate_compact_gotos(table) ⇒ void
This method returns an undefined value.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 120 def validate_compact_gotos(table) keys = %w[encoding row_count dense_width offsets values checks] record(table, "$.payload.tables.gotos", keys) enum(table.fetch("encoding"), "$.payload.tables.gotos.encoding", ["row-displacement-v1"]) validate_row_count(table, "$.payload.tables.gotos") dense_width = nullable_integer(table.fetch("dense_width"), "$.payload.tables.gotos.dense_width", minimum: 1) rows = validate_displacement(table, value_key: "values", path: "$.payload.tables.gotos") do |column, state, cell_path| invalid(cell_path, "column must reference a nonterminal") unless @nonterminal_ids.include?(column) invalid(cell_path, "column exceeds dense_width") if dense_width && column >= dense_width validate_state(state, "#{cell_path}.state") end validate_canonical_displacement( table, rows, value_key: "values", width_key: "dense_width", width: dense_width, dense: true, path: "$.payload.tables.gotos" ) end |
#validate_defaults(defaults) ⇒ void
This method returns an undefined value.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 198 def validate_defaults(defaults) values = array(defaults, "$.payload.tables.default_actions") unless values.length == @state_count invalid("$.payload.tables.default_actions", "must contain one entry per state") end values.each_with_index do |code, state| next if code.nil? path = "$.payload.tables.default_actions[#{state}]" code = integer(code, path) invalid(path, "must be an error or reduction") if code >= 0 end end |
#validate_displacement(table, value_key:, path:) {|arg0, arg1, arg2| ... } ⇒ Array[Hash[Integer, ValidationSupport::json_value]]
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 146 def validate_displacement(table, value_key:, path:) offsets = integer_array(table.fetch("offsets"), "#{path}.offsets", allow_nil: false, minimum: 0) invalid("#{path}.offsets", "must contain one offset per state") unless offsets.length == @state_count minimum = value_key == "codes" ? nil : 0 values = integer_array(table.fetch(value_key), "#{path}.#{value_key}", allow_nil: true, minimum: minimum) checks = integer_array(table.fetch("checks"), "#{path}.checks", allow_nil: true, minimum: 0) invalid(path, "#{value_key} and checks must have equal length") unless values.length == checks.length rows = Array.new(@state_count) { {} } #: Array[Hash[Integer, ValidationSupport::json_value]] checks.each_index do |index| row = checks[index] value = values[index] invalid("#{path}[#{index}]", "value and check occupancy must match") unless row.nil? == value.nil? next unless row invalid("#{path}.checks[#{index}]", "references a missing state") unless row.between?(0, @state_count - 1) column = index - offsets.fetch(row) invalid("#{path}[#{index}]", "has a negative column") if column.negative? yield(column, value, "#{path}[#{index}]") rows.fetch(row)[column] = value end rows end |
#validate_gotos(table) ⇒ void
This method returns an undefined value.
88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 88 def validate_gotos(table) invalid("$.payload.tables.gotos", "must be an object") unless table.is_a?(Hash) table_data = table #: Hash[String, ValidationSupport::json_value] encoding = table_data["encoding"] expected = @representation == "compact" ? "row-displacement-v1" : "sparse-rows-v1" invalid("$.payload.tables.gotos.encoding", "does not match table representation") unless encoding == expected case encoding when "sparse-rows-v1" then validate_sparse_gotos(table_data) when "row-displacement-v1" then validate_compact_gotos(table_data) else invalid("$.payload.tables.gotos.encoding", "is unsupported") end end |
#validate_row_count(table, path) ⇒ void
This method returns an undefined value.
139 140 141 142 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 139 def validate_row_count(table, path) count = integer(table.fetch("row_count"), "#{path}.row_count", minimum: 1) invalid("#{path}.row_count", "must equal state_count") unless count == @state_count end |
#validate_sparse_actions(table) ⇒ void
This method returns an undefined value.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 50 def validate_sparse_actions(table) record(table, "$.payload.tables.actions", %w[encoding rows]) rows = array(table.fetch("rows"), "$.payload.tables.actions.rows") invalid("$.payload.tables.actions.rows", "must contain one row per state") unless rows.length == @state_count rows.each_with_index do |row, state| token_ids = array(row, "$.payload.tables.actions.rows[#{state}]").map.with_index do |cell, index| path = "$.payload.tables.actions.rows[#{state}][#{index}]" cell = record(cell, path, %w[token_id code]) token_id = integer(cell.fetch("token_id"), "#{path}.token_id", minimum: 0) invalid("#{path}.token_id", "must reference a terminal") unless @terminal_ids.include?(token_id) validate_token_action(cell.fetch("code"), token_id, "#{path}.code") token_id end sorted_unique!(token_ids, "$.payload.tables.actions.rows[#{state}]") end end |
#validate_sparse_gotos(table) ⇒ void
This method returns an undefined value.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 102 def validate_sparse_gotos(table) record(table, "$.payload.tables.gotos", %w[encoding rows]) rows = array(table.fetch("rows"), "$.payload.tables.gotos.rows") invalid("$.payload.tables.gotos.rows", "must contain one row per state") unless rows.length == @state_count rows.each_with_index do |row, state| symbol_ids = array(row, "$.payload.tables.gotos.rows[#{state}]").map.with_index do |cell, index| path = "$.payload.tables.gotos.rows[#{state}][#{index}]" cell = record(cell, path, %w[symbol_id state]) symbol_id = integer(cell.fetch("symbol_id"), "#{path}.symbol_id", minimum: 0) invalid("#{path}.symbol_id", "must reference a nonterminal") unless @nonterminal_ids.include?(symbol_id) validate_state(cell.fetch("state"), "#{path}.state") symbol_id end sorted_unique!(symbol_ids, "$.payload.tables.gotos.rows[#{state}]") end end |
#validate_state(state, path) ⇒ void
This method returns an undefined value.
232 233 234 235 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 232 def validate_state(state, path) state = integer(state, path, minimum: 0) invalid(path, "references a missing state") unless state.between?(0, @state_count - 1) end |
#validate_token_action(code, token_id, path) ⇒ void
This method returns an undefined value.
214 215 216 217 218 219 |
# File 'lib/ibex/table_artifact/validator/tables.rb', line 214 def validate_token_action(code, token_id, path) code = integer(code, path) invalid(path, "accept is only valid for $eof") if code.zero? && !token_id.zero? invalid(path, "$eof cannot be shifted") if token_id.zero? && code.positive? validate_action_code(code, path) end |