1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
|
#!/usr/bin/env ruby
#
# $Id$
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the same terms of ruby.
# see the file "COPYING".
require 'racc/static'
require 'optparse'
def main
output = nil
debug_parser = false
make_logfile = false
logfilename = nil
make_executable = false
rubypath = nil
embed_runtime = false
debug_flags = Racc::DebugFlags.new
line_convert = true
line_convert_all = false
omit_action_call = true
superclass = nil
check_only = false
verbose = false
profiler = RaccProfiler.new(false)
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [options] <input>"
parser.on('-o', '--output-file=PATH',
'output file name [<input>.tab.rb]') {|name|
output = name
}
parser.on('-t', '--debug', 'Outputs debugging parser.') {|fl|
debug_parser = fl
}
parser.on('-g', 'Equivalent to -t (obsolete).') {|fl|
$stderr.puts "racc -g is obsolete. Use racc -t instead." if $VERBOSE
debug_parser = fl
}
parser.on('-v', '--verbose',
'Creates <filename>.output log file.') {|fl|
make_logfile = fl
}
parser.on('-O', '--log-file=PATH',
'Log file name [<input>.output]') {|path|
make_logfile = true
logfilename = path
}
parser.on('-e', '--executable [RUBYPATH]', 'Makes executable parser.') {|path|
executable = true
rubypath = (path == 'ruby' ? nil : path)
}
parser.on('-E', '--embedded', "Embeds Racc runtime in output.") {
embed_runtime = true
}
parser.on('--line-convert-all', 'Converts line numbers of user codes.') {
line_convert_all = true
}
parser.on('-l', '--no-line-convert', 'Never convert line numbers.') {
line_convert = false
line_convert_all = false
}
parser.on('-a', '--no-omit-actions', 'Never omit actions.') {
omit_action_call = false
}
parser.on('--superclass=CLASSNAME',
'Uses CLASSNAME instead of Racc::Parser.') {|name|
superclass = name
}
parser.on('--runtime=FEATURE',
"Uses FEATURE instead of 'racc/parser'") {|feat|
runtime = feature
}
parser.on('-C', '--check-only', 'Checks syntax and quit immediately.') {|fl|
check_only = fl
}
parser.on('-S', '--output-status', 'Outputs internal status time to time.') {
verbose = true
}
parser.on('-P', 'Enables generator profile') {
profiler = RaccProfiler.new(true)
}
parser.on('-D flags', "Flags for Racc debugging (do not use).") {|flags|
debug_flags = Racc::DebugFlags.parse_option_string(flags)
}
#parser.on('--no-extensions', 'Run Racc without any Ruby extension.') {
# Racc.const_set :Racc_No_Extentions, true
#}
parser.on('--version', 'Prints version and quit.') {
puts "racc version #{Racc::Version}"
exit 0
}
parser.on('--runtime-version', 'Prints runtime version and quit.') {
printf "racc runtime version %s (rev. %s); %s\n",
Racc::Parser::Racc_Runtime_Version,
Racc::Parser::Racc_Runtime_Revision,
if Racc::Parser.racc_runtime_type == 'ruby'
sprintf('ruby core version %s (rev. %s)',
Racc::Parser::Racc_Runtime_Core_Version_R,
Racc::Parser::Racc_Runtime_Core_Revision_R)
else
sprintf('c core version %s (rev. %s)',
Racc::Parser::Racc_Runtime_Core_Version_C,
Racc::Parser::Racc_Runtime_Core_Revision_C)
end
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'
exit 1
end
if ARGV.size > 1
$stderr.puts 'too many input'
exit 1
end
input = ARGV[0]
begin
$stderr.puts 'Parsing grammar file...' if verbose
result = profiler.section('parse') {
parser = Racc::GrammarFileParser.new(debug_flags)
parser.parse(File.read(input), File.basename(input))
}
if check_only
$stderr.puts 'syntax ok'
exit 0
end
$stderr.puts 'Generating LALR states...' if verbose
states = profiler.section('nfa') {
Racc::States.new(result.grammar).nfa
}
$stderr.puts "Resolving #{states.size} states..." if verbose
profiler.section('dfa') {
states.dfa
}
$stderr.puts 'Creating parser file...' if verbose
params = result.params.dup
# Overwrites parameters given by a grammar file with command line options.
params.superclass = superclass if superclass
params.omit_action_call = true if omit_action_call
# From command line option
if make_executable
params.make_executable = true
params.interpreter = rubypath
end
params.debug_parser = debug_parser
params.convert_line = line_convert
params.convert_line_all = line_convert_all
params.embed_runtime = embed_runtime
profiler.section('generation') {
generator = Racc::ParserFileGenerator.new(states, params)
generator.generate_parser_file(output || make_filename(input, '.tab.rb'))
}
if make_logfile
profiler.section('logging') {
$stderr.puts 'Creating log file...' if verbose
logfilename ||= make_filename(output || File.basename(input), '.output')
File.open(logfilename, 'w') {|f|
Racc::LogFileGenerator.new(states, debug_flags).output f
}
}
end
if debug_flags.status_logging
log_useless states.grammar
log_conflict states
else
report_useless states.grammar
report_conflict states
end
profiler.report
rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err
raise if $DEBUG or debug_flags.any?
lineno = err.message.slice(/\A\d+:/).to_s
$stderr.puts "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}"
exit 1
end
end
def make_filename(path, suffix)
path.sub(/(?:\..*?)?\z/, suffix)
end
def report_conflict(states)
if states.should_report_srconflict?
$stderr.puts "#{states.n_srconflicts} shift/reduce conflicts"
end
if states.rrconflict_exist?
$stderr.puts "#{states.n_rrconflicts} reduce/reduce conflicts"
end
end
def log_conflict(states)
logging('w') {|f|
f.puts "ex#{states.grammar.n_expected_srconflicts}"
if states.should_report_srconflict?
f.puts "sr#{states.n_srconflicts}"
end
if states.rrconflict_exist?
f.puts "rr#{states.n_rrconflicts}"
end
}
end
def report_useless(grammar)
if grammar.useless_nonterminal_exist?
$stderr.puts "#{grammar.n_useless_nonterminals} useless nonterminals"
end
if grammar.useless_rule_exist?
$stderr.puts "#{grammar.n_useless_rules} useless rules"
end
if grammar.start.useless?
$stderr.puts 'fatal: start symbol does not derive any sentence'
end
end
def log_useless(grammar)
logging('a') {|f|
if grammar.useless_nonterminal_exist?
f.puts "un#{grammar.n_useless_nonterminals}"
end
if grammar.useless_rule_exist?
f.puts "ur#{grammar.n_useless_rules}"
end
}
end
def logging(mode, &block)
File.open("log/#{File.basename(ARGV[0])}", mode, &block)
end
class RaccProfiler
def initialize(really)
@really = really
@log = []
unless ::Process.respond_to?(:times)
# Ruby 1.6
@class = ::Time
else
@class = ::Process
end
end
def section(name)
if @really
t1 = @class.times.utime
result = yield
t2 = @class.times.utime
@log.push [name, t2 - t1]
result
else
yield
end
end
def report
return unless @really
f = $stderr
total = cumulative_time()
f.puts '--task-----------+--sec------+---%-'
@log.each do |name, time|
f.printf "%-19s %s %3d%%\n", name, pjust(time,4,4), (time/total*100).to_i
end
f.puts '-----------------+-----------+-----'
f.printf "%-20s%s\n", 'total', pjust(total,4,4)
end
private
def cumulative_time
t = @log.inject(0) {|sum, (name, time)| sum + time }
t == 0 ? 0.01 : t
end
def pjust(num, i, j)
m = /(\d+)(\.\d+)?/.match(num.to_s)
str = m[1].rjust(i)
str.concat m[2].ljust(j+1)[0,j+1] if m[2]
str
end
end
main
|