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:- use_module(operators).   51:- use_module(lists).   52:- use_module(debug).   53:- use_module(option).   54:- use_module(error).   55:- use_module(apply).

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

*/

   80:- thread_local
   81    open_source/2,          % Stream, State
   82    mode/2.                 % Stream, Data
   83
   84:- multifile
   85    requires_library/2,
   86    prolog:xref_source_identifier/2, % +Source, -Id
   87    prolog:xref_source_time/2,       % +Source, -Modified
   88    prolog:xref_open_source/2,       % +SourceId, -Stream
   89    prolog:xref_close_source/2,      % +SourceId, -Stream
   90    prolog:alternate_syntax/4,       % Syntax, +Module, -Setup, -Restore
   91    prolog:quasi_quotation_syntax/2. % Syntax, Library
   92
   93
   94:- predicate_options(prolog_read_source_term/4, 4,
   95                     [ pass_to(system:read_clause/3, 3)
   96                     ]).   97:- predicate_options(read_source_term_at_location/3, 3,
   98                     [ line(integer),
   99                       offset(integer),
  100                       module(atom),
  101                       operators(list),
  102                       error(-any),
  103                       pass_to(system:read_term/3, 3)
  104                     ]).  105:- predicate_options(directory_source_files/3, 3,
  106                     [ recursive(boolean),
  107                       if(oneof([true,loaded])),
  108                       pass_to(system:absolute_file_name/3,3)
  109                     ]).  110
  111
  112                 /*******************************
  113                 *           READING            *
  114                 *******************************/
 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.
  130prolog_read_source_term(In, Term, Expanded, Options) :-
  131    maplist(read_clause_option, Options),
  132    !,
  133    select_option(subterm_positions(TermPos), Options,
  134                  RestOptions, TermPos),
  135    read_clause(In, Term,
  136                [ subterm_positions(TermPos)
  137                | RestOptions
  138                ]),
  139    expand(Term, TermPos, In, Expanded),
  140    '$current_source_module'(M),
  141    update_state(Term, Expanded, M).
  142prolog_read_source_term(In, Term, Expanded, Options) :-
  143    '$current_source_module'(M),
  144    select_option(syntax_errors(SE), Options, RestOptions0, dec10),
  145    select_option(subterm_positions(TermPos), RestOptions0,
  146                  RestOptions, TermPos),
  147    (   style_check(?(singleton))
  148    ->  FinalOptions = [ singletons(warning) | RestOptions ]
  149    ;   FinalOptions = RestOptions
  150    ),
  151    read_term(In, Term,
  152              [ module(M),
  153                syntax_errors(SE),
  154                subterm_positions(TermPos)
  155              | FinalOptions
  156              ]),
  157    expand(Term, TermPos, In, Expanded),
  158    update_state(Term, Expanded, M).
  159
  160read_clause_option(syntax_errors(_)).
  161read_clause_option(term_position(_)).
  162read_clause_option(process_comment(_)).
  163read_clause_option(comments(_)).
  164
  165:- public
  166    expand/3.                       % Used by Prolog colour
  167
  168expand(Term, In, Exp) :-
  169    expand(Term, _, In, Exp).
  170
  171expand(Var, _, _, Var) :-
  172    var(Var),
  173    !.
  174expand(Term, _, _, Term) :-
  175    no_expand(Term),
  176    !.
  177expand(Term, _, _, _) :-
  178    requires_library(Term, Lib),
  179    ensure_loaded(user:Lib),
  180    fail.
  181expand(Term, _, In, Term) :-
  182    chr_expandable(Term, In),
  183    !.
  184expand(Term, Pos, _, Expanded) :-
  185    expand_term(Term, Pos, Expanded, _).
  186
  187no_expand((:- if(_))).
  188no_expand((:- elif(_))).
  189no_expand((:- else)).
  190no_expand((:- endif)).
  191no_expand((:- require(_))).
  192
  193chr_expandable((:- chr_constraint(_)), In) :-
  194    add_mode(In, chr).
  195chr_expandable((handler(_)), In) :-
  196    mode(In, chr).
  197chr_expandable((rules(_)), In) :-
  198    mode(In, chr).
  199chr_expandable(<=>(_, _), In) :-
  200    mode(In, chr).
  201chr_expandable(@(_, _), In) :-
  202    mode(In, chr).
  203chr_expandable(==>(_, _), In) :-
  204    mode(In, chr).
  205chr_expandable(pragma(_, _), In) :-
  206    mode(In, chr).
  207chr_expandable(option(_, _), In) :-
  208    mode(In, chr).
  209
  210add_mode(Stream, Mode) :-
  211    mode(Stream, Mode),
  212    !.
  213add_mode(Stream, Mode) :-
  214    asserta(mode(Stream, Mode)).
 requires_library(+Term, -Library)
known expansion hooks. May be expanded as multifile predicate.
  220requires_library((:- emacs_begin_mode(_,_,_,_,_)), library(emacs_extend)).
  221requires_library((:- draw_begin_shape(_,_,_,_)),   library(pcedraw)).
  222requires_library((:- use_module(library(pce))),    library(pce)).
  223requires_library((:- pce_begin_class(_,_)),        library(pce)).
  224requires_library((:- pce_begin_class(_,_,_)),      library(pce)).
 update_state(+Term, +Expanded, +Module) is det
Update operators and style-check options from the expanded term.
  230:- multifile
  231    pce_expansion:push_compile_operators/1,
  232    pce_expansion:pop_compile_operators/0.  233
  234update_state(Raw, _, _) :-
  235    Raw == (:- pce_end_class),
  236    !,
  237    ignore(pce_expansion:pop_compile_operators).
  238update_state(Raw, _, SM) :-
  239    subsumes_term((:- pce_extend_class(_)), Raw),
  240    !,
  241    pce_expansion:push_compile_operators(SM).
  242update_state(_Raw, Expanded, M) :-
  243    update_state(Expanded, M).
  244
  245update_state(Var, _) :-
  246    var(Var),
  247    !.
  248update_state([], _) :-
  249    !.
  250update_state([H|T], M) :-
  251    !,
  252    update_state(H, M),
  253    update_state(T, M).
  254update_state((:- Directive), M) :-
  255    nonvar(Directive),
  256    !,
  257    catch(update_directive(Directive, M), _, true).
  258update_state((?- Directive), M) :-
  259    !,
  260    update_state((:- Directive), M).
  261update_state(_, _).
  262
  263update_directive(module(Module, Public), _) :-
  264    atom(Module),
  265    is_list(Public),
  266    !,
  267    '$set_source_module'(Module),
  268    maplist(import_syntax(_,Module, _), Public).
  269update_directive(M:op(P,T,N), SM) :-
  270    atom(M),
  271    ground(op(P,T,N)),
  272    !,
  273    update_directive(op(P,T,N), SM).
  274update_directive(op(P,T,N), SM) :-
  275    ground(op(P,T,N)),
  276    !,
  277    strip_module(SM:N, M, PN),
  278    push_op(P,T,M:PN).
  279update_directive(style_check(Style), _) :-
  280    ground(Style),
  281    style_check(Style),
  282    !.
  283update_directive(use_module(Spec), SM) :-
  284    ground(Spec),
  285    catch(module_decl(Spec, Path, Public), _, fail),
  286    !,
  287    maplist(import_syntax(Path, SM, _), Public).
  288update_directive(use_module(Spec, Imports), SM) :-
  289    ground(Spec),
  290    is_list(Imports),
  291    catch(module_decl(Spec, Path, Public), _, fail),
  292    !,
  293    maplist(import_syntax(Path, SM, Imports), Public).
  294update_directive(pce_begin_class_definition(_,_,_,_), SM) :-
  295    pce_expansion:push_compile_operators(SM),
  296    !.
  297update_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.
  304import_syntax(_, _, _, Var) :-
  305    var(Var),
  306    !.
  307import_syntax(_, M, Imports, Op) :-
  308    Op = op(_,_,_),
  309    \+ \+ member(Op, Imports),
  310    !,
  311    update_directive(Op, M).
  312import_syntax(Path, SM, Imports, Syntax/4) :-
  313    \+ \+ member(Syntax/4, Imports),
  314    load_quasi_quotation_syntax(SM:Path, Syntax),
  315    !.
  316import_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.
  333load_quasi_quotation_syntax(SM:Path, Syntax) :-
  334    atom(Path), atom(Syntax),
  335    source_file_property(Path, module(M)),
  336    functor(ST, Syntax, 4),
  337    predicate_property(M:ST, quasi_quotation_syntax),
  338    !,
  339    use_module(SM:Path, [Syntax/4]).
  340load_quasi_quotation_syntax(SM:Path, Syntax) :-
  341    atom(Path), atom(Syntax),
  342    prolog:quasi_quotation_syntax(Syntax, Spec),
  343    absolute_file_name(Spec, Path2,
  344                       [ file_type(prolog),
  345                         file_errors(fail),
  346                         access(read)
  347                       ]),
  348    Path == Path2,
  349    !,
  350    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.
  358module_decl(Spec, Path, Decl) :-
  359    absolute_file_name(Spec, Path,
  360                       [ file_type(prolog),
  361                         file_errors(fail),
  362                         access(read)
  363                       ]),
  364    setup_call_cleanup(
  365        prolog_open_source(Path, In),
  366        read_module_decl(In, Decl),
  367        prolog_close_source(In)).
  368
  369read_module_decl(In, Decl) :-
  370    read(In, Term0),
  371    read_module_decl(Term0, In, Decl).
  372
  373read_module_decl(Term, _In, Decl) :-
  374    subsumes_term((:- module(_, Decl)), Term),
  375    !,
  376    Term = (:- module(_, Decl)).
  377read_module_decl(Term, In, Decl) :-
  378    subsumes_term((:- encoding(_)), Term),
  379    !,
  380    Term = (:- encoding(Enc)),
  381    set_stream(In, encoding(Enc)),
  382    read(In, Term2),
  383    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 prolog: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.
- prolog:alternate_syntax/4 for locally scoped operators.
  427:- thread_local
  428    last_syntax_error/2.            % location, message
  429
  430read_source_term_at_location(Stream, Term, Options) :-
  431    retractall(last_syntax_error(_,_)),
  432    seek_to_start(Stream, Options),
  433    stream_property(Stream, position(Here)),
  434    '$current_source_module'(DefModule),
  435    option(module(Module), Options, DefModule),
  436    option(operators(Ops), Options, []),
  437    alternate_syntax(Syntax, Module, Setup, Restore),
  438    set_stream_position(Stream, Here),
  439    debug(read, 'Trying with syntax ~w', [Syntax]),
  440    push_operators(Module:Ops),
  441    call(Setup),
  442    Error = error(Formal,_),                 % do not catch timeout, etc.
  443    setup_call_cleanup(
  444        asserta(user:thread_message_hook(_,_,_), Ref), % silence messages
  445        catch(qq_read_term(Stream, Term0,
  446                           [ module(Module)
  447                           | Options
  448                           ]),
  449              Error,
  450              true),
  451        erase(Ref)),
  452    call(Restore),
  453    pop_operators,
  454    (   var(Formal)
  455    ->  !, Term = Term0
  456    ;   assert_error(Error, Options),
  457        fail
  458    ).
  459read_source_term_at_location(_, _, Options) :-
  460    option(error(Error), Options),
  461    !,
  462    setof(CharNo:Msg, retract(last_syntax_error(CharNo, Msg)), Pairs),
  463    last(Pairs, Error).
  464
  465assert_error(Error, Options) :-
  466    option(error(_), Options),
  467    !,
  468    (   (   Error = error(syntax_error(Id),
  469                          stream(_S1, _Line1, _LinePos1, CharNo))
  470        ;   Error = error(syntax_error(Id),
  471                          file(_S2, _Line2, _LinePos2, CharNo))
  472        )
  473    ->  message_to_string(error(syntax_error(Id), _), Msg),
  474        assertz(last_syntax_error(CharNo, Msg))
  475    ;   debug(read, 'Error: ~q', [Error]),
  476        throw(Error)
  477    ).
  478assert_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 prolog: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.
  494alternate_syntax(prolog, _, true,  true).
  495alternate_syntax(Syntax, M, Setup, Restore) :-
  496    prolog:alternate_syntax(Syntax, M, Setup, Restore).
 seek_to_start(+Stream, +Options) is det
Go to the location from where to start reading.
  503seek_to_start(Stream, Options) :-
  504    option(line(Line), Options),
  505    !,
  506    seek(Stream, 0, bof, _),
  507    seek_to_line(Stream, Line).
  508seek_to_start(Stream, Options) :-
  509    option(offset(Start), Options),
  510    !,
  511    seek(Stream, Start, bof, _).
  512seek_to_start(_, _).
 seek_to_line(+Stream, +Line)
Seek to indicated line-number.
  518seek_to_line(Fd, N) :-
  519    N > 1,
  520    !,
  521    skip(Fd, 10),
  522    NN is N - 1,
  523    seek_to_line(Fd, NN).
  524seek_to_line(_, _).
  525
  526
  527                 /*******************************
  528                 *       QUASI QUOTATIONS       *
  529                 *******************************/
 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.
  537qq_read_term(Stream, Term, Options) :-
  538    select(syntax_errors(ErrorMode), Options, Options1),
  539    ErrorMode \== error,
  540    !,
  541    (   ErrorMode == dec10
  542    ->  repeat,
  543        qq_read_syntax_ex(Stream, Term, Options1, Error),
  544        (   var(Error)
  545        ->  !
  546        ;   print_message(error, Error),
  547            fail
  548        )
  549    ;   qq_read_syntax_ex(Stream, Term, Options1, Error),
  550        (   ErrorMode == fail
  551        ->  print_message(error, Error),
  552            fail
  553        ;   ErrorMode == quiet
  554        ->  fail
  555        ;   domain_error(syntax_errors, ErrorMode)
  556        )
  557    ).
  558qq_read_term(Stream, Term, Options) :-
  559    qq_read_term_ex(Stream, Term, Options).
  560
  561qq_read_syntax_ex(Stream, Term, Options, Error) :-
  562    catch(qq_read_term_ex(Stream, Term, Options),
  563          error(syntax_error(Syntax), Context),
  564          Error = error(Syntax, Context)).
  565
  566qq_read_term_ex(Stream, Term, Options) :-
  567    stream_property(Stream, position(Here)),
  568    catch(read_term(Stream, Term, Options),
  569          error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context),
  570          load_qq_and_retry(Here, Syntax, Module, Context, Stream, Term, Options)).
  571
  572load_qq_and_retry(Here, Syntax, Module, _, Stream, Term, Options) :-
  573    set_stream_position(Stream, Here),
  574    prolog:quasi_quotation_syntax(Syntax, Library),
  575    !,
  576    use_module(Module:Library, [Syntax/4]),
  577    read_term(Stream, Term, Options).
  578load_qq_and_retry(_Pos, Syntax, Module, Context, _Stream, _Term, _Options) :-
  579    print_message(warning, quasi_quotation(undeclared, Syntax)),
  580    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.

  591prolog:quasi_quotation_syntax(html,       library(http/html_write)).
  592prolog:quasi_quotation_syntax(javascript, library(http/js_write)).
  593
  594
  595                 /*******************************
  596                 *           SOURCES            *
  597                 *******************************/
 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)).
  614prolog_open_source(Src, Fd) :-
  615    '$push_input_context'(source),
  616    catch((   prolog:xref_open_source(Src, Fd)
  617          ->  Hooked = true
  618          ;   open(Src, read, Fd),
  619              Hooked = false
  620          ), E,
  621          (   '$pop_input_context',
  622              throw(E)
  623          )),
  624    skip_hashbang(Fd),
  625    push_operators([]),
  626    '$current_source_module'(SM),
  627    '$save_lex_state'(LexState, []),
  628    asserta(open_source(Fd, state(Hooked, Src, LexState, SM))).
  629
  630skip_hashbang(Fd) :-
  631    catch((   peek_char(Fd, #)              % Deal with #! script
  632          ->  skip(Fd, 10)
  633          ;   true
  634          ), E,
  635          (   close(Fd, [force(true)]),
  636              '$pop_input_context',
  637              throw(E)
  638          )).
 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.
  656prolog_close_source(In) :-
  657    call_cleanup(
  658        restore_source_context(In, Hooked, Src),
  659        close_source(Hooked, Src, In)).
  660
  661close_source(true, Src, In) :-
  662    catch(prolog:xref_close_source(Src, In), _, false),
  663    !,
  664    '$pop_input_context'.
  665close_source(_, _Src, In) :-
  666    close(In, [force(true)]),
  667    '$pop_input_context'.
  668
  669restore_source_context(In, Hooked, Src) :-
  670    (   at_end_of_stream(In)
  671    ->  true
  672    ;   ignore(catch(expand(end_of_file, _, In, _), _, true))
  673    ),
  674    pop_operators,
  675    retractall(mode(In, _)),
  676    (   retract(open_source(In, state(Hooked, Src, LexState, SM)))
  677    ->  '$restore_lex_state'(LexState),
  678        '$set_source_module'(SM)
  679    ;   assertion(fail)
  680    ).
 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.
  695prolog_canonical_source(Source, Src) :-
  696    var(Source),
  697    !,
  698    Src = Source.
  699prolog_canonical_source(User, user) :-
  700    User == user,
  701    !.
  702prolog_canonical_source(Src, Id) :-             % Call hook
  703    prolog:xref_source_identifier(Src, Id),
  704    !.
  705prolog_canonical_source(Source, Src) :-
  706    source_file(Source),
  707    !,
  708    Src = Source.
  709prolog_canonical_source(Source, Src) :-
  710    absolute_file_name(Source, Src,
  711                       [ file_type(prolog),
  712                         access(read),
  713                         file_errors(fail)
  714                       ]),
  715    !.
 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.
  723file_name_on_path(Path, ShortId) :-
  724    (   file_alias_path(Alias, Dir),
  725        atom_concat(Dir, Local, Path)
  726    ->  (   Alias == '.'
  727        ->  ShortId = Local
  728        ;   file_name_extension(Base, pl, Local)
  729        ->  ShortId =.. [Alias, Base]
  730        ;   ShortId =.. [Alias, Local]
  731        )
  732    ;   ShortId = Path
  733    ).
 file_alias_path(-Alias, ?Dir) is nondet
True if file Alias points to Dir. Multiple solutions are generated with the longest directory first.
  741:- dynamic
  742    alias_cache/2.  743
  744file_alias_path(Alias, Dir) :-
  745    (   alias_cache(_, _)
  746    ->  true
  747    ;   build_alias_cache
  748    ),
  749    (   nonvar(Dir)
  750    ->  ensure_slash(Dir, DirSlash),
  751        alias_cache(Alias, DirSlash)
  752    ;   alias_cache(Alias, Dir)
  753    ).
  754
  755build_alias_cache :-
  756    findall(t(DirLen, AliasLen, Alias, Dir),
  757            search_path(Alias, Dir, AliasLen, DirLen), Ts),
  758    sort(0, >, Ts, List),
  759    forall(member(t(_, _, Alias, Dir), List),
  760           assert(alias_cache(Alias, Dir))).
  761
  762search_path('.', Here, 999, DirLen) :-
  763    working_directory(Here0, Here0),
  764    ensure_slash(Here0, Here),
  765    atom_length(Here, DirLen).
  766search_path(Alias, Dir, AliasLen, DirLen) :-
  767    user:file_search_path(Alias, _),
  768    Alias \== autoload,
  769    Spec =.. [Alias,'.'],
  770    atom_length(Alias, AliasLen0),
  771    AliasLen is 1000 - AliasLen0,   % must do reverse sort
  772    absolute_file_name(Spec, Dir0,
  773                       [ file_type(directory),
  774                         access(read),
  775                         solutions(all),
  776                         file_errors(fail)
  777                       ]),
  778    ensure_slash(Dir0, Dir),
  779    atom_length(Dir, DirLen).
  780
  781ensure_slash(Dir, Dir) :-
  782    sub_atom(Dir, _, _, 0, /),
  783    !.
  784ensure_slash(Dir0, Dir) :-
  785    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.

  806path_segments_atom(Segments, Atom) :-
  807    var(Atom),
  808    !,
  809    (   atomic(Segments)
  810    ->  Atom = Segments
  811    ;   segments_to_list(Segments, List, [])
  812    ->  atomic_list_concat(List, /, Atom)
  813    ;   throw(error(type_error(file_path, Segments), _))
  814    ).
  815path_segments_atom(Segments, Atom) :-
  816    atomic_list_concat(List, /, Atom),
  817    parts_to_path(List, Segments).
  818
  819segments_to_list(Var, _, _) :-
  820    var(Var), !, fail.
  821segments_to_list(A/B, H, T) :-
  822    segments_to_list(A, H, T0),
  823    segments_to_list(B, T0, T).
  824segments_to_list(A, [A|T], T) :-
  825    atomic(A).
  826
  827parts_to_path([One], One) :- !.
  828parts_to_path(List, More/T) :-
  829    (   append(H, [T], List)
  830    ->  parts_to_path(H, More)
  831    ).
 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.

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