Class: Ibex::VerificationReport::CanonicalIR
- Inherits:
-
Object
- Object
- Ibex::VerificationReport::CanonicalIR
- Defined in:
- lib/ibex/verification_report/canonical_ir.rb,
sig/ibex/verification_report/canonical_ir.rbs
Overview
Rebuilds immutable IR with source locations expressed as report logical identities.
Constant Summary collapse
- LOGICAL_ROOT =
"input"
Instance Method Summary collapse
- #absolute_file?(file) ⇒ Boolean
- #build ⇒ IR::Automaton
- #digest(grammar) ⇒ String
- #generate_json(value) ⇒ String
-
#initialize(automaton, source_records:) ⇒ CanonicalIR
constructor
A new instance of CanonicalIR.
- #logical_file(file) ⇒ String
- #normalize_for_json(value) ⇒ json_value
- #normalize_source_identity!(value) ⇒ json_value
- #record_matches?(record, file) ⇒ Boolean
Constructor Details
#initialize(automaton, source_records:) ⇒ CanonicalIR
Returns a new instance of CanonicalIR.
17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 17 def initialize(automaton, source_records:) raise ArgumentError, "source_records must not be empty" if source_records.empty? if source_records.length > LogicalPath::MAX_INPUT_FILES raise ArgumentError, "verification reports support at most #{LogicalPath::MAX_INPUT_FILES} input files" end @automaton = automaton @logical_files = source_records.map.with_index do |record, index| [record, LogicalPath.input(record.path, index)] end end |
Instance Method Details
#absolute_file?(file) ⇒ Boolean
113 114 115 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 113 def absolute_file?(file) file.start_with?("/") || file.match?(%r{\A[A-Za-z]:[\\/]}) end |
#build ⇒ IR::Automaton
30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 30 def build document = JSON.parse(IR::Serialize.dump(@automaton)) normalize_source_identity!(document) grammar = IR::Validator.validate(generate_json(document.fetch("grammar"))) raise ArgumentError, "canonical bundle grammar did not validate" unless grammar.is_a?(IR::Grammar) document["grammar_digest"] = digest(grammar) automaton = IR::Validator.validate(generate_json(document)) raise ArgumentError, "canonical bundle automaton did not validate" unless automaton.is_a?(IR::Automaton) automaton end |
#digest(grammar) ⇒ String
118 119 120 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 118 def digest(grammar) "sha256:#{Digest::SHA256.hexdigest(IR::Serialize.dump(grammar))}" end |
#generate_json(value) ⇒ String
64 65 66 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 64 def generate_json(value) JSON.generate(normalize_for_json(value)) end |
#logical_file(file) ⇒ String
87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 87 def logical_file(file) file_bytes = file.b logical = @logical_files.find { |_record, path| path.b == file_bytes } return logical.fetch(1) if logical candidates = @logical_files.select { |record, _path| record_matches?(record, file) } return candidates.fetch(0).fetch(1) if candidates.one? detail = candidates.empty? ? "is not present in source_records" : "matches multiple source_records" raise ArgumentError, "IR source location #{file.inspect} #{detail}" end |
#normalize_for_json(value) ⇒ json_value
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 69 def normalize_for_json(value) case value when String normalized = value.dup.force_encoding(Encoding::UTF_8) normalized.valid_encoding? ? normalized : normalized.scrub when Array value.map { |child| normalize_for_json(child) } when Hash normalized = {} #: Hash[json_value, json_value] # steep:ignore UnannotatedEmptyCollection value.each_with_object(normalized) do |(key, child), result| result[normalize_for_json(key)] = normalize_for_json(child) end else value end end |
#normalize_source_identity!(value) ⇒ json_value
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 46 def normalize_source_identity!(value) case value when Array then value.each { |child| normalize_source_identity!(child) } when Hash value.each do |key, child| value[key] = if key == "file" && child.is_a?(String) logical_file(child) elsif key == "root" && child.is_a?(String) LOGICAL_ROOT else normalize_source_identity!(child) end end end value end |
#record_matches?(record, file) ⇒ Boolean
100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/ibex/verification_report/canonical_ir.rb', line 100 def record_matches?(record, file) paths = [record.path, *record.access_paths].map(&:b) file_bytes = file.b return true if paths.include?(file_bytes) return true if absolute_file?(file) && paths.include?(File.(file).b) suffix = "/".b + file_bytes !absolute_file?(file) && paths.any? do |path| path == file_bytes || path.end_with?(suffix) end end |