Add command line switch to output warnings to stderr - close #26
This commit is contained in:
parent
4ae5ab79b3
commit
2dd89445fc
@ -31,10 +31,10 @@ class Propane
|
|||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def run(input_file, output_file, log_file)
|
def run(input_file, output_file, log_file, options)
|
||||||
begin
|
begin
|
||||||
grammar = Grammar.new(File.read(input_file))
|
grammar = Grammar.new(File.read(input_file))
|
||||||
generator = Generator.new(grammar, output_file, log_file)
|
generator = Generator.new(grammar, output_file, log_file, options)
|
||||||
generator.generate
|
generator.generate
|
||||||
rescue Error => e
|
rescue Error => e
|
||||||
$stderr.puts e.message
|
$stderr.puts e.message
|
||||||
|
@ -4,15 +4,21 @@ class Propane
|
|||||||
USAGE = <<EOF
|
USAGE = <<EOF
|
||||||
Usage: #{$0} [options] <input-file> <output-file>
|
Usage: #{$0} [options] <input-file> <output-file>
|
||||||
Options:
|
Options:
|
||||||
--log LOG Write log file
|
--log LOG Write log file. This will show all parser states and their
|
||||||
--version Show program version and exit
|
associated shifts and reduces. It can be helpful when
|
||||||
-h, --help Show this usage and exit
|
debugging a grammar.
|
||||||
|
--version Show program version and exit.
|
||||||
|
-h, --help Show this usage and exit.
|
||||||
|
-w Treat warnings as errors. This option will treat shift/reduce
|
||||||
|
conflicts as fatal errors and will print them to stderr in
|
||||||
|
addition to the log file.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def run(args)
|
def run(args)
|
||||||
params = []
|
params = []
|
||||||
|
options = {}
|
||||||
log_file = nil
|
log_file = nil
|
||||||
i = 0
|
i = 0
|
||||||
while i < args.size
|
while i < args.size
|
||||||
@ -29,6 +35,8 @@ EOF
|
|||||||
when "-h", "--help"
|
when "-h", "--help"
|
||||||
puts USAGE
|
puts USAGE
|
||||||
return 0
|
return 0
|
||||||
|
when "-w"
|
||||||
|
options[:warnings_as_errors] = true
|
||||||
when /^-/
|
when /^-/
|
||||||
$stderr.puts "Error: unknown option #{arg}"
|
$stderr.puts "Error: unknown option #{arg}"
|
||||||
return 1
|
return 1
|
||||||
@ -45,7 +53,7 @@ EOF
|
|||||||
$stderr.puts "Error: cannot read #{params[0]}"
|
$stderr.puts "Error: cannot read #{params[0]}"
|
||||||
return 2
|
return 2
|
||||||
end
|
end
|
||||||
Propane.run(*params, log_file)
|
Propane.run(*params, log_file, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@ class Propane
|
|||||||
|
|
||||||
class Generator
|
class Generator
|
||||||
|
|
||||||
def initialize(grammar, output_file, log_file)
|
def initialize(grammar, output_file, log_file, options)
|
||||||
@grammar = grammar
|
@grammar = grammar
|
||||||
@output_file = output_file
|
@output_file = output_file
|
||||||
if log_file
|
if log_file
|
||||||
@ -16,6 +16,7 @@ class Propane
|
|||||||
else
|
else
|
||||||
"d"
|
"d"
|
||||||
end
|
end
|
||||||
|
@options = options
|
||||||
process_grammar!
|
process_grammar!
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ class Propane
|
|||||||
# Generate the lexer.
|
# Generate the lexer.
|
||||||
@lexer = Lexer.new(@grammar)
|
@lexer = Lexer.new(@grammar)
|
||||||
# Generate the parser.
|
# Generate the parser.
|
||||||
@parser = Parser.new(@grammar, rule_sets, @log)
|
@parser = Parser.new(@grammar, rule_sets, @log, @options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check that any referenced ptypes have been defined.
|
# Check that any referenced ptypes have been defined.
|
||||||
|
@ -7,13 +7,14 @@ class Propane
|
|||||||
attr_reader :reduce_table
|
attr_reader :reduce_table
|
||||||
attr_reader :rule_sets
|
attr_reader :rule_sets
|
||||||
|
|
||||||
def initialize(grammar, rule_sets, log)
|
def initialize(grammar, rule_sets, log, options)
|
||||||
@grammar = grammar
|
@grammar = grammar
|
||||||
@rule_sets = rule_sets
|
@rule_sets = rule_sets
|
||||||
@log = log
|
@log = log
|
||||||
@item_sets = []
|
@item_sets = []
|
||||||
@item_sets_set = {}
|
@item_sets_set = {}
|
||||||
@sr_conflicts = Set.new
|
@warnings = Set.new
|
||||||
|
@options = options
|
||||||
start_item = Item.new(grammar.rules.first, 0)
|
start_item = Item.new(grammar.rules.first, 0)
|
||||||
eval_item_sets = Set[ItemSet.new([start_item])]
|
eval_item_sets = Set[ItemSet.new([start_item])]
|
||||||
|
|
||||||
@ -41,6 +42,9 @@ class Propane
|
|||||||
build_follow_sets!
|
build_follow_sets!
|
||||||
build_tables!
|
build_tables!
|
||||||
write_log!
|
write_log!
|
||||||
|
if @warnings.size > 0 && @options[:warnings_as_errors]
|
||||||
|
raise Error.new("Fatal errors (-w):\n" + @warnings.join("\n"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -65,7 +69,7 @@ class Propane
|
|||||||
if item_set.reduce_actions
|
if item_set.reduce_actions
|
||||||
shift_entries.each do |shift_entry|
|
shift_entries.each do |shift_entry|
|
||||||
if item_set.reduce_actions.include?(shift_entry[:symbol])
|
if item_set.reduce_actions.include?(shift_entry[:symbol])
|
||||||
@sr_conflicts << [shift_entry[:symbol], item_set.reduce_actions[shift_entry[:symbol]]]
|
@warnings << "Shift/Reduce conflict between token #{shift_entry[:symbol].name} and rule #{item_set.reduce_actions[shift_entry[:symbol]].name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -318,11 +322,11 @@ class Propane
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if @sr_conflicts.size > 0
|
if @warnings.size > 0
|
||||||
@log.puts
|
@log.puts
|
||||||
@log.puts "Shift/Reduce Conflicts:"
|
@log.puts "Warnings:"
|
||||||
@sr_conflicts.each do |sr_conflict|
|
@warnings.each do |warning|
|
||||||
@log.puts " Shift/Reduce conflict between #{sr_conflict[0].name} and #{sr_conflict[1].name}"
|
@log.puts " #{warning}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -54,6 +54,7 @@ EOF
|
|||||||
else
|
else
|
||||||
command += %W[spec/run/testparser#{options[:name]}.propane spec/run/testparser#{options[:name]}.#{options[:language]} --log spec/run/testparser#{options[:name]}.log]
|
command += %W[spec/run/testparser#{options[:name]}.propane spec/run/testparser#{options[:name]}.#{options[:language]} --log spec/run/testparser#{options[:name]}.log]
|
||||||
end
|
end
|
||||||
|
command += (options[:extra_args] || [])
|
||||||
if (options[:capture])
|
if (options[:capture])
|
||||||
stdout, stderr, status = Open3.capture3(*command)
|
stdout, stderr, status = Open3.capture3(*command)
|
||||||
Results.new(stdout, stderr, status)
|
Results.new(stdout, stderr, status)
|
||||||
@ -195,7 +196,21 @@ EOF
|
|||||||
results = run_propane(capture: true)
|
results = run_propane(capture: true)
|
||||||
expect(results.stderr).to eq ""
|
expect(results.stderr).to eq ""
|
||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
expect(File.binread("spec/run/testparser.log")).to match %r{Shift/Reduce conflict between b and As2}
|
expect(File.binread("spec/run/testparser.log")).to match %r{Shift/Reduce conflict between token b and rule As2}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "errors on shift/reduce conflicts with -w" do
|
||||||
|
write_grammar <<EOF
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
Start -> As? b?;
|
||||||
|
As -> a As2?;
|
||||||
|
As2 -> b a As2?;
|
||||||
|
EOF
|
||||||
|
results = run_propane(extra_args: %w[-w], capture: true)
|
||||||
|
expect(results.stderr).to match %r{Fatal errors \(-w\).*Shift/Reduce conflict between token b and rule As2}m
|
||||||
|
expect(results.status).to_not eq 0
|
||||||
|
expect(File.binread("spec/run/testparser.log")).to match %r{Shift/Reduce conflict between token b and rule As2}
|
||||||
end
|
end
|
||||||
|
|
||||||
%w[d c].each do |language|
|
%w[d c].each do |language|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user