class Temple::Filters::StringSplitter

Compile [:dynamic, “foo#{bar}”] to [:multi, [:static, 'foo'], [:dynamic, 'bar']]

Public Class Methods

compile(code) click to toggle source

`code` param must be valid string literal

# File lib/temple/filters/string_splitter.rb, line 13
def compile(code)
  [].tap do |exps|
    tokens = Ripper.lex(code.strip)
    tokens.pop while tokens.last && [:on_comment, :on_sp].include?(tokens.last[1])

    if tokens.size < 2
      raise(FilterError, "Expected token size >= 2 but got: #{tokens.size}")
    end
    compile_tokens!(exps, tokens)
  end
end

Private Class Methods

compile_tokens!(exps, tokens) click to toggle source
# File lib/temple/filters/string_splitter.rb, line 41
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}").to_s]
    when :on_embexpr_beg
      embedded = shift_balanced_embexpr(tokens)
      exps << [:dynamic, embedded] unless embedded.empty?
    end
  end
end
shift_balanced_embexpr(tokens) click to toggle source
# File lib/temple/filters/string_splitter.rb, line 57
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
strip_quotes!(tokens) click to toggle source
# File lib/temple/filters/string_splitter.rb, line 27
def strip_quotes!(tokens)
  _, type, beg_str = tokens.shift
  if type != :on_tstring_beg
    raise(FilterError, "Expected :on_tstring_beg but got: #{type}")
  end

  _, type, end_str = tokens.pop
  if type != :on_tstring_end
    raise(FilterError, "Expected :on_tstring_end but got: #{type}")
  end

  [beg_str, end_str]
end

Public Instance Methods

call(ast) click to toggle source

Do nothing if ripper is unavailable

# File lib/temple/filters/string_splitter.rb, line 124
def call(ast)
  ast
end
on_dynamic(code) click to toggle source
# File lib/temple/filters/string_splitter.rb, line 77
def on_dynamic(code)
  return [:dynamic, code] unless 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
string_literal?(code) click to toggle source
# File lib/temple/filters/string_splitter.rb, line 95
def string_literal?(code)
  return false if SyntaxChecker.syntax_error?(code)

  type, instructions = Ripper.sexp(code)
  return false if type != :program
  return false if instructions.size > 1

  type, _ = instructions.first
  type == :string_literal
end