Skip to content

Commit 28eae8a

Browse files
authored
Merge pull request #3463 from e-kwsm/decodeEscapes
feat(decodeEscapes): add missed ANSI-C quoting
2 parents d54fdfb + e870efe commit 28eae8a

1 file changed

Lines changed: 29 additions & 22 deletions

File tree

src/ShellCheck/ASTLib.hs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import Data.List
3333
import Data.Maybe
3434
import qualified Data.List.NonEmpty as NE
3535
import qualified Data.Map as Map
36-
import Numeric (showHex)
36+
import Numeric (readHex, readOct, showHex)
3737

3838
import Test.QuickCheck
3939

@@ -386,6 +386,16 @@ prop_getLiteralString10 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\1234"
386386
prop_getLiteralString11 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\1") == Just "\1"
387387
prop_getLiteralString12 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\12") == Just "\o12"
388388
prop_getLiteralString13 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\123") == Just "\o123"
389+
prop_getLiteralString14 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\e[1mfoo\\E[0mbar") == Just "\ESC[1mfoo\ESC[0mbar"
390+
prop_getLiteralString15 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\?") == Just "?"
391+
prop_getLiteralString16 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\u9") == Just "\t"
392+
prop_getLiteralString17 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\u2F") == Just "/"
393+
prop_getLiteralString18 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\u100") == Just "Ā"
394+
prop_getLiteralString19 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\u1d00") == Just ""
395+
prop_getLiteralString20 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\u1D56C") == Just "ᵖC"
396+
prop_getLiteralString21 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\U9z") == Just "\tz"
397+
prop_getLiteralString22 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\u1d00.") == Just "ᴀ."
398+
prop_getLiteralString23 = getLiteralString (T_DollarSingleQuoted (Id 0) "\\U1D56C") == Just "𝕬"
389399

390400
-- Maybe get the literal value of a token, using a custom function
391401
-- to map unrecognized Tokens into strings.
@@ -409,37 +419,34 @@ getLiteralStringExt more = g
409419
'a' -> '\a' : rest
410420
'b' -> '\b' : rest
411421
'e' -> '\x1B' : rest
422+
'E' -> '\x1B' : rest
412423
'f' -> '\f' : rest
413424
'n' -> '\n' : rest
414425
'r' -> '\r' : rest
415426
't' -> '\t' : rest
416427
'v' -> '\v' : rest
428+
'\\' -> '\\' : rest
417429
'\'' -> '\'' : rest
418430
'"' -> '"' : rest
419-
'\\' -> '\\' : rest
431+
'?' -> '?' : rest
420432
'x' ->
421-
case cs of
422-
(x:y:more) | isHexDigit x && isHexDigit y ->
423-
chr (16*(digitToInt x) + (digitToInt y)) : decodeEscapes more
424-
(x:more) | isHexDigit x ->
425-
chr (digitToInt x) : decodeEscapes more
426-
more -> '\\' : 'x' : decodeEscapes more
427-
_ | isOctDigit c ->
428-
let (digits, more) = spanMax isOctDigit 3 (c:cs)
429-
num = (parseOct digits) `mod` 256
430-
in (chr num) : decodeEscapes more
431-
_ -> '\\' : c : rest
433+
case readHex (take 2 cs) of
434+
[(n, s)] -> chr n : s ++ decodeEscapes (drop 2 cs)
435+
_ -> '\\' : 'x' : rest
436+
'u' ->
437+
case readHex (take 4 cs) of
438+
[(n, s)] -> chr n : s ++ decodeEscapes (drop 4 cs)
439+
_ -> '\\' : 'x' : rest
440+
'U' ->
441+
case readHex (take 8 cs) of
442+
[(n, s)] -> chr n : s ++ decodeEscapes (drop 8 cs)
443+
_ -> '\\' : 'x' : rest
444+
_ ->
445+
case readOct (c:take 2 cs) of
446+
[(n, s)] -> chr (n `mod` 256) : s ++ decodeEscapes (drop 2 cs)
447+
_ -> '\\' : c : rest
432448
where
433449
rest = decodeEscapes cs
434-
parseOct = f 0
435-
where
436-
f n "" = n
437-
f n (c:rest) = f (n * 8 + digitToInt c) rest
438-
spanMax f n list =
439-
let (first, second) = span f list
440-
(prefix, suffix) = splitAt n first
441-
in
442-
(prefix, suffix ++ second)
443450
decodeEscapes (c:cs) = c : decodeEscapes cs
444451
decodeEscapes [] = []
445452

0 commit comments

Comments
 (0)