class PuppetLint::Lexer::StringSlurper

Document this TODO

Constants

END_INTERP_PATTERN
END_STRING_PATTERN
ESC_DQUOTE_PATTERN
LBRACE_PATTERN
START_INTERP_PATTERN
UNENC_VAR_PATTERN

Attributes

interp_stack[RW]
results[RW]
scanner[RW]

Public Class Methods

new(string) click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 21
def initialize(string)
  @scanner = StringScanner.new(string)
  @results = []
  @interp_stack = []
  @segment = []
end

Public Instance Methods

consumed_chars() click to toggle source

Get the number of characters consumed by the StringSlurper.

StringScanner from Ruby 2.0 onwards supports charpos which returns the number of characters and is multibyte character aware.

Prior to this, Ruby's multibyte character support in Strings was a bit unusual and neither String#length nor String#split behave as expected, so we use String#scan to split all the consumed text using a UTF-8 aware regex and use the length of the result

# File lib/puppet-lint/lexer/string_slurper.rb, line 110
def consumed_chars
  return scanner.charpos if scanner.respond_to?(:charpos)

  (scanner.pre_match + scanner.matched).scan(%r{.}mu).length
end
end_heredoc(pattern) click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 158
def end_heredoc(pattern)
  results << [:HEREDOC, @segment.join]
  results << [:HEREDOC_TERM, scanner.scan(pattern)]
end
end_interp() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 133
def end_interp
  if interp_stack.empty?
    @segment << scanner.scan(END_INTERP_PATTERN)
    return
  else
    interp_stack.pop
  end

  if interp_stack.empty?
    results << [:INTERP, @segment.join]
    @segment = []
    scanner.skip(END_INTERP_PATTERN)
  else
    @segment << scanner.scan(END_INTERP_PATTERN)
  end
end
end_string() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 163
def end_string
  if interp_stack.empty?
    @segment << scanner.scan(END_STRING_PATTERN).gsub!(%r{"\Z}, '')
    results << [@segment_type, @segment.join]
  else
    @segment << scanner.scan(END_STRING_PATTERN)
  end
end
parse() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 28
def parse
  @segment_type = :STRING

  until scanner.eos?
    if scanner.match?(START_INTERP_PATTERN)
      start_interp
    elsif !interp_stack.empty? && scanner.match?(LBRACE_PATTERN)
      read_char
    elsif scanner.match?(END_INTERP_PATTERN)
      end_interp
    elsif unenclosed_variable?
      unenclosed_variable
    elsif scanner.match?(END_STRING_PATTERN)
      end_string
      break if interp_stack.empty?
    elsif scanner.match?(ESC_DQUOTE_PATTERN)
      @segment << scanner.scan(ESC_DQUOTE_PATTERN)
    else
      read_char
    end
  end

  raise UnterminatedStringError if results.empty? && scanner.matched?

  results
end
parse_heredoc(heredoc_tag) click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 61
def parse_heredoc(heredoc_tag)
  heredoc_name = heredoc_tag[%r{\A"?(.+?)"?(:.+?)?#{PuppetLint::Lexer::WHITESPACE_RE}*(/.*)?\Z}, 1]
  end_heredoc_pattern = %r{^\|?\s*-?\s*#{Regexp.escape(heredoc_name)}$}
  interpolation = heredoc_tag.start_with?('"')

  @segment_type = :HEREDOC

  until scanner.eos?
    if scanner.match?(end_heredoc_pattern)
      end_heredoc(end_heredoc_pattern)
      break if interp_stack.empty?
    elsif interpolation && scanner.match?(START_INTERP_PATTERN)
      start_interp
    elsif interpolation && !interp_stack.empty? && scanner.match?(LBRACE_PATTERN)
      read_char
    elsif interpolation && unenclosed_variable?
      unenclosed_variable
    elsif interpolation && scanner.match?(END_INTERP_PATTERN)
      end_interp
    else
      read_char
    end
  end

  results
end
read_char() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 88
def read_char
  @segment << scanner.getch

  return if interp_stack.empty?

  case @segment.last
  when '{'
    interp_stack.push(true)
  when '}'
    interp_stack.pop
  end
end
start_interp() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 116
def start_interp
  if @segment.last && @segment.last == '\\'
    read_char
    return
  end

  if interp_stack.empty?
    scanner.skip(START_INTERP_PATTERN)
    results << [@segment_type, @segment.join]
    @segment = []
  else
    @segment << scanner.scan(START_INTERP_PATTERN)
  end

  interp_stack.push(true)
end
unenclosed_variable() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 150
def unenclosed_variable
  read_char if scanner.match?(%r{.\$})

  results << [@segment_type, @segment.join]
  results << [:UNENC_VAR, scanner.scan(UNENC_VAR_PATTERN)]
  @segment = []
end
unenclosed_variable?() click to toggle source
# File lib/puppet-lint/lexer/string_slurper.rb, line 55
def unenclosed_variable?
  interp_stack.empty? &&
    scanner.match?(UNENC_VAR_PATTERN) &&
    (@segment.last.nil? ? true : !@segment.last.end_with?('\\'))
end