HaskLike.hs 6.0 KB

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