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-2022, 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_open_source/2, % +Source, -Stream 41 prolog_close_source/1, % +Stream 42 prolog_canonical_source/2, % +Spec, -Id 43 44 load_quasi_quotation_syntax/2, % :Path, +Syntax 45 46 file_name_on_path/2, % +File, -PathSpec 47 file_alias_path/2, % ?Alias, ?Dir 48 path_segments_atom/2, % ?Segments, ?Atom 49 directory_source_files/3, % +Dir, -Files, +Options 50 valid_term_position/2 % +Term, +TermPos 51 ]). 52:- use_module(library(debug), [debug/3, assertion/1]). 53:- autoload(library(apply), [maplist/2, maplist/3, foldl/4]). 54:- autoload(library(error), [domain_error/2, is_of_type/2]). 55:- autoload(library(lists), [member/2, last/2, select/3, append/3, selectchk/3]). 56:- autoload(library(operators), [push_op/3, push_operators/1, pop_operators/0]). 57:- autoload(library(option), [select_option/4, option/3, option/2]).
83:- thread_local 84 open_source/2, % Stream, State 85 mode/2. % Stream, Data 86 87:- multifile 88 requires_library/2, 89 prolog:xref_source_identifier/2, % +Source, -Id 90 prolog:xref_source_time/2, % +Source, -Modified 91 prolog:xref_open_source/2, % +SourceId, -Stream 92 prolog:xref_close_source/2, % +SourceId, -Stream 93 prolog:alternate_syntax/4, % Syntax, +Module, -Setup, -Restore 94 prolog:xref_update_syntax/2, % +Directive, +Module 95 prolog:quasi_quotation_syntax/2. % Syntax, Library 96 97 98:- predicate_options(prolog_read_source_term/4, 4, 99 [ pass_to(system:read_clause/3, 3) 100 ]). 101:- predicate_options(read_source_term_at_location/3, 3, 102 [ line(integer), 103 offset(integer), 104 module(atom), 105 operators(list), 106 error(-any), 107 pass_to(system:read_term/3, 3) 108 ]). 109:- predicate_options(directory_source_files/3, 3, 110 [ recursive(boolean), 111 if(oneof([true,loaded])), 112 pass_to(system:absolute_file_name/3,3) 113 ]). 114 115 116 /******************************* 117 * READING * 118 *******************************/
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).
134prolog_read_source_term(In, Term, Expanded, Options) :- 135 maplist(read_clause_option, Options), 136 !, 137 select_option(subterm_positions(TermPos), Options, 138 RestOptions, TermPos), 139 read_clause(In, Term, 140 [ subterm_positions(TermPos) 141 | RestOptions 142 ]), 143 expand(Term, TermPos, In, Expanded), 144 '$current_source_module'(M), 145 update_state(Term, Expanded, M). 146prolog_read_source_term(In, Term, Expanded, Options) :- 147 '$current_source_module'(M), 148 select_option(syntax_errors(SE), Options, RestOptions0, dec10), 149 select_option(subterm_positions(TermPos), RestOptions0, 150 RestOptions, TermPos), 151 ( style_check(?(singleton)) 152 -> FinalOptions = [ singletons(warning) | RestOptions ] 153 ; FinalOptions = RestOptions 154 ), 155 read_term(In, Term, 156 [ module(M), 157 syntax_errors(SE), 158 subterm_positions(TermPos) 159 | FinalOptions 160 ]), 161 expand(Term, TermPos, In, Expanded), 162 update_state(Term, Expanded, M). 163 164read_clause_option(syntax_errors(_)). 165read_clause_option(term_position(_)). 166read_clause_option(process_comment(_)). 167read_clause_option(comments(_)). 168 169:- public 170 expand/3. % Used by Prolog colour 171 172expand(Term, In, Exp) :- 173 expand(Term, _, In, Exp). 174 175expand(Var, _, _, Var) :- 176 var(Var), 177 !. 178expand(Term, _, _, Term) :- 179 no_expand(Term), 180 !. 181expand(Term, _, _, _) :- 182 requires_library(Term, Lib), 183 ensure_loaded(user:Lib), 184 fail. 185expand(Term, _, In, Term) :- 186 chr_expandable(Term, In), 187 !. 188expand(Term, Pos, _, Expanded) :- 189 expand_term(Term, Pos, Expanded, _). 190 191no_expand((:- if(_))). 192no_expand((:- elif(_))). 193no_expand((:- else)). 194no_expand((:- endif)). 195no_expand((:- require(_))). 196 197chr_expandable((:- chr_constraint(_)), In) :- 198 add_mode(In, chr). 199chr_expandable((handler(_)), In) :- 200 mode(In, chr). 201chr_expandable((rules(_)), In) :- 202 mode(In, chr). 203chr_expandable(<=>(_, _), In) :- 204 mode(In, chr). 205chr_expandable(@(_, _), In) :- 206 mode(In, chr). 207chr_expandable(==>(_, _), In) :- 208 mode(In, chr). 209chr_expandable(pragma(_, _), In) :- 210 mode(In, chr). 211chr_expandable(option(_, _), In) :- 212 mode(In, chr). 213 214add_mode(Stream, Mode) :- 215 mode(Stream, Mode), 216 !. 217add_mode(Stream, Mode) :- 218 asserta(mode(Stream, Mode)).
224requires_library((:- emacs_begin_mode(_,_,_,_,_)), library(emacs_extend)). 225requires_library((:- draw_begin_shape(_,_,_,_)), library(pcedraw)). 226requires_library((:- use_module(library(pce))), library(pce)). 227requires_library((:- pce_begin_class(_,_)), library(pce)). 228requires_library((:- pce_begin_class(_,_,_)), library(pce)).
234:- multifile 235 pce_expansion:push_compile_operators/1, 236 pce_expansion:pop_compile_operators/0. 237 238update_state(Raw, _, _) :- 239 Raw == (:- pce_end_class), 240 !, 241 ignore(pce_expansion:pop_compile_operators). 242update_state(Raw, _, SM) :- 243 subsumes_term((:- pce_extend_class(_)), Raw), 244 !, 245 pce_expansion:push_compile_operators(SM). 246update_state(_Raw, Expanded, M) :- 247 update_state(Expanded, M). 248 249update_state(Var, _) :- 250 var(Var), 251 !. 252update_state([], _) :- 253 !. 254update_state([H|T], M) :- 255 !, 256 update_state(H, M), 257 update_state(T, M). 258update_state((:- Directive), M) :- 259 nonvar(Directive), 260 !, 261 catch(update_directive(Directive, M), _, true). 262update_state((?- Directive), M) :- 263 !, 264 update_state((:- Directive), M). 265update_state(_, _). 266 267update_directive(Directive, Module) :- 268 prolog:xref_update_syntax(Directive, Module), 269 !. 270update_directive(module(Module, Public), _) :- 271 atom(Module), 272 is_list(Public), 273 !, 274 '$set_source_module'(Module), 275 maplist(import_syntax(_,Module, _), Public). 276update_directive(M:op(P,T,N), SM) :- 277 atom(M), 278 ground(op(P,T,N)), 279 !, 280 update_directive(op(P,T,N), SM). 281update_directive(op(P,T,N), SM) :- 282 ground(op(P,T,N)), 283 !, 284 strip_module(SM:N, M, PN), 285 push_op(P,T,M:PN). 286update_directive(style_check(Style), _) :- 287 ground(Style), 288 style_check(Style), 289 !. 290update_directive(use_module(Spec), SM) :- 291 ground(Spec), 292 catch(module_decl(Spec, Path, Public), _, fail), 293 is_list(Public), 294 !, 295 maplist(import_syntax(Path, SM, _), Public). 296update_directive(use_module(Spec, Imports), SM) :- 297 ground(Spec), 298 is_list(Imports), 299 catch(module_decl(Spec, Path, Public), _, fail), 300 is_list(Public), 301 !, 302 maplist(import_syntax(Path, SM, Imports), Public). 303update_directive(pce_begin_class_definition(_,_,_,_), SM) :- 304 pce_expansion:push_compile_operators(SM), 305 !. 306update_directive(_, _).
313import_syntax(_, _, _, Var) :- 314 var(Var), 315 !. 316import_syntax(_, M, Imports, Op) :- 317 Op = op(_,_,_), 318 \+ \+ member(Op, Imports), 319 !, 320 update_directive(Op, M). 321import_syntax(Path, SM, Imports, Syntax/4) :- 322 \+ \+ member(Syntax/4, Imports), 323 load_quasi_quotation_syntax(SM:Path, Syntax), 324 !. 325import_syntax(_,_,_, _).
342load_quasi_quotation_syntax(SM:Path, Syntax) :- 343 atom(Path), atom(Syntax), 344 source_file_property(Path, module(M)), 345 functor(ST, Syntax, 4), 346 predicate_property(M:ST, quasi_quotation_syntax), 347 !, 348 use_module(SM:Path, [Syntax/4]). 349load_quasi_quotation_syntax(SM:Path, Syntax) :- 350 atom(Path), atom(Syntax), 351 prolog:quasi_quotation_syntax(Syntax, Spec), 352 absolute_file_name(Spec, Path2, 353 [ file_type(prolog), 354 file_errors(fail), 355 access(read) 356 ]), 357 Path == Path2, 358 !, 359 use_module(SM:Path, [Syntax/4]).
367module_decl(Spec, Path, Decl) :- 368 absolute_file_name(Spec, Path, 369 [ file_type(prolog), 370 file_errors(fail), 371 access(read) 372 ]), 373 setup_call_cleanup( 374 prolog_open_source(Path, In), 375 read_module_decl(In, Decl), 376 prolog_close_source(In)). 377 378read_module_decl(In, Decl) :- 379 read(In, Term0), 380 read_module_decl(Term0, In, Decl). 381 382read_module_decl((:- module(_, DeclIn)), _In, Decl) => 383 Decl = DeclIn. 384read_module_decl((:- encoding(Enc)), In, Decl) => 385 set_stream(In, encoding(Enc)), 386 read(In, Term2), 387 read_module_decl(Term2, In, Decl). 388read_module_decl(_, _, _) => 389 fail.
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.
det
).433:- thread_local 434 last_syntax_error/2. % location, message 435 436read_source_term_at_location(Stream, Term, Options) :- 437 retractall(last_syntax_error(_,_)), 438 seek_to_start(Stream, Options), 439 stream_property(Stream, position(Here)), 440 '$current_source_module'(DefModule), 441 option(module(Module), Options, DefModule), 442 option(operators(Ops), Options, []), 443 alternate_syntax(Syntax, Module, Setup, Restore), 444 set_stream_position(Stream, Here), 445 debug(read, 'Trying with syntax ~w', [Syntax]), 446 push_operators(Module:Ops), 447 call(Setup), 448 Error = error(Formal,_), % do not catch timeout, etc. 449 setup_call_cleanup( 450 asserta(user:thread_message_hook(_,_,_), Ref), % silence messages 451 catch(qq_read_term(Stream, Term0, 452 [ module(Module) 453 | Options 454 ]), 455 Error, 456 true), 457 erase(Ref)), 458 call(Restore), 459 pop_operators, 460 ( var(Formal) 461 -> !, Term = Term0 462 ; assert_error(Error, Options), 463 fail 464 ). 465read_source_term_at_location(_, _, Options) :- 466 option(error(Error), Options), 467 !, 468 setof(CharNo:Msg, retract(last_syntax_error(CharNo, Msg)), Pairs), 469 last(Pairs, Error). 470 471assert_error(Error, Options) :- 472 option(error(_), Options), 473 !, 474 ( ( Error = error(syntax_error(Id), 475 stream(_S1, _Line1, _LinePos1, CharNo)) 476 ; Error = error(syntax_error(Id), 477 file(_S2, _Line2, _LinePos2, CharNo)) 478 ) 479 -> message_to_string(error(syntax_error(Id), _), Msg), 480 assertz(last_syntax_error(CharNo, Msg)) 481 ; debug(read, 'Error: ~q', [Error]), 482 throw(Error) 483 ). 484assert_error(_, _).
Calls the hook prolog:alternate_syntax/4 with the same signature to allow for user-defined extensions.
500alternate_syntax(prolog, _, true, true). 501alternate_syntax(Syntax, M, Setup, Restore) :- 502 prolog:alternate_syntax(Syntax, M, Setup, Restore).
509seek_to_start(Stream, Options) :- 510 option(line(Line), Options), 511 !, 512 seek(Stream, 0, bof, _), 513 seek_to_line(Stream, Line). 514seek_to_start(Stream, Options) :- 515 option(offset(Start), Options), 516 !, 517 seek(Stream, Start, bof, _). 518seek_to_start(_, _).
524seek_to_line(Fd, N) :- 525 N > 1, 526 !, 527 skip(Fd, 10), 528 NN is N - 1, 529 seek_to_line(Fd, NN). 530seek_to_line(_, _). 531 532 533 /******************************* 534 * QUASI QUOTATIONS * 535 *******************************/
543qq_read_term(Stream, Term, Options) :- 544 select(syntax_errors(ErrorMode), Options, Options1), 545 ErrorMode \== error, 546 !, 547 ( ErrorMode == dec10 548 -> repeat, 549 qq_read_syntax_ex(Stream, Term, Options1, Error), 550 ( var(Error) 551 -> ! 552 ; print_message(error, Error), 553 fail 554 ) 555 ; qq_read_syntax_ex(Stream, Term, Options1, Error), 556 ( ErrorMode == fail 557 -> print_message(error, Error), 558 fail 559 ; ErrorMode == quiet 560 -> fail 561 ; domain_error(syntax_errors, ErrorMode) 562 ) 563 ). 564qq_read_term(Stream, Term, Options) :- 565 qq_read_term_ex(Stream, Term, Options). 566 567qq_read_syntax_ex(Stream, Term, Options, Error) :- 568 catch(qq_read_term_ex(Stream, Term, Options), 569 error(syntax_error(Syntax), Context), 570 Error = error(Syntax, Context)). 571 572qq_read_term_ex(Stream, Term, Options) :- 573 stream_property(Stream, position(Here)), 574 catch(read_term(Stream, Term, Options), 575 error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context), 576 load_qq_and_retry(Here, Syntax, Module, Context, Stream, Term, Options)). 577 578load_qq_and_retry(Here, Syntax, Module, _, Stream, Term, Options) :- 579 set_stream_position(Stream, Here), 580 prolog:quasi_quotation_syntax(Syntax, Library), 581 !, 582 use_module(Module:Library, [Syntax/4]), 583 read_term(Stream, Term, Options). 584load_qq_and_retry(_Pos, Syntax, Module, Context, _Stream, _Term, _Options) :- 585 print_message(warning, quasi_quotation(undeclared, Syntax)), 586 throw(error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context)).
This multifile hook is used by library(prolog_source) to load quasi quotation handlers on demand.
597prologquasi_quotation_syntax(html, library(http/html_write)). 598prologquasi_quotation_syntax(javascript, library(http/js_write)). 599 600 601 /******************************* 602 * SOURCES * 603 *******************************/
process_source(Src) :- prolog_open_source(Src, In), call_cleanup(process(Src), prolog_close_source(In)).
620prolog_open_source(Src, Fd) :- 621 '$push_input_context'(source), 622 catch(( prolog:xref_open_source(Src, Fd) 623 -> Hooked = true 624 ; open(Src, read, Fd), 625 Hooked = false 626 ), E, 627 ( '$pop_input_context', 628 throw(E) 629 )), 630 skip_hashbang(Fd), 631 push_operators([]), 632 '$current_source_module'(SM), 633 '$save_lex_state'(LexState, []), 634 asserta(open_source(Fd, state(Hooked, Src, LexState, SM))). 635 636skip_hashbang(Fd) :- 637 catch(( peek_char(Fd, #) % Deal with #! script 638 -> skip(Fd, 10) 639 ; true 640 ), E, 641 ( close(Fd, [force(true)]), 642 '$pop_input_context', 643 throw(E) 644 )).
expand_term(end_of_file, _)
to allow expansion
modules to clean-up.662prolog_close_source(In) :- 663 call_cleanup( 664 restore_source_context(In, Hooked, Src), 665 close_source(Hooked, Src, In)). 666 667close_source(true, Src, In) :- 668 catch(prolog:xref_close_source(Src, In), _, false), 669 !, 670 '$pop_input_context'. 671close_source(_, _Src, In) :- 672 close(In, [force(true)]), 673 '$pop_input_context'. 674 675restore_source_context(In, Hooked, Src) :- 676 ( at_end_of_stream(In) 677 -> true 678 ; ignore(catch(expand(end_of_file, _, In, _), _, true)) 679 ), 680 pop_operators, 681 retractall(mode(In, _)), 682 ( retract(open_source(In, state(Hooked, Src, LexState, SM))) 683 -> '$restore_lex_state'(LexState), 684 '$set_source_module'(SM) 685 ; assertion(fail) 686 ).
force(true)
is used.701prolog_canonical_source(Source, Src) :- 702 var(Source), 703 !, 704 Src = Source. 705prolog_canonical_source(User, user) :- 706 User == user, 707 !. 708prolog_canonical_source(Src, Id) :- % Call hook 709 prolog:xref_source_identifier(Src, Id), 710 !. 711prolog_canonical_source(Source, Src) :- 712 source_file(Source), 713 !, 714 Src = Source. 715prolog_canonical_source(Source, Src) :- 716 absolute_file_name(Source, Src, 717 [ file_type(prolog), 718 access(read), 719 file_errors(fail) 720 ]), 721 !.
729file_name_on_path(Path, ShortId) :-
730 ( file_alias_path(Alias, Dir),
731 atom_concat(Dir, Local, Path)
732 -> ( Alias == '.'
733 -> ShortId = Local
734 ; file_name_extension(Base, pl, Local)
735 -> ShortId =.. [Alias, Base]
736 ; ShortId =.. [Alias, Local]
737 )
738 ; ShortId = Path
739 ).
747:- dynamic 748 alias_cache/2. 749 750file_alias_path(Alias, Dir) :- 751 ( alias_cache(_, _) 752 -> true 753 ; build_alias_cache 754 ), 755 ( nonvar(Dir) 756 -> ensure_slash(Dir, DirSlash), 757 alias_cache(Alias, DirSlash) 758 ; alias_cache(Alias, Dir) 759 ). 760 761build_alias_cache :- 762 findall(t(DirLen, AliasLen, Alias, Dir), 763 search_path(Alias, Dir, AliasLen, DirLen), Ts), 764 sort(0, >, Ts, List), 765 forall(member(t(_, _, Alias, Dir), List), 766 assert(alias_cache(Alias, Dir))). 767 768search_path('.', Here, 999, DirLen) :- 769 working_directory(Here0, Here0), 770 ensure_slash(Here0, Here), 771 atom_length(Here, DirLen). 772search_path(Alias, Dir, AliasLen, DirLen) :- 773 user:file_search_path(Alias, _), 774 Alias \== autoload, % TBD: Multifile predicate? 775 Alias \== noautoload, 776 Spec =.. [Alias,'.'], 777 atom_length(Alias, AliasLen0), 778 AliasLen is 1000 - AliasLen0, % must do reverse sort 779 absolute_file_name(Spec, Dir0, 780 [ file_type(directory), 781 access(read), 782 solutions(all), 783 file_errors(fail) 784 ]), 785 ensure_slash(Dir0, Dir), 786 atom_length(Dir, DirLen). 787 788ensure_slash(Dir, Dir) :- 789 sub_atom(Dir, _, _, 0, /), 790 !. 791ensure_slash(Dir0, Dir) :- 792 atom_concat(Dir0, /, Dir).
?- 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.
813path_segments_atom(Segments, Atom) :- 814 var(Atom), 815 !, 816 ( atomic(Segments) 817 -> Atom = Segments 818 ; segments_to_list(Segments, List, []) 819 -> atomic_list_concat(List, /, Atom) 820 ; throw(error(type_error(file_path, Segments), _)) 821 ). 822path_segments_atom(Segments, Atom) :- 823 atomic_list_concat(List, /, Atom), 824 parts_to_path(List, Segments). 825 826segments_to_list(Var, _, _) :- 827 var(Var), !, fail. 828segments_to_list(A/B, H, T) :- 829 segments_to_list(A, H, T0), 830 segments_to_list(B, T0, T). 831segments_to_list(A, [A|T], T) :- 832 atomic(A). 833 834parts_to_path([One], One) :- !. 835parts_to_path(List, More/T) :- 836 ( append(H, [T], List) 837 -> parts_to_path(H, More) 838 ).
true
(default false
), recurse into subdirectoriestrue
(default loaded
), only report loaded files.
Other options are passed to absolute_file_name/3, unless
loaded(true)
is passed.
853directory_source_files(Dir, SrcFiles, Options) :- 854 option(if(loaded), Options, loaded), 855 !, 856 absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]), 857 ( option(recursive(true), Options) 858 -> ensure_slash(AbsDir, Prefix), 859 findall(F, ( source_file(F), 860 sub_atom(F, 0, _, _, Prefix) 861 ), 862 SrcFiles) 863 ; findall(F, ( source_file(F), 864 file_directory_name(F, AbsDir) 865 ), 866 SrcFiles) 867 ). 868directory_source_files(Dir, SrcFiles, Options) :- 869 absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]), 870 directory_files(AbsDir, Files), 871 phrase(src_files(Files, AbsDir, Options), SrcFiles). 872 873src_files([], _, _) --> 874 []. 875src_files([H|T], Dir, Options) --> 876 { file_name_extension(_, Ext, H), 877 user:prolog_file_type(Ext, prolog), 878 \+ user:prolog_file_type(Ext, qlf), 879 dir_file_path(Dir, H, File0), 880 absolute_file_name(File0, File, 881 [ file_errors(fail) 882 | Options 883 ]) 884 }, 885 !, 886 [File], 887 src_files(T, Dir, Options). 888src_files([H|T], Dir, Options) --> 889 { \+ special(H), 890 option(recursive(true), Options), 891 dir_file_path(Dir, H, SubDir), 892 exists_directory(SubDir), 893 !, 894 catch(directory_files(SubDir, Files), _, fail) 895 }, 896 !, 897 src_files(Files, SubDir, Options), 898 src_files(T, Dir, Options). 899src_files([_|T], Dir, Options) --> 900 src_files(T, Dir, Options). 901 902special(.). 903special(..). 904 905% avoid dependency on library(filesex), which also pulls a foreign 906% dependency. 907dir_file_path(Dir, File, Path) :- 908 ( sub_atom(Dir, _, _, 0, /) 909 -> atom_concat(Dir, File, Path) 910 ; atom_concat(Dir, /, TheDir), 911 atom_concat(TheDir, File, Path) 912 ).
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)
945valid_term_position(Term, TermPos) :- 946 valid_term_position(0, 0x7fffffffffffffff, Term, TermPos). 947 948valid_term_position(OuterFrom, OuterTo, _Term, TermPos), 949 var(TermPos), 950 OuterFrom =< OuterTo => true. 951valid_term_position(OuterFrom, OuterTo, Var, From-To), 952 var(Var), 953 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true. 954valid_term_position(OuterFrom, OuterTo, Atom, From-To), 955 atom(Atom), 956 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true. 957valid_term_position(OuterFrom, OuterTo, Number, From-To), 958 number(Number), 959 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true. 960valid_term_position(OuterFrom, OuterTo, [], From-To), 961 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true. 962valid_term_position(OuterFrom, OuterTo, String, string_position(From,To)), 963 ( string(String) 964 -> true 965 ; is_of_type(codes, String) 966 -> true 967 ; is_of_type(chars, String) 968 -> true 969 ; atom(String) 970 ), 971 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => true. 972valid_term_position(OuterFrom, OuterTo, {Arg}, 973 brace_term_position(From,To,ArgPos)), 974 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 975 valid_term_position(From, To, Arg, ArgPos). 976valid_term_position(OuterFrom, OuterTo, [Hd|Tl], 977 list_position(From,To,ElemsPos,none)), 978 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 979 term_position_list_tail([Hd|Tl], _HdPart, []), 980 maplist(valid_term_position, [Hd|Tl], ElemsPos). 981valid_term_position(OuterFrom, OuterTo, [Hd|Tl], 982 list_position(From, To, ElemsPos, TailPos)), 983 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 984 term_position_list_tail([Hd|Tl], HdPart, Tail), 985 maplist(valid_term_position(From,To), HdPart, ElemsPos), 986 valid_term_position(Tail, TailPos). 987valid_term_position(OuterFrom, OuterTo, Term, 988 term_position(From,To, FFrom,FTo,SubPos)), 989 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 990 compound_name_arguments(Term, Name, Arguments), 991 valid_term_position(Name, FFrom-FTo), 992 maplist(valid_term_position(From,To), Arguments, SubPos). 993valid_term_position(OuterFrom, OuterTo, Dict, 994 dict_position(From,To,TagFrom,TagTo,KeyValuePosList)), 995 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 996 dict_pairs(Dict, Tag, Pairs), 997 valid_term_position(Tag, TagFrom-TagTo), 998 foldl(valid_term_position_dict(From,To), Pairs, KeyValuePosList, []). 999% key_value_position(From, To, SepFrom, SepTo, Key, KeyPos, ValuePos) 1000% is handled in valid_term_position_dict. 1001valid_term_position(OuterFrom, OuterTo, Term, 1002 parentheses_term_position(From,To,ContentPos)), 1003 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 1004 valid_term_position(From, To, Term, ContentPos). 1005valid_term_position(OuterFrom, OuterTo, _Term, 1006 quasi_quotation_position(From,To, 1007 SyntaxTerm,SyntaxPos,_ContentPos)), 1008 valid_term_position_from_to(OuterFrom, OuterTo, From, To) => 1009 valid_term_position(From, To, SyntaxTerm, SyntaxPos). 1010 1011valid_term_position_from_to(OuterFrom, OuterTo, From, To) :- 1012 integer(OuterFrom), 1013 integer(OuterTo), 1014 integer(From), 1015 integer(To), 1016 OuterFrom =< OuterTo, 1017 From =< To, 1018 OuterFrom =< From, 1019 To =< OuterTo. 1020 1021:- det(valid_term_position_dict/5). 1022valid_term_position_dict(OuterFrom, OuterTo, Key-Value, 1023 KeyValuePosList0, KeyValuePosList1) :- 1024 selectchk(key_value_position(From,To,SepFrom,SepTo,Key,KeyPos,ValuePos), 1025 KeyValuePosList0, KeyValuePosList1), 1026 valid_term_position_from_to(OuterFrom, OuterTo, From, To), 1027 valid_term_position_from_to(OuterFrom, OuterTo, SepFrom, SepTo), 1028 SepFrom >= OuterFrom, 1029 valid_term_position(From, SepFrom, Key, KeyPos), 1030 valid_term_position(SepTo, To, Value, ValuePos).
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].
1047:- det(term_position_list_tail/3). 1048term_position_list_tail([X|Xs], HdPart, Tail) => 1049 HdPart = [X|HdPart2], 1050 term_position_list_tail(Xs, HdPart2, Tail). 1051term_position_list_tail(Tail0, HdPart, Tail) => 1052 HdPart = [], 1053 Tail0 = Tail. 1054 1055 1056 /******************************* 1057 * MESSAGES * 1058 *******************************/ 1059 1060:- multifile 1061 prolog:message//1. 1062 1063prologmessage(quasi_quotation(undeclared, Syntax)) --> 1064 [ 'Undeclared quasi quotation syntax: ~w'-[Syntax], nl, 1065 'Autoloading can be defined using prolog:quasi_quotation_syntax/2' 1066 ]
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
prolog_clause.pl
prolog_colour.pl
*/