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