gdritter repos s-cargot / 74003e3
More README changes Getty Ritter 9 years ago
1 changed file(s) with 88 addition(s) and 2 deletion(s). Collapse all Expand all
8686 Left "Found atom in cdr position"
8787 ~~~~
8888
89 # Comments
89 ## Atom Types
90
91 Any type can serve as an underlying atom type provided that it has
92 an AttoParsec parser and a serializer (i.e. a way of turning it
93 into `Text`.) For these examples, I'm going to use a very simple
94 serializer that is roughly like the one found in `Data.SCargot.Basic`,
95 which parses symbolic tokens of letters, numbers, and some
96 punctuation characters. This means that the 'serializer' here
97 is just the identity function:
98
99 ~~~~.haskell
100 spec :: SExprSpec Text (SExpr Text)
101 spec = mkSpec (takeWhile1 (\ c -> isAlphaNum c || c `elem` "+-*/!?")) id
102 ~~~~
103
104 A more elaborate atom type would distinguish between different
105 varieties of token, so a small example (that understands just
106 identifiers and numbers) is
107
108 ~~~~.haskell
109 import Data.Char (isDigit, isAlpha)
110 import Data.Text (Text)
111 import qualified Data.Text as T
112
113 data Atom = Ident Text | Num Int deriving (Eq, Show)
114
115 pAtom :: Parser Atom
116 pAtom = ((Num . read . T.unpack) <$> takeWhile1 isDigit)
117 <|> (Ident <$> takeWhile1 isAlpha)
118
119 sAtom :: Atom -> Text
120 sAtom (Ident t) = t
121 sAtom (Num n) = T.pack (show n)
122
123 mySpec :: SExprSpec Atom (SExpr Atom)
124 mySpec = mkSpec pAtom sAtom
125 ~~~~
126
127 We can then use this newly created atom type within an S-expression
128 for both parsing and serialization:
129
130 ~~~~
131 *Data.SCargot.General T> decode mySpec "(foo 1)"
132 Right [SCons (SAtom (Ident "foo")) (SCons (SAtom (Num 1)) SNil)]
133 *Data.SCargot.General T> encode mySpec [SCons (SAtom (Num 0)) SNil]
134 "(0)"
135 ~~~~
136
137 ## Carrier Types
138
139 As pointed out above, there are three different carrier types that are
140 used to represent S-expressions by the library, but you can use any
141 type as a carrier type for a spec. This is particularly useful when
142 you want to do your own parsing. For example, if we wanted to parse
143 a small S-expression-based arithmetic language, we could define a
144 data type and transformations from and to an S-expression type:
145
146 ~~~~.haskell
147 import Data.Char (isDigit)
148 import Data.Text (Text)
149 import qualified Data.Text as T
150
151 data Expr = Add Expr Expr | Num Int deriving (Eq, Show)
152
153 toExpr :: RichSExpr Text -> Either String Expr
154 toExpr (RSList [RSAtom "+", l, r]) = Add <$> toExpr l <*> toExpr r
155 toExpr (RSAtom c)
156 | T.all isDigit c = pure (Num (read (T.unpack c)))
157 | otherwise = Left "Non-numeric token as argument"
158 toExpr _ = "Unrecognized s-expr"
159
160 fromExpr :: Expr -> RichSExpr Text
161 fromExpr (Add x y) = RSList [RSAtom "+", fromExpr x, fromExpr y]
162 fromExpr (Num n) = RSAtom (T.pack (show n))
163 ~~~~
164
165 then we could use the `convertSpec` function to add this directly to
166 the `SExprSpec`:
167
168 ~~~~
169 *Data.SCargot.General T> decode (convertSpec toExpr fromExpr (asRich spec)) "(+ 1 2)"
170 Right [Add (Num 1) (Num 2)]
171 *Data.SCargot.General T> decode (convertSpec toExpr fromExpr (asRich spec)) "(0 1 2)"
172 Left "Unrecognized s-expr"
173 ~~~~
174
175 ## Comments
90176
91177 By default, an S-expression spec does not include a comment syntax, but
92178 the provided `withSemicolonComments` function will cause it to understand
115201 Right [SCons (SAtom "a") (SCons (SAtom "b") SNil)]
116202 ~~~~
117203
118 # Reader Macros
204 ## Reader Macros
119205
120206 A _reader macro_ is a Lisp macro which is invoked during read time. This
121207 allows the _lexical_ syntax of a Lisp to be modified. The most commonly