ruby/libexec/racc2y

196 строки
4.5 KiB
Ruby
Executable File

#!/usr/local/bin/ruby
#
# $Id$
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is feee software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the LGPL, see the file "COPYING".
#
require 'racc/grammarfileparser'
require 'racc/info'
require 'optparse'
def main
@with_action = true
with_header = false
with_inner = false
with_footer = false
output = nil
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [-AHIF] [-oFILENAME] GRAMMARFILE"
parser.on('-o', '--output=FILENAME', 'output file name [<input>.yacc]') {|name|
output = name
}
parser.on('-A', '--without-action', 'Does not include actions.') {
@with_action = false
}
parser.on('-H', '--with-header', 'Includes header part.') {
with_header = true
}
parser.on('-I', '--with-inner', 'Includes inner part.') {
with_inner = true
}
parser.on('-F', '--with-footer', 'Includes footer part.') {
with_footer = true
}
parser.on('--version', 'Prints version and quit.') {
puts "racc2y version #{Racc::Version}"
exit 0
}
parser.on('--copyright', 'Prints copyright and quit.') {
puts Racc::Copyright
exit 0
}
parser.on('--help', 'Prints this message and quit.') {
puts parser.help
exit 1
}
begin
parser.parse!
rescue OptionParser::ParseError => err
$stderr.puts err.message
$stderr.puts parser.help
exit 1
end
if ARGV.empty?
$stderr.puts "no input file"
exit 1
end
unless ARGV.size == 1
$stderr.puts "too many inputs"
exit 1
end
input = ARGV[0]
begin
result = Racc::GrammarFileParser.parse_file(input)
result.grammar.init
File.open(output || "#{input}.yacc", 'w') {|f|
f.puts "/* generated from #{input} */"
if with_header
f.puts
f.puts '%{'
print_user_codes f, result.params.header
f.puts '%}'
end
f.puts
print_terminals f, result.grammar
f.puts
print_precedence_table f, precedence_table(result.grammar)
f.puts
f.puts '%%'
print_grammar f, result.grammar
f.puts '%%'
if with_inner
f.puts '/*---- inner ----*/'
print_user_codes f, result.params.inner
end
if with_footer
f.puts '/*---- footer ----*/'
print_user_codes f, result.params.footer
end
}
rescue SystemCallError => err
$stderr.puts err.message
exit 1
end
end
def print_terminals(f, grammar)
init_indent = '%token'.size
f.print '%token'
columns = init_indent
grammar.symboltable.each_terminal do |t|
next unless t.terminal?
next if t.dummy?
next if t == grammar.symboltable.anchor
next if t == grammar.symboltable.error
unless t.value.kind_of?(String)
if columns > 60
f.puts
f.print ' ' * init_indent
columns = init_indent
end
columns += f.write(" #{yacc_symbol(t)}")
end
end
f.puts
end
def precedence_table(grammar)
table = []
grammar.symboltable.select {|sym| sym.precedence }.each do |sym|
(table[sym.prec] ||= [sym.assoc]).push sym
end
table.compact
end
def print_precedence_table(f, table)
return if table.empty?
f.puts '/* precedance table */'
table.each do |syms|
assoc = syms.shift
f.printf '%%%-8s ', assoc.to_s.downcase
f.puts syms.map {|s| yacc_symbol(s) }.join(' ')
end
f.puts
end
def print_grammar(f, grammar)
prev_target = nil
indent = 10
embactions = []
grammar.each do |rule|
if rule.target.dummy?
embactions.push rule.action unless rule.action.empty?
next
end
if rule.target == prev_target
f.print ' ' * indent, '|'
else
prev_target = rule.target
f.printf "\n%-10s:", yacc_symbol(prev_target)
end
rule.symbols.each do |s|
if s.dummy? # target of dummy rule for embedded action
f.puts
print_action f, embactions.shift, indent
f.print ' ' * (indent + 1)
else
f.print ' ', yacc_symbol(s)
end
end
if rule.specified_prec
f.print ' %prec ', yacc_symbol(rule.specified_prec)
end
f.puts
unless rule.action.empty?
print_action f, rule.action, indent
end
end
end
def print_action(f, action, indent)
return unless @with_action
f.print ' ' * (indent + 4), "{\n"
f.print ' ' * (indent + 6), action.source.text.strip, "\n"
f.print ' ' * (indent + 4) , "}\n"
end
def print_user_codes(f, srcs)
return if srcs.empty?
srcs.each do |src|
f.puts src.text
end
end
def yacc_symbol(s)
s.to_s.gsub('"', "'")
end
main