Class: Parser::Lexer::Literal

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

Constant Summary collapse

DELIMITERS =
{ '(' => ')', '[' => ']', '{' => '}', '<' => '>' }
SPACE =
' '.ord
TAB =
"\t".ord
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.



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

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)



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

def dedent_level
  @dedent_level
end

#heredoc_eObject (readonly)



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

def heredoc_e
  @heredoc_e
end

#saved_herebody_sObject



40
41
42
# File 'lib/parser/lexer/literal.rb', line 40

def saved_herebody_s
  @saved_herebody_s
end

#str_sObject (readonly)



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

def str_s
  @str_s
end

Instance Method Details

#backslash_delimited?Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/parser/lexer/literal.rb', line 116

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

#end_interp_brace_and_try_closingObject



191
192
193
194
195
# File 'lib/parser/lexer/literal.rb', line 191

def end_interp_brace_and_try_closing
  @interp_braces -= 1

  (@interp_braces == 0)
end

#extend_contentObject



218
219
220
# File 'lib/parser/lexer/literal.rb', line 218

def extend_content
  @space_emitted = false
end

#extend_space(ts, te) ⇒ Object



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

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



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

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

  @buffer << string
end

#flush_stringObject



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

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)


104
105
106
# File 'lib/parser/lexer/literal.rb', line 104

def heredoc?
  !!@heredoc_e
end

#infer_indent_level(line) ⇒ Object



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

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)


91
92
93
# File 'lib/parser/lexer/literal.rb', line 91

def interpolate?
  @interpolate
end

#munge_escape?(character) ⇒ Boolean

Returns:

  • (Boolean)


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

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



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

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)


108
109
110
# File 'lib/parser/lexer/literal.rb', line 108

def plain_heredoc?
  heredoc? && !@dedent_body
end

#regexp?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/parser/lexer/literal.rb', line 100

def regexp?
  type == :tREGEXP_BEG
end

#squiggly_heredoc?Boolean

Returns:

  • (Boolean)


112
113
114
# File 'lib/parser/lexer/literal.rb', line 112

def squiggly_heredoc?
  heredoc? && @dedent_body
end

#start_interp_braceObject



187
188
189
# File 'lib/parser/lexer/literal.rb', line 187

def start_interp_brace
  @interp_braces += 1
end

#supports_line_continuation_via_slash?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/parser/lexer/literal.rb', line 232

def supports_line_continuation_via_slash?
  !words? && @interpolate
end

#typeObject



120
121
122
# File 'lib/parser/lexer/literal.rb', line 120

def type
  @start_tok
end

#words?Boolean

Returns:

  • (Boolean)


95
96
97
98
# File 'lib/parser/lexer/literal.rb', line 95

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