HaskLike.hs 5.8 KB

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