123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- #!/usr/bin/env ruby
- require 'optparse'
- require 'rubygems'
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
- require 'treetop'
- require 'treetop/version'
- require 'treetop/polyglot'
- options = {}
- parser = OptionParser.new do |opts|
- exts = Treetop::Polyglot::VALID_GRAMMAR_EXT.collect { |i| '.' + i }
- opts.banner = "Treetop Parsing Expression Grammar (PEG) Comand Line Compiler"
- opts.define_head "Usage: tt [options] grammar_file[#{exts.join('|')}] ..."
- opts.separator ''
- opts.separator 'Examples:'
- opts.separator ' tt foo.tt # 1 grammar -> 1 parser source'
- opts.separator ' tt foo bar.treetop # 2 grammars -> 2 separate parsers'
- opts.separator ' tt -o alt_name.rb foo # alternately named output file'
- opts.separator ''
- opts.separator ''
- opts.separator 'NOTE: while treetop grammar files *must* have one of the following'
- opts.separator 'filename extensions, the extension name is not required when calling'
- opts.separator 'the compiler with grammar file names.'
- opts.separator ''
- opts.separator " Valid extensions: #{exts.join(', ')}"
- opts.separator ''
- opts.separator ''
- opts.separator 'Options:'
- opts.on('-o', '--output FILENAME', 'Write parser source to FILENAME') do |fn|
- options[:out_file] = fn
- end
- opts.on('-f', '--force', 'Overwrite existing output file(s)') do
- options[:force] = true
- end
- opts.on_tail('-v', '--version', 'Show Treetop version') do
- puts "Treetop v#{Treetop::VERSION::STRING}"
- exit
- end
- opts.on_tail('-h', '--help', 'Show this help message') do
- puts opts
- exit
- end
- end
- file_list = parser.parse!
- # check options and arg constraints
- if file_list.empty? || (options[:out_file] && file_list.size > 1)
- puts parser
- exit 1
- end
- def grammar_exist?(filename)
- if File.extname(filename).empty?
- Treetop::Polyglot::VALID_GRAMMAR_EXT.each do |ext|
- fn_ext = "#{filename}.#{ext}"
- return true if File.exist?(fn_ext) && !File.zero?(fn_ext)
- end
- end
- File.exist?(filename) && !File.zero?(filename)
- end
- def full_grammar_filename(filename)
- return filename if !File.extname(filename).empty?
- Treetop::Polyglot::VALID_GRAMMAR_EXT.each do |ext|
- fn_ext = "#{filename}.#{ext}"
- return fn_ext if File.exist?(fn_ext) && !File.zero?(fn_ext)
- end
- end
- def protect_output?(filename, forced=false)
- if !forced and
- File.exist?(filename) and
- (l=File.open(filename) { |f| f.gets rescue "" }) != Treetop::Compiler::AUTOGENERATED
- puts "ERROR: '#{filename}' output already exists; skipping compilation...\n"
- return true
- end
- false
- end
- compiler = Treetop::Compiler::GrammarCompiler.new
- while !file_list.empty?
- treetop_file = file_list.shift
- # handle nonexistent and existent grammar files mixed together
- if !grammar_exist?(treetop_file)
- puts "ERROR: input grammar file '#{treetop_file}' does not exist; continuing...\n"
- next
- end
- # try to compile
- treetop_file = full_grammar_filename(treetop_file)
- std_output_file = treetop_file.gsub(Treetop::Polyglot::VALID_GRAMMAR_EXT_REGEXP, '.rb')
- if options[:out_file]
- # explicit output file name option; never overwrite unless forced
- next if protect_output?(options[:out_file], options[:force])
- compiler.compile(treetop_file, options[:out_file])
- else
- # compile one input file from input file list option; never overwrite unless forced
- next if protect_output?(std_output_file, options[:force])
- compiler.compile(treetop_file)
- end
- end
|