1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
|
require 'ripper'
require 'hamlit/ruby_expression'
module Hamlit
class StringSplitter < Temple::Filter
class << self
# `code` param must be valid string literal
def compile(code)
[].tap do |exps|
tokens = Ripper.lex(code.strip)
tokens.pop while tokens.last && %i[on_comment on_sp].include?(tokens.last[1])
if tokens.size < 2
raise Hamlit::InternalError.new("Expected token size >= 2 but got: #{tokens.size}")
end
compile_tokens!(exps, tokens)
end
end
private
def strip_quotes!(tokens)
_, type, beg_str = tokens.shift
if type != :on_tstring_beg
raise Hamlit::InternalError.new("Expected :on_tstring_beg but got: #{type}")
end
_, type, end_str = tokens.pop
if type != :on_tstring_end
raise Hamlit::InternalError.new("Expected :on_tstring_end but got: #{type}")
end
[beg_str, end_str]
end
def compile_tokens!(exps, tokens)
beg_str, end_str = strip_quotes!(tokens)
until tokens.empty?
_, type, str = tokens.shift
case type
when :on_tstring_content
exps << [:static, eval("#{beg_str}#{str}#{end_str}")]
when :on_embexpr_beg
embedded = shift_balanced_embexpr(tokens)
exps << [:dynamic, embedded] unless embedded.empty?
end
end
end
def shift_balanced_embexpr(tokens)
String.new.tap do |embedded|
embexpr_open = 1
until tokens.empty?
_, type, str = tokens.shift
case type
when :on_embexpr_beg
embexpr_open += 1
when :on_embexpr_end
embexpr_open -= 1
break if embexpr_open == 0
end
embedded << str
end
end
end
end
def on_dynamic(code)
return [:dynamic, code] unless RubyExpression.string_literal?(code)
return [:dynamic, code] if code.include?("\n")
temple = [:multi]
StringSplitter.compile(code).each do |type, content|
case type
when :static
temple << [:static, content]
when :dynamic
temple << on_dynamic(content)
end
end
temple
end
end
end
|