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