1{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE ForeignFunctionInterface #-}
3{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE PackageImports #-}
8{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE TypeFamilies #-}
13-- | Low-level, streaming YAML interface. For a higher-level interface, see
16 ( -- * The event stream
22 -- * Encoding and decoding
32import qualified Data.ByteString.Internal as B
33import qualified Data.ByteString.Char8 as B8
34import qualified Data.ByteString
35import qualified Data.ByteString.Unsafe as BU
36import Data.ByteString (ByteString, packCStringLen)
40import Foreign.ForeignPtr
41import Foreign.Marshal.Alloc
44import Control.Monad.IO.Class
46import Control.Exception (throwIO, Exception, finally)
47import Control.Applicative
48import Control.Monad.Trans.Resource
49import Data.Conduit hiding (Source, Sink, Conduit)
50import Control.Exception (mask_)
57 | EventAlias !AnchorName
58 | EventScalar !ByteString !Tag !Style !Anchor
59 | EventSequenceStart !Anchor
61 | EventMappingStart !Anchor
72 deriving (Show, Read, Eq, Enum, Bounded, Ord, Data, Typeable)
84 deriving (Show, Eq, Read, Data, Typeable)
86type AnchorName = String
87type Anchor = Maybe AnchorName
89tagToString :: Tag -> String
90tagToString StrTag = "tag:yaml.org,2002:str"
91tagToString FloatTag = "tag:yaml.org,2002:float"
92tagToString NullTag = "tag:yaml.org,2002:null"
93tagToString BoolTag = "tag:yaml.org,2002:bool"
94tagToString SetTag = "tag:yaml.org,2002:set"
95tagToString IntTag = "tag:yaml.org,2002:int"
96tagToString SeqTag = "tag:yaml.org,2002:seq"
97tagToString MapTag = "tag:yaml.org,2002:map"
98tagToString (UriTag s) = s
101bsToTag :: ByteString -> Tag
102bsToTag = stringToTag . B8.unpack
104stringToTag :: String -> Tag
105stringToTag "tag:yaml.org,2002:str" = StrTag
106stringToTag "tag:yaml.org,2002:float" = FloatTag
107stringToTag "tag:yaml.org,2002:null" = NullTag
108stringToTag "tag:yaml.org,2002:bool" = BoolTag
109stringToTag "tag:yaml.org,2002:set" = SetTag
110stringToTag "tag:yaml.org,2002:int" = IntTag
111stringToTag "tag:yaml.org,2002:seq" = SeqTag
112stringToTag "tag:yaml.org,2002:map" = MapTag
113stringToTag "" = NoTag
114stringToTag s = UriTag s
117type Parser = Ptr ParserStruct
122type EventRaw = Ptr EventRawStruct
126foreign import ccall unsafe "yaml_parser_initialize"
127 c_yaml_parser_initialize :: Parser -> IO CInt
129foreign import ccall unsafe "yaml_parser_delete"
130 c_yaml_parser_delete :: Parser -> IO ()
132foreign import ccall unsafe "yaml_parser_set_input_string"
133 c_yaml_parser_set_input_string :: Parser
138foreign import ccall unsafe "yaml_parser_set_input_file"
139 c_yaml_parser_set_input_file :: Parser
144type File = Ptr FileStruct
146foreign import ccall unsafe "fopen"
151foreign import ccall unsafe "fclose"
155foreign import ccall unsafe "fclose_helper"
156 c_fclose_helper :: File -> IO ()
158foreign import ccall unsafe "yaml_parser_parse"
159 c_yaml_parser_parse :: Parser -> EventRaw -> IO CInt
161foreign import ccall unsafe "yaml_event_delete"
162 c_yaml_event_delete :: EventRaw -> IO ()
164foreign import ccall "get_parser_error_problem"
165 c_get_parser_error_problem :: Parser -> IO (Ptr CUChar)
167foreign import ccall "get_parser_error_context"
168 c_get_parser_error_context :: Parser -> IO (Ptr CUChar)
170foreign import ccall unsafe "get_parser_error_index"
171 c_get_parser_error_index :: Parser -> IO CULong
173foreign import ccall unsafe "get_parser_error_line"
174 c_get_parser_error_line :: Parser -> IO CULong
176foreign import ccall unsafe "get_parser_error_column"
177 c_get_parser_error_column :: Parser -> IO CULong
179makeString :: MonadIO m => (a -> m (Ptr CUChar)) -> a -> m String
181 cchar <- castPtr `liftM` f a
184 else liftIO $ peekCString cchar
186data EventType = YamlNoEvent
187 | YamlStreamStartEvent
189 | YamlDocumentStartEvent
190 | YamlDocumentEndEvent
193 | YamlSequenceStartEvent
194 | YamlSequenceEndEvent
195 | YamlMappingStartEvent
196 | YamlMappingEndEvent
199foreign import ccall unsafe "get_event_type"
200 c_get_event_type :: EventRaw -> IO CInt
202foreign import ccall unsafe "get_scalar_value"
203 c_get_scalar_value :: EventRaw -> IO (Ptr CUChar)
205foreign import ccall unsafe "get_scalar_length"
206 c_get_scalar_length :: EventRaw -> IO CULong
208foreign import ccall unsafe "get_scalar_tag"
209 c_get_scalar_tag :: EventRaw -> IO (Ptr CUChar)
211foreign import ccall unsafe "get_scalar_tag_len"
212 c_get_scalar_tag_len :: EventRaw -> IO CULong
214foreign import ccall unsafe "get_scalar_style"
215 c_get_scalar_style :: EventRaw -> IO CInt
217foreign import ccall unsafe "get_scalar_anchor"
218 c_get_scalar_anchor :: EventRaw -> IO CString
220foreign import ccall unsafe "get_sequence_start_anchor"
221 c_get_sequence_start_anchor :: EventRaw -> IO CString
223foreign import ccall unsafe "get_mapping_start_anchor"
224 c_get_mapping_start_anchor :: EventRaw -> IO CString
226foreign import ccall unsafe "get_alias_anchor"
227 c_get_alias_anchor :: EventRaw -> IO CString
229getEvent :: EventRaw -> IO (Maybe Event)
231 et <- c_get_event_type er
232 case toEnum $ fromEnum et of
233 YamlNoEvent -> return Nothing
234 YamlStreamStartEvent -> return $ Just EventStreamStart
235 YamlStreamEndEvent -> return $ Just EventStreamEnd
236 YamlDocumentStartEvent -> return $ Just EventDocumentStart
237 YamlDocumentEndEvent -> return $ Just EventDocumentEnd
239 yanchor <- c_get_alias_anchor er
240 anchor <- if yanchor == nullPtr
241 then error "got YamlAliasEvent with empty anchor"
242 else peekCString yanchor
243 return $ Just $ EventAlias anchor
244 YamlScalarEvent -> do
245 yvalue <- c_get_scalar_value er
246 ylen <- c_get_scalar_length er
247 ytag <- c_get_scalar_tag er
248 ytag_len <- c_get_scalar_tag_len er
249 ystyle <- c_get_scalar_style er
250 let ytag_len' = fromEnum ytag_len
251 let yvalue' = castPtr yvalue
252 let ytag' = castPtr ytag
253 let ylen' = fromEnum ylen
254 bs <- packCStringLen (yvalue', ylen')
257 then return Data.ByteString.empty
258 else packCStringLen (ytag', ytag_len')
259 let style = toEnum $ fromEnum ystyle
260 yanchor <- c_get_scalar_anchor er
261 anchor <- if yanchor == nullPtr
263 else fmap Just $ peekCString yanchor
264 return $ Just $ EventScalar bs (bsToTag tagbs) style anchor
265 YamlSequenceStartEvent -> do
266 yanchor <- c_get_sequence_start_anchor er
267 anchor <- if yanchor == nullPtr
269 else fmap Just $ peekCString yanchor
270 return $ Just $ EventSequenceStart anchor
271 YamlSequenceEndEvent -> return $ Just EventSequenceEnd
272 YamlMappingStartEvent -> do
273 yanchor <- c_get_mapping_start_anchor er
274 anchor <- if yanchor == nullPtr
276 else fmap Just $ peekCString yanchor
277 return $ Just $ EventMappingStart anchor
278 YamlMappingEndEvent -> return $ Just EventMappingEnd
283type Emitter = Ptr EmitterStruct
287foreign import ccall unsafe "yaml_emitter_initialize"
288 c_yaml_emitter_initialize :: Emitter -> IO CInt
290foreign import ccall unsafe "yaml_emitter_delete"
291 c_yaml_emitter_delete :: Emitter -> IO ()
294type Buffer = Ptr BufferStruct
298foreign import ccall unsafe "buffer_init"
299 c_buffer_init :: Buffer -> IO ()
301foreign import ccall unsafe "get_buffer_buff"
302 c_get_buffer_buff :: Buffer -> IO (Ptr CUChar)
304foreign import ccall unsafe "get_buffer_used"
305 c_get_buffer_used :: Buffer -> IO CULong
307foreign import ccall unsafe "my_emitter_set_output"
308 c_my_emitter_set_output :: Emitter -> Buffer -> IO ()
310foreign import ccall unsafe "yaml_emitter_set_output_file"
311 c_yaml_emitter_set_output_file :: Emitter -> File -> IO ()
313foreign import ccall unsafe "yaml_emitter_emit"
314 c_yaml_emitter_emit :: Emitter -> EventRaw -> IO CInt
316foreign import ccall unsafe "yaml_stream_start_event_initialize"
317 c_yaml_stream_start_event_initialize :: EventRaw -> CInt -> IO CInt
319foreign import ccall unsafe "yaml_stream_end_event_initialize"
320 c_yaml_stream_end_event_initialize :: EventRaw -> IO CInt
322foreign import ccall unsafe "yaml_scalar_event_initialize"
323 c_yaml_scalar_event_initialize
325 -> Ptr CUChar -- anchor
327 -> Ptr CUChar -- value
329 -> CInt -- plain_implicit
330 -> CInt -- quoted_implicit
334foreign import ccall unsafe "simple_document_start"
335 c_simple_document_start :: EventRaw -> IO CInt
337foreign import ccall unsafe "yaml_document_end_event_initialize"
338 c_yaml_document_end_event_initialize :: EventRaw -> CInt -> IO CInt
340foreign import ccall unsafe "yaml_sequence_start_event_initialize"
341 c_yaml_sequence_start_event_initialize
349foreign import ccall unsafe "yaml_sequence_end_event_initialize"
350 c_yaml_sequence_end_event_initialize :: EventRaw -> IO CInt
352foreign import ccall unsafe "yaml_mapping_start_event_initialize"
353 c_yaml_mapping_start_event_initialize
361foreign import ccall unsafe "yaml_mapping_end_event_initialize"
362 c_yaml_mapping_end_event_initialize :: EventRaw -> IO CInt
364foreign import ccall unsafe "yaml_alias_event_initialize"
365 c_yaml_alias_event_initialize
370toEventRaw :: Event -> (EventRaw -> IO a) -> IO a
371toEventRaw e f = allocaBytes eventSize $ \er -> do
374 c_yaml_stream_start_event_initialize
376 0 -- YAML_ANY_ENCODING
378 c_yaml_stream_end_event_initialize er
379 EventDocumentStart ->
380 c_simple_document_start er
382 c_yaml_document_end_event_initialize er 1
383 EventScalar bs thetag style0 anchor -> do
384 BU.unsafeUseAsCStringLen bs $ \(value, len) -> do
385 let value' = castPtr value :: Ptr CUChar
386 len' = fromIntegral len :: CInt
387 let thetag' = tagToString thetag
388 withCString thetag' $ \tag' -> do
391 PlainNoTag -> (1, Plain)
393 style' = toEnum $ fromEnum style
395 qi = if null thetag' then 1 else 0
398 c_yaml_scalar_event_initialize
405 qi -- quoted_implicit
408 withCString anchor' $ \anchorP' -> do
409 let anchorP = castPtr anchorP'
410 c_yaml_scalar_event_initialize
417 qi -- quoted_implicit
419 EventSequenceStart Nothing ->
420 c_yaml_sequence_start_event_initialize
425 0 -- YAML_ANY_SEQUENCE_STYLE
426 EventSequenceStart (Just anchor) ->
427 withCString anchor $ \anchor' -> do
428 let anchorP = castPtr anchor'
429 c_yaml_sequence_start_event_initialize
434 0 -- YAML_ANY_SEQUENCE_STYLE
436 c_yaml_sequence_end_event_initialize er
437 EventMappingStart Nothing ->
438 c_yaml_mapping_start_event_initialize
443 0 -- YAML_ANY_SEQUENCE_STYLE
444 EventMappingStart (Just anchor) ->
445 withCString anchor $ \anchor' -> do
446 let anchorP = castPtr anchor'
447 c_yaml_mapping_start_event_initialize
452 0 -- YAML_ANY_SEQUENCE_STYLE
454 c_yaml_mapping_end_event_initialize er
456 withCString anchor $ \anchorP' -> do
457 let anchorP = castPtr anchorP'
458 c_yaml_alias_event_initialize
461 unless (ret == 1) $ throwIO $ ToEventRawException ret
464newtype ToEventRawException = ToEventRawException CInt
465 deriving (Show, Typeable)
466instance Exception ToEventRawException
468decode :: MonadResource m => B.ByteString
469#if MIN_VERSION_conduit(1, 0, 0)
474decode bs | B8.null bs = return ()
476 bracketP alloc cleanup (runParser . fst)
479 ptr <- mallocBytes parserSize
480 res <- c_yaml_parser_initialize ptr
483 c_yaml_parser_delete ptr
485 throwIO $ YamlException "Yaml out of memory"
487 let (bsfptr, offset, len) = B.toForeignPtr bs
488 let bsptrOrig = unsafeForeignPtrToPtr bsfptr
489 let bsptr = castPtr bsptrOrig `plusPtr` offset
490 c_yaml_parser_set_input_string ptr bsptr (fromIntegral len)
492 cleanup (ptr, bsfptr) = do
493 touchForeignPtr bsfptr
494 c_yaml_parser_delete ptr
497decodeFile :: MonadResource m => FilePath
498#if MIN_VERSION_conduit(1, 0, 0)
504 bracketP alloc cleanup (runParser . fst)
507 ptr <- mallocBytes parserSize
508 res <- c_yaml_parser_initialize ptr
511 c_yaml_parser_delete ptr
513 throwIO $ YamlException "Yaml out of memory"
516 $ withCString file $ \file' -> withCString "r" $ \r' ->
520 c_fclose_helper file'
521 c_yaml_parser_delete ptr
523 throwIO $ YamlException
524 $ "Yaml file not found: " ++ file
526 c_yaml_parser_set_input_file ptr file'
528 cleanup (ptr, file') = do
529 c_fclose_helper file'
530 c_yaml_parser_delete ptr
533runParser :: MonadResource m => Parser
534#if MIN_VERSION_conduit(1, 0, 0)
540 e <- liftIO $ parserParseOne' parser
542 Left err -> liftIO $ throwIO err
543 Right Nothing -> return ()
544 Right (Just ev) -> yield ev >> runParser parser
546parserParseOne' :: Parser
547 -> IO (Either YamlException (Maybe Event))
548parserParseOne' parser = allocaBytes eventSize $ \er -> do
549 res <- liftIO $ c_yaml_parser_parse parser er
550 flip finally (c_yaml_event_delete er) $
553 problem <- makeString c_get_parser_error_problem parser
554 context <- makeString c_get_parser_error_context parser
555 index <- c_get_parser_error_index parser
556 line <- c_get_parser_error_line parser
557 column <- c_get_parser_error_column parser
558 let problemMark = YamlMark (fromIntegral index) (fromIntegral line) (fromIntegral column)
559 return $ Left $ YamlParseException problem context problemMark
560 else Right <$> getEvent er
562encode :: MonadResource m
563#if MIN_VERSION_conduit(1, 0, 0)
564 => Consumer Event m ByteString
566 => GSink Event m ByteString
569 runEmitter alloc close
572 fbuf <- mallocForeignPtrBytes bufferSize
573 withForeignPtr fbuf c_buffer_init
574 withForeignPtr fbuf $ c_my_emitter_set_output emitter
576 close _ fbuf = withForeignPtr fbuf $ \b -> do
577 ptr' <- c_get_buffer_buff b
578 len <- c_get_buffer_used b
579 fptr <- newForeignPtr_ $ castPtr ptr'
580 return $ B.fromForeignPtr fptr 0 $ fromIntegral len
582encodeFile :: MonadResource m
584#if MIN_VERSION_conduit(1, 0, 0)
585 -> Consumer Event m ()
590 bracketP getFile c_fclose $ \file -> runEmitter (alloc file) (\u _ -> return u)
593 file <- withCString filePath $
594 \filePath' -> withCString "w" $
595 \w' -> c_fopen filePath' w'
597 then throwIO $ YamlException $ "could not open file for write: " ++ filePath
600 alloc file emitter = c_yaml_emitter_set_output_file emitter file
602runEmitter :: MonadResource m
603 => (Emitter -> IO a) -- ^ alloc
604#if MIN_VERSION_conduit(1, 0, 0)
605 -> (() -> a -> IO b) -- ^ close
606 -> Consumer Event m b
608 -> (u -> a -> IO b) -- ^ close
609 -> Pipe l Event o u m b
611runEmitter allocI closeI =
612 bracketP alloc cleanup go
615 emitter <- mallocBytes emitterSize
616 res <- c_yaml_emitter_initialize emitter
617 when (res == 0) $ throwIO $ YamlException "c_yaml_emitter_initialize failed"
620 cleanup (emitter, _) = do
621 c_yaml_emitter_delete emitter
627#if MIN_VERSION_conduit(1, 0, 0)
628 loop = await >>= maybe (close ()) push
630 loop = awaitE >>= either close push
634 _ <- liftIO $ toEventRaw e $ c_yaml_emitter_emit emitter
636 close u = liftIO $ closeI u a
638-- | The pointer position
639data YamlMark = YamlMark { yamlIndex :: Int, yamlLine :: Int, yamlColumn :: Int }
642data YamlException = YamlException String
643 -- | problem, context, index, position line, position column
644 | YamlParseException { yamlProblem :: String, yamlContext :: String, yamlProblemMark :: YamlMark }
645 deriving (Show, Typeable)
646instance Exception YamlException
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list