#!/usr/bin/env ruby ### ### $Release: 2.7.0 $ ### copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ### #--begin of require 'erubis/main' ### ### $Release: 2.7.0 $ ### copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ### require 'yaml' #--begin of require 'erubis' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## ## ## an implementation of eRuby ## ## ex. ## input = <<'END' ##
<%=text%>
' into '_buf << %Q`\#{text}
`'" end def convert_input(src, input) pat = @pattern regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat) pos = 0 is_bol = true # is beginning of line str = '' input.scan(regexp) do |indicator, code, tailch, rspace| match = Regexp.last_match() len = match.begin(0) - pos text = input[pos, len] pos = match.end(0) ch = indicator ? indicator[0] : nil lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol) is_bol = rspace ? true : false _add_text_to_str(str, text) ## * when '<%= %>', do nothing ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>' if ch == ?= # <%= %> rspace = nil if tailch && !tailch.empty? str << lspace if lspace add_expr(str, code, indicator) str << rspace if rspace elsif ch == ?\# # <%# %> n = code.count("\n") + (rspace ? 1 : 0) if @trim && lspace && rspace add_text(src, str) str = '' add_stmt(src, "\n" * n) else str << lspace if lspace add_text(src, str) str = '' add_stmt(src, "\n" * n) str << rspace if rspace end else # <% %> if @trim && lspace && rspace add_text(src, str) str = '' add_stmt(src, "#{lspace}#{code}#{rspace}") else str << lspace if lspace add_text(src, str) str = '' add_stmt(src, code) str << rspace if rspace end end end #rest = $' || input # ruby1.8 rest = pos == 0 ? input : input[pos..-1] # ruby1.9 _add_text_to_str(str, rest) add_text(src, str) end def add_text(src, text) return if !text || text.empty? #src << " _buf << %Q`" << text << "`;" if text[-1] == ?\n text[-1] = "\\n" src << " #{@bufvar} << %Q`#{text}`\n" else src << " #{@bufvar} << %Q`#{text}`;" end end def _add_text_to_str(str, text) return if !text || text.empty? str << text.gsub(/[`\#\\]/, '\\\\\&') end def add_expr_escaped(str, code) str << "\#{#{escaped_expr(code)}}" end def add_expr_literal(str, code) str << "\#{#{code}}" end end end #--end of require 'erubis/enhancer' #require 'erubis/tiny' #--begin of require 'erubis/engine/eruby' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis ## ## code generator for Ruby ## module RubyGenerator include Generator #include ArrayBufferEnhancer include StringBufferEnhancer def init_generator(properties={}) super @escapefunc ||= "Erubis::XmlHelper.escape_xml" @bufvar = properties[:bufvar] || "_buf" end def self.supported_properties() # :nodoc: return [] end def escape_text(text) text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\' end def escaped_expr(code) return "#{@escapefunc}(#{code})" end #-- #def add_preamble(src) # src << "#{@bufvar} = [];" #end #++ def add_text(src, text) src << " #{@bufvar} << '" << escape_text(text) << "';" unless text.empty? end def add_stmt(src, code) #src << code << ';' src << code src << ';' unless code[-1] == ?\n end def add_expr_literal(src, code) src << " #{@bufvar} << (" << code << ').to_s;' end def add_expr_escaped(src, code) src << " #{@bufvar} << " << escaped_expr(code) << ';' end def add_expr_debug(src, code) code.strip! s = (code.dump =~ /\A"(.*)"\z/) && $1 src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");' end #-- #def add_postamble(src) # src << "\n#{@bufvar}.join\n" #end #++ end ## ## engine for Ruby ## class Eruby < Basic::Engine include RubyEvaluator include RubyGenerator end ## ## fast engine for Ruby ## class FastEruby < Eruby include InterpolationEnhancer end ## ## swtich '<%= %>' to escaped and '<%== %>' to not escaped ## class EscapedEruby < Eruby include EscapeEnhancer end ## ## sanitize expression (<%= ... %>) by default ## ## this is equivalent to EscapedEruby and is prepared only for compatibility. ## class XmlEruby < Eruby include EscapeEnhancer end class PI::Eruby < PI::Engine include RubyEvaluator include RubyGenerator def init_converter(properties={}) @pi = 'rb' super(properties) end end end #--end of require 'erubis/engine/eruby' #require 'erubis/engine/enhanced' # enhanced eruby engines #require 'erubis/engine/optimized' # generates optimized ruby code #require 'erubis/engine/ephp' #require 'erubis/engine/ec' #require 'erubis/engine/ejava' #require 'erubis/engine/escheme' #require 'erubis/engine/eperl' #require 'erubis/engine/ejavascript' #--begin of require 'erubis/local-setting' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## ## ## you can add site-local settings here. ## this files is required by erubis.rb ## #--end of require 'erubis/local-setting' #--end of require 'erubis' #--begin of require 'erubis/tiny' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## module Erubis ## ## tiny and the simplest implementation of eRuby ## ## ex. ## eruby = TinyEruby.new(File.read('example.rhtml')) ## print eruby.src # print ruby code ## print eruby.result(binding()) # eval ruby code with Binding object ## print eruby.evalute(context) # eval ruby code with context object ## class TinyEruby def initialize(input=nil) @src = convert(input) if input end attr_reader :src EMBEDDED_PATTERN = /<%(=+|\#)?(.*?)-?%>/m def convert(input) src = "_buf = '';" # preamble pos = 0 input.scan(EMBEDDED_PATTERN) do |indicator, code| m = Regexp.last_match text = input[pos...m.begin(0)] pos = m.end(0) #src << " _buf << '" << escape_text(text) << "';" text.gsub!(/['\\]/, '\\\\\&') src << " _buf << '" << text << "';" unless text.empty? if !indicator # <% %> src << code << ";" elsif indicator == '#' # <%# %> src << ("\n" * code.count("\n")) else # <%= %> src << " _buf << (" << code << ").to_s;" end end #rest = $' || input # ruby1.8 rest = pos == 0 ? input : input[pos..-1] # ruby1.9 #src << " _buf << '" << escape_text(rest) << "';" rest.gsub!(/['\\]/, '\\\\\&') src << " _buf << '" << rest << "';" unless rest.empty? src << "\n_buf.to_s\n" # postamble return src end #def escape_text(text) # return text.gsub!(/['\\]/, '\\\\\&') || text #end def result(_binding=TOPLEVEL_BINDING) eval @src, _binding end def evaluate(_context=Object.new) if _context.is_a?(Hash) _obj = Object.new _context.each do |k, v| _obj.instance_variable_set("@#{k}", v) end _context = _obj end _context.instance_eval @src end end module PI end class PI::TinyEruby def initialize(input=nil, options={}) @escape = options[:escape] || 'Erubis::XmlHelper.escape_xml' @src = convert(input) if input end attr_reader :src EMBEDDED_PATTERN = /(^[ \t]*)?<\?rb(\s.*?)\?>([ \t]*\r?\n)?|@(!+)?\{(.*?)\}@/m def convert(input) src = "_buf = '';" # preamble pos = 0 input.scan(EMBEDDED_PATTERN) do |lspace, stmt, rspace, indicator, expr| match = Regexp.last_match len = match.begin(0) - pos text = input[pos, len] pos = match.end(0) #src << " _buf << '" << escape_text(text) << "';" text.gsub!(/['\\]/, '\\\\\&') src << " _buf << '" << text << "';" unless text.empty? if stmt # if lspace && rspace src << "#{lspace}#{stmt}#{rspace}" else src << " _buf << '" << lspace << "';" if lspace src << stmt << ";" src << " _buf << '" << rspace << "';" if rspace end else # ${...}, $!{...} if !indicator src << " _buf << " << @escape << "(" << expr << ");" elsif indicator == '!' src << " _buf << (" << expr << ").to_s;" end end end #rest = $' || input # ruby1.8 rest = pos == 0 ? input : input[pos..-1] # ruby1.9 #src << " _buf << '" << escape_text(rest) << "';" rest.gsub!(/['\\]/, '\\\\\&') src << " _buf << '" << rest << "';" unless rest.empty? src << "\n_buf.to_s\n" # postamble return src end #def escape_text(text) # return text.gsub!(/['\\]/, '\\\\\&') || text #end def result(_binding=TOPLEVEL_BINDING) eval @src, _binding end def evaluate(_context=Object.new) if _context.is_a?(Hash) _obj = Object.new _context.each do |k, v| _obj.instance_variable_set("@#{k}", v) end _context = _obj end _context.instance_eval @src end end end #--end of require 'erubis/tiny' #--begin of require 'erubis/engine/enhanced' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/enhancer' #--already included require 'erubis/engine/eruby' module Erubis #-- ## moved to engine/ruby.rb #class EscapedEruby < Eruby # include EscapeEnhancer #end #++ #-- ### (obsolete) #class FastEruby < Eruby # include FastEnhancer #end #++ class StdoutEruby < Eruby include StdoutEnhancer end class PrintOutEruby < Eruby include PrintOutEnhancer end class PrintEnabledEruby < Eruby include PrintEnabledEnhancer end class ArrayEruby < Eruby include ArrayEnhancer end class ArrayBufferEruby < Eruby include ArrayBufferEnhancer end class StringBufferEruby < Eruby include StringBufferEnhancer end class StringIOEruby < Eruby include StringIOEnhancer end class ErboutEruby < Eruby include ErboutEnhancer end class NoTextEruby < Eruby include NoTextEnhancer end class NoCodeEruby < Eruby include NoCodeEnhancer end class SimplifiedEruby < Eruby include SimplifyEnhancer end class StdoutSimplifiedEruby < Eruby include StdoutEnhancer include SimplifyEnhancer end class PrintOutSimplifiedEruby < Eruby include PrintOutEnhancer include SimplifyEnhancer end class BiPatternEruby < Eruby include BiPatternEnhancer end class PercentLineEruby < Eruby include PercentLineEnhancer end class PrefixedLineEruby < Eruby include PrefixedLineEnhancer end class HeaderFooterEruby < Eruby include HeaderFooterEnhancer end class DeleteIndentEruby < Eruby include DeleteIndentEnhancer end class InterpolationEruby < Eruby include InterpolationEnhancer end end #--end of require 'erubis/engine/enhanced' #--begin of require 'erubis/engine/optimized' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine/eruby' module Erubis module OptimizedGenerator include Generator def self.supported_properties() # :nodoc: return [] end def init_generator(properties={}) super @escapefunc ||= "Erubis::XmlHelper.escape_xml" @initialized = false @prev_is_expr = false end protected def escape_text(text) text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\' end def escaped_expr(code) @escapefunc ||= 'Erubis::XmlHelper.escape_xml' return "#{@escapefunc}(#{code})" end def switch_to_expr(src) return if @prev_is_expr @prev_is_expr = true src << ' _buf' end def switch_to_stmt(src) return unless @prev_is_expr @prev_is_expr = false src << ';' end def add_preamble(src) #@initialized = false #@prev_is_expr = false end def add_text(src, text) return if text.empty? if @initialized switch_to_expr(src) src << " << '" << escape_text(text) << "'" else src << "_buf = '" << escape_text(text) << "';" @initialized = true end end def add_stmt(src, code) switch_to_stmt(src) if @initialized #super src << code src << ';' unless code[-1] == ?\n end def add_expr_literal(src, code) unless @initialized; src << "_buf = ''"; @initialized = true; end switch_to_expr(src) src << " << (" << code << ").to_s" end def add_expr_escaped(src, code) unless @initialized; src << "_buf = ''"; @initialized = true; end switch_to_expr(src) src << " << " << escaped_expr(code) end def add_expr_debug(src, code) code.strip! s = (code.dump =~ /\A"(.*)"\z/) && $1 src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");' end def add_postamble(src) #super if @initialized src << "\n_buf\n" if @initialized end end # end of class OptimizedEruby ## ## Eruby class which generates optimized ruby code ## class OptimizedEruby < Basic::Engine # Eruby include RubyEvaluator include OptimizedGenerator def init_converter(properties={}) @pi = 'rb' super(properties) end end ## ## XmlEruby class which generates optimized ruby code ## class OptimizedXmlEruby < OptimizedEruby include EscapeEnhancer def add_expr_debug(src, code) switch_to_stmt(src) if indicator == '===' && !@initialized super end end # end of class OptimizedXmlEruby end #--end of require 'erubis/engine/optimized' #--already included require 'erubis/engine/eruby' #--begin of require 'erubis/engine/ephp' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module PhpGenerator include Generator def self.supported_properties() # :nodoc: return [] end def init_generator(properties={}) super @escapefunc ||= 'htmlspecialchars' end def add_preamble(src) # empty end def escape_text(text) return text.gsub!(/<\?xml\b/, '<?xml') || text end def add_text(src, text) src << escape_text(text) end def add_expr_literal(src, code) code.strip! src << "" end def add_expr_escaped(src, code) add_expr_literal(src, escaped_expr(code)) end def add_expr_debug(src, code) code.strip! s = code.gsub(/\'/, "\\'") src << "" end def add_stmt(src, code) src << "\n" else src << code << "?>" end end def add_postamble(src) # empty end end ## ## engine for PHP ## class Ephp < Basic::Engine include PhpGenerator end class EscapedEphp < Ephp include EscapeEnhancer end #class XmlEphp < Ephp # include EscapeEnhancer #end class PI::Ephp < PI::Engine include PhpGenerator def init_converter(properties={}) @pi = 'php' super(properties) end end end #--end of require 'erubis/engine/ephp' #--begin of require 'erubis/engine/ec' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module CGenerator include Generator def self.supported_properties() # :nodoc: return [ [:indent, '', "indent spaces (ex. ' ')"], [:out, 'stdout', "output file pointer name"], ] end def init_generator(properties={}) super @escapefunc ||= "escape" @indent = properties[:indent] || '' @out = properties[:out] || 'stdout' end def add_preamble(src) src << "#line 1 \"#{self.filename}\"\n" if self.filename end def escape_text(text) @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" } text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } return text end def escaped_expr(code) return "#{@escapefunc}(#{code.strip}, #{@out})" end def add_text(src, text) return if text.empty? src << (src.empty? || src[-1] == ?\n ? @indent : ' ') src << "fputs(" i = 0 text.each_line do |line| src << "\n" << @indent << ' ' if i > 0 i += 1 src << '"' << escape_text(line) << '"' end src << ", #{@out});" #<< (text[-1] == ?\n ? "\n" : "") src << "\n" if text[-1] == ?\n end def add_stmt(src, code) src << code end def add_expr_literal(src, code) src << @indent if src.empty? || src[-1] == ?\n src << " fprintf(#{@out}, " << code.strip << ');' end def add_expr_escaped(src, code) src << @indent if src.empty? || src[-1] == ?\n src << ' ' << escaped_expr(code) << ';' end def add_expr_debug(src, code) code.strip! s = nil if code =~ /\A\".*?\"\s*,\s*(.*)/ s = $1.gsub(/[%"]/, '\\\1') + '=' end src << @indent if src.empty? || src[-1] == ?\n src << " fprintf(stderr, \"*** debug: #{s}\" #{code});" end def add_postamble(src) # empty end end ## ## engine for C ## class Ec < Basic::Engine include CGenerator end class EscapedEc < Ec include EscapeEnhancer end #class XmlEc < Ec # include EscapeEnhancer #end class PI::Ec < PI::Engine include CGenerator def init_converter(properties={}) @pi = 'c' super(properties) end end end #--end of require 'erubis/engine/ec' #--begin of require 'erubis/engine/ecpp' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module CppGenerator include Generator def self.supported_properties() # :nodoc: return [ [:indent, '', "indent spaces (ex. ' ')"], [:bufvar, '_buf', "buffer variable name"], ] end def init_generator(properties={}) super @escapefunc ||= "escape" @indent = properties[:indent] || '' @bufvar = properties[:bufvar] || '_buf' end def add_preamble(src) src << "#line 1 \"#{self.filename}\"\n" if self.filename end def escape_text(text) @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" } text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } return text end def escaped_expr(code) return "#{@escapefunc}(#{code.strip})" end def add_text(src, text) return if text.empty? src << (src.empty? || src[-1] == ?\n ? @indent : ' ') src << "_buf << " i = 0 text.each_line do |line| src << "\n" << @indent << " " if i > 0 i += 1 src << '"' << escape_text(line) << '"' end src << ";" #<< (text[-1] == ?\n ? "\n" : "") src << "\n" if text[-1] == ?\n end def add_stmt(src, code) src << code end def add_expr_literal(src, code) src << @indent if src.empty? || src[-1] == ?\n src << " _buf << (" << code.strip << ");" end def add_expr_escaped(src, code) src << @indent if src.empty? || src[-1] == ?\n src << ' ' << escaped_expr(code) << ';' end def add_expr_debug(src, code) code.strip! src << @indent if src.empty? || src[-1] == ?\n src << " std::cerr << \"*** debug: #{code.gsub(/(")/, '\\\&')}=\" << (#{code});" end def add_postamble(src) # empty end end ## ## engine for C ## class Ecpp < Basic::Engine include CppGenerator end class EscapedEcpp < Ecpp include EscapeEnhancer end #class XmlEcpp < Ecpp # include EscapeEnhancer #end class PI::Ecpp < PI::Engine include CppGenerator def init_converter(properties={}) @pi = 'cpp' super(properties) end end end #--end of require 'erubis/engine/ecpp' #--begin of require 'erubis/engine/ejava' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module JavaGenerator include Generator def self.supported_properties() # :nodoc: return [ [:indent, '', "indent spaces (ex. ' ')"], [:bufvar, '_buf', "output buffer variable name"], [:bufclass, 'StringBuffer', "output buffer class (ex. 'StringBuilder')"], ] end def init_generator(properties={}) super @escapefunc ||= 'escape' @indent = properties[:indent] || '' @bufvar = properties[:bufvar] || '_buf' @bufclass = properties[:bufclass] || 'StringBuffer' end def add_preamble(src) src << "#{@indent}#{@bufclass} #{@bufvar} = new #{@bufclass}();" end def escape_text(text) @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" } return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text end def add_text(src, text) return if text.empty? src << (src.empty? || src[-1] == ?\n ? @indent : ' ') src << @bufvar << ".append(" i = 0 text.each_line do |line| src << "\n" << @indent << ' + ' if i > 0 i += 1 src << '"' << escape_text(line) << '"' end src << ");" << (text[-1] == ?\n ? "\n" : "") end def add_stmt(src, code) src << code end def add_expr_literal(src, code) src << @indent if src.empty? || src[-1] == ?\n code.strip! src << " #{@bufvar}.append(#{code});" end def add_expr_escaped(src, code) add_expr_literal(src, escaped_expr(code)) end def add_expr_debug(src, code) code.strip! src << @indent if src.empty? || src[-1] == ?\n src << " System.err.println(\"*** debug: #{code}=\"+(#{code}));" end def add_postamble(src) src << "\n" if src[-1] == ?; src << @indent << "return " << @bufvar << ".toString();\n" #src << @indent << "System.out.print(" << @bufvar << ".toString());\n" end end ## ## engine for Java ## class Ejava < Basic::Engine include JavaGenerator end class EscapedEjava < Ejava include EscapeEnhancer end #class XmlEjava < Ejava # include EscapeEnhancer #end class PI::Ejava < PI::Engine include JavaGenerator def init_converter(properties={}) @pi = 'java' super(properties) end end end #--end of require 'erubis/engine/ejava' #--begin of require 'erubis/engine/escheme' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module SchemeGenerator include Generator def self.supported_properties() # :nodoc: return [ [:func, '_add', "function name (ex. 'display')"], ] end def init_generator(properties={}) super @escapefunc ||= 'escape' @func = properties[:func] || '_add' # or 'display' end def add_preamble(src) return unless @func == '_add' src << "(let ((_buf '())) " + \ "(define (_add x) (set! _buf (cons x _buf))) " #src << "(let* ((_buf '())" + \ # " (_add (lambda (x) (set! _buf (cons x _buf))))) " end def escape_text(text) @table_ ||= { '"'=>'\\"', '\\'=>'\\\\' } text.gsub!(/["\\]/) { |m| @table_[m] } return text end def escaped_expr(code) code.strip! return "(#{@escapefunc} #{code})" end def add_text(src, text) return if text.empty? t = escape_text(text) if t[-1] == ?\n t[-1, 1] = '' src << "(#{@func} \"" << t << "\\n\")\n" else src << "(#{@func} \"" << t << '")' end end def add_stmt(src, code) src << code end def add_expr_literal(src, code) code.strip! src << "(#{@func} #{code})" end def add_expr_escaped(src, code) add_expr_literal(src, escaped_expr(code)) end def add_expr_debug(src, code) s = (code.strip! || code).gsub(/\"/, '\\"') src << "(display \"*** debug: #{s}=\")(display #{code.strip})(display \"\\n\")" end def add_postamble(src) return unless @func == '_add' src << "\n" unless src[-1] == ?\n src << " (reverse _buf))\n" end end ## ## engine for Scheme ## class Escheme < Basic::Engine include SchemeGenerator end class EscapedEscheme < Escheme include EscapeEnhancer end #class XmlEscheme < Escheme # include EscapeEnhancer #end class PI::Escheme < PI::Engine include SchemeGenerator def init_converter(properties={}) @pi = 'scheme' super(properties) end end end #--end of require 'erubis/engine/escheme' #--begin of require 'erubis/engine/eperl' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module PerlGenerator include Generator def self.supported_properties() # :nodoc: return [ [:func, 'print', "function name"], ] end def init_generator(properties={}) super @escapefunc ||= 'encode_entities' @func = properties[:func] || 'print' end def add_preamble(src) src << "use HTML::Entities; "; end def escape_text(text) return text.gsub!(/['\\]/, '\\\\\&') || text end def add_text(src, text) src << @func << "('" << escape_text(text) << "'); " unless text.empty? end def add_expr_literal(src, code) code.strip! src << @func << "(" << code << "); " end def add_expr_escaped(src, code) add_expr_literal(src, escaped_expr(code)) end def add_expr_debug(src, code) code.strip! s = code.gsub(/\'/, "\\'") src << @func << "('*** debug: #{code}=', #{code}, \"\\n\");" end def add_stmt(src, code) src << code end def add_postamble(src) src << "\n" unless src[-1] == ?\n end end ## ## engine for Perl ## class Eperl < Basic::Engine include PerlGenerator end class EscapedEperl < Eperl include EscapeEnhancer end #class XmlEperl < Eperl # include EscapeEnhancer #end class PI::Eperl < PI::Engine include PerlGenerator def init_converter(properties={}) @pi = 'perl' super(properties) end end end #--end of require 'erubis/engine/eperl' #--begin of require 'erubis/engine/ejavascript' ## ## $Release: 2.7.0 $ ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved. ## #--already included require 'erubis/engine' #--already included require 'erubis/enhancer' module Erubis module JavascriptGenerator include Generator def self.supported_properties() # :nodoc: list = [] #list << [:indent, '', "indent spaces (ex. ' ')"] #list << [:bufvar, '_buf', "output buffer variable name"] list << [:docwrite, true, "use 'document.write()' when true"] return list end def init_generator(properties={}) super @escapefunc ||= 'escape' @indent = properties[:indent] || '' @bufvar = properties[:bufvar] || '_buf' @docwrite = properties[:docwrite] != false # '!= false' will be removed in the next release end def add_preamble(src) src << "#{@indent}var #{@bufvar} = [];" end def escape_text(text) @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n\\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" } return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text end def add_indent(src, indent) src << (src.empty? || src[-1] == ?\n ? indent : ' ') end def add_text(src, text) return if text.empty? add_indent(src, @indent) src << @bufvar << '.push("' s = escape_text(text) if s[-1] == ?\n s[-2, 2] = '' src << s << "\");\n" else src << s << "\");" end end def add_stmt(src, code) src << code end def add_expr_literal(src, code) add_indent(src, @indent) code.strip! src << "#{@bufvar}.push(#{code});" end def add_expr_escaped(src, code) add_expr_literal(src, escaped_expr(code)) end def add_expr_debug(src, code) add_indent(src, @indent) code.strip! src << "alert(\"*** debug: #{code}=\"+(#{code}));" end def add_postamble(src) src << "\n" if src[-1] == ?; if @docwrite src << @indent << 'document.write(' << @bufvar << ".join(\"\"));\n" else src << @indent << @bufvar << ".join(\"\");\n" end end end ## ## engine for JavaScript ## class Ejavascript < Basic::Engine include JavascriptGenerator end class EscapedEjavascript < Ejavascript include EscapeEnhancer end #class XmlEjavascript < Ejavascript # include EscapeEnhancer #end class PI::Ejavascript < PI::Engine include JavascriptGenerator def init_converter(properties={}) @pi = 'js' super(properties) end end end #--end of require 'erubis/engine/ejavascript' module Erubis Ejs = Ejavascript EscapedEjs = EscapedEjavascript class CommandOptionError < ErubisError end ## ## main class of command ## ## ex. ## Main.main(ARGV) ## class Main def self.main(argv=ARGV) status = 0 begin Main.new.execute(ARGV) rescue CommandOptionError => ex $stderr.puts ex.message status = 1 end exit(status) end def initialize @single_options = "hvxztTSbeBXNUC" @arg_options = "pcrfKIlaE" #C @option_names = { 'h' => :help, 'v' => :version, 'x' => :source, 'z' => :syntax, 'T' => :unexpand, 't' => :untabify, # obsolete 'S' => :intern, 'b' => :bodyonly, 'B' => :binding, 'p' => :pattern, 'c' => :context, #'C' => :class, 'e' => :escape, 'r' => :requires, 'f' => :datafiles, 'K' => :kanji, 'I' => :includes, 'l' => :lang, 'a' => :action, 'E' => :enhancers, 'X' => :notext, 'N' => :linenum, 'U' => :unique, 'C' => :compact, } assert unless @single_options.length + @arg_options.length == @option_names.length (@single_options + @arg_options).each_byte do |ch| assert unless @option_names.key?(ch.chr) end end def execute(argv=ARGV) ## parse command-line options options, properties = parse_argv(argv, @single_options, @arg_options) filenames = argv options['h'] = true if properties[:help] opts = Object.new arr = @option_names.collect {|ch, name| "def #{name}; @#{name}; end\n" } opts.instance_eval arr.join options.each do |ch, val| name = @option_names[ch] opts.instance_variable_set("@#{name}", val) end ## help, version, enhancer list if opts.help || opts.version puts version() if opts.version puts usage() if opts.help puts show_properties() if opts.help puts show_enhancers() if opts.help return end ## include path opts.includes.split(/,/).each do |path| $: << path end if opts.includes ## require library opts.requires.split(/,/).each do |library| require library end if opts.requires ## action action = opts.action action ||= 'syntax' if opts.syntax action ||= 'convert' if opts.source || opts.notext ## lang lang = opts.lang || 'ruby' action ||= 'convert' if opts.lang ## class name of Eruby #classname = opts.class classname = nil klass = get_classobj(classname, lang, properties[:pi]) ## kanji code $KCODE = opts.kanji if opts.kanji ## read context values from yaml file datafiles = opts.datafiles context = load_datafiles(datafiles, opts) ## parse context data if opts.context context = parse_context_data(opts.context, opts) end ## properties for engine properties[:escape] = true if opts.escape && !properties.key?(:escape) properties[:pattern] = opts.pattern if opts.pattern #properties[:trim] = false if opts.notrim properties[:preamble] = properties[:postamble] = false if opts.bodyonly properties[:pi] = nil if properties[:pi] == true ## create engine and extend enhancers engine = klass.new(nil, properties) enhancers = get_enhancers(opts.enhancers) #enhancers.push(Erubis::EscapeEnhancer) if opts.escape enhancers.each do |enhancer| engine.extend(enhancer) engine.bipattern = properties[:bipattern] if enhancer == Erubis::BiPatternEnhancer end ## no-text engine.extend(Erubis::NoTextEnhancer) if opts.notext ## convert and execute val = nil msg = "Syntax OK\n" if filenames && !filenames.empty? filenames.each do |filename| File.file?(filename) or raise CommandOptionError.new("#{filename}: file not found.") engine.filename = filename engine.convert!(File.read(filename)) val = do_action(action, engine, context, filename, opts) msg = nil if val end else engine.filename = filename = '(stdin)' engine.convert!($stdin.read()) val = do_action(action, engine, context, filename, opts) msg = nil if val end print msg if action == 'syntax' && msg end private def do_action(action, engine, context, filename, opts) case action when 'convert' s = manipulate_src(engine.src, opts) when nil, 'exec', 'execute' s = opts.binding ? engine.result(context.to_hash) : engine.evaluate(context) when 'syntax' s = check_syntax(filename, engine.src) else raise "*** internal error" end print s if s return s end def manipulate_src(source, opts) flag_linenum = opts.linenum flag_unique = opts.unique flag_compact = opts.compact if flag_linenum n = 0 source.gsub!(/^/) { n += 1; "%5d: " % n } source.gsub!(/^ *\d+:\s+?\n/, '') if flag_compact source.gsub!(/(^ *\d+:\s+?\n)+/, "\n") if flag_unique else source.gsub!(/^\s*?\n/, '') if flag_compact source.gsub!(/(^\s*?\n)+/, "\n") if flag_unique end return source end def usage(command=nil) command ||= File.basename($0) buf = [] buf << "erubis - embedded program converter for multi-language" buf << "Usage: #{command} [..options..] [file ...]" buf << " -h, --help : help" buf << " -v : version" buf << " -x : show converted code" buf << " -X : show converted code, only ruby code and no text part" buf << " -N : numbering: add line numbers (for '-x/-X')" buf << " -U : unique: compress empty lines to a line (for '-x/-X')" buf << " -C : compact: remove empty lines (for '-x/-X')" buf << " -b : body only: no preamble nor postamble (for '-x/-X')" buf << " -z : syntax checking" buf << " -e : escape (equal to '--E Escape')" buf << " -p pattern : embedded pattern (default '<% %>')" buf << " -l lang : convert but no execute (ruby/php/c/cpp/java/scheme/perl/js)" buf << " -E e1,e2,... : enhancer names (Escape, PercentLine, BiPattern, ...)" buf << " -I path : library include path" buf << " -K kanji : kanji code (euc/sjis/utf8) (default none)" buf << " -c context : context data string (yaml inline style or ruby code)" buf << " -f datafile : context data file ('*.yaml', '*.yml', or '*.rb')" #buf << " -t : expand tab characters in YAML file" buf << " -T : don't expand tab characters in YAML file" buf << " -S : convert mapping key from string to symbol in YAML file" buf << " -B : invoke 'result(binding)' instead of 'evaluate(context)'" buf << " --pi=name : parse '' instead of '<% ... %>'" #' # -T : don't trim spaces around '<% %>' # -c class : class name (XmlEruby/PercentLineEruby/...) (default Eruby) # -r library : require library # -a : action (convert/execute) return buf.join("\n") end def collect_supported_properties(erubis_klass) list = [] erubis_klass.ancestors.each do |klass| if klass.respond_to?(:supported_properties) list.concat(klass.supported_properties) end end return list end def show_properties s = "supported properties:\n" basic_props = collect_supported_properties(Erubis::Basic::Engine) pi_props = collect_supported_properties(Erubis::PI::Engine) list = [] common_props = basic_props & pi_props list << ['(common)', common_props] list << ['(basic)', basic_props - common_props] list << ['(pi)', pi_props - common_props] %w[ruby php c cpp java scheme perl javascript].each do |lang| klass = Erubis.const_get("E#{lang}") list << [lang, collect_supported_properties(klass) - basic_props] end list.each do |lang, props| s << " * #{lang}\n" props.each do |name, default_val, desc| s << (" --%-23s : %s\n" % ["#{name}=#{default_val.inspect}", desc]) end end s << "\n" return s end def show_enhancers dict = {} ObjectSpace.each_object(Module) do |mod| dict[$1] = mod if mod.name =~ /\AErubis::(.*)Enhancer\z/ end s = "enhancers:\n" dict.sort_by {|name, mod| name }.each do |name, mod| s << (" %-13s : %s\n" % [name, mod.desc]) end return s end def version return Erubis::VERSION end def parse_argv(argv, arg_none='', arg_required='', arg_optional='') options = {} context = {} while argv[0] && argv[0][0] == ?- optstr = argv.shift optstr = optstr[1, optstr.length-1] # if optstr[0] == ?- # context optstr =~ /\A\-([-\w]+)(?:=(.*))?/ or raise CommandOptionError.new("-#{optstr}: invalid context value.") name, value = $1, $2 name = name.gsub(/-/, '_').intern #value = value.nil? ? true : YAML.load(value) # error, why? value = value.nil? ? true : YAML.load("---\n#{value}\n") context[name] = value # else # options while optstr && !optstr.empty? optchar = optstr[0].chr optstr = optstr[1..-1] if arg_none.include?(optchar) options[optchar] = true elsif arg_required.include?(optchar) arg = optstr.empty? ? argv.shift : optstr or raise CommandOptionError.new("-#{optchar}: #{@option_names[optchar]} required.") options[optchar] = arg optstr = nil elsif arg_optional.include?(optchar) arg = optstr.empty? ? true : optstr options[optchar] = arg optstr = nil else raise CommandOptionError.new("-#{optchar}: unknown option.") end end end # end # end of while return options, context end def untabify(str, width=8) list = str.split(/\t/) last = list.pop sb = '' list.each do |s| column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length n = width - (column % width) sb << s << (' ' * n) end sb << last return sb end #-- #def untabify(str, width=8) # sb = '' # str.scan(/(.*?)\t/m) do |s, | # len = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length # sb << s << (" " * (width - len % width)) # end # return $' ? (sb << $') : str #end #++ def get_classobj(classname, lang, pi) classname ||= "E#{lang}" base_module = pi ? Erubis::PI : Erubis begin klass = base_module.const_get(classname) rescue NameError klass = nil end unless klass if lang msg = "-l #{lang}: invalid language name (class #{base_module.name}::#{classname} not found)." else msg = "-c #{classname}: invalid class name." end raise CommandOptionError.new(msg) end return klass end def get_enhancers(enhancer_names) return [] unless enhancer_names enhancers = [] shortname = nil begin enhancer_names.split(/,/).each do |name| shortname = name enhancers << Erubis.const_get("#{shortname}Enhancer") end rescue NameError raise CommandOptionError.new("#{shortname}: no such Enhancer (try '-h' to show all enhancers).") end return enhancers end def load_datafiles(filenames, opts) context = Erubis::Context.new return context unless filenames filenames.split(/,/).each do |filename| filename.strip! test(?f, filename) or raise CommandOptionError.new("#{filename}: file not found.") if filename =~ /\.ya?ml$/ if opts.unexpand ydoc = YAML.load_file(filename) else ydoc = YAML.load(untabify(File.read(filename))) end ydoc.is_a?(Hash) or raise CommandOptionError.new("#{filename}: root object is not a mapping.") intern_hash_keys(ydoc) if opts.intern context.update(ydoc) elsif filename =~ /\.rb$/ str = File.read(filename) context2 = Erubis::Context.new _instance_eval(context2, str) context.update(context2) else CommandOptionError.new("#{filename}: '*.yaml', '*.yml', or '*.rb' required.") end end return context end def _instance_eval(_context, _str) _context.instance_eval(_str) end def parse_context_data(context_str, opts) if context_str[0] == ?{ require 'yaml' ydoc = YAML.load(context_str) unless ydoc.is_a?(Hash) raise CommandOptionError.new("-c: root object is not a mapping.") end intern_hash_keys(ydoc) if opts.intern return ydoc else context = Erubis::Context.new context.instance_eval(context_str, '-c') return context end end def intern_hash_keys(obj, done={}) return if done.key?(obj.__id__) case obj when Hash done[obj.__id__] = obj obj.keys.each do |key| obj[key.intern] = obj.delete(key) if key.is_a?(String) end obj.values.each do |val| intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array) end when Array done[obj.__id__] = obj obj.each do |val| intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array) end end end def check_syntax(filename, src) require 'open3' #command = (ENV['_'] || 'ruby') + ' -wc' # ENV['_'] stores command name bin = ENV['_'] && File.basename(ENV['_']) =~ /^ruby/ ? ENV['_'] : 'ruby' command = bin + ' -wc' stdin, stdout, stderr = Open3.popen3(command) stdin.write(src) stdin.close result = stdout.read() stdout.close() errmsg = stderr.read() stderr.close() return nil unless errmsg && !errmsg.empty? errmsg =~ /\A-:(\d+): / linenum, message = $1, $' return "#{filename}:#{linenum}: #{message}" end if defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx" def check_syntax(filename, src) require 'compiler' verbose = $VERBOSE msg = nil begin $VERBOSE = true Rubinius::Compiler.compile_string(src, filename) rescue SyntaxError => ex ex_linenum = ex.line linenum = 0 srcline = src.each_line do |line| linenum += 1 break line if linenum == ex_linenum end msg = "#{ex.message}\n" msg << srcline msg << "\n" unless srcline =~ /\n\z/ msg << (" " * (ex.column-1)) << "^\n" ensure $VERBOSE = verbose end return msg end end end end #--end of require 'erubis/main' Erubis::Main.main(ARGV)