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)  2014-2015, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(rdfa,
   36          [ read_rdfa/3,                % +Input, -RDF, +Options
   37            xml_rdfa/3                  % +XMLDom, -RDF, +Options
   38          ]).   39:- use_module(library(semweb/rdf_db),
   40	    [ rdf_register_prefix/2,
   41	      rdf_meta/1,
   42	      rdf_global_id/2,
   43	      rdf_equal/2,
   44	      rdf_is_bnode/1,
   45	      rdf_global_term/2,
   46	      rdf_transaction/2,
   47	      rdf_assert/4,
   48	      rdf_set_graph/2,
   49              op(_,_,_)
   50	    ]).   51:- use_module(library(xpath),[xpath/3, op(_,_,_)]).   52
   53:- autoload(library(apply),[maplist/3,maplist/2,exclude/3,include/3]).   54:- autoload(library(debug),[debugging/1,debug/3]).   55:- autoload(library(error),[instantiation_error/1,type_error/2]).   56:- autoload(library(gui_tracer),[gtrace/0]).   57:- autoload(library(lists),[append/2,reverse/2,member/2,append/3]).   58:- autoload(library(option),[merge_options/3,option/2,option/3]).   59:- autoload(library(prolog_stack),[backtrace/1]).   60:- autoload(library(sgml),
   61	    [ load_xml/3, load_html/3, xml_basechar/1, xml_ideographic/1,
   62	      xml_digit/1, xml_combining_char/1, xml_extender/1
   63	    ]).   64:- autoload(library(sgml_write),[xml_write/2]).   65:- autoload(library(uri),
   66	    [ uri_file_name/2, uri_components/2, uri_data/3, uri_data/4,
   67	      iri_normalized/3, iri_normalized/2, uri_normalized/3
   68	    ]).   69:- autoload(library(dcg/basics),[blanks/2,blank/2,alpha_to_lower/3]).   70:- autoload(library(http/http_open),[http_open/3]).

Extract RDF from an HTML or XML DOM

This module implements extraction of RDFa triples from parsed XML or HTML documents. It has two interfaces: read_rdfa/3 to read triples from some input (stream, file, URL) and xml_rdfa/3 to extract triples from an HTML or XML document that is already parsed with load_html/3 or load_xml/3.

See also
- http://www.w3.org/TR/2013/REC-rdfa-core-20130822/
- http://www.w3.org/TR/html-rdfa/ */
   84:- rdf_register_prefix(rdfa, 'http://www.w3.org/ns/rdfa#').   85
   86:- rdf_meta
   87    add_triple(+, r, r, o),
   88    add_incomplete_triple(+, t).
   89
   90:- discontiguous
   91    term_expansion/2.   92
   93:- predicate_options(xml_rdfa/3, 3,
   94                     [ base(atom),
   95                       anon_prefix(any),
   96                       lang(atom),
   97                       vocab(atom),
   98                       markup(atom)
   99                     ]).  100:- predicate_options(read_dom/3, 3,
  101                     [ pass_to(load_html/3, 3),
  102                       pass_to(load_xml/3, 3)
  103                     ]).  104:- predicate_options(read_rdfa/3, 3,
  105                     [ pass_to(read_dom/3, 3),
  106                       pass_to(xml_rdfa/3, 3),
  107                       pass_to(system:open/4, 4),
  108                       pass_to(http_open:http_open/3, 3)
  109                     ]).  110
  111
  112                 /*******************************
  113                 *          STREAM READING      *
  114                 *******************************/
 read_rdfa(+Input, -Triples, +Options) is det
True when Triples is a list of rdf(S,P,O) triples extracted from Input. Input is either a stream, a file name, a URL referencing a file name or a URL that is valid for http_open/3. Options are passed to open/4, http_open/3 and xml_rdfa/3. If no base is provided in Options, a base is deduced from Input.
  124read_rdfa(Input, Triples, Options) :-
  125    setup_call_cleanup(
  126        open_input(Input, In, NewOptions, Close, Options),
  127        read_dom(In, DOM, Options),
  128        close_input(Close)),
  129    merge_options(Options, NewOptions, RDFaOptions),
  130    xml_rdfa(DOM, Triples, RDFaOptions).
  131
  132open_input(Input, In, NewOptions, Close, Options) :-
  133    open_input2(Input, In, NewOptions, Close0, Options),
  134    detect_bom(In, Close0, Close).
  135
  136open_input2(stream(In), In, Options, true, _) :-
  137    !,
  138    (   stream_property(In, file_name(Name)),
  139        to_uri(Name, URI)
  140    ->  Options = [base(URI)]
  141    ;   Options = []
  142    ).
  143open_input2(In, In, Options, true, _) :-
  144    is_stream(In),
  145    !,
  146    (   stream_property(In, file_name(Name)),
  147        to_uri(Name, URI)
  148    ->  Options = [base(URI)]
  149    ;   Options = []
  150    ).
  151open_input2(URL, In, [base(URL)], close(In), Options) :-
  152    atom(URL),
  153    uri_file_name(URL, File),
  154    !,
  155    open(File, read, In, Options).
  156open_input2(URL, In, [base(Base)], close(In), Options) :-
  157    atom(URL),
  158    to_uri2(URL, Base),
  159    !,
  160    http_open(URL, In, Options).
  161open_input2(File, In, [base(URI)], close(In), Options) :-
  162    absolute_file_name(File, Path, [access(read)]),
  163    uri_file_name(URI, Path),
  164    open(Path, read, In, Options).
 detect_bom(+In, +Close0, -Close) is det
We may be loading a binary stream. In that case we want to do BOM detection.
  171detect_bom(In, Close0, Close) :-
  172    stream_property(In, type(binary)),
  173    stream_property(In, encoding(Enc)),
  174    catch(set_stream(In, encoding(bom)),_,fail),
  175    !,
  176    merge_close(Close0, set_stream(In, encoding(Enc)), Close).
  177detect_bom(_, Close, Close).
  178
  179merge_close(true, Close, Close) :- !.
  180merge_close(Close, _, Close).
  181
  182to_uri(URI0, URI) :-
  183    to_uri2(URI0, URI),
  184    !.
  185to_uri(URI0, URI) :-
  186    absolute_file_name(URI0, Path),
  187    uri_file_name(URI, Path).
  188
  189to_uri2(URI0, Base) :-
  190    uri_components(URI0, Components),
  191    uri_data(scheme, Components, Scheme),
  192    ground(Scheme),
  193    http_scheme(Scheme),
  194    !,
  195    uri_data(fragment, Components, _, Components2),
  196    uri_components(Base, Components2).
  197
  198http_scheme(http).
  199http_scheme(https).
  200
  201close_input(true).
  202close_input(close(X)) :- close(X).
  203close_input(set_stream(In, encoding(Enc))) :- set_stream(In, encoding(Enc)).
  204
  205read_dom(In, DOM, Options) :-
  206    option(dialect(Dialect), Options),
  207    !,
  208    (   xml_dialect(Dialect)
  209    ->  load_xml(stream(In), DOM, Options)
  210    ;   load_html(stream(In), DOM, Options)
  211    ).
  212read_dom(In, DOM, Options) :-
  213    peek_string(In, 1000, Start),
  214    guess_dialect(Start, Dialect),
  215    read_dom(In, DOM, [dialect(Dialect)|Options]).
  216
  217xml_dialect(xml).
  218xml_dialect(xmlns).
  219xml_dialect(svg).
  220xml_dialect(xhtml).
  221xml_dialect(xhtml5).
  222
  223guess_dialect(Start, Dialect) :-
  224    sub_string(Start, _, _, _, "<?xml"),
  225    !,
  226    Dialect = xml.
  227guess_dialect(Start, Dialect) :-
  228    sub_string(Start, _, _, _, "<html"),
  229    !,
  230    (   sub_string(Start, _, _, _, "xmlns:")
  231    ->  Dialect = xhtml
  232    ;   string_codes(Start, Codes),
  233        phrase(html_doctype(DialectFound), Codes, _)
  234    ->  Dialect = DialectFound
  235    ;   Dialect = html
  236    ).
  237guess_dialect(Start, Dialect) :-
  238    sub_string(Start, _, _, _, "<svg"),
  239    !,
  240    Dialect = svg.
  241guess_dialect(_, xml).
  242
  243html_doctype(html5) -->
  244    blanks,
  245    "<!DOCTYPE", blank, blanks, "html", blanks, ">",
  246    !.
  247html_doctype(html4) -->
  248    blanks,
  249    "<!", icase_string(`doctype`), blank, blanks, icase_string(`html`),
  250    blank, blanks,
  251    icase_string(`public`),
  252    blank,
  253    !.
  254
  255icase_string([]) --> [].
  256icase_string([H|T]) --> alpha_to_lower(H), icase_string(T).
  257
  258
  259                 /*******************************
  260                 *        DOM PROCESSING        *
  261                 *******************************/
 xml_rdfa(+DOM, -RDF, +Options)
True when RDF is a list of rdf(S,P,O) terms extracted from DOM according to the RDFa specification. Options processed:
base(+BaseURI)
URI to use for ''. Normally set to the document URI.
anon_prefix(+AnnonPrefix)
Prefix for blank nodes.
lang(+Lang)
Default for lang
vocab(+Vocab)
Default for vocab
markup(+Markup)
Markup language processed (xhtml, xml, ...)
  279xml_rdfa(DOM, _, _) :-
  280    var(DOM),
  281    !,
  282    instantiation_error(DOM).
  283xml_rdfa(DOM, RDF, Options) :-
  284    is_list(DOM),
  285    !,
  286    maplist(xml_rdfa_aux(Options), DOM, RDFList),
  287    append(RDFList, RDF).
  288xml_rdfa(DOM, RDF, Options) :-
  289    DOM = element(_,_,_),
  290    !,
  291    rdfa_evaluation_context(DOM, EvalContext, Options),
  292    process_node(DOM, EvalContext),
  293    arg(1, EvalContext.triples, List),
  294    reverse(List, RDF0),
  295    apply_patterns(RDF0, RDF).
  296% XML Processing Instruction (PI).
  297xml_rdfa(DOM, [], _) :-
  298    DOM = pi(_),
  299    !.
  300xml_rdfa(DOM, _, _) :-
  301    type_error(xml_dom, DOM).
  302
  303xml_rdfa_aux(Options, DOM, RDF) :-
  304    xml_rdfa(DOM, RDF, Options).
  305
  306process_node(DOM, EvalContext) :-
  307    rdfa_local_context(EvalContext, LocalContext),  % 7.5.1
  308    update_vocab(DOM, LocalContext),                % 7.5.2
  309    update_prefixes(DOM, LocalContext),             % 7.5.3
  310    update_lang(DOM, LocalContext),                 % 7.5.4
  311    update_subject(DOM, LocalContext),              % 7.5.5, 7.5.6
  312    emit_typeof(DOM, LocalContext),                 % 7.5.7
  313    update_list_mapping(DOM, LocalContext),         % 7.5.8
  314    step_7_5_9(DOM, LocalContext),                  % 7.5.9
  315    step_7_5_10(DOM, LocalContext),                 % 7.5.10
  316    update_property_value(DOM, LocalContext),       % 7.5.11
  317    complete_triples(LocalContext),                 % 7.5.12
  318    descent(DOM, LocalContext),                     % 7.5.13
  319    complete_lists(LocalContext),
  320    !.                % 7.5.14
  321process_node(DOM, EvalContext) :-
  322    print_message(warning, rdfa(failed(DOM, EvalContext))),
  323    (   debugging(rdfa(test))
  324    ->  gtrace,
  325        process_node(DOM, EvalContext)
  326    ;   true
  327    ).
 rdfa_evaluation_context(+DOM, -Context, +Options)
7.5.0: Create the initial evaluation context
To be done
- : derive markup from DOM
  336rdfa_evaluation_context(DOM, Context, Options) :-
  337    Context = rdfa_eval{base:Base,                  % atom
  338                        parent_subject:Base,        % atom
  339                        parent_object:null,         % null or atom
  340                        incomplete_triples:[],      % list
  341                        list_mapping:ListMapping,   % IRI --> list(List)
  342                        lang:Lang,                  % null or atom
  343                        iri_mapping:IRIMappings,    % dict
  344                        term_mapping:TermMappings,  % dict
  345                        vocab:Vocab,                % null or atom
  346                        bnode_id:bnode(1),          % integer
  347                        markup:Markup,              % Processing profile
  348                        anon_prefix:AnonPrefix,
  349                        named_bnodes:r{v:_{}},
  350                        root:DOM,                   % XML DOM
  351                        triples:triples([])},       % list
  352    empty_list_mapping(ListMapping),
  353    option(markup(Markup), Options, xhtml),
  354    base(DOM, Options, Base),
  355    default_vocab(Markup, DefaultVocab),
  356    option(lang(Lang), Options, ''),
  357    option(vocab(Vocab), Options, DefaultVocab),
  358    (   option(anon_prefix(AnonPrefix), Options)
  359    ->  true
  360    ;   atom_concat('__', Base, AnonPrefix)
  361    ),
  362    default_prefixes(Markup, DefPrefixes),
  363    mapping(prefixes(IRIMappings0), Options),
  364    put_dict(DefPrefixes, IRIMappings0, IRIMappings),
  365    mapping(terms(TermMappings), Options).
  366
  367base(DOM, _Options, Base) :-
  368    xpath(DOM, //base(@href=Base), _),
  369    !.
  370base(_DOM, Options, Base) :-
  371    option(base(Base0), Options),
  372    rdf_global_id(Base0, Base),
  373    !.
  374base(_, _, 'http://www.example.org/').
  375
  376mapping(Term, Options) :-
  377    Term =.. [Name, Value],
  378    (   TermG =.. [Name, Var],
  379        option(TermG, Options)
  380    ->  dict_create(Value, Name, Var)
  381    ;   dict_create(Value, Name, [])
  382    ).
 default_prefixes(+Markup, -Dict)
Create a default prefix map. Which prefixes are supposed to be in this map?
  389default_prefixes(Markup, _{'':DefPrefix}) :-
  390    default_prefix_mapping(Markup, DefPrefix).
 rdfa_core_prefix(?Prefix, ?URI) is nondet
RDFa initial context prefix declarations.
See also
- http://www.w3.org/2011/rdfa-context/rdfa-1.1
  398rdfa_core_prefix(dcat,    'http://www.w3.org/ns/dcat#').
  399rdfa_core_prefix(qb,      'http://purl.org/linked-data/cube#').
  400rdfa_core_prefix(grddl,   'http://www.w3.org/2003/g/data-view#').
  401rdfa_core_prefix(ma,      'http://www.w3.org/ns/ma-ont#').
  402rdfa_core_prefix(org,     'http://www.w3.org/ns/org#').
  403rdfa_core_prefix(owl,     'http://www.w3.org/2002/07/owl#').
  404rdfa_core_prefix(prov,    'http://www.w3.org/ns/prov#').
  405rdfa_core_prefix(rdf,     'http://www.w3.org/1999/02/22-rdf-syntax-ns#').
  406rdfa_core_prefix(rdfa,    'http://www.w3.org/ns/rdfa#').
  407rdfa_core_prefix(rdfs,    'http://www.w3.org/2000/01/rdf-schema#').
  408rdfa_core_prefix(rif,     'http://www.w3.org/2007/rif#').
  409rdfa_core_prefix(rr,      'http://www.w3.org/ns/r2rml#').
  410rdfa_core_prefix(sd,      'http://www.w3.org/ns/sparql-service-description#').
  411rdfa_core_prefix(skos,    'http://www.w3.org/2004/02/skos/core#').
  412rdfa_core_prefix(skosxl,  'http://www.w3.org/2008/05/skos-xl#').
  413rdfa_core_prefix(wdr,     'http://www.w3.org/2007/05/powder#').
  414rdfa_core_prefix(void,    'http://rdfs.org/ns/void#').
  415rdfa_core_prefix(wdrs,    'http://www.w3.org/2007/05/powder-s#').
  416rdfa_core_prefix(xhv,     'http://www.w3.org/1999/xhtml/vocab#').
  417rdfa_core_prefix(xml,     'http://www.w3.org/XML/1998/namespace').
  418rdfa_core_prefix(xsd,     'http://www.w3.org/2001/XMLSchema#').
  419rdfa_core_prefix(cc,      'http://creativecommons.org/ns#').
  420rdfa_core_prefix(ctag,    'http://commontag.org/ns#').
  421rdfa_core_prefix(dc,      'http://purl.org/dc/terms/').
  422rdfa_core_prefix(dcterms, 'http://purl.org/dc/terms/').
  423rdfa_core_prefix(dc11,    'http://purl.org/dc/elements/1.1/').
  424rdfa_core_prefix(foaf,    'http://xmlns.com/foaf/0.1/').
  425rdfa_core_prefix(gr,      'http://purl.org/goodrelations/v1#').
  426rdfa_core_prefix(ical,    'http://www.w3.org/2002/12/cal/icaltzd#').
  427rdfa_core_prefix(og,      'http://ogp.me/ns#').
  428rdfa_core_prefix(rev,     'http://purl.org/stuff/rev#').
  429rdfa_core_prefix(sioc,    'http://rdfs.org/sioc/ns#').
  430rdfa_core_prefix(v,       'http://rdf.data-vocabulary.org/#').
  431rdfa_core_prefix(vcard,   'http://www.w3.org/2006/vcard/ns#').
  432rdfa_core_prefix(schema,  'http://schema.org/').
  433
  434default_prefix_mapping(xhtml, 'http://www.w3.org/1999/xhtml/vocab#') :- !.
  435default_prefix_mapping(_,     'http://www.example.org/').
  436
  437default_vocab(_, '').
 rdfa_local_context(EvalContext, LocalContext)
7.5.1: Create the local context
  443rdfa_local_context(EvalContext, LocalContext) :-
  444    LocalContext = rdfa_local{skip_element:false,
  445                              new_subject:null,
  446                              current_object_resource:null,
  447                              typed_resource:null,
  448                              iri_mapping:IRIMappings,
  449                              incomplete_triples:[],
  450                              list_mapping:ListMapping,
  451                              lang:Lang,
  452                              term_mapping:TermMapping,
  453                              vocab:Vocab,
  454                              eval_context:EvalContext
  455                             },
  456    _{ iri_mapping:IRIMappings,
  457       list_mapping:ListMapping,
  458       lang:Lang,
  459       term_mapping:TermMapping,
  460       vocab:Vocab
  461     } :< EvalContext.
 update_vocab(+DOM, +Context) is det
7.5.2. Handle @vocab
  468update_vocab(DOM, Context) :-
  469    xpath(DOM, /(*(@vocab=Vocab0)), _),
  470    !,
  471    (   Vocab0 == ''
  472    ->  Vocab = ''                  % Host Language defined default?
  473    ;   iri(Vocab0, Vocab, Context)
  474    ),
  475    nb_set_dict(vocab, Context, Vocab),
  476    add_triple(Context,
  477               Context.eval_context.base,
  478               rdfa:usesVocabulary,
  479               Vocab).
  480update_vocab(_, _).
 update_prefixes(+DOM, +Context) is det
7.5.3: Update prefix map using @prefix and @xmlns. First processes xmlns:Prefix=IRI.
  487update_prefixes(DOM, Context) :-
  488    DOM=element(_,Attrs,_),
  489    xmlns_dict(Attrs, _{}, Dict0),
  490    (   xpath(DOM, /(*(@prefix=PrefixDecl)), _)
  491    ->  prefix_dict(PrefixDecl, Dict0, Dict)
  492    ;   Dict = Dict0
  493    ),
  494    Dict \= _{},
  495    !,
  496    put_dict(Dict, Context.iri_mapping, NewMapping),
  497    b_set_dict(iri_mapping, Context, NewMapping).
  498update_prefixes(_, _).
  499
  500xmlns_dict([], Dict, Dict).
  501xmlns_dict([Attr=IRI|T0], Dict0, Dict) :-
  502    (   Attr = xmlns:Name
  503    ;   atom_concat('xmlns:', Name, Attr)
  504    ),
  505    !,
  506    downcase_atom(Name, Prefix),
  507    put_dict(Prefix, Dict0, IRI, Dict1),
  508    xmlns_dict(T0, Dict1, Dict).
  509xmlns_dict([_|T0], Dict0, Dict) :-
  510    xmlns_dict(T0, Dict0, Dict).
  511
  512prefix_dict(Text, Dict0, Dict) :-
  513    atom_codes(Text, Codes),
  514    phrase(prefixes(Dict0, Dict), Codes).
 update_lang(+DOM, +Context) is det
7.5.4: Update lang
  520update_lang(DOM, Context) :-
  521    DOM=element(_,Attrs,_),
  522    (   (   memberchk(xml:lang=Lang, Attrs)         % XML with namespaces
  523        ;   memberchk('xml:lang'=Lang, Attrs)       % XML without namespaces
  524        ;   memberchk(lang=Lang, Attrs)             % HTML 5
  525        )
  526    ->  nb_set_dict(lang, Context, Lang)
  527    ;   true
  528    ),
  529    (   (   memberchk(xml:base=Base, Attrs)         % XML with namespaces
  530        ;   memberchk('xml:base'=Base, Attrs)       % XML without namespaces
  531        )
  532    ->  nb_set_dict(base, Context.eval_context, Base)
  533    ;   true
  534    ).
 update_subject(+DOM, +Context) is det
7.5.5 and 7.5.6: establish a value for new subject
  541update_subject(DOM, Context) :-
  542    DOM=element(E,Attrs,_),
  543    \+ has_attribute(rel, Attrs, Context),
  544    \+ has_attribute(rev, Attrs, Context),    % Commit to rule-set 7.5.5
  545    !,
  546    (   memberchk(property=_, Attrs),
  547        \+ memberchk(content=_, Attrs),
  548        \+ memberchk(datatype=_, Attrs)
  549    ->  (   (   about(DOM, About, Context)  % 7.5.5.1
  550            ;   About = Context.eval_context.parent_object
  551            ),
  552            About \== null
  553        ->  nb_set_dict(new_subject, Context, About)
  554        ;   true
  555        ),
  556        (   memberchk(typeof=_, Attrs)
  557        ->  (   (   iri_attr(about, Attrs, TypedIRI, Context),
  558                    TypedIRI \== null
  559                ;   DOM == Context.eval_context.root
  560                ->  iri('', TypedIRI, Context)
  561                ;   (   iri_attr(resource, Attrs, TypedIRI, Context)
  562                    ;   iri_attr(href,     Attrs, TypedIRI, Context)
  563                    ;   iri_attr(src,      Attrs, TypedIRI, Context)
  564                    ;   new_bnode(TypedIRI, Context)
  565                    ),
  566                    TypedIRI \== null
  567                ->  nb_set_dict(typed_resource, Context, TypedIRI),
  568                    nb_set_dict(current_object_resource, Context, TypedIRI)
  569                )
  570            ->  nb_set_dict(typed_resource, Context, TypedIRI)
  571            ;   true
  572            )
  573        ;   true
  574        )
  575    ;   (   new_subject_attr_2(SubjectAttr),        % 7.5.5.2
  576            memberchk(SubjectAttr=About0, Attrs),
  577            attr_convert(SubjectAttr, About0, About, Context),
  578            About \== null
  579        ->  true
  580        ;   html_root(E, Context),
  581            About = Context.eval_context.parent_object,
  582            About \== null
  583        ->  true
  584        ;   DOM == Context.eval_context.root
  585        ->  iri('', About, Context)
  586        ;   memberchk(typeof=_, Attrs)
  587        ->  new_bnode(About, Context)
  588        ;   About = Context.eval_context.parent_object,
  589            About \== null
  590        ->  (   \+ memberchk(typeof=_, Attrs)
  591            ->  nb_set_dict(skip_element, Context, true)
  592            ;   true
  593            )
  594        ),
  595        debug(rdfa(new_subject), '~w: set new_subject to ~p', [E, About]),
  596        nb_set_dict(new_subject, Context, About),
  597        (   memberchk(typeof=_, Attrs)
  598        ->  nb_set_dict(typed_resource, Context, About)
  599        ;   true
  600        )
  601    ).
  602update_subject(DOM, Context) :-
  603    DOM=element(_,Attrs,_),                 % 7.5.6
  604    (   iri_attr(about, Attrs, NewSubject, Context)
  605    ->  nb_set_dict(new_subject, Context, NewSubject),
  606        (   memberchk(typeof=_, Attrs)
  607        ->  nb_set_dict(typed_resource, Context, NewSubject)
  608        ;   true
  609        )
  610    ;   true        % was \+ memberchk(resource=_, Attrs):
  611                    % If no resource is provided ...
  612    ->  (   DOM == Context.eval_context.root
  613        ->  iri('', NewSubject, Context),
  614            nb_set_dict(new_subject, Context, NewSubject),
  615            (   memberchk(typeof=_, Attrs)
  616            ->  nb_set_dict(typed_resource, Context, NewSubject)
  617            ;   true
  618            )
  619        ;   NewSubject = Context.eval_context.parent_object,
  620            NewSubject \== null
  621        ->  nb_set_dict(new_subject, Context, NewSubject)
  622        ;   true
  623        )
  624    ),
  625    (   (   iri_attr(resource, Attrs, CurrentObjectResource, Context)
  626        ;   iri_attr(href,     Attrs, CurrentObjectResource, Context)
  627        ;   iri_attr(src,      Attrs, CurrentObjectResource, Context)
  628        ;   memberchk(typeof=_, Attrs),
  629            \+ memberchk(about=_, Attrs),
  630            new_bnode(CurrentObjectResource, Context)
  631        ),
  632        CurrentObjectResource \== null
  633    ->  nb_set_dict(current_object_resource, Context, CurrentObjectResource)
  634    ;   true
  635    ),
  636    (   memberchk(typeof=_, Attrs),
  637        \+ memberchk(about=_, Attrs)
  638    ->  nb_set_dict(typed_resource, Context,
  639                    Context.current_object_resource)
  640    ;   true
  641    ).
  642
  643new_subject_attr_2(about).
  644new_subject_attr_2(resource).
  645new_subject_attr_2(href).
  646new_subject_attr_2(src).
  647
  648html_root(head, Context) :- html_markup(Context.eval_context.markup).
  649html_root(body, Context) :- html_markup(Context.eval_context.markup).
  650
  651html_markup(html).
  652html_markup(xhtml).
 emit_typeof(+DOM, +LocalContext) is det
7.5.7: emit triples for @typeof value.
  658emit_typeof(DOM, Context) :-
  659    DOM = element(_,Attrs,_),
  660    Subject = Context.typed_resource,
  661    Subject \== null,
  662    memberchk(typeof=TypeOf, Attrs),
  663    !,
  664    iri_list(TypeOf, IRIs, Context),
  665    maplist(type_triple(Context), IRIs).
  666emit_typeof(_, _).
  667
  668type_triple(Context, IRI) :-
  669    add_triple(Context, Context.typed_resource, rdf:type, IRI).
 update_list_mapping(+DOM, +Context) is det
7.5.8: Create a list mapping if appropriate
  675update_list_mapping(_DOM, Context) :-
  676    Context.new_subject \== null,
  677    Context.new_subject \== Context.eval_context.parent_object,
  678    !,
  679    empty_list_mapping(ListMapping),
  680    b_set_dict(list_mapping, Context, ListMapping).
  681update_list_mapping(_, _).
 empty_list_mapping(-Mapping) is det
empty_list_mapping(+Mapping) is semidet
 get_list_mapping(+IRI, +Mapping, -List) is semidet
 add_list_mapping(+IRI, !Mapping, +List) is det
Manage a list mapping. Note this needs to be wrapped in a term to be able to extend the mapping while keeping its identity.
  691empty_list_mapping(list_mapping(_{})).
  692
  693get_list_mapping(IRI, list_mapping(Dict), Dict.get(IRI)).
  694
  695add_list_mapping(IRI, LM, List) :-
  696    LM = list_mapping(Dict),
  697    setarg(1, LM, Dict.put(IRI, List)).
  698
  699list_mapping_pairs(list_mapping(Dict), Pairs) :-
  700    dict_pairs(Dict, _, Pairs).
 step_7_5_9(+DOM, +Context)
  705step_7_5_9(_DOM, Context) :-
  706    Context.current_object_resource == null,
  707    !.
  708step_7_5_9(DOM, Context) :-
  709    DOM = element(_,Attrs,_),
  710    memberchk(inlist=_, Attrs),
  711    has_attribute(rel, Attrs, Rel, Context),
  712    !,
  713    iri_list(Rel, Preds, Context),
  714    CurrentObjectResource = Context.current_object_resource,
  715    maplist(add_property_list(Context, CurrentObjectResource),
  716            Preds).
  717step_7_5_9(DOM, Context) :-
  718    DOM = element(_,Attrs,_),
  719    (   has_attribute(rel, Attrs, Rel, Context),
  720        \+ memberchk(inlist=_, Attrs)
  721    ->  iri_list(Rel, RelIRIs, Context),
  722        maplist(rel_triple(Context), RelIRIs)
  723    ;   true
  724    ),
  725    (   has_attribute(rev, Attrs, Rev, Context)
  726    ->  iri_list(Rev, RevIRIs, Context),
  727        maplist(rev_triple(Context), RevIRIs)
  728    ;   true
  729    ).
  730
  731rel_triple(Context, IRI) :-
  732    add_triple(Context,
  733               Context.new_subject, IRI, Context.current_object_resource).
  734
  735rev_triple(Context, IRI) :-
  736    add_triple(Context,
  737               Context.current_object_resource, IRI, Context.new_subject).
 step_7_5_10(+DOM, +Context)
Similar to step_7_5_9, but adding to incomplete triples.
  743step_7_5_10(_DOM, Context) :-
  744    Context.current_object_resource \== null,
  745    !.
  746step_7_5_10(DOM, Context) :-
  747    DOM = element(_,Attrs,_),
  748    memberchk(inlist=_, Attrs),
  749    has_attribute(rel, Attrs, Rel, Context),
  750    !,
  751    set_current_object_resource_to_bnode(Context),
  752    iri_list(Rel, IRIs, Context),
  753    maplist(incomplete_ll_triple(Context), IRIs).
  754step_7_5_10(DOM, Context) :-
  755    DOM = element(_,Attrs,_),
  756    (   has_attribute(rel, Attrs, Rel, Context),
  757        \+ memberchk(inlist=_, Attrs)
  758    ->  iri_list(Rel, RelIRIs, Context),
  759        set_current_object_resource_to_bnode(Context),
  760        maplist(incomplete_rel_triple(Context), RelIRIs)
  761    ;   true
  762    ),
  763    (   has_attribute(rev, Attrs, Rev, Context)
  764    ->  iri_list(Rev, RevIRIs, Context),
  765        set_current_object_resource_to_bnode(Context),
  766        maplist(incomplete_rev_triple(Context), RevIRIs)
  767    ;   true
  768    ).
  769
  770set_current_object_resource_to_bnode(Context) :-
  771    new_bnode(BNode, Context),
  772    b_set_dict(current_object_resource, Context, BNode).
  773
  774incomplete_ll_triple(Context, IRI) :-
  775    LM = Context.list_mapping,
  776    (   get_list_mapping(IRI, LM, LL)
  777    ->  true
  778    ;   LL = list([]),
  779        add_list_mapping(IRI, LM, LL)
  780    ),
  781    add_incomplete_triple(Context, _{list:LL, direction:none}).
  782
  783incomplete_rel_triple(Context, IRI) :-
  784    add_incomplete_triple(Context, _{predicate:IRI, direction:forward}).
  785
  786incomplete_rev_triple(Context, IRI) :-
  787    add_incomplete_triple(Context, _{predicate:IRI, direction:reverse}).
 update_property_value(+DOM, +Context) is det
7.5.11: establish current property value.
  794update_property_value(DOM, Context) :-
  795    DOM = element(Element,Attrs,Content),
  796    memberchk(property=PropSpec, Attrs),
  797    !,
  798    iri_list(PropSpec, Preds, Context),
  799    (   memberchk(datatype=DTSpec, Attrs)
  800    ->  (   DTSpec \== '',
  801            term_or_curie_or_absiri(DTSpec, DataType, Context),
  802            DataType \== null
  803        ->  (   (   rdf_equal(rdf:'XMLLiteral', DataType)
  804                ;   rdf_equal(rdf:'HTML', DataType)
  805                )
  806            ->  content_xml(Content, Text)
  807            ;   content_text(DOM, Text, Context)
  808            ),
  809            Obj0 = literal(type(DataType, Text))
  810        ;   content_text(DOM, Text, Context),
  811            Obj0 = literal(Text)
  812        )
  813    ;   memberchk(content=Text, Attrs)
  814    ->  Obj0 = literal(Text)
  815    ;   \+ has_attribute(rel, Attrs, Context),
  816        \+ has_attribute(rev, Attrs, Context),
  817        %\+ memberchk(content=_, Attrs),    % already guaranteed
  818        (   iri_attr(resource, Attrs, Obj0, Context)
  819        ;   iri_attr(href,     Attrs, Obj0, Context)
  820        ;   iri_attr(src,      Attrs, Obj0, Context)
  821        ),
  822        Obj0 \== null
  823    ->  true
  824    ;   (   memberchk(datetime=DateTime, Attrs)
  825        ;   Element == time,
  826            Content = [DateTime]
  827        ),
  828        html_markup(Context.eval_context.markup)
  829    ->  (   date_time_type(DateTime, DataType)
  830        ->  Obj0 = literal(type(DataType, DateTime))
  831        ;   Obj0 = literal(DateTime)
  832        )
  833    ;   memberchk(typeof=_, Attrs),
  834        \+ memberchk(about=_, Attrs)
  835    ->  Obj0 = Context.typed_resource
  836    ;   content_text(Content, Text, Context), % "as a plain literal"???
  837        Obj0 = literal(Text)
  838    ),
  839    (   Obj0 = literal(Text),
  840        atomic(Text),
  841        Context.lang \== ''
  842    ->  Obj = literal(lang(Context.lang, Text))
  843    ;   Obj = Obj0
  844    ),
  845    (   memberchk(inlist=_, Attrs)
  846    ->  maplist(add_property_list(Context, Obj), Preds)
  847    ;   NewSubject = Context.new_subject,
  848        maplist(add_property(Context, NewSubject, Obj), Preds)
  849    ).
  850update_property_value(_, _).
  851
  852add_property_list(Context, Obj, Pred) :-
  853    LM = Context.list_mapping,
  854    (   get_list_mapping(Pred, LM, LL)
  855    ->  LL = list(Old),
  856        setarg(1, LL, [Obj|Old])
  857    ;   add_list_mapping(Pred, LM, list([Obj]))
  858    ).
  859
  860add_property(Context, Subject, Object, Pred) :-
  861    add_triple(Context, Subject, Pred, Object).
  862
  863content_text(element(_,Attrs,_), Text, _Context) :-
  864    memberchk(content=Text, Attrs),
  865    !.
  866content_text(element(_,Attrs,_), Text, Context) :-
  867    memberchk(datetime=Text, Attrs),
  868    html_markup(Context.eval_context.markup),
  869    !.
  870content_text(element(_,_,Content), Text, _Context) :-
  871    !,
  872    phrase(text_nodes(Content), Texts),
  873    atomic_list_concat(Texts, Text).
  874content_text(Content, Text, _Context) :-
  875    !,
  876    phrase(text_nodes(Content), Texts),
  877    atomic_list_concat(Texts, Text).
  878
  879text_nodes([]) --> !.
  880text_nodes([H|T]) --> !, text_nodes(H), text_nodes(T).
  881text_nodes(element(_,_,Content)) --> !, text_nodes(Content).
  882text_nodes(CDATA) --> [CDATA].
  883
  884content_xml(DOM, Text) :-
  885    with_output_to(atom(Text), xml_write(DOM, [header(false)])).
 complete_triples(+Context)
7.5.12: Complete incomplete triples
  891complete_triples(Context) :-
  892    Context.skip_element == false,
  893    Context.new_subject \== null,
  894    Context.eval_context.incomplete_triples \== [],
  895    !,
  896    reverse(Context.eval_context.incomplete_triples, Incomplete),
  897    maplist(complete_triple(Context), Incomplete).
  898complete_triples(_).
  899
  900complete_triple(Context, Dict) :-
  901    complete_triple(Dict.direction, Dict, Context).
  902
  903complete_triple(none, Dict, Context) :-
  904    List = Dict.list,
  905    List = list(Old),
  906    setarg(1, List, [Context.new_subject|Old]).
  907complete_triple(forward, Dict, Context) :-
  908    add_triple(Context,
  909               Context.eval_context.parent_subject,
  910               Dict.predicate,
  911               Context.new_subject).
  912complete_triple(reverse, Dict, Context) :-
  913    add_triple(Context,
  914               Context.new_subject,
  915               Dict.predicate,
  916               Context.eval_context.parent_subject).
 descent(DOM, Context)
7.5.13: Descent into the children
  923descent(element(_,_,Content), Context) :-
  924    (   Context.skip_element == true
  925    ->  maplist(descent_skip(Context), Content)
  926    ;   maplist(descent_no_skip(Context), Content)
  927    ).
  928
  929descent_skip(Context, DOM) :-
  930    DOM = element(E,_,_),
  931    !,
  932    debug(rdfa(descent), 'skip: ~w: new_subject=~p',
  933          [E, Context.new_subject]),
  934    process_node(DOM, Context.eval_context.put(
  935                          _{ lang:Context.lang,
  936                             vocab:Context.vocab,
  937                             iri_mapping:Context.iri_mapping
  938                           })).
  939descent_skip(_, _).
  940
  941descent_no_skip(Context, DOM) :-
  942    DOM = element(E,_,_),
  943    !,
  944    (   ParentSubject = Context.new_subject,
  945        ParentSubject \== null
  946    ->  true
  947    ;   ParentSubject = Context.eval_context.parent_subject
  948    ),
  949    (   ParentObject = Context.current_object_resource,
  950        ParentObject \== null
  951    ->  true
  952    ;   ParentObject = ParentSubject
  953    ),
  954    debug(rdfa(descent), 'no skip: ~w: parent subject = ~p, object = ~p',
  955          [E, ParentSubject, ParentObject]),
  956    process_node(DOM, Context.eval_context.put(
  957                          _{ parent_subject:ParentSubject,
  958                             parent_object:ParentObject,
  959                             iri_mapping:Context.iri_mapping,
  960                             incomplete_triples:Context.incomplete_triples,
  961                             list_mapping:Context.list_mapping,
  962                             lang:Context.lang,
  963                             vocab:Context.vocab
  964                            })).
  965descent_no_skip(_, _).
 complete_lists(+Context) is det
7.5.14: Complete possibly pending lists
  971complete_lists(Context) :-
  972    empty_list_mapping(Context.list_mapping),
  973    !.
  974complete_lists(Context) :-
  975    (   CurrentSubject = Context.new_subject,
  976        CurrentSubject \== null
  977    ->  true
  978    ;   CurrentSubject = Context.eval_context.base
  979    ),
  980    list_mapping_pairs(Context.list_mapping, Pairs),
  981    maplist(complete_list(Context, CurrentSubject), Pairs).
  982
  983complete_list(Context, _, IRI-_) :-
  984    get_list_mapping(IRI, Context.eval_context.list_mapping, _),
  985    !.
  986complete_list(Context, CurrentSubject, IRI-list(List0)) :-
  987    reverse(List0, List),
  988    emit_list(List, ListURI, Context),
  989    add_triple(Context, CurrentSubject, IRI, ListURI).
  990
  991emit_list([], NIL, _) :-
  992    rdf_equal(NIL, rdf:nil).
  993emit_list([H|T], URI, Context) :-
  994    emit_list(T, TailURI, Context),
  995    new_bnode(URI, Context),
  996    add_triple(Context, URI, rdf:first, H),
  997    add_triple(Context, URI, rdf:rest, TailURI).
 has_attribute(+Name, +Attrs, +Context) is semidet
 has_attribute(+Name, +Attrs, -Value, +Context) is semidet
True if Attrs contains Name. We sometimes need to ignore Attributes if their value is invalid.
See also
- HTML+RDFa, 3.1 Additional RDFa Processing Rules, point 7.
 1008has_attribute(Name, Attrs, Context) :-
 1009    has_attribute(Name, Attrs, _, Context).
 1010
 1011has_attribute(rel, Attrs, Rel, Context) :-
 1012    memberchk(rel=Rel, Attrs),
 1013    html_markup(Context.eval_context.markup),
 1014    memberchk(property=_, Attrs),
 1015    !,
 1016    html_non_empty_rel(Rel, Context).
 1017has_attribute(rev, Attrs, Rev, Context) :-
 1018    memberchk(rev=Rev, Attrs),
 1019    html_markup(Context.eval_context.markup),
 1020    memberchk(property=_, Attrs),
 1021    !,
 1022    html_non_empty_rel(Rev, Context).
 1023has_attribute(Name, Attrs, Value, _Context) :-
 1024    memberchk(Name=Value, Attrs).
 1025
 1026html_non_empty_rel(Spec, Context) :-
 1027    Sep = "\s\t\n\r",
 1028    split_string(Spec, Sep, Sep, SpecList),
 1029    member(Spec1, SpecList),
 1030    safe_curie_or_curie_or_absiri(Spec1, _, Context),
 1031    !.
 iri_attr(+AttName, +Attrs, -IRI, +Context) is semidet
 1036iri_attr(Name, Attrs, IRI, Context) :-
 1037    memberchk(Name=IRI0, Attrs),
 1038    attr_convert(Name, IRI0, IRI, Context).
 1039
 1040attr_convert(about, Spec, IRI, Context) :-
 1041    safe_curie_or_curie_or_iri(Spec, IRI, Context).
 1042attr_convert(href, Spec, IRI, Context) :-
 1043    iri(Spec, IRI, Context).
 1044attr_convert(src, Spec, IRI, Context) :-
 1045    iri(Spec, IRI, Context).
 1046attr_convert(resource, Spec, IRI, Context) :-
 1047    safe_curie_or_curie_or_iri(Spec, IRI, Context).
 1048attr_convert(vocab, Spec, IRI, Context) :-
 1049    iri(Spec, IRI, Context).
 1050attr_convert(datatype, Spec, IRI, Context) :-
 1051    term_or_curie_or_absiri(Spec, IRI, Context).
 1052
 1053
 1054about(DOM, About, Context) :-
 1055    DOM=element(_,Attrs,_),
 1056    (   memberchk(about=About0, Attrs)
 1057    ->  safe_curie_or_curie_or_iri(About0, About, Context)
 1058    ;   DOM == Context.eval_context.root
 1059    ->  iri('', About, Context)
 1060    ).
 new_bnode(-BNode, +Context) is det
Create a new blank node. Note that the current id is kept in a term to avoid copying the counter on the descent step.
 1067new_bnode(BNode, Context) :-
 1068    EvalCtx = Context.eval_context,
 1069    Node = EvalCtx.bnode_id,
 1070    arg(1, Node, Id),
 1071    succ(Id, Id1),
 1072    nb_setarg(1, Node, Id1),
 1073    Prefix = EvalCtx.anon_prefix,
 1074    (   atom(Prefix)
 1075    ->  atom_concat(Prefix, Id, BNode)
 1076    ;   BNode = bnode(Id)
 1077    ).
 iri_list(+Spec, -IRIs, +Context) is det
True when IRIs is a list of fulfy qualified IRIs from Spec
 1083iri_list(Spec, IRIs, Context) :-
 1084    Sep = "\s\t\n\r",
 1085    split_string(Spec, Sep, Sep, SpecList),
 1086    (   SpecList == [""]
 1087    ->  IRIs = []
 1088    ;   maplist(ctx_to_iri(Context), SpecList, IRIs0),
 1089        exclude(==(null), IRIs0, IRIs)
 1090    ).
 1091
 1092ctx_to_iri(Context, Spec, IRI) :-
 1093    term_or_curie_or_absiri(Spec, IRI, Context).
 iri(+Spec, -IRI, +Context)
Used for @href and @src attributes
 1099iri(Spec, IRI, Context) :-
 1100    iri_normalized(Spec, Context.eval_context.base, IRI).
 1101
 1102abs_iri(Spec, IRI) :-
 1103    uri_components(Spec, Components),
 1104    uri_data(authority, Components, Authority), nonvar(Authority),
 1105    uri_data(scheme,    Components, Scheme),    nonvar(Scheme),
 1106    !,
 1107    iri_normalized(Spec, IRI).
 safe_curie_or_curie_or_iri(+Spec, -IRI, +Context) is det
Implement section 7.4, CURIE and IRI Processing. Used for @about and @resource
 1115safe_curie_or_curie_or_iri(Spec, IRI, Context) :-
 1116    safe_curie_or_curie_or_absiri(Spec, IRI, Context),
 1117    !.
 1118safe_curie_or_curie_or_iri(Spec, IRI, Context) :-
 1119    uri_normalized(Spec, Context.eval_context.base, IRI).
 1120
 1121safe_curie_or_curie_or_absiri(Spec, IRI, _Context) :-
 1122    abs_iri(Spec, IRI0),
 1123    !,
 1124    IRI = IRI0.
 1125safe_curie_or_curie_or_absiri(Spec, IRI, Context) :-
 1126    atom_codes(Spec, Codes),
 1127    (   safe_curie(Codes, Curie)
 1128    ->  (   phrase(curie(IRI, Context), Curie)
 1129        ->  true
 1130        ;   IRI = null
 1131        )
 1132    ;   phrase(curie(IRI, Context), Codes)
 1133    ).
 1134
 1135safe_curie(Codes, Curie) :-
 1136    append([0'[|Curie], `]`, Codes).
 1137
 1138curie(IRI, Context) -->
 1139    "_:", !, reference_or_empty(Reference),
 1140    {   IRI = Context.eval_context.named_bnodes.v.get(Reference)
 1141    ->  true
 1142    ;   new_bnode(IRI, Context),
 1143        b_set_dict(v, Context.eval_context.named_bnodes,
 1144                   Context.eval_context.named_bnodes.v.put(Reference, IRI))
 1145    }.
 1146curie(IRI, Context) -->
 1147    ":", !, reference_or_empty(Reference),
 1148    { atom_concat(Context.iri_mapping.get(''), Reference, IRI) }.
 1149curie(IRI, Context) -->
 1150    nc_name(Prefix), ":", !, reference_or_empty(Reference),
 1151    {   atom_concat(Context.iri_mapping.get(Prefix), Reference, IRI0)
 1152    ->  IRI = IRI0
 1153    ;   rdfa_core_prefix(Prefix, URIPrefix)
 1154    ->  atom_concat(URIPrefix, Reference, IRI)
 1155    }.
 term_or_curie_or_absiri(+Spec, -IRI, +Context) is det
Used for @datatype and @property, @typeof, @rel and @rev
 1161term_or_curie_or_absiri(Spec, IRI, _Context) :-
 1162    abs_iri(Spec, IRI0),
 1163    !,
 1164    IRI = IRI0.
 1165term_or_curie_or_absiri(Spec, IRI, Context) :-
 1166    atom_codes(Spec, Codes),
 1167    (   phrase(term(Term), Codes),
 1168        downcase_atom(Term, LwrCase)
 1169    ->  (   Vocab = Context.vocab,
 1170            Vocab \== ''
 1171        ->  atom_concat(Vocab, Term, IRI)
 1172        ;   term_iri(LwrCase, Context.eval_context.markup, IRI0)
 1173        ->  IRI = IRI0
 1174        ;   IRI = Context.term_mapping.get(Term)
 1175        ->  true
 1176        ;   dict_pairs(Context.term_mapping, _Tag, Pairs),
 1177            member(TermCaps-IRI, Pairs),
 1178            downcase_atom(TermCaps, LwrCase)
 1179        ->  true
 1180        ;   IRI = null
 1181        )
 1182    ;   phrase(curie(IRI, Context), Codes)
 1183    ->  true
 1184    ;   uri_normalized(Spec, Context.eval_context.base, IRI)
 1185    ).
 term_iri(?Term, ?Markup, ?IRI)
See also
- http://www.w3.org/2011/rdfa-context/xhtml-rdfa-1.1
 1191term_expansion(term_iri(Term, Markup), term_iri(Term, Markup, URI)) :-
 1192    default_prefix_mapping(Markup, Prefix),
 1193    atom_concat(Prefix, Term, URI).
 1194
 1195term_iri(alternate,  xhtml).
 1196term_iri(appendix,   xhtml).
 1197term_iri(cite,       xhtml).
 1198term_iri(bookmark,   xhtml).
 1199term_iri(contents,   xhtml).
 1200term_iri(chapter,    xhtml).
 1201term_iri(copyright,  xhtml).
 1202term_iri(first,      xhtml).
 1203term_iri(glossary,   xhtml).
 1204term_iri(help,       xhtml).
 1205term_iri(icon,       xhtml).
 1206term_iri(index,      xhtml).
 1207term_iri(last,       xhtml).
 1208term_iri(meta,       xhtml).
 1209term_iri(next,       xhtml).
 1210term_iri(prev,       xhtml).
 1211term_iri(previous,   xhtml).
 1212term_iri(section,    xhtml).
 1213term_iri(start,      xhtml).
 1214term_iri(stylesheet, xhtml).
 1215term_iri(subsection, xhtml).
 1216term_iri(top,        xhtml).
 1217term_iri(up,         xhtml).
 1218term_iri(p3pv1,      xhtml).
 1219
 1220term_iri(describedby, _, 'http://www.w3.org/2007/05/powder-s#describedby').
 1221term_iri(license,     _, 'http://www.w3.org/1999/xhtml/vocab#license').
 1222term_iri(role,        _, 'http://www.w3.org/1999/xhtml/vocab#role').
 1223
 1224                 /*******************************
 1225                 *           GRAMMARS           *
 1226                 *******************************/
 1227
 1228prefixes(Dict0, Dict) -->
 1229    ws, nc_name(Name), ws, ":", ws, reference(IRI), !, ws,
 1230    prefixes(Dict0.put(Name,IRI), Dict).
 1231prefixes(Dict, Dict) --> [].
 1232
 1233ws --> ws1, !, ws.
 1234ws --> [].
 1235
 1236ws1 --> " ".
 1237ws1 --> "\t".
 1238ws1 --> "\r".
 1239ws1 --> "\n".
 1240
 1241nc_name(Name) -->
 1242    [H], {nc_name_start_code(H)},
 1243    nc_name_codes(Codes),
 1244    { atom_codes(Name0, [H|Codes]),
 1245      downcase_atom(Name0, Name)
 1246    }.
 term(-Term)//
7.4.3
 1252term(Term) -->
 1253    [H], {nc_name_start_code(H)},
 1254    term_codes(Codes),
 1255    { atom_codes(Term, [H|Codes])
 1256    }.
 1257
 1258
 1259nc_name_codes([H|T]) --> nc_name_code(H), !, nc_name_codes(T).
 1260nc_name_codes([]) --> [].
 1261
 1262nc_name_code(H) --> [H], {nc_name_code(H)}.
 1263
 1264term_codes([H|T]) --> term_code(H), !, term_codes(T).
 1265term_codes([]) --> [].
 1266
 1267term_code(H) --> [H], {term_code(H)}.
 1268
 1269nc_name_start_code(0':) :- !, fail.
 1270nc_name_start_code(C) :- xml_basechar(C), !.
 1271nc_name_start_code(C) :- xml_ideographic(C).
 1272
 1273nc_name_code(0':) :- !, fail.
 1274nc_name_code(C) :- xml_basechar(C), !.
 1275nc_name_code(C) :- xml_digit(C), !.
 1276nc_name_code(C) :- xml_ideographic(C), !.
 1277nc_name_code(C) :- xml_combining_char(C), !.
 1278nc_name_code(C) :- xml_extender(C), !.
 1279
 1280term_code(0'/) :- !.
 1281term_code(C) :- nc_name_code(C).
 1282
 1283reference(IRI) -->
 1284    [H],
 1285    reference_codes(T),
 1286    { atom_codes(IRI, [H|T]) }.
 1287
 1288reference_codes([])    --> ws1, !.
 1289reference_codes([H|T]) --> [H], !, reference_codes(T).
 1290reference_codes([]) --> [].
 1291
 1292reference_or_empty(IRI) -->
 1293    reference_codes(Codes),
 1294    { atom_codes(IRI, Codes) }.
 date_time_type(+DateTime, -DataType) is semidet
True when DataType is the xsd type that matches the lexical representation of DateTime
 1302date_time_type(DateTime, DataType) :-
 1303    atom_codes(DateTime, Codes),
 1304    phrase(date_time_type(DataType), Codes).
 1305
 1306date_time_type(DT) --> duration,   !, { rdf_equal(DT, xsd:duration) }.
 1307date_time_type(DT) --> date_time,  !, { rdf_equal(DT, xsd:dateTime) }.
 1308date_time_type(DT) --> date,       !, { rdf_equal(DT, xsd:date) }.
 1309date_time_type(DT) --> time,       !, { rdf_equal(DT, xsd:time) }.
 1310date_time_type(DT) --> gyearmonth, !, { rdf_equal(DT, xsd:gYearMonth) }.
 1311date_time_type(DT) --> gyear,      !, { rdf_equal(DT, xsd:gYear) }.
 1312
 1313duration   --> opt_minus, "P",
 1314    opt_dy, opt_dm, opt_dd,
 1315    (   "T"
 1316    ->  opt_dh, opt_dm, opt_ds
 1317    ;   ""
 1318    ).
 1319
 1320date_time  --> opt_minus, yyyy, "-", !, mM, "-", dd,
 1321    "T", hh, ":", mm, ":", ss, opt_fraction, opt_zzzzzz.
 1322date       --> opt_minus, yyyy, "-", !, mM, "-", dd.
 1323time       --> hh, ":", mm, ":", ss, opt_fraction.
 1324gyearmonth --> opt_minus, yyyy, "-", !, mM.
 1325gyear      --> opt_minus, yyyy.
 1326
 1327opt_minus --> "-", !.
 1328opt_minus --> "".
 1329
 1330yyyy --> dnzs, d, d, d, d.
 1331
 1332dnzs --> "".
 1333dnzs --> dnz, dnzs.
 1334
 1335opt_fraction --> ".", !, ds.
 1336opt_fraction --> "".
 1337
 1338mM --> d(V1), d(V2), { M is V1*10+V2, M >= 1, M =< 12 }.
 1339dd --> d(V1), d(V2), { M is V1*10+V2, M >= 1, M =< 31 }.
 1340hh --> d(V1), d(V2), { M is V1*10+V2, M =< 23 }.
 1341mm --> d(V1), d(V2), { M is V1*10+V2, M =< 59 }.
 1342ss --> d(V1), d(V2), { M is V1*10+V2, M =< 59 }.
 1343
 1344d(V) --> [D], { between(0'0, 0'9, D), V is D-0'0 }.
 1345d    --> [D], { between(0'0, 0'9, D) }.
 1346dnz  --> [D], { between(0'1, 0'9, D) }.
 1347
 1348ds --> d, !, ds.
 1349ds --> "".
 1350
 1351opt_zzzzzz --> sign, hh, ":", mm.
 1352opt_zzzzzz --> "Z".
 1353opt_zzzzzz --> "".
 1354
 1355sign --> "+".
 1356sign --> "-".
 1357
 1358opt_dy --> ( int, "Y" | "" ).
 1359opt_dm --> ( int, "M" | "" ).
 1360opt_dd --> ( int, "D" | "" ).
 1361opt_dh --> ( int, "H" | "" ).
 1362opt_ds --> ( int, ("." -> int ; ""), "S" | "" ).
 1363
 1364int --> d, ds.
 1365
 1366                 /*******************************
 1367                 *           TRIPLES            *
 1368                 *******************************/
 add_triple(+Context, +S, +P, +O) is det
Add a triple to the global evaluation context. Triples are embedded in a term, so we can use setarg/3 on the list, while the evaluation context is copied for descending the node hierarchy.
 1377add_triple(Context, S, P, O) :-
 1378    (   debugging(rdfa(triple))
 1379    ->  debug(rdfa(triple), 'Added { ~p ~p ~p }', [S,P,O]),
 1380        backtrace(4)
 1381    ;   true
 1382    ),
 1383    valid_subject(S),
 1384    valid_predicate(P),
 1385    valid_object(O),
 1386    !,
 1387    Triples = Context.eval_context.triples,
 1388    arg(1, Triples, Old),
 1389    setarg(1, Triples, [rdf(S,P,O)|Old]).
 1390add_triple(_, _, _, _).                 % ignored invalid triple.
 1391
 1392valid_subject(S)   :- S \== null.
 1393valid_predicate(P) :- P \== null, \+ rdf_is_bnode(P).
 1394valid_object(O)    :- O \== null, ( atom(O) -> true ; valid_literal(O) ).
 1395
 1396valid_literal(literal(Plain)) :-
 1397    atom(Plain),
 1398    !.
 1399valid_literal(literal(type(T, _))) :-
 1400    !,
 1401    T \== null.
 1402valid_literal(literal(lang(_,_))).
 1403
 1404add_incomplete_triple(Context, Dict) :-
 1405    debug(rdfa(incomplete), 'Incomplete: ~p', [Dict]),
 1406    b_set_dict(incomplete_triples, Context,
 1407               [ Dict
 1408               | Context.incomplete_triples
 1409               ]).
 1410
 1411
 1412                 /*******************************
 1413                 *            PATTERNS          *
 1414                 *******************************/
 apply_patterns(+TriplesIn, -TriplesOut) is det
Apply RDFa patterns. We need several passes do deal with ordering issues and the possibility that patterns are invalid:
  1. find patterns from rdf(_,rdfa:copy,Pattern)
  2. collect the properties for these patterns and delete patterns that do not have rdf:type rdfa:Pattern.
  3. Actually copy the patterns and delete the patterns themselves.
 1426apply_patterns(TriplesIn, TriplesOut) :-
 1427    referenced_patterns(TriplesIn, Pairs),
 1428    (   Pairs == []
 1429    ->  TriplesOut = TriplesIn
 1430    ;   sort(Pairs, UniquePairs),
 1431        dict_pairs(Dict, _, UniquePairs),
 1432        pattern_properties(TriplesIn, Dict),
 1433        delete_invalid_patterns(Dict, Patterns),
 1434        phrase(apply_patterns(TriplesIn, Patterns), TriplesOut)
 1435    ).
 1436
 1437term_expansion(TIn, TOut) :-
 1438    rdf_global_term(TIn, TOut).
 1439
 1440referenced_patterns([], []).
 1441referenced_patterns([rdf(_,rdfa:copy,O)|T0], [O-[]|T]) :-
 1442    !,
 1443    referenced_patterns(T0, T).
 1444referenced_patterns([_|T0], T) :-
 1445    referenced_patterns(T0, T).
 1446
 1447pattern_properties([], _).
 1448pattern_properties([rdf(S,P,O)|T], Dict) :-
 1449    ignore(b_set_dict(S, Dict, [P-O|Dict.get(S)])),
 1450    pattern_properties(T, Dict).
 1451
 1452delete_invalid_patterns(Patterns0, Patterns) :-
 1453    dict_pairs(Patterns0, Tag, Pairs0),
 1454    include(rdfa_pattern, Pairs0, Pairs),
 1455    dict_pairs(Patterns,  Tag, Pairs).
 1456
 1457rdfa_pattern(_-PO) :-
 1458    memberchk((rdf:type)-(rdfa:'Pattern'), PO).
 1459
 1460apply_patterns([], _) --> [].
 1461apply_patterns([rdf(S,rdfa:copy,O)|T0], Dict) -->
 1462    !,
 1463    copy_pattern(Dict.O, S),
 1464    apply_patterns(T0, Dict).
 1465apply_patterns([rdf(S,_,_)|T0], Dict) -->
 1466    { _ = Dict.get(S) },
 1467    !,
 1468    apply_patterns(T0, Dict).
 1469apply_patterns([H|T], Dict) -->
 1470    [H],
 1471    apply_patterns(T, Dict).
 1472
 1473copy_pattern([], _) --> [].
 1474copy_pattern([(rdf:type)-(rdfa:'Pattern')|T], S) -->
 1475    !,
 1476    copy_pattern(T, S).
 1477copy_pattern([P-O|T], S) -->
 1478    [rdf(S,P,O)],
 1479    copy_pattern(T, S).
 1480
 1481
 1482                 /*******************************
 1483                 *       HOOK INTO RDF-DB       *
 1484                 *******************************/
 1485
 1486:- multifile
 1487    rdf_db:rdf_load_stream/3,
 1488    rdf_db:rdf_file_type/2.
 rdf_db:rdf_load_stream(+Format, +Stream, :Options)
Register library(semweb/rdfa) as loader for HTML RDFa files.
To be done
- Which options need to be forwarded to read_rdfa/3?
 1496rdf_db:rdf_load_stream(rdfa, Stream, _Module:Options1):-
 1497    rdf_db:graph(Options1, Graph),
 1498    atom_concat('__', Graph, BNodePrefix),
 1499    merge_options([anon_prefix(BNodePrefix)], Options1, Options2),
 1500    read_rdfa(Stream, Triples, Options2),
 1501    rdf_transaction(( forall(member(rdf(S,P,O), Triples),
 1502                             rdf_assert(S, P, O, Graph)),
 1503                      rdf_set_graph(Graph, modified(false))
 1504                    ),
 1505                    parse(Graph)).
 1506
 1507rdf_db:rdf_file_type(html, rdfa)