1 % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5
6 :- module(tools_strings,
7 [string_concatenate/3,
8 ajoin/2, ajoin_with_limit/3, ajoin_with_sep/3,
9 safe_name/2,
10 predicate_functor/3,
11 atom_codes_with_limit/2, atom_codes_with_limit/3,
12 truncate_atom/3,
13 convert_cli_arg/2,
14 convert_to_number/2,
15 convert_atom_to_number/2
16 ]).
17
18 :- use_module(module_information).
19
20 :- module_info(group,infrastructure).
21 :- module_info(description,'A few utilities on strings/atoms seperated out from tools.pl to avoid cyclic module dependencies.').
22
23
24 :- mode string_concatenate(i,i,o).
25 string_concatenate(X,Y,XY) :- atom(X),atom(Y),!, atom_concat(X,Y,XY).
26 string_concatenate(X,Y,XY) :-
27 safe_name(X,Xs),safe_name(Y,Ys),append(Xs,Ys,XYs),atom_codes(XY,XYs).
28
29 % tests are stored in tools.pl to avoid cyclic module dependencies
30 %:- assert_must_succeed((tools_strings: ajoin_with_sep([link,a,xa],'.',Text), Text == 'link.a.xa')).
31 %:- assert_must_succeed((tools_strings: ajoin_with_sep([link],'.',Text), Text == 'link')).
32
33 ajoin_with_sep(List,Sep,Text) :- ajoin_with_sep_aux(List,'',Sep,Text).
34
35 ajoin_with_sep_aux([],Txt,_Sep,Text) :- Text=Txt.
36 ajoin_with_sep_aux([H|T],Txt,Sep,Text) :-
37 ( Txt == '' -> toAtom(H,TTxt)
38 ; ajoin([Txt,Sep,H],TTxt)), ajoin_with_sep_aux(T,TTxt,Sep,Text).
39
40
41 /* Concats a list of atoms, but checks if numbers or compound terms are
42 involved, which are converted to simple atoms */
43 ajoin(ListOfAtoms,Atom) :- ajoin_with_limit(ListOfAtoms,10000000,Atom).
44 % atom_codes with list of 100000000 takes very long, with 10000000 a few seconds
45
46 %safe_atom_concat(A,B,C,ExceptionOcc) :-
47 % on_exception(error(representation_error(max_atom_length),_),atom_concat(A,B,C),
48 % (print(exception(max_atom_length)),nl,A=C,ExceptionOcc=true)).
49
50 :- use_module(library(codesio),[write_to_codes/2]).
51
52 toAtom(Number,Atom) :- number(Number),!,number_chars(Number,C),atom_chars(Atom,C).
53 toAtom(Atom,Atom) :- atomic(Atom),!.
54 toAtom(Term,Atom) :- write_to_codes(Term,Codes),safe_atom_codes(Atom,Codes).
55
56 toCodes(Number,Codes) :- number(Number),!,number_codes(Number,Codes).
57 toCodes(Atom,Codes) :- atomic(Atom),!,atom_codes(Atom,Codes).
58 toCodes(Term,Codes) :- write_to_codes(Term,Codes).
59
60 % a copy of safe_atom_codes/2 from tools to avoid module dependency on error_manager
61 safe_atom_codes(V,C) :- var(V),var(C),!,
62 print_error('Variables in call: '),print_error(safe_atom_codes(V,C)),
63 C='$VARIABLE$'.
64 safe_atom_codes(A,C) :-
65 on_exception(error(representation_error(max_atom_length),_),atom_codes(A,C),
66 (print(exception(max_atom_length)),nl,atom_codes_with_limit(A,1000,C))).
67
68 % will concatenate until the Limit is reached or exceeded; it may produce atoms longer than Limit
69 % (if first atom already longer than limit + it adds ...
70 %:- assert_must_succeed((tools: ajoin_with_limit(['A','B','C','D'],100,Text), Text == 'ABCD')).
71 %:- assert_must_succeed((tools: ajoin_with_limit(['A','B','C','D'],2,Text), Text == 'AB...')).
72
73
74 ajoin_with_limit(Atoms,Limit,Result) :-
75 ajoin_codes_with_limit(Atoms,Limit,Codes),
76 safe_atom_codes(Atom,Codes), Result=Atom.
77
78 %:- assert_must_succeed((tools: ajoin_codes_with_limit(['A','B','C','D'],100,Text), Text == "ABCD")).
79 ajoin_codes_with_limit([],_,[]).
80 ajoin_codes_with_limit([Atom|TAtoms],Limit,Res) :-
81 toCodes(Atom,AtomCodes),
82 add_codes(AtomCodes,TAtoms,Limit,Res).
83
84 add_codes([],TAtoms,Limit,Res) :- !, ajoin_codes_with_limit(TAtoms,Limit,Res).
85 add_codes(_,_,Limit,Res) :- Limit < 1, !, Res = "...".
86 add_codes([H|T],TAtoms,Limit,[H|TR]) :- L1 is Limit-1,
87 add_codes(T,TAtoms,L1,TR).
88
89
90
91 :- use_module(tools_printing,[print_error/1]).
92 safe_name(X,N) :- atom(X),!, atom_codes(X,N).
93 safe_name(X,N) :- number(X),!, number_codes(X,N).
94 safe_name(X,N) :- var(X),!, N="var".
95 safe_name(lambda_res(X),[114,101,115,95|N]) :- !, atom_codes(X,N).
96 safe_name(X,N) :- functor(X,F,_),atom_codes(F,N), print_error(non_atomic_in_safe_name(X)).
97
98
99 predicate_functor(X,F,N) :- var(X),!, print_error(var_in_predicate_functor),F='$VAR',N=0.
100 predicate_functor(_Module:Pred,F,N) :- !,predicate_functor(Pred,F,N).
101 predicate_functor(P,F,N) :- functor(P,F,N).
102
103
104
105 atom_codes_with_limit(A,C) :-
106 on_exception(error(representation_error(max_atom_length),_),
107 atom_codes(A,C),
108 (print(exception(max_atom_length)),nl,atom_codes_with_limit(A,1000,C))).
109
110
111 atom_codes_with_limit(A,Limit,Codes) :- var(A), Limit >= 0, !,
112 truncate_codes(Codes,Limit,TCodes,_),
113 atom_codes(A,TCodes).
114 atom_codes_with_limit(A,Limit,Codes) :- Limit < 1, !, atom_codes(A,Codes).
115 atom_codes_with_limit(A,Limit,Codes) :- atom_codes(A,Codes1),
116 truncate_codes(Codes,Limit,Codes1,_).
117
118
119 truncate_codes([],_,[],false).
120 truncate_codes([H|T],Count,Res,Trunc) :-
121 Count<1 -> Res = [46,46,46],Trunc=true /* '...' */
122 ; Res = [H|TT], C1 is Count-1, truncate_codes(T,C1,TT,Trunc).
123
124 %:- assert_must_succeed((tools_strings:truncate_atom(abcd,100,Text), Text == 'abcd')).
125 %:- assert_must_succeed((tools_strings:truncate_atom(abcd,2,Text), Text == 'ab...')).
126 %:- assert_must_succeed((tools_strings:truncate_atom(abcd,0,Text), Text == '...')).
127 % TO DO: could be made more efficient by using something like sub_atom(Atom,0,Limit,_,NewAtom)
128 truncate_atom(Atom,Limit,NewAtom) :-
129 atom_codes(Atom,Codes),
130 truncate_codes(Codes,Limit,TCodes,Trunc),
131 (Trunc=true -> atom_codes(NewAtom,TCodes) ; NewAtom=Atom).
132
133
134 convert_cli_arg(PrefVal,Value) :- compound(PrefVal),!,Value=PrefVal.
135 convert_cli_arg(Atom,Value) :-
136 convert_atom_to_number(Atom,Nr),!, /* convert '12' to 12 */
137 Value=Nr.
138 convert_cli_arg(V,V).
139
140 convert_to_number(Nr,Res) :- number(Nr),!,Res=Nr.
141 convert_to_number(Atom,Nr) :- convert_atom_to_number(Atom,Nr).
142
143 convert_atom_to_number(Atom,Nr) :-
144 atom(Atom), atom_codes(Atom,C),
145 on_exception(error(syntax_error(_N),_),number_codes(Nr,C),
146 fail). % in this case safe_number_codes fails ; we cannot convert the codes into a number