Lrama v0.5.6
This commit is contained in:
parent
c75d54a36c
commit
4655d2108e
Notes:
git
2023-09-13 09:04:10 +00:00
@ -3,4 +3,4 @@
|
||||
$LOAD_PATH << File.join(__dir__, "../lib")
|
||||
require "lrama"
|
||||
|
||||
Lrama::Command.new(ARGV.dup).run
|
||||
Lrama::Command.new.run(ARGV.dup)
|
||||
|
@ -5,6 +5,8 @@ require "lrama/counterexamples"
|
||||
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"
|
||||
|
@ -1,53 +1,34 @@
|
||||
require 'optparse'
|
||||
|
||||
module Lrama
|
||||
class Command
|
||||
def initialize(argv)
|
||||
@argv = argv
|
||||
def run(argv)
|
||||
options = OptionParser.new.parse(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
|
||||
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
|
||||
|
||||
@ -55,108 +36,5 @@ module Lrama
|
||||
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 !@report.empty? && @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
|
||||
|
@ -213,19 +213,33 @@ module Lrama
|
||||
string, line = lex_string(ss, "'", line, lines)
|
||||
str << string
|
||||
next
|
||||
|
||||
# $ references
|
||||
# It need to wrap an identifier with brackets to use ".-" for identifiers
|
||||
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
|
||||
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
|
||||
references << [:dollar, "$", tag, str.length, str.length + ss[0].length - 1]
|
||||
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
|
||||
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
|
||||
references << [:dollar, Integer(ss[2]), tag, str.length, str.length + ss[0].length - 1]
|
||||
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_.][-a-zA-Z0-9_.]*)/) # $foo, $expr, $<long>program
|
||||
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
|
||||
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
|
||||
references << [:dollar, ss[2], tag, str.length, str.length + ss[0].length - 1]
|
||||
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $expr.right, $expr-right, $<long>program (named reference with brackets)
|
||||
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
|
||||
references << [:dollar, ss[2], tag, str.length, str.length + ss[0].length - 1]
|
||||
|
||||
# @ references
|
||||
# It need to wrap an identifier with brackets to use ".-" for identifiers
|
||||
when ss.scan(/@\$/) # @$
|
||||
references << [:at, "$", nil, str.length, str.length + ss[0].length - 1]
|
||||
when ss.scan(/@(\d+)/) # @1
|
||||
references << [:at, Integer(ss[1]), nil, str.length, str.length + ss[0].length - 1]
|
||||
when ss.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
|
||||
references << [:at, ss[1], nil, str.length, str.length + ss[0].length - 1]
|
||||
when ss.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @expr.right, @expr-right (named reference with brackets)
|
||||
references << [:at, ss[1], nil, str.length, str.length + ss[0].length - 1]
|
||||
|
||||
when ss.scan(/{/)
|
||||
brace_count += 1
|
||||
when ss.scan(/}/)
|
||||
|
@ -28,7 +28,13 @@ module Lrama
|
||||
if lhs.referred_by?(ref_name)
|
||||
'$'
|
||||
else
|
||||
rhs.find_index {|token| token.referred_by?(ref_name) } + 1
|
||||
index = rhs.find_index {|token| token.referred_by?(ref_name) }
|
||||
|
||||
if index
|
||||
index + 1
|
||||
else
|
||||
raise "'#{ref_name}' is invalid name."
|
||||
end
|
||||
end
|
||||
[ref[0], value, ref[2], ref[3], ref[4]]
|
||||
else
|
||||
|
124
tool/lrama/lib/lrama/option_parser.rb
Normal file
124
tool/lrama/lib/lrama/option_parser.rb
Normal file
@ -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 !@options.grammar_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 !@report.empty? && @options.report_file.nil? && @options.grammar_file
|
||||
@options.report_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".output"
|
||||
end
|
||||
|
||||
if !@options.header_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
tool/lrama/lib/lrama/options.rb
Normal file
23
tool/lrama/lib/lrama/options.rb
Normal file
@ -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
|
@ -1,3 +1,3 @@
|
||||
module Lrama
|
||||
VERSION = "0.5.5".freeze
|
||||
VERSION = "0.5.6".freeze
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user