#!/usr/bin/env ruby

def evaluate(s,l)
	begin
		return eval(s, TOPLEVEL_BINDING,  l.file,l.linenum)
	rescue
		puts $!
		puts "in #{s}"
		puts "at #{l.file} #{l.linenum}"
		exit(1)
	end
end
def mkregexp(s)
	Regexp.new('\s*#'+s+'(\s*$|\s+(.*)$)')
end
class RubyPP
	class Line
		attr_accessor :str,:linenum,:file
		def initialize(s,l,f)
			@str,@linenum,@file=s,l,f
		end
		def interpret()
			state=0
			buf=""
			arg=""
			par=0
			str2=""
			str.each_byte{|b|
				case state
				when -1
					str2<< b
					state=0
				when 0
					if b==?\\
						state=-1
						str2<< b
					elsif b==?\#
						state=1
					else 
						str2<< b
					end
				when 1
					if b==?\{
						state=2
						buf=""
						par=1
					elsif b==?\#
						state=0
						str2 << b
					else
						state=4
						buf=""<< b
					end
				when 2
					case b
					when ?\{
						par+=1
					when ?\}
						par-=1
						if par==0
							state=0
							str2 << evaluate(buf,self).to_s
						end
					when ?\\
						state=3
					end
					buf << b
				when 3
					buf << b
					state=2
				when 4

					if b==?(
						state=5
						par=1
						buf="method( :"+buf+").call("
					else
						buf << b
					end
				when 5
					buf<< b
					if b==?(
						par+=1
					elsif b==?)
						par-=1
					end
					if par==0
						str2<< evaluate(buf,self).to_s
						state=0
					end
				end
			}
			return Line.new(str2,linenum,file)
		end

	end
	class Input
		attr_accessor :input
		def initialize(i=[])
			@input=i
			@ln=-1
		end
		def gets
			@ln+=1
			$line=@input[@ln]
		end
		def reset
			@ln=-1
		end
		def each
			while li=gets
				yield(li)
			end
		end
		def <<(s)
			@input << s
		end
		def Input.readfile(filename)
			inp=Input.new
			file=filename ? File.open(filename): $stdin
			i=1
			file.each{|l| inp<<Line.new(l,i,filename);i+=1}
			inp
		end
		def output(filename)
			file=filename ? File.open(filename,"w"): $stdout
			@input.each{|l| puts l.str}
		end
	end

	def initialize
		@directives={}
		@paired={}
	end
	def adddirective(str,paired=true,&pro)
		@directives[mkregexp(str)]=pro
		@paired[mkregexp(str)]=paired
	end
	def parlvl(str)
		str=str.str if str.is_a? Line
	 	return -1 if mkregexp("END")===str

		@directives.each{|r,p|
			return 1 if @paired[r] && r===str
		}
		return 0
	end
	def processline(input)
		l=input.gets
		return nil if not l
		@directives.each{|d,pro|
			if d===l.str
				if @paired[d ]
				par=1
				inp=Input.new
				while true
					li=input.gets
					raise "#{d} without #END" if !li
					par+= parlvl(li.str)
					break if par==0
					inp<< li
				end
				else
					inp=input
				end
				p= pro.call(l,$2,inp)
				p=p.input if p.is_a? RubyPP::Input
				return p
			end
		}
		return [l.interpret]
	end
	def process(input)
		input=Input.new(input) if input.is_a? Array
		out=[]
		while l=processline(input)
			out.concat(l)
		end
		Input.new(out)
	end
end

$RubyPP=RubyPP.new
$RubyPP.adddirective("RUBY"){|l,exp,input|
	s=""
	input.each {|li| s+=li.str}
	evaluate(s,l)
	RubyPP::Input.new
}

$RubyPP.adddirective("IF"){|l,exp,input|
	r=RubyPP::Input.new
	use= evaluate(exp,l)
	used=use
	par=1
	out=[]
	usenow=true
	input.each{|li|
		par+=$RubyPP.parlvl(li.str)
		case li.str
		when mkregexp("ELSE")
			if par==1	
				use=!used
				usenow=false
			end
		when mkregexp("ELSEIF")
			if par==1
				usenow=false
				if !used&& evaluate($2,li)
					used=use=true
				else 
					use=false
				end
			end
		end
		out<< li if use && usenow
		usenow=true
	}
	$RubyPP.process(out)
}
$RubyPP.adddirective("WHILE"){|l,exp,input|
	out=[]
	out.concat($RubyPP.process(input)) while evaluate(exp,l)
	out
}
$RubyPP.adddirective("INCLUDE",false){|l,exp,input|
	file=evaluate(exp,l) 
	includes(file)
}
$RubyPP.adddirective("require",false){|l,exp,input| require evaluate(exp,l)}
def includes(filename)
	if filename

		filename=File.expand_path(filename)
		wdir=Dir.getwd
		Dir.chdir(File.dirname(filename))
		out=$RubyPP.process(RubyPP::Input.readfile(filename))
		Dir.chdir(wdir)
	else
		out=$RubyPP.process(RubyPP::Input.readfile(filename))
	end
	out
end


if __FILE__ == $0 then
	success = false
	begin
		includes(ARGV[0]).output(ARGV[1])
		success = true
	ensure
		if not success then
			File.unlink(ARGV[1]) if ARGV[1]
		end
	end
end

