%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Prolog % % Shortcut generator v1.0 % % This program is known to work with SWI-Prolog version 2.9.5, which you % can find at % % http://www.swi.psy.uva.nl/usr/jan/SWI-Prolog.html % % Purpose: to generate shortcuts for labels in menus and dialogs that are % guaranteed to be unique within a set of labels. The shortcuts are % generated in a prioritized manner, such that characters at the beginning % of words are preferred to characters in the middle of words. % % You might find this progam useful if you are translating LyX. % % Don't use this file in the development/tools-directory; copy it % somewhere out of the LyX-distribution tree, unless you're improving % the tool itself. The input is to be hard-coded into the file and that % may cause unnecessary garbage to appear in any patch you're putting % together. % % The labels are supposed to be entered at "LABELS" below, % and you get one solution (hopefully) with "go.". % If there are no solutions, the Prolog-interpretator will reply with a % "No." % % You can get all candidate solutions with "all.", but this requires % lots of memory. % % You can inspect the correspondance between the original strings and % the prioritized ones with "inspect.". % % I have no idea what the big O for the algorithm is, but I suspect it's % rather steep. % Asger speculates that the algorithm is O(n^m), where n is the cardinality % of the candidate sets, while m is the number of sets. Since we do an % exhausitive search, this has to be the case. % Predicates: % print out one solution, i.e. a set of label/shortcut's % writef/2 may be specific for SWI-prolog show_one_alternative([]). show_one_alternative([L/_/C|Rest]):- writef('%s|#%n\n',[L,C]), show_one_alternative(Rest). % printout predicate for "all." show_solutions([]). show_solutions([H|T]):- write('-----------\n'), show_one_alternative(H), show_solutions(T). % print out correspondance between original strings and prioritized ones. show_priority([]). show_priority([L/P/_|Rest]):- writef('%s|%n\n',[L,P]), show_priority(Rest). % character is from a set of allowed characters. % "AZaz09"=[65, 90, 97, 122, 48, 57] allowed_char(C):- C >= 97, C =< 122. % a-z allowed_char(C):- C >= 65, C =< 90. % A-Z allowed_char(C):- C >= 48, C =< 57. % 0-9 % turn lowercase to uppercase; alt- is case insensitive uppercase(L,U):- (L >= 97, L =< 122) -> U is L - 32; U is L. % possible_char/2: Gets all characters in label, one after one. possible_char(_/Label,Char):- member(Char,Label). % the character is part of the label % prepare_labels/2: Prepares all labels. Constructs a new list of pairs % where the original string is coupled with the prepared string. prepare_labels([], []). prepare_labels([H1|T1], [H1/H2|T2]):- prepare_string(H1, H2), prepare_labels(T1, T2). % prepare_string/2: Prepares a string by removing duplicate characters, % prioritizing initials letters, removing illegal characters and turning % lowercase to uppercase characters. prepare_string(Label,Result):- string_to_list(Label,List1), % make a list of the string prioritize(List1, List2), % Prioritize string filter_chars(List2, List3), % Filter out unwanted chars unique(List3, Result). % Remove duplicates % prioritize/2: This predicate rearranges a list, such that % chars at the beginning of words are put first in the list. % i.e. prioritize("Foo bar", "Fboo ar")" is true. prioritize(L1,L2):- initial_chars(L1, I), % Find all initial characters subtract(L1, I, Rest), % and the others append(I, Rest, L2). % and we have the result. % initial_chars/2: Returns a list of characters that appear at the beginning % of words. i.e. initial_chars("Foo bar", "Fb") is true. initial_chars([],[]). initial_chars([A|T1], [A|T3]):- rest_after_space(T1, T2), % Return rest of list after space initial_chars(T2, T3). % rest_after_space/2: Returns the list after the first space. % i.e. "rest_after_space("Foo bar", "bar") is true. rest_after_space([], []). rest_after_space([32, H1|T1], [H1|T1]):- !. rest_after_space([_|T1], T2):- rest_after_space(T1, T2). % filterchars/2: Filter outs non-allowed characters from list, and turns % lowercase to uppercase. filter_chars([], []). filter_chars([H|T1], [C|T2]):- allowed_char(H), !, uppercase(H, C), filter_chars(T1, T2). filter_chars([_|T1], T2):- filter_chars(T1, T2). % unique/2: This predicate removes duplicate characters without reordering. % i.e. unique("Foo bar", "Fo bar") is true. unique([], []). unique([H|T1], [H|T3]):- delete(T1, H, T2), % Remove duplicates. unique(T2,T3). % working_shortcuts/2 instantiates Char in the first argument for a whole % menu/dialog. working_shortcuts([],_). % end of the list working_shortcuts([Label/Char|Rest],Used):- possible_char(Label,Char), % i.e. part of the label string \+member(Char,Used), % not used by any other shortcut working_shortcuts(Rest,[Char|Used]). % and all the other labels have % shortcuts % The prolog motor will backtrack up and down the list of labels % until it finds a set with unique characters for each label % Label strings goes here. % Just cut&paste the strings from the LyX-source, and remove any % control sequences for menus. We could add a couple of predicates to scan % a file, but re-consulting this file takes only a fraction of a second % so I didn't bother to add a fancy user interface here. The solution is % printed like