1 
2 
3 //          Copyright Ferdinand Majerech 2011.
4 // Distributed under the Boost Software License, Version 1.0.
5 //    (See accompanying file LICENSE_1_0.txt or copy at
6 //          http://www.boost.org/LICENSE_1_0.txt)
7 
8 module dyaml.escapes;
9 
10 package:
11 
12 import std.meta : AliasSeq;
13 alias escapes = AliasSeq!('0', 'a', 'b', 't', '\t', 'n', 'v', 'f', 'r', 'e', ' ',
14                           '/', '\"', '\\', 'N', '_', 'L', 'P');
15 
16 /// YAML hex codes specifying the length of the hex number.
17 alias escapeHexCodeList = AliasSeq!('x', 'u', 'U');
18 
19 /// Convert a YAML escape to a dchar.
20 dchar fromEscape(dchar escape) @safe pure nothrow @nogc
21 {
22     switch(escape)
23     {
24         case '0':  return '\0';
25         case 'a':  return '\x07';
26         case 'b':  return '\x08';
27         case 't':  return '\x09';
28         case '\t': return '\x09';
29         case 'n':  return '\x0A';
30         case 'v':  return '\x0B';
31         case 'f':  return '\x0C';
32         case 'r':  return '\x0D';
33         case 'e':  return '\x1B';
34         case '/':  return '/';
35         case ' ':  return '\x20';
36         case '\"': return '\"';
37         case '\\': return '\\';
38         case 'N':  return '\x85'; //'\u0085';
39         case '_':  return '\xA0';
40         case 'L':  return '\u2028';
41         case 'P':  return '\u2029';
42         default:   assert(false, "No such YAML escape");
43     }
44 }
45 
46 /**
47  * Convert a dchar to a YAML escape.
48  *
49  * Params:
50  *      value = The possibly escapable character.
51  *
52  * Returns:
53  *      If the character passed as parameter can be escaped, returns the matching
54  *      escape, otherwise returns a null character.
55  */
56 dchar toEscape(dchar value) @safe pure nothrow @nogc
57 {
58     switch(value)
59     {
60         case '\0':   return '0';
61         case '\x07': return 'a';
62         case '\x08': return 'b';
63         case '\x09': return 't';
64         case '\x0A': return 'n';
65         case '\x0B': return 'v';
66         case '\x0C': return 'f';
67         case '\x0D': return 'r';
68         case '\x1B': return 'e';
69         case '\"':   return '\"';
70         case '\\':   return '\\';
71         case '\xA0': return '_';
72         case '\x85': return 'N';
73         case '\u2028': return 'L';
74         case '\u2029': return 'P';
75         default: return 0;
76     }
77 }
78 
79 /// Get the length of a hexadecimal number determined by its hex code.
80 ///
81 /// Need a function as associative arrays don't work with @nogc.
82 /// (And this may be even faster with a function.)
83 uint escapeHexLength(dchar hexCode) @safe pure nothrow @nogc
84 {
85     switch(hexCode)
86     {
87         case 'x': return 2;
88         case 'u': return 4;
89         case 'U': return 8;
90         default:  assert(false, "No such YAML hex code");
91     }
92 }
93 
94 // Issue #302: Support optional escaping of forward slashes in string
95 // for JSON compatibility
96 @safe unittest
97 {
98     import dyaml.loader : Loader;
99 
100     const str = `{
101     "forward/slashes": "can\/be\/optionally\/escaped"
102 }`;
103 
104     auto node = Loader.fromString(str).load();
105     assert(node["forward/slashes"] == "can/be/optionally/escaped");
106 }