1 module dini.utils; 2 3 import std.format : format, formatElement, FormatSpec, FormatException, formattedRead; 4 import std.traits : arity, isCallable, Parameters, ReturnType; 5 6 7 enum bool isBoxer(alias boxer) = isCallable!boxer 8 && arity!boxer == 1 9 && is(Parameters!boxer[0] == string); 10 11 alias BoxerType(alias boxer) = ReturnType!boxer; 12 13 14 static char[char] escapeSequences; 15 static this() { 16 escapeSequences = [ 17 'n': '\n', 'r': '\r', 't': '\t', 'b': '\b', '\\': '\\', 18 '#': '#', ';': ';', '=': '=', ':': ':', '"': '"', '\'': '\'' 19 ]; 20 } 21 22 string parseEscapeSequences(string input) 23 { 24 bool inEscape; 25 const(char)[] result = []; 26 result.reserve(input.length); 27 28 for(auto i = 0; i < input.length; i++) { 29 char c = input[i]; 30 31 if (inEscape) { 32 if (c in escapeSequences) 33 result ~= escapeSequences[c]; 34 else if (c == 'x') { 35 ubyte n; 36 if (i + 3 > input.length) 37 throw new FormatException("Invalid escape sequence (\\x)"); 38 string s = input[i+1..i+3]; 39 if (formattedRead(s, "%x", &n) < 1) 40 throw new FormatException("Invalid escape sequence (\\x)"); 41 result ~= cast(char)n; 42 i += 2; 43 } 44 else { 45 throw new FormatException("Invalid escape sequence (\\%s..)".format(c)); 46 } 47 } 48 else if (!inEscape && c == '\\') { 49 inEscape = true; 50 continue; 51 } 52 else result ~= c; 53 54 inEscape = false; 55 } 56 57 return cast(string)result; 58 } 59 60 unittest { 61 assert(parseEscapeSequences("abc wef ' n r ;a") == "abc wef ' n r ;a"); 62 assert(parseEscapeSequences(`\\n \\\\\\\\\\r`) == `\n \\\\\r`); 63 assert(parseEscapeSequences(`hello\nworld`) == "hello\nworld"); 64 assert(parseEscapeSequences(`multi\r\nline \#notacomment`) == "multi\r\nline #notacomment"); 65 assert(parseEscapeSequences(`welp \x5A\x41\x7a`) == "welp ZAz"); 66 }