36
37:- module(prolog_main,
38 [ main/0,
39 argv_options/3, 40 argv_options/4, 41 argv_usage/1, 42 cli_parse_debug_options/2, 43 cli_debug_opt_type/3, 44 cli_debug_opt_help/2, 45 cli_debug_opt_meta/2, 46 cli_enable_development_system/0
47 ]). 48:- autoload(library(apply), [maplist/2, maplist/3, partition/4]). 49:- autoload(library(lists), [append/3]). 50:- autoload(library(pairs), [pairs_keys/2, pairs_values/2]). 51:- autoload(library(prolog_code), [pi_head/2]). 52:- autoload(library(prolog_debug), [spy/1]). 53:- autoload(library(dcg/high_order), [sequence//3, sequence//2]). 54:- autoload(library(option), [option/2]). 55
56:- meta_predicate
57 argv_options(:, -, -),
58 argv_options(:, -, -, +),
59 argv_usage(:). 60
61:- dynamic
62 interactive/0. 63
92
93:- module_transparent
94 main/0. 95
110
111main :-
112 current_prolog_flag(break_level, _),
113 !,
114 current_prolog_flag(argv, Av),
115 context_module(M),
116 M:main(Av).
117main :-
118 context_module(M),
119 set_signals,
120 current_prolog_flag(argv, Av),
121 catch_with_backtrace(M:main(Av), Error, throw(Error)),
122 ( interactive
123 -> cli_enable_development_system
124 ; true
125 ).
126
127set_signals :-
128 on_signal(int, _, interrupt).
129
134
135interrupt(_Sig) :-
136 halt(1).
137
138 141
241
242argv_options(M:Argv, Positional, Options) :-
243 in(M:opt_type(_,_,_)),
244 !,
245 argv_options(M:Argv, Positional, Options, [on_error(halt(1))]).
246argv_options(_:Argv, Positional, Options) :-
247 argv_untyped_options(Argv, Positional, Options).
248
263
264argv_options(Argv, Positional, Options, POptions) :-
265 option(on_error(halt(Code)), POptions),
266 !,
267 E = error(_,_),
268 catch(opt_parse(Argv, Positional, Options, POptions), E,
269 ( print_message(error, E),
270 halt(Code)
271 )).
272argv_options(Argv, Positional, Options, POptions) :-
273 opt_parse(Argv, Positional, Options, POptions).
274
282
283argv_untyped_options([], Pos, Opts) =>
284 Pos = [], Opts = [].
285argv_untyped_options([--|R], Pos, Ops) =>
286 Pos = R, Ops = [].
287argv_untyped_options([H0|T0], R, Ops), sub_atom(H0, 0, _, _, --) =>
288 Ops = [H|T],
289 ( sub_atom(H0, B, _, A, =)
290 -> B2 is B-2,
291 sub_atom(H0, 2, B2, _, Name),
292 sub_string(H0, _, A, 0, Value0),
293 convert_option(Name, Value0, Value)
294 ; sub_atom(H0, 2, _, 0, Name0),
295 ( sub_atom(Name0, 0, _, _, 'no-')
296 -> sub_atom(Name0, 3, _, 0, Name),
297 Value = false
298 ; Name = Name0,
299 Value = true
300 )
301 ),
302 canonical_name(Name, PlName),
303 H =.. [PlName,Value],
304 argv_untyped_options(T0, R, T).
305argv_untyped_options([H|T0], Ops, T) =>
306 Ops = [H|R],
307 argv_untyped_options(T0, R, T).
308
309convert_option(password, String, String) :- !.
310convert_option(_, String, Number) :-
311 number_string(Number, String),
312 !.
313convert_option(_, String, Atom) :-
314 atom_string(Atom, String).
315
316canonical_name(Name, PlName) :-
317 split_string(Name, "-_", "", Parts),
318 atomic_list_concat(Parts, '_', PlName).
319
329
330opt_parse(M:Argv, _Positional, _Options, _POptions) :-
331 opt_needs_help(M:Argv),
332 !,
333 argv_usage(M:debug),
334 halt(0).
335opt_parse(M:Argv, Positional, Options, POptions) :-
336 opt_parse(Argv, Positional, Options, M, POptions).
337
338opt_needs_help(M:[Arg]) :-
339 in(M:opt_type(_, help, boolean)),
340 !,
341 in(M:opt_type(Opt, help, boolean)),
342 ( short_opt(Opt)
343 -> atom_concat(-, Opt, Arg)
344 ; atom_concat(--, Opt, Arg)
345 ),
346 !.
347opt_needs_help(_:['-h']).
348opt_needs_help(_:['-?']).
349opt_needs_help(_:['--help']).
350
351opt_parse([], Positional, Options, _, _) =>
352 Positional = [],
353 Options = [].
354opt_parse([--|T], Positional, Options, _, _) =>
355 Positional = T,
356 Options = [].
357opt_parse([H|T], Positional, Options, M, POptions), atom_concat(--, Long, H) =>
358 take_long(Long, T, Positional, Options, M, POptions).
359opt_parse([H|T], Positional, Options, M, POptions),
360 H \== '-',
361 string_concat(-, Opts, H) =>
362 string_chars(Opts, Shorts),
363 take_shorts(Shorts, T, Positional, Options, M, POptions).
364opt_parse(Argv, Positional, Options, _M, POptions),
365 option(options_after_arguments(false), POptions) =>
366 Positional = Argv,
367 Options = [].
368opt_parse([H|T], Positional, Options, M, POptions) =>
369 Positional = [H|PT],
370 opt_parse(T, PT, Options, M, POptions).
371
372
373take_long(Long, T, Positional, Options, M, POptions) :- 374 sub_atom(Long, B, _, A, =),
375 !,
376 sub_atom(Long, 0, B, _, LName0),
377 sub_atom(Long, _, A, 0, VAtom),
378 canonical_name(LName0, LName),
379 ( in(M:opt_type(LName, Name, Type))
380 -> opt_value(Type, Long, VAtom, Value),
381 Opt =.. [Name,Value],
382 Options = [Opt|OptionsT],
383 opt_parse(T, Positional, OptionsT, M, POptions)
384 ; opt_error(unknown_option(M:LName0))
385 ).
386take_long(LName0, T, Positional, Options, M, POptions) :- 387 canonical_name(LName0, LName),
388 take_long_(LName, T, Positional, Options, M, POptions).
389
390take_long_(Long, T, Positional, Options, M, POptions) :- 391 opt_bool_type(Long, Name, Value, M), 392 !,
393 Opt =.. [Name,Value],
394 Options = [Opt|OptionsT],
395 opt_parse(T, Positional, OptionsT, M, POptions).
396take_long_(Long, T, Positional, Options, M, POptions) :- 397 ( atom_concat('no_', LName, Long)
398 ; atom_concat('no', LName, Long)
399 ),
400 in(M:opt_type(LName, Name, Type)),
401 type_optional_bool(Type, Value0),
402 !,
403 negate(Value0, Value),
404 Opt =.. [Name,Value],
405 Options = [Opt|OptionsT],
406 opt_parse(T, Positional, OptionsT, M, POptions).
407take_long_(Long, T, Positional, Options, M, POptions) :- 408 in(M:opt_type(Long, Name, Type)),
409 type_optional_bool(Type, Value),
410 ( T = [VAtom|_],
411 sub_atom(VAtom, 0, _, _, -)
412 -> true
413 ; T == []
414 ),
415 Opt =.. [Name,Value],
416 Options = [Opt|OptionsT],
417 opt_parse(T, Positional, OptionsT, M, POptions).
418take_long_(Long, T, Positional, Options, M, POptions) :- 419 in(M:opt_type(Long, Name, Type)),
420 !,
421 ( T = [VAtom|T1]
422 -> opt_value(Type, Long, VAtom, Value),
423 Opt =.. [Name,Value],
424 Options = [Opt|OptionsT],
425 opt_parse(T1, Positional, OptionsT, M, POptions)
426 ; opt_error(missing_value(Long, Type))
427 ).
428take_long_(Long, _, _, _, M, _) :-
429 opt_error(unknown_option(M:Long)).
430
431take_shorts([], T, Positional, Options, M, POptions) :-
432 opt_parse(T, Positional, Options, M, POptions).
433take_shorts([H|T], Argv, Positional, Options, M, POptions) :-
434 opt_bool_type(H, Name, Value, M),
435 !,
436 Opt =.. [Name,Value],
437 Options = [Opt|OptionsT],
438 take_shorts(T, Argv, Positional, OptionsT, M, POptions).
439take_shorts([H|T], Argv, Positional, Options, M, POptions) :-
440 in(M:opt_type(H, Name, Type)),
441 !,
442 ( T == []
443 -> ( Argv = [VAtom|ArgvT]
444 -> opt_value(Type, H, VAtom, Value),
445 Opt =.. [Name,Value],
446 Options = [Opt|OptionsT],
447 take_shorts(T, ArgvT, Positional, OptionsT, M, POptions)
448 ; opt_error(missing_value(H, Type))
449 )
450 ; atom_chars(VAtom, T),
451 opt_value(Type, H, VAtom, Value),
452 Opt =.. [Name,Value],
453 Options = [Opt|OptionsT],
454 take_shorts([], Argv, Positional, OptionsT, M, POptions)
455 ).
456take_shorts([H|_], _, _, _, M, _) :-
457 opt_error(unknown_option(M:H)).
458
459opt_bool_type(Opt, Name, Value, M) :-
460 in(M:opt_type(Opt, Name, Type)),
461 type_bool(Type, Value).
462
463type_bool(Type, Value) :-
464 ( Type == boolean
465 -> Value = true
466 ; Type = boolean(Value)
467 ).
468
469type_optional_bool((A|B), Value) =>
470 ( type_optional_bool(A, Value)
471 -> true
472 ; type_optional_bool(B, Value)
473 ).
474type_optional_bool(Type, Value) =>
475 type_bool(Type, Value).
476
477negate(true, false).
478negate(false, true).
479
483
484opt_value(Type, _Opt, VAtom, Value) :-
485 opt_convert(Type, VAtom, Value),
486 !.
487opt_value(Type, Opt, VAtom, _) :-
488 opt_error(value_type(Opt, Type, VAtom)).
489
491
492opt_convert(A|B, Spec, Value) :-
493 ( opt_convert(A, Spec, Value)
494 -> true
495 ; opt_convert(B, Spec, Value)
496 ).
497opt_convert(boolean, Spec, Value) :-
498 to_bool(Spec, Value).
499opt_convert(boolean(_), Spec, Value) :-
500 to_bool(Spec, Value).
501opt_convert(number, Spec, Value) :-
502 atom_number(Spec, Value).
503opt_convert(integer, Spec, Value) :-
504 atom_number(Spec, Value),
505 integer(Value).
506opt_convert(float, Spec, Value) :-
507 atom_number(Spec, Value0),
508 Value is float(Value0).
509opt_convert(nonneg, Spec, Value) :-
510 atom_number(Spec, Value),
511 integer(Value),
512 Value >= 0.
513opt_convert(natural, Spec, Value) :-
514 atom_number(Spec, Value),
515 integer(Value),
516 Value >= 1.
517opt_convert(between(Low, High), Spec, Value) :-
518 atom_number(Spec, Value0),
519 ( ( float(Low) ; float(High) )
520 -> Value is float(Value0)
521 ; integer(Value0),
522 Value = Value0
523 ),
524 Value >= Low, Value =< High.
525opt_convert(atom, Value, Value).
526opt_convert(oneof(List), Value, Value) :-
527 memberchk(Value, List).
528opt_convert(string, Value0, Value) :-
529 atom_string(Value0, Value).
530opt_convert(file, Spec, Value) :-
531 prolog_to_os_filename(Value, Spec).
532opt_convert(file(Access), Spec, Value) :-
533 ( Spec == '-'
534 -> Value = '-'
535 ; prolog_to_os_filename(Value, Spec),
536 ( access_file(Value, Access)
537 -> true
538 ; opt_error(access_file(Spec, Access))
539 )
540 ).
541opt_convert(directory, Spec, Value) :-
542 prolog_to_os_filename(Value, Spec).
543opt_convert(directory(Access), Spec, Value) :-
544 prolog_to_os_filename(Value, Spec),
545 access_directory(Value, Access).
546opt_convert(term, Spec, Value) :-
547 term_string(Value, Spec, []).
548opt_convert(term(Options), Spec, Value) :-
549 term_string(Term, Spec, Options),
550 ( option(variable_names(Bindings), Options)
551 -> Value = Term-Bindings
552 ; Value = Term
553 ).
554
555access_directory(Dir, read) =>
556 exists_directory(Dir),
557 access_file(Dir, read).
558access_directory(Dir, write) =>
559 exists_directory(Dir),
560 access_file(Dir, write).
561access_directory(Dir, create) =>
562 ( exists_directory(Dir)
563 -> access_file(Dir, write)
564 ; \+ exists_file(Dir),
565 file_directory_name(Dir, Parent),
566 exists_directory(Parent),
567 access_file(Parent, write)
568 ).
569
570to_bool(true, true).
571to_bool('True', true).
572to_bool('TRUE', true).
573to_bool(on, true).
574to_bool('On', true).
575to_bool(yes, true).
576to_bool('Yes', true).
577to_bool('1', true).
578to_bool(false, false).
579to_bool('False', false).
580to_bool('FALSE', false).
581to_bool(off, false).
582to_bool('Off', false).
583to_bool(no, false).
584to_bool('No', false).
585to_bool('0', false).
586
613
614argv_usage(M:Level) :-
615 print_message(Level, opt_usage(M)).
616
617:- multifile
618 prolog:message//1. 619
620prolog:message(opt_usage(M)) -->
621 usage(M).
622
623usage(M) -->
624 usage_text(M:header),
625 usage_line(M),
626 usage_options(M),
627 usage_text(M:footer).
628
633
634usage_text(M:Which) -->
635 { in(M:opt_help(help(Which), Help))
636 },
637 !,
638 ( {Which == header}
639 -> user_text(M:Help), [nl]
640 ; [nl], user_text(M:Help)
641 ).
642usage_text(_) -->
643 [].
644
645user_text(M:Entries) -->
646 { is_list(Entries) },
647 sequence(help_elem(M), Entries).
648user_text(_:Help) -->
649 [ '~w'-[Help] ].
650
651help_elem(M, \Callable) -->
652 { callable(Callable) },
653 call(M:Callable),
654 !.
655help_elem(_M, Elem) -->
656 [ Elem ].
657
658usage_line(M) -->
659 [ ansi(comment, 'Usage: ', []) ],
660 cmdline(M),
661 ( {in(M:opt_help(help(usage), Help))}
662 -> user_text(M:Help)
663 ; [ ' [options]'-[] ]
664 ),
665 [ nl, nl ].
666
667cmdline(_M) -->
668 { current_prolog_flag(associated_file, AbsFile),
669 file_base_name(AbsFile, Base),
670 current_prolog_flag(os_argv, Argv),
671 append(Pre, [File|_], Argv),
672 file_base_name(File, Base),
673 append(Pre, [File], Cmd),
674 !
675 },
676 sequence(cmdarg, [' '-[]], Cmd).
677cmdline(_M) -->
678 { current_prolog_flag(saved_program, true),
679 current_prolog_flag(os_argv, OsArgv),
680 append(_, ['-x', State|_], OsArgv),
681 !
682 },
683 cmdarg(State).
684cmdline(_M) -->
685 { current_prolog_flag(os_argv, [Argv0|_])
686 },
687 cmdarg(Argv0).
688
689cmdarg(A) -->
690 [ '~w'-[A] ].
691
697
698usage_options(M) -->
699 { findall(Opt, get_option(M, Opt), Opts),
700 maplist(options_width, Opts, OptWidths),
701 max_list(OptWidths, MaxOptWidth),
702 tty_width(Width),
703 OptColW is min(MaxOptWidth, 30),
704 HelpColW is Width-4-OptColW
705 },
706 [ ansi(comment, 'Options:', []), nl ],
707 sequence(opt_usage(OptColW, HelpColW), [nl], Opts).
708
711:- if(current_predicate(tty_size/2)). 712tty_width(Width) :-
713 catch(tty_size(_, Width), _, Width = 80).
714:- else. 715tty_width(80).
716:- endif. 717
718opt_usage(OptColW, HelpColW, opt(_Name, Type, Short, Long, Help, Meta)) -->
719 options(Type, Short, Long, Meta),
720 [ '~t~*:| '-[OptColW] ],
721 help_text(Help, OptColW, HelpColW).
722
723help_text([First|Lines], Indent, _Width) -->
724 !,
725 [ '~w'-[First], nl ],
726 sequence(rest_line(Indent), [nl], Lines).
727help_text(Text, _Indent, Width) -->
728 { string_length(Text, Len),
729 Len =< Width
730 },
731 !,
732 [ '~w'-[Text] ].
733help_text(Text, Indent, Width) -->
734 { wrap_text(Width, Text, [First|Lines])
735 },
736 [ '~w'-[First], nl ],
737 sequence(rest_line(Indent), [nl], Lines).
738
739rest_line(Indent, Line) -->
740 [ '~t~*| ~w'-[Indent, Line] ].
741
747
748wrap_text(Width, Text, Wrapped) :-
749 split_string(Text, " \t\n", " \t\n", Words),
750 wrap_lines(Words, Width, Wrapped).
751
752wrap_lines([], _, []).
753wrap_lines([H|T0], Width, [Line|Lines]) :-
754 !,
755 string_length(H, Len),
756 take_line(T0, T1, Width, Len, LineWords),
757 atomics_to_string([H|LineWords], " ", Line),
758 wrap_lines(T1, Width, Lines).
759
760take_line([H|T0], T, Width, Here, [H|Line]) :-
761 string_length(H, Len),
762 NewHere is Here+Len+1,
763 NewHere =< Width,
764 !,
765 take_line(T0, T, Width, NewHere, Line).
766take_line(T, T, _, _, []).
767
771
772options(Type, ShortOpt, LongOpts, Meta) -->
773 { append(ShortOpt, LongOpts, Opts) },
774 sequence(option(Type, Meta), [', '-[]], Opts).
775
776option(boolean, _, Opt) -->
777 opt(Opt).
778option(_Type, [Meta], Opt) -->
779 \+ { short_opt(Opt) },
780 !,
781 opt(Opt),
782 [ '[='-[], ansi(var, '~w', [Meta]), ']'-[] ].
783option(_Type, Meta, Opt) -->
784 opt(Opt),
785 ( { short_opt(Opt) }
786 -> [ ' '-[] ]
787 ; [ '='-[] ]
788 ),
789 [ ansi(var, '~w', [Meta]) ].
790
794
795options_width(opt(_Name, boolean, Short, Long, _Help, _Meta), W) =>
796 length(Short, SCount),
797 length(Long, LCount),
798 maplist(atom_length, Long, LLens),
799 sum_list(LLens, LLen),
800 W is ((SCount+LCount)-1)*2 + 801 SCount*2 +
802 LCount*2 + LLen.
803options_width(opt(_Name, _Type, Short, Long, _Help, Meta), W) =>
804 length(Short, SCount),
805 length(Long, LCount),
806 ( Meta = [MName]
807 -> atom_length(MName, MLen0),
808 MLen is MLen0+2
809 ; atom_length(Meta, MLen)
810 ),
811 maplist(atom_length, Long, LLens),
812 sum_list(LLens, LLen),
813 W is ((SCount+LCount)-1)*2 + 814 SCount*3 + SCount*MLen +
815 LCount*3 + LLen + LCount*MLen.
816
822
823get_option(M, opt(help, boolean, [h,?], [help],
824 Help, -)) :-
825 \+ in(M:opt_type(_, help, boolean)), 826 ( in(M:opt_help(help, Help))
827 -> true
828 ; Help = "Show this help message and exit"
829 ).
830get_option(M, opt(Name, TypeName, Short, Long, Help, Meta)) :-
831 findall(Name, in(M:opt_type(_, Name, _)), Names),
832 list_to_set(Names, UNames),
833 member(Name, UNames),
834 findall(Opt-Type,
835 in(M:opt_type(Opt, Name, Type)),
836 Pairs),
837 option_type(Name, Pairs, TypeT),
838 functor(TypeT, TypeName, _),
839 pairs_keys(Pairs, Opts),
840 partition(short_opt, Opts, Short, Long),
841 ( in(M:opt_help(Name, Help))
842 -> true
843 ; Help = ''
844 ),
845 ( in(M:opt_meta(Name, Meta0))
846 -> true
847 ; upcase_atom(TypeName, Meta0)
848 ),
849 ( \+ type_bool(TypeT, _),
850 type_optional_bool(TypeT, _)
851 -> Meta = [Meta0]
852 ; Meta = Meta0
853 ).
854
855option_type(Name, Pairs, Type) :-
856 pairs_values(Pairs, Types),
857 sort(Types, [Type|UTypes]),
858 ( UTypes = []
859 -> true
860 ; print_message(warning,
861 error(opt_error(multiple_types(Name, [Type|UTypes])),_))
862 ).
863
868
869in(Goal) :-
870 pi_head(PI, Goal),
871 current_predicate(PI),
872 call(Goal).
873
874short_opt(Opt) :-
875 atom_length(Opt, 1).
876
877 880
884
885opt_error(Error) :-
886 throw(error(opt_error(Error), _)).
887
888:- multifile
889 prolog:error_message//1. 890
891prolog:error_message(opt_error(Error)) -->
892 opt_error(Error).
893
894opt_error(unknown_option(M:Opt)) -->
895 [ 'Unknown option: '-[] ],
896 opt(Opt),
897 hint_help(M).
898opt_error(missing_value(Opt, Type)) -->
899 [ 'Option '-[] ],
900 opt(Opt),
901 [ ' requires an argument (of type ~p)'-[Type] ].
902opt_error(value_type(Opt, Type, Found)) -->
903 [ 'Option '-[] ],
904 opt(Opt), [' requires'],
905 type(Type),
906 [ ' (found '-[], ansi(code, '~w', [Found]), ')'-[] ].
907opt_error(access_file(File, exist)) -->
908 [ 'File '-[], ansi(code, '~w', [File]),
909 ' does not exist'-[]
910 ].
911opt_error(access_file(File, Access)) -->
912 { access_verb(Access, Verb) },
913 [ 'Cannot access file '-[], ansi(code, '~w', [File]),
914 ' for '-[], ansi(code, '~w', [Verb])
915 ].
916
917access_verb(read, reading).
918access_verb(write, writing).
919access_verb(append, writing).
920access_verb(execute, executing).
921
922hint_help(M) -->
923 { in(M:opt_type(Opt, help, boolean)) },
924 !,
925 [ ' (' ], opt(Opt), [' for help)'].
926hint_help(_) -->
927 [ ' (-h for help)'-[] ].
928
929opt(Opt) -->
930 { short_opt(Opt) },
931 !,
932 [ ansi(bold, '-~w', [Opt]) ].
933opt(Opt) -->
934 [ ansi(bold, '--~w', [Opt]) ].
935
936type(A|B) -->
937 type(A), [' or'],
938 type(B).
939type(oneof([One])) -->
940 !,
941 [ ' ' ],
942 atom(One).
943type(oneof(List)) -->
944 !,
945 [ ' one of '-[] ],
946 sequence(atom, [', '], List).
947type(between(Low, High)) -->
948 !,
949 [ ' a number '-[],
950 ansi(code, '~w', [Low]), '..', ansi(code, '~w', [High])
951 ].
952type(nonneg) -->
953 [ ' a non-negative integer'-[] ].
954type(natural) -->
955 [ ' a positive integer (>= 1)'-[] ].
956type(file(Access)) -->
957 [ ' a file with ~w access'-[Access] ].
958type(Type) -->
959 [ ' an argument of type '-[], ansi(code, '~w', [Type]) ].
960
961atom(A) -->
962 [ ansi(code, '~w', [A]) ].
963
964
965 968
984
985cli_parse_debug_options([], []).
986cli_parse_debug_options([H|T0], Opts) :-
987 debug_option(H),
988 !,
989 cli_parse_debug_options(T0, Opts).
990cli_parse_debug_options([H|T0], [H|T]) :-
991 cli_parse_debug_options(T0, T).
992
1012
1013cli_debug_opt_type(debug, debug, string).
1014cli_debug_opt_type(spy, spy, string).
1015cli_debug_opt_type(gspy, gspy, string).
1016cli_debug_opt_type(interactive, interactive, boolean).
1017
1018cli_debug_opt_help(debug,
1019 "Call debug(Topic). See debug/1 and debug/3. \c
1020 Multiple topics may be separated by : or ;").
1021cli_debug_opt_help(spy,
1022 "Place a spy-point on Predicate. \c
1023 Multiple topics may be separated by : or ;").
1024cli_debug_opt_help(gspy,
1025 "As --spy using the graphical debugger. See tspy/1 \c
1026 Multiple topics may be separated by `;`").
1027cli_debug_opt_help(interactive,
1028 "Start the Prolog toplevel after main/1 completes.").
1029
1030cli_debug_opt_meta(debug, 'TOPICS').
1031cli_debug_opt_meta(spy, 'PREDICATES').
1032cli_debug_opt_meta(gspy, 'PREDICATES').
1033
1034:- meta_predicate
1035 spy_from_string(1, +). 1036
1037debug_option(interactive(true)) :-
1038 asserta(interactive).
1039debug_option(debug(Spec)) :-
1040 split_string(Spec, ";", "", Specs),
1041 maplist(debug_from_string, Specs).
1042debug_option(spy(Spec)) :-
1043 split_string(Spec, ";", "", Specs),
1044 maplist(spy_from_string(spy), Specs).
1045debug_option(gspy(Spec)) :-
1046 split_string(Spec, ";", "", Specs),
1047 maplist(spy_from_string(cli_gspy), Specs).
1048
1049debug_from_string(TopicS) :-
1050 term_string(Topic, TopicS),
1051 debug(Topic).
1052
1053spy_from_string(Pred, Spec) :-
1054 atom_pi(Spec, PI),
1055 call(Pred, PI).
1056
1057cli_gspy(PI) :-
1058 ( exists_source(library(threadutil))
1059 -> use_module(library(threadutil), [tspy/1]),
1060 Goal = tspy(PI)
1061 ; exists_source(library(gui_tracer))
1062 -> use_module(library(gui_tracer), [gspy/1]),
1063 Goal = gspy(PI)
1064 ; Goal = spy(PI)
1065 ),
1066 call(Goal).
1067
1068atom_pi(Atom, Module:PI) :-
1069 split(Atom, :, Module, PiAtom),
1070 !,
1071 atom_pi(PiAtom, PI).
1072atom_pi(Atom, Name//Arity) :-
1073 split(Atom, //, Name, Arity),
1074 !.
1075atom_pi(Atom, Name/Arity) :-
1076 split(Atom, /, Name, Arity),
1077 !.
1078atom_pi(Atom, _) :-
1079 format(user_error, 'Invalid predicate indicator: "~w"~n', [Atom]),
1080 halt(1).
1081
1082split(Atom, Sep, Before, After) :-
1083 sub_atom(Atom, BL, _, AL, Sep),
1084 !,
1085 sub_atom(Atom, 0, BL, _, Before),
1086 sub_atom(Atom, _, AL, 0, AfterAtom),
1087 ( atom_number(AfterAtom, After)
1088 -> true
1089 ; After = AfterAtom
1090 ).
1091
1092
1102
1103cli_enable_development_system :-
1104 on_signal(int, _, debug),
1105 set_prolog_flag(xpce_threaded, true),
1106 set_prolog_flag(message_ide, true),
1107 ( current_prolog_flag(xpce_version, _)
1108 -> use_module(library(pce_dispatch)),
1109 memberchk(Goal, [pce_dispatch([])]),
1110 call(Goal)
1111 ; true
1112 ),
1113 set_prolog_flag(toplevel_goal, prolog).
1114
1115
1116 1119
1120:- multifile
1121 prolog:called_by/2. 1122
1123prolog:called_by(main, [main(_)]).
1124prolog:called_by(argv_options(_,_,_),
1125 [ opt_type(_,_,_),
1126 opt_help(_,_),
1127 opt_meta(_,_)
1128 ])