1{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE ForeignFunctionInterface #-}
3{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE RankNTypes #-}
9{-# LANGUAGE TypeFamilies #-}
11-- | Low-level, streaming YAML interface. For a higher-level interface, see
14 ( -- * The event stream
20 -- * Encoding and decoding
30import Prelude hiding (pi)
32import Data.Bits ((.|.))
35import Foreign.ForeignPtr
36#if MIN_VERSION_base(4,7,0)
37import Foreign.ForeignPtr.Unsafe
39import Foreign.Marshal.Alloc
40import qualified System.Posix.Internals as Posix
42#if !MIN_VERSION_base(4,8,0)
43import Control.Applicative
45import Control.Exception (mask_, throwIO, Exception, finally)
47import Control.Monad.IO.Class
48import Control.Monad.Trans.Resource
49import Data.Conduit hiding (Source, Sink, Conduit)
52import Data.ByteString (ByteString, packCStringLen)
53import qualified Data.ByteString
54import qualified Data.ByteString.Char8 as B8
55import qualified Data.ByteString.Internal as B
56import qualified Data.ByteString.Unsafe as BU
63 | EventAlias !AnchorName
64 | EventScalar !ByteString !Tag !Style !Anchor
65 | EventSequenceStart !Anchor
67 | EventMappingStart !Anchor
78 deriving (Show, Read, Eq, Enum, Bounded, Ord, Data, Typeable)
90 deriving (Show, Eq, Read, Data, Typeable)
92type AnchorName = String
93type Anchor = Maybe AnchorName
95tagToString :: Tag -> String
96tagToString StrTag = "tag:yaml.org,2002:str"
97tagToString FloatTag = "tag:yaml.org,2002:float"
98tagToString NullTag = "tag:yaml.org,2002:null"
99tagToString BoolTag = "tag:yaml.org,2002:bool"
100tagToString SetTag = "tag:yaml.org,2002:set"
101tagToString IntTag = "tag:yaml.org,2002:int"
102tagToString SeqTag = "tag:yaml.org,2002:seq"
103tagToString MapTag = "tag:yaml.org,2002:map"
104tagToString (UriTag s) = s
105tagToString NoTag = ""
107bsToTag :: ByteString -> Tag
108bsToTag = stringToTag . B8.unpack
110stringToTag :: String -> Tag
111stringToTag "tag:yaml.org,2002:str" = StrTag
112stringToTag "tag:yaml.org,2002:float" = FloatTag
113stringToTag "tag:yaml.org,2002:null" = NullTag
114stringToTag "tag:yaml.org,2002:bool" = BoolTag
115stringToTag "tag:yaml.org,2002:set" = SetTag
116stringToTag "tag:yaml.org,2002:int" = IntTag
117stringToTag "tag:yaml.org,2002:seq" = SeqTag
118stringToTag "tag:yaml.org,2002:map" = MapTag
119stringToTag "" = NoTag
120stringToTag s = UriTag s
123type Parser = Ptr ParserStruct
128type EventRaw = Ptr EventRawStruct
132foreign import ccall unsafe "yaml_parser_initialize"
133 c_yaml_parser_initialize :: Parser -> IO CInt
135foreign import ccall unsafe "yaml_parser_delete"
136 c_yaml_parser_delete :: Parser -> IO ()
138foreign import ccall unsafe "yaml_parser_set_input_string"
139 c_yaml_parser_set_input_string :: Parser
144foreign import ccall unsafe "yaml_parser_set_input_file"
145 c_yaml_parser_set_input_file :: Parser
150type File = Ptr FileStruct
153foreign import ccall unsafe "_fdopen"
155foreign import ccall unsafe "fdopen"
160foreign import ccall unsafe "fclose"
164foreign import ccall unsafe "fclose_helper"
165 c_fclose_helper :: File -> IO ()
167foreign import ccall unsafe "yaml_parser_parse"
168 c_yaml_parser_parse :: Parser -> EventRaw -> IO CInt
170foreign import ccall unsafe "yaml_event_delete"
171 c_yaml_event_delete :: EventRaw -> IO ()
173foreign import ccall "get_parser_error_problem"
174 c_get_parser_error_problem :: Parser -> IO (Ptr CUChar)
176foreign import ccall "get_parser_error_context"
177 c_get_parser_error_context :: Parser -> IO (Ptr CUChar)
179foreign import ccall unsafe "get_parser_error_index"
180 c_get_parser_error_index :: Parser -> IO CULong
182foreign import ccall unsafe "get_parser_error_line"
183 c_get_parser_error_line :: Parser -> IO CULong
185foreign import ccall unsafe "get_parser_error_column"
186 c_get_parser_error_column :: Parser -> IO CULong
188makeString :: MonadIO m => (a -> m (Ptr CUChar)) -> a -> m String
190 cchar <- castPtr `liftM` f a
193 else liftIO $ peekCString cchar
195data EventType = YamlNoEvent
196 | YamlStreamStartEvent
198 | YamlDocumentStartEvent
199 | YamlDocumentEndEvent
202 | YamlSequenceStartEvent
203 | YamlSequenceEndEvent
204 | YamlMappingStartEvent
205 | YamlMappingEndEvent
208foreign import ccall unsafe "get_event_type"
209 c_get_event_type :: EventRaw -> IO CInt
211foreign import ccall unsafe "get_scalar_value"
212 c_get_scalar_value :: EventRaw -> IO (Ptr CUChar)
214foreign import ccall unsafe "get_scalar_length"
215 c_get_scalar_length :: EventRaw -> IO CULong
217foreign import ccall unsafe "get_scalar_tag"
218 c_get_scalar_tag :: EventRaw -> IO (Ptr CUChar)
220foreign import ccall unsafe "get_scalar_tag_len"
221 c_get_scalar_tag_len :: EventRaw -> IO CULong
223foreign import ccall unsafe "get_scalar_style"
224 c_get_scalar_style :: EventRaw -> IO CInt
226foreign import ccall unsafe "get_scalar_anchor"
227 c_get_scalar_anchor :: EventRaw -> IO CString
229foreign import ccall unsafe "get_sequence_start_anchor"
230 c_get_sequence_start_anchor :: EventRaw -> IO CString
232foreign import ccall unsafe "get_mapping_start_anchor"
233 c_get_mapping_start_anchor :: EventRaw -> IO CString
235foreign import ccall unsafe "get_alias_anchor"
236 c_get_alias_anchor :: EventRaw -> IO CString
238getEvent :: EventRaw -> IO (Maybe Event)
240 et <- c_get_event_type er
241 case toEnum $ fromEnum et of
242 YamlNoEvent -> return Nothing
243 YamlStreamStartEvent -> return $ Just EventStreamStart
244 YamlStreamEndEvent -> return $ Just EventStreamEnd
245 YamlDocumentStartEvent -> return $ Just EventDocumentStart
246 YamlDocumentEndEvent -> return $ Just EventDocumentEnd
248 yanchor <- c_get_alias_anchor er
249 anchor <- if yanchor == nullPtr
250 then error "got YamlAliasEvent with empty anchor"
251 else peekCString yanchor
252 return $ Just $ EventAlias anchor
253 YamlScalarEvent -> do
254 yvalue <- c_get_scalar_value er
255 ylen <- c_get_scalar_length er
256 ytag <- c_get_scalar_tag er
257 ytag_len <- c_get_scalar_tag_len er
258 ystyle <- c_get_scalar_style er
259 let ytag_len' = fromEnum ytag_len
260 let yvalue' = castPtr yvalue
261 let ytag' = castPtr ytag
262 let ylen' = fromEnum ylen
263 bs <- packCStringLen (yvalue', ylen')
266 then return Data.ByteString.empty
267 else packCStringLen (ytag', ytag_len')
268 let style = toEnum $ fromEnum ystyle
269 yanchor <- c_get_scalar_anchor er
270 anchor <- if yanchor == nullPtr
272 else Just <$> peekCString yanchor
273 return $ Just $ EventScalar bs (bsToTag tagbs) style anchor
274 YamlSequenceStartEvent -> do
275 yanchor <- c_get_sequence_start_anchor er
276 anchor <- if yanchor == nullPtr
278 else Just <$> peekCString yanchor
279 return $ Just $ EventSequenceStart anchor
280 YamlSequenceEndEvent -> return $ Just EventSequenceEnd
281 YamlMappingStartEvent -> do
282 yanchor <- c_get_mapping_start_anchor er
283 anchor <- if yanchor == nullPtr
285 else Just <$> peekCString yanchor
286 return $ Just $ EventMappingStart anchor
287 YamlMappingEndEvent -> return $ Just EventMappingEnd
292type Emitter = Ptr EmitterStruct
296foreign import ccall unsafe "yaml_emitter_initialize"
297 c_yaml_emitter_initialize :: Emitter -> IO CInt
299foreign import ccall unsafe "yaml_emitter_delete"
300 c_yaml_emitter_delete :: Emitter -> IO ()
303type Buffer = Ptr BufferStruct
307foreign import ccall unsafe "buffer_init"
308 c_buffer_init :: Buffer -> IO ()
310foreign import ccall unsafe "get_buffer_buff"
311 c_get_buffer_buff :: Buffer -> IO (Ptr CUChar)
313foreign import ccall unsafe "get_buffer_used"
314 c_get_buffer_used :: Buffer -> IO CULong
316foreign import ccall unsafe "my_emitter_set_output"
317 c_my_emitter_set_output :: Emitter -> Buffer -> IO ()
319#ifndef __NO_UNICODE__
320foreign import ccall unsafe "yaml_emitter_set_unicode"
321 c_yaml_emitter_set_unicode :: Emitter -> CInt -> IO ()
324foreign import ccall unsafe "yaml_emitter_set_output_file"
325 c_yaml_emitter_set_output_file :: Emitter -> File -> IO ()
327foreign import ccall unsafe "yaml_emitter_emit"
328 c_yaml_emitter_emit :: Emitter -> EventRaw -> IO CInt
330foreign import ccall unsafe "yaml_stream_start_event_initialize"
331 c_yaml_stream_start_event_initialize :: EventRaw -> CInt -> IO CInt
333foreign import ccall unsafe "yaml_stream_end_event_initialize"
334 c_yaml_stream_end_event_initialize :: EventRaw -> IO CInt
336foreign import ccall unsafe "yaml_scalar_event_initialize"
337 c_yaml_scalar_event_initialize
339 -> Ptr CUChar -- anchor
341 -> Ptr CUChar -- value
343 -> CInt -- plain_implicit
344 -> CInt -- quoted_implicit
348foreign import ccall unsafe "simple_document_start"
349 c_simple_document_start :: EventRaw -> IO CInt
351foreign import ccall unsafe "yaml_document_end_event_initialize"
352 c_yaml_document_end_event_initialize :: EventRaw -> CInt -> IO CInt
354foreign import ccall unsafe "yaml_sequence_start_event_initialize"
355 c_yaml_sequence_start_event_initialize
363foreign import ccall unsafe "yaml_sequence_end_event_initialize"
364 c_yaml_sequence_end_event_initialize :: EventRaw -> IO CInt
366foreign import ccall unsafe "yaml_mapping_start_event_initialize"
367 c_yaml_mapping_start_event_initialize
375foreign import ccall unsafe "yaml_mapping_end_event_initialize"
376 c_yaml_mapping_end_event_initialize :: EventRaw -> IO CInt
378foreign import ccall unsafe "yaml_alias_event_initialize"
379 c_yaml_alias_event_initialize
384toEventRaw :: Event -> (EventRaw -> IO a) -> IO a
385toEventRaw e f = allocaBytes eventSize $ \er -> do
388 c_yaml_stream_start_event_initialize
390 0 -- YAML_ANY_ENCODING
392 c_yaml_stream_end_event_initialize er
393 EventDocumentStart ->
394 c_simple_document_start er
396 c_yaml_document_end_event_initialize er 1
397 EventScalar bs thetag style0 anchor -> do
398 BU.unsafeUseAsCStringLen bs $ \(value, len) -> do
399 let value' = castPtr value :: Ptr CUChar
400 len' = fromIntegral len :: CInt
401 let thetag' = tagToString thetag
402 withCString thetag' $ \tag' -> do
405 PlainNoTag -> (1, Plain)
407 style' = toEnum $ fromEnum style
409 qi = if null thetag' then 1 else 0
412 c_yaml_scalar_event_initialize
419 qi -- quoted_implicit
422 withCString anchor' $ \anchorP' -> do
423 let anchorP = castPtr anchorP'
424 c_yaml_scalar_event_initialize
431 qi -- quoted_implicit
433 EventSequenceStart Nothing ->
434 c_yaml_sequence_start_event_initialize
439 0 -- YAML_ANY_SEQUENCE_STYLE
440 EventSequenceStart (Just anchor) ->
441 withCString anchor $ \anchor' -> do
442 let anchorP = castPtr anchor'
443 c_yaml_sequence_start_event_initialize
448 0 -- YAML_ANY_SEQUENCE_STYLE
450 c_yaml_sequence_end_event_initialize er
451 EventMappingStart Nothing ->
452 c_yaml_mapping_start_event_initialize
457 0 -- YAML_ANY_SEQUENCE_STYLE
458 EventMappingStart (Just anchor) ->
459 withCString anchor $ \anchor' -> do
460 let anchorP = castPtr anchor'
461 c_yaml_mapping_start_event_initialize
466 0 -- YAML_ANY_SEQUENCE_STYLE
468 c_yaml_mapping_end_event_initialize er
470 withCString anchor $ \anchorP' -> do
471 let anchorP = castPtr anchorP'
472 c_yaml_alias_event_initialize
475 unless (ret == 1) $ throwIO $ ToEventRawException ret
478newtype ToEventRawException = ToEventRawException CInt
479 deriving (Show, Typeable)
480instance Exception ToEventRawException
482decode :: MonadResource m => B.ByteString
483#if MIN_VERSION_conduit(1, 0, 0)
488decode bs | B8.null bs = return ()
490 bracketP alloc cleanup (runParser . fst)
493 ptr <- mallocBytes parserSize
494 res <- c_yaml_parser_initialize ptr
497 c_yaml_parser_delete ptr
499 throwIO $ YamlException "Yaml out of memory"
501 let (bsfptr, offset, len) = B.toForeignPtr bs
502 let bsptrOrig = unsafeForeignPtrToPtr bsfptr
503 let bsptr = castPtr bsptrOrig `plusPtr` offset
504 c_yaml_parser_set_input_string ptr bsptr (fromIntegral len)
506 cleanup (ptr, bsfptr) = do
507 touchForeignPtr bsfptr
508 c_yaml_parser_delete ptr
511-- XXX copied from GHC.IO.FD
512std_flags, read_flags, output_flags, write_flags :: CInt
513std_flags = Posix.o_NOCTTY
514output_flags = std_flags .|. Posix.o_CREAT .|. Posix.o_TRUNC
515read_flags = std_flags .|. Posix.o_RDONLY
516write_flags = output_flags .|. Posix.o_WRONLY
518-- | Open a C FILE* from a file path, using internal GHC API to work correctly
519-- on all platforms, even on non-ASCII filenames. The opening mode must be
520-- indicated via both 'rawOpenFlags' and 'openMode'.
521openFile :: FilePath -> CInt -> String -> IO File
522openFile file rawOpenFlags openMode = do
523 fd <- liftIO $ Posix.withFilePath file $ \file' ->
524 Posix.c_open file' rawOpenFlags 0o666
526 then withCString openMode $ \openMode' -> c_fdopen fd openMode'
529decodeFile :: MonadResource m => FilePath
530#if MIN_VERSION_conduit(1, 0, 0)
536 bracketP alloc cleanup (runParser . fst)
539 ptr <- mallocBytes parserSize
540 res <- c_yaml_parser_initialize ptr
543 c_yaml_parser_delete ptr
545 throwIO $ YamlException "Yaml out of memory"
547 file' <- openFile file read_flags "r"
550 c_yaml_parser_delete ptr
552 throwIO $ YamlException
553 $ "Yaml file not found: " ++ file
555 c_yaml_parser_set_input_file ptr file'
557 cleanup (ptr, file') = do
558 c_fclose_helper file'
559 c_yaml_parser_delete ptr
562runParser :: MonadResource m => Parser
563#if MIN_VERSION_conduit(1, 0, 0)
569 e <- liftIO $ parserParseOne' parser
571 Left err -> liftIO $ throwIO err
572 Right Nothing -> return ()
573 Right (Just ev) -> yield ev >> runParser parser
575parserParseOne' :: Parser
576 -> IO (Either YamlException (Maybe Event))
577parserParseOne' parser = allocaBytes eventSize $ \er -> do
578 res <- liftIO $ c_yaml_parser_parse parser er
579 flip finally (c_yaml_event_delete er) $
582 problem <- makeString c_get_parser_error_problem parser
583 context <- makeString c_get_parser_error_context parser
584 index <- c_get_parser_error_index parser
585 line <- c_get_parser_error_line parser
586 column <- c_get_parser_error_column parser
587 let problemMark = YamlMark (fromIntegral index) (fromIntegral line) (fromIntegral column)
588 return $ Left $ YamlParseException problem context problemMark
589 else Right <$> getEvent er
591encode :: MonadResource m
592#if MIN_VERSION_conduit(1, 0, 0)
593 => Consumer Event m ByteString
595 => GSink Event m ByteString
598 runEmitter alloc close
601 fbuf <- mallocForeignPtrBytes bufferSize
602 withForeignPtr fbuf c_buffer_init
603 withForeignPtr fbuf $ c_my_emitter_set_output emitter
605 close _ fbuf = withForeignPtr fbuf $ \b -> do
606 ptr' <- c_get_buffer_buff b
607 len <- c_get_buffer_used b
608 fptr <- newForeignPtr_ $ castPtr ptr'
609 return $ B.fromForeignPtr fptr 0 $ fromIntegral len
611encodeFile :: MonadResource m
613#if MIN_VERSION_conduit(1, 0, 0)
614 -> Consumer Event m ()
619 bracketP getFile c_fclose $ \file -> runEmitter (alloc file) (\u _ -> return u)
622 file <- openFile filePath write_flags "w"
624 then throwIO $ YamlException $ "could not open file for write: " ++ filePath
627 alloc file emitter = c_yaml_emitter_set_output_file emitter file
629runEmitter :: MonadResource m
630 => (Emitter -> IO a) -- ^ alloc
631#if MIN_VERSION_conduit(1, 0, 0)
632 -> (() -> a -> IO b) -- ^ close
633 -> Consumer Event m b
635 -> (u -> a -> IO b) -- ^ close
636 -> Pipe l Event o u m b
638runEmitter allocI closeI =
639 bracketP alloc cleanup go
642 emitter <- mallocBytes emitterSize
643 res <- c_yaml_emitter_initialize emitter
644 when (res == 0) $ throwIO $ YamlException "c_yaml_emitter_initialize failed"
645#ifndef __NO_UNICODE__
646 c_yaml_emitter_set_unicode emitter 1
650 cleanup (emitter, _) = do
651 c_yaml_emitter_delete emitter
657#if MIN_VERSION_conduit(1, 0, 0)
658 loop = await >>= maybe (close ()) push
660 loop = awaitE >>= either close push
664 _ <- liftIO $ toEventRaw e $ c_yaml_emitter_emit emitter
666 close u = liftIO $ closeI u a
668-- | The pointer position
669data YamlMark = YamlMark { yamlIndex :: Int, yamlLine :: Int, yamlColumn :: Int }
672data YamlException = YamlException String
673 -- | problem, context, index, position line, position column
674 | YamlParseException { yamlProblem :: String, yamlContext :: String, yamlProblemMark :: YamlMark }
675 deriving (Show, Typeable)
676instance 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