Optimal string literal tokenizing algorithm
A rule of thumb for performance in C# is to avoid allocations. In this case, we should try to remove buffer.ToString ().TrimEnd ('\\'), as well as the calls to Substring and Replace that appear at the end.

Of course you need to set performance goals and profile the code yourself on realistic input.  Single line strings can reasonably be expected to be quite short (say 200 chars?), so what you have already might be performant enough, but I'll illustrate an alternative way.

First let's make this a static method so it will be easier to unit test. As I mentioned in the comments, there's a bug in the code -- unit testing can help you catch such bugs.

private static string ReadSingleLineStringLiteral(TextReader reader)


And a convenience method for testing

private static string ReadSingleLineStringLiteral(string line)
{
    using (var reader = new StringReader(line))
    {
        return ReadSingleLineStringLiteral(reader);
    }
}


Now let's just handle the simple case, where there are no escape characters. Let's write some unit tests

Assert.AreEqual("", ReadSingleLineStringLiteral("\""));
Assert.AreEqual("jo dude", ReadSingleLineStringLiteral("jo dude\""));
Assert.Throws&lt;ArgumentException&gt;(() =&gt; ReadSingleLineStringLiteral(""));


And write the simplest thing that gets them to pass

var sb = new StringBuilder();
for (var c = reader.Read(); c != -1; c = reader.Read())
{
    switch (c)
    {
    case '"':
        return sb.ToString();
    default:
        sb.Append((char)c);
        break;
    }
}

throw new ArgumentException("Unexpected end of input");


Single line strings shouldn't contain new lines, so let's add a new test case

Assert.Throws&lt;ArgumentException&gt;(() =&gt; ReadSingleLineStringLiteral("\n\""));


And a matching case to our method

    switch (c)
    {
    case '"':
        return sb.ToString();
    case '\n':
        throw new ArgumentException("Single line string contains new line");
    default:
        sb.Append((char)c);
        break;
    }


Now let's move on to escape sequences.

Assert.AreEqual("jo \"dude\"", ReadSingleLineStringLiteral("jo \\\"dude\\\"\""));
Assert.AreEqual("this is a backslash \\ token", ReadSingleLineStringLiteral("this is a backslash \\\\ token\""));
Assert.AreEqual("this is a carriage return \r\nthis is next line", ReadSingleLineStringLiteral("this is a carriage return \\r\\nthis is next line\""));


We add another case

    switch (c)
    {
    case '"':
        return sb.ToString();
    case '\\':
        AppendEscapeCharacter(reader, sb);
        break;
    case '\n':
        throw new ArgumentException("Single line string contains new line");
    default:
        sb.Append((char)c);
        break;
    }


And the corresponding method

private static void AppendEscapeCharacter(TextReader reader, StringBuilder sb)
{
    var c = reader.Read(); 
    switch (c)
    {
    case -1:
        throw new ArgumentException("Unexpected end of input");
    case '"':
    case '\'':
    case '\\':
        sb.Append((char)c);
        break;
    case 'n':
        sb.Append('\n');
        break;
    case 'r':
        sb.Append('\r');
        break;
    default:
        throw new ArgumentException(string.Format("Invalid escape sequence '\\{0}'", (char)c));
    }
}


There are more cases to handle, but this should be enough to get you started.