Class: Ibex::Runtime::SyntaxRepairer
- Inherits:
-
Object
- Object
- Ibex::Runtime::SyntaxRepairer
- Defined in:
- lib/ibex/runtime/syntax_repair.rb,
sig/ibex/runtime/syntax_repair.rbs
Overview
Executes one fresh syntax-only repair attempt without mutating its source SyntaxSession. Generated lexer actions retain the acknowledged trust profile; parser production actions remain suppressed.
Instance Method Summary collapse
- #bounded_failure(status, reason, parsed, diagnostics) ⇒ SyntaxRepairResult
- #build_selected(capture, parsed, diagnostics) ⇒ SyntaxRepairResult
- #call ⇒ SyntaxRepairResult
- #cancellation_checkpoint! ⇒ void
- #enforce_limit!(resource, limit, observed) ⇒ void
- #first_diagnostic_byte(result) ⇒ Integer?
- #fresh_validation(source) ⇒ SyntaxSessionResult
- #immutable_diagnostics(diagnostics) ⇒ Array[SyntaxSessionDiagnostic]
-
#initialize(parser_class, source, baseline, execution_profile:, resource_limits:, limits:, cancellation:, policy:, token_text:) ⇒ SyntaxRepairer
constructor
A new instance of SyntaxRepairer.
- #input_range(input) ⇒ [ Integer, Integer ]
- #install_capture(parser) ⇒ Array[[ RepairPlan, Array[RepairInput] ]]
- #location_value(location, key) ⇒ Object
- #normalize_text_edits(edits) ⇒ Array[CST::TextEdit]
- #original_text(start_byte, end_byte) ⇒ String
- #project_edits(plan, inputs) ⇒ Array[SyntaxRepairEdit]
- #punctuation_literal(name) ⇒ String?
- #replacement_text(edit) ⇒ String?
- #unavailable(reason, parsed, diagnostics, plan: nil) ⇒ SyntaxRepairResult
- #validate_edit_limits!(edits) ⇒ void
- #validate_policy(policy) ⇒ RepairPolicy
- #validate_token_text(value) ⇒ Hash[String, String]
- #validation_status(validation, source) ⇒ Symbol
Constructor Details
#initialize(parser_class, source, baseline, execution_profile:, resource_limits:, limits:, cancellation:, policy:, token_text:) ⇒ SyntaxRepairer
Returns a new instance of SyntaxRepairer.
230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 230 def initialize(parser_class, source, baseline, execution_profile:, resource_limits:, limits:, cancellation:, policy:, token_text:) @parser_class = parser_class @source = source @baseline = baseline @execution_profile = execution_profile @resource_limits = resource_limits @limits = limits @cancellation = cancellation @policy = validate_policy(policy) @token_text = validate_token_text(token_text) end |
Instance Method Details
#bounded_failure(status, reason, parsed, diagnostics) ⇒ SyntaxRepairResult
412 413 414 415 416 417 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 412 def bounded_failure(status, reason, parsed, diagnostics) SyntaxRepairResult.new( status: status, bounded_status: status, reason: reason, plan: nil, text_edits: [], syntax_root: parsed.syntax_root, diagnostics: diagnostics, updated_source: nil, validation: nil ) end |
#build_selected(capture, parsed, diagnostics) ⇒ SyntaxRepairResult
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 281 def build_selected(capture, parsed, diagnostics) runtime_plan, inputs = capture projected = project_edits(runtime_plan, inputs) plan = SyntaxRepairPlan.new(runtime_plan: runtime_plan, edits: projected) return unavailable(:missing_token_text, parsed, diagnostics, plan: plan) if projected.any? { |edit| %i[insert replace].include?(edit.kind) && edit.replacement_text.nil? } text_edits = normalize_text_edits(projected) validate_edit_limits!(text_edits) updated_source = @source.apply(text_edits) enforce_limit!(:source_bytes, @limits.max_source_bytes, updated_source.bytesize) cancellation_checkpoint! validation = fresh_validation(updated_source) cancellation_checkpoint! return unavailable(:validation_source_not_consumed, parsed, diagnostics, plan: plan) unless updated_source.text.start_with?(validation.syntax_root.to_source) status = validation_status(validation, updated_source) SyntaxRepairResult.new( status: status, bounded_status: :selected, reason: nil, plan: plan, text_edits: text_edits, syntax_root: parsed.syntax_root, diagnostics: diagnostics, updated_source: updated_source, validation: validation ) rescue ArgumentError => e return unavailable(:overlapping_text_edits, parsed, diagnostics, plan: plan) if e..include?("text edits overlap") raise end |
#call ⇒ SyntaxRepairResult
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 244 def call cancellation_checkpoint! parser = @parser_class.__send__(:new, resource_limits: @resource_limits) captures = install_capture(parser) parser.repair_policy = @policy parser.observe { |_event| cancellation_checkpoint! } parsed = parser.__send__(:parse_syntax_with_cache, @source, CST::NodeCache.new) cancellation_checkpoint! diagnostics = immutable_diagnostics(parsed.diagnostics) return unavailable(:repair_source_not_consumed, parsed, diagnostics) unless @source.text.start_with?(parsed.syntax_root.to_source) outcomes = parser.__send__(:syntax_repair_search_results) return bounded_failure(:exhausted, :search_exhausted, parsed, diagnostics) if outcomes.any? { |outcome| outcome.status == :exhausted } return bounded_failure(:not_found, :no_repair_plan, parsed, diagnostics) if captures.empty? return unavailable(:multiple_repair_segments, parsed, diagnostics) unless captures.one? build_selected(captures.fetch(0), parsed, diagnostics) rescue ResourceLimitError => e raise SyntaxSessionResourceLimitError.new(resource: e.resource, limit: e.limit, observed: e.observed), cause: e end |
#cancellation_checkpoint! ⇒ void
This method returns an undefined value.
483 484 485 486 487 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 483 def cancellation_checkpoint! return unless @cancellation&.cancelled? raise SyntaxSessionCancelled, "syntax repair operation was cancelled" end |
#enforce_limit!(resource, limit, observed) ⇒ void
This method returns an undefined value.
476 477 478 479 480 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 476 def enforce_limit!(resource, limit, observed) return unless observed > limit raise SyntaxSessionResourceLimitError.new(resource: resource, limit: limit, observed: observed) end |
#first_diagnostic_byte(result) ⇒ Integer?
405 406 407 408 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 405 def first_diagnostic_byte(result) value = result.diagnostics.first&.data&.dig("location", "start_byte") value if value.is_a?(Integer) end |
#fresh_validation(source) ⇒ SyntaxSessionResult
383 384 385 386 387 388 389 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 383 def fresh_validation(source) SyntaxSession.new( @parser_class, source, execution_profile: @execution_profile, resource_limits: @resource_limits, limits: @limits, cancellation: @cancellation, blender: false ).result end |
#immutable_diagnostics(diagnostics) ⇒ Array[SyntaxSessionDiagnostic]
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 429 def immutable_diagnostics(diagnostics) diagnostics.map do |diagnostic| if diagnostic.is_a?(ParseError) SyntaxSessionDiagnostic.new( kind: :parse_error, data: { message: diagnostic., token_id: diagnostic.token_id, token_name: diagnostic.token_name, expected_tokens: diagnostic.expected_tokens, location: EventSanitizer.location(diagnostic.location) } ) else data = diagnostic #: Hash[Object?, Object?] SyntaxSessionDiagnostic.new(kind: :syntax_error, data: data) end end.freeze end |
#input_range(input) ⇒ [ Integer, Integer ]
328 329 330 331 332 333 334 335 336 337 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 328 def input_range(input) location = input.location start_byte = location_value(location, :start_byte) end_byte = location_value(location, :end_byte) unless start_byte.is_a?(Integer) && end_byte.is_a?(Integer) raise ArgumentError, "syntax repair requires byte-oriented generated lexer locations" end [start_byte, end_byte] end |
#install_capture(parser) ⇒ Array[[ RepairPlan, Array[RepairInput] ]]
270 271 272 273 274 275 276 277 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 270 def install_capture(parser) captures = [] #: Array[[RepairPlan, Array[RepairInput]]] parser.define_singleton_method(:on_repair) do |plan| inputs = __send__(:syntax_repair_inputs) captures << [plan, inputs] end captures end |
#location_value(location, key) ⇒ Object? #location_value(location, key) ⇒ Object
448 449 450 451 452 453 454 455 456 457 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 448 def location_value(location, key) return location.public_send(key) if location.respond_to?(key) if location.is_a?(Hash) hash = location #: Hash[Object, Object] return hash[key] || hash[key.to_s] end nil end |
#normalize_text_edits(edits) ⇒ Array[CST::TextEdit]
367 368 369 370 371 372 373 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 367 def normalize_text_edits(edits) CST::TextEdit.normalize(edits.map do |edit| insert_text = edit.replacement_text || raise(ArgumentError, "missing token text") delete_length = edit.kind == :insert ? 0 : edit.end_byte - edit.start_byte CST::TextEdit.new(start: edit.start_byte, delete_length: delete_length, insert_text: insert_text) end) end |
#original_text(start_byte, end_byte) ⇒ String
362 363 364 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 362 def original_text(start_byte, end_byte) (@source.text.byteslice(start_byte, end_byte - start_byte) || "".b).freeze end |
#project_edits(plan, inputs) ⇒ Array[SyntaxRepairEdit]
312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 312 def project_edits(plan, inputs) plan.edits.map do |edit| input = inputs.fetch(edit.position) start_byte, end_byte = input_range(input) replacement = replacement_text(edit) end_byte = start_byte if edit.kind == :insert SyntaxRepairEdit.new( kind: edit.kind, position: edit.position, token_id: edit.token_id, token_name: edit.token_name, cost: edit.cost, start_byte: start_byte, end_byte: end_byte, original_text: edit.kind == :insert ? "".b : original_text(start_byte, end_byte), replacement_text: replacement ) end end |
#punctuation_literal(name) ⇒ String?
350 351 352 353 354 355 356 357 358 359 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 350 def punctuation_literal(name) inner = if name.length >= 2 && ["'", '"'].include?(name[0]) && name[-1] == name[0] name[1...-1] else name end return unless inner && !inner.empty? && inner.match?(/\A[[:punct:]]+\z/) inner.b.freeze end |
#replacement_text(edit) ⇒ String?
340 341 342 343 344 345 346 347 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 340 def replacement_text(edit) return "".b if edit.kind == :delete explicit = @token_text[edit.token_name] return explicit if explicit punctuation_literal(edit.token_name) end |
#unavailable(reason, parsed, diagnostics, plan: nil) ⇒ SyntaxRepairResult
421 422 423 424 425 426 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 421 def unavailable(reason, parsed, diagnostics, plan: nil) SyntaxRepairResult.new( status: :unavailable, bounded_status: :selected, reason: reason, plan: plan, text_edits: [], syntax_root: parsed.syntax_root, diagnostics: diagnostics, updated_source: nil, validation: nil ) end |
#validate_edit_limits!(edits) ⇒ void
This method returns an undefined value.
376 377 378 379 380 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 376 def validate_edit_limits!(edits) enforce_limit!(:edits_per_operation, @limits.max_edits_per_operation, edits.length) inserted = edits.sum { |edit| edit.insert_text.bytesize } enforce_limit!(:inserted_bytes, @limits.max_inserted_bytes, inserted) end |
#validate_policy(policy) ⇒ RepairPolicy
460 461 462 463 464 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 460 def validate_policy(policy) return policy if policy.is_a?(RepairPolicy) raise ArgumentError, "policy must be an Ibex::Runtime::RepairPolicy" end |
#validate_token_text(value) ⇒ Hash[String, String]
467 468 469 470 471 472 473 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 467 def validate_token_text(value) unless value.is_a?(Hash) && value.all? { |name, text| name.is_a?(String) && text.is_a?(String) && !text.empty? } raise ArgumentError, "token_text must map String token names to nonempty String source bytes" end value.to_h { |name, text| [name.dup.freeze, text.b.freeze] }.freeze end |
#validation_status(validation, source) ⇒ Symbol
392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/ibex/runtime/syntax_repair.rb', line 392 def validation_status(validation, source) return :accepted if validation.success? && validation.syntax_root.to_source == source.text baseline_count = @baseline.diagnostics.length current_count = validation.diagnostics.length return :progress if current_count < baseline_count before = first_diagnostic_byte(@baseline) after = first_diagnostic_byte(validation) after.is_a?(Integer) && before.is_a?(Integer) && after > before ? :progress : :rejected end |