gdritter repos s-cargot / 94563a1
Several changes: new helper functions for decoding from various reprs; new Comments module; new IsString instance for SExpr types... Getty Ritter 9 years ago
9 changed file(s) with 326 addition(s) and 25 deletion(s). Collapse all Expand all
66 , asWellFormed
77 , addReader
88 , setComment
9 , withSemicolonComments
9 , withLispComments
1010 , withQuote
1111 ) where
1212
1515 import Data.Text (Text)
1616
1717 import Data.SCargot.Repr.Basic
18 import Data.SCargot.General hiding (withQuote)
18 import Data.SCargot.General
19 import Data.SCargot.Comments (withLispComments)
1920
2021 isAtomChar :: Char -> Bool
2122 isAtomChar c = isAlphaNum c
3839 -- storage or configuration formats.
3940 basicSpec :: SExprSpec Text (SExpr Text)
4041 basicSpec = mkSpec (takeWhile1 isAtomChar) id
41
42 -- | Add the ability to understand a quoted S-Expression.
43 -- This means that @'sexpr@ becomes sugar for
44 -- @(quote sexpr)@. This is a variation on the identically-named
45 -- function in Data.SCargot.General that has been specialized
46 -- for the Basic atom type.
47 withQuote :: SExprSpec Text a -> SExprSpec Text a
48 withQuote = addReader '\'' (fmap go)
49 where go s = SCons (SAtom "quote") (SCons s SNil)
1 {-# LANGUAGE OverloadedStrings #-}
2
3 module Data.SCargot.Comments
4 ( -- * Comment Syntax
5 -- $intro
6 -- * Lisp Comments
7 withLispComments
8 -- * Other Existing Comment Syntaxes
9 -- ** Scripting Language Syntax
10 -- $script
11 , withOctothorpeComments
12 -- ** C-Like Syntax
13 -- $clike
14 , withCLikeLineComments
15 , withCLikeBlockComments
16 , withCLikeComments
17 -- ** Haskell Syntax
18 -- $haskell
19 , withHaskellLineComments
20 , withHaskellBlockComments
21 , withHaskellComments
22 -- * Comment Syntax Helper Functions
23 , lineComment
24 , simpleBlockComment
25 ) where
26
27 import Control.Applicative ((<|>))
28 import Control.Monad (void)
29 import Data.Attoparsec.Text
30 import Data.Text (Text)
31
32 import Prelude hiding (takeWhile)
33
34 import Data.SCargot.General
35
36 -- | Given a string, produce a comment parser that matches that
37 -- initial string and ignores everything until the end of the
38 -- line.
39 lineComment :: Text -> Comment
40 lineComment s = string s >> takeWhile (/= '\n') >> return ()
41
42 -- | Given two strings, a begin and an end delimeter, produce a
43 -- parser that matches the beginning delimeter and then ignores
44 -- everything until it finds the end delimiter. This does not
45 -- consider nesting, so, for example, a comment created with
46 --
47 -- > curlyComment :: Comment
48 -- > curlyComment = simpleBlockComment "{" "}"
49 --
50 -- will consider
51 --
52 -- > { this { comment }
53 --
54 -- to be a complete comment, despite the improper nesting. This is
55 -- analogous to standard C-style comments in which
56 --
57 -- > /* this /* comment */
58 --
59 -- is a complete comment.
60 simpleBlockComment :: Text -> Text -> Comment
61 simpleBlockComment begin end =
62 string begin >>
63 manyTill anyChar (string end) >>
64 return ()
65
66 -- | Lisp-style line-oriented comments start with @;@ and last
67 -- until the end of the line. This is usually the comment
68 -- syntax you want.
69 withLispComments :: SExprSpec t a -> SExprSpec t a
70 withLispComments = setComment (lineComment ";")
71
72 -- | C++-like line-oriented comment start with @//@ and last
73 -- until the end of the line.
74 withCLikeLineComments :: SExprSpec t a -> SExprSpec t a
75 withCLikeLineComments = setComment (lineComment "//")
76
77 -- | C-like block comments start with @/*@ and end with @*/@.
78 -- They do not nest.
79 withCLikeBlockComments :: SExprSpec t a -> SExprSpec t a
80 withCLikeBlockComments = setComment (simpleBlockComment "/*" "*/")
81
82 -- | C-like comments include both line- and block-comments, the
83 -- former starting with @//@ and the latter contained within
84 -- @//* ... *//@.
85 withCLikeComments :: SExprSpec t a -> SExprSpec t a
86 withCLikeComments = setComment (lineComment "//" <|>
87 simpleBlockComment "/*" "*/")
88
89 -- | Haskell line-oriented comments start with @--@ and last
90 -- until the end of the line.
91 withHaskellLineComments :: SExprSpec t a -> SExprSpec t a
92 withHaskellLineComments = setComment (lineComment "--")
93
94 -- | Haskell block comments start with @{-@ and end with @-}@.
95 -- They do not nest.
96 withHaskellBlockComments :: SExprSpec t a -> SExprSpec t a
97 withHaskellBlockComments = setComment (simpleBlockComment "{-" "-}")
98
99 -- | Haskell comments include both the line-oriented @--@ comments
100 -- and the block-oriented @{- ... -}@ comments
101 withHaskellComments :: SExprSpec t a -> SExprSpec t a
102 withHaskellComments = setComment (lineComment "--" <|>
103 simpleBlockComment "{-" "-}")
104
105 -- | Many scripting and shell languages use these, which begin with
106 -- @#@ and last until the end of the line.
107 withOctothorpeComments :: SExprSpec t a -> SExprSpec t a
108 withOctothorpeComments = setComment (lineComment "#")
109
110
111 {- $intro
112
113 By default a 'SExprSpec' will not understand any kind of comment
114 syntax. Most varieties of s-expression will, however, want some kind
115 of commenting capability, so the below functions will produce a new
116 'SExprSpec' which understands various kinds of comment syntaxes.
117
118 For example:
119
120 > mySpec :: SExprSpec Text (SExpr Text)
121 > mySpec = asWellFormed (mkSpec (takeWhile1 isAlphaNum) id)
122 >
123 > myLispySpec :: SExprSpec Text (SExpr Text)
124 > myLispySpec = withLispComments mySpec
125 >
126 > myCLikeSpec :: SExprSpec Text (SExpr Text)
127 > myCLikeSpec = withCLikeComment mySpec
128
129 We can then use these to parse s-expressions with different kinds of
130 comment syntaxes:
131
132 > decode mySpec "(foo ; a lisp comment\n bar)\n"
133 > Left "Failed reading: takeWhile1"
134 > decode myLispySpec "(foo ; a lisp comment\n bar)\n"
135 > Right [WFSList [WFSAtom "foo", WFSAtom "bar"]]
136 > decode mySpec "(foo /* a c-like\n comment */ bar)\n"
137 > Left "Failed reading: takeWhile1"
138 > decode myCLikeSpec "(foo /* a c-like\n comment */ bar)\n"
139 > Right [WFSList [WFSAtom "foo", WFSAtom "bar"]]
140
141 -}
142
143 {- $script
144 > (one # a comment
145 > two # another one
146 > three)
147 -}
148
149 {- $clike
150 > (one // a comment
151 > two /* another
152 > one */
153 > three)
154 -}
155
156 -- $haskell
157 -- > (one -- a comment
158 -- > two {- another
159 -- > one -}
160 -- > three)
1111 -- * Specific SExprSpec Conversions
1212 , asRich
1313 , asWellFormed
14 , withSemicolonComments
1514 , withQuote
1615 -- * Using a SExprSpec
1716 , decode
3029 import Data.Map.Strict (Map)
3130 import qualified Data.Map.Strict as M
3231 import Data.Monoid ((<>))
32 import Data.String (IsString)
3333 import Data.Text (Text, pack, unpack)
3434 import qualified Data.Text as T
3535
163163 setComment :: Comment -> SExprSpec a c -> SExprSpec a c
164164 setComment c spec = spec { comment = Just c }
165165
166 -- | Add the ability to skip line comments beginning with a semicolon.
167 withSemicolonComments :: SExprSpec a c -> SExprSpec a c
168 withSemicolonComments = setComment (char ';' >> takeWhile (/='\n') >> return ())
169
170166 -- | Add the ability to understand a quoted S-Expression. In general,
171167 -- many Lisps use @'sexpr@ as sugar for @(quote sexpr)@. This is
172168 -- a convenience function which allows you to easily add quoted
173169 -- expressions to a 'SExprSpec', provided that you supply which
174170 -- atom you want substituted in for the symbol @quote@.
175 withQuote :: a -> SExprSpec a (SExpr a) -> SExprSpec a (SExpr a)
176 withQuote q = addReader '\'' (fmap go)
177 where go s = SCons (SAtom q) (SCons s SNil)
171 withQuote :: IsString t => SExprSpec t (SExpr t) -> SExprSpec t (SExpr t)
172 withQuote = addReader '\'' (fmap go)
173 where go s = SCons "quote" (SCons s SNil)
178174
179175 parseGenericSExpr ::
180176 Parser atom -> ReaderMacroMap atom -> Parser () -> Parser (SExpr atom)
77 , pattern (:::)
88 , pattern A
99 , pattern Nil
10 -- * Useful processing functions
11 , fromPair
12 , fromList
1013 ) where
1114
15 import Control.Applicative ((<$>), (<*>), pure)
1216 import Data.SCargot.Repr as R
1317
1418 -- | A shorter infix alias for `SCons`
1923
2024 -- | A (slightly) shorter alias for `SNil`
2125 pattern Nil = SNil
26
27
28 type S t = R.SExpr t
29 type Parse t a = R.SExpr t -> Either String a
30
31 -- | Utility function for parsing a pair of things.
32 fromPair :: Parse t a -> Parse t b -> Parse t (a, b)
33 fromPair pl pr (l ::: r ::: Nil) = (,) <$> pl l <*> pr r
34 fromPair _ _ sx = fail ("Expected two-element list")
35
36 -- | Utility function for parsing a list of things.
37 fromList :: Parse t a -> Parse t [a]
38 fromList p (s ::: ss) = (:) <$> p s <*> fromList p ss
39 fromList p Nil = pure []
40 fromList _ sx = fail ("Expected list")
41
42 gatherList :: S t -> Either String [S t]
43 gatherList (x ::: xs) = (:) <$> pure x <*> gatherList xs
44 gatherList Nil = pure []
45 gatherList sx = fail ("Expected list")
46
47 asPair :: ((S t, S t) -> Either String a) -> S t -> Either String a
48 asPair f (l ::: r ::: SNil) = f (l, r)
49 asPair _ sx = fail ("Expected two-element list")
50
51 asList :: ([S t] -> Either String a) -> S t -> Either String a
52 asList f ls = gatherList ls >>= f
53
54 asSymbol :: (t -> Either String a) -> S t -> Either String a
55 asSymbol f (A s) = f s
56 asSymbol _ sx = fail ("Expected symbol")
57
58 asAssoc :: ([(S t, S t)] -> Either String a) -> S t -> Either String a
59 asAssoc f ss = gatherList ss >>= mapM go >>= f
60 where go (a ::: b ::: Nil) = return (a, b)
61 go sx = fail ("Expected two-element list")
1111 , pattern L
1212 , pattern DL
1313 , pattern Nil
14 -- * Useful processing functions
15 , fromPair
16 , fromList
1417 ) where
1518
19 import Control.Applicative ((<$>), (<*>), pure)
1620 import Data.SCargot.Repr as R
1721
1822 -- | A shorter infix alias to grab the head
3034
3135 -- | A shorter alias for `RSList []`
3236 pattern Nil = R.RSList []
37
38 type S t = R.RichSExpr t
39 type Parse t a = S t -> Either String a
40
41 -- | Utility function for parsing a pair of things.
42 fromPair :: Parse t a -> Parse t b -> Parse t (a, b)
43 fromPair pl pr = asPair $ \(l,r) -> (,) <$> pl l <*> pr r
44
45 -- | Utility function for parsing a list of things.
46 fromList :: Parse t a -> Parse t [a]
47 fromList p = asList $ \ss -> mapM p ss
48
49 asPair :: ((S t, S t) -> Either String a) -> S t -> Either String a
50 asPair f (L [l, r]) = f (l, r)
51 asPair _ sx = fail ("Expected two-element list")
52
53 asList :: ([S t] -> Either String a) -> S t -> Either String a
54 asList f (L ls) = f ls
55 asList _ sx = fail ("Expected list")
56
57 asSymbol :: (t -> Either String a) -> S t -> Either String a
58 asSymbol f (A s) = f s
59 asSymbol _ sx = fail ("Expected symbol")
60
61 asAssoc :: ([(S t, S t)] -> Either String a) -> S t -> Either String a
62 asAssoc f (L ss) = gatherPairs ss >>= f
63 where gatherPairs (L [a, b] : ss) = (:) <$> pure (a, b) <*> gatherPairs ss
64 gatherPairs [] = pure []
65 gatherPairs _ = fail "..."
66 asAssoc _ sx = fail ("Expected assoc list")
1010 , pattern L
1111 , pattern A
1212 , pattern Nil
13 -- * Useful processing functions
14 , fromPair
15 , fromList
1316 ) where
1417
18 import Control.Applicative ((<$>), (<*>), pure)
1519 import Data.SCargot.Repr as R
1620
1721 -- | A shorter infix alias to grab the head
2630
2731 -- | A shorter alias for `WFSList []`
2832 pattern Nil = R.WFSList []
33
34 type S t = R.WellFormedSExpr t
35 type Parse t a = R.WellFormedSExpr t -> Either String a
36
37 -- | Utility function for parsing a pair of things.
38 fromPair :: Parse t a -> Parse t b -> Parse t (a, b)
39 fromPair pl pr (L [l, r]) = (,) <$> pl l <*> pr r
40 fromPair _ _ sx = fail ("Expected two-element list")
41
42 -- | Utility function for parsing a list of things.
43 fromList :: Parse t a -> Parse t [a]
44 fromList p (L ss) = mapM p ss
45 fromList _ sx = fail ("Expected list")
46
47
48 asPair :: ((S t, S t) -> Either String a) -> S t -> Either String a
49 asPair f (L [l, r]) = f (l, r)
50 asPair _ sx = fail ("Expected two-element list")
51
52 asList :: ([S t] -> Either String a) -> S t -> Either String a
53 asList f (L ls) = f ls
54 asList _ sx = fail ("Expected list")
55
56 asSymbol :: (t -> Either String a) -> S t -> Either String a
57 asSymbol f (A s) = f s
58 asSymbol _ sx = fail ("Expected symbol")
59
60 asAssoc :: ([(S t, S t)] -> Either String a) -> S t -> Either String a
61 asAssoc f (L ss) = gatherPairs ss >>= f
62 where gatherPairs (L [a, b] : ss) = (:) <$> pure (a, b) <*> gatherPairs ss
63 gatherPairs [] = pure []
64 gatherPairs _ = fail "..."
65 asAssoc _ sx = fail ("Expected assoc list")
1313 , fromWellFormed
1414 ) where
1515
16 import Data.String (IsString(..))
17
1618 -- | All S-Expressions can be understood as a sequence
1719 -- of @cons@ cells (represented here by 'SCons'), the
1820 -- empty list @nil@ (represented by 'SNil') or an
2224 | SAtom atom
2325 | SNil
2426 deriving (Eq, Show, Read, Functor)
27
28 instance IsString atom => IsString (SExpr atom) where
29 fromString = SAtom . fromString
2530
2631 -- | Sometimes, the cons-based interface is too low
2732 -- level, and we'd rather have the lists themselves
3944 | RSDotted [RichSExpr atom] atom
4045 | RSAtom atom
4146 deriving (Eq, Show, Read, Functor)
47
48 instance IsString atom => IsString (RichSExpr atom) where
49 fromString = RSAtom . fromString
4250
4351 -- | It should always be true that
4452 --
7078 | WFSAtom atom
7179 deriving (Eq, Show, Read, Functor)
7280
81 instance IsString atom => IsString (WellFormedSExpr atom) where
82 fromString = WFSAtom . fromString
83
7384 -- | This will be @Nothing@ if the argument contains an
7485 -- improper list. It should hold that
7586 --
7788 --
7889 -- and also (more tediously) that
7990 --
80 -- > case fromWellFormed (toWellFormed x) of
91 -- > case toWellFormed x of
8192 -- > Left _ -> True
82 -- > Right y -> x == y
93 -- > Right y -> x == fromWellFormed y
8394 toWellFormed :: SExpr atom -> Either String (WellFormedSExpr atom)
8495 toWellFormed SNil = return (WFSList [])
8596 toWellFormed (SAtom a) = return (WFSAtom a)
77 import qualified Data.Text as T
88
99 pToken :: Parser ByteString
10 pToken = undefined
10 pToken = do
11 x <- char (isAlpha || isTokenPunct)
12 xs <- takeWhile1 (isAlpha || isDigit || isTokenPunct)
13
14 isTokenPunct :: Char -> Bool
15 isTokenPunct c = c `elem` "-./_:*+="
16
17 pWithLength :: Parser ByteString
18 pWithLength = do
19 n <- takeWhile1 isDigit
20 pFindType (Just (read (T.unpack n)))
21
22 pFindType :: Maybe Int -> Parser ByteString
23 pFindType len = do
24 c <- peekChar
25 case c of
26 ':' -> case len of
27 Just l -> pVerbatim l
28 Nothing -> fail "Verbatim encoding without length given"
29 '"' -> pQuoted len
30 '#' -> pHex len
31 '{' -> pBase64Verbatim len
32 '|' -> pBase64 len
33 _ -> case len of
34 Just _ -> fail "Unexpected length field"
35 Nothing -> pToken
1136
1237 pQuoted :: Maybe Int -> Parser ByteString
1338 pQuoted = do
1742 return ss
1843
1944 pHex :: Parser ByteString
20 pHex = undefined
45 pHex = do
46
2147
2248 pVerbatim :: Int -> Parser ByteString
2349 pVerbatim = do
2450 char ':'
2551 take n
2652
27 pBase64Verbatim :: Parser ByteString
28 pBase64 :: Parser ByteString
53 pBase64Verbatim :: Maybe Int -> Parser ByteString
54 pBase64Verbatim = undefined
55
56 pBase64 :: Maybe Int -> Parser ByteString
57 pBase64 = undefined
1818 Data.SCargot.Repr.Rich,
1919 Data.SCargot.Repr.WellFormed,
2020 Data.SCargot.General,
21 Data.SCargot.Basic,
22 Data.SCargot.Comments,
2123 Data.SCargot.Tutorial
2224 -- other-modules:
2325 -- other-extensions: