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-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]).

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

*/

   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                 *******************************/
 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.
  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).
  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).
  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)).
 requires_library(+Term, -Library)
known expansion hooks. May be expanded as multifile predicate.
  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)).
 update_state(+Term, +Expanded, +Module) is det
Update operators and style-check options from Term or Expanded.
  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) =>
  251    update_state(Expanded, M).
  252
  253update_state(Var, _) :-
  254    var(Var),
  255    !.
  256update_state([], _) :-
  257    !.
  258update_state([H|T], M) :-
  259    !,
  260    update_state(H, M),
  261    update_state(T, M).
  262update_state((:- Directive), M) :-
  263    nonvar(Directive),
  264    !,
  265    catch(update_directive(Directive, M), _, true).
  266update_state((?- Directive), M) :-
  267    !,
  268    update_state((:- Directive), M).
  269update_state(html_write:html_meta_head(Head,Module,Meta), _M) :-
  270    (   html_write:html_meta_head(Head,Module,Meta)
  271    ->  true
  272    ;   assertz(html_write:html_meta_head(Head,Module,Meta))
  273    ).
  274update_state(_, _).
  275
  276update_directive(Directive, Module) :-
  277    prolog:xref_update_syntax((:- Directive), Module),
  278    !.
  279update_directive(module(Module, Public), _) :-
  280    atom(Module),
  281    is_list(Public),
  282    !,
  283    '$set_source_module'(Module),
  284    maplist(import_syntax(_,Module, _), Public).
  285update_directive(M:op(P,T,N), SM) :-
  286    atom(M),
  287    ground(op(P,T,N)),
  288    !,
  289    update_directive(op(P,T,N), SM).
  290update_directive(op(P,T,N), SM) :-
  291    ground(op(P,T,N)),
  292    !,
  293    strip_module(SM:N, M, PN),
  294    push_op(P,T,M:PN).
  295update_directive(style_check(Style), _) :-
  296    ground(Style),
  297    style_check(Style),
  298    !.
  299update_directive(use_module(Spec), SM) :-
  300    ground(Spec),
  301    catch(module_decl(Spec, Path, Public), _, fail),
  302    is_list(Public),
  303    !,
  304    maplist(import_syntax(Path, SM, _), Public).
  305update_directive(use_module(Spec, Imports), SM) :-
  306    ground(Spec),
  307    is_list(Imports),
  308    catch(module_decl(Spec, Path, Public), _, fail),
  309    is_list(Public),
  310    !,
  311    maplist(import_syntax(Path, SM, Imports), Public).
  312update_directive(pce_begin_class_definition(_,_,_,_), SM) :-
  313    pce_expansion:push_compile_operators(SM),
  314    !.
  315update_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.
  322import_syntax(_, _, _, Var) :-
  323    var(Var),
  324    !.
  325import_syntax(_, M, Imports, Op) :-
  326    Op = op(_,_,_),
  327    \+ \+ member(Op, Imports),
  328    !,
  329    update_directive(Op, M).
  330import_syntax(Path, SM, Imports, Syntax/4) :-
  331    \+ \+ member(Syntax/4, Imports),
  332    load_quasi_quotation_syntax(SM:Path, Syntax),
  333    !.
  334import_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.
  351load_quasi_quotation_syntax(SM:Path, Syntax) :-
  352    atom(Path), atom(Syntax),
  353    source_file_property(Path, module(M)),
  354    functor(ST, Syntax, 4),
  355    predicate_property(M:ST, quasi_quotation_syntax),
  356    !,
  357    use_module(SM:Path, [Syntax/4]).
  358load_quasi_quotation_syntax(SM:Path, Syntax) :-
  359    atom(Path), atom(Syntax),
  360    prolog:quasi_quotation_syntax(Syntax, Spec),
  361    absolute_file_name(Spec, Path2,
  362                       [ file_type(prolog),
  363                         file_errors(fail),
  364                         access(read)
  365                       ]),
  366    Path == Path2,
  367    !,
  368    use_module(SM:Path, [Syntax/4]).
 module_decl(+FileSpec, -Source, -Exports) 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.
  376module_decl(Spec, Source, Exports) :-
  377    absolute_file_name(Spec, Path,
  378                       [ file_type(prolog),
  379                         file_errors(fail),
  380                         access(read)
  381                       ]),
  382    module_decl_(Path, Source, Exports).
  383
  384module_decl_(Path, Source, Exports) :-
  385    file_name_extension(_, qlf, Path),
  386    !,
  387    '$qlf_module'(Path, Info),
  388    _{file:Source, exports:Exports} :< Info.
  389module_decl_(Path, Path, Exports) :-
  390    setup_call_cleanup(
  391        prolog_open_source(Path, In),
  392        read_module_decl(In, Exports),
  393        prolog_close_source(In)).
  394
  395read_module_decl(In, Decl) :-
  396    read(In, Term0),
  397    read_module_decl(Term0, In, Decl).
  398
  399read_module_decl((:- module(_, DeclIn)), _In, Decl) =>
  400    Decl = DeclIn.
  401read_module_decl((:- encoding(Enc)), In, Decl) =>
  402    set_stream(In, encoding(Enc)),
  403    read(In, Term2),
  404    read_module_decl(Term2, In, Decl).
  405read_module_decl(_, _, _) =>
  406    fail.
 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.
  450:- thread_local
  451    last_syntax_error/2.            % location, message
  452
  453read_source_term_at_location(Stream, Term, Options) :-
  454    retractall(last_syntax_error(_,_)),
  455    seek_to_start(Stream, Options),
  456    stream_property(Stream, position(Here)),
  457    '$current_source_module'(DefModule),
  458    option(module(Module), Options, DefModule),
  459    option(operators(Ops), Options, []),
  460    alternate_syntax(Syntax, Module, Setup, Restore),
  461    set_stream_position(Stream, Here),
  462    debug(read, 'Trying with syntax ~w', [Syntax]),
  463    push_operators(Module:Ops),
  464    call(Setup),
  465    Error = error(Formal,_),                 % do not catch timeout, etc.
  466    setup_call_cleanup(
  467        asserta(user:thread_message_hook(_,_,_), Ref), % silence messages
  468        catch(qq_read_term(Stream, Term0,
  469                           [ module(Module)
  470                           | Options
  471                           ]),
  472              Error,
  473              true),
  474        erase(Ref)),
  475    call(Restore),
  476    pop_operators,
  477    (   var(Formal)
  478    ->  !, Term = Term0
  479    ;   assert_error(Error, Options),
  480        fail
  481    ).
  482read_source_term_at_location(_, _, Options) :-
  483    option(error(Error), Options),
  484    !,
  485    setof(CharNo:Msg, retract(last_syntax_error(CharNo, Msg)), Pairs),
  486    last(Pairs, Error).
  487
  488assert_error(Error, Options) :-
  489    option(error(_), Options),
  490    !,
  491    (   (   Error = error(syntax_error(Id),
  492                          stream(_S1, _Line1, _LinePos1, CharNo))
  493        ;   Error = error(syntax_error(Id),
  494                          file(_S2, _Line2, _LinePos2, CharNo))
  495        )
  496    ->  message_to_string(error(syntax_error(Id), _), Msg),
  497        assertz(last_syntax_error(CharNo, Msg))
  498    ;   debug(read, 'Error: ~q', [Error]),
  499        throw(Error)
  500    ).
  501assert_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.
  517alternate_syntax(prolog, _, true,  true).
  518alternate_syntax(Syntax, M, Setup, Restore) :-
  519    prolog:alternate_syntax(Syntax, M, Setup, Restore).
 seek_to_start(+Stream, +Options) is det
Go to the location from where to start reading.
  526seek_to_start(Stream, Options) :-
  527    option(line(Line), Options),
  528    !,
  529    seek(Stream, 0, bof, _),
  530    seek_to_line(Stream, Line).
  531seek_to_start(Stream, Options) :-
  532    option(offset(Start), Options),
  533    !,
  534    seek(Stream, Start, bof, _).
  535seek_to_start(_, _).
 seek_to_line(+Stream, +Line)
Seek to indicated line-number.
  541seek_to_line(Fd, N) :-
  542    N > 1,
  543    !,
  544    skip(Fd, 10),
  545    NN is N - 1,
  546    seek_to_line(Fd, NN).
  547seek_to_line(_, _).
  548
  549
  550                 /*******************************
  551                 *       QUASI QUOTATIONS       *
  552                 *******************************/
 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.
  560qq_read_term(Stream, Term, Options) :-
  561    select(syntax_errors(ErrorMode), Options, Options1),
  562    ErrorMode \== error,
  563    !,
  564    (   ErrorMode == dec10
  565    ->  repeat,
  566        qq_read_syntax_ex(Stream, Term, Options1, Error),
  567        (   var(Error)
  568        ->  !
  569        ;   print_message(error, Error),
  570            fail
  571        )
  572    ;   qq_read_syntax_ex(Stream, Term, Options1, Error),
  573        (   ErrorMode == fail
  574        ->  print_message(error, Error),
  575            fail
  576        ;   ErrorMode == quiet
  577        ->  fail
  578        ;   domain_error(syntax_errors, ErrorMode)
  579        )
  580    ).
  581qq_read_term(Stream, Term, Options) :-
  582    qq_read_term_ex(Stream, Term, Options).
  583
  584qq_read_syntax_ex(Stream, Term, Options, Error) :-
  585    catch(qq_read_term_ex(Stream, Term, Options),
  586          error(syntax_error(Syntax), Context),
  587          Error = error(Syntax, Context)).
  588
  589qq_read_term_ex(Stream, Term, Options) :-
  590    stream_property(Stream, position(Here)),
  591    catch(read_term(Stream, Term, Options),
  592          error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context),
  593          load_qq_and_retry(Here, Syntax, Module, Context, Stream, Term, Options)).
  594
  595load_qq_and_retry(Here, Syntax, Module, _, Stream, Term, Options) :-
  596    set_stream_position(Stream, Here),
  597    prolog:quasi_quotation_syntax(Syntax, Library),
  598    !,
  599    use_module(Module:Library, [Syntax/4]),
  600    read_term(Stream, Term, Options).
  601load_qq_and_retry(_Pos, Syntax, Module, Context, _Stream, _Term, _Options) :-
  602    print_message(warning, quasi_quotation(undeclared, Syntax)),
  603    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.

  614prolog:quasi_quotation_syntax(html,       library(http/html_write)).
  615prolog:quasi_quotation_syntax(javascript, library(http/js_write)).
 prolog_file_directives(+File, -Directives, +Options) is det
True when Directives is a list of directives that appear in the source file File. Reading directives stops at the first non-directive term. Processing deals with expand_term/2 as well as conditional compilation. Options processed:
canonical_source(-Source)
Unify Source with the canonical source identifier as also used by library(prolog_xref).
silent(+Boolean)
If true (default false), do not report syntax errors and other errors.
  632prolog_file_directives(File, Directives, Options) :-
  633    option(canonical_source(Path), Options, _),
  634    prolog_canonical_source(File, Path),
  635    in_temporary_module(
  636        TempModule,
  637        true,
  638        read_directives(TempModule, Path, Directives, Options)).
  639
  640read_directives(TempModule, Path, Directives, Options) :-
  641    setup_call_cleanup(
  642        read_directives_setup(TempModule, Path, In, State),
  643        phrase(read_directives(In, Options, [true]), Directives),
  644        read_directives_cleanup(In, State)).
  645
  646read_directives_setup(TempModule, Path, In, state(OldM, OldXref)) :-
  647    prolog_open_source(Path, In),
  648    '$set_source_module'(OldM, TempModule),
  649    current_prolog_flag(xref, OldXref),
  650    set_prolog_flag(xref, true).
  651
  652read_directives_cleanup(In, state(OldM, OldXref)) :-
  653    '$set_source_module'(OldM),
  654    set_prolog_flag(xref, OldXref),
  655    prolog_close_source(In).
  656
  657read_directives(In, Options, State) -->
  658    {  E = error(_,_),
  659       repeat,
  660       catch(prolog_read_source_term(In, Term, Expanded,
  661                                     [ process_comment(true),
  662                                       syntax_errors(error)
  663                                     ]),
  664             E, report_syntax_error(E, Options))
  665    -> nonvar(Term),
  666       Term = (:-_)
  667    },
  668    !,
  669    terms(Expanded, State, State1),
  670    read_directives(In, Options, State1).
  671read_directives(_, _, _) --> [].
  672
  673report_syntax_error(_, Options) :-
  674    option(silent(true), Options),
  675    !,
  676    fail.
  677report_syntax_error(E, _Options) :-
  678    print_message(warning, E),
  679    fail.
  680
  681terms(Var, State, State) --> { var(Var) }, !.
  682terms([H|T], State0, State) -->
  683    !,
  684    terms(H, State0, State1),
  685    terms(T, State1, State).
  686terms((:-if(Cond)), State0, [True|State0]) -->
  687    !,
  688    { eval_cond(Cond, True) }.
  689terms((:-elif(Cond)), [True0|State], [True|State]) -->
  690    !,
  691    { eval_cond(Cond, True1),
  692      elif(True0, True1, True)
  693    }.
  694terms((:-else), [True0|State], [True|State]) -->
  695    !,
  696    { negate(True0, True) }.
  697terms((:-endif), [_|State], State) -->  !.
  698terms(H, State, State) -->
  699    (   {State = [true|_]}
  700    ->  [H]
  701    ;   []
  702    ).
  703
  704eval_cond(Cond, true) :-
  705    catch(Cond, error(_,_), fail),
  706    !.
  707eval_cond(_, false).
  708
  709elif(true,  _,    else_false) :- !.
  710elif(false, true, true) :- !.
  711elif(True,  _,    True).
  712
  713negate(true,       false).
  714negate(false,      true).
  715negate(else_false, else_false).
  716
  717                 /*******************************
  718                 *           SOURCES            *
  719                 *******************************/
 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)).
  736prolog_open_source(Src, Fd) :-
  737    '$push_input_context'(source),
  738    catch((   prolog:xref_open_source(Src, Fd)
  739          ->  Hooked = true
  740          ;   open(Src, read, Fd),
  741              Hooked = false
  742          ), E,
  743          (   '$pop_input_context',
  744              throw(E)
  745          )),
  746    skip_hashbang(Fd),
  747    push_operators([]),
  748    '$current_source_module'(SM),
  749    '$save_lex_state'(LexState, []),
  750    asserta(open_source(Fd, state(Hooked, Src, LexState, SM))).
  751
  752skip_hashbang(Fd) :-
  753    catch((   peek_char(Fd, #)              % Deal with #! script
  754          ->  skip(Fd, 10)
  755          ;   true
  756          ), E,
  757          (   close(Fd, [force(true)]),
  758              '$pop_input_context',
  759              throw(E)
  760          )).
 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.
  778prolog_close_source(In) :-
  779    call_cleanup(
  780        restore_source_context(In, Hooked, Src),
  781        close_source(Hooked, Src, In)).
  782
  783close_source(true, Src, In) :-
  784    catch(prolog:xref_close_source(Src, In), _, false),
  785    !,
  786    '$pop_input_context'.
  787close_source(_, _Src, In) :-
  788    close(In, [force(true)]),
  789    '$pop_input_context'.
  790
  791restore_source_context(In, Hooked, Src) :-
  792    (   at_end_of_stream(In)
  793    ->  true
  794    ;   ignore(catch(expand(end_of_file, _, In, _), _, true))
  795    ),
  796    pop_operators,
  797    retractall(mode(In, _)),
  798    (   retract(open_source(In, state(Hooked, Src, LexState, SM)))
  799    ->  '$restore_lex_state'(LexState),
  800        '$set_source_module'(SM)
  801    ;   assertion(fail)
  802    ).
 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.
  817prolog_canonical_source(Source, Src) :-
  818    var(Source),
  819    !,
  820    Src = Source.
  821prolog_canonical_source(User, user) :-
  822    User == user,
  823    !.
  824prolog_canonical_source(Src, Id) :-             % Call hook
  825    prolog:xref_source_identifier(Src, Id),
  826    !.
  827prolog_canonical_source(Source, Src) :-
  828    source_file(Source),
  829    !,
  830    Src = Source.
  831prolog_canonical_source(Source, Src) :-
  832    absolute_file_name(Source, Src,
  833                       [ file_type(prolog),
  834                         access(read),
  835                         file_errors(fail)
  836                       ]),
  837    !.
 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.
  845file_name_on_path(Path, ShortId) :-
  846    (   file_alias_path(Alias, Dir),
  847        atom_concat(Dir, Local, Path)
  848    ->  (   Alias == '.'
  849        ->  ShortId = Local
  850        ;   file_name_extension(Base, pl, Local)
  851        ->  ShortId =.. [Alias, Base]
  852        ;   ShortId =.. [Alias, Local]
  853        )
  854    ;   ShortId = Path
  855    ).
 file_alias_path(-Alias, ?Dir) is nondet
True if file Alias points to Dir. Multiple solutions are generated with the longest directory first.
  863:- dynamic
  864    alias_cache/2.  865
  866file_alias_path(Alias, Dir) :-
  867    (   alias_cache(_, _)
  868    ->  true
  869    ;   build_alias_cache
  870    ),
  871    (   nonvar(Dir)
  872    ->  ensure_slash(Dir, DirSlash),
  873        alias_cache(Alias, DirSlash)
  874    ;   alias_cache(Alias, Dir)
  875    ).
  876
  877build_alias_cache :-
  878    findall(t(DirLen, AliasLen, Alias, Dir),
  879            search_path(Alias, Dir, AliasLen, DirLen), Ts),
  880    sort(0, >, Ts, List),
  881    forall(member(t(_, _, Alias, Dir), List),
  882           assert(alias_cache(Alias, Dir))).
  883
  884search_path('.', Here, 999, DirLen) :-
  885    working_directory(Here0, Here0),
  886    ensure_slash(Here0, Here),
  887    atom_length(Here, DirLen).
  888search_path(Alias, Dir, AliasLen, DirLen) :-
  889    user:file_search_path(Alias, _),
  890    Alias \== autoload,             % TBD: Multifile predicate?
  891    Alias \== noautoload,
  892    Spec =.. [Alias,'.'],
  893    atom_length(Alias, AliasLen0),
  894    AliasLen is 1000 - AliasLen0,   % must do reverse sort
  895    absolute_file_name(Spec, Dir0,
  896                       [ file_type(directory),
  897                         access(read),
  898                         solutions(all),
  899                         file_errors(fail)
  900                       ]),
  901    ensure_slash(Dir0, Dir),
  902    atom_length(Dir, DirLen).
  903
  904ensure_slash(Dir, Dir) :-
  905    sub_atom(Dir, _, _, 0, /),
  906    !.
  907ensure_slash(Dir0, Dir) :-
  908    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.

  929path_segments_atom(Segments, Atom) :-
  930    var(Atom),
  931    !,
  932    (   atomic(Segments)
  933    ->  Atom = Segments
  934    ;   segments_to_list(Segments, List, [])
  935    ->  atomic_list_concat(List, /, Atom)
  936    ;   throw(error(type_error(file_path, Segments), _))
  937    ).
  938path_segments_atom(Segments, Atom) :-
  939    atomic_list_concat(List, /, Atom),
  940    parts_to_path(List, Segments).
  941
  942segments_to_list(Var, _, _) :-
  943    var(Var), !, fail.
  944segments_to_list(A/B, H, T) :-
  945    segments_to_list(A, H, T0),
  946    segments_to_list(B, T0, T).
  947segments_to_list(A, [A|T], T) :-
  948    atomic(A).
  949
  950parts_to_path([One], One) :- !.
  951parts_to_path(List, More/T) :-
  952    (   append(H, [T], List)
  953    ->  parts_to_path(H, More)
  954    ).
 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.

  969directory_source_files(Dir, SrcFiles, Options) :-
  970    option(if(loaded), Options, loaded),
  971    !,
  972    absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
  973    (   option(recursive(true), Options)
  974    ->  ensure_slash(AbsDir, Prefix),
  975        findall(F, (  source_file(F),
  976                      sub_atom(F, 0, _, _, Prefix)
  977                   ),
  978                SrcFiles)
  979    ;   findall(F, ( source_file(F),
  980                     file_directory_name(F, AbsDir)
  981                   ),
  982                SrcFiles)
  983    ).
  984directory_source_files(Dir, SrcFiles, Options) :-
  985    absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
  986    directory_files(AbsDir, Files),
  987    phrase(src_files(Files, AbsDir, Options), SrcFiles).
  988
  989src_files([], _, _) -->
  990    [].
  991src_files([H|T], Dir, Options) -->
  992    { file_name_extension(_, Ext, H),
  993      user:prolog_file_type(Ext, prolog),
  994      \+ user:prolog_file_type(Ext, qlf),
  995      dir_file_path(Dir, H, File0),
  996      absolute_file_name(File0, File,
  997                         [ file_errors(fail)
  998                         | Options
  999                         ])
 1000    },
 1001    !,
 1002    [File],
 1003    src_files(T, Dir, Options).
 1004src_files([H|T], Dir, Options) -->
 1005    { \+ special(H),
 1006      option(recursive(true), Options),
 1007      dir_file_path(Dir, H, SubDir),
 1008      exists_directory(SubDir),
 1009      !,
 1010      catch(directory_files(SubDir, Files), _, fail)
 1011    },
 1012    !,
 1013    src_files(Files, SubDir, Options),
 1014    src_files(T, Dir, Options).
 1015src_files([_|T], Dir, Options) -->
 1016    src_files(T, Dir, Options).
 1017
 1018special(.).
 1019special(..).
 1020
 1021% avoid dependency on library(filesex), which also pulls a foreign
 1022% dependency.
 1023dir_file_path(Dir, File, Path) :-
 1024    (   sub_atom(Dir, _, _, 0, /)
 1025    ->  atom_concat(Dir, File, Path)
 1026    ;   atom_concat(Dir, /, TheDir),
 1027        atom_concat(TheDir, File, Path)
 1028    ).
 valid_term_position(@Term, @TermPos) is semidet
Check that a Term has an appropriate TermPos layout. An incorrect TermPos results in either failure of this predicate or an error.

If a position in TermPos is a variable, the validation of the corresponding part of Term succeeds. This matches the term_expansion/4 treats "unknown" layout information. If part of a TermPos is given, then all its "from" and "to" information must be specified; for example, string_position(X,Y) is an error but string_position(0,5) succeeds. The position values are checked for being plausible -- e.g., string_position(5,0) will fail.

This should always succeed:

read_term(Term, [subterm_positions(TermPos)]),
valid_term_position(Term, TermPos)
Arguments:
Term- Any Prolog term including a variable).
TermPos- The detailed layout of the term, for example from using read_term(Term, subterm_positions(TermPos).
Errors
- existence_error(matching_rule, Subterm) if a subterm of Term is inconsistent with the corresponding part of TermPos.
See also
- read_term/2, read_term/3, term_string/3
- expand_term/4, term_expansion/4, expand_goal/4, expand_term/4
- clause_info/4, clause_info/5
- unify_clause_hook/5
 1061valid_term_position(Term, TermPos) :-
 1062    valid_term_position(0, 0x7fffffffffffffff, Term, TermPos).
 1063
 1064valid_term_position(OuterFrom, OuterTo, _Term, TermPos),
 1065        var(TermPos),
 1066        OuterFrom =< OuterTo => true.
 1067valid_term_position(OuterFrom, OuterTo, Var, From-To),
 1068        var(Var),
 1069        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1070valid_term_position(OuterFrom, OuterTo, Atom, From-To),
 1071        atom(Atom),
 1072        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1073valid_term_position(OuterFrom, OuterTo, Number, From-To),
 1074        number(Number),
 1075        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1076valid_term_position(OuterFrom, OuterTo, [], From-To),
 1077        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1078valid_term_position(OuterFrom, OuterTo, String, string_position(From,To)),
 1079        (   string(String)
 1080        ->  true
 1081        ;   is_of_type(codes, String)
 1082        ->  true
 1083        ;   is_of_type(chars, String)
 1084        ->  true
 1085        ;   atom(String)
 1086        ),
 1087        valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true.
 1088valid_term_position(OuterFrom, OuterTo, {Arg},
 1089                    brace_term_position(From,To,ArgPos)),
 1090        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1091    valid_term_position(From, To, Arg, ArgPos).
 1092valid_term_position(OuterFrom, OuterTo, [Hd|Tl],
 1093                    list_position(From,To,ElemsPos,none)),
 1094        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1095    term_position_list_tail([Hd|Tl], _HdPart, []),
 1096    maplist(valid_term_position, [Hd|Tl], ElemsPos).
 1097valid_term_position(OuterFrom, OuterTo, [Hd|Tl],
 1098                    list_position(From, To, ElemsPos, TailPos)),
 1099        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1100    term_position_list_tail([Hd|Tl], HdPart, Tail),
 1101    maplist(valid_term_position(From,To), HdPart, ElemsPos),
 1102    valid_term_position(Tail, TailPos).
 1103valid_term_position(OuterFrom, OuterTo, Term,
 1104                    term_position(From,To, FFrom,FTo,SubPos)),
 1105        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1106    compound_name_arguments(Term, Name, Arguments),
 1107    valid_term_position(Name, FFrom-FTo),
 1108    maplist(valid_term_position(From,To), Arguments, SubPos).
 1109valid_term_position(OuterFrom, OuterTo, Dict,
 1110                    dict_position(From,To,TagFrom,TagTo,KeyValuePosList)),
 1111        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1112    dict_pairs(Dict, Tag, Pairs),
 1113    valid_term_position(Tag, TagFrom-TagTo),
 1114    foldl(valid_term_position_dict(From,To), Pairs, KeyValuePosList, []).
 1115% key_value_position(From, To, SepFrom, SepTo, Key, KeyPos, ValuePos)
 1116% is handled in valid_term_position_dict.
 1117valid_term_position(OuterFrom, OuterTo, Term,
 1118                    parentheses_term_position(From,To,ContentPos)),
 1119        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1120    valid_term_position(From, To, Term, ContentPos).
 1121valid_term_position(OuterFrom, OuterTo, _Term,
 1122                    quasi_quotation_position(From,To,
 1123                                             SyntaxTerm,SyntaxPos,_ContentPos)),
 1124        valid_term_position_from_to(OuterFrom, OuterTo, From, To) =>
 1125    valid_term_position(From, To, SyntaxTerm, SyntaxPos).
 1126
 1127valid_term_position_from_to(OuterFrom, OuterTo, From, To) :-
 1128    integer(OuterFrom),
 1129    integer(OuterTo),
 1130    integer(From),
 1131    integer(To),
 1132    OuterFrom =< OuterTo,
 1133    From =< To,
 1134    OuterFrom =< From,
 1135    To =< OuterTo.
 1136
 1137:- det(valid_term_position_dict/5). 1138valid_term_position_dict(OuterFrom, OuterTo, Key-Value,
 1139                         KeyValuePosList0, KeyValuePosList1) :-
 1140    selectchk(key_value_position(From,To,SepFrom,SepTo,Key,KeyPos,ValuePos),
 1141              KeyValuePosList0, KeyValuePosList1),
 1142    valid_term_position_from_to(OuterFrom, OuterTo, From, To),
 1143    valid_term_position_from_to(OuterFrom, OuterTo, SepFrom, SepTo),
 1144    SepFrom >= OuterFrom,
 1145    valid_term_position(From, SepFrom, Key, KeyPos),
 1146    valid_term_position(SepTo, To, Value, ValuePos).
 term_position_list_tail(@List, -HdPart, -Tail) is det
Similar to append(HdPart, [Tail], List) for proper lists, but also works for inproper lists, in which case it unifies Tail with the tail of the partial list. HdPart is always a proper list:
?- prolog_source:term_position_list_tail([a,b,c], Hd, Tl).
Hd = [a, b, c],
Tl = [].
?- prolog_source:term_position_list_tail([a,b|X], Hd, Tl).
X = Tl,
Hd = [a, b].
 1163:- det(term_position_list_tail/3). 1164term_position_list_tail([X|Xs], HdPart, Tail) =>
 1165    HdPart = [X|HdPart2],
 1166    term_position_list_tail(Xs, HdPart2, Tail).
 1167term_position_list_tail(Tail0, HdPart, Tail) =>
 1168    HdPart = [],
 1169    Tail0 = Tail.
 1170
 1171
 1172                 /*******************************
 1173                 *           MESSAGES           *
 1174                 *******************************/
 1175
 1176:- multifile
 1177    prolog:message//1. 1178
 1179prolog:message(quasi_quotation(undeclared, Syntax)) -->
 1180    [ 'Undeclared quasi quotation syntax: ~w'-[Syntax], nl,
 1181      'Autoloading can be defined using prolog:quasi_quotation_syntax/2'
 1182    ]