HaskLike.hs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Data.SCargot.Language.HaskLike
  3. ( -- $info
  4. HaskLikeAtom(..)
  5. , haskLikeParser
  6. , haskLikePrinter
  7. ) where
  8. import Control.Applicative ((<$>), (<*>), (<$))
  9. import Data.Maybe (catMaybes)
  10. import Data.String (IsString(..))
  11. import Data.Text (Text, pack)
  12. import Text.Parsec
  13. import Text.Parsec.Text (Parser)
  14. import Prelude hiding (concatMap)
  15. import Data.SCargot.Common
  16. import Data.SCargot.Repr.Basic (SExpr)
  17. import Data.SCargot (SExprParser, SExprPrinter, mkParser, flatPrint)
  18. {- $info
  19. This module is intended for simple, ad-hoc configuration or data formats
  20. that might not need their on rich structure but might benefit from a few
  21. various kinds of literals. The 'haskLikeParser' understands identifiers as
  22. defined by R5RS, as well as string, integer, and floating-point literals
  23. as defined by the Haskell spec. It does __not__ natively understand other
  24. data types, such as booleans, vectors, bitstrings.
  25. -}
  26. -- | An atom type that understands Haskell-like values as well as
  27. -- Scheme-like identifiers.
  28. data HaskLikeAtom
  29. = HSIdent Text -- ^ An identifier, parsed according to the R5RS Scheme
  30. -- standard
  31. | HSString Text -- ^ A string, parsed according to the syntax for string
  32. -- literals in the Haskell report
  33. | HSInt Integer -- ^ An arbitrary-sized integer value, parsed according to
  34. -- the syntax for integer literals in the Haskell report
  35. | HSFloat Double -- ^ A double-precision floating-point value, parsed
  36. -- according to the syntax for floats in the Haskell
  37. -- report
  38. deriving (Eq, Show)
  39. instance IsString HaskLikeAtom where
  40. fromString = HSIdent . fromString
  41. pString :: Parser Text
  42. pString = pack . catMaybes <$> between (char '"') (char '"') (many (val <|> esc))
  43. where val = Just <$> satisfy (\ c -> c /= '"' && c /= '\\' && c > '\026')
  44. esc = do char '\\'
  45. Nothing <$ (gap <|> char '&') <|>
  46. Just <$> code
  47. gap = many1 space >> char '\\'
  48. code = eEsc <|> eNum <|> eCtrl <|> eAscii
  49. eCtrl = char '^' >> unCtrl <$> upper
  50. eNum = (toEnum . fromInteger) <$>
  51. (decNumber <|> (char 'o' >> octNumber)
  52. <|> (char 'x' >> hexNumber))
  53. eEsc = choice [ char a >> return b | (a, b) <- escMap ]
  54. eAscii = choice [ try (string a >> return b)
  55. | (a, b) <- asciiMap ]
  56. unCtrl c = toEnum (fromEnum c - fromEnum 'A' + 1)
  57. escMap :: [(Char, Char)]
  58. escMap = zip "abfntv\\\"\'" "\a\b\f\n\r\t\v\\\"\'"
  59. asciiMap :: [(String, Char)]
  60. asciiMap = zip
  61. ["BS","HT","LF","VT","FF","CR","SO","SI","EM"
  62. ,"FS","GS","RS","US","SP","NUL","SOH","STX","ETX"
  63. ,"EOT","ENQ","ACK","BEL","DLE","DC1","DC2","DC3"
  64. ,"DC4","NAK","SYN","ETB","CAN","SUB","ESC","DEL"]
  65. ("\BS\HT\LF\VT\FF\CR\SO\SI\EM\FS\GS\RS\US\SP\NUL\SOH" ++
  66. "\STX\ETX\EOT\ENQ\ACK\BEL\DLE\DC1\DC2\DC3\DC4\NAK" ++
  67. "\SYN\ETB\CAN\SUB\ESC\DEL")
  68. pFloat :: Parser Double
  69. pFloat = do
  70. n <- decNumber
  71. withDot n <|> noDot n
  72. where withDot n = do
  73. char '.'
  74. m <- decNumber
  75. e <- option 1.0 exponent
  76. return ((fromIntegral n + asDec m 0) * e)
  77. noDot n = do
  78. e <- exponent
  79. return (fromIntegral n * e)
  80. exponent = do
  81. oneOf "eE"
  82. s <- power
  83. x <- decNumber
  84. return (10 ** s (fromIntegral x))
  85. asDec 0 k = k
  86. asDec n k =
  87. asDec (n `div` 10) ((fromIntegral (n `rem` 10) + k) * 0.1)
  88. power :: Num a => Parser (a -> a)
  89. power = negate <$ char '-' <|> id <$ char '+' <|> return id
  90. pInt :: Parser Integer
  91. pInt = do
  92. s <- power
  93. n <- pZeroNum <|> decNumber
  94. return (fromIntegral (s n))
  95. pZeroNum :: Parser Integer
  96. pZeroNum = char '0' >>
  97. ( (oneOf "xX" >> hexNumber)
  98. <|> (oneOf "oO" >> octNumber)
  99. <|> decNumber
  100. <|> return 0
  101. )
  102. pHaskLikeAtom :: Parser HaskLikeAtom
  103. pHaskLikeAtom
  104. = HSFloat <$> (try pFloat <?> "float")
  105. <|> HSInt <$> (try pInt <?> "integer")
  106. <|> HSString <$> (pString <?> "string literal")
  107. <|> HSIdent <$> (parseR5RSIdent <?> "token")
  108. sHaskLikeAtom :: HaskLikeAtom -> Text
  109. sHaskLikeAtom (HSIdent t) = t
  110. sHaskLikeAtom (HSString s) = pack (show s)
  111. sHaskLikeAtom (HSInt i) = pack (show i)
  112. sHaskLikeAtom (HSFloat f) = pack (show f)
  113. -- | This `SExprParser` understands s-expressions that contain
  114. -- Scheme-like tokens, as well as string literals, integer
  115. -- literals, and floating-point literals. Each of these values
  116. -- is parsed according to the lexical rules in the Haskell
  117. -- report, so the same set of string escapes, numeric bases,
  118. -- and floating-point options are available. This spec does
  119. -- not parse comments and does not understand any reader
  120. -- macros.
  121. --
  122. -- >>> decode haskLikeParser "(0x01 \"\\x65lephant\")"
  123. -- Right [SCons (SAtom (HSInt 1)) (SCons (SAtom (HSString "elephant")) SNil)]
  124. haskLikeParser :: SExprParser HaskLikeAtom (SExpr HaskLikeAtom)
  125. haskLikeParser = mkParser pHaskLikeAtom
  126. -- | This 'SExprPrinter' emits s-expressions that contain Scheme-like
  127. -- tokens as well as string literals, integer literals, and floating-point
  128. -- literals, which will be emitted as the literals produced by Haskell's
  129. -- 'show' function. This printer will produce a flat s-expression with
  130. -- no indentation of any kind.
  131. --
  132. -- >>> encode haskLikePrinter [L [A (HSInt 1), A (HSString "elephant")]]
  133. -- "(1 \"elephant\")"
  134. haskLikePrinter :: SExprPrinter HaskLikeAtom (SExpr HaskLikeAtom)
  135. haskLikePrinter = flatPrint sHaskLikeAtom