I am adapting the following existing python parser:
def parse_hexchar(c): if (c >= '0') and (c <= '9'): return ord(c) - ord('0') elif (c >= 'a') and (c <= 'f'): return ord(c) - ord('a') + 10 elif (c >= 'A') and (c <= 'F'): return ord(c) - ord('A') + 10 else: return -1 def unescape_string (buf): dst = "" src = 0 l = len(buf) while src < l: c = buf[src] if c == '\\' and ((src+1) < l): c = buf[src+1] if c == 'n': dst += '\n' src += 2 continue elif c == 't': dst += '\t' src += 2 continue elif c == 'r': dst += '\r' src += 2 continue elif c == '"': dst += '"' src += 2 continue elif c == '\n': src += 2 # skip until next non whitespace character while src < l: c = buf[src] if c in ' \t': src += 1 else: break continue elif c == 'x' and ((src+3) < l): c0 = parse_hexchar(buf[src+2]) c1 = parse_hexchar(buf[src+3]) if (c0 >= 0) and (c1 >= 0): dst += chr((c0 << 4) | c1) src += 3 continue dst += c src += 1 return dst
(its input has already been extracted at an earlier stage that guarantees it doesn't have any unescaped quotes etc, so it is doing less validation than you may naively expect.)
My current translation to APL runs as follows
I←{⍺[⍵]} ⋄ sub←{⍵⍵(⍺⍺I⍳)@(∊∘⍵⍵)⍵}
ht nl cr←⎕ucs 9 10 13 ⋄ wesc←(ht nl cr)sub'tnr'
segment←{⍵⊆⍨(⌈\(⍳⍴⍵)+(2×1⌽'x'=⍵)(+×⊢)>/∘⌽¨,\'\'=⍵)}
splice←{⍵/⍨⊃>⍥(⌈\⍳∘⍴×⊢)/((~⍵∊,¨' ',ht) (⍵∊⊂'\',nl))}
hex←{16⊥¯1+(16↑⎕d,⎕c⎕a)⍳⎕c⍵}
unescape←{∊⎕ucs∘(hex 2∘↓)¨@(4=≢¨)(¯1↑wesc)¨@(2=≢¨)splice segment ⍵}
Test case:
unescape '\n,normal escapes: \(\)\"\''\\\\ character escapes: \n,\t,\x22stuff\x22\r,escaped line-\',nl,' breaks'
,normal escapes: ()"'\\ character escapes:
, ,"stuff"
,escaped line-break
which works well enough, but feels unwieldy, especially splice
.
Suggestions? Comments? Concerns?
name←{function code}
is akin to a pythondef name(args): ...
It shows, in JSON terms, the string"\\n,normal escapes: \\(\\)\\\"\\'\\\\\\\\ character escapes: \\n,\\t,\\x22stuff\\x22\\r,escaped line-\\\n breaks"
being transformed to"\n,normal escapes: ()\"'\\\\ character escapes: \n,\t,\"stuff\"\r,escaped line-breaks"
The python is provided as reference for what the second, APL, code block should do. \$\endgroup\$