More README changes
Getty Ritter
9 years ago
86 | 86 | Left "Found atom in cdr position" |
87 | 87 | ~~~~ |
88 | 88 | |
89 |
# |
|
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 | |
90 | 176 | |
91 | 177 | By default, an S-expression spec does not include a comment syntax, but |
92 | 178 | the provided `withSemicolonComments` function will cause it to understand |
115 | 201 | Right [SCons (SAtom "a") (SCons (SAtom "b") SNil)] |
116 | 202 | ~~~~ |
117 | 203 | |
118 |
# |
|
204 | ## Reader Macros | |
119 | 205 | |
120 | 206 | A _reader macro_ is a Lisp macro which is invoked during read time. This |
121 | 207 | allows the _lexical_ syntax of a Lisp to be modified. The most commonly |