class Parser::Lexer::Literal
def clear_buffer
def clear_buffer @buffer = '' # Prime the buffer with lexer encoding; otherwise, # concatenation will produce varying results. if defined?(Encoding) @buffer.force_encoding(@lexer.encoding) end @buffer_s = nil @buffer_e = nil end
def coerce_encoding(string)
def coerce_encoding(string) if defined?(Encoding) string.dup.force_encoding(Encoding::BINARY) else string end end
def delimiter?(delimiter)
def delimiter?(delimiter) if @indent @end_delim == delimiter.lstrip else @end_delim == delimiter end end
def emit(token, type, s, e)
def emit(token, type, s, e) @lexer.send(:emit, token, type, s, e) end
def emit_start_tok
def emit_start_tok str_e = @heredoc_e || @str_s + @str_type.length emit(@start_tok, @str_type, @str_s, str_e) end
def end_interp_brace_and_try_closing
def end_interp_brace_and_try_closing @interp_braces -= 1 (@interp_braces == 0) end
def extend_content
def extend_content @space_emitted = false end
def extend_space(ts, te)
def extend_space(ts, te) flush_string unless @space_emitted emit(:tSPACE, nil, ts, te) @space_emitted = true end end
def extend_string(string, ts, te)
def extend_string(string, ts, te) if @buffer_s.nil? @buffer_s = ts end @buffer_e = te if defined?(Encoding) @buffer << string.encode(@lexer.encoding) else @buffer << string end end
def flush_string
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
def heredoc?
def heredoc? !!@heredoc_e end
def initialize(lexer, str_type, delimiter, str_s, heredoc_e = nil, indent = false)
def initialize(lexer, str_type, delimiter, str_s, heredoc_e = nil, indent = 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) message = ERRORS[:unexpected_percent_str] % { :type => str_type } lexer.send(:diagnostic, :error, message, @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 @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. unless @heredoc_e || @str_type.end_with?(delimiter) @str_type << delimiter end clear_buffer emit_start_tok unless @monolithic end
def interpolate?
def interpolate? @interpolate end
def munge_escape?(character)
def munge_escape?(character) character = coerce_encoding(character) if words? && character =~ /[ \t\v\r\f\n]/ true else ['\\', @start_delim, @end_delim].include?(character) end end
def nest_and_try_closing(delimiter, ts, te)
def nest_and_try_closing(delimiter, ts, te) 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 # Emit the string as a single token if it's applicable. if @monolithic emit(MONOLITHIC[@start_tok], @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
def regexp?
def regexp? type == :tREGEXP_BEG end
def start_interp_brace
def start_interp_brace @interp_braces += 1 end
def type
def type @start_tok end
def words?
def words? type == :tWORDS_BEG || type == :tQWORDS_BEG || type == :tSYMBOLS_BEG || type == :tQSYMBOLS_BEG end