Class: Parser::Lexer::Literal

Inherits:
Object
  • Object
show all
Defined in:
lib/parser/lexer/literal.rb

Constant Summary collapse

DELIMITERS =
{ '(' => ')', '[' => ']', '{' => '}', '<' => '>' }
TYPES =
{
# type       start token     interpolate?
  "'"   => [ :tSTRING_BEG,   false ],
  "<<'" => [ :tSTRING_BEG,   false ],
  '%q'  => [ :tSTRING_BEG,   false ],
  '"'   => [ :tSTRING_BEG,   true  ],
  '<<"' => [ :tSTRING_BEG,   true  ],
  '%'   => [ :tSTRING_BEG,   true  ],
  '%Q'  => [ :tSTRING_BEG,   true  ],

  '%w'  => [ :tQWORDS_BEG,   false ],
  '%W'  => [ :tWORDS_BEG,    true  ],

  '%i'  => [ :tQSYMBOLS_BEG, false ],
  '%I'  => [ :tSYMBOLS_BEG,  true  ],

  ":'"  => [ :tSYMBEG,       false ],
  '%s'  => [ :tSYMBEG,       false ],
  ':"'  => [ :tSYMBEG,       true  ],

  '/'   => [ :tREGEXP_BEG,   true  ],
  '%r'  => [ :tREGEXP_BEG,   true  ],

  '%x'  => [ :tXSTRING_BEG,  true  ],
  '`'   => [ :tXSTRING_BEG,  true  ],
  '<<`' => [ :tXSTRING_BEG,  true  ],
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lexer, str_type, delimiter, str_s, heredoc_e = nil, indent = false, dedent_body = false, label_allowed = false) ⇒ Literal

Returns a new instance of Literal.



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
# File 'lib/parser/lexer/literal.rb', line 40

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

Instance Attribute Details

#dedent_levelObject (readonly)



37
38
39
# File 'lib/parser/lexer/literal.rb', line 37

def dedent_level
  @dedent_level
end

#heredoc_eObject (readonly)



37
38
39
# File 'lib/parser/lexer/literal.rb', line 37

def heredoc_e
  @heredoc_e
end

#saved_herebody_sObject



38
39
40
# File 'lib/parser/lexer/literal.rb', line 38

def saved_herebody_s
  @saved_herebody_s
end

#str_sObject (readonly)



37
38
39
# File 'lib/parser/lexer/literal.rb', line 37

def str_s
  @str_s
end

Instance Method Details

#backslash_delimited?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'lib/parser/lexer/literal.rb', line 114

def backslash_delimited?
  @end_delim == '\\'.freeze
end

#end_interp_brace_and_try_closingObject



189
190
191
192
193
# File 'lib/parser/lexer/literal.rb', line 189

def end_interp_brace_and_try_closing
  @interp_braces -= 1

  (@interp_braces == 0)
end

#extend_contentObject



216
217
218
# File 'lib/parser/lexer/literal.rb', line 216

def extend_content
  @space_emitted = false
end

#extend_space(ts, te) ⇒ Object



220
221
222
223
224
225
226
227
228
# File 'lib/parser/lexer/literal.rb', line 220

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) ⇒ Object



195
196
197
198
199
200
# File 'lib/parser/lexer/literal.rb', line 195

def extend_string(string, ts, te)
  @buffer_s ||= ts
  @buffer_e = te

  @buffer << string
end

#flush_stringObject



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/parser/lexer/literal.rb', line 202

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?Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/parser/lexer/literal.rb', line 102

def heredoc?
  !!@heredoc_e
end

#infer_indent_level(line) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/parser/lexer/literal.rb', line 166

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?Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/parser/lexer/literal.rb', line 89

def interpolate?
  @interpolate
end

#munge_escape?(character) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
# File 'lib/parser/lexer/literal.rb', line 122

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) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/parser/lexer/literal.rb', line 132

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?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/parser/lexer/literal.rb', line 106

def plain_heredoc?
  heredoc? && !@dedent_body
end

#regexp?Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/parser/lexer/literal.rb', line 98

def regexp?
  type == :tREGEXP_BEG
end

#squiggly_heredoc?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/parser/lexer/literal.rb', line 110

def squiggly_heredoc?
  heredoc? && @dedent_body
end

#start_interp_braceObject



185
186
187
# File 'lib/parser/lexer/literal.rb', line 185

def start_interp_brace
  @interp_braces += 1
end

#supports_line_continuation_via_slash?Boolean

Returns:

  • (Boolean)


230
231
232
# File 'lib/parser/lexer/literal.rb', line 230

def supports_line_continuation_via_slash?
  !words? && @interpolate
end

#typeObject



118
119
120
# File 'lib/parser/lexer/literal.rb', line 118

def type
  @start_tok
end

#words?Boolean

Returns:

  • (Boolean)


93
94
95
96
# File 'lib/parser/lexer/literal.rb', line 93

def words?
  type == :tWORDS_BEG || type == :tQWORDS_BEG ||
    type == :tSYMBOLS_BEG || type == :tQSYMBOLS_BEG
end