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_MESSAGES =
{ undeclared_terminal: ->(warning) { "undeclared terminal #{warning[:symbol]}" }, unused_terminal: ->(warning) { "unused terminal #{warning[:symbol]}" }, unused_precedence: ->(warning) { "unused precedence #{warning[:symbol]}" }, unreachable_terminal: ->(warning) { "declared terminal #{warning[:symbol]} is unreachable" }, unreachable_nonterminal: ->(warning) { "unreachable nonterminal #{warning[:symbol]}" }, duplicate_production: lambda do |warning| "duplicate production #{warning[:production]} (first defined as #{warning[:original]})" end, implicit_empty: ->(_warning) { "implicit empty alternative; write %empty to document intent" }, empty_language: ->(warning) { "start symbol #{warning[:symbol]} derives no terminal sentence" }, lexer_redos: lambda do |warning| subject = warning[:symbol] ? " for #{warning[:symbol]}" : "" "lexer pattern#{subject} may exhibit excessive backtracking" end }.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) ⇒ 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
- #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.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/ibex/cli/outputs.rb', line 147 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
116 117 118 |
# File 'lib/ibex/cli/outputs.rb', line 116 def conflict_count(count, kind) "#{count} #{kind} conflict#{'s' unless count == 1}" end |
#conflict_messages(summary, input_path) ⇒ Array[String]
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/ibex/cli/outputs.rb', line 77 def (summary, input_path) = [] #: Array[String] unless summary[:expectation_met] << "#{input_path}:1:1: #{summary[:sr]} shift/reduce conflicts; expected #{summary[:expected_sr]}" end if summary.key?(:rr_expectation_met) unless summary[:rr_expectation_met] << "#{input_path}:1:1: #{summary[:rr]} reduce/reduce conflicts; expected #{summary[:expected_rr]}" end elsif summary[:rr].positive? << "#{input_path}:1:1: #{summary[:rr]} reduce/reduce conflicts" end end |
#default_output_path(input_path, extension) ⇒ String
133 134 135 136 137 138 139 |
# File 'lib/ibex/cli/outputs.rb', line 133 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) ⇒ String
55 56 57 58 59 60 61 62 63 64 |
# File 'lib/ibex/cli/outputs.rb', line 55 def format_grammar_warning(warning, input_path) location = warning[:loc] rendered = if location "#{location[:file] || input_path}:#{location[:line] || 1}:#{location[:column] || 1}" else "#{input_path}:1:1" end formatter = WARNING_MESSAGES.fetch(warning[:type]) { ->(item) { item[:type].to_s.tr("_", " ") } } "#{rendered}: warning: #{formatter.call(warning)}" end |
#handle_grammar_warnings(grammar, input_path) ⇒ void
This method returns an undefined value.
41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/ibex/cli/outputs.rb', line 41 def handle_grammar_warnings(grammar, input_path) categories = @options[:warnings] return if categories.nil? || categories.include?(:none) || grammar.warnings.empty? = grammar.warnings.map { |warning| format_grammar_warning(warning, input_path) } if categories.include?(:error) promoted = .map { || .sub(": warning:", ": warning treated as error:") } raise Ibex::Error, promoted.join("\n") end .each { || @stderr.puts() } end |
#ir_file_mode(path) ⇒ Integer
165 166 167 168 169 |
# File 'lib/ibex/cli/outputs.rb', line 165 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.
67 68 69 70 71 72 73 74 |
# File 'lib/ibex/cli/outputs.rb', line 67 def report_conflicts(automaton, input_path) = (automaton.conflict_summary, input_path) if @options[:warnings]&.include?(:error) && .any? raise Ibex::Error, .map { || "#{}; conflict treated as error" }.join("\n") end .each { || @stderr.puts() } end |
#report_status(message) ⇒ void
This method returns an undefined value.
142 143 144 |
# File 'lib/ibex/cli/outputs.rb', line 142 def report_status() @stderr.puts("ibex: #{}") if @options[:status] end |
#suggest_ielr(automaton, input_path) ⇒ void
This method returns an undefined value.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/ibex/cli/outputs.rb', line 93 def suggest_ielr(automaton, input_path) return unless @options[:suggest_ielr] return unless automaton.algorithm == "lalr1" return unless [nil, :lalr].include?(@options[:algorithm]) summary = automaton.conflict_summary return if summary[:expectation_met] && summary.fetch(:rr_expectation_met, summary[:rr].zero?) ielr = LALR::Builder.new( automaton.grammar, algorithm: :ielr, entry_isolation: @options[:entry_isolation] == true ).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? @stderr.puts("#{input_path}:1:1: note: --algorithm=ielr avoids #{avoided.join(' and ')}; " \ "consider --algorithm=ielr") end |
#warning_categories(value) ⇒ Array[Symbol]
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/ibex/cli/outputs.rb', line 29 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.
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/ibex/cli/outputs.rb', line 121 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 |