View source with formatted 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)  2006-2025, University of Amsterdam
    7                              VU University Amsterdam
    8                              SWI-Prolog Solutions b.v.
    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(prolog_source,
   38          [ prolog_read_source_term/4,  % +Stream, -Term, -Expanded, +Options
   39            read_source_term_at_location/3, %Stream, -Term, +Options
   40            prolog_file_directives/3,   % +File, -Directives, +Options
   41            prolog_open_source/2,       % +Source, -Stream
   42            prolog_close_source/1,      % +Stream
   43            prolog_canonical_source/2,  % +Spec, -Id
   44
   45            load_quasi_quotation_syntax/2, % :Path, +Syntax
   46
   47            file_name_on_path/2,        % +File, -PathSpec
   48            file_alias_path/2,          % ?Alias, ?Dir
   49            path_segments_atom/2,       % ?Segments, ?Atom
   50            directory_source_files/3,   % +Dir, -Files, +Options
   51            valid_term_position/2       % +Term, +TermPos
   52          ]).   53:- use_module(library(debug), [debug/3, assertion/1]).   54:- autoload(library(apply), [maplist/2, maplist/3, foldl/4]).   55:- autoload(library(error), [domain_error/2, is_of_type/2]).   56:- autoload(library(lists), [member/2, last/2, select/3, append/3, selectchk/3]).   57:- autoload(library(operators), [push_op/3, push_operators/1, pop_operators/0]).   58:- autoload(library(option), [select_option/4, option/3, option/2]).   59:- autoload(library(modules),[in_temporary_module/3]).   60
   61
   62/** <module> Examine Prolog source-files
   63
   64This module provides predicates  to  open,   close  and  read terms from
   65Prolog source-files. This may seem  easy,  but   there  are  a couple of
   66problems that must be taken care of.
   67
   68        * Source files may start with #!, supporting PrologScript
   69        * Embedded operators declarations must be taken into account
   70        * Style-check options must be taken into account
   71        * Operators and style-check options may be implied by directives
   72        * On behalf of the development environment we also wish to
   73          parse PceEmacs buffers
   74
   75This module concentrates these issues  in   a  single  library. Intended
   76users of the library are:
   77
   78        $ prolog_xref.pl :   The Prolog cross-referencer
   79        $ prolog_clause.pl : Get details about (compiled) clauses
   80        $ prolog_colour.pl : Colourise source-code
   81        $ PceEmacs :         Emacs syntax-colouring
   82        $ PlDoc :            The documentation framework
   83*/
   84
   85:- thread_local
   86    open_source/2,          % Stream, State
   87    mode/2.                 % Stream, Data
   88
   89:- multifile
   90    requires_library/2,
   91    prolog:xref_source_identifier/2, % +Source, -Id
   92    prolog:xref_source_time/2,       % +Source, -Modified
   93    prolog:xref_open_source/2,       % +SourceId, -Stream
   94    prolog:xref_close_source/2,      % +SourceId, -Stream
   95    prolog:alternate_syntax/4,       % Syntax, +Module, -Setup, -Restore
   96    prolog:xref_update_syntax/2,     % +Directive, +Module
   97    prolog:quasi_quotation_syntax/2. % Syntax, Library
   98
   99
  100:- predicate_options(prolog_read_source_term/4, 4,
  101                     [ pass_to(system:read_clause/3, 3)
  102                     ]).  103:- predicate_options(read_source_term_at_location/3, 3,
  104                     [ line(integer),
  105                       offset(integer),
  106                       module(atom),
  107                       operators(list),
  108                       error(-any),
  109                       pass_to(system:read_term/3, 3)
  110                     ]).  111:- predicate_options(directory_source_files/3, 3,
  112                     [ recursive(boolean),
  113                       if(oneof([true,loaded])),
  114                       pass_to(system:absolute_file_name/3,3)
  115                     ]).  116
  117
  118                 /*******************************
  119                 *           READING            *
  120                 *******************************/
  121
  122%!  prolog_read_source_term(+In, -Term, -Expanded, +Options) is det.
  123%
  124%   Read a term from a Prolog source-file.  Options is a option list
  125%   that is forwarded to read_clause/3.
  126%
  127%   This predicate is intended to read the   file from the start. It
  128%   tracks  directives  to  update  its   notion  of  the  currently
  129%   effective syntax (e.g., declared operators).
  130%
  131%   @param Term     Term read
  132%   @param Expanded Result of term-expansion on the term
  133%   @see   read_source_term_at_location/3 for reading at an
  134%          arbitrary location.
  135
  136prolog_read_source_term(In, Term, Expanded, Options) :-
  137    maplist(read_clause_option, Options),
  138    !,
  139    select_option(subterm_positions(TermPos), Options,
  140                  RestOptions, TermPos),
  141    read_clause(In, Term,
  142                [ subterm_positions(TermPos)
  143                | RestOptions
  144                ]),
  145    expand(Term, TermPos, In, Expanded),
  146    '$current_source_module'(M),
  147    update_state(Term, Expanded, M, In).
  148prolog_read_source_term(In, Term, Expanded, Options) :-
  149    '$current_source_module'(M),
  150    select_option(syntax_errors(SE), Options, RestOptions0, dec10),
  151    select_option(subterm_positions(TermPos), RestOptions0,
  152                  RestOptions, TermPos),
  153    (   style_check(?(singleton))
  154    ->  FinalOptions = [ singletons(warning) | RestOptions ]
  155    ;   FinalOptions = RestOptions
  156    ),
  157    read_term(In, Term,
  158              [ module(M),
  159                syntax_errors(SE),
  160                subterm_positions(TermPos)
  161              | FinalOptions
  162              ]),
  163    expand(Term, TermPos, In, Expanded),
  164    update_state(Term, Expanded, M, In).
  165
  166read_clause_option(syntax_errors(_)).
  167read_clause_option(term_position(_)).
  168read_clause_option(process_comment(_)).
  169read_clause_option(comments(_)).
  170
  171:- public
  172    expand/3.                       % Used by Prolog colour
  173
  174expand(Term, In, Exp) :-
  175    expand(Term, _, In, Exp).
  176
  177expand(Var, _, _, Var) :-
  178    var(Var),
  179    !.
  180expand(Term, _, _, Term) :-
  181    no_expand(Term),
  182    !.
  183expand(Term, _, _, _) :-
  184    requires_library(Term, Lib),
  185    ensure_loaded(user:Lib),
  186    fail.
  187expand(Term, _, In, Term) :-
  188    chr_expandable(Term, In),
  189    !.
  190expand(Term, Pos, _, Expanded) :-
  191    expand_term(Term, Pos, Expanded, _).
  192
  193no_expand((:- if(_))).
  194no_expand((:- elif(_))).
  195no_expand((:- else)).
  196no_expand((:- endif)).
  197no_expand((:- require(_))).
  198
  199chr_expandable((:- chr_constraint(_)), In) :-
  200    add_mode(In, chr).
  201chr_expandable((handler(_)), In) :-
  202    mode(In, chr).
  203chr_expandable((rules(_)), In) :-
  204    mode(In, chr).
  205chr_expandable(<=>(_, _), In) :-
  206    mode(In, chr).
  207chr_expandable(@(_, _), In) :-
  208    mode(In, chr).
  209chr_expandable(==>(_, _), In) :-
  210    mode(In, chr).
  211chr_expandable(pragma(_, _), In) :-
  212    mode(In, chr).
  213chr_expandable(option(_, _), In) :-
  214    mode(In, chr).
  215
  216add_mode(Stream, Mode) :-
  217    mode(Stream, Mode),
  218    !.
  219add_mode(Stream, Mode) :-
  220    asserta(mode(Stream, Mode)).
  221
  222%!  requires_library(+Term, -Library)
  223%
  224%   known expansion hooks.  May be expanded as multifile predicate.
  225
  226requires_library((:- emacs_begin_mode(_,_,_,_,_)), library(emacs_extend)).
  227requires_library((:- draw_begin_shape(_,_,_,_)),   library(pcedraw)).
  228requires_library((:- use_module(library(pce))),    library(pce)).
  229requires_library((:- pce_begin_class(_,_)),        library(pce)).
  230requires_library((:- pce_begin_class(_,_,_)),      library(pce)).
  231requires_library((:- html_meta(_)),                library(http/html_decl)).
  232
  233%!  update_state(+Term, +Expanded, +Module, +In) is det.
  234%
  235%   Update operators and style-check options from Term or Expanded.
  236
  237:- multifile
  238    pce_expansion:push_compile_operators/1,
  239    pce_expansion:pop_compile_operators/0.  240
  241update_state((:- pce_end_class), _, _, _) =>
  242    ignore(pce_expansion:pop_compile_operators).
  243update_state((:- pce_extend_class(_)), _, SM, _) =>
  244    pce_expansion:push_compile_operators(SM).
  245update_state(Raw, _, Module, _),
  246    catch(prolog:xref_update_syntax(Raw, Module),
  247          error(_,_),
  248          fail) =>
  249    true.
  250update_state(_Raw, Expanded, M, In) =>
  251    update_state(Expanded, M, In).
  252
  253update_state(Var, _, _) :-
  254    var(Var),
  255    !.
  256update_state([], _, _) :-
  257    !.
  258update_state([H|T], M, In) :-
  259    !,
  260    update_state(H, M, In),
  261    update_state(T, M, In).
  262update_state((:- Directive), M, In) :-
  263    nonvar(Directive),
  264    !,
  265    catch(update_directive(Directive, M, In), _, true).
  266update_state((?- Directive), M, In) :-
  267    !,
  268    update_state((:- Directive), M, In).
  269update_state(MetaDecl, _M, _) :-
  270    MetaDecl = html_write:html_meta_head(_Head,_Module,_Meta),
  271    (   clause(MetaDecl, true)
  272    ->  true
  273    ;   assertz(MetaDecl)
  274    ).
  275update_state(_, _, _).
  276
  277%!  update_directive(+Directive, +Module, +In) is det.
  278
  279update_directive(Directive, Module, _) :-
  280    prolog:xref_update_syntax((:- Directive), Module),
  281    !.
  282update_directive(encoding(Enc), _, In) :-
  283    !,
  284    set_stream(In, encoding(Enc)).
  285update_directive(module(Module, Public), _, _) :-
  286    atom(Module),
  287    is_list(Public),
  288    !,
  289    '$set_source_module'(Module),
  290    maplist(import_syntax(_,Module, _), Public).
  291update_directive(M:op(P,T,N), SM, In) :-
  292    atom(M),
  293    ground(op(P,T,N)),
  294    !,
  295    update_directive(op(P,T,N), SM, In).
  296update_directive(op(P,T,N), SM, _) :-
  297    ground(op(P,T,N)),
  298    !,
  299    strip_module(SM:N, M, PN),
  300    push_op(P,T,M:PN).
  301update_directive(style_check(Style), _, _) :-
  302    ground(Style),
  303    style_check(Style),
  304    !.
  305update_directive(use_module(Spec), SM, _) :-
  306    ground(Spec),
  307    catch(module_decl(Spec, Path, Public), _, fail),
  308    is_list(Public),
  309    !,
  310    maplist(import_syntax(Path, SM, _), Public).
  311update_directive(use_module(Spec, Imports), SM, _) :-
  312    ground(Spec),
  313    is_list(Imports),
  314    catch(module_decl(Spec, Path, Public), _, fail),
  315    is_list(Public),
  316    !,
  317    maplist(import_syntax(Path, SM, Imports), Public).
  318update_directive(pce_begin_class_definition(_,_,_,_), SM, _) :-
  319    pce_expansion:push_compile_operators(SM),
  320    !.
  321update_directive(_, _, _).
  322
  323%!  import_syntax(+Path, +Module, +Imports, +ExportStatement) is det.
  324%
  325%   Import syntax affecting aspects  of   a  declaration. Deals with
  326%   op/3 terms and Syntax/4  quasi   quotation  declarations.
  327
  328import_syntax(_, _, _, Var) :-
  329    var(Var),
  330    !.
  331import_syntax(_, M, Imports, Op) :-
  332    Op = op(_,_,_),
  333    \+ \+ member(Op, Imports),
  334    !,
  335    update_directive(Op, M, _).
  336import_syntax(Path, SM, Imports, Syntax/4) :-
  337    \+ \+ member(Syntax/4, Imports),
  338    load_quasi_quotation_syntax(SM:Path, Syntax),
  339    !.
  340import_syntax(_,_,_, _).
  341
  342
  343%!  load_quasi_quotation_syntax(:Path, +Syntax) is semidet.
  344%
  345%   Import quasi quotation syntax Syntax from   Path into the module
  346%   specified by the  first  argument.   Quasi  quotation  syntax is
  347%   imported iff:
  348%
  349%     - It is already loaded
  350%     - It is declared with prolog:quasi_quotation_syntax/2
  351%
  352%   @tbd    We need a better way to know that an import affects the
  353%           syntax or compilation process.  This is also needed for
  354%           better compatibility with systems that provide a
  355%           separate compiler.
  356
  357load_quasi_quotation_syntax(SM:Path, Syntax) :-
  358    atom(Path), atom(Syntax),
  359    source_file_property(Path, module(M)),
  360    functor(ST, Syntax, 4),
  361    predicate_property(M:ST, quasi_quotation_syntax),
  362    !,
  363    use_module(SM:Path, [Syntax/4]).
  364load_quasi_quotation_syntax(SM:Path, Syntax) :-
  365    atom(Path), atom(Syntax),
  366    prolog:quasi_quotation_syntax(Syntax, Spec),
  367    absolute_file_name(Spec, Path2,
  368                       [ file_type(prolog),
  369                         file_errors(fail),
  370                         access(read)
  371                       ]),
  372    Path == Path2,
  373    !,
  374    use_module(SM:Path, [Syntax/4]).
  375
  376%!  module_decl(+FileSpec, -Source, -Exports) is semidet.
  377%
  378%   If FileSpec refers to a Prolog  module   file,  unify  Path with the
  379%   canonical file path to the file and Decl with the second argument of
  380%   the module declaration.
  381
  382module_decl(Spec, Source, Exports) :-
  383    absolute_file_name(Spec, Path,
  384                       [ file_type(prolog),
  385                         file_errors(fail),
  386                         access(read)
  387                       ]),
  388    module_decl_(Path, Source, Exports).
  389
  390module_decl_(Path, Source, Exports) :-
  391    file_name_extension(_, qlf, Path),
  392    !,
  393    '$qlf_module'(Path, Info),
  394    _{file:Source, exports:Exports} :< Info.
  395module_decl_(Path, Path, Exports) :-
  396    setup_call_cleanup(
  397        prolog_open_source(Path, In),
  398        read_module_decl(In, Exports),
  399        prolog_close_source(In)).
  400
  401read_module_decl(In, Decl) :-
  402    read(In, Term0),
  403    read_module_decl(Term0, In, Decl).
  404
  405read_module_decl((:- module(_, DeclIn)), _In, Decl) =>
  406    Decl = DeclIn.
  407read_module_decl((:- encoding(Enc)), In, Decl) =>
  408    set_stream(In, encoding(Enc)),
  409    read(In, Term2),
  410    read_module_decl(Term2, In, Decl).
  411read_module_decl(_, _, _) =>
  412    fail.
  413
  414
  415%!  read_source_term_at_location(+Stream, -Term, +Options) is semidet.
  416%
  417%   Try to read a Prolog term form   an  arbitrary location inside a
  418%   file. Due to Prolog's dynamic  syntax,   e.g.,  due  to operator
  419%   declarations that may change anywhere inside   the file, this is
  420%   theoreticaly   impossible.   Therefore,   this    predicate   is
  421%   fundamentally _heuristic_ and may fail.   This predicate is used
  422%   by e.g., clause_info/4 and by  PceEmacs   to  colour the current
  423%   clause.
  424%
  425%   This predicate has two ways to  find   the  right syntax. If the
  426%   file is loaded, it can be  passed   the  module using the module
  427%   option. This deals with  module  files   that  define  the  used
  428%   operators globally for  the  file.  Second,   there  is  a  hook
  429%   prolog:alternate_syntax/4 that can be used to temporary redefine
  430%   the syntax.
  431%
  432%   The options below are processed in   addition  to the options of
  433%   read_term/3. Note that  the  =line=   and  =offset=  options are
  434%   mutually exclusive.
  435%
  436%     * line(+Line)
  437%     If present, start reading at line Line.
  438%     * offset(+Characters)
  439%     Use seek/4 to go to the indicated location.  See seek/4
  440%     for limitations of seeking in text-files.
  441%     * module(+Module)
  442%     Use syntax from the given module. Default is the current
  443%     `source module'.
  444%     * operators(+List)
  445%     List of additional operator declarations to enforce while
  446%     reading the term.
  447%     * error(-Error)
  448%     If no correct parse can be found, unify Error with a term
  449%     Offset:Message that indicates the (character) location of
  450%     the error and the related message.  Adding this option
  451%     makes read_source_term_at_location/3 deterministic (=det=).
  452%
  453%   @see Use read_source_term/4 to read a file from the start.
  454%   @see prolog:alternate_syntax/4 for locally scoped operators.
  455
  456:- thread_local
  457    last_syntax_error/2.            % location, message
  458
  459read_source_term_at_location(Stream, Term, Options) :-
  460    retractall(last_syntax_error(_,_)),
  461    seek_to_start(Stream, Options),
  462    stream_property(Stream, position(Here)),
  463    '$current_source_module'(DefModule),
  464    option(module(Module), Options, DefModule),
  465    option(operators(Ops), Options, []),
  466    alternate_syntax(Syntax, Module, Setup, Restore),
  467    set_stream_position(Stream, Here),
  468    debug(read, 'Trying with syntax ~w', [Syntax]),
  469    push_operators(Module:Ops),
  470    call(Setup),
  471    Error = error(Formal,_),                 % do not catch timeout, etc.
  472    setup_call_cleanup(
  473        asserta(user:thread_message_hook(_,_,_), Ref), % silence messages
  474        catch(qq_read_term(Stream, Term0,
  475                           [ module(Module)
  476                           | Options
  477                           ]),
  478              Error,
  479              true),
  480        erase(Ref)),
  481    call(Restore),
  482    pop_operators,
  483    (   var(Formal)
  484    ->  !, Term = Term0
  485    ;   assert_error(Error, Options),
  486        fail
  487    ).
  488read_source_term_at_location(_, _, Options) :-
  489    option(error(Error), Options),
  490    !,
  491    setof(CharNo:Msg, retract(last_syntax_error(CharNo, Msg)), Pairs),
  492    last(Pairs, Error).
  493
  494assert_error(Error, Options) :-
  495    option(error(_), Options),
  496    !,
  497    (   (   Error = error(syntax_error(Id),
  498                          stream(_S1, _Line1, _LinePos1, CharNo))
  499        ;   Error = error(syntax_error(Id),
  500                          file(_S2, _Line2, _LinePos2, CharNo))
  501        )
  502    ->  message_to_string(error(syntax_error(Id), _), Msg),
  503        assertz(last_syntax_error(CharNo, Msg))
  504    ;   debug(read, 'Error: ~q', [Error]),
  505        throw(Error)
  506    ).
  507assert_error(_, _).
  508
  509
  510%!  alternate_syntax(?Syntax, +Module, -Setup, -Restore) is nondet.
  511%
  512%   Define an alternative  syntax  to  try   reading  a  term  at an
  513%   arbitrary location in module Module.
  514%
  515%   Calls the hook prolog:alternate_syntax/4 with the same signature
  516%   to allow for user-defined extensions.
  517%
  518%   @param  Setup is a deterministic goal to enable this syntax in
  519%           module.
  520%   @param  Restore is a deterministic goal to revert the actions of
  521%           Setup.
  522
  523alternate_syntax(prolog, _, true,  true).
  524alternate_syntax(Syntax, M, Setup, Restore) :-
  525    prolog:alternate_syntax(Syntax, M, Setup, Restore).
  526
  527
  528%!  seek_to_start(+Stream, +Options) is det.
  529%
  530%   Go to the location from where to start reading.
  531
  532seek_to_start(Stream, Options) :-
  533    option(line(Line), Options),
  534    !,
  535    seek(Stream, 0, bof, _),
  536    seek_to_line(Stream, Line).
  537seek_to_start(Stream, Options) :-
  538    option(offset(Start), Options),
  539    !,
  540    seek(Stream, Start, bof, _).
  541seek_to_start(_, _).
  542
  543%!  seek_to_line(+Stream, +Line)
  544%
  545%   Seek to indicated line-number.
  546
  547seek_to_line(Fd, N) :-
  548    N > 1,
  549    !,
  550    skip(Fd, 10),
  551    NN is N - 1,
  552    seek_to_line(Fd, NN).
  553seek_to_line(_, _).
  554
  555
  556                 /*******************************
  557                 *       QUASI QUOTATIONS       *
  558                 *******************************/
  559
  560%!  qq_read_term(+Stream, -Term, +Options)
  561%
  562%   Same  as  read_term/3,  but  dynamically    loads   known  quasi
  563%   quotations. Quasi quotations that  can   be  autoloaded  must be
  564%   defined using prolog:quasi_quotation_syntax/2.
  565
  566qq_read_term(Stream, Term, Options) :-
  567    select(syntax_errors(ErrorMode), Options, Options1),
  568    ErrorMode \== error,
  569    !,
  570    (   ErrorMode == dec10
  571    ->  repeat,
  572        qq_read_syntax_ex(Stream, Term, Options1, Error),
  573        (   var(Error)
  574        ->  !
  575        ;   print_message(error, Error),
  576            fail
  577        )
  578    ;   qq_read_syntax_ex(Stream, Term, Options1, Error),
  579        (   ErrorMode == fail
  580        ->  print_message(error, Error),
  581            fail
  582        ;   ErrorMode == quiet
  583        ->  fail
  584        ;   domain_error(syntax_errors, ErrorMode)
  585        )
  586    ).
  587qq_read_term(Stream, Term, Options) :-
  588    qq_read_term_ex(Stream, Term, Options).
  589
  590qq_read_syntax_ex(Stream, Term, Options, Error) :-
  591    catch(qq_read_term_ex(Stream, Term, Options),
  592          error(syntax_error(Syntax), Context),
  593          Error = error(Syntax, Context)).
  594
  595qq_read_term_ex(Stream, Term, Options) :-
  596    stream_property(Stream, position(Here)),
  597    catch(read_term(Stream, Term, Options),
  598          error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context),
  599          load_qq_and_retry(Here, Syntax, Module, Context, Stream, Term, Options)).
  600
  601load_qq_and_retry(Here, Syntax, Module, _, Stream, Term, Options) :-
  602    set_stream_position(Stream, Here),
  603    prolog:quasi_quotation_syntax(Syntax, Library),
  604    !,
  605    use_module(Module:Library, [Syntax/4]),
  606    read_term(Stream, Term, Options).
  607load_qq_and_retry(_Pos, Syntax, Module, Context, _Stream, _Term, _Options) :-
  608    print_message(warning, quasi_quotation(undeclared, Syntax)),
  609    throw(error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context)).
  610
  611%!  prolog:quasi_quotation_syntax(+Syntax, -Library) is semidet.
  612%
  613%   True when the quasi quotation syntax   Syntax can be loaded from
  614%   Library.  Library  must  be   a    valid   first   argument  for
  615%   use_module/2.
  616%
  617%   This multifile hook is used   by  library(prolog_source) to load
  618%   quasi quotation handlers on demand.
  619
  620prolog:quasi_quotation_syntax(html,       library(http/html_write)).
  621prolog:quasi_quotation_syntax(javascript, library(http/js_write)).
  622
  623
  624%!  prolog_file_directives(+Input, -Directives, +Options) is det.
  625%
  626%   True when Directives is a list  of   directives  that  appear in the
  627%   source  file  File.  Reading   directives    stops   at   the  first
  628%   non-directive term. Processing deals with   expand_term/2 as well as
  629%   conditional compilation.  Options processed:
  630%
  631%     - canonical_source(-Source)
  632%       Unify Source with the canonical source identifier as also
  633%       used by library(prolog_xref).
  634%     - silent(+Boolean)
  635%       If `true` (default `false`), do not report syntax errors and
  636%       other errors.
  637%
  638%   @arg Input is a ground term acceptable by prolog_open_source/2.
  639
  640prolog_file_directives(File, Directives, Options) :-
  641    option(canonical_source(Path), Options, _),
  642    prolog_canonical_source(File, Path),
  643    in_temporary_module(
  644        TempModule,
  645        true,
  646        read_directives(TempModule, Path, Directives, Options)).
  647
  648read_directives(TempModule, Path, Directives, Options) :-
  649    setup_call_cleanup(
  650        read_directives_setup(TempModule, Path, In, State),
  651        phrase(read_directives(In, Options, [true]), Directives),
  652        read_directives_cleanup(In, State)).
  653
  654read_directives_setup(TempModule, Path, In, OldM) :-
  655    prolog_open_source(Path, In),
  656    '$set_source_module'(OldM, TempModule),
  657    push_prolog_flag(xref, true).
  658
  659read_directives_cleanup(In, OldM) :-
  660    '$set_source_module'(OldM),
  661    pop_prolog_flag(xref),
  662    prolog_close_source(In).
  663
  664read_directives(In, Options, State) -->
  665    {  E = error(_,_),
  666       repeat,
  667       catch(prolog_read_source_term(In, Term, Expanded,
  668                                     [ process_comment(true),
  669                                       syntax_errors(error)
  670                                     ]),
  671             E, report_syntax_error(E, Options))
  672    -> nonvar(Term),
  673       Term = (:-_)
  674    },
  675    !,
  676    terms(Expanded, State, State1),
  677    read_directives(In, Options, State1).
  678read_directives(_, _, _) --> [].
  679
  680report_syntax_error(_, Options) :-
  681    option(silent(true), Options),
  682    !,
  683    fail.
  684report_syntax_error(E, _Options) :-
  685    print_message(warning, E),
  686    fail.
  687
  688terms(Var, State, State) --> { var(Var) }, !.
  689terms([H|T], State0, State) -->
  690    !,
  691    terms(H, State0, State1),
  692    terms(T, State1, State).
  693terms((:-if(Cond)), State0, [True|State0]) -->
  694    !,
  695    { eval_cond(Cond, True) }.
  696terms((:-elif(Cond)), [True0|State], [True|State]) -->
  697    !,
  698    { eval_cond(Cond, True1),
  699      elif(True0, True1, True)
  700    }.
  701terms((:-else), [True0|State], [True|State]) -->
  702    !,
  703    { negate(True0, True) }.
  704terms((:-endif), [_|State], State) -->  !.
  705terms(H, State, State) -->
  706    (   {State = [true|_]}
  707    ->  [H]
  708    ;   []
  709    ).
  710
  711eval_cond(Cond, true) :-
  712    catch(Cond, error(_,_), fail),
  713    !.
  714eval_cond(_, false).
  715
  716elif(true,  _,    else_false) :- !.
  717elif(false, true, true) :- !.
  718elif(True,  _,    True).
  719
  720negate(true,       false).
  721negate(false,      true).
  722negate(else_false, else_false).
  723
  724                 /*******************************
  725                 *           SOURCES            *
  726                 *******************************/
  727
  728%!  prolog_open_source(+CanonicalId:atomic, -Stream:stream) is det.
  729%
  730%   Open     source     with     given     canonical     id     (see
  731%   prolog_canonical_source/2)  and  remove  the  #!  line  if  any.
  732%   Streams  opened  using  this  predicate  must  be  closed  using
  733%   prolog_close_source/1. Typically using the skeleton below. Using
  734%   this   skeleton,   operator   and    style-check   options   are
  735%   automatically restored to the values before opening the source.
  736%
  737%   ```
  738%   process_source(Src) :-
  739%           prolog_open_source(Src, In),
  740%           call_cleanup(process(Src), prolog_close_source(In)).
  741%   ```
  742%
  743%   @arg   CanonicalId   is   one   of   (1)    a   term   accepted   by
  744%   prolog:xref_open_source/2, (2) a file name or   (3)  an already open
  745%   stream.  In  the  last  case,   the    stream   is   not  closed  by
  746%   prolog_close_source/1.
  747
  748prolog_open_source(Src, Fd) :-
  749    '$push_input_context'(source),
  750    catch(do_open_source(Src, Fd, Hooked),
  751          E,
  752          (   '$pop_input_context',
  753              throw(E)
  754          )),
  755    skip_hashbang(Fd),
  756    push_operators([]),
  757    '$current_source_module'(SM),
  758    '$save_lex_state'(LexState, []),
  759    asserta(open_source(Fd, state(Hooked, Src, LexState, SM))).
  760
  761do_open_source(Src, Stream, stream) :-
  762    is_stream(Src),
  763    !,
  764    Stream = Src.
  765do_open_source(Src, Stream, hooked) :-
  766    prolog:xref_open_source(Src, Stream),
  767    !.
  768do_open_source(Src, Stream, opened) :-
  769    open(Src, read, Stream).
  770
  771
  772skip_hashbang(Fd) :-
  773    catch((   peek_char(Fd, #)              % Deal with #! script
  774          ->  skip(Fd, 10)
  775          ;   true
  776          ), E,
  777          (   close(Fd, [force(true)]),
  778              '$pop_input_context',
  779              throw(E)
  780          )).
  781
  782%!  prolog:xref_open_source(+SourceID, -Stream)
  783%
  784%   Hook  to  open   an   xref   SourceID.    This   is   used   for
  785%   cross-referencing non-files, such as XPCE   buffers,  files from
  786%   archives,  git  repositories,   etc.    When   successful,   the
  787%   corresponding  prolog:xref_close_source/2  hook  is  called  for
  788%   closing the source.
  789
  790
  791%!  prolog_close_source(+In:stream) is det.
  792%
  793%   Close  a  stream  opened  using  prolog_open_source/2.  Restores
  794%   operator and style options. If the stream   has not been read to
  795%   the end, we call expand_term(end_of_file,  _) to allow expansion
  796%   modules to clean-up.
  797
  798prolog_close_source(In) :-
  799    call_cleanup(
  800        restore_source_context(In, Hooked, Src),
  801        close_source(Hooked, Src, In)).
  802
  803close_source(stream, _Src, _Stream) :-
  804    !,
  805    '$pop_input_context'.
  806close_source(hooked, Src, In) :-
  807    catch(prolog:xref_close_source(Src, In), _, false),
  808    !,
  809    '$pop_input_context'.
  810close_source(_, _Src, In) :-  % If hook fails we must do normal close
  811    close(In, [force(true)]),
  812    '$pop_input_context'.
  813
  814restore_source_context(In, Hooked, Src) :-
  815    (   at_end_of_stream(In)
  816    ->  true
  817    ;   ignore(catch(expand(end_of_file, _, In, _), _, true))
  818    ),
  819    pop_operators,
  820    retractall(mode(In, _)),
  821    (   retract(open_source(In, state(Hooked, Src, LexState, SM)))
  822    ->  '$restore_lex_state'(LexState),
  823        '$set_source_module'(SM)
  824    ;   assertion(fail)
  825    ).
  826
  827%!  prolog:xref_close_source(+SourceID, +Stream) is semidet.
  828%
  829%   Called by prolog_close_source/1 to  close   a  source previously
  830%   opened by the hook prolog:xref_open_source/2.  If the hook fails
  831%   close/2 using the option force(true) is used.
  832
  833%!  prolog_canonical_source(+SourceSpec:ground, -Id:atomic) is semidet.
  834%
  835%   Given a user-specification of a source,   generate  a unique and
  836%   indexable  identifier  for   it.   For    files   we   use   the
  837%   prolog_canonical absolute filename. Id must   be valid input for
  838%   prolog_open_source/2.
  839
  840prolog_canonical_source(Source, Src) :-
  841    var(Source),
  842    !,
  843    Src = Source.
  844prolog_canonical_source(User, user) :-
  845    User == user,
  846    !.
  847prolog_canonical_source(Stream, Src) :-
  848    is_stream(Stream),
  849    !,
  850    Src = Stream.
  851prolog_canonical_source(Src, Id) :-             % Call hook
  852    prolog:xref_source_identifier(Src, Id),
  853    !.
  854prolog_canonical_source(Source, Src) :-
  855    source_file(Source),
  856    !,
  857    Src = Source.
  858prolog_canonical_source(Source, Src) :-
  859    absolute_file_name(Source, Src,
  860                       [ file_type(prolog),
  861                         access(read),
  862                         file_errors(fail)
  863                       ]),
  864    !.
  865
  866
  867%!  file_name_on_path(+File:atom, -OnPath) is det.
  868%
  869%   True if OnPath a description of File   based  on the file search
  870%   path. This performs the inverse of absolute_file_name/3.
  871
  872file_name_on_path(Path, ShortId) :-
  873    (   file_alias_path(Alias, Dir),
  874        atom_concat(Dir, Local, Path)
  875    ->  (   Alias == '.'
  876        ->  ShortId = Local
  877        ;   file_name_extension(Base, pl, Local)
  878        ->  ShortId =.. [Alias, Base]
  879        ;   ShortId =.. [Alias, Local]
  880        )
  881    ;   ShortId = Path
  882    ).
  883
  884
  885%!  file_alias_path(-Alias, ?Dir) is nondet.
  886%
  887%   True if file Alias points to Dir.  Multiple solutions are
  888%   generated with the longest directory first.
  889
  890:- dynamic
  891    alias_cache/2.  892
  893file_alias_path(Alias, Dir) :-
  894    (   alias_cache(_, _)
  895    ->  true
  896    ;   build_alias_cache
  897    ),
  898    (   nonvar(Dir)
  899    ->  ensure_slash(Dir, DirSlash),
  900        alias_cache(Alias, DirSlash)
  901    ;   alias_cache(Alias, Dir)
  902    ).
  903
  904build_alias_cache :-
  905    findall(t(DirLen, AliasLen, Alias, Dir),
  906            search_path(Alias, Dir, AliasLen, DirLen), Ts),
  907    sort(0, >, Ts, List),
  908    forall(member(t(_, _, Alias, Dir), List),
  909           assert(alias_cache(Alias, Dir))).
  910
  911search_path('.', Here, 999, DirLen) :-
  912    working_directory(Here0, Here0),
  913    ensure_slash(Here0, Here),
  914    atom_length(Here, DirLen).
  915search_path(Alias, Dir, AliasLen, DirLen) :-
  916    user:file_search_path(Alias, _),
  917    Alias \== autoload,             % TBD: Multifile predicate?
  918    Alias \== noautoload,
  919    Spec =.. [Alias,'.'],
  920    atom_length(Alias, AliasLen0),
  921    AliasLen is 1000 - AliasLen0,   % must do reverse sort
  922    absolute_file_name(Spec, Dir0,
  923                       [ file_type(directory),
  924                         access(read),
  925                         solutions(all),
  926                         file_errors(fail)
  927                       ]),
  928    ensure_slash(Dir0, Dir),
  929    atom_length(Dir, DirLen).
  930
  931ensure_slash(Dir, Dir) :-
  932    sub_atom(Dir, _, _, 0, /),
  933    !.
  934ensure_slash(Dir0, Dir) :-
  935    atom_concat(Dir0, /, Dir).
  936
  937
  938%!  path_segments_atom(+Segments, -Atom) is det.
  939%!  path_segments_atom(-Segments, +Atom) is det.
  940%
  941%   Translate between a path  represented  as   a/b/c  and  an  atom
  942%   representing the same path. For example:
  943%
  944%     ==
  945%     ?- path_segments_atom(a/b/c, X).
  946%     X = 'a/b/c'.
  947%     ?- path_segments_atom(S, 'a/b/c'), display(S).
  948%     /(/(a,b),c)
  949%     S = a/b/c.
  950%     ==
  951%
  952%   This predicate is part of  the   Prolog  source  library because
  953%   SWI-Prolog  allows  writing  paths   as    /-nested   terms  and
  954%   source-code analysis programs often need this.
  955
  956path_segments_atom(Segments, Atom) :-
  957    var(Atom),
  958    !,
  959    (   atomic(Segments)
  960    ->  Atom = Segments
  961    ;   segments_to_list(Segments, List, [])
  962    ->  atomic_list_concat(List, /, Atom)
  963    ;   throw(error(type_error(file_path, Segments), _))
  964    ).
  965path_segments_atom(Segments, Atom) :-
  966    atomic_list_concat(List, /, Atom),
  967    parts_to_path(List, Segments).
  968
  969segments_to_list(Var, _, _) :-
  970    var(Var), !, fail.
  971segments_to_list(A/B, H, T) :-
  972    segments_to_list(A, H, T0),
  973    segments_to_list(B, T0, T).
  974segments_to_list(A, [A|T], T) :-
  975    atomic(A).
  976
  977parts_to_path([One], One) :- !.
  978parts_to_path(List, More/T) :-
  979    (   append(H, [T], List)
  980    ->  parts_to_path(H, More)
  981    ).
  982
  983%!  directory_source_files(+Dir, -Files, +Options) is det.
  984%
  985%   True when Files is a sorted list  of Prolog source files in Dir.
  986%   Options:
  987%
  988%     * recursive(boolean)
  989%     If =true= (default =false=), recurse into subdirectories
  990%     * if(Condition)
  991%     If =true= (default =loaded=), only report loaded files.
  992%
  993%   Other  options  are  passed    to  absolute_file_name/3,  unless
  994%   loaded(true) is passed.
  995
  996directory_source_files(Dir, SrcFiles, Options) :-
  997    option(if(loaded), Options, loaded),
  998    !,
  999    absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
 1000    (   option(recursive(true), Options)
 1001    ->  ensure_slash(AbsDir, Prefix),
 1002        findall(F, (  source_file(F),
 1003                      sub_atom(F, 0, _, _, Prefix)
 1004                   ),
 1005                SrcFiles)
 1006    ;   findall(F, ( source_file(F),
 1007                     file_directory_name(F, AbsDir)
 1008                   ),
 1009                SrcFiles)
 1010    ).
 1011directory_source_files(Dir, SrcFiles, Options) :-
 1012    absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
 1013    directory_files(AbsDir, Files),
 1014    phrase(src_files(Files, AbsDir, Options), SrcFiles).
 1015
 1016src_files([], _, _) -->
 1017    [].
 1018src_files([H|T], Dir, Options) -->
 1019    { file_name_extension(_, Ext, H),
 1020      user:prolog_file_type(Ext, prolog),
 1021      \+ user:prolog_file_type(Ext, qlf),
 1022      dir_file_path(Dir, H, File0),
 1023      absolute_file_name(File0, File,
 1024                         [ file_errors(fail)
 1025                         | Options
 1026                         ])
 1027    },
 1028    !,
 1029    [File],
 1030    src_files(T, Dir, Options).
 1031src_files([H|T], Dir, Options) -->
 1032    { \+ special(H),
 1033      option(recursive(true), Options),
 1034      dir_file_path(Dir, H, SubDir),
 1035      exists_directory(SubDir),
 1036      !,
 1037      catch(directory_files(SubDir, Files), _, fail)
 1038    },
 1039    !,
 1040    src_files(Files, SubDir, Options),
 1041    src_files(T, Dir, Options).
 1042src_files([_|T], Dir, Options) -->
 1043    src_files(T, Dir, Options).
 1044
 1045special(.).
 1046special(..).
 1047
 1048% avoid dependency on library(filesex), which also pulls a foreign
 1049% dependency.
 1050dir_file_path(Dir, File, Path) :-
 1051    (   sub_atom(Dir, _, _, 0, /)
 1052    ->  atom_concat(Dir, File, Path)
 1053    ;   atom_concat(Dir, /, TheDir),
 1054        atom_concat(TheDir, File, Path)
 1055    ).
 1056
 1057
 1058%!  valid_term_position(@Term, @TermPos) is semidet.
 1059%
 1060%   Check that a Term has an   appropriate  TermPos layout. An incorrect
 1061%   TermPos results in either failure of this predicate or an error.
 1062%
 1063%   If a position in TermPos  is  a   variable,  the  validation  of the
 1064%   corresponding   part   of   Term   succeeds.    This   matches   the
 1065%   term_expansion/4 treats "unknown" layout information.   If part of a
 1066%   TermPos is given, then all its "from"   and "to" information must be
 1067%   specified; for example,    string_position(X,Y)   is   an  error but
 1068%   string_position(0,5) succeeds.   The position values are checked for
 1069%   being plausible -- e.g., string_position(5,0) will fail.
 1070%
 1071%   This should always succeed:
 1072%
 1073%       read_term(Term, [subterm_positions(TermPos)]),
 1074%       valid_term_position(Term, TermPos)
 1075%
 1076%   @arg Term Any Prolog term including a variable).
 1077%   @arg TermPos The detailed layout of the term, for example
 1078%        from using =|read_term(Term, subterm_positions(TermPos)|=.
 1079%
 1080%   @error existence_error(matching_rule, Subterm) if a subterm of Term
 1081%          is inconsistent with the corresponding part of TermPos.
 1082%
 1083%   @see read_term/2, read_term/3, term_string/3
 1084%   @see expand_term/4, term_expansion/4, expand_goal/4, expand_term/4
 1085%   @see clause_info/4, clause_info/5
 1086%   @see prolog_clause:unify_clause_hook/5
 1087
 1088valid_term_position(Term, TermPos) :-
 1089    valid_term_position(0, 0x7fffffffffffffff, Term, TermPos).
 1090
 1091valid_term_position(OuterFrom, OuterTo, _Term, TermPos),
 1092        var(TermPos),
 1093        OuterFrom =< OuterTo => true.
 1094valid_term_position(OuterFrom, OuterTo, Var, From-To),
 1095        var(Var),
 1096        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1097valid_term_position(OuterFrom, OuterTo, Atom, From-To),
 1098        atom(Atom),
 1099        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1100valid_term_position(OuterFrom, OuterTo, Number, From-To),
 1101        number(Number),
 1102        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1103valid_term_position(OuterFrom, OuterTo, [], From-To),
 1104        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1105valid_term_position(OuterFrom, OuterTo, String, string_position(From,To)),
 1106        (   string(String)
 1107        ->  true
 1108        ;   is_of_type(codes, String)
 1109        ->  true
 1110        ;   is_of_type(chars, String)
 1111        ->  true
 1112        ;   atom(String)
 1113        ),
 1114        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1115valid_term_position(OuterFrom, OuterTo, {Arg},
 1116                    brace_term_position(From,To,ArgPos)),
 1117        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1118    valid_term_position(From, To, Arg, ArgPos).
 1119valid_term_position(OuterFrom, OuterTo, [Hd|Tl],
 1120                    list_position(From,To,ElemsPos,none)),
 1121        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1122    term_position_list_tail([Hd|Tl], _HdPart, []),
 1123    maplist(valid_term_position, [Hd|Tl], ElemsPos).
 1124valid_term_position(OuterFrom, OuterTo, [Hd|Tl],
 1125                    list_position(From, To, ElemsPos, TailPos)),
 1126        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1127    term_position_list_tail([Hd|Tl], HdPart, Tail),
 1128    maplist(valid_term_position(From,To), HdPart, ElemsPos),
 1129    valid_term_position(Tail, TailPos).
 1130valid_term_position(OuterFrom, OuterTo, Term,
 1131                    term_position(From,To, FFrom,FTo,SubPos)),
 1132        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1133    compound_name_arguments(Term, Name, Arguments),
 1134    valid_term_position(Name, FFrom-FTo),
 1135    maplist(valid_term_position(From,To), Arguments, SubPos).
 1136valid_term_position(OuterFrom, OuterTo, Dict,
 1137                    dict_position(From,To,TagFrom,TagTo,KeyValuePosList)),
 1138        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1139    dict_pairs(Dict, Tag, Pairs),
 1140    valid_term_position(Tag, TagFrom-TagTo),
 1141    foldl(valid_term_position_dict(From,To), Pairs, KeyValuePosList, []).
 1142% key_value_position(From, To, SepFrom, SepTo, Key, KeyPos, ValuePos)
 1143% is handled in valid_term_position_dict.
 1144valid_term_position(OuterFrom, OuterTo, Term,
 1145                    parentheses_term_position(From,To,ContentPos)),
 1146        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1147    valid_term_position(From, To, Term, ContentPos).
 1148valid_term_position(OuterFrom, OuterTo, _Term,
 1149                    quasi_quotation_position(From,To,
 1150                                             SyntaxTerm,SyntaxPos,_ContentPos)),
 1151        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1152    valid_term_position(From, To, SyntaxTerm, SyntaxPos).
 1153
 1154valid_term_position_from_to(OuterFrom, OuterTo, From, To) :-
 1155    integer(OuterFrom),
 1156    integer(OuterTo),
 1157    integer(From),
 1158    integer(To),
 1159    OuterFrom =< OuterTo,
 1160    From =< To,
 1161    OuterFrom =< From,
 1162    To =< OuterTo.
 1163
 1164:- det(valid_term_position_dict/5). 1165valid_term_position_dict(OuterFrom, OuterTo, Key-Value,
 1166                         KeyValuePosList0, KeyValuePosList1) :-
 1167    selectchk(key_value_position(From,To,SepFrom,SepTo,Key,KeyPos,ValuePos),
 1168              KeyValuePosList0, KeyValuePosList1),
 1169    valid_term_position_from_to(OuterFrom, OuterTo, From, To),
 1170    valid_term_position_from_to(OuterFrom, OuterTo, SepFrom, SepTo),
 1171    SepFrom >= OuterFrom,
 1172    valid_term_position(From, SepFrom, Key, KeyPos),
 1173    valid_term_position(SepTo, To, Value, ValuePos).
 1174
 1175%!  term_position_list_tail(@List, -HdPart, -Tail) is det.
 1176%
 1177%   Similar to append(HdPart, [Tail], List) for   proper lists, but also
 1178%   works for inproper lists, in which  case   it  unifies Tail with the
 1179%   tail of the partial list. HdPart is always a proper list:
 1180%
 1181%   ```
 1182%   ?- prolog_source:term_position_list_tail([a,b,c], Hd, Tl).
 1183%   Hd = [a, b, c],
 1184%   Tl = [].
 1185%   ?- prolog_source:term_position_list_tail([a,b|X], Hd, Tl).
 1186%   X = Tl,
 1187%   Hd = [a, b].
 1188%   ```
 1189
 1190:- det(term_position_list_tail/3). 1191term_position_list_tail([X|Xs], HdPart, Tail) =>
 1192    HdPart = [X|HdPart2],
 1193    term_position_list_tail(Xs, HdPart2, Tail).
 1194term_position_list_tail(Tail0, HdPart, Tail) =>
 1195    HdPart = [],
 1196    Tail0 = Tail.
 1197
 1198
 1199                 /*******************************
 1200                 *           MESSAGES           *
 1201                 *******************************/
 1202
 1203:- multifile
 1204    prolog:message//1. 1205
 1206prolog:message(quasi_quotation(undeclared, Syntax)) -->
 1207    [ 'Undeclared quasi quotation syntax: ~w'-[Syntax], nl,
 1208      'Autoloading can be defined using prolog:quasi_quotation_syntax/2'
 1209    ]