handle C-style escapes in Format
I was happily able to repurpose some code from Git.Filename to handle this. I remember writing that code... a whole afternoon at a coffee shop, after which I felt I'd struggled with Haskell and git, and sorta lost, in needing to write this nasty peice of code. But was also pleased at the use of a pair of functions and quickcheck that allowed me to get it 100% right. So, turns out I not only got it right, but the code wasn't as special-purpose as I'd feared. Yay!
This commit is contained in:
parent
a0872a8ec3
commit
cba3ce08df
3 changed files with 90 additions and 68 deletions
|
@ -8,10 +8,7 @@
|
|||
|
||||
module Git.Filename where
|
||||
|
||||
import qualified Codec.Binary.UTF8.String
|
||||
import Data.Char
|
||||
import Data.Word (Word8)
|
||||
import Text.Printf
|
||||
import Utility.Format (decode_c, encode_c)
|
||||
|
||||
import Common
|
||||
|
||||
|
@ -19,64 +16,12 @@ decode :: String -> FilePath
|
|||
decode [] = []
|
||||
decode f@(c:s)
|
||||
-- encoded strings will be inside double quotes
|
||||
| c == '"' && end s == ['"'] = unescape ("", beginning s)
|
||||
| c == '"' && end s == ['"'] = decode_c $ beginning s
|
||||
| otherwise = f
|
||||
where
|
||||
e = '\\'
|
||||
unescape (b, []) = b
|
||||
-- look for escapes starting with '\'
|
||||
unescape (b, v) = b ++ fst pair ++ unescape (handle $ snd pair)
|
||||
where
|
||||
pair = span (/= e) v
|
||||
isescape x = x == e
|
||||
-- \NNN is an octal encoded character
|
||||
handle (x:n1:n2:n3:rest)
|
||||
| isescape x && alloctal = (fromoctal, rest)
|
||||
where
|
||||
alloctal = isOctDigit n1 &&
|
||||
isOctDigit n2 &&
|
||||
isOctDigit n3
|
||||
fromoctal = [chr $ readoctal [n1, n2, n3]]
|
||||
readoctal o = Prelude.read $ "0o" ++ o :: Int
|
||||
-- \C is used for a few special characters
|
||||
handle (x:nc:rest)
|
||||
| isescape x = ([echar nc], rest)
|
||||
where
|
||||
echar 'a' = '\a'
|
||||
echar 'b' = '\b'
|
||||
echar 'f' = '\f'
|
||||
echar 'n' = '\n'
|
||||
echar 'r' = '\r'
|
||||
echar 't' = '\t'
|
||||
echar 'v' = '\v'
|
||||
echar a = a
|
||||
handle n = ("", n)
|
||||
|
||||
{- Should not need to use this, except for testing decode. -}
|
||||
encode :: FilePath -> String
|
||||
encode s = foldl (++) "\"" (map echar s) ++ "\""
|
||||
where
|
||||
e c = '\\' : [c]
|
||||
echar '\a' = e 'a'
|
||||
echar '\b' = e 'b'
|
||||
echar '\f' = e 'f'
|
||||
echar '\n' = e 'n'
|
||||
echar '\r' = e 'r'
|
||||
echar '\t' = e 't'
|
||||
echar '\v' = e 'v'
|
||||
echar '\\' = e '\\'
|
||||
echar '"' = e '"'
|
||||
echar x
|
||||
| ord x < 0x20 = e_num x -- low ascii
|
||||
| ord x >= 256 = e_utf x
|
||||
| ord x > 0x7E = e_num x -- high ascii
|
||||
| otherwise = [x] -- printable ascii
|
||||
where
|
||||
showoctal i = '\\' : printf "%03o" i
|
||||
e_num c = showoctal $ ord c
|
||||
-- unicode character is decomposed to
|
||||
-- Word8s and each is shown in octal
|
||||
e_utf c = showoctal =<< (Codec.Binary.UTF8.String.encode [c] :: [Word8])
|
||||
encode s = "\"" ++ encode_c s ++ "\""
|
||||
|
||||
{- for quickcheck -}
|
||||
prop_idempotent_deencode :: String -> Bool
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue