Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve output when executing the help command #97

Merged
merged 6 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exe/lrama
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
$LOAD_PATH << File.join(__dir__, "../lib")
require "lrama"

Lrama::Command.new(ARGV.dup).run
Lrama::Command.new.run(ARGV.dup)
2 changes: 2 additions & 0 deletions lib/lrama.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
require "lrama/digraph"
require "lrama/grammar"
require "lrama/lexer"
require "lrama/option_parser"
require "lrama/options"
require "lrama/output"
require "lrama/parser"
require "lrama/report"
Expand Down
152 changes: 15 additions & 137 deletions lib/lrama/command.rb
Original file line number Diff line number Diff line change
@@ -1,162 +1,40 @@
require 'optparse'

module Lrama
class Command
def initialize(argv)
@argv = argv

@skeleton = "bison/yacc.c"
@header = false
@header_file = nil
@report = []
@report_file = nil
@outfile = "y.tab.c"
@trace = []
@error_recovery = false
@grammar_file = nil
@report_file = nil
@trace_opts = nil
@report_opts = nil
end
def run(argv)
options = OptionParser.new.parse(argv)

def run
parse_option

Report::Duration.enable if @trace_opts[:time]
Report::Duration.enable if options.trace_opts[:time]

warning = Lrama::Warning.new
grammar = Lrama::Parser.new(@y.read).parse
@y.close if @y != STDIN
states = Lrama::States.new(grammar, warning, trace_state: (@trace_opts[:automaton] || @trace_opts[:closure]))
grammar = Lrama::Parser.new(options.y.read).parse
options.y.close if options.y != STDIN
states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
states.compute
context = Lrama::Context.new(states)

if @report_file
if options.report_file
reporter = Lrama::StatesReporter.new(states)
File.open(@report_file, "w+") do |f|
reporter.report(f, **@report_opts)
File.open(options.report_file, "w+") do |f|
reporter.report(f, **options.report_opts)
end
end

File.open(@outfile, "w+") do |f|
File.open(options.outfile, "w+") do |f|
Lrama::Output.new(
out: f,
output_file_path: @outfile,
template_name: @skeleton,
grammar_file_path: @grammar_file,
header_file_path: @header_file,
output_file_path: options.outfile,
template_name: options.skeleton,
grammar_file_path: options.grammar_file,
header_file_path: options.header_file,
context: context,
grammar: grammar,
error_recovery: @error_recovery,
error_recovery: options.error_recovery,
).render
end

if warning.has_error?
exit 1
end
end

private

def validate_report(report)
bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
others = %w[verbose]
list = bison_list + others
not_supported = %w[cex none]
h = { grammar: true }

report.each do |r|
if list.include?(r) && !not_supported.include?(r)
h[r.to_sym] = true
else
raise "Invalid report option \"#{r}\"."
end
end

if h[:all]
(bison_list - not_supported).each do |r|
h[r.to_sym] = true
end

h.delete(:all)
end

return h
end

def validate_trace(trace)
list = %w[
none locations scan parse automaton bitsets
closure grammar resource sets muscles tools
m4-early m4 skeleton time ielr cex all
]
h = {}

trace.each do |t|
if list.include?(t)
h[t.to_sym] = true
else
raise "Invalid trace option \"#{t}\"."
end
end

return h
end

def parse_option
opt = OptionParser.new

# opt.on('-h') {|v| p v }
opt.on('-V', '--version') {|v| puts "lrama #{Lrama::VERSION}"; exit 0 }

# Tuning the Parser
opt.on('-S', '--skeleton=FILE') {|v| @skeleton = v }
opt.on('-t') { } # Do nothing

# Output Files:
opt.on('-h', '--header=[FILE]') {|v| @header = true; @header_file = v }
opt.on('-d') { @header = true }
opt.on('-r', '--report=THINGS', Array) {|v| @report = v }
opt.on('--report-file=FILE') {|v| @report_file = v }
opt.on('-v') { } # Do nothing
opt.on('-o', '--output=FILE') {|v| @outfile = v }

# Hidden
opt.on('--trace=THINGS', Array) {|v| @trace = v }

# Error Recovery
opt.on('-e') {|v| @error_recovery = true }

opt.parse!(@argv)

@trace_opts = validate_trace(@trace)
@report_opts = validate_report(@report)

@grammar_file = @argv.shift

if !@grammar_file
abort "File should be specified\n"
end

if @grammar_file == '-'
@grammar_file = @argv.shift or abort "File name for STDIN should be specified\n"
@y = STDIN
else
@y = File.open(@grammar_file, 'r')
end

if [email protected]? && @report_file.nil? && @grammar_file
@report_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".output"
end

if !@header_file && @header
case
when @outfile
@header_file = File.dirname(@outfile) + "/" + File.basename(@outfile, ".*") + ".h"
when @grammar_file
@header_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".h"
end
end
end
end
end
124 changes: 124 additions & 0 deletions lib/lrama/option_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
require 'optparse'

module Lrama
# Handle option parsing for the command line interface.
class OptionParser
def initialize
@options = Options.new
@trace = []
@report = []
end

def parse(argv)
parse_by_option_parser(argv)

@options.trace_opts = validate_trace(@trace)
@options.report_opts = validate_report(@report)
@options.grammar_file = argv.shift

if [email protected]_file
abort "File should be specified\n"
end

if @options.grammar_file == '-'
@options.grammar_file = argv.shift or abort "File name for STDIN should be specified\n"
else
@options.y = File.open(@options.grammar_file, 'r')
end

if [email protected]? && @options.report_file.nil? && @options.grammar_file
@options.report_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".output"
end

if [email protected]_file && @options.header
case
when @options.outfile
@options.header_file = File.dirname(@options.outfile) + "/" + File.basename(@options.outfile, ".*") + ".h"
when @options.grammar_file
@options.header_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".h"
end
end

@options
end

private

def parse_by_option_parser(argv)
::OptionParser.new do |o|
o.banner = <<~BANNER
Lrama is LALR (1) parser generator written by Ruby.

Usage: lrama [options] FILE
BANNER
o.separator ''
o.separator 'Tuning the Parser:'
o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v }
o.on('-t', 'reserved, do nothing') { }
o.separator ''
o.separator 'Output:'
o.on('-h', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v }
o.on('-d', 'also produce a header file') { @options.header = true }
o.on('-r', '--report=THINGS', Array, 'also produce details on the automaton') {|v| @report = v }
o.on('--report-file=FILE', 'also produce details on the automaton output to a file named FILE') {|v| @options.report_file = v }
o.on('-o', '--output=FILE', 'leave output to FILE') {|v| @options.outfile = v }
o.on('--trace=THINGS', Array, 'also output trace logs at runtime') {|v| @trace = v }
o.on('-v', 'reserved, do nothing') { }
o.separator ''
o.separator 'Error Recovery:'
o.on('-e', 'enable error recovery') {|v| @options.error_recovery = true }
o.separator ''
o.separator 'Other options:'
o.on('-V', '--version', "output version information and exit") {|v| puts "lrama #{Lrama::VERSION}"; exit 0 }
o.on('--help', "display this help and exit") {|v| puts o; exit 0 }
o.separator ''
o.parse!(argv)
end
end

def validate_report(report)
bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
others = %w[verbose]
list = bison_list + others
not_supported = %w[cex none]
h = { grammar: true }

report.each do |r|
if list.include?(r) && !not_supported.include?(r)
h[r.to_sym] = true
else
raise "Invalid report option \"#{r}\"."
end
end

if h[:all]
(bison_list - not_supported).each do |r|
h[r.to_sym] = true
end

h.delete(:all)
end

return h
end

def validate_trace(trace)
list = %w[
none locations scan parse automaton bitsets
closure grammar resource sets muscles tools
m4-early m4 skeleton time ielr cex all
]
h = {}

trace.each do |t|
if list.include?(t)
h[t.to_sym] = true
else
raise "Invalid trace option \"#{t}\"."
end
end

return h
end
end
end
23 changes: 23 additions & 0 deletions lib/lrama/options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Lrama
# Command line options.
class Options
attr_accessor :skeleton, :header, :header_file,
:report_file, :outfile,
:error_recovery, :grammar_file,
:report_file, :trace_opts, :report_opts, :y

def initialize
@skeleton = "bison/yacc.c"
@header = false
@header_file = nil
@report_file = nil
@outfile = "y.tab.c"
@error_recovery = false
@grammar_file = nil
@report_file = nil
@trace_opts = nil
@report_opts = nil
@y = STDIN
end
end
end
Loading