packages feed

hydra-0.1.0: src/main/haskell/Hydra/Ext/Rdf/Serde.hs

-- | Serialize RDF using an approximation (because it does not yet support Unicode escape sequences) of the N-triples format

module Hydra.Ext.Rdf.Serde (
  rdfGraphToString,
) where

import qualified Hydra.Ext.Rdf.Syntax as Rdf
import Hydra.Util.Codetree.Script
import qualified Hydra.Util.Codetree.Ast as CT

import qualified Data.List as L
import qualified Data.Set as S


-- IRIREF ::= '<' ([^#x00-#x20<>"{}|^`\] | UCHAR)* '>'
-- TODO: Unicode escape sequences
escapeIriStr :: String -> String
escapeIriStr s = L.concat (esc <$> s)
  where
    esc c = if c >= '\128' || c <= '\32' || S.member c others
      then "?"
      else [c]
    others = S.fromList $ "<>\"{}|^`\\"

-- STRING_LITERAL_QUOTE ::= '"' ([^#x22#x5C#xA#xD] | ECHAR | UCHAR)* '"'
-- TODO: Unicode escape sequences
escapeLiteralString :: String -> String
escapeLiteralString s = L.concat (esc <$> s)
  where
    esc c = if c >= '\128'
      then "?"
      else case c of
        '\"' -> "\\\""
        '\\' -> "\\\\"
        '\n' -> "\\n"
        '\r' -> "\\r"
        _ -> [c]

rdfGraphToString :: Rdf.Graph -> String
rdfGraphToString = printExpr . writeGraph

writeBlankNode :: Rdf.BlankNode -> CT.Expr
writeBlankNode bnode = noSep [cst "_:", cst $ Rdf.unBlankNode bnode]

writeGraph :: Rdf.Graph -> CT.Expr
writeGraph g = newlineSep (writeTriple <$> (S.toList $ Rdf.unGraph g))

writeIri :: Rdf.Iri -> CT.Expr
writeIri iri = noSep [cst "<", cst $ escapeIriStr $ Rdf.unIri iri, cst ">"]

-- LANGTAG ::= '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)*
-- Note: we simply trust language tags to be valid
writeLanguageTag :: Rdf.LanguageTag -> CT.Expr
writeLanguageTag lang = noSep [cst "@", cst $ Rdf.unLanguageTag lang]

writeLiteral :: Rdf.Literal -> CT.Expr
writeLiteral lit = noSep [cst lex, suffix]
  where
    suffix = case Rdf.literalLanguageTag lit of
      Nothing -> noSep [cst "^^", writeIri dt]
      Just lang -> writeLanguageTag lang
    lex = "\"" ++ (escapeLiteralString $ Rdf.literalLexicalForm lit) ++ "\""
    dt = Rdf.literalDatatypeIri lit

writeNode :: Rdf.Node -> CT.Expr
writeNode n = case n of
  Rdf.NodeIri iri -> writeIri iri
  Rdf.NodeBnode bnode -> writeBlankNode bnode
  Rdf.NodeLiteral lit -> writeLiteral lit

writeResource :: Rdf.Resource -> CT.Expr
writeResource r = case r of
  Rdf.ResourceIri iri -> writeIri iri
  Rdf.ResourceBnode bnode -> writeBlankNode bnode

writeTriple :: Rdf.Triple -> CT.Expr
writeTriple t = spaceSep [
    writeResource $ Rdf.tripleSubject t,
    writeIri $ Rdf.triplePredicate t,
    writeNode $ Rdf.tripleObject t,
    cst "."]