Class: Parser::Lexer::Literal

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

Constant Summary collapse

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



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

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)



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

def dedent_level
  @dedent_level
end

#heredoc_eObject (readonly)



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

def heredoc_e
  @heredoc_e
end

#saved_herebody_sObject



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

def saved_herebody_s
  @saved_herebody_s
end

#str_sObject (readonly)



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

def str_s
  @str_s
end

Instance Method Details

#backslash_delimited?Boolean

Returns:

  • (Boolean)


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

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

#end_interp_brace_and_try_closingObject



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

def end_interp_brace_and_try_closing
  @interp_braces -= 1

  (@interp_braces == 0)
end

#extend_contentObject



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

def extend_content
  @space_emitted = false
end

#extend_space(ts, te) ⇒ Object



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

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



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

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

  @buffer << string
end

#flush_stringObject



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

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)


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

def heredoc?
  !!@heredoc_e
end

#infer_indent_level(line) ⇒ Object



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

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)


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

def interpolate?
  @interpolate
end

#munge_escape?(character) ⇒ Boolean

Returns:

  • (Boolean)


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

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



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

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)


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

def plain_heredoc?
  heredoc? && !@dedent_body
end

#regexp?Boolean

Returns:

  • (Boolean)


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

def regexp?
  type == :tREGEXP_BEG
end

#squiggly_heredoc?Boolean

Returns:

  • (Boolean)


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

def squiggly_heredoc?
  heredoc? && @dedent_body
end

#start_interp_braceObject



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

def start_interp_brace
  @interp_braces += 1
end

#supports_line_continuation_via_slash?Boolean

Returns:

  • (Boolean)


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

def supports_line_continuation_via_slash?
  !words? && @interpolate
end

#typeObject



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

def type
  @start_tok
end

#words?Boolean

Returns:

  • (Boolean)


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

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