#!/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 [.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