1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    2% Bousi-Prolog directive evaluator
    3
    4:- module(directives, [
    5		directive/3,            % +Name, +Arguments, +Program, -Equations
    6		directive/2,            % +Name, +Arguments
    7		is_directive_valid/2    % +Name, +Arguments
    8   ]).    9
   10:- use_module(flags).   11:- use_module(utilities).   12%:- ensure_loaded(wn(wn_gen_prox_equations)).
   13%:- ensure_loaded(wn(wn_utilities)).
   14
   15:- use_module(library(lists)).   16
   17%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   18
   19:- set_prolog_flag(double_quotes, codes).   20
   21ensure_loaded_wn :-
   22  ensure_loaded(wn(wn_gen_prox_equations)),
   23  ensure_loaded(wn(wn_utilities)).
   24
   25%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   26% Predicate for executing a BPL directive
   27%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 directive(+Name, +Arguments)
Evaluates and executes (at run-time) a Bousi-Prolog directive with the given Name and list of Arguments. Basically, this predicate will alter the BPL flags defined in the 'flags' module.
   37directive(Name, Arguments) :-
   38  directive(Name, Arguments, _).
 directive(+Name, +Arguments, -Equations)
This call is performed at compile-time. Its third argument contains the new equations that will replace the directive in the compiled program
   47directive(lambda_cut, [Lambda], []) :-
   48	% Sets the lambda-cut value, i.e., the lower bound allowed
   49	% for the approximation degree of weak unifications
   50	flags:set_bpl_flag(lambda_cut(Lambda)).
   51
   52directive(filtering, [Boolean], []) :-
   53	% Sets the filtering behavior, i.e., if Boolean=true, then
   54  % apply filtering; otherwise, do not.
   55	flags:set_bpl_flag(filtering(Boolean)).
   56
   57directive(transitivity, [Type], []) :-
   58	% Sets properties for the relation used in weak unifications (by
   59	% default, this relation is always symmetric and reflexive)
   60	ClosureProperties = [symmetric, reflexive, transitive(Type)],
   61	flags:remove_bpl_flag(relation_properties(sim, _OldClosureProperties)),
   62	flags:add_bpl_flag(relation_properties(sim, ClosureProperties)).
   63
   64directive(fuzzy_rel, [RelName, ClosureProperties], []) :-
   65	% Sets closure properties for "most/less general than"
   66	% relations or user defined fuzzy relations
   67	flags:remove_bpl_flag(relation_properties(RelName, _OldClosureProperties)),
   68	flags:add_bpl_flag(relation_properties(RelName, ClosureProperties)).
   69
   70directive(domain, [Name, Min, Max, Unit], []) :-
   71	% Registers a new fuzzy domain
   72	flags:add_bpl_flag(fuzzy_domain(Name, [Min, Max, Unit])),
   73	flags:add_bpl_flag(fuzzy_subsets(Name, [])).
   74
   75directive(fuzzy_set, [DomainName, Subsets], []) :-
   76	% Registers a new fuzzy set composed of one or more fuzzy subsets
   77	% (fuzzy subsets are added to the list of current fuzzy sets of
   78	% the specified domain)
   79	flags:get_bpl_flag(fuzzy_subsets(DomainName, CurrentSubsets)),
   80	append(CurrentSubsets, Subsets, NewSubsets),
   81	% Duplicates are removed before building the new list of subsets
   82	list_to_set(NewSubsets, NewSubsetsNoDuplicates),
   83	flags:remove_bpl_flag(fuzzy_subsets(DomainName, CurrentSubsets)),
   84	flags:add_bpl_flag(fuzzy_subsets(DomainName, NewSubsetsNoDuplicates)).
   85
   86directive(weak_unification, [Type], []) :-
   87	% Sets the algorithm used for weak unifications: either a1, or a2 or a3
   88	% (by default, it is 'a1' - Sessa's incomplete algorithm for proximity relations)
   89	flags:set_bpl_flag(weak_unification(Type)).
   90
   91directive(ext_block_equs, [Boolean], []) :-
   92	% Sets whether blocks are externally (true) or internally (false) computed 
   93	% for the weak unification algorithm 'a3'
   94	% (by default, it is true)
   95	flags:set_bpl_flag(ext_block_equs(Boolean)).
   96
   97directive(fuzzy_logic, [TNorm], []) :-
   98	% Sets the current t-norm for resolution.  
   99	% (by default, it is min)
  100	retractall(evaluator:t_norm_current_op(_, _, _)),
  101	clause(evaluator:t_norm_op(TNorm, L, R, D), Body),
  102	assert((evaluator:t_norm_current_op(L, R, D) :- Body)),
  103	flags:set_bpl_flag(fuzzy_logic(TNorm)).
  104
  105directive(wn_connect, [], []) :-
  106  ensure_loaded_wn.
  107
  108directive(wn_connect, [QFolder], []) :-
  109	% Loads the WordNet interface  
  110	% Folder is the location of WordNet 3.0 installation
  111	utilities:remove_quotes(QFolder,Folder),
  112	setenv('WNDB', Folder),
  113  ensure_loaded_wn.
  114
  115directive(wn_gen_prox_equations, [Measure, ListOfListOfWords], Equations) :-
  116	% Generate proximity equations between each pair of words in each 
  117	% list of ListOfListOfWords 
  118	is_list(ListOfListOfWords),
  119	!,
  120	wn_gen_prox_equations:wn_gen_prox_equations_list(ListOfListOfWords, Measure, Equations).
  121	
  122directive(wn_gen_prox_equations, [_Measure, _Auto], []).
  123	% Generate proximity equations between each pair of words in the program 
  124%	wn_gen_prox_equations:wn_auto_gen_prox_equations(Program, Equations).
  125	
  126	
  127% replaced_directive(?DirectiveName)
  128%
  129%     True if the directive DirectiveName is replaced during the compilation
  130%     For example, the directive wn_gen_prox_equations is replaced by all
  131%     the proximity equations that it automatically generates
  132
  133replaced_directive(wn_gen_prox_equations).
  134
  135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  136% Predicate for checking whether a BPL directive is correct or not
  137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 is_directive_valid(+Name, +Arguments)
Checks if Name is the name of a Bousi-Prolog directive and, in that case, checks also if its Arguments are correct. If Arguments are invalid, a 'directive_error' exception with an error message is thrown, whereas if Name doesn't represent a Bousi-Prolog directive, this predicate fails.
throws
- directive_error(Message) Name is the name of a Bousi-Prolog directive but its Arguments aren't valid.
  152%
  153% :- lambda_cut(Lambda)
  154%
  155
  156is_directive_valid(lambda_cut, [Lambda]) :-
  157	number(Lambda), Lambda >= 0, Lambda =< 1,
  158	% Directive is OK
  159	!.
  160
  161is_directive_valid(lambda_cut, [_Lambda]) :-
  162	% Lambda-cut value is invalid
  163	!,
  164	throw_invalid_argument_error(lambda_cut, 1,
  165		'Lambda-cut value must be a number in range [0.0, 1.0].').
  166
  167is_directive_valid(lambda_cut, Arguments) :-
  168	% Wrong number of arguments
  169	length(Arguments, Arity),
  170	throw_wrong_arity_error(lambda_cut, Arity, 1).
  171
  172%
  173% :- filtering(Boolean)
  174%
  175
  176is_directive_valid(filtering, [Boolean]) :-
  177	memberchk(Boolean,[true,false]),
  178	% Directive is OK
  179	!.
  180
  181is_directive_valid(filtering, [_Boolean]) :-
  182	% Filtering value is invalid
  183	!,
  184	throw_invalid_argument_error(filtering, 1,
  185		'Filtering value must be either \'true\' or \'false\'.').
  186
  187is_directive_valid(filtering, Arguments) :-
  188	% Wrong number of arguments
  189	length(Arguments, Arity),
  190	throw_wrong_arity_error(filtering, Arity, 1).
  191
  192%
  193% :- transitivity(Type)
  194%
  195
  196is_directive_valid(transitivity, [Type]) :-
  197	nonvar(Type),
  198	member(Type, [yes, no, min, product, luka, drastic, nilpotent, hamacher]),
  199	% Directive is OK
  200	!.
  201
  202is_directive_valid(transitivity, [_Type]) :-
  203	% Transitivity type is invalid
  204	!,
  205	throw_invalid_argument_error(transitivity, 1,
  206		'Transitivity type must be in domain [yes, no, min, product, luka].').
  207
  208is_directive_valid(transitivity, Arguments) :-
  209	% Wrong number of arguments
  210	length(Arguments, Arity),
  211	throw_wrong_arity_error(transitivity, Arity, 1).
  212
  213%
  214% :- weak_unification(Type)
  215%
  216
  217is_directive_valid(weak_unification, [Type]) :-
  218	nonvar(Type),
  219	member(Type, [a1, a2, a3]),
  220	% Directive is OK
  221	!.
  222
  223is_directive_valid(weak_unification, [_Type]) :-
  224	% Weak_unification type is invalid
  225	!,
  226	throw_invalid_argument_error(weak_unification, 1,
  227		'Weak unification algorithm must be in domain [a1, a2, a3].').
  228
  229is_directive_valid(weak_unification, Arguments) :-
  230	% Wrong number of arguments
  231	length(Arguments, Arity),
  232	throw_wrong_arity_error(weak_unification, Arity, 1).
  233
  234%
  235% :- ext_block_equs(Type)
  236%
  237
  238is_directive_valid(ext_block_equs, [Type]) :-
  239	nonvar(Type),
  240	member(Type, [true, false]),
  241	% Directive is OK
  242	!.
  243
  244is_directive_valid(ext_block_equs, [_Type]) :-
  245	% ext_block_equs type is invalid
  246	!,
  247	throw_invalid_argument_error(ext_block_equs, 1,
  248		'External block equations processing must be in domain [true, false].').
  249
  250is_directive_valid(ext_block_equs, Arguments) :-
  251	% Wrong number of arguments
  252	length(Arguments, Arity),
  253	throw_wrong_arity_error(ext_block_equs, Arity, 1).
  254
  255%
  256% :- fuzzy_logic(TNorm)
  257%
  258
  259is_directive_valid(fuzzy_logic, [TNorm]) :-
  260	nonvar(TNorm),
  261	member(TNorm, [min, product, luka, drastic, nilpotent, hamacher]),
  262	% Directive is OK
  263	!.
  264
  265is_directive_valid(fuzzy_logic, [_TNorm]) :-
  266	% fuzzy_logic t-norm is invalid
  267	!,
  268	throw_invalid_argument_error(fuzzy_logic, 1,
  269		'Fuzzy logic t-norm must be in domain [min, product, luka, drastic, nilpotent, hamacher].').
  270
  271is_directive_valid(fuzzy_logic, Arguments) :-
  272	% Wrong number of arguments
  273	length(Arguments, Arity),
  274	throw_wrong_arity_error(fuzzy_logic, Arity, 1).
  275
  276%
  277% :- fuzzy_rel(Relation, Properties)
  278%
  279
  280is_directive_valid(fuzzy_rel, [RelName, Properties]) :-
  281	nonvar(RelName), nonvar(Properties),
  282	RelName \== sim,
  283	utilities:relation_name(_RelSymbol, RelName),
  284	is_list(Properties),
  285	list_to_set(Properties, Properties),
  286	subset(Properties, [symmetric, reflexive, transitive, transitive(_)]),
  287	((RelName == lEqThan ; RelName == gEqThan) ->
  288		member(reflexive, Properties),
  289		(member(transitive, Properties) ; member(transitive(_), Properties))
  290	;
  291		true
  292	),
  293	(member(transitive(TNorm), Properties) ->
  294		member(TNorm, [yes, no, min, luka, product])
  295	;
  296		true
  297	),
  298	% Directive is OK
  299	!.
  300
  301is_directive_valid(fuzzy_rel, [sim, _Properties]) :-
  302	% Similarity relation properties can't be changed
  303	!,
  304	throw_invalid_argument_error(fuzzy_rel, 2,
  305		'Similarity relation properties can\'t be changed with this directive. \c
  306		 Use \'transitivity/1\' to add transitivity to similarity relation.').
  307
  308is_directive_valid(fuzzy_rel, [RelName, _Properties]) :-
  309	(RelName == lEqThan ; RelName == gEqThan),
  310	% "Less/Most general than" properties are partially fixed
  311	!,
  312	throw_invalid_argument_error(fuzzy_rel, 2,
  313		'"Less/Most general than" relations must be reflexive and transitive, \c
  314		 and transitive type must be in domain [yes, no, min, product, luka].').
  315
  316is_directive_valid(fuzzy_rel, [RelName, _Properties]) :-
  317	nonvar(RelName),
  318	utilities:relation_name(_RelSymbol, RelName),
  319	% Closure properties list is invalid
  320	!,
  321	throw_invalid_argument_error(fuzzy_rel, 2,
  322		'Closure properties must be a subset of {symmetric, reflexive, \c
  323		 transitive, transitive(Type)}, and transitive type must be in domain \c
  324		 [yes, no, min, product, luka].').
  325
  326is_directive_valid(fuzzy_rel, [_RelName, _Properties]) :-
  327	% Relation symbol is invalid
  328	!,
  329	throw_invalid_argument_error(fuzzy_rel, 2,
  330		'Relation symbol must be one of these: ~>, <~, ~1~, ~2~, ~3~.').
  331
  332is_directive_valid(fuzzy_rel, Arguments) :-
  333	% Wrong number of arguments
  334	length(Arguments, Arity),
  335	throw_wrong_arity_error(fuzzy_rel, Arity, 2).
  336
  337%
  338% :- domain(Name, Min, Max, Unit)
  339%
  340
  341is_directive_valid(domain, [Name, Min, Max, Unit]) :-
  342	atomic(Name), nonvar(Name), not(number(Name)),
  343	not(utilities:atom_is_variable(Name)),
  344	atomic(Unit), nonvar(Unit), not(number(Unit)),
  345	not(utilities:atom_is_variable(Unit)),
  346	integer(Min), integer(Max),
  347	Min < Max,
  348 	not(flags:get_bpl_flag(fuzzy_domain(Name, [_, _, _]))),
  349	% Directive is OK
  350	!.
  351
  352is_directive_valid(domain, [Name, Min, Max, Unit]) :-
  353	atomic(Name), nonvar(Name), not(number(Name)),
  354	not(utilities:atom_is_variable(Name)),
  355	atomic(Unit), nonvar(Unit), not(number(Unit)),
  356	not(utilities:atom_is_variable(Unit)),
  357	integer(Min), integer(Max),
  358	Min < Max,
  359	% Domain has already been defined
  360	!,
  361	swritef(Message, 'Fuzzy domain \'%w\' has already been defined.', [Name]),
  362	throw_invalid_argument_error(domain, 4, Message).
  363
  364is_directive_valid(domain, [Name, Min, Max, Unit]) :-
  365	atomic(Name), nonvar(Name), not(number(Name)),
  366	not(utilities:atom_is_variable(Name)),
  367	atomic(Unit), nonvar(Unit), not(number(Unit)),
  368	not(utilities:atom_is_variable(Unit)),
  369	integer(Min), integer(Max),
  370	% Minimum and maximum values are swapped
  371	!,
  372	throw_invalid_argument_error(domain, 4,
  373		'Minimum value must be greater than maximum value.').
  374
  375is_directive_valid(domain, [Name, _Min, _Max, Unit]) :-
  376	atomic(Name), nonvar(Name), not(number(Name)),
  377	not(utilities:atom_is_variable(Name)),
  378	atomic(Unit), nonvar(Unit), not(number(Unit)),
  379	not(utilities:atom_is_variable(Unit)),
  380	% Minimum and/or maximum values aren't integer numbers
  381	!,
  382	throw_invalid_argument_error(domain, 4,
  383		'Minimum and maximum values must be integers numbers.').
  384
  385is_directive_valid(domain, [Name, _Min, _Max, _Unit]) :-
  386	(compound(Name) ; var(Name) ; number(Name)
  387	 ; utilities:atom_is_variable(Name)),
  388	% Domain name is a number, a free variable or a compound term
  389	!,
  390	throw_invalid_argument_error(domain, 4,
  391		'Domain name must be an atom.').
  392
  393is_directive_valid(domain, [_Name, _Min, _Max, Unit]) :-
  394	(compound(Unit) ; var(Unit) ; number(Unit)
  395	 ; utilities:atom_is_variable(Unit)),
  396	% Domain measure unit is a number, a free variable or a compound term
  397	!,
  398	throw_invalid_argument_error(domain, 4,
  399		'Domain measure unit must be an atom.').
  400
  401is_directive_valid(domain, Arguments) :-
  402	% Wrong number of arguments
  403	length(Arguments, Arity),
  404	throw_wrong_arity_error(domain, Arity, 4).
  405
  406%
  407% :- fuzzy_set(DomainName, [SetName(V1, V2, V3, V4), SetName(V1, V2, V3), ...])
  408%
  409
  410is_directive_valid(fuzzy_set, [DomainName, Subsets]) :-
  411	atomic(DomainName), nonvar(DomainName), not(number(DomainName)),
  412	not(utilities:atom_is_variable(DomainName)),
  413	flags:get_bpl_flag(fuzzy_domain(DomainName, _)),
  414	findall(Name, flags:get_bpl_flag(fuzzy_domain(Name, [_Min, _Max, _Unit])), Domains),
  415	get_subset_names(Domains, CurrentSubsetNames),
  416	check_fuzzy_subsets(Subsets, CurrentSubsetNames),
  417	% Directive is OK
  418	!.
  419
  420is_directive_valid(fuzzy_set, [DomainName, _Subsets]) :-
  421	(compound(DomainName) ; var(DomainName) ; number(DomainName)
  422	 ; utilities:atom_is_variable(DomainName)),
  423	% Domain name is a number, a free variable or a compound term
  424	!,
  425	swritef(Message, 'Domain name must be an atom.', [DomainName]),
  426	throw_invalid_argument_error(fuzzy_set, 2, Message).
  427
  428is_directive_valid(fuzzy_set, [DomainName, _Subsets]) :-
  429	% Domain hasn't been defined yet
  430	!,
  431	swritef(Message, 'Fuzzy domain \'%w\' is undefined.', [DomainName]),
  432	throw_invalid_argument_error(fuzzy_set, 2, Message).
  433
  434is_directive_valid(fuzzy_set, Arguments) :-
  435	% Wrong number of arguments
  436	length(Arguments, Arity),
  437	throw_wrong_arity_error(fuzzy_set, Arity, 2).
  438
  439is_directive_valid(wn_connect, []) :-
  440	(getenv('WNDB',_Folder)
  441	 ->
  442	  true
  443	 ;
  444	 	utilities:checkwnenv(Folder),
  445	  (exists_directory(Folder)
  446	    ->
  447	     true
  448	   ; 
  449  	  % The WNDB variable is not set
  450    	throw_invalid_argument_error(wn_connect, 0,
  451    		'WNDB environment variable is not set or is incorrect. Please either set it at the OS terminal or use this directive with an argument indicating the directory of the Wordnet database.')
  452    )
  453  ).
  454
  455is_directive_valid(wn_connect, [QFolder]) :-
  456  utilities:remove_quotes(QFolder,Folder),
  457	(exists_directory(Folder)
  458	 ->
  459	  setenv('WNDB',QFolder)
  460	 ;
  461	  % The argument is not a valid directory
  462  	throw_invalid_argument_error(wn_connect, 1,
  463  		'Argument must be an existent directory.')).
  464
  465is_directive_valid(wn_gen_prox_equations, [Measure, ListOfListOfWords]) :-
  466  is_list(ListOfListOfWords),
  467  (maplist(is_list,ListOfListOfWords)
  468   ->
  469    ensure_loaded_wn,
  470  	(\+ wn_measure(Measure)
  471  	 ->
  472  	  % The first argument is not a valid measure
  473    	throw_invalid_argument_error(wn_gen_prox_equations, 2,
  474    		'Argument must be a valid measure.')
  475     ;
  476  	  flatten(ListOfListOfWords, ListOfWords),
  477  	  check_wn_words(ListOfWords, WordNotFound),
  478  	  (var(WordNotFound)
  479    	 ->
  480    	  true
  481    	 ;
  482    	  % One word is not found
  483    	  atomic_list_concat(['Word not found in WordNet database: ', WordNotFound, '.'], ErrorMessage),
  484      	throw_invalid_argument_error(wn_gen_prox_equations, 2,
  485      		ErrorMessage)))
  486   ;
  487	  % The second argument is not a list of lists
  488  	throw_invalid_argument_error(wn_gen_prox_equations, 2,
  489  		'Second argument must be a list of lists.')
  490  ).
  491    	
  492    
  493%is_directive_valid(wn_gen_prox_equations, _).
  494
  495is_directive_valid(wn_gen_prox_equations, [Measure, Auto]) :-
  496  ensure_loaded_wn,
  497	(\+ wn_measure(Measure)
  498	 ->
  499	  % The argument is not a valid measure
  500  	throw_invalid_argument_error(wn_gen_prox_equations, 1,
  501  		'Argument must be a valid measure.')
  502   ;
  503  	(\+ member(Auto, [auto, automatic, automatically])
  504  	 ->
  505  	  % The argument is not a valid switch
  506    	throw_invalid_argument_error(wn_gen_prox_equations, 1,
  507    		'Argument must be either ''auto'', or ''automatic'', or ''automatically''.')
  508     ;
  509      true)).
  510
  511
  512
  513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  514% Additional predicate for checking fuzzy_set/2 directive
  515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 check_fuzzy_subsets(+Subsets, +DefinedSubsets)
Checks if the list of fuzzy subsets of a certain domain is valid. DefinedSets must be a list with all the fuzzy subsets that have already been defined for all domains.
  525check_fuzzy_subsets([], _DefinedSubsets).
  526
  527check_fuzzy_subsets([Subset|MoreSubsets], DefinedSubsets) :-
  528	compound(Subset),
  529	Subset =.. [SubsetName|Values],
  530	atomic(SubsetName), nonvar(SubsetName), not(number(SubsetName)),
  531	not((member(AnySubset, DefinedSubsets), AnySubset =.. [SubsetName|_])),
  532	(length(Values, 3) ; length(Values, 4)),
  533	ascending_numbers(Values),
  534	% This fuzzy subset is OK
  535	!,
  536	NewDefinedSubsets = [SubsetName|DefinedSubsets],
  537	check_fuzzy_subsets(MoreSubsets, NewDefinedSubsets).
  538
  539check_fuzzy_subsets([Subset|_MoreSubsets], DefinedSubsets) :-
  540	compound(Subset),
  541	Subset =.. [SubsetName|Values],
  542	atomic(SubsetName), nonvar(SubsetName), not(number(SubsetName)),
  543	not((member(AnySubset, DefinedSubsets), AnySubset =.. [SubsetName|_])),
  544	(length(Values, 3) ; length(Values, 4)),
  545	% One of the values that define the fuzzy subset isn't a number,
  546	% or the numbers are not in ascending order
  547	!,
  548	throw_invalid_argument_error(fuzzy_set, 2,
  549		'The points that define a fuzzy subset must be integer numbers, \c
  550		 and they must be in ascending order.').
  551
  552check_fuzzy_subsets([Subset|_MoreSubsets], DefinedSubsets) :-
  553	compound(Subset),
  554	Subset =.. [SubsetName|_Values],
  555	atomic(SubsetName), nonvar(SubsetName), not(number(SubsetName)),
  556	not((member(AnySubset, DefinedSubsets), AnySubset =.. [SubsetName|_])),
  557	% The fuzzy subset has been defined with an unexpected number of values
  558	!,
  559	throw_invalid_argument_error(fuzzy_set, 2,
  560		'Each linguistic term must be defined by a list with 3 integer \c
  561	     numbers for triangular subsets or 4 integer numbers for \c
  562	     trapezoidal subsets.').
  563
  564check_fuzzy_subsets([Subset|_MoreSubsets], _DefinedSubsets) :-
  565	compound(Subset),
  566	Subset =.. [SubsetName|_Values],
  567	atomic(SubsetName), nonvar(SubsetName), not(number(SubsetName)),
  568	% Fuzzy subset has already been defined
  569	!,
  570	swritef(Message, 'Fuzzy subset \'%w\' is defined more than once.', [SubsetName]),
  571	throw_invalid_argument_error(fuzzy_set, 2, Message).
  572
  573check_fuzzy_subsets([Subset|_MoreSubsets], _DefinedSubsets) :-
  574	compound(Subset),
  575	Subset =.. [SubsetName|_Values],
  576	(compound(SubsetName) ; var(SubsetName) ; number(SubsetName)),
  577	% Fuzzy subset name is a number, a free variable or a compound term
  578	!,
  579	swritef(Message, 'Fuzzy subset name must be an atom.', [SubsetName]),
  580	throw_invalid_argument_error(fuzzy_set, 2, Message).
  581
  582check_fuzzy_subsets([_Subset|_MoreSubsets], _DefinedSubsets) :-
  583	% Fuzzy set definition isn't a compound term
  584	!,
  585	throw_invalid_argument_error(fuzzy_set, 2,
  586		'A fuzzy set must be defined using a compound term.').
  587
  588
  589
  590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  591% Predicates for throwing errors when BPL directives aren't valid
  592%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 throw_invalid_argument_error(+Name, +Arity, +Message)
Throws a directive_error/1 exception indicating that the Name/Arity Bousi-Prolog directive specified isn't correct because of the reason explained in Message string.
throws
- directive_error(Message)
  604throw_invalid_argument_error(Name, Arity, Message) :-
  605	swritef(ErrorMessage, 'BPL directive \'%w/%w\' is invalid. %w',
  606		[Name, Arity, Message]),
  607	throw(directive_error(ErrorMessage)).
 throw_wrong_arity_error(+Name, +Arity, +CorrectArity)
Throws a directive_error/1 exception indicating that Name/Arity is not a valid Bousi-Prolog directive, but Name/CorrectArity is.
throws
- directive_error(Message)
  618throw_wrong_arity_error(Name, Arity, CorrectArity) :-
  619	swritef(ErrorMessage, 'BPL directive \'%w/%w\' is invalid. Use \'%w/%w\' \c
  620		instead.', [Name, Arity, Name, CorrectArity]),
  621	throw(directive_error(ErrorMessage)).
  622
  623
  624
  625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  626% Miscellaneous predicates
  627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 get_subset_names(+DomainNames, -SubsetNames)
Returns a list with the names of all the subsets defined in each of the domains of the DomainNames list.
  636get_subset_names(DomainNames, SubsetNames) :-
  637	get_subset_names_aux(DomainNames, [], SubsetNames),
  638	!.
 get_subset_names_aux(+DomainNames, +SubsetNames, -FinalSubsetNames)
Internal predicate used by get_subset_names/2 which includes an accumulator pair. FinalSubsetNames is unified with SubsetNames when DomainNames list has no more items.
See also
- get_subset_names/2
  650get_subset_names_aux([], FinalSubsetNames, FinalSubsetNames).
  651
  652get_subset_names_aux([Domain|MoreDomains], SubsetNames, FinalSubsetNames) :-
  653	(
  654		% Gets the names of the subsets of the given domain
  655		flags:get_bpl_flag(fuzzy_subsets(Domain, Subsets)),
  656		findall(SubsetName, (member(SubsetDefinition, Subsets),
  657		                     SubsetDefinition =.. [SubsetName|_]),
  658		       ThisSubsetNames)
  659	;
  660		% The given domain doesn't have any subset yet
  661		ThisSubsetNames = []
  662	),
  663	% Adds the subset names to the list and calls this predicate recursively
  664	append(SubsetNames, ThisSubsetNames, NextSubsetNames),
  665	get_subset_names_aux(MoreDomains, NextSubsetNames, FinalSubsetNames)