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 ]).
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( , ).
true
(default), use a
for the predicate rdf:type
.
Otherwise use the full resource.true
(default false
), emit numeric datatypes using
Prolog's write to achieve canonical output.true
(default), write some informative comments
between the output segmentstrue
(default), using P-O and O-grouping.true
(default), inline bnodes that are used once.true
(default), omit the type if allowed by turtle.true
(default false
), do not print the final
informational message.true
(default false
), write [...] and (...) on a
single line.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)) ]),
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).
encoding(utf8)
,indent(0)
,tab_distance(0)
,subject_white_lines(1)
,align_prefixes(false)
,user_prefixes(false)
comment(false)
,group(false)
,single_line_bnodes(true)
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).
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 ]).
graph(+Graph)
option and instead processes one additional
option:
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 ).
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).
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 ).
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 *******************************/
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).
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).
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).
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).
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 --> "#".
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 *******************************/
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).
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 ).
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).
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 ).
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).
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).
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).
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', []).
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).
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 ).
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).
written
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 ).
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 ).
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 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).
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).
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(_, _, _).
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 *******************************/
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- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1284sort_bnodes(BNodes, Sorted, _State) :-
1285 sort(BNodes, Sorted).
1291sort_bnode_pairs(Pairs, Sorted, _State) :-
1292 sort(Pairs, Sorted).
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?
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]).
1341bnode_hash(BNode, Hash) :- 1342 term_hash(BNode, Hash). 1343 1344 1345 /******************************* 1346 * PRIMITIVES * 1347 *******************************/
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).
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).
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]).
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 1546prologmessage(rdf(saved(File, Time, SavedSubjects, SavedTriples))) --> 1547 [ 'Saved ~D triples about ~D subjects into '-[SavedTriples, SavedSubjects] ], 1548 rdf_output(File), 1549 [ ' (~3f sec)'-[Time] ]. 1550prologmessage(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] ]
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: