35
36:- module(prolog_source,
37 [ prolog_read_source_term/4, 38 read_source_term_at_location/3, 39 prolog_open_source/2, 40 prolog_close_source/1, 41 prolog_canonical_source/2, 42
43 load_quasi_quotation_syntax/2, 44
45 file_name_on_path/2, 46 file_alias_path/2, 47 path_segments_atom/2, 48 directory_source_files/3 49 ]). 50:- autoload(library(apply),[maplist/2]). 51:- autoload(library(debug),[debug/3,assertion/1]). 52:- autoload(library(error),[domain_error/2]). 53:- autoload(library(lists),[member/2,last/2,select/3,append/3]). 54:- autoload(library(operators),
55 [push_op/3,push_operators/1,pop_operators/0]). 56:- autoload(library(option),[select_option/4,option/3,option/2]). 57
58
81
82:- thread_local
83 open_source/2, 84 mode/2. 85
86:- multifile
87 requires_library/2,
88 prolog:xref_source_identifier/2, 89 prolog:xref_source_time/2, 90 prolog:xref_open_source/2, 91 prolog:xref_close_source/2, 92 prolog:alternate_syntax/4, 93 prolog:xref_update_syntax/2, 94 prolog:quasi_quotation_syntax/2. 95
96
97:- predicate_options(prolog_read_source_term/4, 4,
98 [ pass_to(system:read_clause/3, 3)
99 ]). 100:- predicate_options(read_source_term_at_location/3, 3,
101 [ line(integer),
102 offset(integer),
103 module(atom),
104 operators(list),
105 error(-any),
106 pass_to(system:read_term/3, 3)
107 ]). 108:- predicate_options(directory_source_files/3, 3,
109 [ recursive(boolean),
110 if(oneof([true,loaded])),
111 pass_to(system:absolute_file_name/3,3)
112 ]). 113
114
115 118
132
133prolog_read_source_term(In, Term, Expanded, Options) :-
134 maplist(read_clause_option, Options),
135 !,
136 select_option(subterm_positions(TermPos), Options,
137 RestOptions, TermPos),
138 read_clause(In, Term,
139 [ subterm_positions(TermPos)
140 | RestOptions
141 ]),
142 expand(Term, TermPos, In, Expanded),
143 '$current_source_module'(M),
144 update_state(Term, Expanded, M).
145prolog_read_source_term(In, Term, Expanded, Options) :-
146 '$current_source_module'(M),
147 select_option(syntax_errors(SE), Options, RestOptions0, dec10),
148 select_option(subterm_positions(TermPos), RestOptions0,
149 RestOptions, TermPos),
150 ( style_check(?(singleton))
151 -> FinalOptions = [ singletons(warning) | RestOptions ]
152 ; FinalOptions = RestOptions
153 ),
154 read_term(In, Term,
155 [ module(M),
156 syntax_errors(SE),
157 subterm_positions(TermPos)
158 | FinalOptions
159 ]),
160 expand(Term, TermPos, In, Expanded),
161 update_state(Term, Expanded, M).
162
163read_clause_option(syntax_errors(_)).
164read_clause_option(term_position(_)).
165read_clause_option(process_comment(_)).
166read_clause_option(comments(_)).
167
168:- public
169 expand/3. 170
171expand(Term, In, Exp) :-
172 expand(Term, _, In, Exp).
173
174expand(Var, _, _, Var) :-
175 var(Var),
176 !.
177expand(Term, _, _, Term) :-
178 no_expand(Term),
179 !.
180expand(Term, _, _, _) :-
181 requires_library(Term, Lib),
182 ensure_loaded(user:Lib),
183 fail.
184expand(Term, _, In, Term) :-
185 chr_expandable(Term, In),
186 !.
187expand(Term, Pos, _, Expanded) :-
188 expand_term(Term, Pos, Expanded, _).
189
190no_expand((:- if(_))).
191no_expand((:- elif(_))).
192no_expand((:- else)).
193no_expand((:- endif)).
194no_expand((:- require(_))).
195
196chr_expandable((:- chr_constraint(_)), In) :-
197 add_mode(In, chr).
198chr_expandable((handler(_)), In) :-
199 mode(In, chr).
200chr_expandable((rules(_)), In) :-
201 mode(In, chr).
202chr_expandable(<=>(_, _), In) :-
203 mode(In, chr).
204chr_expandable(@(_, _), In) :-
205 mode(In, chr).
206chr_expandable(==>(_, _), In) :-
207 mode(In, chr).
208chr_expandable(pragma(_, _), In) :-
209 mode(In, chr).
210chr_expandable(option(_, _), In) :-
211 mode(In, chr).
212
213add_mode(Stream, Mode) :-
214 mode(Stream, Mode),
215 !.
216add_mode(Stream, Mode) :-
217 asserta(mode(Stream, Mode)).
218
222
223requires_library((:- emacs_begin_mode(_,_,_,_,_)), library(emacs_extend)).
224requires_library((:- draw_begin_shape(_,_,_,_)), library(pcedraw)).
225requires_library((:- use_module(library(pce))), library(pce)).
226requires_library((:- pce_begin_class(_,_)), library(pce)).
227requires_library((:- pce_begin_class(_,_,_)), library(pce)).
228
232
233:- multifile
234 pce_expansion:push_compile_operators/1,
235 pce_expansion:pop_compile_operators/0. 236
237update_state(Raw, _, _) :-
238 Raw == (:- pce_end_class),
239 !,
240 ignore(pce_expansion:pop_compile_operators).
241update_state(Raw, _, SM) :-
242 subsumes_term((:- pce_extend_class(_)), Raw),
243 !,
244 pce_expansion:push_compile_operators(SM).
245update_state(_Raw, Expanded, M) :-
246 update_state(Expanded, M).
247
248update_state(Var, _) :-
249 var(Var),
250 !.
251update_state([], _) :-
252 !.
253update_state([H|T], M) :-
254 !,
255 update_state(H, M),
256 update_state(T, M).
257update_state((:- Directive), M) :-
258 nonvar(Directive),
259 !,
260 catch(update_directive(Directive, M), _, true).
261update_state((?- Directive), M) :-
262 !,
263 update_state((:- Directive), M).
264update_state(_, _).
265
266update_directive(Directive, Module) :-
267 prolog:xref_update_syntax(Directive, Module),
268 !.
269update_directive(module(Module, Public), _) :-
270 atom(Module),
271 is_list(Public),
272 !,
273 '$set_source_module'(Module),
274 maplist(import_syntax(_,Module, _), Public).
275update_directive(M:op(P,T,N), SM) :-
276 atom(M),
277 ground(op(P,T,N)),
278 !,
279 update_directive(op(P,T,N), SM).
280update_directive(op(P,T,N), SM) :-
281 ground(op(P,T,N)),
282 !,
283 strip_module(SM:N, M, PN),
284 push_op(P,T,M:PN).
285update_directive(style_check(Style), _) :-
286 ground(Style),
287 style_check(Style),
288 !.
289update_directive(use_module(Spec), SM) :-
290 ground(Spec),
291 catch(module_decl(Spec, Path, Public), _, fail),
292 is_list(Public),
293 !,
294 maplist(import_syntax(Path, SM, _), Public).
295update_directive(use_module(Spec, Imports), SM) :-
296 ground(Spec),
297 is_list(Imports),
298 catch(module_decl(Spec, Path, Public), _, fail),
299 is_list(Public),
300 !,
301 maplist(import_syntax(Path, SM, Imports), Public).
302update_directive(pce_begin_class_definition(_,_,_,_), SM) :-
303 pce_expansion:push_compile_operators(SM),
304 !.
305update_directive(_, _).
306
311
312import_syntax(_, _, _, Var) :-
313 var(Var),
314 !.
315import_syntax(_, M, Imports, Op) :-
316 Op = op(_,_,_),
317 \+ \+ member(Op, Imports),
318 !,
319 update_directive(Op, M).
320import_syntax(Path, SM, Imports, Syntax/4) :-
321 \+ \+ member(Syntax/4, Imports),
322 load_quasi_quotation_syntax(SM:Path, Syntax),
323 !.
324import_syntax(_,_,_, _).
325
326
340
341load_quasi_quotation_syntax(SM:Path, Syntax) :-
342 atom(Path), atom(Syntax),
343 source_file_property(Path, module(M)),
344 functor(ST, Syntax, 4),
345 predicate_property(M:ST, quasi_quotation_syntax),
346 !,
347 use_module(SM:Path, [Syntax/4]).
348load_quasi_quotation_syntax(SM:Path, Syntax) :-
349 atom(Path), atom(Syntax),
350 prolog:quasi_quotation_syntax(Syntax, Spec),
351 absolute_file_name(Spec, Path2,
352 [ file_type(prolog),
353 file_errors(fail),
354 access(read)
355 ]),
356 Path == Path2,
357 !,
358 use_module(SM:Path, [Syntax/4]).
359
365
366module_decl(Spec, Path, Decl) :-
367 absolute_file_name(Spec, Path,
368 [ file_type(prolog),
369 file_errors(fail),
370 access(read)
371 ]),
372 setup_call_cleanup(
373 prolog_open_source(Path, In),
374 read_module_decl(In, Decl),
375 prolog_close_source(In)).
376
377read_module_decl(In, Decl) :-
378 read(In, Term0),
379 read_module_decl(Term0, In, Decl).
380
381read_module_decl((:- module(_, DeclIn)), _In, Decl) =>
382 Decl = DeclIn.
383read_module_decl((:- encoding(Enc)), In, Decl) =>
384 set_stream(In, encoding(Enc)),
385 read(In, Term2),
386 read_module_decl(Term2, In, Decl).
387read_module_decl(_, _, _) =>
388 fail.
389
390
431
432:- thread_local
433 last_syntax_error/2. 434
435read_source_term_at_location(Stream, Term, Options) :-
436 retractall(last_syntax_error(_,_)),
437 seek_to_start(Stream, Options),
438 stream_property(Stream, position(Here)),
439 '$current_source_module'(DefModule),
440 option(module(Module), Options, DefModule),
441 option(operators(Ops), Options, []),
442 alternate_syntax(Syntax, Module, Setup, Restore),
443 set_stream_position(Stream, Here),
444 debug(read, 'Trying with syntax ~w', [Syntax]),
445 push_operators(Module:Ops),
446 call(Setup),
447 Error = error(Formal,_), 448 setup_call_cleanup(
449 asserta(user:thread_message_hook(_,_,_), Ref), 450 catch(qq_read_term(Stream, Term0,
451 [ module(Module)
452 | Options
453 ]),
454 Error,
455 true),
456 erase(Ref)),
457 call(Restore),
458 pop_operators,
459 ( var(Formal)
460 -> !, Term = Term0
461 ; assert_error(Error, Options),
462 fail
463 ).
464read_source_term_at_location(_, _, Options) :-
465 option(error(Error), Options),
466 !,
467 setof(CharNo:Msg, retract(last_syntax_error(CharNo, Msg)), Pairs),
468 last(Pairs, Error).
469
470assert_error(Error, Options) :-
471 option(error(_), Options),
472 !,
473 ( ( Error = error(syntax_error(Id),
474 stream(_S1, _Line1, _LinePos1, CharNo))
475 ; Error = error(syntax_error(Id),
476 file(_S2, _Line2, _LinePos2, CharNo))
477 )
478 -> message_to_string(error(syntax_error(Id), _), Msg),
479 assertz(last_syntax_error(CharNo, Msg))
480 ; debug(read, 'Error: ~q', [Error]),
481 throw(Error)
482 ).
483assert_error(_, _).
484
485
498
499alternate_syntax(prolog, _, true, true).
500alternate_syntax(Syntax, M, Setup, Restore) :-
501 prolog:alternate_syntax(Syntax, M, Setup, Restore).
502
503
507
508seek_to_start(Stream, Options) :-
509 option(line(Line), Options),
510 !,
511 seek(Stream, 0, bof, _),
512 seek_to_line(Stream, Line).
513seek_to_start(Stream, Options) :-
514 option(offset(Start), Options),
515 !,
516 seek(Stream, Start, bof, _).
517seek_to_start(_, _).
518
522
523seek_to_line(Fd, N) :-
524 N > 1,
525 !,
526 skip(Fd, 10),
527 NN is N - 1,
528 seek_to_line(Fd, NN).
529seek_to_line(_, _).
530
531
532 535
541
542qq_read_term(Stream, Term, Options) :-
543 select(syntax_errors(ErrorMode), Options, Options1),
544 ErrorMode \== error,
545 !,
546 ( ErrorMode == dec10
547 -> repeat,
548 qq_read_syntax_ex(Stream, Term, Options1, Error),
549 ( var(Error)
550 -> !
551 ; print_message(error, Error),
552 fail
553 )
554 ; qq_read_syntax_ex(Stream, Term, Options1, Error),
555 ( ErrorMode == fail
556 -> print_message(error, Error),
557 fail
558 ; ErrorMode == quiet
559 -> fail
560 ; domain_error(syntax_errors, ErrorMode)
561 )
562 ).
563qq_read_term(Stream, Term, Options) :-
564 qq_read_term_ex(Stream, Term, Options).
565
566qq_read_syntax_ex(Stream, Term, Options, Error) :-
567 catch(qq_read_term_ex(Stream, Term, Options),
568 error(syntax_error(Syntax), Context),
569 Error = error(Syntax, Context)).
570
571qq_read_term_ex(Stream, Term, Options) :-
572 stream_property(Stream, position(Here)),
573 catch(read_term(Stream, Term, Options),
574 error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context),
575 load_qq_and_retry(Here, Syntax, Module, Context, Stream, Term, Options)).
576
577load_qq_and_retry(Here, Syntax, Module, _, Stream, Term, Options) :-
578 set_stream_position(Stream, Here),
579 prolog:quasi_quotation_syntax(Syntax, Library),
580 !,
581 use_module(Module:Library, [Syntax/4]),
582 read_term(Stream, Term, Options).
583load_qq_and_retry(_Pos, Syntax, Module, Context, _Stream, _Term, _Options) :-
584 print_message(warning, quasi_quotation(undeclared, Syntax)),
585 throw(error(syntax_error(unknown_quasi_quotation_syntax(Syntax, Module)), Context)).
586
595
596prolog:quasi_quotation_syntax(html, library(http/html_write)).
597prolog:quasi_quotation_syntax(javascript, library(http/js_write)).
598
599
600 603
618
619prolog_open_source(Src, Fd) :-
620 '$push_input_context'(source),
621 catch(( prolog:xref_open_source(Src, Fd)
622 -> Hooked = true
623 ; open(Src, read, Fd),
624 Hooked = false
625 ), E,
626 ( '$pop_input_context',
627 throw(E)
628 )),
629 skip_hashbang(Fd),
630 push_operators([]),
631 '$current_source_module'(SM),
632 '$save_lex_state'(LexState, []),
633 asserta(open_source(Fd, state(Hooked, Src, LexState, SM))).
634
635skip_hashbang(Fd) :-
636 catch(( peek_char(Fd, #) 637 -> skip(Fd, 10)
638 ; true
639 ), E,
640 ( close(Fd, [force(true)]),
641 '$pop_input_context',
642 throw(E)
643 )).
644
652
653
660
661prolog_close_source(In) :-
662 call_cleanup(
663 restore_source_context(In, Hooked, Src),
664 close_source(Hooked, Src, In)).
665
666close_source(true, Src, In) :-
667 catch(prolog:xref_close_source(Src, In), _, false),
668 !,
669 '$pop_input_context'.
670close_source(_, _Src, In) :-
671 close(In, [force(true)]),
672 '$pop_input_context'.
673
674restore_source_context(In, Hooked, Src) :-
675 ( at_end_of_stream(In)
676 -> true
677 ; ignore(catch(expand(end_of_file, _, In, _), _, true))
678 ),
679 pop_operators,
680 retractall(mode(In, _)),
681 ( retract(open_source(In, state(Hooked, Src, LexState, SM)))
682 -> '$restore_lex_state'(LexState),
683 '$set_source_module'(SM)
684 ; assertion(fail)
685 ).
686
692
699
700prolog_canonical_source(Source, Src) :-
701 var(Source),
702 !,
703 Src = Source.
704prolog_canonical_source(User, user) :-
705 User == user,
706 !.
707prolog_canonical_source(Src, Id) :- 708 prolog:xref_source_identifier(Src, Id),
709 !.
710prolog_canonical_source(Source, Src) :-
711 source_file(Source),
712 !,
713 Src = Source.
714prolog_canonical_source(Source, Src) :-
715 absolute_file_name(Source, Src,
716 [ file_type(prolog),
717 access(read),
718 file_errors(fail)
719 ]),
720 !.
721
722
727
728file_name_on_path(Path, ShortId) :-
729 ( file_alias_path(Alias, Dir),
730 atom_concat(Dir, Local, Path)
731 -> ( Alias == '.'
732 -> ShortId = Local
733 ; file_name_extension(Base, pl, Local)
734 -> ShortId =.. [Alias, Base]
735 ; ShortId =.. [Alias, Local]
736 )
737 ; ShortId = Path
738 ).
739
740
745
746:- dynamic
747 alias_cache/2. 748
749file_alias_path(Alias, Dir) :-
750 ( alias_cache(_, _)
751 -> true
752 ; build_alias_cache
753 ),
754 ( nonvar(Dir)
755 -> ensure_slash(Dir, DirSlash),
756 alias_cache(Alias, DirSlash)
757 ; alias_cache(Alias, Dir)
758 ).
759
760build_alias_cache :-
761 findall(t(DirLen, AliasLen, Alias, Dir),
762 search_path(Alias, Dir, AliasLen, DirLen), Ts),
763 sort(0, >, Ts, List),
764 forall(member(t(_, _, Alias, Dir), List),
765 assert(alias_cache(Alias, Dir))).
766
767search_path('.', Here, 999, DirLen) :-
768 working_directory(Here0, Here0),
769 ensure_slash(Here0, Here),
770 atom_length(Here, DirLen).
771search_path(Alias, Dir, AliasLen, DirLen) :-
772 user:file_search_path(Alias, _),
773 Alias \== autoload, 774 Alias \== noautoload,
775 Spec =.. [Alias,'.'],
776 atom_length(Alias, AliasLen0),
777 AliasLen is 1000 - AliasLen0, 778 absolute_file_name(Spec, Dir0,
779 [ file_type(directory),
780 access(read),
781 solutions(all),
782 file_errors(fail)
783 ]),
784 ensure_slash(Dir0, Dir),
785 atom_length(Dir, DirLen).
786
787ensure_slash(Dir, Dir) :-
788 sub_atom(Dir, _, _, 0, /),
789 !.
790ensure_slash(Dir0, Dir) :-
791 atom_concat(Dir0, /, Dir).
792
793
811
812path_segments_atom(Segments, Atom) :-
813 var(Atom),
814 !,
815 ( atomic(Segments)
816 -> Atom = Segments
817 ; segments_to_list(Segments, List, [])
818 -> atomic_list_concat(List, /, Atom)
819 ; throw(error(type_error(file_path, Segments), _))
820 ).
821path_segments_atom(Segments, Atom) :-
822 atomic_list_concat(List, /, Atom),
823 parts_to_path(List, Segments).
824
825segments_to_list(Var, _, _) :-
826 var(Var), !, fail.
827segments_to_list(A/B, H, T) :-
828 segments_to_list(A, H, T0),
829 segments_to_list(B, T0, T).
830segments_to_list(A, [A|T], T) :-
831 atomic(A).
832
833parts_to_path([One], One) :- !.
834parts_to_path(List, More/T) :-
835 ( append(H, [T], List)
836 -> parts_to_path(H, More)
837 ).
838
851
852directory_source_files(Dir, SrcFiles, Options) :-
853 option(if(loaded), Options, loaded),
854 !,
855 absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
856 ( option(recursive(true), Options)
857 -> ensure_slash(AbsDir, Prefix),
858 findall(F, ( source_file(F),
859 sub_atom(F, 0, _, _, Prefix)
860 ),
861 SrcFiles)
862 ; findall(F, ( source_file(F),
863 file_directory_name(F, AbsDir)
864 ),
865 SrcFiles)
866 ).
867directory_source_files(Dir, SrcFiles, Options) :-
868 absolute_file_name(Dir, AbsDir, [file_type(directory), access(read)]),
869 directory_files(AbsDir, Files),
870 phrase(src_files(Files, AbsDir, Options), SrcFiles).
871
872src_files([], _, _) -->
873 [].
874src_files([H|T], Dir, Options) -->
875 { file_name_extension(_, Ext, H),
876 user:prolog_file_type(Ext, prolog),
877 \+ user:prolog_file_type(Ext, qlf),
878 dir_file_path(Dir, H, File0),
879 absolute_file_name(File0, File,
880 [ file_errors(fail)
881 | Options
882 ])
883 },
884 !,
885 [File],
886 src_files(T, Dir, Options).
887src_files([H|T], Dir, Options) -->
888 { \+ special(H),
889 option(recursive(true), Options),
890 dir_file_path(Dir, H, SubDir),
891 exists_directory(SubDir),
892 !,
893 catch(directory_files(SubDir, Files), _, fail)
894 },
895 !,
896 src_files(Files, SubDir, Options),
897 src_files(T, Dir, Options).
898src_files([_|T], Dir, Options) -->
899 src_files(T, Dir, Options).
900
901special(.).
902special(..).
903
906dir_file_path(Dir, File, Path) :-
907 ( sub_atom(Dir, _, _, 0, /)
908 -> atom_concat(Dir, File, Path)
909 ; atom_concat(Dir, /, TheDir),
910 atom_concat(TheDir, File, Path)
911 ).
912
913
914
915 918
919:- multifile
920 prolog:message//1. 921
922prolog:message(quasi_quotation(undeclared, Syntax)) -->
923 [ 'Undeclared quasi quotation syntax: ~w'-[Syntax], nl,
924 'Autoloading can be defined using prolog:quasi_quotation_syntax/2'
925 ]