HaskLike.hs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Data.SCargot.HaskLike ( -- $info
  3. haskLikeSpec
  4. , HaskLikeAtom(..)
  5. ) where
  6. import Control.Applicative ((<$>), (<*>), (<$))
  7. import Data.Maybe (catMaybes)
  8. import Data.String (IsString(..))
  9. import Data.Text (Text, pack)
  10. import Text.Parsec
  11. import Text.Parsec.Text (Parser)
  12. import Prelude hiding (concatMap)
  13. import Data.SCargot.Repr.Basic (SExpr)
  14. import Data.SCargot.General (SExprSpec, mkSpec)
  15. {- $info
  16. This module is intended for simple, ad-hoc configuration or data formats
  17. that might not need their on rich structure but might benefit from a few
  18. various literal formats. the 'haskLikeSpec' understands identifiers as
  19. defined by R6RS as well as string, integer, and floating-point literals
  20. as defined by the Haskell spec, but won't get any Lisp-specific vector
  21. literals or other structure.
  22. -}
  23. -- | An atom type that understands Haskell-like values as well as
  24. -- Scheme-like identifiers.
  25. data HaskLikeAtom
  26. = HSIdent Text -- ^ An identifier, parsed according to the R6RS Scheme
  27. -- standard
  28. | HSString Text -- ^ A string, parsed according to the syntax for string
  29. -- literals in the Haskell report
  30. | HSInt Integer -- ^ An arbitrary-sized integer value, parsed according to
  31. -- the syntax for integer literals in the Haskell report
  32. | HSFloat Double -- ^ A double-precision floating-point value, parsed
  33. -- according to the syntax for floats in the Haskell
  34. -- report
  35. deriving (Eq, Show)
  36. instance IsString HaskLikeAtom where
  37. fromString = HSIdent . fromString
  38. pToken :: Parser Text
  39. pToken = pack <$> ( (:) <$> initial <*> many subsequent
  40. <|> string "+"
  41. <|> string "-"
  42. <|> string "..."
  43. )
  44. initial :: Parser Char
  45. initial = letter <|> oneOf "!$%&*/:<=>?^_~"
  46. subsequent :: Parser Char
  47. subsequent = initial <|> digit <|> oneOf "+-.@"
  48. pString :: Parser Text
  49. pString = pack . catMaybes <$> between (char '"') (char '"') (many (val <|> esc))
  50. where val = Just <$> satisfy (\ c -> c /= '"' && c /= '\\' && c > '\026')
  51. esc = do char '\\'
  52. Nothing <$ (gap <|> char '&') <|>
  53. Just <$> code
  54. gap = many1 space >> char '\\'
  55. code = eEsc <|> eNum <|> eCtrl <|> eAscii
  56. eCtrl = char '^' >> unCtrl <$> upper
  57. eNum = (toEnum . fromInteger) <$>
  58. (decimal <|> (char 'o' >> number 8 octDigit)
  59. <|> (char 'x' >> number 16 hexDigit))
  60. eEsc = choice [ char a >> return b | (a, b) <- escMap ]
  61. eAscii = choice [ try (string a >> return b)
  62. | (a, b) <- asciiMap ]
  63. unCtrl c = toEnum (fromEnum c - fromEnum 'A' + 1)
  64. escMap :: [(Char, Char)]
  65. escMap = zip "abfntv\\\"\'" "\a\b\f\n\r\t\v\\\"\'"
  66. asciiMap :: [(String, Char)]
  67. asciiMap = zip
  68. ["BS","HT","LF","VT","FF","CR","SO","SI","EM"
  69. ,"FS","GS","RS","US","SP","NUL","SOH","STX","ETX"
  70. ,"EOT","ENQ","ACK","BEL","DLE","DC1","DC2","DC3"
  71. ,"DC4","NAK","SYN","ETB","CAN","SUB","ESC","DEL"]
  72. ("\BS\HT\LF\VT\FF\CR\SO\SI\EM\FS\GS\RS\US\SP\NUL\SOH" ++
  73. "\STX\ETX\EOT\ENQ\ACK\BEL\DLE\DC1\DC2\DC3\DC4\NAK" ++
  74. "\SYN\ETB\CAN\SUB\ESC\DEL")
  75. decimal :: Parser Integer
  76. decimal = number 10 digit
  77. number :: Integer -> Parser Char -> Parser Integer
  78. number base digits = foldl go 0 <$> many1 digits
  79. where go x d = base * x + toInteger (value d)
  80. value c
  81. | c == 'a' || c == 'A' = 0xa
  82. | c == 'b' || c == 'B' = 0xb
  83. | c == 'c' || c == 'C' = 0xc
  84. | c == 'd' || c == 'D' = 0xd
  85. | c == 'e' || c == 'E' = 0xe
  86. | c == 'f' || c == 'F' = 0xf
  87. | c >= '0' && c <= '9' = fromEnum c - fromEnum '0'
  88. | otherwise = error ("Unknown letter in number: " ++ show c)
  89. pFloat :: Parser Double
  90. pFloat = fail "???"
  91. pInt :: Parser Integer
  92. pInt = do
  93. s <- negate <$ char '-' <|> id <$ char '+' <|> return id
  94. n <- pZeroNum <|> decimal
  95. return (s n)
  96. pZeroNum :: Parser Integer
  97. pZeroNum = char '0' >>
  98. ( (oneOf "xX" >> number 16 hexDigit)
  99. <|> (oneOf "oO" >> number 8 octDigit)
  100. <|> decimal
  101. <|> return 0
  102. )
  103. pHaskLikeAtom :: Parser HaskLikeAtom
  104. pHaskLikeAtom =
  105. HSInt <$> (try pInt <?> "integer")
  106. <|> HSFloat <$> (try pFloat <?> "float")
  107. <|> HSString <$> (pString <?> "string literal")
  108. <|> HSIdent <$> (pToken <?> "token")
  109. sHaskLikeAtom :: HaskLikeAtom -> Text
  110. sHaskLikeAtom (HSIdent t) = t
  111. sHaskLikeAtom (HSString s) = pack (show s)
  112. sHaskLikeAtom (HSInt i) = pack (show i)
  113. sHaskLikeAtom (HSFloat f) = pack (show f)
  114. -- | This `SExprSpec` understands s-expressions that contain
  115. -- Scheme-like tokens, as well as string literals, integer
  116. -- literals, and floating-point literals. These are read
  117. -- and shown with Haskell lexical syntax, so the same set
  118. -- of values understood by GHC should be understood by this
  119. -- spec as well. This includes string escapes, different
  120. -- number bases, and so forth.
  121. haskLikeSpec :: SExprSpec HaskLikeAtom (SExpr HaskLikeAtom)
  122. haskLikeSpec = mkSpec pHaskLikeAtom sHaskLikeAtom