зеркало из https://github.com/github/ruby.git
196 строки
4.5 KiB
Plaintext
196 строки
4.5 KiB
Plaintext
|
#!/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
|