class Parser::Lexer::Literal

Constants

DELIMITERS
TYPES

Attributes

dedent_level[R]
heredoc_e[R]
saved_herebody_s[RW]
str_s[R]

Public Class Methods

new(lexer, str_type, delimiter, str_s, heredoc_e = nil, indent = false, dedent_body = false, label_allowed = false) click to toggle source
# File lib/parser/lexer/literal.rb, line 38
def initialize(lexer, str_type, delimiter, str_s, heredoc_e = nil,
               indent = false, dedent_body = false, label_allowed = false)
  @lexer       = lexer
  @nesting     = 1

  # DELIMITERS and TYPES are hashes with keys encoded in binary.
  # Coerce incoming data to the same encoding.
  str_type     = coerce_encoding(str_type)
  delimiter    = coerce_encoding(delimiter)

  unless TYPES.include?(str_type)
    lexer.send(:diagnostic, :error, :unexpected_percent_str,
               { :type => str_type }, @lexer.send(:range, str_s, str_s + 2))
  end

  # String type. For :'foo', it is :'
  @str_type    = str_type
  # Start of the string type specifier.
  @str_s       = str_s

  @start_tok, @interpolate = TYPES[str_type]
  @start_delim = DELIMITERS.include?(delimiter) ? delimiter : nil
  @end_delim   = DELIMITERS.fetch(delimiter, delimiter)

  @heredoc_e     = heredoc_e
  @indent        = indent
  @label_allowed = label_allowed

  @dedent_body   = dedent_body
  @dedent_level  = nil

  @interp_braces = 0

  @space_emitted = true

  # Monolithic strings are glued into a single token, e.g.
  # tSTRING_BEG tSTRING_CONTENT tSTRING_END -> tSTRING.
  @monolithic  = (@start_tok == :tSTRING_BEG  &&
                  %w(' ").include?(str_type) &&
                  !heredoc?)

  # Capture opening delimiter in percent-literals.
  @str_type += delimiter if @str_type.start_with?('%'.freeze)

  clear_buffer

  emit_start_tok unless @monolithic
end

Public Instance Methods

backslash_delimited?() click to toggle source
# File lib/parser/lexer/literal.rb, line 112
def backslash_delimited?
  @end_delim == '\\'.freeze
end
end_interp_brace_and_try_closing() click to toggle source
# File lib/parser/lexer/literal.rb, line 187
def end_interp_brace_and_try_closing
  @interp_braces -= 1

  (@interp_braces == 0)
end
extend_content() click to toggle source
# File lib/parser/lexer/literal.rb, line 214
def extend_content
  @space_emitted = false
end
extend_space(ts, te) click to toggle source
# File lib/parser/lexer/literal.rb, line 218
def extend_space(ts, te)
  flush_string

  unless @space_emitted
    emit(:tSPACE, nil, ts, te)

    @space_emitted = true
  end
end
extend_string(string, ts, te) click to toggle source
# File lib/parser/lexer/literal.rb, line 193
def extend_string(string, ts, te)
  @buffer_s ||= ts
  @buffer_e = te

  @buffer << string
end
flush_string() click to toggle source
# File lib/parser/lexer/literal.rb, line 200
def flush_string
  if @monolithic
    emit_start_tok
    @monolithic = false
  end

  unless @buffer.empty?
    emit(:tSTRING_CONTENT, @buffer, @buffer_s, @buffer_e)

    clear_buffer
    extend_content
  end
end
heredoc?() click to toggle source
# File lib/parser/lexer/literal.rb, line 100
def heredoc?
  !!@heredoc_e
end
infer_indent_level(line) click to toggle source
# File lib/parser/lexer/literal.rb, line 164
def infer_indent_level(line)
  return if !@dedent_body

  indent_level = 0
  line.each_char do |char|
    case char
    when ?\s
      indent_level += 1
    when ?\t
      indent_level += (8 - indent_level % 8)
    else
      if @dedent_level.nil? || @dedent_level > indent_level
        @dedent_level = indent_level
      end
      break
    end
  end
end
interpolate?() click to toggle source
# File lib/parser/lexer/literal.rb, line 87
def interpolate?
  @interpolate
end
munge_escape?(character) click to toggle source
# File lib/parser/lexer/literal.rb, line 120
def munge_escape?(character)
  character = coerce_encoding(character)

  if words? && character =~ /[ \t\v\r\f\n]/
    true
  else
    ['\\'.freeze, @start_delim, @end_delim].include?(character)
  end
end
nest_and_try_closing(delimiter, ts, te, lookahead=nil) click to toggle source
# File lib/parser/lexer/literal.rb, line 130
def nest_and_try_closing(delimiter, ts, te, lookahead=nil)
  delimiter = coerce_encoding(delimiter)

  if @start_delim && @start_delim == delimiter
    @nesting += 1
  elsif delimiter?(delimiter)
    @nesting -= 1
  end

  # Finalize if last matching delimiter is closed.
  if @nesting == 0
    if words?
      extend_space(ts, ts)
    end

    if lookahead && @label_allowed && lookahead[0] == ?: &&
       lookahead[1] != ?: && @start_tok == :tSTRING_BEG
      # This is a quoted label.
      flush_string
      emit(:tLABEL_END, @end_delim, ts, te + 1)
    elsif @monolithic
      # Emit the string as a single token.
      emit(:tSTRING, @buffer, @str_s, te)
    else
      # If this is a heredoc, @buffer contains the sentinel now.
      # Just throw it out. Lexer flushes the heredoc after each
      # non-heredoc-terminating \n anyway, so no data will be lost.
      flush_string unless heredoc?

      emit(:tSTRING_END, @end_delim, ts, te)
    end
  end
end
plain_heredoc?() click to toggle source
# File lib/parser/lexer/literal.rb, line 104
def plain_heredoc?
  heredoc? && !@dedent_body
end
regexp?() click to toggle source
# File lib/parser/lexer/literal.rb, line 96
def regexp?
  type == :tREGEXP_BEG
end
squiggly_heredoc?() click to toggle source
# File lib/parser/lexer/literal.rb, line 108
def squiggly_heredoc?
  heredoc? && @dedent_body
end
start_interp_brace() click to toggle source
# File lib/parser/lexer/literal.rb, line 183
def start_interp_brace
  @interp_braces += 1
end
supports_line_continuation_via_slash?() click to toggle source
# File lib/parser/lexer/literal.rb, line 228
def supports_line_continuation_via_slash?
  !words? && @interpolate
end
type() click to toggle source
# File lib/parser/lexer/literal.rb, line 116
def type
  @start_tok
end
words?() click to toggle source
# File lib/parser/lexer/literal.rb, line 91
def words?
  type == :tWORDS_BEG || type == :tQWORDS_BEG ||
    type == :tSYMBOLS_BEG || type == :tQSYMBOLS_BEG
end

Protected Instance Methods

clear_buffer() click to toggle source
# File lib/parser/lexer/literal.rb, line 246
def clear_buffer
  @buffer = ''.dup

  # Prime the buffer with lexer encoding; otherwise,
  # concatenation will produce varying results.
  @buffer.force_encoding(@lexer.source_buffer.source.encoding)

  @buffer_s = nil
  @buffer_e = nil
end
coerce_encoding(string) click to toggle source
# File lib/parser/lexer/literal.rb, line 242
def coerce_encoding(string)
  string.b
end
delimiter?(delimiter) click to toggle source
# File lib/parser/lexer/literal.rb, line 234
def delimiter?(delimiter)
  if @indent
    @end_delim == delimiter.lstrip
  else
    @end_delim == delimiter
  end
end
emit(token, type, s, e) click to toggle source
# File lib/parser/lexer/literal.rb, line 262
def emit(token, type, s, e)
  @lexer.send(:emit, token, type, s, e)
end
emit_start_tok() click to toggle source
# File lib/parser/lexer/literal.rb, line 257
def emit_start_tok
  str_e = @heredoc_e || @str_s + @str_type.length
  emit(@start_tok, @str_type, @str_s, str_e)
end