yaml-0.0.0: Text/Libyaml.chs
{-# LANGUAGE ForeignFunctionInterface #-}
module Text.Libyaml
( Event (..)
, parserParse
, withParser
, emitEvents
, withEmitter
) where
#include <yaml.h>
#include "../../../c/helper.h"
import C2HS
import qualified Data.ByteString.Internal as B
import Control.Monad
{#context lib="yaml" #}
{#pointer *yaml_parser_t as Parser newtype#}
parserSize :: Int
parserSize = {#sizeof yaml_parser_t#}
{#pointer *yaml_event_t as EventRaw newtype#}
eventSize :: Int
eventSize = {#sizeof yaml_event_t#}
{#fun unsafe yaml_parser_initialize
{ id `Parser'
} -> `Int' #}
{#fun unsafe yaml_parser_delete
{ id `Parser'
} -> `()' #}
withParser :: B.ByteString -> (Parser -> IO a) -> IO a
withParser bs f = do
allocaBytes parserSize $ \p ->
do
let p' = Parser p
_res <- yaml_parser_initialize p'
-- FIXME check res
let (fptr, offset, len) = B.toForeignPtr bs
ret <- withForeignPtr fptr $ \ptr ->
do
let ptr' = castPtr ptr `plusPtr` offset
yaml_parser_set_input_string p' ptr' len
f p'
yaml_parser_delete p'
return ret
withEventRaw :: (EventRaw -> IO a) -> IO a
withEventRaw f = allocaBytes eventSize $ f . EventRaw
{#fun unsafe yaml_parser_set_input_string
{ id `Parser'
, id `Ptr CUChar'
, `Int'
} -> `()' #}
{#fun unsafe yaml_parser_parse
{ id `Parser'
, id `EventRaw'
} -> `Int' #}
{#fun unsafe yaml_event_delete
{ id `EventRaw'
} -> `()' #}
parserParse :: Parser -> IO [Event]
parserParse parser = do
event <- parserParseOne parser
case event of
EventStreamEnd -> return [event]
_ -> do
rest <- parserParse parser
return $! event : rest
{#fun unsafe print_parser_error
{ id `Parser'
} -> `()' #}
parserParseOne :: Parser -> IO Event
parserParseOne parser = withEventRaw $ \er -> do
res <- yaml_parser_parse parser er
when (res == 0) $
print_parser_error parser >> fail "yaml_parser_parse failed"
event <- getEvent er
yaml_event_delete er
return event
data Event =
EventNone
| EventStreamStart
| EventStreamEnd
| EventDocumentStart
| EventDocumentEnd
| EventAlias
| EventScalar B.ByteString
| EventSequenceStart
| EventSequenceEnd
| EventMappingStart
| EventMappingEnd
deriving (Show)
{#enum yaml_event_type_e as EventType {underscoreToCase} deriving (Show) #}
getEvent :: EventRaw -> IO Event
getEvent (EventRaw ptr') = do
et <- {#get yaml_event_t.type#} ptr'
case toEnum $ fromEnum et of
YamlNoEvent -> return EventNone
YamlStreamStartEvent -> return EventStreamStart
YamlStreamEndEvent -> return EventStreamEnd
YamlDocumentStartEvent -> return EventDocumentStart
YamlDocumentEndEvent -> return EventDocumentEnd
YamlAliasEvent -> return EventAlias
YamlScalarEvent -> do
yvalue <- {#get yaml_event_t.data.scalar.value#} ptr'
ylen <- {#get yaml_event_t.data.scalar.length#} ptr'
let yvalue' = castPtr yvalue
let ylen' = fromEnum ylen
let ylen'' = toEnum $ fromEnum ylen
bs <- B.create ylen' $ \dest -> B.memcpy dest yvalue' ylen''
return $ EventScalar bs
YamlSequenceStartEvent -> return EventSequenceStart
YamlSequenceEndEvent -> return EventSequenceEnd
YamlMappingStartEvent -> return EventMappingStart
YamlMappingEndEvent -> return EventMappingEnd
-- Emitter
{#pointer *yaml_emitter_t as Emitter newtype#}
emitterSize :: Int
emitterSize = {#sizeof yaml_emitter_t#}
{#fun unsafe yaml_emitter_initialize
{ id `Emitter'
} -> `Int' #}
{#fun unsafe yaml_emitter_delete
{ id `Emitter'
} -> `()' #}
{#pointer *buffer_t as Buffer newtype#}
bufferSize :: Int
bufferSize = {#sizeof buffer_t#}
{#fun unsafe buffer_init
{ id `Buffer'
} -> `()' #}
withBuffer :: (Buffer -> IO ()) -> IO B.ByteString
withBuffer f = do
allocaBytes bufferSize $ \b -> do
let b' = Buffer b
buffer_init b'
f b'
ptr' <- {#get buffer_t.buff#} b
len <- {#get buffer_t.used#} b
fptr <- newForeignPtr_ $ castPtr ptr'
return $! B.fromForeignPtr fptr 0 $ fromIntegral len
{#fun unsafe yaml_emitter_set_output
{ id `Emitter'
, id `FunPtr (Ptr () -> Ptr CUChar -> CULong -> IO CInt)'
, id `Ptr ()'
} -> `()' #}
foreign import ccall "buffer.h &buffer_append"
buffer_append :: FunPtr (Ptr () -> Ptr CUChar -> CULong -> IO CInt)
withEmitter :: (Emitter -> IO ()) -> IO B.ByteString
withEmitter f = do
allocaBytes emitterSize $ \e -> do
let e' = Emitter e
_res <- yaml_emitter_initialize e'
-- FIXME check res
bs <- withBuffer $ \(Buffer b) -> do
let fun = buffer_append
yaml_emitter_set_output e' buffer_append $ castPtr b
f e'
yaml_emitter_delete e'
return bs
{#fun unsafe yaml_emitter_emit
{ id `Emitter'
, id `EventRaw'
} -> `Int' #}
{#fun unsafe print_emitter_error
{ id `Emitter'
} -> `()' #}
emitEvents :: Emitter -> [Event] -> IO ()
emitEvents _ [] = return ()
emitEvents emitter (e:rest) = do
res <- toEventRaw e $ yaml_emitter_emit emitter
when (res == 0) $
print_emitter_error emitter >> fail "yaml_emitter_emit failed"
emitEvents emitter rest
{#fun unsafe yaml_stream_start_event_initialize
{ id `EventRaw'
, `Int'
} -> `Int' #}
{#fun unsafe yaml_stream_end_event_initialize
{ id `EventRaw'
} -> `Int' #}
{#fun unsafe yaml_scalar_event_initialize
{ id `EventRaw'
, id `Ptr CUChar' -- anchor
, id `Ptr CUChar' -- tag
, id `Ptr CUChar' -- value
, `Int'
, `Int'
, `Int'
, `Int'
} -> `Int' #}
{#fun unsafe simple_document_start
{ id `EventRaw'
} -> `Int' #}
{#fun unsafe yaml_document_end_event_initialize
{ id `EventRaw'
, `Int'
} -> `Int' #}
{#fun unsafe yaml_sequence_start_event_initialize
{ id `EventRaw'
, id `Ptr CUChar'
, id `Ptr CUChar'
, `Int'
, `Int'
} -> `Int' #}
{#fun unsafe yaml_sequence_end_event_initialize
{ id `EventRaw'
} -> `Int' #}
{#fun unsafe yaml_mapping_start_event_initialize
{ id `EventRaw'
, id `Ptr CUChar'
, id `Ptr CUChar'
, `Int'
, `Int'
} -> `Int' #}
{#fun unsafe yaml_mapping_end_event_initialize
{ id `EventRaw'
} -> `Int' #}
toEventRaw :: Event -> (EventRaw -> IO a) -> IO a
toEventRaw e f = withEventRaw $ \er -> do
case e of
EventStreamStart ->
yaml_stream_start_event_initialize
er
0 -- YAML_ANY_ENCODING
EventStreamEnd ->
yaml_stream_end_event_initialize er
EventDocumentStart ->
simple_document_start er
EventDocumentEnd ->
yaml_document_end_event_initialize er 1
EventScalar bs -> do
let (fvalue, offset, len) = B.toForeignPtr bs
withForeignPtr fvalue $ \value -> do
let value' = value `plusPtr` offset
yaml_scalar_event_initialize
er
nullPtr
nullPtr
value'
len
0
1
0 -- YAML_ANY_SCALAR_STYLE
EventSequenceStart ->
yaml_sequence_start_event_initialize
er
nullPtr
nullPtr
1
0 -- YAML_ANY_SEQUENCE_STYLE
EventSequenceEnd ->
yaml_sequence_end_event_initialize er
EventMappingStart ->
yaml_mapping_start_event_initialize
er
nullPtr
nullPtr
1
0 -- YAML_ANY_SEQUENCE_STYLE
EventMappingEnd ->
yaml_mapping_end_event_initialize er
EventAlias -> error "toEventRaw: EventAlias not supported"
EventNone -> error "toEventRaw: EventNone not supported"
f er