private static StringSlice ParseString(ref StringSlice text) { ConsumeWhitespace(ref text); if (text.Length == 0) { throw new QueryParseException("string", text); } char start = text[0]; if (start == '\'' || start == '"') { char terminator = start; // Keep a StringBuilder for unescaping (only if needed) and track what we've copied to it already StringBuilder unescapedForm = null; int nextCopyFrom = 1; int terminatorIndex = 1; for (; terminatorIndex < text.Length - 1; ++terminatorIndex) { if (text[terminatorIndex] == terminator) { // If this wasn't a doubled quote, the string is done if (text[terminatorIndex + 1] != terminator) { break; } // Copy to the StringBuilder without either quote if (unescapedForm == null) { unescapedForm = new StringBuilder(); } text.Substring(nextCopyFrom, terminatorIndex - nextCopyFrom).AppendTo(unescapedForm); // Include the second quote next time nextCopyFrom = terminatorIndex + 1; // Skip to look after the second quote next iteration terminatorIndex += 1; } } // Ensure the string is terminated if (terminatorIndex == text.Length || text[terminatorIndex] != terminator) { throw new QueryParseException("string terminator", text); } if (unescapedForm != null) { if (nextCopyFrom < terminatorIndex) { text.Substring(nextCopyFrom, terminatorIndex - nextCopyFrom).AppendTo(unescapedForm); } text = text.Substring(terminatorIndex + 1); return(unescapedForm.ToString()); } else { StringSlice resultSlice = text.Substring(1, terminatorIndex - 1); text = text.Substring(terminatorIndex + 1); return(resultSlice); } } else { // Read until whitespace or ')' (so closing a subexpression doesn't require a space) int terminatorIndex = 0; for (; terminatorIndex < text.Length; ++terminatorIndex) { char c = text[terminatorIndex]; if (c == ')' || IsWhitespace(c)) { break; } } // Consume and return the string StringSlice result = text.Substring(0, terminatorIndex); text = text.Substring(terminatorIndex); return(result); } }
public Literal(StringSlice text, T value) { Text = text; Value = value; }
internal QueryParseException(string categoryExpected, StringSlice text) : this($"Expected {categoryExpected} but found {(text.Length == 0 ? "<End>" : text)}") { }