Module: Ibex::ErrorMessages::ParserV2
- Included in:
- Parser
- Defined in:
- lib/ibex/error_messages/parser_v2.rb,
sig/ibex/error_messages/parser_v2.rbs
Overview
Sentence-keyed ibex-messages v2 parsing, mixed into Parser so both versions share UTF-8, escape, and positioned-diagnostic helpers.
Instance Method Summary collapse
- #append_v2_message_line(message_lines, line, index) ⇒ void
- #close_v2_entry(opening_index, index, status, sentence, state, error_id, entry_name, message_lines) ⇒ [ Entry, Integer ]
- #parse_sentence(source, line) ⇒ Array[String]
- #parse_v2 ⇒ Document
- #parse_v2_entry(opening_index) ⇒ [ Entry, Integer ]
- #parse_v2_opening(opening_index) ⇒ [ :active | :unreachable | :removed, Array[String]?, Integer? ]
- #quoted_token_end(source, index, quote, line) ⇒ Integer
- #read_sentence_token(source, index, line) ⇒ [ String, Integer ]
- #read_v2_entry_body(opening_index, status, sentence, state) ⇒ [ Entry, Integer ]
- #read_v2_metadata(line, index, error_id, state, entry_name, message_lines) ⇒ [ String?, Integer?, String? ]
- #record_v2_entry(entry, entries, identifiers, keys) ⇒ void
- #reject_v2_duplicate(declarations, key, line, label) ⇒ void
- #scan_quoted_token(source, index, quote, line) ⇒ Integer
- #skip_sentence_space(source, index) ⇒ Integer
Instance Method Details
#append_v2_message_line(message_lines, line, index) ⇒ void
This method returns an undefined value.
127 128 129 130 131 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 127 def (, line, index) content = line.delete_prefix("|").delete_prefix(" ") content_column = line.start_with?("| ") ? 3 : 2 << decode_line(content, index + 1, content_column) end |
#close_v2_entry(opening_index, index, status, sentence, state, error_id, entry_name, message_lines) ⇒ [ Entry, Integer ]
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 96 def close_v2_entry(opening_index, index, status, sentence, state, error_id, entry_name, ) fail_at(opening_index + 1, 1, "missing `## E0001` error id") unless error_id = .empty? ? nil : .join("\n") fail_at(opening_index + 1, 1, "message must not be empty") if &.strip&.empty? entry = Entry.new( state: state, status: status, message: , line: opening_index + 1, sentence: sentence, error_id: error_id, entry: entry_name ) [entry, index + 1] end |
#parse_sentence(source, line) ⇒ Array[String]
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 134 def parse_sentence(source, line) tokens = [] #: Array[String] index = 0 loop do index = skip_sentence_space(source, index) break if index == source.length token, index = read_sentence_token(source, index, line) tokens << token end fail_at(line, 1, "error sentence must contain at least one token") if tokens.empty? tokens end |
#parse_v2 ⇒ Document
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 17 def parse_v2 entries = [] #: Array[Entry] identifiers = {} #: Hash[String, Integer] keys = {} #: Hash[String, Integer] index = 1 while index < @lines.length line = @lines.fetch(index) if ignorable?(line) && !line.lstrip.start_with?("##") index += 1 next end entry, index = parse_v2_entry(index) record_v2_entry(entry, entries, identifiers, keys) end Document.new(version: 2, entries: entries) end |
#parse_v2_entry(opening_index) ⇒ [ Entry, Integer ]
48 49 50 51 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 48 def parse_v2_entry(opening_index) status, sentence, state = parse_v2_opening(opening_index) read_v2_entry_body(opening_index, status, sentence, state) end |
#parse_v2_opening(opening_index) ⇒ [ :active | :unreachable | :removed, Array[String]?, Integer? ]
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 55 def parse_v2_opening(opening_index) opening = @lines.fetch(opening_index) if (legacy = opening.match(/\Alegacy-state:[ \t]*([0-9]+)\z/)) return [:removed, nil, Integer(legacy[1] || "0", 10)] end if (sentence = opening.match(/\A(sentence|unreachable):[ \t]*(.*)\z/)) status = sentence[1] == "unreachable" ? :unreachable : :active #: :unreachable | :active return [status, parse_sentence(sentence[2] || "", opening_index + 1), nil] end fail_at(opening_index + 1, column(opening), "expected `sentence: TOKENS`, `unreachable: TOKENS`, or `legacy-state: N`") end |
#quoted_token_end(source, index, quote, line) ⇒ Integer
168 169 170 171 172 173 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 168 def quoted_token_end(source, index, quote, line) ending = scan_quoted_token(source, index, quote, line) return ending if ending == source.length || source[ending]&.match?(/\s/) fail_at(line, ending + 1, "quoted token must be followed by whitespace") end |
#read_sentence_token(source, index, line) ⇒ [ String, Integer ]
155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 155 def read_sentence_token(source, index, line) start = index quote = source[index] if ["'", '"'].include?(quote) index = quoted_token_end(source, index, quote || "", line) else index += 1 while index < source.length && !source[index]&.match?(/\s/) end token = source[start...index] || raise(Ibex::Error, "missing sentence token") [token, index] end |
#read_v2_entry_body(opening_index, status, sentence, state) ⇒ [ Entry, Integer ]
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 72 def read_v2_entry_body(opening_index, status, sentence, state) error_id = nil #: String? entry_name = nil #: String? = [] #: Array[String] index = opening_index + 1 while index < @lines.length line = @lines.fetch(index) if line.strip == "end" return close_v2_entry( opening_index, index, status, sentence, state, error_id, entry_name, ) end error_id, state, entry_name = ( line, index, error_id, state, entry_name, ) index += 1 end fail_at(opening_index + 1, 1, "unterminated error-sentence entry") end |
#read_v2_metadata(line, index, error_id, state, entry_name, message_lines) ⇒ [ String?, Integer?, String? ]
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 109 def (line, index, error_id, state, entry_name, ) stripped = line.strip if (match = stripped.match(/\A##[ \t]+(E[0-9]{4,})\z/)) fail_at(index + 1, column(line), "duplicate error id") if error_id error_id = match[1] elsif (match = stripped.match(/\A# state:[ \t]*([0-9]+)\z/)) state = Integer(match[1] || "0", 10) elsif (match = stripped.match(/\A# entry:[ \t]*(\S+)\z/)) entry_name = match[1] elsif line.start_with?("|") (, line, index) elsif !ignorable?(line) fail_at(index + 1, column(line), "expected metadata, a `| ` message line, a comment, or `end`") end [error_id, state, entry_name] end |
#record_v2_entry(entry, entries, identifiers, keys) ⇒ void
This method returns an undefined value.
37 38 39 40 41 42 43 44 45 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 37 def record_v2_entry(entry, entries, identifiers, keys) error_id = entry.error_id || raise(Ibex::Error, "missing error id") reject_v2_duplicate(identifiers, error_id, entry.line, "error id") key = entry.sentence ? "#{entry.entry}\0#{entry.sentence.join("\0")}" : "legacy\0#{entry.state}" reject_v2_duplicate(keys, key, entry.line, "sentence") identifiers[error_id] = entry.line keys[key] = entry.line entries << entry end |
#reject_v2_duplicate(declarations, key, line, label) ⇒ void
This method returns an undefined value.
192 193 194 195 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 192 def reject_v2_duplicate(declarations, key, line, label) previous = declarations[key] fail_at(line, 1, "duplicate #{label}; first declared at line #{previous}") if previous end |
#scan_quoted_token(source, index, quote, line) ⇒ Integer
176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 176 def scan_quoted_token(source, index, quote, line) index += 1 while index < source.length character = source[index] if character == "\\" index += 2 elsif character == quote return index + 1 else index += 1 end end fail_at(line, source.length + 1, "unterminated quoted token") end |
#skip_sentence_space(source, index) ⇒ Integer
149 150 151 152 |
# File 'lib/ibex/error_messages/parser_v2.rb', line 149 def skip_sentence_space(source, index) index += 1 while index < source.length && source[index]&.match?(/\s/) index end |