Module: Ibex::CLIOutputs
- Included in:
- CLI
- Defined in:
- lib/ibex/cli/outputs.rb,
sig/ibex/cli/outputs.rbs
Overview
Files, warnings, and progress emitted by CLI pipeline stages.
Constant Summary collapse
- WARNING_MESSAGE_IDS =
{ undeclared_terminal: "warning.undeclared_terminal", unused_terminal: "warning.unused_terminal", unused_precedence: "warning.unused_precedence", unreachable_terminal: "warning.unreachable_terminal", unreachable_nonterminal: "warning.unreachable_nonterminal", duplicate_production: "warning.duplicate_production", implicit_empty: "warning.implicit_empty", empty_language: "warning.empty_language", lexer_redos: "warning.lexer_redos" }.freeze
Instance Method Summary collapse
- #atomic_write_ir(path, source) ⇒ void
- #conflict_count(count, kind) ⇒ String
- #conflict_messages(summary, input_path) ⇒ Array[String]
- #default_output_path(input_path, extension) ⇒ String
- #format_grammar_warning(warning, input_path, promoted: false) ⇒ String
- #grammar_warning_message(warning) ⇒ String
- #handle_grammar_warnings(grammar, input_path) ⇒ void
- #ir_file_mode(path) ⇒ Integer
- #report_conflicts(automaton, input_path) ⇒ void
- #report_status(message) ⇒ void
-
#suggest_ielr(automaton, input_path) ⇒ void
rubocop:disable Metrics/AbcSize -- the bounded comparison reports each conflict class independently.
- #warning_categories(value) ⇒ Array[Symbol]
- #write_report(automaton, input_path) ⇒ void
Instance Method Details
#atomic_write_ir(path, source) ⇒ void
This method returns an undefined value.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ibex/cli/outputs.rb', line 174 def atomic_write_ir(path, source) require "tempfile" target_path = File.symlink?(path) ? File.realpath(path) : path directory = File.dirname(File.(target_path)) basename = File.basename(target_path) Tempfile.create([".#{basename}.", ".tmp"], directory) do |temporary| temporary.binmode temporary.write(source) temporary.flush temporary.fsync temporary.chmod(ir_file_mode(target_path)) temporary.close File.rename(temporary.path, target_path) end end |
#conflict_count(count, kind) ⇒ String
142 143 144 145 |
# File 'lib/ibex/cli/outputs.rb', line 142 def conflict_count(count, kind) plural = @language == "en" && count != 1 ? "s" : "" Messages.translate("conflict.count", language: @language, count: count, kind: kind, plural: plural) end |
#conflict_messages(summary, input_path) ⇒ Array[String]
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/ibex/cli/outputs.rb', line 86 def (summary, input_path) = [] #: Array[String] unless summary[:expectation_met] detail = Messages.translate( "conflict.shift_reduce_expected", language: @language, actual: summary[:sr], expected: summary[:expected_sr] ) << "#{input_path}:1:1: #{detail}" end if summary.key?(:rr_expectation_met) unless summary[:rr_expectation_met] detail = Messages.translate( "conflict.reduce_reduce_expected", language: @language, actual: summary[:rr], expected: summary[:expected_rr] ) << "#{input_path}:1:1: #{detail}" end elsif summary[:rr].positive? detail = Messages.translate("conflict.reduce_reduce", language: @language, actual: summary[:rr]) << "#{input_path}:1:1: #{detail}" end end |
#default_output_path(input_path, extension) ⇒ String
160 161 162 163 164 165 166 |
# File 'lib/ibex/cli/outputs.rb', line 160 def default_output_path(input_path, extension) directory = File.dirname(input_path) basename = File.basename(input_path) replaced = basename.sub(/\.[^.]+\z/, extension) replaced = "#{basename}#{extension}" if replaced == basename directory == "." ? replaced : File.join(directory, replaced) end |
#format_grammar_warning(warning, input_path, promoted: false) ⇒ String
52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/ibex/cli/outputs.rb', line 52 def format_grammar_warning(warning, input_path, promoted: false) location = warning[:loc] rendered = if location "#{location[:file] || input_path}:#{location[:line] || 1}:#{location[:column] || 1}" else "#{input_path}:1:1" end label_id = promoted ? "label.warning_as_error" : "label.warning" label = Messages.translate(label_id, language: @language) "#{rendered}: #{label}: #{(warning)}" end |
#grammar_warning_message(warning) ⇒ String
65 66 67 68 69 70 |
# File 'lib/ibex/cli/outputs.rb', line 65 def (warning) id = WARNING_MESSAGE_IDS.fetch(warning[:type]) id = "warning.lexer_redos_symbol" if warning[:type] == :lexer_redos && warning[:symbol] values = warning.to_h.transform_values(&:to_s) #: Hash[Symbol, String] Messages.translate(id, language: @language, **values) end |
#handle_grammar_warnings(grammar, input_path) ⇒ void
This method returns an undefined value.
37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ibex/cli/outputs.rb', line 37 def handle_grammar_warnings(grammar, input_path) categories = @options[:warnings] return if categories.nil? || categories.include?(:none) || grammar.warnings.empty? if categories.include?(:error) promoted = grammar.warnings.map do |warning| format_grammar_warning(warning, input_path, promoted: true) end raise Ibex::Error, promoted.join("\n") end grammar.warnings.each { |warning| @stderr.puts(format_grammar_warning(warning, input_path)) } end |
#ir_file_mode(path) ⇒ Integer
192 193 194 195 196 |
# File 'lib/ibex/cli/outputs.rb', line 192 def ir_file_mode(path) return File.stat(path).mode & 0o777 if File.exist?(path) 0o666 & ~File.umask end |
#report_conflicts(automaton, input_path) ⇒ void
This method returns an undefined value.
73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/ibex/cli/outputs.rb', line 73 def report_conflicts(automaton, input_path) = (automaton.conflict_summary, input_path) if @options[:warnings]&.include?(:error) && .any? promoted = .map do || Messages.translate("conflict.treated_as_error", language: @language, detail: ) end raise Ibex::Error, promoted.join("\n") end .each { || @stderr.puts() } end |
#report_status(message) ⇒ void
This method returns an undefined value.
169 170 171 |
# File 'lib/ibex/cli/outputs.rb', line 169 def report_status() @stderr.puts("ibex: #{}") if @options[:status] end |
#suggest_ielr(automaton, input_path) ⇒ void
This method returns an undefined value.
rubocop:disable Metrics/AbcSize -- the bounded comparison reports each conflict class independently.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/ibex/cli/outputs.rb', line 116 def suggest_ielr(automaton, input_path) return unless @options[:suggest_ielr] return unless automaton.algorithm == "lalr1" return unless configuration_value("parser.algorithm") == :lalr summary = automaton.conflict_summary return if summary[:expectation_met] && summary.fetch(:rr_expectation_met, summary[:rr].zero?) ielr = LALR::Builder.new( Configuration::AnalysisGrammar.for_algorithm(automaton.grammar, :ielr), algorithm: :ielr, entry_isolation: configuration_value("parser.entries") == :isolated ).build removed_sr = [summary[:sr] - ielr.conflict_summary[:sr], 0].max removed_rr = [summary[:rr] - ielr.conflict_summary[:rr], 0].max avoided = [] #: Array[String] avoided << conflict_count(removed_sr, "shift/reduce") if removed_sr.positive? avoided << conflict_count(removed_rr, "reduce/reduce") if removed_rr.positive? return if avoided.empty? conjunction = @language == "ja" ? "、" : " and " detail = Messages.translate("note.ielr", language: @language, avoided: avoided.join(conjunction)) @stderr.puts("#{input_path}:1:1: #{detail}") end |
#warning_categories(value) ⇒ Array[Symbol]
25 26 27 28 29 30 31 32 33 34 |
# File 'lib/ibex/cli/outputs.rb', line 25 def warning_categories(value) categories = value.split(",").map(&:strip).reject(&:empty?).map(&:to_sym) unknown = categories - %i[all error none] raise OptionParser::InvalidArgument, "unknown warning category #{unknown.first}" if unknown.any? if categories.empty? || (categories.include?(:none) && categories.length > 1) raise OptionParser::InvalidArgument, "warning category none cannot be combined" end categories end |
#write_report(automaton, input_path) ⇒ void
This method returns an undefined value.
148 149 150 151 152 153 154 155 156 157 |
# File 'lib/ibex/cli/outputs.rb', line 148 def write_report(automaton, input_path) require_relative "../codegen/report" path = @options[:log_file] || default_output_path(input_path, ".output") report = Codegen::Report.render( automaton, max_tokens: @options[:counterexample_max_tokens], max_configurations: @options[:counterexample_max_configurations] ) register_artifact(:report, path, report, status: true) end |