1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2003-2024, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(swi_option, 39 [ option/2, % +Term, +List 40 option/3, % +Term, +List, +Default 41 select_option/3, % +Term, +Options, -RestOpts 42 select_option/4, % +Term, +Options, -RestOpts, +Default 43 merge_options/3, % +New, +Old, -Merged 44 meta_options/3, % :IsMeta, :OptionsIn, -OptionsOut 45 dict_options/2 % ?Dict, ?Options 46 ]). 47:- autoload(library(lists), [selectchk/3]). 48:- autoload(library(error), [must_be/2, domain_error/2]). 49:- autoload(library(pairs), [map_list_to_pairs/3, pairs_values/2]). 50 51:- set_prolog_flag(generate_debug_info, false). 52 53:- meta_predicate 54 meta_options( , , ). 55 56/** <module> Option list processing 57 58The library(option) provides some utilities for processing option lists. 59Option lists are commonly used as an alternative for many arguments. 60Examples of built-in predicates are open/4 and write_term/3. Naming the 61arguments results in more readable code, and the list nature makes it 62easy to extend the list of options accepted by a predicate. Option lists 63come in two styles, both of which are handled by this library. 64 65 - Name(Value) <br> 66 This is the preferred style. 67 - Name = Value <br> 68 This is often used, but deprecated. 69 70SWI-Prolog _dicts_ provide a convenient and efficient alternative to 71option lists. For this reason, both built-in predicates and predicates 72that use this library support dicts transparantly. 73 74Processing option lists inside time-critical code (loops) can cause 75serious overhead. The above mentioned _dicts_ is the preferred 76mitigation. A more portable alternative is to define a record using 77library(record) and initialise this using make_<record>/2. In addition 78to providing good performance, this also provides type-checking and 79central declaration of defaults. 80 81Options typically have exactly one argument. The library does support 82options with 0 or more than one argument with the following 83restrictions: 84 85 - The predicate option/3 and select_option/4, involving default are 86 meaningless. They perform an arg(1, Option, Default), causing 87 failure without arguments and filling only the first option-argument 88 otherwise. 89 - meta_options/3 can only qualify options with exactly one argument. 90 91@see library(record) 92@see Option processing capabilities may be declared using the 93 directive predicate_options/3. 94*/ 95 96%! option(?Option, +Options) is semidet. 97% 98% Get an Option from Options. Fails silently if the option does not 99% appear in Options. If Option appears multiple times in Options, the 100% first value is used. 101% 102% @arg Option Term of the form Name(?Value). 103% @arg Options is a list of Name(Value) or `Name=Value` or a dict. 104 105option(Opt, Options), is_dict(Options) => 106 functor(Opt, Name, 1), 107 get_dict(Name, Options, Val), 108 arg(1, Opt, Val). 109option(Opt, Options), is_list(Options) => 110 functor(Opt, Name, Arity), 111 functor(GenOpt, Name, Arity), 112 get_option(GenOpt, Options), 113 !, 114 Opt = GenOpt. 115 116get_option(Opt, Options) :- 117 memberchk(Opt, Options), 118 !. 119get_option(Opt, Options) :- 120 functor(Opt, OptName, 1), 121 arg(1, Opt, OptVal), 122 memberchk(OptName=OptVal, Options), 123 !. 124 125 126%! option(?Option, +Options, +Default) is det. 127% 128% Get an Option from Options. If Option does not appear in Options, 129% unify the value with Default. If Option appears multiple times in 130% Options, the first value is used. For example 131% 132% ?- option(max_depth(D), [x(a), max_depth(20)], 10). 133% D = 20. 134% ?- option(max_depth(D), [x(a)], 10). 135% D = 10. 136% 137% @arg Option Term of the form Name(?Value). 138% @arg Options is a list of Name(Value) or `Name=Value` or a dict. 139 140option(Opt, Options, Default), is_dict(Options) => 141 functor(Opt, Name, 1), 142 ( get_dict(Name, Options, Val) 143 -> true 144 ; Val = Default 145 ), 146 arg(1, Opt, Val). 147option(Opt, Options, Default), is_list(Options) => 148 functor(Opt, Name, Arity), 149 functor(GenOpt, Name, Arity), 150 ( get_option(GenOpt, Options) 151 -> Opt = GenOpt 152 ; arg(1, Opt, Default) 153 ). 154 155%! select_option(?Option, +Options, -RestOptions) is semidet. 156% 157% Get and remove Option from Options. As option/2, removing the 158% matching option from Options and unifying the remaining options with 159% RestOptions. If Option appears multiple times in Options, the first 160% value is used. Note that if Options contains multiple terms that are 161% compatible to Option, the first is used to set the value of Option 162% and the duplicate appear in RestOptions. 163 164select_option(Opt, Options0, Options), is_dict(Options0) => 165 functor(Opt, Name, 1), 166 get_dict(Name, Options0, Val), 167 arg(1, Opt, Val), 168 del_dict(Name, Options0, Val, Options). 169select_option(Opt, Options0, Options), is_list(Options0) => 170 functor(Opt, Name, Arity), 171 functor(GenOpt, Name, Arity), 172 get_option(GenOpt, Options0, Options), 173 Opt = GenOpt. 174 175get_option(Opt, Options0, Options) :- 176 selectchk(Opt, Options0, Options), 177 !. 178get_option(Opt, Options0, Options) :- 179 functor(Opt, OptName, 1), 180 arg(1, Opt, OptVal), 181 selectchk(OptName=OptVal, Options0, Options). 182 183%! select_option(?Option, +Options, -RestOptions, +Default) is det. 184% 185% Get and remove Option with default value. As select_option/3, 186% but if Option is not in Options, its value is unified with 187% Default and RestOptions with Options. 188 189select_option(Option, Options, RestOptions, Default), is_dict(Options) => 190 functor(Option, Name, 1), 191 ( del_dict(Name, Options, Val, RestOptions) 192 -> true 193 ; Val = Default, 194 RestOptions = Options 195 ), 196 arg(1, Option, Val). 197select_option(Option, Options, RestOptions, Default), is_list(Options) => 198 functor(Option, Name, Arity), 199 functor(GenOpt, Name, Arity), 200 ( get_option(GenOpt, Options, RestOptions) 201 -> Option = GenOpt 202 ; RestOptions = Options, 203 arg(1, Option, Default) 204 ). 205 206 207%! merge_options(+New, +Old, -Merged) is det. 208% 209% Merge two option sets. If Old is a dict, Merged is a dict. Otherwise 210% Merged is a sorted list of options using the canonical format 211% Name(Value) holding all options from New and Old, after removing 212% conflicting options from Old. 213% 214% Multi-values options (e.g., proxy(Host, Port)) are allowed, where 215% both option-name and arity define the identity of the option. 216 217merge_options(NewDict, OldDict, Dict), 218 is_dict(NewDict), is_dict(OldDict) => 219 put_dict(NewDict, OldDict, Dict). 220merge_options(New, OldDict, Dict), 221 is_dict(OldDict) => 222 dict_options(NewDict, New), 223 put_dict(NewDict, OldDict, Dict). 224merge_options(NewDict, OldList, List), 225 is_dict(NewDict) => 226 dict_options(NewDict, NewList), 227 merge_option_lists(NewList, OldList, List). 228merge_options(NewList, OldList, List), 229 is_list(NewList), is_list(OldList) => 230 merge_option_lists(NewList, OldList, List). 231 232merge_option_lists([], Old, Merged) :- 233 !, 234 canonicalise_options(Old, Merged). 235merge_option_lists(New, [], Merged) :- 236 !, 237 canonicalise_options(New, Merged). 238merge_option_lists(New, Old, Merged) :- 239 canonicalise_options(New, NCanonical), 240 canonicalise_options(Old, OCanonical), 241 sort(NCanonical, NSorted), 242 sort(OCanonical, OSorted), 243 ord_merge(NSorted, OSorted, Merged). 244 245ord_merge([], L, L) :- !. 246ord_merge(L, [], L) :- !. 247ord_merge([NO|TN], [OO|TO], Merged) :- 248 sort_key(NO, NKey), 249 sort_key(OO, OKey), 250 compare(Diff, NKey, OKey), 251 ord_merge(Diff, NO, NKey, OO, OKey, TN, TO, Merged). 252 253ord_merge(=, NO, _, _, _, TN, TO, [NO|T]) :- 254 ord_merge(TN, TO, T). 255ord_merge(<, NO, _, OO, OKey, TN, TO, [NO|T]) :- 256 ( TN = [H|TN2] 257 -> sort_key(H, NKey), 258 compare(Diff, NKey, OKey), 259 ord_merge(Diff, H, NKey, OO, OKey, TN2, TO, T) 260 ; T = [OO|TO] 261 ). 262ord_merge(>, NO, NKey, OO, _, TN, TO, [OO|T]) :- 263 ( TO = [H|TO2] 264 -> sort_key(H, OKey), 265 compare(Diff, NKey, OKey), 266 ord_merge(Diff, NO, NKey, H, OKey, TN, TO2, T) 267 ; T = [NO|TN] 268 ). 269 270sort_key(Option, Name-Arity) :- 271 functor(Option, Name, Arity). 272 273%! canonicalise_options(+OptionsIn, -OptionsOut) is det. 274% 275% Rewrite option list from possible Name=Value to Name(Value) 276 277canonicalise_options(Dict, Out) :- 278 is_dict(Dict), 279 !, 280 dict_pairs(Dict, _, Pairs), 281 canonicalise_options2(Pairs, Out). 282canonicalise_options(In, Out) :- 283 memberchk(_=_, In), % speedup a bit if already ok. 284 !, 285 canonicalise_options2(In, Out). 286canonicalise_options(Options, Options). 287 288canonicalise_options2([], []). 289canonicalise_options2([H0|T0], [H|T]) :- 290 canonicalise_option(H0, H), 291 canonicalise_options2(T0, T). 292 293canonicalise_option(Name=Value, H) :- 294 !, 295 H =.. [Name,Value]. 296canonicalise_option(Name-Value, H) :- 297 !, 298 H =.. [Name,Value]. 299canonicalise_option(H, H). 300 301 302%! meta_options(+IsMeta, :Options0, -Options) is det. 303% 304% Perform meta-expansion on options that are module-sensitive. 305% Whether an option name is module-sensitive is determined by 306% calling call(IsMeta, Name). Here is an example: 307% 308% ``` 309% meta_options(is_meta, OptionsIn, Options), 310% ... 311% 312% is_meta(callback). 313% ``` 314% 315% Meta-options must have exactly one argument. This argument will 316% be qualified. 317% 318% @tbd Should be integrated with declarations from 319% predicate_options/3. 320 321meta_options(IsMeta, Context:Options0, Options), is_dict(Options0) => 322 dict_pairs(Options0, Class, Pairs0), 323 meta_options(Pairs0, IsMeta, Context, Pairs), 324 dict_pairs(Options, Class, Pairs). 325meta_options(IsMeta, Context:Options0, Options), is_list(Options0) => 326 must_be(list, Options0), 327 meta_options(Options0, IsMeta, Context, Options). 328 329meta_options([], _, _, []). 330meta_options([H0|T0], IM, Context, [H|T]) :- 331 meta_option(H0, IM, Context, H), 332 meta_options(T0, IM, Context, T). 333 334meta_option(Name=V0, IM, Context, Name=(M:V)) :- 335 call(IM, Name), 336 !, 337 strip_module(Context:V0, M, V). 338meta_option(Name-V0, IM, Context, Name-(M:V)) :- 339 call(IM, Name), 340 !, 341 strip_module(Context:V0, M, V). 342meta_option(O0, IM, Context, O) :- 343 compound(O0), 344 O0 =.. [Name,V0], 345 call(IM, Name), 346 !, 347 strip_module(Context:V0, M, V), 348 O =.. [Name,M:V]. 349meta_option(O, _, _, O). 350 351%! dict_options(?Dict, ?Options) is det. 352% 353% Convert between an option list and a dictionary. One of the 354% arguments must be instantiated. If the option list is created, 355% it is created in canonical form, i.e., using Option(Value) with 356% the Options sorted in the standard order of terms. Note that the 357% conversion is not always possible due to different constraints 358% and conversion may thus lead to (type) errors. 359% 360% - Dict keys can be integers. This is not allowed in canonical 361% option lists. 362% - Options can hold multiple options with the same key. This is 363% not allowed in dicts. This predicate removes all but the 364% first option on the same key. 365% - Options can have more than one value (name(V1,V2)). This is 366% not allowed in dicts. 367% 368% Also note that most system predicates and predicates using this 369% library for processing the option argument can both work with 370% classical Prolog options and dicts objects. 371 372dict_options(Dict, Options) :- 373 nonvar(Dict), 374 !, 375 dict_pairs(Dict, _, Pairs), 376 canonicalise_options2(Pairs, Options). 377dict_options(Dict, Options) :- 378 canonicalise_options(Options, Options1), 379 map_list_to_pairs(key_name, Options1, Keyed), 380 sort(1, @<, Keyed, UniqueKeyed), 381 pairs_values(UniqueKeyed, Unique), 382 dict_create(Dict, _, Unique). 383 384key_name(Opt, Key) :- 385 functor(Opt, Key, 1), 386 !. 387key_name(Opt, _) :- 388 domain_error(option, Opt)