require 'erb'
class ERB
class Compiler # :nodoc:
class TrimScanner < Scanner # :nodoc:
def initialize(src, trim_mode, percent)
super
@trim_mode = trim_mode
@percent = percent
if @trim_mode == '>'
@scan_line = self.method(:trim_line1)
elsif @trim_mode == '<>'
@scan_line = self.method(:trim_line2)
elsif @trim_mode == '-'
@scan_line = self.method(:explicit_trim_line)
else
@scan_line = self.method(:scan_line)
end
end
attr_accessor :stag
def scan(&block)
@stag = nil
if @percent
@src.each_line do |line|
percent_line(line, &block)
end
else
@scan_line.call(@src, &block)
end
nil
end
def percent_line(line, &block)
if @stag || line[0] != ?%
return @scan_line.call(line, &block)
end
line[0] = ''
if line[0] == ?%
@scan_line.call(line, &block)
else
yield(PercentLine.new(line.chomp))
end
end
def scan_line(line)
line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%b|<%|%>|\n|\z)/m) do |tokens|
tokens.each do |token|
next if token.empty?
yield(token)
end
end
end
def trim_line1(line)
line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%b|<%|%>\n|%>|\n|\z)/m) do |tokens|
tokens.each do |token|
next if token.empty?
if token == "%>\n"
yield('%>')
yield(:cr)
else
yield(token)
end
end
end
end
def trim_line2(line)
head = nil
line.scan(/(.*?)(<%%|%%>|<%=|<%b|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
tokens.each do |token|
next if token.empty?
head = token unless head
if token == "%>\n"
yield('%>')
if is_erb_stag?(head)
yield(:cr)
else
yield("\n")
end
head = nil
else
yield(token)
head = nil if token == "\n"
end
end
end
end
def explicit_trim_line(line)
line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%b|<%|-%>\n|\n|-%>|%>|\z)/m) do |tokens|
tokens.each do |token|
next if token.empty?
if @stag.nil? && /[ \t]*<%-/ =~ token
yield('<%')
elsif @stag && token == "-%>\n"
yield('%>')
yield(:cr)
elsif @stag && token == '-%>'
yield('%>')
else
yield(token)
end
end
end
end
remove_const :ERB_STAG
ERB_STAG = %w(<%b <%= <%# <%)
def is_erb_stag?(s)
ERB_STAG.member?(s)
end
end
Scanner.default_scanner = TrimScanner
class SimpleScanner < Scanner # :nodoc:
def scan
@src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
tokens.each do |token|
next if token.empty?
yield(token)
end
end
end
end
Scanner.regist_scanner(SimpleScanner, nil, false)
begin
require 'strscan'
class SimpleScanner2 < Scanner # :nodoc:
def scan
stag_reg = /(.*?)(<%%|<%=|<%#|<%|\z)/m
etag_reg = /(.*?)(%%>|%>|\z)/m
scanner = StringScanner.new(@src)
while ! scanner.eos?
scanner.scan(@stag ? etag_reg : stag_reg)
yield(scanner[1])
yield(scanner[2])
end
end
end
Scanner.regist_scanner(SimpleScanner2, nil, false)
class ExplicitScanner < Scanner # :nodoc:
def scan
stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m
etag_reg = /(.*?)(%%>|-%>|%>|\z)/m
scanner = StringScanner.new(@src)
while ! scanner.eos?
scanner.scan(@stag ? etag_reg : stag_reg)
yield(scanner[1])
elem = scanner[2]
if /[ \t]*<%-/ =~ elem
yield('<%')
elsif elem == '-%>'
yield('%>')
yield(:cr) if scanner.scan(/(\n|\z)/)
else
yield(elem)
end
end
end
end
Scanner.regist_scanner(ExplicitScanner, '-', false)
rescue LoadError
end
def compile(s)
enc = s.encoding
raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
s = s.dup.force_encoding("ASCII-8BIT") # don't use constant Enoding::ASCII_8BIT for miniruby
enc = detect_magic_comment(s) || enc
out = Buffer.new(self, enc)
content = ''
scanner = make_scanner(s)
scanner.scan do |token|
next if token.nil?
next if token == ''
if scanner.stag.nil?
case token
when PercentLine
add_put_cmd(out, content) if content.size > 0
content = ''
out.push(token.to_s)
out.cr
when :cr
out.cr
when '<%', '<%=', '<%#', '<%b'
scanner.stag = token
@line << content if content.size > 0
#add_put_cmd(out, content) if content.size > 0
content = ''
when "\n"
content << "\n"
@line << content
add_put_cmd(out, @line) if @print_line
@print_line = true
@line = ''
content = ''
when '<%%'
content << '<%'
else
content << token
end
else
case token
when '%>'
case scanner.stag
when '<%'
if content[-1] == ?\n
content.chop!
out.push(content)
out.cr
else
out.push(content)
end
when '<%='
add_put_cmd(out, @line)
@line = ''
add_insert_cmd(out, content)
when '<%b'
## ytti
rslt = eval(content, @binding)
if rslt
@line << rslt.to_s
else
@print_line = false
end
when '<%#'
out.push("# #{content_dump(content)}")
end
scanner.stag = nil
content = ''
when '%%>'
content << '%>'
else
content << token
end
end
end
@line << content if content.size > 0
out.close
return out.script, enc
end
def initialize(trim_mode, binding=nil)
@percent, @trim_mode = prepare_trim_mode(trim_mode)
@put_cmd = 'print'
@insert_cmd = @put_cmd
@pre_cmd = []
@post_cmd = []
@binding = binding
@print_line = true
@line = ''
end
end
end
class ERB
def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout', binding=nil)
@safe_level = safe_level
compiler = make_compiler(trim_mode, binding)
set_eoutvar(compiler, eoutvar)
@src, @enc = *compiler.compile(str)
@filename = nil
end
def make_compiler(trim_mode, binding=nil)
ERB::Compiler.new(trim_mode, binding)
end
end