* parse.y used to be so easy to read ;)
Could this, or something like this, go into <source>/sample/
where exyacc.rb is distributed?
It's not really a RIPPER tool because it does nothing to help that.
It may look ugly but the FSM I wrote is 3 times the line count. :)
I wouldn't want anyone else to have to waste time writing this.
Use as you wish - no need to ask.
daz
#--------------------------- unRIPPER.rb
#
# Strip #IF/ELSE/ENDIF RIPPER code (as #undef RIPPER)
# and all RIPPER pre-processing blocks from parse.y
#
# Written in 'perlish' with inspiration from:
# <RubySource>/sample/exyacc.rb
#
# daz - 2009/03/09 (12:00) - Free of copyright
#
require 'optparse'
### ==========
Ecc = "\x10" # Comment End Code (temp)
Hcc = "\x11" # Comment Control Code (replaces '#' within C-comment)
IFcc = "\x12" # #IF(*) Control Code
IF_RE =
/^(?!\n)\s*#\s*#{IFcc}(N?DEF)?\s+([^\n]*?)\n^([^#{IFcc}]*?\n)^(?!\n)\s*#\s*END#{IFcc}[^\n]*?\n/mi
# RIPPER pre-processing regex (for '/*%%%*/' and '/*%c%*/' blocks)
# ( ref: <RubyDir>/ext/ripper/tools/preproc.rb )
RIP_RE = %r{^\s*?/\*%(%|c)%\*/.*?\r?\n(.*?)^\s*/\*%(c?).*?%\*/.*?\r?\n}m
# [ ( 1 ) ] ^(_2_)^ [ (3)] [ ]
### ==========
def run(input, output)
y_source = nil
File.open(input) do |fin|
y_source = fin.read
end
# Make all '*/' a single character to simplify next regex
y_source.gsub!(%r{\*/}, Ecc)
#
# Temporarily substitute '#'-within-comment by Hcc code
# to prevent disruption of real #IF blocks.
y_source.gsub!(%r</\*[^#{Ecc}]*?#{Ecc}>) do |cm|
cm.gsub!(/(^\s*?)#(\s*IF|ELIF|ELSE|ENDIF)/i, "\\1#{Hcc}\\2")
cm.chop! << '*/' # restore '*/' (from Ecc control code)
end
# Temporarily replace all 'IF's and END'IF's with a control code
# so that nested blocks are correctly grouped.
y_source.gsub!(/^(\s*)#(\s*)IF(N?DEF)?(\s)/i, "\\1#\\2#{IFcc}\\3\\4")
y_source.gsub!(/^(\s*)#(\s*END)IF(\s)/i, "\\1#\\2#{IFcc}\\3")
# Process #IF/#ELSE/#ENDIF blocks
rep = true
rep = y_source.gsub!(IF_RE) do |if_blk|
m0, df, dfid, ieblk = [$&, ($1 && $1.downcase == 'def'), $2, $3]
if true and dfid == 'RIPPER'
iblk, eblk = ieblk.split(/(?!\n)^\s*#\s*ELSE(?>.*?\n)/i)
(df ? eblk : iblk) #IF def : ndef RIPPER
else
m0.gsub!("#{IFcc}", 'if') # copy entire non-RIPPER IF/ENDIF
end
end while rep
# Restore all '#'-within-comment
y_source.gsub!("#{Hcc}", '/*?*/')
## Remove RIPPER pre-processing directives
y_source.gsub!(RIP_RE) do
m0, m1, m2, m3 = [$&, $1, $2, $3]
if m1 == 'c'
if m3 != 'c' or m2 !~ /\A\s*\z/
puts '~~'*30, m0, ' ~'*30
p [m1, m2, m3], m0
puts '^^'*30
raise '? FIXME ?'
end
m2 = ''
end
m2
end
yield y_source if block_given?
if output
File.open(output, 'w') do |fout|
fout.write y_source
end
else
STDOUT.write y_source
end
end
output = nil
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [--output=PATH] <parse.y>"
parser.on('--output=PATH', 'The output file - defaults to STDOUT') do |path|
output = path.gsub("\\", '/')
end
parser.on('--help', 'Prints this message and quits.') do
puts parser.help
exit true
end
begin
parser.parse!
rescue OptionParser::ParseError => err
STDERR.puts err.message
STDERR.puts parser.help
exit false
end
unless ARGV.size == 1
p ARGV
abort "Wrong number of arguments (#{ARGV.size} for 1)"
end
input = ARGV[0].gsub("\\", '/')
out_name = output || 'STDOUT'
STDERR.puts 'Input : ' << input
STDERR.puts 'Output: ' << out_name
raise '^ ? Oops ? ^' if output == input
run(input, output) do |y_source|
# --- Any custom processing, here ---
STDERR.puts 'Writing output file ' << out_name
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^
end
#--------------------------- END