home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / ruby164.zip / rbemx164.zip / ruby / bin / racc.cmd < prev    next >
OS/2 REXX Batch file  |  2001-06-18  |  14KB  |  626 lines

  1. extproc ruby -Sx
  2. #!/usr/local/bin/ruby
  3. #
  4. # racc
  5. #
  6. #   Copyright (c) 1999-2001 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
  7. #
  8. #   This program is free software.
  9. #   You can distribute/modify this program under the terms of
  10. #   the GNU Lesser General Public License version 2 or later.
  11. #
  12.  
  13.  
  14. ##### main -----------------------------------------------------
  15.  
  16. def racc_main
  17.   opt = get_options
  18.   require opt['-R'] || 'racc/compiler'
  19.   srcfn = ARGV[0]
  20.  
  21.   racc = Racc::Compiler.new
  22.   begin
  23.     racc_main0 racc, srcfn, opt
  24.   rescue Racc::ParseError, Racc::ScanError, Racc::RaccError, Errno::ENOENT
  25.     raise if racc.debug
  26.     msg = $!.to_s
  27.     unless /\A\d/ === msg then
  28.       msg[0,0] = ' '
  29.     end
  30.     $stderr.puts "#{File.basename $0}:#{srcfn}:" + msg
  31.     exit 1
  32.   end
  33. end
  34.  
  35.  
  36. def racc_main0( racc, srcfn, opt )
  37.   srcstr = nil
  38.   File.open( srcfn, 'r' ) {|f| srcstr = f.read }
  39.  
  40.   set_flags1 racc, opt
  41.   if opt['-P'] then
  42.     prof = pcompile( racc, srcstr, srcfn ) {
  43.             set_flags2 racc, opt }
  44.   else
  45.     racc.parse( srcstr, srcfn )
  46.     if opt['--check-only'] then
  47.       $stderr.puts 'syntax ok'
  48.       exit 0
  49.     end
  50.     set_flags2 racc, opt
  51.     racc.compile
  52.   end
  53.  
  54.   write_table_file racc, srcfn, opt
  55.  
  56.   if prof then
  57.     report_profile prof
  58.   end
  59.  
  60.   if opt['--verbose'] then
  61.     output_fn = opt['--log-file'] ||
  62.                 make_filename( opt['--output'] || srcfn, '.output' )
  63.     File.open( output_fn, 'w' ) do |f|
  64.       racc.output f
  65.     end
  66.   end
  67.  
  68.   report_conflict racc, opt
  69.   report_useless racc, opt
  70. end
  71.  
  72.  
  73.  
  74. ##### parse arg ------------------------------------------------
  75.  
  76. require 'getoptlong'
  77. require 'racc/info'
  78.  
  79.  
  80. Racc_Options = <<S
  81.  
  82. o -g --debug            - output parser for user level debugging
  83. o -o --output-file <outfile>  file name of output [<fname>.tab.rb]
  84. o -e --executable <rubypath>  insert #! line in output ('ruby' to default)
  85. o -E --embedded         - output file which don't need runtime
  86. o -l --no-line-convert  - never convert line numbers (for ruby<=1.4.3)
  87. o -c --line-convert-all - convert line numbers also header and footer
  88. x -s --super <super>      use <super> instead of Racc::Parser
  89. x -r --runtime <file>     use <file> instead of 'racc/parser'
  90. o -a --no-omit-actions  - never omit actions
  91.  
  92. o -v --verbose          - create <filename>.output file
  93. o -O --log-file <fname>   file name of verbose output [<fname>.output]
  94.  
  95. o -C --check-only       - syntax check only
  96. o -S --output-status    - output status time to time
  97. o -h --help             - print this message and quit
  98. o -  --version          - print version and quit
  99. o -  --runtime-version  - print runtime version and quit
  100. o -  --copyright        - print copyright and quit
  101.  
  102. x -P -                  - report profile
  103. x -R - <req>              require <req> instead of 'racc/libracc'             
  104. x -D - <flags>            set (racc's) debug flags
  105. S
  106.  
  107. def get_options
  108.   tmp = Racc_Options.collect {|line|
  109.     next if /\A\s*\z/ === line
  110.     disp, sopt, lopt, takearg, doc = line.strip.split(/\s+/, 5)
  111.     a = []
  112.     a.push lopt unless lopt == '-'
  113.     a.push sopt unless sopt == '-'
  114.     a.push takearg == '-' ?
  115.            GetoptLong::NO_ARGUMENT : GetoptLong::REQUIRED_ARGUMENT
  116.     a
  117.   }
  118.   par = GetoptLong.new( *tmp.compact )
  119.   par.quiet = true
  120.  
  121.   opt = {}
  122.   begin
  123.     par.each do |name, arg|
  124.       if opt[name] then
  125.         raise GetoptLong::InvalidOption,
  126.               "#{File.basename $0}: option #{name} was given twice"
  127.       end
  128.       opt[name] = arg.empty? ? true : arg
  129.     end
  130.   rescue GetoptLong::AmbigousOption, GetoptLong::InvalidOption,
  131.          GetoptLong::MissingArgument, GetoptLong::NeedlessArgument
  132.     usage 1, $!.message
  133.   end
  134.  
  135.  
  136.   if opt['--help'] then
  137.     usage 0
  138.   end
  139.  
  140.   if opt['--version'] then
  141.     puts "racc version #{Racc::Version}"
  142.     exit 0
  143.   end
  144.  
  145.   if opt['--runtime-version'] then
  146.     require 'racc/parser'
  147.     printf "racc runtime in Ruby:%s, in C:%s; %s version is running\n",
  148.            Racc::Parser::Racc_ruby_parser_version,
  149.            if defined? Racc::Parser::Racc_c_parser_version then
  150.              Racc::Parser::Racc_c_parser_version
  151.            else
  152.              '(not exist)'
  153.            end,
  154.            Racc::Parser.racc_runtime_type.capitalize
  155.     exit 0
  156.   end
  157.  
  158.   if opt['--copyright'] then
  159.     puts "racc version #{Racc::Version}"
  160.     puts "#{Racc::Copyright} <aamine@dp.u-netsurf.ne.jp>"
  161.     exit 0
  162.   end
  163.  
  164.   if ARGV.empty? then
  165.     usage 1, 'no grammer file given'
  166.   end
  167.   if ARGV.size > 1 then
  168.     usage 1, 'too many grammer files given'
  169.   end
  170.  
  171.  
  172.   opt['--line-convert'] = true
  173.   if opt['--no-line-convert'] then
  174.     opt['--line-convert'] = opt['--line-convert-all'] = false
  175.   end
  176.  
  177.   opt['--omit-action'] = true
  178.   if opt['--no-omit-action'] then
  179.     opt['--omit-action'] = false
  180.   end
  181.  
  182.   opt
  183. end
  184.  
  185.  
  186. def usage( status = 1, msg = nil )
  187.   f = $stderr
  188.   f.puts "#{File.basename $0}: #{msg}" if msg
  189.   f.print <<S
  190.  
  191. usage: racc [options] <grammer file>
  192.  
  193. options:
  194. S
  195.  
  196.   Racc_Options.each do |line|
  197.     if /\A\s*\z/ === line then
  198.       f.puts
  199.       next
  200.     end
  201.  
  202.     disp, sopt, lopt, takearg, doc = line.strip.split(/\s+/, 5)
  203.     if disp == 'o' then
  204.       sopt = nil if sopt == '-'
  205.       lopt = nil if lopt == '-'
  206.       opt = [sopt, lopt].compact.join(',')
  207.  
  208.       takearg = nil if takearg == '-'
  209.       opt = [opt, takearg].compact.join(' ')
  210.  
  211.       f.printf "%-27s %s\n", opt, doc
  212.     end
  213.   end
  214.  
  215.   exit status
  216. end
  217.  
  218.  
  219.  
  220. ##### compile ----------------------------------------------
  221.  
  222. def set_flags1( racc, opt )
  223.   racc.verbose = opt['--output-status']
  224.  
  225.   if opt['-D'] and /p/ === opt['-D'] then
  226.     racc.d_parse = true
  227.   end
  228. end
  229.  
  230. def set_flags2( racc, opt )
  231.   # overwrite source's
  232.   racc.debug_parser = opt['--debug']
  233.   racc.convert_line = opt['--line-convert']
  234.   racc.omit_action  = opt['--omit-action']
  235.  
  236.   if opt['-D'] or $DEBUG then
  237.     optd = opt['-D'] || ''
  238.     $stdout.sync = true
  239.     racc.debug   = true
  240.     racc.d_rule  = true if /r/ === optd
  241.     racc.d_token = true if /t/ === optd
  242.     racc.d_state = true if /s/ === optd
  243.     racc.d_la    = true if /l/ === optd
  244.     racc.d_prec  = true if /c/ === optd
  245.   end
  246. end
  247.  
  248.  
  249. ##### profile
  250.  
  251. def pcompile( racc, srcstr, srcfn )
  252.   times = []
  253.   times.push [ 'parse',   get_lap{ racc.parse(srcstr, srcfn) } ]
  254.   yield
  255.   times.push [ 'state',   get_lap{ racc.nfa } ]
  256.   times.push [ 'resolve', get_lap{ racc.dfa } ]
  257.   times
  258. end
  259.  
  260. def get_lap
  261.   begt = Time.times.utime
  262.   yield
  263.   endt = Time.times.utime
  264.   endt - begt
  265. end
  266.  
  267. def report_profile( prof )
  268.   out = $stderr
  269.  
  270.   whole = 0
  271.   prof.each do |arr|
  272.     whole += arr[1]
  273.   end
  274.   if whole == 0 then
  275.     whole = 0.01
  276.   end
  277.  
  278.   out.puts '--task-----------+--sec------+---%-'
  279.  
  280.   prof.each do |arr|
  281.     name, time = arr
  282.     out.printf( "%-19s %s %3d%%\n",
  283.                 name, pjust(time,4,4), (time/whole * 100).to_i )
  284.   end
  285.  
  286.   out.puts '-----------------+-----------+-----'
  287.   out.printf( "%-20s%s\n",
  288.               'total', pjust(whole,4,4) )
  289. end
  290.  
  291. def pjust( num, i, j )
  292.   m = /(\d+)(\.\d+)?/.match( num.to_s )
  293.   str = m[1].rjust(i)
  294.   if m[2] then
  295.     str << m[2].ljust(j+1)[0,j+1]
  296.   end
  297.  
  298.   str
  299. end
  300.  
  301.  
  302.  
  303. ##### output -----------------------------------------------
  304.  
  305. require 'amstd/rubyemu'
  306. require 'amstd/rbparams'
  307.  
  308.  
  309. def write_table_file( racc, srcfn, opt )
  310.   tabfn = opt['--output-file'] || make_filename( srcfn, '.tab.rb' )
  311.  
  312.   RaccTableFile.new( tabfn, srcfn, opt['--line-convert'] ) do |f|
  313.     f.executable opt['--executable'] if opt['--executable']
  314.     f.require opt['--embedded'], opt['--runtime'] || 'racc/parser'
  315.     f.header opt['--line-convert-all']
  316.     f.parser_class( racc.parser.class_name,
  317.                     opt['--super'] ||
  318.                     racc.parser.super_class ||
  319.                     '::Racc::Parser') {
  320.       f.inner
  321.       racc.source f.file
  322.     }
  323.     f.footer opt['--line-convert-all']
  324.   end
  325.  
  326.   if opt['--executable'] then
  327.     File.chmod( 0755, tabfn )
  328.   else
  329.     File.chmod( 0644, tabfn )
  330.   end
  331. end
  332.  
  333. def make_filename( fname, suffix )
  334.   fname.sub /(?:\.[^.]*)?\z/, suffix
  335. end
  336.  
  337.  
  338. class RaccTableFile
  339.  
  340.   def initialize( tabfn, srcfn, c )
  341.     @tabfn = tabfn
  342.     @srcfn = srcfn
  343.     @convline = c
  344.  
  345.     @header_labels = %w( header prepare )
  346.     @inner_labels  = %w( inner )
  347.     @footer_labels = %w( footer driver )
  348.  
  349.     @is_top = false
  350.     @uniq = {}
  351.  
  352.     init_user_code
  353.  
  354.     File.open( tabfn, 'w' ) do |f|
  355.       @f = f
  356.       yield self
  357.     end
  358.   end
  359.  
  360.   def file
  361.     @f
  362.   end
  363.  
  364.  
  365.   def executable( path )
  366.     @f.print "#!#{path == 'ruby' ? RubyParams::RUBY_PATH : path}\n\n"
  367.   end
  368.  
  369.  
  370.   def require( embed, parser_rb )
  371.     if embed then
  372.       @f.print <<S
  373. #
  374. # #{@tabfn}: generated by racc (runtime embedded)
  375. #
  376.  
  377. S
  378.       is_top {
  379.         embed 'racc/parser.rb'
  380.       }
  381.       @f.puts
  382.     else
  383.       @f.puts  "require '#{parser_rb}'"
  384.     end
  385.   end
  386.  
  387.  
  388.   def parser_class( classname, superclass )
  389.     @f.puts
  390.  
  391.     mods = classname.split('::')
  392.     real = mods.pop
  393.     mods.each_with_index do |m,i|
  394.       @f.puts  "#{'  ' * i}module #{m}"
  395.       @f.puts
  396.     end
  397.     @f.puts  "#{'  ' * mods.size}class #{real} < #{superclass}"
  398.  
  399.     yield
  400.  
  401.     @f.puts "#{'  ' * mods.size}end   \# class #{real}"
  402.     mods.reverse.each_with_index do |m,i|
  403.       @f.puts
  404.       @f.puts  "#{'  ' * (mods.size - i - 1)}end   \# module #{m}"
  405.     end
  406.   end
  407.  
  408.  
  409.   def header( conv )
  410.     is_top {
  411.       add_part conv, @header_labels
  412.     }
  413.   end
  414.  
  415.   def inner
  416.     add_part @convline, @inner_labels
  417.   end
  418.  
  419.   def footer( conv )
  420.     is_top {
  421.       add_part conv, @footer_labels
  422.     }
  423.   end
  424.  
  425.  
  426.   private
  427.  
  428.  
  429.   ###
  430.   ### init
  431.   ###
  432.  
  433.   def init_user_code
  434.     @usercode = Racc::RaccParser.get_ucode( @srcfn )
  435.     check_dup_ucode
  436.   end
  437.  
  438.   def check_dup_ucode
  439.     [ @header_labels, @inner_labels, @footer_labels ].each do |nms|
  440.       a = []
  441.       nms.each do |nm|
  442.         a.push nm if @usercode[nm]
  443.       end
  444.       if a.size > 1 then
  445.         raise Racc::RaccError,
  446.           "'" + a.join("' and '") + "' used at same time; must be only one"
  447.       end
  448.     end
  449.   end
  450.  
  451.  
  452.   ###
  453.   ### embed
  454.   ###
  455.  
  456.   def embed( rbfile )
  457.     @f.print <<S
  458. ###### #{rbfile}
  459.  
  460. unless $".index '#{rbfile}' then
  461. $".push '#{rbfile}'
  462. S
  463.     add_file RubyEmulator.find_feature rbfile
  464.     @f.puts "end   # end of #{rbfile}"
  465.   end
  466.  
  467.  
  468.   ###
  469.   ### part
  470.   ###
  471.  
  472.   def add_part( conv, names )
  473.     str, lineno, fnames = getent(names)
  474.     tmpconv( conv ) {
  475.       add str, @srcfn, lineno if str and not /\A\s*\z/ === str
  476.     }
  477.     fnames.each {|n| add_file n } if fnames
  478.   end
  479.  
  480.   def getent( names )
  481.     names.each do |n|
  482.       return @usercode[n] if @usercode[n]
  483.     end
  484.     nil
  485.   end
  486.  
  487.   def tmpconv( b )
  488.     save, @convline = @convline, b
  489.     yield
  490.     @convline = save
  491.   end
  492.  
  493.  
  494.   ###
  495.   ### low level code
  496.   ###
  497.  
  498.   def add_file( fname )
  499.     File.open( fname ) do |f|
  500.       add f.read, fname, 1
  501.     end
  502.   end
  503.  
  504.   def add( str, fn, lineno )
  505.     @f.puts
  506.     if @convline then
  507.       surrounding_by_eval( fn, str, lineno ) {
  508.         @f.puts str
  509.       }
  510.     else
  511.       @f.puts str
  512.     end
  513.   end
  514.  
  515.  
  516.   def surrounding_by_eval( nm, str, lineno )
  517.     sep = makesep( nm, str )
  518.     @f.print 'type.' if toplevel?
  519.     @f.puts "module_eval <<'#{sep}', '#{nm}', #{lineno}"
  520.     yield
  521.     @f.puts sep
  522.   end
  523.  
  524.   def is_top
  525.     @is_top = true
  526.     yield
  527.     @is_top = false
  528.   end
  529.  
  530.   def toplevel?
  531.     @is_top
  532.   end
  533.  
  534.   def makesep( nm, str )
  535.     sep = uniqlabel(nm)
  536.     sep *= 2 while str.index(sep)
  537.     sep
  538.   end
  539.  
  540.   def uniqlabel( nm )
  541.     ret = "..end #{nm} modeval..id"
  542.     srand
  543.     5.times do
  544.       ret << sprintf('%02x', rand(255))
  545.     end
  546.     ret << sprintf('%02x', rand(255)) while @uniq[ret]
  547.     @uniq[ret] = true
  548.     ret
  549.   end
  550.  
  551. end   # class RaccTableFile
  552.  
  553.  
  554.  
  555. ##### report -----------------------------------------------
  556.  
  557. def report_conflict( racc, opt )
  558.   sr = 0
  559.   rr = 0
  560.   racc.statetable.each do |st|
  561.     sr += st.srconf.size if st.srconf
  562.     rr += st.rrconf.size if st.rrconf
  563.   end
  564.  
  565.   if opt['-D'] and /o/ === opt['-D'] then
  566.     debugout {|f|
  567.       f.print "ex#{racc.ruletable.expect}\n"
  568.       f.print "sr#{sr}\n" if sr > 0
  569.       f.print "rr#{rr}\n" if rr > 0
  570.     }
  571.     return
  572.   end
  573.       
  574.   ex = racc.ruletable.expect
  575.   if (sr > 0 or ex) and sr != ex then
  576.     $stderr.puts "#{sr} shift/reduce conflicts"
  577.   end
  578.   if rr > 0 then
  579.     $stderr.puts "#{rr} reduce/reduce conflicts"
  580.   end
  581. end
  582.  
  583.  
  584. def report_useless( racc, opt )
  585.   nt = 0
  586.   rl = 0
  587.   racc.symboltable.each_nonterm do |t|
  588.     if t.useless? then
  589.       nt += 1
  590.     end
  591.   end
  592.   racc.ruletable.each do |r|
  593.     if r.useless? then
  594.       rl += 1
  595.     end
  596.   end
  597.  
  598.   if opt['-D'] and /o/ === opt['-D'] then
  599.     debugout('a') {|f|
  600.       f.print "un#{nt}\n" if nt > 0
  601.       f.print "ur#{rl}\n" if rl > 0
  602.     }
  603.     return
  604.   end
  605.  
  606.   if nt > 0 or rl > 0 then
  607.     $stderr.printf "%s%s%s\n",
  608.                    nt > 0                 ? "#{nt} useless nonterminals" : '',
  609.                    ((nt > 0) && (rl > 0)) ? ' and '                     : '',
  610.                    rl > 0                 ? "#{rl} useless rules"        : ''
  611.   end
  612.  
  613.   if racc.ruletable.start.useless? then
  614.     $stderr.puts 'fatal: start symbol does not derive any sentence'
  615.   end
  616. end
  617.  
  618. def debugout( flag = 'w', &block )
  619.   File.open( "log/#{File.basename(ARGV[0])}", flag, &block )
  620. end
  621.  
  622.  
  623. ##### entry point ------------------------------------------
  624.  
  625. racc_main
  626.