File indexing completed on 2024-04-21 04:35:50

0001 ##
0002 # This file is part of KDevelop
0003 #
0004 # Copyright (C) 2012-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
0005 #
0006 # This program is free software: you can redistribute it and/or modify
0007 # it under the terms of the GNU General Public License as published by
0008 # the Free Software Foundation, either version 3 of the License, or
0009 # (at your option) any later version.
0010 #
0011 # This program is distributed in the hope that it will be useful,
0012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 # GNU General Public License for more details.
0015 #
0016 # You should have received a copy of the GNU General Public License
0017 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 
0019 
0020 #!/usr/bin/env ruby
0021 
0022 
0023 # This script is the responsible of extracting all the info from the Ruby
0024 # source code (MRI) in order to generate a file containing all the builtin
0025 # modules, classes and methods. You should call this script like this:
0026 #
0027 #   $ ruby kdevruby_doc.rb /absolute/path/to/mri builtins.rb
0028 #
0029 # This may take a while, but then you'll get a brand new builtins.rb file
0030 # containing all the Ruby's builtins.
0031 #
0032 # NOTE: Brace yourselves, this script is a complete hack. It re-opens
0033 # RDoc::RDoc and retrieves everything from it with black magic and unicorns.
0034 
0035 
0036 require 'rdoc/rdoc'
0037 
0038 
0039 # Re-open the RDoc::RDoc class. This way, we can extract all the info of
0040 # builtin classes, modules and methods. The method to be called is
0041 # RDoc::RDoc#parse_files that, in short parses all the given directory to
0042 # fetch info. After that, you can call the kdev_classes & kdev_modules methods
0043 # in order to retrieve hashes with all the extracted info.
0044 class RDoc::RDoc
0045   attr_reader :kdev_classes, :kdev_modules
0046 
0047   # Public: Just call super and create a dummy value for @options.
0048   def initialize
0049     super
0050     @options = RDoc::Options.new
0051     @kdev_classes, @kdev_modules = {}, {}
0052   end
0053 
0054   # Public: Re-implemented from RDoc::RDoc. Parse all the files from
0055   # a directory.
0056   #
0057   # directory - a String containing the path to the directory.
0058   def parse_files(directory)
0059     files = get_files_from directory
0060     @stats = RDoc::Stats.new files.size, @options.verbosity
0061     files.each { |f| parse_file(f) }
0062   end
0063 
0064   # Public: Parse a file.
0065   #
0066   # name - a String containing the path to the file to be parsed.
0067   def parse_file(name)
0068     content = IO.read name
0069     top_level = RDoc::TopLevel.new name
0070     puts "Parsing: #{top_level.relative_name}"
0071     parser = RDoc::Parser.for top_level, name, content, @options, @stats
0072     return unless parser
0073 
0074     parser.scan
0075     @kdev_classes = eclasses(top_level.classes_hash, @kdev_classes)
0076     @kdev_modules = emodules(top_level.modules_hash, @kdev_modules)
0077   end
0078 
0079   private
0080 
0081   # Internal: Get the files from the given directory.
0082   #
0083   # d - A String containing the given directory.
0084   #
0085   # Returns an Array containing all the files inside the given directory.
0086   def get_files_from(d)
0087     files = []
0088     Dir.glob(File.join(d, '*.{c, rb}')).each { |f| files << f if File.file? f }
0089     files
0090   end
0091 
0092   # Internal: Extract all the classes from the given parameters.
0093   #
0094   # hash    - A Hash containing all the info on classes.
0095   # initial - A Hash containing calculations from previous calls.
0096   #
0097   # Returns a Hash containing the extracted results.
0098   def eclasses(hash, initial)
0099     final = initial || {}
0100     hash.each do |h|
0101       next if h.empty?
0102       h = h.last
0103       who = h.name.to_sym
0104       element = { comment: h.comment, ancestors: {} }
0105       ancestors = []
0106       if h.superclass.nil?
0107         h.ancestors.each do |a|
0108           ancestors << (a.is_a? String) ? a.to_sym : a.name.to_sym
0109         end
0110       else
0111         base = (h.superclass.is_a? String) ? h.superclass : h.superclass.name
0112         element[:superclass] = base.to_sym
0113         who = (h.name == h.name.downcase) ? h.name.capitalize : h.name
0114         who = who.to_sym
0115         (h.ancestors - [h.superclass]).each do |a|
0116           if a.is_a? String
0117             ancestors << a.to_sym
0118           else
0119             ancestors << a.name.to_sym
0120           end
0121         end
0122       end
0123       if final.include? who
0124         final[who] = fmerge(final[who], ancestors, h)
0125       else
0126         element[:ancestors] = ancestors || {}
0127         element[:classes] = eclasses(h.classes_hash, {})
0128         element[:methods] = emethods(h.methods_hash, {})
0129         final[who] = element
0130       end
0131     end
0132     final
0133   end
0134 
0135   # Internal: Extract all the modules from the given parameters.
0136   #
0137   # hash    - A Hash containing all the info on modules.
0138   # initial - A Hash containing calculations from previous calls.
0139   #
0140   # Returns a Hash containing the extracted results.
0141   def emodules(hash, initial)
0142     final = initial || {}
0143     hash.each do |h|
0144       next if h.empty?
0145       h = h.last
0146       who = h.name.to_sym
0147       ancestors = h.ancestors.map { |a| a.name.to_sym }
0148       if final.include? who
0149         final[who] = fmerge(final[who], ancestors, h)
0150       else
0151         element = { comment: h.comment }
0152         element[:ancestors] = ancestors
0153         element[:classes] = eclasses(h.classes_hash, {})
0154         element[:methods] = emethods(h.methods_hash, {})
0155         final[who] = element
0156       end
0157     end
0158     final
0159   end
0160 
0161   # Internal: Extract all the methods from the given parameters.
0162   #
0163   # hash    - A Hash containing all the info on methods.
0164   # initial - A Hash containing calculations from previous calls.
0165   #
0166   # Returns a Hash containing the extracted results.
0167   def emethods(hash, initial)
0168     final = initial || {}
0169     hash.each do |m|
0170       method = m.last
0171       call_seq = method.call_seq
0172       unless call_seq.nil?
0173         call_seq.split(/\n\s*/).each { |k| final = get_method_string(method, k, final) }
0174       end
0175     end
0176     final
0177   end
0178 
0179   # Internal: Extract all what we can from the call_seq string of a method.
0180   #
0181   # method - The method object from RDoc.
0182   # str    - The call_seq string from the documentation of the method.
0183   # hash   - A Hash containing calculations from previous calls.
0184   #
0185   # Returns a Hash containing the extracted results.
0186   def get_method_string(method, str, hash)
0187     final = hash || {}
0188 
0189     # Extract block
0190     str = str.gsub(/\{(.+)\}/, '')
0191     block = !$1.nil? ? '&block' : nil
0192     str = str.gsub(/(\[\s*\])/, '')
0193     block = nil if !$1.nil?
0194 
0195     # +per_se+ is the signature itself, and +ret+ the expected return type
0196     arrow = str.include?('=>') ? '=>' : '->'
0197     per_se, ret = str.split(arrow).map(&:strip)
0198     return final if per_se.nil?
0199 
0200     # Extract name and arguments of the method
0201     name, args = per_se.split('(')
0202     name = name.split('.')
0203     name = if (name.size > 1)
0204       name.last
0205     else
0206       method.name
0207     end
0208     name = filter_name(name).to_sym
0209 
0210     # Method arguments
0211     args_str = '';
0212     if args.nil?
0213       args_str = "(#{block})" unless block.nil?
0214       if final.include? name
0215         if final[name][:args].split(',').size < args_str.split(',').size && method.singleton != final[name][:singleton]
0216           final[name][:args] = args_str
0217         end
0218       else
0219         final[name] = { comment: method.comment, args: args_str, singleton: method.singleton }
0220       end
0221     else
0222       # Separate between normal parameters and optional parameters ...
0223       args = args.chop.gsub(/\[(.+)\]/, '')
0224       opts = $1.scan(/\w+/) unless $1.nil?
0225 
0226       # ... and join them again but now with the proper format.
0227       res = ''
0228       if args.empty?
0229         res = opts.join('=0, ') + '=0' unless opts.nil?
0230       else
0231         res = args.gsub(/nil/, 'null')
0232         res += ', ' + opts.join('=0, ') + '=0' unless opts.nil?
0233       end
0234       res = strip_reserved(res)
0235       res = fix_typos(res)
0236       if res.include?('arg*more')
0237         args_str = "(#{block})"
0238       elsif res =~ /\+|\*/
0239         args_str = '()'
0240       else
0241         res.gsub!('  , string=0', 'string=0')
0242         res.gsub!(/filename=$/, "filename=''")
0243         args_str = block.nil? ? "(#{res})" : "(#{res}, #{block})"
0244       end
0245 
0246       if final.include? name
0247         if final[name][:args].split(',').size < args_str.split(',').size && method.singleton != final[name][:singleton]
0248           final[name][:args] = args_str
0249         end
0250       else
0251         final[name] = { comment: method.comment, args: args_str, singleton: method.singleton }
0252       end
0253     end
0254     final[name][:return] = get_return(ret)
0255     final
0256   end
0257 
0258   # Internal: Force merge. Forces the merge of internal hashes.
0259   #
0260   # element   - The element to be updated.
0261   # ancestors - The ancestors of the given element.
0262   # h         - The RDoc object of the Class/Module.
0263   #
0264   # Returns the same element but with some entries updated.
0265   def fmerge(element, ancestors, h)
0266     if element[:ancestors].nil?
0267       element[:ancestors] = ancestors
0268     else
0269       element[:ancestors] |= (ancestors)
0270     end
0271     if element[:classes].nil?
0272       element[:classes] = eclasses(h.classes_hash, element[:classes])
0273     else
0274       element[:classes].merge!(eclasses(h.classes_hash, element[:classes]))
0275     end
0276     if element[:methods].nil?
0277       element[:methods] = emethods(h.methods_hash, element[:methods])
0278     else
0279       element[:methods].merge!(emethods(h.methods_hash, element[:methods]))
0280     end
0281     element
0282   end
0283 
0284   # Internal: The following are some methods to clean typos
0285   # from Ruby's documentation.
0286   #
0287   # Clean the given name.
0288   #
0289   # name - A String containing the name to clean.
0290   def filter_name(name)
0291     name = name.gsub(/(\w+) = (\w+)/) { "#{$1}= #{$2}" }
0292     name.gsub!('enc or nil', 'enc_or_nil')
0293     name.gsub!('proc_obj or nil', 'proc_or_nil')
0294     name = '[]' if name == ']' or name == ' ] ]' or name == '] ]' or name == ' ]'
0295     name
0296   end
0297 
0298   # Internal: Fix random typos from the documentation by hand.
0299   #
0300   # res - A String containing the documentation to clean.
0301   def fix_typos(res)
0302     res.gsub!(/,\s*,/, ',')
0303     res.gsub!('"ext_enc:int_enc"', 'enc')
0304     res.gsub!('"root"', 'root')
0305     res.gsub!(/\w*: \w*/, 'random')
0306     res.gsub!(/\.\.\.?/, '*more') # arg1, ...  =>  arg1, *more
0307     res.gsub!(/\+|\*/, '*more')
0308     res
0309   end
0310 
0311   # Internal: Clean the given string from reserved words.
0312   #
0313   # str - The given string.
0314   def strip_reserved(str)
0315     str = str.gsub(/module/, 'modul').gsub(/class/, 'klass')
0316     str.gsub(/end/, '_end').gsub(/begin/, '_begin')
0317   end
0318 
0319   # Internal: get the return value for a method.
0320   # NOTE: it has to be improved like a lot ...
0321   def get_return(ret)
0322     return nil if ret.nil? || ret.empty?
0323     {
0324       'anIO' => "''", 'obj' => 'Object.new', 'string' => "''",
0325       'integer' => '0', 'Numeric' => '0', 'array' => '[]', 'float' => '0.0',
0326       'true or false' => 'true || false', 'aBignum' => 'Bignum.new',
0327       'time' => 'Time.new', 'a_rational' => 'Rational.new', 'number' => '0',
0328       'numeric' => '0', 'fixnum' => '0', 'int' => '0',
0329       'new_time' => 'Time.new', 'new_str' => "''", 'str' => "''",
0330       'new_str or nil' => "'' || nil", 'an_enumerator' => 'Enumerator.new',
0331       '0 .. 255' => '0..255', 'anArray' => '[]', 'an_array' => '[]',
0332       'symbol' => ':a', 'enumerator' => 'Enumerator.new',
0333       '[head, match, tail]' => '[]', '[head, sep, tail]' => '[]',
0334       'sym' => ':a', 'other_symbol' => ':a', 'a_proc' => 'Proc.new',
0335       'encoding' => 'Encoding.new', 'char' => "''", 'prc' => 'Proc.new',
0336       'result_of_proc' => 'Proc.new', '[String, Fixnum]' => "['', 0]",
0337       'binding' => 'Binding.new', 'method' => 'Method.new',
0338       'new_method' => 'Method.new', 'unbound_method' => 'Method.new',
0339       'class_or_module' => 'Class.new || Module.new', 'self' => 'self',
0340       'a_binding' => 'Binding.new', 'rational' => 'Rational.new',
0341       '-1, 0, +1 or nil' => '1 || nil', '(0/1)' => '(0/1)', '1' => '1',
0342       'matchdata or nil' => 'MatchData.new || nil', 'object' => 'Object.new',
0343       'str or nil' => "'' || nil", 'fixnum or nil' => '1 || nil',
0344       'self or nil' => 'self || nil', 'proc' => 'Proc.new',
0345       'other_string' => "''", '$_' => '$_', 'hsh' => '{}', 'a_hash' => '{}',
0346       'Hash' => '{}', 'Enumerator' => 'Enumerator.new',
0347       'Array or nil' => '[] || nil', 'array or nil' => '[] || nil',
0348       'string or nil' => "'' || nil", '0' => '0', 'anObject' => 'Object.new',
0349       '(true or false)' => 'true || false', 'Integer' => '0',
0350       'new_hash' => '{}', 'hsh or new_hash' => '{}', 'real' => '0',
0351       'hash or nil' => '{} || nil', 'value' => 'Object.new',
0352       'numeric_result' => '0', 'integer or float' => '0 || 0.0',
0353       'an_exception or exc' => 'Exception.new', 'module' => 'Module.new',
0354       'aTime' => 'Time.new', 'abs_file_name' => "''", 'real_pathname' => "''",
0355       'base_name' => "''", 'ary' => '[]', '0 or false' => '0 || false',
0356       'obj     or nil' => 'Object.new || nil', '0, 1' => '0',
0357       'enc' => 'Encoding.new', 'mod' => 'Module.new', 'rng' => '1..2',
0358       'num' => '0', 'integer_result' => '0', 'hsh_result' => '{}',
0359       'key' => 'Object.new', 'hash' => '{}', 'Array' => '[]',
0360       'anArray or obj' => '[] || Object.new', '"/home/me"' => "''",
0361       '"/root"' => "''", '[enc1, enc2, ...]' => '[Encoding.new]',
0362       '["enc1", "enc2", ...]' => '[Encoding.new]', 'new_ary' => '[]',
0363       'destination_string' => "''", 'dir' => 'Dir.new', 'aDir' => 'Dir.new',
0364       'exception or nil' => 'Exception.new || nil', 'enum' => 'Enumerator.new',
0365       'encoding or nil' => 'Encoding.new || nil', 'ARGF' => 'ARGF',
0366       'new_ary or nil' => '[] || nil', 'String' => "''", '0.0' => '0.0',
0367       'Integer or nil' => '0 || nil', 'file_name' => "''", 'dir_name' => "''",
0368       'path' => "''", 'stat' => "''", 'filename' => "''", 'false' => 'false',
0369       'outbuf' => "''", 'string, outbuf, or nil' => "'' || nil", '""' => "''",
0370       '{}' => '{}', '(0+0i)' => 'Complex.new',
0371       'complex' => 'Complex.new', 'Complex(0,num)' => 'Complex.new',
0372       '0 or float' => '0 || 0.0', 'other  ->  0 or nil' => '0 || nil',
0373       'lazy_enum' => 'Enumerator::Lazy.new', 'thread' => 'Thread.new',
0374       'thr' => 'Thread.new', 'thr or nil' => 'nil || Thread.new',
0375       'true/false' => 'true || false', 'result of the block' => 'Object.new',
0376       '0 or nil' => '0 || nil', 'boolean' => 'true || false', 'io' => 'IO.new',
0377       'ios' => 'IO.new', 'array  or  nil' => '[] || nil', 'pid' => '0',
0378       '[read_io, write_io]' => '[IO.new]', 'io or nil' => 'IO.new || nil',
0379       'true' => 'true', 'String or nil' => "'' || nil",
0380       'obj or nil' => 'Object.new || nil', 'new_regexp' => '//',
0381       'lazy_enumerator' => 'Enumerator::Lazy.new', 'class' => 'Class.new',
0382       'matchdata' => 'MatchData.new', 're or nil' => '// || nil',
0383       'an_object' => 'Object.new', 'old_seed' => '1', 'struct' => 'Struct.new',
0384       'true, false or nil' => 'true || false || nil', 'regexp' => '//',
0385       'StructClass' => 'Struct.new', 'ary or nil' => '[] || nil',
0386       'ary  or  nil' => '[] || nil', 'obj  or nil' => 'Object.new || nil',
0387       'obj or other_ary or nil' => '[] || nil', 'file' => 'File.new',
0388       'obj or nil' => 'Object.new || nil', 'int or nil' => '1 || nil',
0389       'nil, -1, +1' => '1 || nil', 'ARGV' => 'ARGV', 'IO' => 'IO',
0390       'Fixnum or nil' => '0 || nil', 'Fixnum' => '0', 'result' => 'Object.new',
0391       'integer or nil' => '1 || nil', '[name1, name2, ...]' => '[]',
0392       'signal_exception' => 'SignalException.new', 'elem' => 'Object.new',
0393       'other_module   -> -1, 0, +1, or nil' => '1 || nil',
0394       'other -> 0 or nil' => '0 || nil', 'new_string' => "''",
0395       'true, false, or nil' => 'true || false || nil',
0396       'new_ary  or  nil' => '[]', 'aBinaryString' => "''",
0397       'other_string   -> -1, 0, +1 or nil' => '1 || nil',
0398       '[pid, status]' => '[1]', 'prng' => 'Random.new',
0399       'system_exit' => 'SystemExit.new', 'name_error' => 'NameError.new',
0400       'no_method_error' => 'NoMethodError.new', 'hsh or nil' => '{} || nil',
0401       'an_array  or  nil' => '[] || nil', 'an_array or nil' => '[] || nil',
0402       'system_call_error_subclass' => 'SystemCallError',
0403       'ENV or nil' => 'ENV || nil', 'name' => "''",
0404       'real  ->  -1, 0, +1 or nil' => '1 || nil', 'e' => 'Exception.new',
0405       'numeric  ->  -1, 0, +1 or nil' => '1 || nil',
0406       'env' => 'ENV', '"ENV"' => 'ENV', 'enc or nil' => 'Encoding.new || nil',
0407       '[ fraction, exponent ]' => '[]', '[float, -1 or 1]' => '[0.0, 0]',
0408       'a_class' => 'Class.new', 'a_super_class or nil' => 'Class.new || nil',
0409       '"true"' => 'true', '!obj' => 'Object.new', '"false"' => 'false',
0410       'bool' => 'true || false', 'item or nil' => 'Object.new || nil',
0411       'item or result of block' => 'Object.new || nil',
0412       '[min, max]' => '[]', 'other_ary   ->  -1, 0, +1 or nil' => '1 || nil',
0413       'numeric   -> -1, 0, +1 or nil' => '1 || nil',
0414       'other_time -> -1, 0, +1 or nil' => '1 || nil',
0415       'other_symbol       -> -1, 0, +1 or nil' => '1 || nil',
0416       'a_string' => "''", 'exception' => 'Exception.new',
0417       'thgrp' => 'ThreadGroup.new', 'aStructTms' => 'Struct::Tms.new',
0418       'other_stat    -> -1, 0, 1, nil' => '1 || nil',
0419       '[ [pid1,status1], ...]' => '[]', '[cur_limit, max_limit]' => '[]',
0420       'an_array_of_array' => '[]', '[obj, ...]' => '[]',
0421       'IO or File object' => 'IO.new || File.new', 'mutex' => 'Mutex.new',
0422       'other_stat    -> -1, 0, 1, nil' => '1 || nil',
0423       'a_lazy_enumerator' => 'Enumerator::Lazy.new', 'fiber' => 'Fiber.new',
0424       'int, Float::INFINITY or nil' => '1 || 0.0 || nil',
0425       '[ true_array, false_array ]' => '[[true], [false]]',
0426       'string, false or nil' => "'' || false || nil",
0427       'thgrp or nil' => 'ThreadGroup.new || nil'
0428     }[ret]
0429   end
0430 end
0431 
0432 # Re-open the File class so we can print the results of the RDoc::TopLevel's
0433 # hashes into files in an easier way.
0434 class File
0435   # Public: Print a comment.
0436   #
0437   # comment - A String containing the comment.
0438   def print_comment(comment)
0439     return if comment.empty?
0440     puts '##'
0441     puts comment.split("\n").each { |c| c.insert(0, '# ') << "\n" }.join
0442   end
0443 
0444   # Public: Print all the modules.
0445   #
0446   # hash - A Hash containing all the info of all the modules.
0447   def print_modules(hash)
0448     hash.each do |k, v|
0449       print_comment v[:comment]
0450       puts "module #{k}"
0451       v[:ancestors].each { |m| puts "include #{m}" unless m.empty? }
0452       print_classes v[:classes]
0453       print_methods v[:methods]
0454       puts "end\n\n"
0455     end
0456   end
0457 
0458   # Public: Print all the classes.
0459   #
0460   # hash - A Hash containing all the info of all the classes.
0461   def print_classes(hash)
0462     hash.each do |k, v|
0463       print_comment v[:comment]
0464       if v[:superclass].nil?
0465         puts "class #{k}"
0466       else
0467         puts "class #{k} < #{v[:superclass]}"
0468       end
0469       v[:ancestors].each { |m| puts "include #{m}" unless m.empty? }
0470       print_classes v[:classes]
0471       print_methods v[:methods]
0472       puts "end\n\n"
0473     end
0474   end
0475 
0476   # Public: Print all the methods.
0477   #
0478   # hash - A Hash containing all the info of all the methods.
0479   def print_methods(hash)
0480     hash.each do |k, v|
0481       print_comment v[:comment]
0482       if v[:singleton]
0483         print "def self.#{k}"
0484       else
0485         print "def #{k}"
0486       end
0487       print v[:args]
0488       if !(k == :new && v[:singleton]) && !v[:return].nil?
0489         print "; #{v[:return]}"
0490       end
0491       puts "; end\n"
0492     end
0493   end
0494 end
0495 
0496 
0497 ##
0498 # Here starts the main program
0499 
0500 
0501 if ARGV.size != 2
0502   print <<-USAGE
0503 KDevelop Ruby Documentation generator.
0504 Usage:
0505   ruby kdevruby_doc.rb source_dir output_file
0506   USAGE
0507   exit
0508 end
0509 ruby_dir, ruby_out = ARGV
0510 
0511 # Parse all the files contained inside the +ruby_dir+ directory.
0512 doc = RDoc::RDoc.new
0513 puts 'Generating documentation. Please be patient, this may take a while.'
0514 doc.parse_files ruby_dir
0515 
0516 # Print everything into the +ruby_out+ file.
0517 output = File.open(ruby_out, 'w')
0518 ruby_version = File.open("#{ruby_dir}/version.h", &:readline)
0519 ruby_version.match(/\"(.+)\"/)
0520 output.puts <<HEADER
0521 ##
0522 # Ruby Version: #{$1}
0523 # This file is generated, all changes made in this file will be lost!\n\n
0524 HEADER
0525 output.print_modules doc.kdev_modules
0526 output.print_classes doc.kdev_classes
0527 
0528 
0529 # Print all the pre-defined global variables.
0530 {
0531   '$! = Exception.new' => "The exception information message set by 'raise'. ",
0532   "$@ = ['']" => 'Array of backtrace of the last exception thrown.',
0533   "$& = ''" => 'The string matched by the last successful match.',
0534   "$` = ''" => 'The string to the left  of the last successful match.',
0535   "$' = ''" => 'The string to the right of the last successful match.',
0536   "$+ = ''" => 'The highest group matched by the last successful match.',
0537   '$~ = MatchData.new' => 'The information about the last match in the current scope.',
0538   '$= = true || false' => 'The flag for case insensitive, nil by default.',
0539   "$/ = ''" => 'The input record separator, newline by default.',
0540   "$\\ = ''"=> 'The output record separator for the print and IO#write. Default is nil.',
0541   "$, = ''" => 'The output field separator for the print and Array#join.',
0542   "$; = ''" => 'The default separator for String#split.',
0543   '$. = 0' => 'The current input line number of the last file that was read.',
0544   '$< = IO.new' => 'The virtual concatenation file of the files given on command line (or from $stdin if no files were given).',
0545   '$> = IO.new' => 'The default output for print, printf. $stdout by default.',
0546   "$_ = ''" => 'The last input line of string by gets or readline.',
0547   "$0 = ''" => 'Contains the name of the script being executed. May be assignable.',
0548   "$* = ['']" => 'Command line arguments given for the script sans args.',
0549   '$$ = 0' => 'The process number of the Ruby running this script.',
0550   '$? = 0' => 'The status of the last executed child process.',
0551   "$: = ['']" => 'Load path for scripts and binary modules by load or require.',
0552   "$\" = ['']" => 'The array contains the module names loaded by require.',
0553   '$DEBUG = true || false' => 'The status of the -d switch.',
0554   "$FILENAME = ''" =>  'Current input file from $<. Same as $<.filename.',
0555   '$LOAD_PATH = $:' => 'The alias to the $:.',
0556   '$stderr = IO.new' => 'The current standard error output.',
0557   '$stdin = IO.new'  => 'The current standard input.',
0558   '$stdout = IO.new' => 'The current standard output.',
0559   '$VERBOSE = true || false'=> 'The verbose flag, which is set by the -v switch.',
0560   '$-0 = $/' => 'The alias to $/.',
0561   '$-a = true || false' => 'True if option -a is set. Read-only variable.',
0562   '$-d = $DEBUG' => 'The alias to $DEBUG.',
0563   '$-F = $;' => 'The alias to $;.',
0564   '$-i = nil' => 'In in-place-edit mode, this variable holds the extension, otherwise nil.',
0565   '$-I = $:' => 'The alias to $:.',
0566   '$-l = true || false' => 'True if option -l is set. Read-only variable.',
0567   '$-p = true || false' => 'True if option -p is set. Read-only variable.',
0568   '$-v = $VERBOSE' => 'The alias to $VERBOSE.',
0569   '$-w = true || false' => 'True if option -w is set.'
0570 }.each { |k, v| output.puts "# #{v}\n#{k}\n\n" }
0571 
0572 # Print all the pre-defined constants.
0573 {
0574   'TRUE = true' => 'The typical true value.',
0575   'FALSE = false' => 'The false itself.',
0576   'NIL = nil' => 'The nil itself.',
0577   'STDIN = $stdin' => 'The standard input. The default value for $stdin.',
0578   'STDOUT = $stdout' => 'The standard output. The default value for $stdout.',
0579   'STDERR = $stderr' => 'The standard error output. The default value for $stderr.',
0580   "ENV = {''=>''}" => 'The hash contains current environment variables.',
0581   'ARGF = $<' => 'The alias to the $<.',
0582   'ARGV = $*' => 'The alias to the $*.',
0583   'DATA = File.new' => 'The file object of the script, pointing just after __END__.',
0584   "RUBY_VERSION = ''" => 'The ruby version string (VERSION was deprecated).',
0585   "RUBY_RELEASE_DATE = ''" => 'The release date string.',
0586   "RUBY_PLATFORM = ''" => 'The platform identifier.'
0587 }.each { |k, v| output.puts "# #{v}\n#{k}\n\n" }