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)  2006-2016, University of Amsterdam
    7                              Vu University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(prolog_source,
   37          [ prolog_read_source_term/4,  % +Stream, -Term, -Expanded, +Options
   38            read_source_term_at_location/3, %Stream, -Term, +Options
   39            prolog_open_source/2,       % +Source, -Stream
   40            prolog_close_source/1,      % +Stream
   41            prolog_canonical_source/2,  % +Spec, -Id
   42
   43            load_quasi_quotation_syntax/2, % :Path, +Syntax
   44
   45            file_name_on_path/2,        % +File, -PathSpec
   46            file_alias_path/2,          % ?Alias, ?Dir
   47            path_segments_atom/2,       % ?Segments, ?Atom
   48            directory_source_files/3    % +Dir, -Files, +Options
   49          ]).   50:- autoload(library(apply),[maplist/2]).   51:- autoload(library(debug),[debug/3,assertion/1]).   52:- autoload(library(error),[domain_error/2]).   53:- autoload(library(lists),[member/2,last/2,select/3,append/3]).   54:- autoload(library(operators),
   55	    [push_op/3,push_operators/1,pop_operators/0]).   56:- autoload(library(option),[select_option/4,option/3,option/2]).

Examine Prolog source-files

This module provides predicates to open, close and read terms from Prolog source-files. This may seem easy, but there are a couple of problems that must be taken care of.

This module concentrates these issues in a single library. Intended users of the library are:

prolog_xref.pl
The Prolog cross-referencer
prolog_clause.pl
Get details about (compiled) clauses
prolog_colour.pl
Colourise source-code
PceEmacs
Emacs syntax-colouring
PlDoc
The documentation framework

*/

   82:- thread_local
   83    open_source/2,          % Stream, State
   84    mode/2.                 % Stream, Data
   85
   86:- multifile
   87    requires_library/2,
   88    prolog:xref_source_identifier/2, % +Source, -Id
   89    prolog:xref_source_time/2,       % +Source, -Modified
   90    prolog:xref_open_source/2,       % +SourceId, -Stream
   91    prolog:xref_close_source/2,      % +SourceId, -Stream
   92    prolog:alternate_syntax/4,       % Syntax, +Module, -Setup, -Restore
   93    prolog:quasi_quotation_syntax/2. % Syntax, Library
   94
   95
   96:- predicate_options(prolog_read_source_term/4, 4,
   97                     [ pass_to(system:read_clause/3, 3)
   98                     ]).   99:- predicate_options(read_source_term_at_location/3, 3,
  100                     [ line(integer),
  101                       offset(integer),
  102                       module(atom),
  103                       operators(list),
  104                       error(-any),
  105                       pass_to(system:read_term/3, 3)
  106                     ]).  107:- predicate_options(directory_source_files/3, 3,
  108                     [ recursive(boolean),
  109                       if(oneof([true,loaded])),
  110                       pass_to(system:absolute_file_name/3,3)
  111                     ]).  112
  113
  114                 /*******************************
  115                 *           READING            *
  116                 *******************************/
 prolog_read_source_term(+In, -Term, -Expanded, +Options) is det
Read a term from a Prolog source-file. Options is a option list that is forwarded to read_clause/3.

This predicate is intended to read the file from the start. It tracks directives to update its notion of the currently effective syntax (e.g., declared operators).

Arguments:
Term- Term read
Expanded- Result of term-expansion on the term
See also
- read_source_term_at_location/3 for reading at an arbitrary location.
  132prolog_read_source_term(In, Term, Expanded, Options) :-
  133    maplist(read_clause_option, Options),
  134    !,
  135    select_option(subterm_positions(TermPos), Options,
  136                  RestOptions, TermPos),
  137    read_clause(In, Term,
  138                [ subterm_positions(TermPos)
  139                | RestOptions
  140                ]),
  141    expand(Term, TermPos, In, Expanded),
  142    '$current_source_module'(M),
  143    update_state(Term, Expanded, M).
  144prolog_read_source_term(In, Term, Expanded, Options) :-
  145    '$current_source_module'(M),
  146    select_option(syntax_errors(SE), Options, RestOptions0, dec10),
  147    select_option(subterm_positions(TermPos), RestOptions0,
  148                  RestOptions, TermPos),
  149    (   style_check(?(singleton))
  150    ->  FinalOptions = [ singletons(warning) | RestOptions ]
  151    ;   FinalOptions = RestOptions
  152    ),
  153    read_term(In, Term,
  154              [ module(M),
  155                syntax_errors(SE),
  156                subterm_positions(TermPos)
  157              | FinalOptions
  158              ]),
  159    expand(Term, TermPos, In, Expanded),
  160    update_state(Term, Expanded, M).
  161
  162read_clause_option(syntax_errors(_)).
  163read_clause_option(term_position(_)).
  164read_clause_option(process_comment(_)).
  165read_clause_option(comments(_)).
  166
  167:- public
  168    expand/3.                       % Used by Prolog colour
  169
  170expand(Term, In, Exp) :-
  171    expand(Term, _, In, Exp).
  172
  173expand(Var, _, _, Var) :-
  174    var(Var),
  175    !.
  176expand(Term, _, _, Term) :-
  177    no_expand(Term),
  178    !.
  179expand(Term, _, _, _) :-
  180    requires_library(Term, Lib),
  181    ensure_loaded(user:Lib),
  182    fail.
  183expand(Term, _, In, Term) :-
  184    chr_expandable(Term, In),
  185    !.
  186expand(Term, Pos, _, Expanded) :-
  187    expand_term(Term, Pos, Expanded, _).
  188
  189no_expand((:- if(_))).
  190no_expand((:- elif(_))).
  191no_expand((:- else)).
  192no_expand((:- endif)).
  193no_expand((:- require(_))).
  194
  195chr_expandable((:- chr_constraint(_)), In) :-
  196    add_mode(In, chr).
  197chr_expandable((handler(_)), In) :-
  198    mode(In, chr).
  199chr_expandable((rules(_)), In) :-
  200    mode(In, chr).
  201chr_expandable(<=>(_, _), In) :-
  202    mode(In, chr).
  203chr_expandable(@(_, _), In) :-
  204    mode(In, chr).
  205chr_expandable(==>(_, _), In) :-
  206    mode(In, chr).
  207chr_expandable(pragma(_, _), In) :-
  208    mode(In, chr).
  209chr_expandable(option(_, _), In) :-
  210    mode(In, chr).
  211
  212add_mode(Stream, Mode) :-
  213    mode(Stream, Mode),
  214    !.
  215add_mode(Stream, Mode) :-
  216    asserta(mode(Stream, Mode)).
 requires_library(+Term, -Library)
known expansion hooks. May be expanded as multifile predicate.
  222requires_library((:- emacs_begin_mode(_,_,_,_,_)), library(emacs_extend)).
  223requires_library((:- draw_begin_shape(_,_,_,_)),   library(pcedraw)).
  224requires_library((:- use_module(library(pce))),    library(pce)).
  225requires_library((:- pce_begin_class(_,_)),        library(pce)).
  226requires_library((:- pce_begin_class(_,_,_)),      library(pce)).
 update_state(+Term, +Expanded, +Module) is det
Update operators and style-check options from the expanded term.
  232:- multifile
  233    pce_expansion:push_compile_operators/1,
  234    pce_expansion:pop_compile_operators/0.  235
  236update_state(Raw, _, _) :-
  237    Raw == (:- pce_end_class),
  238    !,
  239    ignore(pce_expansion:pop_compile_operators).
  240update_state(Raw, _, SM) :-
  241    subsumes_term((:- pce_extend_class(_)), Raw),
  242    !,
  243    pce_expansion:push_compile_operators(SM).
  244update_state(_Raw, Expanded, M) :-
  245    update_state(Expanded, M).
  246
  247update_state(Var, _) :-
  248    var(Var),
  249    !.
  250update_state([], _) :-
  251    !.
  252update_state([H|T], M) :-
  253    !,
  254    update_state(H, M),
  255    update_state(T, M).
  256update_state((:- Directive), M) :-
  257    nonvar(Directive),
  258    !,
  259    catch(update_directive(Directive, M), _, true).
  260update_state((?- Directive), M) :-
  261    !,
  262    update_state((:- Directive), M).
  263update_state(_, _).
  264
  265update_directive(module(Module, Public), _) :-
  266    atom(Module),
  267    is_list(Public),
  268    !,
  269    '$set_source_module'(Module),
  270    maplist(import_syntax(_,Module, _), Public).
  271update_directive(M:op(P,T,N), SM) :-
  272    atom(M),
  273    ground(op(P,T,N)),
  274    !,
  275    update_directive(op(P,T,N), SM).
  276update_directive(op(P,T,N), SM) :-
  277    ground(op(P,T,N)),
  278    !,
  279    strip_module(SM:N, M, PN),
  280    push_op(P,T,M:PN).
  281update_directive(style_check(Style), _) :-
  282    ground(Style),
  283    style_check(Style),
  284    !.
  285update_directive(use_module(Spec), SM) :-
  286    ground(Spec),
  287    catch(module_decl(Spec, Path, Public), _, fail),
  288    !,
  289    maplist(import_syntax(Path, SM, _), Public).
  290update_directive(use_module(Spec, Imports), SM) :-
  291    ground(Spec),
  292    is_list(Imports),
  293    catch(module_decl(Spec, Path, Public), _, fail),
  294    !,
  295    maplist(import_syntax(Path, SM, Imports), Public).
  296update_directive(pce_begin_class_definition(_,_,_,_), SM) :-
  297    pce_expansion:push_compile_operators(SM),
  298    !.
  299update_directive(_, _).
 import_syntax(+Path, +Module, +Imports, +ExportStatement) is det
Import syntax affecting aspects of a declaration. Deals with op/3 terms and Syntax/4 quasi quotation declarations.
  306import_syntax(_, _, _, Var) :-
  307    var(Var),
  308    !.
  309import_syntax(_, M, Imports, Op) :-
  310    Op = op(_,_,_),
  311    \+ \+ member(Op, Imports),
  312    !,
  313    update_directive(Op, M).
  314import_syntax(Path, SM, Imports, Syntax/4) :-
  315    \+ \+ member(Syntax/4, Imports),
  316    load_quasi_quotation_syntax(SM:Path, Syntax),
  317    !.
  318import_syntax(_,_,_, _).
 load_quasi_quotation_syntax(:Path, +Syntax) is semidet
Import quasi quotation syntax Syntax from Path into the module specified by the first argument. Quasi quotation syntax is imported iff:
To be done
- We need a better way to know that an import affects the syntax or compilation process. This is also needed for better compatibility with systems that provide a separate compiler.
  335load_quasi_quotation_syntax(SM:Path, Syntax) :-
  336    atom(Path), atom(Syntax),
  337    source_file_property(Path, module(M)),
  338    functor(ST, Syntax, 4),
  339    predicate_property(M:ST, quasi_quotation_syntax),
  340    !,
  341    use_module(SM:Path, [Syntax/4]).
  342load_quasi_quotation_syntax(SM:Path, Syntax) :-
  343    atom(Path), atom(Syntax),
  344    prolog:quasi_quotation_syntax(Syntax, Spec),
  345    absolute_file_name(Spec, Path2,
  346                       [ file_type(prolog),
  347                         file_errors(fail),
  348                         access(read)
  349                       ]),
  350    Path == Path2,
  351    !,
  352    use_module(SM:Path, [Syntax/4]).
 module_decl(+FileSpec, -Path, -Decl) is semidet
If FileSpec refers to a Prolog module file, unify Path with the canonical file path to the file and Decl with the second argument of the module declaration.
  360module_decl(Spec, Path, Decl) :-
  361    absolute_file_name(Spec, Path,
  362                       [ file_type(prolog),
  363                         file_errors(fail),
  364                         access(read)
  365                       ]),
  366    setup_call_cleanup(
  367        prolog_open_source(Path, In),
  368        read_module_decl(In, Decl),
  369        prolog_close_source(In)).
  370
  371read_module_decl(In, Decl) :-
  372    read(In, Term0),
  373    read_module_decl(Term0, In, Decl).
  374
  375read_module_decl(Term, _In, Decl) :-
  376    subsumes_term((:- module(_, Decl)), Term),
  377    !,
  378    Term = (:- module(_, Decl)).
  379read_module_decl(Term, In, Decl) :-
  380    subsumes_term((:- encoding(_)), Term),
  381    !,
  382    Term = (:- encoding(Enc)),
  383    set_stream(In, encoding(Enc)),
  384    read(In, Term2),
  385    read_module_decl(Term2, In, Decl).
 read_source_term_at_location(+Stream, -Term, +Options) is semidet
Try to read a Prolog term form an arbitrary location inside a file. Due to Prolog's dynamic syntax, e.g., due to operator declarations that may change anywhere inside the file, this is theoreticaly impossible. Therefore, this predicate is fundamentally heuristic and may fail. This predicate is used by e.g., clause_info/4 and by PceEmacs to colour the current clause.

This predicate has two ways to find the right syntax. If the file is loaded, it can be passed the module using the module option. This deals with module files that define the used operators globally for the file. Second, there is a hook alternate_syntax/4 that can be used to temporary redefine the syntax.

The options below are processed in addition to the options of read_term/3. Note that the line and offset options are mutually exclusive.

line(+Line)
If present, start reading at line Line.
offset(+Characters)
Use seek/4 to go to the indicated location. See seek/4 for limitations of seeking in text-files.
module(+Module)
Use syntax from the given module. Default is the current `source module'.
operators(+List)
List of additional operator declarations to enforce while reading the term.
error(-Error)
If no correct parse can be found, unify Error with a term Offset:Message that indicates the (character) location of the error and the related message. Adding this option makes read_source_term_at_location/3 deterministic (det).
See also
- Use read_source_term/4 to read a file from the start.
- alternate_syntax/4 for locally scoped operators.
  429:- thread_local
  430    last_syntax_error/2.            % location, message
  431
  432read_source_term_at_location(Stream, Term, Options) :-
  433    retractall(last_syntax_error(_,_)),
  434    seek_to_start(Stream, Options),
  435    stream_property(Stream, position(Here)),
  436    '$current_source_module'(DefModule),
  437    option(module(Module), Options, DefModule),
  438    option(operators(Ops), Options, []),
  439    alternate_syntax(Syntax, Module, Setup, Restore),
  440    set_stream_position(Stream, Here),
  441    debug(read, 'Trying with syntax ~w', [Syntax]),
  442    push_operators(Module:Ops),
  443    call(Setup),
  444    Error = error(Formal,_),                 % do not catch timeout, etc.
  445    setup_call_cleanup(
  446        asserta(user:thread_message_hook(_,_,_), Ref), % silence messages
  447        catch(qq_read_term(Stream, Term0,
  448                           [ module(Module)
  449                           | Options
  450                           ]),
  451              Error,
  452              true),
  453        erase(Ref)),
  454    call(Restore),
  455    pop_operators,
  456    (   var(Formal)
  457    ->  !, Term = Term0
  458    ;   assert_error(Error, Options),
  459        fail
  460    ).
  461read_source_term_at_location(_, _, Options) :-
  462    option(error(Error), Options),
  463    !,
  464    setof(CharNo:Msg, retract(last_syntax_error(CharNo, Msg)), Pairs),
  465    last(Pairs, Error).
  466
  467assert_error(Error, Options) :-
  468    option(error(_), Options),
  469    !,
  470    (   (   Error = error(syntax_error(Id),
  471                          stream(_S1, _Line1, _LinePos1, CharNo))
  472        ;   Error = error(syntax_error(Id),
  473                          file(_S2, _Line2, _LinePos2, CharNo))
  474        )
  475    ->  message_to_string(error(syntax_error(Id), _), Msg),
  476        assertz(last_syntax_error(CharNo, Msg))
  477    ;   debug(read, 'Error: ~q', [Error]),
  478        throw(Error)
  479    ).
  480assert_error(_, _).
 alternate_syntax(?Syntax, +Module, -Setup, -Restore) is nondet
Define an alternative syntax to try reading a term at an arbitrary location in module Module.

Calls the hook alternate_syntax/4 with the same signature to allow for user-defined extensions.

Arguments:
Setup- is a deterministic goal to enable this syntax in module.
Restore- is a deterministic goal to revert the actions of Setup.
  496alternate_syntax(prolog, _, true,  true).
  497alternate_syntax(Syntax, M, Setup, Restore) :-
  498    prolog:alternate_syntax(Syntax, M, Setup, Restore).
 seek_to_start(+Stream, +Options) is det
Go to the location from where to start reading.
  505seek_to_start(Stream, Options) :-
  506    option(line(Line), Options),
  507    !,
  508    seek(Stream, 0, bof, _),
  509    seek_to_line(Stream, Line).
  510seek_to_start(Stream, Options) :-
  511    option(offset(Start), Options),
  512    !,
  513    seek(Stream, Start, bof, _).
  514seek_to_start(_, _).
 seek_to_line(+Stream, +Line)
Seek to indicated line-number.
  520seek_to_line(Fd, N) :-
  521    N > 1,
  522    !,
  523    skip(Fd, 10),
  524    NN is N - 1,
  525    seek_to_line(Fd, NN).
  526seek_to_line(_, _).
  527
  528
  529                 /*******************************
  530                 *       QUASI QUOTATIONS       *
  531                 *******************************/
 qq_read_term(+Stream, -Term, +Options)
Same as read_term/3, but dynamically loads known quasi quotations. Quasi quotations that can be autoloaded must be defined using prolog:quasi_quotation_syntax/2.
  539qq_read_term(Stream, Term, Options) :-
  540    select(syntax_errors(ErrorMode), Options, Options1),
  541    ErrorMode \== error,
  542    !,
  543    (   ErrorMode == dec10
  544    ->  repeat,
  545        qq_read_syntax_ex(Stream, Term, Options1, Error),
  546        (   var(Error)
  547        ->  !
  548        ;   print_message(error, Error),
  549            fail
  550        )
  551    ;   qq_read_syntax_ex(Stream, Term, Options1, Error),
  552        (   ErrorMode == fail
  553        ->  print_message(error, Error),
  554            fail
  555        ;   ErrorMode == quiet
  556        ->  fail
  557        ;   domain_error(syntax_errors, ErrorMode)
  558        )
  559    ).
  560qq_read_term(Stream, Term, Options) :-
  561    qq_read_term_ex(Stream, Term, Options).
  562
  563qq_read_syntax_ex(Stream, Term, Options, Error) :-
  564    catch(qq_read_term_ex(Stream, Term, Options),
  565          error(syntax_error(Syntax), Context),
  566          Error = error(Syntax, Context)).
  567
  568qq_read_term_ex(Stream, Term, Options) :-
  569    stream_property(Stream, position(Here)),
  570    catch(read_term(Stream, Term, Options),
  571          error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context),
  572          load_qq_and_retry(Here, Syntax, Module, Context, Stream, Term, Options)).
  573
  574load_qq_and_retry(Here, Syntax, Module, _, Stream, Term, Options) :-
  575    set_stream_position(Stream, Here),
  576    prolog:quasi_quotation_syntax(Syntax, Library),
  577    !,
  578    use_module(Module:Library, [Syntax/4]),
  579    read_term(Stream, Term, Options).
  580load_qq_and_retry(_Pos, Syntax, Module, Context, _Stream, _Term, _Options) :-
  581    print_message(warning, quasi_quotation(undeclared, Syntax)),
  582    throw(error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context)).
 prolog:quasi_quotation_syntax(+Syntax, -Library) is semidet
True when the quasi quotation syntax Syntax can be loaded from Library. Library must be a valid first argument for use_module/2.

This multifile hook is used by library(prolog_source) to load quasi quotation handlers on demand.

  593prolog:quasi_quotation_syntax(html,       library(http/html_write)).
  594prolog:quasi_quotation_syntax(javascript, library(http/js_write)).
  595
  596
  597                 /*******************************
  598                 *           SOURCES            *
  599                 *******************************/
 prolog_open_source(+CanonicalId:atomic, -Stream:stream) is det
Open source with given canonical id (see prolog_canonical_source/2) and remove the #! line if any. Streams opened using this predicate must be closed using prolog_close_source/1. Typically using the skeleton below. Using this skeleton, operator and style-check options are automatically restored to the values before opening the source.
process_source(Src) :-
        prolog_open_source(Src, In),
        call_cleanup(process(Src), prolog_close_source(In)).
  616prolog_open_source(Src, Fd) :-
  617    '$push_input_context'(source),
  618    catch((   prolog:xref_open_source(Src, Fd)
  619          ->  Hooked = true
  620          ;   open(Src, read, Fd),
  621              Hooked = false
  622          ), E,
  623          (   '$pop_input_context',
  624              throw(E)
  625          )),
  626    skip_hashbang(Fd),
  627    push_operators([]),
  628    '$current_source_module'(SM),
  629    '$save_lex_state'(LexState, []),
  630    asserta(open_source(Fd, state(Hooked, Src, LexState, SM))).
  631
  632skip_hashbang(Fd) :-
  633    catch((   peek_char(Fd, #)              % Deal with #! script
  634          ->  skip(Fd, 10)
  635          ;   true
  636          ), E,
  637          (   close(Fd, [force(true)]),
  638              '$pop_input_context',
  639              throw(E)
  640          )).
 prolog:xref_open_source(+SourceID, -Stream)
Hook to open an xref SourceID. This is used for cross-referencing non-files, such as XPCE buffers, files from archives, git repositories, etc. When successful, the corresponding prolog:xref_close_source/2 hook is called for closing the source.
 prolog_close_source(+In:stream) is det
Close a stream opened using prolog_open_source/2. Restores operator and style options. If the stream has not been read to the end, we call expand_term(end_of_file, _) to allow expansion modules to clean-up.
  658prolog_close_source(In) :-
  659    call_cleanup(
  660        restore_source_context(In, Hooked, Src),
  661        close_source(Hooked, Src, In)).
  662
  663close_source(true, Src, In) :-
  664    catch(prolog:xref_close_source(Src, In), _, false),
  665    !,
  666    '$pop_input_context'.
  667close_source(_, _Src, In) :-
  668    close(In, [force(true)]),
  669    '$pop_input_context'.
  670
  671restore_source_context(In, Hooked, Src) :-
  672    (   at_end_of_stream(In)
  673    ->  true
  674    ;   ignore(catch(expand(end_of_file, _, In, _), _, true))
  675    ),
  676    pop_operators,
  677    retractall(mode(In, _)),
  678    (   retract(open_source(In, state(Hooked, Src, LexState, SM)))
  679    ->  '$restore_lex_state'(LexState),
  680        '$set_source_module'(SM)
  681    ;   assertion(fail)
  682    ).
 prolog:xref_close_source(+SourceID, +Stream) is semidet
Called by prolog_close_source/1 to close a source previously opened by the hook prolog:xref_open_source/2. If the hook fails close/2 using the option force(true) is used.
 prolog_canonical_source(+SourceSpec:ground, -Id:atomic) is semidet
Given a user-specification of a source, generate a unique and indexable identifier for it. For files we use the prolog_canonical absolute filename. Id must be valid input for prolog_open_source/2.
  697prolog_canonical_source(Source, Src) :-
  698    var(Source),
  699    !,
  700    Src = Source.
  701prolog_canonical_source(User, user) :-
  702    User == user,
  703    !.
  704prolog_canonical_source(Src, Id) :-             % Call hook
  705    prolog:xref_source_identifier(Src, Id),
  706    !.
  707prolog_canonical_source(Source, Src) :-
  708    source_file(Source),
  709    !,
  710    Src = Source.
  711prolog_canonical_source(Source, Src) :-
  712    absolute_file_name(Source, Src,
  713                       [ file_type(prolog),
  714                         access(read),
  715                         file_errors(fail)
  716                       ]),
  717    !.
 file_name_on_path(+File:atom, -OnPath) is det
True if OnPath a description of File based on the file search path. This performs the inverse of absolute_file_name/3.
  725file_name_on_path(Path, ShortId) :-
  726    (   file_alias_path(Alias, Dir),
  727        atom_concat(Dir, Local, Path)
  728    ->  (   Alias == '.'
  729        ->  ShortId = Local
  730        ;   file_name_extension(Base, pl, Local)
  731        ->  ShortId =.. [Alias, Base]
  732        ;   ShortId =.. [Alias, Local]
  733        )
  734    ;   ShortId = Path
  735    ).
 file_alias_path(-Alias, ?Dir) is nondet
True if file Alias points to Dir. Multiple solutions are generated with the longest directory first.
  743:- dynamic
  744    alias_cache/2.  745
  746file_alias_path(Alias, Dir) :-
  747    (   alias_cache(_, _)
  748    ->  true
  749    ;   build_alias_cache
  750    ),
  751    (   nonvar(Dir)
  752    ->  ensure_slash(Dir, DirSlash),
  753        alias_cache(Alias, DirSlash)
  754    ;   alias_cache(Alias, Dir)
  755    ).
  756
  757build_alias_cache :-
  758    findall(t(DirLen, AliasLen, Alias, Dir),
  759            search_path(Alias, Dir, AliasLen, DirLen), Ts),
  760    sort(0, >, Ts, List),
  761    forall(member(t(_, _, Alias, Dir), List),
  762           assert(alias_cache(Alias, Dir))).
  763
  764search_path('.', Here, 999, DirLen) :-
  765    working_directory(Here0, Here0),
  766    ensure_slash(Here0, Here),
  767    atom_length(Here, DirLen).
  768search_path(Alias, Dir, AliasLen, DirLen) :-
  769    user:file_search_path(Alias, _),
  770    Alias \== autoload,             % TBD: Multifile predicate?
  771    Alias \== noautoload,
  772    Spec =.. [Alias,'.'],
  773    atom_length(Alias, AliasLen0),
  774    AliasLen is 1000 - AliasLen0,   % must do reverse sort
  775    absolute_file_name(Spec, Dir0,
  776                       [ file_type(directory),
  777                         access(read),
  778                         solutions(all),
  779                         file_errors(fail)
  780                       ]),
  781    ensure_slash(Dir0, Dir),
  782    atom_length(Dir, DirLen).
  783
  784ensure_slash(Dir, Dir) :-
  785    sub_atom(Dir, _, _, 0, /),
  786    !.
  787ensure_slash(Dir0, Dir) :-
  788    atom_concat(Dir0, /, Dir).
 path_segments_atom(+Segments, -Atom) is det
path_segments_atom(-Segments, +Atom) is det
Translate between a path represented as a/b/c and an atom representing the same path. For example:
?- path_segments_atom(a/b/c, X).
X = 'a/b/c'.
?- path_segments_atom(S, 'a/b/c'), display(S).
/(/(a,b),c)
S = a/b/c.

This predicate is part of the Prolog source library because SWI-Prolog allows writing paths as /-nested terms and source-code analysis programs often need this.

  809path_segments_atom(Segments, Atom) :-
  810    var(Atom),
  811    !,
  812    (   atomic(Segments)
  813    ->  Atom = Segments
  814    ;   segments_to_list(Segments, List, [])
  815    ->  atomic_list_concat(List, /, Atom)
  816    ;   throw(error(type_error(file_path, Segments), _))
  817    ).
  818path_segments_atom(Segments, Atom) :-
  819    atomic_list_concat(List, /, Atom),
  820    parts_to_path(List, Segments).
  821
  822segments_to_list(Var, _, _) :-
  823    var(Var), !, fail.
  824segments_to_list(A/B, H, T) :-
  825    segments_to_list(A, H, T0),
  826    segments_to_list(B, T0, T).
  827segments_to_list(A, [A|T], T) :-
  828    atomic(A).
  829
  830parts_to_path([One], One) :- !.
  831parts_to_path(List, More/T) :-
  832    (   append(H, [T], List)
  833    ->  parts_to_path(H, More)
  834    ).
 directory_source_files(+Dir, -Files, +Options) is det
True when Files is a sorted list of Prolog source files in Dir. Options:
recursive(boolean)
If true (default false), recurse into subdirectories
if(Condition)
If true (default loaded), only report loaded files.

Other options are passed to absolute_file_name/3, unless loaded(true) is passed.

  849directory_source_files(Dir, SrcFiles, Options) :-
  850    option(if(loaded), Options, loaded),
  851    !,
  852    absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
  853    (   option(recursive(true), Options)
  854    ->  ensure_slash(AbsDir, Prefix),
  855        findall(F, (  source_file(F),
  856                      sub_atom(F, 0, _, _, Prefix)
  857                   ),
  858                SrcFiles)
  859    ;   findall(F, ( source_file(F),
  860                     file_directory_name(F, AbsDir)
  861                   ),
  862                SrcFiles)
  863    ).
  864directory_source_files(Dir, SrcFiles, Options) :-
  865    absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
  866    directory_files(AbsDir, Files),
  867    phrase(src_files(Files, AbsDir, Options), SrcFiles).
  868
  869src_files([], _, _) -->
  870    [].
  871src_files([H|T], Dir, Options) -->
  872    { file_name_extension(_, Ext, H),
  873      user:prolog_file_type(Ext, prolog),
  874      \+ user:prolog_file_type(Ext, qlf),
  875      dir_file_path(Dir, H, File0),
  876      absolute_file_name(File0, File,
  877                         [ file_errors(fail)
  878                         | Options
  879                         ])
  880    },
  881    !,
  882    [File],
  883    src_files(T, Dir, Options).
  884src_files([H|T], Dir, Options) -->
  885    { \+ special(H),
  886      option(recursive(true), Options),
  887      dir_file_path(Dir, H, SubDir),
  888      exists_directory(SubDir),
  889      !,
  890      catch(directory_files(SubDir, Files), _, fail)
  891    },
  892    !,
  893    src_files(Files, SubDir, Options),
  894    src_files(T, Dir, Options).
  895src_files([_|T], Dir, Options) -->
  896    src_files(T, Dir, Options).
  897
  898special(.).
  899special(..).
  900
  901% avoid dependency on library(filesex), which also pulls a foreign
  902% dependency.
  903dir_file_path(Dir, File, Path) :-
  904    (   sub_atom(Dir, _, _, 0, /)
  905    ->  atom_concat(Dir, File, Path)
  906    ;   atom_concat(Dir, /, TheDir),
  907        atom_concat(TheDir, File, Path)
  908    ).
  909
  910
  911
  912                 /*******************************
  913                 *           MESSAGES           *
  914                 *******************************/
  915
  916:- multifile
  917    prolog:message//1.  918
  919prolog:message(quasi_quotation(undeclared, Syntax)) -->
  920    [ 'Undeclared quasi quotation syntax: ~w'-[Syntax], nl,
  921      'Autoloading can be defined using prolog:quasi_quotation_syntax/2'
  922    ]