More README changes
Getty Ritter
10 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 |