View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        jan@swi-prolog.org
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2025, SWI-Prolog Solutions b.v.
    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(html_decl,
   36          [ (html_meta)/1,              % +Spec
   37            html_no_content/1,          % ?Element
   38
   39            op(1150, fx, html_meta)
   40          ]).   41:- autoload(library(apply),[maplist/3,maplist/4]).   42:- if(exists_source(library(http/http_dispatch))).   43:- autoload(library(http/http_dispatch), [http_location_by_id/2]).   44:- endif.

HTML emitter analysis and IDE support

This library supports declaring DCG rules that process HTML terms. It supports the cross-referencer as well as syntax highlighting that is based on library(prolog_colour). */

   54                 /*******************************
   55                 *     META-PREDICATE SUPPORT   *
   56                 *******************************/
 html_meta +Heads is det
This directive can be used to declare that an HTML rendering rule takes HTML content as argument. It has two effects. It emits the appropriate meta_predicate/1 and instructs the built-in editor (PceEmacs) to provide proper colouring for the arguments. The arguments in Head are the same as for meta_predicate or can be constant html. For example:
:- html_meta
    page(html,html,?,?).
   72html_meta(Spec) :-
   73    throw(error(context_error(nodirective, html_meta(Spec)), _)).
   74
   75html_meta_decls(Var, _, _) :-
   76    var(Var),
   77    !,
   78    instantiation_error(Var).
   79html_meta_decls((A,B), (MA,MB), [MH|T]) :-
   80    !,
   81    html_meta_decl(A, MA, MH),
   82    html_meta_decls(B, MB, T).
   83html_meta_decls(A, MA, [MH]) :-
   84    html_meta_decl(A, MA, MH).
   85
   86html_meta_decl(Head, MetaHead,
   87               html_decl:html_meta_head(GenHead, Module, Head)) :-
   88    functor(Head, Name, Arity),
   89    functor(GenHead, Name, Arity),
   90    prolog_load_context(module, Module),
   91    Head =.. [Name|HArgs],
   92    maplist(html_meta_decl, HArgs, MArgs),
   93    MetaHead =.. [Name|MArgs].
   94
   95html_meta_decl(html, :) :- !.
   96html_meta_decl(Meta, Meta).
   97
   98system:term_expansion((:- html_meta(Heads)),
   99                      [ (:- meta_predicate(Meta))
  100                      | MetaHeads
  101                      ]) :-
  102    html_meta_decls(Heads, Meta, MetaHeads).
  103
  104:- multifile
  105    html_meta_head/3.  106
  107html_meta_colours(Head, Goal, built_in-Colours) :-
  108    Head =.. [_|MArgs],
  109    Goal =.. [_|Args],
  110    maplist(meta_colours, MArgs, Args, Colours).
  111
  112meta_colours(html, HTML, Colours) :-
  113    !,
  114    html_colours(HTML, Colours).
  115meta_colours(I, _, Colours) :-
  116    integer(I), I>=0,
  117    !,
  118    Colours = meta(I).
  119meta_colours(_, _, classify).
  120
  121html_meta_called(Head, Goal, Called) :-
  122    Head =.. [_|MArgs],
  123    Goal =.. [_|Args],
  124    meta_called(MArgs, Args, Called, []).
  125
  126meta_called([], [], Called, Called).
  127meta_called([html|MT], [A|AT], Called, Tail) :-
  128    !,
  129    phrase(called_by(A), Called, Tail1),
  130    meta_called(MT, AT, Tail1, Tail).
  131meta_called([0|MT], [A|AT], [A|CT0], CT) :-
  132    !,
  133    meta_called(MT, AT, CT0, CT).
  134meta_called([I|MT], [A|AT], [A+I|CT0], CT) :-
  135    integer(I), I>0,
  136    !,
  137    meta_called(MT, AT, CT0, CT).
  138meta_called([_|MT], [_|AT], Called, Tail) :-
  139    !,
  140    meta_called(MT, AT, Called, Tail).
  141
  142
  143
  144                 /*******************************
  145                 *      PCE EMACS SUPPORT       *
  146                 *******************************/
  147
  148:- multifile
  149    prolog_colour:goal_colours/2,
  150    prolog_colour:style/2,
  151    prolog_colour:message//1,
  152    prolog:called_by/2,
  153    prolog:xref_update_syntax/2.        % +Term, +Module
  154
  155prolog_colour:goal_colours(Goal, Colours) :-
  156    (   html_meta_head(Goal, _Module, Head)
  157    ->  true
  158    ;   dyn_html_meta_head(Goal, _Module, Head)
  159    ),
  160    html_meta_colours(Head, Goal, Colours).
  161prolog_colour:goal_colours(html_meta(_),
  162                           built_in-[meta_declarations([html])]).
  163
  164                                        % TBD: Check with do_expand!
  165html_colours(Var, classify) :-
  166    var(Var),
  167    !.
  168html_colours(\List, html_raw-[list-Colours]) :-
  169    is_list(List),
  170    !,
  171    list_colours(List, Colours).
  172html_colours(\_, html_call-[dcg]) :- !.
  173html_colours(_:Term, built_in-[classify,Colours]) :-
  174    !,
  175    html_colours(Term, Colours).
  176html_colours(&(Entity), functor-[entity(Entity)]) :- !.
  177html_colours(List, list-ListColours) :-
  178    List = [_|_],
  179    !,
  180    list_colours(List, ListColours).
  181html_colours(Var=Spec, functor-[classify,SpecColors]) :-
  182    var(Var),
  183    !,
  184    html_colours(Spec, SpecColors).
  185html_colours(Format-Args, functor-[FormatColor,ArgsColors]) :-
  186    !,
  187    format_colours(Format, FormatColor),
  188    format_arg_colours(Args, Format, ArgsColors).
  189html_colours(Term, TermColours) :-
  190    compound(Term),
  191    compound_name_arguments(Term, Name, Args),
  192    Name \== '.',
  193    !,
  194    (   Args = [One]
  195    ->  TermColours = html(Name)-ArgColours,
  196        (   html_no_content(Name)
  197        ->  attr_colours(One, ArgColours)
  198        ;   html_colours(One, Colours),
  199            ArgColours = [Colours]
  200        )
  201    ;   Args = [AList,Content]
  202    ->  TermColours = html(Name)-[AColours, Colours],
  203        attr_colours(AList, AColours),
  204        html_colours(Content, Colours)
  205    ;   TermColours = error
  206    ).
  207html_colours(_, classify).
  208
  209list_colours(Var, classify) :-
  210    var(Var),
  211    !.
  212list_colours([], []).
  213list_colours([H0|T0], [H|T]) :-
  214    !,
  215    html_colours(H0, H),
  216    list_colours(T0, T).
  217list_colours(Last, Colours) :-          % improper list
  218    html_colours(Last, Colours).
  219
  220attr_colours(Var, classify) :-
  221    var(Var),
  222    !.
  223attr_colours([], classify) :- !.
  224attr_colours(Term, list-Elements) :-
  225    Term = [_|_],
  226    !,
  227    attr_list_colours(Term, Elements).
  228attr_colours(Name=Value, built_in-[html_attribute(Name), VColour]) :-
  229    !,
  230    attr_value_colour(Value, VColour).
  231attr_colours(NS:Term, built_in-[ html_xmlns(NS),
  232                                 html_attribute(Name)-[classify]
  233                               ]) :-
  234    compound(Term),
  235    compound_name_arity(Term, Name, 1).
  236attr_colours(Term, html_attribute(Name)-[VColour]) :-
  237    compound(Term),
  238    compound_name_arity(Term, Name, 1),
  239    !,
  240    Term =.. [Name,Value],
  241    attr_value_colour(Value, VColour).
  242attr_colours(Name, html_attribute(Name)) :-
  243    atom(Name),
  244    !.
  245attr_colours(Term, classify) :-
  246    compound(Term),
  247    compound_name_arity(Term, '.', 2),
  248    !.
  249attr_colours(_, error).
  250
  251attr_list_colours(Var, classify) :-
  252    var(Var),
  253    !.
  254attr_list_colours([], []).
  255attr_list_colours([H0|T0], [H|T]) :-
  256    attr_colours(H0, H),
  257    attr_list_colours(T0, T).
  258
  259attr_value_colour(Var, classify) :-
  260    var(Var).
  261attr_value_colour(location_by_id(ID), sgml_attr_function-[Colour]) :-
  262    !,
  263    location_id(ID, Colour).
  264attr_value_colour(#(ID), sgml_attr_function-[Colour]) :-
  265    !,
  266    location_id(ID, Colour).
  267attr_value_colour(A+B, sgml_attr_function-[CA,CB]) :-
  268    !,
  269    attr_value_colour(A, CA),
  270    attr_value_colour(B, CB).
  271attr_value_colour(encode(_), sgml_attr_function-[classify]) :- !.
  272attr_value_colour(Atom, classify) :-
  273    atomic(Atom),
  274    !.
  275attr_value_colour([_|_], classify) :- !.
  276attr_value_colour(_Fmt-_Args, classify) :- !.
  277attr_value_colour(Term, classify) :-
  278    compound(Term),
  279    compound_name_arity(Term, '.', 2),
  280    !.
  281attr_value_colour(_, error).
  282
  283location_id(ID, classify) :-
  284    var(ID),
  285    !.
  286:- if(current_predicate(http_location_for_id/1)).  287location_id(ID, Class) :-
  288    (   catch(http_location_by_id(ID, Location), _, fail)
  289    ->  Class = http_location_for_id(Location)
  290    ;   Class = http_no_location_for_id(ID)
  291    ).
  292:- endif.  293location_id(_, classify).
  294
  295format_colours(Format, format_string) :- atom(Format), !.
  296format_colours(Format, format_string) :- string(Format), !.
  297format_colours(_Format, type_error(text)).
  298
  299format_arg_colours(Args, _Format, classify) :- is_list(Args), !.
  300format_arg_colours(_, _, type_error(list)).
 html_no_content(?Element)
True when Element has no content.
  306html_no_content(area).
  307html_no_content(base).
  308html_no_content(br).
  309html_no_content(col).
  310html_no_content(embed).
  311html_no_content(hr).
  312html_no_content(img).
  313html_no_content(input).
  314html_no_content(link).
  315html_no_content(meta).
  316html_no_content(param).
  317html_no_content(source).
  318html_no_content(track).
  319html_no_content(wbr).
  320
  321:- op(990, xfx, :=).                    % allow compiling without XPCE
  322:- op(200, fy, @).  323
  324prolog_colour:style(html(_),                    [colour(magenta4), bold(true)]).
  325prolog_colour:style(entity(_),                  [colour(magenta4)]).
  326prolog_colour:style(html_attribute(_),          [colour(magenta4)]).
  327prolog_colour:style(html_xmlns(_),              [colour(magenta4)]).
  328prolog_colour:style(format_string(_),           [colour(magenta4)]).
  329prolog_colour:style(sgml_attr_function,         [colour(blue)]).
  330prolog_colour:style(http_location_for_id(_),    [bold(true)]).
  331prolog_colour:style(http_no_location_for_id(_), [colour(red), bold(true)]).
  332
  333
  334prolog_colour:message(html(Element)) -->
  335    [ '~w: SGML element'-[Element] ].
  336prolog_colour:message(entity(Entity)) -->
  337    [ '~w: SGML entity'-[Entity] ].
  338prolog_colour:message(html_attribute(Attr)) -->
  339    [ '~w: SGML attribute'-[Attr] ].
  340prolog_colour:message(sgml_attr_function) -->
  341    [ 'SGML Attribute function'-[] ].
  342prolog_colour:message(http_location_for_id(Location)) -->
  343    [ 'ID resolves to ~w'-[Location] ].
  344prolog_colour:message(http_no_location_for_id(ID)) -->
  345    [ '~w: no such ID'-[ID] ].
  346
  347
  348                /*******************************
  349                *        XREF HTML_META        *
  350                *******************************/
  351
  352:- dynamic dyn_html_meta_head/3 as volatile.  353
  354prolog:xref_update_syntax((:- html_meta(Decls)), Module) :-
  355    dyn_meta_heads(Decls, Module).
  356
  357dyn_meta_heads((A,B), Module) =>
  358    dyn_meta_heads(A, Module),
  359    dyn_meta_heads(B, Module).
  360dyn_meta_heads(QHead, Module) =>
  361    strip_module(Module:QHead, M, Head),
  362    most_general_goal(Head, Gen),
  363    retractall(dyn_html_meta_head(Gen, M, _)),
  364    asserta(dyn_html_meta_head(Gen, M, Head)).
  365
  366%       prolog:called_by(+Goal, -Called)
  367%
  368%       Hook into library(pce_prolog_xref).  Called is a list of callable
  369%       or callable+N to indicate (DCG) arglist extension.
  370
  371prolog:called_by(Goal, Called) :-
  372    (   html_meta_head(Goal, _Module, Head)
  373    ->  true
  374    ;   dyn_html_meta_head(Goal, _Module, Head)
  375    ),
  376    html_meta_called(Head, Goal, Called).
  377
  378called_by(Term) -->
  379    called_by(Term, _).
  380
  381called_by(Var, _) -->
  382    { var(Var) },
  383    !,
  384    [].
  385called_by(\G, M) -->
  386    !,
  387    (   { is_list(G) }
  388    ->  called_by(G, M)
  389    ;   {atom(M)}
  390    ->  [(M:G)+2]
  391    ;   [G+2]
  392    ).
  393called_by([], _) -->
  394    !,
  395    [].
  396called_by([H|T], M) -->
  397    !,
  398    called_by(H, M),
  399    called_by(T, M).
  400called_by(M:Term, _) -->
  401    !,
  402    (   {atom(M)}
  403    ->  called_by(Term, M)
  404    ;   []
  405    ).
  406called_by(Var=Term, M) -->
  407    { var(Var) },
  408    called_by(Term, M).
  409called_by(Term, M) -->
  410    { compound(Term),
  411      !,
  412      Term =.. [_|Args]
  413    },
  414    called_by(Args, M).
  415called_by(_, _) -->
  416    []