View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2009-2020, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(rdf_turtle_write,
   38          [ rdf_save_turtle/2,                  % +File, +Options
   39            rdf_save_canonical_turtle/2,        % +File, +Options
   40            rdf_save_trig/2,                    % +File, +Options
   41            rdf_save_canonical_trig/2,          % +File, +Options
   42            rdf_save_ntriples/2                 % +File, +Options
   43          ]).   44:- use_module(library(record),[(record)/1, op(_,_,record)]).   45:- use_module(library(semweb/turtle), []). % we make calls to public preds here
   46:- use_module(library(semweb/rdf_db),
   47              [ rdf_graph/1, rdf_graph_prefixes/3, rdf_current_prefix/2,
   48                rdf_is_bnode/1, rdf_equal/2, rdf_graph_property/2,
   49                rdf_statistics/1, rdf/4, rdf_resource/1, rdf_subject/1,
   50                rdf/3, rdf_global_id/2
   51              ]).   52
   53:- autoload(library(apply),[maplist/3,include/3,partition/4]).   54:- autoload(library(debug),[assertion/1]).   55:- autoload(library(error),[must_be/2,existence_error/2,type_error/2]).   56:- autoload(library(lists),
   57	    [append/2,reverse/2,delete/3,append/3,select/3,member/2]).   58:- autoload(library(option),[meta_options/3]).   59:- autoload(library(pairs),
   60	    [ transpose_pairs/2,
   61	      map_list_to_pairs/3,
   62	      pairs_values/2,
   63	      group_pairs_by_key/2
   64	    ]).   65:- autoload(library(rbtrees),
   66	    [ ord_list_to_rbtree/2,
   67	      rb_lookup/3,
   68	      rb_insert/4,
   69	      rb_empty/1,
   70	      rb_update/5
   71	    ]).   72:- autoload(library(sgml),
   73	    [xml_name/1,xml_is_dom/1,xsd_number_string/2]).   74:- autoload(library(sgml_write),[xml_write/2]).   75:- autoload(library(url),[file_name_to_url/2,parse_url/2]).   76
   77:- predicate_options(rdf_save_turtle/2, 2,
   78                     [ graph(atom),
   79                       base(atom),
   80                       encoding(oneof([utf8])),
   81                       indent(nonneg),
   82                       tab_distance(nonneg),
   83                       silent(boolean),
   84                       subject_white_lines(nonneg),
   85                       align_prefixes(boolean),
   86                       user_prefixes(boolean),
   87                       prefixes(list),
   88                       only_known_prefixes(boolean),
   89                       comment(boolean),
   90                       group(boolean),
   91                       inline_bnodes(boolean),
   92                       single_line_bnodes(boolean),
   93                       abbreviate_literals(boolean),
   94                       canonize_numbers(boolean),
   95                       canonical(boolean),
   96                       a(boolean),
   97                       expand(any)
   98                     ]).   99:- predicate_options(rdf_save_canonical_turtle/2, 2,
  100                     [ pass_to(rdf_save_turtle/2, 2)
  101                     ]).

Turtle - Terse RDF Triple Language writer

This module implements the Turtle language for representing the RDF triple model as defined by Dave Beckett from the Institute for Learning and Research Technology University of Bristol in the document:

The Turtle format is designed as an RDF serialization that is easy to read and write by both machines and humans. Due to the latter property, this library goes a long way in trying to produce human-readable output.

In addition to the human-readable format, this library can write a canonical representation of RDF graphs. The canonical representation has the following properties:

To be done
- Low-level string output takes 28% of the time. Move to C? */
  137:- record
  138    tw_state(graph,                 % graph being saved
  139             graphs:list(atom),     % TriG graphs being saved
  140             base,                  % The base-URI
  141             encoding=utf8,         % Desired encoding
  142             indent:nonneg=8,       % Indent for ; and ,-lists
  143             tab_distance:nonneg=8, % Tab distance
  144             silent:boolean=false,  % If true, do not print a message
  145             subject_white_lines:nonneg=1,%Extra lines between subjects
  146             a:boolean=true,        % Use 'a' for rdf:type
  147             align_prefixes:boolean=true,%Align prefix declarations
  148             prefixes:list,         % Provide prefixes
  149             user_prefixes:boolean=true,% Use rdf_current_ns/2?
  150             only_known_prefixes:boolean=false,% Only use known prefixes
  151             comment:boolean=true,  % write some comments into the file
  152             group:boolean=true,    % Group using ; and ,
  153             inline_bnodes:boolean=true, % Inline single-used bnodes
  154             single_line_bnodes:boolean=false, % No newline after ;
  155             abbreviate_literals:boolean=true, % Abbreviate known types
  156             canonize_numbers:boolean=false, % How to write numbers
  157             canonical:boolean=false,
  158             expand:any=lookup,     % Access to the triples
  159                                    % Private fields
  160             bnode_id=0,            % Incrementing bnode-id
  161             nodeid_map,            % RBTree mapping NodeIDs to Refs
  162             bnode_hash,            % RBTree holding reuse-count of hashes
  163             subject_count=0,       % # subjects saved
  164             triple_count=0,        % # triples saved
  165             base_root,             % Root URL of base
  166             base_dir,              % Directory
  167             base_path,             % Path of base
  168             prefix_map).           % List of Prefix-Map
  169
  170
  171:- meta_predicate
  172    rdf_save_turtle(+, :),
  173    rdf_save_canonical_turtle(+, :),
  174    rdf_save_canonical_trig(+, :),
  175    rdf_save_trig(+, :).
 rdf_save_turtle(+Out, :Options) is det
Save an RDF graph as Turtle. Options processed are:
a(+Boolean)
If true (default), use a for the predicate rdf:type. Otherwise use the full resource.
align_prefixes(+Boolean)
Nicely align the @prefix declarations
base(+Base)
Save relative to the given Base
canonize_numbers(+Boolean)
If true (default false), emit numeric datatypes using Prolog's write to achieve canonical output.
comment(+Boolean)
It true (default), write some informative comments between the output segments
encoding(+Encoding)
Encoding used for the output stream. Default is UTF-8.
expand(:Goal)
Query an alternative graph-representation. See below.
indent(+Column)
Indentation for ; -lists. `0' does not indent, but writes on the same line. Default is 8.
graph(+Graph)
Save only the named graph
group(+Boolean)
If true (default), using P-O and O-grouping.
inline_bnodes(+Boolean)
if true (default), inline bnodes that are used once.
abbreviate_literals(+Boolean)
if true (default), omit the type if allowed by turtle.
only_known_prefixes(+Boolean)
Only use prefix notation for known prefixes. Without, some documents produce huge amounts of prefixes.
prefixes(+List)
If provided, uses exactly these prefixes. List is a list of prefix specifications, where each specification is either a term Prefix_-_URI or a prefix that is known to rdf_current_prefix/2.
silent(+Boolean)
If true (default false), do not print the final informational message.
single_line_bnodes(+Bool)
If true (default false), write [...] and (...) on a single line.
subject_white_lines(+Count)
Extra white lines to insert between statements about a different subject. Default is 1.
tab_distance(+Tab)
Distance between tab-stops. `0' forces the library to use only spaces for layout. Default is 8.
user_prefixes(+Boolean)
If true (default), use prefixes from rdf_current_prefix/2.

The option expand allows for serializing alternative graph representations. It is called through call/5, where the first argument is the expand-option, followed by S,P,O,G. G is the graph-option (which is by default a variable). This notably allows for writing RDF graphs represented as rdf(S,P,O) using the following code fragment:

triple_in(RDF, S,P,O,_G) :-
    member(rdf(S,P,O), RDF).

    ...,
    rdf_save_turtle(Out, [ expand(triple_in(RDF)) ]),
Arguments:
Out- is one of stream(Stream), a stream handle, a file-URL or an atom that denotes a filename.
  250rdf_save_turtle(Spec, QOptions) :-
  251    meta_options(is_meta, QOptions, Options),
  252    thread_self(Me),
  253    thread_statistics(Me, cputime, T0),
  254    must_be(list, Options),
  255    make_tw_state(Options, State0, _Rest),
  256    init_base(State0, State1),
  257    init_prefix_map(State1, State),
  258    tw_state_encoding(State, Enc),
  259    setup_call_cleanup(
  260        open_output(Spec, Enc, Stream, Cleanup),
  261        ( tw_prefix_map(State, Stream),
  262          tw_graph(State, Stream)
  263        ),
  264        Cleanup),
  265    thread_statistics(Me, cputime, T1),
  266    Time is T1-T0,
  267    tw_state_triple_count(State, SavedTriples),
  268    tw_state_subject_count(State, SavedSubjects),
  269    (   tw_state_silent(State, true)
  270    ->  true
  271    ;   print_message(informational,
  272                      rdf(saved(Spec, Time, SavedSubjects, SavedTriples)))
  273    ).
  274
  275is_meta(expand).
 rdf_save_canonical_turtle(+Spec, :Options) is det
Save triples in a canonical format. This is the same as rdf_save_turtle/2, but using different defaults. In particular:
To be done
- Work in progress. Notably blank-node handling is incomplete.
  295rdf_save_canonical_turtle(Spec, M:Options) :-
  296    canonical_options(CannonicalOptions, Options),
  297    rdf_save_turtle(Spec, M:CannonicalOptions).
  298
  299canonical_options([ encoding(utf8),
  300                    indent(0),
  301                    tab_distance(0),
  302                    subject_white_lines(1),
  303                    align_prefixes(false),
  304                    user_prefixes(false),
  305                    comment(false),
  306                    group(false),
  307                    single_line_bnodes(true),
  308                    canonical(true)
  309                  | Options
  310                  ],
  311                  Options).
 rdf_save_ntriples(+Spec, :Options) is det
Save RDF using ntriples format. The ntriples format is a subset of Turtle, writing each triple fully qualified on its own line.
  319rdf_save_ntriples(File, Options):-
  320    rdf_save_turtle(File,
  321                    [ comment(false),
  322                      encoding(utf8),
  323                      group(false),
  324                      prefixes([]),
  325                      subject_white_lines(0),
  326                      a(false),
  327                      inline_bnodes(false),
  328                      abbreviate_literals(false)
  329                    | Options
  330                    ]).
 rdf_save_trig(+Spec, :Options) is det
Save multiple RDF graphs into a TriG file. Options are the same as for rdf_save_turtle/2. rdf_save_trig/2 ignores the graph(+Graph) option and instead processes one additional option:
graphs(+ListOfGraphs)
List of graphs to save. When omitted, all graphs in the RDF store are stored in the TriG file.
  344rdf_save_trig(Spec, QOptions) :-
  345    meta_options(is_meta, QOptions, Options),
  346    thread_self(Me),
  347    thread_statistics(Me, cputime, T0),
  348    must_be(list, Options),
  349    make_tw_state(Options, State0, _Rest),
  350    init_base(State0, State1),
  351    trig_graphs(State1, Graphs),
  352    init_prefix_map(State1, Graphs, State2),
  353    tw_state_encoding(State2, Enc),
  354    setup_call_cleanup(
  355        open_output(Spec, Enc, Stream, Cleanup),
  356        ( tw_prefix_map(State2, Stream),
  357          tw_trig_graphs(Graphs, Stream, State2, State)
  358        ),
  359        Cleanup),
  360    thread_statistics(Me, cputime, T1),
  361    Time is T1-T0,
  362    tw_state_triple_count(State, SavedTriples),
  363    tw_state_subject_count(State, SavedSubjects),
  364    length(Graphs, SavedGraphs),
  365    (   tw_state_silent(State, true)
  366    ->  true
  367    ;   print_message(informational,
  368                      rdf(saved(Spec, Time, SavedSubjects, SavedTriples, SavedGraphs)))
  369    ).
 rdf_save_canonical_trig(+Spec, :Options) is det
Save triples in a canonical format. See rdf_save_canonical_turtle/2 foir details.
  377rdf_save_canonical_trig(Spec, M:Options) :-
  378    canonical_options(CannonicalOptions, Options),
  379    rdf_save_trig(Spec, M:CannonicalOptions).
  380
  381tw_trig_graphs([], _, State, State).
  382tw_trig_graphs([H|T], Stream, State0, State) :-
  383    set_graph_of_tw_state(H, State0, State1),
  384    nl(Stream),
  385    tw_resource(H, State1, Stream),
  386    format(Stream, ' {~n', []),
  387    tw_graph(State1, Stream),
  388    format(Stream, '~N}~n', []),
  389    set_bnode_id_of_tw_state(0, State1, State2),
  390    set_nodeid_map_of_tw_state(_, State2, State3),
  391    set_bnode_hash_of_tw_state(_, State3, State4),
  392    tw_trig_graphs(T, Stream, State4, State).
 trig_graphs(+State, -Graphs) is det
True when Graphs is the (sorted) list of graphs we must save. If the expand argument is used and no graphs are specified, it enumerates all triples and extracts the graphs.
  401trig_graphs(State, Graphs) :-
  402    tw_state_graphs(State, Graphs),
  403    (   nonvar(Graphs)
  404    ->  true
  405    ;   tw_state_expand(State, Expand),
  406        (   Expand == lookup
  407        ->  findall(G, rdf_graph(G), Graphs0)
  408        ;   findall(G, call(Expand,_S,_P,_O,G), Graphs0)
  409        ),
  410        sort(Graphs0, Graphs)
  411    ).
 open_output(+Spec, +Encoding, -Stream, -Cleanup) is det
Open output Spec, returning a stream using Encoding.
Arguments:
Cleanup- is a goal that must be used to revert the side effects of open_output/4.
  421open_output(stream(Out), Encoding, Out, Cleanup) :-
  422    !,
  423    stream_property(Out, encoding(Old)),
  424    (   (   Old == Encoding
  425        ;   Old == wchar_t          % Internal encoding
  426        )
  427    ->  Cleanup = true
  428    ;   set_stream(Out, encoding(Encoding)),
  429        Cleanup = set_stream(Out, encoding(Old))
  430    ).
  431open_output(Stream, Encoding, Out, Cleanup) :-
  432    \+ atom(Stream),
  433    is_stream(Stream),
  434    !,
  435    open_output(stream(Stream), Encoding, Out, Cleanup).
  436open_output(Spec, Encoding, Out,
  437            close(Out)) :-
  438    out_to_file(Spec, File),
  439    open(File, write, Out, [encoding(Encoding)]).
  440
  441out_to_file(URL, File) :-
  442    atom(URL),
  443    file_name_to_url(File, URL),
  444    !.
  445out_to_file(File, File).
  446
  447
  448                 /*******************************
  449                 *            PREFIXES          *
  450                 *******************************/
 init_prefix_map(+State, -State) is det
Set the prefix_map of State. The prefix map is list of Prefix-URI of prefixes to use for emitting the graph requested in State. If multiple prefixes are present where the one is a prefix of the other, the longer one appears first in the list.
  459init_prefix_map(State0, State) :-
  460    tw_state_prefixes(State0, Prefixes),
  461    nonvar(Prefixes),
  462    !,
  463    user_prefix_map(Prefixes, PrefixMap),
  464    set_prefix_map_of_tw_state(PrefixMap, State0, State).
  465init_prefix_map(State0, State) :-
  466    tw_state_graph(State0, Graph),
  467    graph_prefix_map(State0, Graph, PrefixMap),
  468    set_prefix_map_of_tw_state(PrefixMap, State0, State).
  469
  470init_prefix_map(State0, _Graphs, State) :-      % TriG version
  471    tw_state_prefixes(State0, Prefixes),
  472    nonvar(Prefixes),
  473    !,
  474    user_prefix_map(Prefixes, PrefixMap),
  475    set_prefix_map_of_tw_state(PrefixMap, State0, State).
  476init_prefix_map(State0, Graphs, State) :-       % TriG version
  477    maplist(graph_prefixes(State0), Graphs, NestedPrefixes),
  478    append(NestedPrefixes, Prefixes0),
  479    sort(Prefixes0, Prefixes),
  480    prefix_map(State0, Prefixes, PrefixMap),
  481    set_prefix_map_of_tw_state(PrefixMap, State0, State).
  482
  483graph_prefix_map(State, Graph, PrefixMap) :-
  484    graph_prefixes(State, Graph, Prefixes),
  485    prefix_map(State, Prefixes, PrefixMap).
  486
  487graph_prefixes(State0, Graph, Prefixes) :-
  488    tw_state_expand(State0, Expand),
  489    tw_state_only_known_prefixes(State0, OnlyKnown),
  490    rdf_graph_prefixes(Graph, Prefixes,
  491                       [ filter(turtle_prefix(OnlyKnown)),
  492                         expand(Expand),
  493                         min_count(2),
  494                         get_prefix(turtle:iri_turtle_prefix)
  495                       ]).
  496
  497prefix_map(State, Prefixes, PrefixMap) :-
  498    remove_base(State, Prefixes, Prefixes2),
  499    prefix_names(Prefixes2, State, Pairs),
  500    transpose_pairs(Pairs, URI_Abrevs),
  501    reverse(URI_Abrevs, RURI_Abrevs),
  502    flip_pairs(RURI_Abrevs, PrefixMap).
 user_prefix_map(+Prefixes, -PrefixMap) is det
Convert a list of prefix specifications to a list Prefix-URI, longest URI first.
  509user_prefix_map(Prefixes, PrefixMap) :-
  510    must_be(list, Prefixes),
  511    maplist(prefix_pair, Prefixes, Pairs),
  512    map_list_to_pairs(prefix_length, Pairs, LenPairs),
  513    sort(LenPairs, LenPairs1),
  514    pairs_values(LenPairs1, RevPrefixMap),
  515    reverse(RevPrefixMap, PrefixMap).
  516
  517prefix_pair(Prefix-URI, Prefix-URI) :-
  518    !,
  519    must_be(atom, Prefix),
  520    must_be(atom, URI).
  521prefix_pair(Prefix, Prefix-URI) :-
  522    must_be(atom, Prefix),
  523    (   rdf_current_prefix(Prefix, URI)
  524    ->  true
  525    ;   existence_error(prefix, Prefix)
  526    ).
  527
  528prefix_length(_-URI, Len) :- atom_length(URI, Len).
 turtle_prefix(+OnlyKnown, +Where, +Prefix, +URI) is semidet
Test whether we want to include the proposed prefix in the @prefix declaration.
  535:- public turtle_prefix/4.              % called through rdf_graph_prefixes/3.
  536
  537turtle_prefix(true, _, Prefix, _) :-
  538    !,
  539    rdf_current_prefix(_, Prefix),
  540    !.
  541turtle_prefix(_, _, Prefix, URI) :-
  542    sub_atom(Prefix, _, 1, 0, Last),
  543    turtle_prefix_char(Last),
  544    atom_concat(Prefix, Local, URI),
  545    \+ sub_atom(Local, _, _, _, '.').
  546
  547turtle_prefix_char('#').
  548turtle_prefix_char('/').
  549
  550
  551remove_base(State, Prefixes, PrefixesNoBase) :-
  552    tw_state_base_dir(State, BaseDir),
  553    atom(BaseDir),
  554    !,
  555    delete(Prefixes, BaseDir, PrefixesNoBase).
  556remove_base(_State, Prefixes, Prefixes).
  557
  558flip_pairs([], []).
  559flip_pairs([Key-Val|Pairs], [Val-Key|Flipped]) :-
  560    flip_pairs(Pairs, Flipped).
  561
  562prefix_names(URIs, State, Prefixes) :-
  563    prefix_names(URIs, State, 1, Prefixes, []).
  564
  565prefix_names([], _, _, List, List) :- !.
  566prefix_names(URIs, State, Len, Prefixes, Tail) :-
  567    prefix_names(URIs, State, Len, Prefixes, PTail, Rest),
  568    Len1 is Len + 1,
  569    prefix_names(Rest, State, Len1, PTail, Tail).
  570
  571prefix_names(URIs, State, Len, Prefixes, PTail, Rest) :-
  572    map_list_to_pairs(propose_abbrev(State, Len), URIs, Pairs),
  573    !,
  574    keysort(Pairs, Sorted),
  575    unique(Sorted, Prefixes, PTail, Rest).
  576prefix_names(URIs, _, _, Prefixes, PTail, []) :-
  577    number_prefixes(URIs, 1, Prefixes, PTail).
  578
  579number_prefixes([], _, PL, PL).
  580number_prefixes([H|T0], N, [P-H|PL], T) :-
  581    atomic_concat(ns, N, P),
  582    succ(N, N1),
  583    number_prefixes(T0, N1, PL, T).
  584
  585unique([], L, L, []).
  586unique([A-U|T0], [A-U|T], L, Rest) :-
  587    T0 \= [A-_|_],
  588    !,
  589    unique(T0, T, L, Rest).
  590unique([A-U|T0], Prefixes, L, [U|Rest0]) :-
  591    strip_keys(T0, A, T1, Rest0, Rest),
  592    unique(T1, Prefixes, L, Rest).
  593
  594strip_keys([A-U|T0], A, T, [U|R0], R) :-
  595    !,
  596    strip_keys(T0, A, T, R0, R).
  597strip_keys(L, _, L, R, R).
 propose_abbrev(+State, +Len, +URI, -Abbrev) is multi
Propose an abbreviation for URI. Backtracking yields longer ones.
  605propose_abbrev(_, _, URI, Abbrev) :-
  606    well_known_ns(Abbrev, URI),
  607    !.
  608propose_abbrev(State, _, URI, Abbrev) :-
  609    tw_state_user_prefixes(State, true),
  610    rdf_current_prefix(Abbrev, URI),
  611    !.
  612propose_abbrev(_, Len, URI, Abbrev) :-
  613    namespace_parts(URI, Parts),
  614    include(abbrev_part, Parts, Names),
  615    reverse(Names, RevNames),
  616    length(Use, Len),
  617    append(Use, _, RevNames),
  618    atomic_list_concat(Use, -, Abbrev).
  619
  620abbrev_part(X) :-
  621    xml_name(X),
  622    \+ well_known_ns(X, _),
  623    \+ well_known_extension(X).
  624
  625well_known_ns(rdf,  'http://www.w3.org/1999/02/22-rdf-syntax-ns#').
  626well_known_ns(rdfs, 'http://www.w3.org/2000/01/rdf-schema#').
  627well_known_ns(owl,  'http://www.w3.org/2002/07/owl#').
  628well_known_ns(xsd,  'http://www.w3.org/2001/XMLSchema#').
  629well_known_ns(dc,   'http://purl.org/dc/elements/1.1/').
  630
  631well_known_extension(ttl).
  632well_known_extension(nt).
  633well_known_extension(n3).
  634well_known_extension(xml).
  635well_known_extension(rdf).
  636well_known_extension(owl).
 namespace_parts(+URL, -Parts)
  640namespace_parts(URL, Parts) :-
  641    atom_codes(URL, Codes),
  642    phrase(parts(Parts), Codes),
  643    !.
  644namespace_parts(URL, _) :-
  645    format(user_error, 'Couldn\'t split ~q~n', [URL]),
  646    fail.
  647
  648parts(List) --> sep2, parts2(List).
  649
  650parts2([H|T]) -->
  651    string(Codes),  {Codes \== []},
  652    sep,
  653    !,
  654    {atom_codes(H, Codes)},
  655    parts2(T).
  656parts2([]) --> [].
  657
  658string([]) --> [].
  659string([H|T]) --> [H], string(T).
  660
  661sep --> sep_char, sep2.
  662sep([], []).
  663
  664sep2 --> sep_char, !, sep2.
  665sep2 --> [].
  666
  667sep_char --> "/".
  668sep_char --> ":".
  669sep_char --> ".".
  670sep_char --> "?".
  671sep_char --> "#".
 init_base(+State0, -State) is det
Initialise dealing with the base URI. It sets two attributes of the state: base_root and base_path.
  679init_base(State0, State) :-
  680    tw_state_base(State0, BaseURI),
  681    atom(BaseURI),
  682    !,
  683    parse_url(BaseURI, Attributes),
  684    include(root_part, Attributes, RootAttrs),
  685    parse_url(BaseRoot, RootAttrs),
  686    memberchk(path(BasePath), Attributes),
  687    file_directory_name(BasePath, BaseDir),
  688    atomic_list_concat([BaseRoot, BaseDir, /], BaseDirURI),
  689    set_base_root_of_tw_state(BaseRoot, State0, State1),
  690    set_base_path_of_tw_state(BasePath, State1, State2),
  691    set_base_dir_of_tw_state(BaseDirURI, State2, State).
  692init_base(State, State).
  693
  694root_part(protocol(_)).
  695root_part(host(_)).
  696root_part(port(_)).
  697
  698
  699                 /*******************************
  700                 *              SAVE            *
  701                 *******************************/
 tw_graph(+State, +Out) is det
Write an RDF graph as Turtle data.
To be done
- Write unconnected and multi-connected blank-nodes.
  709tw_graph(State, Out) :-
  710    subjects(State, Subjects),
  711    length(Subjects, SubjectCount),
  712    inc_subject_count(State, SubjectCount),
  713    partition(rdf_is_bnode, Subjects, BNodes, ProperSubjects),
  714    maplist(pair_var, BNodes, Pairs),
  715    ord_list_to_rbtree(Pairs, BNTree),
  716    tw_state_nodeid_map(State, BNTree),
  717    (   ProperSubjects == []
  718    ->  true
  719    ;   length(ProperSubjects, PSCount),
  720        comment(State, 'Named toplevel resources (~D)', [PSCount], Out),
  721        tw_proper_subjects(ProperSubjects, State, Out)
  722    ),
  723    tw_bnodes(Pairs, State, Out).
  724
  725pair_var(BNode, BNode-_).
  726
  727tw_prefix_map(State, Out) :-
  728    tw_state_prefix_map(State, PrefixMap),
  729    tw_prefix_map(PrefixMap, State, Out).
 tw_prefix_map(+PrefixMap, +State, +Out) is det
Write the @base and @prefix declarations
  735tw_prefix_map(PrefixMap, State, Out) :-
  736    tw_state_align_prefixes(State, true),
  737    !,
  738    longest_prefix(PrefixMap, 0, Length),
  739    PrefixCol is Length+10,
  740    tw_base(PrefixCol, State, Out),
  741    tw_prefix_map(PrefixMap, PrefixCol, State, Out).
  742tw_prefix_map(PrefixMap, State, Out) :-
  743    tw_base(0, State, Out),
  744    tw_prefix_map(PrefixMap, 0, State, Out).
  745
  746longest_prefix([], L, L).
  747longest_prefix([Prefix-_|T], L0, L) :-
  748    atom_length(Prefix, L1),
  749    L2 is max(L0, L1),
  750    longest_prefix(T, L2, L).
  751
  752
  753tw_base(Col, State, Out) :-
  754    tw_state_base(State, Base),
  755    atom(Base),
  756    !,
  757    format(Out, '@base ~t~*|', [Col]),
  758    turtle:turtle_write_uri(Out, Base),
  759    format(Out, ' .~n', []).
  760tw_base(_, _, _).
  761
  762
  763tw_prefix_map([], _, _, _).
  764tw_prefix_map([Prefix-URI|T], Col, State, Out) :-
  765    format(Out, '@prefix ~t~w: ~*|', [Prefix, Col]),
  766    tw_relative_uri(URI, State, Out),
  767    format(Out, ' .~n', []),
  768    (   T == []
  769    ->  true
  770    ;   tw_prefix_map(T, Col, State, Out)
  771    ).
 tw_proper_subjects(+Subjects, +State, +Out) is det
Write the subjects that are not Bnodes.
  778tw_proper_subjects([], _, _).
  779tw_proper_subjects([H|T], State, Out) :-
  780    separate_subjects(State, Out),
  781    tw_subject(H, H, State, Out),
  782    tw_proper_subjects(T, State, Out).
  783
  784
  785separate_subjects(State, Out) :-
  786    tw_state_subject_white_lines(State, ExtraLines),
  787    put_n(ExtraLines, '\n', Out).
 tw_subject(+URI, +State, +Out) is det
Write a toplevel non-bnode subject.
  793tw_subject(URI, Ref, State, Out) :-
  794    subject_triples(URI, State, Pairs),
  795    length(Pairs, Count),
  796    inc_triple_count(State, Count),
  797    group_po(Pairs, Grouped),
  798    tw_subject_triples(Grouped, Ref, State, Out).
  799
  800group_po(Pairs, Grouped) :-
  801    group_pairs_by_key(Pairs, Grouped0),
  802    rdf_equal(rdf:type, RDFType),
  803    (   select(RDFType-Types, Grouped0, Grouped1)
  804    ->  Grouped = [RDFType-Types|Grouped1]
  805    ;   Grouped = Grouped0
  806    ).
 tw_bnodes(+Pairs, +State, +Out) is det
Write the Bnodes. Pairs is a list URI-Ref, where Ref is one of written if the Bnode is already written; an integer if it is used multiple times or a variable if it has not been written. The order in which we deal with bnodes is defined as follows:
  823tw_bnodes(Pairs, State, Out) :-
  824    tw_top_bnodes(Pairs, State, Out, Rest1),
  825    tw_numbered_bnodes(Rest1, State, Out, 1, Rest2),
  826    tw_cyclic_bnodes(Rest2, State, Out, 0).
  827
  828
  829tw_numbered_bnodes([], _, _, _, []) :- !.
  830tw_numbered_bnodes(Pairs, State, Out, Level, Rest) :-
  831    multi_referenced(Pairs, RefPairs, Rest0),
  832    (   RefPairs == []
  833    ->  Rest = Rest0
  834    ;   length(RefPairs, Count),
  835        comment(State, 'Level ~D multi-referenced blank-nodes (~D)',
  836                [ Level, Count ], Out),
  837        tw_ref_bnodes(RefPairs, State, Out),
  838        Level1 is Level + 1,
  839        tw_numbered_bnodes(Rest0, State, Out, Level1, Rest)
  840    ).
  841
  842multi_referenced([], [], []).
  843multi_referenced([H|T], RefPairs, Rest) :-
  844    H = _-Ref,
  845    (   Ref == written
  846    ->  multi_referenced(T, RefPairs, Rest)
  847    ;   var(Ref)
  848    ->  Rest = [H|TR],
  849        multi_referenced(T, RefPairs, TR)
  850    ;   assertion(Ref = bnode(_)),
  851        RefPairs = [H|TRP],         % assigned reference
  852        multi_referenced(T, TRP, Rest)
  853    ).
  854
  855tw_ref_bnodes([], _, _).
  856tw_ref_bnodes([BNode-Ref|T], State, Out) :-
  857    separate_subjects(State, Out),
  858    tw_subject(BNode, Ref, State, Out),
  859    tw_ref_bnodes(T, State, Out).
 tw_top_bnodes(+Pairs, +State, +Out, -Rest)
Write the top bnodes: those that do not appear as an object anywhere.
  867tw_top_bnodes(Pairs, State, Out, Rest) :-
  868    unreferenced(Pairs, State, TopBNodes, Rest),
  869    (   TopBNodes == []
  870    ->  true
  871    ;   length(TopBNodes, Count),
  872        comment(State, 'Toplevel blank-nodes (~D)', [Count], Out),
  873        sort_bnodes(TopBNodes, SortedTopBNodes, State),
  874        tw_top_bnodes(SortedTopBNodes, State, Out)
  875    ).
  876
  877unreferenced([], _, [], []).
  878unreferenced([H|T], State, UnrefPairs, Rest) :-
  879    H = BNode-Ref,
  880    (   Ref == written
  881    ->  unreferenced(T, State, UnrefPairs, Rest)
  882    ;   var(Ref),
  883        object_link_count(BNode, State, 0)
  884    ->  UnrefPairs = [H|URT],
  885        unreferenced(T, State, URT, Rest)
  886    ;   Rest = [H|TR],
  887        unreferenced(T, State, UnrefPairs, TR)
  888    ).
  889
  890tw_top_bnodes([], _, _).
  891tw_top_bnodes([BNode-_|T], State, Out) :-
  892    tw_bnode(BNode, State, Out),
  893    tw_top_bnodes(T, State, Out).
  894
  895
  896tw_bnode(BNode, State, Out) :-
  897    subject_triples(BNode, State, Pairs),
  898    length(Pairs, Count),
  899    inc_triple_count(State, Count),
  900    (   tw_state_inline_bnodes(State, true)
  901    ->  tw_bnode_triples(Pairs, State, Out),
  902        format(Out, ' .~n', [])
  903    ;   next_bnode_id(State, BNode, Ref),
  904        tw_bnode_ntriples(Pairs, Ref, State, Out)
  905    ).
  906
  907tw_bnode_triples(Pairs, State, Out) :-
  908    group_po(Pairs, Grouped),
  909    (   tw_state_single_line_bnodes(State, true)
  910    ->  format(Out, '[ ', []),
  911        tw_triples(Grouped, -1, State, Out),
  912        format(Out, ' ]', [])
  913    ;   line_position(Out, Indent),
  914        format(Out, '[ ', []),
  915        line_position(Out, AIndent),
  916        tw_triples(Grouped, AIndent, State, Out),
  917        nl_indent(Out, State, Indent),
  918        format(Out, ']', [])
  919    ).
  920
  921tw_bnode_ntriples([], _, _, _).
  922tw_bnode_ntriples([P-O|T], Ref, State, Out) :-
  923    tw_bnode_ref(Ref, Out),
  924    format(Out, ' ', []),
  925    tw_predicate(P, State, Out),
  926    format(Out, ' ', []),
  927    tw_object(O, State, Out),
  928    format(Out, ' .~n', []),
  929    tw_bnode_ntriples(T, Ref, State, Out).
 tw_cyclic_bnodes(+Pairs, +BNode, +State, +Out, +Cycle)
The rest. These are groups of bnodes that are reachable, but we cannot find a starting point, neither from a named resource, nor from an unlinked bnode. As long as we are not considering stable canonical output, we can break the cycle at any point.
  939tw_cyclic_bnodes([], _State, _Out, _) :- !.
  940tw_cyclic_bnodes(Pairs, State, Out, Cycle0) :-
  941    (   tw_state_canonical(State, true)
  942    ->  sort_bnode_pairs(Pairs, BNodes, State)
  943    ;   BNodes = Pairs
  944    ),
  945    succ(Cycle0, Cycle),
  946    BNodes = [BNode-Ref|_],
  947    next_bnode_id(State, BNode, Ref),
  948    comment(State, 'Breaking cycle ~D', [Cycle], Out),
  949    tw_numbered_bnodes(Pairs, State, Out, 1, Rest),
  950    tw_cyclic_bnodes(Rest, State, Out, Cycle).
 tw_subject_triples(+Grouped, +Subject, +State, +Out)
Save triples on Subject. Combine groups of triples with the same subject (;) and same subject+predicate (,).
Arguments:
Subject- is either a URI or an integer. The latter is used for writing a named bnode.
  961tw_subject_triples([], _, _, _) :- !.
  962tw_subject_triples(Grouped, URI, State, Out) :-
  963    tw_state_group(State, false),
  964    !,
  965    tw_ungrouped_triples(Grouped, URI, State, Out).
  966tw_subject_triples(Grouped, URI, State, Out) :-
  967    tw_resource(URI, State, Out),
  968    (   tw_state_indent(State, Indent),
  969        Indent > 0
  970    ->  nl_indent(Out, State, Indent)
  971    ;   put_char(Out, ' '),
  972        line_position(Out, Indent)
  973    ),
  974    tw_triples(Grouped, Indent, State, Out),
  975    format(Out, ' .~n', []).
 tw_ungrouped_triples(+Grouped, +URI, +State, +Out)
Write triples for subject URI as one line per triple. Used for canonical output.
  982tw_ungrouped_triples([], _, _, _).
  983tw_ungrouped_triples([P-Vs|Groups], URI, State, Out) :-
  984    partition(rdf_is_bnode, Vs, BNVs, ProperVs),
  985    tw_ungrouped_values(ProperVs, P, URI, State, Out),
  986    sort_bnodes(BNVs, SortedBNVs, State),
  987    tw_ungrouped_values(SortedBNVs, P, URI, State, Out),
  988    tw_ungrouped_triples(Groups, URI, State, Out).
  989
  990tw_ungrouped_values([], _, _, _, _).
  991tw_ungrouped_values([V|T], P, URI, State, Out) :-
  992    tw_resource(URI, State, Out),
  993    put_char(Out, ' '),
  994    tw_predicate(P, State, Out),
  995    put_char(Out, ' '),
  996    tw_object(V, State, Out),
  997    format(Out, ' .~n', []),
  998    tw_ungrouped_values(T, P, URI, State, Out).
 tw_triples(+Groups, +Indent, +State, +Out) is det
Triple writer that uses ; and ,- grouping
 1005tw_triples([P-Vs|MoreGroups], Indent, State, Out) :-
 1006    tw_write_pvs(Vs, P, State, Out),
 1007    (   MoreGroups == []
 1008    ->  true
 1009    ;   format(Out, ' ;', []),
 1010        nl_indent(Out, State, Indent),
 1011        tw_triples(MoreGroups, Indent, State, Out)
 1012    ).
 1013
 1014tw_write_pvs(Values, P, State, Out) :-
 1015    tw_predicate(P, State, Out),
 1016    put_char(Out, ' '),
 1017    line_position(Out, Indent),
 1018    tw_write_vs(Values, Indent, State, Out).
 1019
 1020tw_predicate(P, State, Out) :-
 1021    (   rdf_equal(P, rdf:type),
 1022        tw_state_a(State, true)
 1023    ->  format(Out, 'a', [])
 1024    ;   tw_resource(P, State, Out)
 1025    ).
 1026
 1027tw_write_vs([H|T], Indent, State, Out) :-
 1028    tw_object(H, State, Out),
 1029    (   T == []
 1030    ->  true
 1031    ;   format(Out, ' ,', []),
 1032        nl_indent(Out, State, Indent),
 1033        tw_write_vs(T, Indent, State, Out)
 1034    ).
 tw_object(+Value, +State, +Out) is det
Write the object of a triple.
 1040tw_object(Value, State, Out) :-
 1041    rdf_is_bnode(Value),
 1042    !,
 1043    tw_bnode_object(Value, State, Out).
 1044tw_object(Value, State, Out) :-
 1045    atom(Value),
 1046    !,
 1047    tw_resource(Value, State, Out).
 1048tw_object(Literal, State, Out) :-
 1049    tw_literal(Literal, State, Out).
 tw_bnode_object(+Value, +State, +Out) is det
Write a Bnode value. There are a number of cases:
 1062tw_bnode_object(BNode, State, Out) :-
 1063    tw_state_nodeid_map(State, BNTree),
 1064    rb_lookup(BNode, Ref, BNTree),
 1065    !,
 1066    (   var(Ref)
 1067    ->  (   tw_state_inline_bnodes(State, true),
 1068            tw_unshared_bnode(BNode, State, Out)
 1069        ->  Ref = written
 1070        ;   next_bnode_id(State, BNode, Ref),
 1071            tw_bnode_ref(Ref, Out)
 1072        )
 1073    ;   tw_bnode_ref(Ref, Out)
 1074    ).
 1075tw_bnode_object(BNode, State, Out) :-
 1076    object_link_count(BNode, State, N),
 1077    N > 1,
 1078    !,
 1079    tw_state_nodeid_map(State, BNTree0),
 1080    rb_insert(BNTree0, BNode, Ref, BNTree),
 1081    set_nodeid_map_of_tw_state(BNTree, State),
 1082    next_bnode_id(State, BNode, Ref),
 1083    tw_bnode_ref(Ref, Out).
 1084tw_bnode_object(BNode, State, Out) :-
 1085    next_bnode_id(State, BNode, Ref),
 1086    tw_bnode_ref(Ref, Out).
 1087
 1088tw_bnode_ref(bnode(Ref), Out) :-
 1089    (   integer(Ref)
 1090    ->  format(Out, '_:bn~w', [Ref])
 1091    ;   format(Out, '_:~w', [Ref])
 1092    ).
 tw_unshared_bnode(+BNode, +State, +Out) is semidet
Write a bnode if this is the only place it is used.
 1098tw_unshared_bnode(BNode, State, Out) :-
 1099    object_link_count(BNode, State, 1),
 1100    subject_triples(BNode, State, Pairs),
 1101    (   Pairs == []
 1102    ->  format(Out, '[]', [])
 1103    ;   pairs_unshared_collection(Pairs, State, Collection)
 1104    ->  (   Collection == []
 1105        ->  format(Out, '()', [])
 1106        ;   tw_state_nodeid_map(State, BNTree),
 1107            rb_lookup(BNode, written, BNTree),
 1108            length(Collection, NMembers),
 1109            Triples is 2*NMembers,
 1110            inc_triple_count(State, Triples),
 1111            (   tw_state_single_line_bnodes(State, true)
 1112            ->  format(Out, '( ', []),
 1113                tw_collection(Collection, -1, State, Out),
 1114                format(Out, ' )', [])
 1115            ;   line_position(Out, Indent),
 1116                format(Out, '( ', []),
 1117                line_position(Out, AIndent),
 1118                tw_collection(Collection, AIndent, State, Out),
 1119                nl_indent(Out, State, Indent),
 1120                format(Out, ')', [])
 1121            )
 1122        )
 1123    ;   tw_bnode_triples(Pairs, State, Out)
 1124    ).
 1125
 1126tw_collection([H|T], Indent, State, Out) :-
 1127    tw_object(H, State, Out),
 1128    (   T \== []
 1129    ->  nl_indent(Out, State, Indent),
 1130        tw_collection(T, Indent, State, Out)
 1131    ;   true
 1132    ).
 unshared_collection(+URI, +State, -Members) is semidet
True if URI denodes an RDF list that is made up from bnodes, is linked exactly once to its context and contains no extra triples.
 1140unshared_collection(C, _, []) :-
 1141    rdf_equal(C, rdf:nil),
 1142    !.
 1143unshared_collection(C, State, List) :-
 1144    rdf_is_bnode(C),
 1145    object_link_count(C, State, 1),
 1146    tw_state_nodeid_map(State, BNTree),
 1147    rb_lookup(C, written, BNTree),
 1148    subject_triples(C, State, Pairs),
 1149    pairs_unshared_collection(Pairs, State, List).
 1150
 1151pairs_unshared_collection(Pairs, State, [H|T]) :-
 1152    rdf_equal(rdf:first, RDFFirst),
 1153    rdf_equal(rdf:rest, RDFRest),
 1154    Pairs = [ RDFFirst-H,
 1155              RDFRest-Rest
 1156            | More
 1157            ],
 1158    (   More == []
 1159    ;   rdf_equal(rdf:type, RDFType),
 1160        rdf_equal(rdf:'List', RDFList),
 1161        More == [RDFType-RDFList]
 1162    ),
 1163    unshared_collection(Rest, State, T).
 object_link_count(+BNode, +STate, -Count) is det
Number of times BNode is used as an object in the graph
 1170object_link_count(BNode, State, Count) :-
 1171    tw_state_graph(State, Graph),
 1172    tw_state_expand(State, Expand),
 1173    findall(S-P, call(Expand,S,P,BNode,Graph), Pairs0),
 1174    sort(Pairs0, Pairs),            % remove duplicates
 1175    length(Pairs, Count).
 nl_indent(+Out, +State, +Indent) is det
Write a newline and indent to column Indent.
 1181nl_indent(Out, _, -1) :-
 1182    !,
 1183    put_char(Out, ' ').
 1184nl_indent(Out, State, Indent) :-
 1185    nl(Out),
 1186    tw_state_tab_distance(State, TD),
 1187    (   TD == 0
 1188    ->  tab(Out, Indent)
 1189    ;   Tabs is Indent//TD,
 1190        Spaces is Indent mod TD,
 1191        put_n(Tabs, '\t', Out),
 1192        put_n(Spaces, ' ', Out)
 1193    ).
 1194
 1195put_n(N, Char, Out) :-
 1196    N > 0,
 1197    !,
 1198    put_char(Out, Char),
 1199    N2 is N - 1,
 1200    put_n(N2, Char, Out).
 1201put_n(_, _, _).
 subject_triples(+URI, +State, -Pairs) is det
Pairs is a sorted list of P-O pairs representing all triples on the subject URI.
 1209subject_triples(URI, State, Pairs) :-
 1210    tw_state_graph(State, Graph),
 1211    tw_state_expand(State, Expand),
 1212    findall(P-O, call(Expand, URI, P, O, Graph), Pairs0),
 1213    sort(Pairs0, Pairs).
 1214
 1215
 1216                 /*******************************
 1217                 *          GRAPH-LOGIC         *
 1218                 *******************************/
 subjects(+State, -Subjects:ord_set) is det
Subjects is a list of all subjects in the graph requested in State.
 1225subjects(State, Subjects) :-
 1226    tw_state_expand(State, Expand),
 1227    tw_state_graph(State, Graph),
 1228    (   Expand == lookup,
 1229        atom(Graph),
 1230        (   rdf_graph_property(Graph, triples(Count))
 1231        ->  true
 1232        ;   Count = 0                       % non-existing graph
 1233        ),
 1234        rdf_statistics(triples(Total)),
 1235        Count * 10 < Total
 1236    ->  findall(S, rdf(S,_,_,Graph), List),
 1237        sort(List, Subjects)
 1238    ;   Expand \== lookup
 1239    ->  findall(S, call(Expand, S,_,_,Graph), List),
 1240        sort(List, Subjects)
 1241    ;   findall(Subject, subject(State, Subject), AllSubjects),
 1242        sort(AllSubjects, Subjects)
 1243    ).
 1244
 1245
 1246subject(State, Subject) :-
 1247    tw_state_graph(State, Graph),
 1248    (   atom(Graph)
 1249    ->  rdf_resource(Subject),
 1250        (   rdf(Subject, _, _, Graph)
 1251        ->  true
 1252        )
 1253    ;   rdf_subject(Subject)
 1254    ).
 1255
 1256
 1257:- public lookup/4.                     % called from expand hook.
 1258
 1259lookup(S,P,O,G) :-
 1260    (   var(G)
 1261    ->  rdf(S,P,O)
 1262    ;   rdf(S,P,O,G)
 1263    ).
 1264
 1265
 1266                 /*******************************
 1267                 *        CANONICAL ORDERING    *
 1268                 *******************************/
 1269
 1270/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1271This section deals with the two problems of canonical graphs:
 1272
 1273    * Keep blank nodes in the same order
 1274    * Assign stable names to blank nodes that we need to
 1275      give a name.  There are two cases: (1) a blank nodes is
 1276      used in more than one place and (2) a blank node series
 1277      is cyclic.
 1278- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 sort_bnodes(+BNodes, -Sorted, +State) is det
Sort a list of blank nodes.
 1284sort_bnodes(BNodes, Sorted, _State) :-
 1285    sort(BNodes, Sorted).
 sort_bnode_pairs(+Pairs, -Sorted, +State) is det
Sort a list of Pairs BNode-Ref
 1291sort_bnode_pairs(Pairs, Sorted, _State) :-
 1292    sort(Pairs, Sorted).
 bnode_to_term(+BNode, -Term, +State)
Term is a canonical representation of the graph formed by BNode. The transformation of a bnode is
bnode(p-[o1,o2,..], ..)

The arguments are alphabetically sorted on predicate (can't we leave the preds out them?) and the objects are alphabetically sorted. Sorting multiple bnode values?

 next_bnode_id(+State, +BNode, -Ref) is det
Generate a node-id for BNode. When writing non-canonically, we simply number the bnodes. Otherwise we want a more stable numbering. Our numbering is a hash of the content of the bnode. It is not unlikely that we find muliple copies, and therefore we number the full id is bn_<hash>_<n>, <n> counting 0...
 1314next_bnode_id(State, _BNode, bnode(Ref)) :-
 1315    tw_state_canonical(State, false),
 1316    !,
 1317    tw_state_bnode_id(State, Ref0),
 1318    Ref is Ref0+1,
 1319    nb_set_bnode_id_of_tw_state(Ref, State).
 1320next_bnode_id(State, BNode, bnode(Ref)) :-
 1321    bnode_hash(BNode, Hash),
 1322    tw_state_bnode_hash(State, BNHash),
 1323    (   var(BNHash)
 1324    ->  rb_empty(BNHash)
 1325    ;   true
 1326    ),
 1327    (   rb_update(BNHash, Hash, C0, C, BNHash1)
 1328    ->  C is C0+1
 1329    ;   C = 0,
 1330        rb_insert(BNHash, Hash, C, BNHash1)
 1331    ),
 1332    set_bnode_hash_of_tw_state(BNHash1, State),
 1333    format(atom(Ref), 'bn_~w_~d', [Hash, C]).
 bnode_hash(+BNode, -Hash) is det
Hash is the hash-value for a bnode.
To be done
- : Hash on content.
 1341bnode_hash(BNode, Hash) :-
 1342    term_hash(BNode, Hash).
 1343
 1344
 1345                 /*******************************
 1346                 *           PRIMITIVES         *
 1347                 *******************************/
 tw_resource(+Resource, +State, +Out) is det
Write a resource
 1353tw_resource(BNodeID, _, Out) :-
 1354    BNodeID = bnode(_),
 1355    !,
 1356    tw_bnode_ref(BNodeID, Out).
 1357tw_resource(Resource, State, Out) :-
 1358    tw_state_prefix_map(State, PrefixMap),
 1359    member(Prefix-Full, PrefixMap),
 1360    atom_concat(Full, Name, Resource),
 1361    (   turtle:turtle_pn_local(Name)
 1362    ->  true
 1363    ;   Name == ''
 1364    ),
 1365    !,
 1366    format(Out, '~w:', [Prefix]),
 1367    turtle:turtle_write_pn_local(Out, Name).
 1368tw_resource(Resource, State, Out) :-
 1369    tw_relative_uri(Resource, State, Out).
 1370
 1371
 1372tw_relative_uri(Resource, State, Out) :-
 1373    tw_state_base_root(State, Root),
 1374    atom(Root),
 1375    atom_concat(Root, ResPath, Resource),
 1376    sub_atom(ResPath, 0, _, _, /),
 1377    tw_state_base_path(State, BasePath),
 1378    relative_path(ResPath, BasePath, RelPath),
 1379    !,
 1380    turtle:turtle_write_uri(Out, RelPath).
 1381tw_relative_uri(Resource, _, Out) :-
 1382    turtle:turtle_write_uri(Out, Resource).
 1383
 1384relative_path(Path, RelTo, RelPath) :-
 1385    atomic_list_concat(PL, /, Path),
 1386    atomic_list_concat(RL, /, RelTo),
 1387    delete_common_prefix(PL, RL, PL1, PL2),
 1388    to_dot_dot(PL2, DotDot, PL1),
 1389    atomic_list_concat(DotDot, /, RelPath).
 1390
 1391delete_common_prefix([H|T01], [H|T02], T1, T2) :-
 1392    !,
 1393    delete_common_prefix(T01, T02, T1, T2).
 1394delete_common_prefix(T1, T2, T1, T2).
 1395
 1396to_dot_dot([], Tail, Tail).
 1397to_dot_dot([_], Tail, Tail) :- !.
 1398to_dot_dot([_|T0], ['..'|T], Tail) :-
 1399    to_dot_dot(T0, T, Tail).
 tw_literal(+Literal, +State, +Out) is det
Write a literal value to the stream Out.
 1406tw_literal(^^(Value, Type), State, Out) :-
 1407    !,
 1408    tw_typed_literal(Type, Value, State, Out).
 1409tw_literal(literal(type(Type, Value)), State, Out) :-
 1410    !,
 1411    tw_typed_literal(Type, Value, State, Out).
 1412tw_literal(@(Value, Lang), State, Out) :-
 1413    !,
 1414    tw_quoted_string(Value, State, Out),
 1415    downcase_atom(Lang, TurtleLang),        % Turtle lang = [a-z]+('-'[a-z0-9]+)*
 1416    format(Out, '@~w', [TurtleLang]).
 1417tw_literal(literal(lang(Lang, Value)), State, Out) :-
 1418    !,
 1419    tw_quoted_string(Value, State, Out),
 1420    downcase_atom(Lang, TurtleLang),        % Turtle lang = [a-z]+('-'[a-z0-9]+)*
 1421    format(Out, '@~w', [TurtleLang]).
 1422tw_literal(literal(Value), State, Out) :-
 1423    atom(Value),
 1424    !,
 1425    rdf_equal(xsd:string, TypeString),
 1426    tw_typed_literal(TypeString, Value, State, Out).
 1427                                                % Add types automatically
 1428tw_literal(literal(Value), State, Out) :-
 1429    integer(Value),
 1430    !,
 1431    rdf_equal(Type, xsd:integer),
 1432    tw_typed_literal(Type, Value, State, Out).
 1433tw_literal(literal(Value), State, Out) :-
 1434    float(Value),
 1435    !,
 1436    rdf_equal(Type, xsd:double),
 1437    tw_typed_literal(Type, Value, State, Out).
 1438tw_literal(literal(Value), State, Out) :-
 1439    xml_is_dom(Value),
 1440    !,
 1441    rdf_equal(Type, rdf:'XMLLiteral'),
 1442    tw_typed_literal(Type, Value, State, Out).
 1443tw_literal(Literal, _State, _Out) :-
 1444    type_error(rdf_literal, Literal).
 1445
 1446
 1447tw_typed_literal(Type, Value, State, Out) :-
 1448    tw_state_abbreviate_literals(State, true),
 1449    tw_abbreviated_literal(Type, Value, State, Out),
 1450    !.
 1451tw_typed_literal(Type, Value, State, Out) :-
 1452    (atom(Value) ; string(Value)),
 1453    !,
 1454    tw_quoted_string(Value, State, Out),
 1455    write(Out, '^^'),
 1456    tw_resource(Type, State, Out).
 1457tw_typed_literal(Type, Value, State, Out) :-
 1458    rdf_equal(Type, rdf:'XMLLiteral'),
 1459    !,
 1460    with_output_to(string(Tmp),
 1461                   xml_write(Value, [header(false)])),
 1462    tw_quoted_string(Tmp, State, Out),
 1463    write(Out, '^^'),
 1464    tw_resource(Type, State, Out).
 1465tw_typed_literal(Type, Value, State, Out) :-
 1466    format(string(Tmp), '~q', [Value]),
 1467    tw_quoted_string(Tmp, State, Out),
 1468    write(Out, '^^'),
 1469    tw_resource(Type, State, Out).
 tw_abbreviated_literal(+Type, +Value, +State, +Out) is semidet
Turtle abbreviated typed literals.
To be done
- : Deal with canonical forms (or is this a task of the RDF parser?
- : What if the value is not in the lexical space of the type?
 1480term_expansion((tw_abbreviated_literal(NS:Local, Value, State, Out) :- Body),
 1481               (tw_abbreviated_literal(Type, Value, State, Out) :- Body)) :-
 1482    atom(NS),
 1483    rdf_global_id(NS:Local, Type).
 1484
 1485tw_abbreviated_literal(xsd:integer, Value, State, Out) :-
 1486    (   tw_state_canonize_numbers(State, false)
 1487    ->  write(Out, Value)
 1488    ;   atom_number(Value, Int),
 1489        format(Out, '~d', [Int])
 1490    ).
 1491tw_abbreviated_literal(xsd:double, Value, State, Out) :-
 1492    (   tw_state_canonize_numbers(State, false)
 1493    ->  write(Out, Value)
 1494    ;   ValueF is float(Value),
 1495        xsd_number_string(ValueF, FloatS),
 1496        format(Out, '~s', [FloatS])
 1497    ).
 1498tw_abbreviated_literal(xsd:string, Value, State, Out) :-
 1499    tw_quoted_string(Value, State, Out).
 1500tw_abbreviated_literal(xsd:decimal, Value, _, Out) :-
 1501    format(Out, '~w', [Value]).
 1502tw_abbreviated_literal(xsd:boolean, Value, _, Out) :-
 1503    format(Out, '~w', [Value]).
 tw_quoted_string(+Atom, +State, +Out) is det
Write Atom to Out as a quoted string. We only use the single-"..." representation.
 1511tw_quoted_string(Atom, _, Out) :-
 1512    turtle:turtle_write_quoted_string(Out, Atom).
 1513
 1514
 1515                 /*******************************
 1516                 *             COMMENT          *
 1517                 *******************************/
 1518
 1519comment(State, Format, Args, Out) :-
 1520    tw_state_comment(State, true),
 1521    !,
 1522    format(Out, '~n# ', []),
 1523    format(Out, Format, Args),
 1524    format(Out, '~n', []).
 1525comment(_, _, _, _).
 1526
 1527
 1528
 1529                 /*******************************
 1530                 *           STATISTICS         *
 1531                 *******************************/
 1532
 1533inc_triple_count(State, Count) :-
 1534    tw_state_triple_count(State, C0),
 1535    C1 is C0+Count,
 1536    nb_set_triple_count_of_tw_state(C1, State).
 1537
 1538inc_subject_count(State, Count) :-
 1539    tw_state_subject_count(State, C0),
 1540    C1 is C0+Count,
 1541    nb_set_subject_count_of_tw_state(C1, State).
 1542
 1543:- multifile
 1544    prolog:message//1. 1545
 1546prolog:message(rdf(saved(File, Time, SavedSubjects, SavedTriples))) -->
 1547    [ 'Saved ~D triples about ~D subjects into '-[SavedTriples, SavedSubjects] ],
 1548    rdf_output(File),
 1549    [ ' (~3f sec)'-[Time] ].
 1550prolog:message(rdf(saved(File, Time, SavedSubjects, SavedTriples,
 1551                         SavedGraphs))) -->
 1552    [ 'Saved ~D graphs, ~D triples about ~D subjects into '-
 1553      [SavedGraphs, SavedTriples, SavedSubjects] ],
 1554    rdf_output(File),
 1555    [ ' (~3f sec)'-[Time] ].
 1556
 1557rdf_output(StreamSpec) -->
 1558    { (   StreamSpec = stream(Stream)
 1559      ->  true
 1560      ;   Stream = StreamSpec
 1561      ),
 1562      is_stream(Stream),
 1563      stream_property(Stream, file_name(File))
 1564    },
 1565    !,
 1566    [ '~p'-[File] ].
 1567rdf_output(File) -->
 1568    [ '~p'-[File] ]