Function unescapeString

Unescapes a D string, effectively being the same as mixing in the string into some function call, but only for single string literals.

string unescapeString(InvalidEscapeAction invalidEscapeAction = InvalidEscapeAction.error) (
  string input
);

Strips quotes, prefixes and suffixes, interprets escape sequences in normal double quoted strings and interprets hex strings. Returns simple slices for non-escaped strings.

It's undefined how invalid/malformed strings are evaluated.

Bugs

doesn't check for validity of token strings, doesn't interpret named character entity escape sequences, (HTML-kind escape sequences) doesn't check nesting level of delimited strings.

Standards

https://dlang.org/spec/lex.html#string_literals

Example

assert(unescapeString(q{r"I am Oz"}) == r"I am Oz");
assert(unescapeString(q{r"c:\games\Sudoku.exe"}) == r"c:\games\Sudoku.exe");
assert(unescapeString(q{r"ab\n"}) == r"ab\n");

assert(unescapeString(q{`the Great and Powerful.`}) == `the Great and Powerful.`);
assert(unescapeString(q{`c:\games\Empire.exe`}) == `c:\games\Empire.exe`);
assert(unescapeString(q{`The "lazy" dog`}) == `The "lazy" dog`);
assert(unescapeString(q{`a"b\n`}) == `a"b\n`);

assert(unescapeString(q{"Who are you?"}) == "Who are you?");
assert(unescapeString(q{"c:\\games\\Doom.exe"}) == "c:\\games\\Doom.exe");
assert(unescapeString(q{"ab\n"}) == "ab\n");

assert(unescapeString(`x"0A"`) == hexString!"0A");
assert(unescapeString(`x"00 FBCD 32FD 0A"`) == hexString!"00 FBCD 32FD 0A");

assert(unescapeString(`q"(foo(xxx))"`) == q"(foo(xxx))");
assert(unescapeString(`q"[foo{]"`) == q"[foo{]");
assert(unescapeString(`q"<foo{>"`) == q"<foo{>");
assert(unescapeString(`q"{foo(}"`) == q"{foo(}");
assert(unescapeString(`q"EOS
This
is a multi-line
heredoc string
EOS"`) == q"EOS
This
is a multi-line
heredoc string
EOS");
assert(unescapeString(`q"/foo]/"`) == `foo]`);

assert(unescapeString(`q{this is the voice of}`) == q{this is the voice of});
assert(unescapeString(`q{/*}*/ }`) == q{/*}*/ });
assert(unescapeString(`q{ world(q{control}); }`) == q{ world(q{control}); });
assert(unescapeString(`q{ __TIME__ }`) == q{ __TIME__ });

assert(unescapeString(q{"hello"c}) == "hello");
assert(unescapeString(q{"hello"w}) == "hello");
assert(unescapeString(q{"hello"d}) == "hello");

assert(unescapeString(`""`) == "");
assert(unescapeString(`"hello\'world\"cool\""`) == "hello\'world\"cool\"");
assert(unescapeString(`"\x0A"`) == "\x0A");
assert(unescapeString(`"\u200b"`) == "\u200b");
assert(unescapeString(`"\U0001F4A9"`) == "\U0001F4A9");
assert(unescapeString(`"\0"`) == "\0");
assert(unescapeString(`"\1"`) == "\1");
assert(unescapeString(`"\12"`) == "\12");
assert(unescapeString(`"\127"`) == "\127");
assert(unescapeString(`"\1278"`) == "\1278");
assert(unescapeString(`"\12a8"`) == "\12a8");
assert(unescapeString(`"\1a28"`) == "\1a28");
assert(unescapeString(`x"afDE"`) == "\xaf\xDE");
assert(unescapeString("\"hello\nworld\rfoo\r\nbar\u2028ok\u2029\"")
		== "hello\nworld\nfoo\nbar\nok\n");

Example

string testNoChange = "hello\nworld!";
assert(normalizeNewLines(testNoChange).ptr is testNoChange.ptr);

assert(normalizeNewLines("hello\rworld") == "hello\nworld");
assert(normalizeNewLines("hello\r\nworld") == "hello\nworld");
assert(normalizeNewLines("hello\r\n\nworld") == "hello\n\nworld");
assert(normalizeNewLines("hello\u2028\nworld") == "hello\n\nworld");
assert(normalizeNewLines("hello\u2029\nworld") == "hello\n\nworld");
assert(normalizeNewLines("hello\r") == "hello\n");