View source with raw comments or as raw
   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)  2007-2016, University of Amsterdam
   7                              VU University Amsterdam
   8    All rights reserved.
   9
  10    Redistribution and use in source and binary forms, with or without
  11    modification, are permitted provided that the following conditions
  12    are met:
  13
  14    1. Redistributions of source code must retain the above copyright
  15       notice, this list of conditions and the following disclaimer.
  16
  17    2. Redistributions in binary form must reproduce the above copyright
  18       notice, this list of conditions and the following disclaimer in
  19       the documentation and/or other materials provided with the
  20       distribution.
  21
  22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33    POSSIBILITY OF SUCH DAMAGE.
  34*/
  35
  36:- module(settings,
  37          [ setting/4,                  % :Name, +Type, +Default, +Comment
  38            setting/2,                  % :Name, ?Value
  39            set_setting/2,              % :Name, +Value
  40            set_setting_default/2,      % :Name, +Value
  41            restore_setting/1,          % :Name
  42            load_settings/1,            % +File
  43            load_settings/2,            % +File, +Options
  44            save_settings/0,
  45            save_settings/1,            % +File
  46            current_setting/1,          % Module:Name
  47            setting_property/2,         % ?Setting, ?Property
  48            list_settings/0,
  49            list_settings/1,            % +Module
  50
  51            convert_setting_text/3      % +Type, +Text, -Value
  52          ]).
  53:- use_module(library(error)).
  54:- use_module(library(broadcast)).
  55:- use_module(library(debug)).
  56:- use_module(library(option)).
  57:- use_module(library(arithmetic)).
  58:- set_prolog_flag(generate_debug_info, false).
  59
  60/** <module> Setting management
  61
  62This library allows management  of   configuration  settings  for Prolog
  63applications. Applications define settings  in   one  or  multiple files
  64using the directive setting/4 as illustrated below:
  65
  66==
  67:- use_module(library(settings)).
  68
  69:- setting(version, atom,   '1.0', 'Current version').
  70:- setting(timeout, number,    20, 'Timeout in seconds').
  71==
  72
  73The directive is subject to   term_expansion/2,  which guarantees proper
  74synchronisation of the  database  if   source-files  are  reloaded. This
  75implies it is *not* possible to call setting/4 as a predicate.
  76
  77Settings are local to a  module.  This   implies  they  are defined in a
  78two-level namespace. Managing settings  per   module  greatly simplifies
  79assembling large applications from multiple   modules that configuration
  80through  settings.  This  settings  management  library  ensures  proper
  81access, loading and saving of settings.
  82
  83@see    library(config) distributed with XPCE provides an alternative
  84        aimed at graphical applications.
  85@author Jan Wielemaker
  86*/
  87
  88:- dynamic
  89    st_value/3,                     % Name, Module, Value
  90    st_default/3,                   % Name, Module, Value
  91    local_file/1.                   % Path
  92
  93:- multifile
  94    current_setting/6.              % Name, Module, Type, Default, Comment, Source
  95
  96:- meta_predicate
  97    setting(:, +, +, +),
  98    setting(:, ?),
  99    set_setting(:, +),
 100    set_setting_default(:, +),
 101    current_setting(:),
 102    restore_setting(:).
 103
 104:- predicate_options(load_settings/2, 2, [undefined(oneof([load,error]))]).
 105
 106curr_setting(Name, Module, Type, Default, Comment, Src) :-
 107    current_setting(Name, Module, Type, Default0, Comment, Src),
 108    (   st_default(Name, Module, Default1)
 109    ->  Default = Default1
 110    ;   Default = Default0
 111    ).
 112
 113%!  setting(:Name, +Type, +Default, +Comment) is det.
 114%
 115%   Define a  setting.  Name denotes  the name of the  setting, Type
 116%   its type.  Default is the  value before it is  modified. Default
 117%   can  refer  to  environment  variables and  can  use  arithmetic
 118%   expressions as defined by eval_default/4.
 119%
 120%   If a second declaration for  a   setting  is  encountered, it is
 121%   ignored  if  Type  and  Default  are    the  same.  Otherwise  a
 122%   permission_error is raised.
 123%
 124%   @param Name     Name of the setting (an atom)
 125%   @param Type     Type for setting.  One of =any= or a type defined
 126%                   by must_be/2.
 127%   @param Default  Default value for the setting.
 128%   @param Comment  Atom containing a (short) descriptive note.
 129
 130
 131setting(Name, Type, Default, Comment) :-
 132    throw(error(context_error(nodirective,
 133                              setting(Name, Type, Default, Comment)),
 134                _)).
 135
 136:- multifile
 137    system:term_expansion/2.
 138
 139system:term_expansion((:- setting(QName, Type, Default, Comment)),
 140                    Expanded) :-
 141    \+ current_prolog_flag(xref, true),
 142    prolog_load_context(module, M0),
 143    strip_module(M0:QName, Module, Name),
 144    must_be(atom, Name),
 145    to_atom(Comment, CommentAtom),
 146    eval_default(Default, Module, Type, Value),
 147    check_type(Type, Value),
 148    source_location(File, Line),
 149    (   current_setting(Name, Module, OType, ODef, _, OldLoc),
 150        (   OType \=@= Type
 151        ;    ODef \=@= Default
 152        ),
 153        OldLoc \= (File:_)
 154    ->  format(string(Message),
 155               'Already defined at: ~w', [OldLoc]),
 156        throw(error(permission_error(redefine, setting, Module:Name),
 157                    context(Message, _)))
 158    ;   Expanded = settings:current_setting(Name, Module, Type, Default,
 159                                            CommentAtom, File:Line)
 160    ).
 161
 162to_atom(Atom, Atom) :-
 163    atom(Atom),
 164    !.
 165to_atom(String, Atom) :-
 166    format(atom(Atom), '~s', String).
 167
 168%!  setting(:Name, ?Value) is nondet.
 169%
 170%   True when Name is a currently   defined setting with Value. Note
 171%   that setting(Name, Value) only enumerates   the  settings of the
 172%   current  module.  All  settings   can    be   enumerated   using
 173%   setting(Module:Name, Value). This predicate is  =det= if Name is
 174%   ground.
 175%
 176%   @error  existence_error(setting, Name)
 177
 178setting(Module:Name, Value) :-
 179    (   nonvar(Name), nonvar(Module)
 180    ->  (   st_value(Name, Module, Value0)
 181        ->  Value = Value0
 182        ;   curr_setting(Name, Module, Type, Default, _, _)
 183        ->  eval_default(Default, Module, Type, Value)
 184        ;   existence_error(setting, Module:Name)
 185        )
 186    ;   current_setting(Name, Module, _, _, _, _),
 187        setting(Module:Name, Value)
 188    ).
 189
 190
 191:- dynamic
 192    setting_cache/3.
 193:- volatile
 194    setting_cache/3.
 195
 196%!  clear_setting_cache is det.
 197%
 198%   Clear the cache for evaluation of default values.
 199
 200clear_setting_cache :-
 201    retractall(setting_cache(_,_,_)).
 202
 203%!  eval_default(+Default, +Module, +Type, -Value) is det.
 204%
 205%   Convert the settings default value. The notation allows for some
 206%   `function-style' notations to make the library more generic:
 207%
 208%           * env(Name)
 209%           Get value from the given environment variable. The value
 210%           is handed to convert_setting_text/3 to convert the
 211%           textual representation into a Prolog term.  Raises an
 212%           existence_error of the variable is not defined.
 213%
 214%           * env(Name, Default)
 215%           As env(Name), but uses the value Default if the variable
 216%           is not defined.
 217%
 218%           * setting(Name)
 219%           Ask the value of another setting.
 220%
 221%           * Expression
 222%           If Type is numeric, evaluate the expression.  env(Var)
 223%           evaluates to the value of an environment variable.
 224%           If Type is =atom=, concatenate A+B+....  Elements of the
 225%           expression can be env(Name).
 226
 227:- multifile
 228    eval_default/3.                 % +Default, +Type, -Value
 229
 230eval_default(Default, _, _Type, Value) :-
 231    var(Default),
 232    !,
 233    Value = Default.
 234eval_default(Default, _, Type, Value) :-
 235    eval_default(Default, Type, Val),
 236    !,
 237    Value = Val.
 238eval_default(Default, _, _, Value) :-
 239    atomic(Default),
 240    !,
 241    Value = Default.
 242eval_default(Default, _, Type, Value) :-
 243    setting_cache(Default, Type, Val),
 244    !,
 245    Value = Val.
 246eval_default(env(Name), _, Type, Value) :-
 247    !,
 248    (   getenv(Name, TextValue)
 249    ->  convert_setting_text(Type, TextValue, Val),
 250        assert(setting_cache(env(Name), Type, Val)),
 251        Value = Val
 252    ;   existence_error(environment_variable, Name)
 253    ).
 254eval_default(env(Name, Default), _, Type, Value) :-
 255    !,
 256    (   getenv(Name, TextValue)
 257    ->  convert_setting_text(Type, TextValue, Val)
 258    ;   Val = Default
 259    ),
 260    assert(setting_cache(env(Name), Type, Val)),
 261    Value = Val.
 262eval_default(setting(Name), Module, Type, Value) :-
 263    !,
 264    strip_module(Module:Name, M, N),
 265    setting(M:N, Value),
 266    must_be(Type, Value).
 267eval_default(Expr, _, Type, Value) :-
 268    numeric_type(Type, Basic),
 269    !,
 270    arithmetic_expression_value(Expr, Val0),
 271    (   Basic == float
 272    ->  Val is float(Val0)
 273    ;   Basic = integer
 274    ->  Val is round(Val0)
 275    ;   Val = Val0
 276    ),
 277    assert(setting_cache(Expr, Type, Val)),
 278    Value = Val.
 279eval_default(A+B, Module, atom, Value) :-
 280    !,
 281    phrase(expr_to_list(A+B, Module), L),
 282    atomic_list_concat(L, Val),
 283    assert(setting_cache(A+B, atom, Val)),
 284    Value = Val.
 285eval_default(List, Module, list(Type), Value) :-
 286    !,
 287    eval_list_default(List, Module, Type, Val),
 288    assert(setting_cache(List, list(Type), Val)),
 289    Value = Val.
 290eval_default(Default, _, _, Default).
 291
 292
 293%!  eval_list_default(+List, +Module, +ElementType, -DefaultList)
 294%
 295%   Evaluate the default for a list of values.
 296
 297eval_list_default([], _, _, []).
 298eval_list_default([H0|T0], Module, Type, [H|T]) :-
 299    eval_default(H0, Module, Type, H),
 300    eval_list_default(T0, Module, Type, T).
 301
 302%!  expr_to_list(+Expression, +Module)// is det.
 303%
 304%   Process the components to create an  atom. Atom concatenation is
 305%   expressed as A+B. Components may refer to envrionment variables.
 306
 307expr_to_list(A+B, Module) -->
 308    !,
 309    expr_to_list(A, Module),
 310    expr_to_list(B, Module).
 311expr_to_list(env(Name), _) -->
 312    !,
 313    (   { getenv(Name, Text) }
 314    ->  [Text]
 315    ;   { existence_error(environment_variable, Name) }
 316    ).
 317expr_to_list(env(Name, Default), _) -->
 318    !,
 319    (   { getenv(Name, Text) }
 320    ->  [Text]
 321    ;   [Default]
 322    ).
 323expr_to_list(setting(Name), Module) -->
 324    !,
 325    { strip_module(Module:Name, M, N),
 326      setting(M:N, Value)
 327    },
 328    [ Value ].
 329expr_to_list(A, _) -->
 330    [A].
 331
 332%!  env(+Name:atom, -Value:number) is det.
 333%!  env(+Name:atom, +Default:number, -Value:number) is det
 334%
 335%   Evaluate  environment  variables   on    behalf   of  arithmetic
 336%   expressions.
 337
 338:- arithmetic_function(env/1).
 339:- arithmetic_function(env/2).
 340
 341env(Name, Value) :-
 342    (   getenv(Name, Text)
 343    ->  convert_setting_text(number, Text, Value)
 344    ;   existence_error(environment_variable, Name)
 345    ).
 346env(Name, Default, Value) :-
 347    (   getenv(Name, Text)
 348    ->  convert_setting_text(number, Text, Value)
 349    ;   Value = Default
 350    ).
 351
 352
 353%!  numeric_type(+Type, -BaseType)
 354%
 355%   True if Type is a numeric type   and  BaseType is the associated
 356%   basic Prolog type. BaseType is  one   of  =integer=,  =float= or
 357%   =number=.
 358
 359numeric_type(integer, integer).
 360numeric_type(nonneg, integer).
 361numeric_type(float, float).
 362numeric_type(between(L,_), Type) :-
 363    ( integer(L) -> Type = integer ; Type = float ).
 364
 365
 366%!  set_setting(:Name, +Value) is det.
 367%
 368%   Change a setting. Performs existence   and type-checking for the
 369%   setting. If the effective value  of   the  setting is changed it
 370%   broadcasts the event below.
 371%
 372%           settings(changed(Module:Name, Old, New))
 373%
 374%   @error  existence_error(setting, Name)
 375%   @error  type_error(Type, Value)
 376
 377set_setting(QName, Value) :-
 378    strip_module(QName, Module, Name),
 379    must_be(atom, Name),
 380    (   curr_setting(Name, Module, Type, Default0, _Comment, _Src),
 381        eval_default(Default0, Module, Type, Default)
 382    ->  setting(Module:Name, Old),
 383        (   Value == Default
 384        ->  retract_setting(Module:Name)
 385        ;   st_value(Name, Module, Value)
 386        ->  true
 387        ;   check_type(Type, Value)
 388        ->  retract_setting(Module:Name),
 389            assert_setting(Module:Name, Value)
 390        ),
 391        (   Old == Value
 392        ->  true
 393        ;   broadcast(settings(changed(Module:Name, Old, Value))),
 394            clear_setting_cache     % might influence dependent settings
 395        )
 396    ;   existence_error(setting, Name)
 397    ).
 398
 399retract_setting(Module:Name) :-
 400    retractall(st_value(Name, Module, _)).
 401
 402assert_setting(Module:Name, Value) :-
 403    assert(st_value(Name, Module, Value)).
 404
 405%!  restore_setting(:Name) is det.
 406%
 407%   Restore the value of setting Name   to  its default. Broadcast a
 408%   change like set_setting/2 if  the  current   value  is  not  the
 409%   default.
 410
 411restore_setting(QName) :-
 412    strip_module(QName, Module, Name),
 413    must_be(atom, Name),
 414    (   st_value(Name, Module, Old)
 415    ->  retract_setting(Module:Name),
 416        setting(Module:Name, Value),
 417        (   Old \== Value
 418        ->  broadcast(settings(changed(Module:Name, Old, Value)))
 419        ;   true
 420        )
 421    ;   true
 422    ).
 423
 424%!  set_setting_default(:Name, +Default) is det.
 425%
 426%   Change the default for a setting.  The   effect  is  the same as
 427%   set_setting/2, but the new value is  considered the default when
 428%   saving and restoring  a  setting.  It   is  intended  to  change
 429%   application defaults in a particular context.
 430
 431set_setting_default(QName, Default) :-
 432    strip_module(QName, Module, Name),
 433    must_be(atom, Name),
 434    (   current_setting(Name, Module, Type, Default0, _Comment, _Src)
 435    ->  retractall(settings:st_default(Name, Module, _)),
 436        retract_setting(Module:Name),
 437        (   Default == Default0
 438        ->  true
 439        ;   assert(settings:st_default(Name, Module, Default))
 440        ),
 441        eval_default(Default, Module, Type, Value),
 442        set_setting(Module:Name, Value)
 443    ;   existence_error(setting, Module:Name)
 444    ).
 445
 446
 447                 /*******************************
 448                 *             TYPES            *
 449                 *******************************/
 450
 451%!  check_type(+Type, +Term)
 452%
 453%   Type  checking  for  settings.  Currently  simply  forwarded  to
 454%   must_be/2.
 455
 456check_type(Type, Term) :-
 457    must_be(Type, Term).
 458
 459
 460                 /*******************************
 461                 *             FILE             *
 462                 *******************************/
 463
 464%!  load_settings(File) is det.
 465%!  load_settings(File, +Options) is det.
 466%
 467%   Load local settings from File. Succeeds  if File does not exist,
 468%   setting the default save-file to File.  Options are:
 469%
 470%     * undefined(+Action)
 471%     Define how to handle settings that are not defined.  When
 472%     =error=, an error is printed and the setting is ignored.
 473%     when =load=, the setting is loaded anyway, waiting for a
 474%     definition.
 475
 476load_settings(File) :-
 477    load_settings(File, []).
 478
 479load_settings(File, Options) :-
 480    absolute_file_name(File, Path,
 481                       [ access(read),
 482                         file_errors(fail)
 483                       ]),
 484    !,
 485    assert(local_file(Path)),
 486    open(Path, read, In, [encoding(utf8)]),
 487    read_setting(In, T0),
 488    call_cleanup(load_settings(T0, In, Options), close(In)),
 489    clear_setting_cache.
 490load_settings(File, _) :-
 491    absolute_file_name(File, Path,
 492                       [ access(write),
 493                         file_errors(fail)
 494                       ]),
 495    !,
 496    assert(local_file(Path)).
 497load_settings(_, _).
 498
 499load_settings(end_of_file, _, _) :- !.
 500load_settings(Setting, In, Options) :-
 501    catch(store_setting(Setting, Options), E,
 502          print_message(warning, E)),
 503    read_setting(In, Next),
 504    load_settings(Next, In, Options).
 505
 506read_setting(In, Term) :-
 507    read_term(In, Term,
 508              [ syntax_errors(dec10)
 509              ]).
 510
 511%!  store_setting(Term, +Options)
 512%
 513%   Store setting loaded from file in the Prolog database.
 514
 515store_setting(setting(Module:Name, Value), _) :-
 516    curr_setting(Name, Module, Type, Default0, _Commentm, _Src),
 517    !,
 518    eval_default(Default0, Module, Type, Default),
 519    (   Value == Default
 520    ->  true
 521    ;   check_type(Type, Value)
 522    ->  retractall(st_value(Name, Module, _)),
 523        assert(st_value(Name, Module, Value)),
 524        broadcast(settings(changed(Module:Name, Default, Value)))
 525    ).
 526store_setting(setting(Module:Name, Value), Options) :-
 527    !,
 528    (   option(undefined(load), Options, load)
 529    ->  retractall(st_value(Name, Module, _)),
 530        assert(st_value(Name, Module, Value))
 531    ;   existence_error(setting, Module:Name)
 532    ).
 533store_setting(Term, _) :-
 534    type_error(setting, Term).
 535
 536%!  save_settings is det.
 537%!  save_settings(+File) is det.
 538%
 539%   Save modified settings to File.
 540
 541save_settings :-
 542    local_file(File),
 543    !,
 544    save_settings(File).
 545
 546save_settings(File) :-
 547    absolute_file_name(File, Path,
 548                       [ access(write)
 549                       ]),
 550    !,
 551    open(Path, write, Out,
 552         [ encoding(utf8),
 553           bom(true)
 554         ]),
 555    write_setting_header(Out),
 556    forall(current_setting(Name, Module, _, _, _, _),
 557           save_setting(Out, Module:Name)),
 558    close(Out).
 559
 560
 561write_setting_header(Out) :-
 562    get_time(Now),
 563    format_time(string(Date), '%+', Now),
 564    format(Out, '/*  Saved settings~n', []),
 565    format(Out, '    Date: ~w~n', [Date]),
 566    format(Out, '*/~n~n', []).
 567
 568save_setting(Out, Module:Name) :-
 569    curr_setting(Name, Module, Type, Default, Comment, _Src),
 570    (   st_value(Name, Module, Value),
 571        \+ ( eval_default(Default, Module, Type, DefValue),
 572             debug(setting, '~w <-> ~w~n', [DefValue, Value]),
 573             DefValue =@= Value
 574           )
 575    ->  format(Out, '~n%\t~w~n', [Comment]),
 576        format(Out, 'setting(~q:~q, ~q).~n', [Module, Name, Value])
 577    ;   true
 578    ).
 579
 580%!  current_setting(?Setting) is nondet.
 581%
 582%   True if Setting is a currently defined setting
 583
 584current_setting(Setting) :-
 585    ground(Setting),
 586    !,
 587    strip_module(Setting, Module, Name),
 588    current_setting(Name, Module, _, _, _, _).
 589current_setting(Module:Name) :-
 590    current_setting(Name, Module, _, _, _, _).
 591
 592%!  setting_property(+Setting, +Property) is det.
 593%!  setting_property(?Setting, ?Property) is nondet.
 594%
 595%   Query currently defined settings.  Property is one of
 596%
 597%           * comment(-Atom)
 598%           * type(-Type)
 599%           Type of the setting.
 600%           * default(-Default)
 601%           Default value.  If this is an expression, it is
 602%           evaluated.
 603%           * source(-File:-Line)
 604%           Location where the setting is defined.
 605
 606setting_property(Setting, Property) :-
 607    ground(Setting),
 608    !,
 609    Setting = Module:Name,
 610    curr_setting(Name, Module, Type, Default, Comment, Src),
 611    !,
 612    setting_property(Property, Module, Type, Default, Comment, Src).
 613setting_property(Setting, Property) :-
 614    Setting = Module:Name,
 615    curr_setting(Name, Module, Type, Default, Comment, Src),
 616    setting_property(Property, Module, Type, Default, Comment, Src).
 617
 618setting_property(type(Type), _, Type, _, _, _).
 619setting_property(default(Default), M, Type, Default0, _, _) :-
 620    eval_default(Default0, M, Type, Default).
 621setting_property(comment(Comment), _, _, _, Comment, _).
 622setting_property(source(Src), _, _, _, _, Src).
 623
 624%!  list_settings is det.
 625%!  list_settings(+Module) is det.
 626%
 627%   List settings to =current_output=. The   second  form only lists
 628%   settings on the matching module.
 629%
 630%   @tbd    Compute the required column widths
 631
 632list_settings :-
 633    list_settings(_).
 634
 635list_settings(Spec) :-
 636    spec_term(Spec, Term),
 637    TS1 = 25,
 638    TS2 = 40,
 639    format('~`=t~72|~n'),
 640    format('~w~t~*| ~w~w~t~*| ~w~n',
 641           ['Name', TS1, 'Value (*=modified)', '', TS2, 'Comment']),
 642    format('~`=t~72|~n'),
 643    forall(current_setting(Term),
 644           list_setting(Term, TS1, TS2)).
 645
 646spec_term(M:S, M:S) :- !.
 647spec_term(M, M:_).
 648
 649
 650list_setting(Module:Name, TS1, TS2) :-
 651    curr_setting(Name, Module, Type, Default0, Comment, _Src),
 652    eval_default(Default0, Module, Type, Default),
 653    setting(Module:Name, Value),
 654    (   Value \== Default
 655    ->  Modified = (*)
 656    ;   Modified = ''
 657    ),
 658    format('~w~t~*| ~q~w~t~*| ~w~n', [Module:Name, TS1, Value, Modified, TS2, Comment]).
 659
 660
 661                 /*******************************
 662                 *            TYPES             *
 663                 *******************************/
 664
 665%!  convert_setting_text(+Type, +Text, -Value)
 666%
 667%   Converts from textual form to  Prolog   Value.  Used  to convert
 668%   values obtained from the environment.  Public to provide support
 669%   in user-interfaces to this library.
 670%
 671%   @error  type_error(Type, Value)
 672
 673:- multifile
 674    convert_text/3.                 % +Type, +Text, -Value
 675
 676convert_setting_text(Type, Text, Value) :-
 677    convert_text(Type, Text, Value),
 678    !.
 679convert_setting_text(atom, Value, Value) :-
 680    !,
 681    must_be(atom, Value).
 682convert_setting_text(boolean, Value, Value) :-
 683    !,
 684    must_be(boolean, Value).
 685convert_setting_text(integer, Atom, Number) :-
 686    !,
 687    term_to_atom(Term, Atom),
 688    Number is round(Term).
 689convert_setting_text(float, Atom, Number) :-
 690    !,
 691    term_to_atom(Term, Atom),
 692    Number is float(Term).
 693convert_setting_text(between(L,U), Atom, Number) :-
 694    !,
 695    (   integer(L)
 696    ->  convert_setting_text(integer, Atom, Number)
 697    ;   convert_setting_text(float, Atom, Number)
 698    ),
 699    must_be(between(L,U), Number).
 700convert_setting_text(Type, Atom, Term) :-
 701    term_to_atom(Term, Atom),
 702    must_be(Type, Term).
 703
 704
 705                 /*******************************
 706                 *            SANDBOX           *
 707                 *******************************/
 708
 709:- multifile
 710    sandbox:safe_meta_predicate/1.
 711
 712sandbox:safe_meta_predicate(settings:setting/2).