1:- encoding(utf8).
    2:- module(
    3  rdf_print,
    4  [
    5    rdf_dcg_node//1,      % +Node
    6    rdf_dcg_node//2,      % +Node, +Options
    7    rdf_dcg_predicate//1, % +Predicate
    8    rdf_dcg_predicate//2, % +Predicate, +Options
    9    rdf_dcg_triples//1,   % +Triples
   10    rdf_dcg_triples//2    % +Triples, +Options
   11  ]
   12).

RDF printing

KeyValueDefaultDescription
indentnonneg0The number of spaces for the outer
indentation level.
max_lengthnonneginfThe maximum length of an RDF term.
prefix2aliasassoc(iri,atom))A custom list of prefix/IRI
mappings that overrules and/or
extends the prefix declarations.

@note RDF prefix expansion does not work in DCGs.

*/

   29:- use_module(library(aggregate)).   30:- use_module(library(apply)).   31:- use_module(library(clpfd)).   32:- use_module(library(error)).   33:- use_module(library(lists)).   34:- use_module(library(solution_sequences)).   35
   36:- use_module(library(abnf)).   37:- use_module(library(atom_ext)).   38:- use_module(library(call_ext)).   39:- use_module(library(dcg)).   40:- use_module(library(dict)).   41:- use_module(library(rdf_prefix)).   42:- use_module(library(rdf_term)).   43
   44:- rdf_meta
   45   rdf_dcg_literal(t, +, ?, ?),   rdf_dcg_node(o, ?, ?),   rdf_dcg_node(o, +, ?, ?),   rdf_dcg_predicate(r, ?, ?),   rdf_dcg_predicate(r, +, ?, ?),   rdf_dcg_triples(t, ?, ?),   rdf_dcg_triples(t, +, ?, ?).
 rdf_dcg_bnode(+BNode:rdf_bnode, +Options:options)// is det
Arguments:
Options- The following options are supported:
  • max_length(+or([positive_integer,oneof([inf])]))
   61rdf_dcg_bnode(BNode, Options) -->
   62  {dict_get(max_length, Options, inf, Max)},
   63  "_:",
   64  ellipsis(BNode, Max).
   65
   66
   67
   68%! rdf_dcg_iri(+Iri:iri, +Options:options)// is det.
   69%
   70% @param Options The following options are supported:
   71%
   72%        * max_length(+or([positive_integer,oneof([inf])]))
   73%
   74%        * prefix2alias(+assoc(iri,atom))
   75
   76% Abbreviated IRI notation.
   77rdf_dcg_iri(Iri, Options) -->
   78  {
   79    (   dict_get(prefix2alias, Options, Prefix2Alias)
   80    ->  % Abbreviated based on the prefix map specified in options.
   81        (   gen_assoc(Prefix, Prefix2Alias, Alias),
   82            atom_prefix(Iri, Prefix)
   83        ->  atom_concat(Prefix, Local, Iri)
   84        )
   85    ;   % Abbreviated based on the global prefix declarations.
   86        rdf_prefix_iri(Alias:Local, Iri),
   87        \+ sub_atom(Local, /)
   88    )
   89  }, !,
   90  {
   91    atom_length(Alias, AliasLength),
   92    Minus #= AliasLength + 1,
   93    dict_get(max_length, Options, inf, Length),
   94    (   Length == inf
   95    ->  Max = inf
   96    ;   Length =< Minus
   97    ->  Max = Length
   98    ;   Max = Length - Minus
   99    )
  100  },
  101  atom(Alias),
  102  ":",
  103  ellipsis(Local, Max).
  104% Full IRI notation.
  105rdf_dcg_iri(Iri, Options) -->
  106  {dict_get(max_length, Options, inf, Max)},
  107  "<",
  108  ellipsis(Iri, Max),
  109  ">".
 rdf_dcg_lexical_form(+LexicalForm:atom, +Options:options)// is det
Arguments:
Options- The following options are supported:
  • max_length(+or([positive_integer,oneof([inf])]))
  119rdf_dcg_lexical_form(Lex, Options) -->
  120  {
  121    dict_get(max_length, Options, inf, Length),
  122    extra_quotes_(Lex, ExtraQuotes)
  123  },
  124  ({ExtraQuotes == true} -> "\"\"\"" ; "\""),
  125  ellipsis(Lex, Length),
  126  ({ExtraQuotes == true} -> "\"\"\"" ; "\"").
  127
  128lex_([0'\\|T]) --> !,
  129  "\\\\",
  130  lex_(T).
  131lex_([H|T]) --> !,
  132  [H],
  133  lex_(T).
  134lex_([]) --> "".
  135
  136extra_quotes_(Atom, true) :-
  137  atom_codes(Atom, Codes),
  138  member(Code, [0'",0'\n]),%"
  139  memberchk(Code, Codes), !.
  140extra_quotes_(_, false).
 rdf_dcg_litteral(+Litteral:rdf_literal, +Options:options)// is det
Arguments:
Options- are passed to rdf_dcg_iri//2 and rdf_dcg_lexical_form//2.
  149% Language-tagged string.
  150rdf_dcg_literal(literal(lang(LTag,Lex)), Options) --> !,
  151  rdf_dcg_lexical_form(Lex, Options),
  152  "@",
  153  atom(LTag).
  154% xsd:boolean, xsd:decimal, xsd:float, xsd:integer
  155rdf_dcg_literal(literal(type(D,Lex)), _) -->
  156  {
  157    rdf_prefix_memberchk(
  158      D,
  159      [xsd:boolean,xsd:decimal,xsd:float,xsd:integer]
  160    )
  161  }, !,
  162  atom(Lex).
  163% xsd:string
  164rdf_dcg_literal(literal(type(D,Lex)), Options) -->
  165  {
  166    rdf_equal(D, xsd:string), !,
  167    rdf_canonical_lexical_form(D, Lex, CanonicalLex)
  168  },
  169  rdf_dcg_lexical_form(CanonicalLex, Options).
  170% Typed literal without abbreviated notation.
  171rdf_dcg_literal(literal(type(D,Lex)), Options) -->
  172  rdf_dcg_lexical_form(Lex, Options),
  173  "^^",
  174  rdf_dcg_iri(D, Options).
 rdf_dcg_node(+Node:rdf_node)// is det
 rdf_dcg_node(+Node:rdf_node, +Options:options)// is det
Arguments:
Options- are passed to rdf_dcg_bnode//2, rdf_dcg_iri//2 and rdf_dcg_literal//2.
  184rdf_dcg_node(Node) -->
  185  rdf_dcg_node(Node, options{}).
  186
  187
  188rdf_dcg_node(Var, _) -->
  189  {var(Var)}, !,
  190  {instantiation_error(Var)}.
  191% Blank nodes.
  192rdf_dcg_node(BNode, Options) -->
  193  {rdf_is_bnode(BNode)}, !,
  194  rdf_dcg_bnode(BNode, Options).
  195% Literals.
  196rdf_dcg_node(Literal, Options) -->
  197  {rdf_is_literal(Literal)}, !,
  198  rdf_dcg_literal(Literal, Options).
  199% IRIs that are not well-known IRIs.
  200rdf_dcg_node(Iri, Options) -->
  201  {rdf_is_iri(Iri)}, !,
  202  rdf_dcg_iri(Iri, Options).
  203% Syntax error.
  204rdf_dcg_node(Term, _) -->
  205  {syntax_error(rdf_term(Term))}.
 rdf_dcg_predicate(+Predicate:iri)// is det
 rdf_dcg_predicate(+Predicate:iri, +Options:options)// is det
Arguments:
Options- are passed to rdf_dcg_iri//2.
  214rdf_dcg_predicate(P) -->
  215  rdf_dcg_predicate(P, options{}).
  216
  217
  218rdf_dcg_predicate(P, _) -->
  219  {rdf_equal(P, rdf:type)}, !,
  220  "a".
  221rdf_dcg_predicate(Iri, Options) -->
  222  rdf_dcg_iri(Iri, Options).
 rdf_dcg_triples(+Triples:list(tp))// is det
 rdf_dcg_triples(+Triples:list(tp), +Options:options)// is det
Prints the given triples, using the abbreviations defined in Turtle 1.1.
See also
- https://www.w3.org/TR/turtle/
  234rdf_dcg_triples(Triples) -->
  235  rdf_dcg_triples(Triples, options{}).
  236
  237
  238rdf_dcg_triples(Triples, Options) -->
  239  {dict_get(graph, Options, _NoGraphName, GraphName)},
  240  rdf_dcg_prefixes(Triples),
  241  rdf_dcg_groups0([GraphName-Triples], Options).
  242
  243rdf_dcg_prefixes(Triples) -->
  244  {
  245    aggregate_all(
  246      set(Alias-Prefix),
  247      (
  248        member(tp(S,P,O), Triples),
  249        member(Term, [S,P,O]),
  250        term_iri_(Term, Iri),
  251        rdf_prefix_iri(Alias, _, Iri),
  252        rdf_prefix(Alias, Prefix)
  253      ),
  254      Pairs
  255    )
  256  },
  257  '*'(rdf_dcg_prefix, Pairs), !,
  258  ({Pairs == []} -> "" ; "\n").
  259
  260term_iri_(Iri, Iri) :-
  261  rdf_is_iri(Iri), !.
  262term_iri_(Literal, Iri) :-
  263  rdf_literal_datatype_iri(Literal, Iri).
  264
  265rdf_dcg_prefix(Alias-Prefix) -->
  266  "prefix ",
  267  atom(Alias),
  268  ": <",
  269  atom(Prefix),
  270  ">\n".
  271
  272rdf_dcg_groups0([], _) --> !, "".
  273rdf_dcg_groups0([G-Triples|Groups], Options) -->
  274  {dict_get(indent, Options, 0, I1)},
  275  (   {var(G)}
  276  ->  {I2 = I1}
  277  ;   tab(I1),
  278      rdf_dcg_node(G, Options),
  279      " {\n",
  280      {I2 = I1 + 2}
  281  ),
  282  rdf_dcg_triples0(I2, Triples, Options),
  283  ({var(G)} -> "" ; "}\n"),
  284  rdf_dcg_groups0(Groups, Options).
  285
  286rdf_dcg_triples0(I, Triples, Options) -->
  287  {
  288    partition(is_skip_tp_(Triples), Triples, SkipTriples, NonSkipTriples),
  289    triples_to_groups0(NonSkipTriples, Groups)
  290  },
  291  rdf_dcg_subjects0(I, Groups, SkipTriples, Options).
 is_skip_tp_(+TP:tp) is semidet
Succeeds if TP should be skipped for the purposes of printing.
  297is_skip_tp_(Triples, tp(S,_,_)) :-
  298  ( % blank node subject term
  299    rdf_is_bnode(S)
  300  ; % well-known IRI subject term
  301    rdf_is_bnode_iri(S)
  302  ), !,
  303  memberchk(tp(_,_,S), Triples), !.
  304% RDF list
  305is_skip_tp_(_, tp(_,P,_)) :-
  306  rdf_prefix_memberchk(P, [rdf:first,rdf:rest]), !.
  307% blank node object term
  308is_skip_tp_(_, tp(_,_,O)) :-
  309  rdf_is_bnode(O).
 triples_to_groups0(+Triples:list(tp), -Groups:ordset(pair(rdf_node,ordset(pair(rdf_predicate,ordset(rdf_node)))))) is det
  314triples_to_groups0(Triples, Groups) :-
  315  aggregate_all(
  316    set(S-SGroups),
  317    (
  318      distinct(S, member(tp(S,_,_), Triples)),
  319      triples_to_groups1(S, Triples, SGroups)
  320    ),
  321    Groups
  322  ).
  323
  324triples_to_groups1(S, Triples, SGroups) :-
  325  aggregate_all(
  326    set(P-Os),
  327    (
  328      distinct(P, member(tp(S,P,_), Triples)),
  329      triples_to_groups2(S, P, Triples, Os)
  330    ),
  331    SGroups
  332  ).
  333
  334triples_to_groups2(S, P, Triples, Os) :-
  335  aggregate_all(set(O), member(tp(S,P,O), Triples), Os).
 rdf_dcg_subjects0(+Indent:nonneg, +Groups, +SkipTriples:list(tp), +Options:options)// is det
  342rdf_dcg_subjects0(_, [], _, _) --> !, "".
  343rdf_dcg_subjects0(I1, [S-SGroups|Groups], SkipTriples1, Options) -->
  344  tab(I1),
  345  rdf_dcg_node(S, Options),
  346  {I2 is I1 + 2},
  347  rdf_dcg_predicates1(I2, SGroups, SkipTriples1, SkipTriples2, Options),
  348  nl,
  349  ({Groups == []} -> "" ; nl),
  350  rdf_dcg_subjects0(I1, Groups, SkipTriples2, Options).
  351
  352% There is exactly one predicate-object pair; emit it on the same
  353% line.
  354rdf_dcg_predicates1(I, [P-[O]], SkipTriples1, SkipTriples2, Options) --> !,
  355  " ",
  356  rdf_dcg_predicate(P, Options),
  357  rdf_dcg_complex_object_(I, true, O, SkipTriples1, SkipTriples2, Options),
  358  ".".
  359% There is more than one predicate-object pair; do something special.
  360rdf_dcg_predicates1(I, SGroups1, SkipTriples1, SkipTriples2, Options) -->
  361  % Display instance-of statements first (regardless of predicate term
  362  % order).
  363  {types_to_the_front(SGroups1, SGroups2)},
  364  rdf_dcg_predicates2(I, false, false, SGroups2, SkipTriples1, SkipTriples2, Options).
  365
  366types_to_the_front(SGroups1, [P-Os|SGroups2]) :-
  367  rdf_prefix_iri(rdf, type, P),
  368  selectchk(P-Os, SGroups1, SGroups2), !.
  369types_to_the_front(SGroups, SGroups).
  370
  371% No more predicates.
  372rdf_dcg_predicates2(_, _, _, [], SkipTriples, SkipTriples, _) --> !, "".
  373% Another predicate.
  374rdf_dcg_predicates2(I1, StartOfBlock, InBlock, [P-Os|Groups], SkipTriples1, SkipTriples3, Options) -->
  375  start_of_block(I1, StartOfBlock),
  376  rdf_dcg_predicate(P, Options),
  377  {I2 is I1 + 2},
  378  rdf_dcg_objects1(I2, Os, SkipTriples1, SkipTriples2, Options),
  379  ({Groups == []} -> ({InBlock == true} -> " " ; ".") ; ";"),
  380  rdf_dcg_predicates2(I1, false, InBlock, Groups, SkipTriples2, SkipTriples3, Options).
  381
  382start_of_block(I, false) --> !,
  383  nl,
  384  tab(I).
  385start_of_block(_, true) --> " ".
  386
  387% There is exactly one object.  Emit it on the same line.
  388rdf_dcg_objects1(I, [O], SkipTriples1, SkipTriples2, Options) --> !,
  389  rdf_dcg_complex_object_(I, true, O, SkipTriples1, SkipTriples2, Options).
  390% There are multiple objects: emit each one on its own line.
  391rdf_dcg_objects1(I, Os, SkipTriples1, SkipTriples2, Options) -->
  392  rdf_dcg_objects2(I, Os, SkipTriples1, SkipTriples2, Options).
  393
  394% No more objects.
  395rdf_dcg_objects2(_, [], SkipTriples, SkipTriples, _) --> !, "".
  396% Another object.
  397rdf_dcg_objects2(I, [O|Os], SkipTriples1, SkipTriples3, Options) -->
  398  rdf_dcg_complex_object_(I, false, O, SkipTriples1, SkipTriples2, Options),
  399  ({Os == []} -> "" ; ","),
  400  rdf_dcg_objects2(I, Os, SkipTriples2, SkipTriples3, Options).
 rdf_dcg_complex_object_(+Indent:nonneg, +InLine:boolean, +Node:rdf_node, +SkipTriples1:list(tp), +SkipTriples2:list(tp), +Options:options)// is det
  409% The object term is an RDF list (collection).
  410rdf_dcg_complex_object_(I, InLine, RdfList, SkipTriples1, SkipTriples2, Options) -->
  411  {
  412    linear_list_(RdfList, SkipTriples1, SkipTriples2, Terms),
  413    Terms = [_|_]
  414  }, !,
  415  ({InLine == true} -> " " ; nl, tab(I)),
  416  rdf_dcg_list_(Terms, Options).
  417% The object term can be emitted an anonymous node.
  418rdf_dcg_complex_object_(I1, _, Node, SkipTriples1, SkipTriples3, Options) -->
  419  {
  420    turtle_object_(Node, SkipTriples1, SkipTriples2, Pairs),
  421    Pairs = [_|_], !,
  422    group_pairs_by_key(Pairs, Groups),
  423    I2 is I1 + 2
  424  },
  425  nl,
  426  tab(I1),
  427  "[",
  428  rdf_dcg_predicates2(I2, true, true, Groups, SkipTriples2, SkipTriples3, Options),
  429  "]".
  430% The object term is an atomic term.
  431rdf_dcg_complex_object_(I, InLine, Term, SkipTriples, SkipTriples, Options) -->
  432  ({InLine == true} -> " " ; nl, tab(I)),
  433  rdf_dcg_node(Term, Options).
 turtle_object_(+RdfList:rdf_node, +SkipTriples1:list(tp), -SkipTriples2:list(tp), -P_O_Pairs:list(pair(rdf_predicate,rdf_node))) is det
  439turtle_object_(S, SkipTriples1, SkipTriples3, [P-O|T]) :-
  440  rdf_prefix_selectchk(tp(S,P,O), SkipTriples1, SkipTriples2), !,
  441  turtle_object_(S, SkipTriples2, SkipTriples3, T).
  442turtle_object_(_, SkipTriples, SkipTriples, []).
 linear_list_(+RdfList:rdf_node, +SkipTriples1:list(tp), -SkipTriples2:list(tp), -Terms:list(rdf_term)) is det
  449linear_list_(S1, SkipTriples1, SkipTriples4, [H|T]) :-
  450  rdf_prefix_selectchk(tp(S1,rdf:first,H), SkipTriples1, SkipTriples2),
  451  rdf_prefix_selectchk(tp(S1,rdf:rest,S2), SkipTriples2, SkipTriples3), !,
  452  linear_list_(S2, SkipTriples3, SkipTriples4, T).
  453linear_list_(_, SkipTriples, SkipTriples, []).
 rdf_dcg_list_(+Terms:list(rdf_term), +Options:options)// is det
  457rdf_dcg_list_(Terms, Options) -->
  458  "(",
  459  (   {Terms = [H|T]}
  460  ->  " ",
  461      rdf_dcg_node(H, Options),
  462      rdf_dcg_list_tail_(T, Options),
  463      " "
  464  ;   ""
  465  ),
  466  ")".
 rdf_dcg_list_tail_(+Terms:list(rdf_term), +Options:options)// is det
  470rdf_dcg_list_tail_([H|T], Options) --> !,
  471  " ",
  472  rdf_dcg_node(H, Options),
  473  rdf_dcg_list_tail_(T, Options).
  474rdf_dcg_list_tail_([], _) --> ""