1 module nxt.quoting; 2 3 import nxt.array_traits : isCharArray; 4 5 Chars[] quotedWords(Chars)(Chars s, 6 const scope string quoteBeginChar = `"`, 7 const scope string quoteEndChar = `"`) 8 if (isCharArray!Chars) 9 { 10 typeof(return) words; 11 import std.array : array; 12 import nxt.splitter_ex : splitterASCIIAmong; 13 import std.algorithm : filter; 14 import std.string : indexOf, lastIndexOf; 15 import std.range.primitives : empty; 16 while (!s.empty) 17 { 18 auto quoteBeginI = s.indexOf(quoteBeginChar); 19 if (quoteBeginI >= 0) 20 { 21 auto currI = quoteBeginI; 22 23 auto prefixBeginI = s[0 .. currI].lastIndexOf(' '); 24 if (prefixBeginI >= 0) 25 { 26 currI = prefixBeginI + 1; 27 } 28 29 words ~= s[0 .. currI].splitterASCIIAmong!(' ') 30 .filter!(a => !a.empty) 31 .array; 32 33 auto quoteEndI = s[quoteBeginI + 1 .. $].indexOf(quoteEndChar) + quoteBeginI + 1; 34 auto suffixEndI = s[quoteEndI + 1 .. $].indexOf(' '); 35 if (suffixEndI >= 0) 36 { 37 quoteEndI = quoteEndI + suffixEndI; 38 } 39 words ~= s[currI .. quoteEndI + 1]; 40 s = s[quoteEndI + 1 .. $]; 41 } 42 else 43 { 44 words ~= s.splitterASCIIAmong!(' ') 45 .filter!(a => !a.empty) 46 .array; 47 s = []; 48 } 49 } 50 return words; 51 } 52 53 /// 54 @system pure unittest { 55 import std.stdio; 56 import std.algorithm.comparison : equal; 57 const t = `verb:is noun:"New York" a noun:"big city"@en `; 58 const x = t.quotedWords; 59 const xs = [`verb:is`, `noun:"New York"`, `a`, `noun:"big city"@en`]; 60 assert(equal(x, xs)); 61 /+ TODO: assert(equal(` verb:is name:"New York"@en article:a noun:"big city"@en `.quotedWords, +/ 62 // [`verb:is`, `name:"New York"@en`, `article:a`, `noun:"big city"@en`])); 63 }