converter.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. ##
  2. ## $Release: 2.7.0 $
  3. ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
  4. ##
  5. require 'erubis/util'
  6. module Erubis
  7. ##
  8. ## convert
  9. ##
  10. module Converter
  11. attr_accessor :preamble, :postamble, :escape
  12. def self.supported_properties # :nodoc:
  13. return [
  14. [:preamble, nil, "preamble (no preamble when false)"],
  15. [:postamble, nil, "postamble (no postamble when false)"],
  16. [:escape, nil, "escape expression or not in default"],
  17. ]
  18. end
  19. def init_converter(properties={})
  20. @preamble = properties[:preamble]
  21. @postamble = properties[:postamble]
  22. @escape = properties[:escape]
  23. end
  24. ## convert input string into target language
  25. def convert(input)
  26. codebuf = "" # or []
  27. @preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
  28. convert_input(codebuf, input)
  29. @postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
  30. @_proc = nil # clear cached proc object
  31. return codebuf # or codebuf.join()
  32. end
  33. protected
  34. ##
  35. ## detect spaces at beginning of line
  36. ##
  37. def detect_spaces_at_bol(text, is_bol)
  38. lspace = nil
  39. if text.empty?
  40. lspace = "" if is_bol
  41. elsif text[-1] == ?\n
  42. lspace = ""
  43. else
  44. rindex = text.rindex(?\n)
  45. if rindex
  46. s = text[rindex+1..-1]
  47. if s =~ /\A[ \t]*\z/
  48. lspace = s
  49. #text = text[0..rindex]
  50. text[rindex+1..-1] = ''
  51. end
  52. else
  53. if is_bol && text =~ /\A[ \t]*\z/
  54. #lspace = text
  55. #text = nil
  56. lspace = text.dup
  57. text[0..-1] = ''
  58. end
  59. end
  60. end
  61. return lspace
  62. end
  63. ##
  64. ## (abstract) convert input to code
  65. ##
  66. def convert_input(codebuf, input)
  67. not_implemented
  68. end
  69. end
  70. module Basic
  71. end
  72. ##
  73. ## basic converter which supports '<% ... %>' notation.
  74. ##
  75. module Basic::Converter
  76. include Erubis::Converter
  77. def self.supported_properties # :nodoc:
  78. return [
  79. [:pattern, '<% %>', "embed pattern"],
  80. [:trim, true, "trim spaces around <% ... %>"],
  81. ]
  82. end
  83. attr_accessor :pattern, :trim
  84. def init_converter(properties={})
  85. super(properties)
  86. @pattern = properties[:pattern]
  87. @trim = properties[:trim] != false
  88. end
  89. protected
  90. ## return regexp of pattern to parse eRuby script
  91. def pattern_regexp(pattern)
  92. @prefix, @postfix = pattern.split() # '<% %>' => '<%', '%>'
  93. #return /(.*?)(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
  94. #return /(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
  95. return /#{@prefix}(=+|-|\#|%)?(.*?)([-=])?#{@postfix}([ \t]*\r?\n)?/m
  96. end
  97. module_function :pattern_regexp
  98. #DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
  99. #DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
  100. #DEFAULT_REGEXP = /<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
  101. DEFAULT_REGEXP = pattern_regexp('<% %>')
  102. public
  103. def convert_input(src, input)
  104. pat = @pattern
  105. regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
  106. pos = 0
  107. is_bol = true # is beginning of line
  108. input.scan(regexp) do |indicator, code, tailch, rspace|
  109. match = Regexp.last_match()
  110. len = match.begin(0) - pos
  111. text = input[pos, len]
  112. pos = match.end(0)
  113. ch = indicator ? indicator[0] : nil
  114. lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
  115. is_bol = rspace ? true : false
  116. add_text(src, text) if text && !text.empty?
  117. ## * when '<%= %>', do nothing
  118. ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
  119. if ch == ?= # <%= %>
  120. rspace = nil if tailch && !tailch.empty?
  121. add_text(src, lspace) if lspace
  122. add_expr(src, code, indicator)
  123. add_text(src, rspace) if rspace
  124. elsif ch == ?\# # <%# %>
  125. n = code.count("\n") + (rspace ? 1 : 0)
  126. if @trim && lspace && rspace
  127. add_stmt(src, "\n" * n)
  128. else
  129. add_text(src, lspace) if lspace
  130. add_stmt(src, "\n" * n)
  131. add_text(src, rspace) if rspace
  132. end
  133. elsif ch == ?% # <%% %>
  134. s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
  135. add_text(src, s)
  136. else # <% %>
  137. if @trim && lspace && rspace
  138. add_stmt(src, "#{lspace}#{code}#{rspace}")
  139. else
  140. add_text(src, lspace) if lspace
  141. add_stmt(src, code)
  142. add_text(src, rspace) if rspace
  143. end
  144. end
  145. end
  146. #rest = $' || input # ruby1.8
  147. rest = pos == 0 ? input : input[pos..-1] # ruby1.9
  148. add_text(src, rest)
  149. end
  150. ## add expression code to src
  151. def add_expr(src, code, indicator)
  152. case indicator
  153. when '='
  154. @escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
  155. when '=='
  156. @escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
  157. when '==='
  158. add_expr_debug(src, code)
  159. end
  160. end
  161. end
  162. module PI
  163. end
  164. ##
  165. ## Processing Instructions (PI) converter for XML.
  166. ## this class converts '<?rb ... ?>' and '${...}' notation.
  167. ##
  168. module PI::Converter
  169. include Erubis::Converter
  170. def self.desc # :nodoc:
  171. "use processing instructions (PI) instead of '<% %>'"
  172. end
  173. def self.supported_properties # :nodoc:
  174. return [
  175. [:trim, true, "trim spaces around <% ... %>"],
  176. [:pi, 'rb', "PI (Processing Instrunctions) name"],
  177. [:embchar, '@', "char for embedded expression pattern('@{...}@')"],
  178. [:pattern, '<% %>', "embed pattern"],
  179. ]
  180. end
  181. attr_accessor :pi, :prefix
  182. def init_converter(properties={})
  183. super(properties)
  184. @trim = properties.fetch(:trim, true)
  185. @pi = properties[:pi] if properties[:pi]
  186. @embchar = properties[:embchar] || '@'
  187. @pattern = properties[:pattern]
  188. @pattern = '<% %>' if @pattern.nil? #|| @pattern == true
  189. end
  190. def convert(input)
  191. code = super(input)
  192. return @header || @footer ? "#{@header}#{code}#{@footer}" : code
  193. end
  194. protected
  195. def convert_input(codebuf, input)
  196. unless @regexp
  197. @pi ||= 'e'
  198. ch = Regexp.escape(@embchar)
  199. if @pattern
  200. left, right = @pattern.split(' ')
  201. @regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
  202. else
  203. @regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
  204. end
  205. end
  206. #
  207. is_bol = true
  208. pos = 0
  209. input.scan(@regexp) do |pi_arg, stmt, rspace,
  210. indicator1, expr1, indicator2, expr2|
  211. match = Regexp.last_match
  212. len = match.begin(0) - pos
  213. text = input[pos, len]
  214. pos = match.end(0)
  215. lspace = stmt ? detect_spaces_at_bol(text, is_bol) : nil
  216. is_bol = stmt && rspace ? true : false
  217. add_text(codebuf, text) # unless text.empty?
  218. #
  219. if stmt
  220. if @trim && lspace && rspace
  221. add_pi_stmt(codebuf, "#{lspace}#{stmt}#{rspace}", pi_arg)
  222. else
  223. add_text(codebuf, lspace) if lspace
  224. add_pi_stmt(codebuf, stmt, pi_arg)
  225. add_text(codebuf, rspace) if rspace
  226. end
  227. else
  228. add_pi_expr(codebuf, expr1 || expr2, indicator1 || indicator2)
  229. end
  230. end
  231. #rest = $' || input # ruby1.8
  232. rest = pos == 0 ? input : input[pos..-1] # ruby1.9
  233. add_text(codebuf, rest)
  234. end
  235. #--
  236. #def convert_input(codebuf, input)
  237. # parse_stmts(codebuf, input)
  238. # #parse_stmts2(codebuf, input)
  239. #end
  240. #
  241. #def parse_stmts(codebuf, input)
  242. # #regexp = pattern_regexp(@pattern)
  243. # @pi ||= 'e'
  244. # @stmt_pattern ||= /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
  245. # is_bol = true
  246. # pos = 0
  247. # input.scan(@stmt_pattern) do |pi_arg, code, rspace|
  248. # match = Regexp.last_match
  249. # len = match.begin(0) - pos
  250. # text = input[pos, len]
  251. # pos = match.end(0)
  252. # lspace = detect_spaces_at_bol(text, is_bol)
  253. # is_bol = rspace ? true : false
  254. # parse_exprs(codebuf, text) # unless text.empty?
  255. # if @trim && lspace && rspace
  256. # add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
  257. # else
  258. # add_text(codebuf, lspace)
  259. # add_pi_stmt(codebuf, code, pi_arg)
  260. # add_text(codebuf, rspace)
  261. # end
  262. # end
  263. # rest = $' || input
  264. # parse_exprs(codebuf, rest)
  265. #end
  266. #
  267. #def parse_exprs(codebuf, input)
  268. # unless @expr_pattern
  269. # ch = Regexp.escape(@embchar)
  270. # if @pattern
  271. # left, right = @pattern.split(' ')
  272. # @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
  273. # else
  274. # @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
  275. # end
  276. # end
  277. # pos = 0
  278. # input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
  279. # indicator = indicator1 || indicator2
  280. # code = code1 || code2
  281. # match = Regexp.last_match
  282. # len = match.begin(0) - pos
  283. # text = input[pos, len]
  284. # pos = match.end(0)
  285. # add_text(codebuf, text) # unless text.empty?
  286. # add_pi_expr(codebuf, code, indicator)
  287. # end
  288. # rest = $' || input
  289. # add_text(codebuf, rest)
  290. #end
  291. #++
  292. def add_pi_stmt(codebuf, code, pi_arg) # :nodoc:
  293. case pi_arg
  294. when nil ; add_stmt(codebuf, code)
  295. when 'header' ; @header = code
  296. when 'footer' ; @footer = code
  297. when 'comment'; add_stmt(codebuf, "\n" * code.count("\n"))
  298. when 'value' ; add_expr_literal(codebuf, code)
  299. else ; add_stmt(codebuf, code)
  300. end
  301. end
  302. def add_pi_expr(codebuf, code, indicator) # :nodoc:
  303. case indicator
  304. when nil, '', '==' # @{...}@ or <%== ... %>
  305. @escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
  306. when '!', '=' # @!{...}@ or <%= ... %>
  307. @escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
  308. when '!!', '===' # @!!{...}@ or <%=== ... %>
  309. add_expr_debug(codebuf, code)
  310. else
  311. # ignore
  312. end
  313. end
  314. end
  315. end