on master help sign in
Test flag and removed attempt dependency.

Now using c-m-f directly with a simple YAttempt data type. This way, the external API only shows the actual exceptions that can be thrown. Not doing automatic toObject/fromObject stuff. Migrated from bytestring-class to convertible for BS conversions.

commitb9ae04f4031532afed22f1c56c673a30a2d605c2 authorMichael Snoyman <michael@snoyman.com> dateSun 15 Nov 2009, 00:08 (17 years ago) parent810394b
3 files changed+138−78
Text/Libyaml.hs+49−17 Text/Yaml.hs+71−53 yaml.cabal+18−8
Text/Libyaml.hs+49−17
@@ -1,5 +1,8 @@
11{-# LANGUAGE EmptyDataDecls #-}
22{-# LANGUAGE ForeignFunctionInterface #-}
3+{-# LANGUAGE DeriveDataTypeable #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
36
47{-# INCLUDE <yaml.h> #-}
58{-# INCLUDE <helper.h> #-}
@@ -20,7 +23,9 @@ import Foreign.Ptr
2023import Foreign.ForeignPtr
2124import Foreign.Marshal.Alloc
2225
23−import Data.Attempt
26+import Control.Monad.Failure
27+import Data.Typeable (Typeable)
28+import Control.Exception (Exception)
2429
2530data ParserStruct
2631type Parser = Ptr ParserStruct
@@ -71,12 +76,21 @@ foreign import ccall unsafe "yaml_event_delete"
7176foreign import ccall "print_parser_error"
7277 c_print_parser_error :: Parser -> IO ()
7378
74−parserParseOne :: Parser -> IO (Attempt Event)
79+data YamlException =
80+ YamlParserException String
81+ | YamlEmitterException String
82+ deriving (Show, Typeable)
83+instance Exception YamlException
84+
85+parserParseOne :: MonadFailure YamlException m
86+ => Parser
87+ -> IO (m Event)
7588parserParseOne parser = withEventRaw $ \er -> do
7689 res <- c_yaml_parser_parse parser er
7790 event <-
7891 if res == 0
79− then c_print_parser_error parser >> return (failureString "yaml_parser_parse failed") -- FIXME get the actual error message
92+ then c_print_parser_error parser
93+ >> return (failure $ YamlParserException "FIXME")
8094 else return `fmap` getEvent er
8195 c_yaml_event_delete er
8296 return event
@@ -140,19 +154,29 @@ getEvent er = do
140154 YamlMappingStartEvent -> return EventMappingStart
141155 YamlMappingEndEvent -> return EventMappingEnd
142156
143−parserParse :: Parser -> IO (Attempt [Event])
157+data YAttempt v = YSuccess v | YFailure YamlException -- FIXME use c-m-e?
158+instance Monad YAttempt where
159+ return = YSuccess
160+ YSuccess v >>= f = f v
161+ YFailure e >>= _ = YFailure e
162+instance MonadFailure YamlException YAttempt where
163+ failure = YFailure
164+
165+parserParse :: MonadFailure YamlException m
166+ => Parser
167+ -> IO (m [Event])
144168parserParse parser = do
145169 event' <- parserParseOne parser
146170 case event' of
147− Failure e -> return $ Failure e
148− Success event ->
171+ YFailure e -> return $ failure e
172+ YSuccess event ->
149173 case event of
150− EventStreamEnd -> return $ Success [event]
174+ EventStreamEnd -> return $ return [event]
151175 _ -> do
152176 rest <- parserParse parser
153177 case rest of
154− Failure e -> return $ Failure e
155− Success rest' -> return $ Success $ event : rest'
178+ YFailure e -> return $ failure e
179+ YSuccess rest' -> return $ return $ event : rest'
156180
157181-- Emitter
158182
@@ -181,22 +205,26 @@ foreign import ccall unsafe "get_buffer_buff"
181205foreign import ccall unsafe "get_buffer_used"
182206 c_get_buffer_used :: Buffer -> IO CULong
183207
184−withBuffer :: (Buffer -> IO (Attempt ())) -> IO (Attempt B.ByteString)
208+withBuffer :: MonadFailure YamlException m
209+ => (Buffer -> IO (YAttempt ()))
210+ -> IO (m B.ByteString)
185211withBuffer f = allocaBytes bufferSize $ \b -> do
186212 c_buffer_init b
187213 aRes <- f b
188214 case aRes of
189− Failure e -> return $ Failure e
190− Success () -> do
215+ YFailure e -> return $ failure e
216+ YSuccess () -> do
191217 ptr' <- c_get_buffer_buff b
192218 len <- c_get_buffer_used b
193219 fptr <- newForeignPtr_ $ castPtr ptr'
194− return $ Success $ B.fromForeignPtr fptr 0 $ fromIntegral len
220+ return $ return $ B.fromForeignPtr fptr 0 $ fromIntegral len
195221
196222foreign import ccall unsafe "my_emitter_set_output"
197223 c_my_emitter_set_output :: Emitter -> Buffer -> IO ()
198224
199−withEmitter :: (Emitter -> IO (Attempt ())) -> IO (Attempt B.ByteString)
225+withEmitter :: MonadFailure YamlException m
226+ => (Emitter -> IO (YAttempt ()))
227+ -> IO (m B.ByteString)
200228withEmitter f = allocaBytes emitterSize $ \e -> do
201229 _res <- c_yaml_emitter_initialize e
202230 -- FIXME check res
@@ -212,12 +240,16 @@ foreign import ccall unsafe "yaml_emitter_emit"
212240foreign import ccall unsafe "print_emitter_error"
213241 c_print_emitter_error :: Emitter -> IO ()
214242
215−emitEvents :: Emitter -> [Event] -> IO (Attempt ())
216−emitEvents _ [] = return $ Success ()
243+emitEvents :: MonadFailure YamlException m
244+ => Emitter
245+ -> [Event]
246+ -> IO (m ())
247+emitEvents _ [] = return $ return ()
217248emitEvents emitter (e:rest) = do
218249 res <- toEventRaw e $ c_yaml_emitter_emit emitter
219250 if res == 0
220− then c_print_emitter_error emitter >> return (failureString "yaml_emitter_emit failed") -- FIXME get the actual error message
251+ then c_print_emitter_error emitter
252+ >> return (failure $ YamlEmitterException "FIXME")
221253 else emitEvents emitter rest
222254
223255foreign import ccall unsafe "yaml_stream_start_event_initialize"
Text/Yaml.hs+71−53
@@ -1,6 +1,7 @@
11{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE FlexibleContexts #-}
33{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE CPP #-}
45---------------------------------------------------------
56--
67-- Module : Text.Yaml
@@ -21,84 +22,97 @@ module Text.Yaml
2122 , encodeFile
2223 , decode
2324 , decodeFile
25+#if TEST
2426 , testSuite
27+#endif
2528 ) where
2629
2730import Prelude hiding (readList)
2831import Text.Libyaml
2932import qualified Data.ByteString as B
30−import Data.ByteString.Class
33+import qualified Data.ByteString.Lazy as BL
3134import Data.Object
32−import Data.Object.Raw
35+import Data.Object.Text
3336import System.IO.Unsafe
34−import Control.Arrow (first, (***))
35−import Control.Monad (replicateM, liftM)
37+import Control.Arrow (first)
38+import Control.Monad (liftM, join)
3639import Control.Monad.Trans
3740import Control.Monad.Attempt
41+import Data.Convertible
3842
43+import Data.Text.Lazy (Text)
44+import qualified Data.Text.Lazy as LT
45+import qualified Data.Text.Lazy.Encoding as LTE
46+
47+#if TEST
3948import Test.Framework (testGroup, Test)
4049import Test.Framework.Providers.HUnit
4150import Test.Framework.Providers.QuickCheck (testProperty)
4251import Test.HUnit hiding (Test, path)
4352import Test.QuickCheck
53+#endif
4454
45−import Control.Exception
46−
47−encode :: (StrictByteString bs, ToObject o Raw Raw) => o -> bs
55+encode :: (ConvertSuccess B.ByteString a, ToObject o Text Text) => o -> a
4856encode = unsafePerformIO . encode'
4957
50−decode :: (StrictByteString bs, FromObject o Raw Raw, MonadFailure SomeException m)
51− => bs
52− -> m o
53−decode bs = unsafePerformIO $ do
54− a <- runAttemptT $ decode' bs -- FIXME
55− case a of
56− Success s -> return $ return s
57− Failure e -> return $ failure e
58+decode :: (ConvertSuccess a B.ByteString,
59+ MonadFailure YamlException m)
60+ => a
61+ -> m TextObject
62+decode = unsafePerformIO . decode'
5863
59−encodeFile :: ToObject o Raw Raw => FilePath -> o -> IO ()
60−encodeFile path o = encode' o >>= B.writeFile path
64+encodeFile :: ToObject o Text Text => FilePath -> o -> IO ()
65+encodeFile path o = encode' o >>= ltWriteFile path
6166
62−decodeFile :: (FromObject o Raw Raw, MonadFailure SomeException m, MonadIO m)
63− => FilePath
64− -> m o
65−decodeFile path = liftIO (B.readFile path) >>= decode'
67+ltWriteFile :: FilePath -> Text -> IO ()
68+ltWriteFile fp = BL.writeFile fp . LTE.encodeUtf8
6669
67−encode' :: (StrictByteString bs, ToObject o Raw Raw) => o -> IO bs
70+decodeFile :: (MonadFailure YamlException m,
71+ MonadIO m)
72+ => FilePath
73+ -> m TextObject
74+decodeFile path = do
75+ contents <- liftIO (ltReadFile path)
76+ join $ liftIO $ decode' contents
77+
78+ltReadFile :: FilePath -> IO Text
79+ltReadFile = fmap LTE.decodeUtf8 . BL.readFile
80+
81+encode' :: (ConvertSuccess B.ByteString a,
82+ ToObject o Text Text)
83+ => o
84+ -> IO a
6885encode' o =
6986 let events = objectToEvents $ toObject o
7087 result = withEmitter $ flip emitEvents events
71− in fromStrictByteString `fmap` joinAttempt result
88+ in convertSuccess `fmap` joinAttempt result
7289
73−decode' :: (StrictByteString bs, FromObject o Raw Raw, MonadIO m,
74− MonadFailure SomeException m)
75− => bs
76− -> m o
90+decode' :: (ConvertSuccess a B.ByteString,
91+ MonadFailure YamlException m
92+ )
93+ => a
94+ -> IO (m TextObject)
7795decode' bs = do
78− events <- liftIO $ withParser (toStrictByteString bs) parserParse
79− attempt failure' (myFA . fromObject . eventsToRawObject) events
80− where
81− myFA = attempt failure' return
82− failure' :: (MonadFailure SomeException m, Exception e) => e -> m a
83− failure' = failure . SomeException
84−
85−objectToEvents :: RawObject -> [Event]
96+ events <- withParser (convertSuccess bs) parserParse
97+ return $ liftM eventsToTextObject events
98+
99+objectToEvents :: TextObject -> [Event]
86100objectToEvents y = concat
87101 [ [EventStreamStart, EventDocumentStart]
88102 , writeSingle y
89103 , [EventDocumentEnd, EventStreamEnd]] where
90− writeSingle :: RawObject -> [Event]
91− writeSingle (Scalar (Raw bs)) = [EventScalar $ toStrictByteString bs]
104+ writeSingle :: TextObject -> [Event]
105+ writeSingle (Scalar text) = [EventScalar $ convertSuccess text]
92106 writeSingle (Sequence ys) =
93107 (EventSequenceStart : concatMap writeSingle ys)
94108 ++ [EventSequenceEnd]
95109 writeSingle (Mapping pairs) =
96110 EventMappingStart : concatMap writePair pairs ++ [EventMappingEnd]
97− writePair :: (Raw, RawObject) -> [Event]
98− writePair (Raw k, v) = EventScalar (fromLazyByteString k) : writeSingle v
111+ writePair :: (Text, TextObject) -> [Event]
112+ writePair (k, v) = EventScalar (convertSuccess k) : writeSingle v
99113
100−eventsToRawObject :: [Event] -> RawObject
101−eventsToRawObject = fst . readSingle . dropWhile isIgnored where
114+eventsToTextObject :: [Event] -> TextObject
115+eventsToTextObject = fst . readSingle . dropWhile isIgnored where
102116 isIgnored EventAlias = False
103117 isIgnored (EventScalar _) = False
104118 isIgnored EventSequenceStart = False
@@ -106,37 +120,40 @@ eventsToRawObject = fst . readSingle . dropWhile isIgnored where
106120 isIgnored EventMappingStart = False
107121 isIgnored EventMappingEnd = False
108122 isIgnored _ = True
109− readSingle :: [Event] -> (RawObject, [Event])
123+ readSingle :: [Event] -> (TextObject, [Event])
110124 readSingle [] = error "readSingle: no more events"
111125 readSingle (EventScalar bs:rest) =
112− (Scalar $ Raw $ toLazyByteString bs, rest)
126+ (Scalar $ convertSuccess bs, rest)
113127 readSingle (EventSequenceStart:rest) = readList [] rest
114128 readSingle (EventMappingStart:rest) = readMap [] rest
115129 readSingle (x:_) = error $ "readSingle: " ++ show x
116− readList :: [RawObject] -> [Event] -> (RawObject, [Event])
130+ readList :: [TextObject] -> [Event] -> (TextObject, [Event])
117131 readList nodes (EventSequenceEnd:rest) =
118132 (Sequence $ reverse nodes, rest)
119133 readList nodes events =
120134 let (next, rest) = readSingle events
121135 in readList (next : nodes) rest
122− readMap :: [(B.ByteString, RawObject)]
136+ readMap :: [(B.ByteString, TextObject)]
123137 -> [Event]
124− -> (RawObject, [Event])
138+ -> (TextObject, [Event])
125139 readMap pairs (EventMappingEnd:rest) =
126− (Mapping $ map (first $ Raw . toLazyByteString) $ reverse pairs, rest)
140+ (Mapping $ map (first convertSuccess) $ reverse pairs, rest)
127141 readMap pairs (EventScalar bs:events) =
128142 let (next, rest) = readSingle events
129− in readMap ((fromStrictByteString bs, next) : pairs) rest
143+ in readMap ((bs, next) : pairs) rest
130144 readMap _ (e:_) = error $ "Unexpected event in readMap: " ++ show e
131145 readMap _ [] = error "Unexpected empty event list in readMap"
132146
133147newtype MyString = MyString String
134148 deriving (Eq, Show)
135−instance ToScalar MyString Raw where
136− toScalar (MyString s) = toScalar s
137−instance FromScalar MyString Raw where
138− fromScalar raw = MyString `liftM` fromScalar raw
139−
149+instance ConvertAttempt MyString Text where
150+ convertAttempt = return . convertSuccess
151+instance ConvertSuccess MyString Text where
152+ convertSuccess (MyString s) = convertSuccess s
153+instance ConvertAttempt Text MyString where
154+ convertAttempt raw = MyString `liftM` convertAttempt raw
155+
156+#if TEST
140157propEncodeDecode :: Object MyString MyString -> Bool
141158propEncodeDecode o = fromAttempt (decode (encode o :: B.ByteString))
142159 == Just o
@@ -174,3 +191,4 @@ instance Arbitrary MyString where
174191 size <- arbitrary
175192 s <- replicateM (size `mod` 5) $ elements ['A'..'Z']
176193 return $! MyString s
194+#endif
yaml.cabal+18−8
@@ -15,22 +15,22 @@ extra-source-files: c/helper.h,
1515 c/yaml_private.h,
1616 c/yaml.h
1717
18+flag buildtests
19+ description: Build the executable to run unit tests
20+ default: False
21+
1822library
1923 build-depends: base >= 4 && < 5,
20− bytestring-class,
2124 data-object == 0.2.0,
2225 bytestring >= 0.9.1.4 && < 1,
2326 haskell98,
24− test-framework,
25− test-framework-quickcheck,
26− test-framework-hunit,
27− HUnit,
2827 time,
29− QuickCheck >= 1 && < 2,
30− attempt,
28+ attempt >= 0.0.2,
3129 safe-failure,
3230 transformers >= 0.1.4.0,
33− control-monad-failure
31+ control-monad-failure >= 0.4,
32+ text >= 0.5,
33+ convertible >= 1.2.0
3434 exposed-modules: Text.Yaml
3535 other-modules: Text.Libyaml
3636 ghc-options: -Wall
@@ -46,6 +46,16 @@ library
4646 include-dirs: c
4747
4848executable yamltest
49+ if flag(buildtests)
50+ Buildable: True
51+ cpp-options: -DTEST
52+ build-depends: test-framework,
53+ test-framework-quickcheck,
54+ test-framework-hunit,
55+ HUnit,
56+ QuickCheck >= 1 && < 2
57+ else
58+ Buildable: False
4959 ghc-options: -Wall
5060 c-sources: c/helper.c,
5161 c/api.c,