%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  core compiler and runtime
%
%  This file is part of Logtalk <https://logtalk.org/>
%  SPDX-FileCopyrightText: 1998-2025 Paulo Moura <pmoura@logtalk.org>
%  SPDX-License-Identifier: Apache-2.0
%
%  Licensed under the Apache License, Version 2.0 (the "License");
%  you may not use this file except in compliance with the License.
%  You may obtain a copy of the License at
%
%      http://www.apache.org/licenses/LICENSE-2.0
%
%  Unless required by applicable law or agreed to in writing, software
%  distributed under the License is distributed on an "AS IS" BASIS,
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%  See the License for the specific language governing permissions and
%  limitations under the License.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  operator declarations
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% message-sending and super call operators

% message-sending to an explicit object
:- op(600, xfy, ::).
% message-sending to "self"
:- op(600,  fy, ::).
% "super" call (calls an inherited or imported method definition)
:- op(600,  fy, ^^).


% mode operators

% input argument (instantiated); ISO Prolog standard operator
:- op(200, fy, (+)).
% input/output argument
:- op(200, fy, (?)).
% input argument (not modified by the call)
:- op(200, fy, (@)).
% output argument (not instantiated); ISO Prolog standard operator
:- op(200, fy, (-)).
% ground argument
:- op(200, fy, ++).
% unbound argument (typically when returning an opaque term)
:- op(200, fy, --).


% bitwise left-shift operator (used for context-switching calls)
% some backend Prolog compilers don't declare this ISO Prolog standard operator!
:- op(400, yfx, <<).

% bitwise right-shift operator (used for lambda expressions)
% some backend Prolog compilers don't declare this ISO Prolog standard operator!
:- op(400, yfx, >>).


% predicate alias operator (alternative to the ::/2 or :/2 operators depending on the context)
% first introduced in SWI-Prolog and YAP also for defining aliases to module predicates
:- op(700, xfx, as).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  runtime directives (bookkeeping tables)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% tables of defined events and monitors

% '$lgt_before_event_'(Obj, Msg, Sender, Monitor, Call)
:- dynamic('$lgt_before_event_'/5).
% '$lgt_after_event_'(Obj, Msg, Sender, Monitor, Call)
:- dynamic('$lgt_after_event_'/5).


% tables of loaded entities, entity and predicate properties, plus entity relations

% '$lgt_current_protocol_'(Ptc, Prefix, Dcl, Rnm, Flags)
:- multifile('$lgt_current_protocol_'/5).
:- dynamic('$lgt_current_protocol_'/5).
% '$lgt_current_category_'(Ctg, Prefix, Dcl, Def, Rnm, Flags)
:- multifile('$lgt_current_category_'/6).
:- dynamic('$lgt_current_category_'/6).
% '$lgt_current_object_'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, Flags)
:- multifile('$lgt_current_object_'/11).
:- dynamic('$lgt_current_object_'/11).

% '$lgt_entity_property_'(Entity, Property)
:- multifile('$lgt_entity_property_'/2).
:- dynamic('$lgt_entity_property_'/2).
% '$lgt_predicate_property_'(Entity, Functor/Arity, Property)
:- multifile('$lgt_predicate_property_'/3).
:- dynamic('$lgt_predicate_property_'/3).

% '$lgt_implements_protocol_'(ObjOrCtg, Ptc, Scope)
:- multifile('$lgt_implements_protocol_'/3).
:- dynamic('$lgt_implements_protocol_'/3).
% '$lgt_imports_category_'(Obj, Ctg, Scope)
:- multifile('$lgt_imports_category_'/3).
:- dynamic('$lgt_imports_category_'/3).
% '$lgt_instantiates_class_'(Instance, Class, Scope)
:- multifile('$lgt_instantiates_class_'/3).
:- dynamic('$lgt_instantiates_class_'/3).
% '$lgt_specializes_class_'(Class, Superclass, Scope)
:- multifile('$lgt_specializes_class_'/3).
:- dynamic('$lgt_specializes_class_'/3).
% '$lgt_extends_category_'(Ctg, ExtCtg, Scope)
:- multifile('$lgt_extends_category_'/3).
:- dynamic('$lgt_extends_category_'/3).
% '$lgt_extends_object_'(Prototype, Parent, Scope)
:- multifile('$lgt_extends_object_'/3).
:- dynamic('$lgt_extends_object_'/3).
% '$lgt_extends_protocol_'(Ptc, ExtPtc, Scope)
:- multifile('$lgt_extends_protocol_'/3).
:- dynamic('$lgt_extends_protocol_'/3).
% '$lgt_complemented_object_'(Obj, Ctg, Dcl, Def, Rnm)
:- dynamic('$lgt_complemented_object_'/5).

% '$lgt_uses_predicate_'(Entity, Obj, Original, Alias, Ctx)
:- multifile('$lgt_uses_predicate_'/5).
% '$lgt_use_module_predicate_'(Entity, Module, Original, Alias, Ctx)
:- multifile('$lgt_use_module_predicate_'/5).

% table of loaded files

% '$lgt_loaded_file_'(Basename, Directory, Mode, Flags, TextProperties, ObjectFile, TimeStamp)
:- multifile('$lgt_loaded_file_'/7).
:- dynamic('$lgt_loaded_file_'/7).
% '$lgt_included_file_'(File, Line, MainBasename, MainDirectory, TimeStamp)
:- multifile('$lgt_included_file_'/5).
:- dynamic('$lgt_included_file_'/5).
% '$lgt_failed_file_'(SourceFile)
:- dynamic('$lgt_failed_file_'/1).
% '$lgt_parent_file_'(SourceFile, ParentSourceFile)
:- dynamic('$lgt_parent_file_'/2).
% '$lgt_file_loading_stack_'(SourceFile, Directory)
:- dynamic('$lgt_file_loading_stack_'/2).


% runtime flag values

% '$lgt_current_flag_'(Name, Value)
:- dynamic('$lgt_current_flag_'/2).


% static binding caches

% '$lgt_send_to_obj_static_binding_'(Obj, Pred, ExCtx, Call)
:- dynamic('$lgt_send_to_obj_static_binding_'/4).


% dynamic binding lookup caches for messages and super calls

% '$lgt_send_to_obj_'(Obj, Pred, ExCtx)
:- dynamic('$lgt_send_to_obj_'/3).
% '$lgt_send_to_obj_ne_'(Obj, Pred, ExCtx)
:- dynamic('$lgt_send_to_obj_ne_'/3).
% '$lgt_send_to_self_'(Obj, Pred, ExCtx)
:- dynamic('$lgt_send_to_self_'/3).
% '$lgt_obj_super_call_'(Super, Pred, ExCtx)
:- dynamic('$lgt_obj_super_call_'/3).
% '$lgt_ctg_super_call_'(Ctg, Pred, ExCtx)
:- dynamic('$lgt_ctg_super_call_'/3).


% dynamic binding lookup cache for asserting and retracting dynamic facts

% '$lgt_db_lookup_cache_'(Obj, Fact, Sender, TFact, UpdateData)
:- dynamic('$lgt_db_lookup_cache_'/5).


% table of library paths

% logtalk_library_path(Library, Path)
:- multifile(logtalk_library_path/2).
:- dynamic(logtalk_library_path/2).


% extension point for logtalk_make/1

% logtalk_make_target_action(Target)
:- multifile(logtalk_make_target_action/1).
:- dynamic(logtalk_make_target_action/1).


% extension point for the linter

% logtalk_linter_hook(Goal, Flag, File, Lines, Type, Entity, Warning)
:- multifile(logtalk_linter_hook/7).


% term- and goal-expansion default compiler hooks

% '$lgt_hook_term_expansion_'(Term, ExpandedTerms)
:- dynamic('$lgt_hook_term_expansion_'/2).
% '$lgt_hook_goal_expansion_'(Goal, ExpandedGoal)
:- dynamic('$lgt_hook_goal_expansion_'/2).


% engines

% '$lgt_current_engine_'(Object, Engine, TermQueue, Id)
:- dynamic('$lgt_current_engine_'/4).


% counters

% '$lgt_dynamic_entity_counter_'(Kind, Base, Count)
:- dynamic('$lgt_dynamic_entity_counter_'/3).
% '$lgt_threaded_tag_counter_'(Tag)
:- dynamic('$lgt_threaded_tag_counter_'/1).
% '$lgt_threaded_engine_tag_counter_'(Tag)
:- dynamic('$lgt_threaded_engine_tag_counter_'/1).


% debugging hook predicates

:- multifile('$logtalk#0.trace_event#2'/3).
:- dynamic('$logtalk#0.trace_event#2'/3).

:- multifile('$logtalk#0.debug_handler#1'/2).

:- multifile('$logtalk#0.debug_handler#3'/4).


% internal initialization flags

:- dynamic('$lgt_built_in_entities_loaded_'/0).
:- dynamic('$lgt_runtime_initialization_completed_'/0).


% user-defined flags

% '$lgt_user_defined_flag_'(Flag, Access, Type)
:- dynamic('$lgt_user_defined_flag_'/3).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  compiler directives
%
%  (used for source file compilation and runtime creation of new entities)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_pp_file_compiler_flag_'(Name, Value)
:- dynamic('$lgt_pp_file_compiler_flag_'/2).
% '$lgt_pp_entity_compiler_flag_'(Name, Value)
:- dynamic('$lgt_pp_entity_compiler_flag_'/2).

% '$lgt_pp_dcl_'(Clause)
:- dynamic('$lgt_pp_dcl_'/1).
% '$lgt_pp_def_'(Clause)
:- dynamic('$lgt_pp_def_'/1).
% '$lgt_pp_ddef_'(Clause)
:- dynamic('$lgt_pp_ddef_'/1).
% '$lgt_pp_super_'(Clause)
:- dynamic('$lgt_pp_super_'/1).

% '$lgt_pp_synchronized_'(Head, Mutex, File, Lines)
:- dynamic('$lgt_pp_synchronized_'/4).
% '$lgt_pp_predicate_mutex_counter_'(Count)
:- dynamic('$lgt_pp_predicate_mutex_counter_'/1).
% '$lgt_pp_dynamic_'(Head, Original, File, Lines)
:- dynamic('$lgt_pp_dynamic_'/4).
% '$lgt_pp_discontiguous_'(Head, File, Lines)
:- dynamic('$lgt_pp_discontiguous_'/3).
% '$lgt_pp_mode_'(Mode, Determinism, File, Lines)
:- dynamic('$lgt_pp_mode_'/4).
% '$lgt_pp_public_'(Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_public_'/4).
% '$lgt_pp_protected_'(Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_protected_'/4).
% '$lgt_pp_private_'(Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_private_'/4).
% '$lgt_pp_meta_predicate_'(PredTemplate, MetaTemplate, File, Lines)
:- dynamic('$lgt_pp_meta_predicate_'/4).
% '$lgt_pp_predicate_alias_'(Entity, Pred, Alias, NonTerminalFlag, File, Lines)
:- dynamic('$lgt_pp_predicate_alias_'/6).
% '$lgt_pp_non_terminal_'(Functor, Arity, ExtArity)
:- dynamic('$lgt_pp_non_terminal_'/3).
% '$lgt_pp_multifile_'(Head, Original, File, Lines)
:- dynamic('$lgt_pp_multifile_'/4).
% '$lgt_pp_coinductive_'(Head, TestHead, HeadExCtx, TCHead, BodyExCtx, THead, DHead, File, Lines)
:- dynamic('$lgt_pp_coinductive_'/9).
% '$lgt_pp_coinductive_head_'(Head, HeadExCtx, TCHead)
:- dynamic('$lgt_pp_coinductive_head_'/3).

% '$lgt_pp_object_'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, Flags)
:- dynamic('$lgt_pp_object_'/11).
% '$lgt_pp_category_'(Ctg, Prefix, Dcl, Def, Rnm, Flags)
:- dynamic('$lgt_pp_category_'/6).
% '$lgt_pp_protocol_'(Ptc, Prefix, Dcl, Rnm, Flags)
:- dynamic('$lgt_pp_protocol_'/5).
% '$lgt_pp_entity_'(Type, Entity, Prefix)
:- dynamic('$lgt_pp_entity_'/3).
% '$lgt_pp_module_'(Module)
:- dynamic('$lgt_pp_module_'/1).

% '$lgt_pp_entity_lines_'(Entity, Lines)
:- dynamic('$lgt_pp_entity_lines_'/2).

% '$lgt_pp_parameter_variables_'(ParameterVariables)
:- dynamic('$lgt_pp_parameter_variables_'/1).

% '$lgt_pp_object_alias_'(Obj, Alias, CompilationContext, File, Lines)
:- dynamic('$lgt_pp_object_alias_'/5).

% '$lgt_pp_module_alias_'(Module, Alias, CompilationContext, File, Lines)
:- dynamic('$lgt_pp_module_alias_'/5).

% '$lgt_pp_uses_predicate_'(Obj, Predicate, Alias, CompilationContext, File, Lines)
:- dynamic('$lgt_pp_uses_predicate_'/6).
% '$lgt_pp_uses_non_terminal_'(Obj, NonTerminal, NonTerminalAlias, Predicate, PredicateAlias, CompilationContext, File, Lines)
:- dynamic('$lgt_pp_uses_non_terminal_'/8).
% '$lgt_pp_use_module_predicate_'(Module, Predicate, Alias, CompilationContext, File, Lines)
:- dynamic('$lgt_pp_use_module_predicate_'/6).
% '$lgt_pp_use_module_non_terminal_'(Module, NonTerminal, NonTerminalAlias, Predicate, PredicateAlias, CompilationContext, File, Lines)
:- dynamic('$lgt_pp_use_module_non_terminal_'/8).
% '$lgt_pp_entity_info_'(List, File, Lines)
:- dynamic('$lgt_pp_entity_info_'/3).
% '$lgt_pp_predicate_info_'(Predicate, List, File, Lines)
:- dynamic('$lgt_pp_predicate_info_'/4).

% '$lgt_pp_implemented_protocol_'(Ptc, ObjOrCtg, Prefix, Dcl, Scope)
:- dynamic('$lgt_pp_implemented_protocol_'/5).
% '$lgt_pp_imported_category_'(Ctg, Obj, Prefix, Dcl, Def, Scope)
:- dynamic('$lgt_pp_imported_category_'/6).
% '$lgt_pp_extended_object_'(Parent, Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Scope)
:- dynamic('$lgt_pp_extended_object_'/11).
% '$lgt_pp_instantiated_class_'(Class, Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Scope)
:- dynamic('$lgt_pp_instantiated_class_'/11).
% '$lgt_pp_specialized_class_'(Superclass, Class, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Scope)
:- dynamic('$lgt_pp_specialized_class_'/11).
% '$lgt_pp_extended_protocol_'(ExtPtc, Ptc, Prefix, Dcl, Scope)
:- dynamic('$lgt_pp_extended_protocol_'/5).
% '$lgt_pp_extended_category_'(ExtCtg, Ctg, Prefix, Dcl, Def, Scope)
:- dynamic('$lgt_pp_extended_category_'/6).
% '$lgt_pp_complemented_object_'(Obj, Ctg, Dcl, Def, Rnm)
:- dynamic('$lgt_pp_complemented_object_'/5).

% '$lgt_pp_file_initialization_'(Goal, Lines)
:- dynamic('$lgt_pp_file_initialization_'/2).
% '$lgt_pp_file_entity_initialization_'(Object, Goal, Lines)
:- dynamic('$lgt_pp_file_entity_initialization_'/3).

% '$lgt_pp_object_initialization_'(Goal, SourceData, Lines)
:- dynamic('$lgt_pp_object_initialization_'/3).
% '$lgt_pp_final_object_initialization_'(Goal, Lines)
:- dynamic('$lgt_pp_final_object_initialization_'/2).

% '$lgt_pp_entity_meta_directive_'(Directive, SourceData, Lines)
:- dynamic('$lgt_pp_entity_meta_directive_'/3).

% '$lgt_pp_redefined_built_in_'(Head, ExCtx, THead)
:- dynamic('$lgt_pp_redefined_built_in_'/3).

% '$lgt_pp_directive_'(Directive)
:- dynamic('$lgt_pp_directive_'/1).
% '$lgt_pp_prolog_term_'(Term, Lines)
:- dynamic('$lgt_pp_prolog_term_'/2).
% '$lgt_pp_entity_term_'(Term, SourceData, Lines)
:- dynamic('$lgt_pp_entity_term_'/3).
% '$lgt_pp_final_entity_term_'(Term, Lines)
:- dynamic('$lgt_pp_final_entity_term_'/2).
% '$lgt_pp_entity_aux_clause_'(Clause)
:- dynamic('$lgt_pp_entity_aux_clause_'/1).
% '$lgt_pp_final_entity_aux_clause_'(Clause)
:- dynamic('$lgt_pp_final_entity_aux_clause_'/1).

% '$lgt_pp_number_of_clauses_rules_'(Functor, Arity, NumberOfClauses, NumberOfRules)
:- dynamic('$lgt_pp_number_of_clauses_rules_'/4).
% '$lgt_pp_number_of_clauses_rules_'(Other, Functor, Arity, NumberOfClauses, NumberOfRules)
:- dynamic('$lgt_pp_number_of_clauses_rules_'/5).

% '$lgt_pp_predicate_declaration_location_'(Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_predicate_declaration_location_'/4).
% '$lgt_pp_predicate_definition_location_'(Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_predicate_definition_location_'/4).
% '$lgt_pp_defines_predicate_'(Head, Functor/Arity, ExCtx, THead, Mode, Origin)
:- dynamic('$lgt_pp_defines_predicate_'/6).
% '$lgt_pp_inline_predicate_'(Functor/Arity)
:- dynamic('$lgt_pp_inline_predicate_'/1).

% '$lgt_pp_predicate_definition_location_'(Other, Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_predicate_definition_location_'/5).

% '$lgt_pp_non_tail_recursive_predicate_'(Functor, Arity, File, Lines)
:- dynamic('$lgt_pp_non_tail_recursive_predicate_'/4).
% '$lgt_pp_predicate_recursive_calls_'(Functor, Arity, Counter)
:- dynamic('$lgt_pp_predicate_recursive_calls_'/3).
% '$lgt_pp_calls_predicate_'(Functor/Arity, TFunctor/TArity, HeadFunctor/HeadArity, File, Lines)
:- dynamic('$lgt_pp_calls_predicate_'/5).
% '$lgt_pp_calls_self_predicate_'(Functor/Arity, HeadFunctor/HeadArity, File, Lines)
:- dynamic('$lgt_pp_calls_self_predicate_'/4).
% '$lgt_pp_calls_super_predicate_'(Functor/Arity, HeadFunctor/HeadArity, File, Lines)
:- dynamic('$lgt_pp_calls_super_predicate_'/4).

% '$lgt_pp_updates_predicate_'(Dynamic, HeadFunctor/HeadArity, File, Lines)
:- dynamic('$lgt_pp_updates_predicate_'/4).

% '$lgt_pp_non_portable_predicate_'(Head, File, Lines)
:- dynamic('$lgt_pp_non_portable_predicate_'/3).
% '$lgt_pp_non_portable_function_'(Function, File, Lines)
:- dynamic('$lgt_pp_non_portable_function_'/3).
% '$lgt_pp_missing_function_'(Function, File, Lines)
:- dynamic('$lgt_pp_missing_function_'/3).

% '$lgt_pp_missing_meta_predicate_directive_'(Head, File, Lines)
:- dynamic('$lgt_pp_missing_meta_predicate_directive_'/3).
% '$lgt_pp_missing_dynamic_directive_'(Head, File, Lines)
:- dynamic('$lgt_pp_missing_dynamic_directive_'/3).
% '$lgt_pp_missing_discontiguous_directive_'(Head, File, Lines)
:- dynamic('$lgt_pp_missing_discontiguous_directive_'/3).
% '$lgt_pp_missing_multifile_directive_'(PI, File, Lines)
:- dynamic('$lgt_pp_missing_multifile_directive_'/3).
% '$lgt_pp_missing_use_module_directive_'(Module, Functor/Arity)
:- dynamic('$lgt_pp_missing_use_module_directive_'/2).

% '$lgt_pp_previous_predicate_'(Head, Mode)
:- dynamic('$lgt_pp_previous_predicate_'/2).

% '$lgt_pp_defines_non_terminal_'(Functor, Arity, ExtArity)
:- dynamic('$lgt_pp_defines_non_terminal_'/3).
% '$lgt_pp_calls_non_terminal_'(Functor, Arity, ExtArity, Lines)
:- dynamic('$lgt_pp_calls_non_terminal_'/4).

% '$lgt_pp_referenced_object_'(Object, File, Lines)
:- dynamic('$lgt_pp_referenced_object_'/3).
% '$lgt_pp_referenced_protocol_'(Protocol, File, Lines)
:- dynamic('$lgt_pp_referenced_protocol_'/3).
% '$lgt_pp_referenced_category_'(Category, File, Lines)
:- dynamic('$lgt_pp_referenced_category_'/3).
% '$lgt_pp_referenced_module_'(Module, File, Lines)
:- dynamic('$lgt_pp_referenced_module_'/3).

% '$lgt_pp_referenced_object_message_'(Object, Functor/Arity, AliasFunctor/AliasArity, HeadFunctor/HeadArity, File, Lines)
:- dynamic('$lgt_pp_referenced_object_message_'/6).
% '$lgt_pp_referenced_module_predicate_'(Module, Functor/Arity, AliasFunctor/AliasArity, HeadFunctor/HeadArity, File, Lines)
:- dynamic('$lgt_pp_referenced_module_predicate_'/6).

% '$lgt_pp_global_operator_'(Priority, Specifier, Operator)
:- dynamic('$lgt_pp_global_operator_'/3).
% '$lgt_pp_file_operator_'(Priority, Specifier, Operator)
:- dynamic('$lgt_pp_file_operator_'/3).
% '$lgt_pp_entity_operator_'(Priority, Specifier, Operator, Scope, File, Lines)
:- dynamic('$lgt_pp_entity_operator_'/6).

% '$lgt_pp_warnings_top_goal_'(Goal)
:- dynamic('$lgt_pp_warnings_top_goal_'/1).
% '$lgt_pp_compiling_warnings_counter_'(Counter)
:- dynamic('$lgt_pp_compiling_warnings_counter_'/1).
% '$lgt_pp_loading_warnings_counter_'(Counter)
:- dynamic('$lgt_pp_loading_warnings_counter_'/1).

% '$lgt_pp_hook_term_expansion_'(Term, Terms)
:- dynamic('$lgt_pp_hook_term_expansion_'/2).
% '$lgt_pp_hook_goal_expansion_'(Goal, ExpandedGoal)
:- dynamic('$lgt_pp_hook_goal_expansion_'/2).

% '$lgt_pp_built_in_'
:- dynamic('$lgt_pp_built_in_'/0).
% '$lgt_pp_dynamic_'
:- dynamic('$lgt_pp_dynamic_'/0).
% '$lgt_pp_threaded_'
:- dynamic('$lgt_pp_threaded_'/0).

% '$lgt_pp_file_encoding_'(SourceFile, LogtalkEncoding, PrologEncoding, Line)
:- dynamic('$lgt_pp_file_encoding_'/4).
% '$lgt_pp_file_bom_'(SourceFile, BOM)
:- dynamic('$lgt_pp_file_bom_'/2).
% '$lgt_pp_file_paths_flags_'(Basename, Directory, SourceFile, ObjectFile, Flags)
:- dynamic('$lgt_pp_file_paths_flags_'/5).

% '$lgt_pp_runtime_clause_'(Clause)
:- dynamic('$lgt_pp_runtime_clause_'/1).

% '$lgt_pp_cc_if_found_'(Goal)
:- dynamic('$lgt_pp_cc_if_found_'/1).
% '$lgt_pp_cc_skipping_'
:- dynamic('$lgt_pp_cc_skipping_'/0).
% '$lgt_pp_cc_mode_'(Action)
:- dynamic('$lgt_pp_cc_mode_'/1).

% '$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines)
:- dynamic('$lgt_pp_term_source_data_'/5).

% '$lgt_pp_aux_predicate_counter_'(Counter)
:- dynamic('$lgt_pp_aux_predicate_counter_'/1).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  top-level interpreter versions of the message-sending and context
%  switching call control constructs
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% top-level interpreter message-sending calls

Obj::Pred :-
	var(Obj),
	'$lgt_execution_context'(ExCtx, user, user, user, Obj, [], []),
	throw(error(instantiation_error, logtalk(Obj::Pred, ExCtx))).

{Obj}::Pred :-
	!,
	% use current default value of the "events" flag
	'$lgt_current_flag_'(events, Events),
	'$lgt_comp_ctx'(Ctx, _, _, user, user, user, Obj, _, [], [], ExCtx, runtime, [], _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, Obj, [], []),
	catch(
		'$lgt_compile_message_to_object'(Pred, {Obj}, Call, Events, Ctx),
		Error,
		'$lgt_runtime_error_handler'(error(Error, logtalk({Obj}::Pred, ExCtx)))
	),
	(	nonvar(Obj),
		'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags),
		Flags /\ 512 =:= 512 ->
		% object compiled in debug mode
		catch('$lgt_debug'(top_goal({Obj}::Pred, Call), ExCtx), Error, '$lgt_runtime_error_handler'(Error))
	;	% object not compiled in debug mode or non-existing object or invalid object identifier
		catch(Call, Error, '$lgt_runtime_error_handler'(Error))
	).

Obj::Pred :-
	% use current default value of the "events" flag
	'$lgt_current_flag_'(events, Events),
	'$lgt_comp_ctx'(Ctx, _, _, user, user, user, Obj, _, [], [], ExCtx, runtime, [], _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, Obj, [], []),
	catch(
		'$lgt_compile_message_to_object'(Pred, Obj, Call, Events, Ctx),
		Error,
		'$lgt_runtime_error_handler'(error(Error, logtalk(Obj::Pred, ExCtx)))
	),
	(	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags),
		Flags /\ 512 =:= 512 ->
		% object compiled in debug mode
		catch('$lgt_debug'(top_goal(Obj::Pred, Call), ExCtx), Error, '$lgt_runtime_error_handler'(Error))
	;	% object not compiled in debug mode or non-existing object
		catch(Call, Error, '$lgt_runtime_error_handler'(Error))
	).



% top-level interpreter context-switch calls (debugging control construct)

Obj<<Goal :-
	var(Obj),
	'$lgt_execution_context'(ExCtx, user, user, user, Obj, [], []),
	throw(error(instantiation_error, logtalk(Obj<<Goal, ExCtx))).

{Obj}<<Goal :-
	!,
	'$lgt_execution_context'(ExCtx, user, user, user, Obj, [], []),
	catch(
		'$lgt_compile_context_switch_call'({Obj}, Goal, Call, ExCtx),
		Error,
		'$lgt_runtime_error_handler'(error(Error, logtalk({Obj}<<Goal, ExCtx)))
	),
	(	nonvar(Obj),
		'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags),
		Flags /\ 512 =:= 512 ->
		% object compiled in debug mode
		catch('$lgt_debug'(top_goal({Obj}<<Goal, Call), ExCtx), Error, '$lgt_runtime_error_handler'(Error))
	;	% object not compiled in debug mode or non-existing object or invalid object identifier
		catch(Call, Error, '$lgt_runtime_error_handler'(Error))
	).

Obj<<Goal :-
	'$lgt_execution_context'(ExCtx, user, user, user, Obj, [], []),
	catch(
		'$lgt_compile_context_switch_call'(Obj, Goal, Call, ExCtx),
		Error,
		'$lgt_runtime_error_handler'(error(Error, logtalk(Obj<<Goal, ExCtx)))
	),
	(	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags),
		Flags /\ 512 =:= 512 ->
		% object compiled in debug mode
		catch('$lgt_debug'(top_goal(Obj<<Goal, Call), ExCtx), Error, '$lgt_runtime_error_handler'(Error))
	;	% object not compiled in debug mode or non-existing object
		catch(Call, Error, '$lgt_runtime_error_handler'(Error))
	).



% '$lgt_runtime_error_handler'(@term)
%
% top-level runtime error handler
%
% it tries to decode internal predicate names and deal with variations of
% Prolog error handling due to the lack of standardization by calling an
% adapter file predicate to try to normalize the error terms

'$lgt_runtime_error_handler'(Variable) :-
	var(Variable),
	throw(error(instantiation_error, logtalk(throw(_), _))).

'$lgt_runtime_error_handler'(logtalk_debugger_aborted) :-
	!,
	'$lgt_execution_context'(ExCtx, logtalk, logtalk, logtalk, logtalk, [], []),
	'$logtalk#0.print_message#3'(comment(debugging), debugger, logtalk_debugger_aborted, ExCtx).

'$lgt_runtime_error_handler'(error(Variable, Context)) :-
	var(Variable),
	throw(error(instantiation_error, logtalk(throw(_), Context))).

'$lgt_runtime_error_handler'(error(error(Error, _), Context)) :-
	!,
	'$lgt_runtime_error_handler'(error(Error, Context)).

'$lgt_runtime_error_handler'(error(existence_error(thread,Queue), TContext)) :-
	'$lgt_runtime_thread_error_handler_helper'(TContext, Context),
	throw(error(existence_error(thread,Queue), Context)).

'$lgt_runtime_error_handler'(Error) :-
	(	'$lgt_normalize_error_term'(Error, NormalizedError) ->
		'$lgt_runtime_normalized_error_handler'(NormalizedError)
	;	'$lgt_runtime_normalized_error_handler'(Error)
	).

'$lgt_runtime_normalized_error_handler'(error(existence_error(procedure, ':'(Module,PI)), Context)) :-
	% assuming we're running with a backend compiler supporting modules,
	% check that the error is the context of the module where Logtalk is loaded
	atom(Module),
	'$lgt_user_module_qualification'(xx, ':'(Module,xx)),
	!,
	'$lgt_runtime_normalized_error_handler'(error(existence_error(procedure, PI), Context)).

'$lgt_runtime_normalized_error_handler'(error(existence_error(procedure, TFunctor/6), _)) :-
	(	atom_concat(Prefix, '_idcl', TFunctor) ->
		true
	;	atom_concat(Prefix, '_dcl', TFunctor)
	),
	'$lgt_prefix_to_entity'(Prefix, Obj),
	(	'$lgt_instantiates_class_'(_, Obj, _)
	;	'$lgt_specializes_class_'(_, Obj, _)
	;	'$lgt_extends_object_'(_, Obj, _)
	;	'$lgt_complemented_object_'(Obj, _, _, _, _)
	),
	\+ '$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _),
	throw(error(existence_error(object, Obj), logtalk(_, _))).

'$lgt_runtime_normalized_error_handler'(error(existence_error(procedure, TFunctor/5), _)) :-
	atom_concat(Prefix, '_dcl', TFunctor),
	'$lgt_prefix_to_entity'(Prefix, CtgOrPtc),
	(	'$lgt_implements_protocol_'(_, CtgOrPtc, _), \+ '$lgt_current_protocol_'(CtgOrPtc, _, _, _, _),
		throw(error(existence_error(protocol, CtgOrPtc), logtalk(_, _)))
	;	'$lgt_extends_protocol_'(_, CtgOrPtc, _), \+ '$lgt_current_protocol_'(CtgOrPtc, _, _, _, _),
		throw(error(existence_error(protocol, CtgOrPtc), logtalk(_, _)))
	;	'$lgt_imports_category_'(_, CtgOrPtc, _), \+ '$lgt_current_category_'(CtgOrPtc, _, _, _, _, _),
		throw(error(existence_error(category, CtgOrPtc), logtalk(_, _)))
	;	'$lgt_extends_category_'(_, CtgOrPtc, _), \+ '$lgt_current_category_'(CtgOrPtc, _, _, _, _, _),
		throw(error(existence_error(category, CtgOrPtc), logtalk(_, _)))
	).

'$lgt_runtime_normalized_error_handler'(error(existence_error(procedure, TFunctor/TArity), logtalk(Goal, ExCtx))) :-
	'$lgt_decompile_predicate_indicators'(TFunctor/TArity, _, _, Functor/Arity),
	throw(error(existence_error(procedure, Functor/Arity), logtalk(Goal, ExCtx))).

'$lgt_runtime_normalized_error_handler'(error(existence_error(procedure, TFunctor/TArity), _)) :-
	'$lgt_decompile_predicate_indicators'(TFunctor/TArity, _, _, Functor/Arity),
	throw(error(existence_error(procedure, Functor/Arity), logtalk(_, _))).

'$lgt_runtime_normalized_error_handler'(Error) :-
	throw(Error).


'$lgt_runtime_thread_error_handler_helper'(logtalk(threaded_exit(TGoal),ExCtx), logtalk(threaded_exit(Goal),ExCtx)) :-
	!,
	'$lgt_runtime_thread_error_tgoal_goal'(TGoal, Goal).

'$lgt_runtime_thread_error_handler_helper'(logtalk(threaded_exit(TGoal,Tag),ExCtx), logtalk(threaded_exit(Goal,Tag),ExCtx)) :-
	!,
	'$lgt_runtime_thread_error_tgoal_goal'(TGoal, Goal).

'$lgt_runtime_thread_error_handler_helper'(Context, Context).


'$lgt_runtime_thread_error_tgoal_goal'('$lgt_send_to_obj_ne_nv'(Self,Goal0,_), Goal) :-
	!,
	(	Self == user ->
		Goal = Goal0
	;	Goal = Self::Goal0
	).

'$lgt_runtime_thread_error_tgoal_goal'('$lgt_send_to_obj_nv'(Self,Goal0,_), Goal) :-
	!,
	(	Self == user ->
		Goal = Goal0
	;	Goal = Self::Goal0
	).

'$lgt_runtime_thread_error_tgoal_goal'(TGoal, Goal) :-
	functor(TGoal, TFunctor, TArity),
	'$lgt_decompile_predicate_indicators'(TFunctor/TArity, _, _, Functor/Arity),
	functor(Goal, Functor, Arity),
	'$lgt_unify_head_thead_arguments'(Goal, TGoal, _).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  built-in predicates
%
%  in general, two main clauses are provided: one for calls in "user", e.g.
%  calls at the top-level, and one for compiled calls
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% current_object(?object_identifier)

current_object(Obj) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_object'(Obj, ExCtx).


'$lgt_current_object'(Obj, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(current_object(Obj), ExCtx)),
	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _).



% current_protocol(?protocol_identifier)

current_protocol(Ptc) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_protocol'(Ptc, ExCtx).


'$lgt_current_protocol'(Ptc, ExCtx) :-
	'$lgt_check'(var_or_protocol_identifier, Ptc, logtalk(current_protocol(Ptc), ExCtx)),
	'$lgt_current_protocol_'(Ptc, _, _, _, _).



% current_category(?category_identifier)

current_category(Ctg) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_category'(Ctg, ExCtx).


'$lgt_current_category'(Ctg, ExCtx) :-
	'$lgt_check'(var_or_category_identifier, Ctg, logtalk(current_category(Ctg), ExCtx)),
	'$lgt_current_category_'(Ctg, _, _, _, _, _).



% object_property(?object_identifier, ?object_property)
%
% the implementation ensures that no spurious choice-points are
% created when the predicate is called with a bound property argument

object_property(Obj, Prop) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_object_property'(Obj, Prop, ExCtx).


'$lgt_object_property'(Obj, Prop, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(object_property(Obj, Prop), ExCtx)),
	'$lgt_check'(var_or_object_property, Prop, logtalk(object_property(Obj, Prop), ExCtx)),
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, DDcl, DDef, Rnm, Flags),
	'$lgt_object_property'(Prop, Obj, Dcl, Def, DDcl, DDef, Rnm, Flags).


'$lgt_object_property'(module, _, _, _, _, _, _, Flags) :-
	Flags /\ 1024 =:= 1024.
'$lgt_object_property'(debugging, _, _, _, _, _, _, Flags) :-
	Flags /\ 512 =:= 512.
'$lgt_object_property'(context_switching_calls, _, _, _, _, _, _, Flags) :-
	Flags /\ 256 =:= 256.
'$lgt_object_property'(dynamic_declarations, _, _, _, _, _, _, Flags) :-
	Flags /\ 128 =:= 128.
'$lgt_object_property'(complements(Complements), _, _, _, _, _, _, Flags) :-
	(	Flags /\ 64 =:= 64 ->
		Complements = allow
	;	Flags /\ 32 =:= 32,
		Complements = restrict
	).
'$lgt_object_property'(complements, _, _, _, _, _, _, Flags) :-
	% deprecated Logtalk 2.x object property
	(	Flags /\ 64 =:= 64 ->
		true
	;	Flags /\ 32 =:= 32
	).
'$lgt_object_property'(events, Obj, _, _, _, _, _, Flags) :-
	(	Obj == user ->
		% depends on the current default value of the flag
		'$lgt_current_flag_'(events, allow)
	;	% fixed value (at compilation time) for all other objects
		Flags /\ 16 =:= 16
	).
'$lgt_object_property'(source_data, _, _, _, _, _, _, Flags) :-
	Flags /\ 8 =:= 8.
'$lgt_object_property'(threaded, _, _, _, _, _, _, Flags) :-
	Flags /\ 4 =:= 4.
'$lgt_object_property'((dynamic), _, _, _, _, _, _, Flags) :-
	Flags /\ 2 =:= 2.
'$lgt_object_property'(static, _, _, _, _, _, _, Flags) :-
	Flags /\ 2 =:= 0.
'$lgt_object_property'(built_in, _, _, _, _, _, _, Flags) :-
	Flags /\ 1 =:= 1.
'$lgt_object_property'(file(Path), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, file_lines(Basename, Directory, _, _)) ->
		atom_concat(Directory, Basename, Path)
	;	fail
	).
'$lgt_object_property'(file(Basename, Directory), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, file_lines(Basename, Directory, _, _)) ->
		true
	;	fail
	).
'$lgt_object_property'(lines(Start, End), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, file_lines(_, _, Start, End)) ->
		true
	;	fail
	).
'$lgt_object_property'(directive(Start, End), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, directive(Start, End)) ->
		true
	;	fail
	).
'$lgt_object_property'(info(Info), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, info(Info)) ->
		true
	;	fail
	).
'$lgt_object_property'(public(Resources), Obj, Dcl, _, DDcl, _, _, Flags) :-
	'$lgt_object_property_resources'(Obj, Dcl, DDcl, Flags, p(p(p)), Resources).
'$lgt_object_property'(protected(Resources), Obj, Dcl, _, DDcl, _, _, Flags) :-
	'$lgt_object_property_resources'(Obj, Dcl, DDcl, Flags, p(p), Resources).
'$lgt_object_property'(private(Resources), Obj, Dcl, _, DDcl, _, _, Flags) :-
	'$lgt_object_property_resources'(Obj, Dcl, DDcl, Flags, p, Resources).
'$lgt_object_property'(declares(Predicate, Properties), Obj, Dcl, _, DDcl, _, _, Flags) :-
	'$lgt_object_property_declares'(Obj, Dcl, DDcl, Flags, Predicate, Properties).
'$lgt_object_property'(defines(Predicate, Properties), Obj, _, Def, _, DDef, _, Flags) :-
	'$lgt_object_property_defines'(Obj, Def, DDef, Predicate, Flags, Properties).
'$lgt_object_property'(includes(Predicate, From, Properties), Obj, _, _, _, _, _, _) :-
	'$lgt_entity_property_includes'(Obj, Predicate, From, Properties).
'$lgt_object_property'(provides(Predicate, To, Properties), Obj, _, _, _, _, _, _) :-
	'$lgt_entity_property_provides'(Obj, Predicate, To, Properties).
'$lgt_object_property'(alias(Alias, Properties), Obj, _, _, _, _, _, _) :-
	'$lgt_entity_property_alias'(Obj, Alias, Properties).
'$lgt_object_property'(calls(Predicate, Properties), Obj, _, _, _, _, _, _) :-
	'$lgt_entity_property_calls'(Obj, Predicate, Properties).
'$lgt_object_property'(updates(Predicate, Properties), Obj, _, _, _, _, _, _) :-
	'$lgt_entity_property_updates'(Obj, Predicate, Properties).
'$lgt_object_property'(number_of_clauses(Total), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, number_of_clauses(Total, _)) ->
		true
	;	fail
	).
'$lgt_object_property'(number_of_rules(Total), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, number_of_rules(Total, _)) ->
		true
	;	fail
	).
'$lgt_object_property'(number_of_user_clauses(TotalUser), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, number_of_clauses(_, TotalUser)) ->
		true
	;	fail
	).
'$lgt_object_property'(number_of_user_rules(TotalUser), Obj, _, _, _, _, _, _) :-
	(	'$lgt_entity_property_'(Obj, number_of_rules(_, TotalUser)) ->
		true
	;	fail
	).


'$lgt_object_property_resources'(Obj, Dcl, DDcl, Flags, Scope, Resources) :-
	% the caller uses this predicate to group object resources by scope
	findall(
		Resource,
		'$lgt_object_property_resource'(Obj, Dcl, DDcl, Flags, Scope, Resource),
		Resources
	).


'$lgt_object_property_resource'(_, Dcl, _, _, Scope, Functor/Arity) :-
	call(Dcl, Predicate, Scope, _, _),
	functor(Predicate, Functor, Arity).

'$lgt_object_property_resource'(_, _, DDcl, Flags, Scope, Functor/Arity) :-
	Flags /\ 128 =:= 128,
	% dynamic predicate declarations are allowed
	call(DDcl, Predicate, Scope),
	functor(Predicate, Functor, Arity).

'$lgt_object_property_resource'(Obj, _, _, _, Scope, op(Priority, Specifier, Operator)) :-
	'$lgt_entity_property_'(Obj, op(Priority, Specifier, Operator, Scope)).



% category_property(?category_identifier, ?category_property)
%
% the implementation ensures that no spurious choice-points are
% created when the predicate is called with a bound property argument

category_property(Ctg, Prop) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_category_property'(Ctg, Prop, ExCtx).


'$lgt_category_property'(Ctg, Prop, ExCtx) :-
	'$lgt_check'(var_or_category_identifier, Ctg, logtalk(category_property(Ctg, Prop), ExCtx)),
	'$lgt_check'(var_or_category_property, Prop, logtalk(category_property(Ctg, Prop), ExCtx)),
	'$lgt_current_category_'(Ctg, _, Dcl, Def, Rnm, Flags),
	'$lgt_category_property'(Prop, Ctg, Dcl, Def, Rnm, Flags).


'$lgt_category_property'(debugging, _, _, _, _, Flags) :-
	Flags /\ 512 =:= 512.
'$lgt_category_property'(events, _, _, _, _, Flags) :-
	Flags /\ 16 =:= 16.
'$lgt_category_property'(source_data, _, _, _, _, Flags) :-
	Flags /\ 8 =:= 8.
'$lgt_category_property'((dynamic), _, _, _, _, Flags) :-
	Flags /\ 2 =:= 2.
'$lgt_category_property'(static, _, _, _, _, Flags) :-
	Flags /\ 2 =:= 0.
'$lgt_category_property'(built_in, _, _, _, _, Flags) :-
	Flags /\ 1 =:= 1.
'$lgt_category_property'(file(Path), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, file_lines(Basename, Directory, _, _)) ->
		atom_concat(Directory, Basename, Path)
	;	fail
	).
'$lgt_category_property'(file(Basename, Directory), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, file_lines(Basename, Directory, _, _)) ->
		true
	;	fail
	).
'$lgt_category_property'(lines(Start, End), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, file_lines(_, _, Start, End)) ->
		true
	;	fail
	).
'$lgt_category_property'(directive(Start, End), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, directive(Start, End)) ->
		true
	;	fail
	).
'$lgt_category_property'(info(Info), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, info(Info)) ->
		true
	;	fail
	).
'$lgt_category_property'(public(Resources), Ctg, Dcl, _, _, Flags) :-
	'$lgt_category_property_resources'(Ctg, Dcl, Flags, p(p(p)), Resources).
'$lgt_category_property'(protected(Resources), Ctg, Dcl, _, _, Flags) :-
	'$lgt_category_property_resources'(Ctg, Dcl, Flags, p(p), Resources).
'$lgt_category_property'(private(Resources), Ctg, Dcl, _, _, Flags) :-
	'$lgt_category_property_resources'(Ctg, Dcl, Flags, p, Resources).
'$lgt_category_property'(declares(Predicate, Properties), Ctg, Dcl, _, _, _) :-
	'$lgt_category_property_declares'(Ctg, Dcl, Predicate, Properties).
'$lgt_category_property'(defines(Predicate, Properties), Ctg, _, Def, _, Flags) :-
	'$lgt_category_property_defines'(Ctg, Def, Predicate, Flags, Properties).
'$lgt_category_property'(includes(Predicate, From, Properties), Ctg, _, _, _, _) :-
	'$lgt_entity_property_includes'(Ctg, Predicate, From, Properties).
'$lgt_category_property'(provides(Predicate, To, Properties), Ctg, _, _, _, _) :-
	'$lgt_entity_property_provides'(Ctg, Predicate, To, Properties).
'$lgt_category_property'(calls(Predicate, Properties), Ctg, _, _, _, _) :-
	'$lgt_entity_property_calls'(Ctg, Predicate, Properties).
'$lgt_category_property'(updates(Predicate, Properties), Ctg, _, _, _, _) :-
	'$lgt_entity_property_updates'(Ctg, Predicate, Properties).
'$lgt_category_property'(alias(Alias, Properties), Ctg, _, _, _, _) :-
	'$lgt_entity_property_alias'(Ctg, Alias, Properties).
'$lgt_category_property'(number_of_clauses(Total), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, number_of_clauses(Total, _)) ->
		true
	;	fail
	).
'$lgt_category_property'(number_of_rules(Total), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, number_of_rules(Total, _)) ->
		true
	;	fail
	).
'$lgt_category_property'(number_of_user_clauses(TotalUser), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, number_of_clauses(_, TotalUser)) ->
		true
	;	fail
	).
'$lgt_category_property'(number_of_user_rules(TotalUser), Ctg, _, _, _, _) :-
	(	'$lgt_entity_property_'(Ctg, number_of_rules(_, TotalUser)) ->
		true
	;	fail
	).


'$lgt_category_property_resources'(Ctg, Dcl, Flags, Scope, Resources) :-
	% the caller uses this predicate to group object resources by scope
	findall(
		Resource,
		'$lgt_category_property_resource'(Ctg, Dcl, Flags, Scope, Resource),
		Resources
	).


'$lgt_category_property_resource'(Ctg, Dcl, _, Scope, Functor/Arity) :-
	call(Dcl, Predicate, Scope, _, _, Ctg),
	functor(Predicate, Functor, Arity).

'$lgt_category_property_resource'(Ctg, _, _, Scope, op(Priority, Specifier, Operator)) :-
	'$lgt_entity_property_'(Ctg, op(Priority, Specifier, Operator, Scope)).


% protocol_property(?protocol_identifier, ?protocol_property)
%
% the implementation ensures that no spurious choice-points are
% created when the predicate is called with a bound property argument

protocol_property(Ptc, Prop) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_protocol_property'(Ptc, Prop, ExCtx).


'$lgt_protocol_property'(Ptc, Prop, ExCtx) :-
	'$lgt_check'(var_or_protocol_identifier, Ptc, logtalk(protocol_property(Ptc, Prop), ExCtx)),
	'$lgt_check'(var_or_protocol_property, Prop, logtalk(protocol_property(Ptc, Prop), ExCtx)),
	'$lgt_current_protocol_'(Ptc, _, Dcl, Rnm, Flags),
	'$lgt_protocol_property'(Prop, Ptc, Dcl, Rnm, Flags).


'$lgt_protocol_property'(debugging, _, _, _, Flags) :-
	Flags /\ 512 =:= 512.
'$lgt_protocol_property'(source_data, _, _, _, Flags) :-
	Flags /\ 8 =:= 8.
'$lgt_protocol_property'((dynamic), _, _, _, Flags) :-
	Flags /\ 2 =:= 2.
'$lgt_protocol_property'(static, _, _, _, Flags) :-
	Flags /\ 2 =:= 0.
'$lgt_protocol_property'(built_in, _, _, _, Flags) :-
	Flags /\ 1 =:= 1.
'$lgt_protocol_property'(file(Path), Ptc, _, _, _) :-
	(	'$lgt_entity_property_'(Ptc, file_lines(Basename, Directory, _, _)) ->
		atom_concat(Directory, Basename, Path)
	;	fail
	).
'$lgt_protocol_property'(file(Basename, Directory), Ptc, _, _, _) :-
	(	'$lgt_entity_property_'(Ptc, file_lines(Basename, Directory, _, _)) ->
		true
	;	fail
	).
'$lgt_protocol_property'(lines(Start, End), Ptc, _, _, _) :-
	(	'$lgt_entity_property_'(Ptc, file_lines(_, _, Start, End)) ->
		true
	;	fail
	).
'$lgt_protocol_property'(directive(Start, End), Ptc, _, _, _) :-
	(	'$lgt_entity_property_'(Ptc, directive(Start, End)) ->
		true
	;	fail
	).
'$lgt_protocol_property'(info(Info), Ptc, _, _, _) :-
	(	'$lgt_entity_property_'(Ptc, info(Info)) ->
		true
	;	fail
	).
'$lgt_protocol_property'(public(Resources), Ptc, Dcl, _, Flags) :-
	'$lgt_protocol_property_resources'(Ptc, Dcl, Flags, p(p(p)), Resources).
'$lgt_protocol_property'(protected(Resources), Ptc, Dcl, _, Flags) :-
	'$lgt_protocol_property_resources'(Ptc, Dcl, Flags, p(p), Resources).
'$lgt_protocol_property'(private(Resources), Ptc, Dcl, _, Flags) :-
	'$lgt_protocol_property_resources'(Ptc, Dcl, Flags, p, Resources).
'$lgt_protocol_property'(declares(Predicate, Properties), Ptc, Dcl, _, _) :-
	'$lgt_protocol_property_declares'(Ptc, Dcl, Predicate, Properties).
'$lgt_protocol_property'(alias(Alias, Properties), Ptc, _, _, _) :-
	'$lgt_entity_property_alias'(Ptc, Alias, Properties).


'$lgt_protocol_property_resources'(Ptc, Dcl, Flags, Scope, Resources) :-
	% the caller uses this predicate to group object resources by scope
	findall(
		Resource,
		'$lgt_protocol_property_resource'(Ptc, Dcl, Flags, Scope, Resource),
		Resources
	).


'$lgt_protocol_property_resource'(Ptc, Dcl, _, Scope, Functor/Arity) :-
	call(Dcl, Predicate, Scope, _, _, Ptc),
	functor(Predicate, Functor, Arity).

'$lgt_protocol_property_resource'(Ptc, _, _, Scope, op(Priority, Specifier, Operator)) :-
	'$lgt_entity_property_'(Ptc, op(Priority, Specifier, Operator, Scope)).


'$lgt_object_property_declares'(Obj, Dcl, DDcl, EntityFlags, Functor/Arity, Properties) :-
	(	call(Dcl, Predicate, Scope, Meta, Flags)
	;	EntityFlags /\ 128 =:= 128,
		% dynamic predicate declarations are allowed
		call(DDcl, Predicate, Scope),
		Meta = no,
		Flags = 2
	),
	functor(Predicate, Functor, Arity),
	'$lgt_scope'(ScopeAsAtom, Scope),
	'$lgt_entity_property_declares'(Obj, Functor/Arity, ScopeAsAtom, Meta, Flags, Properties).


'$lgt_category_property_declares'(Ctg, Dcl, Functor/Arity, Properties) :-
	call(Dcl, Predicate, Scope, Meta, Flags, Ctg),
	functor(Predicate, Functor, Arity),
	'$lgt_scope'(ScopeAsAtom, Scope),
	'$lgt_entity_property_declares'(Ctg, Functor/Arity, ScopeAsAtom, Meta, Flags, Properties).


'$lgt_protocol_property_declares'(Ptc, Dcl, Functor/Arity, Properties) :-
	call(Dcl, Predicate, Scope, Meta, Flags, Ptc),
	functor(Predicate, Functor, Arity),
	'$lgt_scope'(ScopeAsAtom, Scope),
	'$lgt_entity_property_declares'(Ptc, Functor/Arity, ScopeAsAtom, Meta, Flags, Properties).


'$lgt_entity_property_declares'(Entity, Functor/Arity, Scope, Meta, Flags, Properties) :-
	(	'$lgt_predicate_property_'(Entity, Functor/Arity, info(Info)) ->
		Properties0 = [info(Info)]
	;	Properties0 = []
	),
	findall(mode(Mode, Solutions), '$lgt_predicate_property_'(Entity, Functor/Arity, mode(Mode, Solutions)), Modes),
	'$lgt_append'(Modes, Properties0, Properties1),
	(	'$lgt_predicate_property_'(Entity, Functor/Arity, declaration_location(Location)) ->
		(	Location = include(File, BeginLine-EndLine) ->
			Properties2 = [include(File), lines(BeginLine,EndLine), line_count(BeginLine)| Properties1]
		;	Location = BeginLine-EndLine,
			Properties2 = [lines(BeginLine,EndLine), line_count(BeginLine)| Properties1]
		)
	;	Properties2 = Properties1
	),
	(	%Flags /\ 64 =:= 64,
		Meta == no ->
		Properties7 = Properties6
	;	Properties7 = [meta_predicate(Meta)| Properties6]
	),
	(	Flags /\ 32 =:= 32,
		'$lgt_predicate_property_'(Entity, Functor/Arity, coinductive(Template)) ->
		Properties3 = [coinductive(Template)| Properties2]
	;	Properties3 = Properties2
	),
	(	Flags /\ 16 =:= 16 ->
		Properties4 = [(multifile)| Properties3]
	;	Properties4 = Properties3
	),
	(	Flags /\ 8 =:= 8 ->
		Arity2 is Arity - 2,
		Properties5 = [non_terminal(Functor//Arity2)| Properties4]
	;	Properties5 = Properties4
	),
	(	Flags /\ 4 =:= 4 ->
		Properties6 = [synchronized| Properties5]
	;	Properties6 = Properties5
	),
	(	Flags /\ 2 =:= 2 ->
		Properties = [Scope, scope(Scope), (dynamic)| Properties7]
	;	Properties = [Scope, scope(Scope), static| Properties7]
	).


'$lgt_object_property_defines'(Obj, Def, DDef, Functor/Arity, Flags, Properties) :-
	(	call(Def, Predicate, _, _)
	;	call(DDef, Predicate, _, _)
	),
	functor(Predicate, Functor, Arity),
	'$lgt_entity_property_defines'(Obj, Functor/Arity, Flags, Properties).


'$lgt_category_property_defines'(Ctg, Def, Functor/Arity, Flags, Properties) :-
	call(Def, Predicate, _, _, Ctg),
	functor(Predicate, Functor, Arity),
	'$lgt_entity_property_defines'(Ctg, Functor/Arity, Flags, Properties).


'$lgt_entity_property_defines'(Entity, Functor/Arity, _, Properties) :-
	'$lgt_predicate_property_'(Entity, Functor/Arity, flags_clauses_rules_location(Flags, Clauses, Rules, Location)),
	!,
	(	Location = include(File, BeginLine-EndLine) ->
		Properties0 = [include(File), lines(BeginLine,EndLine), line_count(BeginLine), number_of_clauses(Clauses), number_of_rules(Rules)]
	;	Location == 0-0 ->
		% auxiliary predicate
		Properties0 = [number_of_clauses(Clauses), number_of_rules(Rules)]
	;	Location = BeginLine-EndLine,
		Properties0 = [lines(BeginLine,EndLine), line_count(BeginLine), number_of_clauses(Clauses), number_of_rules(Rules)]
	),
	(	Flags /\ 8 =:= 8 ->
		Properties1 = [recursive| Properties0]
	;	Properties1 = Properties0
	),
	(	Flags /\ 4 =:= 4 ->
		Properties2 = [inline| Properties1]
	;	Properties2 = Properties1
	),
	(	Flags /\ 2 =:= 2 ->
		Arity2 is Arity - 2,
		Properties3 = [non_terminal(Functor//Arity2)| Properties2]
	;	Properties3 = Properties2
	),
	(	Flags /\ 1 =:= 1 ->
		Properties = [auxiliary| Properties3]
	;	Properties = Properties3
	).
% likely a dynamic or a multifile predicate with no local clauses
'$lgt_entity_property_defines'(_, _, Flags, [number_of_clauses(0), number_of_rules(0)]) :-
	Flags /\ 2 =:= 0,
	% static entity
	!.

% dynamically created entity
'$lgt_entity_property_defines'(_, _, _, []).


'$lgt_entity_property_includes'(Entity, Functor/Arity, From, Properties) :-
	'$lgt_predicate_property_'(From, Functor/Arity, clauses_rules_location_to(Clauses, Rules, Location, Entity)),
	(	Location = include(File, BeginLine-EndLine) ->
		LocationProperties = [include(File), lines(BeginLine,EndLine), line_count(BeginLine)]
	;	Location = BeginLine-EndLine,
		LocationProperties = [lines(BeginLine,EndLine), line_count(BeginLine)]
	),
	Properties = [number_of_clauses(Clauses), number_of_rules(Rules)| LocationProperties].


'$lgt_entity_property_provides'(Entity, Functor/Arity, To, Properties) :-
	'$lgt_predicate_property_'(Entity, Functor/Arity, clauses_rules_location_to(Clauses, Rules, Location, To)),
	(	Location = include(File, BeginLine-EndLine) ->
		LocationProperties = [include(File), lines(BeginLine,EndLine), line_count(BeginLine)]
	;	Location = BeginLine-EndLine,
		LocationProperties = [lines(BeginLine,EndLine), line_count(BeginLine)]
	),
	Properties = [number_of_clauses(Clauses), number_of_rules(Rules)| LocationProperties].


'$lgt_entity_property_alias'(Entity, Alias, Properties) :-
	'$lgt_entity_property_'(Entity, object_alias(Original, Alias, Location)),
	(	Location = include(File, BeginLine-EndLine) ->
		Properties = [object, for(Original), include(File), lines(BeginLine,EndLine), line_count(BeginLine)]
	;	Location = BeginLine-EndLine,
		Properties = [object, for(Original), lines(BeginLine,EndLine), line_count(BeginLine)]
	).

'$lgt_entity_property_alias'(Entity, Alias, Properties) :-
	'$lgt_entity_property_'(Entity, module_alias(Original, Alias, Location)),
	(	Location = include(File, BeginLine-EndLine) ->
		Properties = [module, for(Original), include(File), lines(BeginLine,EndLine), line_count(BeginLine)]
	;	Location = BeginLine-EndLine,
		Properties = [module, for(Original), lines(BeginLine,EndLine), line_count(BeginLine)]
	).

'$lgt_entity_property_alias'(Entity, AliasFunctor/Arity, Properties) :-
	'$lgt_entity_property_'(Entity, predicate_alias(From, OriginalFunctor/Arity, AliasFunctor/Arity, NonTerminalFlag, Location)),
	(	Location = include(File, BeginLine-EndLine) ->
		LocationProperties = [include(File), lines(BeginLine,EndLine), line_count(BeginLine)]
	;	Location = BeginLine-EndLine,
		LocationProperties = [lines(BeginLine,EndLine), line_count(BeginLine)]
	),
	(	NonTerminalFlag =:= 1 ->
		Arity2 is Arity - 2,
		Properties = [predicate, for(OriginalFunctor/Arity), from(From), non_terminal(AliasFunctor//Arity2)| LocationProperties]
	;	Properties = [predicate, for(OriginalFunctor/Arity), from(From)| LocationProperties]
	).


'$lgt_entity_property_calls'(Entity, Call, Properties) :-
	'$lgt_entity_property_'(Entity, calls(Call, Caller, Alias, NonTerminal, Location)),
	(	NonTerminal == no ->
		NonTerminalProperty = []
	;	NonTerminalProperty = [non_terminal(NonTerminal)]
	),
	(	Location = include(File, BeginLine-EndLine) ->
		LocationProperties = [include(File), lines(BeginLine,EndLine), line_count(BeginLine)| NonTerminalProperty]
	;	Location = BeginLine-EndLine,
		LocationProperties = [lines(BeginLine,EndLine), line_count(BeginLine)| NonTerminalProperty]
	),
	(	Alias == no ->
		OtherProperties = LocationProperties
	;	OtherProperties = [alias(Alias)| LocationProperties]
	),
	Properties = [caller(Caller)| OtherProperties].


'$lgt_entity_property_updates'(Entity, Predicate, Properties) :-
	'$lgt_entity_property_'(Entity, updates(Predicate, Updater, Alias, NonTerminal, Location)),
	(	NonTerminal == no ->
		NonTerminalProperty = []
	;	NonTerminalProperty = [non_terminal(NonTerminal)]
	),
	(	Location = include(File, BeginLine-EndLine) ->
		LocationProperties = [include(File), lines(BeginLine,EndLine), line_count(BeginLine)| NonTerminalProperty]
	;	Location = BeginLine-EndLine,
		LocationProperties = [lines(BeginLine,EndLine), line_count(BeginLine)| NonTerminalProperty]
	),
	(	Alias == no ->
		OtherProperties = LocationProperties
	;	OtherProperties = [alias(Alias)| LocationProperties]
	),
	Properties = [updater(Updater)| OtherProperties].



% create_object(?object_identifier, +list, +list, +list)

create_object(Obj, Relations, Directives, Clauses) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_create_object'(Obj, Relations, Directives, Clauses, ExCtx).


'$lgt_create_object'(Obj, Relations, Directives, Clauses, ExCtx) :-
	nonvar(Obj),
	(	\+ callable(Obj),
		throw(error(type_error(object_identifier, Obj), logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)))
	;	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _),
		throw(error(permission_error(modify, object, Obj), logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)))
	;	'$lgt_current_category_'(Obj, _, _, _, _, _),
		throw(error(permission_error(modify, category, Obj), logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)))
	;	'$lgt_current_protocol_'(Obj, _, _, _, _),
		throw(error(permission_error(modify, protocol, Obj), logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)))
	;	functor(Obj, '{}', 1),
		throw(error(permission_error(create, object, Obj), logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)))
	).

'$lgt_create_object'(Obj, Relations, Directives, Clauses, ExCtx) :-
	'$lgt_check'(list, Relations, logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)),
	'$lgt_check'(list, Directives, logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)),
	'$lgt_check'(list, Clauses, logtalk(create_object(Obj, Relations, Directives, Clauses), ExCtx)),
	catch(
		'$lgt_create_object_checked'(Obj, Relations, Directives, Clauses),
		Error,
		'$lgt_create_entity_error_handler'(Error, create_object(Obj, Relations, Directives, Clauses), ExCtx)
	).


'$lgt_create_object_checked'(Obj, Relations, Directives, Clauses) :-
	(	var(Obj) ->
		'$lgt_generate_entity_identifier'(object, Obj)
	;	true
	),
	% set the initial compilation context for compiling the object directives and clauses
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, runtime, _, '-'(-1, -1), _),
	% we need to compile the object relations first as we need to know if we are compiling
	% a prototype or an instance/class when compiling the object identifier as the generated
	% internal functors are different for each case
	'$lgt_compile_object_relations'(Relations, Obj, Ctx),
	'$lgt_compile_object_identifier'(Obj, Ctx),
	assertz('$lgt_pp_dynamic_'),
	'$lgt_compile_logtalk_directives'(Directives, Ctx),
	% the list of clauses may also include grammar rules
	'$lgt_compile_runtime_terms'(Clauses),
	'$lgt_generate_def_table_clauses'(Ctx),
	'$lgt_compile_predicate_calls'(runtime),
	'$lgt_generate_object_clauses',
	'$lgt_generate_object_directives',
	'$lgt_assert_dynamic_entity'(object),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_cc_clauses',
	'$lgt_clean_pp_object_clauses',
	'$lgt_clean_pp_runtime_clauses'.



% create_category(?category_identifier, +list, +list, +list)

create_category(Ctg, Relations, Directives, Clauses) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_create_category'(Ctg, Relations, Directives, Clauses, ExCtx).


'$lgt_create_category'(Ctg, Relations, Directives, Clauses, ExCtx) :-
	nonvar(Ctg),
	(	\+ callable(Ctg),
		throw(error(type_error(category_identifier, Ctg), logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)))
	;	'$lgt_current_category_'(Ctg, _, _, _, _, _),
		throw(error(permission_error(modify, category, Ctg), logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)))
	;	'$lgt_current_object_'(Ctg, _, _, _, _, _, _, _, _, _, _),
		throw(error(permission_error(modify, object, Ctg), logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)))
	;	'$lgt_current_protocol_'(Ctg, _, _, _, _),
		throw(error(permission_error(modify, protocol, Ctg), logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)))
	).

'$lgt_create_category'(Ctg, Relations, Directives, Clauses, ExCtx) :-
	'$lgt_check'(list, Relations, logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)),
	'$lgt_check'(list, Directives, logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)),
	'$lgt_check'(list, Clauses, logtalk(create_category(Ctg, Relations, Directives, Clauses), ExCtx)),
	catch(
		'$lgt_create_category_checked'(Ctg, Relations, Directives, Clauses),
		Error,
		'$lgt_create_entity_error_handler'(Error, create_category(Ctg, Relations, Directives, Clauses), ExCtx)
	).


'$lgt_create_category_checked'(Ctg, Relations, Directives, Clauses) :-
	(	var(Ctg) ->
		'$lgt_generate_entity_identifier'(category, Ctg)
	;	true
	),
	% set the initial compilation context for compiling the category directives and clauses
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, runtime, _, '-'(-1, -1), _),
	'$lgt_compile_category_identifier'(Ctg, Ctx),
	'$lgt_compile_category_relations'(Relations, Ctg, Ctx),
	assertz('$lgt_pp_dynamic_'),
	'$lgt_compile_logtalk_directives'(Directives, Ctx),
	% the list of clauses may also include grammar rules
	'$lgt_compile_runtime_terms'(Clauses),
	'$lgt_generate_def_table_clauses'(Ctx),
	'$lgt_compile_predicate_calls'(runtime),
	'$lgt_generate_category_clauses',
	'$lgt_generate_category_directives',
	'$lgt_assert_dynamic_entity'(category),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_cc_clauses',
	'$lgt_clean_pp_category_clauses',
	'$lgt_clean_pp_runtime_clauses',
	% complementing categories can invalidate dynamic binding cache entries
	(	'$lgt_member'(Relation, Relations),
		functor(Relation, complements, _) ->
		'$lgt_clean_lookup_caches'
	;	true
	).



% create_protocol(?protocol_identifier, +list, +list)

create_protocol(Ptc, Relations, Directives) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_create_protocol'(Ptc, Relations, Directives, ExCtx).


'$lgt_create_protocol'(Ptc, Relations, Directives, ExCtx) :-
	nonvar(Ptc),
	(	\+ atom(Ptc),
		throw(error(type_error(protocol_identifier, Ptc), logtalk(create_protocol(Ptc, Relations, Directives), ExCtx)))
	;	'$lgt_current_protocol_'(Ptc, _, _, _, _),
		throw(error(permission_error(modify, protocol, Ptc), logtalk(create_protocol(Ptc, Relations, Directives), ExCtx)))
	;	'$lgt_current_object_'(Ptc, _, _, _, _, _, _, _, _, _, _),
		throw(error(permission_error(modify, object, Ptc), logtalk(create_protocol(Ptc, Relations, Directives), ExCtx)))
	;	'$lgt_current_category_'(Ptc, _, _, _, _, _),
		throw(error(permission_error(modify, category, Ptc), logtalk(create_protocol(Ptc, Relations, Directives), ExCtx)))
	).

'$lgt_create_protocol'(Ptc, Relations, Directives, ExCtx) :-
	'$lgt_check'(list, Relations, logtalk(create_protocol(Ptc, Relations, Directives), ExCtx)),
	'$lgt_check'(list, Directives, logtalk(create_protocol(Ptc, Relations, Directives), ExCtx)),
	catch(
		'$lgt_create_protocol_checked'(Ptc, Relations, Directives),
		Error,
		'$lgt_create_entity_error_handler'(Error, create_protocol(Ptc, Relations, Directives), ExCtx)
	).


'$lgt_create_protocol_checked'(Ptc, Relations, Directives) :-
	(	var(Ptc) ->
		'$lgt_generate_entity_identifier'(protocol, Ptc)
	;	true
	),
	% set the initial compilation context for compiling the protocol directives
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, runtime, _, '-'(-1, -1), _),
	'$lgt_compile_protocol_identifier'(Ptc, Ctx),
	'$lgt_compile_protocol_relations'(Relations, Ptc, Ctx),
	assertz('$lgt_pp_dynamic_'),
	'$lgt_compile_logtalk_directives'(Directives, Ctx),
	'$lgt_generate_protocol_clauses',
	'$lgt_generate_protocol_directives',
	'$lgt_assert_dynamic_entity'(protocol),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_cc_clauses',
	'$lgt_clean_pp_protocol_clauses',
	'$lgt_clean_pp_runtime_clauses'.



% '$lgt_generate_entity_identifier'(+atom, -entity_identifier)
%
% generates a new, unique, entity identifier by appending an integer to a base char
%
% note that it's possible to run out of (generated) entity identifiers when using a
% backend Prolog compiler with bounded integer support

'$lgt_generate_entity_identifier'(Kind, Identifier) :-
	retract('$lgt_dynamic_entity_counter_'(Kind, Base, Count)),
	char_code(Base, Code),
	repeat,
		'$lgt_next_integer'(Count, New),
		number_codes(New, Codes),
		atom_codes(Identifier, [Code| Codes]),
	% objects, protocols, and categories share a single namespace and there's
	% no guarantee that a user named entity will not clash with the generated
	% identifier despite the use of a per-entity type base character
	\+ '$lgt_current_protocol_'(Identifier, _, _, _, _),
	\+ '$lgt_current_object_'(Identifier, _, _, _, _, _, _, _, _, _, _),
	\+ '$lgt_current_category_'(Identifier, _, _, _, _, _),
	asserta('$lgt_dynamic_entity_counter_'(Kind, Base, New)),
	!.


'$lgt_next_integer'(I, I).
'$lgt_next_integer'(I, K) :-
	J is I + 1,
	'$lgt_next_integer'(J, K).



% '$lgt_create_entity_error_handler'(@nonvar, @callable, @execution_context)
%
% error handler for the dynamic entity creation built-in predicates;
% handles both compiler first stage and second stage errors

'$lgt_create_entity_error_handler'(error(Error,_), Goal, ExCtx) :-
	!,
	% compiler second stage error; unwrap the error
	'$lgt_create_entity_error_handler'(Error, Goal, ExCtx).

'$lgt_create_entity_error_handler'(Error, Goal, ExCtx) :-
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	throw(error(Error, logtalk(Goal, ExCtx))).



% abolish_object(+object_identifier)

abolish_object(Obj) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_abolish_object'(Obj, ExCtx).


'$lgt_abolish_object'(Obj, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(abolish_object(Obj), ExCtx)),
	'$lgt_abolish_object_checked'(Obj, ExCtx).


'$lgt_abolish_object_checked'(Obj, ExCtx) :-
	(	'$lgt_current_object_'(Obj, _, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, Flags) ->
		(	Flags /\ 2 =:= 2 ->
			% dynamic object
			'$lgt_abolish_entity_predicates'(Def),
			'$lgt_abolish_entity_predicates'(DDef),
			abolish(Dcl/4),
			abolish(Dcl/6),
			abolish(Def/3),
			abolish(Def/5),
			abolish(Super/5),
			abolish(IDcl/6),
			abolish(IDef/5),
			abolish(DDcl/2),
			abolish(DDef/3),
			abolish(Rnm/3),
			retractall('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _)),
			retractall('$lgt_entity_property_'(Obj, _)),
			retractall('$lgt_predicate_property_'(Obj, _, _)),
			retractall('$lgt_extends_object_'(Obj, _, _)),
			retractall('$lgt_instantiates_class_'(Obj, _, _)),
			retractall('$lgt_specializes_class_'(Obj, _, _)),
			retractall('$lgt_implements_protocol_'(Obj, _, _)),
			retractall('$lgt_imports_category_'(Obj, _, _)),
			forall(
				'$lgt_current_engine_'(Obj, Engine, _, _),
				'$lgt_threaded_engine_destroy'(Engine, ExCtx)
			),
			'$lgt_clean_lookup_caches'
		;	throw(error(permission_error(modify, static_object, Obj), logtalk(abolish_object(Obj), ExCtx)))
		)
	;	throw(error(existence_error(object, Obj), logtalk(abolish_object(Obj), ExCtx)))
	).



% abolish_category(+category_identifier)

abolish_category(Ctg) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_abolish_category'(Ctg, ExCtx).


'$lgt_abolish_category'(Ctg, ExCtx) :-
	'$lgt_check'(category_identifier, Ctg, logtalk(abolish_category(Ctg), ExCtx)),
	'$lgt_abolish_category_checked'(Ctg, ExCtx).


'$lgt_abolish_category_checked'(Ctg, ExCtx) :-
	(	'$lgt_current_category_'(Ctg, _, Dcl, Def, Rnm, Flags) ->
		(	Flags /\ 2 =:= 2 ->
			% dynamic category
			'$lgt_abolish_entity_predicates'(Def),
			abolish(Dcl/4),
			abolish(Dcl/5),
			abolish(Def/3),
			abolish(Def/4),
			abolish(Rnm/3),
			retractall('$lgt_current_category_'(Ctg, _, _, _, _, _)),
			retractall('$lgt_entity_property_'(Ctg, _)),
			retractall('$lgt_predicate_property_'(Ctg, _, _)),
			retractall('$lgt_extends_category_'(Ctg, _, _)),
			retractall('$lgt_implements_protocol_'(Ctg, _, _)),
			retractall('$lgt_complemented_object_'(_, Ctg, _, _, _)),
			'$lgt_clean_lookup_caches'
		;	throw(error(permission_error(modify, static_category, Ctg), logtalk(abolish_category(Ctg), ExCtx)))
		)
	;	throw(error(existence_error(category, Ctg), logtalk(abolish_category(Ctg), ExCtx)))
	).



% abolish_protocol(@protocol_identifier)

abolish_protocol(Ptc) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_abolish_protocol'(Ptc, ExCtx).


'$lgt_abolish_protocol'(Ptc, ExCtx) :-
	'$lgt_check'(protocol_identifier, Ptc, logtalk(abolish_protocol(Ptc), ExCtx)),
	'$lgt_abolish_protocol_checked'(Ptc, ExCtx).


'$lgt_abolish_protocol_checked'(Ptc, ExCtx) :-
	(	'$lgt_current_protocol_'(Ptc, _, Dcl, Rnm, Flags) ->
		(	Flags /\ 2 =:= 2 ->
			% dynamic protocol
			abolish(Dcl/4),
			abolish(Dcl/5),
			abolish(Rnm/3),
			retractall('$lgt_current_protocol_'(Ptc, _, _, _, _)),
			retractall('$lgt_entity_property_'(Ptc, _)),
			retractall('$lgt_predicate_property_'(Ptc, _, _)),
			retractall('$lgt_extends_protocol_'(Ptc, _, _)),
			'$lgt_clean_lookup_caches'
		;	throw(error(permission_error(modify, static_protocol, Ptc), logtalk(abolish_protocol(Ptc), ExCtx)))
		)
	;	throw(error(existence_error(protocol, Ptc), logtalk(abolish_protocol(Ptc), ExCtx)))
	).



% '$lgt_abolish_entity_predicates'(+atom)
%
% auxiliary predicate used when abolishing objects and categories

'$lgt_abolish_entity_predicates'(Def) :-
	call(Def, _, _, Call),
		'$lgt_unwrap_compiled_head'(Call, Pred),
		functor(Pred, Functor, Arity),
		abolish(Functor/Arity),
	fail.

'$lgt_abolish_entity_predicates'(_).



% implements_protocol(?object_identifier, ?protocol_identifier)
% implements_protocol(?category_identifier, ?protocol_identifier)

implements_protocol(ObjOrCtg, Ptc) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_implements_protocol'(ObjOrCtg, Ptc, ExCtx).


'$lgt_implements_protocol'(ObjOrCtg, Ptc, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, ObjOrCtg, logtalk(implements_protocol(ObjOrCtg, Ptc), ExCtx)),
	'$lgt_check'(var_or_protocol_identifier, Ptc, logtalk(implements_protocol(ObjOrCtg, Ptc), ExCtx)),
	'$lgt_implements_protocol_'(ObjOrCtg, Ptc, _).



% implements_protocol(?object_identifier, ?protocol_identifier, ?atom)
% implements_protocol(?category_identifier, ?protocol_identifier, ?atom)

implements_protocol(ObjOrCtg, Ptc, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_implements_protocol'(ObjOrCtg, Ptc, Scope, ExCtx).


'$lgt_implements_protocol'(ObjOrCtg, Ptc, Scope, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, ObjOrCtg, logtalk(implements_protocol(ObjOrCtg, Ptc, Scope), ExCtx)),
	'$lgt_check'(var_or_protocol_identifier, Ptc, logtalk(implements_protocol(ObjOrCtg, Ptc, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(implements_protocol(ObjOrCtg, Ptc, Scope), ExCtx)),
	'$lgt_implements_protocol_'(ObjOrCtg, Ptc, Scope).



% imports_category(?object_identifier, ?category_identifier)

imports_category(Obj, Ctg) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_imports_category'(Obj, Ctg, ExCtx).


'$lgt_imports_category'(Obj, Ctg, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(imports_category(Obj, Ctg), ExCtx)),
	'$lgt_check'(var_or_category_identifier, Ctg, logtalk(imports_category(Obj, Ctg), ExCtx)),
	'$lgt_imports_category_'(Obj, Ctg, _).



% imports_category(?object_identifier, ?category_identifier, ?atom)

imports_category(Obj, Ctg, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_imports_category'(Obj, Ctg, Scope, ExCtx).


'$lgt_imports_category'(Obj, Ctg, Scope, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(imports_category(Obj, Ctg, Scope), ExCtx)),
	'$lgt_check'(var_or_category_identifier, Ctg, logtalk(imports_category(Obj, Ctg, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(imports_category(Obj, Ctg, Scope), ExCtx)),
	'$lgt_imports_category_'(Obj, Ctg, Scope).



% instantiates_class(?object_identifier, ?object_identifier)

instantiates_class(Obj, Class) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_instantiates_class'(Obj, Class, ExCtx).


'$lgt_instantiates_class'(Obj, Class, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(instantiates_class(Obj, Class), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Class, logtalk(instantiates_class(Obj, Class), ExCtx)),
	'$lgt_instantiates_class_'(Obj, Class, _).



% instantiates_class(?object_identifier, ?object_identifier, ?atom)

instantiates_class(Obj, Class, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_instantiates_class'(Obj, Class, Scope, ExCtx).


'$lgt_instantiates_class'(Obj, Class, Scope, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(instantiates_class(Obj, Class, Scope), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Class, logtalk(instantiates_class(Obj, Class, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(instantiates_class(Obj, Class, Scope), ExCtx)),
	'$lgt_instantiates_class_'(Obj, Class, Scope).



% specializes_class(?object_identifier, ?object_identifier)

specializes_class(Class, Superclass) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_specializes_class'(Class, Superclass, ExCtx).


'$lgt_specializes_class'(Class, Superclass, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Class, logtalk(specializes_class(Class, Superclass), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Superclass, logtalk(specializes_class(Class, Superclass), ExCtx)),
	'$lgt_specializes_class_'(Class, Superclass, _).



% specializes_class(?object_identifier, ?object_identifier, ?atom)

specializes_class(Class, Superclass, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_specializes_class'(Class, Superclass, Scope, ExCtx).


'$lgt_specializes_class'(Class, Superclass, Scope, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Class, logtalk(specializes_class(Class, Superclass, Scope), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Superclass, logtalk(specializes_class(Class, Superclass, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(specializes_class(Class, Superclass, Scope), ExCtx)),
	'$lgt_specializes_class_'(Class, Superclass, Scope).



% extends_category(?category_identifier, ?category_identifier)

extends_category(Ctg, ExtCtg) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_extends_category'(Ctg, ExtCtg, ExCtx).


'$lgt_extends_category'(Ctg, ExtCtg, ExCtx) :-
	'$lgt_check'(var_or_category_identifier, Ctg, logtalk(extends_category(Ctg, ExtCtg), ExCtx)),
	'$lgt_check'(var_or_category_identifier, ExtCtg, logtalk(extends_category(Ctg, ExtCtg), ExCtx)),
	'$lgt_extends_category_'(Ctg, ExtCtg, _).



% extends_category(?category_identifier, ?category_identifier, ?atom)

extends_category(Ctg, ExtCtg, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_extends_category'(Ctg, ExtCtg, Scope, ExCtx).


'$lgt_extends_category'(Ctg, ExtCtg, Scope, ExCtx) :-
	'$lgt_check'(var_or_category_identifier, Ctg, logtalk(extends_category(Ctg, ExtCtg, Scope), ExCtx)),
	'$lgt_check'(var_or_category_identifier, ExtCtg, logtalk(extends_category(Ctg, ExtCtg, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(extends_category(Ctg, ExtCtg, Scope), ExCtx)),
	'$lgt_extends_category_'(Ctg, ExtCtg, Scope).



% extends_protocol(?protocol_identifier, ?protocol_identifier)

extends_protocol(Ptc, ExtPtc) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_extends_protocol'(Ptc, ExtPtc, ExCtx).


'$lgt_extends_protocol'(Ptc, ExtPtc, ExCtx) :-
	'$lgt_check'(var_or_protocol_identifier, Ptc, logtalk(extends_protocol(Ptc, ExtPtc), ExCtx)),
	'$lgt_check'(var_or_protocol_identifier, ExtPtc, logtalk(extends_protocol(Ptc, ExtPtc), ExCtx)),
	'$lgt_extends_protocol_'(Ptc, ExtPtc, _).



% extends_protocol(?protocol_identifier, ?protocol_identifier, ?atom)

extends_protocol(Ptc, ExtPtc, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_extends_protocol'(Ptc, ExtPtc, Scope, ExCtx).


'$lgt_extends_protocol'(Ptc, ExtPtc, Scope, ExCtx) :-
	'$lgt_check'(var_or_protocol_identifier, Ptc, logtalk(extends_protocol(Ptc, ExtPtc, Scope), ExCtx)),
	'$lgt_check'(var_or_protocol_identifier, ExtPtc, logtalk(extends_protocol(Ptc, ExtPtc, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(extends_protocol(Ptc, ExtPtc, Scope), ExCtx)),
	'$lgt_extends_protocol_'(Ptc, ExtPtc, Scope).



% extends_object(?object_identifier, ?object_identifier)

extends_object(Prototype, Parent) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_extends_object'(Prototype, Parent, ExCtx).


'$lgt_extends_object'(Prototype, Parent, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Prototype, logtalk(extends_object(Prototype, Parent), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Parent, logtalk(extends_object(Prototype, Parent), ExCtx)),
	'$lgt_extends_object_'(Prototype, Parent, _).



% extends_object(?object_identifier, ?object_identifier, ?atom)

extends_object(Prototype, Parent, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_extends_object'(Prototype, Parent, Scope, ExCtx).


'$lgt_extends_object'(Prototype, Parent, Scope, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, Prototype, logtalk(extends_object(Prototype, Parent, Scope), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Parent, logtalk(extends_object(Prototype, Parent, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(extends_object(Prototype, Parent, Scope), ExCtx)),
	'$lgt_extends_object_'(Prototype, Parent, Scope).



% complements_object(?category_identifier, ?object_identifier)

complements_object(Category, Object) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_complements_object'(Category, Object, ExCtx).


'$lgt_complements_object'(Category, Object, ExCtx) :-
	'$lgt_check'(var_or_category_identifier, Category, logtalk(complements_object(Category, Object), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Object, logtalk(complements_object(Category, Object), ExCtx)),
	'$lgt_complemented_object_'(Object, Category, _, _, _).



% conforms_to_protocol(?object_identifier, ?protocol_identifier)
% conforms_to_protocol(?category_identifier, ?protocol_identifier)

conforms_to_protocol(ObjOrCtg, Protocol) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_conforms_to_protocol'(ObjOrCtg, Protocol, ExCtx).


'$lgt_conforms_to_protocol'(ObjOrCtg, Protocol, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, ObjOrCtg, logtalk(conforms_to_protocol(ObjOrCtg, Protocol), ExCtx)),
	'$lgt_check'(var_or_protocol_identifier, Protocol, logtalk(conforms_to_protocol(ObjOrCtg, Protocol), ExCtx)),
	(	var(ObjOrCtg) ->
		'$lgt_conforms_to_protocol_checked'(ObjOrCtg, Protocol, _)
	;	var(Protocol) ->
		'$lgt_conforms_to_protocol_checked'(ObjOrCtg, Protocol, _)
	;	% deterministic query
		'$lgt_conforms_to_protocol_checked'(ObjOrCtg, Protocol, _),
		!
	).



% conforms_to_protocol(?object_identifier, ?protocol_identifier, ?atom)
% conforms_to_protocol(?category_identifier, ?protocol_identifier, ?atom)

conforms_to_protocol(ObjOrCtg, Protocol, Scope) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_conforms_to_protocol'(ObjOrCtg, Protocol, Scope, ExCtx).


'$lgt_conforms_to_protocol'(ObjOrCtg, Protocol, Scope, ExCtx) :-
	'$lgt_check'(var_or_object_identifier, ObjOrCtg, logtalk(conforms_to_protocol(ObjOrCtg, Protocol, Scope), ExCtx)),
	'$lgt_check'(var_or_protocol_identifier, Protocol, logtalk(conforms_to_protocol(ObjOrCtg, Protocol, Scope), ExCtx)),
	'$lgt_check'(var_or_scope, Scope, logtalk(conforms_to_protocol(ObjOrCtg, Protocol, Scope), ExCtx)),
	(	var(ObjOrCtg) ->
		'$lgt_conforms_to_protocol_checked'(ObjOrCtg, Protocol, Scope)
	;	var(Protocol) ->
		'$lgt_conforms_to_protocol_checked'(ObjOrCtg, Protocol, Scope)
	;	% deterministic query
		'$lgt_conforms_to_protocol_checked'(ObjOrCtg, Protocol, Scope),
		!
	).


'$lgt_conforms_to_protocol_checked'(Object, Protocol, Scope) :-
	'$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	(	\+ '$lgt_instantiates_class_'(Object, _, _),
		\+ '$lgt_specializes_class_'(Object, _, _) ->
		'$lgt_prototype_conforms_to_protocol'(Object, Protocol, Scope)
	;	'$lgt_instance_conforms_to_protocol'(Object, Protocol, Scope)
	).

'$lgt_conforms_to_protocol_checked'(Category, Protocol, Scope) :-
	'$lgt_current_category_'(Category, _, _, _, _, _),
	'$lgt_category_conforms_to_protocol'(Category, Protocol, Scope).



'$lgt_prototype_conforms_to_protocol'(Prototype, Protocol, Scope) :-
	'$lgt_complemented_object_'(Prototype, Category, _, _, _),
	'$lgt_category_conforms_to_protocol'(Category, Protocol, Scope).

'$lgt_prototype_conforms_to_protocol'(Prototype, Protocol, Scope) :-
	'$lgt_implements_protocol_'(Prototype, Protocol0, ImplementationScope),
	(	Protocol = Protocol0,
		Scope = ImplementationScope
	;	'$lgt_protocol_conforms_to_protocol'(Protocol0, Protocol, InheritedScope),
		'$lgt_filter_scope'(ImplementationScope, InheritedScope, Scope)
	).

'$lgt_prototype_conforms_to_protocol'(Prototype, Protocol, Scope) :-
	'$lgt_imports_category_'(Prototype, Category, ImportScope),
	'$lgt_category_conforms_to_protocol'(Category, Protocol, InheritedScope),
	'$lgt_filter_scope'(ImportScope, InheritedScope, Scope).

'$lgt_prototype_conforms_to_protocol'(Prototype, Protocol, Scope) :-
	'$lgt_extends_object_'(Prototype, Parent, ExtensionScope),
	'$lgt_prototype_conforms_to_protocol'(Parent, Protocol, InheritedScope),
	'$lgt_filter_scope'(ExtensionScope, InheritedScope, Scope).


'$lgt_instance_conforms_to_protocol'(Instance, Protocol, Scope) :-
	'$lgt_instantiates_class_'(Instance, Class, InstantiationScope),
	'$lgt_class_conforms_to_protocol'(Class, Protocol, InheritedScope),
	'$lgt_filter_scope'(InstantiationScope, InheritedScope, Scope).


'$lgt_class_conforms_to_protocol'(Class, Protocol, Scope) :-
	'$lgt_complemented_object_'(Class, Category, _, _, _),
	'$lgt_category_conforms_to_protocol'(Category, Protocol, Scope).

'$lgt_class_conforms_to_protocol'(Class, Protocol, Scope) :-
	'$lgt_implements_protocol_'(Class, Protocol0, ImplementationScope),
	(	Protocol = Protocol0,
		Scope = ImplementationScope
	;	'$lgt_protocol_conforms_to_protocol'(Protocol0, Protocol, InheritedScope),
		'$lgt_filter_scope'(ImplementationScope, InheritedScope, Scope)
	).

'$lgt_class_conforms_to_protocol'(Class, Protocol, Scope) :-
	'$lgt_imports_category_'(Class, Category, ImportScope),
	'$lgt_category_conforms_to_protocol'(Category, Protocol, InheritedScope),
	'$lgt_filter_scope'(ImportScope, InheritedScope, Scope).

'$lgt_class_conforms_to_protocol'(Class, Protocol, Scope) :-
	'$lgt_specializes_class_'(Class, Superclass, SpecializationScope),
	'$lgt_class_conforms_to_protocol'(Superclass, Protocol, InheritedScope),
	'$lgt_filter_scope'(SpecializationScope, InheritedScope, Scope).


'$lgt_protocol_conforms_to_protocol'(Protocol0, Protocol, Scope) :-
	'$lgt_extends_protocol_'(Protocol0, Protocol1, ExtensionScope),
	(	Protocol = Protocol1,
		Scope = ExtensionScope
	;	'$lgt_protocol_conforms_to_protocol'(Protocol1, Protocol, InheritedScope),
		'$lgt_filter_scope'(ExtensionScope, InheritedScope, Scope)
	).


'$lgt_category_conforms_to_protocol'(Category, Protocol, Scope) :-
	'$lgt_implements_protocol_'(Category, Protocol0, ImplementationScope),
	(	Protocol = Protocol0,
		Scope = ImplementationScope
	;	'$lgt_protocol_conforms_to_protocol'(Protocol0, Protocol, InheritedScope),
		'$lgt_filter_scope'(ImplementationScope, InheritedScope, Scope)
	).

'$lgt_category_conforms_to_protocol'(Category, Protocol, Scope) :-
	'$lgt_extends_category_'(Category, ExtendedCategory, ExtensionScope),
	'$lgt_category_conforms_to_protocol'(ExtendedCategory, Protocol, InheritedScope),
	'$lgt_filter_scope'(ExtensionScope, InheritedScope, Scope).


% public relations don't change predicate scopes
'$lgt_filter_scope'((public), Scope, Scope).
% protected relations change public predicates to protected predicates
'$lgt_filter_scope'(protected, Scope, protected) :-
	Scope \= (private).



% current_event(?event, ?term, ?term, ?term, ?object_identifier)

current_event(Event, Obj, Msg, Sender, Monitor) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_event'(Event, Obj, Msg, Sender, Monitor, ExCtx).


'$lgt_current_event'(Event, Obj, Msg, Sender, Monitor, ExCtx) :-
	'$lgt_check'(var_or_event, Event, logtalk(current_event(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(current_event(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_callable, Msg, logtalk(current_event(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Sender, logtalk(current_event(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Monitor, logtalk(current_event(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_current_event_checked'(Event, Obj, Msg, Sender, Monitor).


'$lgt_current_event_checked'(before, Obj, Msg, Sender, Monitor) :-
	'$lgt_before_event_'(Obj, Msg, Sender, Monitor, _).

'$lgt_current_event_checked'(after, Obj, Msg, Sender, Monitor) :-
	'$lgt_after_event_'(Obj, Msg, Sender, Monitor, _).



% define_events(@term, @term, @term, @term, +object_identifier)

define_events(Event, Obj, Msg, Sender, Monitor) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_define_events'(Event, Obj, Msg, Sender, Monitor, ExCtx).


'$lgt_define_events'(Event, Obj, Msg, Sender, Monitor, ExCtx) :-
	'$lgt_check'(var_or_event, Event, logtalk(define_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(define_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_callable, Msg, logtalk(define_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Sender, logtalk(define_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(object_identifier, Monitor, logtalk(define_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	(	'$lgt_current_object_'(Monitor, _, _, Def, _, _, _, _, _, _, _) ->
		'$lgt_execution_context'(MonitorExCtx, _, Monitor, Monitor, Monitor, [], []),
		(	var(Event) ->
			'$lgt_define_events'(before, Obj, Msg, Sender, Monitor, Def, MonitorExCtx),
			'$lgt_define_events'(after, Obj, Msg, Sender, Monitor, Def, MonitorExCtx)
		;	Event == before ->
			'$lgt_define_events'(before, Obj, Msg, Sender, Monitor, Def, MonitorExCtx)
		;	% Event == after
			'$lgt_define_events'(after, Obj, Msg, Sender, Monitor, Def, MonitorExCtx)
		)
	;	throw(error(existence_error(object, Monitor), logtalk(define_events(Event, Obj, Msg, Sender, Monitor), ExCtx)))
	).


'$lgt_define_events'(before, Obj, Msg, Sender, Monitor, Def, ExCtx) :-
	(	call(Def, before(Obj, Msg, Sender), ExCtx, Call, _, _) ->
		retractall('$lgt_before_event_'(Obj, Msg, Sender, Monitor, _)),
		assertz('$lgt_before_event_'(Obj, Msg, Sender, Monitor, Call))
	;	throw(error(existence_error(procedure, before/3), logtalk(define_events(before, Obj, Msg, Sender, Monitor), ExCtx)))
	).

'$lgt_define_events'(after, Obj, Msg, Sender, Monitor, Def, ExCtx) :-
	(	call(Def, after(Obj, Msg, Sender), ExCtx, Call, _, _) ->
		retractall('$lgt_after_event_'(Obj, Msg, Sender, Monitor, _)),
		assertz('$lgt_after_event_'(Obj, Msg, Sender, Monitor, Call))
	;	throw(error(existence_error(procedure, after/3), logtalk(define_events(after, Obj, Msg, Sender, Monitor), ExCtx)))
	).



% abolish_events(@term, @term, @term, @term, @term)

abolish_events(Event, Obj, Msg, Sender, Monitor) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_abolish_events'(Event, Obj, Msg, Sender, Monitor, ExCtx).


'$lgt_abolish_events'(Event, Obj, Msg, Sender, Monitor, ExCtx) :-
	'$lgt_check'(var_or_event, Event, logtalk(abolish_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Obj, logtalk(abolish_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_callable, Msg, logtalk(abolish_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Sender, logtalk(abolish_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	'$lgt_check'(var_or_object_identifier, Monitor, logtalk(abolish_events(Event, Obj, Msg, Sender, Monitor), ExCtx)),
	(	var(Event) ->
		retractall('$lgt_before_event_'(Obj, Msg, Sender, Monitor, _)),
		retractall('$lgt_after_event_'(Obj, Msg, Sender, Monitor, _))
	;	Event == before ->
		retractall('$lgt_before_event_'(Obj, Msg, Sender, Monitor, _))
	;	% Event == after
		retractall('$lgt_after_event_'(Obj, Msg, Sender, Monitor, _))
	).



% built-in multi-threading meta-predicates


% threaded(+callable)

threaded(Goals) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded(Goals), ExCtx))).

threaded(Goals) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_check'(qualified_callable, Goals, logtalk(threaded(Goals), ExCtx)),
	'$lgt_compile_threaded_call'(Goals, MTGoals),
	catch(MTGoals, Error, '$lgt_runtime_error_handler'(Error)).


% threaded_call(@callable, -nonvar)

threaded_call(Goal, Tag) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_call(Goal, Tag), ExCtx))).

threaded_call(Goal, Tag) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_call_tagged'(Goal, Goal, ExCtx, Tag), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_call(@callable)

threaded_call(Goal) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_call(Goal), ExCtx))).

threaded_call(Goal) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_call'(Goal, Goal, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_once(@callable, -nonvar)

threaded_once(Goal, Tag) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_once(Goal, Tag), ExCtx))).

threaded_once(Goal, Tag) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_once_tagged'(Goal, Goal, ExCtx, Tag), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_once(@callable)

threaded_once(Goal) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_once(Goal), ExCtx))).

threaded_once(Goal) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_once'(Goal, Goal, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_ignore(@callable)

threaded_ignore(Goal) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_ignore(Goal), ExCtx))).

threaded_ignore(Goal) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_ignore'(Goal, Goal, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_exit(+callable, +nonvar)

threaded_exit(Goal, Tag) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_exit(Goal, Tag), ExCtx))).

threaded_exit(Goal, Tag) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_exit_tagged'(Goal, ExCtx, Tag), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_exit(+callable)

threaded_exit(Goal) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_exit(Goal), ExCtx))).

threaded_exit(Goal) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_exit'(Goal, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_peek(+callable, +nonvar)

threaded_peek(Goal, Tag) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_peek(Goal, Tag), ExCtx))).

threaded_peek(Goal, Tag) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_peek_tagged'(Goal, ExCtx, Tag), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_peek(+callable)

threaded_peek(Goal) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_peek(Goal), ExCtx))).

threaded_peek(Goal) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_peek'(Goal, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_cancel(+nonvar)

threaded_cancel(Tag) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_cancel(Tag), ExCtx))).

threaded_cancel(Tag) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_cancel_tagged'(Tag, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_create(@term, @callable, ?nonvar)

threaded_engine_create(AnswerTemplate, Goal, Engine) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_create(AnswerTemplate, Goal, Engine), ExCtx))).

threaded_engine_create(AnswerTemplate, Goal, Engine) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_engine_create'(AnswerTemplate, Goal, Goal, ExCtx, Engine), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_self(?nonvar)

threaded_engine_self(Engine) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_self(Engine), ExCtx))).

threaded_engine_self(Engine) :-
	'$lgt_threaded_engine_self'(user, Engine).


% threaded_engine(?nonvar)

threaded_engine(Engine) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine(Engine), ExCtx))).

threaded_engine(Engine) :-
	'$lgt_current_engine'(user, Engine).


% threaded_engine_next(@nonvar, ?term)

threaded_engine_next(Engine, Answer) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_next(Engine, Answer), ExCtx))).

threaded_engine_next(Engine, Answer) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_engine_next'(Engine, Answer, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_next_reified(@nonvar, ?term)

threaded_engine_next_reified(Engine, Answer) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_next_reified(Engine, Answer), ExCtx))).

threaded_engine_next_reified(Engine, Answer) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_engine_next_reified'(Engine, Answer, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_yield(@term)

threaded_engine_yield(Answer) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_yield(Answer), ExCtx))).

threaded_engine_yield(Answer) :-
	catch('$lgt_threaded_engine_yield'(Answer, user), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_post(@nonvar, @term)

threaded_engine_post(Engine, Term) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_post(Engine, Term), ExCtx))).

threaded_engine_post(Engine, Term) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_engine_post'(Engine, Term, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_fetch(?term)

threaded_engine_fetch(Term) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_fetch(Term), ExCtx))).

threaded_engine_fetch(Term) :-
	catch('$lgt_threaded_engine_fetch'(Term, user), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_engine_destroy(+nonvar)

threaded_engine_destroy(Engine) :-
	\+ '$lgt_prolog_feature'(engines, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(engines), logtalk(threaded_engine_destroy(Engine), ExCtx))).

threaded_engine_destroy(Engine) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	catch('$lgt_threaded_engine_destroy'(Engine, ExCtx), Error, '$lgt_runtime_error_handler'(Error)).


% threaded_wait(?nonvar)

threaded_wait(Message) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_wait(Message), ExCtx))).

threaded_wait(Message) :-
	'$lgt_current_object_'(user, Prefix, _, _, _, _, _, _, _, _, _),
	'$lgt_threaded_wait'(Message, Prefix).


% threaded_notify(@term)

threaded_notify(Message) :-
	\+ '$lgt_prolog_feature'(threads, supported),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	throw(error(resource_error(threads), logtalk(threaded_notify(Message), ExCtx))).

threaded_notify(Message) :-
	'$lgt_current_object_'(user, Prefix, _, _, _, _, _, _, _, _, _),
	'$lgt_threaded_notify'(Message, Prefix).



% compiling and loading built-in predicates


% '$lgt_compiler_flag'(+atom, ?nonvar)
%
% gets/checks the current value of a compiler flag; the default flag
% values and the backend Prolog feature flags are cached at startup

'$lgt_compiler_flag'(Name, Value) :-
	(	'$lgt_pp_entity_compiler_flag_'(Name, CurrentValue) ->
		% flag value as defined within the entity being compiled
		Value = CurrentValue
	;	'$lgt_pp_file_compiler_flag_'(Name, CurrentValue) ->
		% flag value as defined in the flags argument of the
		% compiling/loading predicates or in the source file
		Value = CurrentValue
	;	'$lgt_current_flag_'(Name, Value)
		% default value for the current Logtalk session,
		% cached or set by calls to the set_logtalk_flag/2 predicate
	).



% logtalk_compile(@source_file_name)
% logtalk_compile(@list(source_file_name))
%
% compiles to disk a source file or list of source files using default flags
%
% top-level calls use the current working directory for resolving any relative
% source file paths while compiled calls in a source file use the source file
% directory by default

logtalk_compile(Files) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_directory'(Directory),
	'$lgt_logtalk_compile'(Files, Directory, ExCtx).


'$lgt_logtalk_compile'(Files, Directory, ExCtx) :-
	catch(
		'$lgt_logtalk_compile_files'(Files, Directory),
		error(Error, _),
		'$lgt_logtalk_compile_error_handler'(Error, Files, ExCtx)
	).


'$lgt_logtalk_compile_files'(Files, Directory) :-
	'$lgt_init_warnings_counter'(logtalk_compile(Files)),
	'$lgt_check_and_expand_source_files'(Files, ExpandedFiles),
	'$lgt_compile_files'(ExpandedFiles, ['$relative_to'(Directory)]),
	'$lgt_report_warning_numbers'(logtalk_compile(Files)),
	'$lgt_clean_pp_file_clauses'.


'$lgt_logtalk_compile_error_handler'(Error, Files, ExCtx) :-
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	throw(error(Error, logtalk(logtalk_compile(Files), ExCtx))).



% logtalk_compile(@source_file_name, @list(compiler_flag))
% logtalk_compile(@list(source_file_name), @list(compiler_flag))
%
% compiles to disk a source file or a list of source files using a list of flags
%
% top-level calls use the current working directory for resolving any relative
% source file paths while compiled calls in a source file use the source file
% directory by default
%
% note that we can only clean the compiler flags after reporting warning numbers as the
% report/1 flag might be included in the list of flags but we cannot test for it as its
% value should only be used in the default code for printing messages

logtalk_compile(Files, Flags) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_directory'(Directory),
	'$lgt_logtalk_compile'(Files, Flags, Directory, ExCtx).


'$lgt_logtalk_compile'(Files, Flags, Directory, ExCtx) :-
	catch(
		'$lgt_logtalk_compile_files'(Files, Flags, Directory),
		error(Error, _),
		'$lgt_logtalk_compile_error_handler'(Error, Files, Flags, ExCtx)
	).


'$lgt_logtalk_compile_files'(Files, Flags, Directory) :-
	'$lgt_init_warnings_counter'(logtalk_compile(Files, Flags)),
	'$lgt_check_and_expand_source_files'(Files, ExpandedFiles),
	'$lgt_check_compiler_flags'(Flags),
	(	'$lgt_member'(relative_to(_), Flags) ->
		'$lgt_compile_files'(ExpandedFiles, Flags)
	;	'$lgt_compile_files'(ExpandedFiles, ['$relative_to'(Directory)| Flags])
	),
	'$lgt_report_warning_numbers'(logtalk_compile(Files, Flags)),
	'$lgt_clean_pp_file_clauses'.


'$lgt_logtalk_compile_error_handler'(Error, Files, Flags, ExCtx) :-
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	throw(error(Error, logtalk(logtalk_compile(Files, Flags), ExCtx))).



% predicates for compilation warning counting and reporting

'$lgt_reset_warnings_counter' :-
	retractall('$lgt_pp_warnings_top_goal_'(_)),
	retractall('$lgt_pp_compiling_warnings_counter_'(_)),
	retractall('$lgt_pp_loading_warnings_counter_'(_)).


'$lgt_init_warnings_counter'(Goal) :-
	(	'$lgt_pp_warnings_top_goal_'(_) ->
		% not top compilation/loading goal; do nothing
		true
	;	% remember top compilation/loading goal
		assertz('$lgt_pp_warnings_top_goal_'(Goal)),
		% initialize compilation warnings counter
		retractall('$lgt_pp_compiling_warnings_counter_'(_)),
		assertz('$lgt_pp_compiling_warnings_counter_'(0)),
		% initialize loading warnings counter
		retractall('$lgt_pp_loading_warnings_counter_'(_)),
		assertz('$lgt_pp_loading_warnings_counter_'(0))
	).


'$lgt_increment_compiling_warnings_counter' :-
	once(retract('$lgt_pp_compiling_warnings_counter_'(Old))),
	New is Old + 1,
	assertz('$lgt_pp_compiling_warnings_counter_'(New)).


'$lgt_increment_loading_warnings_counter' :-
	once(retract('$lgt_pp_loading_warnings_counter_'(Old))),
	New is Old + 1,
	assertz('$lgt_pp_loading_warnings_counter_'(New)).


'$lgt_report_warning_numbers'(Goal) :-
	(	retract('$lgt_pp_warnings_top_goal_'(Goal)),
		% top compilation/loading goal
		retract('$lgt_pp_compiling_warnings_counter_'(CCounter)),
		retract('$lgt_pp_loading_warnings_counter_'(LCounter)) ->
		% report compilation and loading warnings
		'$lgt_print_message'(
			comment(warnings),
			compilation_and_loading_warnings(CCounter, LCounter)
		)
	;	% not top compilation/loading goal
		true
	).



% '$lgt_check_and_expand_source_files'(@nonvar, -nonvar)
% '$lgt_check_and_expand_source_files'(@list, -list)
%
% check if the source file names are valid (but not if the file exists)
% and return their absolute paths when using library notation or when
% they start with an environment variable (assumes environment variables
% use POSIX syntax in Prolog internal file paths)

'$lgt_check_and_expand_source_files'([File| Files], [Path| Paths]) :-
	!,
	'$lgt_check_and_expand_source_file'(File, Path),
	'$lgt_check_and_expand_source_files'(Files, Paths).

'$lgt_check_and_expand_source_files'([], []) :-
	!.

'$lgt_check_and_expand_source_files'(File, Path) :-
	'$lgt_check_and_expand_source_file'(File, Path).


'$lgt_check_and_expand_source_file'(File, Path) :-
	(	atom(File) ->
		'$lgt_prolog_os_file_name'(NormalizedFile, File),
		(	sub_atom(NormalizedFile, 0, 1, _, '$') ->
			'$lgt_expand_path'(NormalizedFile, Path)
		;	Path = NormalizedFile
		)
	;	compound(File),
		File =.. [Library, Basename],
		atom(Basename) ->
		% library notation
		'$lgt_prolog_os_file_name'(NormalizedBasename, Basename),
		(	'$lgt_expand_library_alias'(Library, Directory) ->
			atom_concat(Directory, NormalizedBasename, Path)
		;	throw(error(existence_error(library, Library), _))
		)
	;	% invalid source file specification
		ground(File) ->
		throw(error(type_error(source_file_name, File), _))
	;	throw(error(instantiation_error, _))
	).



% '$lgt_expand_library_alias'(+atom, -atom)
%
% converts a library alias into its corresponding path; uses a depth
% bound to prevent loops (inspired by similar code in SWI-Prolog)

'$lgt_expand_library_alias'(Library, Path) :-
	'$lgt_expand_library_alias'(Library, Path0, 16),
	% expand the library path into an absolute path as it may
	% contain environment variables that need to be expanded
	(	sub_atom(Path0, 0, 1, _, '/') ->
		% this covers the case of embedded applications created in a POSIX system
		% and being run on a Windows system where a path starting with a slash
		% would not be recognized as an absolute path by '$lgt_expand_path'/2
		Path1 = Path0
	;	'$lgt_expand_path'(Path0, Path1)
	),
	% make sure that the library path ends with a slash
	(	sub_atom(Path1, _, 1, 0, '/') ->
		Path = Path1
	;	atom_concat(Path1, '/', Path)
	).


'$lgt_expand_library_alias'(Library, Path, Depth) :-
	logtalk_library_path(Library, Location), !,
	(	compound(Location),
		Location =.. [Prefix, Directory],
		atom(Directory) ->
		% assume library notation (a compound term)
		Depth > 0,
		NewDepth is Depth - 1,
		'$lgt_expand_library_alias'(Prefix, PrefixPath0, NewDepth),
		% make sure that the prefix path ends with a slash
		(	sub_atom(PrefixPath0, _, 1, 0, '/') ->
			atom_concat(PrefixPath0, Directory, Path)
		;	atom_concat(PrefixPath0, '/', PrefixPath1),
			atom_concat(PrefixPath1, Directory, Path)
		)
	;	atom(Location) ->
		% assume the final component of the library path
		Path = Location
	;	ground(Location) ->
		throw(error(type_error(library_path, Location), _))
	;	throw(error(instantiation_error, _))
	).



% '$lgt_check_compiler_flags'(@list)
%
% checks if the compiler flags are valid

'$lgt_check_compiler_flags'([Flag| Flags]) :-
	!,
	(	var(Flag) ->
		throw(error(instantiation_error, _))
	;	Flag =.. [Name, Value] ->
		'$lgt_check'(read_write_flag, Name, _),
		'$lgt_check'(flag_value, Name+Value, _)
	;	% invalid flag syntax
		compound(Flag) ->
		throw(error(domain_error(compiler_flag, Flag), _))
	;	throw(error(type_error(compound, Flag), _))
	),
	'$lgt_check_compiler_flags'(Flags).

'$lgt_check_compiler_flags'([]) :-
	!.

'$lgt_check_compiler_flags'(Flags) :-
	throw(error(type_error(list, Flags), _)).



% '$lgt_set_compiler_flags'(@list)
%
% sets the compiler flags

'$lgt_set_compiler_flags'(Flags) :-
	'$lgt_assert_compiler_flags'(Flags),
	% only one of the optimize and debug flags can be turned on at the same time
	(	'$lgt_member'(optimize(on), Flags) ->
		retractall('$lgt_pp_file_compiler_flag_'(debug, _)),
		assertz('$lgt_pp_file_compiler_flag_'(debug, off))
	;	'$lgt_member'(debug(on), Flags) ->
		retractall('$lgt_pp_file_compiler_flag_'(optimize, _)),
		assertz('$lgt_pp_file_compiler_flag_'(optimize, off))
	;	'$lgt_member'(linter(Linter), Flags) ->
		'$lgt_set_compiler_linter_flag'(Linter)
	;	true
	),
	(	'$lgt_pp_file_compiler_flag_'(hook, HookEntity) ->
		% pre-compile hooks in order to speed up entity compilation
		(	current_object(HookEntity) ->
			'$lgt_comp_ctx'(Ctx, _, _, user, user, user, HookEntity, _, [], [], ExCtx, runtime, [], _, _),
			'$lgt_execution_context'(ExCtx, user, user, user, HookEntity, [], []),
			'$lgt_current_flag_'(events, Events),
			'$lgt_compile_message_to_object'(term_expansion(Term, Terms), HookEntity, TermExpansionGoal, Events, Ctx),
			'$lgt_compile_message_to_object'(goal_expansion(Goal, ExpandedGoal), HookEntity, GoalExpansionGoal, Events, Ctx)
		;	atom(HookEntity),
			'$lgt_prolog_feature'(modules, supported),
			current_module(HookEntity) ->
			TermExpansionGoal = ':'(HookEntity, term_expansion(Term, Terms)),
			GoalExpansionGoal = ':'(HookEntity, goal_expansion(Goal, ExpandedGoal))
		;	throw(error(existence_error(object, HookEntity), _))
		),
		retractall('$lgt_pp_hook_term_expansion_'(_, _)),
		assertz((
			'$lgt_pp_hook_term_expansion_'(Term, Terms) :-
				catch(TermExpansionGoal, Error, '$lgt_term_expansion_error'(HookEntity, Term, Error))
		)),
		retractall('$lgt_pp_hook_goal_expansion_'(_, _)),
		assertz((
			'$lgt_pp_hook_goal_expansion_'(Goal, ExpandedGoal) :-
				catch(GoalExpansionGoal, Error, '$lgt_goal_expansion_error'(HookEntity, Goal, Error))
		))
	;	true
	).


% term-expansion errors result in a warning message and a failure

'$lgt_term_expansion_error'(HookEntity, Term, Error) :-
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_loading_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_print_message'(
			warning(expansion),
			term_expansion_error(File, Lines, Type, Entity, HookEntity, Term, Error)
		)
	;	'$lgt_print_message'(
			warning(expansion),
			term_expansion_error(File, Lines, HookEntity, Term, Error)
		)
	),
	fail.


% goal-expansion errors result in a warning message and a failure

'$lgt_goal_expansion_error'(HookEntity, Goal, Error) :-
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_loading_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_print_message'(
			warning(expansion),
			goal_expansion_error(File, Lines, Type, Entity, HookEntity, Goal, Error)
		)
	;	'$lgt_print_message'(
			warning(expansion),
			goal_expansion_error(File, Lines, HookEntity, Goal, Error)
		)
	),
	fail.



'$lgt_assert_compiler_flags'([]).

'$lgt_assert_compiler_flags'([Flag| Flags]) :-
	Flag =.. [Name, Value],
	retractall('$lgt_pp_file_compiler_flag_'(Name, _)),
	assertz('$lgt_pp_file_compiler_flag_'(Name, Value)),
	'$lgt_assert_compiler_flags'(Flags).



% logtalk_load(@source_file_name)
% logtalk_load(@list(source_file_name))
%
% compiles to disk and then loads to memory a source file or a list of source
% files using default compiler flags
%
% top-level calls use the current working directory for resolving any relative
% source file paths while compiled calls in a source file use the source file
% directory by default

logtalk_load(Files) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_directory'(Directory),
	'$lgt_logtalk_load'(Files, Directory, ExCtx).


'$lgt_logtalk_load'(Files, Directory, ExCtx) :-
	catch(
		'$lgt_logtalk_load_files'(Files, Directory),
		error(Error, _),
		'$lgt_logtalk_load_error_handler'(Error, Files, ExCtx)
	).


'$lgt_logtalk_load_files'(Files, Directory) :-
	'$lgt_init_warnings_counter'(logtalk_load(Files)),
	'$lgt_check_and_expand_source_files'(Files, ExpandedFiles),
	'$lgt_load_files'(ExpandedFiles, ['$relative_to'(Directory)]),
	'$lgt_report_warning_numbers'(logtalk_load(Files)),
	'$lgt_clean_pp_file_clauses'.


'$lgt_logtalk_load_error_handler'(Error, Files, ExCtx) :-
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	throw(error(Error, logtalk(logtalk_load(Files), ExCtx))).



% logtalk_load(@source_file_name, @list(compiler_flag))
% logtalk_load(@list(source_file_name), @list(compiler_flag))
%
% compiles to disk and then loads to memory a source file or a list of source
% files using a list of compiler flags
%
% top-level calls use the current working directory for resolving any relative
% source file paths while compiled calls in a source file use the source file
% directory by default
%
% note that we can only clean the compiler flags after reporting warning
% numbers as the report/1 flag might be in the list of flags but we cannot
% test for it as its value should only be used in the default code for
% printing messages

logtalk_load(Files, Flags) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_directory'(Directory),
	'$lgt_logtalk_load'(Files, Flags, Directory, ExCtx).


'$lgt_logtalk_load'(Files, Flags, Directory, ExCtx) :-
	catch(
		'$lgt_logtalk_load_files'(Files, Flags, Directory),
		error(Error, _),
		'$lgt_logtalk_load_error_handler'(Error, Files, Flags, ExCtx)
	).


'$lgt_logtalk_load_files'(Files, Flags, Directory) :-
	'$lgt_init_warnings_counter'(logtalk_load(Files, Flags)),
	'$lgt_check_and_expand_source_files'(Files, ExpandedFiles),
	'$lgt_check_compiler_flags'(Flags),
	(	'$lgt_member'(relative_to(_), Flags) ->
		'$lgt_load_files'(ExpandedFiles, Flags)
	;	'$lgt_load_files'(ExpandedFiles, ['$relative_to'(Directory)| Flags])
	),
	'$lgt_report_warning_numbers'(logtalk_load(Files, Flags)),
	'$lgt_clean_pp_file_clauses'.


'$lgt_logtalk_load_error_handler'(Error, Files, Flags, ExCtx) :-
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	throw(error(Error, logtalk(logtalk_load(Files, Flags), ExCtx))).



% logtalk_make
%
% reloads all Logtalk source files that have been modified since the
% time they are last loaded

logtalk_make :-
	logtalk_make(all).



% logtalk_make(+atom)
%
% performs a make target

logtalk_make(Target) :-
	(	var(Target) ->
		'$lgt_print_message'(warning(make), no_make_target_specified),
		fail
	;	'$lgt_valid_logtalk_make_target'(Target) ->
		'$lgt_logtalk_make'(Target),
		'$lgt_logtalk_make_target_actions'(Target)
	;	'$lgt_print_message'(warning(make), invalid_make_target(Target)),
		fail
	).


% reload of changed Logtalk source files
'$lgt_valid_logtalk_make_target'(all).
% recompile files in debug mode
'$lgt_valid_logtalk_make_target'(debug).
% recompile files in normal mode
'$lgt_valid_logtalk_make_target'(normal).
% recompile files in optimal mode
'$lgt_valid_logtalk_make_target'(optimal).
% clean all intermediate Prolog files
'$lgt_valid_logtalk_make_target'(clean).
% list missing entities and missing predicates
'$lgt_valid_logtalk_make_target'(check).
% list circular entity references
'$lgt_valid_logtalk_make_target'(circular).
% generate documentation
'$lgt_valid_logtalk_make_target'(documentation).
% clean dynamic binding caches
'$lgt_valid_logtalk_make_target'(caches).


'$lgt_logtalk_make_target_actions'(Target) :-
	logtalk_make_target_action(Target),
	fail.
'$lgt_logtalk_make_target_actions'(_).


% recompilation of source files that failed to load
'$lgt_logtalk_make'(all) :-
	'$lgt_failed_file_'(Path),
	% the following predicate may no longer be defined depending on what caused the failure
	'$lgt_pp_file_paths_flags_'(_, _, Path, _, Flags),
	'$lgt_file_exists'(Path),
	logtalk_load(Path, Flags),
	fail.
'$lgt_logtalk_make'(all) :-
	'$lgt_failed_file_'(Path),
	'$lgt_decompose_file_name'(Path, Directory, Name, Extension),
	atom_concat(Name, Extension, Basename),
	'$lgt_loaded_file_'(Basename, Directory, _, Flags, _, _, _),
	% typically a descendant file failure propagated to a parent file
	'$lgt_file_exists'(Path),
	logtalk_load(Path, Flags),
	fail.
% recompilation of changed source files since last loaded
'$lgt_logtalk_make'(all) :-
	'$lgt_loaded_file_'(Basename, Directory, _, Flags, _, _, LoadingTimeStamp),
	atom_concat(Directory, Basename, Path),
	'$lgt_file_exists'(Path),
	'$lgt_file_modification_time'(Path, CurrentTimeStamp),
	LoadingTimeStamp @< CurrentTimeStamp,
	\+ '$lgt_member'(reload(skip), Flags),
	logtalk_load(Path, Flags),
	fail.
% recompilation of included source files since last loaded
'$lgt_logtalk_make'(all) :-
	'$lgt_included_file_'(Path, _, MainBasename, MainDirectory, LoadingTimeStamp),
	'$lgt_file_exists'(Path),
	'$lgt_file_modification_time'(Path, CurrentTimeStamp),
	LoadingTimeStamp @< CurrentTimeStamp,
	% force reloading by marking the main file loading as failed
	atom_concat(MainDirectory, MainBasename, MainPath),
	assertz('$lgt_failed_file_'(MainPath)),
	'$lgt_loaded_file_'(MainBasename, MainDirectory, _, Flags, _, ObjectFile, _),
	% ensure that the main file is recompiled so that it
	% includes the contents of the modified include file
	(	'$lgt_file_exists'(ObjectFile) ->
		'$lgt_delete_file'(ObjectFile)
	;	true
	),
	'$lgt_file_exists'(MainPath),
	logtalk_load(MainPath, Flags),
	fail.
% recompilation due to a change to the compilation mode (e.g., from "normal" to "debug")
'$lgt_logtalk_make'(all) :-
	% find all files impacted by a change to compilation mode (this excludes all files
	% that are compiled with an explicit compilation mode set using the corresponding
	% compiler option)
	findall(
		file(Path, Flags),
		(	'$lgt_loaded_file_'(Basename, Directory, Mode, Flags, _, _, _),
			'$lgt_changed_compilation_mode'(Mode, Flags),
			atom_concat(Directory, Basename, Path)
		),
		Files
	),
	% filter files that will be reloaded by a parent file that will also be reloaded
	'$lgt_member'(file(Path,Flags), Files),
	\+ (
		'$lgt_parent_file_'(Path, Parent),
		'$lgt_member'(file(Parent,_), Files)
	),
	'$lgt_file_exists'(Path),
	logtalk_load(Path, Flags),
	fail.
'$lgt_logtalk_make'(all) :-
	'$lgt_print_message'(comment(make), modified_files_reloaded).

'$lgt_logtalk_make'(debug) :-
	'$lgt_print_message'(comment(make), reload_files_in_mode(debug)),
	'$lgt_set_compiler_flag'(debug, on),
	'$lgt_logtalk_make'(all).

'$lgt_logtalk_make'(normal) :-
	'$lgt_print_message'(comment(make), reload_files_in_mode(normal)),
	'$lgt_set_compiler_flag'(debug, off),
	'$lgt_set_compiler_flag'(optimize, off),
	'$lgt_logtalk_make'(all).

'$lgt_logtalk_make'(optimal) :-
	'$lgt_print_message'(comment(make), reload_files_in_mode(optimal)),
	'$lgt_set_compiler_flag'(optimize, on),
	'$lgt_logtalk_make'(all).

'$lgt_logtalk_make'(clean) :-
	'$lgt_loaded_file_'(_, _, _, _, _, ObjectFile, _),
	'$lgt_delete_intermediate_files'(ObjectFile),
	fail.
'$lgt_logtalk_make'(clean) :-
	'$lgt_print_message'(comment(make), intermediate_files_deleted).

'$lgt_logtalk_make'(check) :-
	'$lgt_print_message'(comment(make), scanning_for_missing_entities_predicates),
	setof(Protocol, '$lgt_missing_protocol'(Protocol), Protocols),
	'$lgt_print_message'(warning(make), missing_protocols(Protocols)),
	fail.
'$lgt_logtalk_make'(check) :-
	setof(Category, '$lgt_missing_category'(Category), Categories),
	'$lgt_print_message'(warning(make), missing_categories(Categories)),
	fail.
'$lgt_logtalk_make'(check) :-
	setof(Object, '$lgt_missing_object'(Object), Objects),
	'$lgt_print_message'(warning(make), missing_objects(Objects)),
	fail.
'$lgt_logtalk_make'(check) :-
	'$lgt_prolog_feature'(modules, supported),
	setof(Module, '$lgt_missing_module'(Module), Modules),
	'$lgt_print_message'(warning(make), missing_modules(Modules)),
	fail.
'$lgt_logtalk_make'(check) :-
	setof(Predicate, '$lgt_missing_predicate'(Predicate), Predicates),
	'$lgt_print_message'(warning(make), missing_predicates(Predicates)),
	fail.
'$lgt_logtalk_make'(check) :-
	'$lgt_print_message'(comment(make), completed_scanning_for_missing_entities_predicates),
	fail.
'$lgt_logtalk_make'(check) :-
	'$lgt_print_message'(comment(make), scanning_for_duplicated_library_aliases),
	findall(Alias, logtalk_library_path(Alias, _), Aliases),
	setof(Duplicate, Rest^('$lgt_select'(Duplicate, Aliases, Rest), '$lgt_member'(Duplicate, Rest)), Duplicates),
	'$lgt_print_message'(warning(make), duplicated_library_aliases(Duplicates)),
	fail.
'$lgt_logtalk_make'(check) :-
	'$lgt_print_message'(comment(make), scanning_for_library_paths_end_slash),
	findall(Alias-Path, (logtalk_library_path(Alias, Path), atom(Path), \+ sub_atom(Path, _, 1, 0, '/')), Paths),
	Paths \== [],
	'$lgt_print_message'(warning(make), library_paths_dont_end_with_slash(Paths)),
	fail.
'$lgt_logtalk_make'(check) :-
	'$lgt_print_message'(comment(make), completed_scanning_of_library_alias_definitions).

'$lgt_logtalk_make'(circular) :-
	'$lgt_print_message'(comment(make), scanning_for_circular_dependencies),
	setof(CircularReference, '$lgt_circular_reference'(CircularReference), CircularReferences),
	'$lgt_print_message'(warning(make), circular_references(CircularReferences)),
	fail.
'$lgt_logtalk_make'(circular) :-
	'$lgt_print_message'(comment(make), completed_scanning_for_circular_dependencies).

'$lgt_logtalk_make'(documentation) :-
	'$lgt_print_message'(comment(make), running_all_defined_documentation_actions).

'$lgt_logtalk_make'(caches) :-
	'$lgt_clean_lookup_caches',
	'$lgt_print_message'(comment(make), dynamic_binding_caches_deleted).


% deal with changes to the default compilation mode
% when no explicit compilation mode as specified

'$lgt_changed_compilation_mode'(debug, Flags) :-
	\+ '$lgt_member'(debug(_), Flags),
	\+ '$lgt_compiler_flag'(debug, on).

'$lgt_changed_compilation_mode'(optimal, Flags) :-
	\+ '$lgt_member'(optimize(_), Flags),
	\+ '$lgt_compiler_flag'(optimize, on).

'$lgt_changed_compilation_mode'(normal, _) :-
	(	'$lgt_compiler_flag'(debug, on) ->
		true
	;	'$lgt_compiler_flag'(optimize, on)
	).


% find missing entities for logtalk_make(check)

'$lgt_missing_protocol'(Protocol-Reference) :-
	'$lgt_implements_protocol_'(Entity, Protocol, _),
	\+ '$lgt_current_protocol_'(Protocol, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).

'$lgt_missing_protocol'(Protocol-Reference) :-
	'$lgt_extends_protocol_'(Entity, Protocol, _),
	\+ '$lgt_current_protocol_'(Protocol, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).


'$lgt_missing_category'(Category-Reference) :-
	'$lgt_imports_category_'(Entity, Category, _),
	\+ '$lgt_current_category_'(Category, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).

'$lgt_missing_category'(Category-Reference) :-
	'$lgt_extends_category_'(Entity, Category, _),
	\+ '$lgt_current_category_'(Category, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).


'$lgt_missing_object'(Object-Reference) :-
	'$lgt_extends_object_'(Entity, Object, _),
	\+ '$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).

'$lgt_missing_object'(Object-Reference) :-
	'$lgt_instantiates_class_'(Entity, Object, _),
	\+ '$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).

'$lgt_missing_object'(Object-Reference) :-
	'$lgt_specializes_class_'(Entity, Object, _),
	\+ '$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).

'$lgt_missing_object'(Object-Reference) :-
	'$lgt_complemented_object_'(Object, Entity, _, _, _),
	\+ '$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Reference).

'$lgt_missing_object'(Object-Reference) :-
	'$lgt_entity_property_'(Entity, calls(Object::_, _, _, _, Location)),
	% note that the next call always fails when Object is not bound
	\+ '$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	'$lgt_missing_reference'(Entity, Location, Reference).


'$lgt_missing_module'(Module-Reference) :-
	'$lgt_entity_property_'(Entity, calls(':'(Module,_), _, _, _, Location)),
	% note that the next call always fails when Module is not bound;
	% given the call, assume that the backend compiler supports modules
	\+ current_module(Module),
	'$lgt_missing_reference'(Entity, Location, Reference).


% find missing predicates for logtalk_make(check)

'$lgt_missing_predicate'((Object::Predicate)-Reference) :-
	'$lgt_entity_property_'(Entity, calls(Object::Predicate, _, _, _, Location)),
	% the object may only be known at runtime; reject those cases
	nonvar(Object),
	% require loaded objects as the missing objects are already listed
	'$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
	% ignore objects that can forward the predicates calls
	\+ '$lgt_implements_protocol_'(Object, forwarding, _),
	\+ '$lgt_current_predicate'(Object, Predicate, Entity, p(p(p)), _),
	'$lgt_missing_reference'(Entity, Location, Reference).

'$lgt_missing_predicate'((^^Functor/Arity)-Reference) :-
	'$lgt_entity_property_'(Entity, calls(^^Functor/Arity, _, _, _, Location)),
	functor(Template, Functor, Arity),
	(	'$lgt_current_object_'(Entity, _, Dcl, _, _, IDcl, _, _, _, _, _) ->
		(	\+ '$lgt_instantiates_class_'(Entity, _, _),
			\+ '$lgt_specializes_class_'(Entity, _, _) ->
			% prototype
			\+ call(Dcl, Template, _, _, _, _, _)
		;	% instance and/or class
			\+ call(Dcl, Template, _, _, _, _, _),
			\+ call(IDcl, Template, _, _, _, _, _)
		)
	;	'$lgt_current_category_'(Entity, _, Dcl, _, _, _),
		\+ call(Dcl, Template, _, _, _, _)
	),
	'$lgt_missing_reference'(Entity, Location, Reference).

'$lgt_missing_predicate'((::Functor/Arity)-Reference) :-
	'$lgt_entity_property_'(Entity, calls(::Functor/Arity, _, _, _, Location)),
	functor(Template, Functor, Arity),
	(	'$lgt_current_object_'(Entity, _, Dcl, _, _, IDcl, _, _, _, _, _) ->
		(	\+ '$lgt_instantiates_class_'(Entity, _, _),
			\+ '$lgt_specializes_class_'(Entity, _, _) ->
			% prototype
			\+ call(Dcl, Template, _, _, _, _, _)
		;	% instance and/or class
			\+ call(Dcl, Template, _, _, _, _, _),
			\+ call(IDcl, Template, _, _, _, _, _)
		)
	;	'$lgt_current_category_'(Entity, _, Dcl, _, _, _),
		\+ call(Dcl, Template, _, _, _, _)
	),
	'$lgt_missing_reference'(Entity, Location, Reference).

'$lgt_missing_predicate'((Functor/Arity)-Reference) :-
	'$lgt_entity_property_'(Entity, calls(Functor/Arity, _, _, _, Location)),
	(	'$lgt_current_object_'(Entity, _, Dcl, Def, _, _, _, DDcl, DDef, _, Flags) ->
		\+ '$lgt_object_property_declares'(Entity, Dcl, DDcl, Flags, Functor/Arity, _),
		\+ '$lgt_object_property_defines'(Entity, Def, DDef, Functor/Arity, Flags, _)
	;	'$lgt_current_category_'(Entity, _, Dcl, Def, _, Flags),
		\+ '$lgt_category_property_declares'(Entity, Dcl, Functor/Arity, _),
		\+ '$lgt_category_property_defines'(Entity, Def, Functor/Arity, Flags, _)
	),
	'$lgt_missing_reference'(Entity, Location, Reference).

'$lgt_missing_predicate'((':'(Module,Predicate))-Reference) :-
	'$lgt_prolog_feature'(modules, supported),
	'$lgt_entity_property_'(Entity, calls(':'(Module,Predicate), _, _, _, Location)),
	% the module may only be known at runtime; reject those cases
	nonvar(Module),
	% require loaded modules as the missing modules are already listed
	current_module(Module),
	\+ '$lgt_current_module_predicate'(Module, Predicate),
	'$lgt_missing_reference'(Entity, Location, Reference).


% construct reference term for missing entities and predicates

'$lgt_missing_reference'(Entity, reference(Kind,Entity,Path,StartLine)) :-
	% find the entity type
	(	'$lgt_current_protocol_'(Entity, _, _, _, _) ->
		Kind = protocol
	;	'$lgt_current_category_'(Entity, _, _, _, _, _) ->
		Kind = category
	;	'$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, _),
		Kind = object
	),
	% find the reference file and line
	(	'$lgt_entity_property_'(Entity, file_lines(File, Directory, StartLine, _)) ->
		atom_concat(Directory, File, Path)
	;	% dynamically created entity
		Path = '',
		StartLine = -1
	).

'$lgt_missing_reference'(Entity, Location, reference(Kind,Entity,Path,StartLine)) :-
	% find the entity type
	(	'$lgt_current_protocol_'(Entity, _, _, _, _) ->
		Kind = protocol
	;	'$lgt_current_category_'(Entity, _, _, _, _, _) ->
		Kind = category
	;	'$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, _),
		Kind = object
	),
	% find the reference file and line
	(	Location = include(Path, StartLine-_) ->
		% reference found in included file
		true
	;	Location = StartLine-_,
		(	'$lgt_entity_property_'(Entity, file_lines(File, Directory, _, _)) ->
			atom_concat(Directory, File, Path)
		;	% dynamically created entity
			Path = ''
		)
	).


% find circular dependencies for logtalk_make(circular); we only check
% for mutual and triangular dependencies due to the computational cost

'$lgt_circular_reference'((Object1-Object2)-references([Path1-Line1,Path2-Line2])) :-
	'$lgt_current_object_'(Object1, _, _, _, _, _, _, _, _, _, _),
	'$lgt_current_object_'(Object2, _, _, _, _, _, _, _, _, _, _),
	Object1 \== Object2,
	functor(Object1, Functor1, Arity1),
	functor(Object2, Functor2, Arity2),
	Functor1-Arity1 @< Functor2-Arity2,
	(	'$lgt_entity_property_'(Object1, calls(Entity2::_, _, _, _, Line1)),
		nonvar(Entity2), Entity2 = Object2,
		'$lgt_entity_property_'(Object2, calls(Entity1::_, _, _, _, Line2)),
		nonvar(Entity1), Entity1 = Object1 ->
		true
	;	fail
	),
	'$lgt_circular_reference_object_path'(Object1, Path1),
	'$lgt_circular_reference_object_path'(Object2, Path2).

'$lgt_circular_reference'((Object1-Object2-Object3)-references([Path1-Line1,Path2-Line2,Path3-Line3])) :-
	'$lgt_current_object_'(Object1, _, _, _, _, _, _, _, _, _, _),
	'$lgt_current_object_'(Object2, _, _, _, _, _, _, _, _, _, _),
	Object1 \== Object2,
	'$lgt_current_object_'(Object3, _, _, _, _, _, _, _, _, _, _),
	Object1 \== Object3,
	Object2 \== Object3,
	functor(Object1, Functor1, Arity1),
	functor(Object2, Functor2, Arity2),
	Functor1-Arity1 @< Functor2-Arity2,
	functor(Object3, Functor3, Arity3),
	Functor2-Arity2 @< Functor3-Arity3,
	(	'$lgt_entity_property_'(Object1, calls(Entity2::_, _, _, _, Line1)),
		nonvar(Entity2), Entity2 = Object2,
		'$lgt_entity_property_'(Object2, calls(Entity3::_, _, _, _, Line2)),
		nonvar(Entity3), Entity3 = Object3,
		'$lgt_entity_property_'(Object3, calls(Entity1::_, _, _, _, Line3)),
		nonvar(Entity1), Entity1 = Object1 ->
		true
	;	fail
	),
	'$lgt_circular_reference_object_path'(Object1, Path1),
	'$lgt_circular_reference_object_path'(Object2, Path2),
	'$lgt_circular_reference_object_path'(Object3, Path3).


'$lgt_circular_reference_object_path'(Object, Path) :-
	(	'$lgt_entity_property_'(Object, file_lines(File, Directory, _, _)) ->
		atom_concat(Directory, File, Path)
	;	Path = ''
	).



% logtalk_load_context(?atom, ?nonvar)
%
% provides access to the compilation/loading context
%
% this predicate is the Logtalk version of the prolog_load_context/2
% predicate found on some compilers such as Quintus Prolog, SICStus
% Prolog, SWI-Prolog, and YAP
%
% when called from initialization/1 directives, calls to this predicate
% are resolved at compile-time when the key is instantiated

logtalk_load_context(Key, Value) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_logtalk_load_context'(Key, Value, ExCtx).


'$lgt_logtalk_load_context'(Key, Value, ExCtx) :-
	(	var(Key) ->
		'$lgt_logtalk_load_context_checked'(Key, Value)
	;	'$lgt_valid_logtalk_load_context_key'(Key) ->
		'$lgt_logtalk_load_context_checked'(Key, Value)
	;	callable(Key) ->
		throw(error(domain_error(logtalk_load_context_key, Key), logtalk(logtalk_load_context(Key, Value), ExCtx)))
	;	throw(error(type_error(callable, Key), logtalk(logtalk_load_context(Key, Value), ExCtx)))
	).


'$lgt_logtalk_load_context_checked'(source, SourceFile) :-
	'$lgt_pp_file_paths_flags_'(_, _, SourceFile, _, _).

'$lgt_logtalk_load_context_checked'(directory, Directory) :-
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _).

'$lgt_logtalk_load_context_checked'(basename, Basename) :-
	'$lgt_pp_file_paths_flags_'(Basename, _, _, _, _).

'$lgt_logtalk_load_context_checked'(target, ObjectFile) :-
	% full path of the generated intermediate Prolog file
	'$lgt_pp_file_paths_flags_'(_, _, _, ObjectFile, _).

'$lgt_logtalk_load_context_checked'(flags, Flags) :-
	% only returns the explicit flags passed in the second argument
	% of the logtalk_compile/2 and logtalk_load/2 predicates
	'$lgt_pp_file_paths_flags_'(_, _, _, _, Flags).

'$lgt_logtalk_load_context_checked'(entity_name, Entity) :-
	% deprecated key; use entity_identifier key instead
	'$lgt_pp_entity_'(_, Entity, _).

'$lgt_logtalk_load_context_checked'(entity_identifier, Entity) :-
	'$lgt_pp_entity_'(_, Entity, _).

'$lgt_logtalk_load_context_checked'(entity_prefix, Prefix) :-
	'$lgt_pp_entity_'(_, _, Prefix).

'$lgt_logtalk_load_context_checked'(entity_type, Type) :-
	(	'$lgt_pp_module_'(_) ->
		Type = module
	;	'$lgt_pp_entity_'(Type, _, _)
	).

'$lgt_logtalk_load_context_checked'(entity_relation, Relation) :-
	'$lgt_logtalk_load_context_entity_relation'(Relation).

'$lgt_logtalk_load_context_checked'(term, Term) :-
	% full file term being compiled
	'$lgt_pp_term_source_data_'(Term, _, _, _, _).

'$lgt_logtalk_load_context_checked'(variables, Variables) :-
	% variables of the full file term being compiled
	'$lgt_pp_term_source_data_'(Term, _, _, _, _),
	term_variables(Term, Variables).

'$lgt_logtalk_load_context_checked'(variable_names, VariableNames) :-
	% variable names for the full file term being compiled
	'$lgt_pp_term_source_data_'(_, VariableNames, _, _, _).

'$lgt_logtalk_load_context_checked'(variable_names(Term), VariableNames) :-
	% variable names for the full file term being compiled
	'$lgt_pp_term_source_data_'(Term, VariableNames, _, _, _).

'$lgt_logtalk_load_context_checked'(singletons, Singletons) :-
	% singleton variables in the full file term being compiled
	'$lgt_pp_term_source_data_'(_, _, Singletons, _, _).

'$lgt_logtalk_load_context_checked'(singletons(Term), Singletons) :-
	% singleton variables in the full file term being compiled
	'$lgt_pp_term_source_data_'(Term, _, Singletons, _, _).

'$lgt_logtalk_load_context_checked'(parameter_variables, ParameterVariablePairs) :-
	% only succeeds when compiling a parametric entity containing parameter variables
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs).

'$lgt_logtalk_load_context_checked'(file, File) :-
	% when compiling terms from an included file, this key returns the full
	% path of the included file unlike the "source" key which always returns
	% the full path of the main file
	'$lgt_pp_term_source_data_'(_, _, _, File, _).

'$lgt_logtalk_load_context_checked'(term_position, Lines) :-
	% term position of the full file term being compiled
	'$lgt_pp_term_source_data_'(_, _, _, _, Lines).

'$lgt_logtalk_load_context_checked'(stream, Stream) :-
	% avoid a spurious choice-point with some backend Prolog compilers
	stream_property(Stream, alias(logtalk_compiler_input)), !.


'$lgt_logtalk_load_context_entity_relation'(extends_protocol(Ptc, ExtPtc, Scope)) :-
   '$lgt_pp_extended_protocol_'(ExtPtc, Ptc, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(implements_protocol(ObjOrCtg, Ptc, Scope)) :-
   '$lgt_pp_implemented_protocol_'(Ptc, ObjOrCtg, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(extends_category(Ctg, ExtCtg, Scope)) :-
   '$lgt_pp_extended_category_'(ExtCtg, Ctg, _, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(imports_category(Obj, Ctg, Scope)) :-
   '$lgt_pp_imported_category_'(Ctg, Obj, _, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(extends_object(Prototype, Parent, Scope)) :-
   '$lgt_pp_extended_object_'(Parent, Prototype, _, _, _, _, _, _, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(instantiates_class(Obj, Class, Scope)) :-
   '$lgt_pp_instantiated_class_'(Class, Obj, _, _, _, _, _, _, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(specializes_class(Class, Superclass, Scope)) :-
   '$lgt_pp_specialized_class_'(Superclass, Class, _, _, _, _, _, _, _, _, Scope).

'$lgt_logtalk_load_context_entity_relation'(complements_object(Ctg, Obj)) :-
   '$lgt_pp_complemented_object_'(Obj, Ctg, _, _, _).


% lgt_valid_logtalk_load_context_key(@nonvar)

'$lgt_valid_logtalk_load_context_key'(entity_identifier).
'$lgt_valid_logtalk_load_context_key'(entity_prefix).
'$lgt_valid_logtalk_load_context_key'(entity_type).
'$lgt_valid_logtalk_load_context_key'(entity_relation).
'$lgt_valid_logtalk_load_context_key'(source).
'$lgt_valid_logtalk_load_context_key'(file).
'$lgt_valid_logtalk_load_context_key'(basename).
'$lgt_valid_logtalk_load_context_key'(directory).
'$lgt_valid_logtalk_load_context_key'(stream).
'$lgt_valid_logtalk_load_context_key'(target).
'$lgt_valid_logtalk_load_context_key'(flags).
'$lgt_valid_logtalk_load_context_key'(term).
'$lgt_valid_logtalk_load_context_key'(term_position).
'$lgt_valid_logtalk_load_context_key'(variables).
'$lgt_valid_logtalk_load_context_key'(parameter_variables).
'$lgt_valid_logtalk_load_context_key'(variable_names).
'$lgt_valid_logtalk_load_context_key'(variable_names(_)).
'$lgt_valid_logtalk_load_context_key'(singletons).
'$lgt_valid_logtalk_load_context_key'(singletons(_)).



% set_logtalk_flag(+atom, +nonvar)
%
% sets a global flag value
%
% global flag values can always be overridden when compiling and loading source
% files by using either a set_logtalk_flag/2 directive (whose scope is local to
% the file or the entity containing it) or by passing a list of flag values in
% the calls to the logtalk_compile/2 and logtalk_load/2 predicates

set_logtalk_flag(Flag, Value) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_set_logtalk_flag'(Flag, Value, ExCtx).


'$lgt_set_logtalk_flag'(Flag, Value, ExCtx) :-
	'$lgt_check'(read_write_flag, Flag, logtalk(set_logtalk_flag(Flag, Value), ExCtx)),
	'$lgt_check'(flag_value, Flag + Value, logtalk(set_logtalk_flag(Flag, Value), ExCtx)),
	'$lgt_set_compiler_flag'(Flag, Value).


'$lgt_set_compiler_flag'(optimize, Value) :-
	!,
	retractall('$lgt_current_flag_'(optimize, _)),
	assertz('$lgt_current_flag_'(optimize, Value)),
	'$lgt_clean_lookup_caches',
	% only one of the optimize and debug flags can be turned on at the same time
	(	Value == on ->
		retractall('$lgt_current_flag_'(debug, _)),
		assertz('$lgt_current_flag_'(debug, off))
	;	true
	).

'$lgt_set_compiler_flag'(debug, Value) :-
	!,
	retractall('$lgt_current_flag_'(debug, _)),
	assertz('$lgt_current_flag_'(debug, Value)),
	'$lgt_clean_lookup_caches',
	% only one of the optimize and debug flags can be turned on at the same time
	(	Value == on ->
		retractall('$lgt_current_flag_'(optimize, _)),
		assertz('$lgt_current_flag_'(optimize, off))
	;	true
	).

'$lgt_set_compiler_flag'(hook, Value) :-
	!,
	retractall('$lgt_current_flag_'(hook, _)),
	assertz('$lgt_current_flag_'(hook, Value)),
	% pre-compile hook calls for better performance when compiling files
	'$lgt_compile_hooks'(Value).

'$lgt_set_compiler_flag'(linter, Value) :-
	!,
	'$lgt_set_compiler_linter_flag'(Value).

'$lgt_set_compiler_flag'(Flag, Value) :-
	retractall('$lgt_current_flag_'(Flag, _)),
	assertz('$lgt_current_flag_'(Flag, Value)).


'$lgt_set_compiler_linter_flag'(on) :-
	forall(
		'$lgt_linter_flag'(Flag),
		'$lgt_set_compiler_flag'(Flag, warning)
	).

'$lgt_set_compiler_linter_flag'(default) :-
	forall(
		'$lgt_linter_flag'(Flag),
		(	'$lgt_default_flag'(Flag, Default),
			'$lgt_set_compiler_flag'(Flag, Default)
		)
	).

'$lgt_set_compiler_linter_flag'(off) :-
	forall(
		'$lgt_linter_flag'(Flag),
		'$lgt_set_compiler_flag'(Flag, silent)
	).



% current_logtalk_flag(?atom, ?nonvar)
%
% tests/gets flag values

current_logtalk_flag(Flag, Value) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_current_logtalk_flag'(Flag, Value, ExCtx).


'$lgt_current_logtalk_flag'(Flag, Value, ExCtx) :-
	(	var(Flag) ->
		% enumerate, by backtracking, existing flags
		(	'$lgt_valid_flag'(Flag)
		;	'$lgt_user_defined_flag_'(Flag, _, _)
		),
		'$lgt_compiler_flag'(Flag, Value)
	;	'$lgt_valid_flag'(Flag) ->
		'$lgt_compiler_flag'(Flag, Value)
	;	'$lgt_user_defined_flag_'(Flag, _, _) ->
		'$lgt_compiler_flag'(Flag, Value)
	;	% invalid flag; generate error
		'$lgt_check'(flag, Flag, logtalk(current_logtalk_flag(Flag, Value), ExCtx))
	).



% create_logtalk_flag(+atom, +ground, +list)
%
% creates a new flag
%
% based on the specification of the create_prolog_flag/3
% built-in predicate of SWI-Prolog

create_logtalk_flag(Flag, Value, Options) :-
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []),
	'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx).


'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx) :-
	'$lgt_check'(atom, Flag, logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx)),
	'$lgt_check'(ground, Value, logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx)),
	'$lgt_check'(ground, Options, logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx)),
	'$lgt_check'(list, Options, logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx)),
	fail.

'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx) :-
	'$lgt_valid_flag'(Flag),
	throw(error(permission_error(modify,flag,Flag), logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx))).

'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx) :-
	'$lgt_member'(Option, Options),
	Option \= access(_),
	Option \= keep(_),
	Option \= type(_),
	throw(error(domain_error(flag_option,Option), logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx))).

'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx) :-
	'$lgt_member'(access(Access), Options),
	Access \== read_write,
	Access \== read_only,
	throw(error(domain_error(flag_option,access(Access)), logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx))).

'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx) :-
	'$lgt_member'(keep(Keep), Options),
	Keep \== true,
	Keep \== false,
	throw(error(domain_error(flag_option,keep(Keep)), logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx))).

'$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx) :-
	'$lgt_member'(type(Type0), Options),
	(	'$lgt_map_user_defined_flag_type'(Type0, Type) ->
		(	call(Type, Value) ->
			fail
		;	throw(error(type_error(Type0,Value), logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx)))
		)
	;	throw(error(domain_error(flag_option,type(Type0)), logtalk(create_logtalk_flag(Flag, Value, Options), ExCtx)))
	).

'$lgt_create_logtalk_flag'(Flag, _, Options, _) :-
	'$lgt_user_defined_flag_'(Flag, _, _),
	'$lgt_member'(keep(true), Options),
	!.

'$lgt_create_logtalk_flag'(Flag, Value, Options, _) :-
	(	'$lgt_member'(access(Access), Options) ->
		true
	;	Access = read_write
	),
	(	'$lgt_member'(type(Type0), Options) ->
		'$lgt_map_user_defined_flag_type'(Type0, Type)
	;	% infer type from the initial value
		Value == true ->
		Type = '$lgt_is_boolean'
	;	Value == false ->
		Type = '$lgt_is_boolean'
	;	atom(Value) ->
		Type = atom
	;	integer(Value) ->
		Type = integer
	;	float(Value) ->
		Type = float
	;	% catchall
		Type = ground
	),
	retractall('$lgt_user_defined_flag_'(Flag, _, _)),
	assertz('$lgt_user_defined_flag_'(Flag, Access, Type)),
	retractall('$lgt_current_flag_'(Flag, _)),
	assertz('$lgt_current_flag_'(Flag, Value)).


% map the flag type to a closure that can be called with the flag
% value as argument for type-checking
'$lgt_map_user_defined_flag_type'(boolean, '$lgt_is_boolean').
'$lgt_map_user_defined_flag_type'(atom, atom).
'$lgt_map_user_defined_flag_type'(integer, integer).
'$lgt_map_user_defined_flag_type'(float, float).
'$lgt_map_user_defined_flag_type'(term, ground).



% '$lgt_version_data'(?compound)
%
% current Logtalk version for use with the current_logtalk_flag/2 predicate
%
% the last argument is an atom: 'aNN' for alpha versions, 'bNN' for beta
% versions, 'rcNN' for release candidates (with N being a decimal digit),
% and 'stable' for stable versions

'$lgt_version_data'(logtalk(3, 90, 1, stable)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  built-in methods
%
%  calls to these methods are always compiled
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_object_exists'(@var, +callable, +execution_context)
% '$lgt_object_exists'(+object_identifier, +callable, +execution_context)
%
% checks if an object exists at runtime; this is necessary in order to
% prevent trivial messages such as true/0 or repeat/0 from succeeding
% when the target object doesn't exist; used in the compilation of ::/2
% calls

'$lgt_object_exists'(Obj, Pred, ExCtx) :-
	(	var(Obj) ->
		throw(error(instantiation_error, logtalk(Obj::Pred, ExCtx)))
	;	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _) ->
		true
	;	% we have already verified that we have is a valid object identifier
		% when we generated calls to this predicate
		throw(error(existence_error(object, Obj), logtalk(Obj::Pred, ExCtx)))
	).



% '$lgt_current_op'(+object_identifier, ?operator_priority, ?operator_specifier, ?atom, +object_identifier, +scope, @execution_context)
%
% current_op/3 built-in method
%
% local operator declarations without a scope declaration are invisible

'$lgt_current_op'(Obj, Priority, Specifier, Operator, Sender, Scope, ExCtx) :-
	'$lgt_check'(object, Obj, logtalk(Obj::current_op(Priority, Specifier, Operator), ExCtx)),
	'$lgt_check'(var_or_operator_priority, Priority, logtalk(current_op(Priority, Specifier, Operator), ExCtx)),
	'$lgt_check'(var_or_operator_specifier, Specifier, logtalk(current_op(Priority, Specifier, Operator), ExCtx)),
	'$lgt_check'(var_or_atom, Operator, logtalk(current_op(Priority, Specifier, Operator), ExCtx)),
	(	Obj == user ->
		current_op(Priority, Specifier, Operator)
	;	'$lgt_entity_property_'(Obj, op(Priority, Specifier, Operator, OpScope)),
		% don't return local operator declarations
		OpScope \== l,
		% check that the operator declaration is within the scope of the caller
		\+ \+ (OpScope = Scope; Obj = Sender)
	;	% also return global operators that aren't overridden by entity operators
		current_op(Priority, Specifier, Operator),
		\+ (
			'$lgt_entity_property_'(Obj, op(_, OtherSpecifier, Operator, _)),
			'$lgt_same_operator_class'(Specifier, OtherSpecifier)
		)
	).



% '$lgt_current_predicate'(+object_identifier, ?predicate_indicator, +object_identifier, +scope, @execution_context)
%
% current_predicate/1 built-in method
%
% local predicates without a scope declaration are invisible

'$lgt_current_predicate'(Obj, Pred, _, _, ExCtx) :-
	'$lgt_check'(var_or_predicate_indicator, Pred, logtalk(current_predicate(Pred), ExCtx)),
	'$lgt_check'(object, Obj, logtalk(Obj::current_predicate(Pred), ExCtx)),
	fail.

'$lgt_current_predicate'(user, Pred, _, _, _) :-
	!,
	current_predicate(Pred).

'$lgt_current_predicate'(Obj, Functor/Arity, Obj, _, ExCtx) :-
	ground(Functor/Arity),
	functor(Head, Functor, Arity),
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, _, _, Head, Ctx)
	;	'$lgt_use_module_predicate_'(Entity, _, _, Head, Ctx)
	),
	!.

'$lgt_current_predicate'(Obj, Functor/Arity, Sender, LookupScope, _) :-
	ground(Functor/Arity),
	!,
	% make the current_predicate/1 method deterministic when its argument is ground
	'$lgt_current_object_'(Obj, _, Dcl, _, _, _, _, _, _, _, _),
	(	call(Dcl, Pred, PredScope, _, _, SCtn, _),
		functor(Pred, Functor, Arity) ->
		% commit to the first solution found as an inherited
		% predicate can always be re-declared
		(	\+ \+ PredScope = LookupScope ->
			true
		;	Sender = SCtn
		)
	;	fail
	).

'$lgt_current_predicate'(Obj, Functor/Arity, Sender, LookupScope, _) :-
	'$lgt_current_object_'(Obj, _, Dcl, _, _, _, _, _, _, _, _),
	% use findall/3 + sort/2 to avoid a setof/3 call with five
	% existentially-qualified variables or an auxiliary predicate
	findall(Functor/Arity, (call(Dcl, Pred, _, _, _, _, _), functor(Pred, Functor, Arity)), Preds),
	sort(Preds, SortedPreds),
	'$lgt_member'(Functor/Arity, SortedPreds),
	functor(Pred, Functor, Arity),
	(	call(Dcl, Pred, PredScope, _, _, SCtn, _) ->
		% commit to the first solution found as an inherited
		% predicate can always be re-declared
		(	\+ \+ PredScope = LookupScope ->
			true
		;	Sender = SCtn
		)
	;	fail
	).

'$lgt_current_predicate'(Obj, Functor/Arity, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, _, _, Head, Ctx)
	;	'$lgt_use_module_predicate_'(Entity, _, _, Head, Ctx)
	),
	functor(Head, Functor, Arity).



% '$lgt_predicate_property'(+object_identifier, @callable, ?predicate_property, +object_identifier, +scope, @execution_context)
%
% predicate_property/2 built-in method
%
% local predicates without a scope declaration are invisible and Prolog
% built-in predicates are interpreted as private predicates
%
% the implementation ensures that no spurious choice-points are created when
% the method is called with a bound and deterministic property argument

'$lgt_predicate_property'(Obj, Pred, Prop, _, _, ExCtx) :-
	'$lgt_check'(callable, Pred, logtalk(predicate_property(Pred, Prop), ExCtx)),
	'$lgt_check'(var_or_predicate_property, Prop, logtalk(predicate_property(Pred, Prop), ExCtx)),
	'$lgt_check'(object, Obj, logtalk(Obj::predicate_property(Pred, Prop), ExCtx)),
	fail.

'$lgt_predicate_property'(user, Pred, Prop, _, _, _) :-
	!,
	'$lgt_predicate_property'(Pred, Prop).

'$lgt_predicate_property'(Obj, Pred, Prop, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_uses_predicate_'(Entity, Other, Original, Pred, Ctx),
	!,
	'$lgt_predicate_property'(Other, Original, Prop, Obj, p(p(p)), ExCtx).

'$lgt_predicate_property'(Obj, Pred, Prop, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_use_module_predicate_'(Entity, Module, Original, Pred, Ctx),
	!,
	'$lgt_predicate_property'(':'(Module, Original), Prop).

'$lgt_predicate_property'(Obj, Pred, Prop, Sender, LookupScope, _) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, Rnm, ObjFlags),
	call(Dcl, Pred, PredScope, Meta, PredFlags, SCtn, TCtn),
	% predicate declaration found
	!,
	(	\+ \+ PredScope = LookupScope ->
		true
	;	Sender = SCtn
	),
	% query is within scope
	'$lgt_scope'(ScopeAsAtom, PredScope),
	(	'$lgt_current_object_'(TCtn, _, TCtnDcl, _, _, _, _, _, _, _, _) ->
		true
	;	'$lgt_current_protocol_'(TCtn, _, TCtnDcl, _, _) ->
		true
	;	'$lgt_current_category_'(TCtn, _, TCtnDcl, _, _, _)
	),
	(	call(TCtnDcl, Pred, _, _, _) ->
		% found static declaration for the predicate
		'$lgt_predicate_property_user'(Prop, Pred, Pred, Obj, ScopeAsAtom, Meta, PredFlags, TCtn, Obj, Def, Rnm)
	;	PredFlags /\ 2 =:= 2 ->
		% dynamically declared predicate; aliases can only be defined for statically declared predicates
		'$lgt_predicate_property_user'(Prop, Pred, Pred, Obj, ScopeAsAtom, Meta, PredFlags, TCtn, Obj, Def, Rnm)
	;	% no predicate declaration; we may be querying properties of a predicate alias
		'$lgt_find_original_predicate'(Obj, Rnm, ObjFlags, Pred, Original, Entity),
		'$lgt_predicate_property_user'(Prop, Pred, Original, Entity, ScopeAsAtom, Meta, PredFlags, TCtn, Obj, Def, Rnm)
	).

'$lgt_predicate_property'(Obj, Pred, Prop, Sender, LookupScope, _) :-
	'$lgt_built_in_method'(Pred, PredScope, Meta, Flags),
	!,
	(	\+ \+ PredScope = LookupScope ->
		true
	;	Sender = Obj
	),
	'$lgt_scope'(ScopeAsAtom, PredScope),
	'$lgt_predicate_property_built_in_method'(Prop, Pred, ScopeAsAtom, Meta, Flags).

'$lgt_predicate_property'(Obj, Pred, Prop, Obj, _, _) :-
	'$lgt_logtalk_built_in_predicate'(Pred, Meta),
	!,
	'$lgt_predicate_property_logtalk_built_in'(Prop, Meta).

'$lgt_predicate_property'(Obj, Pred, Prop, Obj, _, _) :-
	'$lgt_prolog_built_in_predicate'(Pred),
	!,
	'$lgt_predicate_property_prolog_built_in'(Prop, Pred).


'$lgt_predicate_property_user'(alias_of(Original), Alias, Original, _, _, _, _, _, _, _, _) :-
	Alias \= Original.
'$lgt_predicate_property_user'(alias_declared_in(Entity), Alias, Original, Entity, _, _, _, _, _, _, _) :-
	Alias \= Original.
'$lgt_predicate_property_user'(alias_declared_in(Entity, Line), Alias, Original, Entity, _, _, _, _, _, _, _) :-
	Alias \= Original,
	functor(Original, OriginalFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	'$lgt_entity_property_'(Entity, predicate_alias(_, OriginalFunctor/Arity, AliasFunctor/Arity, _, Line)).
'$lgt_predicate_property_user'(logtalk, _, _, _, _, _, _, _, _, _, _).
'$lgt_predicate_property_user'(scope(Scope), _, _, _, Scope, _, _, _, _, _, _).
'$lgt_predicate_property_user'((public), _, _, _, (public), _, _, _, _, _, _).
'$lgt_predicate_property_user'(protected, _, _, _, protected, _, _, _, _, _, _).
'$lgt_predicate_property_user'((private), _, _, _, (private), _, _, _, _, _, _).
'$lgt_predicate_property_user'((dynamic), _, _, _, _, _, Flags, _, _, _, _) :-
	Flags /\ 2 =:= 2.
'$lgt_predicate_property_user'(static, _, _, _, _, _, Flags, _, _, _, _) :-
	Flags /\ 2 =\= 2.
'$lgt_predicate_property_user'(declared_in(TCtn), _, _, _, _, _, _, TCtn, _, _, _).
'$lgt_predicate_property_user'(declared_in(TCtn, Line), _, Original, _, _, _, _, TCtn, _, _, _) :-
	functor(Original, Functor, Arity),
	(	'$lgt_predicate_property_'(TCtn, Functor/Arity, declaration_location(Location)) ->
		(	Location = include(_, Line-_) ->
			true
		;	Location = Line-_
		)
	;	fail
	).
'$lgt_predicate_property_user'(meta_predicate(Meta), Alias, _, _, _, Meta0, _, _, _, _, _) :-
	Meta0 \== no,
	functor(Alias, AliasFunctor, _),
	Meta0 =.. [_| MetaArgs],
	Meta =.. [AliasFunctor| MetaArgs].
'$lgt_predicate_property_user'(coinductive(Template), Alias, Original, _, _, _, _, TCtn, _, _, _) :-
	functor(Original, Functor, Arity),
	(	'$lgt_predicate_property_'(TCtn, Functor/Arity, coinductive(Template0)) ->
		functor(Alias, AliasFunctor, _),
		Template0 =.. [_| ModeArgs],
		Template =.. [AliasFunctor| ModeArgs]
	;	fail
	).
'$lgt_predicate_property_user'((multifile), _, _, _, _, _, PredFlags, _, _, _, _) :-
	PredFlags /\ 16 =:= 16.
'$lgt_predicate_property_user'(non_terminal(Functor//Arity), Alias, _, _, _, _, PredFlags, _, _, _, _) :-
	PredFlags /\ 8 =:= 8,
	functor(Alias, Functor, ExtArity),
	Arity is ExtArity - 2.
'$lgt_predicate_property_user'(synchronized, _, _, _, _, _, PredFlags, _, _, _, _) :-
	PredFlags /\ 4 =:= 4.
'$lgt_predicate_property_user'(defined_in(DCtn), Alias, _, _, _, _, _, _, _, Def, _) :-
	(	call(Def, Alias, _, _, _, DCtn) ->
		true
	;	fail
	).
'$lgt_predicate_property_user'(defined_in(DCtn, Line), Alias, Original, _, _, _, _, _, _, Def, _) :-
	(	call(Def, Alias, _, _, _, DCtn) ->
		(	functor(Original, Functor, Arity),
			'$lgt_predicate_property_'(DCtn, Functor/Arity, flags_clauses_rules_location(_, _, _, Location)) ->
			(	Location = include(_, Line-_) ->
				true
			;	Location = Line-_
			)
		;	fail
		)
	;	fail
	).
'$lgt_predicate_property_user'(recursive, Alias, Original, _, _, _, _, _, _, Def, _) :-
	(	call(Def, Alias, _, _, _, DCtn) ->
		(	functor(Original, Functor, Arity),
			'$lgt_predicate_property_'(DCtn, Functor/Arity, flags_clauses_rules_location(Flags, _, _, _)) ->
			Flags /\ 8 =:= 8
		;	fail
		)
	;	fail
	).
'$lgt_predicate_property_user'(inline, Alias, Original, _, _, _, _, _, _, Def, _) :-
	(	call(Def, Alias, _, _, _, DCtn) ->
		(	functor(Original, Functor, Arity),
			'$lgt_predicate_property_'(DCtn, Functor/Arity, flags_clauses_rules_location(Flags, _, _, _)) ->
			Flags /\ 4 =:= 4
		;	fail
		)
	;	fail
	).
'$lgt_predicate_property_user'(redefined_from(Super), Alias, _, _, _, _, _, _, Obj, Def, _) :-
	(	call(Def, Alias, _, _, _, DCtn) ->
		'$lgt_find_overridden_predicate'(DCtn, Obj, Alias, Super)
	;	fail
	).
'$lgt_predicate_property_user'(redefined_from(Super, Line), Alias, Original, _, _, _, _, _, Obj, Def, _) :-
	(	call(Def, Alias, _, _, _, DCtn) ->
		(	'$lgt_find_overridden_predicate'(DCtn, Obj, Alias, Super),
			functor(Original, Functor, Arity),
			'$lgt_predicate_property_'(Super, Functor/Arity, flags_clauses_rules_location(_, _, _, Location)) ->
			(	Location = include(_, Line-_) ->
				true
			;	Location = Line-_
			)
		;	fail
		)
	;	fail
	).
'$lgt_predicate_property_user'(info(Info), _, Original, _, _, _, _, TCtn, _, _, _) :-
	functor(Original, Functor, Arity),
	(	'$lgt_predicate_property_'(TCtn, Functor/Arity, info(Info)) ->
		true
	;	fail
	).
'$lgt_predicate_property_user'(mode(Mode, Solutions), Alias, Original, _, _, _, _, TCtn, _, _, _) :-
	functor(Original, Functor, Arity),
	% we cannot make the mode/2 property deterministic as a predicate can support several different modes
	'$lgt_predicate_property_'(TCtn, Functor/Arity, mode(Mode0, Solutions)),
	functor(Alias, AliasFunctor, _),
	Mode0 =.. [_| ModeArgs],
	Mode =.. [AliasFunctor| ModeArgs].
'$lgt_predicate_property_user'(number_of_clauses(N), Alias, Original, _, _, _, PredFlags, _, Obj, Def, _) :-
	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags),
	Flags /\ 2 =:= 0,
	% static object
	(	call(Def, Alias, _, _, _, DCtn) ->
		functor(Original, Functor, Arity),
		(	'$lgt_predicate_property_'(DCtn, Functor/Arity, flags_clauses_rules_location(_, N0, _, _)) ->
			true
		;	N0 is 0
		),
		(	PredFlags /\ 16 =:= 16 ->
			% multifile predicate
			findall(N1, '$lgt_predicate_property_'(_, Functor/Arity, clauses_rules_location_to(N1, _, _, DCtn)), N1s),
			'$lgt_sum_list'([N0| N1s], N)
		;	N is N0
		)
	;	fail
	).
'$lgt_predicate_property_user'(number_of_rules(N), Alias, Original, _, _, _, PredFlags, _, Obj, Def, _) :-
	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags),
	Flags /\ 2 =:= 0,
	% static object
	(	call(Def, Alias, _, _, _, DCtn) ->
		functor(Original, Functor, Arity),
		(	'$lgt_predicate_property_'(DCtn, Functor/Arity, flags_clauses_rules_location(_, _, N0, _)) ->
			true
		;	N0 is 0
		),
		(	PredFlags /\ 16 =:= 16 ->
			% multifile predicate
			findall(N1, '$lgt_predicate_property_'(_, Functor/Arity, clauses_rules_location_to(_, N1, _, DCtn)), N1s),
			'$lgt_sum_list'([N0| N1s], N)
		;	N is N0
		)
	;	fail
	).


'$lgt_predicate_property_built_in_method'(logtalk, _, _, _, _).
'$lgt_predicate_property_built_in_method'(scope(Scope), _, Scope, _, _).
'$lgt_predicate_property_built_in_method'((public), _, (public), _, _).
'$lgt_predicate_property_built_in_method'(protected, _, protected, _, _).
'$lgt_predicate_property_built_in_method'((private), _, (private), _, _).
'$lgt_predicate_property_built_in_method'(built_in, _, _, _, _).	%Flags /\ 1 =:= 1.
'$lgt_predicate_property_built_in_method'((dynamic), _, _, _, Flags) :-
	Flags /\ 2 =:= 2.
'$lgt_predicate_property_built_in_method'(static, _, _, _, Flags) :-
	Flags /\ 2 =\= 2.
'$lgt_predicate_property_built_in_method'(meta_predicate(Meta), _, _, Meta, _) :-
	Meta \== no.
'$lgt_predicate_property_built_in_method'((multifile), _, _, _, Flags) :-
	Flags /\ 16 =:= 16.
'$lgt_predicate_property_built_in_method'(non_terminal(Functor//Arity), Pred, _, _, Flags) :-
	Flags /\ 8 =:= 8,
	functor(Pred, Functor, ExtArity),
	Arity is ExtArity - 2.
'$lgt_predicate_property_built_in_method'(synchronized, _, _, _, Flags) :-
	Flags /\ 4 =:= 4.


'$lgt_predicate_property_logtalk_built_in'(logtalk, _).
'$lgt_predicate_property_logtalk_built_in'(scope(private), _).
'$lgt_predicate_property_logtalk_built_in'((private), _).
'$lgt_predicate_property_logtalk_built_in'(built_in, _).
'$lgt_predicate_property_logtalk_built_in'(static, _).
'$lgt_predicate_property_logtalk_built_in'(meta_predicate(Meta), Meta) :-
	Meta \== no.


'$lgt_predicate_property_prolog_built_in'(foreign, Pred) :-
	catch('$lgt_predicate_property'(Pred, foreign), _, fail).
'$lgt_predicate_property_prolog_built_in'(prolog, Pred) :-
	\+ catch('$lgt_predicate_property'(Pred, foreign), _, fail).
'$lgt_predicate_property_prolog_built_in'(scope(private), _).
'$lgt_predicate_property_prolog_built_in'((private), _).
'$lgt_predicate_property_prolog_built_in'(meta_predicate(Meta), Pred) :-
	'$lgt_prolog_meta_predicate'(Pred, Meta0, _),
	Meta0 =.. [_| MetaArgs0],
	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(MetaArgs0, MetaArgs),
	Meta =.. [_| MetaArgs].
'$lgt_predicate_property_prolog_built_in'(built_in, _).
'$lgt_predicate_property_prolog_built_in'((dynamic), Pred) :-
	'$lgt_predicate_property'(Pred, (dynamic)).
'$lgt_predicate_property_prolog_built_in'(static, Pred) :-
	'$lgt_predicate_property'(Pred, static).
'$lgt_predicate_property_prolog_built_in'((multifile), Pred) :-
	'$lgt_predicate_property'(Pred, (multifile)).



% '$lgt_scope'(?atom, ?nonvar).
%
% converts between user and internal scope representation;
% this representation was chosen as it allows testing if a scope is either
% public or protected by a single unification step with the p(_) term

'$lgt_scope'((private), p).
'$lgt_scope'(protected, p(p)).
'$lgt_scope'((public),  p(p(p))).



% '$lgt_filter_scope'(@nonvar, -nonvar)
%
% filters the predicate scope;
% used in the implementation of protected-qualified relations between entities;
% public predicates become protected predicates, protected and private predicates
% are unaffected

'$lgt_filter_scope'(p(_), p(p)).
'$lgt_filter_scope'(p, p).



% '$lgt_filter_scope_container'(@nonvar, @object_identifier, @object_identifier, -object_identifier)
%
% filters the predicate scope container;
% used in the implementation of private-qualified relations between entities;
% when the predicate is public or protected, the object inheriting the predicate
% becomes the scope container; when the predicate is private, the scope container
% is the inherited scope container

'$lgt_filter_scope_container'(p(_), _, SCtn, SCtn).
'$lgt_filter_scope_container'(p, SCtn, _, SCtn).



% '$lgt_find_original_predicate'(+object_identifier, +atom, +integer, +callable, -callable, -entity_identifier)
%
% finds the predicate pointed by an alias and the entity where the alias is declared

'$lgt_find_original_predicate'(Obj, Rnm, Flags, Alias, Pred, Entity) :-
	% we add a fifth argument to properly handle class hierarchies if necessary
	'$lgt_find_original_predicate'(Obj, Rnm, Flags, Alias, Pred, Entity, _),
	!.


'$lgt_find_original_predicate'(Obj, _, Flags, Alias, Pred, Entity, _) :-
	Flags /\ 64 =:= 64,		% "complements" flag set to "allow"
	'$lgt_complemented_object_'(Obj, Ctg, _, _, Rnm),
	'$lgt_find_original_predicate'(Ctg, Rnm, 0, Alias, Pred, Entity, _).

'$lgt_find_original_predicate'(Entity, Rnm, _, Alias, Pred, Entity, _) :-
	once(call(Rnm, _, Pred, Alias)),
	Pred \= Alias,
	!.

'$lgt_find_original_predicate'(Obj, _, Flags, Alias, Pred, Entity, _) :-
	Flags /\ 32 =:= 32,		% "complements" flag set to "restrict"
	'$lgt_complemented_object_'(Obj, Ctg, _, _, Rnm),
	'$lgt_find_original_predicate'(Ctg, Rnm, 0, Alias, Pred, Entity, _).

'$lgt_find_original_predicate'(Obj, _, _, Alias, Pred, Entity, _) :-
	'$lgt_implements_protocol_'(Obj, Ptc, _),
	'$lgt_current_protocol_'(Ptc, _, _, Rnm, _),
	'$lgt_find_original_predicate'(Ptc, Rnm, 0, Alias, Pred, Entity, _).

'$lgt_find_original_predicate'(Ptc, _, _, Alias, Pred, Entity, _) :-
	'$lgt_extends_protocol_'(Ptc, ExtPtc, _),
	'$lgt_current_protocol_'(ExtPtc, _, _, Rnm, _),
	'$lgt_find_original_predicate'(ExtPtc, Rnm, 0, Alias, Pred, Entity, _).

'$lgt_find_original_predicate'(Ctg, _, _, Alias, Pred, Entity, _) :-
	'$lgt_extends_category_'(Ctg, ExtCtg, _),
	'$lgt_current_category_'(ExtCtg, _, _, _, Rnm, _),
	'$lgt_find_original_predicate'(ExtCtg, Rnm, 0, Alias, Pred, Entity, _).

'$lgt_find_original_predicate'(Obj, _, _, Alias, Pred, Entity, _) :-
	'$lgt_imports_category_'(Obj, Ctg, _),
	'$lgt_current_category_'(Ctg, _, _, _, Rnm, _),
	'$lgt_find_original_predicate'(Ctg, Rnm, 0, Alias, Pred, Entity, _).

'$lgt_find_original_predicate'(Obj, _, _, Alias, Pred, Entity, prototype) :-
	'$lgt_extends_object_'(Obj, Parent, _),
	'$lgt_current_object_'(Parent, _, _, _, _, _, _, _, _, Rnm, Flags),
	'$lgt_find_original_predicate'(Parent, Rnm, Flags, Alias, Pred, Entity, prototype).

'$lgt_find_original_predicate'(Instance, _, _, Alias, Pred, Entity, instance) :-
	'$lgt_instantiates_class_'(Instance, Class, _),
	'$lgt_current_object_'(Class, _, _, _, _, _, _, _, _, Rnm, Flags),
	'$lgt_find_original_predicate'(Class, Rnm, Flags, Alias, Pred, Entity, superclass).

'$lgt_find_original_predicate'(Class, _, _, Alias, Pred, Entity, superclass) :-
	'$lgt_specializes_class_'(Class, Superclass, _),
	'$lgt_current_object_'(Superclass, _, _, _, _, _, _, _, _, Rnm, Flags),
	'$lgt_find_original_predicate'(Superclass, Rnm, Flags, Alias, Pred, Entity, superclass).



% '$lgt_find_overridden_predicate'(+entity_identifier, +entity_identifier, +callable, -entity_identifier)
%
% finds the entity containing the overridden predicate definition (assuming that the
% start lookup entity contains a overriding definition for the predicate)

'$lgt_find_overridden_predicate'(Obj, Self, Pred, DefCtn) :-
	'$lgt_current_object_'(Obj, _, _, _, Super, _, _, _, _, _, _),
	% for classes, we need to be sure we use the correct clause for "super" by looking into "self"
	'$lgt_execution_context'(ExCtx, _, _, _, Self, _, _),
	call(Super, Pred, ExCtx, _, _, DefCtn),
	DefCtn \= Obj,
	!.

'$lgt_find_overridden_predicate'(Ctg, _, Pred, DefCtn) :-
	'$lgt_current_category_'(Ctg, _, _, Def, _, _),
	call(Def, Pred, _, _, DefCtn),
	DefCtn \= Ctg,
	!.



% '$lgt_abolish'(+object_identifier, +predicate_indicator, +object_identifier, +scope, @execution_context)
%
% abolish/1 built-in method

'$lgt_abolish'(Obj, Pred, Sender, TestScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::abolish(Pred), ExCtx)),
	'$lgt_check'(predicate_indicator, Pred, logtalk(abolish(Pred), ExCtx)),
	'$lgt_abolish_checked'(Obj, Pred, Sender, TestScope, ExCtx).


'$lgt_abolish_checked'(user, Functor/Arity, _, _, _) :-
	!,
	abolish(Functor/Arity).

'$lgt_abolish_checked'(Obj, Functor/Arity, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	functor(Head, Functor, Arity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		functor(Original, OriginalFunctor, OriginalArity),
		'$lgt_abolish_checked'(Other, OriginalFunctor/OriginalArity, Obj, p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		abolish(':'(Module, (Original/Arity)))
	;	fail
	),
	!.

'$lgt_abolish_checked'(Obj, Functor/Arity, Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, _, _, _, _, DDcl, DDef, _, ObjFlags),
	!,
	functor(Pred, Functor, Arity),
	(	call(Dcl, Pred, Scope, _, PredFlags) ->
		% local static predicate declaration found
		(	(Scope = TestScope; Sender = Obj) ->
			% predicate is within the scope of the sender
			(	PredFlags /\ 2 =:= 2 ->
				% static declaration for a dynamic predicate
				throw(error(permission_error(modify, predicate_declaration, Functor/Arity), logtalk(abolish(Functor/Arity), ExCtx)))
			;	% predicate is static
				throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(abolish(Functor/Arity), ExCtx)))
			)
		;	% predicate is not within the scope of the sender
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(abolish(Functor/Arity), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(abolish(Functor/Arity), ExCtx)))
			)
		)
	;	% no static predicate declaration...
		ObjFlags /\ 128 =:= 128,
		% ... but dynamic declarations are allowed
		DDclClause =.. [DDcl, Pred, _],
		call(DDclClause) ->
		% dynamic predicate declaration found
		retractall(DDclClause),
		DDefClause =.. [DDef, Pred, _, TPred0],
		(	call(DDefClause) ->
			% predicate clauses exist
			'$lgt_unwrap_compiled_head'(TPred0, TPred),
			functor(TPred, TFunctor, TArity),
			abolish(TFunctor/TArity),
			retractall(DDefClause),
			'$lgt_clean_lookup_caches'(Pred)
		;	% no predicate clauses currently exist but may have existed in the past
			'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
			abolish(TFunctor/TArity)
		)
	;	% no dynamic predicate declaration found
		DDefClause =.. [DDef, Pred, _, TPred0],
		call(DDefClause) ->
		% local dynamic predicate
		'$lgt_unwrap_compiled_head'(TPred0, TPred),
		functor(TPred, TFunctor, TArity),
		abolish(TFunctor/TArity),
		retractall(DDefClause),
		'$lgt_clean_lookup_caches'(Pred)
	;	% no predicate declaration
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(abolish(Functor/Arity), ExCtx)))
	).

'$lgt_abolish_checked'(Obj, Pred, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::abolish(Pred), ExCtx))).



% '$lgt_asserta'(+object_identifier, @clause, +object_identifier, +scope, +scope, @execution_context)
%
% asserta/1 built-in method
%
% asserting facts uses a caching mechanism that saves the compiled form of the
% facts to improve performance

'$lgt_asserta'(Obj, Clause, Sender, _, _, _) :-
	nonvar(Obj),
	nonvar(Clause),
	'$lgt_db_lookup_cache_'(Obj, Clause, Sender, TClause, _),
	!,
	asserta(TClause).

'$lgt_asserta'(Obj, Clause, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::asserta(Clause), ExCtx)),
	'$lgt_check'(clause, Clause, logtalk(asserta(Clause), ExCtx)),
	(	Clause = (Head :- Body) ->
		(	Body == true ->
			'$lgt_asserta_fact_checked'(Obj, Head, Sender, TestScope, DclScope, ExCtx)
		;	'$lgt_asserta_rule_checked'(Obj, Clause, Sender, TestScope, DclScope, ExCtx)
		)
	;	'$lgt_asserta_fact_checked'(Obj, Clause, Sender, TestScope, DclScope, ExCtx)
	).


'$lgt_asserta_rule_checked'(Obj, (Head:-Body), Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_asserta_rule_checked'(Other, (Original:-Body), Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		asserta(':'(Module, (Original:-Body)))
	;	fail
	),
	!.

'$lgt_asserta_rule_checked'(Obj, (Head:-Body), Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, Meta, SCtn, DclScope, asserta((Head:-Body)), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, _),
			'$lgt_goal_meta_arguments'(Meta, Head, MetaArgs),
			'$lgt_comp_ctx'(Ctx, Head, GExCtx, _, _, _, _, Prefix, MetaArgs, _, GExCtx, runtime, _, _, _),
			'$lgt_compile_body'(Body, _, TBody, DBody, Ctx),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				asserta((THead :- ('$lgt_nop'(Body), '$lgt_debug'(rule(Obj, Head, 0, nil, 0), GExCtx), DBody)))
			;	asserta((THead :- ('$lgt_nop'(Body), TBody)))
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(asserta((Head:-Body)), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(asserta((Head:-Body)), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(asserta((Head:-Body)), ExCtx)))
	).

'$lgt_asserta_rule_checked'(Obj, Clause, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), Obj::asserta(Clause), ExCtx)).


'$lgt_asserta_fact_checked'(Obj, Head, Sender, _, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, _),
	!,
	asserta(THead).

'$lgt_asserta_fact_checked'(Obj, Head, Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_asserta_fact_checked'(Other, Original, Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		asserta(':'(Module, Original))
	;	fail
	),
	!.

'$lgt_asserta_fact_checked'(Obj, Head, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, _, SCtn, DclScope, asserta(Head), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, Update),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				asserta((THead :- '$lgt_debug'(fact(Obj, Head, 0, nil, 0), GExCtx)))
			;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, SCtn, DclScope, Type, Sender, THead, DDef, Update),
				asserta(THead)
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(asserta(Head), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(asserta(Head), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(asserta(Head), ExCtx)))
	).

'$lgt_asserta_fact_checked'(Obj, Head, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::asserta(Head), ExCtx))).



% '$lgt_assertz'(+object_identifier, @clause, +object_identifier, +scope, +scope, @execution_context)
%
% assertz/1 built-in method
%
% asserting facts uses a caching mechanism that saves the compiled form of the
% facts to improve performance

'$lgt_assertz'(Obj, Clause, Sender, _, _, _) :-
	nonvar(Obj),
	nonvar(Clause),
	'$lgt_db_lookup_cache_'(Obj, Clause, Sender, TClause, _),
	!,
	assertz(TClause).

'$lgt_assertz'(Obj, Clause, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::assertz(Clause), ExCtx)),
	'$lgt_check'(clause, Clause, logtalk(assertz(Clause), ExCtx)),
	(	Clause = (Head :- Body) ->
		(	Body == true ->
			'$lgt_assertz_fact_checked'(Obj, Head, Sender, TestScope, DclScope, ExCtx)
		;	'$lgt_assertz_rule_checked'(Obj, Clause, Sender, TestScope, DclScope, ExCtx)
		)
	;	'$lgt_assertz_fact_checked'(Obj, Clause, Sender, TestScope, DclScope, ExCtx)
	).


'$lgt_assertz_rule_checked'(Obj, (Head:-Body), Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_assertz_rule_checked'(Other, (Original:-Body), Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		assertz(':'(Module, (Original:-Body)))
	;	fail
	),
	!.

'$lgt_assertz_rule_checked'(Obj, (Head:-Body), Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, Meta, SCtn, DclScope, assertz((Head:-Body)), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, _),
			'$lgt_goal_meta_arguments'(Meta, Head, MetaArgs),
			'$lgt_comp_ctx'(Ctx, Head, GExCtx, _, _, _, _, Prefix, MetaArgs, _, GExCtx, runtime, _, _, _),
			'$lgt_compile_body'(Body, _, TBody, DBody, Ctx),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				assertz((THead :- ('$lgt_nop'(Body), '$lgt_debug'(rule(Obj, Head, 0, nil, 0), GExCtx), DBody)))
			;	assertz((THead :- ('$lgt_nop'(Body), TBody)))
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(assertz((Head:-Body)), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(assertz((Head:-Body)), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(assertz((Head:-Body)), ExCtx)))
	).

'$lgt_assertz_rule_checked'(Obj, Clause, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::assertz(Clause), ExCtx))).


'$lgt_assertz_fact_checked'(Obj, Head, Sender, _, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, _),
	!,
	assertz(THead).

'$lgt_assertz_fact_checked'(Obj, Head, Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_assertz_fact_checked'(Other, Original, Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		assertz(':'(Module, Original))
	;	fail
	),
	!.

'$lgt_assertz_fact_checked'(Obj, Head, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, _, SCtn, DclScope, assertz(Head), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, Update),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				assertz((THead :- '$lgt_debug'(fact(Obj, Head, 0, nil, 0), GExCtx)))
			;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, SCtn, DclScope, Type, Sender, THead, DDef, Update),
				assertz(THead)
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(assertz(Head), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(assertz(Head), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(assertz(Head), ExCtx)))
	).

'$lgt_assertz_fact_checked'(Obj, Head, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::assertz(Head), ExCtx))).



% gets or sets (if it doesn't exist) the declaration for an asserted predicate (but we must
% not add a scope declaration when asserting clauses for a *local* dynamic predicate)

'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, ObjFlags, Pred, Scope, Type, Meta, SCtn, DclScope, Goal, ExCtx) :-
	(	call(Dcl, Pred, Scope, Meta, PredFlags, SCtn, _) ->
		% predicate declaration found; get predicate type
		(	PredFlags /\ 2 =:= 2 ->
			Type = (dynamic)
		;	Type = (static)
		)
	;	% no predicate declaration; check for a local dynamic predicate if we're asserting locally
		(DclScope == p, call(DDef, Pred, _, _)) ->
		Scope = DclScope, Type = (dynamic), Meta = no, SCtn = Obj
	;	% not a declared predicate and not a local dynamic predicate
		(	DclScope == p
			% object asserting a new predicate in itself
		;	ObjFlags /\ 128 =:= 128
			% dynamic declaration of new predicates allowed
		) ->
		'$lgt_term_template'(Pred, DPred),
		Clause =.. [DDcl, DPred, DclScope],
		assertz(Clause),
		Scope = DclScope, Type = (dynamic), Meta = no, SCtn = Obj
	;	% object doesn't allow dynamic declaration of new predicates
		functor(Pred, Functor, Arity),
		throw(error(permission_error(create, predicate_declaration, Functor/Arity), logtalk(Goal, ExCtx)))
	).



% gets or sets (if it doesn't exist) the compiled call for an asserted predicate

'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, ExCtx, THead, NeedsUpdate) :-
	(	call(Def, Head, ExCtx, THead0) ->
		% static definition lookup entries don't require update goals
		'$lgt_unwrap_compiled_head'(THead0, THead),
		NeedsUpdate = false
	;	call(DDef, Head, ExCtx, THead0) ->
		% dynamic definition lookup entries always require update goals
		'$lgt_unwrap_compiled_head'(THead0, THead),
		NeedsUpdate = true
	;	% no definition lookup entry exists; construct and assert a dynamic one
		functor(Head, Functor, Arity),
		functor(GHead, Functor, Arity),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		functor(THead, TFunctor, TArity),
		'$lgt_unify_head_thead_arguments'(GHead, THead, ExCtx),
		(	Flags /\ 512 =:= 512 ->
			% object compiled in debug mode
			DDefClause =.. [DDef, GHead, ExCtx, '$lgt_debug'(goal(GHead,THead), ExCtx)]
		;	DDefClause =.. [DDef, GHead, ExCtx, THead]
		),
		assertz(DDefClause),
		'$lgt_clean_lookup_caches'(GHead),
		NeedsUpdate = true,
		GHead = Head
	).



% '$lgt_clause'(+object_identifier, +callable, ?callable, +object_identifier, +scope, @execution_context)
%
% clause/2 built-in method

'$lgt_clause'(Obj, Head, Body, Sender, TestScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::clause(Head, Body), ExCtx)),
	'$lgt_check'(clause, (Head:-Body), logtalk(clause(Head, Body), ExCtx)),
	'$lgt_clause_checked'(Obj, Head, Body, Sender, TestScope, ExCtx).


'$lgt_clause_checked'(Obj, Head, Body, Sender, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, _),
	!,
	clause(THead, TBody),
	(	TBody = ('$lgt_nop'(Body), _) ->
		% rules (compiled both in normal and debug mode)
		true
	;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
		% facts compiled in debug mode
		Body = true
	;	% facts compiled in normal mode
		TBody = Body
	).

'$lgt_clause_checked'(Obj, Head, Body, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx),
	Obj \== Other,
	!,
	'$lgt_clause_checked'(Other, Original, Body, Obj, p(p(p)), ExCtx).

'$lgt_clause_checked'(_, Head, Body, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx),
	!,
	clause(':'(Module,Original), Body).

'$lgt_clause_checked'(Obj, Head, Body, Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, ObjFlags),
	!,
	(	call(Dcl, Head, Scope, _, PredFlags, SCtn, _) ->
		(	(PredFlags /\ 2 =:= 2; ObjFlags /\ 2 =:= 2, Sender = SCtn) ->
			% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
			(	(Scope = TestScope; Sender = SCtn) ->
				(	(call(DDef, Head, _, THead0); call(Def, Head, _, THead0)) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					clause(THead, TBody),
					(	TBody = ('$lgt_nop'(Body), _) ->
						true
					;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
						Body = true
					;	TBody = Body
					)
				)
			;	% predicate is not within the scope of the sender
				functor(Head, Functor, Arity),
				(	Scope == p ->
					throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(clause(Head, Body), ExCtx)))
				;	throw(error(permission_error(access, protected_predicate, Functor/Arity), logtalk(clause(Head, Body), ExCtx)))
				)
			)
		;	% predicate is static
			functor(Head, Functor, Arity),
			throw(error(permission_error(access, static_predicate, Functor/Arity), logtalk(clause(Head, Body), ExCtx)))
		)
	;	Obj = Sender,
		(call(DDef, Head, _, THead0); call(Def, Head, _, THead0)) ->
		% local dynamic predicate with no scope declaration
		'$lgt_unwrap_compiled_head'(THead0, THead),
		clause(THead, TBody),
		(	TBody = ('$lgt_nop'(Body), _) ->
			true
		;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
			Body = true
		;	TBody = Body
		)
	;	% unknown predicate
		functor(Head, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(clause(Head, Body), ExCtx)))
	).

'$lgt_clause_checked'(Obj, Head, Body, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::clause(Head, Body), ExCtx))).



% '$lgt_retract'(+object_identifier, @clause, +object_identifier, +scope, @execution_context)
%
% retract/1 built-in method
%
% the implementation must ensure that retracting the last clause for a
% predicate allows any inherited clauses to be found again as they are
% no longer being overridden

'$lgt_retract'(Obj, Clause, Sender, _, _) :-
	nonvar(Obj),
	nonvar(Clause),
	'$lgt_db_lookup_cache_'(Obj, Clause, Sender, TClause, UpdateData),
	!,
	retract(TClause),
	'$lgt_update_ddef_table_opt'(UpdateData).

'$lgt_retract'(Obj, Clause, Sender, TestScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::retract(Clause), ExCtx)),
	'$lgt_check'(clause, Clause, logtalk(retract(Clause), ExCtx)),
	(	Clause = (Head :- Body) ->
		(	var(Body) ->
			'$lgt_retract_var_body_checked'(Obj, Clause, Sender, TestScope, ExCtx)
		;	Body == true ->
			'$lgt_retract_fact_checked'(Obj, Head, Sender, TestScope, ExCtx)
		;	'$lgt_retract_rule_checked'(Obj, Clause, Sender, TestScope, ExCtx)
		)
	;	'$lgt_retract_fact_checked'(Obj, Clause, Sender, TestScope, ExCtx)
	).


'$lgt_retract_var_body_checked'(Obj, (Head:-Body), Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx),
	Obj \== Other,
	!,
	'$lgt_retract_var_body_checked'(Other, (Original:-Body), Obj, p(p(p)), ExCtx).

'$lgt_retract_var_body_checked'(_, (Head:-Body), _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx),
	!,
	retract((':'(Module,Original) :- Body)).

'$lgt_retract_var_body_checked'(Obj, (Head:-Body), Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, ObjFlags),
	!,
	(	call(Dcl, Head, Scope, _, PredFlags, SCtn, _) ->
		(	(PredFlags /\ 2 =:= 2; ObjFlags /\ 2 =:= 2, Sender = SCtn) ->
			% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
			(	(Scope = TestScope; Sender = SCtn) ->
				(	call(DDef, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					retract((THead :- TBody)),
					(	TBody = ('$lgt_nop'(Body), _) ->
						true
					;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
						Body = true
					;	TBody = Body
					),
					'$lgt_update_ddef_table'(DDef, Head, THead)
				;	call(Def, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					retract((THead :- TBody)),
					(	TBody = ('$lgt_nop'(Body), _) ->
						true
					;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
						Body = true
					;	TBody = Body
					)
				)
			;	% predicate is not within the scope of the sender
				functor(Head, Functor, Arity),
				(	Scope == p ->
					throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
				;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
				)
			)
		;	% predicate is static
			functor(Head, Functor, Arity),
			throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
		)
	;	Obj = Sender,
		call(DDef, Head, _, THead0) ->
		% local dynamic predicate with no scope declaration
		'$lgt_unwrap_compiled_head'(THead0, THead),
		retract((THead :- TBody)),
		(	TBody = ('$lgt_nop'(Body), _) ->
			true
		;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
			Body = true
		;	TBody = Body
		)
	;	% unknown predicate
		functor(Head, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
	).

'$lgt_retract_var_body_checked'(Obj, (Head:-Body), _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::retract((Head:-Body)), ExCtx))).


'$lgt_retract_rule_checked'(Obj, (Head:-Body), Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx),
	Obj \== Other,
	!,
	'$lgt_retract_rule_checked'(Other, (Original:-Body), Obj, p(p(p)), ExCtx).

'$lgt_retract_rule_checked'(_, (Head:-Body), _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx),
	!,
	retract((':'(Module,Original) :- Body)).

'$lgt_retract_rule_checked'(Obj, (Head:-Body), Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, ObjFlags),
	!,
	(	call(Dcl, Head, Scope, _, PredFlags, SCtn, _) ->
		(	(PredFlags /\ 2 =:= 2; ObjFlags /\ 2 =:= 2, Sender = SCtn) ->
			% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
			(	(Scope = TestScope; Sender = SCtn) ->
				(	call(DDef, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					retract((THead :- ('$lgt_nop'(Body), _))),
					'$lgt_update_ddef_table'(DDef, Head, THead)
				;	call(Def, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					retract((THead :- ('$lgt_nop'(Body), _)))
				)
			;	% predicate is not within the scope of the sender
				functor(Head, Functor, Arity),
				(	Scope == p ->
					throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
				;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
				)
			)
		;	% predicate is static
			functor(Head, Functor, Arity),
			throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
		)
	;	Obj = Sender,
		call(DDef, Head, _, THead0) ->
		% local dynamic predicate with no scope declaration
		'$lgt_unwrap_compiled_head'(THead0, THead),
		retract((THead :- ('$lgt_nop'(Body), _)))
	;	% unknown predicate
		functor(Head, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(retract((Head:-Body)), ExCtx)))
	).

'$lgt_retract_rule_checked'(Obj, (Head:-Body), _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::retract((Head:-Body)), ExCtx))).


'$lgt_retract_fact_checked'(Obj, Head, Sender, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, UpdateData),
	!,
	retract(THead),
	'$lgt_update_ddef_table_opt'(UpdateData).

'$lgt_retract_fact_checked'(Obj, Head, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx),
	Obj \== Other,
	!,
	'$lgt_retract_fact_checked'(Other, Original, Obj, p(p(p)), ExCtx).

'$lgt_retract_fact_checked'(_, Head, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx),
	!,
	retract(':'(Module,Original)).

'$lgt_retract_fact_checked'(Obj, Head, Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, ObjFlags),
	!,
	(	call(Dcl, Head, Scope, _, PredFlags, SCtn, _) ->
		(	(PredFlags /\ 2 =:= 2; ObjFlags /\ 2 =:= 2, Sender = SCtn) ->
			% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
			Type = (dynamic),
			(	(Scope = TestScope; Sender = SCtn) ->
				(	call(DDef, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					(	ObjFlags /\ 512 =:= 512 ->
						% object compiled in debug mode
						retract((THead :- '$lgt_debug'(fact(_, _, _, _, _), _)))
					;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, SCtn, Scope, Type, Sender, THead, DDef, true),
						retract(THead)
					),
					'$lgt_update_ddef_table'(DDef, Head, THead)
				;	call(Def, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					(	ObjFlags /\ 512 =:= 512 ->
						% object compiled in debug mode
						retract((THead :- '$lgt_debug'(fact(_, _, _, _, _), _)))
					;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, Scope, Type, Sender, THead),
						retract(THead)
					)
				)
			;	% predicate is not within the scope of the sender
				functor(Head, Functor, Arity),
				(	Scope == p ->
					throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(retract(Head), ExCtx)))
				;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(retract(Head), ExCtx)))
				)
			)
		;	% predicate is static
			functor(Head, Functor, Arity),
			throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(retract(Head), ExCtx)))
		)
	;	Obj = Sender,
		call(DDef, Head, _, THead0) ->
		% local dynamic predicate with no scope declaration
		'$lgt_unwrap_compiled_head'(THead0, THead),
		(	ObjFlags /\ 512 =:= 512 ->
			% object compiled in debug mode
			retract((THead :- '$lgt_debug'(fact(_, _, _, _, _), _)))
		;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, p, (dynamic), Sender, THead),
			retract(THead)
		)
	;	% unknown predicate
		functor(Head, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(retract(Head), ExCtx)))
	).

'$lgt_retract_fact_checked'(Obj, Head, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::retract(Head), ExCtx))).



% '$lgt_retractall'(+object_identifier, @callable, +object_identifier, +scope, @execution_context)
%
% retractall/1 built-in method
%
% the implementation must ensure that retracting the last clause for a
% predicate allows any inherited clauses to be found again as they are
% no longer being overridden

'$lgt_retractall'(Obj, Head, Sender, _, _) :-
	nonvar(Obj),
	nonvar(Head),
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, UpdateData),
	!,
	retractall(THead),
	'$lgt_update_ddef_table_opt'(UpdateData).

'$lgt_retractall'(Obj, Head, Sender, TestScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::retractall(Head), ExCtx)),
	'$lgt_check'(callable, Head, logtalk(retractall(Head), ExCtx)),
	'$lgt_retractall_checked'(Obj, Head, Sender, TestScope, ExCtx).


'$lgt_retractall_checked'(Obj, Head, Sender, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, UpdateData),
	!,
	retractall(THead),
	'$lgt_update_ddef_table_opt'(UpdateData).

'$lgt_retractall_checked'(Obj, Head, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_retractall_checked'(Other, Original, Obj, p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		retractall(':'(Module,Original))
	;	fail
	),
	!.

'$lgt_retractall_checked'(Obj, Head, Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, ObjFlags),
	!,
	(	call(Dcl, Head, Scope, _, PredFlags, SCtn, _) ->
		% predicate scope declaration found
		(	(PredFlags /\ 2 =:= 2; ObjFlags /\ 2 =:= 2, Sender = SCtn) ->
			% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
			Type = (dynamic),
			(	(Scope = TestScope; Sender = SCtn) ->
				(	call(DDef, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					retractall(THead),
					'$lgt_update_ddef_table'(DDef, Head, THead)
				;	call(Def, Head, _, THead0) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					(	ObjFlags /\ 512 =:= 512 ->
						% object compiled in debug mode
						true
					;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, Scope, Type, Sender, THead)
					),
					retractall(THead)
				;	true
				)
			;	% predicate is not within the scope of the sender
				functor(Head, Functor, Arity),
				(	Scope == p ->
					throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(retractall(Head), ExCtx)))
				;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(retractall(Head), ExCtx)))
				)
			)
		;	% predicate is static
			functor(Head, Functor, Arity),
			throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(retractall(Head), ExCtx)))
		)
	;	Obj = Sender,
		call(DDef, Head, _, THead0) ->
		% local dynamic predicate with no scope declaration
		'$lgt_unwrap_compiled_head'(THead0, THead),
		(	ObjFlags /\ 512 =:= 512 ->
			% object compiled in debug mode
			true
		;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, p, (dynamic), Sender, THead)
		),
		retractall(THead)
	;	% unknown predicate
		functor(Head, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(retractall(Head), ExCtx)))
	).

'$lgt_retractall_checked'(Obj, Head, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::retractall(Head), ExCtx))).



% '$lgt_asserta'(+object_identifier, @clause, @term, +object_identifier, +scope, +scope, @execution_context)
%
% asserta/2 built-in method that takes a clause reference if supported
% as built-in predicates by the backend Prolog compiler
%
% asserting facts uses a caching mechanism that saves the compiled form of the
% facts to improve performance

'$lgt_asserta'(Obj, Clause, Ref, Sender, _, _, _) :-
	nonvar(Obj),
	nonvar(Clause),
	'$lgt_db_lookup_cache_'(Obj, Clause, Sender, TClause, _),
	!,
	asserta(TClause, Ref).

'$lgt_asserta'(Obj, Clause, Ref, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::asserta(Clause, Ref), ExCtx)),
	'$lgt_check'(clause, Clause, logtalk(asserta(Clause, Ref), ExCtx)),
	(	Clause = (Head :- Body) ->
		(	Body == true ->
			'$lgt_asserta_fact_checked'(Obj, Head, Ref, Sender, TestScope, DclScope, ExCtx)
		;	'$lgt_asserta_rule_checked'(Obj, Clause, Ref, Sender, TestScope, DclScope, ExCtx)
		)
	;	'$lgt_asserta_fact_checked'(Obj, Clause, Ref, Sender, TestScope, DclScope, ExCtx)
	).


'$lgt_asserta_rule_checked'(Obj, (Head:-Body), Ref, Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_asserta_rule_checked'(Other, (Original:-Body), Ref, Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		asserta(':'(Module, (Original:-Body)), Ref)
	;	fail
	),
	!.

'$lgt_asserta_rule_checked'(Obj, (Head:-Body), Ref, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, Meta, SCtn, DclScope, asserta((Head:-Body), Ref), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, _),
			'$lgt_goal_meta_arguments'(Meta, Head, MetaArgs),
			'$lgt_comp_ctx'(Ctx, Head, GExCtx, _, _, _, _, Prefix, MetaArgs, _, GExCtx, runtime, _, _, _),
			'$lgt_compile_body'(Body, _, TBody, DBody, Ctx),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				asserta((THead :- ('$lgt_nop'(Body), '$lgt_debug'(rule(Obj, Head, 0, nil, 0), GExCtx), DBody)), Ref)
			;	asserta((THead :- ('$lgt_nop'(Body), TBody)), Ref)
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(asserta((Head:-Body), Ref), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(asserta((Head:-Body), Ref), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(asserta((Head:-Body), Ref), ExCtx)))
	).

'$lgt_asserta_rule_checked'(Obj, Clause, Ref, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), Obj::asserta(Clause, Ref), ExCtx)).


'$lgt_asserta_fact_checked'(Obj, Head, Ref, Sender, _, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, _),
	!,
	asserta(THead, Ref).

'$lgt_asserta_fact_checked'(Obj, Head, Ref, Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_asserta_fact_checked'(Other, Original, Ref, Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		asserta(':'(Module, Original), Ref)
	;	fail
	),
	!.

'$lgt_asserta_fact_checked'(Obj, Head, Ref, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, _, SCtn, DclScope, asserta(Head, Ref), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, Update),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				asserta((THead :- '$lgt_debug'(fact(Obj, Head, 0, nil, 0), GExCtx)), Ref)
			;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, SCtn, DclScope, Type, Sender, THead, DDef, Update),
				asserta(THead, Ref)
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(asserta(Head, Ref), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(asserta(Head, Ref), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(asserta(Head, Ref), ExCtx)))
	).

'$lgt_asserta_fact_checked'(Obj, Head, Ref, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::asserta(Head, Ref), ExCtx))).



% '$lgt_assertz'(+object_identifier, @clause, @term, +object_identifier, +scope, +scope, @execution_context)
%
% assertz/2 built-in method that takes a clause reference if supported
% as built-in predicates by the backend Prolog compiler
%
% asserting facts uses a caching mechanism that saves the compiled form of the
% facts to improve performance

'$lgt_assertz'(Obj, Clause, Ref, Sender, _, _, _) :-
	nonvar(Obj),
	nonvar(Clause),
	'$lgt_db_lookup_cache_'(Obj, Clause, Sender, TClause, _),
	!,
	assertz(TClause, Ref).

'$lgt_assertz'(Obj, Clause, Ref, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::assertz(Clause, Ref), ExCtx)),
	'$lgt_check'(clause, Clause, logtalk(assertz(Clause, Ref), ExCtx)),
	(	Clause = (Head :- Body) ->
		(	Body == true ->
			'$lgt_assertz_fact_checked'(Obj, Head, Ref, Sender, TestScope, DclScope, ExCtx)
		;	'$lgt_assertz_rule_checked'(Obj, Clause, Ref, Sender, TestScope, DclScope, ExCtx)
		)
	;	'$lgt_assertz_fact_checked'(Obj, Clause, Ref, Sender, TestScope, DclScope, ExCtx)
	).


'$lgt_assertz_rule_checked'(Obj, (Head:-Body), Ref, Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_assertz_rule_checked'(Other, (Original:-Body), Ref, Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		assertz(':'(Module, (Original:-Body)), Ref)
	;	fail
	),
	!.

'$lgt_assertz_rule_checked'(Obj, (Head:-Body), Ref, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, Meta, SCtn, DclScope, assertz((Head:-Body), Ref), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, _),
			'$lgt_goal_meta_arguments'(Meta, Head, MetaArgs),
			'$lgt_comp_ctx'(Ctx, Head, GExCtx, _, _, _, _, Prefix, MetaArgs, _, GExCtx, runtime, _, _, _),
			'$lgt_compile_body'(Body, _, TBody, DBody, Ctx),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				assertz((THead :- ('$lgt_nop'(Body), '$lgt_debug'(rule(Obj, Head, 0, nil, 0), GExCtx), DBody)), Ref)
			;	assertz((THead :- ('$lgt_nop'(Body), TBody)), Ref)
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(assertz((Head:-Body), Ref), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(assertz((Head:-Body), Ref), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(assertz((Head:-Body), Ref), ExCtx)))
	).

'$lgt_assertz_rule_checked'(Obj, Clause, Ref, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::assertz(Clause, Ref), ExCtx))).


'$lgt_assertz_fact_checked'(Obj, Head, Ref, Sender, _, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, _),
	!,
	assertz(THead, Ref).

'$lgt_assertz_fact_checked'(Obj, Head, Ref, Obj, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	(	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx) ->
		'$lgt_assertz_fact_checked'(Other, Original, Ref, Obj, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx) ->
		assertz(':'(Module, Original), Ref)
	;	fail
	),
	!.

'$lgt_assertz_fact_checked'(Obj, Head, Ref, Sender, TestScope, DclScope, ExCtx) :-
	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, _, _, _, DDcl, DDef, _, Flags),
	!,
	'$lgt_assert_pred_dcl'(Obj, Dcl, DDcl, DDef, Flags, Head, Scope, Type, _, SCtn, DclScope, assertz(Head, Ref), ExCtx),
	(	(Type == (dynamic); Flags /\ 2 =:= 2, Sender = SCtn) ->
		% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
		(	(Scope = TestScope; Sender = SCtn) ->
			'$lgt_assert_pred_def'(Def, DDef, Flags, Prefix, Head, GExCtx, THead, Update),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				assertz((THead :- '$lgt_debug'(fact(Obj, Head, 0, nil, 0), GExCtx)), Ref)
			;	'$lgt_add_db_lookup_cache_entry'(Obj, Head, SCtn, DclScope, Type, Sender, THead, DDef, Update),
				assertz(THead, Ref)
			)
		;	% predicate is not within the scope of the sender
			functor(Head, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(modify, private_predicate, Functor/Arity), logtalk(assertz(Head, Ref), ExCtx)))
			;	throw(error(permission_error(modify, protected_predicate, Functor/Arity), logtalk(assertz(Head, Ref), ExCtx)))
			)
		)
	;	% predicate is static
		functor(Head, Functor, Arity),
		throw(error(permission_error(modify, static_predicate, Functor/Arity), logtalk(assertz(Head, Ref), ExCtx)))
	).

'$lgt_assertz_fact_checked'(Obj, Head, Ref, _, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::assertz(Head, Ref), ExCtx))).



% '$lgt_clause'(+object_identifier, +callable, ?callable, +object_identifier, +scope, @execution_context)
%
% clause/3 built-in method that takes a clause reference if supported
% as built-in predicates by the backend Prolog compiler

'$lgt_clause'(Obj, Head, Body, Ref, Sender, TestScope, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj::clause(Head, Body, Ref), ExCtx)),
	(	var(Ref) ->
		'$lgt_check'(clause, (Head:-Body), logtalk(clause(Head, Body, Ref), ExCtx))
	;	'$lgt_check'(var_or_callable, Head, logtalk(clause(Head, Body, Ref), ExCtx)),
		'$lgt_check'(var_or_callable, Body, logtalk(clause(Head, Body, Ref), ExCtx))
	),
	'$lgt_clause_checked'(Obj, Head, Body, Ref, Sender, TestScope, ExCtx).


'$lgt_clause_checked'(Obj, Head, Body, Ref, Obj, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_uses_predicate_'(Entity, Other, Original, Head, Ctx),
	Obj \== Other,
	!,
	'$lgt_clause_checked'(Other, Original, Body, Ref, Obj, p(p(p)), ExCtx).

'$lgt_clause_checked'(_, Head, Body, Ref, _, _, ExCtx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_use_module_predicate_'(Entity, Module, Original, Head, Ctx),
	!,
	clause(':'(Module,Original), Body, Ref).

'$lgt_clause_checked'(Obj, Head, Body, Ref, _, _, ExCtx) :-
	nonvar(Ref),
	!,
	clause(THead0, TBody, Ref),
	'$lgt_wrap_compiled_head'(Head, THead0, ExCtx, THead),
	'$lgt_current_object_'(Obj, _, _, Def, _, _, _, _, DDef, _, _),
	once((call(DDef, Head, _, THead); call(Def, Head, _, THead))),
	(	TBody = ('$lgt_nop'(Body), _) ->
		% rules (compiled both in normal and debug mode)
		true
	;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
		% facts compiled in debug mode
		Body = true
	;	% facts compiled in normal mode
		TBody = Body
	).

'$lgt_clause_checked'(Obj, Head, Body, Ref, Sender, _, _) :-
	'$lgt_db_lookup_cache_'(Obj, Head, Sender, THead, _),
	!,
	clause(THead, TBody, Ref),
	(	TBody = ('$lgt_nop'(Body), _) ->
		% rules (compiled both in normal and debug mode)
		true
	;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
		% facts compiled in debug mode
		Body = true
	;	% facts compiled in normal mode
		TBody = Body
	).

'$lgt_clause_checked'(Obj, Head, Body, Ref, Sender, TestScope, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, ObjFlags),
	!,
	(	call(Dcl, Head, Scope, _, PredFlags, SCtn, _) ->
		(	(PredFlags /\ 2 =:= 2; ObjFlags /\ 2 =:= 2, Sender = SCtn) ->
			% either a dynamic predicate or a dynamic object that is both the sender and the predicate scope container
			(	(Scope = TestScope; Sender = SCtn) ->
				(	(call(DDef, Head, _, THead0); call(Def, Head, _, THead0)) ->
					'$lgt_unwrap_compiled_head'(THead0, THead),
					clause(THead, TBody, Ref),
					(	TBody = ('$lgt_nop'(Body), _) ->
						true
					;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
						Body = true
					;	TBody = Body
					)
				)
			;	% predicate is not within the scope of the sender
				functor(Head, Functor, Arity),
				(	Scope == p ->
					throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(clause(Head, Body, Ref), ExCtx)))
				;	throw(error(permission_error(access, protected_predicate, Functor/Arity), logtalk(clause(Head, Body, Ref), ExCtx)))
				)
			)
		;	% predicate is static
			functor(Head, Functor, Arity),
			throw(error(permission_error(access, static_predicate, Functor/Arity), logtalk(clause(Head, Body, Ref), ExCtx)))
		)
	;	Obj = Sender,
		(call(DDef, Head, _, THead0); call(Def, Head, _, THead0)) ->
		% local dynamic predicate with no scope declaration
		'$lgt_unwrap_compiled_head'(THead0, THead),
		clause(THead, TBody, Ref),
		(	TBody = ('$lgt_nop'(Body), _) ->
			true
		;	TBody = '$lgt_debug'(fact(_, _, _, _, _), _) ->
			Body = true
		;	TBody = Body
		)
	;	% unknown predicate
		functor(Head, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(clause(Head, Body, Ref), ExCtx)))
	).

'$lgt_clause_checked'(Obj, Head, Body, Ref, _, _, ExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::clause(Head, Body, Ref), ExCtx))).



% '$lgt_nop'(+clause)
%
% used as the first goal in the body of asserted predicate clauses that are
% rules to save the original clause body and thus support the implementation
% of the clause/2 built-in method

'$lgt_nop'(_).



% '$lgt_add_db_lookup_cache_entry'(@object_identifier, @callable, @callable, +atom, @object_identifier, @callable)
%
% adds a new database lookup cache entry (when an update goal is not required)

'$lgt_add_db_lookup_cache_entry'(Obj, Head, Scope, Type, Sender, THead) :-
	'$lgt_term_template'(Obj, GObj),
	'$lgt_term_template'(Head, GHead),
	'$lgt_term_template'(THead, GTHead),
	'$lgt_unify_head_thead_arguments'(GHead, GTHead, _),
	(	(Scope = p(p(p)), Type == (dynamic)) ->
		asserta('$lgt_db_lookup_cache_'(GObj, GHead, _, GTHead, true))
	;	'$lgt_term_template'(Sender, GSender),
		asserta('$lgt_db_lookup_cache_'(GObj, GHead, GSender, GTHead, true))
	).



% '$lgt_add_db_lookup_cache_entry'(@object_identifier, @callable, @callable, @callable, +atom, @object_identifier, @callable, +atom, +atom)
%
% adds a new database lookup cache entry

'$lgt_add_db_lookup_cache_entry'(Obj, Head, SCtn, Scope, Type, Sender, THead, DDef, NeedsUpdate) :-
	'$lgt_term_template'(Obj, GObj),
	'$lgt_term_template'(Head, GHead),
	'$lgt_term_template'(THead, GTHead),
	'$lgt_unify_head_thead_arguments'(GHead, GTHead, _),
	(	NeedsUpdate == true, Sender \= SCtn ->
		'$lgt_term_template'(Head, UHead),
		'$lgt_term_template'(THead, UTHead),
		UClause =.. [DDef, UHead, _, _],
		(	(Scope = p(p(p)), Type == (dynamic)) ->
			asserta('$lgt_db_lookup_cache_'(GObj, GHead, _, GTHead, update(UHead, UTHead, UClause)))
		;	'$lgt_term_template'(Sender, GSender),
			asserta('$lgt_db_lookup_cache_'(GObj, GHead, GSender, GTHead, update(UHead, UTHead, UClause)))
		)
	;	(	(Scope = p(p(p)), Type == (dynamic)) ->
			asserta('$lgt_db_lookup_cache_'(GObj, GHead, _, GTHead, true))
		;	'$lgt_term_template'(Sender, GSender),
			asserta('$lgt_db_lookup_cache_'(GObj, GHead, GSender, GTHead, true))
		)
	).



% '$lgt_unify_head_thead_arguments'(+callable, +callable, @term)
%
% compiled clause heads use an extra argument for passing the execution context

'$lgt_unify_head_thead_arguments'(Head, THead, ExCtx) :-
	Head =.. [_| Args],
	THead =.. [_| TArgs],
	'$lgt_append'(Args, [ExCtx], TArgs).



% '$lgt_phrase'(+grbody, ?list, +execution_context, +atom)
%
% phrase/2 built-in method implementation for calls where the first argument is only known at runtime

'$lgt_phrase'(GRBody, Input, ExCtx, _) :-
	var(GRBody),
	throw(error(instantiation_error, logtalk(phrase(GRBody, Input), ExCtx))).

'$lgt_phrase'('$lgt_local'(GRBody), Input, ExCtx, _) :-
	!,
	'$lgt_phrase'(GRBody, Input, ExCtx, local).

'$lgt_phrase'(GRBody, Input, ExCtx, Where) :-
	'$lgt_comp_ctx_mode'(Ctx, runtime),
	catch(
		'$lgt_dcg_body'(GRBody, S0, S, Pred, Ctx),
		Error,
		throw(error(Error, logtalk(phrase(GRBody, Input), ExCtx)))
	),
	Input = S0, [] = S,
	'$lgt_metacall'(Pred, ExCtx, Where).



% '$lgt_phrase'(+grbody, ?list, ?list, +execution_context)
%
% phrase/3 built-in method implementation for calls where the first argument is only known at runtime

'$lgt_phrase'(GRBody, Input, Rest, ExCtx, _) :-
	var(GRBody),
	throw(error(instantiation_error, logtalk(phrase(GRBody, Input, Rest), ExCtx))).

'$lgt_phrase'('$lgt_local'(GRBody), Input, Rest, ExCtx, _) :-
	!,
	'$lgt_phrase'(GRBody, Input, Rest, ExCtx, local).

'$lgt_phrase'(GRBody, Input, Rest, ExCtx, Where) :-
	'$lgt_comp_ctx_mode'(Ctx, runtime),
	catch(
		'$lgt_dcg_body'(GRBody, S0, S, Pred, Ctx),
		Error,
		throw(error(Error, logtalk(phrase(GRBody, Input, Rest), ExCtx)))
	),
	Input = S0, Rest = S,
	'$lgt_metacall'(Pred, ExCtx, Where).



% '$lgt_expand_term_local'(+object_identifier,   ?term, ?term, @execution_context)
% '$lgt_expand_term_local'(+category_identifier, ?term, ?term, @execution_context)
%
% expand_term/2 local calls
%
% calls the term_expansion/2 user-defined hook predicate if defined and within scope

'$lgt_expand_term_local'(Entity, Term, Expansion, ExCtx) :-
	(	var(Term) ->
		Expansion = Term
	;	'$lgt_term_expansion_local'(Entity, Term, Expand, ExCtx) ->
		Expansion = Expand
	;	Term = (_ --> _) ->
		% default grammar rule expansion
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, [], _, _, runtime, _, _, _),
		catch(
			'$lgt_dcg_rule'(Term, Clause, Ctx),
			Error,
			throw(error(Error, logtalk(expand_term(Term,_), ExCtx)))
		),
		(	Clause = (Head :- Body),
			'$lgt_compiler_flag'(optimize, on) ->
			'$lgt_simplify_goal'(Body, SBody),
			(	SBody == true ->
				Expansion = Head
			;	Expansion = (Head :- SBody)
			)
		;	% fact and/or optimization disabled
			Expansion = Clause
		)
	;	Expansion = Term
	).


% '$lgt_term_expansion_local'(+object_identifier, ?term, ?term, +execution_context)
%
% to avoid failures when the call is made from a multifile predicate clause,
% first the term_expansion/2 definition container is located and then the
% call is reduced to a local call

'$lgt_term_expansion_local'(Obj, Term, Expansion, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, _),
	!,
	(	call(Dcl, term_expansion(_, _), Scope, _, _, SCtn, _) ->
		(	(Scope = p(_); Obj = SCtn) ->
			(	call(Def, term_expansion(_, _), _, _, _, DCtn) ->
				(	'$lgt_current_object_'(DCtn, _, _, DCtnDef, _, _, _, _, DCtnDDef, _, _) ->
					(	call(DCtnDef, term_expansion(Term, Expansion), ExCtx, Call) ->
						true
					;	call(DCtnDDef, term_expansion(Term, Expansion), ExCtx, Call)
					)
				;	'$lgt_current_category_'(DCtn, _, _, DCtnDef, _, _),
					call(DCtnDef, term_expansion(Term, Expansion), ExCtx, Call)
				)
			;	% no definition found
				fail
			)
		;	% declaration is out of scope but we can still try a local definition
			call(Def, term_expansion(Term, Expansion), ExCtx, Call) ->
			true
		;	call(DDef, term_expansion(Term, Expansion), ExCtx, Call)
		)
	;	% no declaration for the term_expansion/2 hook predicate found;
		% check for a local definition
		call(Def, term_expansion(Term, Expansion), ExCtx, Call) ->
		true
	;	call(DDef, term_expansion(Term, Expansion), ExCtx, Call)
	),
	!,
	once(Call).

'$lgt_term_expansion_local'(Ctg, Term, Expansion, ExCtx) :-
	'$lgt_current_category_'(Ctg, _, Dcl, Def, _, _),
	(	call(Dcl, term_expansion(_, _), Scope, _, _, DclCtn) ->
		(	(Scope = p(_); Ctg = DclCtn) ->
			(	call(Def, term_expansion(_, _), _, _, DCtn) ->
				'$lgt_current_category_'(DCtn, _, _, DCtnDef, _, _),
				call(DCtnDef, term_expansion(Term, Expansion), ExCtx, Call)
			;	% no definition found
				fail
			)
		;	% declaration is out of scope but we can still try a local definition
			call(Def, term_expansion(Term, Expansion), ExCtx, Call)
		)
	;	% no declaration for the term_expansion/2 hook predicate found;
		% check for a local definition
		call(Def, term_expansion(Term, Expansion), ExCtx, Call)
	),
	!,
	once(Call).



% '$lgt_expand_term_message'(+object_identifier,   ?term, ?term, +object_identifier, @scope, @execution_context)
%
% expand_term/2 messages
%
% calls the term_expansion/2 user-defined hook predicate if defined and within scope

'$lgt_expand_term_message'(Entity, Term, Expansion, Sender, Scope, ExCtx) :-
	(	var(Term) ->
		Expansion = Term
	;	'$lgt_term_expansion_message'(Entity, Term, Expand, Sender, Scope) ->
		Expansion = Expand
	;	Term = (_ --> _) ->
		% default grammar rule expansion
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, [], _, _, runtime, _, _, _),
		catch(
			'$lgt_dcg_rule'(Term, Clause, Ctx),
			Error,
			throw(error(Error, logtalk(expand_term(Term,_), ExCtx)))
		),
		(	Clause = (Head :- Body),
			'$lgt_compiler_flag'(optimize, on) ->
			'$lgt_simplify_goal'(Body, SBody),
			(	SBody == true ->
				Expansion = Head
			;	Expansion = (Head :- SBody)
			)
		;	% fact and/or optimization disabled
			Expansion = Clause
		)
	;	Expansion = Term
	).


% '$lgt_term_expansion_message'(+object_identifier, ?term, ?term, +object_identifier, @scope)

'$lgt_term_expansion_message'(Obj, Term, Expansion, Sender, LookupScope) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, _),
	(	call(Dcl, term_expansion(_, _), PredScope, _, _, SCtn, _) ->
		(	(PredScope = LookupScope; Sender = SCtn) ->
			'$lgt_execution_context'(ExCtx, Obj, Sender, Obj, Obj, [], []),
			call(Def, term_expansion(Term, Expansion), ExCtx, Call, _, _)
		;	% message is out of scope
			fail
		)
	;	% no declaration for the term_expansion/2 hook predicate found
		fail
	),
	!,
	once(Call).



% '$lgt_expand_goal_local'(+object_identifier,   ?term, ?term, @execution_context)
% '$lgt_expand_goal_local'(+category_identifier, ?term, ?term, @execution_context)
%
% expand_goal/2 local calls
%
% calls the goal_expansion/2 user-defined hook predicate if defined and within scope

'$lgt_expand_goal_local'(Obj, Goal, ExpandedGoal, ExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, DDef, _, _),
	!,
	(	call(Dcl, goal_expansion(_, _), Scope, _, _, SCtn, _) ->
		(	(Scope = p(_); Obj = SCtn) ->
			'$lgt_expand_goal_object_scoped'(Goal, ExpandedGoal, Def, ExCtx)
		;	% declaration is out of scope but we can still try a local definition
			'$lgt_expand_goal_object_local'(Goal, ExpandedGoal, Def, DDef, ExCtx, [])
		)
	;	% no declaration for the goal_expansion/2 hook predicate found;
		% try to use a local definition if it exists
		'$lgt_expand_goal_object_local'(Goal, ExpandedGoal, Def, DDef, ExCtx, [])
	).

'$lgt_expand_goal_local'(Ctg, Goal, ExpandedGoal, ExCtx) :-
	'$lgt_current_category_'(Ctg, _, Dcl, Def, _, _),
	(	call(Dcl, goal_expansion(_, _), Scope, _, _, DclCtn) ->
		(	(Scope = p(_); Ctg = DclCtn) ->
			'$lgt_expand_goal_category_scoped'(Goal, ExpandedGoal, Def, ExCtx)
		;	% declaration is out of scope but we can still try a local definition
			'$lgt_expand_goal_category_local'(Goal, ExpandedGoal, Def, ExCtx, [])
		)
	;	% no declaration for the goal_expansion/2 hook predicate found;
		% try to use a local definition if it exists
		'$lgt_expand_goal_category_local'(Goal, ExpandedGoal, Def, ExCtx, [])
	).


% '$lgt_expand_goal_object_scoped'(?term, ?term, +atom, +execution_context)
%
% to avoid failures when the call is made from a multifile predicate clause,
% first the goal_expansion/2 definition container is located and then the
% call is reduced to a local call

'$lgt_expand_goal_object_scoped'(Goal, ExpandedGoal, Def, ExCtx) :-
	(	call(Def, goal_expansion(_, _), _, _, _, DCtn) ->
		(	'$lgt_current_object_'(DCtn, _, _, DCtnDef, _, _, _, _, DCtnDDef, _, _) ->
			'$lgt_expand_goal_object_local'(Goal, ExpandedGoal, DCtnDef, DCtnDDef, ExCtx, [])
		;	'$lgt_current_category_'(DCtn, _, _, DCtnDef, _, _),
			'$lgt_expand_goal_category_local'(Goal, ExpandedGoal, DCtnDef, ExCtx, [])
		)
	;	% no goal_expansion/2 hook predicate definition found
		ExpandedGoal = Goal
	).


% '$lgt_expand_goal_object_local'(?term, ?term, +atom, +atom, +execution_context, +list)

'$lgt_expand_goal_object_local'(Goal, ExpandedGoal, Def, DDef, ExCtx, ExpandedGoals) :-
	(	var(Goal) ->
		ExpandedGoal = Goal
	;	'$lgt_push_if_new'(ExpandedGoals, Goal, NewExpandedGoals),
		% lookup local goal_expansion/2 hook predicate definition
		(	call(Def, goal_expansion(Goal, ExpandedGoal0), ExCtx, Call)
		;	call(DDef, goal_expansion(Goal, ExpandedGoal0), ExCtx, Call)
		) ->
		(	call(Call),
			Goal \== ExpandedGoal0 ->
			'$lgt_expand_goal_object_local'(ExpandedGoal0, ExpandedGoal, Def, DDef, ExCtx, NewExpandedGoals)
		;	% fixed-point found
			ExpandedGoal = Goal
		)
	;	% no local goal_expansion/2 hook predicate definition found
		ExpandedGoal = Goal
	).


% '$lgt_expand_goal_category_scoped'(?term, ?term, +atom, +execution_context)
%
% to avoid failures when the call is made from a multifile predicate clause,
% first the goal_expansion/2 definition container is located and then the
% call is reduced to a local call

'$lgt_expand_goal_category_scoped'(Goal, ExpandedGoal, Def, ExCtx) :-
	(	call(Def, goal_expansion(_, _), _, _, DCtn) ->
		'$lgt_current_category_'(DCtn, _, _, DCtnDef, _, _),
		'$lgt_expand_goal_category_local'(Goal, ExpandedGoal, DCtnDef, ExCtx, [])
	;	% no local goal_expansion/2 hook predicate definition found
		ExpandedGoal = Goal
	).


% '$lgt_expand_goal_object_local'(?term, ?term, +atom, +execution_context, +list)

'$lgt_expand_goal_category_local'(Goal, ExpandedGoal, Def, ExCtx, ExpandedGoals) :-
	(	var(Goal) ->
		ExpandedGoal = Goal
	;	'$lgt_push_if_new'(ExpandedGoals, Goal, NewExpandedGoals),
		% lookup local goal_expansion/2 hook predicate definition
		call(Def, goal_expansion(Goal, ExpandedGoal0), ExCtx, Call) ->
		(	call(Call),
			Goal \== ExpandedGoal0 ->
			'$lgt_expand_goal_category_local'(ExpandedGoal0, ExpandedGoal, Def, ExCtx, NewExpandedGoals)
		;	% fixed-point found
			ExpandedGoal = Goal
		)
	;	% no local goal_expansion/2 hook predicate definition found
		ExpandedGoal = Goal
	).



% '$lgt_expand_goal_message'(+object_identifier, ?term, ?term, +object_identifier, @scope)
%
% expand_goal/2 messages
%
% calls the goal_expansion/2 user-defined hook predicate if defined and within scope

'$lgt_expand_goal_message'(Obj, Goal, ExpandedGoal, Sender, LookupScope) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, _),
	(	% lookup visible goal_expansion/2 hook predicate declaration
		call(Dcl, goal_expansion(_, _), PredScope, _, _, SCtn, _) ->
		(	(PredScope = LookupScope; Sender = SCtn) ->
			'$lgt_execution_context'(ExCtx, Obj, Sender, Obj, Obj, [], []),
			'$lgt_expand_goal_message_aux'(Goal, ExpandedGoal, Def, ExCtx, [])
		;	% message is out of scope
			ExpandedGoal = Goal
		)
	;	% no declaration for the goal_expansion/2 hook predicate found
		ExpandedGoal = Goal
	).


% '$lgt_expand_goal_message_aux'(?term, ?term, +atom, +execution_context, +list)

'$lgt_expand_goal_message_aux'(Goal, ExpandedGoal, Def, ExCtx, ExpandedGoals) :-
	(	var(Goal) ->
		ExpandedGoal = Goal
	;	'$lgt_push_if_new'(ExpandedGoals, Goal, NewExpandedGoals),
		% lookup visible goal_expansion/2 hook predicate definition
		call(Def, goal_expansion(Goal, ExpandedGoal0), ExCtx, Call, _, _) ->
		(	call(Call),
			Goal \== ExpandedGoal0 ->
			'$lgt_expand_goal_message_aux'(ExpandedGoal0, ExpandedGoal, Def, ExCtx, NewExpandedGoals)
		;	% fixed-point found
			ExpandedGoal = Goal
		)
	;	% no visible goal_expansion/2 hook predicate definition found
		ExpandedGoal = Goal
	).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  message-sending
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_send_to_self'(?term, +compilation_context)
%
% runtime processing of a message-sending call when the message is not
% known at compile-time

'$lgt_send_to_self'(Pred, Ctx) :-
	% we must ensure that the argument is valid before compiling the message
	% sending goal otherwise there would be a potential for an endless loop
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_check'(callable, Pred, logtalk(::Pred, ExCtx)),
	catch('$lgt_compile_message_to_self'(Pred, TPred, Ctx), Error, throw(error(Error, logtalk(::Pred, ExCtx)))),
	call(TPred).



% '$lgt_send_to_self_'(+object_identifier, +callable, +execution_context)
%
% the last clause of this dynamic binding cache predicate must always exist
% and must call the predicate that generates the missing cache entry

'$lgt_send_to_self_'(Obj, Pred, SenderExCtx) :-
	'$lgt_send_to_self_nv'(Obj, Pred, SenderExCtx).



% '$lgt_send_to_self_nv'(+object_identifier, +callable, +execution_context)
%
% runtime processing of a message-sending call when the arguments have already
% been type-checked; generates a cache entry to speed up future calls

'$lgt_send_to_self_nv'(Obj, Pred, SenderExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, _),
	'$lgt_execution_context'(SenderExCtx, _, _, Sender, _, _, _),
	(	% lookup predicate declaration
		call(Dcl, Pred, Scope, Meta, _, SCtn, _) ->
		(	% check scope
			(Scope = p(_); Sender = SCtn) ->
			(	% construct predicate, object, and "sender" templates
				'$lgt_term_template'(Pred, GPred),
				'$lgt_term_template'(Obj, GObj),
				'$lgt_term_template'(Sender, GSender),
				% get the execution context for meta-calls
				'$lgt_goal_meta_call_context'(Meta, GSenderExCtx, GSender, GMetaCallCtx),
				% lookup predicate definition
				'$lgt_execution_context'(GExCtx, _, GSender, GObj, GObj, GMetaCallCtx, []),
				call(Def, GPred, GExCtx, GCall, _, _) ->
				% cache lookup result (the cut prevents backtracking into the catchall clause)
				asserta(('$lgt_send_to_self_'(GObj, GPred, GSenderExCtx) :- !, GCall)),
				% unify message arguments and call method
				GObj = Obj, GPred = Pred, GSender = Sender, GSenderExCtx = SenderExCtx,
				call(GCall)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% message is not within the scope of the sender
			functor(Pred, Functor, Arity),
			throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(::Pred, SenderExCtx)))
		)
	;	% no predicate declaration, check if it's a built-in method
		'$lgt_built_in_method'(Pred, Scope, _, _) ->
		(	Scope == p ->
			functor(Pred, Functor, Arity),
			throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(::Pred, SenderExCtx)))
		;	% Scope == p(p(p)),
			'$lgt_comp_ctx'(Ctx, _, _, _, Sender, Obj, Obj, _, [], _, _, runtime, _, _, _),
			'$lgt_compile_message_to_self'(Pred, Call, Ctx),
			call(Call)
		)
	;	% message not understood; check for a message forwarding handler
		call(Def, forward(Pred), ExCtx, Call, _, _) ->
		'$lgt_execution_context'(ExCtx, _, Sender, Obj, Obj, [], []),
		call(Call)
	;	% give up and throw an existence error
		functor(Pred, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(::Pred, SenderExCtx)))
	).



% '$lgt_send_to_obj_rt'(?term, ?term, +atom, +compilation_context)
%
% runtime processing of a message-sending call when the message and
% possibly the receiver object are not known at compile-time

'$lgt_send_to_obj_rt'(Obj, Pred, Events, Ctx) :-
	% we must ensure that the message is valid before compiling the
	% message-sending goal otherwise an endless loop could result
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_check'(callable, Pred, logtalk(Obj::Pred, ExCtx)),
	catch(
		'$lgt_compile_message_to_object'(Pred, Obj, TPred, Events, Ctx),
		Error,
		throw(error(Error, logtalk(Obj::Pred, ExCtx)))
	),
	call(TPred).



% '$lgt_send_to_obj'(+object_identifier, +callable, +execution_context)
%
% runtime processing of an event-aware message-sending call when the
% receiver object is not known at compile-time; as using the cache
% only requires a bound first argument, we delay errors other than an
% instantiation error for a small performance gain

'$lgt_send_to_obj'(Obj, Pred, SenderExCtx) :-
	(	nonvar(Obj) ->
		'$lgt_send_to_obj_'(Obj, Pred, SenderExCtx)
	;	throw(error(instantiation_error, logtalk(Obj::Pred, SenderExCtx)))
	).



% '$lgt_send_to_obj_'(+object_identifier, +callable, +execution_context)
%
% the last clause of this dynamic binding cache predicate must always exist
% and must call the predicate that generates the missing cache entry

'$lgt_send_to_obj_'(Obj, Pred, SenderExCtx) :-
	'$lgt_send_to_obj_nv'(Obj, Pred, SenderExCtx).



% '$lgt_send_to_obj_nv'(+object_identifier, +callable, +execution_context)
%
% runtime processing of an event-aware message-sending call when the arguments
% have already been type-checked; generates a cache entry to speed up future calls

'$lgt_send_to_obj_nv'(Obj, Pred, SenderExCtx) :-
	'$lgt_execution_context'(SenderExCtx, _, _, Sender, _, _, _),
	% call all before event handlers
	\+ ('$lgt_before_event_'(Obj, Pred, Sender, _, Before), \+ Before),
	% process the message; we cannot simply call '$lgt_send_to_obj_ne'/3
	% as the generated cache entries are different
	'$lgt_send_to_obj_nv_inner'(Obj, Pred, Sender, SenderExCtx),
	% call all after event handlers
	\+ ('$lgt_after_event_'(Obj, Pred, Sender, _, After), \+ After).


'$lgt_send_to_obj_nv_inner'(Obj, Pred, Sender, SenderExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, _),
	!,
	(	% lookup predicate declaration
		call(Dcl, Pred, Scope, Meta, _, SCtn, _) ->
		(	% check public scope
			Scope = p(p(_)) ->
			(	% construct predicate and object templates
				'$lgt_term_template'(Pred, GPred),
				'$lgt_term_template'(Obj, GObj),
				% get the execution context for meta-calls
				'$lgt_goal_meta_call_context'(Meta, GSenderExCtx, GSender, GMetaCallCtx),
				% lookup predicate definition
				'$lgt_execution_context'(GExCtx, _, GSender, GObj, GObj, GMetaCallCtx, []),
				call(Def, GPred, GExCtx, GCall, _, _) ->
				GGCall = '$lgt_guarded_method_call'(GObj, GPred, GSender, GCall),
				% cache lookup result (the cut prevents backtracking into the catchall clause)
				asserta(('$lgt_send_to_obj_'(GObj, GPred, GSenderExCtx) :- !, GGCall)),
				% unify message arguments and call method
				GObj = Obj, GPred = Pred, GSender = Sender, GSenderExCtx = SenderExCtx,
				call(GCall)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% protected or private scope: check if sender and scope container are the same;
			% do not cache the lookup result as it's only valid when the sender unifies with
			% the scope container
			Sender = SCtn ->
			(	'$lgt_execution_context'(ExCtx, _, Sender, Obj, Obj, _, []),
				% lookup predicate definition
				call(Def, Pred, ExCtx, Call, _, _) ->
				'$lgt_guarded_method_call'(Obj, Pred, Sender, Call)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% message is not within the scope of the sender
			functor(Pred, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
			;	throw(error(permission_error(access, protected_predicate, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
			)
		)
	;	% no predicate declaration, check if it's a built-in method
		'$lgt_built_in_method'(Pred, Scope, _, _) ->
		(	Scope == p ->
			functor(Pred, Functor, Arity),
			throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
		;	% Scope == p(p(p)),
			'$lgt_comp_ctx'(Ctx, _, _, _, Sender, Obj, Obj, _, _, _, _, runtime, _, _, _),
			'$lgt_compile_message_to_object'(Pred, Obj, Call, allow, Ctx),
			call(Call)
		)
	;	% message not understood; check for a message forwarding handler
		call(Def, forward(Pred), ExCtx, Call, _, _) ->
		'$lgt_execution_context'(ExCtx, _, Sender, Obj, Obj, [], []),
		call(Call)
	;	% give up and throw an existence error
		functor(Pred, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
	).

'$lgt_send_to_obj_nv_inner'({Proxy}, Pred, _, SenderExCtx) :-
	!,
	% parametric object proxy
	catch(Proxy, error(Error, _), throw(error(Error, logtalk({Proxy}::Pred, SenderExCtx)))),
	'$lgt_send_to_obj_'(Proxy, Pred, SenderExCtx).

'$lgt_send_to_obj_nv_inner'(Obj, Pred, _, _) :-
	atom(Obj),
	'$lgt_prolog_feature'(modules, supported),
	current_module(Obj),
	!,
	% allow Obj::Pred to be used as a shortcut for calling module predicates
	':'(Obj, Pred).

'$lgt_send_to_obj_nv_inner'(Obj, Pred, _, SenderExCtx) :-
	\+ callable(Obj),
	throw(error(type_error(object_identifier, Obj), logtalk(Obj::Pred, SenderExCtx))).

'$lgt_send_to_obj_nv_inner'(Obj, Pred, _, SenderExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::Pred, SenderExCtx))).



% '$lgt_guarded_method_call'(+object_identifier, +callable, +object_identifier, +callable)
%
% wraps the method call with the before and after event handler calls; the "before" event handler
% may prevent a method from being executed by failing and an "after" event handler may prevent a
% method from succeeding by failing; however, event handlers cannot modify the method call

'$lgt_guarded_method_call'(Obj, Msg, Sender, Method) :-
	% call before event handlers
	\+ ('$lgt_before_event_'(Obj, Msg, Sender, _, Before), \+ Before),
	% call method
	call(Method),
	% call after event handlers
	\+ ('$lgt_after_event_'(Obj, Msg, Sender, _, After), \+ After).



% '$lgt_send_to_obj_ne'(+object_identifier, +callable, +execution_context)
%
% runtime processing of an event-transparent message-sending call when
% the receiver object is not known at compile-time; as using the cache
% only requires a bound first argument, we delay errors other than an
% instantiation error for a small performance gain

'$lgt_send_to_obj_ne'(Obj, Pred, SenderExCtx) :-
	(	nonvar(Obj) ->
		'$lgt_send_to_obj_ne_'(Obj, Pred, SenderExCtx)
	;	throw(error(instantiation_error, logtalk(Obj::Pred, SenderExCtx)))
	).



% '$lgt_send_to_obj_ne_'(+object_identifier, +callable, +execution_context)
%
% the last clause of this dynamic binding cache predicate must always exist
% and must call the predicate that generates the missing cache entry

'$lgt_send_to_obj_ne_'(Obj, Pred, SenderExCtx) :-
	'$lgt_send_to_obj_ne_nv'(Obj, Pred, SenderExCtx).



% '$lgt_send_to_obj_ne_nv'(+object_identifier, +term, +execution_context)
%
% runtime processing of an event-transparent message-sending call when the arguments
% have already been type-checked; generates a cache entry to speed up future calls

'$lgt_send_to_obj_ne_nv'(Obj, Pred, SenderExCtx) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, _),
	!,
	'$lgt_execution_context'(SenderExCtx, _, _, Sender, _, _, _),
	(	% lookup predicate declaration
		call(Dcl, Pred, Scope, Meta, _, SCtn, _) ->
		(	% check public scope
			Scope = p(p(_)) ->
			(	% construct predicate and object templates
				'$lgt_term_template'(Pred, GPred),
				'$lgt_term_template'(Obj, GObj),
				% get the execution context for meta-calls
				'$lgt_goal_meta_call_context'(Meta, GSenderExCtx, GSender, GMetaCallCtx),
				% lookup predicate definition
				'$lgt_execution_context'(GExCtx, _, GSender, GObj, GObj, GMetaCallCtx, []),
				call(Def, GPred, GExCtx, GCall, _, _) ->
				% cache lookup result (the cut prevents backtracking into the catchall clause)
				asserta(('$lgt_send_to_obj_ne_'(GObj, GPred, GSenderExCtx) :- !, GCall)),
				% unify message arguments and call method
				GObj = Obj, GPred = Pred, GSender = Sender, GSenderExCtx = SenderExCtx,
				call(GCall)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% protected or private scope: check if sender and scope container are the same;
			% do not cache the lookup result as it's only valid when the sender unifies with
			% the scope container
			Sender = SCtn ->
			(	% lookup predicate definition
				'$lgt_execution_context'(ExCtx, _, Sender, Obj, Obj, _, []),
				call(Def, Pred, ExCtx, Call, _, _) ->
				call(Call)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% message is not within the scope of the sender
			functor(Pred, Functor, Arity),
			(	Scope == p ->
				throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
			;	throw(error(permission_error(access, protected_predicate, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
			)
		)
	;	% no predicate declaration, check if it's a built-in method
		'$lgt_built_in_method'(Pred, Scope, _, _) ->
		(	Scope == p ->
			functor(Pred, Functor, Arity),
			throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
		;	% Scope == p(p(p)),
			'$lgt_comp_ctx'(Ctx, _, _, _, Sender, Obj, Obj, _, _, _, _, runtime, _, _, _),
			'$lgt_compile_message_to_object'(Pred, Obj, Call, deny, Ctx),
			call(Call)
		)
	;	% message not understood; check for a message forwarding handler
		call(Def, forward(Pred), ExCtx, Call, _, _) ->
		'$lgt_execution_context'(ExCtx, _, Sender, Obj, Obj, [], []),
		call(Call)
	;	% give up and throw an existence error
		functor(Pred, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(Obj::Pred, SenderExCtx)))
	).

'$lgt_send_to_obj_ne_nv'({Proxy}, Pred, SenderExCtx) :-
	!,
	% parametric object proxy
	catch(Proxy, error(Error, _), throw(error(Error, logtalk({Proxy}::Pred, SenderExCtx)))),
	'$lgt_send_to_obj_ne_'(Proxy, Pred, SenderExCtx).

'$lgt_send_to_obj_ne_nv'(Obj, Pred, _) :-
	atom(Obj),
	'$lgt_prolog_feature'(modules, supported),
	current_module(Obj),
	!,
	% allow Obj::Pred to be used as a shortcut for calling module predicates
	':'(Obj, Pred).

'$lgt_send_to_obj_ne_nv'(Obj, Pred, SenderExCtx) :-
	\+ callable(Obj),
	throw(error(type_error(object_identifier, Obj), logtalk(Obj::Pred, SenderExCtx))).

'$lgt_send_to_obj_ne_nv'(Obj, Pred, SenderExCtx) :-
	throw(error(existence_error(object, Obj), logtalk(Obj::Pred, SenderExCtx))).



% '$lgt_obj_super_call'(+atom, +term, +execution_context)
%
% runtime processing of an object "super" call when the predicate called is
% not known at compile-time; as using the cache only requires a bound first
% argument, we delay errors other than an instantiation error for a small
% performance gain

'$lgt_obj_super_call'(Super, Pred, ExCtx) :-
	(	nonvar(Pred) ->
		'$lgt_obj_super_call_'(Super, Pred, ExCtx)
	;	throw(error(instantiation_error, logtalk(^^Pred, ExCtx)))
	).



% '$lgt_obj_super_call_'(+atom, +callable, +execution_context)
%
% the last clause of this dynamic binding cache predicate must always exist
% and must call the predicate that generates the missing cache entry

'$lgt_obj_super_call_'(Super, Pred, ExCtx) :-
	'$lgt_obj_super_call_nv'(Super, Pred, ExCtx).



% '$lgt_obj_super_call_nv'(+atom, +callable, +execution_context)
%
% runtime processing of an object "super" call when the arguments have already
% been type-checked; generates a cache entry to speed up future calls
%
% we may need to pass "self" when looking for the inherited predicate definition
% in order to be able to select the correct "super" clause for those cases where
% "this" both instantiates and specializes other objects

'$lgt_obj_super_call_nv'(Super, Pred, ExCtx) :-
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(Self, _, Dcl, _, _, _, _, _, _, _, _),
	(	% lookup predicate declaration (the predicate must not be
		% declared in the same entity making the "super" call)
		call(Dcl, Pred, Scope, _, _, SCtn, TCtn), TCtn \= This ->
		(	% check scope
			(Scope = p(_); This = SCtn) ->
			(	% construct predicate, "this", and "self" templates
				'$lgt_term_template'(Pred, GPred),
				'$lgt_term_template'(This, GThis),
				'$lgt_term_template'(Self, GSelf),
				% check if we have a dependency on "self" to select the correct "super" clause
				(	'$lgt_extends_object_'(GThis, _, _) ->
					true
				;	'$lgt_execution_context'(GExCtx, _, _, GThis, GSelf, _, _)
				),
				% lookup predicate definition (the predicate must not be
				% defined in the same entity making the "super" call)
				call(Super, GPred, GExCtx, GCall, _, DefCtn), DefCtn \= GThis ->
				% cache lookup result (the cut prevents backtracking into the catchall clause)
				asserta(('$lgt_obj_super_call_'(Super, GPred, GExCtx) :- !, GCall)),
				% unify message arguments and call inherited definition
				GPred = Pred, GExCtx = ExCtx,
				call(GCall)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% predicate is not within the scope of the sender
			functor(Pred, Functor, Arity),
			throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(^^Pred, ExCtx)))
		)
	;	% no predicate declaration, check if it's a private built-in method
		'$lgt_built_in_method'(Pred, p, _, _) ->
		functor(Pred, Functor, Arity),
		throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(^^Pred, ExCtx)))
	;	% non-callable term error
		\+ callable(Pred) ->
		throw(error(type_error(callable, Pred), logtalk(^^Pred, ExCtx)))
	;	% give up and throw an existence error
		functor(Pred, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(^^Pred, ExCtx)))
	).



% '$lgt_ctg_super_call'(+category_identifier, +term, +execution_context)
%
% runtime processing of a category "super" call when the predicate called
% is not known at compile-time;  as using the cache only requires a bound
% first argument, we delay errors other than an instantiation error for a
% small performance gain

'$lgt_ctg_super_call'(Ctg, Pred, ExCtx) :-
	(	nonvar(Pred) ->
		'$lgt_ctg_super_call_'(Ctg, Pred, ExCtx)
	;	throw(error(instantiation_error, logtalk(^^Pred, ExCtx)))
	).




% '$lgt_ctg_super_call_'(+category_identifier, +callable, +execution_context)
%
% the last clause of this dynamic binding cache predicate must always exist
% and must call the predicate that generates the missing cache entry

'$lgt_ctg_super_call_'(Ctg, Pred, ExCtx) :-
	'$lgt_ctg_super_call_nv'(Ctg, Pred, ExCtx).



% '$lgt_ctg_super_call_nv'(+category_identifier, +callable, +execution_context)
%
% runtime processing of a category "super" call when the arguments have already
% been type-checked; generates a cache entry to speed up future calls

'$lgt_ctg_super_call_nv'(Ctg, Pred, ExCtx) :-
	'$lgt_current_category_'(Ctg, _, Dcl, Def, _, _),
	(	% lookup predicate declaration (the predicate must not be
		% declared in the same entity making the "super" call)
		call(Dcl, Pred, Scope, _, _, DclCtn), DclCtn \= Ctg ->
		(	% check that the call is within scope (i.e., public or protected)
			Scope = p(_) ->
			(	% construct category and predicate templates
				'$lgt_term_template'(Ctg, GCtg),
				'$lgt_term_template'(Pred, GPred),
				% lookup predicate definition (the predicate must not be
				% defined in the same entity making the "super" call)
				call(Def, GPred, GExCtx, GCall, DefCtn), DefCtn \= Ctg ->
				% cache lookup result (the cut prevents backtracking into the catchall clause)
				asserta(('$lgt_ctg_super_call_'(GCtg, GPred, GExCtx) :- !, GCall)),
				% unify message arguments and call inherited definition
				GCtg = Ctg, GPred = Pred, GExCtx = ExCtx,
				call(GCall)
			;	% no definition found; fail as per closed-world assumption
				fail
			)
		;	% predicate is not within the scope of the sender
			functor(Pred, Functor, Arity),
			throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(^^Pred, ExCtx)))
		)
	;	% no predicate declaration, check if it's a private built-in method
		'$lgt_built_in_method'(Pred, p, _, _) ->
		functor(Pred, Functor, Arity),
		throw(error(permission_error(access, private_predicate, Functor/Arity), logtalk(^^Pred, ExCtx)))
	;	% non-callable term error
		\+ callable(Pred) ->
		throw(error(type_error(callable, Pred), logtalk(^^Pred, ExCtx)))
	;	% give up and throw an existence error
		functor(Pred, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(^^Pred, ExCtx)))
	).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  meta-calls
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_lambda'(+curly_bracketed_term, @callable)
%
% calls a lambda-call with free variables but no parameters (Free/Goal) where the
% arguments are already checked and compiled; typically used in bagof/3 and setof/3
% as an alternative to the enumeration of all existentially quantified variables

'$lgt_lambda'(Free, Goal) :-
	'$lgt_copy_term_without_constraints'(Free/Goal, Free/GoalCopy),
	call(GoalCopy).



% '$lgt_metacall'(?term, +list, +execution_context, +atom)
%
% performs a runtime meta-call constructed from a closure and a list
% of additional arguments
%
% the last argument is either "local", in case of a local meta-call,
% or "runtime", in case the decision between a local meta-call or a
% meta-call in the sender is decided at runtime

'$lgt_metacall'(Closure, ExtraArgs, ExCtx, _) :-
	var(Closure),
	Call =.. [call, Closure| ExtraArgs],
	throw(error(instantiation_error, logtalk(Call, ExCtx))).

'$lgt_metacall'('$lgt_closure'(TFunctor, TArgs, ExCtx), ExtraArgs, _, _) :-
	% pre-compiled closure (note that the closure may be called from a mapping
	% predicate, which prevents us to use a difference list based solution to
	% avoid the calls to append/3 and =../2 as that would fix the extra arguments
	% in the goal on the first closure call and thus break the followup calls)
	!,
	'$lgt_append'(TArgs, ExtraArgs, FullArgs),
	TGoal =.. [TFunctor| FullArgs],
	call(TGoal, ExCtx).

'$lgt_metacall'('$lgt_local'(Closure), ExtraArgs, ExCtx, _) :-
	!,
	'$lgt_metacall'(Closure, ExtraArgs, ExCtx, local).

'$lgt_metacall'({Closure}, ExtraArgs, ExCtx, _) :-
	!,
	% compiler bypass (call of external code)
	(	atom(Closure) ->
		Goal =.. [Closure| ExtraArgs],
		call(Goal)
	;	compound(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		call(Goal)
	;	var(Closure) ->
		Call =.. [call, {Closure}| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	Call =.. [call, {Closure}| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	).

'$lgt_metacall'(::Closure, ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_execution_context'(ExCtx, _, _, _, Self0, CallerExCtx, _),
	(	CallerExCtx == [] ->
		Self = Self0,
		SelfExCtx = ExCtx
	;	Where == (local) ->
		Self = Self0,
		SelfExCtx = ExCtx
	;	'$lgt_execution_context'(CallerExCtx, _, _, _, Self, _, _),
		SelfExCtx = CallerExCtx
	),
	(	atom(Closure) ->
		Goal =.. [Closure| ExtraArgs],
		'$lgt_send_to_self_'(Self, Goal, SelfExCtx)
	;	compound(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		'$lgt_send_to_self_'(Self, Goal, SelfExCtx)
	;	var(Closure) ->
		Call =.. [call, ::Closure| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	Call =.. [call, ::Closure| ExtraArgs],
		throw(error(type_error(callable, ::Closure), logtalk(Call, ExCtx)))
	).

'$lgt_metacall'(^^Closure, ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_execution_context'(ExCtx, Entity0, _, _, _, CallerExCtx, _),
	(	CallerExCtx == [] ->
		Entity = Entity0,
		SuperExCtx = ExCtx
	;	Where == (local) ->
		Entity = Entity0,
		SuperExCtx = ExCtx
	;	'$lgt_execution_context'(CallerExCtx, Entity, _, _, _, _, _),
		SuperExCtx = CallerExCtx
	),
	(	atom(Closure) ->
		Goal =.. [Closure| ExtraArgs]
	;	compound(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs]
	;	var(Closure) ->
		Call =.. [call, ^^Closure| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	Call =.. [call, ^^Closure| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	),
	(	'$lgt_current_object_'(Entity, _, _, _, Super, _, _, _, _, _, _) ->
		'$lgt_obj_super_call_'(Super, Goal, SuperExCtx)
	;	'$lgt_current_category_'(Entity, _, _, _, _, _),
		'$lgt_ctg_super_call_'(Entity, Goal, SuperExCtx)
	).

'$lgt_metacall'(Obj::Closure, ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_execution_context'(ExCtx, _, Sender0, This, _, CallerExCtx0, _),
	(	CallerExCtx0 == [] ->
		CallerExCtx = ExCtx,
		Sender = This
	;	Where == (local) ->
		CallerExCtx = ExCtx,
		Sender = This
	;	CallerExCtx = CallerExCtx0,
		Sender = Sender0
	),
	(	callable(Obj), callable(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		(	'$lgt_current_object_'(Sender, _, _, _, _, _, _, _, _, _, Flags),
			Flags /\ 16 =:= 16 ->
			'$lgt_send_to_obj_'(Obj, Goal, CallerExCtx)
		;	'$lgt_send_to_obj_ne_'(Obj, Goal, CallerExCtx)
		)
	;	var(Obj) ->
		Call =.. [call, Obj::Closure| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	var(Closure) ->
		Call =.. [call, Obj::Closure| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	\+ callable(Closure) ->
		Call =.. [call, Obj::Closure| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	;	Call =.. [call, Obj::Closure| ExtraArgs],
		throw(error(type_error(object_identifier, Obj), logtalk(Call, ExCtx)))
	).

'$lgt_metacall'([Obj::Closure], ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_execution_context'(ExCtx, _, Sender0, _, _, CallerExCtx0, _),
	(	CallerExCtx0 == [] ->
		CallerExCtx1 = ExCtx,
		Sender = Sender0
	;	Where == (local) ->
		CallerExCtx1 = ExCtx,
		Sender = Sender0
	;	'$lgt_execution_context'(CallerExCtx0, _, Sender, _, _, _, _),
		CallerExCtx1 = CallerExCtx0
	),
	(	callable(Obj), callable(Closure), Obj \= Sender ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		% prevent the original sender, which is preserved when delegating a message, to be reset to "this"
		'$lgt_execution_context'(CallerExCtx1, Entity, Sender, _, Self, MetaCallCtx, Stack),
		'$lgt_execution_context'(CallerExCtx, Entity, Sender, Sender, Self, MetaCallCtx, Stack),
		(	'$lgt_current_object_'(Sender, _, _, _, _, _, _, _, _, _, Flags),
			Flags /\ 16 =:= 16 ->
			'$lgt_send_to_obj_'(Obj, Goal, CallerExCtx)
		;	'$lgt_send_to_obj_ne_'(Obj, Goal, CallerExCtx)
		)
	;	var(Obj) ->
		Call =.. [call, [Obj::Closure]| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	var(Closure) ->
		Call =.. [call, [Obj::Closure]| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	\+ callable(Closure) ->
		Call =.. [call, [Obj::Closure]| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	;	\+ callable(Obj) ->
		Call =.. [call, [Obj::Closure]| ExtraArgs],
		throw(error(type_error(object_identifier, Obj), logtalk(Call, ExCtx)))
	;	% Obj = Sender ->
		Call =.. [call, [Obj::Closure]| ExtraArgs],
		throw(error(permission_error(access, object, Sender), logtalk(Call, ExCtx)))
	).

'$lgt_metacall'(Obj<<Closure, ExtraArgs, ExCtx, _) :-
	!,
	(	callable(Obj), callable(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		'$lgt_call_within_context_nv'(Obj, Goal, ExCtx)
	;	var(Obj) ->
		Call =.. [call, Obj<<Closure| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	var(Closure) ->
		Call =.. [call, Obj<<Closure| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	\+ callable(Closure) ->
		Call =.. [call, Obj<<Closure| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	;	Call =.. [call, Obj<<Closure| ExtraArgs],
		throw(error(type_error(object_identifier, Obj), logtalk(Call, ExCtx)))
	).

'$lgt_metacall'(':'(Module, Closure), ExtraArgs, ExCtx, _) :-
	!,
	(	atom(Module), callable(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		':'(Module, Goal)
	;	var(Module) ->
		Call =.. [call, ':'(Module, Closure)| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	var(Closure) ->
		Call =.. [call, ':'(Module, Closure)| ExtraArgs],
		throw(error(instantiation_error, logtalk(Call, ExCtx)))
	;	\+ atom(Module) ->
		Call =.. [call, ':'(Module, Closure)| ExtraArgs],
		throw(error(type_error(module_identifier, Module), logtalk(Call, ExCtx)))
	;	Call =.. [call, ':'(Module, Closure)| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	).

'$lgt_metacall'(Free/Lambda, ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_check'(curly_bracketed_term, Free, logtalk(Free/Lambda, ExCtx)),
	'$lgt_execution_context'(ExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack),
	'$lgt_copy_term_without_constraints'(Free/Lambda+MetaCallCtx, Free/LambdaCopy+MetaCallCtxCopy),
	'$lgt_execution_context'(NewExCtx, Entity, Sender, This, Self, MetaCallCtxCopy, Stack),
	'$lgt_metacall'(LambdaCopy, ExtraArgs, NewExCtx, Where).

'$lgt_metacall'(Free/Parameters>>Lambda, ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_check'(curly_bracketed_term, Free, logtalk(Free/Parameters>>Lambda, ExCtx)),
	'$lgt_execution_context'(ExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack),
	'$lgt_copy_term_without_constraints'(Free/Parameters>>Lambda+MetaCallCtx, Free/ParametersCopy>>LambdaCopy+MetaCallCtxCopy),
	'$lgt_unify_lambda_parameters'(ParametersCopy, ExtraArgs, Rest, Free/Parameters>>Lambda, This),
	'$lgt_execution_context'(NewExCtx, Entity, Sender, This, Self, MetaCallCtxCopy, Stack),
	'$lgt_metacall'(LambdaCopy, Rest, NewExCtx, Where).

'$lgt_metacall'(Parameters>>Lambda, ExtraArgs, ExCtx, Where) :-
	!,
	'$lgt_execution_context'(ExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack),
	'$lgt_copy_term_without_constraints'(Parameters>>Lambda+MetaCallCtx, ParametersCopy>>LambdaCopy+MetaCallCtxCopy),
	'$lgt_unify_lambda_parameters'(ParametersCopy, ExtraArgs, Rest, Parameters>>Lambda, ExCtx),
	'$lgt_execution_context'(NewExCtx, Entity, Sender, This, Self, MetaCallCtxCopy, Stack),
	'$lgt_metacall'(LambdaCopy, Rest, NewExCtx, Where).

'$lgt_metacall'(Closure, ExtraArgs, ExCtx, Where) :-
	(	atom(Closure) ->
		Goal =.. [Closure| ExtraArgs]
	;	compound(Closure) ->
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs]
	;	Call =.. [call, Closure| ExtraArgs],
		throw(error(type_error(callable, Closure), logtalk(Call, ExCtx)))
	),
	'$lgt_execution_context'(ExCtx, _, _, _, _, CallerExCtx, _),
	(	CallerExCtx == [] ->
		'$lgt_metacall_local'(Goal, ExCtx)
	;	Where == (local) ->
		'$lgt_metacall_local'(Goal, ExCtx)
	;	'$lgt_metacall_sender'(Goal, ExCtx, CallerExCtx, ExtraArgs)
	).


'$lgt_unify_lambda_parameters'((-), _, _, Lambda, ExCtx) :-
	% catch variables and lists with unbound tails
	(	Lambda = _/Parameters>>_
	;	Lambda = Parameters>>_
	),
	throw(error(type_error(list, Parameters), logtalk(Lambda, ExCtx))).

'$lgt_unify_lambda_parameters'([], ExtraArguments, ExtraArguments, _, _) :-
	!.

'$lgt_unify_lambda_parameters'([Parameter| Parameters], [Argument| Arguments], ExtraArguments, Lambda, ExCtx) :-
	!,
	Parameter = Argument,
	'$lgt_unify_lambda_parameters'(Parameters, Arguments, ExtraArguments, Lambda, ExCtx).

'$lgt_unify_lambda_parameters'(_, _, _, Lambda, ExCtx) :-
	throw(error(representation_error(lambda_parameters), logtalk(Lambda, ExCtx))).



% '$lgt_metacall'(?term, +execution_context, +atom)
%
% performs a meta-call at runtime
%
% the last argument is either "local", in case of a local meta-call,
% or "runtime", in case the decision between a local meta-call or a
% meta-call in the sender is decided at runtime

'$lgt_metacall'(Goal, ExCtx, _) :-
	var(Goal),
	throw(error(instantiation_error, logtalk(call(Goal), ExCtx))).

'$lgt_metacall'('$lgt_local'(Goal), ExCtx, _) :-
	!,
	'$lgt_metacall'(Goal, ExCtx, local).

'$lgt_metacall'({Goal}, ExCtx, _) :-
	% pre-compiled meta-calls or calls in "user" (compiler bypass)
	!,
	(	callable(Goal) ->
		call(Goal)
	;	var(Goal) ->
		throw(error(instantiation_error, logtalk({Goal}, ExCtx)))
	;	throw(error(type_error(callable, Goal), logtalk({Goal}, ExCtx)))
	).

'$lgt_metacall'(Goal, ExCtx, Where) :-
	'$lgt_execution_context'(ExCtx, _, _, _, _, CallerExCtx, _),
	(	CallerExCtx == [] ->
		'$lgt_metacall_local'(Goal, ExCtx)
	;	Where == (local) ->
		'$lgt_metacall_local'(Goal, ExCtx)
	;	'$lgt_metacall_sender'(Goal, ExCtx, CallerExCtx, [])
	).



% '$lgt_quantified_metacall'(?term, +execution_context, +atom)
%
% performs a possibly qualified meta-call at runtime for goals within bagof/3 and setof/3 calls
%
% the first argument is the original goal in the bagof/3 or setof/3 call and it's used to check
% in which context the meta-call should take place
%
% the second argument is the original goal without existential variables that will be meta-called

'$lgt_quantified_metacall'(Goal, ExCtx, _) :-
	var(Goal),
	throw(error(instantiation_error, logtalk(call(Goal), ExCtx))).

'$lgt_quantified_metacall'('$lgt_local'(Goal), ExCtx, _) :-
	!,
	'$lgt_quantified_metacall'(Goal, ExCtx, local).

'$lgt_quantified_metacall'({Goal}, ExCtx, _) :-
	% pre-compiled meta-calls or calls in "user" (compiler bypass)
	!,
	(	callable(Goal) ->
		call(Goal)
	;	var(Goal) ->
		throw(error(instantiation_error, logtalk({Goal}, ExCtx)))
	;	throw(error(type_error(callable, Goal), logtalk({Goal}, ExCtx)))
	).

'$lgt_quantified_metacall'(Goal, ExCtx, Where) :-
	'$lgt_execution_context'(ExCtx, _, _, _, _, CallerExCtx, _),
	(	CallerExCtx == [] ->
		'$lgt_metacall_local'(Goal, ExCtx)
	;	Where == (local) ->
		'$lgt_metacall_local'(Goal, ExCtx)
	;	'$lgt_metacall_sender'(Goal, ExCtx, CallerExCtx, [])
	).



% '$lgt_metacall_local'(+callable, +execution_context)
%
% performs a local meta-call at runtime

'$lgt_metacall_local'(Pred, ExCtx) :-
	'$lgt_execution_context'(ExCtx, Entity, Sender, This, Self, _, Stack),
	(	'$lgt_current_object_'(Entity, Prefix, _, Def, _, _, _, _, DDef, _, Flags) ->
		(	% in the most common case we're meta-calling a user defined static predicate
			call(Def, Pred, ExCtx, TPred) ->
			call(TPred)
		;	% or a user defined dynamic predicate
			call(DDef, Pred, ExCtx, TPred) ->
			call(TPred)
		;	% in the worst case we need to compile the meta-call
			'$lgt_comp_ctx'(Ctx, _, ExCtx, Entity, Sender, This, Self, Prefix, [], _, ExCtx, runtime, Stack, _, _),
			catch('$lgt_compile_body'(Pred, _, TPred, DPred, Ctx), Error, throw(error(Error, logtalk(call(Pred), ExCtx)))),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				catch(DPred, error(Error,_), throw(error(Error, logtalk(call(Pred), ExCtx))))
			;	catch(TPred, error(Error,_), throw(error(Error, logtalk(call(Pred), ExCtx))))
			)
		)
	;	'$lgt_current_category_'(Entity, Prefix, _, Def, _, Flags),
		(	% in the most common case we're meta-calling a user defined predicate
			call(Def, Pred, ExCtx, TPred) ->
			call(TPred)
		;	% in the worst case we need to compile the meta-call
			'$lgt_comp_ctx'(Ctx, _, ExCtx, Entity, Sender, This, Self, Prefix, [], _, ExCtx, runtime, [], _, _),
			catch('$lgt_compile_body'(Pred, _, TPred, DPred, Ctx), Error, throw(error(Error, logtalk(call(Pred), ExCtx)))),
			(	Flags /\ 512 =:= 512 ->
				% category compiled in debug mode
				catch(DPred, error(Error,_), throw(error(Error, logtalk(call(Pred), ExCtx))))
			;	catch(TPred, error(Error,_), throw(error(Error, logtalk(call(Pred), ExCtx))))
			)
		)
	).



% '$lgt_metacall_sender'(+callable, +execution_context, +execution_context, +list)
%
% performs a meta-call in "sender" at runtime
%
% we must pass any extra arguments (a non-empty list when processing closures)
% as compilation context meta-variables to properly compile calls to control
% constructs (e.g., conjunctions) where those extra arguments must be called in
% the correct context

'$lgt_metacall_sender'(Pred, ExCtx, CallerExCtx, ExtraArgs) :-
	'$lgt_execution_context'(CallerExCtx, CallerEntity, Sender, This, Self, _, Stack),
	(	CallerEntity == user ->
		catch(Pred, error(Error,_), throw(error(Error, logtalk(call(Pred), CallerExCtx))))
	;	'$lgt_current_object_'(CallerEntity, CallerPrefix, _, Def, _, _, _, _, DDef, _, Flags) ->
		(	% in the most common case we're meta-calling a user defined static predicate
			call(Def, Pred, CallerExCtx, TPred) ->
			call(TPred)
		;	% or a user defined dynamic predicate
			call(DDef, Pred, CallerExCtx, TPred) ->
			call(TPred)
		;	% in the worst case we have a control construct or a built-in predicate
			(	ExtraArgs == [] ->
				MetaCallCtx = []
			;	MetaCallCtx = ExCtx
			),
			'$lgt_execution_context'(NewCallerExCtx, CallerEntity, Sender, This, Self, MetaCallCtx, Stack),
			'$lgt_comp_ctx'(Ctx, _, NewCallerExCtx, CallerEntity, Sender, This, Self, CallerPrefix, ExtraArgs, MetaCallCtx, NewCallerExCtx, runtime, Stack, _, _),
			catch('$lgt_compile_body'(Pred, _, TPred, DPred, Ctx), Error, throw(error(Error, logtalk(call(Pred), CallerExCtx)))),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				catch(DPred, error(Error,_), throw(error(Error, logtalk(call(Pred), CallerExCtx))))
			;	catch(TPred, error(Error,_), throw(error(Error, logtalk(call(Pred), CallerExCtx))))
			)
		)
	;	'$lgt_current_category_'(CallerEntity, CallerPrefix, _, Def, _, Flags),
		(	% in the most common case we're meta-calling a user defined static predicate
			call(Def, Pred, CallerExCtx, TPred) ->
			call(TPred)
		;	% in the worst case we have a control construct or a built-in predicate
			(	ExtraArgs == [] ->
				MetaCallCtx = []
			;	MetaCallCtx = ExCtx
			),
			'$lgt_execution_context'(NewCallerExCtx, CallerEntity, Sender, This, Self, MetaCallCtx, Stack),
			'$lgt_comp_ctx'(Ctx, _, NewCallerExCtx, CallerEntity, Sender, This, Self, CallerPrefix, ExtraArgs, MetaCallCtx, NewCallerExCtx, runtime, Stack, _, _),
			catch('$lgt_compile_body'(Pred, _, TPred, DPred, Ctx), Error, throw(error(Error, logtalk(call(Pred), CallerExCtx)))),
			(	Flags /\ 512 =:= 512 ->
				% object compiled in debug mode
				catch(DPred, error(Error,_), throw(error(Error, logtalk(call(Pred), CallerExCtx))))
			;	catch(TPred, error(Error,_), throw(error(Error, logtalk(call(Pred), CallerExCtx))))
			)
		)
	).



% '$lgt_call_within_context'(?term, ?term, +object_identifier)
%
% calls a goal within the context of the specified object when the object and/or the
% goal are only known at runtime
%
% used mostly for debugging and for writing unit tests, the permission to perform a
% context-switching call can be disabled in a per-object basis by using the compiler
% flag "context_switching_calls"

'$lgt_call_within_context'(Obj, Goal, ExCtx) :-
	'$lgt_check'(object_identifier, Obj, logtalk(Obj<<Goal, ExCtx)),
	'$lgt_check'(callable, Goal, logtalk(Obj<<Goal, ExCtx)),
	'$lgt_compile_context_switch_call'(Obj, Goal, TGoal, ExCtx),
	call(TGoal).



% '$lgt_call_within_context_nv'(+object_identifier, +callable, +execution_context)
%
% calls a goal within the context of the specified object (arguments type-checked
% at compile-time)

'$lgt_call_within_context_nv'(Obj, Goal, ExCtx) :-
	(	Obj == user ->
		catch(Goal, Error, '$lgt_runtime_error_handler'(error(Error, logtalk(user<<Goal, ExCtx))))
	;	'$lgt_current_object_'(Obj, Prefix, _, Def, _, _, _, _, DDef, _, Flags) ->
		(	Flags /\ 256 =:= 256 ->
			% object compiled with context-switching calls allowed
			'$lgt_execution_context'(ObjExCtx, Obj, Obj, Obj, Obj, [], []),
			(	% in the most common case we're calling a user defined static predicate
				call(Def, Goal, ObjExCtx, TGoal) ->
				catch(TGoal, Error, '$lgt_runtime_error_handler'(error(Error, logtalk(Obj<<Goal, ExCtx))))
				% or a user defined dynamic predicate
			;	call(DDef, Goal, ObjExCtx, TGoal) ->
				catch(TGoal, Error, '$lgt_runtime_error_handler'(error(Error, logtalk(Obj<<Goal, ExCtx))))
			;	% in the worst case we need to compile the goal
				'$lgt_comp_ctx'(ObjCtx, _, ObjExCtx, Obj, Obj, Obj, Obj, Prefix, [], _, ObjExCtx, runtime, [], _, _),
				catch('$lgt_compile_body'(Goal, _, TGoal, DGoal, ObjCtx), Error, throw(error(Error, logtalk(Obj<<Goal, ExCtx)))),
				(	Flags /\ 512 =:= 512 ->
					% object compiled in debug mode
					catch(DGoal, Error, '$lgt_runtime_error_handler'(error(Error, logtalk(Obj<<Goal, ExCtx))))
				;	catch(TGoal, Error, '$lgt_runtime_error_handler'(error(Error, logtalk(Obj<<Goal, ExCtx))))
				)
			)
		;	throw(error(permission_error(access, database, Goal), logtalk(Obj<<Goal, ExCtx)))
		)
	;	throw(error(existence_error(object, Obj), logtalk(Obj<<Goal, ExCtx)))
	).



% '$lgt_call_in_this_checked'(+callable, +execution_context)
%
% calls a dynamic and declared predicate in "this" from within a category at runtime;
% also used to call overridden predicate definitions from complementing categories

'$lgt_call_in_this_checked'(Pred, ExCtx) :-
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	'$lgt_current_object_'(This, _, _, Def, _, _, _, _, DDef, _, _),
	(	% the object definition may include some initial clauses for the predicate
		call(Def, Pred, ExCtx, TPred) ->
		call(TPred)
	;	% or the clauses for the predicate may be defined only at runtime
		call(DDef, Pred, ExCtx, TPred) ->
		call(TPred)
	;	% no definition found; fail as per closed-world assumption
		fail
	).



% '$lgt_call_in_this'(+callable, +execution_context)
%
% calls a dynamic predicate in "this" from within a category at runtime;
% also used to call overridden predicate definitions from complementing categories

'$lgt_call_in_this'(Pred, ExCtx) :-
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	'$lgt_current_object_'(This, _, Dcl, Def, _, _, _, _, DDef, _, _),
	(	\+ call(Dcl, Pred, _, _, _, _, _) ->
		% unknown predicate
		functor(Pred, Functor, Arity),
		throw(error(existence_error(predicate_declaration, Functor/Arity), logtalk(Pred, ExCtx)))
	;	% the object definition may include some initial clauses for the predicate
		call(Def, Pred, ExCtx, TPred) ->
		call(TPred)
	;	% or the clauses for the predicate may be defined only at runtime
		call(DDef, Pred, ExCtx, TPred) ->
		call(TPred)
	;	% no definition found; fail as per closed-world assumption
		fail
	).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  support for categories that complement objects (hot patching)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% lookup predicate declarations in any category that complements the given object

'$lgt_complemented_object'(This, ThisDcl, Alias, Scope, Meta, Flags, SCtn, TCtn) :-
	'$lgt_complemented_object_'(This, _, Dcl, _, Rnm),
	(	call(Dcl, Alias, Scope, Meta, Flags, TCtn),
		SCtn = This
	;	% categories can define aliases for complemented object predicates
		call(Rnm, This, Pred, Alias),
		Pred \= Alias,
		call(ThisDcl, Pred, Scope, Meta, Flags, SCtn, TCtn)
	).



% lookup predicate definitions in any category that complements the given object

'$lgt_complemented_object'(This, ThisDef, Alias, OExCtx, Call, Ctn) :-
	'$lgt_complemented_object_'(This, Ctg, _, Def, Rnm),
	'$lgt_execution_context_update_this_entity'(OExCtx, This, This, CExCtx, This, Ctg),
	(	call(Def, Alias, CExCtx, Call, Ctn)
	;	% categories may also define aliases for complemented object predicates
		call(Rnm, This, Pred, Alias),
		Pred \= Alias,
		call(ThisDef, Pred, OExCtx, Call, _, Ctn)
	).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  debugging base support
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_debug'(+compound, @execution_context)
%
% calls all defined trace event handlers and either use a loaded debug
% handler provider for the debug event or simply call the debugging goals
% to prevent execution of code compiled in debug mode to simply fail
%
% we can have multiple trace event handlers but only one debug handler
% (the compiler prints a warning when attempting to load a second handler)

'$lgt_debug'(Event, ExCtx) :-
	'$logtalk#0.trace_event#2'(Event, ExCtx, _),
	fail.

'$lgt_debug'(Event, ExCtx) :-
	'$logtalk#0.active_debug_handler_#1'(Provider, _),
	!,
	'$logtalk#0.debug_handler#3'(Provider, Event, ExCtx, _).

% top_goal(Goal, TGoal)
'$lgt_debug'(top_goal(_, TGoal), _) :-
	call(TGoal).

% goal(Goal, TGoal)
'$lgt_debug'(goal(_, TGoal), _) :-
	call(TGoal).

% fact(Entity, Fact, ClauseNumber, File, BeginLine)
'$lgt_debug'(fact(_, _, _, _, _), _).

% rule(Entity, Head, ClauseNumber, File, BeginLine)
'$lgt_debug'(rule(_, _, _, _, _), _).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  message printing support
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_print_message'(+atom_or_compound, +atom, +nonvar)
%
% internal predicate used by the compiler and runtime to print a message;
% we fake the execution context argument to call the corresponding method
% in the "logtalk" built-in object

'$lgt_print_message'(Kind, Message) :-
	(	'$lgt_built_in_entities_loaded_' ->
		% "logtalk" built-in object loaded
		'$lgt_execution_context'(ExCtx, logtalk, logtalk, logtalk, logtalk, [], []),
		'$logtalk#0.print_message#3'(Kind, core, Message, ExCtx)
	;	% still compiling the default built-in entities
		'$lgt_compiler_flag'(report, off) ->
		% no message printing required
		true
	;	% bare-bones message printing
		write('core '), write(Kind), write(': '), writeq(Message), nl
	).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  compiler
%
%  compiles Logtalk source files into intermediate Prolog source files
%  and calls the backend Prolog compiler on the generated files
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_load_files'(@source_file_name, @list(compiler_flag))
% '$lgt_load_files'(@list(source_file_name), @list(compiler_flag))
%
% compiles to disk and then loads to memory a source file or a list of source files
%
% a call to this predicate can trigger other calls to it, therefore we *must* clean
% the compilation auxiliary predicates *before* compiling a file

'$lgt_load_files'([], _) :-
	!.

'$lgt_load_files'([File| Files], Flags) :-
	!,
	'$lgt_clean_pp_file_clauses',
	'$lgt_set_compiler_flags'(Flags),
	'$lgt_load_file'(File, Flags),
	'$lgt_load_files'(Files, Flags).

'$lgt_load_files'(File, Flags) :-
	'$lgt_load_files'([File], Flags).



% '$lgt_load_file'(@source_file_name, @list)
%
% compiles to disk and then loads to memory a source file

'$lgt_load_file'(File, [RelativeTo| Flags]) :-
	(	'$lgt_source_file_name'(File, [RelativeTo| Flags], Directory, Name, Extension, SourceFile),
		\+ '$lgt_file_loading_stack_'(SourceFile, Directory),
		atom_concat(Name, Extension, Basename),
		(	'$lgt_loaded_file_'(Basename, Directory, _, _, _, _, _)
			% file already loaded; possibly an embedded application in which case we
			% don't want to throw a file existence error as the original source file
			% may not exist, or no longer exist, on the system where we are running
		;	'$lgt_file_exists'(SourceFile)
		) ->
		true
	;	'$lgt_source_file_name'(File, [RelativeTo| Flags], Directory, Name, Extension, SourceFile),
		'$lgt_file_loading_stack_'(SourceFile, Directory) ->
		% file trying to recursively load itself
		throw(error(permission_error(load, file, File), _))
	;	throw(error(existence_error(file, File), _))
	),
	(	'$lgt_loaded_file_'(Basename, Directory, PreviousMode, PreviousFlags, _, _, LoadingTimeStamp),
		\+ '$lgt_failed_file_'(SourceFile) ->
		% we're attempting to reload a file
		(	'$lgt_member'(reload(Reload), PreviousFlags) ->
			true
		;	'$lgt_compiler_flag'(reload, Reload)
		),
		(	Reload == skip ->
			% skip reloading already loaded files
			'$lgt_print_message'(comment(loading), skipping_reloading_file(SourceFile, Flags)),
			% but save the file loading dependency on a parent file if it exists
			'$lgt_save_file_loading_dependency'(SourceFile)
		;	Reload == changed,
			PreviousFlags == Flags,
			\+ '$lgt_changed_compilation_mode'(PreviousMode, PreviousFlags),
			'$lgt_file_modification_time'(SourceFile, CurrentTimeStamp),
			CurrentTimeStamp @=< LoadingTimeStamp ->
			% file was not modified since loaded and same explicit flags and compilation mode as before
			'$lgt_print_message'(comment(loading), skipping_reloading_file(SourceFile, Flags)),
			% but save the file loading dependency on a parent file if it exists
			'$lgt_save_file_loading_dependency'(SourceFile)
		;	% we're reloading a source file
			'$lgt_print_message'(silent(loading), reloading_file(SourceFile, Flags)),
			'$lgt_compile_and_load_file'(Directory, Name, Extension, Basename, SourceFile, Flags, reloading),
			'$lgt_print_message'(comment(loading), reloaded_file(SourceFile, Flags))
		)
	;	% first time loading this source file or previous attempt failed due to compilation error
		'$lgt_print_message'(silent(loading), loading_file(SourceFile, Flags)),
		'$lgt_compile_and_load_file'(Directory, Name, Extension, Basename, SourceFile, Flags, loading),
		'$lgt_print_message'(comment(loading), loaded_file(SourceFile, Flags))
	).


'$lgt_compile_and_load_file'(Directory, Name, Extension, Basename, SourceFile, Flags, Action) :-
	'$lgt_object_file_names'(Directory, Name, Extension, ObjectFilePid, ObjectFileDialect),
	retractall('$lgt_pp_file_paths_flags_'(_, _, _, _, _)),
	(	'$lgt_compiler_flag'(clean, on) ->
		ObjectFile = ObjectFilePid
	;	ObjectFile = ObjectFileDialect
	),
	assertz('$lgt_pp_file_paths_flags_'(Basename, Directory, SourceFile, ObjectFile, Flags)),
	retractall('$lgt_failed_file_'(SourceFile)),
	% save the file loading dependency on a parent file if it exists
	'$lgt_save_file_loading_dependency'(SourceFile),
	retractall('$lgt_file_loading_stack_'(SourceFile, Directory)),
	asserta('$lgt_file_loading_stack_'(SourceFile, Directory)),
	% compile the source file to an intermediate Prolog file on disk;
	% a syntax error while reading the terms in a source file results
	% in a printed message and failure instead of an exception but we
	% need to pass the failure up to the caller
	(	'$lgt_compile_file'(SourceFile, Flags, ObjectFile, Action) ->
		true
	;	retractall('$lgt_file_loading_stack_'(SourceFile, Directory)),
		'$lgt_propagate_failure_to_parent_files'(SourceFile),
		fail
	),
	% compile and load the intermediate Prolog file
	'$lgt_load_compiled_file'(SourceFile, ObjectFile),
	retractall('$lgt_file_loading_stack_'(SourceFile, _)),
	retractall('$lgt_pp_file_paths_flags_'(_, _, _, _, _)),
	% cleanup intermediate files if necessary
	(	'$lgt_compiler_flag'(clean, on) ->
		'$lgt_delete_intermediate_files'(ObjectFilePid),
		'$lgt_delete_intermediate_files'(ObjectFileDialect)
	;	true
	).


'$lgt_save_file_loading_dependency'(SourceFile) :-
	(	'$lgt_file_loading_stack_'(ParentSourceFile, _),
		SourceFile \== ParentSourceFile ->
		% as a file can have multiple parents, we only
		% ensure that there aren't duplicated entries
		retractall('$lgt_parent_file_'(SourceFile, ParentSourceFile)),
		asserta('$lgt_parent_file_'(SourceFile, ParentSourceFile))
	;	% no parent file
		true
	).


'$lgt_load_compiled_file'(SourceFile, ObjectFile) :-
	% retrieve the backend Prolog specific file loading options
	'$lgt_compiler_flag'(prolog_loader, DefaultOptions),
	% loading a file can result in the redefinition of existing
	% entities thus potentially invalidating cache entries
	'$lgt_clean_lookup_caches',
	'$lgt_report_redefined_entities',
	(	'$lgt_pp_file_encoding_'(SourceFile, _, Encoding, _) ->
		% use the same encoding as the original source file but do not use the inferred
		% bom/1 option as it would only work with some backend Prolog compilers
		Options = [encoding(Encoding)| DefaultOptions]
	;	Options = DefaultOptions
	),
	% clean all runtime clauses as an initialization goal in the intermediate Prolog file
	% that is loaded next may create dynamic entities
	'$lgt_clean_pp_runtime_clauses',
	% load the generated intermediate Prolog file but cope with unexpected error or failure
	(	(	catch('$lgt_load_prolog_code'(ObjectFile, SourceFile, Options), Error, true) ->
			(	var(Error) ->
				true
			;	% an error while loading the generated intermediate Prolog files is usually
				% caused by backend write_canonical/2 and/or read_term/3 predicate bugs
				'$lgt_print_message'(error, loading_error(SourceFile, Error)),
				fail
			)
		;	'$lgt_print_message'(error, loading_failure(SourceFile)),
			fail
		) ->
		true
	;	% loading of the intermediate Prolog file failed
		retractall('$lgt_file_loading_stack_'(SourceFile, _)),
		retractall('$lgt_pp_file_paths_flags_'(_, _, _, _, _)),
		'$lgt_propagate_failure_to_parent_files'(SourceFile),
		'$lgt_delete_intermediate_files'(ObjectFile),
		fail
	).


'$lgt_delete_intermediate_files'(ObjectFile) :-
	% try to delete the intermediate Prolog file (ignore failure or error)
	'$lgt_file_exists'(ObjectFile),
	catch('$lgt_delete_file'(ObjectFile), _, true),
	fail.

'$lgt_delete_intermediate_files'(ObjectFile) :-
	% try to delete any Prolog dialect specific auxiliary files (ignore failure or error)
	'$lgt_file_extension'(object, ObjectExtension),
	atom_concat(Name, ObjectExtension, ObjectFile),
	'$lgt_file_extension'(tmp, TmpExtension),
	atom_concat(Name, TmpExtension, TmpFile),
	'$lgt_file_exists'(TmpFile),
	catch('$lgt_delete_file'(TmpFile), _, true),
	fail.

'$lgt_delete_intermediate_files'(_).



% '$lgt_report_redefined_entities'
%
% prints a warning for all entities that are about to be redefined
%
% also retracts old runtime clauses for the entity being redefined for safety

'$lgt_report_redefined_entities' :-
	(	'$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Entity, _, _, _, _))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_category_'(Entity, _, _, _, _, _))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, _))
	),
	'$lgt_redefined_entity'(Entity, Type, OldFile, NewFile, Lines),
	'$lgt_report_redefined_entity'(Type, Entity, OldFile, NewFile, Lines),
	'$lgt_retract_old_runtime_clauses'(Type, Entity),
	fail.

'$lgt_report_redefined_entities'.



% '$lgt_redefined_entity'(@entity_identifier, -atom, -atom, -atom, -pair(integer))
%
% true if an entity of the same name is already loaded; returns entity type

'$lgt_redefined_entity'(Entity, Type, OldFile, NewFile, Lines) :-
	% check that an entity with the same identifier is already loaded
	(	'$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, Flags) ->
		Type = object
	;	'$lgt_current_protocol_'(Entity, _, _, _, Flags) ->
		Type = protocol
	;	'$lgt_current_category_'(Entity, _, _, _, _, Flags),
		Type = category
	),
	(	Flags /\ 1 =:= 1 ->
		% built-in entity; no redefinition allowed
		throw(permission_error(modify, Type, Entity))
	;	% redefinable entity but, in the presence of entity dynamic predicates, when
		% using some backend Prolog compilers, some old dynamic clauses may persist
		true
	),
	(	% check file information using the file_lines/4 entity property, if available
		'$lgt_entity_property_'(Entity, file_lines(OldBasename, OldDirectory, _, _)),
		'$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, file_lines(NewBasename, NewDirectory, Start, End))) ->
		atom_concat(OldDirectory, OldBasename, OldFile),
		atom_concat(NewDirectory, NewBasename, NewFile),
		Lines = Start-End
	;	% no file_lines/4 entity property (due to compilation with the source_data flag turned off)
		OldFile = nil,
		NewFile = nil,
		Lines = '-'(-1, -1)
	).



% '$lgt_report_redefined_entity'(+atom, @entity_identifier, +atom, +atom, +pair(integer))
%
% prints an informative message or a warning for a redefined entity

'$lgt_report_redefined_entity'(Type, Entity, OldFile, NewFile, Lines) :-
	(	OldFile == NewFile ->
		% either reloading the same source file or no source file data is available; assume entity redefinition normal
		'$lgt_print_message'(comment(loading), redefining_entity(Type, Entity))
	;	% we've conflicting entity definitions coming from different source files
		'$lgt_increment_loading_warnings_counter',
		'$lgt_print_message'(warning(loading), redefining_entity_from_file(NewFile, Lines, Type, Entity, OldFile))
	).



% '$lgt_retract_old_runtime_clauses'(+atom, @entity_identifier)
%
% cleans all references to an entity that is about to be redefined
% from the runtime tables

'$lgt_retract_old_runtime_clauses'(object, Entity) :-
	retractall('$lgt_before_event_'(_, _, _, Entity, _)),
	retractall('$lgt_after_event_'(_, _, _, Entity, _)),
	retractall('$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, _)),
	retractall('$lgt_entity_property_'(Entity, _)),
	retractall('$lgt_predicate_property_'(Entity, _, _)),
	retractall('$lgt_implements_protocol_'(Entity, _, _)),
	retractall('$lgt_imports_category_'(Entity, _, _)),
	retractall('$lgt_instantiates_class_'(Entity, _, _)),
	retractall('$lgt_specializes_class_'(Entity, _, _)),
	retractall('$lgt_extends_object_'(Entity, _, _)),
	retractall('$lgt_current_engine_'(Entity, _, _, _)).

'$lgt_retract_old_runtime_clauses'(protocol, Entity) :-
	retractall('$lgt_current_protocol_'(Entity, _, _, _, _)),
	retractall('$lgt_entity_property_'(Entity, _)),
	retractall('$lgt_predicate_property_'(Entity, _, _)),
	retractall('$lgt_extends_protocol_'(Entity, _, _)).

'$lgt_retract_old_runtime_clauses'(category, Entity) :-
	retractall('$lgt_current_category_'(Entity, _, _, _, _, _)),
	retractall('$lgt_entity_property_'(Entity, _)),
	retractall('$lgt_predicate_property_'(Entity, _, _)),
	retractall('$lgt_implements_protocol_'(Entity, _, _)),
	retractall('$lgt_extends_category_'(Entity, _, _)),
	retractall('$lgt_complemented_object_'(_, Entity, _, _, _)).



% '$lgt_compile_files'(@source_file_name, @list(compiler_flag))
% '$lgt_compile_files'(@list(source_file_name), @list(compiler_flag))
%
% compiles to disk a source file or a list of source files
%
% a call to this predicate can trigger other calls to it, therefore we *must*
% clean the compilation auxiliary predicates *before* compiling a file

'$lgt_compile_files'([], _) :-
	!,
	retractall('$lgt_pp_file_paths_flags_'(_, _, _, _, _)).

'$lgt_compile_files'([File| Files], [RelativeTo| Flags]) :-
	!,
	'$lgt_clean_pp_file_clauses',
	'$lgt_set_compiler_flags'(Flags),
	(	'$lgt_source_file_name'(File, [RelativeTo| Flags], Directory, Name, Extension, SourceFile),
		'$lgt_file_exists'(SourceFile) ->
		true
	;	throw(error(existence_error(file, File), _))
	),
	'$lgt_object_file_names'(Directory, Name, Extension, ObjectFilePid, ObjectFileDialect),
	atom_concat(Name, Extension, Basename),
	retractall('$lgt_pp_file_paths_flags_'(_, _, _, _, _)),
	(	'$lgt_compiler_flag'(clean, on) ->
		ObjectFile = ObjectFilePid
	;	ObjectFile = ObjectFileDialect
	),
	assertz('$lgt_pp_file_paths_flags_'(Basename, Directory, SourceFile, ObjectFile, Flags)),
	'$lgt_compile_file'(SourceFile, Flags, ObjectFile, compiling),
	'$lgt_compile_files'(Files, [RelativeTo| Flags]).

'$lgt_compile_files'(File, Flags) :-
	'$lgt_compile_files'([File], Flags).



% '$lgt_compile_file'(@source_file_name, @list, @source_file_name, +atom)
%
% compiles to disk a source file

'$lgt_compile_file'(SourceFile, Flags, ObjectFile, Action) :-
	(	% ensure that we disregard any existing the intermediate Prolog file
		% if we're reloading as that may be required due to different flags
		Action \== reloading,
		% interpret a clean(on) setting as (also) meaning that any
		% existing intermediate Prolog files should be disregarded
		'$lgt_compiler_flag'(clean, off),
		'$lgt_file_exists'(ObjectFile),
		'$lgt_up_to_date_object_file'(SourceFile, ObjectFile) ->
		'$lgt_print_message'(silent(compiling), up_to_date_file(SourceFile, Flags))
	;	% the intermediate Prolog file doesn't exist or it's outdated
		'$lgt_print_message'(silent(compiling), compiling_file(SourceFile, Flags)),
		'$lgt_compile_file'(SourceFile, ObjectFile),
		'$lgt_compiler_flag'(prolog_compiler, Options),
		'$lgt_compile_prolog_code'(ObjectFile, SourceFile, Options),
		(	Action == loading ->
			'$lgt_print_message'(silent(compiling), compiled_file(SourceFile, Flags))
		;	% Action == compiling,
			'$lgt_print_message'(comment(compiling), compiled_file(SourceFile, Flags))
		)
	).


% a file can be loaded by a loader file that, in turn, may also be loaded by
% another loader file; propagating a file loading failure to its parent files
% provides better top-level usability allowing reloading of fixed files by
% simply reloading the loader files, which also ensures loading of any files
% to be loaded after the broken file that were not loaded in the previous
% attempt

'$lgt_propagate_failure_to_parent_files'(File) :-
	(	'$lgt_parent_file_'(File, Parent) ->
		'$lgt_propagate_failure_to_parent_files'(Parent)
	;	assertz('$lgt_failed_file_'(File))
	).



% '$lgt_up_to_date_object_file'(+atom, +atom)
%
% Check that the object file is up-to-date

'$lgt_up_to_date_object_file'(SourceFile, ObjectFile) :-
	'$lgt_file_modification_time'(SourceFile, SourceFileTime),
	'$lgt_file_modification_time'(ObjectFile, ObjectFileTime),
	SourceFileTime @=< ObjectFileTime.



% '$lgt_write_entity_code'(+atom, @entity_identifier)
%
% writes to disk the entity compiled code

'$lgt_write_entity_code'(object, Obj) :-
	'$lgt_pp_object_'(Obj, _, _, _, _, _, _, _, _, Rnm, _),
	'$lgt_write_entity_code'(Rnm).

'$lgt_write_entity_code'(protocol, Ptc) :-
	'$lgt_pp_protocol_'(Ptc, _, _, Rnm, _),
	'$lgt_write_entity_code'(Rnm).

'$lgt_write_entity_code'(category, Ctg) :-
	'$lgt_pp_category_'(Ctg, _, _, _, Rnm, _),
	'$lgt_write_entity_code'(Rnm).


'$lgt_write_entity_code'(Rnm) :-
	'$lgt_pp_file_paths_flags_'(_, _, Path, _, _),
	% avoid a spurious choice-point with some backend Prolog compilers
	stream_property(Output, alias(logtalk_compiler_output)), !,
	catch(
		'$lgt_write_entity_code'(Output, Path, Rnm),
		Error,
		'$lgt_compiler_output_stream_error_handler'(Output, Error)
	).


'$lgt_write_entity_code'(Output, Path, Rnm) :-
	% write any plain Prolog terms that may precede the entity definition
	'$lgt_write_prolog_terms'(Output, Path),
	'$lgt_write_entity_directives'(Output, Path),
	'$lgt_write_entity_clauses'(Output, Path, Rnm).



% '$lgt_source_file_name'(+atom, +list(callable), -atom, -atom, -atom, -atom)
%
% converts a source file specification into a source file directory, basename,
% and full path
%
% the source file specification can be either absolute or relative and may or
% may not include a file name extension
%
% when the source file specification doesn't include a file extension, this
% predicate provides a solution for each defined Logtalk and Prolog source
% file extension; callers should test if the returned full path exists and
% commit to that solution when not simply generating all possible solutions

'$lgt_source_file_name'(FilePath, Flags, Directory, Name, Extension, SourceFile) :-
	(	(	sub_atom(FilePath, 0, 1, _, '/')
			% this covers the case of embedded applications created in a POSIX system
			% and being run on a Windows system where a path starting with a slash
			% would not be recognized as an absolute path by '$lgt_expand_path'/2
		;	'$lgt_expand_path'(FilePath, FilePath)
		) ->
		% assume full path
		SourceFile0 = FilePath
	;	% assume relative path and try possible alternatives
		(	once('$lgt_file_loading_stack_'(_, ParentDirectory)),
			% parent file exists; try first a path relative to its directory
			atom_concat(ParentDirectory, FilePath, SourceFile0)
		;	(	'$lgt_member'(relative_to(BasePath), Flags)
			;	'$lgt_member'('$relative_to'(BasePath), Flags)
			),
			(	sub_atom(BasePath, _, 1, 0, '/') ->
				atom_concat(BasePath, FilePath, SourceFile0)
			;	atom_concat(BasePath, '/', BasePathSlash),
				atom_concat(BasePathSlash, FilePath, SourceFile0)
			)
		;	% we may have a relative file path without any parent file
			% (e.g., when the user changes the working directory to the
			% directory containing the file to be loaded)
			'$lgt_expand_path'(FilePath, SourceFile0)
		)
	),
	'$lgt_decompose_file_name'(SourceFile0, Directory, Name0, Extension0),
	(	% file extensions are defined in the Prolog adapter files (there
		% might be multiple extensions defined for the same type of file)
		'$lgt_file_extension'(logtalk, Extension0) ->
		% declared extension for this type of file is present
		SourceFile = SourceFile0,
		Name = Name0,
		Extension = Extension0
	;	'$lgt_file_extension'(prolog, Extension0) ->
		% assume Prolog file being compiled as a Logtalk file
		SourceFile = SourceFile0,
		Name = Name0,
		Extension = Extension0
	;	% no Logtalk or Prolog extension for this type of file; generate possible
		% basenames starting with Logtalk extensions followed by Prolog extensions
		(	'$lgt_file_extension'(logtalk, Extension)
		;	'$lgt_file_extension'(prolog, Extension)
		),
		atom_concat(SourceFile0, Extension, SourceFile),
		atom_concat(Name0, Extension0, Name)
	;	% use basename as-is
		SourceFile = SourceFile0,
		atom_concat(Name0, Extension0, Name),
		Extension = ''
	).



% '$lgt_object_file_names'(+atom, +atom, +atom, -atom)
%
% converts a source file full path into two object file full paths: one that
% includes the process identifier for use when the clean flag is turned on
% (to allow parallel Logtalk processes) and one that includes the backend
% identifier for use when the clean flag is turned off (to avoid file name
% conflicts when running with backends generate the same directory hashes)

'$lgt_object_file_names'(SourceDirectory, SourceName, SourceExtension, ObjectFilePid, ObjectFileDialect) :-
	% temporary files are stored in the defined scratch directory
	'$lgt_compiler_flag'(scratch_directory, ScratchDirectory0),
	% allow using library notation to specify the scratch directory
	'$lgt_check_and_expand_source_file'(ScratchDirectory0, ScratchDirectory1),
	% make sure that the scratch directory path ends with a slash
	(	sub_atom(ScratchDirectory1, _, _, 0, '/') ->
		ScratchDirectory = ScratchDirectory1
	;	atom_concat(ScratchDirectory1, '/', ScratchDirectory)
	),
	(	sub_atom(ScratchDirectory, 0, 2, _, './') ->
		% relative directory path
		sub_atom(ScratchDirectory, 2, _, 0, ScratchDirectorySuffix),
		atom_concat(SourceDirectory, ScratchDirectorySuffix, ObjectDirectory)
	;	% assume absolute directory path
		ObjectDirectory = ScratchDirectory
	),
	% add a suffix based on the original extension to the file name to avoid
	% intermediate and temporary file name conflicts when compiling two or
	% more source files that share the same name but use different extensions
	(	'$lgt_source_extension_suffix'(SourceExtension, Suffix) ->
		true
	;	sub_atom(SourceExtension, 1, _, 0, Suffix0) ->
		atom_concat('_', Suffix0, Suffix)
	),
	% append (if supported by the backend compiler) a directory hash value to the
	% intermediate Prolog file name to try to avoid file name collisions when
	% collecting all the intermediate files in the same directory (possibly for
	% embedding); when compiling with the "clean" flag turned on (its default
	% value), also include in the file name the process identifier to avoid file
	% name clashes when running parallel Logtalk processes
	'$lgt_directory_hashes'(SourceDirectory, HashDialect, HashPid),
	'$lgt_object_file_name'(ObjectDirectory, SourceName, HashDialect, Suffix, ObjectFileDialect),
	'$lgt_object_file_name'(ObjectDirectory, SourceName, HashPid, Suffix, ObjectFilePid).


'$lgt_object_file_name'(ObjectDirectory, SourceName, Hash, Suffix, ObjectFile) :-
	atom_concat(SourceName, Hash, ObjectName0),
	atom_concat(ObjectName0, Suffix, ObjectName),
	% there must be a single object file extension defined in the Prolog adapter files
	'$lgt_file_extension'(object, ObjectExtension),
	atom_concat(ObjectName, ObjectExtension, ObjectBasename),
	atom_concat(ObjectDirectory, ObjectBasename, ObjectFile),
	% make sure the scratch directory exists
	'$lgt_make_directory'(ObjectDirectory).


% common source extensions and corresponding precomputed suffixes
'$lgt_source_extension_suffix'('.lgt', '_lgt').
'$lgt_source_extension_suffix'('.logtalk', '_logtalk').
'$lgt_source_extension_suffix'('.pl', '_pl').
'$lgt_source_extension_suffix'('.pro', '_pro').
'$lgt_source_extension_suffix'('.prolog', '_prolog').
'$lgt_source_extension_suffix'('', '').



% '$lgt_compile_file'(+atom, +atom)
%
% compiles a source file storing the resulting code in memory

'$lgt_compile_file'(SourceFile, ObjectFile) :-
	% open the Logtalk source code file for reading
	catch(
		'$lgt_open'(SourceFile, read, Input, [alias(logtalk_compiler_input)]),
		OpenError,
		'$lgt_compiler_stream_error_handler'(OpenError)
	),
	% look for an encoding/1 directive that, when present, must be the first term on a source file
	catch(
		'$lgt_read_file_term'(SourceFile, Input, Term, Singletons, Lines),
		InputError,
		'$lgt_compiler_first_term_error_handler'(SourceFile, Lines, InputError)
	),
	catch(
		'$lgt_check_for_encoding_directive'(Term, SourceFile, Lines, Input, NewInput, [alias(logtalk_compiler_input)], OutputOptions),
		FirstTermError,
		'$lgt_compiler_first_term_error_handler'(SourceFile, Lines, FirstTermError)
	),
	% open a Prolog file for writing the generated code using any found encoding/1 directive
	catch(
		'$lgt_open'(ObjectFile, write, Output, [alias(logtalk_compiler_output)| OutputOptions]),
		OpenError,
		'$lgt_compiler_stream_error_handler'(OpenError)
	),
	catch(
		'$lgt_write_encoding_directive'(Output, SourceFile),
		WriteError,
		'$lgt_compiler_stream_error_handler'(WriteError)
	),
	% generate a begin_of_file term for use by the term-expansion mechanism
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, compile(user,_,_), _, 0-0, _),
	catch(
		'$lgt_compile_file_term'(begin_of_file, Ctx),
		Error,
		'$lgt_compiler_error_handler'(Error)
	),
	% read and compile the remaining terms in the Logtalk source file
	catch(
		'$lgt_compile_file_term'(Term, Singletons, Lines, SourceFile, NewInput),
		Error,
		'$lgt_compiler_error_handler'(Error)
	),
	'$lgt_close'(NewInput),
	% finish writing the generated Prolog file
	catch(
		'$lgt_write_runtime_tables'(Output),
		OutputError,
		'$lgt_compiler_output_stream_error_handler'(Output, OutputError)
	),
	'$lgt_close'(Output),
	'$lgt_restore_global_operator_table',
	'$lgt_check_file_naming'.


'$lgt_write_runtime_tables'(Output) :-
	'$lgt_generate_loaded_file_table_entry'(SourceFile),
	% write out any Prolog code occurring after the last source file entity
	'$lgt_write_prolog_terms'(Output, SourceFile),
	% write entity runtime directives and clauses
	'$lgt_write_runtime_clauses'(Output, SourceFile),
	% write initialization/1 directive at the end of the file to improve
	% compatibility with non-ISO compliant Prolog compilers
	'$lgt_write_initialization_directive'(Output, SourceFile).


'$lgt_generate_loaded_file_table_entry'(SourceFile) :-
	'$lgt_pp_file_paths_flags_'(Basename, Directory, SourceFile, ObjectFile, Flags),
	% the make predicate will reload a file if the compilation mode changed ...
	(	'$lgt_compiler_flag'(debug, on) ->
		Mode = debug
	;	'$lgt_compiler_flag'(optimize, on) ->
		Mode = optimal
	;	Mode = normal
	),
	% ... or if the file modification date changed (e.g., to fix compilation errors)
	'$lgt_file_modification_time'(SourceFile, TimeStamp),
	% compute text properties that are only available after successful file compilation
	(	'$lgt_pp_file_encoding_'(SourceFile, Encoding, _, _) ->
		(	'$lgt_pp_file_bom_'(SourceFile, BOM) ->
			TextProperties = [encoding(Encoding), BOM]
		;	TextProperties = [encoding(Encoding)]
		)
	;	TextProperties = []
	),
	assertz('$lgt_pp_runtime_clause_'('$lgt_loaded_file_'(Basename, Directory, Mode, Flags, TextProperties, ObjectFile, TimeStamp))).



% '$lgt_check_for_encoding_directive'(?term, +atom, +pair(integer), @stream, -stream, +list, -list)
%
% encoding/1 directives must be used during entity compilation and for the
% encoding of the generated Prolog files; a BOM present in the source file
% is inherited by the generated Prolog file

'$lgt_check_for_encoding_directive'(Term, _, _, _, _, _, _) :-
	var(Term),
	throw(error(instantiation_error, term)).

'$lgt_check_for_encoding_directive'((:- Term), _, _, _, _, _, _) :-
	var(Term),
	throw(error(instantiation_error, directive(Term))).

'$lgt_check_for_encoding_directive'((:- encoding(Encoding)), SourceFile, BeginLine-EndLine, Input, NewInput, InputOptions, [encoding(PrologEncoding)|BOM]) :-
	!,
	(	var(Encoding) ->
		throw(error(instantiation_error, directive(encoding(Encoding))))
	;	'$lgt_prolog_feature'(encoding_directive, EncodingDirective),
		% avoid a trivial failure warning with some Prolog backends
		% by checking the flag value in a separate goal
		EncodingDirective == unsupported ->
		throw(error(resource_error(text_encoding_support), directive(encoding(Encoding))))
	;	% the conversion between Logtalk and Prolog encodings is defined in the adapter files
		(	'$lgt_decompose_file_name'(SourceFile, _, _, Extension),
			'$lgt_file_extension'(prolog, Extension),
			'$lgt_logtalk_prolog_encoding'(LogtalkEncoding, Encoding, Input) ->
			% converted Prolog specific encoding to Logtalk encoding;
			% possibly compiling a module as an object
			PrologEncoding = Encoding
		;	LogtalkEncoding = Encoding,
			'$lgt_logtalk_prolog_encoding'(LogtalkEncoding, PrologEncoding, Input)
		) ->
		assertz('$lgt_pp_file_encoding_'(SourceFile, LogtalkEncoding, PrologEncoding, BeginLine)),
		% check that the encoding/1 directive is found in the first line
		(	BeginLine =:= 1 ->
			true
		;	'$lgt_compiler_flag'(encodings, silent) ->
			true
		;	'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(warning(encodings), misplaced_encoding_directive(SourceFile, BeginLine-EndLine))
		),
		% close and reopen the source file using the specified encoding
		'$lgt_close'(Input),
		'$lgt_open'(SourceFile, read, NewInput, [encoding(PrologEncoding)| InputOptions]),
		(	(	catch(stream_property(NewInput, bom(Boolean)), _, fail)
				% SWI-Prolog and YAP
			;	catch(stream_property(NewInput, encoding_signature(Boolean)), _, fail)
				% SICStus Prolog
			) ->
			BOM = [bom(Boolean)],
			assertz('$lgt_pp_file_bom_'(SourceFile, bom(Boolean)))
		;	BOM = []
		),
		% throw away the already processed encoding/1 directive
		'$lgt_read_file_term'(SourceFile, NewInput, _, _, _)
	;	% encoding not recognized
		atom(Encoding) ->
		throw(error(domain_error(text_encoding, Encoding), directive(encoding(Encoding))))
	;	throw(error(type_error(atom, Encoding), directive(encoding(Encoding))))
	).

% assume no encoding/1 directive present on the source file
'$lgt_check_for_encoding_directive'(_, _, _, Input, Input, _, []).



% as per coding guidelines, the basename of a file that defines a single
% entity should by the name of the entity or, in the case of parametric
% entities, the name of the entity concatenated with the number of the
% parameters, possible separated by an underscore

'$lgt_check_file_naming' :-
	(	'$lgt_compiler_flag'(naming, warning),
		findall(
			Entity,
			(	'$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Entity, _, _, _, _))
			;	'$lgt_pp_runtime_clause_'('$lgt_current_category_'(Entity, _, _, _, _, _))
			;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, _))
			),
			[Single]
		) ->
		'$lgt_pp_file_paths_flags_'(Basename, _, SourceFile, _, _),
		(	functor(Single, Name, Arity),
			(	Expected = Name
			;	number_codes(Arity, ArityCodes),
				(	atom_codes(ArityAtom, [95| ArityCodes])
				;	atom_codes(ArityAtom, ArityCodes)
				),
				atom_concat(Name, ArityAtom, Expected)
			),
			'$lgt_file_extension'(logtalk, Extension),
			atom_concat(Expected, Extension, Basename) ->
			true
		;	'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(warning(naming), file_and_entity_names_differ(SourceFile, Single))
		)
	;	true
	).



% '$lgt_compile_file_term'(@term, +list, +pair(integer), +atom, @stream)

'$lgt_compile_file_term'((-), _, _, _, _) :-
	% catch variables
	throw(error(instantiation_error, term)).

'$lgt_compile_file_term'(end_of_file, _, Lines, _, _) :-
	!,
	% set the initial compilation context and the position for compiling the end_of_file term
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, compile(user,_,_), _, Lines, _),
	% allow for term-expansion of the end_of_file term
	'$lgt_compile_file_term'(end_of_file, Ctx).

'$lgt_compile_file_term'(Term, _, _, File, Input) :-
	'$lgt_pp_cc_skipping_',
	% we're performing conditional compilation and skipping terms ...
	\+ '$lgt_is_conditional_compilation_directive'(Term),
	% ... except for conditional compilation directives
	!,
	'$lgt_read_file_term'(File, Input, Next, NextSingletons, NextLines),
	'$lgt_compile_file_term'(Next, NextSingletons, NextLines, File, Input).

'$lgt_compile_file_term'(Term, Singletons, Lines, File, Input) :-
	'$lgt_report_singleton_variables'(Singletons, Term, File, Lines),
	% set the initial compilation context and the position for compiling the term
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, compile(user,_,_), _, Lines, _),
	'$lgt_compile_file_term'(Term, Ctx),
	'$lgt_read_file_term'(File, Input, Next, NextSingletons, NextLines),
	'$lgt_compile_file_term'(Next, NextSingletons, NextLines, File, Input).



% '$lgt_add_referenced_object'(@object_identifier, @compilation_context)
%
% adds referenced object for later checking of references to unknown objects;
% we also save the line numbers for the first reference to the object
%
% the definition is optimized to minimize the number of inferences for
% runtime resolved ::/2 calls

'$lgt_add_referenced_object'(Obj, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	(	Mode == runtime ->
		true
	;	Mode = compile(aux,_,_) ->
		true
	;	% compiling a reference in a source file
		'$lgt_pp_referenced_object_'(Obj, _, _) ->
		% not the first reference to this object
		true
	;	atom(Obj) ->
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_referenced_object_'(Obj, File, Lines))
	;	% parametric object
		'$lgt_term_template'(Obj, Template),
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_referenced_object_'(Template, File, Lines))
	).



% '$lgt_add_referenced_protocol'(@protocol_identifier, @compilation_context)
%
% adds referenced protocol for later checking of references to unknown protocols
% we also save the line numbers for the first reference to the protocol

'$lgt_add_referenced_protocol'(Ptc, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	(	Mode == runtime ->
		true
	;	Mode = compile(aux,_,_) ->
		true
	;	% compiling a reference in a source file
		'$lgt_pp_referenced_protocol_'(Ptc, _, _) ->
		% not the first reference to this protocol
		true
	;	'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_referenced_protocol_'(Ptc, File, Lines))
	).



% '$lgt_add_referenced_category'(@category_identifier, @compilation_context)
%
% adds referenced category for later checking of references to unknown categories
% we also save the line numbers for the first reference to the category

'$lgt_add_referenced_category'(Ctg, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	(	Mode == runtime ->
		true
	;	Mode = compile(aux,_,_) ->
		true
	;	% compiling a reference in a source file
		'$lgt_pp_referenced_category_'(Ctg, _, _) ->
		% not the first reference to this category
		true
	;	atom(Ctg) ->
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_referenced_category_'(Ctg, File, Lines))
	;	% parametric category
		'$lgt_term_template'(Ctg, Template),
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_referenced_category_'(Template, File, Lines))
	).



% '$lgt_add_referenced_module'(@term, @compilation_context)
%
% adds referenced module for later checking of references to unknown modules;
% we also save the line numbers for the first reference to the module

'$lgt_add_referenced_module'(Module, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	(	Mode == runtime ->
		true
	;	Mode = compile(aux,_,_) ->
		true
	;	% compiling a reference in a source file
		var(Module) ->
		% module instantiated only at runtime
		true
	;	'$lgt_pp_referenced_module_'(Module, _, _) ->
		% not the first reference to this module
		true
	;	'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_referenced_module_'(Module, File, Lines))
	).



% '$lgt_add_referenced_object_message'(@compilation_mode, @term, @callable, @callable, @term)
%
% adds referenced object and message for supporting using reflection to
% retrieve cross-reference information

'$lgt_add_referenced_object_message'(runtime, _, _, _, _).

'$lgt_add_referenced_object_message'(compile(aux,_,_), _, _, _, _) :-
	!.

'$lgt_add_referenced_object_message'(compile(user,_,_), Obj, Pred, Alias, Head) :-
	(	var(Head) ->
		% not compiling a clause
		true
	;	% add reference if first but be careful to not instantiate the object argument which may only be known at runtime
		functor(Pred, PredFunctor, PredArity),
		functor(Head, HeadFunctor, HeadArity),
		'$lgt_source_file_context'(File, Lines),
		(	\+ \+ '$lgt_pp_referenced_object_message_'(Obj, PredFunctor/PredArity, _, HeadFunctor/HeadArity, File, Lines) ->
			true
		;	functor(Alias, AliasFunctor, AliasArity),
			(	compound(Obj) ->
				% compile-time parametric object
				'$lgt_term_template'(Obj, Template),
				assertz('$lgt_pp_referenced_object_message_'(Template, PredFunctor/PredArity, AliasFunctor/AliasArity, HeadFunctor/HeadArity, File, Lines))
			;	% runtime instantiated object or non-parametric object
				assertz('$lgt_pp_referenced_object_message_'(Obj, PredFunctor/PredArity, AliasFunctor/AliasArity, HeadFunctor/HeadArity, File, Lines))
			)
		)
	).



% '$lgt_add_referenced_module_predicate'(@compilation_mode, @term, @callable, @callable, @term)
%
% adds referenced module for later checking of references to unknown modules
% we also save the line numbers for the first reference to the module

'$lgt_add_referenced_module_predicate'(runtime, _, _, _, _).

'$lgt_add_referenced_module_predicate'(compile(aux,_,_), _, _, _, _) :-
	!.

'$lgt_add_referenced_module_predicate'(compile(user,_,_), Module, Pred, Alias, Head) :-
	(	var(Head) ->
		% not compiling a clause
		true
	;	% add reference if first but be careful to not instantiate the module argument which may only be known at runtime
		functor(Pred, PredFunctor, PredArity),
		functor(Head, HeadFunctor, HeadArity),
		'$lgt_source_file_context'(File, Lines),
		(	\+ \+ '$lgt_pp_referenced_module_predicate_'(Module, PredFunctor/PredArity, _, HeadFunctor/HeadArity, File, Lines) ->
			true
		;	functor(Alias, AliasFunctor, AliasArity),
			assertz('$lgt_pp_referenced_module_predicate_'(Module, PredFunctor/PredArity, AliasFunctor/AliasArity, HeadFunctor/HeadArity, File, Lines))
		)
	).



% '$lgt_add_entity_source_data'(@atom, @entity_identifier)
%
% adds entity source data when the corresponding flag is turned on

'$lgt_add_entity_source_data'(Kind, Entity) :-
	(	'$lgt_compiler_flag'(source_data, on) ->
		'$lgt_pp_file_paths_flags_'(_, _, MainFile, _, _),
		'$lgt_add_entity_properties'(Kind, Entity, MainFile),
		'$lgt_add_entity_predicate_properties'(Entity, MainFile)
	;	true
	).



% '$lgt_add_entity_properties'(@atom, @entity_identifier, +atom)
%
% adds entity properties related to the entity source file

'$lgt_add_entity_properties'(Kind, Entity, _) :-
	(	Kind == object ->
		'$lgt_pp_referenced_object_'(Entity, _, StartDirective-EndDirective)
	;	Kind == protocol ->
		'$lgt_pp_referenced_protocol_'(Entity, _, StartDirective-EndDirective)
	;	% Kind == category,
		'$lgt_pp_referenced_category_'(Entity, _, StartDirective-EndDirective)
	),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, directive(StartDirective, EndDirective)))),
	'$lgt_pp_file_paths_flags_'(Basename, Directory, _, _, _),
	'$lgt_pp_entity_lines_'(Entity, Start-End),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, file_lines(Basename, Directory, Start, End)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_referenced_object_message_'(Object, PredicateFunctor/PredicateArity, AliasFunctor/AliasArity, Caller, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	functor(Predicate, PredicateFunctor, PredicateArity),
	(	'$lgt_pp_uses_non_terminal_'(Object, _, _, Predicate, _, _, _, _) ->
		PredicateArity2 is PredicateArity - 2,
		NonTerminal = PredicateFunctor//PredicateArity2
	;	NonTerminal = no
	),
	(	PredicateFunctor/PredicateArity == AliasFunctor/AliasArity ->
		assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(Object::PredicateFunctor/PredicateArity, Caller, no, NonTerminal, Location))))
	;	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(Object::PredicateFunctor/PredicateArity, Caller, AliasFunctor/AliasArity, NonTerminal, Location))))
	),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_referenced_module_predicate_'(Module, PredicateFunctor/PredicateArity, AliasFunctor/AliasArity, Caller, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	functor(Predicate, PredicateFunctor, PredicateArity),
	(	'$lgt_pp_use_module_non_terminal_'(Module, _, _, Predicate, _, _, _, _) ->
		PredicateArity2 is PredicateArity - 2,
		NonTerminal = PredicateFunctor//PredicateArity2
	;	NonTerminal = no
	),
	(	PredicateFunctor/PredicateArity == AliasFunctor/AliasArity ->
		assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(':'(Module,PredicateFunctor/PredicateArity), Caller, no, NonTerminal, Location))))
	;	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(':'(Module,PredicateFunctor/PredicateArity), Caller, AliasFunctor/AliasArity, NonTerminal, Location))))
	),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_calls_self_predicate_'(Predicate, Caller, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(::Predicate, Caller, no, no, Location)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_calls_super_predicate_'(Predicate, Caller, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(^^Predicate, Caller, no, no, Location)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_calls_predicate_'(Predicate, _, Caller, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, calls(Predicate, Caller, no, no, Location)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_updates_predicate_'(Dynamic, Updater, File, Lines),
	'$lgt_updates_property_alias_non_terminal'(Dynamic, Alias, NonTerminal),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, updates(Dynamic, Updater, Alias, NonTerminal, Location)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, _) :-
	'$lgt_pp_entity_info_'(Info, _, _),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, info(Info)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, _) :-
	findall(Define, '$lgt_pp_number_of_clauses_rules_'(_, _, Define, _), Defines),
	'$lgt_sum_list'(Defines, TotalDefines),
	findall(
		AuxDefine,
		(	'$lgt_pp_defines_predicate_'(_, Functor/Arity, _, _, _, aux),
			'$lgt_pp_number_of_clauses_rules_'(Functor, Arity, AuxDefine, _)
		),
		AuxDefines
	),
	'$lgt_sum_list'(AuxDefines, TotalAuxDefines),
	findall(Provide, '$lgt_pp_number_of_clauses_rules_'(_, _, _, Provide, _), Provides),
	'$lgt_sum_list'(Provides, TotalProvides),
	Total is TotalDefines + TotalProvides,
	TotalUser is Total - TotalAuxDefines,
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, number_of_clauses(Total, TotalUser)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, _) :-
	findall(Define, '$lgt_pp_number_of_clauses_rules_'(_, _, _, Define), Defines),
	'$lgt_sum_list'(Defines, TotalDefines),
	findall(
		AuxDefine,
		(	'$lgt_pp_defines_predicate_'(_, Functor/Arity, _, _, _, aux),
			'$lgt_pp_number_of_clauses_rules_'(Functor, Arity, _, AuxDefine)
		),
		AuxDefines
	),
	'$lgt_sum_list'(AuxDefines, TotalAuxDefines),
	findall(Provide, '$lgt_pp_number_of_clauses_rules_'(_, _, _, _, Provide), Provides),
	'$lgt_sum_list'(Provides, TotalProvides),
	Total is TotalDefines + TotalProvides,
	TotalUser is Total - TotalAuxDefines,
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, number_of_rules(Total, TotalUser)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_object_alias_'(Original, Alias, _, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, object_alias(Original, Alias, Location)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_module_alias_'(Original, Alias, _, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, module_alias(Original, Alias, Location)))),
	fail.

'$lgt_add_entity_properties'(_, Entity, MainFile) :-
	'$lgt_pp_predicate_alias_'(For, Original, Alias, NonTerminalFlag, File, Lines),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	functor(Original, OriginalFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, predicate_alias(For, OriginalFunctor/Arity, AliasFunctor/Arity, NonTerminalFlag, Location)))),
	fail.

'$lgt_add_entity_properties'(_, _, _).


% auxiliary predicate to compute the updates/2 object/category property
% alias and non-terminal indicators for the update predicate

'$lgt_updates_property_alias_non_terminal'(Object::PredicateFunctor/PredicateArity, AliasIndicator, NonTerminalIndicator) :-
	!,
	functor(Predicate, PredicateFunctor, PredicateArity),
	(	'$lgt_pp_uses_predicate_'(Object, Predicate, Alias, _, _, _) ->
		functor(Alias, AliasFunctor, AliasArity),
		AliasIndicator = AliasFunctor/AliasArity
	;	AliasIndicator = no
	),
	(	'$lgt_pp_uses_non_terminal_'(Object, _, _, Predicate, _, _, _, _) ->
		NonTerminalArity is PredicateArity - 2,
		NonTerminalIndicator = PredicateFunctor//NonTerminalArity
	;	NonTerminalIndicator = no
	).

'$lgt_updates_property_alias_non_terminal'(':'(Module,PredicateFunctor/PredicateArity), AliasIndicator, NonTerminalIndicator) :-
	!,
	functor(Predicate, PredicateFunctor, PredicateArity),
	(	'$lgt_pp_use_module_predicate_'(Module, Predicate, Alias, _, _, _) ->
		functor(Alias, AliasFunctor, AliasArity),
		AliasIndicator = AliasFunctor/AliasArity
	;	AliasIndicator = no
	),
	(	'$lgt_pp_use_module_non_terminal_'(Module, _, _, Predicate, _, _, _, _) ->
		NonTerminalArity is PredicateArity - 2,
		NonTerminalIndicator = PredicateFunctor//NonTerminalArity
	;	NonTerminalIndicator = no
	).

'$lgt_updates_property_alias_non_terminal'(_, no, no).



% '$lgt_add_entity_predicate_properties'(@entity_identifier, +atom)
%
% saves all entity predicate properties (at the end of entity compilation)
% for use with the reflection built-in predicates and methods

'$lgt_add_entity_predicate_properties'(Entity, MainFile) :-
	'$lgt_pp_predicate_definition_location_'(Other, Functor, Arity, File, Lines),
	% multifile predicate clauses defined in Entity for Other
	'$lgt_property_location'(MainFile, File, Lines, Location),
	'$lgt_pp_number_of_clauses_rules_'(Other, Functor, Arity, Clauses, Rules),
	assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/Arity, clauses_rules_location_to(Clauses,Rules,Location,Other)))),
	fail.

'$lgt_add_entity_predicate_properties'(Entity, MainFile) :-
	'$lgt_pp_predicate_declaration_location_'(Functor, Arity, File, Lines),
	% local predicate clauses
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/Arity, declaration_location(Location)))),
	\+ '$lgt_pp_defines_predicate_'(_, Functor/Arity, _, _, _, _),
	assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/Arity, flags_clauses_rules_location(0, 0, 0, 0-0)))),
	fail.

'$lgt_add_entity_predicate_properties'(Entity, MainFile) :-
	'$lgt_pp_defines_predicate_'(_, Functor/Arity, _, _, _, Origin),
	(	'$lgt_pp_predicate_recursive_calls_'(Functor, Arity, _) ->
		Flags0 is 8
	;	Flags0 is 0
	),
	(	'$lgt_pp_inline_predicate_'(Functor/Arity) ->
		Flags1 is Flags0 + 4
	;	Flags1 is Flags0
	),
	(	'$lgt_pp_defines_non_terminal_'(Functor, _, Arity) ->
		Flags2 is Flags1 + 2
	;	Flags2 is Flags1
	),
	(	Origin == aux ->
		Flags is Flags2 + 1,
		File = MainFile,
		Lines = 0-0
	;	Flags is Flags2,
		'$lgt_pp_predicate_definition_location_'(Functor, Arity, File, Lines)
	),
	'$lgt_pp_number_of_clauses_rules_'(Functor, Arity, Clauses, Rules),
	'$lgt_property_location'(MainFile, File, Lines, Location),
	assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/Arity, flags_clauses_rules_location(Flags, Clauses, Rules, Location)))),
	fail.

'$lgt_add_entity_predicate_properties'(Entity, _) :-
	'$lgt_pp_mode_'(Mode, Solutions, _, _),
		functor(Mode, Functor, Arity),
		(	'$lgt_pp_non_terminal_'(Functor, Arity, ExtArity) ->
			assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/ExtArity, mode(Mode, Solutions))))
		;	assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/Arity, mode(Mode, Solutions))))
		),
	fail.

'$lgt_add_entity_predicate_properties'(Entity, _) :-
	'$lgt_pp_predicate_info_'(Predicate, Info, _, _),
		assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Predicate, info(Info)))),
	fail.

'$lgt_add_entity_predicate_properties'(_, _).


% the property location is the lines pair when found on the main file
% or a include(File,Lines) term when found in an included file

'$lgt_property_location'(MainFile, MainFile, Lines, Lines) :-
	!.

'$lgt_property_location'(_, File, Lines, include(File, Lines)).



% '$lgt_report_singleton_variables'(+compilation_mode, @list, @term, +atom, @pair(integer))
%
% reports the singleton variables found while compiling an entity term in the given mode

'$lgt_report_singleton_variables'(runtime, _, _, _, _).

'$lgt_report_singleton_variables'(compile(_,_,_), Singletons, Term, File, Lines) :-
	'$lgt_report_singleton_variables'(Singletons, Term, File, Lines).



% '$lgt_report_singleton_variables'(@list, @term, +atom, @pair(integer))
%
% reports the singleton variables found while compiling an entity term

'$lgt_report_singleton_variables'([], _, _, _).

'$lgt_report_singleton_variables'([Singleton| Singletons], Term, File, Lines) :-
	(	'$lgt_compiler_flag'(singleton_variables, warning),
		'$lgt_filter_singleton_variable_names'([Singleton| Singletons], Term, Names),
		Names \== [] ->
		'$lgt_increment_compiling_warnings_counter',
		(	'$lgt_pp_entity_'(Type, Entity, _) ->
			'$lgt_print_message'(
				warning(singleton_variables),
				singleton_variables(File, Lines, Type, Entity, Names, Term)
			)
		;	'$lgt_print_message'(
				warning(singleton_variables),
				singleton_variables(File, Lines, Names, Term)
			)
		)
	;	true
	).



% '$lgt_filter_singleton_variable_names'(@list, @term, -list(atom))
%
% filters variables whose name start with an underscore from a singletons list if
% the corresponding compiler flag sets their interpretation to don't care variables

'$lgt_filter_singleton_variable_names'(Singletons, Term, Names) :-
	(	'$lgt_compiler_flag'(underscore_variables, dont_care) ->
		'$lgt_filter_dont_care_variables'(Singletons, SingletonsFiltered)
	;	SingletonsFiltered = Singletons
	),
	(	'$lgt_pp_parameter_variables_'(ParameterVariables) ->
		'$lgt_filter_parameter_variables'(SingletonsFiltered, ParameterVariables, Names)
	;	Term = (:- Directive),
		nonvar(Directive),
		'$lgt_logtalk_opening_directive'(Directive) ->
		'$lgt_filter_parameter_variables'(SingletonsFiltered, Names)
	;	'$lgt_singleton_variable_names'(SingletonsFiltered, Names)
	).


'$lgt_singleton_variable_names'([], []).

'$lgt_singleton_variable_names'([Name = _| Singletons], [Name| Names]) :-
	'$lgt_singleton_variable_names'(Singletons, Names).


'$lgt_filter_dont_care_variables'([], []).

'$lgt_filter_dont_care_variables'([Name = Variable| VariableNames], FilteredVariableNames) :-
	(	'$lgt_parameter_variable_name'(Name) ->
		FilteredVariableNames = [Name = Variable| Rest],
		'$lgt_filter_dont_care_variables'(VariableNames, Rest)
	;	sub_atom(Name, 0, 1, _, '_') ->
		'$lgt_filter_dont_care_variables'(VariableNames, FilteredVariableNames)
	;	FilteredVariableNames = [Name = Variable| Rest],
		'$lgt_filter_dont_care_variables'(VariableNames, Rest)
	).


'$lgt_filter_parameter_variables'([], _, []).

'$lgt_filter_parameter_variables'([Name = _| VariableNames], ParameterVariables, Names) :-
	(	'$lgt_member'(Name-_, ParameterVariables) ->
		'$lgt_filter_parameter_variables'(VariableNames, ParameterVariables, Names)
	;	Names = [Name| Rest],
		'$lgt_filter_parameter_variables'(VariableNames, ParameterVariables, Rest)
	).


'$lgt_filter_parameter_variables'([], []).

'$lgt_filter_parameter_variables'([Name = _| VariableNames], Names) :-
	(	'$lgt_parameter_variable_name'(Name) ->
		'$lgt_filter_parameter_variables'(VariableNames, Names)
	;	Names = [Name| Rest],
		'$lgt_filter_parameter_variables'(VariableNames, Rest)
	).



% '$lgt_compiler_error_handler'(+atom, +atom, +pair(integer), @compound)
%
% closes the streams being used for reading and writing terms, restores
% the operator table, reports the compilation error found, and, finally,
% fails in order to abort the compilation process

'$lgt_compiler_error_handler'(SourceFile, ObjectFile, Lines, Error) :-
	stream_property(Input, alias(logtalk_compiler_input)),
	stream_property(Output, alias(logtalk_compiler_output)), !,
	'$lgt_print_message'(error, compiler_error(SourceFile, Lines, Error)),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	catch('$lgt_close'(Input), _, true),
	(	nonvar(Output) ->
		catch('$lgt_close'(Output), _, true),
		% try to delete the intermediate Prolog files in order to prevent
		% problems by mistaken the broken files by good ones
		'$lgt_delete_intermediate_files'(ObjectFile)
	;	true
	),
	!,
	fail.



% '$lgt_compiler_error_handler'(@compound)

'$lgt_compiler_error_handler'(Error) :-
	'$lgt_pp_file_paths_flags_'(_, _, MainSourceFile, ObjectFile, _),
	(	'$lgt_source_file_context'(SourceFile, Lines) ->
		true
	;	% no file context information available for last term read; likely
		% due to a syntax error when trying to read a main file term as syntax
		% errors in included files are handled when reading a file to terms
		SourceFile = MainSourceFile,
		(	stream_property(Input, alias(logtalk_compiler_input)),
			'$lgt_stream_current_line_number'(Input, Line) ->
			Lines = Line-Line
		;	% some backend Prolog compilers do not support, or do not always support
			% (e.g., when a syntax error occurs) querying a stream line number
			Lines = '-'(-1, -1)
		)
	),
	'$lgt_compiler_error_handler'(SourceFile, ObjectFile, Lines, Error).



% '$lgt_compiler_first_term_error_handler'(+atom, +pair(integer), @compound)
%
% closes the stream being used for reading, restores the operator table,
% reports the compilation error found, and, finally, fails in order to
% abort the compilation process

'$lgt_compiler_first_term_error_handler'(SourceFile, Lines, Error) :-
	(	nonvar(Lines) ->
		true
	;	% no line information available likely due to a syntax error
		stream_property(Input, alias(logtalk_compiler_input)),
		'$lgt_stream_current_line_number'(Input, Line) ->
		Lines = Line-Line
	;	% some backend Prolog compilers do not support, or do not always support
		% (e.g., when a syntax error occurs) querying a stream line number
		Lines = '-'(-1, -1)
	),
	'$lgt_print_message'(error, compiler_error(SourceFile, Lines, Error)),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	stream_property(Input, alias(logtalk_compiler_input)),
	catch('$lgt_close'(Input), _, true),
	!,
	fail.



% '$lgt_compiler_output_stream_error_handler'(@stream, @compound)
%
% closes the stream being used for writing compiled terms, restores
% the operator table, reports the compilation error found, and, finally,
% fails in order to abort the compilation process

'$lgt_compiler_output_stream_error_handler'(Stream, Error) :-
	'$lgt_print_message'(error, compiler_stream_error(Error)),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	catch('$lgt_close'(Stream), _, true),
	!,
	fail.



% '$lgt_compiler_stream_error_handler'(@compound)
%
% closes input and output streams if open, restores the operator table,
% reports the compilation error found, and, finally, fails in order to
% abort the compilation process

'$lgt_compiler_stream_error_handler'(Error) :-
	(	stream_property(Input, alias(logtalk_compiler_input)) ->
		catch('$lgt_close'(Input), _, true)
	;	true
	),
	(	stream_property(Output, alias(logtalk_compiler_output)) ->
		catch('$lgt_close'(Output), _, true)
	;	true
	),
	'$lgt_print_message'(error, compiler_stream_error(Error)),
	'$lgt_restore_global_operator_table',
	'$lgt_clean_pp_file_clauses',
	'$lgt_clean_pp_entity_clauses',
	'$lgt_reset_warnings_counter',
	!,
	fail.



% '$lgt_read_file_term'(+atom, @stream, -term, @list(var), -pair(integer))
%
% remember term position and variable names in order to support the
% logtalk_load_context/2 predicate and more informative compiler warning
% and error messages

'$lgt_read_file_term'(File, Stream, Term, Singletons, Lines) :-
	% we retract first the position and variable names for the previous
	% read term as we may get a syntax error while reading the next term;
	% this will allow us to use the stream position if necessary to find
	% the approximated position of the error
	retractall('$lgt_pp_term_source_data_'(_, _, _, _, _)),
	% the actual read term predicate is defined in the adapter files as
	% there's no standard option for returning the read term position
	'$lgt_read_term'(Stream, Term, [variable_names(VariableNames), singletons(Singletons)], Lines),
	'$lgt_report_variable_naming_issues'(VariableNames, File, Lines),
	assertz('$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines)).



% '$lgt_second_stage'(+atom, @entity_identifier, +compilation_context)
%
% compiler second stage
%
% the first stage collected data on all directives, clause heads, and
% grammar rules heads thus enabling the compilation of initialization/1
% goals and clause body goals

'$lgt_second_stage'(Type, Entity, Ctx) :-
	catch(
		'$lgt_compile_entity'(Type, Entity, Ctx),
		Error,
		'$lgt_second_stage_error_handler'(Error)
	).


'$lgt_compile_entity'(Type, Entity, Ctx) :-
	'$lgt_generate_entity_code'(Type, Ctx),
	'$lgt_inline_calls'(Type),
	'$lgt_report_lint_issues'(Type, Entity),
	'$lgt_write_entity_code'(Type, Entity),
	'$lgt_add_entity_source_data'(Type, Entity),
	'$lgt_save_entity_runtime_clause'(Type),
	'$lgt_restore_file_operator_table',
	'$lgt_clean_pp_entity_clauses'(Type).


'$lgt_second_stage_error_handler'(Error) :-
	'$lgt_pp_file_paths_flags_'(_, _, _, ObjectFile, _),
	% get the source file from the context as we may be reporting
	% an error in an included file instead of in the main file
	(	'$lgt_source_file_context'(SourceFile, Lines) ->
		true
	;	% assume auxiliary clause and thus not part of the source file
		'$lgt_pp_file_paths_flags_'(_, _, SourceFile, _, _),
		Lines = '-'(0, 0)
	),
	'$lgt_compiler_error_handler'(SourceFile, ObjectFile, Lines, Error).



% '$lgt_compile_entity_flags'(+atom, -integer)
%
% defines the entity flags value when compiling or dynamically creating a new entity
%
% we use integers in decimal notation instead of binary notation to avoid standards
% compliance issues with some Prolog compilers

'$lgt_compile_entity_flags'(protocol, Flags) :-
	(	'$lgt_compiler_flag'(debug, on) ->
		Debug = 512
	;	Debug = 0
	),
	(	'$lgt_compiler_flag'(source_data, on) ->
		SourceData = 8
	;	SourceData = 0
	),
	(	'$lgt_pp_dynamic_' ->
		Dynamic = 2
	;	Dynamic = 0
	),
	(	'$lgt_pp_built_in_' ->
		BuiltIn = 1
	;	BuiltIn = 0
	),
	Flags is Debug + SourceData + Dynamic + BuiltIn.

'$lgt_compile_entity_flags'(category, Flags) :-
	(	'$lgt_compiler_flag'(debug, on) ->
		Debug = 512
	;	Debug = 0
	),
	(	'$lgt_compiler_flag'(events, allow) ->
		Events = 16
	;	Events = 0
	),
	(	'$lgt_compiler_flag'(source_data, on) ->
		SourceData = 8
	;	SourceData = 0
	),
	(	'$lgt_pp_dynamic_' ->
		Dynamic = 2
	;	Dynamic = 0
	),
	(	'$lgt_pp_built_in_' ->
		BuiltIn = 1
	;	BuiltIn = 0
	),
	Flags is Debug + Events + SourceData + Dynamic + BuiltIn.

'$lgt_compile_entity_flags'(object, Flags) :-
	(	'$lgt_pp_module_'(_) ->
		Module = 1024
	;	Module = 0
	),
	(	'$lgt_compiler_flag'(debug, on) ->
		Debug = 512
	;	Debug = 0
	),
	(	'$lgt_compiler_flag'(context_switching_calls, allow) ->
		ContextSwitchingCalls = 256
	;	ContextSwitchingCalls = 0
	),
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		DynamicDeclarations = 128
	;	DynamicDeclarations = 0
	),
	'$lgt_compiler_flag'(complements, ComplementsFlag),
	(	ComplementsFlag == deny ->
		Complements = 0
	;	ComplementsFlag == allow ->
		Complements = 64
	;	% ComplementsFlag == restrict,
		Complements = 32
	),
	(	'$lgt_compiler_flag'(events, allow) ->
		Events = 16
	;	Events = 0
	),
	(	'$lgt_compiler_flag'(source_data, on) ->
		SourceData = 8
	;	SourceData = 0
	),
	(	'$lgt_pp_threaded_' ->
		Threaded = 4
	;	Threaded = 0
	),
	(	'$lgt_pp_dynamic_' ->
		Dynamic = 2
	;	Dynamic = 0
	),
	(	'$lgt_pp_built_in_' ->
		BuiltIn = 1
	;	BuiltIn = 0
	),
	Flags is Module + Debug + ContextSwitchingCalls + DynamicDeclarations + Complements + Events + SourceData + Threaded + Dynamic + BuiltIn.



% saves the entity runtime clause after computing the final value of its flags

'$lgt_save_entity_runtime_clause'(object) :-
	'$lgt_pp_object_'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, _),
	'$lgt_compile_entity_flags'(object, Flags),
	assertz('$lgt_pp_runtime_clause_'('$lgt_current_object_'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, Flags))).

'$lgt_save_entity_runtime_clause'(protocol) :-
	'$lgt_pp_protocol_'(Ptc, Prefix, Dcl, Rnm, _),
	'$lgt_compile_entity_flags'(protocol, Flags),
	assertz('$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Ptc, Prefix, Dcl, Rnm, Flags))).

'$lgt_save_entity_runtime_clause'(category) :-
	'$lgt_pp_category_'(Ctg, Prefix, Dcl, Def, Rnm, _),
	'$lgt_compile_entity_flags'(category, Flags),
	assertz('$lgt_pp_runtime_clause_'('$lgt_current_category_'(Ctg, Prefix, Dcl, Def, Rnm, Flags))).



% cleans up all dynamic predicates used during source file compilation

'$lgt_clean_pp_file_clauses' :-
	retractall('$lgt_pp_file_initialization_'(_, _)),
	retractall('$lgt_pp_file_entity_initialization_'(_, _, _)),
	retractall('$lgt_pp_file_encoding_'(_, _, _, _)),
	retractall('$lgt_pp_file_bom_'(_, _)),
	retractall('$lgt_pp_file_compiler_flag_'(_, _)),
	retractall('$lgt_pp_term_source_data_'(_, _, _, _, _)),
	% a Logtalk source file may contain only plain Prolog terms
	% instead of plain Prolog terms intermixed between entities
	% definitions; there might also be plain Prolog terms after
	% the last entity definition
	retractall('$lgt_pp_prolog_term_'(_, _)),
	% retract all file-specific flag values
	retractall('$lgt_pp_file_compiler_flag_'(_, _)),
	% retract all file-specific term and goal expansion hooks
	retractall('$lgt_pp_hook_term_expansion_'(_, _)),
	retractall('$lgt_pp_hook_goal_expansion_'(_, _)),
	'$lgt_clean_pp_cc_clauses',
	'$lgt_clean_pp_runtime_clauses'.



% cleans up all dynamic predicates used for conditional compilation

'$lgt_clean_pp_cc_clauses' :-
	retractall('$lgt_pp_cc_if_found_'(_)),
	retractall('$lgt_pp_cc_skipping_'),
	retractall('$lgt_pp_cc_mode_'(_)).



% cleans up the dynamic predicate used for entity runtime clauses

'$lgt_clean_pp_runtime_clauses' :-
	retractall('$lgt_pp_runtime_clause_'(_)).



% cleans up all dynamic predicates used during entity compilation

'$lgt_clean_pp_entity_clauses' :-
	'$lgt_clean_pp_object_clauses',
	'$lgt_clean_pp_protocol_clauses',
	'$lgt_clean_pp_category_clauses'.

'$lgt_clean_pp_entity_clauses'(object) :-
	'$lgt_clean_pp_object_clauses'.

'$lgt_clean_pp_entity_clauses'(protocol) :-
	'$lgt_clean_pp_protocol_clauses'.

'$lgt_clean_pp_entity_clauses'(category) :-
	'$lgt_clean_pp_category_clauses'.

'$lgt_clean_pp_object_clauses' :-
	retractall('$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_module_'(_)),
	retractall('$lgt_pp_object_initialization_'(_, _, _)),
	retractall('$lgt_pp_final_object_initialization_'(_, _)),
	retractall('$lgt_pp_imported_category_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_threaded_'),
	'$lgt_clean_pp_common_object_category_clauses',
	'$lgt_clean_pp_common_entity_clauses'.

'$lgt_clean_pp_protocol_clauses' :-
	retractall('$lgt_pp_protocol_'(_, _, _, _, _)),
	retractall('$lgt_pp_extended_protocol_'(_, _, _, _, _)),
	'$lgt_clean_pp_common_entity_clauses'.

'$lgt_clean_pp_category_clauses' :-
	retractall('$lgt_pp_category_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_complemented_object_'(_, _, _, _, _)),
	retractall('$lgt_pp_extended_category_'(_, _, _, _, _, _)),
	'$lgt_clean_pp_common_object_category_clauses',
	'$lgt_clean_pp_common_entity_clauses'.

'$lgt_clean_pp_common_object_category_clauses' :-
	retractall('$lgt_pp_implemented_protocol_'(_, _, _, _, _)),
	retractall('$lgt_pp_parameter_variables_'(_)),
	retractall('$lgt_pp_object_alias_'(_, _, _, _, _)),
	retractall('$lgt_pp_module_alias_'(_, _, _, _, _)),
	retractall('$lgt_pp_uses_predicate_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_uses_non_terminal_'(_, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_use_module_predicate_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_use_module_non_terminal_'(_, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_def_'(_)),
	retractall('$lgt_pp_ddef_'(_)),
	retractall('$lgt_pp_super_'(_)),
	retractall('$lgt_pp_number_of_clauses_rules_'(_, _, _, _)),
	retractall('$lgt_pp_number_of_clauses_rules_'(_, _, _, _, _)),
	retractall('$lgt_pp_predicate_definition_location_'(_, _, _, _)),
	retractall('$lgt_pp_predicate_definition_location_'(_, _, _, _, _)),
	retractall('$lgt_pp_redefined_built_in_'(_, _, _)),
	retractall('$lgt_pp_defines_predicate_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_inline_predicate_'(_)),
	retractall('$lgt_pp_non_tail_recursive_predicate_'(_, _, _, _)),
	retractall('$lgt_pp_predicate_recursive_calls_'(_, _, _)),
	retractall('$lgt_pp_calls_predicate_'(_, _, _, _, _)),
	retractall('$lgt_pp_calls_self_predicate_'(_, _, _, _)),
	retractall('$lgt_pp_calls_super_predicate_'(_, _, _, _)),
	retractall('$lgt_pp_updates_predicate_'(_, _, _, _)),
	retractall('$lgt_pp_non_portable_predicate_'(_, _, _)),
	retractall('$lgt_pp_non_portable_function_'(_, _, _)),
	retractall('$lgt_pp_missing_function_'(_, _, _)),
	retractall('$lgt_pp_missing_meta_predicate_directive_'(_, _, _)),
	retractall('$lgt_pp_missing_dynamic_directive_'(_, _, _)),
	retractall('$lgt_pp_missing_discontiguous_directive_'(_, _, _)),
	retractall('$lgt_pp_missing_multifile_directive_'(_, _, _)),
	retractall('$lgt_pp_missing_use_module_directive_'(_, _)),
	retractall('$lgt_pp_previous_predicate_'(_, _)),
	retractall('$lgt_pp_defines_non_terminal_'(_, _, _)),
	retractall('$lgt_pp_calls_non_terminal_'(_, _, _, _)),
	retractall('$lgt_pp_referenced_object_'(_, _, _)),
	retractall('$lgt_pp_referenced_category_'(_, _, _)),
	retractall('$lgt_pp_referenced_module_'(_, _, _)),
	retractall('$lgt_pp_referenced_object_message_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_referenced_module_predicate_'(_, _, _, _, _, _)).

'$lgt_clean_pp_common_entity_clauses' :-
	retractall('$lgt_pp_entity_lines_'(_, _)),
	retractall('$lgt_pp_entity_compiler_flag_'(_, _)),
	retractall('$lgt_pp_entity_'(_, _, _)),
	retractall('$lgt_pp_entity_info_'(_, _, _)),
	retractall('$lgt_pp_predicate_info_'(_, _, _, _)),
	retractall('$lgt_pp_directive_'(_)),
	retractall('$lgt_pp_synchronized_'(_, _, _, _)),
	retractall('$lgt_pp_predicate_mutex_counter_'(_)),
	retractall('$lgt_pp_public_'(_, _, _, _)),
	retractall('$lgt_pp_protected_'(_, _, _, _)),
	retractall('$lgt_pp_private_'(_, _, _, _)),
	retractall('$lgt_pp_dynamic_'(_, _, _, _)),
	retractall('$lgt_pp_discontiguous_'(_, _, _)),
	retractall('$lgt_pp_multifile_'(_, _, _, _)),
	retractall('$lgt_pp_coinductive_'(_, _, _, _, _, _, _, _, _)),
	retractall('$lgt_pp_coinductive_head_'(_, _, _)),
	retractall('$lgt_pp_mode_'(_, _, _, _)),
	retractall('$lgt_pp_meta_predicate_'(_, _, _, _)),
	retractall('$lgt_pp_predicate_alias_'(_, _, _, _, _, _)),
	retractall('$lgt_pp_non_terminal_'(_, _, _)),
	retractall('$lgt_pp_entity_meta_directive_'(_, _, _)),
	retractall('$lgt_pp_dcl_'(_)),
	% clean any plain Prolog terms appearing before an entity definition
	retractall('$lgt_pp_prolog_term_'(_, _)),
	retractall('$lgt_pp_entity_term_'(_, _, _)),
	retractall('$lgt_pp_final_entity_term_'(_, _)),
	retractall('$lgt_pp_entity_aux_clause_'(_)),
	retractall('$lgt_pp_final_entity_aux_clause_'(_)),
	retractall('$lgt_pp_predicate_declaration_location_'(_, _, _, _)),
	retractall('$lgt_pp_referenced_protocol_'(_, _, _)),
	retractall('$lgt_pp_built_in_'),
	retractall('$lgt_pp_dynamic_'),
	retractall('$lgt_pp_aux_predicate_counter_'(_)).



% '$lgt_clean_lookup_caches'
%
% cleans all entries for all dynamic binding lookup caches
%
% this also have the side-effect of removing the catchall clauses
% that generate the cache entries which we must then re-assert

'$lgt_clean_lookup_caches' :-
	retractall('$lgt_send_to_obj_'(_, _, _)),
	retractall('$lgt_send_to_obj_ne_'(_, _, _)),
	retractall('$lgt_send_to_self_'(_, _, _)),
	retractall('$lgt_obj_super_call_'(_, _, _)),
	retractall('$lgt_ctg_super_call_'(_, _, _)),
	retractall('$lgt_db_lookup_cache_'(_, _, _, _, _)),
	'$lgt_reassert_lookup_cache_catchall_clauses'.



% '$lgt_clean_lookup_caches'(@callable)
%
% cleans all entries for a given predicate for all dynamic
% binding lookup caches
%
% this also have the side-effect of removing the catchall clauses
% that generate the cache entries which we must then re-assert

'$lgt_clean_lookup_caches'(Pred) :-
	retractall('$lgt_send_to_obj_'(_, Pred, _)),
	retractall('$lgt_send_to_obj_ne_'(_, Pred, _)),
	retractall('$lgt_send_to_self_'(_, Pred, _)),
	retractall('$lgt_obj_super_call_'(_, Pred, _)),
	retractall('$lgt_ctg_super_call_'(_, Pred, _)),
	retractall('$lgt_db_lookup_cache_'(_, Pred, _, _, _)),
	'$lgt_reassert_lookup_cache_catchall_clauses'.



% '$lgt_reassert_lookup_cache_catchall_clauses'
%
% reasserts the catchall clauses for the dynamic binding
% lookup cache predicates that generate the cache entries

'$lgt_reassert_lookup_cache_catchall_clauses' :-
	assertz(('$lgt_send_to_obj_'(Obj, Pred, ExCtx) :- '$lgt_send_to_obj_nv'(Obj, Pred, ExCtx))),
	assertz(('$lgt_send_to_obj_ne_'(Obj, Pred, ExCtx) :- '$lgt_send_to_obj_ne_nv'(Obj, Pred, ExCtx))),
	assertz(('$lgt_send_to_self_'(Obj, Pred, ExCtx) :- '$lgt_send_to_self_nv'(Obj, Pred, ExCtx))),
	assertz(('$lgt_obj_super_call_'(Super, Pred, ExCtx) :- '$lgt_obj_super_call_nv'(Super, Pred, ExCtx))),
	assertz(('$lgt_ctg_super_call_'(Ctg, Pred, ExCtx) :- '$lgt_ctg_super_call_nv'(Ctg, Pred, ExCtx))),
	% support runtime resolved database messages to the "user" pseudo-object
	assertz('$lgt_db_lookup_cache_'(user, Clause, _, Clause, true)).



% '$lgt_restore_global_operator_table'
%
% restores the global operator table
%
% called after compiling a source file or after dynamically creating a new entity

'$lgt_restore_global_operator_table' :-
	retract('$lgt_pp_entity_operator_'(_, Specifier, Operator, _, _, _)),
		op(0, Specifier, Operator),
	fail.

'$lgt_restore_global_operator_table' :-
	retract('$lgt_pp_file_operator_'(_, Specifier, Operator)),
		op(0, Specifier, Operator),
	fail.

'$lgt_restore_global_operator_table' :-
	retract('$lgt_pp_global_operator_'(Priority, Specifier, Operator)),
		op(Priority, Specifier, Operator),
	fail.

'$lgt_restore_global_operator_table'.



% '$lgt_restore_file_operator_table'
%
% restores the file operator table
%
% called after compiling a source file entity

'$lgt_restore_file_operator_table' :-
	retract('$lgt_pp_entity_operator_'(_, Specifier, Operator, _, _, _)),
		op(0, Specifier, Operator),
	fail.

'$lgt_restore_file_operator_table' :-
	retract('$lgt_pp_file_operator_'(Priority, Specifier, Operator)),
		op(Priority, Specifier, Operator),
	fail.

'$lgt_restore_file_operator_table'.



% '$lgt_activate_file_operators'(+integer, +operator_specifier, +atom_or_atom_list, +compilation_mode)
%
% activates local file operator definitions
%
% any conflicting global operator is saved so that it can be restored later

'$lgt_activate_file_operators'(_, _, [], _) :-
	!.

'$lgt_activate_file_operators'(Priority, Specifier, [Operator| Operators], Mode) :-
	!,
	'$lgt_activate_file_operator'(Priority, Specifier, Operator, Mode),
	'$lgt_activate_file_operators'(Priority, Specifier, Operators, Mode).

'$lgt_activate_file_operators'(Priority, Specifier, Operator, Mode) :-
	'$lgt_activate_file_operator'(Priority, Specifier, Operator, Mode).


'$lgt_activate_file_operator'(Priority, Specifier, Operator, compile(_,_,_)) :-
	'$lgt_compiler_flag'(redefined_operators, warning),
	(	'$lgt_iso_spec_operator'(Operator, OriginalSpecifier, OriginalPriority)
	;	'$lgt_logtalk_spec_operator'(Operator, OriginalSpecifier, OriginalPriority)
	),
	'$lgt_same_operator_class'(Specifier, OriginalSpecifier),
	once((
		Priority  \== OriginalPriority
	;	Specifier \== OriginalSpecifier
	)),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(redefined_operators),
		redefined_operator(File, Lines, op(OriginalPriority,OriginalSpecifier,Operator), op(Priority,Specifier,Operator))
	),
	fail.

'$lgt_activate_file_operator'(Priority, Specifier, Operator, _) :-
	(	current_op(OriginalPriority, OriginalSpecifier, Operator),
		'$lgt_same_operator_class'(Specifier, OriginalSpecifier) ->
		assertz('$lgt_pp_global_operator_'(OriginalPriority, OriginalSpecifier, Operator))
	;	true
	),
	op(Priority, Specifier, Operator),
	assertz('$lgt_pp_file_operator_'(Priority, Specifier, Operator)).



% '$lgt_activate_entity_operators'(+integer, +operator_specifier, +atom_or_atom_list, +scope, +atom, +pair(integer), +compilation_mode)
%
% activates local entity operator definitions
%
% any conflicting file operator is saved so that it can be restored later

'$lgt_activate_entity_operators'(_, _, [], _, _, _, _) :-
	!.

'$lgt_activate_entity_operators'(Priority, Specifier, [Operator| Operators], Scope, File, Lines, Mode) :-
	!,
	'$lgt_activate_entity_operator'(Priority, Specifier, Operator, Scope, File, Lines, Mode),
	'$lgt_activate_entity_operators'(Priority, Specifier, Operators, Scope, File, Lines, Mode).

'$lgt_activate_entity_operators'(Priority, Specifier, Operator, Scope, File, Lines, Mode) :-
	'$lgt_activate_entity_operator'(Priority, Specifier, Operator, Scope, File, Lines, Mode).


'$lgt_activate_entity_operator'(Priority, Specifier, Operator, _, File, Lines, compile(_,_,_)) :-
	'$lgt_compiler_flag'(redefined_operators, warning),
	(	'$lgt_iso_spec_operator'(Operator, OriginalSpecifier, OriginalPriority)
	;	'$lgt_logtalk_spec_operator'(Operator, OriginalSpecifier, OriginalPriority)
	),
	'$lgt_same_operator_class'(Specifier, OriginalSpecifier),
	once((
		Priority  \== OriginalPriority
	;	Specifier \== OriginalSpecifier
	)),
	'$lgt_pp_entity_'(Type, Entity, _),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(redefined_operators),
		redefined_operator(File, Lines, Type, Entity, op(OriginalPriority,OriginalSpecifier,Operator), op(Priority,Specifier,Operator))
	),
	fail.

'$lgt_activate_entity_operator'(Priority, Specifier, Operator, Scope, File, Lines, _) :-
	(	current_op(OriginalPriority, OriginalSpecifier, Operator),
		'$lgt_same_operator_class'(Specifier, OriginalSpecifier) ->
		assertz('$lgt_pp_file_operator_'(OriginalPriority, OriginalSpecifier, Operator))
	;	true
	),
	op(Priority, Specifier, Operator),
	assertz('$lgt_pp_entity_operator_'(Priority, Specifier, Operator, Scope, File, Lines)),
	'$lgt_pp_entity_'(_, Entity, _),
	% save entity operator property
	(	'$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, op(Priority, Specifier, Operator, p(p(p))))) ->
		% handle the case where there is already a public declaration for the operator
		true
	;	'$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, op(Priority, Specifier, Operator, Scope))) ->
		% duplicated operator declarations may originate from e.g. included files
		% or when compiling modules (as objects) that reexport other modules
		true
	;	assertz('$lgt_pp_runtime_clause_'('$lgt_entity_property_'(Entity, op(Priority, Specifier, Operator, Scope))))
	).



% '$lgt_expand_file_directive_goal'(+callable, -callable)
%
% expands a file directive goal
%
% used to expand file level initialization/1 goals and conditional
% compilation directive goals (if/1 and elif/1) and deal with some
% special cases

'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal) :-
	'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal, []).


'$lgt_expand_file_directive_goal'(Goal, call(Goal), _) :-
	var(Goal),
	!.

'$lgt_expand_file_directive_goal'({Goal}, Goal, _) :-
	!.

'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal, ExpandedGoals) :-
	'$lgt_push_if_new'(ExpandedGoals, Goal, NewExpandedGoals),
	'$lgt_expand_file_goal'(Goal, ExpandedGoal0),
	Goal \== ExpandedGoal0,
	!,
	'$lgt_expand_file_directive_goal'(ExpandedGoal0, ExpandedGoal, NewExpandedGoals).

'$lgt_expand_file_directive_goal'((Goal1, Goal2), (ExpandedGoal1, ExpandedGoal2), ExpandedGoals) :-
	!,
	'$lgt_expand_file_directive_goal'(Goal1, ExpandedGoal1, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Goal2, ExpandedGoal2, ExpandedGoals).

'$lgt_expand_file_directive_goal'((IfThen; Else), (TIf -> TThen; TElse), ExpandedGoals) :-
	nonvar(IfThen),
	IfThen = (If -> Then),
	!,
	'$lgt_expand_file_directive_goal'(If, TIf, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Then, TThen, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Else, TElse, ExpandedGoals).

'$lgt_expand_file_directive_goal'((IfThen; Else), ('*->'(TIf, TThen); TElse), ExpandedGoals) :-
	nonvar(IfThen),
	IfThen = '*->'(If, Then),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_expand_file_directive_goal'(If, TIf, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Then, TThen, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Else, TElse, ExpandedGoals).

'$lgt_expand_file_directive_goal'((Goal1; Goal2), (ExpandedGoal1; ExpandedGoal2), ExpandedGoals) :-
	!,
	'$lgt_expand_file_directive_goal'(Goal1, ExpandedGoal0, ExpandedGoals),
	'$lgt_fix_disjunction_left_side'(ExpandedGoal0, ExpandedGoal1),
	'$lgt_expand_file_directive_goal'(Goal2, ExpandedGoal2, ExpandedGoals).

'$lgt_expand_file_directive_goal'('*->'(Goal1, Goal2), '*->'(ExpandedGoal1, ExpandedGoal2), ExpandedGoals) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_expand_file_directive_goal'(Goal1, ExpandedGoal1, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Goal2, ExpandedGoal2, ExpandedGoals).

'$lgt_expand_file_directive_goal'((Goal1 -> Goal2), (ExpandedGoal1 -> ExpandedGoal2), ExpandedGoals) :-
	!,
	'$lgt_expand_file_directive_goal'(Goal1, ExpandedGoal1, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Goal2, ExpandedGoal2, ExpandedGoals).

'$lgt_expand_file_directive_goal'(\+ Goal, \+ ExpandedGoal, ExpandedGoals) :-
	!,
	'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal, ExpandedGoals).

'$lgt_expand_file_directive_goal'(catch(Goal, Catcher, Recovery), catch(ExpandedGoal, Catcher, ExpandedRecovery), ExpandedGoals) :-
	!,
	'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal, ExpandedGoals),
	'$lgt_expand_file_directive_goal'(Recovery, ExpandedRecovery, ExpandedGoals).

% workaround lack of compliance by some backend Prolog compilers

'$lgt_expand_file_directive_goal'(predicate_property(Pred, Prop), '$lgt_predicate_property'(Pred, Prop), _) :-
	!.

% expand calls to set_logtalk_flag/2 when possible to avoid the need of runtime type-checking

'$lgt_expand_file_directive_goal'(set_logtalk_flag(Flag, Value), '$lgt_set_compiler_flag'(Flag, Value), _) :-
	nonvar(Flag),
	nonvar(Value),
	!,
	'$lgt_check'(read_write_flag, Flag),
	'$lgt_check'(flag_value, Flag + Value).

% expand calls to the logtalk_compile/1-2 and logtalk_load/1-2 predicates to
% add a directory argument for default resolving of relative file paths

'$lgt_expand_file_directive_goal'(logtalk_compile(Files), '$lgt_logtalk_compile'(Files, Directory, ExCtx), _) :-
	!,
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []).

'$lgt_expand_file_directive_goal'(logtalk_compile(Files, Flags), '$lgt_logtalk_compile'(Files, Flags, Directory, ExCtx), _) :-
	!,
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []).

'$lgt_expand_file_directive_goal'(logtalk_load(Files), '$lgt_logtalk_load'(Files, Directory, ExCtx), _) :-
	!,
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []).

'$lgt_expand_file_directive_goal'(logtalk_load(Files, Flags), '$lgt_logtalk_load'(Files, Flags, Directory, ExCtx), _) :-
	!,
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, user, [], []).

% expand if possible calls to the logtalk_load_context/2 predicate to support
% embedded applications where the compiled code may no longer be loaded using
% the Logtalk runtime

'$lgt_expand_file_directive_goal'(logtalk_load_context(Key, Value), true, _) :-
	nonvar(Key),
	logtalk_load_context(Key, Value),
	!.

% when the directive is found inside an entity, use any applicable uses/2 or
% use_module/2 directive; this is mainly useful when compiling Prolog modules
% as objects as the user can always write a (::)/2 or (:)/2 goal instead

'$lgt_expand_file_directive_goal'(Goal, Obj::Goal, _) :-
	'$lgt_pp_entity_'(_, _, _),
	'$lgt_pp_uses_predicate_'(Obj, _, Goal, _, _, _),
	!.

'$lgt_expand_file_directive_goal'(Goal, ':'(Module,Goal), _) :-
	'$lgt_pp_entity_'(_, _, _),
	'$lgt_pp_use_module_predicate_'(Module, _, Goal, _, _, _),
	!.

% catchall clause

'$lgt_expand_file_directive_goal'(Goal, Goal, _).



% '$lgt_expand_file_goal'(+callable, -callable)
%
% expands a goal; fails if no goal expansion hook is defined
%
% the callers of this predicate must ensure that a goal
% is repeatedly expanded until a fixed-point is reached
%
% the callers must also take care of the case where the
% goal is wrapped with the {}/1 control construct

'$lgt_expand_file_goal'(Goal, ExpandedGoal) :-
	(	% source-file specific compiler hook
		'$lgt_pp_hook_goal_expansion_'(Goal, ExpandedGoal) ->
		true
	;	% default compiler hook
		'$lgt_hook_goal_expansion_'(Goal, ExpandedGoal) ->
		true
	;	% dialect specific expansion
		'$lgt_prolog_goal_expansion'(Goal, ExpandedGoal) ->
		'$lgt_prolog_goal_expansion_portability_warnings'(Goal, ExpandedGoal)
	;	% no compiler hook defined
		fail
	),
	% the following check means that an expanded goal is checked twice but that
	% allows us to distinguish between user errors and goal-expansion errors
	'$lgt_check'(callable, ExpandedGoal, goal_expansion(Goal, ExpandedGoal)).


'$lgt_prolog_goal_expansion_portability_warnings'(Goal, ExpandedGoal) :-
	(	'$lgt_compiler_flag'(portability, warning) ->
		'$lgt_source_file_context'(File, Lines),
		'$lgt_increment_compiling_warnings_counter',
		(	'$lgt_pp_entity_'(Type, Entity, _) ->
			'$lgt_print_message'(
				warning(portability),
				prolog_dialect_goal_expansion(File, Lines, Type, Entity, Goal, ExpandedGoal)
			)
		;	'$lgt_print_message'(
				warning(portability),
				prolog_dialect_goal_expansion(File, Lines, Goal, ExpandedGoal)
			)
		)
	;	true
	).



% '$lgt_push_if_new'(@term, @callable, -list)
%
% auxiliary predicate to prevent going into an infinite loop when
% goal-expansion results in a goal that contains the expanded goal
%
% calls to this predicate fail if the goal about to be expanded was
% the result of a previous goal expansion (tested using term equality)

'$lgt_push_if_new'(ExpandedGoals, Pred, NewExpandedGoals) :-
	var(ExpandedGoals),
	!,
	NewExpandedGoals = [Pred].
'$lgt_push_if_new'(ExpandedGoals, Pred, _) :-
	'$lgt_member_var'(Pred, ExpandedGoals),
	!,
	fail.
'$lgt_push_if_new'(ExpandedGoals, Pred, [Pred| ExpandedGoals]).



% '$lgt_compile_include_file_terms'(@list(term), +atom, +compilation_context)
%
% compiles a list of file terms (directives, clauses, or grammar rules)
% found in an included file

'$lgt_compile_include_file_terms'([Term-_| Terms], File, Ctx) :-
	'$lgt_pp_cc_skipping_',
	% we're performing conditional compilation and skipping terms ...
	\+ '$lgt_is_conditional_compilation_directive'(Term),
	% ... except for conditional compilation directives
	!,
	'$lgt_compile_include_file_terms'(Terms, File, Ctx).

'$lgt_compile_include_file_terms'([Term-sd(VariableNames,Singletons,Lines)| Terms], File, Ctx) :-
	retractall('$lgt_pp_term_source_data_'(_, _, _, _, _)),
	assertz('$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines)),
	'$lgt_check'(nonvar, Term, term),
	% only the compilation context mode should be shared between different terms
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
	'$lgt_comp_ctx'(NewCtx, _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _),
	'$lgt_compile_file_term'(Term, NewCtx),
	'$lgt_compile_include_file_terms'(Terms, File, Ctx).

'$lgt_compile_include_file_terms'([], _, _).



% '$lgt_compile_file_term'(@nonvar, +compilation_context)
%
% compiles a source file term (clause, directive, or grammar rule)
%
% we allow non-callable terms to be term-expanded; only if that fails
% we throw an error

'$lgt_compile_file_term'(Term, Ctx) :-
	% we must unify any parameter variables used in the term with
	% the corresponding entity parameters before any expansion
	'$lgt_unify_parameter_variables'(Term, Ctx),
	(	Term = {_} ->
		% bypass control construct; skip term-expansion
		'$lgt_compile_expanded_term'(Term, Term, Ctx)
	;	'$lgt_pp_hook_term_expansion_'(Term, ExpandedTerms) ->
		% source-file specific compiler hook
		'$lgt_compile_expanded_terms'(ExpandedTerms, Term, Ctx)
	;	'$lgt_hook_term_expansion_'(Term, ExpandedTerms) ->
		% default compiler hook
		'$lgt_compile_expanded_terms'(ExpandedTerms, Term, Ctx)
	;	'$lgt_prolog_term_expansion'(Term, ExpandedTerms) ->
		% dialect specific expansion
		'$lgt_prolog_term_expansion_portability_warnings'(Term, ExpandedTerms),
		'$lgt_compile_expanded_terms'(ExpandedTerms, Term, Ctx)
	;	% no compiler hook defined
		'$lgt_compile_non_expanded_term'(Term, Ctx)
	).


'$lgt_prolog_term_expansion_portability_warnings'(Term, ExpandedTerms) :-
	(	Term \== ExpandedTerms,
		'$lgt_compiler_flag'(portability, warning) ->
		'$lgt_source_file_context'(File, Lines),
		'$lgt_increment_compiling_warnings_counter',
		(	'$lgt_pp_entity_'(Type, Entity, _) ->
			'$lgt_print_message'(
				warning(portability),
				prolog_dialect_term_expansion(File, Lines, Type, Entity, Term, ExpandedTerms)
			)
		;	'$lgt_print_message'(
				warning(portability),
				prolog_dialect_term_expansion(File, Lines, Term, ExpandedTerms)
			)
		)
	;	true
	).



% '$lgt_compile_expanded_terms'(@list(term), @term, +compilation_context)
% '$lgt_compile_expanded_terms'(@term, @term, +compilation_context)
%
% compiles the expanded terms (which can be a list of terms);
% the second argument is the original term and is used for more
% informative exception terms in case of error
%
% note that the clause order ensures that instantiation errors will be
% caught by the call to the '$lgt_compile_expanded_term'/3 predicate

'$lgt_compile_expanded_terms'([ExpandedTerm| ExpandedTerms], Term, Ctx) :-
	!,
	'$lgt_compile_expanded_term'(ExpandedTerm, Term, Ctx),
	(	ExpandedTerm \== (:- end_object),
		ExpandedTerm \== (:- end_protocol),
		ExpandedTerm \== (:- end_category) ->
		% ensure that only the compilation context mode and the entity prefix are
		% shared between different terms but keep the current term position
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, _, Mode, _, Lines, _),
		'$lgt_comp_ctx'(NewCtx, _, _, _, _, _, _, Prefix, _, _, _, Mode, _, Lines, _)
	;	% share only the compilation context mode and the current term position
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _),
		'$lgt_comp_ctx'(NewCtx, _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _)
	),
	'$lgt_compile_expanded_terms'(ExpandedTerms, Term, NewCtx).

'$lgt_compile_expanded_terms'([], _, _) :-
	!.

'$lgt_compile_expanded_terms'(ExpandedTerm, Term, Ctx) :-
	'$lgt_compile_expanded_term'(ExpandedTerm, Term, Ctx).



% '$lgt_compile_expanded_term'(@term, @term, +compilation_context)
%
% compiles a source file term (a clause, directive, or grammar rule);
% the second argument is the original term and is used for more
% informative exception terms in case of error

'$lgt_compile_expanded_term'((-), Term, _) :-
	% catch variables
	throw(error(instantiation_error, term_expansion(Term, _))).

'$lgt_compile_expanded_term'(begin_of_file, _, _) :-
	!.

'$lgt_compile_expanded_term'(end_of_file, _, Ctx) :-
	'$lgt_pp_module_'(Module),
	!,
	% module definitions start with an opening module/1-2 directive and are assumed
	% to end at the end of a source file; there is no module closing directive; set
	% the initial compilation context and the position for compiling the end_of_file term
	'$lgt_pp_referenced_object_'(Module, _, Start-_),
	'$lgt_comp_ctx_lines'(Ctx, _-End),
	assertz('$lgt_pp_entity_lines_'(Module, Start-End)),
	'$lgt_second_stage'(object, Module, Ctx),
	'$lgt_print_message'(silent(compiling), compiled_entity(module, Module)).

'$lgt_compile_expanded_term'(end_of_file, Term, _) :-
	'$lgt_pp_entity_'(Type, _, _),
	% unexpected end-of-file while compiling an entity
	(	Type == object ->
		throw(error(existence_error(directive, end_object/0), term_expansion(Term, end_of_file)))
	;	Type == protocol ->
		throw(error(existence_error(directive, end_protocol/0), term_expansion(Term, end_of_file)))
	;	% Type == category,
		throw(error(existence_error(directive, end_category/0), term_expansion(Term, end_of_file)))
	).

'$lgt_compile_expanded_term'(end_of_file, Term, _) :-
	'$lgt_pp_cc_if_found_'(_),
	% unexpected end-of-file while compiling a conditional compilation block
	throw(error(existence_error(directive, endif/0), term_expansion(Term, end_of_file))).

'$lgt_compile_expanded_term'(end_of_file, _, _) :-
	!.

'$lgt_compile_expanded_term'({ExpandedTerm}, Term, _) :-
	% bypass control construct; expanded term is final
	!,
	(	callable(ExpandedTerm) ->
		(	'$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines) ->
			SourceData = sd(Term, VariableNames, Singletons, File, Lines)
		;	SourceData = nil, Lines = '-'(-1, -1)
		),
		(	'$lgt_pp_entity_'(_, _, _) ->
			% ensure that the relative order of the entity terms is kept
			assertz('$lgt_pp_entity_term_'({ExpandedTerm}, SourceData, Lines))
		;	% non-entity terms
			assertz('$lgt_pp_prolog_term_'(ExpandedTerm, Lines))
		)
	;	var(ExpandedTerm) ->
		throw(error(instantiation_error, term_expansion(Term, {ExpandedTerm})))
	;	throw(error(type_error(callable, Term), term_expansion(Term, {ExpandedTerm})))
	).

'$lgt_compile_expanded_term'((Head :- Body), _, Ctx) :-
	!,
	'$lgt_comp_ctx_term'(Ctx, (Head :- Body)),
	'$lgt_compile_clause'((Head :- Body), Ctx).

'$lgt_compile_expanded_term'((:- Directive), _, Ctx) :-
	!,
	'$lgt_comp_ctx_term'(Ctx, (:- Directive)),
	'$lgt_compile_directive'(Directive, Ctx).

'$lgt_compile_expanded_term'((Head --> Body), _, Ctx) :-
	!,
	'$lgt_comp_ctx_term'(Ctx, (Head --> Body)),
	'$lgt_compile_grammar_rule'((Head --> Body), Ctx).

'$lgt_compile_expanded_term'(ExpandedTerm, Term, Ctx) :-
	(	callable(ExpandedTerm) ->
		% fact
		'$lgt_comp_ctx_term'(Ctx, ExpandedTerm),
		'$lgt_compile_clause'(ExpandedTerm, Ctx)
	;	throw(error(type_error(callable, ExpandedTerm), term_expansion(Term, ExpandedTerm)))
	).



% '$lgt_compile_non_expanded_term'(@nonvar, +compilation_context)
%
% compiles a source file term (a clause, directive, or grammar rule);
% the second argument is the original term and is used for more
% informative exception terms in case of error

'$lgt_compile_non_expanded_term'(begin_of_file, _) :-
	!.

'$lgt_compile_non_expanded_term'(end_of_file, Ctx) :-
	'$lgt_pp_module_'(Module),
	!,
	% module definitions start with an opening module/1-2 directive and are assumed
	% to end at the end of a source file; there is no module closing directive; set
	% the initial compilation context and the position for compiling the end_of_file term
	'$lgt_pp_referenced_object_'(Module, _, Start-_),
	'$lgt_comp_ctx_lines'(Ctx, _-End),
	assertz('$lgt_pp_entity_lines_'(Module, Start-End)),
	'$lgt_second_stage'(object, Module, Ctx),
	'$lgt_print_message'(silent(compiling), compiled_entity(module, Module)).

'$lgt_compile_non_expanded_term'(end_of_file, _) :-
	'$lgt_pp_entity_'(Type, _, _),
	% unexpected end-of-file while compiling an entity
	(	Type == object ->
		throw(error(existence_error(directive, end_object/0), term(end_of_file)))
	;	Type == protocol ->
		throw(error(existence_error(directive, end_protocol/0), term(end_of_file)))
	;	% Type == category,
		throw(error(existence_error(directive, end_category/0), term(end_of_file)))
	).

'$lgt_compile_non_expanded_term'(end_of_file, _) :-
	'$lgt_pp_cc_if_found_'(_),
	% unexpected end-of-file while compiling a conditional compilation block
	throw(error(existence_error(directive, endif/0), term(end_of_file))).

'$lgt_compile_non_expanded_term'(end_of_file, _) :-
	!.

'$lgt_compile_non_expanded_term'({Term}, _) :-
	% bypass control construct; term is final
	!,
	(	callable(Term) ->
		(	'$lgt_pp_term_source_data_'({Term}, VariableNames, Singletons, File, Lines) ->
			SourceData = sd(Term, VariableNames, Singletons, File, Lines)
		;	SourceData = nil, Lines = '-'(-1, -1)
		),
		(	'$lgt_pp_entity_'(_, _, _) ->
			% ensure that the relative order of the entity terms is kept
			assertz('$lgt_pp_entity_term_'({Term}, SourceData, Lines))
		;	% non-entity terms
			assertz('$lgt_pp_prolog_term_'(Term, Lines))
		)
	;	var(Term) ->
		throw(error(instantiation_error, term({Term})))
	;	throw(error(type_error(callable, Term), term({Term})))
	).

'$lgt_compile_non_expanded_term'((Head :- Body), Ctx) :-
	!,
	'$lgt_comp_ctx_term'(Ctx, (Head :- Body)),
	'$lgt_compile_clause'((Head :- Body), Ctx).

'$lgt_compile_non_expanded_term'((:- Directive), Ctx) :-
	!,
	'$lgt_comp_ctx_term'(Ctx, (:- Directive)),
	'$lgt_compile_directive'(Directive, Ctx).

'$lgt_compile_non_expanded_term'((Head --> Body), Ctx) :-
	!,
	'$lgt_comp_ctx_term'(Ctx, (Head --> Body)),
	'$lgt_compile_grammar_rule'((Head --> Body), Ctx).

'$lgt_compile_non_expanded_term'(Term, Ctx) :-
	(	callable(Term) ->
		'$lgt_compile_clause'(Term, Ctx)
	;	throw(error(type_error(callable, Term), term(Term)))
	).



% '$lgt_compile_runtime_include_file_terms'(@list(term), +atom)
%
% compiles a list of runtime terms (clauses, directives, or grammar rules)
% found in an included file
%
% note that the clause order ensures that instantiation errors will be caught
% by the call to the '$lgt_compile_runtime_term'/2 predicate

'$lgt_compile_runtime_include_file_terms'([Term-_| Terms], File) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, runtime, _, '-'(-1, -1), _),
	'$lgt_compile_runtime_term'(Term, Ctx),
	'$lgt_compile_runtime_include_file_terms'(Terms, File).

'$lgt_compile_runtime_include_file_terms'([], _).



% '$lgt_compile_runtime_terms'(@list(term))
%
% compiles a list of runtime terms (clauses, directives, or grammar rules)
%
% note that the clause order ensures that instantiation errors will be caught
% by the call to the '$lgt_compile_runtime_term'/2 predicate

'$lgt_compile_runtime_terms'([Term| Terms]) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, runtime, _, '-'(-1, -1), _),
	'$lgt_compile_runtime_term'(Term, Ctx),
	'$lgt_compile_runtime_terms'(Terms).

'$lgt_compile_runtime_terms'([]).



% '$lgt_compile_runtime_term'(@term, +compilation_context)
%
% compiles a runtime term (a clause, directive, or grammar rule)

'$lgt_compile_runtime_term'((-), _) :-
	% catch variables
	throw(error(instantiation_error, term)).

'$lgt_compile_runtime_term'(begin_of_file, _) :-
	!.

'$lgt_compile_runtime_term'(end_of_file, _) :-
	!.

'$lgt_compile_runtime_term'({Term}, _) :-
	% bypass control construct; term is final
	!,
	(	callable(Term) ->
		assertz('$lgt_pp_entity_term_'({Term}, nil, '-'(-1, -1)))
	;	var(Term) ->
		throw(error(instantiation_error, term({Term})))
	;	throw(error(type_error(callable, Term), term({Term})))
	).

'$lgt_compile_runtime_term'((Head :- Body), Ctx) :-
	!,
	'$lgt_compile_clause'((Head :- Body), Ctx).

'$lgt_compile_runtime_term'((:- Directive), Ctx) :-
	!,
	'$lgt_compile_directive'(Directive, Ctx).

'$lgt_compile_runtime_term'((Head --> Body), Ctx) :-
	!,
	'$lgt_compile_grammar_rule'((Head --> Body), Ctx).

'$lgt_compile_runtime_term'(Term, _) :-
	\+ callable(Term),
	throw(error(type_error(callable, Term), term(Term))).

'$lgt_compile_runtime_term'(Term, Ctx) :-
	% fact
	'$lgt_compile_clause'(Term, Ctx).



% '$lgt_compile_directive'(@term, +compilation_context)
%
% compiles a directive

'$lgt_compile_directive'((-), _) :-
	% catch variables
	throw(error(instantiation_error, directive(_))).

% conditional compilation directives

'$lgt_compile_directive'(if(Goal), Ctx) :-
	(	Goal = {UserGoal} ->
		% final goal
		'$lgt_check'(callable, UserGoal, directive(if(Goal))),
		fail
	;	'$lgt_check'(callable, Goal, directive(if(Goal))),
		% only expand goals when compiling a source file
		'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal),
		Goal \== ExpandedGoal,
		!,
		'$lgt_compile_directive'(if({ExpandedGoal}), Ctx)
	).

'$lgt_compile_directive'(if(Goal), _) :-
	'$lgt_pp_cc_mode_'(Value),
	% not top-level if/1 directive
	!,
	asserta('$lgt_pp_cc_if_found_'(Goal)),
	(	Value == ignore ->
		% another if ... endif to ignore
		asserta('$lgt_pp_cc_mode_'(ignore))
	;	Value == seek_else ->
		% we're looking for an else; ignore this if ... endif
		asserta('$lgt_pp_cc_mode_'(ignore))
	;	Value == skip_all ->
		asserta('$lgt_pp_cc_mode_'(ignore))
	;	% Value == skip_else,
		(	(	Goal = {UserGoal} ->
				catch(UserGoal, Error, '$lgt_compiler_error_handler'(Error))
			;	catch(Goal, Error, '$lgt_compiler_error_handler'(Error))
			) ->
			asserta('$lgt_pp_cc_mode_'(skip_else))
		;	asserta('$lgt_pp_cc_mode_'(seek_else)),
			retractall('$lgt_pp_cc_skipping_'),
			assertz('$lgt_pp_cc_skipping_')
		)
	).

'$lgt_compile_directive'(if(Goal), _) :-
	% top-level if
	!,
	asserta('$lgt_pp_cc_if_found_'(Goal)),
	(	(	Goal = {UserGoal} ->
			catch(UserGoal, Error, '$lgt_compiler_error_handler'(Error))
		;	catch(Goal, Error, '$lgt_compiler_error_handler'(Error))
		) ->
		asserta('$lgt_pp_cc_mode_'(skip_else))
	;	asserta('$lgt_pp_cc_mode_'(seek_else)),
		retractall('$lgt_pp_cc_skipping_'),
		assertz('$lgt_pp_cc_skipping_')
	).

'$lgt_compile_directive'(elif(Goal), _) :-
	\+ '$lgt_pp_cc_if_found_'(_),
	throw(error(existence_error(directive, if/1), directive(elif(Goal)))).

'$lgt_compile_directive'(elif(Goal), Ctx) :-
	(	Goal = {UserGoal} ->
		% final goal
		'$lgt_check'(callable, UserGoal, directive(elif(Goal))),
		fail
	;	'$lgt_check'(callable, Goal, directive(elif(Goal))),
		% only expand goals when compiling a source file
		'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal),
		Goal \== ExpandedGoal,
		!,
		'$lgt_compile_directive'(elif({ExpandedGoal}), Ctx)
	).

'$lgt_compile_directive'(elif(Goal), _) :-
	'$lgt_pp_cc_mode_'(Mode),
	(	Mode == ignore ->
		% we're inside an if ... endif that we're ignoring
		true
	;	Mode == skip_else ->
		% the corresponding if is true so we must skip this elif
		retractall('$lgt_pp_cc_skipping_'),
		assertz('$lgt_pp_cc_skipping_'),
		retract('$lgt_pp_cc_mode_'(_)),
		asserta('$lgt_pp_cc_mode_'(skip_all))
	;	Mode == skip_all ->
		true
	;	% Mode == seek_else,
		% the corresponding if is false
		retract('$lgt_pp_cc_mode_'(_)),
		(	(	Goal = {UserGoal} ->
				catch(UserGoal, Error, '$lgt_compiler_error_handler'(Error))
			;	catch(Goal, Error, '$lgt_compiler_error_handler'(Error))
			) ->
			retractall('$lgt_pp_cc_skipping_'),
			asserta('$lgt_pp_cc_mode_'(skip_else))
		;	asserta('$lgt_pp_cc_mode_'(seek_else))
		)
	),
	!.

'$lgt_compile_directive'(else, _) :-
	\+ '$lgt_pp_cc_if_found_'(_),
	throw(error(existence_error(directive, if/1), directive(else))).

'$lgt_compile_directive'(else, _) :-
	'$lgt_pp_cc_mode_'(Mode),
	(	Mode == ignore ->
		% we're inside an if ... endif that we're ignoring
		true
	;	Mode == skip_else ->
		% the corresponding if is true so we must skip this else
		% and any enclose if ... endif
		retractall('$lgt_pp_cc_skipping_'),
		assertz('$lgt_pp_cc_skipping_'),
		retract('$lgt_pp_cc_mode_'(_)),
		asserta('$lgt_pp_cc_mode_'(skip_all))
	;	Mode == skip_all ->
		true
	;	% Mode == seek_else ->
		% the corresponding if is false
		retract('$lgt_pp_cc_mode_'(_)),
		asserta('$lgt_pp_cc_mode_'(compile)),
		retractall('$lgt_pp_cc_skipping_')
	),
	!.

'$lgt_compile_directive'(endif, _) :-
	\+ '$lgt_pp_cc_if_found_'(_),
	throw(error(existence_error(directive, if/1), directive(endif))).

'$lgt_compile_directive'(endif, _) :-
	retract('$lgt_pp_cc_if_found_'(_)),
	retract('$lgt_pp_cc_mode_'(Mode)),
	(	Mode \== ignore ->
		retractall('$lgt_pp_cc_skipping_')
	;	\+ '$lgt_pp_cc_if_found_'(_) ->
		retractall('$lgt_pp_cc_skipping_'),
		retractall('$lgt_pp_cc_mode_'(_))
	;	true
	),
	!.

% remaining directives

'$lgt_compile_directive'(Directive, Ctx) :-
	\+ '$lgt_pp_entity_'(_, _, _),
	% not compiling an entity
	\+ '$lgt_logtalk_opening_directive'(Directive),
	% directive occurs before opening entity directive
	!,
	(	'$lgt_logtalk_closing_directive'(Directive) ->
		% closing entity directive occurs before the opening entity directive;
		% the opening directive is probably missing or misspelt
		(	Directive == end_object ->
			throw(error(existence_error(directive, object/1), directive(Directive)))
		;	Directive == end_protocol ->
			throw(error(existence_error(directive, protocol/1), directive(Directive)))
		;	% Directive == end_category ->
			throw(error(existence_error(directive, category/1), directive(Directive)))
		)
	;	% compile it as a source file-level directive
		catch(
			'$lgt_compile_file_directive'(Directive, Ctx),
			Error,
			throw(error(Error, directive(Directive)))
		)
	).

'$lgt_compile_directive'(Directive, Ctx) :-
	'$lgt_logtalk_directive'(Directive),
	!,
	catch(
		'$lgt_compile_logtalk_directive'(Directive, Ctx),
		Error,
		throw(error(Error, directive(Directive)))
	).

'$lgt_compile_directive'(Directive, Ctx) :-
	'$lgt_prolog_meta_directive'(Directive, Meta),
	% as defined in the Prolog adapter files
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(portability, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			compiling_proprietary_prolog_directive(File, Lines, Type, Entity, Directive)
		)
	;	true
	),
	% save the source data information for use in the second compiler stage
	% (where it might be required by calls to the logtalk_load_context/2
	% predicate during goal expansion)
	(	'$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines) ->
		SourceData = sd(Term, VariableNames, Singletons, File, Lines)
	;	SourceData = nil, Lines = '-'(-1, -1)
	),
	assertz('$lgt_pp_entity_meta_directive_'(directive(Directive, Meta), SourceData, Lines)).

'$lgt_compile_directive'(Directive, Ctx) :-
	'$lgt_pp_module_'(Current),
	% we're compiling a module as an object
	Directive \= use_module(_),
	Directive \= ensure_loaded(_),
	% but not unsupported directives that the backend Prolog compiler adapter
	% file failed to expand into supported use_module/2 directives
	(	'$lgt_pp_defines_predicate_'(Directive, _, _, _, _, _)
	;	'$lgt_pp_uses_predicate_'(_, _, Directive, _, _, _)
		% directive is a query for a locally defined predicate
	;	'$lgt_pp_use_module_predicate_'(_, _, Directive, _, _, _)
		% or a predicate referenced in a use_module/2 directive
	;	'$lgt_built_in_predicate'(Directive)
		% or a built-in predicate
	;	\+ '$lgt_control_construct'(Directive),
		'$lgt_find_visible_module_predicate'(Current, Module, Directive),
		% or an implicit call to a module predicate with a missing use_module/2 directive;
		% in practice, this only occurs in backend systems with an autoload mechanism
		functor(Directive, Functor, Arity),
		'$lgt_comp_ctx_mode'(Ctx, Mode),
		'$lgt_remember_missing_use_module_directive'(Mode, Module, Functor/Arity)
	),
	!,
	% compile query as an initialization goal
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(portability, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			compiling_query_as_initialization_goal(File, Lines, Type, Entity, Directive)
		)
	;	true
	),
	'$lgt_compile_logtalk_directive'(initialization(Directive), Ctx).

'$lgt_compile_directive'(encoding(Encoding), Ctx) :-
	'$lgt_source_file_context'(Path, _),
	'$lgt_pp_runtime_clause_'('$lgt_included_file_'(Path, _, _, _, _)),
	% encoding/1 directives may be used in included
	% files but not as entity directives
	!,
	'$lgt_compile_file_directive'(encoding(Encoding), Ctx).

'$lgt_compile_directive'(Directive, _) :-
	functor(Directive, Functor, Arity),
	throw(error(domain_error(directive, Functor/Arity), directive(Directive))).



% '$lgt_compile_file_directive'(@nonvar, +compilation_context)
%
% compiles file-level directives, i.e. directives that are not encapsulated in a Logtalk
% entity; error-checking is delegated in most cases to the backend Prolog compiler

'$lgt_compile_file_directive'(encoding(Encoding), Ctx) :-
	!,
	'$lgt_source_file_context'(File, Lines),
	(	'$lgt_pp_file_encoding_'(File, Encoding, _, Line),
		% encoding/1 directive already found and processed ...
		'$lgt_comp_ctx_lines'(Ctx, Line-_) ->
		% ... same encoding/1 directive that was found and processed
		true
	;	% out-of-place encoding/1 directive, which must be the first term in a source file
		'$lgt_compiler_flag'(encodings, silent) ->
		true
	;	'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(warning(encodings), ignored_encoding_directive(File, Lines))
	).

'$lgt_compile_file_directive'(ensure_loaded(FileSpec), _) :-
	!,
	% perform basic error checking
	'$lgt_check'(ground, FileSpec),
	% try to expand the file spec as the directive may be found in an included file
	'$lgt_expand_module_file_specification'(FileSpec, ExpandedFile),
	% try to call ensure_loaded/1 as a built-in predicate but ignore any errors
	catch(ensure_loaded(ExpandedFile), _, true),
	'$lgt_pp_term_source_data_'(_, _, _, _, Lines),
	assertz('$lgt_pp_prolog_term_'((:- ensure_loaded(ExpandedFile)), Lines)).

'$lgt_compile_file_directive'(use_module(FileSpec), _) :-
	'$lgt_prolog_feature'(modules, unsupported),
	throw(error(domain_error(directive, use_module/1), directive(use_module(FileSpec)))).

'$lgt_compile_file_directive'(use_module(FileSpec), _) :-
	!,
	% perform basic error checking
	'$lgt_check'(ground, FileSpec),
	% try to expand the file spec as the directive may be found in an included file
	'$lgt_expand_module_file_specification'(FileSpec, ExpandedFile),
	% try to call use_module/1 as a built-in predicate but ignore any errors
	catch(use_module(ExpandedFile), _, true),
	'$lgt_pp_term_source_data_'(_, _, _, _, Lines),
	assertz('$lgt_pp_prolog_term_'((:- use_module(ExpandedFile)), Lines)).

'$lgt_compile_file_directive'(use_module(FileSpec, Imports), _) :-
	'$lgt_prolog_feature'(modules, unsupported),
	throw(error(domain_error(directive, use_module/2), directive(use_module(FileSpec, Imports)))).

'$lgt_compile_file_directive'(use_module(FileSpec, Imports), _) :-
	!,
	% perform basic error checking
	'$lgt_check'(ground, FileSpec),
	'$lgt_check'(ground, Imports),
	% try to expand the file spec as the directive may be found in an included file
	'$lgt_expand_module_file_specification'(FileSpec, ExpandedFile),
	% try to call use_module/2 as a built-in predicate but ignore any errors
	catch(use_module(ExpandedFile, Imports), _, true),
	'$lgt_pp_term_source_data_'(_, _, _, _, Lines),
	assertz('$lgt_pp_prolog_term_'((:- use_module(ExpandedFile, Imports)), Lines)).

% handling of this Prolog directive is necessary to
% support the Logtalk term-expansion mechanism
'$lgt_compile_file_directive'(include(File), Ctx) :-
	!,
	% read the file terms for compilation
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, Mode, _, Line-_, _),
	'$lgt_read_file_to_terms'(File, Directory, Path, Terms, Mode),
	% save the dependency in the main file to support make
	'$lgt_pp_file_paths_flags_'(MainBasename, MainDirectory, _, _, _),
	'$lgt_file_modification_time'(Path, TimeStamp),
	assertz('$lgt_pp_runtime_clause_'('$lgt_included_file_'(Path, Line, MainBasename, MainDirectory, TimeStamp))),
	% save loading stack to deal with failed compilation
	retractall('$lgt_file_loading_stack_'(Path, Directory)),
	asserta('$lgt_file_loading_stack_'(Path, Directory)),
	% compile the included file terms
	catch(
		'$lgt_compile_include_file_terms'(Terms, Path, Ctx),
		Error,
		(retract('$lgt_file_loading_stack_'(Path, Directory)), throw(Error))
	),
	retractall('$lgt_file_loading_stack_'(Path, Directory)).

'$lgt_compile_file_directive'(initialization(Goal), Ctx) :-
	!,
	% perform basic error checking
	'$lgt_check'(callable, Goal),
	% initialization directives are collected and moved to the end of file
	% to minimize compatibility issues with backend Prolog compilers
	'$lgt_source_file_context'(_File, Lines),
	(	Goal = {UserGoal} ->
		% final goal
		'$lgt_check'(callable, UserGoal),
		assertz('$lgt_pp_file_initialization_'(Goal, Lines))
	;	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		% goals are only expanded when compiling a source file
		'$lgt_expand_file_directive_goal'(Goal, ExpandedGoal),
		Goal \== ExpandedGoal ->
		assertz('$lgt_pp_file_initialization_'(ExpandedGoal, Lines))
	;	assertz('$lgt_pp_file_initialization_'(Goal, Lines))
	).

'$lgt_compile_file_directive'(op(Priority, Specifier, Operators), Ctx) :-
	!,
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_activate_file_operators'(Priority, Specifier, Operators, Mode),
	'$lgt_pp_term_source_data_'(_, _, _, _, Lines),
	assertz('$lgt_pp_prolog_term_'((:- op(Priority, Specifier, Operators)), Lines)).

'$lgt_compile_file_directive'(set_logtalk_flag(Name, Value), _) :-
	!,
	'$lgt_check'(read_write_flag, Name),
	'$lgt_check'(flag_value, Name+Value),
	% local scope (restricted to the source file being compiled)
	Flag =.. [Name, Value],
	'$lgt_set_compiler_flags'([Flag]).

'$lgt_compile_file_directive'(set_prolog_flag(Flag, Value), Ctx) :-
	!,
	% perform basic error and portability checking
	'$lgt_compile_body'(set_prolog_flag(Flag, Value), _, _, _, Ctx),
	% require a bound value
	'$lgt_check'(nonvar, Value),
	% setting the flag during compilation may or may not work as expected
	% depending on the flag and on the backend Prolog compiler
	set_prolog_flag(Flag, Value),
	% we also copy the directive to the generated intermediate Prolog file
	'$lgt_pp_term_source_data_'(_, _, _, _, Lines),
	assertz('$lgt_pp_prolog_term_'((:- set_prolog_flag(Flag, Value)), Lines)).

'$lgt_compile_file_directive'(multifile(Preds), _) :-
	% perform basic error checking
	'$lgt_flatten_to_list'(Preds, PredsFlatted),
	'$lgt_check_file_predicate_directive_arguments'(PredsFlatted, (multifile)),
	fail.

'$lgt_compile_file_directive'(dynamic(Preds), _) :-
	% perform basic error checking
	'$lgt_flatten_to_list'(Preds, PredsFlatted),
	'$lgt_check_file_predicate_directive_arguments'(PredsFlatted, (dynamic)),
	fail.

'$lgt_compile_file_directive'(discontiguous(Preds), _) :-
	% perform basic error checking
	'$lgt_flatten_to_list'(Preds, PredsFlatted),
	'$lgt_check_file_predicate_directive_arguments'(PredsFlatted, (discontiguous)),
	fail.

'$lgt_compile_file_directive'(Directive, Ctx) :-
	'$lgt_logtalk_built_in_predicate'(Directive, _),
	% Logtalk built-in predicate being used as a directive
	!,
	% directive will be copied to the generated Prolog file
	'$lgt_pp_term_source_data_'(_, _, _, File, Lines),
	assertz('$lgt_pp_prolog_term_'((:- Directive), Lines)),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(warning(portability), logtalk_built_in_predicate_as_directive(File, Lines, Directive))
	;	true
	).

'$lgt_compile_file_directive'(Directive, Ctx) :-
	(	Directive = {_}
	;	Directive = [_| _]
	),
	% assume Logtalk or Prolog top-level shortcut being used as a directive
	!,
	% directive will be copied to the generated Prolog file
	'$lgt_pp_term_source_data_'(_, _, _, File, Lines),
	assertz('$lgt_pp_prolog_term_'((:- Directive), Lines)),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(warning(portability), top_level_shortcut_as_directive(File, Lines, Directive))
	;	true
	).

'$lgt_compile_file_directive'(Directive, Ctx) :-
	% directive will be copied to the generated Prolog file
	'$lgt_pp_term_source_data_'(_, _, _, File, Lines),
	assertz('$lgt_pp_prolog_term_'((:- Directive), Lines)),
	% report a possible portability issue if warranted
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(portability, warning),
		\+ '$lgt_file_directive'(Directive) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(warning(portability), non_standard_file_directive(File, Lines, Directive))
	;	true
	).


% auxiliary predicate for performing basic error checking of file level
% predicate directive arguments

'$lgt_check_file_predicate_directive_arguments'([Pred| Preds], Property) :-
	!,
	'$lgt_check_file_predicate_directive_argument'(Pred, Property),
	'$lgt_check_file_predicate_directive_arguments'(Preds, Property).

'$lgt_check_file_predicate_directive_arguments'([], _).


'$lgt_check_file_predicate_directive_argument'(Obj::Pred, Property) :-
	% Logtalk entity predicates must be defined within an entity but be
	% sure there aren't instantiation or type errors in the directive
	!,
	'$lgt_check'(object_identifier, Obj),
	'$lgt_check'(predicate_or_non_terminal_indicator, Pred),
	throw(permission_error(declare, Property, Obj::Pred)).

'$lgt_check_file_predicate_directive_argument'(':'(Module,Pred), _) :-
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_check'(predicate_or_non_terminal_indicator, Pred).

'$lgt_check_file_predicate_directive_argument'(Pred, _) :-
	'$lgt_check'(predicate_or_non_terminal_indicator, Pred).



'$lgt_expand_module_file_specification'(FileSpec, ExpandedFile) :-
	(	atom(FileSpec),
		% try to expand to an existing Prolog file
		'$lgt_source_file_name'(FileSpec, [], _, _, Extension, ExpandedFile),
		'$lgt_file_extension'(prolog, Extension),
		'$lgt_file_exists'(ExpandedFile) ->
		true
	;	% otherwise try the file spec as-is
		ExpandedFile = FileSpec
	).



% '$lgt_compile_logtalk_directives'(+list(term), +compilation_context)
%
% compiles a list of Logtalk directives when dynamically creating an entity

'$lgt_compile_logtalk_directives'([Directive| Directives], Ctx) :-
	(	var(Directive) ->
		throw(instantiation_error)
	;	'$lgt_logtalk_directive'(Directive) ->
		'$lgt_compile_logtalk_directive'(Directive, Ctx),
		% only the compilation context mode and lines should be shared between different directives
		'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _),
		'$lgt_comp_ctx'(NewCtx, _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _),
		'$lgt_compile_logtalk_directives'(Directives, NewCtx)
	;	'$lgt_prolog_meta_directive'(Directive, _) ->
		'$lgt_compile_directive'(Directive, Ctx)
	;	functor(Directive, Functor, Arity),
		throw(domain_error(directive, Functor/Arity))
	).

'$lgt_compile_logtalk_directives'([], _).



% '$lgt_compile_logtalk_directive'(@term, +compilation_context)
%
% compiles a Logtalk directive and its (possibly empty) list of arguments

'$lgt_compile_logtalk_directive'((-), _) :-
	% catch variables
	throw(instantiation_error).

'$lgt_compile_logtalk_directive'(include(File), Ctx) :-
	% read the file terms for compilation
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_read_file_to_terms'(File, Directory, Path, Terms, Mode),
	% save the dependency in the main file to support make if compiling a source file
	(	Mode == runtime ->
		true
	;	'$lgt_pp_file_paths_flags_'(MainBasename, MainDirectory, _, _, _),
		'$lgt_file_modification_time'(Path, TimeStamp),
		'$lgt_comp_ctx_lines'(Ctx, Line-_),
		assertz('$lgt_pp_runtime_clause_'('$lgt_included_file_'(Path, Line, MainBasename, MainDirectory, TimeStamp)))
	),
	% save loading stack to deal with failed compilation
	retractall('$lgt_file_loading_stack_'(Path, Directory)),
	asserta('$lgt_file_loading_stack_'(Path, Directory)),
	% compile the included file terms
	catch(
		(	Mode == runtime ->
			'$lgt_compile_runtime_include_file_terms'(Terms, Path)
		;	'$lgt_compile_include_file_terms'(Terms, Path, Ctx)
		),
		Error,
		(retract('$lgt_file_loading_stack_'(Path, Directory)), throw(Error))
	),
	retractall('$lgt_file_loading_stack_'(Path, Directory)).

% object opening and closing directives

'$lgt_compile_logtalk_directive'(object(Obj), Ctx) :-
	'$lgt_compile_logtalk_directive'(object_(Obj, []), Ctx).

'$lgt_compile_logtalk_directive'(object(Obj, Relation), Ctx) :-
	'$lgt_compile_logtalk_directive'(object_(Obj, [Relation]), Ctx).

'$lgt_compile_logtalk_directive'(object(Obj, Relation1, Relation2), Ctx) :-
	'$lgt_compile_logtalk_directive'(object_(Obj, [Relation1, Relation2]), Ctx).

'$lgt_compile_logtalk_directive'(object(Obj, Relation1, Relation2, Relation3), Ctx) :-
	'$lgt_compile_logtalk_directive'(object_(Obj, [Relation1, Relation2, Relation3]), Ctx).

'$lgt_compile_logtalk_directive'(object(Obj, Relation1, Relation2, Relation3, Relation4), Ctx) :-
	'$lgt_compile_logtalk_directive'(object_(Obj, [Relation1, Relation2, Relation3, Relation4]), Ctx).

% auxiliary predicate to compile all variants to the object opening directive
'$lgt_compile_logtalk_directive'(object_(Obj, Relations), Ctx) :-
	(	var(Obj) ->
		throw(instantiation_error)
	;	\+ callable(Obj) ->
		throw(type_error(object_identifier, Obj))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _)) ->
		% an object with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, object, Obj))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Obj, _, _, _, _)) ->
		% a protocol with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, protocol, Obj))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_category_'(Obj, _, _, _, _, _)) ->
		% a category with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, category, Obj))
	;	functor(Obj, '{}', 1) ->
		% reserved syntax for object proxies
		throw(permission_error(create, object, Obj))
	;	'$lgt_pp_entity_'(Type, _, _) ->
		% opening object directive found while still compiling the previous entity
		(	Type == object ->
			throw(existence_error(directive, end_object/0))
		;	Type == protocol ->
			throw(existence_error(directive, end_protocol/0))
		;	% Type == category,
			throw(existence_error(directive, end_category/0))
		)
	;	'$lgt_print_message'(silent(compiling), compiling_entity(object, Obj)),
		'$lgt_compile_object_relations'(Relations, Obj, Ctx),
		'$lgt_compile_object_identifier'(Obj, Ctx),
		'$lgt_save_parameter_variables'(Obj)
	).

'$lgt_compile_logtalk_directive'(end_object, Ctx) :-
	(	'$lgt_pp_object_'(Obj, _, _, _, _, _, _, _, _, _, _) ->
		% we're indeed compiling an object
		'$lgt_pp_referenced_object_'(Obj, _, Start-_),
		'$lgt_comp_ctx_lines'(Ctx, _-End),
		assertz('$lgt_pp_entity_lines_'(Obj, Start-End)),
		'$lgt_second_stage'(object, Obj, Ctx),
		'$lgt_print_message'(silent(compiling), compiled_entity(object, Obj))
	;	% entity ending directive mismatch
		throw(existence_error(directive, object/1))
	).

% protocol opening and closing directives

'$lgt_compile_logtalk_directive'(protocol(Ptc), Ctx) :-
	'$lgt_compile_logtalk_directive'(protocol_(Ptc, []), Ctx).

'$lgt_compile_logtalk_directive'(protocol(Ptc, Relation), Ctx) :-
	'$lgt_compile_logtalk_directive'(protocol_(Ptc, [Relation]), Ctx).

% auxiliary predicate to compile all variants to the protocol opening directive
'$lgt_compile_logtalk_directive'(protocol_(Ptc, Relations), Ctx) :-
	(	var(Ptc) ->
		throw(instantiation_error)
	;	\+ atom(Ptc) ->
		throw(type_error(protocol_identifier, Ptc))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Ptc, _, _, _, _, _, _, _, _, _, _)) ->
		% an object with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, object, Ptc))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Ptc, _, _, _, _)) ->
		% a protocol with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, protocol, Ptc))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_category_'(Ptc, _, _, _, _, _)) ->
		% a category with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, category, Ptc))
	;	'$lgt_pp_entity_'(Type, _, _) ->
		% opening protocol directive found while still compiling the previous entity
		(	Type == object ->
			throw(existence_error(directive, end_object/0))
		;	Type == protocol ->
			throw(existence_error(directive, end_protocol/0))
		;	% Type == category,
			throw(existence_error(directive, end_category/0))
		)
	;	'$lgt_print_message'(silent(compiling), compiling_entity(protocol, Ptc)),
		'$lgt_compile_protocol_identifier'(Ptc, Ctx),
		'$lgt_compile_protocol_relations'(Relations, Ptc, Ctx)
	).

'$lgt_compile_logtalk_directive'(end_protocol, Ctx) :-
	(	'$lgt_pp_protocol_'(Ptc, _, _, _, _) ->
		% we're indeed compiling a protocol
		'$lgt_pp_referenced_protocol_'(Ptc, _, Start-_),
		'$lgt_comp_ctx_lines'(Ctx, _-End),
		assertz('$lgt_pp_entity_lines_'(Ptc, Start-End)),
		'$lgt_second_stage'(protocol, Ptc, Ctx),
		'$lgt_print_message'(silent(compiling), compiled_entity(protocol, Ptc))
	;	% entity ending directive mismatch
		throw(existence_error(directive, protocol/1))
	).

% category opening and closing directives

'$lgt_compile_logtalk_directive'(category(Ctg), Ctx) :-
	'$lgt_compile_logtalk_directive'(category_(Ctg, []), Ctx).

'$lgt_compile_logtalk_directive'(category(Ctg, Relation), Ctx) :-
	'$lgt_compile_logtalk_directive'(category_(Ctg, [Relation]), Ctx).

'$lgt_compile_logtalk_directive'(category(Ctg, Relation1, Relation2), Ctx) :-
	'$lgt_compile_logtalk_directive'(category_(Ctg, [Relation1, Relation2]), Ctx).

'$lgt_compile_logtalk_directive'(category(Ctg, Relation1, Relation2, Relation3), Ctx) :-
	'$lgt_compile_logtalk_directive'(category_(Ctg, [Relation1, Relation2, Relation3]), Ctx).

% auxiliary predicate to compile all variants to the category opening directive
'$lgt_compile_logtalk_directive'(category_(Ctg, Relations), Ctx) :-
	(	var(Ctg) ->
		throw(instantiation_error)
	;	\+ callable(Ctg) ->
		throw(type_error(category_identifier, Ctg))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Ctg, _, _, _, _, _, _, _, _, _, _)) ->
		% an object with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, object, Ctg))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Ctg, _, _, _, _)) ->
		% a protocol with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, protocol, Ctg))
	;	'$lgt_pp_runtime_clause_'('$lgt_current_category_'(Ctg, _, _, _, _, _)) ->
		% a category with the same identifier was defined earlier in the same source file
		throw(permission_error(modify, category, Ctg))
	;	'$lgt_pp_entity_'(Type, _, _) ->
		% opening protocol directive found while still compiling the previous entity
		(	Type == object ->
			throw(existence_error(directive, end_object/0))
		;	Type == protocol ->
			throw(existence_error(directive, end_protocol/0))
		;	% Type == category,
			throw(existence_error(directive, end_category/0))
		)
	;	'$lgt_print_message'(silent(compiling), compiling_entity(category, Ctg)),
		'$lgt_compile_category_identifier'(Ctg, Ctx),
		'$lgt_compile_category_relations'(Relations, Ctg, Ctx),
		'$lgt_save_parameter_variables'(Ctg)
	).

'$lgt_compile_logtalk_directive'(end_category, Ctx) :-
	(	'$lgt_pp_category_'(Ctg, _, _, _, _, _) ->
		% we're indeed compiling a category
		'$lgt_pp_referenced_category_'(Ctg, _, Start-_),
		'$lgt_comp_ctx_lines'(Ctx, _-End),
		assertz('$lgt_pp_entity_lines_'(Ctg, Start-End)),
		'$lgt_second_stage'(category, Ctg, Ctx),
		'$lgt_print_message'(silent(compiling), compiled_entity(category, Ctg))
	;	% entity ending directive mismatch
		throw(existence_error(directive, category/1))
	).

% compile modules as objects

'$lgt_compile_logtalk_directive'(module(Module), Ctx) :-
	% empty export list
	'$lgt_compile_logtalk_directive'(module(Module, []), Ctx).

'$lgt_compile_logtalk_directive'(module(Module, Exports), Ctx) :-
	'$lgt_check'(module_identifier, Module),
	'$lgt_check'(list, Exports),
	% remember we are compiling a module
	assertz('$lgt_pp_module_'(Module)),
	'$lgt_print_message'(silent(compiling), compiling_entity(module, Module)),
	'$lgt_compile_object_identifier'(Module, Ctx),
	% make the export list the public resources list
	'$lgt_compile_logtalk_directive'(public(Exports), Ctx).

% set_logtalk_flag/2 entity directive

'$lgt_compile_logtalk_directive'(set_logtalk_flag(Flag, Value), _) :-
	'$lgt_check'(read_write_flag, Flag),
	'$lgt_check'(flag_value, Flag+Value),
	retractall('$lgt_pp_entity_compiler_flag_'(Flag, _)),
	assertz('$lgt_pp_entity_compiler_flag_'(Flag, Value)).

% declare an entity as built-in

'$lgt_compile_logtalk_directive'(built_in, Ctx) :-
	(	(	'$lgt_pp_dynamic_'
		;	'$lgt_comp_ctx_mode'(Ctx, runtime)
		) ->
		'$lgt_pp_entity_'(_, Entity, _),
		throw(permission_error(declare, built_in, Entity))
	;	assertz('$lgt_pp_built_in_')
	).

% create a message queue at object initialization

'$lgt_compile_logtalk_directive'(threaded, _) :-
	'$lgt_pp_entity_'(Type, _, _),
	(	'$lgt_prolog_feature'(engines, unsupported),
		'$lgt_prolog_feature'(threads, unsupported) ->
		throw(resource_error(threads))
	;	Type == object ->
		assertz('$lgt_pp_threaded_')
	;	Type == protocol ->
		throw(domain_error(protocol_directive, threaded/0))
	;	% Type == category,
		throw(domain_error(category_directive, threaded/0))
	).

% dynamic/0 entity directive
%
% (entities are static by default but can be declared dynamic using this directive)

'$lgt_compile_logtalk_directive'((dynamic), _) :-
	(	'$lgt_pp_built_in_' ->
		'$lgt_pp_entity_'(_, Entity, _),
		throw(permission_error(declare, (dynamic), Entity))
	;	assertz('$lgt_pp_dynamic_')
	).

% initialization/1 object directive
%
% this directive cannot be used in categories and protocols as it's not always
% possible to correctly compile initialization goals as there's no valid
% compilation context values for "sender", "this", and "self"

'$lgt_compile_logtalk_directive'(initialization(Goal), Ctx) :-
	'$lgt_pp_entity_'(Type, Entity, Prefix),
	(	Type == object ->
		% MetaVars = [] as we're compiling a local call
		'$lgt_comp_ctx'(Ctx, (:- initialization(Goal)), _, Entity, Entity, Entity, Entity, Prefix, [], _, ExCtx, _, [], Lines, _),
		'$lgt_execution_context'(ExCtx, Entity, Entity, Entity, Entity, [], []),
		% save the source data information for use in the second compiler stage
		% (where it might be required by calls to the logtalk_load_context/2
		% predicate during goal expansion)
		(	'$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines) ->
			SourceData = sd(Term, VariableNames, Singletons, File, Lines)
		;	SourceData = nil
		),
		(	'$lgt_compiler_flag'(debug, on) ->
			assertz('$lgt_pp_object_initialization_'(dgoal(Goal,Ctx), SourceData, Lines))
		;	assertz('$lgt_pp_object_initialization_'(goal(Goal,Ctx), SourceData, Lines))
		)
	;	Type == protocol ->
		throw(domain_error(protocol_directive, (initialization)/1))
	;	% Type == category,
		throw(domain_error(category_directive, (initialization)/1))
	).

% op/3 entity directive (operators are local to entities)

'$lgt_compile_logtalk_directive'(op(Priority, Specifier, Operators), Ctx) :-
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_activate_entity_operators'(Priority, Specifier, Operators, l, File, Lines, Mode).

% uses/1 entity directive

'$lgt_compile_logtalk_directive'(uses(Aliases), _) :-
	'$lgt_pp_entity_'(protocol, _, _),
	throw(error(domain_error(directive, uses/1), directive(uses(Aliases)))).

'$lgt_compile_logtalk_directive'(uses(Aliases), Ctx) :-
	'$lgt_compile_uses_directive'(Aliases, Aliases, Ctx).

% uses/2 predicate directive

'$lgt_compile_logtalk_directive'(uses(Obj, Resources), _) :-
	'$lgt_pp_entity_'(protocol, _, _),
	throw(error(domain_error(directive, uses/2), directive(uses(Obj, Resources)))).

'$lgt_compile_logtalk_directive'(uses(Obj, _), _) :-
	callable(Obj),
	'$lgt_pp_entity_'(object, Obj, _),
	% recursive reference to the object being compiled
	throw(permission_error(uses, self, Obj)).

'$lgt_compile_logtalk_directive'(uses(Obj, Resources), Ctx) :-
	term_variables(Obj, [ObjVariable| ObjVariables]),
	'$lgt_pp_term_source_data_'((:- uses(Obj,Resources)), VariableNames, _, _, _),
	'$lgt_member'(VariableName=Variable, VariableNames),
	'$lgt_member_var'(Variable, [ObjVariable| ObjVariables]),
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
	'$lgt_member'(VariableName-_, ParameterVariablePairs),
	% object argument is or contains a parameter variable
	!,
	'$lgt_compile_uses_directive'(Resources, Resources, Obj, true, Ctx).

'$lgt_compile_logtalk_directive'(uses(Obj, Resources), Ctx) :-
	'$lgt_comp_ctx_entity'(Ctx, Entity),
	term_variables(Entity, [EntityVariable| EntityVariables]),
	'$lgt_pp_term_source_data_'((:- uses(Obj,Resources)), VariableNames, _, _, _),
	'$lgt_member'(VariableName=Variable, VariableNames),
	'$lgt_member_var'(Variable, [EntityVariable| EntityVariables]),
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
	'$lgt_member'(VariableName-_, ParameterVariablePairs),
	% directive uses an entity parameter variable
	!,
	'$lgt_compile_uses_directive'(Resources, Resources, Obj, true, Ctx).

'$lgt_compile_logtalk_directive'(uses(Obj, Resources), Ctx) :-
	'$lgt_check'(object_identifier, Obj),
	'$lgt_add_referenced_object'(Obj, Ctx),
	'$lgt_compile_uses_directive'(Resources, Resources, Obj, false, Ctx).

% use_module/1 entity directive

'$lgt_compile_logtalk_directive'(use_module(FileSpec), _) :-
	'$lgt_pp_module_'(_),
	% compiling a module as an object but Logtalk only supports use_module/2 directives
	throw(error(domain_error(directive, use_module/1), directive(use_module(FileSpec)))).

'$lgt_compile_logtalk_directive'(use_module(Aliases), _) :-
	'$lgt_prolog_feature'(modules, unsupported),
	throw(error(domain_error(directive, use_module/1), directive(use_module(Aliases)))).

'$lgt_compile_logtalk_directive'(use_module(Aliases), _) :-
	'$lgt_pp_entity_'(protocol, _, _),
	throw(error(domain_error(directive, use_module/1), directive(use_module(Aliases)))).

'$lgt_compile_logtalk_directive'(use_module(Aliases), Ctx) :-
	'$lgt_compile_use_module_directive'(Aliases, Aliases, Ctx).

% use_module/2 predicate directive
%
% the first argument must be a module identifier; when a file specification
% is used, as it's usual in Prolog, it must be expanded at the adapter file
% level into a module identifier

'$lgt_compile_logtalk_directive'(use_module(Module, Imports), _) :-
	'$lgt_pp_entity_'(protocol, _, _),
	throw(error(domain_error(directive, use_module/2), directive(use_module(Module, Imports)))).

'$lgt_compile_logtalk_directive'(use_module(Module, _), _) :-
	atom(Module),
	'$lgt_pp_module_'(Module),
	% recursive reference to the module being compiled as an object
	throw(permission_error(use_module, self, Module)).

'$lgt_compile_logtalk_directive'(use_module(Module, Imports), Ctx) :-
	var(Module),
	'$lgt_pp_term_source_data_'((:- use_module(Module,Imports)), VariableNames, _, _, _),
	'$lgt_member'(VariableName=Variable, VariableNames),
	Module == Variable,
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
	'$lgt_member'(VariableName-_, ParameterVariablePairs),
	% module argument is a parameter variable
	!,
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_uses_directive'(Imports, Imports, Module, true, Ctx)
	;	'$lgt_prolog_feature'(modules, unsupported) ->
		% use_module/2 directives in objects or categories require a backend supporting modules
		throw(error(domain_error(directive, use_module/2), directive(use_module(Module, Imports))))
	;	% we're calling module predicates from within an object or a category
		'$lgt_compile_use_module_directive'(Imports, Imports, Module, true, Ctx)
	).

'$lgt_compile_logtalk_directive'(use_module(Module, Imports), Ctx) :-
	'$lgt_check'(module_identifier, Module),
	'$lgt_comp_ctx_entity'(Ctx, Entity),
	term_variables(Entity, [EntityVariable| EntityVariables]),
	'$lgt_pp_term_source_data_'((:- use_module(Module,Imports)), VariableNames, _, _, _),
	'$lgt_member'(VariableName=Variable, VariableNames),
	'$lgt_member_var'(Variable, [EntityVariable| EntityVariables]),
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
	'$lgt_member'(VariableName-_, ParameterVariablePairs),
	% directive uses an entity parameter variable
	!,
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_uses_directive'(Imports, Imports, Module, true, Ctx)
	;	'$lgt_prolog_feature'(modules, unsupported) ->
		% use_module/2 directives in objects or categories require a backend supporting modules
		throw(error(domain_error(directive, use_module/2), directive(use_module(Module, Imports))))
	;	% we're calling module predicates from within an object or a category
		'$lgt_compile_use_module_directive'(Imports, Imports, Module, true, Ctx)
	).

'$lgt_compile_logtalk_directive'(use_module(Module, Imports), Ctx) :-
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_uses_directive'(Imports, Imports, Module, false, Ctx)
	;	'$lgt_prolog_feature'(modules, unsupported) ->
		% use_module/2 directives in objects or categories require a backend supporting modules
		throw(error(domain_error(directive, use_module/2), directive(use_module(Module, Imports))))
	;	% we're calling module predicates from within an object or a category
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_compile_use_module_directive'(Imports, Imports, Module, false, Ctx)
	).

% reexport/2 module directive
%
% the first argument must be a module identifier; when a file specification
% is used, as it's usual in Prolog, it must be expanded at the adapter file
% level into a module identifier

'$lgt_compile_logtalk_directive'(reexport(Module, Exports), Ctx) :-
	% we must be compiling a module as an object
	(	'$lgt_pp_module_'(_) ->
		% assume referenced modules are also compiled as objects
		'$lgt_check'(module_identifier, Module),
		'$lgt_check'(list, Exports),
		'$lgt_compile_reexport_directive'(Exports, Module, Ctx)
	;	throw(error(domain_error(directive, (reexport)/2), directive(reexport(Module, Exports))))
	).

% info/1 entity directive

'$lgt_compile_logtalk_directive'(info(Pairs), Ctx) :-
	'$lgt_compile_entity_info_directive'(Pairs, TPairs),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_entity_info_'(TPairs, File, Lines)),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		(	'$lgt_member'(date is Year/Month/Day, Pairs) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(deprecated),
				deprecated_date_format(File, Lines, Type, Entity, Year/Month/Day, Year-Month-Day)
			)
		;	true
		),
		(	'$lgt_member'(version is Version, Pairs),
			Version \= ':'(_, ':'(_, _)) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(deprecated),
				deprecated_version_format(File, Lines, Type, Entity, Version)
			)
		;	true
		)
	;	true
	).

% info/2 predicate directive

'$lgt_compile_logtalk_directive'(info(Pred, Pairs), Ctx) :-
	'$lgt_source_file_context'(Ctx, File, Lines),
	(	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity) ->
		'$lgt_compile_predicate_info_directive'(Pairs, Functor, Arity, TPairs),
		assertz('$lgt_pp_predicate_info_'(Functor/Arity, TPairs, File, Lines))
	;	'$lgt_valid_non_terminal_indicator'(Pred, Functor, Arity, ExtArity) ->
		'$lgt_compile_predicate_info_directive'(Pairs, Functor, Arity, TPairs),
		assertz('$lgt_pp_predicate_info_'(Functor/ExtArity, TPairs, File, Lines))
	;	var(Pred) ->
		throw(instantiation_error)
	;	throw(type_error(predicate_indicator, Pred))
	).

% synchronized/1 predicate directive

'$lgt_compile_logtalk_directive'(synchronized(Resources), Ctx) :-
	'$lgt_flatten_to_list'(Resources, ResourcesFlatted),
	'$lgt_compile_synchronized_directive'(ResourcesFlatted, Ctx).

% scope directives

'$lgt_compile_logtalk_directive'(public(Resources), Ctx) :-
	'$lgt_flatten_to_list'(Resources, ResourcesFlatted),
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_compile_scope_directive'(ResourcesFlatted, (public), File, Lines, Ctx).

'$lgt_compile_logtalk_directive'(protected(Resources), Ctx) :-
	'$lgt_flatten_to_list'(Resources, ResourcesFlatted),
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_compile_scope_directive'(ResourcesFlatted, protected, File, Lines, Ctx).

'$lgt_compile_logtalk_directive'(private(Resources), Ctx) :-
	'$lgt_flatten_to_list'(Resources, ResourcesFlatted),
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_compile_scope_directive'(ResourcesFlatted, (private), File, Lines, Ctx).

% export/1 module directive
%
% module exported directives are compiled as object public directives

'$lgt_compile_logtalk_directive'(export(Exports), Ctx) :-
	% we must be compiling a module as an object
	(	'$lgt_pp_module_'(_) ->
		'$lgt_compile_logtalk_directive'(public(Exports), Ctx)
	;	throw(error(domain_error(directive, (export)/1), directive(export(Exports))))
	).

% dynamic/1 and discontiguous/1 predicate directives

'$lgt_compile_logtalk_directive'(dynamic(Resources), Ctx) :-
	'$lgt_flatten_to_list'(Resources, ResourcesFlatted),
	'$lgt_compile_dynamic_directive'(ResourcesFlatted, Ctx).

'$lgt_compile_logtalk_directive'(discontiguous(Resources), Ctx) :-
	'$lgt_flatten_to_list'(Resources, ResourcesFlatted),
	'$lgt_compile_discontiguous_directive'(ResourcesFlatted, Ctx).

% meta_predicate/2 and meta_non_terminal/1 predicate directives

'$lgt_compile_logtalk_directive'(meta_predicate(Preds), Ctx) :-
	'$lgt_flatten_to_list'(Preds, PredsFlatted),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object
		'$lgt_compile_module_meta_predicate_directive'(PredsFlatted, TPredsFlatted)
	;	% we're compiling a Logtalk entity
		TPredsFlatted = PredsFlatted
	),
	'$lgt_compile_meta_predicate_directive'(TPredsFlatted, Ctx).

'$lgt_compile_logtalk_directive'(meta_non_terminal(Preds), Ctx) :-
	'$lgt_flatten_to_list'(Preds, PredsFlatted),
	'$lgt_compile_meta_non_terminal_directive'(PredsFlatted, Ctx).

% mode/2 predicate directive

'$lgt_compile_logtalk_directive'(mode(Mode, Solutions), _) :-
	(var(Mode); var(Solutions)),
	throw(instantiation_error).

'$lgt_compile_logtalk_directive'(mode(Mode, _), _) :-
	\+ '$lgt_valid_mode_template'(Mode),
	throw(type_error(mode_term, Mode)).

'$lgt_compile_logtalk_directive'(mode(_, Solutions), _) :-
	\+ '$lgt_valid_number_of_proofs'(Solutions),
	throw(type_error(number_of_proofs, Solutions)).

'$lgt_compile_logtalk_directive'(mode(Mode, Solutions), Ctx) :-
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_mode_'(Mode, Solutions, File, Lines)).

% multifile/2 predicate directive

'$lgt_compile_logtalk_directive'(multifile(Preds), Ctx) :-
	'$lgt_flatten_to_list'(Preds, PredsFlatted),
	'$lgt_compile_multifile_directive'(PredsFlatted, Ctx).

% coinductive/1 predicate directive

'$lgt_compile_logtalk_directive'(coinductive(Preds), Ctx) :-
	(	'$lgt_prolog_feature'(coinduction, supported) ->
		'$lgt_flatten_to_list'(Preds, PredsFlatted),
		'$lgt_compile_coinductive_directive'(PredsFlatted, Ctx)
	;	throw(resource_error(coinduction))
	).

% alias/2 entity directive

'$lgt_compile_logtalk_directive'(alias(Entity, Resources), Ctx) :-
	'$lgt_check'(entity_identifier, Entity),
	'$lgt_compile_alias_directive'(Resources, Resources, Entity, Ctx).



% '$lgt_compile_alias_directive'(+list, +list, @entity_identifier, +compilation_context)
%
% auxiliary predicate for compiling alias/2 directives

'$lgt_compile_alias_directive'(_, _, Entity, _) :-
	'$lgt_pp_entity_'(_, Entity, _),
	throw(permission_error(reference, self, Entity)).

'$lgt_compile_alias_directive'(_, _, Entity, _) :-
	\+ '$lgt_pp_extended_protocol_'(Entity, _, _, _, _),
	\+ '$lgt_pp_implemented_protocol_'(Entity, _, _, _, _),
	\+ '$lgt_pp_extended_category_'(Entity, _, _, _, _, _),
	\+ '$lgt_pp_imported_category_'(Entity, _, _, _, _, _),
	\+ '$lgt_pp_extended_object_'(Entity, _, _, _, _, _, _, _, _, _, _),
	\+ '$lgt_pp_instantiated_class_'(Entity, _, _, _, _, _, _, _, _, _, _),
	\+ '$lgt_pp_specialized_class_'(Entity, _, _, _, _, _, _, _, _, _, _),
	\+ '$lgt_pp_complemented_object_'(Entity, _, _, _, _),
	throw(domain_error(ancestor, Entity)).

'$lgt_compile_alias_directive'([Resource| Resources], Argument, Entity, Ctx) :-
	!,
	'$lgt_check'(ground, Resource),
	'$lgt_compile_alias_directive_resource'(Resource, Entity, Ctx),
	'$lgt_compile_alias_directive'(Resources, Argument, Entity, Ctx).

'$lgt_compile_alias_directive'([], _, _, _) :-
	!.

'$lgt_compile_alias_directive'(_, Argument, _, _) :-
	throw(type_error(list, Argument)).


'$lgt_compile_alias_directive_resource'(as(Original,Alias), Entity, Ctx) :-
	!,
	'$lgt_compile_alias_directive_resource'(Original::Alias, Entity, Ctx).

'$lgt_compile_alias_directive_resource'(Original::Alias, Entity, Ctx) :-
	!,
	'$lgt_check'(predicate_or_non_terminal_indicator, Original),
	'$lgt_check'(predicate_or_non_terminal_indicator, Alias),
	'$lgt_compile_alias_directive_resource'(Original, Alias, Entity, Ctx).

'$lgt_compile_alias_directive_resource'(Resource, _, _) :-
	throw(type_error(predicate_alias_specification, Resource)).


'$lgt_compile_alias_directive_resource'(Functor1/Arity, Functor2/Arity, Entity, Ctx) :-
	!,
	functor(Pred, Functor1, Arity),
	Pred =.. [Functor1| Args],
	Alias =.. [Functor2| Args],
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_predicate_alias_'(Entity, Pred, Alias, 0, File, Lines)).

'$lgt_compile_alias_directive_resource'(Functor1//Arity, Functor2//Arity, Entity, Ctx) :-
	!,
	ExtArity is Arity + 2,
	functor(Pred, Functor1, ExtArity),
	Pred =.. [Functor1| Args],
	Alias =.. [Functor2| Args],
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_predicate_alias_'(Entity, Pred, Alias, 1, File, Lines)).

'$lgt_compile_alias_directive_resource'(Functor1//Arity1, Functor2//Arity2, _, _) :-
	throw(consistency_error(same_arity, Functor1//Arity1, Functor2//Arity2)).

'$lgt_compile_alias_directive_resource'(Functor1/Arity1, Functor2/Arity2, _, _) :-
	throw(consistency_error(same_arity, Functor1/Arity1, Functor2/Arity2)).

'$lgt_compile_alias_directive_resource'(_/_, Functor2//Arity2, _, _) :-
	throw(type_error(predicate_indicator, Functor2//Arity2)).

'$lgt_compile_alias_directive_resource'(_//_, Functor2/Arity2, _, _) :-
	throw(type_error(non_terminal_indicator, Functor2/Arity2)).



% '$lgt_compile_synchronized_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling synchronized/1 directives

'$lgt_compile_synchronized_directive'(Resources, Ctx) :-
	'$lgt_new_predicate_mutex'(Mutex),
	'$lgt_compile_synchronized_directive'(Resources, Mutex, Ctx).


'$lgt_new_predicate_mutex'(Mutex) :-
	'$lgt_pp_entity_'(_, _, Prefix),
	once(retract('$lgt_pp_predicate_mutex_counter_'(Old))),
	New is Old + 1,
	asserta('$lgt_pp_predicate_mutex_counter_'(New)),
	number_codes(New, Codes),
	atom_codes(Atom, Codes),
	atom_concat(Prefix, 'pred_mutex_', Aux),
	atom_concat(Aux, Atom, Mutex).


% note that the clause order ensures that instantiation errors will be caught by
% the call to the '$lgt_compile_synchronized_directive_resource'/1 predicate

'$lgt_compile_synchronized_directive'([Resource| Resources], Mutex, Ctx) :-
	'$lgt_compile_synchronized_directive_resource'(Resource, Mutex, Ctx),
	'$lgt_compile_synchronized_directive'(Resources, Mutex, Ctx).

'$lgt_compile_synchronized_directive'([], _, _).


'$lgt_compile_synchronized_directive_resource'(Pred, Mutex, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	functor(Head, Functor, Arity),
	(	'$lgt_pp_dynamic_'(Head, _, _, _) ->
		% synchronized predicates must be static
		throw(permission_error(modify, dynamic_predicate, Functor/Arity))
	;	'$lgt_pp_defines_predicate_'(Head, _, _, _, _, _) ->
		% synchronized/1 directives must precede the definitions for the declared predicates
		throw(permission_error(modify, predicate_interpretation, Functor/Arity))
	;	'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_synchronized_'(Head, Mutex, File, Lines))
	).

'$lgt_compile_synchronized_directive_resource'(NonTerminal, Mutex, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, Arity, ExtArity),
	!,
	functor(Head, Functor, ExtArity),
	(	'$lgt_pp_dynamic_'(Head, _, _, _) ->
		% synchronized non-terminals must be static
		throw(permission_error(modify, dynamic_non_terminal, Functor//Arity))
	;	'$lgt_pp_defines_non_terminal_'(Functor, Arity, _) ->
		throw(permission_error(modify, non_terminal_interpretation, Functor//Arity))
	;	'$lgt_pp_defines_predicate_'(Head, _, _, _, _, _) ->
		% synchronized/1 directives must precede the definitions for the declared non-terminals
		throw(permission_error(modify, non_terminal_interpretation, Functor//Arity))
	;	'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_synchronized_'(Head, Mutex, File, Lines))
	).

'$lgt_compile_synchronized_directive_resource'(Resource, _, _) :-
	ground(Resource),
	throw(type_error(predicate_indicator, Resource)).

'$lgt_compile_synchronized_directive_resource'(_, _, _) :-
	throw(instantiation_error).



% '$lgt_compile_scope_directive'(+list, @scope, +atom, +integer, +compilation_context)
%
% auxiliary predicate for compiling scope directives
%
% note that the clause order ensures that instantiation errors will be caught
% by the call to the '$lgt_compile_scope_directive_resource'/1 predicate

'$lgt_compile_scope_directive'([Resource| Resources], Scope, File, Lines, Ctx) :-
	'$lgt_compile_scope_directive_resource'(Resource, Scope, File, Lines, Ctx),
	'$lgt_compile_scope_directive'(Resources, Scope, File, Lines, Ctx).

'$lgt_compile_scope_directive'([], _, _, _, _).



% '$lgt_compile_scope_directive_resource'(@term, @scope, +integer, +compilation_context)
%
% auxiliary predicate for compiling scope directive resources

'$lgt_compile_scope_directive_resource'(op(Priority, Specifier, Operators), Scope, File, Lines, Ctx) :-
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	!,
	'$lgt_check_for_duplicated_scope_directives'(op(Priority, Specifier, Operators), Scope),
	'$lgt_scope'(Scope, InternalScope),
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_activate_entity_operators'(Priority, Specifier, Operators, InternalScope, File, Lines, Mode).

'$lgt_compile_scope_directive_resource'(Functor/Arity, Scope, File, StartLine-EndLine, _) :-
	'$lgt_valid_predicate_indicator'(Functor/Arity, Functor, Arity),
	functor(Pred, Functor, Arity),
	(	'$lgt_built_in_method'(Pred, _, _, _) ->
		% clash with a built-in method, whose scope cannot be changed
		throw(permission_error(modify, built_in_method, Functor/Arity))
	;	!,
		'$lgt_check_for_duplicated_scope_directives'(Functor/Arity, Scope),
		'$lgt_add_predicate_scope_directive'(Scope, Functor, Arity, File, StartLine-EndLine),
		assertz('$lgt_pp_predicate_declaration_location_'(Functor, Arity, File, StartLine-EndLine))
	).

'$lgt_compile_scope_directive_resource'(Functor//Arity, Scope, File, StartLine-EndLine, _) :-
	'$lgt_valid_non_terminal_indicator'(Functor//Arity, Functor, Arity, ExtArity),
	functor(Pred, Functor, ExtArity),
	(	'$lgt_built_in_method'(Pred, _, _, _) ->
		% clash with a built-in method, whose scope cannot be changed
		throw(permission_error(modify, built_in_method, Functor//Arity))
	;	!,
		'$lgt_check_for_duplicated_scope_directives'(Functor//Arity+ExtArity, Scope),
		assertz('$lgt_pp_non_terminal_'(Functor, Arity, ExtArity)),
		'$lgt_add_predicate_scope_directive'(Scope, Functor, ExtArity, File, StartLine-EndLine),
		assertz('$lgt_pp_predicate_declaration_location_'(Functor, ExtArity, File, StartLine-EndLine))
	).

'$lgt_compile_scope_directive_resource'(Resource, _, _, _, _) :-
	ground(Resource),
	throw(type_error(predicate_indicator, Resource)).

'$lgt_compile_scope_directive_resource'(_, _, _, _, _) :-
	throw(instantiation_error).


'$lgt_add_predicate_scope_directive'((public), Functor, Arity, File, Lines) :-
	assertz('$lgt_pp_public_'(Functor, Arity, File, Lines)).

'$lgt_add_predicate_scope_directive'(protected, Functor, Arity, File, Lines) :-
	assertz('$lgt_pp_protected_'(Functor, Arity, File, Lines)).

'$lgt_add_predicate_scope_directive'((private), Functor, Arity, File, Lines) :-
	assertz('$lgt_pp_private_'(Functor, Arity, File, Lines)).


'$lgt_check_for_duplicated_scope_directives'(op(_, _, []), _) :-
	!.

'$lgt_check_for_duplicated_scope_directives'(op(Priority, Specifier, [Operator| Operators]), Scope) :-
	!,
	(	'$lgt_pp_entity_operator_'(Priority, Specifier, Operator, Scope, OriginalFile, OriginalLines) ->
		(	'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			Directive =.. [Scope, op(Priority, Specifier, Operator)],
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_directive(File, Lines, Type, Entity, Directive, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	% allow a local operator to also be declared in a scope directive to simplify
		% compilation of included files and compilation of modules as objects
		'$lgt_pp_entity_operator_'(Priority, Specifier, Operator, OriginalScope, _, _),
		OriginalScope \== l ->
		throw(permission_error(modify, operator_scope, op(Priority, Specifier, Operator)))
	;	'$lgt_check_for_duplicated_scope_directives'(op(Priority, Specifier, Operators), Scope)
	).

'$lgt_check_for_duplicated_scope_directives'(op(Priority, Specifier, Operator), Scope) :-
	(	'$lgt_pp_entity_operator_'(Priority, Specifier, Operator, Scope, OriginalFile, OriginalLines) ->
		(	'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			Directive =.. [Scope, op(Priority, Specifier, Operator)],
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_directive(File, Lines, Type, Entity, Directive, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	% allow a local operator to also be declared in a scope directive to simplify
		% compilation of included files and compilation of modules as objects
		'$lgt_pp_entity_operator_'(Priority, Specifier, Operator, OriginalScope, _, _),
		OriginalScope \== l ->
		throw(permission_error(modify, operator_scope, op(Priority, Specifier, Operator)))
	;	true
	).

'$lgt_check_for_duplicated_scope_directives'(Functor/Arity, Scope) :-
	(	(	Scope == (public), '$lgt_pp_public_'(Functor, Arity, OriginalFile, OriginalLines)
		;	Scope == protected, '$lgt_pp_protected_'(Functor, Arity, OriginalFile, OriginalLines)
		;	Scope == (private), '$lgt_pp_private_'(Functor, Arity, OriginalFile, OriginalLines)
		) ->
		(	'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			Directive =.. [Scope, Functor/Arity],
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_directive(File, Lines, Type, Entity, Directive, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	(	'$lgt_pp_public_'(Functor, Arity, _, _)
		;	'$lgt_pp_protected_'(Functor, Arity, _, _)
		;	'$lgt_pp_private_'(Functor, Arity, _, _)
		) ->
		throw(permission_error(modify, predicate_scope, Functor/Arity))
	;	true
	).

'$lgt_check_for_duplicated_scope_directives'(Functor//Arity+ExtArity, Scope) :-
	(	(	Scope == (public), '$lgt_pp_public_'(Functor, ExtArity, OriginalFile, OriginalLines)
		;	Scope == protected, '$lgt_pp_protected_'(Functor, ExtArity, OriginalFile, OriginalLines)
		;	Scope == (private), '$lgt_pp_private_'(Functor, ExtArity, OriginalFile, OriginalLines)
		) ->
		(	'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			Directive =.. [Scope, Functor//Arity],
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_directive(File, Lines, Type, Entity, Directive, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	(	'$lgt_pp_public_'(Functor, ExtArity, _, _)
		;	'$lgt_pp_protected_'(Functor, ExtArity, _, _)
		;	'$lgt_pp_private_'(Functor, ExtArity, _, _)
		) ->
		throw(permission_error(modify, non_terminal_scope, Functor//Arity))
	;	true
	).



% '$lgt_compile_dynamic_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling dynamic/1 directives
%
% note that the clause order ensures that instantiation errors will be caught
% by the call to the '$lgt_compile_dynamic_directive_resource'/1 predicate

'$lgt_compile_dynamic_directive'([Resource| Resources], Ctx) :-
	'$lgt_compile_dynamic_directive_resource'(Resource, Ctx),
	'$lgt_compile_dynamic_directive'(Resources, Ctx).

'$lgt_compile_dynamic_directive'([], _).


'$lgt_compile_dynamic_directive_resource'(Entity::Resource, Ctx) :-
	'$lgt_check'(entity_identifier, Entity),
	nonvar(Resource),
	'$lgt_pp_entity_'(_, Entity0, _),
	'$lgt_variant'(Entity, Entity0),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
		'$lgt_compiler_flag'(general, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(general),
			redundant_entity_qualifier_in_predicate_directive(File, Lines, Type, Entity, Entity::Resource)
		)
	;	true
	),
	'$lgt_compile_dynamic_directive_resource'(Resource, Ctx).

'$lgt_compile_dynamic_directive_resource'(Entity::Pred, _) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(dynamic(Functor/Arity), dynamic(Entity::Pred)),
		assertz('$lgt_pp_directive_'(dynamic(Functor/Arity)))
	;	'$lgt_check'(entity_identifier, Entity),
		functor(Template, Functor, Arity),
		'$lgt_check_primary_dynamic_declaration'(Entity, Template) ->
		'$lgt_entity_to_prefix'(Entity, Prefix),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		'$lgt_check_for_duplicated_directive'(dynamic(TFunctor/TArity), dynamic(Entity::Pred)),
		assertz('$lgt_pp_directive_'(dynamic(TFunctor/TArity)))
	;	throw(permission_error(modify, predicate_declaration, Entity::Pred))
	).

'$lgt_compile_dynamic_directive_resource'(Entity::NonTerminal, _) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(dynamic(Functor/ExtArity), dynamic(Entity::NonTerminal)),
		assertz('$lgt_pp_directive_'(dynamic(Functor/ExtArity)))
	;	'$lgt_check'(entity_identifier, Entity),
		functor(Template, Functor, ExtArity),
		'$lgt_check_primary_dynamic_declaration'(Entity, Template) ->
		'$lgt_entity_to_prefix'(Entity, Prefix),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/ExtArity, TFunctor/TArity),
		'$lgt_check_for_duplicated_directive'(dynamic(TFunctor/TArity), dynamic(Entity::NonTerminal)),
		assertz('$lgt_pp_directive_'(dynamic(TFunctor/TArity)))
	;	throw(permission_error(modify, non_terminal_declaration, Entity::NonTerminal))
	).

'$lgt_compile_dynamic_directive_resource'(':'(Module, Pred), _) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	(	Module == user ->
		'$lgt_check_for_duplicated_directive'(dynamic(Functor/Arity), dynamic(':'(Module, Pred))),
		assertz('$lgt_pp_directive_'(dynamic(Functor/Arity)))
	;	'$lgt_check'(module_identifier, Module),
		'$lgt_check_for_duplicated_directive'(dynamic(':'(Module, Functor/Arity)), dynamic(':'(Module, Pred))),
		assertz('$lgt_pp_directive_'(dynamic(':'(Module, Functor/Arity))))
	).

'$lgt_compile_dynamic_directive_resource'(':'(Module, NonTerminal), _) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	(	Module == user ->
		'$lgt_check_for_duplicated_directive'(dynamic(Functor/ExtArity), dynamic(':'(Module, NonTerminal))),
		assertz('$lgt_pp_directive_'(dynamic(Functor/ExtArity)))
	;	'$lgt_check'(module_identifier, Module),
		'$lgt_check_for_duplicated_directive'(dynamic(':'(Module, Functor/ExtArity)), dynamic(':'(Module, NonTerminal))),
		assertz('$lgt_pp_directive_'(dynamic(':'(Module, Functor/ExtArity))))
	).

'$lgt_compile_dynamic_directive_resource'(Pred, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	functor(Head, Functor, Arity),
	'$lgt_check_predicate_name_conflict'((dynamic), Head, Functor/Arity),
	(	'$lgt_pp_entity_'(category, _, _),
		(	'$lgt_pp_multifile_'(Head, _, _, _) ->
			% categories cannot contain predicates that are both multifile and dynamic
			throw(permission_error(declare, (dynamic), Functor/Arity))
		;	'$lgt_pp_defines_predicate_'(Head, _, _, _, _, _) ->
			% predicate definition occurs before the directive
			throw(permission_error(declare, (dynamic), Functor/Arity))
		)
	;	'$lgt_pp_synchronized_'(Head, _, _, _) ->
		% synchronized predicates must be static
		throw(permission_error(modify, synchronized_predicate, Functor/Arity))
	;	'$lgt_check_for_duplicated_dynamic_directive'(Head, Pred),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_dynamic_'(Head, Functor/Arity, File, Lines))
	).

'$lgt_compile_dynamic_directive_resource'(NonTerminal, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, Arity, ExtArity),
	!,
	functor(Head, Functor, ExtArity),
	'$lgt_check_predicate_name_conflict'((dynamic), Head, Functor//Arity),
	(	'$lgt_pp_entity_'(category, _, _),
		(	'$lgt_pp_multifile_'(Head, _, _, _) ->
			% categories cannot contain non-terminals that are both multifile and dynamic
			throw(permission_error(declare, (dynamic), Functor//Arity))
		;	'$lgt_pp_defines_predicate_'(Head, _, _, _, _, _) ->
			% predicate definition occurs before the directive
			throw(permission_error(declare, (dynamic), Functor//Arity))
		)
	;	'$lgt_pp_synchronized_'(Head, _, _, _) ->
		% synchronized non-terminals must be static
		throw(permission_error(modify, synchronized_non_terminal, Functor//Arity))
	;	'$lgt_check_for_duplicated_dynamic_directive'(Head, NonTerminal),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_dynamic_'(Head, Functor//Arity, File, Lines))
	).

'$lgt_compile_dynamic_directive_resource'(Resource, _) :-
	ground(Resource),
	throw(type_error(predicate_indicator, Resource)).

'$lgt_compile_dynamic_directive_resource'(_, _) :-
	throw(instantiation_error).


'$lgt_check_for_duplicated_dynamic_directive'(Head, PI) :-
	(	'$lgt_pp_dynamic_'(Head, _, OriginalFile, OriginalLines),
		'$lgt_compiler_flag'(duplicated_directives, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(duplicated_directives),
			duplicated_directive(File, Lines, Type, Entity, dynamic(PI), OriginalFile, OriginalLines)
		)
	;	true
	).



'$lgt_check_primary_dynamic_declaration'(Entity, Pred) :-
	% the object or category holding the primary declaration must be loaded
	(	'$lgt_current_object_'(Entity, _, Dcl, _, _, _, _, _, _, _, _)
	;	'$lgt_current_category_'(Entity, _, Dcl, _, _, _)
	), !,
	% the predicate must be declared (i.e., have a scope directive) and dynamic
	(	call(Dcl, Pred, Scope, _, Flags) ->
		functor(Scope, p, _),
		Flags /\ 2 =:= 2
	;	fail
	).



% '$lgt_compile_discontiguous_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling discontiguous/1 directives
%
% note that the clause order ensures that instantiation errors will be caught by
% the call to the '$lgt_compile_discontiguous_directive_resource'/1 predicate

'$lgt_compile_discontiguous_directive'([Resource| Resources], Ctx) :-
	'$lgt_compile_discontiguous_directive_resource'(Resource, Ctx),
	'$lgt_compile_discontiguous_directive'(Resources, Ctx).

'$lgt_compile_discontiguous_directive'([], _).


'$lgt_compile_discontiguous_directive_resource'(Entity::Resource, Ctx) :-
	'$lgt_check'(entity_identifier, Entity),
	nonvar(Resource),
	'$lgt_pp_entity_'(_, Entity0, _),
	'$lgt_variant'(Entity, Entity0),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
		'$lgt_compiler_flag'(general, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(general),
			redundant_entity_qualifier_in_predicate_directive(File, Lines, Type, Entity, Entity::Resource)
		)
	;	true
	),
	'$lgt_compile_discontiguous_directive_resource'(Resource, Ctx).

'$lgt_compile_discontiguous_directive_resource'(Entity::Pred, _) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(discontiguous(Functor/Arity), discontiguous(Entity::Pred)),
		assertz('$lgt_pp_directive_'(discontiguous(Functor/Arity)))
	;	'$lgt_check'(entity_identifier, Entity),
		'$lgt_entity_to_prefix'(Entity, Prefix),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		'$lgt_check_for_duplicated_directive'(discontiguous(TFunctor/TArity), discontiguous(Entity::Pred)),
		assertz('$lgt_pp_directive_'(discontiguous(TFunctor/TArity)))
	).

'$lgt_compile_discontiguous_directive_resource'(Entity::NonTerminal, _) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(discontiguous(Functor/ExtArity), discontiguous(Entity::NonTerminal)),
		assertz('$lgt_pp_directive_'(discontiguous(Functor/ExtArity)))
	;	'$lgt_check'(entity_identifier, Entity),
		'$lgt_entity_to_prefix'(Entity, Prefix),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/ExtArity, TFunctor/TArity),
		'$lgt_check_for_duplicated_directive'(discontiguous(TFunctor/TArity), discontiguous(Entity::NonTerminal)),
		assertz('$lgt_pp_directive_'(discontiguous(TFunctor/TArity)))
	).

'$lgt_compile_discontiguous_directive_resource'(':'(Module, Pred), _) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	(	Module == user ->
		'$lgt_check_for_duplicated_directive'(discontiguous(Functor/Arity), discontiguous(':'(Module, Pred))),
		assertz('$lgt_pp_directive_'(discontiguous(Functor/Arity)))
	;	'$lgt_check'(module_identifier, Module),
		'$lgt_check_for_duplicated_directive'(discontiguous(':'(Module, Functor/Arity)), discontiguous(':'(Module, Pred))),
		assertz('$lgt_pp_directive_'(discontiguous(':'(Module, Functor/Arity))))
	).

'$lgt_compile_discontiguous_directive_resource'(':'(Module, NonTerminal), _) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	(	Module == user ->
		'$lgt_check_for_duplicated_directive'(discontiguous(Functor/ExtArity), discontiguous(':'(Module, NonTerminal))),
		assertz('$lgt_pp_directive_'(discontiguous(Functor/ExtArity)))
	;	'$lgt_check'(module_identifier, Module),
		'$lgt_check_for_duplicated_directive'(discontiguous(':'(Module, Functor/ExtArity)), discontiguous(':'(Module, NonTerminal))),
		assertz('$lgt_pp_directive_'(discontiguous(':'(Module, Functor/ExtArity))))
	).

'$lgt_compile_discontiguous_directive_resource'(Pred, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	functor(Head, Functor, Arity),
	'$lgt_check_for_duplicated_discontiguous_directive'(Head, Pred),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_discontiguous_'(Head, File, Lines)).

'$lgt_compile_discontiguous_directive_resource'(NonTerminal, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	functor(Head, Functor, ExtArity),
	'$lgt_check_for_duplicated_discontiguous_directive'(Head, NonTerminal),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_discontiguous_'(Head, File, Lines)).

'$lgt_compile_discontiguous_directive_resource'(Resource, _) :-
	ground(Resource),
	throw(type_error(predicate_indicator, Resource)).

'$lgt_compile_discontiguous_directive_resource'(_, _) :-
	throw(instantiation_error).


'$lgt_check_for_duplicated_discontiguous_directive'(Head, PI) :-
	(	'$lgt_pp_discontiguous_'(Head, OriginalFile, OriginalLines),
		'$lgt_compiler_flag'(duplicated_directives, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(duplicated_directives),
			duplicated_directive(File, Lines, Type, Entity, discontiguous(PI), OriginalFile, OriginalLines)
		)
	;	true
	).



% '$lgt_compile_meta_predicate_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling meta_predicate/1 directives
%
% note that the clause order ensures that instantiation errors will be caught by
% the call to the '$lgt_compile_meta_predicate_directive_resource'/1 predicate

'$lgt_compile_meta_predicate_directive'([Meta| Metas], Ctx) :-
	'$lgt_compile_meta_predicate_directive_resource'(Meta, Ctx),
	'$lgt_compile_meta_predicate_directive'(Metas, Ctx).

'$lgt_compile_meta_predicate_directive'([], _).


'$lgt_compile_meta_predicate_directive_resource'(Entity::Meta, Ctx) :-
	'$lgt_valid_meta_predicate_template'(Meta),
	!,
	'$lgt_check'(entity_identifier, Entity),
	'$lgt_term_template'(Meta, Template),
	'$lgt_check_for_duplicated_meta_predicate_directive'(Entity::Template, Entity::Meta),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_meta_predicate_'(Entity::Template, Entity::Meta, File, Lines)).

'$lgt_compile_meta_predicate_directive_resource'(':'(Module, Meta), Ctx) :-
	'$lgt_valid_meta_predicate_template'(Meta),
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_term_template'(Meta, Template),
	'$lgt_check_for_duplicated_meta_predicate_directive'(':'(Module,Template), ':'(Module,Meta)),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_meta_predicate_'(':'(Module,Template), ':'(Module,Meta), File, Lines)).

'$lgt_compile_meta_predicate_directive_resource'(Meta, Ctx) :-
	'$lgt_valid_meta_predicate_template'(Meta),
	!,
	'$lgt_term_template'(Meta, Template),
	'$lgt_check_for_duplicated_meta_predicate_directive'(Template, Meta),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_meta_predicate_'(Template, Meta, File, Lines)).

'$lgt_compile_meta_predicate_directive_resource'(Meta, _) :-
	ground(Meta),
	throw(type_error(meta_predicate_template, Meta)).

'$lgt_compile_meta_predicate_directive_resource'(_, _) :-
	throw(instantiation_error).


'$lgt_check_for_duplicated_meta_predicate_directive'(Template, Meta) :-
	(	'$lgt_pp_meta_predicate_'(Template, Meta, OriginalFile, OriginalLines) ->
		(	'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_directive(File, Lines, Type, Entity, meta_predicate(Meta), OriginalFile, OriginalLines)
			)
		;	true
		)
	;	'$lgt_pp_meta_predicate_'(Template, _, _, _) ->
		throw(permission_error(modify, meta_predicate_template, Meta))
	;	true
	).



% '$lgt_compile_meta_non_terminal_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling meta_non_terminal/1 directives
%
% note that the clause order ensures that instantiation errors will be caught by
% the call to the '$lgt_compile_meta_non_terminal_directive_resource'/1 predicate

'$lgt_compile_meta_non_terminal_directive'([Meta| Metas], Ctx) :-
	'$lgt_compile_meta_non_terminal_directive_resource'(Meta, Ctx),
	'$lgt_compile_meta_non_terminal_directive'(Metas, Ctx).

'$lgt_compile_meta_non_terminal_directive'([], _).


'$lgt_compile_meta_non_terminal_directive_resource'(Entity::Meta, Ctx) :-
	'$lgt_valid_meta_predicate_template'(Meta),
	!,
	'$lgt_check'(entity_identifier, Entity),
	'$lgt_extend_meta_non_terminal_template'(Meta, ExtendedMeta),
	'$lgt_term_template'(ExtendedMeta, Template),
	'$lgt_check_for_duplicated_meta_non_terminal_directive'(Entity::Template, Entity::ExtendedMeta, Entity::Meta),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_meta_predicate_'(Entity::Template, Entity::ExtendedMeta, File, Lines)).

'$lgt_compile_meta_non_terminal_directive_resource'(':'(Module, Meta), Ctx) :-
	'$lgt_valid_meta_predicate_template'(Meta),
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_extend_meta_non_terminal_template'(Meta, ExtendedMeta),
	'$lgt_term_template'(ExtendedMeta, Template),
	'$lgt_check_for_duplicated_meta_non_terminal_directive'(':'(Module, Template), ':'(Module, ExtendedMeta), ':'(Module, Meta)),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_meta_predicate_'(':'(Module, Template), ':'(Module, ExtendedMeta), File, Lines)).

'$lgt_compile_meta_non_terminal_directive_resource'(Meta, Ctx) :-
	'$lgt_valid_meta_predicate_template'(Meta),
	!,
	'$lgt_extend_meta_non_terminal_template'(Meta, ExtendedMeta),
	'$lgt_term_template'(ExtendedMeta, Template),
	'$lgt_check_for_duplicated_meta_non_terminal_directive'(Template, ExtendedMeta, Meta),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_meta_predicate_'(Template, ExtendedMeta, File, Lines)).

'$lgt_compile_meta_non_terminal_directive_resource'(Meta, _) :-
	ground(Meta),
	throw(type_error(meta_non_terminal_template, Meta)).

'$lgt_compile_meta_non_terminal_directive_resource'(_, _) :-
	throw(instantiation_error).


'$lgt_extend_meta_non_terminal_template'(Meta, ExtendedMeta) :-
	Meta =.. [Functor| Args],
	'$lgt_compile_meta_non_terminal_directive_args'(Args, ExtendedArgs),
	ExtendedMeta =.. [Functor| ExtendedArgs].


'$lgt_compile_meta_non_terminal_directive_args'([], [*, *]).

'$lgt_compile_meta_non_terminal_directive_args'([Arg| Args], [ExtendedArg| ExtendedArgs]) :-
	(	integer(Arg) ->
		ExtendedArg is Arg + 2
	;	ExtendedArg = Arg
	),
	'$lgt_compile_meta_non_terminal_directive_args'(Args, ExtendedArgs).


'$lgt_check_for_duplicated_meta_non_terminal_directive'(Template, ExtendedMeta, Meta) :-
	(	'$lgt_pp_meta_predicate_'(Template, ExtendedMeta, OriginalFile, OriginalLines) ->
		(	'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_directive(File, Lines, Type, Entity, meta_non_terminal(Meta), OriginalFile, OriginalLines)
			)
		;	true
		)
	;	'$lgt_pp_meta_predicate_'(Template, _, _, _) ->
		throw(permission_error(modify, meta_non_terminal_template, Meta))
	;	true
	).



% '$lgt_compile_multifile_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling multifile/1 directives
%
% when the multifile predicate (or non-terminal) is declared for the module
% "user", the module prefix is removed to ensure code portability when using
% backend Prolog compilers without a module system
%
% note that the clause order ensures that instantiation errors will be caught
% by the call to the '$lgt_compile_multifile_directive_resource'/1 predicate

'$lgt_compile_multifile_directive'([Resource| Resources], Ctx) :-
	'$lgt_compile_multifile_directive_resource'(Resource, Ctx),
	'$lgt_compile_multifile_directive'(Resources, Ctx).

'$lgt_compile_multifile_directive'([], _).


'$lgt_compile_multifile_directive_resource'(Entity::Resource, Ctx) :-
	'$lgt_check'(entity_identifier, Entity),
	nonvar(Resource),
	'$lgt_pp_entity_'(_, Entity0, _),
	'$lgt_variant'(Entity, Entity0),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
		'$lgt_compiler_flag'(general, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(general),
			redundant_entity_qualifier_in_predicate_directive(File, Lines, Type, Entity, Entity::Resource)
		)
	;	true
	),
	'$lgt_compile_multifile_directive_resource'(Resource, Ctx).

'$lgt_compile_multifile_directive_resource'(Entity::Pred, _) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(multifile(Functor/Arity), multifile(Entity::Pred)),
		assertz('$lgt_pp_directive_'(multifile(Functor/Arity)))
	;	functor(Template, Functor, Arity),
		'$lgt_check_primary_multifile_declaration'(Entity, Template) ->
		'$lgt_entity_to_prefix'(Entity, Prefix),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		'$lgt_check_for_duplicated_directive'(multifile(TFunctor/TArity), multifile(Entity::Pred)),
		assertz('$lgt_pp_directive_'(multifile(TFunctor/TArity)))
	;	throw(permission_error(modify, predicate_declaration, Entity::Pred))
	).

'$lgt_compile_multifile_directive_resource'(Entity::NonTerminal, _) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(multifile(Functor/ExtArity), multifile(Entity::NonTerminal)),
		assertz('$lgt_pp_directive_'(multifile(Functor/ExtArity)))
	;	functor(Template, Functor, ExtArity),
		'$lgt_check_primary_multifile_declaration'(Entity, Template) ->
		'$lgt_entity_to_prefix'(Entity, Prefix),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/ExtArity, TFunctor/TArity),
		'$lgt_check_for_duplicated_directive'(multifile(TFunctor/TArity), multifile(Entity::NonTerminal)),
		assertz('$lgt_pp_directive_'(multifile(TFunctor/TArity)))
	;	throw(permission_error(modify, non_terminal_declaration, Entity::NonTerminal))
	).

'$lgt_compile_multifile_directive_resource'(':'(Module, Pred), _) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	(	Module == user ->
		'$lgt_check_for_duplicated_directive'(multifile(Functor/Arity), multifile(':'(Module, Pred))),
		assertz('$lgt_pp_directive_'(multifile(Functor/Arity)))
	;	'$lgt_check'(module_identifier, Module),
		'$lgt_check_for_duplicated_directive'(multifile(':'(Module, Functor/Arity)), multifile(':'(Module, Pred))),
		assertz('$lgt_pp_directive_'(multifile(':'(Module, Functor/Arity))))
	).

'$lgt_compile_multifile_directive_resource'(':'(Module, NonTerminal), _) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtArity),
	!,
	(	Module == user ->
		'$lgt_check_for_duplicated_directive'(multifile(Functor/ExtArity), multifile(':'(Module, NonTerminal))),
		assertz('$lgt_pp_directive_'(multifile(Functor/ExtArity)))
	;	'$lgt_check'(module_identifier, Module),
		'$lgt_check_for_duplicated_directive'(multifile(':'(Module, Functor/ExtArity)), multifile(':'(Module, NonTerminal))),
		assertz('$lgt_pp_directive_'(multifile(':'(Module, Functor/ExtArity))))
	).

'$lgt_compile_multifile_directive_resource'(Pred, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	functor(Head, Functor, Arity),
	'$lgt_pp_entity_'(Type, Entity, Prefix),
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(multifile(Functor/Arity), multifile(Pred)),
		assertz('$lgt_pp_directive_'(multifile(Functor/Arity)))
	;	Type == protocol ->
		% protocols cannot contain predicate definitions
		throw(permission_error(declare, (multifile), Functor/Arity))
	;	Type == category,
	 	'$lgt_pp_dynamic_'(Head, _, _, _) ->
		% categories cannot contain predicates that are both multifile and dynamic
	 	throw(permission_error(declare, (multifile), Functor/Arity))
	;	'$lgt_check_for_duplicated_multifile_directive'(Head, Pred),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_multifile_'(Head, Functor/Arity, File, Lines)),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		assertz('$lgt_pp_directive_'(multifile(TFunctor/TArity)))
	).

'$lgt_compile_multifile_directive_resource'(NonTerminal, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, Arity, ExtArity),
	!,
	functor(Head, Functor, ExtArity),
	'$lgt_pp_entity_'(Type, Entity, Prefix),
	(	Entity == user ->
		'$lgt_check_for_duplicated_directive'(multifile(Functor/ExtArity), multifile(NonTerminal)),
		assertz('$lgt_pp_directive_'(multifile(Functor/ExtArity)))
	;	Type == protocol ->
		% protocols cannot contain non-terminal definitions
		throw(permission_error(declare, (multifile), Functor//Arity))
	;	Type == category,
	 	'$lgt_pp_dynamic_'(Head, _, _, _) ->
		% categories cannot contain non-terminals that are both multifile and dynamic
	 	throw(permission_error(declare, (multifile), Functor//Arity))
	;	'$lgt_check_for_duplicated_multifile_directive'(Head, NonTerminal),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_multifile_'(Head, Functor//Arity, File, Lines)),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/ExtArity, TFunctor/TArity),
		assertz('$lgt_pp_directive_'(multifile(TFunctor/TArity)))
	).

'$lgt_compile_multifile_directive_resource'(Resource, _) :-
	ground(Resource),
	throw(type_error(predicate_indicator, Resource)).

'$lgt_compile_multifile_directive_resource'(_, _) :-
	throw(instantiation_error).


'$lgt_check_primary_multifile_declaration'(Entity, Pred) :-
	% the object or category holding the primary declaration must be loaded
	(	'$lgt_current_object_'(Entity, _, Dcl, _, _, _, _, _, _, _, _)
	;	'$lgt_current_category_'(Entity, _, Dcl, _, _, _)
	), !,
	% the predicate must be declared (i.e., have a scope directive) and multifile
	(	call(Dcl, Pred, Scope, _, Flags) ->
		functor(Scope, p, _),
		Flags /\ 16 =:= 16
	;	fail
	).


'$lgt_check_for_duplicated_multifile_directive'(Head, PI) :-
	(	'$lgt_pp_multifile_'(Head, _, OriginalFile, OriginalLines),
		'$lgt_compiler_flag'(duplicated_directives, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(duplicated_directives),
			duplicated_directive(File, Lines, Type, Entity, multifile(PI), OriginalFile, OriginalLines)
		)
	;	true
	).



% '$lgt_compile_coinductive_directive'(+list, +compilation_context)
%
% auxiliary predicate for compiling coinductive/1 directives
%
% note that the clause order ensures that instantiation errors will be caught by
% the call to the '$lgt_compile_coinductive_directive_resource'/1 predicate

'$lgt_compile_coinductive_directive'([Pred| Preds], Ctx) :-
	'$lgt_compile_coinductive_directive_resource'(Pred, Ctx),
	'$lgt_compile_coinductive_directive'(Preds, Ctx).

'$lgt_compile_coinductive_directive'([], _).


'$lgt_compile_coinductive_directive_resource'(Pred, Ctx) :-
	'$lgt_valid_coinductive_template'(Pred, Functor, Arity, Head, TestHead, Template),
	!,
	(	'$lgt_pp_defines_predicate_'(Head, _, _, _, _, _) ->
		% coinductive/1 directives must precede the definitions for the declared predicates
		throw(permission_error(modify, predicate_interpretation, Functor/Arity))
	;	true
	),
	% construct functor for the auxiliary predicate
	atom_concat(Functor, '__coinductive', CFunctor),
	% construct functor for debugging calls to the auxiliary predicate
	atom_concat(Functor, '__coinduction_preflight', DFunctor),
	functor(DHead, DFunctor, Arity),
	Head =.. [_| Args],
	DHead =.. [_| Args],
	'$lgt_pp_entity_'(_, Entity, Prefix),
	'$lgt_compile_predicate_indicator'(Prefix, CFunctor/Arity, TCFunctor/TCArity),
	functor(TCHead, TCFunctor, TCArity),
	'$lgt_unify_head_thead_arguments'(Head, TCHead, HeadExCtx),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(THead, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(Head, THead, BodyExCtx),
	'$lgt_source_file_context'(Ctx, File, Lines),
	assertz('$lgt_pp_coinductive_'(Head, TestHead, HeadExCtx, TCHead, BodyExCtx, THead, DHead, File, Lines)),
	assertz('$lgt_pp_coinductive_head_'(Head, HeadExCtx, TCHead)),
	assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, Functor/Arity, coinductive(Template)))).

'$lgt_compile_coinductive_directive_resource'(Pred, _) :-
	ground(Pred),
	throw(type_error(predicate_indicator, Pred)).

'$lgt_compile_coinductive_directive_resource'(_, _) :-
	throw(instantiation_error).


% coinductive success is achieved when the current hypothesis is already
% present in the stack of previous hypothesis

'$lgt_check_coinductive_success'(Hypothesis, [Hypothesis| _], Hypothesis).

'$lgt_check_coinductive_success'(TestHead, [_| Stack], Hypothesis) :-
	'$lgt_check_coinductive_success'(TestHead, Stack, Hypothesis).


'$lgt_valid_coinductive_template'(PredicateIndicator, Functor, Arity, Head, Head, Template) :-
	'$lgt_valid_predicate_indicator'(PredicateIndicator, Functor, Arity),
	!,
	functor(Head, Functor, Arity),
	'$lgt_construct_extended_coinductive_template'(Functor, Arity, Template).

'$lgt_valid_coinductive_template'(NonTerminal, Functor, ExtendedArity, Head, Head, Template) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, _, ExtendedArity),
	!,
	functor(Head, Functor, ExtendedArity),
	'$lgt_construct_extended_coinductive_template'(Functor, ExtendedArity, Template).

'$lgt_valid_coinductive_template'(Template, Functor, Arity, Head, TestHead, Template) :-
	'$lgt_check'(callable, Template),
	'$lgt_check'(ground, Template),
	functor(Template, Functor, Arity),
	functor(Head, Functor, Arity),
	Template =.. [Functor| TemplateArgs],
	Head =.. [Functor| HeadArgs],
	'$lgt_map_coinductive_template_args'(TemplateArgs, HeadArgs, TestHeadArgs),
	TestHead =.. [Functor| TestHeadArgs].


% when the argument of the directive is a predicate (or non-terminal) indicator,
% we construct an extended template for the predicate by making all arguments
% relevant for testing for coinductive success

'$lgt_construct_extended_coinductive_template'(Functor, Arity, Template) :-
	functor(Template, Functor, Arity),
	Template =.. [Functor| Args],
	'$lgt_construct_extended_coinductive_template_args'(Args).


'$lgt_construct_extended_coinductive_template_args'([]).

'$lgt_construct_extended_coinductive_template_args'([(+)| Args]) :-
	'$lgt_construct_extended_coinductive_template_args'(Args).


% when only some arguments are relevant for testing for coinductive success,
% we must construct a test head where non-relevant arguments are replaced by
% anonymous variables as these will always unify with any term and thus prevent
% these arguments of causing a failure when checking for coinductive success

'$lgt_map_coinductive_template_args'([], [], []).

'$lgt_map_coinductive_template_args'([(+)| TemplateArgs], [Arg| HeadArgs], [Arg| TestHeadArgs]) :-
	!,
	'$lgt_map_coinductive_template_args'(TemplateArgs, HeadArgs, TestHeadArgs).

'$lgt_map_coinductive_template_args'([(-)| TemplateArgs], [_| HeadArgs], [_| TestHeadArgs]) :-
	'$lgt_map_coinductive_template_args'(TemplateArgs, HeadArgs, TestHeadArgs).


% '$lgt_compile_uses_directive'(Aliases, Aliases, Ctx)
%
% auxiliary predicate for compiling uses/1 directives

'$lgt_compile_uses_directive'([Alias| Aliases], Argument, Ctx) :-
	!,
	'$lgt_compile_uses_directive_alias'(Alias, Argument, Ctx),
	'$lgt_compile_uses_directive'(Aliases, Argument, Ctx).

'$lgt_compile_uses_directive'([], _, _) :-
	!.

'$lgt_compile_uses_directive'(_, Argument, _) :-
	throw(type_error(list, Argument)).


'$lgt_compile_uses_directive_alias'(Obj as Alias, Argument, Ctx) :-
	var(Obj),
	'$lgt_pp_term_source_data_'((:- uses(Argument)), VariableNames, _, _, _),
	'$lgt_member'(VariableName=Variable, VariableNames),
	Obj == Variable,
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
	'$lgt_member'(VariableName-_, ParameterVariablePairs),
	% object argument is a parameter variable
	!,
	'$lgt_check'(object_identifier, Alias),
	(	\+ \+ ('$lgt_pp_object_alias_'(Other, Alias, _, _, _), Obj == Other) ->
		throw(permission_error(repeat, object_alias, Alias))
	;	\+ \+ '$lgt_pp_object_alias_'(_, Alias, _, _, _) ->
		throw(permission_error(modify, object_alias, Alias))
	;	% use a minimal compilation-context to preserve the binding
		% between the parameter variable and the object argument
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_object_alias_'(Obj, Alias, NewCtx, File, Lines))
	).

'$lgt_compile_uses_directive_alias'(Obj as Alias, Argument, Ctx) :-
	!,
	'$lgt_check'(object_identifier, Obj),
	'$lgt_check'(object_identifier, Alias),
	(	\+ \+ '$lgt_pp_object_alias_'(Obj, Alias, _, _, _) ->
		throw(permission_error(repeat, object_alias, Alias))
	;	\+ \+ '$lgt_pp_object_alias_'(_, Alias, _, _, _) ->
		throw(permission_error(modify, object_alias, Alias))
	;	\+ \+ '$lgt_pp_object_alias_'(_, Obj, _, _, _) ->
		throw(permission_error(create, alias_alias, Alias))
	;	'$lgt_variant'(Obj, Alias) ->
		throw(consistency_error(alias_different_from_original, Obj, Alias))
	;	'$lgt_add_referenced_object'(Obj, Ctx),
		'$lgt_source_file_context'(Ctx, File, Lines),
		(	term_variables(Obj, Variables),
			'$lgt_pp_term_source_data_'((:- uses(Argument)), VariableNames, _, _, _),
			'$lgt_member'(VariableName=Variable, VariableNames),
			'$lgt_member_var'(Variable, Variables),
			'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
			'$lgt_member'(VariableName-_, ParameterVariablePairs) ->
			% at least one of the object arguments is a parameter variable;
			% use a minimal compilation-context to preserve the binding
			% between the parameter variable and the object argument
			'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_object_alias_'(Obj, Alias, NewCtx, File, Lines))
		;	assertz('$lgt_pp_object_alias_'(Obj, Alias, _, File, Lines))
		)
	).

'$lgt_compile_uses_directive_alias'(Term, _, _) :-
	throw(type_error(object_alias, Term)).



% '$lgt_compile_uses_directive'(+list, +list, @object_identifier, +boolean, +compilation_context)
%
% auxiliary predicate for compiling uses/2 directives; the boolean flag is true when the
% object argument is or contains parameter variables

'$lgt_compile_uses_directive'([Resource| Resources], Argument, Obj, Flag, Ctx) :-
	!,
	'$lgt_check'(nonvar, Resource),
	'$lgt_compile_uses_directive_resource'(Resource, Obj, Flag, Ctx),
	'$lgt_compile_uses_directive'(Resources, Argument, Obj, Flag, Ctx).

'$lgt_compile_uses_directive'([], _, _, _, _) :-
	!.

'$lgt_compile_uses_directive'(_, Argument, _, _, _) :-
	throw(type_error(list, Argument)).


'$lgt_compile_uses_directive_resource'(op(Priority, Specifier, Operators), _, _, Ctx) :-
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	!,
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_activate_entity_operators'(Priority, Specifier, Operators, l, File, Lines, Mode).

'$lgt_compile_uses_directive_resource'(as(Original,Alias), Obj, Flag, Ctx) :-
	!,
	'$lgt_compile_uses_directive_resource'(Original::Alias, Obj, Flag, Ctx).

'$lgt_compile_uses_directive_resource'(Original::Alias, Obj, Flag, Ctx) :-
	'$lgt_valid_predicate_indicator'(Original, OriginalFunctor, OriginalArity),
	'$lgt_valid_predicate_indicator'(Alias, AliasFunctor, AliasArity),
	!,
	(	Original == Alias ->
		throw(consistency_error(alias_different_from_original, Original, Alias))
	;	OriginalArity =:= AliasArity ->
		'$lgt_compile_uses_directive_predicate_indicator'(OriginalFunctor, AliasFunctor, OriginalArity, Obj, Flag, Ctx)
	;	throw(consistency_error(same_arity, OriginalFunctor/OriginalArity, AliasFunctor/AliasArity))
	).

'$lgt_compile_uses_directive_resource'(Original::Alias, Obj, Flag, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(Original, OriginalFunctor, OriginalArity, ExtendedArity),
	'$lgt_valid_non_terminal_indicator'(Alias, AliasFunctor, AliasArity, _),
	!,
	(	Original == Alias ->
		throw(consistency_error(alias_different_from_original, Original, Alias))
	;	OriginalArity =:= AliasArity ->
		'$lgt_compile_uses_directive_non_terminal_indicator'(OriginalFunctor, AliasFunctor, OriginalArity, ExtendedArity, Obj, Flag, Ctx)
	;	throw(consistency_error(same_arity, OriginalFunctor//OriginalArity, AliasFunctor//AliasArity))
	).

'$lgt_compile_uses_directive_resource'(Original::Alias, Obj, Flag, Ctx) :-
	callable(Original),
	callable(Alias),
	!,
	'$lgt_compile_uses_directive_predicate_call'(Original, Alias, Obj, Flag, Ctx).

'$lgt_compile_uses_directive_resource'(Pred, Obj, Flag, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	'$lgt_compile_uses_directive_predicate_indicator'(Functor, Functor, Arity, Obj, Flag, Ctx).

'$lgt_compile_uses_directive_resource'(NonTerminal, Obj, Flag, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, Arity, ExtArity),
	!,
	'$lgt_compile_uses_directive_non_terminal_indicator'(Functor, Functor, Arity, ExtArity, Obj, Flag, Ctx).

'$lgt_compile_uses_directive_resource'(Resource, _, _, _) :-
	throw(type_error(predicate_indicator, Resource)).


'$lgt_compile_uses_directive_predicate_indicator'(OriginalFunctor, AliasFunctor, Arity, Obj, Flag, Ctx) :-
	functor(Original, OriginalFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity),
	(	'$lgt_pp_uses_predicate_'(Obj, Original, Alias, _, OriginalFile, OriginalLines) ->
		% predicate already listed in another uses/2 directive
		(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
			'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_predicate_reference(File, Lines, Type, Entity, Obj::AliasFunctor/Arity, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	'$lgt_check_predicate_name_conflict'(uses, Alias, AliasFunctor/Arity),
		% unify arguments of TOriginal and TAlias
		Original =.. [_| Args],
		Alias =.. [_| Args],
		% allow for runtime use by adding a local definition that calls the remote definition
		% except when the remote is a built-in predicate in "user" with no alias being defined
		% or a built-in method that would clash with the local definition
		(	Obj == user,
			OriginalFunctor == AliasFunctor,
			'$lgt_predicate_property'(Original, built_in) ->
			% no need for a local definition
			true
		;	% add local definition
			'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
			'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
			'$lgt_compile_clause'((Alias :- Obj::Original), AuxCtx)
		),
		% ensure that this uses/2 directive is found when looking for senders of this message
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_add_referenced_object_message'(Mode, Obj, Original, Alias, Alias),
		(	Flag == true ->
			% shared parameter variables; use a minimal compilation-context to preserve
			% the binding between any parameter variable and the object argument
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_uses_predicate_'(Obj, Original, Alias, NewCtx, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Obj, Original, Alias, NewCtx)))
		;	assertz('$lgt_pp_uses_predicate_'(Obj, Original, Alias, _, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Obj, Original, Alias, _)))
		)
	).


'$lgt_compile_uses_directive_non_terminal_indicator'(OriginalFunctor, AliasFunctor, Arity, ExtArity, Obj, Flag, Ctx) :-
	functor(Original, OriginalFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity),
	(	'$lgt_pp_uses_non_terminal_'(Obj, Original, Alias, _, _, _, OriginalFile, OriginalLines) ->
		% predicate already listed in another uses/2 directive
		(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
			'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_non_terminal_reference(File, Lines, Type, Entity, Obj::AliasFunctor//Arity, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	functor(Pred, OriginalFunctor, ExtArity),
		functor(PredAlias, AliasFunctor, ExtArity),
		'$lgt_check_predicate_name_conflict'(uses, PredAlias, AliasFunctor//Arity),
		% unify arguments of TOriginal and TAlias
		Original =.. [_| Args],
		Alias =.. [_| Args],
		% allow for runtime use by adding a local definition that calls the remote definition
		% except when the remote is a built-in predicate in "user" with no alias being defined
		% or a built-in method that would clash with the local definition
		(	Obj == user,
			OriginalFunctor == AliasFunctor,
			'$lgt_predicate_property'(Pred, built_in) ->
			% no need for a local definition
			true
		;	% add local definition
			'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
			'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
			'$lgt_compile_grammar_rule'((Alias --> Obj::Original), AuxCtx)
		),
		% ensure that the this uses/2 directive is found when looking for senders of this message
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_add_referenced_object_message'(Mode, Obj, Pred, PredAlias, PredAlias),
		(	Flag == true ->
			% shared parameter variables; use a minimal compilation-context to preserve
			% the binding between the parameter variable and the object argument
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_uses_non_terminal_'(Obj, Original, Alias, Pred, PredAlias, NewCtx, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Obj, Pred, PredAlias, NewCtx)))
		;	assertz('$lgt_pp_uses_non_terminal_'(Obj, Original, Alias, Pred, PredAlias, _, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Obj, Pred, PredAlias, _)))
		)
	).


'$lgt_compile_uses_directive_predicate_call'(Original, Alias, Obj, Flag, Ctx) :-
	functor(Alias, AliasFunctor, Arity),
	'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity),
	(	'$lgt_pp_uses_predicate_'(Obj, Original, Alias, _, OriginalFile, OriginalLines) ->
		% predicate already listed in another uses/2 directive
		(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
			'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_predicate_reference(File, Lines, Type, Entity, Obj::AliasFunctor/Arity, OriginalFile, OriginalLines)
			)
		;	true
		)
	;	'$lgt_check_predicate_name_conflict'(uses, Alias, AliasFunctor/Arity),
		% allow for runtime use by adding a local definition that calls the remote definition
		'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
		'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
		(	Obj == user ->
			'$lgt_compile_clause'((Alias :- {Original}), AuxCtx)
		;	'$lgt_compile_clause'((Alias :- Obj::Original), AuxCtx)
		),
		% ensure that this uses/2 directive is found when looking for senders of this message
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_add_referenced_object_message'(Mode, Obj, Original, Alias, Alias),
		(	Flag == true ->
			% shared parameter variables; use a minimal compilation-context to preserve
			% the binding between any parameter variable and the object argument
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_uses_predicate_'(Obj, Original, Alias, NewCtx, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Obj, Original, Alias, NewCtx)))
		;	assertz('$lgt_pp_uses_predicate_'(Obj, Original, Alias, _, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Obj, Original, Alias, _)))
		)
	).



% '$lgt_compile_use_module_directive'(Aliases, Aliases, Ctx)
%
% auxiliary predicate for compiling use_module/1 directives

'$lgt_compile_use_module_directive'([Alias| Aliases], Argument, Ctx) :-
	!,
	'$lgt_compile_use_module_directive_alias'(Alias, Argument, Ctx),
	'$lgt_compile_use_module_directive'(Aliases, Argument, Ctx).

'$lgt_compile_use_module_directive'([], _, _) :-
	!.

'$lgt_compile_use_module_directive'(_, Argument, _) :-
	throw(type_error(list, Argument)).


'$lgt_compile_use_module_directive_alias'(Module as Alias, Argument, Ctx) :-
	var(Module),
	'$lgt_pp_term_source_data_'((:- use_module(Argument)), VariableNames, _, _, _),
	'$lgt_member'(VariableName=Variable, VariableNames),
	Module == Variable,
	'$lgt_pp_parameter_variables_'(ParameterVariablePairs),
	'$lgt_member'(VariableName-_, ParameterVariablePairs),
	% module argument is a parameter variable
	!,
	'$lgt_check'(module_identifier, Alias),
	(	\+ \+ ('$lgt_pp_module_alias_'(Other, Alias, _, _, _), Module == Other) ->
		throw(permission_error(repeat, module_alias, Alias))
	;	\+ \+ '$lgt_pp_module_alias_'(_, Alias, _, _, _) ->
		throw(permission_error(modify, module_alias, Alias))
	;	% use a minimal compilation-context to preserve the binding
		% between the parameter variable and the module argument
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_module_alias_'(Module, Alias, NewCtx, File, Lines))
	).

'$lgt_compile_use_module_directive_alias'(Module as Alias, _, Ctx) :-
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_check'(module_identifier, Alias),
	(	\+ \+ '$lgt_pp_module_alias_'(Module, Alias, _, _, _) ->
		throw(permission_error(repeat, module_alias, Alias))
	;	\+ \+ '$lgt_pp_module_alias_'(_, Alias, _, _, _) ->
		throw(permission_error(modify, module_alias, Alias))
	;	\+ \+ '$lgt_pp_module_alias_'(_, Module, _, _, _) ->
		throw(permission_error(create, module_alias, Alias))
	;	Module == Alias ->
		throw(consistency_error(alias_different_from_original, Module, Alias))
	;	'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_source_file_context'(Ctx, File, Lines),
		assertz('$lgt_pp_module_alias_'(Module, Alias, _, File, Lines))
	).

'$lgt_compile_use_module_directive_alias'(Term, _, _) :-
	throw(type_error(module_alias, Term)).



% '$lgt_compile_use_module_directive'(+list, +list, +atom, +compilation_context)
%
% auxiliary predicate for compiling use_module/2 directives in objects or categories;
% the boolean flag is true when the module argument is a parameter variable

'$lgt_compile_use_module_directive'([Resource| Resources], Argument, Module, Flag, Ctx) :-
	!,
	'$lgt_check'(nonvar, Resource),
	'$lgt_compile_use_module_directive_resource'(Resource, Module, Flag, Ctx),
	'$lgt_compile_use_module_directive'(Resources, Argument, Module, Flag, Ctx).

'$lgt_compile_use_module_directive'([], _, _, _, _) :-
	!.

'$lgt_compile_use_module_directive'(_, Argument, _, _, _) :-
	throw(type_error(list, Argument)).


'$lgt_compile_use_module_directive_resource'(op(Priority, Specifier, Operators), _, _, Ctx) :-
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	!,
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_activate_entity_operators'(Priority, Specifier, Operators, l, File, Lines, Mode).

'$lgt_compile_use_module_directive_resource'(as(Original, Alias), Module, Flag, Ctx) :-
	!,
	'$lgt_compile_use_module_directive_resource'(':'(Original, Alias), Module, Flag, Ctx).

'$lgt_compile_use_module_directive_resource'(':'(Original, Alias), Module, Flag, Ctx) :-
	'$lgt_valid_predicate_indicator'(Original, OriginalFunctor, OriginalArity),
	'$lgt_valid_predicate_indicator'(Alias, AliasFunctor, AliasArity),
	!,
	(	Original == Alias ->
		throw(consistency_error(alias_different_from_original, Original, Alias))
	;	OriginalArity =:= AliasArity ->
		'$lgt_compile_use_module_directive_predicate_indicator'(OriginalFunctor, AliasFunctor, OriginalArity, Module, Flag, Ctx)
	;	throw(consistency_error(same_arity, OriginalFunctor/OriginalArity, AliasFunctor/AliasArity))
	).

'$lgt_compile_use_module_directive_resource'(':'(Original, Alias), Module, Flag, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(Original, OriginalFunctor, OriginalArity, ExtendedArity),
	'$lgt_valid_non_terminal_indicator'(Alias, AliasFunctor, AliasArity, _),
	!,
	(	Original == Alias ->
		throw(consistency_error(alias_different_from_original, Original, Alias))
	;	OriginalArity =:= AliasArity ->
		'$lgt_compile_use_module_directive_non_terminal_indicator'(OriginalFunctor, AliasFunctor, OriginalArity, ExtendedArity, Module, Flag, Ctx)
	;	throw(consistency_error(same_arity, OriginalFunctor//OriginalArity, AliasFunctor//AliasArity))
	).

'$lgt_compile_use_module_directive_resource'(':'(Original, Alias), Module, Flag, Ctx) :-
	callable(Original),
	callable(Alias),
	!,
	'$lgt_compile_use_module_directive_predicate_call'(Original, Alias, Module, Flag, Ctx).

'$lgt_compile_use_module_directive_resource'(Pred, Module, Flag, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	'$lgt_compile_use_module_directive_predicate_indicator'(Functor, Functor, Arity, Module, Flag, Ctx).

'$lgt_compile_use_module_directive_resource'(NonTerminal, Module, Flag, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, Arity, ExtArity),
	!,
	'$lgt_compile_use_module_directive_non_terminal_indicator'(Functor, Functor, Arity, ExtArity, Module, Flag, Ctx).

'$lgt_compile_use_module_directive_resource'(Resource, _, _, _) :-
	throw(type_error(predicate_indicator, Resource)).


'$lgt_compile_use_module_directive_predicate_indicator'(OriginalFunctor, AliasFunctor, Arity, Module, Flag, Ctx) :-
	functor(Original, OriginalFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity),
	(	'$lgt_pp_use_module_predicate_'(Module, Original, Alias, _, OriginalFile, OriginalLines) ->
		% predicate already listed in another uses/2 directive
		(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
			'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_predicate_reference(File, Lines, Type, Entity, ':'(Module, AliasFunctor/Arity), OriginalFile, OriginalLines)
			)
		;	true
		)
	;	'$lgt_check_predicate_name_conflict'(use_module, Alias, AliasFunctor/Arity),
		% unify arguments of TOriginal and TAlias
		Original =.. [_| Args],
		Alias =.. [_| Args],
		% allow for runtime use by adding a local definition that calls the remote definition
		% except when the remote is a built-in predicate in "user" with no alias being defined
		% or a built-in method that would clash with the local definition
		(	Module == user,
			OriginalFunctor == AliasFunctor,
			'$lgt_predicate_property'(Original, built_in) ->
			% no need for a local definition
			true
		;	% add local definition
			'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
			'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
			'$lgt_compile_clause'((Alias :- ':'(Module,Original)), AuxCtx)
		),
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		% ensure that this use_module/2 directive is found when looking for callers of this module predicate
		'$lgt_add_referenced_module_predicate'(Mode, Module, Original, Alias, Alias),
		(	Flag == true ->
			% parameter variable; use a minimal compilation-context to preserve
			% the binding between the parameter variable and the module argument
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_use_module_predicate_'(Module, Original, Alias, NewCtx, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_use_module_predicate_'(Entity, Module, Original, Alias, NewCtx)))
		;	assertz('$lgt_pp_use_module_predicate_'(Module, Original, Alias, _, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_use_module_predicate_'(Entity, Module, Original, Alias, _)))
		)
	).


'$lgt_compile_use_module_directive_non_terminal_indicator'(OriginalFunctor, AliasFunctor, Arity, ExtArity, Module, Flag, Ctx) :-
	functor(Original, OriginalFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity),
	(	'$lgt_pp_use_module_non_terminal_'(Module, Original, Alias, _, _, _, OriginalFile, OriginalLines) ->
		% predicate already listed in another uses/2 directive
		(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
			'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_non_terminal_reference(File, Lines, Type, Entity, ':'(Module, AliasFunctor//Arity), OriginalFile, OriginalLines)
			)
		;	true
		)
	;	functor(Pred, AliasFunctor, ExtArity),
		functor(PredAlias, AliasFunctor, ExtArity),
		'$lgt_check_predicate_name_conflict'(use_module, PredAlias, AliasFunctor//Arity),
		% unify arguments of TOriginal and TAlias
		Original =.. [_| Args],
		Alias =.. [_| Args],
		% allow for runtime use by adding a local definition that calls the remote definition
		% except when the remote is a built-in predicate in "user" with no alias being defined
		% or a built-in method that would clash with the local definition
		(	Module == user,
			OriginalFunctor == AliasFunctor,
			'$lgt_predicate_property'(Pred, built_in) ->
			% no need for a local definition
			true
		;	% add local definition
			'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
			'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
			'$lgt_compile_grammar_rule'((Alias --> ':'(Module,Original)), AuxCtx)
		),
		% ensure that the this use_module/2 directive is found when looking for callers of this module non-terminal
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_add_referenced_module_predicate'(Mode, Module, Pred, PredAlias, PredAlias),
		(	Flag == true ->
			% parameter variable; use a minimal compilation-context to preserve
			% the binding between the parameter variable and the object argument
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_use_module_non_terminal_'(Module, Original, Alias, Pred, PredAlias, NewCtx, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Module, Pred, PredAlias, NewCtx)))
		;	assertz('$lgt_pp_use_module_non_terminal_'(Module, Original, Alias, Pred, PredAlias, _, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_uses_predicate_'(Entity, Module, Pred, PredAlias, _)))
		)
	).


'$lgt_compile_use_module_directive_predicate_call'(Original, Alias, Module, Flag, Ctx) :-
	functor(Alias, AliasFunctor, Arity),
	'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity),
	(	'$lgt_pp_use_module_predicate_'(Module, Original, Alias, _, OriginalFile, OriginalLines) ->
		% predicate already listed in another uses/2 directive
		(	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
			'$lgt_compiler_flag'(duplicated_directives, warning) ->
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(duplicated_directives),
				duplicated_predicate_reference(File, Lines, Type, Entity, ':'(Module, AliasFunctor/Arity), OriginalFile, OriginalLines)
			)
		;	true
		)
	;	'$lgt_check_predicate_name_conflict'(use_module, Alias, AliasFunctor/Arity),
		% allow for runtime use by adding a local definition that calls the remote definition
		'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
		'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
		(	Module == user ->
			'$lgt_compile_clause'((Alias :- {Original}), AuxCtx)
		;	'$lgt_compile_clause'((Alias :- ':'(Module,Original)), AuxCtx)
		),
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		% ensure that this use_module/2 directive is found when looking for callers of this module predicate
		'$lgt_add_referenced_module_predicate'(Mode, Module, Original, Alias, Alias),
		(	Flag == true ->
			% parameter variable; use a minimal compilation-context to preserve
			% the binding between the parameter variable and the module argument
			'$lgt_comp_ctx_exec_ctx'(NewCtx, ExCtx),
			assertz('$lgt_pp_use_module_predicate_'(Module, Original, Alias, NewCtx, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_use_module_predicate_'(Entity, Module, Original, Alias, NewCtx)))
		;	assertz('$lgt_pp_use_module_predicate_'(Module, Original, Alias, _, File, Lines)),
			assertz('$lgt_pp_runtime_clause_'('$lgt_use_module_predicate_'(Entity, Module, Original, Alias, _)))
		)
	).


% auxiliary predicate for checking predicate name conflicts between
% predicates listed in uses/2, use_module/2, and dynamic/1 directives

'$lgt_check_predicate_name_conflict'(Directive, Alias, Culprit) :-
	(	'$lgt_built_in_method'(Alias, _, _, _) ->
		% clash with a built-in method, which cannot be redefined
		throw(permission_error(modify, built_in_method, Culprit))
	;	'$lgt_pp_uses_predicate_'(Obj, _, Alias, _, _, _) ->
		% clash with an earlier uses/2 directive predicate
		throw(permission_error(modify, uses_object_predicate, Obj::Culprit))
	;	'$lgt_pp_uses_non_terminal_'(Obj, _, _, _, Alias, _, _, _) ->
		% clash with an earlier uses/2 directive non-terminal
		throw(permission_error(modify, uses_object_non_terminal, Obj::Culprit))
	;	'$lgt_pp_use_module_predicate_'(Module, _, Alias, _, _, _) ->
		% clash with an earlier use_module/2 directive predicate
		throw(permission_error(modify, uses_module_predicate, ':'(Module,Culprit)))
	;	'$lgt_pp_use_module_non_terminal_'(Module, _, _, _, Alias, _, _, _) ->
		% clash with an earlier use_module/2 directive non-terminal
		throw(permission_error(modify, uses_module_non_terminal, ':'(Module,Culprit)))
	;	Directive \== (dynamic), '$lgt_pp_dynamic_'(Alias, _, _, _) ->
		% clash with an earlier dynamic/1 directive (but allow duplicated dynamic/1 directives)
		throw(permission_error(modify, dynamic_predicate, Culprit))
	;	true
	).



% '$lgt_compile_reexport_directive'(+list, +atom, +compilation_context)
%
% auxiliary predicate for compiling module reexport/2 directives;
% the predicate renaming operator as/2 found on SWI-Prolog and YAP
% is also supported (iff we're compiling a module as an object)

'$lgt_compile_reexport_directive'([], _, _).

'$lgt_compile_reexport_directive'([Resource| Resources], Module, Ctx) :-
	'$lgt_compile_reexport_directive_resource'(Resource, Module, Ctx),
	'$lgt_compile_reexport_directive'(Resources, Module, Ctx).


'$lgt_compile_reexport_directive_resource'(op(Priority, Specifier, Operators), _, Ctx) :-
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	!,
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_activate_entity_operators'(Priority, Specifier, Operators, l, File, Lines, Mode).

'$lgt_compile_reexport_directive_resource'(as(Original, Alias), Module, Ctx) :-
	!,
	'$lgt_compile_reexport_directive_resource'(':'(Original, Alias), Module, Ctx).

'$lgt_compile_reexport_directive_resource'(':'(Original, Alias), Module, Ctx) :-
	'$lgt_valid_predicate_indicator'(Original, OriginalFunctor, Arity),
	'$lgt_valid_predicate_indicator'(Alias, AliasFunctor, Arity),
	!,
	'$lgt_compile_logtalk_directive'(public(AliasFunctor/Arity), Ctx),
	functor(OriginalHead, OriginalFunctor, Arity),
	functor(AliasHead, AliasFunctor, Arity),
	% unify arguments of original and alias
	OriginalHead =.. [_| Args],
	AliasHead =.. [_| Args],
	% add local definition
	'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
	'$lgt_comp_ctx'(AuxCtxOriginal, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
	'$lgt_comp_ctx'(AuxCtxAlias, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
	'$lgt_compile_clause'((OriginalHead :- Module::OriginalHead), AuxCtxOriginal),
	'$lgt_compile_clause'((AliasHead :- Module::OriginalHead), AuxCtxAlias).

'$lgt_compile_reexport_directive_resource'(':'(Original, Alias), Module, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(Original, OriginalFunctor, Arity, _),
	'$lgt_valid_predicate_indicator'(Alias, AliasFunctor, Arity),
	!,
	'$lgt_compile_logtalk_directive'(public(AliasFunctor//Arity), Ctx),
	functor(OriginalHead, OriginalFunctor, Arity),
	functor(AliasHead, AliasFunctor, Arity),
	% unify arguments of original and alias
	OriginalHead =.. [_| Args],
	AliasHead =.. [_| Args],
	% add local definition
	'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
	'$lgt_comp_ctx'(AuxCtxOriginal, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
	'$lgt_comp_ctx'(AuxCtxAlias, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
	'$lgt_compile_grammar_rule'((OriginalHead --> Module::OriginalHead), AuxCtxOriginal),
	'$lgt_compile_grammar_rule'((AliasHead --> Module::OriginalHead), AuxCtxAlias).

'$lgt_compile_reexport_directive_resource'(Pred, Module, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, Functor, Arity),
	!,
	'$lgt_compile_logtalk_directive'(public(Pred), Ctx),
	functor(Head, Functor, Arity),
	% add local definition
	'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
	'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
	'$lgt_compile_clause'((Head :- Module::Head), AuxCtx).

'$lgt_compile_reexport_directive_resource'(NonTerminal, Module, Ctx) :-
	'$lgt_valid_non_terminal_indicator'(NonTerminal, Functor, Arity, _),
	!,
	'$lgt_compile_logtalk_directive'(public(NonTerminal), Ctx),
	functor(Head, Functor, Arity),
	% add local definition
	'$lgt_comp_ctx'(Ctx,    _, _, _, _, _, _, Prefix, _, _, ExCtx, _,                _, Lines, _),
	'$lgt_comp_ctx'(AuxCtx, _, _, _, _, _, _, Prefix, _, _, ExCtx, compile(aux,_,_), _, Lines, _),
	'$lgt_compile_grammar_rule'((Head --> Module::Head), AuxCtx).

'$lgt_compile_reexport_directive_resource'(Resource, _, _) :-
	ground(Resource),
	throw(type_error(predicate_indicator, Resource)).

'$lgt_compile_reexport_directive_resource'(_, _, _) :-
	throw(instantiation_error).



% auxiliary predicate for compiling module's meta predicate directives
% into Logtalk ones by translating the meta-argument specifiers

'$lgt_compile_module_meta_predicate_directive'([Template| Templates], [ConvertedTemplate| ConvertedTemplates]) :-
	'$lgt_compile_module_meta_predicate_directive_template'(Template, ConvertedTemplate),
	'$lgt_compile_module_meta_predicate_directive'(Templates, ConvertedTemplates).

'$lgt_compile_module_meta_predicate_directive'([], []).


'$lgt_compile_module_meta_predicate_directive_template'(':'(Module,Template), ':'(Module,ConvertedTemplate)) :-
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_compile_module_meta_predicate_directive_template'(Template, ConvertedTemplate).

'$lgt_compile_module_meta_predicate_directive_template'(Template, ConvertedTemplate) :-
	'$lgt_check'(callable, Template),
	Template =.. [Functor| Args],
	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(Args, ConvertedArgs),
	ConvertedTemplate =.. [Functor| ConvertedArgs].



% '$lgt_check_for_duplicated_directive'(@callable, @callable)

'$lgt_check_for_duplicated_directive'(TDirective, Directive) :-
	(	'$lgt_pp_directive_'(TDirective),
		'$lgt_compiler_flag'(duplicated_directives, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(duplicated_directives),
			duplicated_directive(File, Lines, Type, Entity, Directive)
		)
	;	true
	).



% auxiliary predicate for translating Prolog dialect meta-argument
% predicate specifiers into Logtalk specifiers

'$lgt_prolog_to_logtalk_meta_argument_specifiers'([], []).

'$lgt_prolog_to_logtalk_meta_argument_specifiers'([Arg| Args], [TArg| TArgs]) :-
	(	\+ ground(Arg) ->
		throw(instantiation_error)
	;	'$lgt_prolog_to_logtalk_meta_argument_specifier_hook'(Arg, TArg) ->
		true
	;	'$lgt_prolog_to_logtalk_meta_argument_specifier'(Arg, TArg) ->
		true
	;	throw(domain_error(meta_argument_specifier, Arg))
	),
	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(Args, TArgs).


% goals and closures are denoted by integers >= 0
'$lgt_prolog_to_logtalk_meta_argument_specifier'(N, N) :-
	integer(N),
	!.
% Prolog to Logtalk notation; this is fragile due to the lack of standardization
'$lgt_prolog_to_logtalk_meta_argument_specifier'((:), (::)).
% mixed-up notation or overriding meta-predicate template being used
'$lgt_prolog_to_logtalk_meta_argument_specifier'((::), (::)).
% predicate indicator
'$lgt_prolog_to_logtalk_meta_argument_specifier'((/), (/)).
% non-terminal indicator
'$lgt_prolog_to_logtalk_meta_argument_specifier'((//), (//)).
% list of goals/closures
'$lgt_prolog_to_logtalk_meta_argument_specifier'([N], [N]) :-
	integer(N),
	!.
% list of predicate indicators
'$lgt_prolog_to_logtalk_meta_argument_specifier'([/], [/]) :-
	!.
% list of non-terminal indicators
'$lgt_prolog_to_logtalk_meta_argument_specifier'([//], [//]) :-
	!.
% goal with possible existential variables qualification
'$lgt_prolog_to_logtalk_meta_argument_specifier'((^), (^)).
% instantiation modes (non meta-arguments)
'$lgt_prolog_to_logtalk_meta_argument_specifier'((@), (*)).
'$lgt_prolog_to_logtalk_meta_argument_specifier'((+), (*)).
'$lgt_prolog_to_logtalk_meta_argument_specifier'((-), (*)).
'$lgt_prolog_to_logtalk_meta_argument_specifier'((?), (*)).
% non meta-arguments
'$lgt_prolog_to_logtalk_meta_argument_specifier'((*), (*)).



% '$lgt_compile_object_relations'(@list(term), @object_identifier, @compilation_context)
%
% compiles the relations of an object with other entities

'$lgt_compile_object_relations'([Relation| Relations], Obj, Ctx) :-
	(	var(Relation) ->
		throw(instantiation_error)
	;	'$lgt_compile_object_relation'(Relation, Obj, Ctx) ->
		true
	;	callable(Relation) ->
		functor(Relation, Functor, Arity),
		throw(domain_error(object_relation, Functor/Arity))
	;	throw(type_error(callable, Relation))
	),
	'$lgt_compile_object_relations'(Relations, Obj, Ctx).

'$lgt_compile_object_relations'([], _, _).



% '$lgt_compile_object_relation'(@nonvar, @object_identifier, @compilation_context)
%
% compiles a relation between an object (the last argument) with other entities

'$lgt_compile_object_relation'(implements(_), _, _) :-
	'$lgt_pp_implemented_protocol_'(_, _, _, _, _),
	throw(permission_error(repeat, entity_relation, implements/1)).

'$lgt_compile_object_relation'(implements(Ptcs), Obj, Ctx) :-
	'$lgt_flatten_to_list'(Ptcs, FlattenedPtcs),
	'$lgt_compile_implements_protocol_relation'(FlattenedPtcs, Obj, Ctx).


'$lgt_compile_object_relation'(imports(_), _, _) :-
	'$lgt_pp_imported_category_'(_, _, _, _, _, _),
	throw(permission_error(repeat, entity_relation, imports/1)).

'$lgt_compile_object_relation'(imports(Ctgs), Obj, Ctx) :-
	'$lgt_flatten_to_list'(Ctgs, FlattenedCtgs),
	'$lgt_compile_imports_category_relation'(FlattenedCtgs, Obj, Ctx).


'$lgt_compile_object_relation'(instantiates(_), _, _) :-
	'$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(permission_error(repeat, entity_relation, instantiates/1)).

'$lgt_compile_object_relation'(instantiates(Classes), Instance, Ctx) :-
	'$lgt_flatten_to_list'(Classes, FlattenedClasses),
	'$lgt_compile_instantiates_class_relation'(FlattenedClasses, Instance, Ctx).


'$lgt_compile_object_relation'(specializes(_), _, _) :-
	'$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(permission_error(repeat, entity_relation, specializes/1)).

'$lgt_compile_object_relation'(specializes(Superclasses), Class, Ctx) :-
	'$lgt_flatten_to_list'(Superclasses, FlattenedSuperclasses),
	'$lgt_compile_specializes_class_relation'(FlattenedSuperclasses, Class, Ctx).


'$lgt_compile_object_relation'(extends(_), _, _) :-
	'$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(permission_error(repeat, entity_relation, extends/1)).

'$lgt_compile_object_relation'(extends(Parents), Prototype, Ctx) :-
	'$lgt_flatten_to_list'(Parents, FlattenedParents),
	'$lgt_compile_extends_object_relation'(FlattenedParents, Prototype, Ctx).



% '$lgt_compile_protocol_relations'(@list(term), @protocol_identifier, @compilation_context)
%
% compiles the relations of a protocol with other entities

'$lgt_compile_protocol_relations'([Relation| Relations], Ptc, Ctx) :-
	(	var(Relation) ->
		throw(instantiation_error)
	;	'$lgt_compile_protocol_relation'(Relation, Ptc, Ctx) ->
		true
	;	callable(Relation) ->
		functor(Relation, Functor, Arity),
		throw(domain_error(protocol_relation, Functor/Arity))
	;	throw(type_error(callable, Relation))
	),
	'$lgt_compile_protocol_relations'(Relations, Ptc, Ctx).

'$lgt_compile_protocol_relations'([], _, _).



% '$lgt_compile_protocol_relation'(@nonvar, @protocol_identifier, @compilation_context)
%
% compiles a relation between a protocol (the last argument) with other entities

'$lgt_compile_protocol_relation'(extends(_), _, _) :-
	'$lgt_pp_extended_protocol_'(_, _, _, _, _),
	throw(permission_error(repeat, entity_relation, extends/1)).

'$lgt_compile_protocol_relation'(extends(Ptcs), Ptc, Ctx) :-
	'$lgt_flatten_to_list'(Ptcs, FlattenedPtcs),
	'$lgt_compile_extends_protocol_relation'(FlattenedPtcs, Ptc, Ctx).



% '$lgt_compile_category_relations'(@list(term), @category_identifier, @compilation_context)
%
% compiles the relations of a category with other entities

'$lgt_compile_category_relations'([Relation| Relations], Ctg, Ctx) :-
	(	var(Relation) ->
		throw(instantiation_error)
	;	'$lgt_compile_category_relation'(Relation, Ctg, Ctx) ->
		true
	;	callable(Relation) ->
		functor(Relation, Functor, Arity),
		throw(domain_error(category_relation, Functor/Arity))
	;	throw(type_error(callable, Relation))
	),
	'$lgt_compile_category_relations'(Relations, Ctg, Ctx).

'$lgt_compile_category_relations'([], _, _).



% '$lgt_compile_category_relation'(@nonvar, @category_identifier, @compilation_context)
%
% compiles a relation between a category (the last argument) with other entities

'$lgt_compile_category_relation'(implements(_), _, _) :-
	'$lgt_pp_implemented_protocol_'(_, _, _, _, _),
	throw(permission_error(repeat, entity_relation, implements/1)).

'$lgt_compile_category_relation'(implements(Ptcs), Ctg, Ctx) :-
	'$lgt_flatten_to_list'(Ptcs, FlattenedPtcs),
	'$lgt_compile_implements_protocol_relation'(FlattenedPtcs, Ctg, Ctx).


'$lgt_compile_category_relation'(extends(_), _, _) :-
	'$lgt_pp_extended_category_'(_, _, _, _, _, _),
	throw(permission_error(repeat, entity_relation, extends/1)).

'$lgt_compile_category_relation'(extends(Ctgs), Ctg, Ctx) :-
	'$lgt_flatten_to_list'(Ctgs, FlattenedCtgs),
	'$lgt_compile_extends_category_relation'(FlattenedCtgs, Ctg, Ctx).


'$lgt_compile_category_relation'(complements(_), _, _) :-
	'$lgt_pp_complemented_object_'(_, _, _, _, _),
	throw(permission_error(repeat, entity_relation, complements/1)).

'$lgt_compile_category_relation'(complements(Objs), Ctg, Ctx) :-
	'$lgt_flatten_to_list'(Objs, FlattenedObjs),
	'$lgt_compile_complements_object_relation'(FlattenedObjs, Ctg, Ctx).



% '$lgt_compile_entity_info_directive'(@list(term), -list(pair))
%
% compiles the entity info/1 directive key-value pairs

'$lgt_compile_entity_info_directive'([Pair| Pairs], [TPair| TPairs]) :-
	(	'$lgt_valid_info_key_value_pair'(Pair, Key, Value) ->
		'$lgt_compile_entity_info_directive_pair'(Key, Value, TPair),
		'$lgt_compile_entity_info_directive'(Pairs, TPairs)
	;	% non-valid pair; generate an error
		'$lgt_check'(key_value_info_pair, Pair)
	).

'$lgt_compile_entity_info_directive'([], []).



% '$lgt_compile_entity_info_directive_pair'(+atom, @nonvar, -compound)
%
% compiles an entity info/1 directive key-value pair

'$lgt_compile_entity_info_directive_pair'(author, Author, author(Author)) :-
	!,
	(	Author = {EntityName}, atom(EntityName) ->
		true
	;	'$lgt_check'(atom_or_string, Author)
	).

'$lgt_compile_entity_info_directive_pair'(comment, Comment, comment(Comment)) :-
	!,
	'$lgt_check'(atom_or_string, Comment).

'$lgt_compile_entity_info_directive_pair'(date, Date, date(Date)) :-
	!,
	(	Date = Year-Month-Day ->
		% ISO 8601 standard format
		'$lgt_check'(non_negative_integer, Year),
		'$lgt_check'(non_negative_integer, Month),
		'$lgt_check'(non_negative_integer, Day)
	;	Date = Year/Month/Day ->
		% deprecated format
		'$lgt_check'(non_negative_integer, Year),
		'$lgt_check'(non_negative_integer, Month),
		'$lgt_check'(non_negative_integer, Day)
	;	throw(type_error(date, Date))
	).

'$lgt_compile_entity_info_directive_pair'(parameters, Parameters, parameters(Parameters)) :-
	!,
	'$lgt_pp_entity_'(_, Entity, _),
	functor(Entity, _, Arity),
	'$lgt_check_entity_info_parameters'(Parameters, Entity, Parameters, 0, Arity).

'$lgt_compile_entity_info_directive_pair'(parnames, Parnames, parnames(Parnames)) :-
	!,
	'$lgt_pp_entity_'(_, Entity, _),
	functor(Entity, _, Arity),
	'$lgt_check_entity_info_parnames'(Parnames, Entity, Parnames, 0, Arity).

'$lgt_compile_entity_info_directive_pair'(remarks, Remarks, remarks(Remarks)) :-
	!,
	'$lgt_check'(list, Remarks),
	(	'$lgt_member'(Remark, Remarks), \+ '$lgt_valid_remark'(Remark) ->
		throw(type_error(remark, Remark))
	;	true
	).

'$lgt_compile_entity_info_directive_pair'(see_also, References, see_also(References)) :-
	!,
	'$lgt_check'(list(entity_identifier), References).

'$lgt_compile_entity_info_directive_pair'(version, Version, version(Version)) :-
	!,
	(	Version = ':'(Major, ':'(Minor, Patch)) ->
		'$lgt_check'(non_negative_integer, Major),
		'$lgt_check'(non_negative_integer, Minor),
		'$lgt_check'(non_negative_integer, Patch)
	;	% deprecated format
		'$lgt_check'(atomic_or_string, Version)
	).

'$lgt_compile_entity_info_directive_pair'(copyright, Copyright, copyright(Copyright)) :-
	!,
	(	Copyright = {EntityName}, atom(EntityName) ->
		true
	;	'$lgt_check'(atom_or_string, Copyright)
	).

'$lgt_compile_entity_info_directive_pair'(license, License, license(License)) :-
	!,
	(	License = {EntityName}, atom(EntityName) ->
		true
	;	'$lgt_check'(atom_or_string, License)
	).

% user-defined entity info pair; no checking
'$lgt_compile_entity_info_directive_pair'(Key, Value, TPair) :-
	TPair =.. [Key, Value].


'$lgt_check_entity_info_parameters'([Pair| Pairs], Entity, Parameters, Counter0, Arity) :-
	!,
	(	Pair = Name - Description ->
		'$lgt_check'(atom_or_string, Name),
		'$lgt_check'(atom_or_string, Description),
		Counter1 is Counter0 + 1,
		'$lgt_check_entity_info_parameters'(Pairs, Entity, Parameters, Counter1, Arity)
	;	throw(type_error(pair, Pair))
	).

'$lgt_check_entity_info_parameters'([], Entity, Parameters, Counter, Arity) :-
	!,
	(	Counter =:= Arity ->
		true
	;	throw(consistency_error(same_number_of_parameters, Entity, Parameters))
	).

'$lgt_check_entity_info_parameters'(_, _, Parameters, _, _) :-
	throw(type_error(list, Parameters)).


'$lgt_check_entity_info_parnames'([Name| Names], Entity, Parnames, Counter0, Arity) :-
	!,
	'$lgt_check'(atom_or_string, Name),
	Counter1 is Counter0 + 1,
	'$lgt_check_entity_info_parnames'(Names, Entity, Parnames, Counter1, Arity).

'$lgt_check_entity_info_parnames'([], Entity, Parnames, Counter, Arity) :-
	!,
	(	Counter =:= Arity ->
		true
	;	throw(consistency_error(same_number_of_parameters, Entity, Parnames))
	).

'$lgt_check_entity_info_parnames'(_, _, Parnames, _, _) :-
	throw(type_error(list, Parnames)).



% '$lgt_compile_predicate_info_directive'(@list(term), +atom, +integer, -list(pair))
%
% compiles the predicate info/2 directive key-value pairs

'$lgt_compile_predicate_info_directive'([Pair| Pairs], Functor, Arity, [TPair| TPairs]) :-
	(	'$lgt_valid_info_key_value_pair'(Pair, Key, Value) ->
		'$lgt_compile_predicate_info_directive_pair'(Key, Value, Functor, Arity, TPair),
		'$lgt_compile_predicate_info_directive'(Pairs, Functor, Arity, TPairs)
	;	% non-valid pair; generate an error
		'$lgt_check'(key_value_info_pair, Pair)
	).

'$lgt_compile_predicate_info_directive'([], _, _, []).



% '$lgt_compile_predicate_info_directive_pair'(+atom, @nonvar, +atom, +integer, -compound)
%
% compiles a predicate info/2 directive key-value pair

'$lgt_compile_predicate_info_directive_pair'(allocation, Allocation, _, _, allocation(Allocation)) :-
	!,
	'$lgt_check'(atom, Allocation),
	(	'$lgt_valid_predicate_allocation'(Allocation) ->
		true
	;	throw(domain_error(allocation, Allocation))
	).

'$lgt_compile_predicate_info_directive_pair'(arguments, Arguments, Functor, Arity, arguments(Arguments)) :-
	!,
	'$lgt_check_predicate_info_arguments'(Arguments, Arguments, 0, Functor, Arity).

'$lgt_compile_predicate_info_directive_pair'(argnames, Argnames, Functor, Arity, argnames(Argnames)) :-
	!,
	'$lgt_check_predicate_info_argnames'(Argnames, Argnames, 0, Functor, Arity).

'$lgt_compile_predicate_info_directive_pair'(comment, Comment, _, _, comment(Comment)) :-
	!,
	'$lgt_check'(atom_or_string, Comment).

'$lgt_compile_predicate_info_directive_pair'(fails_if, FailsIf, _, _, fails_if(FailsIf)) :-
	!,
	'$lgt_check'(atom_or_string, FailsIf).

'$lgt_compile_predicate_info_directive_pair'(exceptions, Exceptions, _, _, exceptions(Exceptions)) :-
	!,
	'$lgt_check'(list, Exceptions),
	(	'$lgt_member'(Exception, Exceptions), \+ '$lgt_valid_predicate_exception'(Exception) ->
		throw(type_error(exception, Exception))
	;	true
	).

'$lgt_compile_predicate_info_directive_pair'(remarks, Remarks, _, _, remarks(Remarks)) :-
	!,
	'$lgt_check'(list, Remarks),
	(	'$lgt_member'(Remark, Remarks), \+ '$lgt_valid_remark'(Remark) ->
		throw(type_error(remark, Remark))
	;	true
	).

'$lgt_compile_predicate_info_directive_pair'(examples, Examples, Functor, Arity, examples(Examples)) :-
	!,
	'$lgt_check'(list, Examples),
	(	'$lgt_member'(Example, Examples), \+ '$lgt_valid_predicate_call_example'(Example, Functor, Arity) ->
		throw(type_error(example, Example))
	;	true
	).

'$lgt_compile_predicate_info_directive_pair'(redefinition, Redefinition, _, _, redefinition(Redefinition)) :-
	!,
	'$lgt_check'(atom, Redefinition),
	(	'$lgt_valid_predicate_redefinition'(Redefinition) ->
		true
	;	throw(domain_error(redefinition, Redefinition))
	).

'$lgt_compile_predicate_info_directive_pair'(see_also, References, _, _, see_also(References)) :-
	!,
	'$lgt_check'(list(predicate_or_non_terminal_indicator), References).

'$lgt_compile_predicate_info_directive_pair'(since, Version, _, _, since(Version)) :-
	!,
	(	Version = ':'(Major, ':'(Minor, Patch)) ->
		'$lgt_check'(non_negative_integer, Major),
		'$lgt_check'(non_negative_integer, Minor),
		'$lgt_check'(non_negative_integer, Patch)
	;	throw(domain_error(since, Version))
	).

% user-defined predicate info pair; no checking
'$lgt_compile_predicate_info_directive_pair'(Key, Value, _, _, TPair) :-
	TPair =.. [Key, Value].


'$lgt_check_predicate_info_arguments'([Pair| Pairs], Arguments, Counter0, Functor, Arity) :-
	!,
	(	Pair = Name - Description ->
		'$lgt_check'(atom_or_string, Name),
		'$lgt_check'(atom_or_string, Description),
		Counter1 is Counter0 + 1,
		'$lgt_check_predicate_info_arguments'(Pairs, Arguments, Counter1, Functor, Arity)
	;	throw(type_error(pair, Pair))
	).

'$lgt_check_predicate_info_arguments'([], Arguments, Counter, Functor, Arity) :-
	!,
	(	Counter =:= Arity ->
		true
	;	throw(consistency_error(same_number_of_arguments, Functor/Arity, Arguments))
	).

'$lgt_check_predicate_info_arguments'(_, Arguments, _, _, _) :-
	throw(type_error(list, Arguments)).


'$lgt_check_predicate_info_argnames'([Name| Names], Argnames, Counter0, Functor, Arity) :-
	!,
	'$lgt_check'(atom_or_string, Name),
	Counter1 is Counter0 + 1,
	'$lgt_check_predicate_info_argnames'(Names, Argnames, Counter1, Functor, Arity).

'$lgt_check_predicate_info_argnames'([], Argnames, Counter, Functor, Arity) :-
	!,
	(	Counter =:= Arity ->
		true
	;	throw(consistency_error(same_number_of_arguments, Functor/Arity, Argnames))
	).

'$lgt_check_predicate_info_argnames'(_, Argnames, _, _, _) :-
	throw(type_error(list, Argnames)).



% '$lgt_compile_grammar_rule'(+grammar_rule, +compilation_context)

'$lgt_compile_grammar_rule'(GrammarRule, Ctx) :-
	catch(
		'$lgt_dcg_rule'(GrammarRule, Clause, Ctx),
		Error,
		throw(error(Error, grammar_rule(GrammarRule)))
	),
	'$lgt_compile_clause'(Clause, Ctx).



% '$lgt_compile_clause'(+clause, +compilation_context)
%
% compiles a source file clause

'$lgt_compile_clause'(Clause, Ctx) :-
	'$lgt_pp_entity_'(Type, Entity, Prefix),
	% compiling an entity clause
	(	Type == protocol ->
		% protocols cannot contain predicate definitions
		throw(error(permission_error(define, predicate, Entity), clause(Clause)))
	;	true
	),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, _, Mode, _, Lines, _),
	catch(
		'$lgt_compile_clause'(Clause, Entity, TClause, DClause, Ctx),
		Error,
		throw(error(Error, clause(Clause)))
	),
	% successful first stage compilation; save the source data information for
	% use in the second compiler stage (where it might be required by calls
	% to the logtalk_load_context/2 predicate during goal expansion or when
	% checking for duplicated clauses)
	(	'$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines) ->
		SourceData = sd(Term, VariableNames, Singletons, File, Lines)
	;	SourceData = nil
	),
	% check which compile clause to save (normal/optimal or debug) and
	% if we have a clause defined by the user or an auxiliary clause
	(	'$lgt_compiler_flag'(debug, on) ->
		(	Mode = compile(aux,_,_) ->
			assertz('$lgt_pp_entity_aux_clause_'(DClause))
		;	assertz('$lgt_pp_entity_term_'(DClause, SourceData, Lines))
		)
	;	(	Mode = compile(aux,_,_) ->
			assertz('$lgt_pp_entity_aux_clause_'(TClause))
		;	assertz('$lgt_pp_entity_term_'(TClause, SourceData, Lines))
		)
	),
	!.

'$lgt_compile_clause'(Clause, _) :-
	\+ '$lgt_pp_entity_'(_, _, _),
	% clause occurs before an opening entity directive
	!,
	(	'$lgt_pp_term_source_data_'(_, _, _, _, Lines) ->
		true
	;	Lines = '-'(-1, -1)
	),
	% copy the clause unchanged to the generated Prolog file
	assertz('$lgt_pp_prolog_term_'(Clause, Lines)).

'$lgt_compile_clause'(Clause, _) :-
	% deal with unexpected clause translation failures
	throw(error(system_error, clause(Clause))).



% '$lgt_compile_clause'(+clause, +entity_identifier, -clause, -clause, +compilation_context)
%
% compiles an entity clause into a normal clause and a debug clause
%
% in this first compiler stage only the clause heads are compiled, which
% allows collecting information about all entity defined predicates; the
% compilation of clause bodies is delayed to the compiler second stage to
% take advantage of the collected information to notably simplify handling
% of redefined built-in predicates
%
% in the case of a clause rule for a multifile predicate, the clause body
% is compiled in the context of the entity defining the clause; still, any
% calls to the parameter/2 method in the clause body will access parameters
% for the defining entity; parameters for the entity for which the clause
% is defined can be accessed through simple unification at the clause head

'$lgt_compile_clause'((Head :- Body), _, _, _, Ctx) :-
	(	'$lgt_variant'(Body, Head) ->
		true
	;	Body = (Goal, _),
		'$lgt_variant'(Goal, Head)
	),
	'$lgt_comp_ctx_term'(Ctx, Term),
	callable(Term),
	\+ functor(Term, (-->), 2),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(left_recursion, warning),
	\+ '$lgt_pp_coinductive_'(Head, _, _, _, _, _, _, _, _),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(left_recursion),
		left_recursion(File, Lines, Type, Entity, (Head :- Body))
	),
	fail.

'$lgt_compile_clause'((Head:-Body), Entity, TClause, DClause, Ctx) :-
	!,
	'$lgt_check'(callable, Head, clause((Head:-Body))),
	'$lgt_head_meta_variables'(Head, MetaVars),
	'$lgt_comp_ctx'(Ctx, Head, ExCtx, _, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, Mode, Stack, _, Term),
	'$lgt_source_file_context'(Ctx, File, BeginLine-EndLine),
	'$lgt_compile_head'(Head, PI, THead, Ctx),
	(	Head = {UserHead} ->
		% clause for a multifile predicate in "user"
		DHead = '$lgt_debug'(rule(Entity, user::UserHead, N, File, BeginLine), ExCtx),
		'$lgt_comp_ctx'(BodyCtx, Head, ExCtx, _, _, _, _, Prefix, MetaVars, _, BodyExCtx, Mode, _, BeginLine-EndLine, Term),
		'$lgt_execution_context_this_entity'(ExCtx, _, user),
		% ensure that ::/1-2 and ^^/2 calls are compiled in the correct context
		(	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
			'$lgt_execution_context'(BodyExCtx, Entity, Entity, Entity, Entity, [], [])
		;	% category
			'$lgt_execution_context'(BodyExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack)
		)
	;	Head = Other::OtherHead ->
		% clause for an object or category multifile predicate
		DHead = '$lgt_debug'(rule(Entity, Head, N, File, BeginLine), ExCtx),
		'$lgt_comp_ctx'(BodyCtx, Head, ExCtx, _, _, _, _, Prefix, MetaVars, _, BodyExCtx, Mode, _, BeginLine-EndLine, Term),
		term_variables(Other, OtherVars),
		term_variables((OtherHead:-Body), ClauseVars),
		'$lgt_intersection'(OtherVars, ClauseVars, CommonVars),
		(	CommonVars == [] ->
			true
		;	% parametric entity sharing variables with the clause
			'$lgt_execution_context_this_entity'(ExCtx, _, Other)
		),
		% ensure that ::/1-2 and ^^/2 calls are compiled in the correct context
		(	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
			'$lgt_execution_context'(BodyExCtx, Entity, Entity, Entity, Entity, [], [])
		;	% category
			'$lgt_execution_context'(BodyExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack)
		)
	;	Head = ':'(_, _) ->
		% clause for a module multifile predicate
		DHead = '$lgt_debug'(rule(Entity, Head, N, File, BeginLine), ExCtx),
		'$lgt_comp_ctx'(BodyCtx, Head, ExCtx, _, _, _, _, Prefix, MetaVars, _, BodyExCtx, Mode, _, BeginLine-EndLine, Term),
		% ensure that ::/1-2 and ^^/2 calls are compiled in the correct context
		(	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
			'$lgt_execution_context'(BodyExCtx, Entity, Entity, Entity, Entity, [], [])
		;	% category
			'$lgt_execution_context'(BodyExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack)
		)
	;	% clause for a local predicate
		DHead = '$lgt_debug'(rule(Entity, Head, N, File, BeginLine), ExCtx),
		BodyCtx = Ctx
	),
	(	'$lgt_pp_dynamic_'(Head, _, _, _) ->
		TClause = drule(THead, '$lgt_nop'(Body), Body, BodyCtx),
		DClause = ddrule(THead, '$lgt_nop'(Body), DHead, Body, BodyCtx)
	;	TClause = srule(THead, Body, BodyCtx),
		DClause = dsrule(THead, DHead, Body, BodyCtx)
	),
	'$lgt_clause_number'(PI, rule, File, BeginLine-EndLine, N).

'$lgt_compile_clause'(Fact, Entity, fact(TFact,Ctx), dfact(TFact,DHead,Ctx), Ctx) :-
	'$lgt_check'(callable, Fact, clause(Fact)),
	'$lgt_compile_head'(Fact, PI, TFact, Ctx),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_source_file_context'(Ctx, File, BeginLine-EndLine),
	(	Fact = {UserFact} ->
		% fact for a multifile predicate in "user"
		DHead = '$lgt_debug'(fact(Entity, user::UserFact, N, File, BeginLine), ExCtx)
	;	Fact = Other::OtherFact ->
		% fact for an entity multifile predicate
		DHead = '$lgt_debug'(fact(Entity, Fact, N, File, BeginLine), ExCtx),
		term_variables(Other, OtherVars),
		term_variables(OtherFact, OtherFactVars),
		'$lgt_intersection'(OtherVars, OtherFactVars, CommonVars),
		(	CommonVars == [] ->
			true
		;	% parametric entity sharing variables with the fact
			'$lgt_comp_ctx'(Ctx, _, _, Other, _, _, _, _, _, _, ExCtx, _, _, _, _),
			'$lgt_execution_context_this_entity'(ExCtx, _, Other)
		)
	;	Fact = ':'(_, _) ->
		% fact for a module multifile predicate
		DHead = '$lgt_debug'(fact(Entity, Fact, N, File, BeginLine), ExCtx)
	;	var(ExCtx) ->
		% local fact
		DHead = '$lgt_debug'(fact(Entity, Fact, N, File, BeginLine), ExCtx)
	;	% parameter variables shared via the execution context
		'$lgt_unify_head_thead_arguments'(Fact, TFact, ExCtx),
		DHead = '$lgt_debug'(fact(Entity, Fact, N, File, BeginLine), ExCtx)
	),
	'$lgt_clause_number'(PI, fact, File, BeginLine-EndLine, N).



% '$lgt_clause_number'(@callable, +atom, +atom, +integer, -integer)
%
% returns the clause number for a compiled predicate; when the clause is the
% first one for the predicate, we also save the definition line in the source
% file (assuming that we're not compiling a clause for a dynamically created
% entity) for use with the reflection built-in predicates and methods

'$lgt_clause_number'(Other::Functor/Arity, fact, File, Lines, Clauses) :-
	!,
	% object or category multifile predicate
	(	retract('$lgt_pp_number_of_clauses_rules_'(Other, Functor, Arity, Clauses0, Rules)) ->
		Clauses is Clauses0 + 1
	;	% first clause found for this predicate
		Clauses = 1,
		Rules = 0,
		assertz('$lgt_pp_predicate_definition_location_'(Other, Functor, Arity, File, Lines))
	),
	assertz('$lgt_pp_number_of_clauses_rules_'(Other, Functor, Arity, Clauses, Rules)).

'$lgt_clause_number'(Other::Functor/Arity, rule, File, Lines, Clauses) :-
	% object or category multifile predicate
	(	retract('$lgt_pp_number_of_clauses_rules_'(Other, Functor, Arity, Clauses0, Rules0)) ->
		Clauses is Clauses0 + 1,
		Rules is Rules0 + 1
	;	% first clause found for this predicate
		Clauses = 1,
		Rules = 1,
		assertz('$lgt_pp_predicate_definition_location_'(Other, Functor, Arity, File, Lines))
	),
	assertz('$lgt_pp_number_of_clauses_rules_'(Other, Functor, Arity, Clauses, Rules)).

% module multifile predicate clause
'$lgt_clause_number'(':'(_, _), _, _, _, 0).

'$lgt_clause_number'({Head}, Kind, File, Lines, Clauses) :-
	% pre-compiled predicate clause head
	'$lgt_clause_number'(user::Head, Kind, File, Lines, Clauses).

'$lgt_clause_number'(Functor/Arity, fact, File, Lines, Clauses) :-
	!,
	% predicate clause for the entity being compiled
	(	retract('$lgt_pp_number_of_clauses_rules_'(Functor, Arity, Clauses0, Rules)) ->
		Clauses is Clauses0 + 1
	;	% first clause found for this predicate
		Clauses = 1,
		Rules = 0,
		assertz('$lgt_pp_predicate_definition_location_'(Functor, Arity, File, Lines))
	),
	assertz('$lgt_pp_number_of_clauses_rules_'(Functor, Arity, Clauses, Rules)).

'$lgt_clause_number'(Functor/Arity, rule, File, Lines, Clauses) :-
	% predicate clause for the entity being compiled
	(	retract('$lgt_pp_number_of_clauses_rules_'(Functor, Arity, Clauses0, Rules0)) ->
		Clauses is Clauses0 + 1,
		Rules is Rules0 + 1
	;	% first clause found for this predicate
		Clauses = 1,
		Rules = 1,
		assertz('$lgt_pp_predicate_definition_location_'(Functor, Arity, File, Lines))
	),
	assertz('$lgt_pp_number_of_clauses_rules_'(Functor, Arity, Clauses, Rules)).



% '$lgt_compile_head'(+callable, -callable, -callable, +compilation_context)
%
% compiles an entity clause head; also returns a term constructed from the
% head predicate indicator to be used as key to compute the clause number


% pre-compiled clause head (we only check for basic instantiation and type errors)

'$lgt_compile_head'({Head}, {Functor/Arity}, Head, _) :-
	!,
	'$lgt_check'(callable, Head),
	functor(Head, Functor, Arity).

% not the first clause for this predicate; reuse the compiled head template
%
% we must ensure that Mode is the same to prevent that the auxiliary clauses
% created for uses/2 and use_module/2 directives would result in a cached
% template being reused for a conflicting user-defined predicate

'$lgt_compile_head'(Head, Functor/Arity, THead, Ctx) :-
	'$lgt_pp_defines_predicate_'(Head, Functor/Arity, ExCtx, THead, Mode, Origin),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	!,
	% only check for a discontiguous predicate for user-defined predicates
	(	'$lgt_pp_previous_predicate_'(Head, Origin) ->
		true
	;	Origin == aux ->
		true
	;	% clauses for the predicate are discontiguous
		'$lgt_check_discontiguous_directive'(Head, Ctx)
	).

% definition of dynamic predicates inside categories is not allowed

'$lgt_compile_head'(Head, _, _, _) :-
	'$lgt_pp_category_'(_, _, _, _, _, _),
	'$lgt_pp_dynamic_'(Head, _, _, _),
	functor(Head, Functor, Arity),
	throw(permission_error(define, dynamic_predicate, Functor/Arity)).

% redefinition of Logtalk built-in methods is not allowed

'$lgt_compile_head'(Head, _, _, _) :-
	'$lgt_built_in_method'(Head, _, _, Flags),
	Head \= _::_,
	Head \= ':'(_, _),
	% not a clause for a multifile predicate
	Flags /\ 2 =\= 2,
	% not a (user defined) dynamic built-in predicate
	functor(Head, Functor, Arity),
	throw(permission_error(modify, built_in_method, Functor/Arity)).

% conflict with a predicate specified in a uses/2 directive

'$lgt_compile_head'(Alias, _, _, _) :-
	'$lgt_pp_uses_predicate_'(Obj, _, Alias, _, _, _),
	functor(Alias, Functor, Arity),
	throw(permission_error(modify, uses_object_predicate, Obj::Functor/Arity)).

% conflict with a predicate specified in a use_module/2 directive

'$lgt_compile_head'(Alias, _, _, _) :-
	'$lgt_pp_use_module_predicate_'(Module, _, Alias, _, _, _),
	functor(Alias, Functor, Arity),
	throw(permission_error(modify, uses_module_predicate, ':'(Module,Functor/Arity))).

% definition of a reserved predicate without reference to the built-in protocol declaring it

'$lgt_compile_head'(Head, _, _, Ctx) :-
	'$lgt_reserved_predicate_protocol'(Head, Protocol),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(general, warning),
	\+ '$lgt_pp_module_'(_),
	\+ '$lgt_pp_implemented_protocol_'(Protocol, _, _, _, _),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(general),
		missing_reference_to_built_in_protocol(File, Lines, Type, Entity, Protocol)
	),
	fail.

% compile the head of a clause of another entity predicate (which we check if declared multifile)

'$lgt_compile_head'(Other::Head, _, _, _) :-
	'$lgt_check'(entity_identifier, Other),
	'$lgt_check'(callable, Head),
	fail.

'$lgt_compile_head'(user::Head, user::Functor/Arity, Head, _) :-
	!,
	functor(Head, Functor, Arity),
	(	'$lgt_pp_directive_'(multifile(Functor/Arity)) ->
		true
	;	'$lgt_pp_missing_multifile_directive_'(user::Functor/Arity, _, _) ->
		true
	;	'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_missing_multifile_directive_'(user::Functor/Arity, File, Lines))
	).

'$lgt_compile_head'(Other::Head, Other::Functor/Arity, THead, Ctx) :-
	!,
	functor(Head, Functor, Arity),
	'$lgt_entity_to_prefix'(Other, Prefix),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	(	'$lgt_pp_directive_'(multifile(TFunctor/TArity)) ->
		true
	;	throw(existence_error(directive, multifile(Other::Functor/Arity)))
	),
	functor(THead, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(Head, THead, ExCtx),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

% compile the head of a clause of a module predicate (which we check if declared multifile)

'$lgt_compile_head'(':'(Module, Head), ':'(Module, Functor/Arity), THead, _) :-
	!,
	'$lgt_check'(callable, Head),
	functor(Head, Functor, Arity),
	(	Module == user ->
		THead = Head
	;	'$lgt_check'(module_identifier, Module),
		THead = ':'(Module, Head)
	),
	(	Module == user, '$lgt_pp_directive_'(multifile(Functor/Arity)) ->
		true
	;	'$lgt_pp_directive_'(multifile(':'(Module, Functor/Arity))) ->
		true
	;	'$lgt_pp_missing_multifile_directive_'(':'(Module,Functor/Arity), _, _) ->
		true
	;	'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_missing_multifile_directive_'(':'(Module,Functor/Arity), File, Lines))
	).

% compile the head of a clause of a user defined predicate

'$lgt_compile_head'(Head, Functor/Arity, THead, Ctx) :-
	% first clause for this predicate
	functor(Head, Functor, Arity),
	(	'$lgt_pp_dynamic_'(Head, _, _, _),
		\+ '$lgt_pp_public_'(Functor, Arity, _, _),
		\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
		\+ '$lgt_pp_private_'(Functor, Arity, _, _) ->
		% dynamic predicate without a scope directive; can be abolished if declared
		% in an object and the abolish message sender is the object itself
		'$lgt_add_ddef_clause'(Head, Functor, Arity, THead, Ctx)
	;	% static predicate and/or scoped dynamic predicate; cannot be abolished
		'$lgt_add_def_clause'(Head, Functor, Arity, THead, Ctx)
	).



% '$lgt_compile_body'(@term, @callable, -callable, -callable, +compilation_context)
%
% compiles an entity clause body


% runtime resolved meta-calls

'$lgt_compile_body'(Pred, Caller, TPred, '$lgt_debug'(goal(Pred, TPred), HeadExCtx), Ctx) :-
	var(Pred),
	!,
	'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, _, _, _, _, _, MetaVars, _, _, Mode, _, _, _),
	'$lgt_check_for_meta_predicate_directive'(Mode, Head, Pred),
	(	'$lgt_member_var'(Pred, MetaVars) ->
		TPred = '$lgt_metacall'(Pred, HeadExCtx, runtime)
	;	TPred = '$lgt_metacall'(Pred, HeadExCtx, local)
	),
	(	Caller == rule,
		'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(suspicious_calls, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(suspicious_calls),
			suspicious_call(File, Lines, Type, Entity, Pred, [call(Pred)])
		)
	;	true
	).

% compiler bypass control construct (opaque to cuts)

'$lgt_compile_body'({Pred}, _, _, _, Ctx) :-
	callable(Pred),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	(	Pred = call(Goal) ->
		% redundant call/1 wrapper
		Alternatives = [{Goal}]
	;	'$lgt_iso_spec_predicate'(Pred),
		\+ '$lgt_built_in_method'(Pred, _, _, _),
		% not a Logtalk built-in method that have a Prolog counterpart
		\+ '$lgt_control_construct'(Pred),
		\+ '$lgt_pp_defines_predicate_'(Pred, _, _, _, _, _),
		% call to a standard Prolog predicate that is not being locally redefined,
		Alternatives = [Pred]
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, {Pred}, Alternatives)
	),
	fail.

'$lgt_compile_body'({Pred}, _, TPred, '$lgt_debug'(goal({Pred}, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var_or_callable, Pred),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	(	var(Pred) ->
		TPred = call(Pred),
		'$lgt_check_for_meta_predicate_directive'(Mode, Head, Pred)
	;	Pred == ! ->
		TPred = true
	;	'$lgt_cut_transparent_control_construct'(Pred) ->
		% we need to keep the call/1 wrapper to preserve {}/1 cut-opaque semantics
		TPred = call(Pred)
	;	TPred = Pred
	).

% protect goal from further goal expansion
%
%'$lgt_compile_body'(@Pred, _, TPred, DPred, Ctx) :-
%	!,
%	'$lgt_check'(var_or_callable, Pred),
%	'$lgt_comp_ctx_mode'(Ctx, Mode),
%	(	Mode == runtime ->
%		'$lgt_compile_body'(Pred, _, TPred, DPred, Ctx)
%	;	Mode = compile(How, Cut, _),
%		'$lgt_comp_ctx'(Ctx,    Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, _,                  Stack, Lines, Term),
%		'$lgt_comp_ctx'(NewCtx, Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, compile(How,Cut,[Pred]), Stack, Lines, Term),
%		'$lgt_compile_body'(Pred, _, TPred, DPred, NewCtx)
%	).

% goal expansion (only applied at compile-time)

'$lgt_compile_body'(Pred, Caller, TPred, DPred, Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, compile(How,Cut,ExpandedGoals), Stack, Lines, Term),
	'$lgt_push_if_new'(ExpandedGoals, Pred, NewExpandedGoals),
	'$lgt_expand_file_goal'(Pred, ExpandedPred),
	Pred \== ExpandedPred,
	!,
	'$lgt_comp_ctx'(NewCtx, Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, compile(How,Cut,NewExpandedGoals), Stack, Lines, Term),
	'$lgt_compile_body'(ExpandedPred, Caller, TPred, DPred, NewCtx).

% message delegation (send a message while preserving the original sender)

'$lgt_compile_body'([Goal], _, _, _, _) :-
	'$lgt_check'(callable, Goal),
	\+ functor(Goal, (::), 2),
	throw(domain_error(message_sending_goal, Goal)).

'$lgt_compile_body'([Alias::Pred], Caller, TPred, DPred, Ctx) :-
	callable(Alias),
	'$lgt_pp_object_alias_'(Obj, Alias, Ctx, _, _),
	!,
	'$lgt_compile_body'([Obj::Pred], Caller, TPred, DPred, Ctx).

'$lgt_compile_body'([Obj::Pred], _, TPred, '$lgt_debug'(goal([Obj::Pred], TPred), ExCtx), Ctx) :-
	!,
	% as delegation keeps the original sender, we cannot use a recursive call
	% to the '$lgt_compile_body'/4 predicate to compile the ::/2 goal as that
	% would reset the sender to "this"
	'$lgt_comp_ctx'(Ctx, Head, _, _, Sender, _, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, Mode, Stack, Lines, Term),
	(	'$lgt_pp_meta_predicate_'(Head, _, _, _) ->
		'$lgt_execution_context'(ExCtx, _, Sender, _, _, MetaCallCtx, _),
		'$lgt_comp_ctx'(NewCtx, Head, _, _, Sender, Sender, Self, Prefix, MetaVars, _, MetaCallCtx, Mode, Stack, Lines, Term)
	;	'$lgt_comp_ctx'(Ctx, Head, _, _, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, Mode, Stack, Lines, Term),
		'$lgt_execution_context'(ExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack),
		'$lgt_comp_ctx'(NewCtx, Head, _, _, Sender, Sender, Self, Prefix, MetaVars, MetaCallCtx, NewExCtx, Mode, Stack, Lines, Term),
		'$lgt_execution_context'(NewExCtx, Entity, Sender, Sender, Self, MetaCallCtx, Stack)
	),
	'$lgt_compiler_flag'(events, Events),
	'$lgt_compile_message_to_object'(Pred, Obj, TPred0, Events, NewCtx),
	% ensure that this control construct cannot be used to break object encapsulation
	TPred = (Obj \= Sender -> TPred0; throw(error(permission_error(access,object,Sender), logtalk([Obj::Pred],ExCtx)))).

% existential quantifier outside bagof/3 and setof/3 calls

'$lgt_compile_body'(_^_, _, _, _, _) :-
	% in some unusual cases, the user may be defining a (^)/2 predicate ...
	\+ '$lgt_pp_defines_predicate_'(_^_, _, _, _, _, _),
	% ... but otherwise (^)/2 cannot be used outside bagof/3 and setof/3 calls
	throw(existence_error(procedure, (^)/2)).

% control constructs

'$lgt_compile_body'((Pred, _), _, _, _, Ctx) :-
	callable(Pred),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, compile(_,_,_), _, _, _),
	callable(Head),
	% ignore multifile predicates
	Head \= ':'(_, _),
	Head \= _::_,
	functor(Pred, Functor, Arity),
	functor(Head, Functor, Arity),
	% non-tail recursive predicate definition
	(	'$lgt_pp_non_tail_recursive_predicate_'(Functor, Arity, _, _) ->
		true
	;	'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_non_tail_recursive_predicate_'(Functor, Arity, File, Lines))
	),
	fail.

'$lgt_compile_body'((Pred1, Pred2), Caller, (TPred1, TPred2), (DPred1, DPred2), Ctx) :-
	!,
	'$lgt_compile_body'(Pred1, Caller, TPred1, DPred1, Ctx),
	(	TPred1 == repeat,
		% check if the repeat loop ends with a cut if compiling a source file
		'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, compile(How,_,ExpandedGoals), Stack, Lines, Term) ->
		'$lgt_comp_ctx'(NewCtx, Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, compile(How,Cut,ExpandedGoals), Stack, Lines, Term),
		'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, NewCtx),
		(	var(Cut),
			% no cut found; note that this lint check is limited to conjunctions where
			% the left side is a call to repeat/0 and the right side contains a cut
			'$lgt_compiler_flag'(suspicious_calls, warning),
			'$lgt_source_file_context'(File, Lines, Type, Entity),
			'$lgt_increment_compiling_warnings_counter',
			'$lgt_print_message'(
				warning(suspicious_calls),
				suspicious_call(File, Lines, Type, Entity, repeat, reason(repeat(Head)))
			) ->
			true
		;	% cut found
			true
		)
	;	'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, Ctx)
	).

'$lgt_compile_body'((If -> _; _), _, _, _, Ctx) :-
	nonvar(If),
	once((If = (Term1 = Term2); If = (Term1 \= Term2))),
	once((number(Term1); number(Term2))),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(arithmetic_expressions),
		suspicious_call(File, Lines, Type, Entity, If, reason(comparing_numbers_using_unification))
	),
	fail.

'$lgt_compile_body'((If -> _; _), _, _, _, Ctx) :-
	nonvar(If),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	(	If == ! ->
		Message = suspicious_cut_in_if_then_else(File, Lines, Type, Entity, Head)
	;	If = (Goal, _), Goal == !,
		Message = suspicious_cut_in_if_then_else(File, Lines, Type, Entity, Head, If)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(conditionals), Message),
	fail.

'$lgt_compile_body'((If -> _; _), _, _, _, Ctx) :-
	nonvar(If),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	If = (Term1 = Term2),
	(	var(Term1), ground(Term2) ->
		true
	;	ground(Term1), var(Term2)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(conditionals),
		suspicious_if_then_else_test(File, Lines, Type, Entity, Head, If)
	),
	fail.

'$lgt_compile_body'((IfThen; Else), Caller, (TIf -> TThen; TElse), (DIf -> DThen; DElse), Ctx) :-
	nonvar(IfThen),
	IfThen = (If -> Then),
	!,
	'$lgt_compile_body'(If, meta, TIf, DIf, Ctx),
	'$lgt_compile_body'(Then, Caller, TThen, DThen, Ctx),
	'$lgt_compile_body'(Else, Caller, TElse, DElse, Ctx).

'$lgt_compile_body'((SoftCut; _), _, _, _, Ctx) :-
	nonvar(SoftCut),
	SoftCut = '*->'(If, _),
	nonvar(If),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	(	If == ! ->
		Message = suspicious_cut_in_soft_cut(File, Lines, Type, Entity, Head)
	;	If = (Goal, _), Goal == !,
		Message = suspicious_cut_in_soft_cut(File, Lines, Type, Entity, Head, If)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(conditionals), Message),
	fail.

'$lgt_compile_body'((SoftCut; _), _, _, _, Ctx) :-
	nonvar(SoftCut),
	SoftCut = '*->'(If, _),
	nonvar(If),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	If = (Term1 = Term2),
	(	var(Term1), ground(Term2) ->
		true
	;	ground(Term1), var(Term2)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(conditionals),
		suspicious_soft_cut_test(File, Lines, Type, Entity, Head, If)
	),
	fail.

'$lgt_compile_body'((SoftCut; Else), _, ('*->'(TIf, TThen); TElse), ('*->'(DIf, DThen); DElse), Ctx) :-
	nonvar(SoftCut),
	SoftCut = '*->'(If, Then),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_compile_body'(If, meta, TIf, DIf, Ctx),
	'$lgt_compile_body'(Then, Caller, TThen, DThen, Ctx),
	'$lgt_compile_body'(Else, Caller, TElse, DElse, Ctx).

'$lgt_compile_body'((Pred1; Pred2), _, _, _, Ctx) :-
	nonvar(Pred1),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(disjunctions, warning),
	(	Pred1 == ! ->
		Message = suspicious_cut_in_disjunction(File, Lines, Type, Entity, Head)
	;	Pred1 = (Goal, _), Goal == !,
		Message = suspicious_cut_in_disjunction(File, Lines, Type, Entity, Head, (Pred1; Pred2))
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(disjunctions), Message),
	fail.

'$lgt_compile_body'((Pred1; Pred2), _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(disjunctions, warning),
	'$lgt_comp_ctx_term'(Ctx, (Head :- (Pred11; Pred22))),
	(Pred1; Pred2) == (Pred11; Pred22),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(disjunctions),
		disjunction_as_body(File, Lines, Type, Entity, Head, (Pred1; Pred2))
	),
	fail.

'$lgt_compile_body'((Pred1; Pred2), Caller, (TPred1; TPred2), (DPred1; DPred2), Ctx) :-
	!,
	'$lgt_compile_body'(Pred1, Caller, TPred10, DPred10, Ctx),
	'$lgt_fix_disjunction_left_side'(TPred10, TPred1),
	'$lgt_fix_disjunction_left_side'(DPred10, DPred1),
	'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, Ctx).

'$lgt_compile_body'('*->'(Pred1, Pred2), Caller, TPred2, DPred2, Ctx) :-
	Pred1 == otherwise,
	'$lgt_predicate_property'(otherwise, built_in),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_construct(File, Lines, Type, Entity, '*->'(Pred1, Pred2), Pred2)
		)
	;	true
	),
	'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, Ctx).

'$lgt_compile_body'('*->'(If, _), _, _, _, Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	nonvar(If),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	(	If == ! ->
		Message = suspicious_cut_in_soft_cut(File, Lines, Type, Entity, Head)
	;	If = (Goal, _), Goal == !,
		Message = suspicious_cut_in_soft_cut(File, Lines, Type, Entity, Head, If)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(conditionals), Message),
	fail.

'$lgt_compile_body'('*->'(If, _), _, _, _, Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	nonvar(If),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	If = (Term1 = Term2),
	(	var(Term1), ground(Term2) ->
		true
	;	ground(Term1), var(Term2)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(conditionals),
		suspicious_soft_cut_test(File, Lines, Type, Entity, Head, If)
	),
	fail.

'$lgt_compile_body'('*->'(Pred1, Pred2), _, _, _, Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	callable(Pred1),
	callable(Pred2),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(conditionals),
		missing_else_part(File, Lines, Type, Entity, '*->'(Pred1, Pred2))
	),
	fail.

'$lgt_compile_body'('*->'(Pred1, Pred2), Caller, '*->'(TPred1, TPred2), '*->'(DPred1, DPred2), Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_compile_body'(Pred1, meta, TPred1, DPred1, Ctx),
	'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, Ctx).

'$lgt_compile_body'((Pred1 -> Pred2), Caller, TPred2, DPred2, Ctx) :-
	Pred1 == otherwise,
	'$lgt_predicate_property'(otherwise, built_in),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_construct(File, Lines, Type, Entity, (Pred1 -> Pred2), Pred2)
		)
	;	true
	),
	'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, Ctx).

'$lgt_compile_body'((If -> _), _, _, _, Ctx) :-
	nonvar(If),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	(	If == ! ->
		Message = suspicious_cut_in_if_then_else(File, Lines, Type, Entity, Head)
	;	If = (Goal, _), Goal == !,
		Message = suspicious_cut_in_if_then_else(File, Lines, Type, Entity, Head, If)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(conditionals), Message),
	fail.

'$lgt_compile_body'((If -> _), _, _, _, Ctx) :-
	nonvar(If),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	If = (Term1 = Term2),
	(	var(Term1), ground(Term2) ->
		true
	;	ground(Term1), var(Term2)
	),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_comp_ctx_head'(Ctx, Head),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(conditionals),
		suspicious_if_then_else_test(File, Lines, Type, Entity, Head, If)
	),
	fail.

'$lgt_compile_body'((Pred1 -> Pred2), _, _, _, Ctx) :-
	callable(Pred1),
	callable(Pred2),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(conditionals, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(conditionals),
		missing_else_part(File, Lines, Type, Entity, (Pred1 -> Pred2))
	),
	fail.

'$lgt_compile_body'((Pred1 -> Pred2), Caller, (TPred1 -> TPred2), (DPred1 -> DPred2), Ctx) :-
	!,
	'$lgt_compile_body'(Pred1, meta, TPred1, DPred1, Ctx),
	'$lgt_compile_body'(Pred2, Caller, TPred2, DPred2, Ctx).

'$lgt_compile_body'(not(Pred), Caller, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(not(_)),
	\+ '$lgt_pp_defines_predicate_'(not(_), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, (not)/1, (\+)/1)
		)
	;	true
	),
	'$lgt_compile_body'(\+ Pred, Caller, TPred, DPred, Ctx).

'$lgt_compile_body'(fail_if(Pred), Caller, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(fail_if(_)),
	\+ '$lgt_pp_defines_predicate_'(fail_if(_), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, fail_if/1, (\+)/1)
		)
	;	true
	),
	'$lgt_compile_body'(\+ Pred, Caller, TPred, DPred, Ctx).

'$lgt_compile_body'(\+ Pred, _, _, _, Ctx) :-
	callable(Pred),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_negated_goal_alternative'(Pred, Alt),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, \+ Pred, [Alt])
	),
	fail.

'$lgt_compile_body'(\+ Pred, _, \+ TPred, '$lgt_debug'(goal(\+ Pred, \+ DPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Pred, meta, TPred, DPred, Ctx).

% warning on cuts on clauses for multifile predicates
'$lgt_compile_body'(!, _, _, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, compile(_,_,_), _, Lines, _),
	callable(Head),
	(	Head = _::_ ->
		true
	;	Head = ':'(_, _) ->
		true
	;	'$lgt_pp_multifile_'(Head, _, _, _)
	),
	% clause for a multifile predicate
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, !, reason(multifile(Head)))
	),
	fail.

% warning on cuts on clauses with variable aliasing in the head
'$lgt_compile_body'(!, _, _, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, compile(_,_,_), _, Lines, _),
	'$lgt_compiler_flag'(steadfastness, warning),
	'$lgt_variable_aliasing'(Head),
	functor(Head, Functor, Arity),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_defines_non_terminal_'(Functor, Arity2, Arity) ->
		'$lgt_print_message'(
			warning(steadfastness),
			possible_non_steadfast_non_terminal(File, Lines, Type, Entity, Functor//Arity2)
		)
	;	'$lgt_print_message'(
			warning(steadfastness),
			possible_non_steadfast_predicate(File, Lines, Type, Entity, Functor/Arity)
		)
	),
	fail.

% when processing the debug event, the compiled goal is meta-called but
% this would make the cut local, changing the semantics of the user code;
% the solution is to use a conjunction for the debug goal of the debug
% event with a cut
'$lgt_compile_body'(!, _, !, ('$lgt_debug'(goal(!, true), ExCtx), !), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	(	Mode == runtime ->
		true
	;	% remember that we found a cut to enable lint checks on repeat loops
		Mode = compile(_, true, _)
	).

'$lgt_compile_body'(true, _, true, '$lgt_debug'(goal(true, true), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(fail, _, fail, '$lgt_debug'(goal(fail, fail), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(false, _, false, '$lgt_debug'(goal(false, false), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(repeat, _, repeat, '$lgt_debug'(goal(repeat, repeat), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(call(Goal), _, _, _, Ctx) :-
	callable(Goal),
	\+ '$lgt_cut_transparent_control_construct'(Goal),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, call(Goal), [Goal])
	),
	fail.

'$lgt_compile_body'(call(Goal), _, TPred, '$lgt_debug'(goal(call(Goal), DPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	(	functor(TGoal, '$lgt_metacall', _) ->
		TPred = TGoal,
		DPred = DGoal
	;	'$lgt_cut_transparent_control_construct'(TGoal) ->
		% we need to keep the call/1 wrapper to preserve call/1 cut-opaque semantics
		TPred = call(TGoal),
		DPred = call(DGoal)
	;	TPred = TGoal,
		DPred = DGoal
	).

'$lgt_compile_body'('$lgt_callN'(Closure, ExtraArgs), _, _, _, Ctx) :-
	var(Closure),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, MetaVars, _, _, _, _, _, _),
	nonvar(Head),
	% ignore multifile predicates
	Head \= ':'(_, _),
	Head \= _::_,
	'$lgt_pp_meta_predicate_'(Head, Meta, _, _),
	% we're compiling a clause for a meta-predicate
	once('$lgt_member_var'(Closure, MetaVars)),
	% the closure is a meta-argument
	'$lgt_length'(ExtraArgs, 0, NExtraArgs),
	Meta =.. [_| MetaArgs],
	% check that the call/N call complies with the meta-predicate declaration
	'$lgt_not_same_meta_arg_extra_args'(MetaArgs, MetaVars, Closure, NExtraArgs),
	% generate the call/N meta template
	findall('*', '$lgt_between'(1, NExtraArgs, _), Stars),
	CallN =.. [call, NExtraArgs| Stars],
	throw(consistency_error(same_closure_specification, CallN, Meta)).

'$lgt_compile_body'('$lgt_callN'(Closure, ExtraArgs), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, _, _, _, _, _, MetaVars, _, _, Mode, _, _, _),
	(	var(Closure) ->
		% we're compiling a runtime meta-call
		'$lgt_check_for_meta_predicate_directive'(Mode, Head, Closure),
		(	'$lgt_member_var'(Closure, MetaVars) ->
			TPred = '$lgt_metacall'(Closure, ExtraArgs, HeadExCtx, runtime)
		;	TPred = '$lgt_metacall'(Closure, ExtraArgs, HeadExCtx, local)
		)
	;	'$lgt_extend_closure'(Closure, ExtraArgs, Goal, Ctx),
		\+ (functor(Goal, call, Arity), Arity >= 2) ->
		% not a call to call/2-N itself; safe to compile it
		'$lgt_compile_body'(Goal, meta, TPred0, _, Ctx),
		(	'$lgt_cut_transparent_control_construct'(TPred0) ->
			% we need to keep the call/1 wrapper to preserve call/2-N cut-opaque semantics
			TPred = call(TPred0)
		;	TPred = TPred0
		)
	;	% runtime resolved meta-call (e.g., a lambda expression)
		TPred = '$lgt_metacall'(Closure, ExtraArgs, HeadExCtx, local)
	),
	CallN =.. [call, Closure| ExtraArgs],
	DPred = '$lgt_debug'(goal(CallN, TPred), HeadExCtx).

'$lgt_compile_body'(once(Goal), _, (TGoal -> true), '$lgt_debug'(goal(once(Goal), (DGoal -> true)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx).

'$lgt_compile_body'(ignore(Goal), _, (TGoal -> true; true), '$lgt_debug'(goal(ignore(Goal), (DGoal -> true; true)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx).

% error handling and throwing predicates

'$lgt_compile_body'(catch(Goal, Catcher, Recovery), _, _, _, Ctx) :-
	var(Catcher),
	term_variables(Recovery, Variables),
	\+ '$lgt_member_var'(Catcher, Variables),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(catchall_catch, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, _, _, _),
	\+ (
		'$lgt_member'(_=Var, VariableNames),
		Catcher == Var
	),
	% assume that Catcher is an anonymous variable
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(catchall_catch),
		catchall_catch(File, Lines, Type, Entity, catch(Goal, Catcher, Recovery))
	),
	fail.

'$lgt_compile_body'(catch(Goal, Catcher, Recovery), _, catch(TGoal, Catcher, TRecovery), '$lgt_debug'(goal(catch(Goal, Catcher, Recovery), catch(DGoal, Catcher, DRecovery)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	'$lgt_compile_body'(Recovery, meta, TRecovery, DRecovery, Ctx).

'$lgt_compile_body'(throw(Error), _, throw(Error), '$lgt_debug'(goal(throw(Error), throw(Error)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(Pred, _, TPred, DPred, Ctx) :-
	'$lgt_built_in_error_method'(Pred),
	!,
	'$lgt_compile_error_method'(Pred, TPred, DPred, Ctx).

% type testing (only lint warnings)

'$lgt_compile_body'(var(Var), _, _, _, Ctx) :-
	var(Var),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(always_true_or_false_goals, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, Singletons, _, _),
	'$lgt_anonymous_or_singleton_variable'(Var, VariableNames, Singletons),
	% var/1 predicate argument is either an anonymous or a
	% singleton variable and thus can never be bound
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(always_true_or_false_goals),
		goal_is_always_true(File, Lines, Type, Entity, var(Var))
	),
	fail.

'$lgt_compile_body'(nonvar(Var), _, _, _, Ctx) :-
	var(Var),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(always_true_or_false_goals, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, Singletons, _, _),
	'$lgt_anonymous_or_singleton_variable'(Var, VariableNames, Singletons),
	% nonvar/1 predicate argument is either an anonymous or a
	% singleton variable and thus can never be bound
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(always_true_or_false_goals),
		goal_is_always_false(File, Lines, Type, Entity, nonvar(Var))
	),
	fail.

'$lgt_compile_body'(ground(Ground), _, _, _, Ctx) :-
	\+ ground(Ground),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(always_true_or_false_goals, warning),
	term_variables(Ground, Variables),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, Singletons, _, _),
	'$lgt_anonymous_or_singleton_variables'(Variables, VariableNames, Singletons),
	% all variables in the ground/1 predicate argument are either
	% anonymous or singleton variables and thus can never be bound
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(always_true_or_false_goals),
		goal_is_always_false(File, Lines, Type, Entity, ground(Ground))
	),
	fail.

% term comparison (only lint warnings)

'$lgt_compile_body'(Exp1 == Exp2, _, _, _, Ctx) :-
	once((float(Exp1); float(Exp2))),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(arithmetic_expressions),
		suspicious_call(File, Lines, Type, Entity, Exp1 == Exp2, reason(float_comparison))
	),
	fail.

'$lgt_compile_body'(Exp1 \== Exp2, _, _, _, Ctx) :-
	once((float(Exp1); float(Exp2))),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(arithmetic_expressions),
		suspicious_call(File, Lines, Type, Entity, Exp1 \== Exp2, reason(float_comparison))
	),
	fail.

% unification (only lint warnings)

'$lgt_compile_body'(Term1 = Term2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_prolog_feature'(coinduction, supported),
	% backend provides minimal support for cyclic terms; calling the next goal
	% while using a backend that doesn't support cyclic terms would end badly
	\+ \+ (
		Term1 = Term2,
		\+ acyclic_term(Term1)
	),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Term1 = Term2, reason(cyclic_terms))
	),
	fail.

'$lgt_compile_body'(Term1 = Term2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	(	Term1 \= Term2 ->
		% unification fails; further instantiation of Term1 or Term2 will not make it succeed
		'$lgt_compiler_flag'(always_true_or_false_goals, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(always_true_or_false_goals),
			goal_is_always_false(File, Lines, Type, Entity, Term1 = Term2)
		)
	;	\+ ground(Term1),
		\+ ground(Term2),
		\+ \+ (
			term_variables(Term1-Term2, Vars0),
			unify_with_occurs_check(Term1, Term2),
			term_variables(Term1-Term2, Vars),
			Vars0 == Vars
		),
		% unification will not bind any variables in the unified terms
		'$lgt_compiler_flag'(suspicious_calls, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(suspicious_calls),
			suspicious_call(File, Lines, Type, Entity, Term1 = Term2, reason(no_variable_bindings_after_unification))
		)
	),
	fail.

'$lgt_compile_body'(unify_with_occurs_check(Term1, Term2), _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	(	\+ unify_with_occurs_check(Term1, Term2) ->
		% unification fails; further instantiation of Term1 or Term2 will not make it succeed
		'$lgt_compiler_flag'(always_true_or_false_goals, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(always_true_or_false_goals),
			goal_is_always_false(File, Lines, Type, Entity, unify_with_occurs_check(Term1, Term2))
		)
	;	\+ ground(Term1),
		\+ ground(Term2),
		\+ \+ (
			term_variables(Term1-Term2, Vars0),
			unify_with_occurs_check(Term1, Term2),
			term_variables(Term1-Term2, Vars),
			Vars0 == Vars
		),
		% unification will not bind any variables in the unified terms
		'$lgt_compiler_flag'(suspicious_calls, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(suspicious_calls),
			suspicious_call(File, Lines, Type, Entity, unify_with_occurs_check(Term1, Term2), reason(no_variable_bindings_after_unification))
		)
	),
	fail.

'$lgt_compile_body'(Term1 \= Term2, _, _, _, Ctx) :-
	once((number(Term1); number(Term2))),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(arithmetic_expressions),
		suspicious_call(File, Lines, Type, Entity, Term1 \= Term2, reason(comparing_numbers_using_unification))
	),
	fail.

'$lgt_compile_body'(Term1 \= Term2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	Term1 \= Term2,
	% goal succeeds; further instantiation of Term1 or Term2 will not make it fail
	'$lgt_compiler_flag'(always_true_or_false_goals, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(always_true_or_false_goals),
		goal_is_always_true(File, Lines, Type, Entity, Term1 \= Term2)
	),
	fail.

% atomic term processing predicates (only lint warnings)

'$lgt_compile_body'(atom_concat(Prefix, Var, Atom), _, _, _, Ctx) :-
	atom(Prefix),
	var(Var),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, _, _, _),
	\+ (
		'$lgt_member'(_=Var0, VariableNames),
		Var0 == Var
	),
	% assume that Var is an anonymous variable
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, atom_concat(Prefix, Var, Atom), [sub_atom(Atom, 0, _, _, Prefix)])
	),
	fail.

% term creation and decomposition predicates (only lint warnings)

'$lgt_compile_body'(Term =.. List, _, _, _, Ctx) :-
	'$lgt_is_list'(List),
	% closed list (compound term with know arity)
	List = [Functor| _],
	nonvar(Functor),
	% with a bound functor
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	ListTerm =.. List,
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Term =.. List, [Term = ListTerm])
	),
	fail.

'$lgt_compile_body'(Term =.. List, _, _, _, Ctx) :-
	nonvar(List),
	List = [Functor| Args],
	var(Args),
	% open list (compound term with unknown arity)
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, _, _, _),
	once((
		'$lgt_member'(_=Functor0, VariableNames),
		Functor0 == Functor
	)),
	% assume that functor is not an anonymous variable
	\+ (
		'$lgt_member'(_=Args0, VariableNames),
		Args0 == Args
	),
	% assume that the list tail is an anonymous variable
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Term =.. List, [functor(Term, Functor, _)])
	),
	fail.

'$lgt_compile_body'(Term =.. List, _, _, _, Ctx) :-
	nonvar(List),
	List = [Functor| Args],
	var(Functor),
	nonvar(Args),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, _, _, _),
	\+ (
		'$lgt_member'(_=Functor0, VariableNames),
		Functor0 == Functor
	),
	% assume that the functor argument is an anonymous variable
	'$lgt_position_relevant_argument_pairs'(Args, 1, VariableNames, [N-Arg], open, Tail),
	\+ (
		'$lgt_member'(_=Tail0, VariableNames),
		Tail0 == Tail
	),
	% assume a single bound argument or non-anonymous variable argument in the compound term
	% arguments; we also require an open list with an anonymous variable as tail for this as
	% otherwise the =../2 call may also being used e.g. to verify the compound term arity
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Term =.. List, [arg(N, Term, Arg)])
	),
	fail.

% lambda expressions

'$lgt_compile_body'(Parameters>>Lambda, _, _, _, Ctx) :-
	'$lgt_check_lambda_expression'(Parameters>>Lambda, Ctx),
	fail.

'$lgt_compile_body'(Free/Parameters>>Lambda, Caller, TPred, DPred, Ctx) :-
	nonvar(Parameters),
	!,
	(	Parameters == [] ->
		'$lgt_compile_body'(Free/Lambda, Caller, TPred, DPred, Ctx)
	;	throw(representation_error(lambda_parameters))
	).

'$lgt_compile_body'(Free/Parameters>>Lambda, _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% lambda expressions are handled as meta-calls
	TPred = '$lgt_metacall'(Free/Parameters>>Lambda, [], ExCtx, local),
	DPred = '$lgt_debug'(goal(Free/Parameters>>Lambda, TPred), ExCtx).

'$lgt_compile_body'(Parameters>>Lambda, Caller, TPred, DPred, Ctx) :-
	nonvar(Parameters),
	!,
	(	Parameters == [] ->
		'$lgt_compile_body'(Lambda, Caller, TPred, DPred, Ctx)
	;	throw(representation_error(lambda_parameters))
	).

'$lgt_compile_body'(Parameters>>Lambda, _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% lambda expressions are handled as meta-calls
	TPred = '$lgt_metacall'(Parameters>>Lambda, [], ExCtx, local),
	DPred = '$lgt_debug'(goal(Parameters>>Lambda, TPred), ExCtx).

'$lgt_compile_body'(Free/Lambda, _, _, _, Ctx) :-
	'$lgt_check_lambda_expression'(Free/Lambda, Ctx),
	fail.

'$lgt_compile_body'(Free/Lambda, Caller, TPred, DPred, Ctx) :-
	nonvar(Free),
	nonvar(Lambda),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_comp_ctx_meta_vars'(Ctx, []) ->
		% generate an auxiliary predicate to replace the lambda expression
		'$lgt_generate_aux_predicate_functor'('_lambda_', Functor),
		(	Free = {Terms} ->
			'$lgt_conjunction_to_list'(Terms, Args)
		;	Args = []
		),
		Head =.. [Functor| Args],
		'$lgt_compile_aux_clauses'([(Head :- Lambda)]),
		'$lgt_compile_body'(Head, Caller, TPred, DPred, Ctx)
	;	% either runtime translation or the lambda expression appears in the
		% body of a meta-predicate clause
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		'$lgt_compile_body'(Lambda, Caller, TLambda, DLambda, Ctx),
		TPred = '$lgt_lambda'(Free, TLambda),
		DPred = '$lgt_debug'(goal(Free/Lambda, '$lgt_lambda'(Free, DLambda)), ExCtx)
	).

'$lgt_compile_body'(Free/Lambda, _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% lambda expressions are handled as meta-calls
	TPred = '$lgt_metacall'(Free/Lambda, [], ExCtx, local),
	DPred = '$lgt_debug'(goal(Free/Lambda, TPred), ExCtx).

% built-in meta-predicates

'$lgt_compile_body'(bagof(Term, QGoal, List), _, _, _, Ctx) :-
	callable(QGoal),
	\+ ground(Term),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_decompose_quantified_body'(QGoal, _, Goal),
	term_variables(Goal, GoalVariables),
	term_variables(Term, TermVariables),
	'$lgt_intersection'(TermVariables, GoalVariables, []),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, bagof(Term,QGoal,List), reason(no_shared_variables(bagof)))
	),
	fail.

'$lgt_compile_body'(bagof(_, QGoal, _), _, _, _, Ctx) :-
	callable(QGoal),
	QGoal = _^_,
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_missing_existential_variables'(QGoal, [Variable| Variables], Goal),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, QGoal, reason(existential_variables([Variable|Variables],Goal)))
	),
	fail.

'$lgt_compile_body'(bagof(_, QGoal, _), _, _, _, Ctx) :-
	callable(QGoal),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_singleton_variables_in_meta_argument'(QGoal, Singletons, Ctx),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, QGoal, reason(singleton_variables(bagof/3,QGoal,Singletons)))
	),
	fail.

'$lgt_compile_body'(bagof(Term, QGoal, List), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, _, _, _, _, _, MetaVars, _, ExCtx, Mode, _, _, _),
	(	var(QGoal) ->
		% runtime meta-call
		'$lgt_check_for_meta_predicate_directive'(Mode, Head, QGoal),
		(	'$lgt_member_var'(QGoal, MetaVars) ->
			TPred = '$lgt_bagof'(Term, QGoal, List, HeadExCtx, runtime)
		;	TPred = '$lgt_bagof'(Term, QGoal, List, HeadExCtx, local)
		),
		DPred = '$lgt_debug'(goal(bagof(Term, QGoal, List), TPred), HeadExCtx)
	;	% compile-time local call
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		'$lgt_compile_quantified_body'(QGoal, meta, TGoal, DGoal, Ctx),
		TPred = bagof(Term, TGoal, List),
		DPred = '$lgt_debug'(goal(bagof(Term, QGoal, List), bagof(Term, DGoal, List)), ExCtx)
	).

'$lgt_compile_body'(findall(Term, Goal, List), _, _, _, Ctx) :-
	nonvar(Goal),
	\+ ground(Term),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	term_variables(Term, TermVariables),
	term_variables(Goal, GoalVariables),
	'$lgt_intersection'(TermVariables, GoalVariables, []),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, _, _, _),
	once((
		'$lgt_member'(_=Term0, VariableNames),
		Term0 == Term
	)),
	% assume that Term is not an anonymous variable
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, findall(Term,Goal,List), reason(no_shared_variables(findall)))
	),
	fail.

'$lgt_compile_body'(findall(Term, Goal, List), _, _, _, Ctx) :-
	var(Term),
	var(List),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	% reinstate relation between term variables and their names
	'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
	'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, _, _, _),
	\+ (
		'$lgt_member'(_=List0, VariableNames),
		List0 == List
	),
	% assume that List is an anonymous variable
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, findall(Term, Goal, List), [(Goal, fail; true)])
	),
	fail.

'$lgt_compile_body'(findall(Term, Goal, List), _, findall(Term, TGoal, List), '$lgt_debug'(goal(findall(Term, Goal, List), findall(Term, DGoal, List)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx).

'$lgt_compile_body'(findall(Term, Goal, List, Tail), _, _, _, Ctx) :-
	nonvar(Goal),
	\+ ground(Term),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	term_variables(Term, TermVariables),
	term_variables(Goal, GoalVariables),
	'$lgt_intersection'(TermVariables, GoalVariables, []),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, findall(Term,Goal,List,Tail), reason(no_shared_variables(findall)))
	),
	fail.

'$lgt_compile_body'(findall(Term, Goal, List, Tail), _, findall(Term, TGoal, List, Tail), '$lgt_debug'(goal(findall(Term, Goal, List, Tail), findall(Term, DGoal, List, Tail)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx).

'$lgt_compile_body'(forall(Gen, Test), _, _, _, Ctx) :-
	callable(Gen),
	callable(Test),
	\+ ground(Gen),
	\+ ground(Test),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	term_variables(Gen, GenVariables),
	term_variables(Test, TestVariables),
	'$lgt_intersection'(GenVariables, TestVariables, []),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, forall(Gen,Test), reason(no_shared_variables(forall)))
	),
	fail.

'$lgt_compile_body'(forall(Gen, Test), _, \+ (TGen, \+ TTest), '$lgt_debug'(goal(forall(Gen, Test), \+ (DGen, \+ DTest)), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Gen, meta, TGen, DGen, Ctx),
	'$lgt_compile_body'(Test, meta, TTest, DTest, Ctx).

'$lgt_compile_body'(setof(Term, QGoal, List), _, _, _, Ctx) :-
	callable(QGoal),
	\+ ground(Term),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_decompose_quantified_body'(QGoal, _, Goal),
	term_variables(Goal, GoalVariables),
	term_variables(Term, TermVariables),
	'$lgt_intersection'(TermVariables, GoalVariables, []),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, setof(Term,QGoal,List), reason(no_shared_variables(setof)))
	),
	fail.

'$lgt_compile_body'(setof(_, QGoal, _), _, _, _, Ctx) :-
	callable(QGoal),
	QGoal = _^_,
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_missing_existential_variables'(QGoal, [Variable| Variables], Goal),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, QGoal, reason(existential_variables([Variable|Variables],Goal)))
	),
	fail.

'$lgt_compile_body'(setof(_, QGoal, _), _, _, _, Ctx) :-
	callable(QGoal),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_singleton_variables_in_meta_argument'(QGoal, Singletons, Ctx),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, QGoal, reason(singleton_variables(setof/3,QGoal,Singletons)))
	),
	fail.

'$lgt_compile_body'(setof(Term, QGoal, List), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, _, _, _, _, _, MetaVars, _, ExCtx, Mode, _, _, _),
	(	var(QGoal) ->
		% runtime meta-call
		'$lgt_check_for_meta_predicate_directive'(Mode, Head, QGoal),
		(	'$lgt_member_var'(QGoal, MetaVars) ->
			TPred = '$lgt_setof'(Term, QGoal, List, HeadExCtx, runtime)
		;	TPred = '$lgt_setof'(Term, QGoal, List, HeadExCtx, local)
		),
		DPred = '$lgt_debug'(goal(setof(Term, QGoal, List), TPred), HeadExCtx)
	;	% compile-time local call
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		'$lgt_compile_quantified_body'(QGoal, meta, TGoal, DGoal, Ctx),
		TPred = setof(Term, TGoal, List),
		DPred = '$lgt_debug'(goal(setof(Term, QGoal, List), setof(Term, DGoal, List)), ExCtx)
	).

% file compilation and loading predicates

'$lgt_compile_body'(logtalk_compile(Files), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	TPred = '$lgt_logtalk_compile'(Files, Directory, ExCtx),
	DPred = '$lgt_debug'(goal(logtalk_compile(Files), TPred), ExCtx).

'$lgt_compile_body'(logtalk_compile(Files, Flags), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	TPred = '$lgt_logtalk_compile'(Files, Flags, Directory, ExCtx),
	DPred = '$lgt_debug'(goal(logtalk_compile(Files, Flags), TPred), ExCtx).

'$lgt_compile_body'(logtalk_load(Files), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	TPred = '$lgt_logtalk_load'(Files, Directory, ExCtx),
	DPred = '$lgt_debug'(goal(logtalk_load(Files), TPred), ExCtx).

'$lgt_compile_body'(logtalk_load(Files, Flags), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_pp_file_paths_flags_'(_, Directory, _, _, _),
	TPred = '$lgt_logtalk_load'(Files, Flags, Directory, ExCtx),
	DPred = '$lgt_debug'(goal(logtalk_load(Files, Flags), TPred), ExCtx).

% file compilation/loading context

'$lgt_compile_body'(logtalk_load_context(Key, Value), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	(	var(Key) ->
		TPred = '$lgt_logtalk_load_context'(Key, Value, ExCtx)
	;	'$lgt_valid_logtalk_load_context_key'(Key) ->
		(	nonvar(Head),
			functor(Head, (:-), 1),
			% compiling a directive an initialization/1 directive
			'$lgt_logtalk_load_context_checked'(Key, Value) ->
			% expand goal to support embedded applications where the compiled
			% code may no longer be loaded using the Logtalk runtime
			TPred = true
		;	TPred = '$lgt_logtalk_load_context_checked'(Key, Value)
		)
	;	callable(Key) ->
		throw(domain_error(logtalk_load_context_key, Key))
	;	throw(type_error(callable, Key))
	),
	DPred = '$lgt_debug'(goal(logtalk_load_context(Key, Value), TPred), ExCtx).

% entity enumeration predicates

'$lgt_compile_body'(current_object(Obj), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Obj) ->
		TPred = '$lgt_current_object'(Obj, ExCtx)
	;	TPred = '$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _)
	),
	DPred = '$lgt_debug'(goal(current_object(Obj), TPred), ExCtx).

'$lgt_compile_body'(current_protocol(Ptc), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Ptc) ->
		TPred = '$lgt_current_protocol'(Ptc, ExCtx)
	;	TPred = '$lgt_current_protocol_'(Ptc, _, _, _, _)
	),
	DPred = '$lgt_debug'(goal(current_protocol(Ptc), TPred), ExCtx).

'$lgt_compile_body'(current_category(Ctg), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Ctg) ->
		TPred = '$lgt_current_category'(Ctg, ExCtx)
	;	TPred = '$lgt_current_category_'(Ctg, _, _, _, _, _)
	),
	DPred = '$lgt_debug'(goal(current_category(Ctg), TPred), ExCtx).

% entity property predicates

'$lgt_compile_body'(object_property(Obj, Prop), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_object_property, Prop),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_object_property'(Obj, Prop, ExCtx),
	DPred = '$lgt_debug'(goal(object_property(Obj, Prop), TPred), ExCtx).

'$lgt_compile_body'(protocol_property(Ptc, Prop), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_check'(var_or_protocol_property, Prop),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_protocol_property'(Ptc, Prop, ExCtx),
	DPred = '$lgt_debug'(goal(protocol_property(Ptc, Prop), TPred), ExCtx).

'$lgt_compile_body'(category_property(Ctg, Prop), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_check'(var_or_category_property, Prop),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_category_property'(Ctg, Prop, ExCtx),
	DPred = '$lgt_debug'(goal(category_property(Ctg, Prop), TPred), ExCtx).

% dynamic entity creation predicates

'$lgt_compile_body'(create_object(Obj, Relations, Directives, Clauses), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(list_or_partial_list, Relations),
	'$lgt_check'(list_or_partial_list, Directives),
	'$lgt_check'(list_or_partial_list, Clauses),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_create_object'(Obj, Relations, Directives, Clauses, ExCtx),
	DPred = '$lgt_debug'(goal(create_object(Obj, Relations, Directives, Clauses), TPred), ExCtx).

'$lgt_compile_body'(create_protocol(Ptc, Relations, Directives), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_check'(list_or_partial_list, Relations),
	'$lgt_check'(list_or_partial_list, Directives),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_create_protocol'(Ptc, Relations, Directives, ExCtx),
	DPred = '$lgt_debug'(goal(create_protocol(Ptc, Relations, Directives), TPred), ExCtx).

'$lgt_compile_body'(create_category(Ctg, Relations, Directives, Clauses), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_check'(list_or_partial_list, Relations),
	'$lgt_check'(list_or_partial_list, Directives),
	'$lgt_check'(list_or_partial_list, Clauses),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_create_category'(Ctg, Relations, Directives, Clauses, ExCtx),
	DPred = '$lgt_debug'(goal(create_category(Ctg, Relations, Directives, Clauses), TPred), ExCtx).

% dynamic entity abolishing predicates

'$lgt_compile_body'(abolish_object(Obj), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Obj) ->
		TPred = '$lgt_abolish_object'(Obj, ExCtx)
	;	TPred = '$lgt_abolish_object_checked'(Obj, ExCtx)
	),
	DPred = '$lgt_debug'(goal(abolish_object(Obj), TPred), ExCtx).

'$lgt_compile_body'(abolish_protocol(Ptc), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Ptc) ->
		TPred = '$lgt_abolish_protocol'(Ptc, ExCtx)
	;	TPred = '$lgt_abolish_protocol_checked'(Ptc, ExCtx)
	),
	DPred = '$lgt_debug'(goal(abolish_protocol(Ptc), TPred), ExCtx).

'$lgt_compile_body'(abolish_category(Ctg), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Ctg) ->
		TPred = '$lgt_abolish_category'(Ctg, ExCtx)
	;	TPred = '$lgt_abolish_category_checked'(Ctg, ExCtx)
	),
	DPred = '$lgt_debug'(goal(abolish_category(Ctg), TPred), ExCtx).

% entity relations predicates

'$lgt_compile_body'(extends_protocol(Ptc, ExtPtc), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_check'(var_or_protocol_identifier, ExtPtc),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_extends_protocol'(Ptc, ExtPtc, ExCtx),
	DPred = '$lgt_debug'(goal(extends_protocol(Ptc, ExtPtc), TPred), ExCtx).

'$lgt_compile_body'(extends_protocol(Ptc, ExtPtc, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_check'(var_or_protocol_identifier, ExtPtc),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_extends_protocol'(Ptc, ExtPtc, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(extends_protocol(Ptc, ExtPtc, Scope), TPred), ExCtx).


'$lgt_compile_body'(implements_protocol(ObjOrCtg, Ptc), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, ObjOrCtg),
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_implements_protocol'(ObjOrCtg, Ptc, ExCtx),
	DPred = '$lgt_debug'(goal(implements_protocol(ObjOrCtg, Ptc), TPred), ExCtx).

'$lgt_compile_body'(implements_protocol(ObjOrCtg, Ptc, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, ObjOrCtg),
	'$lgt_check'(var_or_protocol_identifier, Ptc),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_implements_protocol'(ObjOrCtg, Ptc, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(implements_protocol(ObjOrCtg, Ptc, Scope), TPred), ExCtx).


'$lgt_compile_body'(imports_category(Obj, Ctg), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_imports_category'(Obj, Ctg, ExCtx),
	DPred = '$lgt_debug'(goal(imports_category(Obj, Ctg), TPred), ExCtx).

'$lgt_compile_body'(imports_category(Obj, Ctg, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_imports_category'(Obj, Ctg, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(imports_category(Obj, Ctg, Scope), TPred), ExCtx).


'$lgt_compile_body'(instantiates_class(Obj, Class), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_object_identifier, Class),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_instantiates_class'(Obj, Class, ExCtx),
	DPred = '$lgt_debug'(goal(instantiates_class(Obj, Class), TPred), ExCtx).

'$lgt_compile_body'(instantiates_class(Obj, Class, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_object_identifier, Class),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_instantiates_class'(Obj, Class, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(instantiates_class(Obj, Class, Scope), TPred), ExCtx).


'$lgt_compile_body'(specializes_class(Class, Superclass), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Class),
	'$lgt_check'(var_or_object_identifier, Superclass),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_specializes_class'(Class, Superclass, ExCtx),
	DPred = '$lgt_debug'(goal(specializes_class(Class, Superclass), TPred), ExCtx).

'$lgt_compile_body'(specializes_class(Class, Superclass, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Class),
	'$lgt_check'(var_or_object_identifier, Superclass),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_specializes_class'(Class, Superclass, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(specializes_class(Class, Superclass, Scope), TPred), ExCtx).


'$lgt_compile_body'(extends_category(Ctg, ExtCtg), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_check'(var_or_category_identifier, ExtCtg),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_extends_category'(Ctg, ExtCtg, ExCtx),
	DPred = '$lgt_debug'(goal(extends_category(Ctg, ExtCtg), TPred), ExCtx).

'$lgt_compile_body'(extends_category(Ctg, ExtCtg, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Ctg),
	'$lgt_check'(var_or_category_identifier, ExtCtg),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_extends_category'(Ctg, ExtCtg, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(extends_category(Ctg, ExtCtg, Scope), TPred), ExCtx).


'$lgt_compile_body'(extends_object(Prototype, Parent), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Prototype),
	'$lgt_check'(var_or_object_identifier, Parent),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_extends_object'(Prototype, Parent, ExCtx),
	DPred = '$lgt_debug'(goal(extends_object(Prototype, Parent), TPred), ExCtx).

'$lgt_compile_body'(extends_object(Prototype, Parent, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, Prototype),
	'$lgt_check'(var_or_object_identifier, Parent),
	'$lgt_check'(var_or_scope, Scope),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_extends_object'(Prototype, Parent, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(extends_object(Prototype, Parent, Scope), TPred), ExCtx).


'$lgt_compile_body'(complements_object(Category, Object), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_category_identifier, Category),
	'$lgt_check'(var_or_object_identifier, Object),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_complements_object'(Category, Object, ExCtx),
	DPred = '$lgt_debug'(goal(complements_object(Category, Object), TPred), ExCtx).


'$lgt_compile_body'(conforms_to_protocol(ObjOrCtg, Protocol), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, ObjOrCtg),
	'$lgt_check'(var_or_protocol_identifier, Protocol),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_conforms_to_protocol'(ObjOrCtg, Protocol, ExCtx),
	DPred = '$lgt_debug'(goal(conforms_to_protocol(ObjOrCtg, Protocol), TPred), ExCtx).

'$lgt_compile_body'(conforms_to_protocol(ObjOrCtg, Protocol, Scope), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_object_identifier, ObjOrCtg),
	'$lgt_check'(var_or_protocol_identifier, Protocol),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_conforms_to_protocol'(ObjOrCtg, Protocol, Scope, ExCtx),
	DPred = '$lgt_debug'(goal(conforms_to_protocol(ObjOrCtg, Protocol), TPred), ExCtx).

% events predicates

'$lgt_compile_body'(current_event(Event, Obj, Msg, Sender, Monitor), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_event, Event),
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_callable, Msg),
	'$lgt_check'(var_or_object_identifier, Sender),
	'$lgt_check'(var_or_object_identifier, Monitor),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_current_event'(Event, Obj, Msg, Sender, Monitor, ExCtx),
	DPred = '$lgt_debug'(goal(current_event(Event, Obj, Msg, Sender, Monitor), TPred), ExCtx).

'$lgt_compile_body'(define_events(Event, Obj, Msg, Sender, Monitor), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_event, Event),
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_callable, Msg),
	'$lgt_check'(var_or_object_identifier, Sender),
	'$lgt_check'(var_or_object_identifier, Monitor),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_define_events'(Event, Obj, Msg, Sender, Monitor, ExCtx),
	DPred = '$lgt_debug'(goal(define_events(Event, Obj, Msg, Sender, Monitor), TPred), ExCtx).

'$lgt_compile_body'(abolish_events(Event, Obj, Msg, Sender, Monitor), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_event, Event),
	'$lgt_check'(var_or_object_identifier, Obj),
	'$lgt_check'(var_or_callable, Msg),
	'$lgt_check'(var_or_object_identifier, Sender),
	'$lgt_check'(var_or_object_identifier, Monitor),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_abolish_events'(Event, Obj, Msg, Sender, Monitor, ExCtx),
	DPred = '$lgt_debug'(goal(abolish_events(Event, Obj, Msg, Sender, Monitor), TPred), ExCtx).

% multi-threading meta-predicates

'$lgt_compile_body'(threaded(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded(Goals), _, TGoal, '$lgt_debug'(goal(threaded(Goals), TGoal), ExCtx), Ctx) :-
	var(Goals),
	!,
	'$lgt_comp_ctx'(Ctx, Head, HeadExCtx, _, _, _, _, _, MetaVars, _, _, Mode, _, _, _),
	'$lgt_check_for_meta_predicate_directive'(Mode, Head, Goals),
	(	'$lgt_member_var'(Goals, MetaVars) ->
		TGoal = '$lgt_threaded'(Goals, HeadExCtx, runtime)
	;	TGoal = '$lgt_threaded'(Goals, HeadExCtx, local)
	),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(threaded(Goals), _, MTGoals, '$lgt_debug'(goal(threaded(Goals), MDGoals), ExCtx), Ctx) :-
	!,
	'$lgt_compile_body'(Goals, meta, TGoals, DGoals, Ctx),
	'$lgt_compile_threaded_call'(TGoals, MTGoals),
	'$lgt_compile_threaded_call'(DGoals, MDGoals),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).


'$lgt_compile_body'(threaded_call(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_call(Goal, Tag), _, MTGoal, '$lgt_debug'(goal(threaded_call(Goal, Tag), MDGoal), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var, Tag),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	MTGoal = '$lgt_threaded_call_tagged'(Goal, TGoal, ExCtx, Tag),
	MDGoal = '$lgt_threaded_call_tagged'(Goal, DGoal, ExCtx, Tag).


'$lgt_compile_body'(threaded_call(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_call(Goal), _, MTGoal, '$lgt_debug'(goal(threaded_call(Goal), MDGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	MTGoal = '$lgt_threaded_call'(Goal, TGoal, ExCtx),
	MDGoal = '$lgt_threaded_call'(Goal, DGoal, ExCtx).

'$lgt_compile_body'(threaded_once(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_once(Goal, Tag), _, MTGoal, '$lgt_debug'(goal(threaded_once(Goal, Tag), MDGoal), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var, Tag),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	MTGoal = '$lgt_threaded_once_tagged'(Goal, TGoal, ExCtx, Tag),
	MDGoal = '$lgt_threaded_once_tagged'(Goal, DGoal, ExCtx, Tag).


'$lgt_compile_body'(threaded_once(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_once(Goal), _, MTGoal, '$lgt_debug'(goal(threaded_once(Goal), MDGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	MTGoal = '$lgt_threaded_once'(Goal, TGoal, ExCtx),
	MDGoal = '$lgt_threaded_once'(Goal, DGoal, ExCtx).


'$lgt_compile_body'(threaded_ignore(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_ignore(Goal), _, MTGoal, '$lgt_debug'(goal(threaded_ignore(Goal), MDGoal), ExCtx), Ctx) :-
	!,
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	MTGoal = '$lgt_threaded_ignore'(Goal, TGoal, ExCtx),
	MDGoal = '$lgt_threaded_ignore'(Goal, DGoal, ExCtx).


'$lgt_compile_body'(threaded_exit(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_exit(Goal, Tag), _, TGoal, '$lgt_debug'(goal(threaded_exit(Goal, Tag), TGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% compile the goal just for type-checking and collecting source data
	'$lgt_compile_body'(Goal, meta, _, _, Ctx),
	TGoal = '$lgt_threaded_exit_tagged'(Goal, ExCtx, Tag).


'$lgt_compile_body'(threaded_exit(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_exit(Goal), _, TGoal, '$lgt_debug'(goal(threaded_exit(Goal), TGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% compile the goal just for type-checking and collecting source data
	'$lgt_compile_body'(Goal, meta, _, _, Ctx),
	TGoal = '$lgt_threaded_exit'(Goal, ExCtx).


'$lgt_compile_body'(threaded_peek(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_peek(Goal, Tag), _, TGoal, '$lgt_debug'(goal(threaded_peek(Goal, Tag), TGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% compile the goal just for type-checking and collecting source data
	'$lgt_compile_body'(Goal, meta, _, _, Ctx),
	TGoal = '$lgt_threaded_peek_tagged'(Goal, ExCtx, Tag).


'$lgt_compile_body'(threaded_peek(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_peek(Goal), _, TGoal, '$lgt_debug'(goal(threaded_peek(Goal), TGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	% compile the goal just for type-checking and collecting source data
	'$lgt_compile_body'(Goal, meta, _, _, Ctx),
	TGoal = '$lgt_threaded_peek'(Goal, ExCtx).


'$lgt_compile_body'(threaded_cancel(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_cancel(Tag), _, TGoal, '$lgt_debug'(goal(threaded_cancel(Tag), TGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TGoal = '$lgt_threaded_cancel_tagged'(Tag, ExCtx).


'$lgt_compile_body'(threaded_engine_create(_, _, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_create(AnswerTemplate, Goal, Engine), _, MTGoal, '$lgt_debug'(goal(threaded_engine_create(AnswerTemplate, Goal, Engine), MDGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Goal, meta, TGoal, DGoal, Ctx),
	MTGoal = '$lgt_threaded_engine_create'(AnswerTemplate, Goal, TGoal, ExCtx, Engine),
	MDGoal = '$lgt_threaded_engine_create'(AnswerTemplate, Goal, DGoal, ExCtx, Engine).


'$lgt_compile_body'(threaded_engine_self(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_self(Engine), _, MTGoal, '$lgt_debug'(goal(threaded_engine_self(Engine), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	MTGoal = '$lgt_threaded_engine_self'(This, Engine),
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _).


'$lgt_compile_body'(threaded_engine(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine(Engine), _, MTGoal, '$lgt_debug'(goal(threaded_engine(Engine), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	MTGoal = '$lgt_current_engine'(This, Engine),
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _).


'$lgt_compile_body'(threaded_engine_next(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_next(Engine, Answer), _, MTGoal, '$lgt_debug'(goal(threaded_engine_next_reified(Engine, Answer), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	MTGoal = '$lgt_threaded_engine_next'(Engine, Answer, ExCtx).


'$lgt_compile_body'(threaded_engine_next_reified(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_next_reified(Engine, Answer), _, MTGoal, '$lgt_debug'(goal(threaded_engine_next_reified(Engine, Answer), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	MTGoal = '$lgt_threaded_engine_next_reified'(Engine, Answer, ExCtx).


'$lgt_compile_body'(threaded_engine_yield(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_yield(Answer), _, MTGoal, '$lgt_debug'(goal(threaded_engine_yield(Answer), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	MTGoal = '$lgt_threaded_engine_yield'(Answer, This),
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _).


'$lgt_compile_body'(threaded_engine_post(_, _), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_post(Engine, Message), _, MTGoal, '$lgt_debug'(goal(threaded_engine_post(Engine, Message), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	MTGoal = '$lgt_threaded_engine_post'(Engine, Message, ExCtx).


'$lgt_compile_body'(threaded_engine_fetch(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_fetch(Message), _, MTGoal, '$lgt_debug'(goal(threaded_engine_fetch(Message), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	MTGoal = '$lgt_threaded_engine_fetch'(Message, This),
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _).


'$lgt_compile_body'(threaded_engine_destroy(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_engine_destroy(Engine), _, MTGoal, '$lgt_debug'(goal(threaded_engine_destroy(Engine), MTGoal), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	MTGoal = '$lgt_threaded_engine_destroy'(Engine, ExCtx).


'$lgt_compile_body'(threaded_wait(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_wait(Msg), _, MTPred, '$lgt_debug'(goal(threaded_wait(Msg), MTPred), ExCtx), Ctx) :-
	!,
	(	'$lgt_pp_entity_'(Type, _, Prefix) ->
		true
	;	Type = object	% <</2 call
	),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, Prefix, _, _, ExCtx, _, _, _, _),
	(	nonvar(Head),
		'$lgt_pp_synchronized_'(Head, Mutex, _, _) ->
		(	Type == object ->
			% we're compiling an object predicate
			MTPred = '$lgt_threaded_wait_synch'(Mutex, Msg, Prefix)
		;	% we're compiling a category predicate
			'$lgt_comp_ctx_this'(Ctx, This),
			'$lgt_execution_context_this_entity'(ExCtx, This, _),
			MTPred = '$lgt_threaded_wait_synch_ctg'(Mutex, Msg, This)
		)
	;	(	Type == object ->
			% we're compiling an object predicate
			MTPred = '$lgt_threaded_wait'(Msg, Prefix)
		;	% we're compiling a category predicate
			'$lgt_comp_ctx_this'(Ctx, This),
			'$lgt_execution_context_this_entity'(ExCtx, This, _),
			MTPred = '$lgt_threaded_wait_ctg'(Msg, This)
		)
	).


'$lgt_compile_body'(threaded_notify(_), _, _, _, _) :-
	\+ '$lgt_pp_threaded_',
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, _, _),
	throw(resource_error(threads)).

'$lgt_compile_body'(threaded_notify(Msg), _, MTPred, '$lgt_debug'(goal(threaded_notify(Msg), MTPred), ExCtx), Ctx) :-
	!,
	(	'$lgt_pp_entity_'(Type, _, Prefix) ->
		true
	;	Type = object	% <</2 call
	),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, _, _, _, _),
	(	Type == object ->
		% we're compiling an object predicate
		MTPred = '$lgt_threaded_notify'(Msg, Prefix)
	;	% we're compiling a category predicate
		'$lgt_comp_ctx_this'(Ctx, This),
		'$lgt_execution_context_this_entity'(ExCtx, This, _),
		MTPred = '$lgt_threaded_notify_ctg'(Msg, This)
	).

% message-sending

'$lgt_compile_body'(Alias::Pred, _, TPred, '$lgt_debug'(goal(Alias::Pred, TPred), ExCtx), Ctx) :-
	callable(Alias),
	'$lgt_pp_object_alias_'(Obj, Alias, Ctx, _, _),
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compiler_flag'(events, Events),
	'$lgt_compile_message_to_object'(Pred, Obj, TPred, Events, Ctx).

'$lgt_compile_body'(Obj::Pred, _, TPred, '$lgt_debug'(goal(Obj::Pred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compiler_flag'(events, Events),
	'$lgt_compile_message_to_object'(Pred, Obj, TPred, Events, Ctx).

'$lgt_compile_body'(::Pred, _, TPred, '$lgt_debug'(goal(::Pred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_message_to_self'(Pred, TPred, Ctx).

'$lgt_compile_body'(^^Pred, _, TPred, '$lgt_debug'(goal(^^Pred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_super_call'(Pred, TPred, Ctx).

% context-switching

'$lgt_compile_body'(Obj<<Pred, _, TPred, '$lgt_debug'(goal(Obj<<Pred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_context_switch_call'(Obj, Pred, TPred, ExCtx).

% access to predicate definitions in "this" from categories; in the case of
% complementing categories, it's usually used to call a predicate definition
% that is being overridden by the category; when used within an object, it's
% the same as calling its argument

'$lgt_compile_body'(@Pred, _, TPred, '$lgt_debug'(goal(@Pred, TPred), ExCtx), Ctx) :-
	'$lgt_pp_complemented_object_'(_, _, _, _, _),
	!,
	'$lgt_check'(callable, Pred),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	TPred = '$lgt_call_in_this'(Pred, ExCtx).

'$lgt_compile_body'(@Pred, _, TPred, '$lgt_debug'(goal(@Pred, TPred), ExCtx), Ctx) :-
	'$lgt_pp_entity_'(category, _, _),
	!,
	'$lgt_check'(callable, Pred),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	functor(Pred, Functor, Arity),
		\+ '$lgt_pp_public_'(Functor, Arity, _, _),
		\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
		\+ '$lgt_pp_private_'(Functor, Arity, _, _) ->
		% no scope directive
		TPred = '$lgt_call_in_this'(Pred, ExCtx)
	;	TPred = '$lgt_call_in_this_checked'(Pred, ExCtx)
	).

'$lgt_compile_body'(@Pred, Caller, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(callable, Pred),
	'$lgt_compile_body'(Pred, Caller, TPred, DPred, Ctx).

% calling explicitly qualified module predicates

'$lgt_compile_body'(':'(_, Callable), Caller, TPred, DPred, Ctx) :-
	nonvar(Callable),
	Callable = ':'(Module, Pred),
	% in a module predicate call with multiple prefixes (e.g., m1:m2:m3:goal),
	% only the one that immediately precedes the predicate is relevant
	!,
	'$lgt_compile_body'(':'(Module, Pred), Caller, TPred, DPred, Ctx).

'$lgt_compile_body'(':'(Alias, Pred), Caller, TPred, '$lgt_debug'(goal(':'(Alias, Pred), TPred), ExCtx), Ctx) :-
	atom(Alias),
	'$lgt_pp_module_alias_'(Module, Alias, Ctx, _, _),
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(':'(Module, Pred), Caller, TPred, _, Ctx).

'$lgt_compile_body'(':'(Module, Pred), _, _, _, Ctx) :-
	'$lgt_prolog_feature'(modules, unsupported),
	\+ '$lgt_pp_module_'(_),
	% not compiling a module as an object
	% likely typo where a message-sending goal is intended
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, ':'(Module, Pred), [Module::Pred])
	),
	fail.

'$lgt_compile_body'(':'(Module, Pred), _, _, _, Ctx) :-
	atom(Module),
	callable(Pred),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	logtalk_linter_hook(':'(Module, Pred), Flag, File, Lines, Type, Entity, Warning),
	nonvar(Flag),
	'$lgt_valid_flag'(Flag),
	'$lgt_compiler_flag'(Flag, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(Flag), Warning),
	fail.

'$lgt_compile_body'(':'(Module, Pred), Caller, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Pred),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::Pred, Caller, TPred, DPred, Ctx)
	;	var(Module) ->
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = ':'(Module, Pred),
		DPred = '$lgt_debug'(goal(':'(Module, Pred), TPred), ExCtx)
	;	var(Pred) ->
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = ':'(Module, Pred),
		DPred = '$lgt_debug'(goal(':'(Module, Pred), TPred), ExCtx)
	;	\+ '$lgt_prolog_built_in_database_predicate'(Pred),
		% the meta-predicate templates for the backend Prolog database predicates are usually
		% not usable from Logtalk due the ambiguity of the ":" meta-argument qualifier but they
		% pose no problems when operating in a module database; in this particular case, the
		% explicit-qualified call can be compiled as-is
		(	'$lgt_pp_meta_predicate_'(':'(Module, Pred), ':'(Module, Meta), _, _)
			% we're either overriding the original meta-predicate template or working around a
			% backend Prolog compiler limitation in providing access to meta-predicate templates
		;	catch('$lgt_predicate_property'(':'(Module, Pred), meta_predicate(Meta)), _, fail)
		) ->
		% we're compiling a call to a module meta-predicate
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_add_referenced_module_predicate'(Mode, Module, Pred, Pred, Head),
		Pred =.. [Functor| Args],
		Meta =.. [Functor| MArgs],
		'$lgt_prolog_to_logtalk_meta_argument_specifiers'(MArgs, CMArgs),
		(	'$lgt_member'(CMArg, CMArgs), CMArg == (::) ->
			% the "::" meta-argument specifier is ambiguous in this context
			throw(domain_error(meta_argument_specifier, Meta))
		;	(	'$lgt_prolog_phrase_predicate'(':'(Module, Pred)) ->
				NewCaller = phrase
			;	NewCaller = meta
			),
			'$lgt_compile_prolog_meta_arguments'(Args, CMArgs, NewCaller, Ctx, TArgs, DArgs) ->
			TPred0 =.. [Functor| TArgs],
			TPred = ':'(Module, TPred0),
			DPred0 =.. [Functor| DArgs],
			DPred = '$lgt_debug'(goal(':'(Module, Pred), ':'(Module, DPred0)), ExCtx)
		;	throw(domain_error(meta_directive_template, Meta))
		)
	;	% we're compiling a call to a module predicate
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_add_referenced_module_predicate'(Mode, Module, Pred, Pred, Head),
		TPred = ':'(Module, Pred),
		DPred = '$lgt_debug'(goal(':'(Module, Pred), TPred), ExCtx)
	).

% reflection built-in predicates

'$lgt_compile_body'(current_op(Priority, Specifier, Operator), _, TPred, DPred, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	Entity == user,
	% usually a call from an initialization or conditional compilation directive
	!,
	TPred = current_op(Priority, Specifier, Operator),
	DPred = '$lgt_debug'(goal(current_op(Priority, Specifier, Operator), TPred), ExCtx).

'$lgt_compile_body'(current_op(Priority, Specifier, Operator), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_operator_priority, Priority),
	'$lgt_check'(var_or_operator_specifier, Specifier),
	'$lgt_check'(var_or_atom, Operator),
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
	TPred = '$lgt_current_op'(Database, Priority, Specifier, Operator, Database, p(_), ExCtx),
	DPred = '$lgt_debug'(goal(current_op(Priority, Specifier, Operator), TPred), ExCtx).

'$lgt_compile_body'(current_predicate(Term), _, TPred, DPred, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	Entity == user,
	% usually a call from an initialization or conditional compilation directive
	!,
	TPred = current_predicate(Term),
	DPred = '$lgt_debug'(goal(current_predicate(Term), TPred), ExCtx).

'$lgt_compile_body'(current_predicate(Term), Caller, TPred, DPred, Ctx) :-
	nonvar(Term),
	Term = ':'(Module, Pred),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Pred),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::current_predicate(Pred), Caller, TPred, DPred, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = current_predicate(':'(Module, Pred)),
		DPred = '$lgt_debug'(goal(current_predicate(':'(Module, Pred)), TPred), ExCtx)
	).

'$lgt_compile_body'(current_predicate(Term), Caller, TPred, DPred, Ctx) :-
	'$lgt_valid_predicate_indicator'(Term, AliasFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
		functor(Head, HeadFunctor, Arity),
		'$lgt_compile_body'(Obj::current_predicate(HeadFunctor/Arity), Caller, TPred, DPred, Ctx)
	;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
		functor(Head, HeadFunctor, Arity),
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = current_predicate(':'(Module, HeadFunctor/Arity)),
		DPred = '$lgt_debug'(goal(current_predicate(':'(Module, HeadFunctor/Arity)), TPred), ExCtx)
	;	fail
	),
	!.

'$lgt_compile_body'(current_predicate(Pred), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_predicate_indicator, Pred),
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
	TPred = '$lgt_current_predicate'(Database, Pred, Database, p(_), ExCtx),
	DPred = '$lgt_debug'(goal(current_predicate(Pred), TPred), ExCtx).

'$lgt_compile_body'(predicate_property(Term, Prop), _, TPred, DPred, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	Entity == user,
	% usually a call from an initialization or conditional compilation directive
	!,
	TPred = '$lgt_predicate_property'(Term, Prop),
	DPred = '$lgt_debug'(goal(predicate_property(Term, Prop), TPred), ExCtx).

'$lgt_compile_body'(predicate_property(Term, Prop), Caller, TPred, DPred, Ctx) :-
	nonvar(Term),
	Term = ':'(Module, Head),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::predicate_property(Head, Prop), Caller, TPred, DPred, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = predicate_property(':'(Module, Head), Prop),
		DPred = '$lgt_debug'(goal(predicate_property(':'(Module,Head), Prop), TPred), ExCtx)
	).

'$lgt_compile_body'(predicate_property(Alias, Prop), Caller, TPred, DPred, Ctx) :-
	nonvar(Alias),
	(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
		'$lgt_compile_body'(Obj::predicate_property(Head, Prop), Caller, TPred, DPred, Ctx)
	;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = predicate_property(':'(Module, Head), Prop),
		DPred = '$lgt_debug'(goal(predicate_property(':'(Module,Head), Prop), TPred), ExCtx)
	;	fail
	),
	!.

'$lgt_compile_body'(predicate_property(Pred, Prop), _, TPred, DPred, Ctx) :-
	!,
	'$lgt_check'(var_or_callable, Pred),
	'$lgt_check'(var_or_predicate_property, Prop),
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
	TPred = '$lgt_predicate_property'(Database, Pred, Prop, Database, p(_), ExCtx),
	DPred = '$lgt_debug'(goal(predicate_property(Pred, Prop), TPred), ExCtx).

% database handling built-in predicates

'$lgt_compile_body'(abolish(Functor, Arity), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(abolish(_, _)),
	\+ '$lgt_pp_defines_predicate_'(abolish(_, _), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, abolish/2, abolish/1)
		)
	;	true
	),
	'$lgt_compile_body'(abolish(Functor/Arity), Caller, TCond, DCond, Ctx).

'$lgt_compile_body'(abolish(Term), Caller, TCond, DCond, Ctx) :-
	nonvar(Term),
	Term = ':'(Module, Pred),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Pred),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::abolish(Pred), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = abolish(':'(Module, Pred)),
		DCond = '$lgt_debug'(goal(abolish(':'(Module, Pred)), TCond), ExCtx),
		(	ground(Term) ->
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Pred), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(abolish(Pred), Caller, TCond, DCond, Ctx) :-
	'$lgt_valid_predicate_indicator'(Pred, AliasFunctor, Arity),
	functor(Alias, AliasFunctor, Arity),
	(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
		functor(Head, HeadFunctor, Arity),
		'$lgt_compile_body'(Obj::abolish(HeadFunctor/Arity), Caller, TCond, DCond, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
		'$lgt_remember_updated_predicate'(Mode, Obj::HeadFunctor/Arity, CallerHead)
	;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
		functor(Head, HeadFunctor, Arity),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = abolish(':'(Module, HeadFunctor/Arity)),
		DCond = '$lgt_debug'(goal(abolish(':'(Module, HeadFunctor/Arity)), TCond), ExCtx),
		'$lgt_remember_updated_predicate'(Mode, ':'(Module, HeadFunctor/Arity), CallerHead)
	;	% proceed to next clause
		fail
	),
	!.

'$lgt_compile_body'(abolish(Pred), _, TCond, DCond, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
	'$lgt_check'(var_or_predicate_indicator, Pred),
	'$lgt_check_dynamic_directive'(Mode, Pred),
	(	ground(Pred) ->
		TCond = '$lgt_abolish_checked'(Database, Pred, Database, p(_), ExCtx),
		'$lgt_remember_updated_predicate'(Mode, Pred, Head)
	;	% partially instantiated predicate indicator; runtime check required
		TCond = '$lgt_abolish'(Database, Pred, Database, p(_), ExCtx)
	),
	DCond = '$lgt_debug'(goal(abolish(Pred), TCond), ExCtx).

'$lgt_compile_body'(assert(Clause), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assert(_)),
	\+ '$lgt_pp_defines_predicate_'(assert(_), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, assert/1, assertz/1)
		)
	;	true
	),
	'$lgt_compile_body'(assertz(Clause), Caller, TCond, DCond, Ctx).

'$lgt_compile_body'(asserta(QClause), Caller, TCond, DCond, Ctx) :-
	nonvar(QClause),
	'$lgt_module_qualified_clause'(QClause, Module, Clause, Head, Body),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::asserta(Clause), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = asserta(QClause),
		DCond = '$lgt_debug'(goal(asserta(QClause), TCond), ExCtx),
		(	ground(QClause) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(asserta(Clause), Caller, TCond, DCond, Ctx) :-
	nonvar(Clause),
	(	Clause = (Alias :- Body) ->
		nonvar(Alias),
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::asserta((Head :- Body)), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = asserta((':'(Module,Head) :- Body)),
			DCond = '$lgt_debug'(goal(asserta((':'(Module,Head) :- Body)), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	;	Clause = Alias,
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::asserta(Head), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = asserta(':'(Module,Head)),
			DCond = '$lgt_debug'(goal(asserta(':'(Module,Head)), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	),
	!.

'$lgt_compile_body'(asserta(Clause), _, TCond, DCond, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Clause, TClause) ->
		TCond = asserta(TClause),
		functor(Clause, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'(Clause) ->
			TCond = '$lgt_asserta'(Database, Clause, Database, p(_), p, ExCtx)
		;	'$lgt_check'(clause, Clause),
			(	Clause = (Head :- Body) ->
				(	Body == true ->
					TCond = '$lgt_asserta_fact_checked'(Database, Head, Database, p(_), p, ExCtx)
				;	TCond = '$lgt_asserta_rule_checked'(Database, Clause, Database, p(_), p, ExCtx)
				),
				functor(Head, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			;	TCond = '$lgt_asserta_fact_checked'(Database, Clause, Database, p(_), p, ExCtx),
				functor(Clause, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			)
		),
		'$lgt_check_dynamic_directive'(Mode, Clause)
	),
	DCond = '$lgt_debug'(goal(asserta(Clause), TCond), ExCtx).

'$lgt_compile_body'(assertz(QClause), Caller, TCond, DCond, Ctx) :-
	nonvar(QClause),
	'$lgt_module_qualified_clause'(QClause, Module, Clause, Head, Body),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::assertz(Clause), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = assertz(QClause),
		DCond = '$lgt_debug'(goal(assertz(QClause), TCond), ExCtx),
		(	ground(QClause) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(assertz(Clause), Caller, TCond, DCond, Ctx) :-
	nonvar(Clause),
	(	Clause = (Alias :- Body) ->
		nonvar(Alias),
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::assertz((Head :- Body)), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = assertz((':'(Module,Head) :- Body)),
			DCond = '$lgt_debug'(goal(assertz((':'(Module,Head) :- Body)), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	;	Clause = Alias,
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::assertz(Head), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = assertz(':'(Module,Head)),
			DCond = '$lgt_debug'(goal(assertz(':'(Module,Head)), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	),
	!.

'$lgt_compile_body'(assertz(Clause), _, TCond, DCond, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Clause, TClause) ->
		TCond = assertz(TClause),
		functor(Clause, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'(Clause) ->
			TCond = '$lgt_assertz'(Database, Clause, Database, p(_), p, ExCtx)
		;	'$lgt_check'(clause, Clause),
			(	Clause = (Head :- Body) ->
				(	Body == true ->
					TCond = '$lgt_assertz_fact_checked'(Database, Head, Database, p(_), p, ExCtx)
				;	TCond = '$lgt_assertz_rule_checked'(Database, Clause, Database, p(_), p, ExCtx)
				),
				functor(Head, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			;	TCond = '$lgt_assertz_fact_checked'(Database, Clause, Database, p(_), p, ExCtx),
				functor(Clause, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			)
		),
		'$lgt_check_dynamic_directive'(Mode, Clause)
	),
	DCond = '$lgt_debug'(goal(assertz(Clause), TCond), ExCtx).

'$lgt_compile_body'(clause(QHead, Body), Caller, TCond, DCond, Ctx) :-
	nonvar(QHead),
	QHead = ':'(Module, Head),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::clause(Head, Body), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = clause(QHead, Body),
		DCond = '$lgt_debug'(goal(clause(QHead, Body), TCond), ExCtx),
		(	ground(QHead) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(clause(Alias, Body), Caller, TCond, DCond, Ctx) :-
	nonvar(Alias),
	(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
		'$lgt_compile_body'(Obj::clause(Head, Body), Caller, TCond, DCond, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
	;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = clause(':'(Module,Head), Body),
		DCond = '$lgt_debug'(goal(clause(':'(Module,Head), Body), TCond), ExCtx),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
	;	fail
	),
	!.

'$lgt_compile_body'(clause(Head, Body), _, TCond, DCond, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Head, THead) ->
		'$lgt_check'(var_or_callable, Body),
		TCond = (clause(THead, TBody), (TBody = ('$lgt_nop'(Body), _) -> true; TBody = Body)),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'((Head :- Body)) ->
			TCond = '$lgt_clause'(Database, Head, Body, Database, p(_), ExCtx)
		;	'$lgt_check'(clause, (Head :- Body)),
			TCond = '$lgt_clause_checked'(Database, Head, Body, Database, p(_), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
		),
		'$lgt_check_dynamic_directive'(Mode, Head)
	),
	DCond = '$lgt_debug'(goal(clause(Head, Body), TCond), ExCtx).

'$lgt_compile_body'(retract(QClause), Caller, TCond, DCond, Ctx) :-
	nonvar(QClause),
	'$lgt_module_qualified_clause'(QClause, Module, Clause, Head, Body),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::retract(Clause), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = retract(QClause),
		DCond = '$lgt_debug'(goal(retract(QClause), TCond), ExCtx),
		(	ground(QClause) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(retract(Clause), Caller, TCond, DCond, Ctx) :-
	nonvar(Clause),
	(	Clause = (Alias :- Body) ->
		nonvar(Alias),
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::retract((Head :- Body)), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = retract((':'(Module,Head) :- Body)),
			DCond = '$lgt_debug'(goal(retract((':'(Module,Head) :- Body)), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	;	Clause = Alias,
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::retract(Head), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = retract(':'(Module,Head)),
			DCond = '$lgt_debug'(goal(retract(':'(Module,Head)), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	),
	!.

'$lgt_compile_body'(retract(Clause), _, TCond, DCond, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Clause, TClause) ->
		TCond = retract(TClause),
		functor(Clause, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'(Clause) ->
			TCond = '$lgt_retract'(Database, Clause, Database, p(_), ExCtx)
		;	'$lgt_check'(clause, Clause),
			(	Clause = (Head :- Body) ->
				(	var(Body) ->
					TCond = '$lgt_retract_var_body_checked'(Database, Clause, Database, p(_), ExCtx)
				;	Body == true ->
					TCond = '$lgt_retract_fact_checked'(Database, Head, Database, p(_), ExCtx)
				;	TCond = '$lgt_retract_rule_checked'(Database, Clause, Database, p(_), ExCtx)
				),
				functor(Head, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			;	TCond = '$lgt_retract_fact_checked'(Database, Clause, Database, p(_), ExCtx),
				functor(Clause, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			)
		),
		'$lgt_check_dynamic_directive'(Mode, Clause)
	),
	DCond = '$lgt_debug'(goal(retract(Clause), TCond), ExCtx).

'$lgt_compile_body'(retractall(QHead), Caller, TCond, DCond, Ctx) :-
	nonvar(QHead),
	QHead = ':'(Module, Head),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::retractall(Head), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = retractall(QHead),
		DCond = '$lgt_debug'(goal(retractall(QHead), TCond), ExCtx),
		(	ground(QHead) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(retractall(Alias), Caller, TCond, DCond, Ctx) :-
	nonvar(Alias),
	(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
		'$lgt_compile_body'(Obj::retractall(Head), Caller, TCond, DCond, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
	;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = retractall(':'(Module,Head)),
		DCond = '$lgt_debug'(goal(retractall(':'(Module,Head)), TCond), ExCtx),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
	;	% proceed to next clause
		fail
	),
	!.

'$lgt_compile_body'(retractall(Head), _, TCond, DCond, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Head, THead) ->
		TCond = retractall(THead),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	var(Head) ->
			TCond = '$lgt_retractall'(Database, Head, Database, p(_), ExCtx)
		;	'$lgt_check'(callable, Head),
			TCond = '$lgt_retractall_checked'(Database, Head, Database, p(_), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
		),
		'$lgt_check_dynamic_directive'(Mode, Head)
	),
	DCond = '$lgt_debug'(goal(retractall(Head), TCond), ExCtx).

% database handling built-in predicates that take a clause reference
% if supported as built-in predicates by the backend Prolog compiler

'$lgt_compile_body'(assert(Clause, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assert(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assert(_, _), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, assert/2, assertz/2)
		)
	;	true
	),
	'$lgt_compile_body'(assertz(Clause, Ref), Caller, TCond, DCond, Ctx).

'$lgt_compile_body'(asserta(QClause, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(asserta(_, _)),
	\+ '$lgt_pp_defines_predicate_'(asserta(_, _), _, _, _, _, _),
	nonvar(QClause),
	'$lgt_module_qualified_clause'(QClause, Module, Clause, Head, Body),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::asserta(Clause, Ref), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = asserta(QClause, Ref),
		DCond = '$lgt_debug'(goal(asserta(QClause, Ref), TCond), ExCtx),
		(	ground(QClause) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(asserta(Clause, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(asserta(_, _)),
	\+ '$lgt_pp_defines_predicate_'(asserta(_, _), _, _, _, _, _),
	nonvar(Clause),
	(	Clause = (Alias :- Body) ->
		nonvar(Alias),
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::asserta((Head :- Body), Ref), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = asserta((':'(Module,Head) :- Body), Ref),
			DCond = '$lgt_debug'(goal(asserta((':'(Module,Head) :- Body), Ref), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	;	Clause = Alias,
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::asserta(Head, Ref), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = asserta(':'(Module,Head), Ref),
			DCond = '$lgt_debug'(goal(asserta(':'(Module,Head), Ref), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	),
	!.

'$lgt_compile_body'(asserta(Clause, Ref), _, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(asserta(_, _)),
	\+ '$lgt_pp_defines_predicate_'(asserta(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Clause, TClause) ->
		TCond = asserta(TClause, Ref),
		functor(Clause, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'(Clause) ->
			TCond = '$lgt_asserta'(Database, Clause, Ref, Database, p(_), p)
		;	'$lgt_check'(clause, Clause),
			(	Clause = (Head :- Body) ->
				(	Body == true ->
					TCond = '$lgt_asserta_fact_checked'(Database, Head, Ref, Database, p(_), p, ExCtx)
				;	TCond = '$lgt_asserta_rule_checked'(Database, Clause, Ref, Database, p(_), p, ExCtx)
				),
				functor(Head, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			;	TCond = '$lgt_asserta_fact_checked'(Database, Clause, Ref, Database, p(_), p, ExCtx),
				functor(Clause, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			)
		),
		'$lgt_check_dynamic_directive'(Mode, Clause)
	),
	DCond = '$lgt_debug'(goal(asserta(Clause, Ref), TCond), ExCtx).

'$lgt_compile_body'(assertz(QClause, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assertz(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assertz(_, _), _, _, _, _, _),
	nonvar(QClause),
	'$lgt_module_qualified_clause'(QClause, Module, Clause, Head, Body),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::assertz(Clause, Ref), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = assertz(QClause),
		DCond = '$lgt_debug'(goal(assertz(QClause, Ref), TCond), ExCtx),
		(	ground(QClause) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(assertz(Clause, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assertz(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assertz(_, _), _, _, _, _, _),
	nonvar(Clause),
	(	Clause = (Alias :- Body) ->
		nonvar(Alias),
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::assertz((Head :- Body), Ref), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = assertz((':'(Module,Head) :- Body), Ref),
			DCond = '$lgt_debug'(goal(assertz((':'(Module,Head) :- Body), Ref), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	;	Clause = Alias,
		(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
			'$lgt_compile_body'(Obj::assertz(Head, Ref), Caller, TCond, DCond, Ctx),
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
			'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TCond = assertz(':'(Module,Head), Ref),
			DCond = '$lgt_debug'(goal(assertz(':'(Module,Head), Ref), TCond), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	% proceed to next clause
			fail
		)
	),
	!.

'$lgt_compile_body'(assertz(Clause, Ref), _, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assertz(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assertz(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Clause, TClause) ->
		TCond = assertz(TClause, Ref),
		functor(Clause, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'(Clause) ->
			TCond = '$lgt_assertz'(Database, Clause, Ref, Database, p(_), p, ExCtx)
		;	'$lgt_check'(clause, Clause),
			(	Clause = (Head :- Body) ->
				(	Body == true ->
					TCond = '$lgt_assertz_fact_checked'(Database, Head, Ref, Database, p(_), p, ExCtx)
				;	TCond = '$lgt_assertz_rule_checked'(Database, Clause, Ref, Database, p(_), p, ExCtx)
				),
				functor(Head, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			;	TCond = '$lgt_assertz_fact_checked'(Database, Clause, Ref, Database, p(_), p, ExCtx),
				functor(Clause, Functor, Arity),
				'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
			)
		),
		'$lgt_check_dynamic_directive'(Mode, Clause)
	),
	DCond = '$lgt_debug'(goal(assertz(Clause, Ref), TCond), ExCtx).

'$lgt_compile_body'(clause(QHead, Body, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(clause(_, _, _)),
	\+ '$lgt_pp_defines_predicate_'(clause(_, _, _), _, _, _, _, _),
	nonvar(QHead),
	QHead = ':'(Module, Head),
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Head),
	'$lgt_check'(var_or_callable, Body),
	(	'$lgt_pp_module_'(_) ->
		% we're compiling a module as an object; assume referenced modules are also compiled as objects
		'$lgt_compile_body'(Module::clause(Head, Body, Ref), Caller, TCond, DCond, Ctx)
	;	% we're using modules together with objects
		'$lgt_add_referenced_module'(Module, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = clause(QHead, Body, Ref),
		DCond = '$lgt_debug'(goal(clause(QHead, Body, Ref), TCond), ExCtx),
		(	ground(QHead) ->
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
		;	true
		)
	).

'$lgt_compile_body'(clause(Alias, Body, Ref), Caller, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(clause(_, _, _)),
	\+ '$lgt_pp_defines_predicate_'(clause(_, _, _), _, _, _, _, _),
	nonvar(Alias),
	(	'$lgt_pp_uses_predicate_'(Obj, Head, Alias, Ctx, _, _) ->
		'$lgt_compile_body'(Obj::clause(Head, Body, Ref), Caller, TCond, DCond, Ctx),
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
	;	'$lgt_pp_use_module_predicate_'(Module, Head, Alias, Ctx, _, _) ->
		'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		TCond = clause(':'(Module,Head), Body, Ref),
		DCond = '$lgt_debug'(goal(clause(':'(Module,Head), Body), TCond), ExCtx),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, ':'(Module, Functor/Arity), CallerHead)
	;	fail
	),
	!.

'$lgt_compile_body'(clause(Head, Body, Ref), _, TCond, DCond, Ctx) :-
	'$lgt_prolog_built_in_predicate'(clause(_, _, _)),
	\+ '$lgt_pp_defines_predicate_'(clause(_, _, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, Entity, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	(	'$lgt_optimizable_local_db_call'(Head, THead) ->
		'$lgt_check'(var_or_callable, Body),
		TCond = (clause(THead, TBody, Ref), (TBody = ('$lgt_nop'(Body), _) -> true; TBody = Body)),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
	;	'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx),
		(	'$lgt_runtime_checked_db_clause'((Head :- Body)) ->
			TCond = '$lgt_clause'(Database, Head, Body, Ref, Database, p(_), ExCtx)
		;	'$lgt_check'(clause, (Head :- Body)),
			TCond = '$lgt_clause_checked'(Database, Head, Body, Ref, Database, p(_), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Functor/Arity, CallerHead)
		),
		'$lgt_check_dynamic_directive'(Mode, Head)
	),
	DCond = '$lgt_debug'(goal(clause(Head, Body, Ref), TCond), ExCtx).

'$lgt_compile_body'(erase(Ref), _, erase(Ref), '$lgt_debug'(goal(erase(Ref), erase(Ref)), ExCtx), Ctx) :-
	'$lgt_prolog_built_in_predicate'(erase(_)),
	\+ '$lgt_pp_defines_predicate_'(erase(_), _, _, _, _, _),
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

% term and goal expansion predicates

'$lgt_compile_body'(expand_term(Term, Expansion), _, TPred, '$lgt_debug'(goal(expand_term(Term, Expansion), TPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	TPred = '$lgt_expand_term_local'(Entity, Term, Expansion, ExCtx).

'$lgt_compile_body'(expand_goal(Goal, ExpandedGoal), _, TPred, '$lgt_debug'(goal(expand_goal(Goal, ExpandedGoal), TPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	TPred = '$lgt_expand_goal_local'(Entity, Goal, ExpandedGoal, ExCtx).


% DCG predicates
%
% defer to runtime compilation of variable grammar rule
% body arguments to prevent a compilation endless loop

'$lgt_compile_body'(phrase(GRBody, Input), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, MetaVars, _, ExCtx, _, _, _, _),
	(	'$lgt_member_var'(GRBody, MetaVars) ->
		TPred = '$lgt_phrase'(GRBody, Input, HeadExCtx, runtime)
	;	TPred = '$lgt_phrase'(GRBody, Input, HeadExCtx, local)
	).

'$lgt_compile_body'(phrase(::GRBody, Input), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(::GRBody, Input, HeadExCtx, local).

'$lgt_compile_body'(phrase(Obj::GRBody, Input), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(Obj::GRBody, Input, HeadExCtx, local).

'$lgt_compile_body'(phrase(^^GRBody, Input), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(^^GRBody, Input, HeadExCtx, local).

'$lgt_compile_body'(phrase(Obj<<GRBody, Input), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(Obj<<GRBody, Input, HeadExCtx, local).

'$lgt_compile_body'(phrase(GRBody, Input), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input), DPred), ExCtx), Ctx) :-
	!,
	% the '$lgt_dcg_body'/5 predicate already checks that the grammar rule body is callable
	'$lgt_dcg_body'(GRBody, S0, S, Pred, Ctx),
	'$lgt_check'(list_or_partial_list, Input),
	TPred = (Input = S0, [] = S, TPred0),
	DPred = (Input = S0, [] = S, DPred0),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Pred, phrase, TPred0, DPred0, Ctx).

'$lgt_compile_body'(phrase(GRBody, Input, Rest), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input, Rest), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_check'(list_or_partial_list, Rest),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, MetaVars, _, ExCtx, _, _, _, _),
	(	'$lgt_member_var'(GRBody, MetaVars) ->
		TPred = '$lgt_phrase'(GRBody, Input, Rest, HeadExCtx, runtime)
	;	TPred = '$lgt_phrase'(GRBody, Input, Rest, HeadExCtx, local)
	).

'$lgt_compile_body'(phrase(::GRBody, Input, Rest), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input, Rest), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_check'(list_or_partial_list, Rest),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(::GRBody, Input, Rest, HeadExCtx, local).

'$lgt_compile_body'(phrase(Obj::GRBody, Input, Rest), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input, Rest), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_check'(list_or_partial_list, Rest),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(Obj::GRBody, Input, Rest, HeadExCtx, local).

'$lgt_compile_body'(phrase(^^GRBody, Input, Rest), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input, Rest), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_check'(list_or_partial_list, Rest),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(^^GRBody, Input, Rest, HeadExCtx, local).

'$lgt_compile_body'(phrase(Obj<<GRBody, Input, Rest), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input, Rest), TPred), ExCtx), Ctx) :-
	var(GRBody),
	!,
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_check'(list_or_partial_list, Rest),
	'$lgt_comp_ctx'(Ctx, _, HeadExCtx, _, _, _, _, _, _, _, ExCtx, _, _, _, _),
	TPred = '$lgt_phrase'(Obj<<GRBody, Input, Rest, HeadExCtx, local).

'$lgt_compile_body'(phrase(GRBody, Input, Rest), _, TPred, '$lgt_debug'(goal(phrase(GRBody, Input, Rest), DPred), ExCtx), Ctx) :-
	!,
	% the '$lgt_dcg_body'/5 predicate already checks that the grammar rule body is callable
	'$lgt_dcg_body'(GRBody, S0, S, Pred, Ctx),
	'$lgt_check'(list_or_partial_list, Input),
	'$lgt_check'(list_or_partial_list, Rest),
	TPred = (Input = S0, Rest = S, TPred0),
	DPred = (Input = S0, Rest = S, DPred0),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	'$lgt_compile_body'(Pred, phrase, TPred0, DPred0, Ctx).

% execution-context methods
%
% calls to these methods are compiled inline whenever possible by unifying
% the method argument with the corresponding execution context argument;
% with the exception of context/1, calls with instantiated arguments are
% not inlined as the call may be used as e.g. a condition in an if-then-else
% control construct

'$lgt_compile_body'(context(Context), _, true, '$lgt_debug'(goal(context(Context), true), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var, Context),
	'$lgt_comp_ctx_head'(Ctx, Head0),
	(	term_variables(Head0, Variables),
		'$lgt_member_var'(Context, Variables) ->
		throw(representation_error(acyclic_term))
	;	Head0 = _::Head ->
		% multifile predicate
		true
	;	Head0 = ':'(_,Head) ->
		% assume Prolog module multifile predicate
		true
	;	Head0 = Head
	),
	'$lgt_comp_ctx_head_exec_ctx'(Ctx, ExCtx),
	Context = logtalk(Head, ExCtx).

'$lgt_compile_body'(sender(Sender), _, TPred, '$lgt_debug'(goal(sender(DSender), DPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_head_exec_ctx'(Ctx, ExCtx),
	'$lgt_execution_context'(ExCtx, _, Sender0, _, _, _, _),
	(	var(Sender) ->
		% compile-time unification
		Sender0 = Sender,
		TPred = true,
		DPred = (DSender = Sender)
	;	% we must delay unification to runtime
		TPred = (Sender0 = Sender),
		DPred = TPred,
		DSender = Sender
	).

'$lgt_compile_body'(this(This), _, TPred, '$lgt_debug'(goal(this(DThis), DPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_head_exec_ctx'(Ctx, ExCtx),
	'$lgt_execution_context_this_entity'(ExCtx, This0, _),
	(	var(This) ->
		% compile-time unification
		This0 = This,
		TPred = true,
		DPred = (DThis = This)
	;	% we must delay unification to runtime
		TPred = (This0 = This),
		DPred = TPred,
		DThis = This
	).

'$lgt_compile_body'(self(Self), _, TPred, '$lgt_debug'(goal(self(DSelf), DPred), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx_head_exec_ctx'(Ctx, ExCtx),
	'$lgt_execution_context'(ExCtx, _, _, _, Self0, _, _),
	(	var(Self) ->
		% compile-time unification
		Self0 = Self,
		TPred = true,
		DPred = (DSelf = Self)
	;	% we must delay unification to runtime
		TPred = (Self0 = Self),
		DPred = TPred,
		DSelf = Self
	).

'$lgt_compile_body'(parameter(Arg, _), _, _, _, Ctx) :-
	'$lgt_check'(integer, Arg),
	(	'$lgt_pp_entity_'(_, Entity, _) ->
		% compile-time
		true
	;	% runtime <</2 call
		'$lgt_comp_ctx_entity'(Ctx, Entity)
	),
	\+ compound(Entity),
	throw(type_error(parametric_entity, Entity)).

'$lgt_compile_body'(parameter(Arg, Value), _, TPred, '$lgt_debug'(goal(parameter(Arg, DValue), DPred), ExCtx), Ctx) :-
	!,
	(	'$lgt_pp_entity_'(_, Entity, _) ->
		% compile-time; instantiate the Entity argument in the compilation context
		true
	;	% runtime <</2 call; Entity alreay instantiated in the compilation context
		true
	),
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	functor(Entity, _, Arity),
	(	1 =< Arg, Arg =< Arity ->
		arg(Arg, Entity, Value0),
		(	var(Value) ->
			% parameter compile-time unification
			Value0 = Value,
			TPred = true,
			DPred = (DValue = Value)
		;	% we must delay unification to runtime
			TPred = (Value0 = Value),
			DPred = TPred,
			DValue = Value
		)
	;	throw(domain_error([1,Arity], Arg))
	).

% open/4 portability lint warnings only

'$lgt_compile_body'(open(_, _, _, Options), _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, Mode),
	'$lgt_check_open_stream_options'(Mode, open/4, Options),
	fail.

% term input predicates that need to be operator aware
% (these translations are only applied if there are local entity operators declared)

'$lgt_compile_body'(read_term(Stream, Term, Options), _, '$lgt_iso_read_term'(Stream, Term, Options, Ops), '$lgt_debug'(goal(read_term(Stream, Term, Options), '$lgt_iso_read_term'(Stream, Term, Options, Ops)), ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_check_read_term_options'(Mode, read_term/3, Options),
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	!.

'$lgt_compile_body'(read_term(Term, Options), _, '$lgt_iso_read_term'(Term, Options, Ops), '$lgt_debug'(goal(read_term(Term, Options), '$lgt_iso_read_term'(Term, Options, Ops)), ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_check_read_term_options'(Mode, read_term/2, Options),
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	!.

'$lgt_compile_body'(read(Stream, Term), _, '$lgt_iso_read'(Stream, Term, Ops), '$lgt_debug'(goal(read(Stream, Term), '$lgt_iso_read'(Stream, Term, Ops)), ExCtx), Ctx) :-
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	!.

'$lgt_compile_body'(read(Term), _, '$lgt_iso_read'(Term, Ops), '$lgt_debug'(goal(read(Term), '$lgt_iso_read'(Term, Ops)), ExCtx), Ctx) :-
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	!.

% term output predicates that need to be operator aware
% (these translations are only applied if there are local entity operators declared)

'$lgt_compile_body'(write_term(Stream, Term, Options), _, '$lgt_iso_write_term'(Stream, Term, Options, Ops), '$lgt_debug'(goal(write_term(Stream, Term, Options), '$lgt_iso_write_term'(Stream, Term, Options, Ops)), ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_check_write_term_options'(Mode, write_term/3, Options),
	('$lgt_member'(ignore_ops(Value), Options) -> Value \== true; true),
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	!.

'$lgt_compile_body'(write_term(Term, Options), _, '$lgt_iso_write_term'(Term, Options, Ops), '$lgt_debug'(goal(write_term(Term, Options), '$lgt_iso_write_term'(Term, Options, Ops)), ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_check_write_term_options'(Mode, write_term/2, Options),
	('$lgt_member'(ignore_ops(Value), Options) -> Value \== true; true),
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	!.

'$lgt_compile_body'(write(Stream, Term), _, '$lgt_iso_write'(Stream, Term, Ops), '$lgt_debug'(goal(write(Stream, Term), '$lgt_iso_write'(Stream, Term, Ops)), ExCtx), Ctx) :-
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	!.

'$lgt_compile_body'(write(Term), _, '$lgt_iso_write'(Term, Ops), '$lgt_debug'(goal(write(Term), '$lgt_iso_write'(Term, Ops)), ExCtx), Ctx) :-
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	!.

'$lgt_compile_body'(writeq(Stream, Term), _, '$lgt_iso_writeq'(Stream, Term, Ops), '$lgt_debug'(goal(writeq(Stream, Term), '$lgt_iso_writeq'(Stream, Term, Ops)), ExCtx), Ctx) :-
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	!.

'$lgt_compile_body'(writeq(Term), _, '$lgt_iso_writeq'(Term, Ops), '$lgt_debug'(goal(writeq(Term), '$lgt_iso_writeq'(Term, Ops)), ExCtx), Ctx) :-
	bagof(op(Pr, Spec, Op), Scope^File^Lines^'$lgt_pp_entity_operator_'(Pr, Spec, Op, Scope, File, Lines), Ops),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	!.

% Logtalk flag predicates

'$lgt_compile_body'(set_logtalk_flag(Flag, Value), _, TPred, '$lgt_debug'(goal(DPred, TPred), ExCtx), Ctx) :-
	nonvar(Flag),
	nonvar(Value),
	!,
	'$lgt_check'(read_write_flag, Flag),
	'$lgt_check'(flag_value, Flag + Value),
	TPred = '$lgt_set_compiler_flag'(Flag, Value),
	DPred = set_logtalk_flag(Flag, Value),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(set_logtalk_flag(Flag, Value), _, TPred, '$lgt_debug'(goal(DPred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var_or_read_write_flag, Flag),
	TPred = '$lgt_set_logtalk_flag'(Flag, Value, ExCtx),
	DPred = set_logtalk_flag(Flag, Value),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(current_logtalk_flag(Flag, Value), _, TPred, '$lgt_debug'(goal(DPred, TPred), ExCtx), Ctx) :-
	nonvar(Flag),
	nonvar(Value),
	!,
	'$lgt_check'(flag, Flag),
	'$lgt_check'(flag_value, Flag + Value),
	TPred = '$lgt_compiler_flag'(Flag, Value),
	DPred = current_logtalk_flag(Flag, Value),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(current_logtalk_flag(Flag, Value), _, TPred, '$lgt_debug'(goal(DPred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var_or_flag, Flag),
	TPred = '$lgt_current_logtalk_flag'(Flag, Value, ExCtx),
	DPred = current_logtalk_flag(Flag, Value),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_body'(create_logtalk_flag(Flag, Value, Options), _, TPred, '$lgt_debug'(goal(DPred, TPred), ExCtx), Ctx) :-
	!,
	'$lgt_check'(atom, Flag),
	'$lgt_check'(ground, Value),
	'$lgt_check'(ground, Options),
	'$lgt_check'(list, Options),
	TPred = '$lgt_create_logtalk_flag'(Flag, Value, Options, ExCtx),
	DPred = create_logtalk_flag(Flag, Value, Options),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

% Prolog flag predicates (just basic error and portability checking)

'$lgt_compile_body'(set_prolog_flag(Flag, _), _, _, _, Ctx) :-
	'$lgt_check'(var_or_atom, Flag),
	nonvar(Flag),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	\+ '$lgt_iso_spec_flag'(Flag),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_print_message'(warning(portability), non_standard_prolog_flag(File, Lines, Type, Entity, Flag))
	;	'$lgt_print_message'(warning(portability), non_standard_prolog_flag(File, Lines, Flag))
	),
	fail.

'$lgt_compile_body'(set_prolog_flag(Flag, Value), _, _, _, Ctx) :-
	nonvar(Flag),
	nonvar(Value),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_iso_spec_flag'(Flag),
	\+ '$lgt_iso_spec_flag_value'(Flag, Value),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_print_message'(warning(portability), non_standard_prolog_flag_value(File, Lines, Type, Entity, Flag, Value))
	;	'$lgt_print_message'(warning(portability), non_standard_prolog_flag_value(File, Lines, Flag, Value))
	),
	fail.

'$lgt_compile_body'(current_prolog_flag(Flag, _), _, _, _, Ctx) :-
	'$lgt_check'(var_or_atom, Flag),
	nonvar(Flag),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	\+ '$lgt_iso_spec_flag'(Flag),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_print_message'(warning(portability), non_standard_prolog_flag(File, Lines, Type, Entity, Flag))
	;	'$lgt_print_message'(warning(portability), non_standard_prolog_flag(File, Lines, Flag))
	),
	fail.

'$lgt_compile_body'(current_prolog_flag(Flag, Value), _, _, _, Ctx) :-
	nonvar(Flag),
	nonvar(Value),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_iso_spec_flag'(Flag),
	\+ '$lgt_iso_spec_flag_value'(Flag, Value),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_print_message'(warning(portability), non_standard_prolog_flag_value(File, Lines, Type, Entity, Flag, Value))
	;	'$lgt_print_message'(warning(portability), non_standard_prolog_flag_value(File, Lines, Flag, Value))
	),
	fail.

% arithmetic predicates (portability and trivial fail checks)

'$lgt_compile_body'(Term is Exp, _, _, _, _) :-
	nonvar(Term),
	once((
		integer(Term),
		'$lgt_float_expression'(Exp)
		% integers and floats do not unify (per standard)
	;	float(Term),
		'$lgt_integer_expression'(Exp)
		% integers and floats do not unify (per standard)
	;	\+ number(Term)
		% the standard allows any term in the left side
	)),
	'$lgt_compiler_flag'(always_true_or_false_goals, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(always_true_or_false_goals), goal_is_always_false(File, Lines, Type, Entity, Term is Exp)),
	fail.

'$lgt_compile_body'(Term is Exp, _, _, _, _) :-
	var(Term),
	Term \== Exp,
	term_variables(Exp, ExpVariables),
	once('$lgt_member_var'(Term, ExpVariables)),
	% this could also be a "goal is always false" warning
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Term is Exp, reason(shared_variable(Term)))
	),
	fail.

'$lgt_compile_body'(_ is Exp, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp, Ctx),
	fail.

'$lgt_compile_body'(Exp1 =:= Exp2, _, _, _, Ctx) :-
	once((
		'$lgt_float_expression'(Exp1)
	;	'$lgt_float_expression'(Exp2)
	)),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(arithmetic_expressions),
		suspicious_call(File, Lines, Type, Entity, Exp1 =:= Exp2, reason(float_comparison))
	),
	fail.

'$lgt_compile_body'(Exp1 =:= Exp2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp1, Ctx),
	'$lgt_check_non_portable_functions'(Exp2, Ctx),
	fail.

'$lgt_compile_body'(Exp1 =\= Exp2, _, _, _, Ctx) :-
	once((float(Exp1); float(Exp2))),
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(arithmetic_expressions),
		suspicious_call(File, Lines, Type, Entity, Exp1 =\= Exp2, reason(float_comparison))
	),
	fail.

'$lgt_compile_body'(Exp1 =\= Exp2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp1, Ctx),
	'$lgt_check_non_portable_functions'(Exp2, Ctx),
	fail.

'$lgt_compile_body'(Exp1 < Exp2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp1, Ctx),
	'$lgt_check_non_portable_functions'(Exp2, Ctx),
	fail.

'$lgt_compile_body'(Exp1 =< Exp2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp1, Ctx),
	'$lgt_check_non_portable_functions'(Exp2, Ctx),
	fail.

'$lgt_compile_body'(Exp1 > Exp2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp1, Ctx),
	'$lgt_check_non_portable_functions'(Exp2, Ctx),
	fail.

'$lgt_compile_body'(Exp1 >= Exp2, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_check_non_portable_functions'(Exp1, Ctx),
	'$lgt_check_non_portable_functions'(Exp2, Ctx),
	fail.

% blackboard predicates (requires a backend Prolog compiler natively supporting these built-in predicates)

'$lgt_compile_body'(bb_put(Key, Term), _, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(bb_put(_, _)),
	\+ '$lgt_pp_defines_predicate_'(bb_put(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, _, _, _, _),
	(	atomic(Key) ->
		'$lgt_compile_bb_key'(Key, Prefix, TKey),
		TPred = bb_put(TKey, Term),
		DPred = '$lgt_debug'(goal(bb_put(Key, Term), TPred), ExCtx)
	;	var(Key) ->
		% runtime key translation
		TPred = ('$lgt_compile_bb_key'(Key, Prefix, TKey, bb_put(Key, Term)), bb_put(TKey, Term)),
		DPred = '$lgt_debug'(goal(bb_put(Key, Term), TPred), ExCtx)
	;	throw(type_error(atomic, Key))
	).

'$lgt_compile_body'(bb_get(Key, Term), _, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(bb_get(_, _)),
	\+ '$lgt_pp_defines_predicate_'(bb_get(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, _, _, _, _),
	(	atomic(Key) ->
		'$lgt_compile_bb_key'(Key, Prefix, TKey),
		TPred = bb_get(TKey, Term),
		DPred = '$lgt_debug'(goal(bb_get(Key, Term), TPred), ExCtx)
	;	var(Key) ->
		% runtime key translation
		TPred = ('$lgt_compile_bb_key'(Key, Prefix, TKey, bb_get(Key, Term)), bb_get(TKey, Term)),
		DPred = '$lgt_debug'(goal(bb_get(Key, Term), TPred), ExCtx)
	;	throw(type_error(atomic, Key))
	).

'$lgt_compile_body'(bb_delete(Key, Term), _, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(bb_delete(_, _)),
	\+ '$lgt_pp_defines_predicate_'(bb_delete(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, _, _, _, _),
	(	atomic(Key) ->
		'$lgt_compile_bb_key'(Key, Prefix, TKey),
		TPred = bb_delete(TKey, Term),
		DPred = '$lgt_debug'(goal(bb_delete(Key, Term), TPred), ExCtx)
	;	var(Key) ->
		% runtime key translation
		TPred = ('$lgt_compile_bb_key'(Key, Prefix, TKey, bb_delete(Key, Term)), bb_delete(TKey, Term)),
		DPred = '$lgt_debug'(goal(bb_delete(Key, Term), TPred), ExCtx)
	;	throw(type_error(atomic, Key))
	).

'$lgt_compile_body'(bb_update(Key, Term, New), _, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(bb_update(_, _, _)),
	\+ '$lgt_pp_defines_predicate_'(bb_update(_, _, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, _, _, _, _),
	(	atomic(Key) ->
		'$lgt_compile_bb_key'(Key, Prefix, TKey),
		TPred = bb_update(TKey, Term, New),
		DPred = '$lgt_debug'(goal(bb_update(Key, Term, New), TPred), ExCtx)
	;	var(Key) ->
		% runtime key translation
		TPred = ('$lgt_compile_bb_key'(Key, Prefix, TKey, bb_update(Key, Term, New)), bb_update(TKey, Term, New)),
		DPred = '$lgt_debug'(goal(bb_update(Key, Term, New), TPred), ExCtx)
	;	throw(type_error(atomic, Key))
	).

% call/2-N built-in control construct

'$lgt_compile_body'(CallN, Caller, TPred, DPred, Ctx) :-
	functor(CallN, call, Arity),
	Arity >= 2,
	CallN =.. [call, Closure| ExtraArgs],
	!,
	(	callable(Closure),
		\+ '$lgt_logtalk_control_construct'(Closure),
		Closure \= ':'(_, _),
		'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(suspicious_calls, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		Closure =.. [Functor| Args],
		'$lgt_append'(Args, ExtraArgs, FullArgs),
		Goal =.. [Functor| FullArgs],
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(suspicious_calls),
			suspicious_call(File, Lines, Type, Entity, CallN, [Goal])
		)
	;	true
	),
	'$lgt_check_closure'(Closure, Ctx),
	'$lgt_compile_body'('$lgt_callN'(Closure, ExtraArgs), Caller, TPred, DPred, Ctx).

% call to a meta-predicate from a user-defined meta-predicate;
% must check the number of arguments for shared closures
%
% note that getting the meta-predicate template for non-declared
% built-in meta-predicates or for module meta-predicates is fragile
% due to lack of standardization of meta-predicate specifications

'$lgt_compile_body'(Pred, _, _, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, [_| _], _, _, compile(_,_,_), _, _, _),
	% we're compiling a clause for a meta-predicate as the list of meta-variables is not empty
	functor(Pred, Name, Arity),
	\+ functor(Head, Name, Arity),
	% not a recursive call
	(	'$lgt_pp_meta_predicate_'(Pred, Meta, _, _) ->
		% local user-defined meta-predicate
		true
	;	'$lgt_prolog_meta_predicate'(Pred, Meta, predicate) ->
		% proprietary built-in meta-predicate declared in the adapter files
		true
	;	'$lgt_predicate_property'(Pred, built_in),
		catch('$lgt_predicate_property'(Pred, meta_predicate(Meta)), _, fail) ->
		% non-declared proprietary built-in meta-predicate
		true
	;	'$lgt_pp_use_module_predicate_'(Module, Original, Pred, _, _, _),
		nonvar(Module),
		catch('$lgt_predicate_property'(':'(Module, Original), meta_predicate(Meta)), _, fail) ->
		% meta-predicates specified in a use_module/2 directive
		true
	;	'$lgt_pp_uses_predicate_'(Obj, Original, Pred, _, _, _),
		Obj == user,
		catch('$lgt_predicate_property'(Original, meta_predicate(Meta)), _, fail) ->
		% Prolog meta-predicate undeclared in the adapter file (may not be a built-in)
		true
	;	fail
	),
	Pred =.. [_| PredArgs],
	Meta =.. [_| MetaArgs],
	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(MetaArgs, CMetaArgs),
	nonvar(Head),
	% ignore multifile predicates
	Head \= ':'(_, _),
	Head \= _::_,
	'$lgt_pp_meta_predicate_'(Head, HeadMeta, _, _),
	Head =.. [_| HeadArgs],
	HeadMeta =.. [_| HeadMetaArgs],
	'$lgt_same_number_of_closure_extra_args'(PredArgs, CMetaArgs, HeadArgs, HeadMetaArgs, HeadMeta, Meta),
	fail.

% predicates specified in use_module/2 directives

'$lgt_compile_body'(Alias, Caller, TPred, '$lgt_debug'(goal(Alias, TPred), ExCtx), Ctx) :-
	'$lgt_pp_use_module_predicate_'(Module, Pred, Alias, Ctx, _, _),
	(	Pred == Alias ->
		% no alias is defined
		true
	;	% check that we're renaming a predicate but not (also) changing its argument order as that
		% would break using the closure as a meta-argument when appending the additional arguments
		Pred =.. [_| PredArguments],
		Alias =.. [_| AliasArguments],
		PredArguments == AliasArguments ->
		true
	;	% we're renaming a predicate; use instead the generated auxiliary predicate
		fail
	),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_add_referenced_module_predicate'(Mode, Module, Pred, Alias, Head),
	'$lgt_compile_body'(':'(Module,Pred), Caller, TPred, _, Ctx).

% predicates specified in uses/2 directives
%
% in the case of predicates defined in the pseudo-object "user", the uses/2
% directive is typically used to help document dependencies on Prolog-defined
% predicates (usually, but not necessarily, built-in predicates)

'$lgt_compile_body'(Alias, Caller, TPred, DPred, Ctx) :-
	'$lgt_pp_uses_predicate_'(Obj, Pred, Alias, Ctx, _, _),
	(	Pred == Alias ->
		% no alias is defined
		true
	;	% check that we're renaming a predicate but not (also) changing its argument order as that
		% would break using the closure as a meta-argument when appending the additional arguments
		Pred =.. [_| PredArguments],
		Alias =.. [_| AliasArguments],
		PredArguments == AliasArguments ->
		true
	;	% we're renaming a predicate; use instead the generated auxiliary predicate
		fail
	),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	(	Obj == user ->
		(	(	'$lgt_prolog_meta_predicate'(Pred, Meta, Type)
				% built-in Prolog meta-predicate declared in the adapter file in use
			;	catch('$lgt_predicate_property'(Pred, meta_predicate(Meta)), _, fail)
				% Prolog meta-predicate undeclared in the adapter file (may not be a built-in)
			;	'$lgt_pp_meta_predicate_'(user::Pred, user::Meta, _, _)
				% we're either providing a meta-predicate template or overriding the original
				% meta-predicate template
			) ->
			% meta-predicate
			Pred =.. [Functor| Args],
			Meta =.. [Functor| MArgs],
			(	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(MArgs, CMArgs),
				'$lgt_compile_prolog_meta_arguments'(Args, CMArgs, meta, Ctx, TArgs, DArgs) ->
				TPred =.. [Functor| TArgs],
				DGoal =.. [Functor| DArgs],
				(	Type == control_construct ->
					DPred = DGoal
				;	DPred = '$lgt_debug'(goal(Alias, DGoal), ExCtx)
				)
			;	% meta-predicate template is not usable
				throw(domain_error(meta_predicate_template, Meta))
			)
		;	% non meta-predicate
			TPred = Pred,
			DPred = '$lgt_debug'(goal(Alias, Pred), ExCtx),
			'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx)
		),
		'$lgt_add_referenced_object_message'(Mode, Obj, Pred, Alias, Head)
	;	% objects other than the pseudo-object "user"
		'$lgt_add_referenced_object_message'(Mode, Obj, Pred, Alias, Head),
		'$lgt_compile_body'(Obj::Pred, Caller, TPred, _, Ctx),
		DPred = '$lgt_debug'(goal(Alias, TPred), ExCtx)
	).

% call to a dynamic predicate from within a category; the predicate
% is called instead in the context of the object importing the category
% that received the message under processing (implicit dynamic binding)

'$lgt_compile_body'(Pred, _, TPred, '$lgt_debug'(goal(Pred, TPred), ExCtx), Ctx) :-
	'$lgt_pp_category_'(_, _, _, _, _, _),
	'$lgt_pp_dynamic_'(Pred, _, _, _),
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	functor(Pred, Functor, Arity),
		\+ '$lgt_pp_public_'(Functor, Arity, _, _),
		\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
		\+ '$lgt_pp_private_'(Functor, Arity, _, _) ->
		% no scope directive
		TPred = '$lgt_call_in_this'(Pred, ExCtx)
	;	TPred = '$lgt_call_in_this_checked'(Pred, ExCtx)
	).

% non-callable terms

'$lgt_compile_body'(Pred, _, _, _, _) :-
	\+ callable(Pred),
	throw(type_error(callable, Pred)).

% runtime compilation of a call (usually a meta-call) to a user-defined predicate
%
% required to deal with meta-calls instantiated at runtime

'$lgt_compile_body'(Pred, _, TPred, '$lgt_debug'(goal(Pred, TPred), ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, Sender, This, Self, _, MetaVars, MetaCallCtx, ExCtx, runtime, Stack, _, _),
	nonvar(Entity),
	% in the most common case, we're meta-calling the predicate
	'$lgt_execution_context'(ExCtx, Entity, Sender, This, Self, MetaCallCtx, Stack),
	(	'$lgt_member_var'(Pred, MetaVars) ->
		% goal is a call to a user-defined predicate in sender (i.e., a meta-argument)
		TPred = '$lgt_metacall_sender'(Pred, ExCtx, MetaCallCtx, [])
	;	% goal is a local call to a user-defined predicate
		'$lgt_current_object_'(Entity, _, _, Def, _, _, _, _, DDef, _, _) ->
		(	call(Def, Pred, ExCtx, TPred)
		;	call(DDef, Pred, ExCtx, TPred)
		)
	;	'$lgt_current_category_'(Entity, _, _, Def, _, _),
		call(Def, Pred, ExCtx, TPred)
	),
	!.

% call to a local user-defined predicate

'$lgt_compile_body'(Pred, Caller, _, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, compile(user,_,_), _, Lines, Term),
	Caller \== phrase,
	Term \= (_ --> _),
	functor(Pred, Functor, ExtArity),
	'$lgt_pp_defines_non_terminal_'(Functor, Arity, ExtArity),
	\+ '$lgt_pp_calls_non_terminal_'(Functor, Arity, ExtArity, Lines),
	'$lgt_compiler_flag'(grammar_rules, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(grammar_rules),
		calls_non_terminal_as_predicate(File, Lines, Type, Entity, Functor//Arity)
	),
	fail.

'$lgt_compile_body'(Pred, _, TPred, DPred, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	Entity == user,
	% usually a call from an initialization or conditional compilation directive
	!,
	TPred = Pred,
	DPred = '$lgt_debug'(goal(Pred, TPred), ExCtx).

'$lgt_compile_body'(Pred, _, TPred, '$lgt_debug'(goal(DPred, TPred), ExCtx), Ctx) :-
	'$lgt_pp_coinductive_'(Pred, _, ExCtx, TCPred, _, _, DCPred, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, Lines, _),
	(	'$lgt_pp_defines_predicate_'(Pred, Functor/Arity, _, TPred0, _, _) ->
		'$lgt_check_for_trivial_fails'(Mode, Pred, TPred0, Head),
		% convert the call to the original coinductive predicate into a call to the auxiliary
		% predicate whose compiled normal and debug forms are already computed
		functor(TCPred, TCFunctor, TCArity),
		'$lgt_remember_called_predicate'(Mode, Functor/Arity, TCFunctor/TCArity, Head),
		TPred = TCPred,
		DPred = DCPred
	;	% undefined coinductive predicate
		functor(Pred, Functor, Arity),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		'$lgt_remember_called_predicate'(Mode, Functor/Arity, TFunctor/TArity, Head),
		% closed-world assumption: calls to static, declared but undefined
		% predicates must fail instead of throwing an exception,
		'$lgt_report_undefined_predicate_call'(Mode, Functor/Arity, Lines),
		TPred = fail,
		DPred = Pred
	).

'$lgt_compile_body'(Pred, _, TPred, '$lgt_debug'(goal(Pred, TPred), ExCtx), Ctx) :-
	'$lgt_pp_synchronized_'(Pred, Mutex, _, _),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, Lines, _),
	functor(Pred, Functor, Arity),
	\+ (nonvar(Head), functor(Head, Functor, Arity)),
	% not a recursive call
	!,
	(	'$lgt_pp_defines_predicate_'(Pred, _, ExCtx, TPred0, _, _) ->
		'$lgt_check_for_trivial_fails'(Mode, Pred, TPred0, Head),
		(	'$lgt_prolog_feature'(threads, supported) ->
			TPred = with_mutex(Mutex, TPred0)
		;	% in single-threaded systems, with_mutex/2 is equivalent to once/1
			TPred = once(TPred0)
		),
		functor(TPred0, TFunctor, TArity)
	;	% undefined synchronized predicate
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		% closed-world assumption: calls to static, declared but undefined
		% predicates must fail instead of throwing an exception,
		'$lgt_report_undefined_predicate_call'(Mode, Functor/Arity, Lines),
		TPred = fail
	),
	'$lgt_remember_called_predicate'(Mode, Functor/Arity, TFunctor/TArity, Head).

'$lgt_compile_body'(Pred, Caller, TPred, '$lgt_debug'(goal(Pred, TPred), ExCtx), Ctx) :-
	'$lgt_pp_defines_predicate_'(Pred, Functor/Arity, ExCtx, TPred0, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_check_for_trivial_fails'(Mode, Pred, TPred0, Head),
	functor(TPred0, TFunctor, TArity),
	(	(	'$lgt_pp_meta_predicate_'(Head, _, _, _) ->
			HeadIsMeta = true
		;	HeadIsMeta = false
		),
		'$lgt_pp_meta_predicate_'(Pred, Meta, _, _),
		% local user-defined meta-predicate
		Pred =.. [Functor| Args],
		Meta =.. [Functor| MArgs],
		'$lgt_wrap_local_meta_arguments'(MArgs, Args, HeadIsMeta, Caller, Ctx, TArgs0) ->
		'$lgt_append'(TArgs0, [ExCtx], TArgs),
		TPred =.. [TFunctor| TArgs]
	;	% non meta-predicate or runtime compilation of meta-arguments
		TPred = TPred0
	),
	'$lgt_remember_called_predicate'(Mode, Functor/Arity, TFunctor/TArity, Head).

% call to a foreign predicate but only when compiling a module as an object;
% this is fragile due to the lack of standards for Prolog foreign language interfaces;
% moreover, not all backend Prolog systems support a "foreign" predicate property

'$lgt_compile_body'(Pred, _, Pred, '$lgt_debug'(goal(Pred, Pred), ExCtx), Ctx) :-
	'$lgt_pp_module_'(_),
	% not all backend Prolog systems support a "foreign" predicate property
	catch('$lgt_predicate_property'(Pred, foreign), _, fail),
	\+ '$lgt_prolog_built_in_predicate'(Pred),
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

% implicit call to a module predicate with a missing use_module/2 directive
% but only when compiling a module as an object; in practice, this is only
% usable from backend systems with an autoload mechanism

'$lgt_compile_body'(Pred, _, ':'(Module,Pred), '$lgt_debug'(goal(Pred, ':'(Module,Pred)), ExCtx), Ctx) :-
	'$lgt_pp_module_'(Current),
	\+ '$lgt_prolog_built_in_predicate'(Pred),
	'$lgt_find_visible_module_predicate'(Current, Module, Pred),
	!,
	functor(Pred, Functor, Arity),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_remember_missing_use_module_directive'(Mode, Module, Functor/Arity).

% call to a declared but undefined predicate

'$lgt_compile_body'(Pred, _, TPred, '$lgt_debug'(goal(Pred, TPred), ExCtx), Ctx) :-
	(	'$lgt_pp_dynamic_'(Pred, _, _, _)
	;	'$lgt_pp_multifile_'(Pred, _, _, _)
	),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, _, _),
	functor(Pred, Functor, Arity),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(TPred, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(Pred, TPred, ExCtx),
	'$lgt_remember_called_predicate'(Mode, Functor/Arity, TFunctor/TArity, Head).

'$lgt_compile_body'(Pred, _, fail, '$lgt_debug'(goal(Pred, fail), ExCtx), Ctx) :-
	% take into account the common practice of defining
	% Prolog modules that export built-in predicates
	(	'$lgt_pp_module_'(_) ->
		% compiling a module as an object
		\+ '$lgt_built_in_predicate'(Pred)
	;	true
	),
	functor(Pred, Functor, Arity),
	(	'$lgt_pp_public_'(Functor, Arity, _, _)
	;	'$lgt_pp_protected_'(Functor, Arity, _, _)
	;	'$lgt_pp_private_'(Functor, Arity, _, _)
	;	'$lgt_pp_synchronized_'(Pred, _, _, _)
	;	'$lgt_pp_coinductive_head_'(Pred, _, _)
	;	'$lgt_pp_discontiguous_'(Pred, _, _)
	),
	!,
	% closed-world assumption: calls to static, non-multifile, declared
	% but undefined predicates must fail instead of throwing an exception
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, Lines, _),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	'$lgt_remember_called_predicate'(Mode, Functor/Arity, TFunctor/TArity, Head),
	'$lgt_report_undefined_predicate_call'(Mode, Functor/Arity, Lines).

% call to a deprecated Prolog built-in predicate

'$lgt_compile_body'(Pred, Caller, TPred, DPred, Ctx) :-
	(	'$lgt_prolog_deprecated_built_in_predicate_hook'(Pred, RPred) ->
		true
	;	'$lgt_prolog_deprecated_built_in_predicate'(Pred, RPred)
	),
	'$lgt_prolog_built_in_predicate'(Pred),
	\+ '$lgt_pp_defines_predicate_'(Pred, _, _, _, _, _),
	!,
	% replace it with a call to the standard alternative
	'$lgt_compile_body'(RPred, Caller, TPred, DPred, Ctx),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		functor(Pred, Functor, Arity),
		functor(RPred, RFunctor, RArity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, Functor/Arity, RFunctor/RArity)
		)
	;	true
	).

'$lgt_compile_body'(Pred, _, _, _, Ctx) :-
	(	'$lgt_prolog_deprecated_built_in_predicate_hook'(Pred) ->
		true
	;	'$lgt_prolog_deprecated_built_in_predicate'(Pred)
	),
	'$lgt_prolog_built_in_predicate'(Pred),
	\+ '$lgt_pp_defines_predicate_'(Pred, _, _, _, _, _),
	% no standard alternative; just print a warning
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(deprecated, warning),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_pp_entity_'(Type, Entity, _),
	functor(Pred, Functor, Arity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(deprecated),
		deprecated_predicate(File, Lines, Type, Entity, Functor/Arity)
	),
	fail.

% call to a Prolog built-in predicate

'$lgt_compile_body'(Pred, _, _, _, Ctx) :-
	'$lgt_prolog_built_in_predicate'(Pred),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	logtalk_linter_hook(Pred, Flag, File, Lines, Type, Entity, Warning),
	nonvar(Flag),
	'$lgt_valid_flag'(Flag),
	'$lgt_compiler_flag'(Flag, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(Flag), Warning),
	fail.

'$lgt_compile_body'(Pred, _, TPred, DPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(Pred),
	!,
	(	(	'$lgt_pp_meta_predicate_'(Pred, Meta, _, _),
			Type = predicate
			% we're either overriding the original meta-predicate template or working around a
			% backend Prolog compiler limitation in providing access to meta-predicate templates
		;	'$lgt_prolog_meta_predicate'(Pred, Meta, Type)
			% built-in Prolog meta-predicate declared in the adapter file in use
		;	% lack of standardization of the predicate_property/2 predicate
			% means that the next call may fail to recognize the predicate as
			% a meta-predicate and retrieve a usable meta-predicate template
			catch('$lgt_predicate_property'(Pred, meta_predicate(Meta)), _, fail),
			Type = predicate
		) ->
		% meta-predicate
		Pred =.. [Functor| Args],
		Meta =.. [Functor| MArgs],
		(	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(MArgs, CMArgs),
			(	'$lgt_prolog_phrase_predicate'(Pred) ->
				NewCaller = phrase
			;	NewCaller = meta
			),
			'$lgt_compile_prolog_meta_arguments'(Args, CMArgs, NewCaller, Ctx, TArgs, DArgs) ->
			TGoal =.. [Functor| TArgs],
			DGoal =.. [Functor| DArgs],
			'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
			TPred = TGoal,
			(	Type == control_construct ->
				DPred = DGoal
			;	DPred = '$lgt_debug'(goal(Pred, DGoal), ExCtx)
			),
			'$lgt_check_non_portable_prolog_built_in_call'(Mode, Pred)
		;	% meta-predicate template is not usable
			throw(domain_error(meta_predicate_template, Meta))
		)
	;	% non meta-predicate
		TPred = Pred,
		DPred = '$lgt_debug'(goal(Pred, Pred), ExCtx),
		'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		'$lgt_check_non_portable_prolog_built_in_call'(Mode, Pred),
		'$lgt_check_for_tautology_or_falsehood_goal'(Mode, Pred)
	).

% call to a Logtalk built-in predicate (that is not already handled)

'$lgt_compile_body'(Pred, _, Pred, '$lgt_debug'(goal(Pred, Pred), ExCtx), Ctx) :-
	'$lgt_logtalk_built_in_predicate'(Pred, _),
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

% call to a unknown predicate

'$lgt_compile_body'(Pred, _, TPred, '$lgt_debug'(goal(Pred, TPred), ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, Lines, _),
	functor(Pred, Functor, Arity),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(TPred, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(Pred, TPred, ExCtx),
	'$lgt_remember_called_predicate'(Mode, Functor/Arity, TFunctor/TArity, Head),
	'$lgt_report_unknown_predicate_call'(Mode, Functor/Arity, Lines),
	!.

% unexpected compilation failure

'$lgt_compile_body'(Pred, _, _, _, _) :-
	throw(domain_error(goal, Pred)).



% wrap meta-arguments when a meta-predicate calls another local meta-predicate
% with meta-arguments that are not meta-arguments of the caller
%
% when the caller is not a meta-predicate, no wrapping is required as the
% meta-call context is empty
%
% also optimize bound meta-arguments, which are calls to local predicates,
% when not compiling in debug mode

'$lgt_wrap_local_meta_arguments'([], [], _, _, _, []).

'$lgt_wrap_local_meta_arguments'([MArg| MArgs], [Arg| Args], HeadIsMeta, Caller, Ctx, [WArg| WArgs]) :-
	(	var(Arg) ->
		WArg = Arg
	;	'$lgt_wrap_local_meta_argument'(MArg, Arg, HeadIsMeta, Caller, Ctx, WArg)
	),
	'$lgt_wrap_local_meta_arguments'(MArgs, Args, HeadIsMeta, Caller, Ctx, WArgs).


'$lgt_wrap_local_meta_argument'((*), Arg, _, _, _, Arg) :-
	!.

'$lgt_wrap_local_meta_argument'((::), Arg, _, _, _, Arg) :-
	!.

'$lgt_wrap_local_meta_argument'(MArg, Arg, HeadIsMeta, Caller, Ctx, WArg) :-
	(	'$lgt_compiler_flag'(debug, off),
		'$lgt_compile_static_binding_meta_argument'(MArg, Arg, Caller, Ctx, WArg) ->
		true
	;	HeadIsMeta == true ->
		WArg = '$lgt_local'(Arg)
	;	WArg = Arg
	).



% bagof/3 and setof/3 existential quantifiers

'$lgt_compile_quantified_body'(Term^Pred, Caller, Term^TPred, Term^DPred, Ctx) :-
	!,
	(	var(Pred) ->
		% meta-call resolved at runtime
		'$lgt_compile_body'(Pred, Caller, TPred, DPred, Ctx)
	;	% we can have Term1^Term2^...^Pred
		'$lgt_compile_quantified_body'(Pred, Caller, TPred, DPred, Ctx)
	).

'$lgt_compile_quantified_body'(Pred, Caller, TPred, DPred, Ctx) :-
	'$lgt_compile_body'(Pred, Caller, TPred, DPred, Ctx).



% compute list of existentially qualified variables not occurring in the qualified goal

'$lgt_missing_existential_variables'(QGoal, Variables, Goal) :-
	'$lgt_decompose_quantified_body'(QGoal, Terms, Goal),
	term_variables(Terms, ExistentialVariables),
	term_variables(Goal, GoalVariables),
	'$lgt_filter_missing_existential_variables'(ExistentialVariables, GoalVariables, Variables).


'$lgt_decompose_quantified_body'(Term^Pred, [Term| Terms], Goal) :-
	!,
	(	var(Pred) ->
		!,
		fail
	;	'$lgt_decompose_quantified_body'(Pred, Terms, Goal)
	).

'$lgt_decompose_quantified_body'(Goal, [], Goal).


'$lgt_filter_missing_existential_variables'([], _, []).

'$lgt_filter_missing_existential_variables'([Variable| ExistentialVariables], GoalVariables, Variables) :-
	'$lgt_member_var'(Variable, GoalVariables),
	!,
	'$lgt_filter_missing_existential_variables'(ExistentialVariables, GoalVariables, Variables).

'$lgt_filter_missing_existential_variables'([Variable| ExistentialVariables], GoalVariables, [Variable| Variables]) :-
	'$lgt_filter_missing_existential_variables'(ExistentialVariables, GoalVariables, Variables).



% auxiliary predicate to find a singleton variable in the goal argument of
% bagof/3 and setof/3 calls

'$lgt_singleton_variables_in_meta_argument'(Goal, Singletons, Ctx) :-
	Goal \= _/_,
	% not a lambda expression
	term_variables(Goal, Variables),
	'$lgt_comp_ctx_term'(Ctx, Term),
	bagof(
		Singleton,
		'$lgt_singleton_variable_in_meta_argument'(Variables, Term, Singleton),
		Singletons
	).


'$lgt_singleton_variable_in_meta_argument'([Variable| _], Term, Variable) :-
	'$lgt_count_variable_occurrences'(Term, Variable, 1).

'$lgt_singleton_variable_in_meta_argument'([_| Variables], Term, Variable) :-
	'$lgt_singleton_variable_in_meta_argument'(Variables, Term, Variable).



% '$lgt_fix_disjunction_left_side'(@var_or_callable, -callable)
%
% check if the compilation of the disjunction left-side produced an if-then or
% a soft-cut (e.g., due to goal-expansion) and fix it if necessary to avoid
% converting the disjunction into an if-then-else or a soft-cut with an else part

'$lgt_fix_disjunction_left_side'(Goal0, Goal) :-
	(	var(Goal0) ->
		Goal = Goal0
	;	Goal0 = (_ -> _) ->
		Goal = (Goal0, true)
	;	Goal0 = '*->'(_, _),
		'$lgt_predicate_property'('*->'(_, _), built_in) ->
		Goal = (Goal0, true)
	;	Goal = Goal0
	).



% '$lgt_negated_goal_alternative'(Pred, Alt)
%
% auxiliary table for \+ Goal linter warnings

'$lgt_negated_goal_alternative'(call(Goal), \+ Goal).
'$lgt_negated_goal_alternative'(once(Goal), \+ Goal).
'$lgt_negated_goal_alternative'(Term1 = Term2, Term1 \= Term2).
'$lgt_negated_goal_alternative'(Term1 == Term2, Term1 \== Term2).
'$lgt_negated_goal_alternative'(Term1 =:= Term2, Term1 =\= Term2).
'$lgt_negated_goal_alternative'(var(Term), nonvar(Term)).



% '$lgt_module_qualified_clause'(@nonvar, -atom, -clause, -term, -term)
%
% decomposes a an explicitly module qualified clause

'$lgt_module_qualified_clause'(QClause, Module, Clause, Head, Body) :-
	(	QClause = (QHead :- Body),
		nonvar(QHead),
		QHead = ':'(Module,Head) ->
		Clause = (Head :- Body)
	;	QClause = ':'(Module,Head),
		Clause = Head,
		Body = true
	).



% '$lgt_compile_error_method'(+compilation_context, -compound)
%
% compiles a call to one of the built-in error methods;
% these methods are shorthands to context/1 + throw/1

'$lgt_compile_error_method'(Exception, TPred, DPred, Ctx) :-
	'$lgt_comp_ctx_head'(Ctx, Head0),
	(	Head0 = _::Head ->
		% object (or category) multifile predicate clause
		true
	;	Head0 = ':'(_,Head) ->
		% module multifile predicate clause
		true
	;	% non-multifile predicate
		Head0 = Head
	),
	'$lgt_comp_ctx_head_exec_ctx'(Ctx, ExCtx),
	TPred = throw(error(Exception, logtalk(Head, ExCtx))),
	DPred = '$lgt_debug'(goal(Exception, TPred), ExCtx).



% '$lgt_check_read_term_options'(@compilation_mode, @predicate_indicator, @term)
%
% check read term options portability

'$lgt_check_read_term_options'(runtime, _, _).

'$lgt_check_read_term_options'(compile(_,_,_), Predicate, Options) :-
	(	'$lgt_is_list'(Options),
		'$lgt_compiler_flag'(portability, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_member'(Option, Options),
		nonvar(Option),
		\+ '$lgt_iso_spec_read_term_option'(Option) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			non_standard_predicate_option(File, Lines, Type, Entity, Predicate, Option)
		)
	;	true
	).



% '$lgt_check_write_term_options'(@compilation_mode, @predicate_indicator, @term)
%
% check write term options portability

'$lgt_check_write_term_options'(runtime, _, _).

'$lgt_check_write_term_options'(compile(_,_,_), Predicate, Options) :-
	(	'$lgt_is_list'(Options),
		'$lgt_compiler_flag'(portability, warning),
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_member'(Option, Options),
		nonvar(Option),
		\+ '$lgt_iso_spec_write_term_option'(Option) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			non_standard_predicate_option(File, Lines, Type, Entity, Predicate, Option)
		)
	;	true
	).



% '$lgt_check_open_stream_options'(@compilation_mode, @predicate_indicator, @term)
%
% check open stream options portability

'$lgt_check_open_stream_options'(runtime, _, _).

'$lgt_check_open_stream_options'(compile(_,_,_), Predicate, Options) :-
	'$lgt_is_list'(Options),
	'$lgt_compiler_flag'(portability, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	(	'$lgt_member'(Option, Options),
		nonvar(Option),
		\+ '$lgt_iso_spec_open_stream_option'(Option) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			non_standard_predicate_option(File, Lines, Type, Entity, Predicate, Option)
		)
	;	true
	).



% '$lgt_check_for_meta_predicate_directive'(@compilation_mode, @callable, @term)
%
% remember missing meta_predicate/1 directives

'$lgt_check_for_meta_predicate_directive'(runtime, _, _).

'$lgt_check_for_meta_predicate_directive'(compile(aux,_,_), _, _) :-
	!.

'$lgt_check_for_meta_predicate_directive'(compile(user,_,_), _::_, _) :-
	% clause for multifile object predicate
	!.
'$lgt_check_for_meta_predicate_directive'(compile(user,_,_), ':'(_, _), _) :-
	% clause for multifile module predicate
	!.
'$lgt_check_for_meta_predicate_directive'(compile(user,_,_), Head, MetaArg) :-
	'$lgt_term_template'(Head, Template),
	(	'$lgt_pp_meta_predicate_'(Template, _, _, _) ->
		% meta_predicate/1 directive is present
		true
	;	'$lgt_pp_missing_meta_predicate_directive_'(Template, _, _) ->
		% missing meta_predicate/1 directive already recorded
		true
	;	term_variables(MetaArg, MetaArgVars),
		term_variables(Head, HeadVars),
		'$lgt_member'(MetaArgVar, MetaArgVars),
		'$lgt_member_var'(MetaArgVar, HeadVars) ->
		% the meta-argument is a head argument
		'$lgt_source_file_context'(File, Lines),
		% delay reporting to the end of entity compilation to avoid repeated reports for
		% the same missing directive when a meta-predicate have two or more clauses
		assertz('$lgt_pp_missing_meta_predicate_directive_'(Template, File, Lines))
	;	true
	).



% '$lgt_check_non_portable_prolog_built_in_call'(@compilation_mode, @callable)
%
% remember non-portable Prolog built-in predicate calls

'$lgt_check_non_portable_prolog_built_in_call'(runtime, _).

'$lgt_check_non_portable_prolog_built_in_call'(compile(aux,_,_), _) :-
	!.

'$lgt_check_non_portable_prolog_built_in_call'(compile(user,_,_), Pred) :-
	(	\+ '$lgt_pp_non_portable_predicate_'(Pred, _, _),
		% not already recorded as a non portable call
		\+ '$lgt_iso_spec_predicate'(Pred) ->
		% bona fide non-portable Prolog built-in predicate
		'$lgt_term_template'(Pred, Template),
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_non_portable_predicate_'(Template, File, Lines))
	;	true
	).



% '$lgt_check_for_tautology_or_falsehood_goal'(@compilation_mode, @callable)
%
% check for likely typos in ground calls to some Prolog built-in predicates
% that result in either tautologies or falsehoods

'$lgt_check_for_tautology_or_falsehood_goal'(runtime, _).

'$lgt_check_for_tautology_or_falsehood_goal'(compile(aux,_,_), _) :-
	!.

'$lgt_check_for_tautology_or_falsehood_goal'(compile(user,_,_), Goal) :-
	(	ground(Goal),
		% exclude already handled linter check for (\=)/2 goals
		\+ functor(Goal, (\=), 2),
		'$lgt_compiler_flag'(always_true_or_false_goals, warning),
		(	'$lgt_candidate_tautology_or_falsehood_goal'(Goal)
		;	'$lgt_candidate_tautology_or_falsehood_goal_hook'(Goal)
		) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		(	catch(Goal, Error, true) ->
			(	var(Error) ->
				'$lgt_print_message'(
					warning(always_true_or_false_goals),
					goal_is_always_true(File, Lines, Type, Entity, Goal)
				)
			;	'$lgt_print_message'(
					warning(always_true_or_false_goals),
					goal_is_always_error(File, Lines, Type, Entity, Goal, Error)
				)
			)
		;	'$lgt_print_message'(
				warning(always_true_or_false_goals),
				goal_is_always_false(File, Lines, Type, Entity, Goal)
			)
		)
	;	true
	).



% '$lgt_check_for_trivial_fails'(@compilation_mode, @callable, @callable, @callable)
%
% check for trivial fails due to no matching local clause being available for a goal;
% this check is only performed for local static predicates as dynamic or multifile
% predicates can get new clauses at runtime

'$lgt_check_for_trivial_fails'(runtime, _, _, _).

'$lgt_check_for_trivial_fails'(compile(aux,_,_), _, _, _) :-
	!.

'$lgt_check_for_trivial_fails'(compile(user,_,_), Goal, TGoal, Head) :-
	(	'$lgt_compiler_flag'(trivial_goal_fails, warning),
		% workaround possible creation of a cyclic term with some backend
		% Prolog compilers implementation of the \=2 predicate
		copy_term(Head, HeadCopy),
		Goal \= HeadCopy,
		% not a recursive call which can originate from a predicate with a single clause
		\+ '$lgt_pp_dynamic_'(Goal, _, _, _),
		\+ '$lgt_pp_multifile_'(Goal, _, _, _),
		% not a dynamic or multifile predicate
		\+ '$lgt_pp_entity_term_'(fact(TGoal, _), _, _),
		\+ '$lgt_pp_entity_term_'(srule(TGoal, _, _), _, _),
		\+ '$lgt_pp_entity_term_'(dfact(TGoal, _, _), _, _),
		\+ '$lgt_pp_entity_term_'(dsrule(TGoal, _, _, _), _, _),
		% not a yet to be compiled user-defined fact or rule
		\+ '$lgt_pp_final_entity_term_'(TGoal, _),
		\+ '$lgt_pp_final_entity_term_'((TGoal :- _), _),
		% not an already compiled user-defined fact or rule
		\+ '$lgt_pp_entity_aux_clause_'(fact(TGoal, _)),
		\+ '$lgt_pp_entity_aux_clause_'(srule(TGoal, _, _)),
		\+ '$lgt_pp_entity_aux_clause_'(dfact(TGoal, _, _)),
		\+ '$lgt_pp_entity_aux_clause_'(dsrule(TGoal, _, _, _)),
		% not a yet to be compiled auxiliary fact or rule
		\+ '$lgt_pp_final_entity_aux_clause_'(TGoal),
		\+ '$lgt_pp_final_entity_aux_clause_'((TGoal :- _)) ->
		% not an already compiled auxiliary fact or rule
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		(	functor(Goal, Functor, Arity),
			'$lgt_pp_calls_non_terminal_'(Functor, _, Arity, _) ->
			Goal =.. [Functor| GoalArgs],
			'$lgt_append'(NonTerminalArgs, [_, _], GoalArgs), !,
			NonTerminal =.. [Functor| NonTerminalArgs],
			Message = no_matching_clause_for_non_terminal_goal(File, Lines, Type, Entity, NonTerminal)
		;	Message = no_matching_clause_for_predicate_goal(File, Lines, Type, Entity, Goal)
		),
		'$lgt_print_message'(warning(trivial_goal_fails), Message)
	;	true
	).



% '$lgt_candidate_tautology_or_falsehood_goal'(@callable)
%
% valid candidates are standard built-in predicates with
% no side-effects when called with ground arguments

% unification
'$lgt_candidate_tautology_or_falsehood_goal'(_ = _).
'$lgt_candidate_tautology_or_falsehood_goal'(unify_with_occurs_check(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(_ \= _).
% term comparison
'$lgt_candidate_tautology_or_falsehood_goal'(_ == _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ \== _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ @< _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ @=< _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ @> _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ @>= _).
% arithmetic comparison
'$lgt_candidate_tautology_or_falsehood_goal'(_ < _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ =< _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ > _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ >= _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ =:= _).
'$lgt_candidate_tautology_or_falsehood_goal'(_ =\= _).
'$lgt_candidate_tautology_or_falsehood_goal'(compare(_, _, _)).
% type testing
'$lgt_candidate_tautology_or_falsehood_goal'(acyclic_term(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(atom(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(atomic(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(callable(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(compound(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(float(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(ground(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(integer(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(nonvar(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(number(_)).
'$lgt_candidate_tautology_or_falsehood_goal'(var(_)).
% term creation and decomposition
'$lgt_candidate_tautology_or_falsehood_goal'(_ =.. _).
'$lgt_candidate_tautology_or_falsehood_goal'(arg(_, _, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(copy_term(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(functor(_, _, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(subsumes_term(_, _)).
% atomic term processing
'$lgt_candidate_tautology_or_falsehood_goal'(atom_length(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(atom_concat(_, _, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(sub_atom(_, _, _, _, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(atom_chars(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(atom_codes(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(char_code(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(number_chars(_, _)).
'$lgt_candidate_tautology_or_falsehood_goal'(number_codes(_, _)).



% '$lgt_remember_called_predicate'(@compilation_mode, +predicate_indicator, +predicate_indicator, @callable)
%
% used for checking calls to undefined predicates and for collecting cross-referencing information

'$lgt_remember_called_predicate'(runtime, _, _, _).

'$lgt_remember_called_predicate'(compile(_,_,_), Functor/Arity, TFunctor/TArity, Head) :-
	% currently, the returned line numbers are for the start and end lines of the clause containing the call
	(	Head = Object::Predicate ->
		% call from the body of a Logtalk multifile predicate clause
		Caller = Object::HeadFunctor/HeadArity
	;	Head = ':'(Module,Predicate) ->
		% call from the body of a Prolog module multifile predicate clause
		Caller = ':'(Module,HeadFunctor/HeadArity)
	;	% call from the body of a local entity clause
		Head = Predicate,
		Caller = HeadFunctor/HeadArity
	),
	functor(Predicate, HeadFunctor, HeadArity),
	(	'$lgt_source_file_context'(File, Lines) ->
		true
	;	'$lgt_pp_file_paths_flags_'(_, _, File, _, _),
		Lines = '-'(0, 0)
	),
	(	Caller == Functor/Arity ->
		% recursive call
		(	retract('$lgt_pp_predicate_recursive_calls_'(Functor, Arity, Count0)) ->
			Count is Count0 + 1,
			assertz('$lgt_pp_predicate_recursive_calls_'(Functor, Arity, Count))
		;	assertz('$lgt_pp_predicate_recursive_calls_'(Functor, Arity, 1))
		)
	;	'$lgt_pp_calls_predicate_'(Functor/Arity, _, Caller, File, Lines) ->
		% already recorded for the current clause being compiled
		true
	;	assertz('$lgt_pp_calls_predicate_'(Functor/Arity, TFunctor/TArity, Caller, File, Lines))
	).



% '$lgt_remember_called_self_predicate'(@compilation_mode, +predicate_indicator, @callable)
%
% used for checking calls to undefined predicates and for collecting cross-referencing information

'$lgt_remember_called_self_predicate'(runtime, _, _).

'$lgt_remember_called_self_predicate'(compile(aux,_,_), _, _) :-
	!.

'$lgt_remember_called_self_predicate'(compile(user,_,_), Functor/Arity, Head) :-
	% currently, the returned line numbers are for the start and end lines of the clause containing the call
	(	Head = Object::Predicate ->
		% call from the body of a Logtalk multifile predicate clause
		Caller = Object::HeadFunctor/HeadArity
	;	Head = ':'(Module,Predicate) ->
		% call from the body of a Prolog module multifile predicate clause
		Caller = ':'(Module,HeadFunctor/HeadArity)
	;	% call from the body of a local entity clause
		Head = Predicate,
		Caller = HeadFunctor/HeadArity
	),
	functor(Predicate, HeadFunctor, HeadArity),
	'$lgt_source_file_context'(File, Lines),
	(	'$lgt_pp_calls_self_predicate_'(Functor/Arity, Caller, File, Lines) ->
		% already recorded for the current clause being compiled (however unlikely!)
		true
	;	assertz('$lgt_pp_calls_self_predicate_'(Functor/Arity, Caller, File, Lines))
	).



% '$lgt_remember_called_super_predicate'(@compilation_mode, +predicate_indicator, @callable)
%
% used for checking calls to undefined predicates and for collecting cross-referencing information

'$lgt_remember_called_super_predicate'(runtime, _, _).

'$lgt_remember_called_super_predicate'(compile(aux,_,_), _, _) :-
	!.

'$lgt_remember_called_super_predicate'(compile(user,_,_), Functor/Arity, Head) :-
	% currently, the returned line numbers are for the start and end lines of the clause containing the call
	(	Head = Object::Predicate ->
		% call from the body of a Logtalk multifile predicate clause
		Caller = Object::HeadFunctor/HeadArity
	;	Head = ':'(Module,Predicate) ->
		% call from the body of a Prolog module multifile predicate clause
		Caller = ':'(Module,HeadFunctor/HeadArity)
	;	% call from the body of a local entity clause
		Head = Predicate,
		Caller = HeadFunctor/HeadArity
	),
	functor(Predicate, HeadFunctor, HeadArity),
	'$lgt_source_file_context'(File, Lines),
	(	'$lgt_pp_calls_super_predicate_'(Functor/Arity, Caller, File, Lines) ->
		% already recorded for the current clause being compiled (however unlikely!)
		true
	;	assertz('$lgt_pp_calls_super_predicate_'(Functor/Arity, Caller, File, Lines))
	).



% '$lgt_remember_missing_use_module_directive'(@compilation_mode, +atom, +predicate_indicator)
%
% used only for reporting implicit calls to module predicates with missing use_module/2
% directives when compiling modules as objects

'$lgt_remember_missing_use_module_directive'(runtime, _, _).

'$lgt_remember_missing_use_module_directive'(compile(aux,_,_), _, _) :-
	!.

'$lgt_remember_missing_use_module_directive'(compile(user,_,_), Module, Functor/Arity) :-
	(	'$lgt_pp_missing_use_module_directive_'(Module, Functor/Arity) ->
		% already recorded
		true
	;	assertz('$lgt_pp_missing_use_module_directive_'(Module, Functor/Arity))
	).



% '$lgt_remember_updated_predicate'(@compilation_mode, @term, @callable)
%
% used for collecting cross-referencing information

'$lgt_remember_updated_predicate'(runtime, _, _).

'$lgt_remember_updated_predicate'(compile(aux,_,_), _, _) :-
	!.

'$lgt_remember_updated_predicate'(compile(user,_,_), Dynamic, Head) :-
	% currently, the returned line numbers are for the start and end lines of the clause containing the call
	(	Head = Object::Predicate ->
		% update from the body of a Logtalk multifile predicate clause
		Updater = Object::HeadFunctor/HeadArity
	;	Head = ':'(Module,Predicate) ->
		% update from the body of a Prolog module multifile predicate clause
		Updater = ':'(Module,HeadFunctor/HeadArity)
	;	% update from the body of a local entity clause
		Head = Predicate,
		Updater = HeadFunctor/HeadArity
	),
	functor(Predicate, HeadFunctor, HeadArity),
	'$lgt_source_file_context'(File, Lines),
	(	'$lgt_pp_updates_predicate_'(Dynamic, Updater, File, Lines) ->
		% already recorded for the current clause being compiled (however unlikely!)
		true
	;	assertz('$lgt_pp_updates_predicate_'(Dynamic, Updater, File, Lines))
	).



% '$lgt_bagof'(?term, ?term, ?term, +execution_context, +atom)
%
% handles bagof/3 calls with goals only known at runtime

'$lgt_bagof'(Term, QGoal, List, ExCtx, Where) :-
	'$lgt_convert_quantified_goal'(QGoal, Goal, '$lgt_quantified_metacall'(Goal, ExCtx, Where), TQGoal),
	bagof(Term, TQGoal, List).



% '$lgt_setof'(?term, ?term, ?term, +execution_context)
%
% handles setof/3 calls with goals only known at runtime

'$lgt_setof'(Term, QGoal, List, ExCtx, Where) :-
	'$lgt_convert_quantified_goal'(QGoal, Goal, '$lgt_quantified_metacall'(Goal, ExCtx, Where), TQGoal),
	setof(Term, TQGoal, List).



% '$lgt_convert_quantified_goal'(@callable, -callable, +callable, -callable)
%
% converts a ^/2 goal at runtime (used with bagof/3 and setof/3 calls)
%
% returns both the original goal without existential variables and the compiled
% goal that will be used as the argument for the bagof/3 and setof/3 calls

'$lgt_convert_quantified_goal'(Goal, Goal, TGoal, TGoal) :-
	var(Goal),
	!.

'$lgt_convert_quantified_goal'(Var^Term, Goal, TGoal, Var^TTerm) :-
	!,
	'$lgt_convert_quantified_goal'(Term, Goal, TGoal, TTerm).

'$lgt_convert_quantified_goal'(Goal, Goal, TGoal, TGoal).



% '$lgt_generate_aux_predicate_functor'(+atom, -atom)
%
% generates a new functor for an auxiliary predicate
% based on a base atom and an entity global counter

'$lgt_generate_aux_predicate_functor'(Base, Functor) :-
	(	retract('$lgt_pp_aux_predicate_counter_'(Old)) ->
		New is Old + 1
	;	New is 1
	),
	asserta('$lgt_pp_aux_predicate_counter_'(New)),
	number_codes(New, NewCodes),
	atom_codes(NewAtom, NewCodes),
	atom_concat(Base, NewAtom, Functor).



% '$lgt_compile_bb_key'(@term, +atom, -atom)
%
% compile-time translation of a blackboard key

'$lgt_compile_bb_key'(Key, Prefix, TKey) :-
	(	atom(Key) ->
		atom_concat(Prefix, Key, TKey)
	;	integer(Key) ->
		number_codes(Key, KeyCodes),
		atom_codes(AtomKey, KeyCodes),
		atom_concat(Prefix, AtomKey, TKey)
	;	throw(type_error(atomic, Key))
	).



% '$lgt_compile_bb_key'(@term, +atom, -atom, @callable)
%
% runtime translation of a blackboard key

'$lgt_compile_bb_key'(Key, Prefix, TKey, Goal) :-
	(	var(Key) ->
		throw(error(instantiation_error, Goal))
	;	atomic(Key) ->
		'$lgt_compile_bb_key'(Key, Prefix, TKey)
	;	throw(error(type_error(atomic, Key), Goal))
	).



% '$lgt_threaded'(+callable, +execution_context, +atom)
%
% handling of threaded/1 calls when the argument is only bound at runtime

'$lgt_threaded'(Goals, ExCtx, Where) :-
	(	var(Goals) ->
		throw(error(instantiation_error, threaded(Goals)))
	;	\+ callable(Goals) ->
		throw(error(type_error(callable, Goals), threaded(Goals)))
	;	'$lgt_runtime_threaded_call'(Goals, MTGoals, ExCtx, Where),
		call(MTGoals)
	).



% '$lgt_runtime_threaded_call'(+callable, -callable, +execution_context, +atom)
%
% runtime compilation of the argument of a call to the built-in predicate threaded/1

'$lgt_runtime_threaded_call'((Goal; Goals), '$lgt_threaded_or'(Queue, MTGoals, Results), ExCtx, Where) :-
	!,
	'$lgt_runtime_threaded_or_call'((Goal; Goals), Queue, MTGoals, Results, ExCtx, Where).

'$lgt_runtime_threaded_call'((Goal, Goals), '$lgt_threaded_and'(Queue, MTGoals, Results), ExCtx, Where) :-
	!,
	'$lgt_runtime_threaded_and_call'((Goal, Goals), Queue, MTGoals, Results, ExCtx, Where).

'$lgt_runtime_threaded_call'(Goal, ('$lgt_metacall'(Goal, ExCtx, Where) -> true; fail), ExCtx, Where).


'$lgt_runtime_threaded_or_call'((Goal; Goals), Queue, (MTGoal, MTGoals), [Result| Results], ExCtx, Where) :-
	!,
	'$lgt_runtime_threaded_goal'(Goal, Queue, MTGoal, Result, ExCtx, Where),
	'$lgt_runtime_threaded_or_call'(Goals, Queue, MTGoals, Results, ExCtx, Where).

'$lgt_runtime_threaded_or_call'(Goal, Queue, MTGoal, [Result], ExCtx, Where) :-
	'$lgt_runtime_threaded_goal'(Goal, Queue, MTGoal, Result, ExCtx, Where).


'$lgt_runtime_threaded_and_call'((Goal, Goals), Queue, (MTGoal, MTGoals), [Result| Results], ExCtx, Where) :-
	!,
	'$lgt_runtime_threaded_goal'(Goal, Queue, MTGoal, Result, ExCtx, Where),
	'$lgt_runtime_threaded_and_call'(Goals, Queue, MTGoals, Results, ExCtx, Where).

'$lgt_runtime_threaded_and_call'(Goal, Queue, MTGoal, [Result], ExCtx, Where) :-
	'$lgt_runtime_threaded_goal'(Goal, Queue, MTGoal, Result, ExCtx, Where).


'$lgt_runtime_threaded_goal'(Goal, Queue, '$lgt_threaded_goal'('$lgt_metacall'(Goal, ExCtx, Where), TVars, Queue, Id), id(Id, TVars, _), ExCtx, Where).



% '$lgt_compile_threaded_call'(+callable, -callable)
%
% compiles the argument of a call to the built-in predicate threaded/1

'$lgt_compile_threaded_call'((TGoal; TGoals), '$lgt_threaded_or'(Queue, MTGoals, Results)) :-
	!,
	'$lgt_compile_threaded_or_call'((TGoal; TGoals), Queue, MTGoals, Results).

'$lgt_compile_threaded_call'((TGoal, TGoals), '$lgt_threaded_and'(Queue, MTGoals, Results)) :-
	!,
	'$lgt_compile_threaded_and_call'((TGoal, TGoals), Queue, MTGoals, Results).

'$lgt_compile_threaded_call'(TGoal, (TGoal -> true; fail)).


'$lgt_compile_threaded_or_call'((TGoal; TGoals), Queue, (MTGoal, MTGoals), [Result| Results]) :-
	!,
	'$lgt_compile_threaded_goal'(TGoal, Queue, MTGoal, Result),
	'$lgt_compile_threaded_or_call'(TGoals, Queue, MTGoals, Results).

'$lgt_compile_threaded_or_call'(TGoal, Queue, MTGoal, [Result]) :-
	'$lgt_compile_threaded_goal'(TGoal, Queue, MTGoal, Result).


'$lgt_compile_threaded_and_call'((TGoal, TGoals), Queue, (MTGoal, MTGoals), [Result| Results]) :-
	!,
	'$lgt_compile_threaded_goal'(TGoal, Queue, MTGoal, Result),
	'$lgt_compile_threaded_and_call'(TGoals, Queue, MTGoals, Results).

'$lgt_compile_threaded_and_call'(TGoal, Queue, MTGoal, [Result]) :-
	'$lgt_compile_threaded_goal'(TGoal, Queue, MTGoal, Result).


'$lgt_compile_threaded_goal'(TGoal, Queue, '$lgt_threaded_goal'(TGoal, TVars, Queue, Id), id(Id, TVars, _)).



% '$lgt_compile_prolog_meta_arguments'(@list, @list, +callable, +compilation_context, -list, -list)
%
% compiles the meta-arguments contained in the list of arguments of a call to a Prolog
% meta-predicate or meta-directive (assumes Logtalk meta-predicate notation)
%
% this predicate fails when meta-arguments other than goal and closures are not
% sufficiently instantiated or a meta-argument mode indicator is not supported

'$lgt_compile_prolog_meta_arguments'([], [], _, _, [], []).

'$lgt_compile_prolog_meta_arguments'([Arg| Args], [MArg| MArgs], Caller, Ctx, [TArg| TArgs], [DArg| DArgs]) :-
	(	nonvar(Arg),
		'$lgt_module_meta_argument'(MArg, Arg),
		'$lgt_prolog_feature'(modules, supported) ->
		% explicitly-qualified meta-argument
		TArg = Arg, DArg = Arg
	;	integer(MArg),
		MArg > 0 ->
		% closure meta-argument
		'$lgt_compile_prolog_meta_argument'(closure(MArg), Arg, Caller, Ctx, TArg, DArg)
	;	% remaining cases
		'$lgt_compile_prolog_meta_argument'(MArg, Arg, Caller, Ctx, TArg, DArg)
	),
	'$lgt_compile_prolog_meta_arguments'(Args, MArgs, Caller, Ctx, TArgs, DArgs).


'$lgt_module_meta_argument'(0, ':'(_,_)).
'$lgt_module_meta_argument'(1, ':'(_)).


'$lgt_compile_prolog_meta_argument'(closure(N), Arg, Caller, Ctx, TArg, DArg) :-
	% closure
	'$lgt_check'(var_or_callable, Arg),
	'$lgt_length'(ExtArgs, 0, N),
	(	var(Arg) ->
		ExtArg =.. [call, Arg| ExtArgs]
	;	'$lgt_extend_closure'(Arg, ExtArgs, ExtArg, Ctx) ->
		true
	;	throw(domain_error(closure, Arg))
	),
	'$lgt_compile_body'(ExtArg, Caller, TArg0, DArg0, Ctx),
	% generate an auxiliary predicate to allow the meta-predicate to extend
	% the closure without clashing with the execution-context argument
	'$lgt_generate_aux_predicate_functor'('_closure_', HelperFunctor),
	'$lgt_pp_entity_'(_, _, Prefix),
	atom_concat(Prefix, HelperFunctor, THelperFunctor),
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	THelper =.. [THelperFunctor, Arg, ExCtx],
	TExtHelper =.. [THelperFunctor, Arg, ExCtx| ExtArgs],
	(	'$lgt_compiler_flag'(debug, on) ->
		assertz('$lgt_pp_entity_aux_clause_'({(TExtHelper :- DArg0)}))
	;	assertz('$lgt_pp_entity_aux_clause_'({(TExtHelper :- TArg0)}))
	),
	(	'$lgt_pp_object_'(Entity, _, _, Def, _, _, _, _, _, _, _) ->
		true
	;	'$lgt_pp_category_'(Entity, _, _, Def, _, _)
	),
	% add a def clause to ensure that we don't loose track of the auxiliary clause
	Arity is N + 2,
	'$lgt_length'(TemplateArgs, 0, Arity),
	ExtHelperTemplate =.. [HelperFunctor| TemplateArgs],
	TExtHelperTemplate =.. [THelperFunctor| TemplateArgs],
	Clause =.. [Def, ExtHelperTemplate, _, TExtHelperTemplate],
	assertz('$lgt_pp_def_'(Clause)),
	% add, if applicable, source data information for the auxiliary clause
	(	'$lgt_compiler_flag'(source_data, on) ->
		assertz('$lgt_pp_runtime_clause_'('$lgt_predicate_property_'(Entity, HelperFunctor/Arity, flags_clauses_rules_location(1,1,1,0-0))))
	;	true
	),
	(	'$lgt_prolog_feature'(modules, supported) ->
		% make sure the calls are made in the correct context
		'$lgt_user_module_qualification'(THelper, TArg),
		'$lgt_user_module_qualification'(THelper, DArg)
	;	TArg = THelper,
		DArg = THelper
	).

% normal (non-meta) argument
'$lgt_compile_prolog_meta_argument'((*), Arg, _, _, Arg, Arg).

% goal
'$lgt_compile_prolog_meta_argument'((0), Arg, Caller, Ctx, TArg, DArg) :-
	'$lgt_compile_body'(Arg, Caller, TArg0, DArg0, Ctx),
	(	TArg0 = ':'(_, _) ->
		% the compiled call is already explicitly-qualified
		TArg = TArg0,
		DArg = DArg0
	;	'$lgt_prolog_feature'(modules, supported) ->
		% make sure the calls are made in the correct context
		'$lgt_user_module_qualification'(TArg0, TArg),
		'$lgt_user_module_qualification'(DArg0, DArg)
	;	TArg = TArg0,
		DArg = DArg0
	).

% existentially-quantified goal
'$lgt_compile_prolog_meta_argument'((^), Arg, Caller, Ctx, TArg, DArg) :-
	(	Arg = Vars^Arg0 ->
		'$lgt_compile_body'(Arg0, Caller, TArg0, DArg0, Ctx),
		TArg = Vars^TArg0,
		DArg = Vars^DArg0
	;	'$lgt_compile_body'(Arg, Caller, TArg, DArg, Ctx)
	).

% list of goals
'$lgt_compile_prolog_meta_argument'([0], [], _, _, [], []) :-
	!.
'$lgt_compile_prolog_meta_argument'([0], [Arg| Args], Caller, Ctx, [TArg| TArgs], [DArg| DArgs]) :-
	!,
	'$lgt_compile_prolog_meta_argument'((0), Arg, Caller, Ctx, TArg, DArg),
	'$lgt_compile_prolog_meta_argument'([0], Args, Caller, Ctx, TArgs, DArgs).

% predicate indicator
'$lgt_compile_prolog_meta_argument'((/), [Arg| Args], Caller, Ctx, [TArg| TArgs], [DArg| DArgs]) :-
	!,
	nonvar(Arg),
	'$lgt_compile_prolog_meta_argument'((/), Arg, Caller, Ctx, TArg, DArg),
	'$lgt_compile_prolog_meta_argument'([/], Args, Caller, Ctx, TArgs, DArgs).
'$lgt_compile_prolog_meta_argument'((/), (Arg, Args), Caller, Ctx, (TArg, TArgs), (DArg, DArgs)) :-
	!,
	nonvar(Arg),
	'$lgt_compile_prolog_meta_argument'((/), Arg, Caller, Ctx, TArg, DArg),
	'$lgt_compile_prolog_meta_argument'((/), Args, Caller, Ctx, TArgs, DArgs).
'$lgt_compile_prolog_meta_argument'((/), Arg, _, _, TArg, TArg) :-
	'$lgt_compile_predicate_indicators'(Arg, _, TArg0),
	(	'$lgt_prolog_feature'(modules, supported) ->
		% make sure the predicate indicator refers to the correct context
		'$lgt_user_module_qualification'(TArg0, TArg)
	;	TArg = TArg0
	).

% non-terminal indicator
'$lgt_compile_prolog_meta_argument'((//), Args, Caller, Ctx, TArgs, DArgs) :-
	'$lgt_compile_prolog_meta_argument'((/), Args, Caller, Ctx, TArgs, DArgs).

% list of predicate indicators
'$lgt_compile_prolog_meta_argument'([/], [], _, _, [], []) :-
	!.
'$lgt_compile_prolog_meta_argument'([/], [Arg| Args], Caller, Ctx, [TArg| TArgs], [DArg| DArgs]) :-
	!,
	nonvar(Arg),
	'$lgt_compile_prolog_meta_argument'((/), Arg, Caller, Ctx, TArg, DArg),
	'$lgt_compile_prolog_meta_argument'([/], Args, Caller, Ctx, TArgs, DArgs).

% list of non-terminal indicators
'$lgt_compile_prolog_meta_argument'([//], Args, Caller, Ctx, TArgs, DArgs) :-
	'$lgt_compile_prolog_meta_argument'([/], Args, Caller, Ctx, TArgs, DArgs).



% '$lgt_extend_closure'(@callable, @list(term), -callable, +compilation_context)
%
% extends a closure by appending a list of arguments to construct a goal
%
% this predicate fails if the closure can only be extended at runtime

'$lgt_extend_closure'(Closure, _, _, _) :-
	var(Closure),
	!,
	fail.

'$lgt_extend_closure'(Obj::Closure, ExtArgs, Goal, Ctx) :-
	Obj == user,
	!,
	'$lgt_extend_closure'({Closure}, ExtArgs, Goal, Ctx).

'$lgt_extend_closure'(Obj::Closure, ExtArgs, Obj::Msg, _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Msg).

'$lgt_extend_closure'([Obj::Closure], ExtArgs, [Obj::Msg], _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Msg).

'$lgt_extend_closure'(::Closure, ExtArgs, ::Msg, _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Msg).

'$lgt_extend_closure'(^^Closure, ExtArgs, ^^Msg, _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Msg).

'$lgt_extend_closure'(Obj<<Closure, ExtArgs, Obj<<Goal, _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Goal).

'$lgt_extend_closure'({Closure}, ExtArgs, {Goal}, _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Goal).

'$lgt_extend_closure'(Free/Lambda, ExtArgs, Goal, _) :-
	!,
	Goal =.. [call, Free/Lambda| ExtArgs].

'$lgt_extend_closure'(Parameters>>Lambda, ExtArgs, Goal, _) :-
	!,
	Goal =.. [call, Parameters>>Lambda| ExtArgs].

'$lgt_extend_closure'(':'(Module,Closure), ExtArgs, ':'(Module,Goal), _) :-
	!,
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Goal).

'$lgt_extend_closure'(Closure, ExtArgs, Goal, Ctx) :-
	'$lgt_extend_closure_basic'(Closure, ExtArgs, Alias),
	(	'$lgt_pp_uses_predicate_'(Object, Original, Alias, Ctx, _, _),
		(	Original == Alias ->
			% no alias is defined
			true
		;	% check that we're renaming a predicate but not (also) changing its argument order as that
			% would break using the closure as a meta-argument when appending the additional arguments
			Original =.. [_| OriginalArguments],
			Alias =.. [_| AliasArguments],
			OriginalArguments == AliasArguments
		) ->
		Goal = Object::Original
	;	'$lgt_pp_use_module_predicate_'(Module, Original, Alias, Ctx, _, _),
		(	Original == Alias ->
			% no alias is defined
			true
		;	% check that we're renaming a predicate but not (also) changing its argument order as that
			% would break using the closure as a meta-argument when appending the additional arguments
			Original =.. [_| OriginalArguments],
			Alias =.. [_| AliasArguments],
			OriginalArguments == AliasArguments
		) ->
		Goal = ':'(Module, Original)
	;	Goal = Alias
	).


'$lgt_extend_closure_basic'(Closure, ExtArgs, Goal) :-
	callable(Closure),
	% compile-time closure extension possible
	Closure =.. [Functor| Args],
	'$lgt_append'(Args, ExtArgs, FullArgs),
	Goal =.. [Functor| FullArgs].



% '$lgt_not_same_meta_arg_extra_args'(@list(nonvar), @list(var), @var, +integer)
%
% checks that the number of additional arguments being appended to a closure
% in a call/N call matches the corresponding meta-predicate declaration
% (the relative ordering of the meta-vars is the same of the corresponding
% meta-arguments; assumes Logtalk meta-predicate notation)

'$lgt_not_same_meta_arg_extra_args'([(*)| MetaArgs], MetaVars, Closure, ExtraArgs) :-
	!,
	'$lgt_not_same_meta_arg_extra_args'(MetaArgs, MetaVars, Closure, ExtraArgs).

'$lgt_not_same_meta_arg_extra_args'([(::)| MetaArgs], MetaVars, Closure, ExtraArgs) :-
	!,
	'$lgt_not_same_meta_arg_extra_args'(MetaArgs, MetaVars, Closure, ExtraArgs).

'$lgt_not_same_meta_arg_extra_args'([0| MetaArgs], MetaVars, Closure, ExtraArgs) :-
	!,
	'$lgt_not_same_meta_arg_extra_args'(MetaArgs, MetaVars, Closure, ExtraArgs).

'$lgt_not_same_meta_arg_extra_args'([MetaArg| _], [MetaVar| _], Closure, ExtraArgs) :-
	MetaVar == Closure,
	!,
	integer(MetaArg),
	MetaArg =\= ExtraArgs.

'$lgt_not_same_meta_arg_extra_args'([_| MetaArgs], [_| MetaVars], Closure, ExtraArgs) :-
	'$lgt_not_same_meta_arg_extra_args'(MetaArgs, MetaVars, Closure, ExtraArgs).



% '$lgt_same_number_of_closure_extra_args'(@list, @list, @list, @list, @callable, @callable)
%
% checks that the number of additional arguments being appended to a closure is kept
% when passing a closure from the clause head to a meta-predicate call in the body

'$lgt_same_number_of_closure_extra_args'([], _, _, _, _, _).

'$lgt_same_number_of_closure_extra_args'([PredArg| PredArgs], [PredMetaArg| PredMetaArgs], HeadArgs, HeadMetaArgs, HeadMeta, PredMeta) :-
	(	var(PredArg),
		integer(PredMetaArg), PredMetaArg > 0,
		% argument is a closure
		'$lgt_shared_closure_arg'(PredArg, HeadArgs, HeadMetaArgs, HeadMetaArg) ->
		% shared closure argument
		(	PredMetaArg = HeadMetaArg ->
			% same number of closure extra args
			'$lgt_same_number_of_closure_extra_args'(PredArgs, PredMetaArgs, HeadArgs, HeadMetaArgs, HeadMeta, PredMeta)
		;	throw(consistency_error(same_closure_specification, HeadMeta, PredMeta))
		)
	;	'$lgt_same_number_of_closure_extra_args'(PredArgs, PredMetaArgs, HeadArgs, HeadMetaArgs, HeadMeta, PredMeta)
	).


'$lgt_shared_closure_arg'(PredArg, [HeadArg| _], [HeadMetaArg| _], HeadMetaArg) :-
	PredArg == HeadArg.

'$lgt_shared_closure_arg'(PredArg, [_| HeadArgs], [_| HeadMetaArgs], HeadMetaArg) :-
	'$lgt_shared_closure_arg'(PredArg, HeadArgs, HeadMetaArgs, HeadMetaArg).



% '$lgt_check_dynamic_directive'(@compilation_mode, @term)
%
% checks for a dynamic/1 directive for a predicate that is an argument to the
% database built-in methods; the predicate may be non-instantiated or only
% partially instantiated but must be valid

'$lgt_check_dynamic_directive'(runtime, _).

'$lgt_check_dynamic_directive'(compile(_,_,_), Term) :-
	'$lgt_check_dynamic_directive'(Term).


'$lgt_check_dynamic_directive'(Term) :-
	var(Term),
	% runtime binding argument
	!.

'$lgt_check_dynamic_directive'((Head :- _)) :-
	!,
	'$lgt_check_dynamic_directive'(Head).

'$lgt_check_dynamic_directive'(Functor/Arity) :-
	!,
	(	ground(Functor/Arity) ->
		functor(Head, Functor, Arity),
		'$lgt_check_dynamic_directive'(Head)
	;	true
	).

'$lgt_check_dynamic_directive'(Head) :-
	(	'$lgt_pp_dynamic_'(Head, _, _, _) ->
		% dynamic/1 directive is present
		true
	;	'$lgt_pp_missing_dynamic_directive_'(Head, _, _) ->
		% missing dynamic/1 directive already recorded
		true
	;	'$lgt_pp_entity_'(category, _, _),
		% database predicates act only on objects
		functor(Head, Functor, Arity),
		\+ '$lgt_pp_public_'(Functor, Arity, _, _),
		\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
		\+ '$lgt_pp_private_'(Functor, Arity, _, _) ->
		% no scope directive
		true
	;	'$lgt_term_template'(Head, Template),
		'$lgt_source_file_context'(File, Lines),
		% delay reporting to the end of entity compilation to avoid repeated reports for
		% the same missing directive when a dynamic predicate have two or more clauses
		assertz('$lgt_pp_missing_dynamic_directive_'(Template, File, Lines))
	).



% '$lgt_check_discontiguous_directive'(@callable, @compilation_context)
%
% checks for a discontiguous/1 directive for a predicate

'$lgt_check_discontiguous_directive'(Head, Ctx) :-
	'$lgt_term_template'(Head, Template),
	retractall('$lgt_pp_previous_predicate_'(_, user)),
	assertz('$lgt_pp_previous_predicate_'(Template, user)),
	(	'$lgt_pp_discontiguous_'(Template, _, _) ->
		% discontiguous directive present
		true
	;	'$lgt_pp_missing_discontiguous_directive_'(Template, _, _) ->
		% missing discontiguous/1 directive already recorded
		true
	;	'$lgt_comp_ctx_mode'(Ctx, compile(user,_,_)) ->
		% compiling a source file clause; record missing discontiguous directive
		'$lgt_source_file_context'(File, Lines),
		% delay reporting to the end of entity compilation to avoid repeated reports for the same
		% missing directive when there multiple discontiguous blocks for the same predicate
		assertz('$lgt_pp_missing_discontiguous_directive_'(Template, File, Lines))
	;	% runtime compilation or compiling an auxiliary predicate clause
		true
	).



% '$lgt_optimizable_local_db_call'(@term, -callable)
%
% checks if a call to a database built-in method can be optimized by direct
% translation to a call to the corresponding Prolog built-in predicate

'$lgt_optimizable_local_db_call'(Pred, TPred) :-
	nonvar(Pred),
	% only for objects
	'$lgt_pp_entity_'(object, _, Prefix),
	% only for facts
	(	Pred = (Head :- Body) ->
		Body == true
	;	Head = Pred
	),
	callable(Head),
	% a dynamic directive must be present
	'$lgt_pp_dynamic_'(Head, _, _, _),
	% a scope directive must be present
	functor(Head, Functor, Arity),
	(	'$lgt_pp_public_'(Functor, Arity, _, _)
	;	'$lgt_pp_protected_'(Functor, Arity, _, _)
	;	'$lgt_pp_private_'(Functor, Arity, _, _)
	), !,
	% not compiled in debug mode
	'$lgt_compiler_flag'(debug, off),
	% compile the fact
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(TPred, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(Head, TPred, _).



% '$lgt_db_call_database_execution_context'(@term, @term, -term, +execution_context)
%
% returns the database where a database method call should take place and sets the
% execution context accordingly
%
% this auxiliary predicate ensures that, when calling database methods in the body
% of a multifile predicate clause defined in an object, the object database will be
% used instead of the database of the entity holding the multifile predicate primary
% declaration (which could be a category, making the calls invalid)

'$lgt_db_call_database_execution_context'(Entity, This, Database, ExCtx) :-
	(	'$lgt_pp_entity_'(object, _, _) ->
		Database = Entity,
		'$lgt_execution_context_this_entity'(ExCtx, _, Entity)
	;	% category
		Database = This,
		'$lgt_execution_context_this_entity'(ExCtx, This, _)
	).



% '$lgt_runtime_checked_db_clause'(@term)
%
% true if the argument forces runtime validity check

'$lgt_runtime_checked_db_clause'(Pred) :-
	var(Pred),
	!.

'$lgt_runtime_checked_db_clause'((Head :- Body)) :-
	var(Head),
	!,
	'$lgt_check'(var_or_callable, Body).

'$lgt_runtime_checked_db_clause'((Head :- Body)) :-
	var(Body),
	'$lgt_check'(var_or_callable, Head).



% '$lgt_check_non_portable_functions'(@term, @compilation_context)
%
% checks an arithmetic expression for calls to non-standard Prolog functions

'$lgt_check_non_portable_functions'(Exp, _) :-
	number(Exp),
	!.

'$lgt_check_non_portable_functions'(Exp, _) :-
	var(Exp),
	!.

'$lgt_check_non_portable_functions'(Exp, Ctx) :-
	'$lgt_prolog_deprecated_built_in_function'(Exp, Alt),
	% standard alternative
	once('$lgt_predicate_property'(evaluable_property(_, _), _)),
	evaluable_property(Exp, built_in),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(deprecated, warning),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_pp_entity_'(Type, Entity, _),
	functor(Exp, Functor, Arity),
	functor(Alt, AltFunctor, AltArity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(deprecated),
		deprecated_function(File, Lines, Type, Entity, Functor/Arity, AltFunctor/AltArity)
	),
	fail.

'$lgt_check_non_portable_functions'(Exp, Ctx) :-
	'$lgt_prolog_deprecated_built_in_function'(Exp),
	% no standard alternative
	once('$lgt_predicate_property'(evaluable_property(_, _), _)),
	evaluable_property(Exp, built_in),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(deprecated, warning),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_pp_entity_'(Type, Entity, _),
	functor(Exp, Functor, Arity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(deprecated),
		deprecated_function(File, Lines, Type, Entity, Functor/Arity)
	),
	fail.

'$lgt_check_non_portable_functions'(Exp, Ctx) :-
	(	'$lgt_pp_missing_function_'(Exp, _, _) ->
		% missing function already recorded
		true
	;	'$lgt_predicate_property'(evaluable_property(_, _), _),
		\+ evaluable_property(Exp, _) ->
		% first occurrence of this missing function; record it
		'$lgt_term_template'(Exp, Template),
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_missing_function_'(Template, File, Lines))
	;	% no reliable way of checking if the function is missing
		true
	),
	(	'$lgt_iso_spec_function'(Exp) ->
		% portable call (we assume...!)
		true
	;	'$lgt_pp_non_portable_function_'(Exp, _, _) ->
		% non-portable function already recorded
		true
	;	% first occurrence of this non-portable function; record it
		'$lgt_term_template'(Exp, Template),
		'$lgt_source_file_context'(File, Lines),
		assertz('$lgt_pp_non_portable_function_'(Template, File, Lines))
	),
	(	Exp = [_|_] ->
		% avoid duplicated warnings with the Prolog legacy use of a list
		% with a single character to represent the code of the character
		true
	;	Exp =.. [_| Exps],
		'$lgt_check_non_portable_function_args'(Exps, Ctx)
	).


'$lgt_check_non_portable_function_args'([], _).

'$lgt_check_non_portable_function_args'([Exp| Exps], Ctx) :-
	'$lgt_check_non_portable_functions'(Exp, Ctx),
	'$lgt_check_non_portable_function_args'(Exps, Ctx).



% '$lgt_compile_message_to_object'(@term, @object_identifier, -callable, +atom, +compilation_context)
%
% compiles a message-sending call


% messages to the pseudo-object "user"

'$lgt_compile_message_to_object'(Pred, Obj, _, _, Ctx) :-
	Obj == user,
	'$lgt_check'(var_or_callable, Pred),
	callable(Pred),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_iso_spec_predicate'(Pred),
	\+ '$lgt_built_in_method'(Pred, _, _, _),
	\+ '$lgt_pp_defines_predicate_'(Pred, _, _, _, _, _),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, user::Pred, [Pred])
	),
	fail.

'$lgt_compile_message_to_object'(Pred, Obj, TPred, _, Ctx) :-
	Obj == user,
	callable(Pred),
	!,
	(	\+ '$lgt_prolog_built_in_database_predicate'(Pred),
		% the meta-predicate templates for the backend Prolog database predicates are
		% usually not usable from Logtalk due the ambiguity of the ":" meta-argument
		% qualifier but they pose no problems when operating in "user"; in this
		% particular case, the call can be compiled as-is
		(	'$lgt_prolog_meta_predicate'(Pred, Meta, _)
			% built-in Prolog meta-predicate declared in the adapter file in use
		;	catch('$lgt_predicate_property'(Pred, meta_predicate(Meta)), _, fail)
			% Prolog meta-predicate undeclared in the adapter file (may not be a built-in)
		;	'$lgt_pp_meta_predicate_'(user::Pred, user::Meta, _, _)
			% we're either providing a meta-predicate template or overriding the original
			% meta-predicate template
		) ->
		% meta-predicate
		Pred =.. [Functor| Args],
		Meta =.. [Functor| MArgs],
		(	'$lgt_prolog_to_logtalk_meta_argument_specifiers'(MArgs, CMArgs),
			'$lgt_compile_prolog_meta_arguments'(Args, CMArgs, meta, Ctx, TArgs, _) ->
			TPred =.. [Functor| TArgs]
		;	% meta-predicate template is not usable
			throw(domain_error(meta_predicate_template, Meta))
		)
	;	% non meta-predicate
		TPred = Pred
	),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
	(	'$lgt_prolog_built_in_database_predicate'(Pred) ->
		% update to a dynamic predicate in "user" declared in a uses/2 directive
		true
	;	'$lgt_add_referenced_object_message'(Mode, Obj, Pred, Pred, Head)
	).

% suppress debug messages when compiling in optimized mode

'$lgt_compile_message_to_object'(Pred, Obj, true, _, _) :-
	Obj == logtalk,
	Pred = print_message(Kind, _, _),
	callable(Kind),
	functor(Kind, debug, _),
	'$lgt_compiler_flag'(optimize, on),
	!.

% convenient access to parametric object proxies

'$lgt_compile_message_to_object'(Pred, Obj, (CallProxy, TPred), Events, Ctx) :-
	nonvar(Obj),
	Obj = {Proxy},
	!,
	(	var(Proxy) ->
		CallProxy = call(Proxy)
	;	callable(Proxy) ->
		CallProxy = Proxy
	;	throw(type_error(callable, Proxy))
	),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	'$lgt_compile_message_to_object'(Pred, Proxy, TPred, Events, Ctx).

% type and lint checks

'$lgt_compile_message_to_object'(_, Obj, _, _, Ctx) :-
	(	callable(Obj) ->
		% remember the object receiving the message
		'$lgt_add_referenced_object'(Obj, Ctx),
		fail
	;	nonvar(Obj),
		% invalid object identifier
		throw(type_error(object_identifier, Obj))
	).

% suspicious use of ::/2 instead of ::/1 to send a message to "self"

'$lgt_compile_message_to_object'(Pred, Obj, _, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, compile(_,_,_), _, _, _),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_execution_context'(ExCtx, _, _, _, Self, _, _),
	Self == Obj,
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Obj::Pred, [::Pred])
	),
	fail.

% suspicious use of ::/2 in objects to call a local predicate

'$lgt_compile_message_to_object'(Pred, Obj, _, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, ExCtx, compile(_,_,_), _, _, _),
	'$lgt_pp_entity_'(object, _, _),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _),
	This == Obj,
	% message sent from an object to itself
	nonvar(Pred),
	'$lgt_pp_defines_predicate_'(Pred, _, _, _, _, _),
	% local predicate
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, Obj::Pred, [Pred])
	),
	fail.

% translation performed at runtime

'$lgt_compile_message_to_object'(Pred, Obj, '$lgt_send_to_obj_rt'(Obj, Pred, Events, NewCtx), Events, Ctx) :-
	var(Pred),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, _, Stack, Lines, _),
	'$lgt_comp_ctx'(NewCtx, Head, _, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, runtime, Stack, Lines, _).

% broadcasting control constructs

'$lgt_compile_message_to_object'((Pred1, Pred2), Obj, (TPred1, TPred2), Events, Ctx) :-
	!,
	'$lgt_compile_message_to_object'(Pred1, Obj, TPred1, Events, Ctx),
	'$lgt_compile_message_to_object'(Pred2, Obj, TPred2, Events, Ctx).

'$lgt_compile_message_to_object'((Pred1; Pred2), Obj, (TPred1; TPred2), Events, Ctx) :-
	!,
	'$lgt_compile_message_to_object'(Pred1, Obj, TPred1, Events, Ctx),
	'$lgt_compile_message_to_object'(Pred2, Obj, TPred2, Events, Ctx).

'$lgt_compile_message_to_object'((Pred1 -> Pred2), Obj, (TPred1 -> TPred2), Events, Ctx) :-
	!,
	'$lgt_compile_message_to_object'(Pred1, Obj, TPred1, Events, Ctx),
	'$lgt_compile_message_to_object'(Pred2, Obj, TPred2, Events, Ctx).

'$lgt_compile_message_to_object'('*->'(Pred1, Pred2), Obj, '*->'(TPred1, TPred2), Events, Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_compile_message_to_object'(Pred1, Obj, TPred1, Events, Ctx),
	'$lgt_compile_message_to_object'(Pred2, Obj, TPred2, Events, Ctx).

% built-in methods that cannot be redefined

'$lgt_compile_message_to_object'(!, Obj, ('$lgt_object_exists'(Obj, !, ExCtx), !), _, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_message_to_object'(true, Obj, ('$lgt_object_exists'(Obj, true, ExCtx), true), _, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_message_to_object'(fail, Obj, ('$lgt_object_exists'(Obj, fail, ExCtx), fail), _, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_message_to_object'(false, Obj, ('$lgt_object_exists'(Obj, false, ExCtx), false), _, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

'$lgt_compile_message_to_object'(repeat, Obj, ('$lgt_object_exists'(Obj, repeat, ExCtx), repeat), _, Ctx) :-
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx).

% reflection built-in predicates

'$lgt_compile_message_to_object'(current_op(Priority, Specifier, Operator), Obj, '$lgt_current_op'(Obj, Priority, Specifier, Operator, This, p(p(p)), ExCtx), _, Ctx) :-
	!,
	'$lgt_check'(var_or_operator_priority, Priority),
	'$lgt_check'(var_or_operator_specifier, Specifier),
	'$lgt_check'(var_or_atom, Operator),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _).

'$lgt_compile_message_to_object'(current_predicate(Pred), Obj, '$lgt_current_predicate'(Obj, Pred, This, p(p(p)), ExCtx), _, Ctx) :-
	!,
	'$lgt_check'(var_or_predicate_indicator, Pred),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _).

'$lgt_compile_message_to_object'(predicate_property(Pred, Prop), Obj, '$lgt_predicate_property'(Obj, Pred, Prop, This, p(p(p)), ExCtx), _, Ctx) :-
	!,
	'$lgt_check'(var_or_callable, Pred),
	'$lgt_check'(var_or_predicate_property, Prop),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _).

% database handling built-in predicates

'$lgt_compile_message_to_object'(abolish(Functor, Arity), Obj, TPred, Events, Ctx) :-
	'$lgt_prolog_built_in_predicate'(abolish(_, _)),
	\+ '$lgt_pp_defines_predicate_'(abolish(_, _), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, abolish/2, abolish/1)
		)
	;	true
	),
	'$lgt_compile_message_to_object'(abolish(Functor/Arity), Obj, TPred, Events, Ctx).

'$lgt_compile_message_to_object'(abolish(Pred), Obj, TPred, _, Ctx) :-
	!,
	'$lgt_check'(var_or_predicate_indicator, Pred),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	var(Obj) ->
		TPred = '$lgt_abolish'(Obj, Pred, This, p(p(p)), ExCtx)
	;	ground(Pred) ->
		TPred = '$lgt_abolish_checked'(Obj, Pred, This, p(p(p)), ExCtx),
		'$lgt_remember_updated_predicate'(Mode, Obj::Pred, Head)
	;	% partially instantiated predicate indicator; runtime check required
		TPred = '$lgt_abolish'(Obj, Pred, This, p(p(p)), ExCtx)
	).

'$lgt_compile_message_to_object'(assert(Clause), Obj, TPred, Events, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assert(_)),
	\+ '$lgt_pp_defines_predicate_'(assert(_), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, assert/1, assertz/1)
		)
	;	true
	),
	'$lgt_compile_message_to_object'(assertz(Clause), Obj, TPred, Events, Ctx).

'$lgt_compile_message_to_object'(asserta(Clause), Obj, TPred, _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_asserta'(Obj, Clause, This, p(p(_)), p(p(p)), ExCtx)
	;	var(Obj) ->
		'$lgt_check'(clause, Clause),
		TPred = '$lgt_asserta'(Obj, Clause, This, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	(Clause = (Head :- Body) -> Body == true; Clause = Head) ->
			(	'$lgt_compiler_flag'(optimize, on),
				'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) ->
				TPred = asserta(THead)
			;	TPred = '$lgt_asserta_fact_checked'(Obj, Head, This, p(p(_)), p(p(p)), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	TPred = '$lgt_asserta_rule_checked'(Obj, Clause, This, p(p(_)), p(p(p)), ExCtx),
			Clause = (Head :- _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_object'(assertz(Clause), Obj, TPred, _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_assertz'(Obj, Clause, This, p(p(_)), p(p(p)), ExCtx)
	;	var(Obj) ->
		'$lgt_check'(clause, Clause),
		TPred = '$lgt_assertz'(Obj, Clause, This, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	(Clause = (Head :- Body) -> Body == true; Clause = Head) ->
			(	'$lgt_compiler_flag'(optimize, on),
				'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) ->
				TPred = assertz(THead)
			;	TPred = '$lgt_assertz_fact_checked'(Obj, Head, This, p(p(_)), p(p(p)), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	TPred = '$lgt_assertz_rule_checked'(Obj, Clause, This, p(p(_)), p(p(p)), ExCtx),
			Clause = (Head :- _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_object'(clause(Head, Body), Obj, TPred, _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'((Head :- Body)) ->
		TPred = '$lgt_clause'(Obj, Head, Body, This, p(p(p)), ExCtx)
	;	'$lgt_check'(clause, (Head :- Body)),
		(	var(Obj) ->
			TPred = '$lgt_clause'(Obj, Head, Body, This, p(p(p)), ExCtx)
		;	TPred = '$lgt_clause_checked'(Obj, Head, Body, This, p(p(p)), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_object'(retract(Clause), Obj, TPred, _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_retract'(Obj, Clause, This, p(p(p)), ExCtx)
	;	var(Obj) ->
		'$lgt_check'(clause, Clause),
		TPred = '$lgt_retract'(Obj, Clause, This, p(p(p)), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	Clause = (Head :- Body) ->
			(	var(Body) ->
				TPred = '$lgt_retract_var_body_checked'(Obj, Clause, This, p(p(p)), ExCtx)
			;	Body == true ->
				(	'$lgt_compiler_flag'(optimize, on),
					'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) ->
					TPred = retract(THead)
				;	TPred = '$lgt_retract_fact_checked'(Obj, Head, This, p(p(p)), ExCtx)
				)
			;	TPred = '$lgt_retract_rule_checked'(Obj, Clause, This, p(p(p)), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	TPred = '$lgt_retract_fact_checked'(Obj, Clause, This, p(p(p)), ExCtx),
			functor(Clause, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_object'(retractall(Head), Obj, TPred, _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	var(Head) ->
		TPred = '$lgt_retractall'(Obj, Head, This, p(p(p)), ExCtx)
	;	var(Obj) ->
		'$lgt_check'(callable, Head),
		TPred = '$lgt_retractall'(Obj, Head, This, p(p(p)), ExCtx)
	;	'$lgt_check'(callable, Head),
		(	'$lgt_compiler_flag'(optimize, on),
			'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) ->
			TPred = retractall(THead)
		;	TPred = '$lgt_retractall_checked'(Obj, Head, This, p(p(p)), ExCtx)
		),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
	).

% database handling built-in predicates that take a clause reference
% if supported as built-in predicates by the backend Prolog compiler

'$lgt_compile_message_to_object'(assert(Clause, Ref), Obj, TPred, Events, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assert(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assert(_, _), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, assert/2, assertz/2)
		)
	;	true
	),
	'$lgt_compile_message_to_object'(assertz(Clause, Ref), Obj, TPred, Events, Ctx).

'$lgt_compile_message_to_object'(asserta(Clause, Ref), Obj, TPred, _, Ctx) :-
	'$lgt_prolog_built_in_predicate'(asserta(_, _)),
	\+ '$lgt_pp_defines_predicate_'(asserta(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_asserta'(Obj, Clause, Ref, This, p(p(_)), p(p(p)), ExCtx)
	;	var(Obj) ->
		'$lgt_check'(clause, Clause),
		TPred = '$lgt_asserta'(Obj, Clause, Ref, This, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	(Clause = (Head :- Body) -> Body == true; Clause = Head) ->
			(	'$lgt_compiler_flag'(optimize, on),
				'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) ->
				TPred = asserta(THead, Ref)
			;	TPred = '$lgt_asserta_fact_checked'(Obj, Head, Ref, This, p(p(_)), p(p(p)), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	TPred = '$lgt_asserta_rule_checked'(Obj, Clause, Ref, This, p(p(_)), p(p(p)), ExCtx),
			Clause = (Head :- _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_object'(assertz(Clause, Ref), Obj, TPred, _, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assertz(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assertz(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_assertz'(Obj, Clause, Ref, Ref, This, p(p(_)), p(p(p)), ExCtx)
	;	var(Obj) ->
		'$lgt_check'(clause, Clause),
		TPred = '$lgt_assertz'(Obj, Clause, Ref, This, p(p(_)), p(p(p)), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	(Clause = (Head :- Body) -> Body == true; Clause = Head) ->
			(	'$lgt_compiler_flag'(optimize, on),
				'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) ->
				TPred = assertz(THead, Ref)
			;	TPred = '$lgt_assertz_fact_checked'(Obj, Head, Ref, This, p(p(_)), p(p(p)), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		;	TPred = '$lgt_assertz_rule_checked'(Obj, Clause, Ref, This, p(p(_)), p(p(p)), ExCtx),
			Clause = (Head :- _),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_object'(clause(Head, Body, Ref), Obj, TPred, _, Ctx) :-
	'$lgt_prolog_built_in_predicate'(clause(_, _, _)),
	\+ '$lgt_pp_defines_predicate_'(clause(_, _, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _),
	(	'$lgt_runtime_checked_db_clause'((Head :- Body)) ->
		TPred = '$lgt_clause'(Obj, Head, Body, Ref, This, p(p(p)), ExCtx)
	;	'$lgt_check'(clause, (Head :- Body)),
		(	var(Obj) ->
			TPred = '$lgt_clause'(Obj, Head, Body, Ref, This, p(p(p)), ExCtx)
		;	TPred = '$lgt_clause_checked'(Obj, Head, Body, Ref, This, p(p(p)), ExCtx),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, Obj::Functor/Arity, CallerHead)
		)
	).

% term and goal expansion predicates

'$lgt_compile_message_to_object'(expand_term(Term, Expansion), Obj, '$lgt_expand_term_message'(Obj, Term, Expansion, This, p(p(p)), ExCtx), _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _).

'$lgt_compile_message_to_object'(expand_goal(Goal, ExpandedGoal), Obj, '$lgt_expand_goal_message'(Obj, Goal, ExpandedGoal, This, p(p(p))), _, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, This, _).

% compiler bypass control construct

'$lgt_compile_message_to_object'({Goal}, _, call(Goal), _, _) :-
	!,
	'$lgt_check'(var_or_callable, Goal).

% invalid message

'$lgt_compile_message_to_object'(Pred, _, _, _, _) :-
	\+ callable(Pred),
	throw(type_error(callable, Pred)).

% message is not a built-in control construct or a call to a built-in (meta-)predicate

'$lgt_compile_message_to_object'(Pred, Obj, TPred, Events, Ctx) :-
	var(Obj),
	% translation performed at runtime
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_add_referenced_object_message'(Mode, Obj, Pred, Pred, Head),
	(	Events == allow ->
		TPred = '$lgt_send_to_obj'(Obj, Pred, ExCtx)
	;	TPred = '$lgt_send_to_obj_ne'(Obj, Pred, ExCtx)
	).

% special case where an object sends a message to itself;  the practical
% case is parametric objects where one or more parameters are updated by
% the object predicates

'$lgt_compile_message_to_object'(Pred, Obj, TPred, Events, Ctx) :-
	'$lgt_pp_entity_'(object, Entity, _),
	functor(Obj, Functor, Arity),
	functor(Entity, Functor, Arity),
	'$lgt_pp_defines_predicate_'(Pred, _, _, _, _, _),
	% local predicate
	!,
	'$lgt_comp_ctx'(Ctx,    Head, HeadExCtx, Entity, _,      This, _,    Prefix, MetaVars, _, ExCtx,    Mode, _, Lines, Term),
	'$lgt_comp_ctx'(NewCtx, Head, HeadExCtx, Obj,    Entity, Obj,  Obj,  Prefix, MetaVars, _, NewExCtx, Mode, _, Lines, Term),
	'$lgt_execution_context_this_entity'(ExCtx, This, Entity),
	'$lgt_execution_context'(NewExCtx, Obj, This, Obj, Obj, [], []),
	'$lgt_compile_body'(Pred, message, TPred0, _, NewCtx),
	(	Events == allow ->
		TPred = '$lgt_guarded_method_call'(Obj, Pred, This, TPred0)
	;	TPred = TPred0
	).

'$lgt_compile_message_to_object'(Pred, Obj, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	% check for a user-defined linter warning
	once(logtalk_linter_hook(Obj::Pred, Flag, File, Lines, Type, Entity, Warning)),
	nonvar(Flag),
	'$lgt_valid_flag'(Flag),
	'$lgt_compiler_flag'(Flag, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(Flag), Warning),
	fail.

'$lgt_compile_message_to_object'(Pred, Obj, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_current_object_'(Obj, _, Dcl, _, _, _, _, _, _, _, _),
	\+ call(Dcl, forward(_), _, _, _, _, _),
	\+ call(Dcl, Pred, _, _, _, _, _),
	'$lgt_compiler_flag'(unknown_predicates, warning),
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		Message = message_not_understood(File, Lines, Type, Entity, Obj, Pred)
	;	Message = message_not_understood(File, Lines, Obj, Pred)
	),
	'$lgt_print_message'(warning(unknown_predicates), Message),
	fail.

'$lgt_compile_message_to_object'(Pred, Obj, TPred, Events, Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, This, _, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_add_referenced_object_message'(Mode, Obj, Pred, Pred, Head),
	(	Events == allow ->
		(	'$lgt_compiler_flag'(optimize, on),
			'$lgt_send_to_obj_static_binding'(Obj, Pred, Call, Ctx) ->
			'$lgt_execution_context_this_entity'(ExCtx, This, _),
			TPred = '$lgt_guarded_method_call'(Obj, Pred, This, Call)
		;	TPred = '$lgt_send_to_obj_'(Obj, Pred, ExCtx)
		)
	;	(	'$lgt_compiler_flag'(optimize, on),
			'$lgt_send_to_obj_static_binding'(Obj, Pred, TPred, Ctx) ->
			true
		;	TPred = '$lgt_send_to_obj_ne_'(Obj, Pred, ExCtx)
		)
	).



% '$lgt_compile_message_to_self'(@term, -callable, @execution_context)
%
% compiles the sending of a message to self


% translation performed at runtime

'$lgt_compile_message_to_self'(Pred, '$lgt_send_to_self'(Pred, NewCtx), Ctx) :-
	var(Pred),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, _, Stack, Lines, _),
	'$lgt_comp_ctx'(NewCtx, Head, _, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, runtime, Stack, Lines, _).

% suspicious use of ::/1 instead of a local predicate call in clauses that
% apparently are meant to implement recursive predicate definitions where
% the user intention is to call the local predicate; the user may also
% intend to make a "super" call instead of a message to "self"

'$lgt_compile_message_to_self'(Pred, _, Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, compile(_,_,_), _, _, _),
	'$lgt_compiler_flag'(suspicious_calls, warning),
	functor(Pred, Functor, Arity),
	functor(Head, Functor, Arity),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(suspicious_calls),
		suspicious_call(File, Lines, Type, Entity, ::Pred, [Pred, ^^Pred])
	),
	fail.

% broadcasting control constructs

'$lgt_compile_message_to_self'((Pred1, Pred2), (TPred1, TPred2), Ctx) :-
	!,
	'$lgt_compile_message_to_self'(Pred1, TPred1, Ctx),
	'$lgt_compile_message_to_self'(Pred2, TPred2, Ctx).

'$lgt_compile_message_to_self'((Pred1; Pred2), (TPred1; TPred2), Ctx) :-
	!,
	'$lgt_compile_message_to_self'(Pred1, TPred1, Ctx),
	'$lgt_compile_message_to_self'(Pred2, TPred2, Ctx).

'$lgt_compile_message_to_self'((Pred1 -> Pred2), (TPred1 -> TPred2), Ctx) :-
	!,
	'$lgt_compile_message_to_self'(Pred1, TPred1, Ctx),
	'$lgt_compile_message_to_self'(Pred2, TPred2, Ctx).

'$lgt_compile_message_to_self'('*->'(Pred1, Pred2), '*->'(TPred1, TPred2), Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_compile_message_to_self'(Pred1, TPred1, Ctx),
	'$lgt_compile_message_to_self'(Pred2, TPred2, Ctx).

% built-in methods that cannot be redefined

'$lgt_compile_message_to_self'(!, !, _) :-
	!.

'$lgt_compile_message_to_self'(true, true, _) :-
	!.

'$lgt_compile_message_to_self'(false, false, _) :-
	!.

'$lgt_compile_message_to_self'(fail, fail, _) :-
	!.

'$lgt_compile_message_to_self'(repeat, repeat, _) :-
	!.

% reflection built-in predicates

'$lgt_compile_message_to_self'(current_op(Priority, Specifier, Operator), '$lgt_current_op'(Self, Priority, Specifier, Operator, This, p(_), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var_or_operator_priority, Priority),
	'$lgt_check'(var_or_operator_specifier, Specifier),
	'$lgt_check'(var_or_atom, Operator),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, Self, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _).

'$lgt_compile_message_to_self'(current_predicate(Pred), '$lgt_current_predicate'(Self, Pred, This, p(_), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var_or_predicate_indicator, Pred),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, Self, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _).

'$lgt_compile_message_to_self'(predicate_property(Pred, Prop), '$lgt_predicate_property'(Self, Pred, Prop, This, p(_), ExCtx), Ctx) :-
	!,
	'$lgt_check'(var_or_callable, Pred),
	'$lgt_check'(var_or_predicate_property, Prop),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, Self, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _).

% database handling built-in predicates

'$lgt_compile_message_to_self'(abolish(Functor, Arity), TPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(abolish(_, _)),
	\+ '$lgt_pp_defines_predicate_'(abolish(_, _), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, abolish/2, abolish/1)
		)
	;	true
	),
	'$lgt_compile_message_to_self'(abolish(Functor/Arity), TPred, Ctx).

'$lgt_compile_message_to_self'(abolish(Pred), TPred, Ctx) :-
	!,
	'$lgt_check'(var_or_predicate_indicator, Pred),
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	ground(Pred) ->
		TPred = '$lgt_abolish_checked'(Self, Pred, This, p(_), ExCtx),
		'$lgt_remember_updated_predicate'(Mode, ::Pred, Head)
	;	% partially instantiated predicate indicator; runtime check required
		TPred = '$lgt_abolish'(Self, Pred, This, p(_), ExCtx)
	).

'$lgt_compile_message_to_self'(assert(Clause), TPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assert(_)),
	\+ '$lgt_pp_defines_predicate_'(assert(_), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, assert/1, assertz/1)
		)
	;	true
	),
	'$lgt_compile_message_to_self'(assertz(Clause), TPred, Ctx).

'$lgt_compile_message_to_self'(asserta(Clause), TPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_asserta'(Self, Clause, This, p(_), p(p), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	Clause = (Head :- Body) ->
			(	Body == true ->
				TPred = '$lgt_asserta_fact_checked'(Self, Head, This, p(_), p(p), ExCtx)
			;	TPred = '$lgt_asserta_rule_checked'(Self, Clause, This, p(_), p(p), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		;	TPred = '$lgt_asserta_fact_checked'(Self, Clause, This, p(_), p(p), ExCtx),
			functor(Clause, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_self'(assertz(Clause), TPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_assertz'(Self, Clause, This, p(_), p(p), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	Clause = (Head :- Body) ->
			(	Body == true ->
				TPred = '$lgt_assertz_fact_checked'(Self, Head, This, p(_), p(p), ExCtx)
			;	TPred = '$lgt_assertz_rule_checked'(Self, Clause, This, p(_), p(p), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		;	TPred = '$lgt_assertz_fact_checked'(Self, Clause, This, p(_), p(p), ExCtx),
			functor(Clause, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_self'(clause(Head, Body), TPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'((Head :- Body)) ->
		TPred = '$lgt_clause'(Self, Head, Body, This, p(_), ExCtx)
	;	'$lgt_check'(clause, (Head :- Body)),
		TPred = '$lgt_clause_checked'(Self, Head, Body, This, p(_), ExCtx),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
	).

'$lgt_compile_message_to_self'(retract(Clause), TPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_retract'(Self, Clause, This, p(_), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	Clause = (Head :- Body) ->
			(	var(Body) ->
				TPred = '$lgt_retract_var_body_checked'(Self, Clause, This, p(_), ExCtx)
			;	Body == true ->
				TPred = '$lgt_retract_fact_checked'(Self, Head, This, p(_), ExCtx)
			;	TPred = '$lgt_retract_rule_checked'(Self, Clause, This, p(_), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		;	TPred = '$lgt_retract_fact_checked'(Self, Clause, This, p(_), ExCtx),
			functor(Clause, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_self'(retractall(Head), TPred, Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	var(Head) ->
		TPred = '$lgt_retractall'(Self, Head, This, p(_), ExCtx)
	;	'$lgt_check'(callable, Head),
		TPred = '$lgt_retractall_checked'(Self, Head, This, p(_), ExCtx),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
	).

% database handling built-in predicates that take a clause reference
% if supported as built-in predicates by the backend Prolog compiler

'$lgt_compile_message_to_self'(assert(Clause, Ref), TPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assert(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assert(_, _), _, _, _, _, _),
	!,
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		'$lgt_compiler_flag'(deprecated, warning),
		'$lgt_source_file_context'(File, Lines),
		'$lgt_pp_entity_'(Type, Entity, _) ->
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(deprecated),
			deprecated_predicate(File, Lines, Type, Entity, assert/2, assertz/2)
		)
	;	true
	),
	'$lgt_compile_message_to_self'(assertz(Clause, Ref), TPred, Ctx).

'$lgt_compile_message_to_self'(asserta(Clause, Ref), TPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(asserta(_, _)),
	\+ '$lgt_pp_defines_predicate_'(asserta(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_asserta'(Self, Clause, Ref, This, p(_), p(p), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	Clause = (Head :- Body) ->
			(	Body == true ->
				TPred = '$lgt_asserta_fact_checked'(Self, Head, Ref, This, p(_), p(p), ExCtx)
			;	TPred = '$lgt_asserta_rule_checked'(Self, Clause, Ref, This, p(_), p(p), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		;	TPred = '$lgt_asserta_fact_checked'(Self, Clause, Ref, This, p(_), p(p), ExCtx),
			functor(Clause, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_self'(assertz(Clause, Ref), TPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(assertz(_, _)),
	\+ '$lgt_pp_defines_predicate_'(assertz(_, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'(Clause) ->
		TPred = '$lgt_assertz'(Self, Clause, Ref, This, p(_), p(p), ExCtx)
	;	'$lgt_check'(clause, Clause),
		(	Clause = (Head :- Body) ->
			(	Body == true ->
				TPred = '$lgt_assertz_fact_checked'(Self, Head, Ref, This, p(_), p(p), ExCtx)
			;	TPred = '$lgt_assertz_rule_checked'(Self, Clause, Ref, This, p(_), p(p), ExCtx)
			),
			functor(Head, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		;	TPred = '$lgt_assertz_fact_checked'(Self, Clause, Ref, This, p(_), p(p), ExCtx),
			functor(Clause, Functor, Arity),
			'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
		)
	).

'$lgt_compile_message_to_self'(clause(Head, Body, Ref), TPred, Ctx) :-
	'$lgt_prolog_built_in_predicate'(clause(_, _, _)),
	\+ '$lgt_pp_defines_predicate_'(clause(_, _, _), _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, CallerHead, _, _, _, This, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	(	'$lgt_runtime_checked_db_clause'((Head :- Body)) ->
		TPred = '$lgt_clause'(Self, Head, Body, Ref, This, p(_), ExCtx)
	;	'$lgt_check'(clause, (Head :- Body)),
		TPred = '$lgt_clause_checked'(Self, Head, Body, Ref, This, p(_), ExCtx),
		functor(Head, Functor, Arity),
		'$lgt_remember_updated_predicate'(Mode, ::Functor/Arity, CallerHead)
	).

% term and goal expansion predicates

'$lgt_compile_message_to_self'(expand_term(Term, Expansion), '$lgt_expand_term_message'(Self, Term, Expansion, This, p(_), ExCtx), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, Self, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _).

'$lgt_compile_message_to_self'(expand_goal(Goal, ExpandedGoal), '$lgt_expand_goal_message'(Self, Goal, ExpandedGoal, This, p(_)), Ctx) :-
	!,
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, Self, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _).

% compiler bypass control construct

'$lgt_compile_message_to_self'({Goal}, call(Goal), _) :-
	!,
	'$lgt_check'(var_or_callable, Goal).

% invalid message

'$lgt_compile_message_to_self'(Pred, _, _) :-
	\+ callable(Pred),
	throw(type_error(callable, Pred)).

% message is not a built-in control construct or a call to a built-in
% (meta-)predicate: translation performed at runtime

'$lgt_compile_message_to_self'(Pred, '$lgt_send_to_self_'(Self, Pred, ExCtx), Ctx) :-
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, Self, _, _, _, ExCtx, Mode, _, _, _),
	'$lgt_execution_context'(ExCtx, _, _, _, Self, _, _),
	functor(Pred, Functor, Arity),
	'$lgt_remember_called_self_predicate'(Mode, Functor/Arity, Head),
	!.



% '$lgt_compile_super_call'(@term, -callable, +compilation_context)
%
% compiles calling of redefined predicates ("super" calls)

'$lgt_compile_super_call'(Pred, TPred, Ctx) :-
	'$lgt_pp_object_'(Obj, _, _, _, Super, _, _, _, _, _, _),
	!,
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
	(	\+ '$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _),
		\+ '$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _),
		\+ '$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _),
		\+ '$lgt_pp_imported_category_'(_, _, _, _, _, _) ->
		% invalid goal (no ancestor entity)
		throw(existence_error(ancestor, object))
	;	var(Pred) ->
		% translation performed at runtime
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = '$lgt_obj_super_call'(Super, Pred, ExCtx)
	;	callable(Pred) ->
		(	'$lgt_compiler_flag'(optimize, on),
			'$lgt_obj_related_entities_are_static',
			'$lgt_obj_super_call_static_binding'(Obj, Pred, ExCtx, TPred) ->
			true
		;	TPred = '$lgt_obj_super_call_'(Super, Pred, ExCtx)
		),
		functor(Pred, Functor, Arity),
		'$lgt_remember_called_super_predicate'(Mode, Functor/Arity, Head)
	;	throw(type_error(callable, Pred))
	).

'$lgt_compile_super_call'(Pred, TPred, Ctx) :-
	'$lgt_pp_complemented_object_'(Obj, _, _, _, _),
	% super calls from predicates defined in complementing categories
	% lookup inherited definitions in the complemented object ancestors
	!,
	'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
	(	var(Pred) ->
		TPred = (
			'$lgt_current_object_'(Obj, _, _, _, Super, _, _, _, _, _, _),
			'$lgt_obj_super_call'(Super, Pred, ExCtx)
		)
	;	callable(Pred) ->
		TPred = (
			'$lgt_current_object_'(Obj, _, _, _, Super, _, _, _, _, _, _),
			'$lgt_obj_super_call_'(Super, Pred, ExCtx)
		)
	;	throw(type_error(callable, Pred))
	).

'$lgt_compile_super_call'(Pred, TPred, Ctx) :-
	'$lgt_pp_category_'(Ctg, _, _, _, _, _),
	!,
	(	\+ '$lgt_pp_extended_category_'(_, _, _, _, _, _) ->
		% invalid goal (not an extended category)
		throw(existence_error(ancestor, category))
	;	var(Pred) ->
		% translation performed at runtime
		'$lgt_comp_ctx_exec_ctx'(Ctx, ExCtx),
		TPred = '$lgt_ctg_super_call'(Ctg, Pred, ExCtx)
	;	callable(Pred) ->
		'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, ExCtx, Mode, _, _, _),
		(	'$lgt_compiler_flag'(optimize, on),
			'$lgt_ctg_related_entities_are_static',
			'$lgt_ctg_super_call_static_binding'(Ctg, Pred, ExCtx, TPred) ->
			true
		;	TPred = '$lgt_ctg_super_call_'(Ctg, Pred, ExCtx)
		),
		functor(Pred, Functor, Arity),
		'$lgt_remember_called_super_predicate'(Mode, Functor/Arity, Head)
	;	throw(type_error(callable, Pred))
	).

'$lgt_compile_super_call'(Pred, TPred, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, ExCtx, runtime, _, _, _),
	callable(This),
	'$lgt_current_object_'(This, _, _, _, Super, _, _, _, _, _, _),
	TPred = '$lgt_obj_super_call'(Super, Pred, ExCtx).


'$lgt_obj_related_entities_are_static' :-
	forall(
		'$lgt_pp_extended_object_'(Obj, _, _, _, _, _, _, _, _, _, _),
		('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags), Flags /\ 2 =:= 0)
	),
	forall(
		'$lgt_pp_instantiated_class_'(Obj, _, _, _, _, _, _, _, _, _, _),
		('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags), Flags /\ 2 =:= 0)
	),
	forall(
		'$lgt_pp_specialized_class_'(Obj, _, _, _, _, _, _, _, _, _, _),
		('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags), Flags /\ 2 =:= 0)
	),
	forall(
		'$lgt_pp_imported_category_'(Ctg, _, _, _, _, _),
		('$lgt_current_category_'(Ctg, _, _, _, _, Flags), Flags /\ 2 =:= 0)
	),
	forall(
		'$lgt_pp_implemented_protocol_'(Ptc, _, _, _, _),
		('$lgt_current_protocol_'(Ptc, _, _, _, Flags), Flags /\ 2 =:= 0)
	).


'$lgt_ctg_related_entities_are_static' :-
	forall(
		'$lgt_pp_extended_category_'(Ctg, _, _, _, _, _),
		('$lgt_current_category_'(Ctg, _, _, _, _, Flags), Flags /\ 2 =:= 0)
	),
	forall(
		'$lgt_pp_implemented_protocol_'(Ptc, _, _, _, _),
		('$lgt_current_protocol_'(Ptc, _, _, _, Flags), Flags /\ 2 =:= 0)
	).



% '$lgt_compile_context_switch_call'(@term, @term, -callable, @execution_context)
%
% compiles context-switching calls

'$lgt_compile_context_switch_call'(Obj, Goal, TGoal, ExCtx) :-
	(	var(Obj) ->
		'$lgt_check'(var_or_callable, Goal),
		TGoal = '$lgt_call_within_context'(Obj, Goal, ExCtx)
	;	Obj = {Proxy} ->
		'$lgt_check'(var_or_callable, Proxy),
		(	var(Proxy) ->
			CallProxy = call(Proxy)
		;	CallProxy = Proxy
		),
		'$lgt_compile_context_switch_call'(Proxy, Goal, TGoal0, ExCtx),
		TGoal = (CallProxy, TGoal0)
	;	var(Goal) ->
		'$lgt_check'(var_or_object_identifier, Obj),
		TGoal = '$lgt_call_within_context'(Obj, Goal, ExCtx)
	;	'$lgt_check'(object_identifier, Obj),
		'$lgt_check'(callable, Goal),
		TGoal = '$lgt_call_within_context_nv'(Obj, Goal, ExCtx)
	).



% '$lgt_head_meta_variables'(+callable, -list(variable))
%
% constructs a list of all variables that occur in a position corresponding
% to a meta-argument in the head of clause being compiled

'$lgt_head_meta_variables'(Head, MetaVars) :-
	(	'$lgt_find_head_meta_predicate_template'(Head, Pred, Template) ->
		Pred =.. [_| Args],
		Template =.. [_| MArgs],
		'$lgt_extract_meta_variables'(Args, MArgs, MetaVars)
	;	MetaVars = []
	).


'$lgt_find_head_meta_predicate_template'(Entity::Pred, Pred, Template) :-
	(	'$lgt_pp_meta_predicate_'(Entity::Pred, Entity::Template, _, _)
	;	'$lgt_current_object_'(Entity, _, Dcl, _, _, _, _, _, _, _, _),
		call(Dcl, Pred, _, Template, _), Template \== no
	;	'$lgt_current_category_'(Entity, _, Dcl, _, _, _),
		call(Dcl, Pred, _, Template, _), Template \== no
	),
	!.

'$lgt_find_head_meta_predicate_template'(':'(Module, Pred), Pred, Template) :-
	'$lgt_pp_meta_predicate_'(':'(Module, Pred), ':'(Module, Template), _, _),
	!.

'$lgt_find_head_meta_predicate_template'(Head, Head, Meta) :-
	'$lgt_pp_meta_predicate_'(Head, Meta, _, _).


'$lgt_extract_meta_variables'([], [], []).

'$lgt_extract_meta_variables'([Arg| Args], [MArg| MArgs], MetaVars) :-
	(	MArg == (*) ->
		% normal argument
		'$lgt_extract_meta_variables'(Args, MArgs, MetaVars)
	;	MArg == (::) ->
		term_variables(Arg, MetaVars0),
		'$lgt_append'(MetaVars0, MetaVars1, MetaVars),
		'$lgt_extract_meta_variables'(Args, MArgs, MetaVars1)
	;	integer(MArg),
		% meta-argument (closure or goal)
		nonvar(Arg) ->
		throw(type_error(variable, Arg))
	;	var(Arg) ->
		% meta-argument
		MetaVars = [Arg| RestMetaVars],
		'$lgt_extract_meta_variables'(Args, MArgs, RestMetaVars)
	;	% bound argument and thus not a meta-variable
		'$lgt_extract_meta_variables'(Args, MArgs, MetaVars)
	).



% '$lgt_goal_meta_arguments'(+callable, +callable, -list(term))
%
% constructs a list of all meta-arguments in a goal

'$lgt_goal_meta_arguments'(no, _, []) :-
	!.

'$lgt_goal_meta_arguments'(Meta, Goal, MetaArgs) :-
	% don't require the same predicate name for the meta-predicate
	% template and the goal as the goal may be an alias
	Meta =.. [_| MArgs],
	Goal =.. [_| Args],
	'$lgt_extract_meta_arguments'(MArgs, Args, MetaArgs).


'$lgt_extract_meta_arguments'([], [], []).

'$lgt_extract_meta_arguments'([MArg| MArgs], [Arg| Args], MetaArgs) :-
	(	MArg == (*) ->
		% normal argument
		'$lgt_extract_meta_arguments'(MArgs, Args, MetaArgs)
	;	% meta-argument
		MetaArgs = [Arg| RestMetaArgs],
		'$lgt_extract_meta_arguments'(MArgs, Args, RestMetaArgs)
	).



% '$lgt_goal_meta_call_context'(+callable, @term, @term, -callable)
%
% returns the meta-call execution context: an empty list for local
% meta-calls or the sender execution context when the message is for
% a meta-predicate

'$lgt_goal_meta_call_context'(no, ExCtx, This, []) :-
	!,
	'$lgt_execution_context_this_entity'(ExCtx, This, _).

'$lgt_goal_meta_call_context'(_, ExCtx, This, ExCtx) :-
	'$lgt_execution_context_this_entity'(ExCtx, This, _).



% '$lgt_iso_read_term'(@stream, ?term, +read_options_list, @list)
%
% wraps read_term/3 call with the necessary operator settings

'$lgt_iso_read_term'(Stream, Term, Options, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			read_term(Stream, Term, Options),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_read_term'(?term, +read_options_list, @list)
%
% wraps read_term/2 call with the necessary operator settings

'$lgt_iso_read_term'(Term, Options, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			read_term(Term, Options),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_read'(@stream, ?term, @list)
%
% wraps read/2 call with the necessary operator settings

'$lgt_iso_read'(Stream, Term, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			read(Stream, Term),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_read'(?term, @list)
%
% wraps read/1 call with the necessary operator settings

'$lgt_iso_read'(Term, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			read(Term),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_write_term'(@stream_or_alias, @term, @write_options_list, @list)
%
% wraps write_term/3 call with the necessary operator settings

'$lgt_iso_write_term'(Stream, Term, Options, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			write_term(Stream, Term, Options),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_write_term'(@term, @write_options_list, @list)
%
% wraps write_term/2 call with the necessary operator settings

'$lgt_iso_write_term'(Term, Options, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			write_term(Term, Options),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_write'(@stream_or_alias, @term, @list)
%
% wraps write/2 call with the necessary operator settings

'$lgt_iso_write'(Stream, Term, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			write(Stream, Term),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_write'(@term, @list)
%
% wraps write/1 call with the necessary operator settings

'$lgt_iso_write'(Term, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			write(Term),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_writeq'(@stream_or_alias, @term, @list)
%
% wraps writeq/2 call with the necessary operator settings

'$lgt_iso_writeq'(Stream, Term, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			writeq(Stream, Term),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_iso_writeq'(@term, @list)
%
% wraps writeq/1 call with the necessary operator settings

'$lgt_iso_writeq'(Term, Operators) :-
	catch(
		(	'$lgt_save_operators'(Operators, Saved),
			'$lgt_add_operators'(Operators),
			writeq(Term),
			'$lgt_remove_operators'(Operators),
			'$lgt_add_operators'(Saved)
		),
		Error,
		'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error)
	).



% '$lgt_save_operators'(@list, -list)
%
% saves currently defined operators that might be
% redefined when a list of operators is added

'$lgt_save_operators'([], []).

'$lgt_save_operators'([op(_, Specifier, Operator)| Operators], Saved) :-
	(	current_op(Priority, SCSpecifier, Operator),
		'$lgt_same_operator_class'(Specifier, SCSpecifier) ->
		Saved = [op(Priority, SCSpecifier, Operator)| Saved2]
	;	Saved = Saved2
	),
	'$lgt_save_operators'(Operators, Saved2).



% '$lgt_add_operators'(@list)
%
% adds operators to the global operator table

'$lgt_add_operators'([]).

'$lgt_add_operators'([op(Priority, Specifier, Operator)| Operators]) :-
	op(Priority, Specifier, Operator),
	'$lgt_add_operators'(Operators).



% '$lgt_remove_operators'(@list)
%
% removes operators from the global operator table

'$lgt_remove_operators'([]).

'$lgt_remove_operators'([op(_, Specifier, Operator)| Operators]) :-
	op(0, Specifier, Operator),
	'$lgt_remove_operators'(Operators).



% '$lgt_iso_stream_input_output_error_handler'(@list, @list, @nonvar)
%
% restores operator table to its state before the call
% to one of the '$lgt_iso_read...' that raised an error

'$lgt_iso_stream_input_output_error_handler'(Operators, Saved, Error) :-
	'$lgt_remove_operators'(Operators),
	'$lgt_add_operators'(Saved),
	throw(Error).



% '$lgt_simplify_goal'(+callable, -callable)
%
% simplify the body of a compiled clause by folding left unifications (usually
% resulting from the compilation of grammar rules or from inlined calls to the
% execution-context built-in methods) and by removing redundant calls to true/0
% (but we must be careful with control constructs that are opaque to cuts such
% as call/1 and once/1)

'$lgt_simplify_goal'(Goal, SGoal) :-
	'$lgt_flatten_conjunctions'(Goal, SGoal0),
	'$lgt_fold_left_unifications'(SGoal0, SGoal1),
	'$lgt_remove_redundant_calls'(SGoal1, SGoal).



% '$lgt_flatten_conjunctions'(+callable, -callable)
%
% flattens conjunction of goals
%
% only standard or de facto standard control constructs are traversed to avoid
% compiler performance penalties

'$lgt_flatten_conjunctions'(Goal, Goal) :-
	var(Goal),
	!.

'$lgt_flatten_conjunctions'('*->'(Goal1, Goal2), '*->'(SGoal1, SGoal2)) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_flatten_conjunctions'(Goal1, SGoal1),
	'$lgt_flatten_conjunctions'(Goal2, SGoal2).

'$lgt_flatten_conjunctions'((Goal1 -> Goal2), (SGoal1 -> SGoal2)) :-
	!,
	'$lgt_flatten_conjunctions'(Goal1, SGoal1),
	'$lgt_flatten_conjunctions'(Goal2, SGoal2).

'$lgt_flatten_conjunctions'((Goal1; Goal2), (SGoal1; SGoal2)) :-
	!,
	'$lgt_flatten_conjunctions'(Goal1, SGoal1),
	'$lgt_flatten_conjunctions'(Goal2, SGoal2).

'$lgt_flatten_conjunctions'((Goal1, Goal2), (Goal1, SGoal2)) :-
	var(Goal1),
	!,
	'$lgt_flatten_conjunctions'(Goal2, SGoal2).

'$lgt_flatten_conjunctions'(((Goal1, Goal2), Goal3), Body) :-
	!,
	'$lgt_flatten_conjunctions'((Goal1, (Goal2, Goal3)), Body).

'$lgt_flatten_conjunctions'((Goal1, Goal2), (Goal1, Goal3)) :-
	!,
	'$lgt_flatten_conjunctions'(Goal2, Goal3).

'$lgt_flatten_conjunctions'(\+ Goal, \+ SGoal) :-
	!,
	'$lgt_flatten_conjunctions'(Goal, SGoal).

'$lgt_flatten_conjunctions'(Goal, Goal).



% '$lgt_fold_left_unifications'(+goal, -goal)
%
% folds left unifications; right unifications cannot be folded otherwise
% we may loose steadfastness; the left unifications are typically produced
% when compiling grammar rules to clauses
%
% as the clauses containing the goals being simplified will be asserted
% between the compiler stages, we must be careful to not create cyclic
% terms when performing term unification

'$lgt_fold_left_unifications'(Goal, Goal) :-
	var(Goal),
	!.

'$lgt_fold_left_unifications'((Term1 = Term2), true) :-
	unify_with_occurs_check(Term1, Term2),
	!.

'$lgt_fold_left_unifications'(((Term1 = Term2), Goal), Folded) :-
	unify_with_occurs_check(Term1, Term2),
	!,
	'$lgt_fold_left_unifications'(Goal, Folded).

'$lgt_fold_left_unifications'(Goal, Goal).



% '$lgt_remove_redundant_calls'(+callable, -callable)
%
% removes redundant calls to true/0 from a compiled clause body (we must
% be careful with control constructs that are opaque to cuts such as call/1
% and once/1) and folds pairs of consecutive variable unifications
% (Var1 = Var2, Var2 = Var3) that are usually generated as a by-product of
% the compilation of grammar rules; only standard or de facto standard control
% constructs and meta-predicates are traversed

'$lgt_remove_redundant_calls'(Goal, Goal) :-
	var(Goal),
	!.

'$lgt_remove_redundant_calls'(catch(Goal0, Error, Goal2), SGoal) :-
	nonvar(Goal0),
	Goal0 = call(Goal1),
	!,
	'$lgt_remove_redundant_calls'(catch(Goal1, Error, Goal2), SGoal).

'$lgt_remove_redundant_calls'(catch(Goal1, Error, Goal2), catch(SGoal1, Error, SGoal2)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal1),
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'(call(Goal), true) :-
	Goal == !,
	!.
'$lgt_remove_redundant_calls'(call(Goal), SGoal) :-
	callable(Goal),
	functor(Goal, Functor, _),
	sub_atom(Functor, 0, _, _, '$lgt_'),	% e.g. '$lgt_metacall'
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(call(Goal), call(SGoal)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(once(Goal), true) :-
	Goal == !,
	!.
'$lgt_remove_redundant_calls'(once(Goal), once(SGoal)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(ignore(Goal), ignore(SGoal)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(bagof(Term, Goal, List), bagof(Term, SGoal, List)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(setof(Term, Goal, List), setof(Term, SGoal, List)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(findall(Term, Goal, List), findall(Term, SGoal, List)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(findall(Term, Goal, List, Tail), findall(Term, SGoal, List, Tail)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(forall(Goal1, Goal2), forall(SGoal1, SGoal2)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal1),
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'((IfThen; Else), (SIf -> SThen; SElse)) :-
	nonvar(IfThen),
	IfThen = (If -> Then),
	!,
	'$lgt_remove_redundant_calls'(If, SIf),
	'$lgt_remove_redundant_calls'(Then, SThen),
	'$lgt_remove_redundant_calls'(Else, SElse).

'$lgt_remove_redundant_calls'((IfThen; Else), ('*->'(SIf, SThen); SElse)) :-
	nonvar(IfThen),
	IfThen = '*->'(If, Then),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_remove_redundant_calls'(If, SIf),
	'$lgt_remove_redundant_calls'(Then, SThen),
	'$lgt_remove_redundant_calls'(Else, SElse).

'$lgt_remove_redundant_calls'((Goal1; Goal2), (SGoal1; SGoal2)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal10),
	'$lgt_fix_disjunction_left_side'(SGoal10, SGoal1),
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'((Goal1 -> Goal2), (SGoal1 -> SGoal2)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal1),
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'('*->'(Goal1, Goal2), '*->'(SGoal1, SGoal2)) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal1),
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'((Goal1, Goal2), (Goal1, SGoal2)) :-
	var(Goal1),
	!,
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'((Goal1, Goal2), (SGoal1, Goal2)) :-
	var(Goal2),
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal1).

'$lgt_remove_redundant_calls'((Var1 = Var2a, Var2b = Var3, Goal), SGoal) :-
	Var2a == Var2b,
	'$lgt_remove_redundant_calls'((Var1 = Var3, Goal), SGoal),
	!.

'$lgt_remove_redundant_calls'((Var1 = Var2a, Var2b = Var3), (Var1 = Var3)) :-
	Var2a == Var2b,
	!.

'$lgt_remove_redundant_calls'((Var1 = Var2, Goal), (Var1 = Var2, SGoal)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'((true, Goal), SGoal) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'((Goal, true), SGoal) :-
	% make sure that we don't arrive here while simplifying a (((If->Then),true);Goal) goal (or a
	% as (((If*->Then),true);Goal) goal) as removing the call to true/0 would wrongly convert the
	% disjunction into an if-then-else goal (or a soft-cut goal with an else part)
	Goal \= (_ -> _),
	(	'$lgt_predicate_property'('*->'(_, _), built_in) ->
		Goal \= '*->'(_, _)
	;	true
	),
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'((Goal1, Goal2), (SGoal1, SGoal2)) :-
	!,
	'$lgt_remove_redundant_calls'(Goal1, SGoal1),
	'$lgt_remove_redundant_calls'(Goal2, SGoal2).

'$lgt_remove_redundant_calls'(\+ Goal, \+ SGoal) :-
	!,
	'$lgt_remove_redundant_calls'(Goal, SGoal).

'$lgt_remove_redundant_calls'(Goal, Goal).



% '$lgt_save_parameter_variables'(@object_identifier)
% '$lgt_save_parameter_variables'(@category_identifier)
%
% saves the parameter variable names and positions found
% in parametric entity identifiers for later processing

'$lgt_save_parameter_variables'(Entity) :-
	atom(Entity),
	% non-parametric entity
	!.

'$lgt_save_parameter_variables'(Entity) :-
	% all parameters must be variables
	Entity =.. [_| Parameters],
	'$lgt_member'(Parameter, Parameters),
	nonvar(Parameter),
	throw(type_error(variable, Parameter)).

'$lgt_save_parameter_variables'(_) :-
	'$lgt_pp_term_source_data_'(_, VariableNames, _, _, _),
	'$lgt_parameter_variable_pairs'(VariableNames, 1, ParameterVariablePairs),
	ParameterVariablePairs \== [],
	!,
	% only save a non-empty list of parameter
	% variables to improve compiler performance
	assertz('$lgt_pp_parameter_variables_'(ParameterVariablePairs)).

'$lgt_save_parameter_variables'(_).


'$lgt_parameter_variable_pairs'([], _, []).

'$lgt_parameter_variable_pairs'([VariableName=_| VariableNames], Position, [VariableName-Position| ParameterVariablePairs]) :-
	'$lgt_parameter_variable_name'(VariableName),
	!,
	NextPosition is Position + 1,
	'$lgt_parameter_variable_pairs'(VariableNames, NextPosition, ParameterVariablePairs).

'$lgt_parameter_variable_pairs'([_| VariableNames], Position, ParameterVariablePairs) :-
	NextPosition is Position + 1,
	'$lgt_parameter_variable_pairs'(VariableNames, NextPosition, ParameterVariablePairs).



% '$lgt_parameter_variable_name'(+atom)
%
% checks if a variable name is a parameter variable name (i.e., if the variable
% name starts and ends with an underscore and have at least three characters)

'$lgt_parameter_variable_name'(VariableName) :-
	sub_atom(VariableName, Before, 1, 0, '_'),
	Before >= 2,
	sub_atom(VariableName, 0, 1, _, '_').



% '$lgt_unify_parameter_variables'(+callable, +compilation_context)
%
% unifies any parameter variables found in a parametric entity term
% with the corresponding entity parameters

'$lgt_unify_parameter_variables'(Term, Ctx) :-
	'$lgt_pp_parameter_variables_'(ParameterVariables),
	'$lgt_pp_term_source_data_'(Term, VariableNames, _, _, _),
	VariableNames \== [],
	(	'$lgt_pp_entity_'(_, Entity, _) ->
		% compile-time; instantiate the Entity argument in the compilation context
		true
	;	% runtime <</2 call; Entity already instantiated in the compilation context
		true
	),
	'$lgt_comp_ctx'(Ctx, _, _, Entity, _, _, _, _, _, _, ExCtx, _, _, _, _),
	'$lgt_execution_context_this_entity'(ExCtx, _, Entity),
	'$lgt_unify_parameter_variables'(VariableNames, ParameterVariables, Entity, Unified),
	% ensure that the compilation context is only further instantiated when the
	% term contains at least a parameter variable that is successfully unified
	Unified == true,
	!.

'$lgt_unify_parameter_variables'(_, _).


'$lgt_unify_parameter_variables'([], _, _, _).

'$lgt_unify_parameter_variables'([VariableName=Variable| VariableNames], ParameterVariables, Entity, true) :-
	'$lgt_member'(VariableName-Position, ParameterVariables),
	!,
	arg(Position, Entity, Variable),
	'$lgt_unify_parameter_variables'(VariableNames, ParameterVariables, Entity, true).

'$lgt_unify_parameter_variables'([_| VariableNames], ParameterVariables, Entity, Unified) :-
	'$lgt_unify_parameter_variables'(VariableNames, ParameterVariables, Entity, Unified).



% '$lgt_compile_object_identifier'(@object_identifier, @compilation_context)
%
% from the object identifier construct the set of
% functor prefixes used in the compiled code clauses

'$lgt_compile_object_identifier'(Obj, Ctx) :-
	(	atom(Obj) ->
		GObj = Obj
	;	% parametric object
		'$lgt_term_template'(Obj, GObj)
	),
	'$lgt_add_referenced_object'(GObj, Ctx),
	(	'$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		'$lgt_construct_ic_functors'(GObj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm)
	;	'$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		'$lgt_construct_ic_functors'(GObj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm)
	;	'$lgt_construct_prototype_functors'(GObj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm)
	),
	% the object flags are only computed at the end of the entity compilation
	assertz('$lgt_pp_object_'(GObj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, _)),
	% provide quick access to some common used data on the entity being compiled
	assertz('$lgt_pp_entity_'(object, Obj, Prefix)),
	% initialize the predicate mutex counter
	asserta('$lgt_pp_predicate_mutex_counter_'(0)).



% '$lgt_compile_category_identifier'(@category_identifier, @compilation_context)
%
% from the category identifier construct the set of
% functor prefixes used in the compiled code clauses

'$lgt_compile_category_identifier'(Ctg, Ctx) :-
	(	atom(Ctg) ->
		GCtg = Ctg
	;	% parametric category
		'$lgt_term_template'(Ctg, GCtg)
	),
	'$lgt_add_referenced_category'(GCtg, Ctx),
	'$lgt_construct_category_functors'(GCtg, Prefix, Dcl, Def, Rnm),
	% the category flags are only computed at the end of the entity compilation
	assertz('$lgt_pp_category_'(GCtg, Prefix, Dcl, Def, Rnm, _)),
	% provide quick access to some common used data on the entity being compiled
	assertz('$lgt_pp_entity_'(category, Ctg, Prefix)),
	% initialize the predicate mutex counter
	asserta('$lgt_pp_predicate_mutex_counter_'(0)).



% '$lgt_compile_protocol_identifier'(@protocol_identifier, @compilation_context)
%
% from the protocol identifier construct the set of
% functor prefixes used in the compiled code clauses

'$lgt_compile_protocol_identifier'(Ptc, Ctx) :-
	'$lgt_add_referenced_protocol'(Ptc, Ctx),
	'$lgt_construct_protocol_functors'(Ptc, Prefix, Dcl, Rnm),
	% the protocol flags are only computed at the end of the entity compilation
	assertz('$lgt_pp_protocol_'(Ptc, Prefix, Dcl, Rnm, _)),
	% provide quick access to some common used data on the entity being compiled
	assertz('$lgt_pp_entity_'(protocol, Ptc, Prefix)),
	% initialize the predicate mutex counter; necessary in order to be able to
	% save synchronized predicate properties
	asserta('$lgt_pp_predicate_mutex_counter_'(0)).



% '$lgt_compile_implements_protocol_relation('+list, @object_identifier, @compilation_context)
% '$lgt_compile_implements_protocol_relation'(+list, @category_identifier, @compilation_context)
%
% compiles an "implements" relation between a category or an object and a list of protocols
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate


'$lgt_compile_implements_protocol_relation'([Ref| Refs], ObjOrCtg, Ctx) :-
	'$lgt_check_entity_reference'(protocol, Ref, Scope, Ptc),
	(	ObjOrCtg == Ptc ->
		throw(permission_error(implement, self, ObjOrCtg))
	;	'$lgt_is_object'(Ptc) ->
		throw(type_error(protocol, Ptc))
	;	'$lgt_is_category'(Ptc) ->
		throw(type_error(protocol, Ptc))
	;	'$lgt_add_referenced_protocol'(Ptc, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_implements_protocol_'(ObjOrCtg, Ptc, Scope))),
		'$lgt_construct_protocol_functors'(Ptc, Prefix, Dcl, _),
		assertz('$lgt_pp_implemented_protocol_'(Ptc, ObjOrCtg, Prefix, Dcl, Scope)),
		'$lgt_compile_implements_protocol_relation'(Refs, ObjOrCtg, Ctx)
	).

'$lgt_compile_implements_protocol_relation'([], _, _).



% '$lgt_compile_imports_category_relation'(+list, @object_identifier, @compilation_context)
%
% compiles an "imports" relation between an object and a list of categories
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_imports_category_relation'([Ref| Refs], Obj, Ctx) :-
	'$lgt_check_entity_reference'(category, Ref, Scope, Ctg),
	(	'$lgt_term_template'(Obj, Ctg) ->
		throw(permission_error(import, self, Obj))
	;	'$lgt_is_object'(Ctg) ->
		throw(type_error(category, Ctg))
	;	'$lgt_is_protocol'(Ctg) ->
		throw(type_error(category, Ctg))
	;	'$lgt_add_referenced_category'(Ctg, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_imports_category_'(Obj, Ctg, Scope))),
		'$lgt_construct_category_functors'(Ctg, Prefix, Dcl, Def, _),
		assertz('$lgt_pp_imported_category_'(Ctg, Obj, Prefix, Dcl, Def, Scope)),
		'$lgt_compile_imports_category_relation'(Refs, Obj, Ctx)
	).

'$lgt_compile_imports_category_relation'([], _, _).



% '$lgt_compile_instantiates_class_relation'(+list, @object_identifier, @compilation_context)
%
% compiles an "instantiates" relation between an instance and a list of classes
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_instantiates_class_relation'([Ref| Refs], Obj, Ctx) :-
	'$lgt_check_entity_reference'(object, Ref, Scope, Class),
	(	'$lgt_is_protocol'(Class) ->
		throw(type_error(object, Class))
	;	'$lgt_is_category'(Class) ->
		throw(type_error(object, Class))
	;	'$lgt_is_prototype'(Class) ->
		throw(domain_error(class, Class))
	;	'$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
		throw(permission_error(instantiate, class, Class))
	;	'$lgt_add_referenced_object'(Class, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(Obj, Class, Scope))),
		'$lgt_construct_ic_functors'(Class, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, _),
		assertz('$lgt_pp_instantiated_class_'(Class, Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Scope)),
		'$lgt_compile_instantiates_class_relation'(Refs, Obj, Ctx)
	).

'$lgt_compile_instantiates_class_relation'([], _, _).



% '$lgt_compile_specializes_class_relation'(+list, @object_identifier, @compilation_context)
%
% compiles a "specializes" relation between a class and a list of superclasses
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_specializes_class_relation'([Ref| Refs], Class, Ctx) :-
	'$lgt_check_entity_reference'(object, Ref, Scope, Superclass),
	(	'$lgt_term_template'(Class, Superclass) ->
		throw(permission_error(specialize, self, Class))
	;	'$lgt_is_protocol'(Superclass) ->
		throw(type_error(object, Superclass))
	;	'$lgt_is_category'(Superclass) ->
		throw(type_error(object, Superclass))
	;	'$lgt_is_prototype'(Superclass) ->
		throw(domain_error(class, Superclass))
	;	'$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
		throw(permission_error(specialize, class, Superclass))
	;	'$lgt_add_referenced_object'(Superclass, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(Class, Superclass, Scope))),
		'$lgt_construct_ic_functors'(Superclass, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, _),
		assertz('$lgt_pp_specialized_class_'(Superclass, Class, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Scope)),
		'$lgt_compile_specializes_class_relation'(Refs, Class, Ctx)
	).

'$lgt_compile_specializes_class_relation'([], _, _).



% '$lgt_compile_extends_object_relation'(+list, @object_identifier, @compilation_context)
%
% compiles an "extends" relation between a prototype and a list of parents
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_extends_object_relation'([Ref| Refs], Obj, Ctx) :-
	'$lgt_check_entity_reference'(object, Ref, Scope, Parent),
	(	'$lgt_term_template'(Obj, Parent) ->
		throw(permission_error(extend, self, Obj))
	;	'$lgt_is_protocol'(Parent) ->
		throw(type_error(object, Parent))
	;	'$lgt_is_category'(Parent) ->
		throw(type_error(object, Parent))
	;	'$lgt_is_class'(Parent) ->
		throw(domain_error(prototype, Parent))
	;	'$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		throw(permission_error(extend, prototype, Parent))
	;	'$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		throw(permission_error(extend, prototype, Parent))
	;	'$lgt_add_referenced_object'(Parent, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_extends_object_'(Obj, Parent, Scope))),
		'$lgt_construct_prototype_functors'(Parent, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, _),
		assertz('$lgt_pp_extended_object_'(Parent, Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Scope)),
		'$lgt_compile_extends_object_relation'(Refs, Obj, Ctx)
	).

'$lgt_compile_extends_object_relation'([], _, _).



% '$lgt_compile_extends_protocol_relation'(+list, @protocol_identifier, @compilation_context)
%
% compiles an "extends" relation between a protocol and a list of protocols
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_extends_protocol_relation'([Ref| Refs], Ptc, Ctx) :-
	'$lgt_check_entity_reference'(protocol, Ref, Scope, ExtPtc),
	(	Ptc == ExtPtc ->
		throw(permission_error(extend, self, Ptc))
	;	'$lgt_is_object'(ExtPtc) ->
		throw(type_error(protocol, ExtPtc))
	;	'$lgt_is_category'(ExtPtc) ->
		throw(type_error(protocol, ExtPtc))
	;	'$lgt_add_referenced_protocol'(ExtPtc, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_extends_protocol_'(Ptc, ExtPtc, Scope))),
		'$lgt_construct_protocol_functors'(ExtPtc, Prefix, Dcl, _),
		assertz('$lgt_pp_extended_protocol_'(ExtPtc, Ptc, Prefix, Dcl, Scope)),
		'$lgt_compile_extends_protocol_relation'(Refs, Ptc, Ctx)
	).

'$lgt_compile_extends_protocol_relation'([], _, _).



% '$lgt_compile_extends_category_relation'(+list, @category_identifier, @compilation_context)
%
% compiles an "extends" relation between a category and a list of categories
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_extends_category_relation'([Ref| Refs], Ctg, Ctx) :-
	'$lgt_check_entity_reference'(category, Ref, Scope, ExtCtg),
	(	'$lgt_term_template'(Ctg, ExtCtg) ->
		throw(permission_error(extend, self, Ctg))
	;	'$lgt_is_object'(ExtCtg) ->
		throw(type_error(category, ExtCtg))
	;	'$lgt_is_protocol'(ExtCtg) ->
		throw(type_error(category, ExtCtg))
	;	'$lgt_add_referenced_category'(ExtCtg, Ctx),
		assertz('$lgt_pp_runtime_clause_'('$lgt_extends_category_'(Ctg, ExtCtg, Scope))),
		'$lgt_construct_category_functors'(ExtCtg, Prefix, Dcl, Def, _),
		assertz('$lgt_pp_extended_category_'(ExtCtg, Ctg, Prefix, Dcl, Def, Scope)),
		'$lgt_compile_extends_category_relation'(Refs, Ctg, Ctx)
	).

'$lgt_compile_extends_category_relation'([], _, _).



% '$lgt_compile_complements_object_relation'(+list, @category_identifier, @compilation_context)
%
% compiles a "complements" relation between a category and a list of objects
%
% note that the clause order ensures that instantiation errors will be caught by the call to
% the '$lgt_check_entity_reference'/4 predicate

'$lgt_compile_complements_object_relation'(Objs, Ctg, Ctx) :-
	'$lgt_pp_category_'(Ctg, _, Dcl, Def, Rnm, _),
	'$lgt_compile_complements_object_relation'(Objs, Ctg, Dcl, Def, Rnm, Ctx).


'$lgt_compile_complements_object_relation'([Obj| _], Ctg, _, _, _, _) :-
	'$lgt_check'(object_identifier, Obj),
	(	'$lgt_term_template'(Obj, Ctg) ->
		throw(permission_error(complement, self, Ctg))
	;	'$lgt_is_protocol'(Obj) ->
		throw(type_error(object, Obj))
	;	'$lgt_is_category'(Obj) ->
		throw(type_error(object, Obj))
	;	fail
	).

'$lgt_compile_complements_object_relation'([Obj| _], Ctg, _, _, _, Ctx) :-
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(general, warning),
	(	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags) ->
		% loaded object
		true
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, Flags))
		% object being redefined in the same file as the complementing category;
		% possible but unlikely in practice (except, maybe, in classroom examples)
	),
	Flags /\ 64 =\= 64,
	Flags /\ 32 =\= 32,
	% object compiled with complementing categories support disabled
	'$lgt_source_file_context'(File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(general), complementing_category_ignored(File, Lines, Ctg, Obj)),
	fail.

'$lgt_compile_complements_object_relation'([Obj| Objs], Ctg, Dcl, Def, Rnm, Ctx) :-
	'$lgt_add_referenced_object'(Obj, Ctx),
	% ensure that a new complementing category will take preference over
	% any previously loaded complementing category for the same object
	'$lgt_comp_ctx_lines'(Ctx, Lines),
	asserta('$lgt_pp_file_initialization_'(asserta('$lgt_complemented_object_'(Obj, Ctg, Dcl, Def, Rnm)), Lines)),
	assertz('$lgt_pp_complemented_object_'(Obj, Ctg, Dcl, Def, Rnm)),
	'$lgt_compile_complements_object_relation'(Objs, Ctg, Dcl, Def, Rnm, Ctx).

'$lgt_compile_complements_object_relation'([], _, _, _, _, _).



% '$lgt_is_prototype'(+entity_identifier)
%
% true if the argument is a defined prototype or a prototype being compiled

'$lgt_is_prototype'(Obj) :-
	(	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _) ->
		% existing object; first, check that is not being compiled as a different kind of entity
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Obj, _, _, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_category_'(Obj, _, _, _, _, _)),
		% second, check that it's a prototype
		\+ '$lgt_instantiates_class_'(Obj, _, _),
		\+ '$lgt_instantiates_class_'(_, Obj, _),
		\+ '$lgt_specializes_class_'(Obj, _, _),
		\+ '$lgt_specializes_class_'(_, Obj, _)
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _)) ->
		% object defined earlier in the same file we're compiling; check that it's a prototype
		\+ '$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(Obj, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(_, Obj, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(Obj, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(_, Obj, _))
	;	fail
	).



% '$lgt_is_class'(+entity_identifier)
%
% true if the argument is a defined class or a class being compiled

'$lgt_is_class'(Obj) :-
	(	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _) ->
		% existing object; first, check that is not being compiled as a different kind of entity
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Obj, _, _, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_category_'(Obj, _, _, _, _, _)),
		% second, check that it's an instance or a class
		(	'$lgt_instantiates_class_'(Obj, _, _)
		;	'$lgt_instantiates_class_'(_, Obj, _)
		;	'$lgt_specializes_class_'(Obj, _, _)
		;	'$lgt_specializes_class_'(_, Obj, _)
		), !
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _)) ->
		% object defined earlier in the same file we're compiling; check that it's an instance or a class
		(	'$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(Obj, _, _))
		;	'$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(_, Obj, _))
		;	'$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(Obj, _, _))
		;	'$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(_, Obj, _))
		), !
	;	fail
	).



% '$lgt_is_object'(+entity_identifier)
%
% true if the argument is a defined object or an object being compiled

'$lgt_is_object'(Obj) :-
	(	'$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _) ->
		% existing object; check that is not being compiled as a different kind of entity
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Obj, _, _, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_category_'(Obj, _, _, _, _, _))
	;	'$lgt_pp_object_'(Obj, _, _, _, _, _, _, _, _, _, _) ->
		% object being compiled
		true
	;	'$lgt_pp_runtime_clause_'('$lgt_current_object_'(Obj, _, _, _, _, _, _, _, _, _, _)) ->
		% object defined earlier in the same file we're compiling
		true
	;	fail
	).



% '$lgt_is_protocol'(+entity_identifier)
%
% true if the argument is a defined protocol or a protocol being compiled

'$lgt_is_protocol'(Ptc) :-
	(	'$lgt_current_protocol_'(Ptc, _, _, _, _) ->
		% existing protocol; check that is not being compiled as a different kind of entity
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_object_'(Ptc, _, _, _, _, _, _, _, _, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_category_'(Ptc, _, _, _, _, _))
	;	'$lgt_pp_protocol_'(Ptc, _, _, _, _) ->
		% protocol being compiled
		true
	;	'$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Ptc, _, _, _, _)) ->
		% protocol defined earlier in the same file we're compiling
		true
	;	fail
	).



% '$lgt_is_category'(+entity_identifier)
%
% true if the argument is a defined category or a category being compiled

'$lgt_is_category'(Ctg) :-
	(	'$lgt_current_category_'(Ctg, _, _, _, _, _) ->
		% existing category; check that is not being compiled as a different kind of entity
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_object_'(Ctg, _, _, _, _, _, _, _, _, _, _)),
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Ctg, _, _, _, _))
	;	'$lgt_pp_category_'(Ctg, _, _, _, _, _) ->
		% category being compiled
		true
	;	'$lgt_pp_runtime_clause_'('$lgt_current_category_'(Ctg, _, _, _, _, _)) ->
		% category defined earlier in the same file we're compiling
		true
	;	fail
	).



% '$lgt_inline_calls'(+atom)
%
% inline calls in linking clauses to Prolog module, built-in, and
% foreign predicates when compiling source files in optimal mode
%
% predicate definitions are only inlined when the clause body does
% not contain anonymous variables, which would break the predicate
% semantics (compared with the non-inlined definition) when calling
% the predicate from a bagof/3 or setof/3 goals

'$lgt_inline_calls'(protocol).

'$lgt_inline_calls'(category) :-
	'$lgt_pp_category_'(_, _, _, Def, _, _),
	'$lgt_inline_calls_def'(Def).

'$lgt_inline_calls'(object) :-
	'$lgt_pp_object_'(_, _, _, Def, _, _, _, _, _, _, _),
	'$lgt_inline_calls_def'(Def).


'$lgt_inline_calls_def'(Def) :-
	'$lgt_compiler_flag'(optimize, on),
	\+ '$lgt_pp_dynamic_',
	% static entity
	'$lgt_pp_number_of_clauses_rules_'(Functor, Arity, 1, _),
	% predicate with a single clause
	functor(Head, Functor, Arity),
	\+ '$lgt_pp_dynamic_'(Head, _, _, _),
	\+ '$lgt_pp_multifile_'(Head, _, _, _),
	\+ '$lgt_pp_synchronized_'(Head, _, _, _),
	% static, non-multifile, and no synchronization wrapper
	'$lgt_pp_defines_predicate_'(Head, _, ExCtx, THead, _, user),
	% source file user-defined predicate
	'$lgt_pp_final_entity_term_'((THead :- TBody), _),
	Head =.. [_| HeadArguments],
	term_variables(HeadArguments, HeadVariables),
	HeadArguments == HeadVariables,
	% all head arguments are variables
	\+ '$lgt_variable_aliasing'(Head),
	% don't inline predicate definitions with variable aliasing in the clause
	% head as this can result in optimization bugs when compiling predicate
	% calls due to compile-time variable bindings propagating to previous goals
	% in the same clause body
	'$lgt_inlining_candidate'(TBody, Functor/Arity),
	% valid candidate for inlining
	term_variables(THead, THeadVariables),
	term_variables(TBody, TBodyVariables),
	forall(
		'$lgt_member'(TBodyVariable, TBodyVariables),
		'$lgt_member_var'(TBodyVariable, THeadVariables)
	),
	% no anonymous variables in the body as this would change
	% semantics for calls from bagof/3 and setof/3 goals
	DefClauseOld =.. [Def, Head, _, _],
	retractall('$lgt_pp_def_'(DefClauseOld)),
	DefClauseNew =.. [Def, Head, ExCtx, TBody],
	asserta('$lgt_pp_def_'(DefClauseNew)),
	assertz('$lgt_pp_inline_predicate_'(Functor/Arity)),
	% next candidate predicate
	fail.

'$lgt_inline_calls_def'(_).


'$lgt_inlining_candidate'(':'(Module, Body), _) :-
	% call to a Prolog module predicate
	!,
	atom(Module),
	callable(Body).

'$lgt_inlining_candidate'(TBody, _) :-
	'$lgt_control_construct'(TBody),
	% don't inline control constructs
	!,
	fail.

'$lgt_inlining_candidate'(TBody, _) :-
	'$lgt_logtalk_meta_predicate'(TBody, _, _),
	% don't inline Logtalk built-in meta-predicates
	!,
	fail.

'$lgt_inlining_candidate'(TBody, _) :-
	'$lgt_predicate_property'(TBody, built_in),
	% Prolog built-in predicate
	!.

'$lgt_inlining_candidate'(TBody, _) :-
	% not all backend Prolog systems support a "foreign" predicate property
	catch('$lgt_predicate_property'(TBody, foreign), _, fail),
	% Prolog foreign predicate
	!.

'$lgt_inlining_candidate'(TBody, Functor/Arity) :-
	functor(TBody, TFunctor, TArity),
	'$lgt_pp_referenced_object_message_'(Object, TFunctor/TArity, _, Functor/Arity, _, _),
	Object == user,
	% message to the "user" pseudo-object
	!.

'$lgt_inlining_candidate'(TBody, _) :-
	'$lgt_pp_defines_predicate_'(_, _, _, TBody, _, user),
	% call to a local user-defined predicate
	!.



% '$lgt_logtalk_control_construct'(@callable)
%
% table of Logtalk own control constructs
%
% when these control constructs are used as closures, the additional
% arguments must be appended to the arguments of the goal argument of
% the control construct, not as additional arguments of the control
% construct itself

'$lgt_logtalk_control_construct'(_ :: _).
'$lgt_logtalk_control_construct'(:: _).
'$lgt_logtalk_control_construct'(^^ _).
'$lgt_logtalk_control_construct'(_ << _).
'$lgt_logtalk_control_construct'({_}).
'$lgt_logtalk_control_construct'([_]).
% lambda expressions
'$lgt_logtalk_control_construct'(_ >> _).
'$lgt_logtalk_control_construct'(_ / _).



% '$lgt_control_construct'(?callable)
%
% partial table of control constructs; mainly used to help decide
% if a predicate definition should be compiled inline

'$lgt_control_construct'((_ , _)).
'$lgt_control_construct'((_ ; _)).
'$lgt_control_construct'((_ -> _)).
'$lgt_control_construct'(\+ _).
'$lgt_control_construct'(^^ _).
'$lgt_control_construct'(_ :: _).
'$lgt_control_construct'(:: _).
'$lgt_control_construct'(_ / _).
'$lgt_control_construct'(_ >> _).
'$lgt_control_construct'(_ << _).
'$lgt_control_construct'({_}).
'$lgt_control_construct'(':'(_, _)).
'$lgt_control_construct'(throw(_)).
'$lgt_control_construct'('*->'(_, _)) :-
	'$lgt_prolog_built_in_predicate'('*->'(_, _)).



% '$lgt_cut_transparent_control_construct'(?callable)
%
% table of cut-transparent control constructs; used during
% compilation to check if call/1-N wrappers need to be keep
% for preserving source code semantics when the goal/closure
% argument is bound

'$lgt_cut_transparent_control_construct'(!).
'$lgt_cut_transparent_control_construct'((_ , _)).
'$lgt_cut_transparent_control_construct'((_ ; _)).
'$lgt_cut_transparent_control_construct'((_ -> _)).
'$lgt_cut_transparent_control_construct'('*->'(_, _)) :-
	'$lgt_prolog_built_in_predicate'('*->'(_, _)).



% '$lgt_report_lint_issues'(+atom, @entity_identifier)
%
% reports detected lint issues found while compiling an entity
% (note that some lint issues are reported during compilation)

'$lgt_report_lint_issues'(Type, Entity) :-
	'$lgt_report_missing_directives'(Type, Entity),
	'$lgt_report_non_portable_calls'(Type, Entity),
	'$lgt_report_missing_functions'(Type, Entity),
	'$lgt_report_predicates_called_as_non_terminals'(Type, Entity),
	'$lgt_report_non_tail_recursive_predicates'(Type, Entity),
	'$lgt_report_unknown_entities'(Type, Entity),
	'$lgt_report_unknown_messages'(Type, Entity),
	'$lgt_report_naming_issues'(Type, Entity).



% '$lgt_source_file_context'(-atom, -pair(integer), -atom, -entity_identifier)
%
% returns file, lines, and entity source context for the last term read;
% it fails if the last attempt to read a term resulted in a syntax error

'$lgt_source_file_context'(File, Lines, Type, Entity) :-
	'$lgt_pp_term_source_data_'(_, _, _, File, Lines),
	'$lgt_pp_entity_'(Type, Entity, _).



% '$lgt_source_file_context'(-atom, -pair(integer))
%
% returns file and lines source context for the last term read;
% it fails if the last attempt to read a term resulted in a syntax error

'$lgt_source_file_context'(File, Lines) :-
	'$lgt_pp_term_source_data_'(_, _, _, File, Lines).



% '$lgt_source_file_context'(@compilation_context, -atom, -pair(integer), -atom, -entity_identifier)
%
% returns file, lines, and entity source context for the last term read;
% it fails if the last attempt to read a term resulted in a syntax error;
% in the context of runtime compilation, returns dummy file and line values

'$lgt_source_file_context'(Ctx, File, Lines, Type, Entity) :-
	'$lgt_source_file_context'(Ctx, File, Lines),
	'$lgt_pp_entity_'(Type, Entity, _).



% '$lgt_source_file_context'(@compilation_context, -atom, -pair(integer))
%
% in the context of compiling a file, returns file and lines source context
% for the last term read and fails if the last attempt to read a term
% resulted in a syntax error; in the context of runtime compilation, returns
% dummy file and line values

'$lgt_source_file_context'(Ctx, File, Lines) :-
	(	'$lgt_comp_ctx_mode'(Ctx, runtime) ->
		File = nil, Lines = '-'(-1, -1)
	;	'$lgt_pp_term_source_data_'(_, _, _, File, Lines) ->
		true
	;	% e.g. when compiling auxiliary clauses at runtime
		File = nil, Lines = 0-0
	).



% '$lgt_report_unknown_entities'(+atom, @entity_identifier, +atom)
%
% reports any unknown referenced entities found while compiling an entity

'$lgt_report_unknown_entities'(_, _) :-
	'$lgt_compiler_flag'(unknown_entities, silent),
	!.

'$lgt_report_unknown_entities'(protocol, Entity) :-
	% protocols can only reference other protocols
	!,
	'$lgt_report_unknown_protocols'(protocol, Entity).

'$lgt_report_unknown_entities'(Type, Entity) :-
	'$lgt_report_unknown_objects'(Type, Entity),
	'$lgt_report_unknown_protocols'(Type, Entity),
	'$lgt_report_unknown_categories'(Type, Entity),
	'$lgt_report_unknown_modules'(Type, Entity).



% '$lgt_report_unknown_objects'(+atom, @entity_identifier)
%
% reports any references to unknown objects found while compiling an entity

'$lgt_report_unknown_objects'(Type, Entity) :-
	'$lgt_pp_referenced_object_'(Object, File, Lines),
		% not a currently loaded object
		\+ '$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _),
		% not the object being compiled (self reference)
		\+ '$lgt_pp_object_'(Object, _, _, _, _, _, _, _, _, _, _),
		% not an object defined earlier in the source file being compiled
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, _)),
		'$lgt_increment_compiling_warnings_counter',
		(	atom(Object),
			'$lgt_prolog_feature'(modules, supported),
			current_module(Object) ->
			'$lgt_print_message'(warning(unknown_entities), module_used_as_object(File, Lines, Type, Entity, Object))
		;	'$lgt_print_message'(warning(unknown_entities), reference_to_unknown_object(File, Lines, Type, Entity, Object))
		),
	fail.

'$lgt_report_unknown_objects'(_, _).



% '$lgt_report_unknown_protocols'(+atom, @entity_identifier)
%
% reports any references to unknown protocols found while compiling an entity

'$lgt_report_unknown_protocols'(Type, Entity) :-
	'$lgt_pp_referenced_protocol_'(Protocol, File, Lines),
		% not a currently loaded protocol
		\+ '$lgt_current_protocol_'(Protocol, _, _, _, _),
		% not the protocol being compiled (self reference)
		\+ '$lgt_pp_protocol_'(Protocol, _, _, _, _),
		% not a protocol defined earlier in the source file being compiled
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_protocol_'(Protocol, _, _, _, _)),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(unknown_entities),
			reference_to_unknown_protocol(File, Lines, Type, Entity, Protocol)
		),
	fail.

'$lgt_report_unknown_protocols'(_, _).



% '$lgt_report_unknown_categories'(+atom, @entity_identifier)
%
% reports any references to unknown categories found while compiling an entity

'$lgt_report_unknown_categories'(Type, Entity) :-
	'$lgt_pp_referenced_category_'(Category, File, Lines),
		% not a currently loaded category
		\+ '$lgt_current_category_'(Category, _, _, _, _, _),
		% not the category being compiled (self reference)
		\+ '$lgt_pp_category_'(Category, _, _, _, _, _),
		% not a category defined earlier in the source file being compiled
		\+ '$lgt_pp_runtime_clause_'('$lgt_current_category_'(Category, _, _, _, _, _)),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(unknown_entities),
			reference_to_unknown_category(File, Lines, Type, Entity, Category)
		),
	fail.

'$lgt_report_unknown_categories'(_, _).



% '$lgt_report_unknown_modules'(+atom, @entity_identifier)
%
% reports any references to unknown modules found while compiling an entity

'$lgt_report_unknown_modules'(Type, Entity) :-
	'$lgt_prolog_feature'(modules, supported),
	'$lgt_pp_referenced_module_'(Module, File, Lines),
		% not a currently loaded module
		\+ current_module(Module),
		% not the module being compiled as an object (self reference)
		\+ '$lgt_pp_module_'(Module),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(unknown_entities),
			reference_to_unknown_module(File, Lines, Type, Entity, Module)
		),
	fail.

'$lgt_report_unknown_modules'(_, _).



% '$lgt_report_unknown_messages'(+atom, @entity_identifier)
%
% reports any unknown messages for loaded objects (including built-in objects) and
% calls to unknown predicates for loaded modules found while compiling an entity

'$lgt_report_unknown_messages'(_, _) :-
	'$lgt_compiler_flag'(unknown_predicates, silent),
	!.

'$lgt_report_unknown_messages'(Type, Entity) :-
	'$lgt_pp_uses_predicate_'(Obj, Original, _, _, File, Lines),
	nonvar(Obj),
	functor(Original, Functor, Arity),
	'$lgt_check_predicate_availability'(uses, Obj, Original, Original, Functor, Arity, Type, Entity, File, Lines),
	fail.

'$lgt_report_unknown_messages'(Type, Entity) :-
	'$lgt_pp_uses_non_terminal_'(Obj, Original, _, Pred, _, _, File, Lines),
	nonvar(Obj),
	functor(Pred, Functor, Arity),
	'$lgt_check_predicate_availability'(uses, Obj, Original, Pred, Functor, Arity, Type, Entity, File, Lines),
	fail.

'$lgt_report_unknown_messages'(Type, Entity) :-
	'$lgt_prolog_feature'(modules, supported),
	'$lgt_pp_use_module_predicate_'(Module, Original, _, _, File, Lines),
	nonvar(Module),
	functor(Original, Functor, Arity),
	'$lgt_check_predicate_availability'(use_module, Module, Original, Original, Functor, Arity, Type, Entity, File, Lines),
	fail.

'$lgt_report_unknown_messages'(Type, Entity) :-
	'$lgt_prolog_feature'(modules, supported),
	'$lgt_pp_use_module_non_terminal_'(Module, Original, _, Pred, _, _, File, Lines),
	nonvar(Module),
	functor(Pred, Functor, Arity),
	'$lgt_check_predicate_availability'(use_module, Module, Original, Pred, Functor, Arity, Type, Entity, File, Lines),
	fail.

'$lgt_report_unknown_messages'(_, _).



% auxiliary predicate for checking predicate availability for predicates
% listed in uses/2 and use_module/2 directives but only when the objects
% and modules are loaded

'$lgt_check_predicate_availability'(uses, Obj, Original, Pred, Functor, Arity, Type, Entity, File, Lines) :-
	(	\+ current_object(Obj) ->
		true
	;	Obj::current_predicate(Functor/Arity) ->
		true
	;	Obj == user,
		(	'$lgt_predicate_property'(Pred, built_in)
		;	catch('$lgt_predicate_property'(Pred, foreign), _, fail)
		;	catch('$lgt_predicate_property'(Pred, imported_from(_)), _, fail)
		;	'$lgt_pp_directive_'(dynamic(Functor/Arity))
		;	'$lgt_pp_directive_'(multifile(Functor/Arity))
		) ->
		true
	;	'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(unknown_predicates),
			message_not_understood(File, Lines, Type, Entity, Obj, Original)
		)
	).

'$lgt_check_predicate_availability'(use_module, Module, Original, Pred, Functor, Arity, Type, Entity, File, Lines) :-
	(	\+ current_module(Module) ->
		true
	;	'$lgt_current_module_predicate'(Module, Functor/Arity) ->
		true
	;	Module == user,
		(	'$lgt_predicate_property'(Pred, built_in)
		;	catch('$lgt_predicate_property'(Pred, foreign), _, fail)
		;	catch('$lgt_predicate_property'(Pred, imported_from(_)), _, fail)
		;	'$lgt_pp_directive_'(dynamic(Functor/Arity))
		;	'$lgt_pp_directive_'(multifile(Functor/Arity))
		) ->
		true
	;	'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(unknown_predicates),
			unknown_module_predicate(File, Lines, Type, Entity, Module, Original)
		)
	).



% '$lgt_report_naming_issues'(Type, Entity)
%
% reports names not following official coding guidelines

'$lgt_report_naming_issues'(Type, Entity) :-
	(	'$lgt_compiler_flag'(naming, warning) ->
		'$lgt_report_entity_naming_issues'(Type, Entity),
		'$lgt_report_predicate_naming_issues'(Type, Entity)
	;	true
	).


'$lgt_report_entity_naming_issues'(Type, Entity) :-
	functor(Entity, Name, _),
	atom_chars(Name, Chars),
	(	'$lgt_camel_case_name'(Chars),
		Warning = camel_case_entity_name(File, Lines, Type, Entity)
	;	'$lgt_name_with_digits_in_the_middle'(Chars),
		Warning = entity_name_with_digits_in_the_middle(File, Lines, Type, Entity)
	),
	(	'$lgt_pp_referenced_object_'(Entity, File, Lines) ->
		true
	;	'$lgt_pp_referenced_protocol_'(Entity, File, Lines) ->
		true
	;	'$lgt_pp_referenced_category_'(Entity, File, Lines) ->
		true
	;	'$lgt_pp_file_paths_flags_'(_, _, File, _, _),
		Lines = '-'(-1, -1)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_entity_naming_issues'(_, _).


'$lgt_report_predicate_naming_issues'(Type, Entity) :-
	(	'$lgt_pp_public_'(Name, Arity, _, _)
	;	'$lgt_pp_protected_'(Name, Arity, _, _)
	;	'$lgt_pp_private_'(Name, Arity, _, _)
	),
	% backtrack over all declared predicates
	\+ '$lgt_pp_non_terminal_'(Name, _, Arity),
	% not declared as non-terminals
	functor(Template, Name, Arity),
	\+ '$lgt_pp_defines_predicate_'(Template, _, _, _, _, _),
	% not defined
	atom_chars(Name, Chars),
	(	'$lgt_camel_case_name'(Chars),
		Warning = camel_case_predicate_name(File, Lines, Type, Entity, Name/Arity)
	;	'$lgt_name_with_digits_in_the_middle'(Chars),
		Warning = predicate_name_with_digits_in_the_middle(File, Lines, Type, Entity, Name/Arity)
	),
	(	'$lgt_pp_predicate_declaration_location_'(Name, Arity, File, Lines) ->
		true
	;	'$lgt_source_file_context'(File, _),
		Lines = '-'(-1, -1)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_predicate_naming_issues'(Type, Entity) :-
	'$lgt_pp_non_terminal_'(Name, Arity, ExtArity),
	% declared non-terminal (in a scope directive)
	functor(Template, Name, ExtArity),
	\+ '$lgt_pp_defines_predicate_'(Template, _, _, _, _, _),
	% not defined
	atom_chars(Name, Chars),
	(	'$lgt_camel_case_name'(Chars),
		Warning = camel_case_non_terminal_name(File, Lines, Type, Entity, Name//Arity)
	;	'$lgt_name_with_digits_in_the_middle'(Chars),
		Warning = non_terminal_name_with_digits_in_the_middle(File, Lines, Type, Entity, Name//Arity)
	),
	(	'$lgt_pp_predicate_declaration_location_'(Name, ExtArity, File, Lines) ->
		true
	;	'$lgt_source_file_context'(File, _),
		Lines = '-'(-1, -1)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_predicate_naming_issues'(Type, Entity) :-
	'$lgt_pp_defines_predicate_'(_, Name/Arity, _, _, _, user),
	\+ '$lgt_pp_defines_non_terminal_'(Name, _, Arity),
	\+ '$lgt_pp_public_'(Name, Arity, _, _),
	\+ '$lgt_pp_protected_'(Name, Arity, _, _),
	\+ '$lgt_pp_private_'(Name, Arity, _, _),
	\+ '$lgt_pp_non_terminal_'(Name, _, Arity),
	% user-defined local predicate
	atom_chars(Name, Chars),
	(	'$lgt_camel_case_name'(Chars),
		Warning = camel_case_predicate_name(File, Lines, Type, Entity, Name/Arity)
	;	'$lgt_name_with_digits_in_the_middle'(Chars),
		Warning = predicate_name_with_digits_in_the_middle(File, Lines, Type, Entity, Name/Arity)
	),
	(	'$lgt_pp_predicate_definition_location_'(Name, Arity, File, Lines) ->
		true
	;	'$lgt_source_file_context'(File, _),
		Lines = '-'(-1, -1)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_predicate_naming_issues'(Type, Entity) :-
	'$lgt_pp_defines_non_terminal_'(Name, Arity, ExtArity),
	'$lgt_pp_defines_predicate_'(_, Name/ExtArity, _, _, _, user),
	\+ '$lgt_pp_public_'(Name, ExtArity, _, _),
	\+ '$lgt_pp_protected_'(Name, ExtArity, _, _),
	\+ '$lgt_pp_private_'(Name, ExtArity, _, _),
	\+ '$lgt_pp_non_terminal_'(Name, Arity, ExtArity),
	% user-defined local non-terminal
	atom_chars(Name, Chars),
	(	'$lgt_camel_case_name'(Chars),
		Warning = camel_case_non_terminal_name(File, Lines, Type, Entity, Name//Arity)
	;	'$lgt_name_with_digits_in_the_middle'(Chars),
		Warning = non_terminal_name_with_digits_in_the_middle(File, Lines, Type, Entity, Name//Arity)
	),
	(	'$lgt_pp_predicate_definition_location_'(Name, ExtArity, File, Lines) ->
		true
	;	'$lgt_source_file_context'(File, _),
		Lines = '-'(-1, -1)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_predicate_naming_issues'(_, _).


'$lgt_camel_case_name'(Chars) :-
	'$lgt_append'([_| _], [Char1, Char2| _], Chars),
	a @=< Char1, Char1 @=< z,
	'A' @=< Char2, Char2 @=< 'Z',
	!.


'$lgt_name_with_digits_in_the_middle'(Chars) :-
	'$lgt_append'([_| _], [Char1, Char2| _], Chars),
	'0' @=< Char1, Char1 @=< '9',
	('0' @> Char2; Char2 @> '9'),
	!.



% '$lgt_add_def_clause'(+callable, +atom, +integer, -callable, +compilation_context)
%
% adds a "def" clause (used to translate between user predicate names and internal names)
% and returns the compiled clause head

'$lgt_add_def_clause'(Head, Functor, Arity, THead, Ctx) :-
	functor(HeadTemplate, Functor, Arity),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, Lines, _),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(THeadTemplate, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(HeadTemplate, THeadTemplate, ExCtxTemplate),
	(	'$lgt_pp_object_'(_, _, _, Def, _, _, _, _, _, _, _) ->
		true
	;	'$lgt_pp_category_'(_, _, _, Def, _, _)
	),
	'$lgt_construct_def_clause'(Def, HeadTemplate, ExCtxTemplate, THeadTemplate, Clause),
	assertz('$lgt_pp_def_'(Clause)),
	% the following two calls have side effects, thus ...
	'$lgt_check_for_redefined_built_in'(Mode, HeadTemplate, ExCtxTemplate, THeadTemplate, Lines),
	'$lgt_remember_defined_predicate'(Mode, HeadTemplate, Functor/Arity, ExCtxTemplate, THeadTemplate),
	% ... we need to delay output unifications to after they succeed
	Head = HeadTemplate,
	ExCtx = ExCtxTemplate,
	THead = THeadTemplate.



% '$lgt_add_ddef_clause'(+callable, +atom, +integer, -callable, +compilation_context)
%
% adds a "ddef" clause (used to translate between user predicate names and internal names)
% and returns the compiled clause head

'$lgt_add_ddef_clause'(Head, Functor, Arity, THead, Ctx) :-
	functor(HeadTemplate, Functor, Arity),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, Prefix, _, _, ExCtx, Mode, _, Lines, _),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(THeadTemplate, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(HeadTemplate, THeadTemplate, ExCtxTemplate),
	% only objects can define clauses for dynamic predicates
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, DDef, _, _),
	'$lgt_construct_def_clause'(DDef, HeadTemplate, ExCtxTemplate, THeadTemplate, Clause),
	assertz('$lgt_pp_ddef_'(Clause)),
	% the following two calls have side effects, thus ...
	'$lgt_check_for_redefined_built_in'(Mode, HeadTemplate, ExCtxTemplate, THeadTemplate, Lines),
	'$lgt_remember_defined_predicate'(Mode, HeadTemplate, Functor/Arity, ExCtxTemplate, THeadTemplate),
	% ... we need to delay output unifications to after they succeed
	Head = HeadTemplate,
	ExCtx = ExCtxTemplate,
	THead = THeadTemplate.



% '$lgt_construct_def_clause'(+callable, +callable, +execution_context, +callable, -clause)
%
% constructs a "def" or "ddef" clause (used to translate between user predicate names and internal names)

'$lgt_construct_def_clause'(Def, Head, ExCtx, THead, Clause) :-
	(	'$lgt_pp_synchronized_'(Head, Mutex, _, _) ->
		'$lgt_wrap_compiled_head'(Head, THead, ExCtx, Call),
		(	'$lgt_prolog_feature'(threads, supported) ->
			Clause =.. [Def, Head, ExCtx, with_mutex(Mutex,Call)]
		;	% in single-threaded systems, with_mutex/2 is equivalent to once/1
			Clause =.. [Def, Head, ExCtx, once(Call)]
		)
	;	'$lgt_pp_coinductive_head_'(Head, ExCtx, TCHead) ->
		'$lgt_wrap_compiled_head'(Head, TCHead, ExCtx, Call),
		Clause =.. [Def, Head, ExCtx, Call]
	;	'$lgt_wrap_compiled_head'(Head, THead, ExCtx, Call),
		Clause =.. [Def, Head, ExCtx, Call]
	).



% predicates for wrapping/unwrapping compiled predicate heads to deal with
% compilation in debug mode
%
% the wrapping when in compilation mode ensures that indirect predicate calls
% (e.g., when sending a message) can also be intercepted by debug handlers

'$lgt_wrap_compiled_head'(Head, THead, ExCtx, Call) :-
	(	'$lgt_compiler_flag'(debug, on) ->
		Call = '$lgt_debug'(goal(Head,THead), ExCtx)
	;	Call = THead
	).


'$lgt_unwrap_compiled_head'('$lgt_debug'(goal(_,THead), _), THead) :-
	!.

'$lgt_unwrap_compiled_head'(THead, THead).



% '$lgt_add_def_fail_clause'(@callable, @compilation_context)
%
% adds a "def clause" (used to translate a predicate call) where the
% definition is simply fail due to the predicate being declared, static,
% but undefined (as per closed-world assumption)

'$lgt_add_def_fail_clause'(Head, Ctx) :-
	(	'$lgt_pp_object_'(_, _, _, Def, _, _, _, _, _, _, _) ->
		true
	;	'$lgt_pp_category_'(_, _, _, Def, _, _)
	),
	Clause =.. [Def, Head, _, fail],
	assertz('$lgt_pp_def_'(Clause)),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _),
	'$lgt_check_for_redefined_built_in'(Mode, Head, _, fail, Lines).



% '$lgt_check_for_redefined_built_in'(@compilation_mode, @callable, @execution_context, @callable, @pair)
%
% this predicate is called when adding a def/ddef clause after finding the first clause
% for a predicate or when no clauses are defined for a declared predicate

'$lgt_check_for_redefined_built_in'(runtime, _, _, _, _).

'$lgt_check_for_redefined_built_in'(compile(_,_,_), Head, ExCtx, THead, Lines) :-
	'$lgt_logtalk_built_in_predicate'(Head, _),
	!,
	assertz('$lgt_pp_redefined_built_in_'(Head, ExCtx, THead)),
	retractall('$lgt_pp_non_portable_predicate_'(Head, _, _)),
	(	'$lgt_compiler_flag'(redefined_built_ins, warning) ->
		functor(Head, Functor, Arity),
		'$lgt_source_file_context'(File, _, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(redefined_built_ins),
			redefined_logtalk_built_in_predicate(File, Lines, Type, Entity, Functor/Arity)
		)
	;	true
	).

'$lgt_check_for_redefined_built_in'(compile(_,_,_), Head, ExCtx, THead, Lines) :-
	'$lgt_prolog_built_in_predicate'(Head),
	!,
	assertz('$lgt_pp_redefined_built_in_'(Head, ExCtx, THead)),
	retractall('$lgt_pp_non_portable_predicate_'(Head, _, _)),
	(	'$lgt_compiler_flag'(redefined_built_ins, warning) ->
		functor(Head, Functor, Arity),
		'$lgt_source_file_context'(File, _, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(redefined_built_ins),
			redefined_prolog_built_in_predicate(File, Lines, Type, Entity, Functor/Arity)
		)
	;	true
	).

'$lgt_check_for_redefined_built_in'(compile(_,_,_), _, _, _, _).



% '$lgt_remember_defined_predicate'(@compilation_mode, @callable, +predicate_indicator, +execution_context, @callable)
%
% it's necessary to remember which predicates are defined in order to deal with
% redefinition of built-in predicates, detect missing predicate directives, and
% speed up compilation of other clauses for the same predicates

'$lgt_remember_defined_predicate'(Mode, Head, PI, ExCtx, THead) :-
	(	Mode = compile(aux,_,_) ->
		assertz('$lgt_pp_defines_predicate_'(Head, PI, ExCtx, THead, Mode, aux)),
		retractall('$lgt_pp_previous_predicate_'(_, aux)),
		assertz('$lgt_pp_previous_predicate_'(Head, aux))
	;	% compile(user,_,_) or runtime
		assertz('$lgt_pp_defines_predicate_'(Head, PI, ExCtx, THead, Mode, user)),
		retractall('$lgt_pp_previous_predicate_'(_, user)),
		assertz('$lgt_pp_previous_predicate_'(Head, user))
	).



% '$lgt_report_variable_naming_issues'(+list, +atom, +compound)
%
% reports variable naming issues as per official coding guidelines

'$lgt_report_variable_naming_issues'([], _, _) :-
	!.

'$lgt_report_variable_naming_issues'(_, _, _) :-
	'$lgt_compiler_flag'(naming, silent),
	!.

'$lgt_report_variable_naming_issues'(Names, File, Lines) :-
	'$lgt_member'(Name=_, Names),
	'$lgt_non_camel_case_name'(Name),
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		Warning = non_camel_case_variable_name(File, Lines, Type, Entity, Name)
	;	Warning = non_camel_case_variable_name(File, Lines, Name)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_variable_naming_issues'(Names, File, Lines) :-
	'$lgt_member'(Name=_, Names),
	atom_chars(Name, Chars),
	'$lgt_name_with_digits_in_the_middle'(Chars),
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		Warning = variable_name_with_digits_in_the_middle(File, Lines, Type, Entity, Name)
	;	Warning = variable_name_with_digits_in_the_middle(File, Lines, Name)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_variable_naming_issues'(Names, File, Lines) :-
	'$lgt_name_pair'(Names, Name, OtherName),
	'$lgt_similar_names'(Name, OtherName),
	(	'$lgt_pp_entity_'(Type, Entity, _) ->
		Warning = variable_names_differ_only_on_case(File, Lines, Type, Entity, Name, OtherName)
	;	Warning = variable_names_differ_only_on_case(File, Lines, Name, OtherName)
	),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(warning(naming), Warning),
	fail.

'$lgt_report_variable_naming_issues'(_, _, _).


'$lgt_non_camel_case_name'(Name) :-
	atom_chars(Name, Chars),
	'$lgt_append'(_, [Char1, '_', Char2| _], Chars),
	Char1 \== '_',
	Char2 \== '_',
	!.


'$lgt_name_pair'([Name=_| Names], Name, OtherName) :-
	'$lgt_member'(OtherName=_, Names).

'$lgt_name_pair'([_| Names], Name, OtherName) :-
	'$lgt_name_pair'(Names, Name, OtherName).


'$lgt_similar_names'(Name, OtherName) :-
	atom_length(Name, Length),
	atom_length(OtherName, Length),
	% same length
	sub_atom(Name, _, 1, 0, Last),
	\+ ('0' @=< Last, Last @=< '9'),
	sub_atom(OtherName, _, 1, 0, OtherLast),
	\+ ('0' @=< OtherLast, OtherLast @=< '9'),
	% not ending with a digit
	'$lgt_to_lower_case'(Name, NameLowerCase),
	'$lgt_to_lower_case'(OtherName, OtherNameLowerCase),
	NameLowerCase == OtherNameLowerCase.


'$lgt_to_lower_case'(Name, NameLowerCase) :-
	atom_codes(Name, Codes),
	'$lgt_to_lower_case_codes'(Codes, CodesLowerCase),
	atom_codes(NameLowerCase, CodesLowerCase).


'$lgt_to_lower_case_codes'([], []).

'$lgt_to_lower_case_codes'([Code| Codes], [LowerCode| LowerCodes]) :-
	65 =< Code, Code @=< 90,
	!,
	LowerCode is 97 + Code - 65,
	'$lgt_to_lower_case_codes'(Codes, LowerCodes).

'$lgt_to_lower_case_codes'([Code| Codes], [Code| LowerCodes]) :-
	'$lgt_to_lower_case_codes'(Codes, LowerCodes).



% '$lgt_update_ddef_table'(+atom, @callable, @callable)
%
% retracts a dynamic "ddef clause" (used to translate a predicate call)
% and updated the predicate lookup caches if there are no more (local)
% clauses for the predicate otherwise does nothing; this is required in
% order to allow definitions in ancestor entities to be found

'$lgt_update_ddef_table'(DDef, Head, THead) :-
	'$lgt_term_template'(THead, GTHead),
	(	clause(GTHead, _) ->
		true
	;	DDefClause =.. [DDef, Head, _, _],
		retractall(DDefClause),
		'$lgt_clean_lookup_caches'(Head)
	).



% '$lgt_update_ddef_table_opt'(+callable)
%
% retracts a dynamic "ddef clause" (used to translate a predicate call)
% and updates the predicate lookup caches if there are no more (local)
% clauses for the predicate otherwise does nothing; this is required in
% order to allow definitions in ancestor entities to be found when all
% the overriding clauses in an intermediate object are retracted

'$lgt_update_ddef_table_opt'(true).

'$lgt_update_ddef_table_opt'(update(Head, THead, Clause)) :-
	(	clause(THead, _) ->
		true
	;	retractall(Clause),
		'$lgt_clean_lookup_caches'(Head)
	).



% '$lgt_generate_entity_code'(+atom, +compilation_context)
%
% generates code for the entity being compiled

'$lgt_generate_entity_code'(protocol, _) :-
	'$lgt_generate_protocol_clauses',
	'$lgt_generate_protocol_directives'.

'$lgt_generate_entity_code'(object, Ctx) :-
	'$lgt_generate_def_table_clauses'(Ctx),
	'$lgt_compile_predicate_calls'(compile_time),
	'$lgt_generate_object_clauses',
	'$lgt_generate_object_directives',
	'$lgt_generate_file_object_initialization_goal'.

'$lgt_generate_entity_code'(category, Ctx) :-
	'$lgt_generate_def_table_clauses'(Ctx),
	'$lgt_compile_predicate_calls'(compile_time),
	'$lgt_generate_category_clauses',
	'$lgt_generate_category_directives',
	'$lgt_generate_file_category_initialization_goal'.



'$lgt_generate_object_directives' :-
	'$lgt_generate_object_dynamic_directives',
	'$lgt_generate_object_discontiguous_directives'.



'$lgt_generate_category_directives' :-
	'$lgt_generate_category_dynamic_directives',
	'$lgt_generate_category_discontiguous_directives'.



'$lgt_generate_protocol_directives' :-
	(	'$lgt_pp_dynamic_' ->
		% add the necessary directives to allow abolishing the protocol
		'$lgt_pp_protocol_'(_, _, Dcl, Rnm, _),
		assertz('$lgt_pp_directive_'(dynamic(Dcl/4))),
		assertz('$lgt_pp_directive_'(dynamic(Dcl/5))),
		assertz('$lgt_pp_directive_'(dynamic(Rnm/3)))
	;	true
	).



'$lgt_generate_object_dynamic_directives' :-
	(	'$lgt_pp_dynamic_' ->
		% add the necessary directives to allow abolishing the object
		'$lgt_generate_dynamic_object_dynamic_directives'
	;	'$lgt_generate_static_object_dynamic_directives'
	).



'$lgt_generate_dynamic_object_dynamic_directives' :-
	'$lgt_pp_object_'(_, _, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, _),
	assertz('$lgt_pp_directive_'(dynamic(Dcl/4))),
	assertz('$lgt_pp_directive_'(dynamic(Dcl/6))),
	assertz('$lgt_pp_directive_'(dynamic(Def/3))),
	assertz('$lgt_pp_directive_'(dynamic(Def/5))),
	assertz('$lgt_pp_directive_'(dynamic(Super/5))),
	assertz('$lgt_pp_directive_'(dynamic(IDcl/6))),
	assertz('$lgt_pp_directive_'(dynamic(IDef/5))),
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		assertz('$lgt_pp_directive_'(dynamic(DDcl/2)))
	;	true
	),
	assertz('$lgt_pp_directive_'(dynamic(DDef/3))),
	assertz('$lgt_pp_directive_'(dynamic(Rnm/3))),
	'$lgt_generate_dynamic_entity_dynamic_predicate_directives'.


'$lgt_generate_dynamic_entity_dynamic_predicate_directives' :-
	'$lgt_pp_def_'(Clause),
		% only local table; reject linking clauses
		Clause \= (_ :- _),
		arg(3, Clause, Call),
		'$lgt_unwrap_compiled_head'(Call, Pred),
		functor(Pred, Functor, Arity),
		assertz('$lgt_pp_directive_'(dynamic(Functor/Arity))),
	fail.

'$lgt_generate_dynamic_entity_dynamic_predicate_directives' :-
	'$lgt_pp_ddef_'(Clause),
		% only local table; reject linking clauses
		Clause \= (_ :- _),
		arg(3, Clause, Call),
		'$lgt_unwrap_compiled_head'(Call, Pred),
		functor(Pred, Functor, Arity),
		assertz('$lgt_pp_directive_'(dynamic(Functor/Arity))),
	fail.

'$lgt_generate_dynamic_entity_dynamic_predicate_directives'.



'$lgt_generate_static_object_dynamic_directives' :-
	'$lgt_pp_object_'(_, Prefix, _, _, _, _, _, DDcl, DDef, _, _),
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		assertz('$lgt_pp_directive_'(dynamic(DDcl/2)))
	;	true
	),
	assertz('$lgt_pp_directive_'(dynamic(DDef/3))),
	'$lgt_pp_dynamic_'(Head, _, _, _),
		functor(Head, Functor, Arity),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		assertz('$lgt_pp_directive_'(dynamic(TFunctor/TArity))),
	fail.

'$lgt_generate_static_object_dynamic_directives'.



'$lgt_generate_object_discontiguous_directives' :-
	'$lgt_pp_object_'(_, Prefix, _, _, _, _, _, _, _, _, _),
	'$lgt_pp_discontiguous_'(Head, _, _),
		functor(Head, Functor, Arity),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		assertz('$lgt_pp_directive_'(discontiguous(TFunctor/TArity))),
	fail.

'$lgt_generate_object_discontiguous_directives'.



'$lgt_generate_category_dynamic_directives' :-
	(	'$lgt_pp_dynamic_' ->
		% add the necessary directives to allow abolishing the category
		'$lgt_pp_category_'(_, _, Dcl, Def, Rnm, _),
		assertz('$lgt_pp_directive_'(dynamic(Dcl/4))),
		assertz('$lgt_pp_directive_'(dynamic(Dcl/5))),
		assertz('$lgt_pp_directive_'(dynamic(Def/3))),
		assertz('$lgt_pp_directive_'(dynamic(Rnm/3))),
		'$lgt_generate_dynamic_entity_dynamic_predicate_directives'
	;	true
	).



'$lgt_generate_category_discontiguous_directives' :-
	'$lgt_pp_category_'(_, Prefix, _, _, _, _),
	'$lgt_pp_discontiguous_'(Head, _, _),
		functor(Head, Functor, Arity),
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
		assertz('$lgt_pp_directive_'(discontiguous(TFunctor/TArity))),
	fail.

'$lgt_generate_category_discontiguous_directives'.



'$lgt_generate_object_clauses' :-
	(	'$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		% object plays the role of a class
		'$lgt_generate_ic_clauses'
	;	'$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		% object plays the role of a class
		'$lgt_generate_ic_clauses'
	;	% objects without an instantiation or specialization relation
		% play the role of prototypes
		'$lgt_generate_prototype_clauses'
	).



% '$lgt_generate_dcl_table_clauses'(+atom, -atom)
%
% a predicate declaration table clause is only generated if there is a
% scope declaration for the predicate; the second argument returns the
% atom "true" if there are local clauses and the atom "false" otherwise
%
% the table clauses use a bit pattern representation for the predicate
% properties for compactness and access performance

'$lgt_generate_dcl_table_clauses'(Dcl, _) :-
	(	'$lgt_pp_public_'(Functor, Arity, _, _), Scope = p(p(p))
	;	'$lgt_pp_protected_'(Functor, Arity, _, _), Scope = p(p)
	;	'$lgt_pp_private_'(Functor, Arity, _, _), Scope = p
	),
	functor(Pred, Functor, Arity),
	(	'$lgt_pp_meta_predicate_'(Pred, Template, _, _) ->
		Meta = Template,
		MetaPredicate = 64				% 0b01000000
	;	Meta = no,
		MetaPredicate = 0
	),
	(	'$lgt_pp_coinductive_head_'(Pred, _, _) ->
		Coinductive = 32				% 0b00100000
	;	Coinductive = 0
	),
	(	'$lgt_pp_multifile_'(Pred, _, _, _) ->
		Multifile = 16					% 0b00010000
	;	Multifile = 0
	),
	(	'$lgt_pp_non_terminal_'(Functor, _, Arity) ->
		NonTerminal = 8					% 0b00001000
	;	NonTerminal = 0
	),
	(	'$lgt_pp_synchronized_'(Pred, _, _, _) ->
		Synchronized = 4				% 0b00000100
	;	Synchronized = 0
	),
	(	'$lgt_pp_dynamic_' ->
		Dynamic = 2						% 0b00000010
	;	'$lgt_pp_dynamic_'(Pred, _, _, _) ->
		Dynamic = 2						% 0b00000010
	;	Dynamic = 0
	),
	Flags is MetaPredicate + Coinductive + Multifile + NonTerminal + Synchronized + Dynamic,
	Fact =.. [Dcl, Pred, Scope, Meta, Flags],
	assertz('$lgt_pp_dcl_'(Fact)),
	fail.

'$lgt_generate_dcl_table_clauses'(_, Local) :-
	(	'$lgt_pp_dcl_'(_) ->
		Local = true
	;	Local = false
	).



% '$lgt_generate_def_table_clauses'(+compilation_context)
%
% generates predicate definition table clauses for undefined but
% declared (using a predicate directive) predicates

'$lgt_generate_def_table_clauses'(Ctx) :-
	\+ '$lgt_pp_dynamic_',
	% static entities only otherwise abolishing the dynamic entity would result
	% in an attempt to retract a clause for the fail/0 built-in control construct
	(	'$lgt_pp_complemented_object_'(_, _, _, _, _) ->
		'$lgt_compiler_flag'(complements, restrict)
	;	true
	),
	% complementing categories can add a scope directive for predicates that
	% are defined in the complemented objects; for objects compiled with the
	% complements flag set to allow, we must allow lookup of the predicate
	% definition in the object itself (and elsewhere in its ancestors)
	(	'$lgt_pp_public_'(Functor, Arity, _, _)
	;	'$lgt_pp_protected_'(Functor, Arity, _, _)
	;	'$lgt_pp_private_'(Functor, Arity, _, _)
	;	'$lgt_pp_synchronized_'(Head, _, _, _)
	;	'$lgt_pp_coinductive_head_'(Head, _, _)
	;	'$lgt_pp_discontiguous_'(Head, _, _)
	),
	functor(Head, Functor, Arity),
	\+ '$lgt_pp_multifile_'(Head, _, _, _),
	\+ '$lgt_pp_dynamic_'(Head, _, _, _),
	\+ '$lgt_pp_defines_predicate_'(Head, _, _, _, _, _),
	% declared, static, but undefined predicate;
	% local calls must fail (as per closed-world assumption)
	'$lgt_add_def_fail_clause'(Head, Ctx),
	fail.

'$lgt_generate_def_table_clauses'(Ctx) :-
	'$lgt_pp_entity_'(Type, _, Prefix),
	(	Type == object,
		% categories cannot contain clauses for dynamic predicates
		'$lgt_pp_dynamic_'(Head, _, _, _)
	;	'$lgt_pp_multifile_'(Head, _, _, _),
		\+ '$lgt_pp_dynamic_'(Head, _, _, _)
	),
	\+ '$lgt_pp_defines_predicate_'(Head, _, _, _, _, _),
	% dynamic and/or multifile predicate with no initial set of clauses
	'$lgt_comp_ctx_prefix'(Ctx, Prefix),
	functor(Head, Functor, Arity),
	(	\+ '$lgt_pp_public_'(Functor, Arity, _, _),
		\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
		\+ '$lgt_pp_private_'(Functor, Arity, _, _),
		\+ '$lgt_pp_synchronized_'(Head, _, _, _),
		\+ '$lgt_pp_coinductive_head_'(Head, _, _),
		\+ '$lgt_pp_multifile_'(Head, _, _, _) ->
		'$lgt_add_ddef_clause'(Head, Functor, Arity, _, Ctx)
	;	'$lgt_add_def_clause'(Head, Functor, Arity, _, Ctx)
	),
	fail.

'$lgt_generate_def_table_clauses'(_).



'$lgt_generate_protocol_clauses' :-
	'$lgt_pp_protocol_'(Ptc, _, Dcl, Rnm, _),
	% first, generate the local table of predicate declarations
	'$lgt_generate_dcl_table_clauses'(Dcl, Local),
	% second, generate linking clauses for accessing both local
	% declarations and declarations in related entities (some
	% linking clauses depend on the existence of local predicate
	% declarations)
	'$lgt_generate_protocol_local_clauses'(Local, Ptc, Dcl),
	'$lgt_generate_protocol_extends_clauses'(Dcl, Rnm),
	% third, add a catchall clause if necessary
	'$lgt_generate_protocol_catchall_clauses'(Dcl).



'$lgt_generate_protocol_local_clauses'(true, Ptc, PDcl) :-
	Head =.. [PDcl, Pred, Scope, Meta, Flags, Ptc],
	Body =.. [PDcl, Pred, Scope, Meta, Flags],
	assertz('$lgt_pp_dcl_'((Head:-Body))).

'$lgt_generate_protocol_local_clauses'(false, _, _).



'$lgt_generate_protocol_extends_clauses'(Dcl, Rnm) :-
	'$lgt_pp_extended_protocol_'(ExtPtc, _, _, ExtDcl, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [ExtDcl, Pred, Scope, Meta, Flags, Ctn]
	;	RelationScope == protected ->
		Lookup0 =.. [ExtDcl, Pred, Scope2, Meta, Flags, Ctn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [ExtDcl, Pred, _, Meta, Flags, Ctn]
	),
	(	'$lgt_pp_predicate_alias_'(ExtPtc, _, _, _, _, _) ->
		Head =.. [Dcl, Alias, Scope, Meta, Flags, Ctn],
		Rename =.. [Rnm, ExtPtc, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [Dcl, Pred, Scope, Meta, Flags, Ctn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_protocol_extends_clauses'(_, _).



% when a static protocol is empty, i.e. when it does not contain any predicate
% declarations, and does not extend other protocols, we need a catchall clause
% in order to prevent predicate existence errors when sending a message to an
% object implementing (directly or indirectly) the protocol

'$lgt_generate_protocol_catchall_clauses'(Dcl) :-
	(	'$lgt_pp_dcl_'(_) ->
		% local or inherited predicate declarations exist
		true
	;	% empty, standalone protocol
		'$lgt_pp_dynamic_' ->
		% dynamic protocol; calls to the dynamic predicate implementing the
		% predicate declaration table fail when there are no clauses
		true
	;	% static protocol; generate a catchall clause as the predicate
		% implementing the predicate declaration table is also static
		functor(Head, Dcl, 5),
		assertz('$lgt_pp_dcl_'((Head:-fail)))
	).



'$lgt_generate_category_clauses' :-
	'$lgt_pp_category_'(Ctg, _, Dcl, Def, Rnm, _),
	'$lgt_generate_category_dcl_clauses'(Ctg, Dcl, Rnm),
	'$lgt_generate_category_def_clauses'(Ctg, Def, Rnm).



'$lgt_generate_category_dcl_clauses'(Ctg, Dcl, Rnm) :-
	% first, generate the local table of predicate declarations
	'$lgt_generate_dcl_table_clauses'(Dcl, Local),
	% second, generate linking clauses for accessing both local
	% declarations and declarations in related entities (some
	% linking clauses depend on the existence of local predicate
	% declarations)
	'$lgt_generate_category_local_dcl_clauses'(Local, Ctg, Dcl),
	'$lgt_generate_category_implements_dcl_clauses'(Dcl, Rnm),
	'$lgt_generate_category_extends_dcl_clauses'(Dcl, Rnm),
	% third, add a catchall clause if necessary
	'$lgt_generate_category_catchall_dcl_clauses'(Dcl).



'$lgt_generate_category_local_dcl_clauses'(true, Ctg, CDcl) :-
	Head =.. [CDcl, Pred, Scope, Meta, Flags, Ctg],
	Body =.. [CDcl, Pred, Scope, Meta, Flags],
	assertz('$lgt_pp_dcl_'((Head:-Body))).

'$lgt_generate_category_local_dcl_clauses'(false, _, _).



'$lgt_generate_category_implements_dcl_clauses'(CDcl, Rnm) :-
	'$lgt_pp_implemented_protocol_'(Ptc, _, _, PDcl, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [PDcl, Pred, Scope, Meta, Flags, Ctn]
	;	RelationScope == protected ->
		Lookup0 =.. [PDcl, Pred, Scope2, Meta, Flags, Ctn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [PDcl, Pred, _, Meta, Flags, Ctn]
	),
	(	'$lgt_pp_predicate_alias_'(Ptc, _, _, _, _, _) ->
		Head =.. [CDcl, Alias, Scope, Meta, Flags, Ctn],
		Rename =.. [Rnm, Ptc, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [CDcl, Pred, Scope, Meta, Flags, Ctn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_category_implements_dcl_clauses'(_, _).



'$lgt_generate_category_extends_dcl_clauses'(CDcl, Rnm) :-
	'$lgt_pp_extended_category_'(Ctg, _, _, ECDcl, _, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [ECDcl, Pred, Scope, Meta, Flags, Ctn]
	;	RelationScope == protected ->
		Lookup0 =.. [ECDcl, Pred, Scope2, Meta, Flags, Ctn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [ECDcl, Pred, _, Meta, Flags, Ctn]
	),
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [CDcl, Alias, Scope, Meta, Flags, Ctn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [CDcl, Pred, Scope, Meta, Flags, Ctn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_category_extends_dcl_clauses'(_, _).



% when a static category contains no predicate declarations, does not implement any
% protocol, and does not extend other categories, we need a catchall clause in order
% to prevent predicate existence errors when sending a message to an object importing
% (directly or indirectly) the category

'$lgt_generate_category_catchall_dcl_clauses'(Dcl) :-
	(	'$lgt_pp_dcl_'(_) ->
		% local or inherited predicate declarations exist
		true
	;	% standalone category with no local or inherited predicate declarations
		'$lgt_pp_dynamic_' ->
		% dynamic category; calls to the dynamic predicate implementing the
		% predicate declaration table fail when there are no clauses
		true
	;	% static category; generate a catchall clause as the predicate
		% implementing the predicate declaration table is also static
		functor(Head, Dcl, 5),
		assertz('$lgt_pp_dcl_'((Head:-fail)))
	).



'$lgt_generate_category_def_clauses'(Ctg, Def, Rnm) :-
	'$lgt_generate_category_local_def_clauses'(Ctg, Def),
	'$lgt_generate_category_extends_def_clauses'(Def, Rnm).



'$lgt_generate_category_local_def_clauses'(Ctg, Def) :-
	'$lgt_execution_context_this_entity'(ExCtx, _, Ctg),
	Head =.. [Def, Pred, ExCtx, Call, Ctg],
	(	'$lgt_pp_def_'(_) ->
		Body =.. [Def, Pred, ExCtx, Call]
	;	Body = fail
	),
	assertz('$lgt_pp_def_'((Head:-Body))).



'$lgt_generate_category_extends_def_clauses'(Def, Rnm) :-
	'$lgt_pp_extended_category_'(ExtCtg, Ctg, _, _, ExtDef, _),
	'$lgt_execution_context_update_this_entity'(CExCtx, This, Ctg, EExCtx, This, ExtCtg),
	Lookup =.. [ExtDef, Pred, EExCtx, Call, Ctn],
	(	'$lgt_pp_predicate_alias_'(ExtCtg, _, _, _, _, _) ->
		Head =.. [Def, Alias, CExCtx, Call, Ctn],
		Rename =.. [Rnm, ExtCtg, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [Def, Pred, CExCtx, Call, Ctn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_category_extends_def_clauses'(_, _).



% the database built-in methods need to check if a local declaration or a local definition
% exists for a predicate; in order to avoid predicate existence errors, we need to generate
% a catchall clause for static objects when there are no local predicate declarations or no
% local predicate definitions

'$lgt_generate_object_catchall_local_dcl_clause'(true, _).

'$lgt_generate_object_catchall_local_dcl_clause'(false, Dcl) :-
	(	'$lgt_pp_dynamic_' ->
		% dynamic object; calls to the dynamic predicate implementing the
		% predicate declaration table fail when there are no clauses
		true
	;	% static object; generate a catchall clause as the predicate
		% implementing the predicate declaration table is also static
		functor(Head, Dcl, 4),
		assertz('$lgt_pp_dcl_'((Head:-fail)))
	).



'$lgt_generate_object_catchall_def_clauses'(true, _).

'$lgt_generate_object_catchall_def_clauses'(false, Def) :-
	(	'$lgt_pp_dynamic_' ->
		% dynamic object; calls to the dynamic predicate implementing the
		% predicate definition table fail when there are no clauses
		true
	;	% static object; generate a catchall clause as the predicate
		% implementing the predicate definition table is also static
		functor(Head, Def, 3),
		assertz('$lgt_pp_def_'((Head:-fail)))
	).



'$lgt_generate_prototype_clauses' :-
	'$lgt_pp_object_'(Obj, _, Dcl, Def, Super, _, _, DDcl, DDef, Rnm, _),
	'$lgt_compiler_flag'(complements, Complements),
	'$lgt_generate_prototype_dcl_clauses'(Obj, Dcl, DDcl, Rnm, Complements),
	'$lgt_generate_prototype_def_clauses'(Obj, Def, DDef, Rnm, Complements),
	'$lgt_generate_prototype_super_clauses'(Super, Rnm).



'$lgt_generate_prototype_dcl_clauses'(Obj, Dcl, DDcl, Rnm, Complements) :-
	% first, generate the local table of predicate declarations:
	'$lgt_generate_dcl_table_clauses'(Dcl, Local),
	% second, generate linking clauses for accessing both local
	% declarations and declarations in related entities (some
	% linking clauses depend on the existence of local predicate
	% declarations
	(	Complements == allow ->
		% complementing categories are allowed to override local predicate declarations
		'$lgt_generate_prototype_complements_dcl_clauses'(Obj, Dcl),
		'$lgt_generate_prototype_local_dcl_clauses'(Local, Complements, Obj, Dcl, DDcl)
	;	Complements == restrict ->
		% complementing categories can add to but not override local predicate declarations
		'$lgt_generate_prototype_local_dcl_clauses'(Local, Complements, Obj, Dcl, DDcl),
		'$lgt_generate_prototype_complements_dcl_clauses'(Obj, Dcl)
	;	% Complements == deny,
		'$lgt_generate_prototype_local_dcl_clauses'(Local, Complements, Obj, Dcl, DDcl)
	),
	'$lgt_generate_prototype_implements_dcl_clauses'(Dcl, Rnm),
	'$lgt_generate_prototype_imports_dcl_clauses'(Dcl, Rnm),
	'$lgt_generate_prototype_extends_dcl_clauses'(Dcl, Rnm),
	% third, add a catchall clause if necessary
	'$lgt_generate_object_catchall_local_dcl_clause'(Local, Dcl).



'$lgt_generate_prototype_complements_dcl_clauses'(Obj, Dcl) :-
	Head =.. [Dcl, Pred, Scope, Meta, Flags, SCtn, TCtn],
	Lookup = '$lgt_complemented_object'(Obj, Dcl, Pred, Scope, Meta, Flags, SCtn, TCtn),
	assertz('$lgt_pp_dcl_'((Head:-Lookup))).



'$lgt_generate_prototype_local_dcl_clauses'(true, _, Obj, Dcl, DDcl) :-
	% there are local (compile-time) predicate declarations
	HeadDcl =.. [Dcl, Pred, Scope, Meta, Flags, Obj, Obj],
	BodyDcl =.. [Dcl, Pred, Scope, Meta, Flags],
	% lookup access to local, static, predicate declarations
	assertz('$lgt_pp_dcl_'((HeadDcl:-BodyDcl))),
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		HeadDDcl =.. [Dcl, Pred, Scope, no, 2, Obj, Obj],
		BodyDDcl =.. [DDcl, Pred, Scope],
		% lookup access to local, dynamic, (runtime) predicate declarations
		assertz('$lgt_pp_dcl_'((HeadDDcl:-BodyDDcl)))
	;	true
	).

'$lgt_generate_prototype_local_dcl_clauses'(false, Complements, Obj, Dcl, DDcl) :-
	% no local (compile-time) predicate declarations
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		HeadDDcl =.. [Dcl, Pred, Scope, no, 2, Obj, Obj],
		BodyDDcl =.. [DDcl, Pred, Scope],
		% lookup access to local, dynamic, (runtime) predicate declarations
		assertz('$lgt_pp_dcl_'((HeadDDcl:-BodyDDcl)))
	;	Complements == deny,
		\+ '$lgt_pp_implemented_protocol_'(_, _, _, _, _),
		\+ '$lgt_pp_imported_category_'(_, _, _, _, _, _),
		\+ '$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
		% standalone prototype with no access to predicate declarations
		functor(HeadDDcl, Dcl, 6),
		% catchall clause to avoid lookup errors
		assertz('$lgt_pp_dcl_'((HeadDDcl:-fail)))
	;	true
	).



'$lgt_generate_prototype_implements_dcl_clauses'(ODcl, Rnm) :-
	'$lgt_pp_implemented_protocol_'(Ptc, Obj, _, PDcl, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [PDcl, Pred, Scope, Meta, Flags, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [PDcl, Pred, Scope2, Meta, Flags, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [PDcl, Pred, _, Meta, Flags, TCtn]
	),
	(	'$lgt_pp_predicate_alias_'(Ptc, _, _, _, _, _) ->
		Head =.. [ODcl, Alias, Scope, Meta, Flags, Obj, TCtn],
		Rename =.. [Rnm, Ptc, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [ODcl, Pred, Scope, Meta, Flags, Obj, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_prototype_implements_dcl_clauses'(_, _).



'$lgt_generate_prototype_imports_dcl_clauses'(ODcl, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, CDcl, _, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [CDcl, Pred, Scope, Meta, Flags, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [CDcl, Pred, Scope2, Meta, Flags, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [CDcl, Pred, _, Meta, Flags, TCtn]
	),
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [ODcl, Alias, Scope, Meta, Flags, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [ODcl, Pred, Scope, Meta, Flags, Obj, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_prototype_imports_dcl_clauses'(_, _).



'$lgt_generate_prototype_extends_dcl_clauses'(ODcl, Rnm) :-
	'$lgt_pp_extended_object_'(Parent, Obj, _, PDcl, _, _, _, _, _, _, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [PDcl, Pred, Scope, Meta, Flags, SCtn, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [PDcl, Pred, Scope2, Meta, Flags, SCtn, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup0 =.. [PDcl, Pred, Scope2, Meta, Flags, SCtn2, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope_container'(Scope2, SCtn2, Obj, SCtn))
	),
	(	'$lgt_pp_predicate_alias_'(Parent, _, _, _, _, _) ->
		Head =.. [ODcl, Alias, Scope, Meta, Flags, SCtn, TCtn],
		Rename =.. [Rnm, Parent, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [ODcl, Pred, Scope, Meta, Flags, SCtn, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_prototype_extends_dcl_clauses'(_, _).



'$lgt_generate_prototype_def_clauses'(Obj, Def, DDef, Rnm, Complements) :-
	% some linking clauses depend on the existence of local predicate definitions
	(	'$lgt_pp_def_'(_) ->
		Local = true
	;	Local = false
	),
	(	Complements == allow ->
		% complementing categories are allowed to override local predicate definitions
		'$lgt_generate_prototype_complements_def_clauses'(Obj, Def),
		'$lgt_generate_prototype_local_def_clauses'(Local, Obj, Def, DDef)
	;	Complements == restrict ->
		% complementing categories can add to but not override local predicate definitions
		'$lgt_generate_prototype_local_def_clauses'(Local, Obj, Def, DDef),
		'$lgt_generate_prototype_complements_def_clauses'(Obj, Def)
	;	% Complements == deny,
		'$lgt_generate_prototype_local_def_clauses'(Local, Obj, Def, DDef)
	),
	'$lgt_generate_prototype_imports_def_clauses'(Def, Rnm),
	'$lgt_generate_prototype_extends_def_clauses'(Def, Rnm),
	% add a catchall clause if necessary
	'$lgt_generate_object_catchall_def_clauses'(Local, Def).



'$lgt_generate_prototype_complements_def_clauses'(Obj, Def) :-
	Head =.. [Def, Pred, ExCtx, Call, Obj, TCtn],
	Lookup = '$lgt_complemented_object'(Obj, Def, Pred, ExCtx, Call, TCtn),
	assertz('$lgt_pp_def_'((Head:-Lookup))).



'$lgt_generate_prototype_local_def_clauses'(true, Obj, Def, DDef) :-
	% there are local (compile-time) predicate definitions
	'$lgt_execution_context_this_entity'(ExCtx, Obj, Obj),
	Head =.. [Def, Pred, ExCtx, Call, Obj, Obj],
	BodyDef =.. [Def, Pred, ExCtx, Call],
	% lookup access to local, static, predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDef))),
	BodyDDef =.. [DDef, Pred, ExCtx, Call],
	% lookup access to local, dynamic, (runtime) predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDDef))).

'$lgt_generate_prototype_local_def_clauses'(false, Obj, Def, DDef) :-
	% no local (compile-time) predicate definitions
	'$lgt_execution_context_this_entity'(ExCtx, Obj, Obj),
	Head =.. [Def, Pred, ExCtx, Call, Obj, Obj],
	BodyDDef =.. [DDef, Pred, ExCtx, Call],
	% lookup access to local, dynamic, (runtime) predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDDef))).



'$lgt_generate_prototype_imports_def_clauses'(ODef, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, _, CDef, _),
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, CExCtx, Obj, Ctg),
	Lookup =.. [CDef, Pred, CExCtx, Call, TCtn],
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [ODef, Alias, OExCtx, Call, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [ODef, Pred, OExCtx, Call, Obj, TCtn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_prototype_imports_def_clauses'(_, _).



'$lgt_generate_prototype_extends_def_clauses'(ODef, Rnm) :-
	'$lgt_pp_extended_object_'(Parent, Obj, _, _, PDef, _, _, _, _, _, _),
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, PExCtx, Parent, Parent),
	Lookup =.. [PDef, Pred, PExCtx, Call, SCtn, TCtn],
	(	'$lgt_pp_predicate_alias_'(Parent, _, _, _, _, _) ->
		Head =.. [ODef, Alias, OExCtx, Call, SCtn, TCtn],
		Rename =.. [Rnm, Parent, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [ODef, Pred, OExCtx, Call, SCtn, TCtn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_prototype_extends_def_clauses'(_, _).



% we can have a root object where super have nowhere to go ...

'$lgt_generate_prototype_super_clauses'(Super, _) :-
	\+ '$lgt_pp_imported_category_'(_, _, _, _, _, _),
	\+ '$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _),
	functor(Head, Super, 5),
	assertz('$lgt_pp_super_'((Head:-fail))),
	!.

% ... or we may import some categories

'$lgt_generate_prototype_super_clauses'(Super, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, _, CDef, _),
	% the entity in the object execution context is usually the object itself
	% but it can also be a complementing category; thus, the argument must be
	% left uninstantiated but it will be bound by the runtime
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, _, CExCtx, Obj, Ctg),
	Lookup =.. [CDef, Pred, CExCtx, Call, TCtn],
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [Super, Alias, OExCtx, Call, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_super_'((Head :- Rename, Lookup)))
	;	Head =.. [Super, Pred, OExCtx, Call, Obj, TCtn],
		assertz('$lgt_pp_super_'((Head:-Lookup)))
	),
	fail.

% ... or we may extend some objects

'$lgt_generate_prototype_super_clauses'(Super, Rnm) :-
	'$lgt_pp_extended_object_'(Parent, Obj, _, _, PDef, _, _, _, _, _, _),
	% the entity in the object execution context is usually the object itself
	% but it can also be a complementing category; thus, the argument must be
	% left uninstantiated but it will be bound by the runtime
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, _, PExCtx, Parent, Parent),
	Lookup =.. [PDef, Pred, PExCtx, Call, SCtn, TCtn],
	(	'$lgt_pp_predicate_alias_'(Parent, _, _, _, _, _) ->
		Head =.. [Super, Alias, OExCtx, Call, SCtn, TCtn],
		Rename =.. [Rnm, Parent, Pred, Alias],
		assertz('$lgt_pp_super_'((Head :- Rename, Lookup)))
	;	Head =.. [Super, Pred, OExCtx, Call, SCtn, TCtn],
		assertz('$lgt_pp_super_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_prototype_super_clauses'(_, _).



'$lgt_generate_ic_clauses' :-
	'$lgt_pp_object_'(Obj, _, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, _),
	'$lgt_compiler_flag'(complements, Complements),
	'$lgt_generate_ic_dcl_clauses'(Obj, Dcl, IDcl, DDcl, Rnm, Complements),
	'$lgt_generate_ic_def_clauses'(Obj, Def, IDef, DDef, Rnm, Complements),
	'$lgt_generate_ic_super_clauses'(Obj, Super, Rnm).



'$lgt_generate_ic_dcl_clauses'(Obj, Dcl, IDcl, DDcl, Rnm, Complements) :-
	% first, generate the local table of predicate declarations:
	'$lgt_generate_dcl_table_clauses'(Dcl, Local),
	% second, generate linking clauses for accessing declarations
	% in related entities (for an instance, the lookup for a predicate
	% declaration always start at its classes)
	'$lgt_generate_ic_instantiates_dcl_clauses'(Dcl, Rnm),
	% third, add a catchall clause if necessary
	'$lgt_generate_object_catchall_local_dcl_clause'(Local, Dcl),
	% finaly, generate linking clauses for accessing declarations
	% when we reach the class being compiled during a lookup
	% from a descendant instance
	'$lgt_generate_ic_idcl_clauses'(Local, Obj, Dcl, IDcl, DDcl, Rnm, Complements).



'$lgt_generate_ic_instantiates_dcl_clauses'(ODcl, _) :-
	\+ '$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _),
	% no meta-class for the class we're compiling
	!,
	functor(Head, ODcl, 6),
	assertz('$lgt_pp_dcl_'((Head:-fail))).

'$lgt_generate_ic_instantiates_dcl_clauses'(ODcl, Rnm) :-
	'$lgt_pp_instantiated_class_'(Class, Obj, _, _, _, _, CIDcl, _, _, _, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [CIDcl, Pred, Scope, Meta, Flags, SCtn, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [CIDcl, Pred, Scope2, Meta, Flags, SCtn, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup0 =.. [CIDcl, Pred, Scope2, Meta, Flags, SCtn2, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope_container'(Scope2, SCtn2, Obj, SCtn))
	),
	(	'$lgt_pp_predicate_alias_'(Class, _, _, _, _, _) ->
		Head =.. [ODcl, Alias, Scope, Meta, Flags, SCtn, TCtn],
		Rename =.. [Rnm, Class, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [ODcl, Pred, Scope, Meta, Flags, SCtn, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_instantiates_dcl_clauses'(_, _).



% generates the declaration linking clauses that are used
% when traversing specialization links in order to lookup
% a predicate declaration for a descendant instance

'$lgt_generate_ic_idcl_clauses'(Local, Obj, Dcl, IDcl, DDcl, Rnm, Complements) :-
	% generate linking clauses for accessing declarations in related entities
	(	Complements == allow ->
		% complementing categories are allowed to override local predicate declarations
		'$lgt_generate_ic_complements_idcl_clauses'(Obj, IDcl),
		'$lgt_generate_ic_local_idcl_clauses'(Local, Complements, Obj, Dcl, IDcl, DDcl)
	;	Complements == restrict ->
		% complementing categories can add to but not override local predicate declarations
		'$lgt_generate_ic_local_idcl_clauses'(Local, Complements, Obj, Dcl, IDcl, DDcl),
		'$lgt_generate_ic_complements_idcl_clauses'(Obj, IDcl)
	;	% Complements == deny,
		'$lgt_generate_ic_local_idcl_clauses'(Local, Complements, Obj, Dcl, IDcl, DDcl)
	),
	'$lgt_generate_ic_implements_idcl_clauses'(IDcl, Rnm),
	'$lgt_generate_ic_imports_idcl_clauses'(IDcl, Rnm),
	'$lgt_generate_ic_specializes_idcl_clauses'(IDcl, Rnm).



'$lgt_generate_ic_complements_idcl_clauses'(Obj, IDcl) :-
	Head =.. [IDcl, Pred, Scope, Meta, Flags, SCtn, TCtn],
	Lookup = '$lgt_complemented_object'(Obj, IDcl, Pred, Scope, Meta, Flags, SCtn, TCtn),
	assertz('$lgt_pp_dcl_'((Head:-Lookup))).



'$lgt_generate_ic_local_idcl_clauses'(true, _, Obj, Dcl, IDcl, DDcl) :-
	% there are local (compile-time) predicate declarations
	HeadDcl =.. [IDcl, Pred, Scope, Meta, Flags, Obj, Obj],
	BodyDcl =.. [Dcl, Pred, Scope, Meta, Flags],
	% lookup access to local, static, predicate declarations
	assertz('$lgt_pp_dcl_'((HeadDcl:-BodyDcl))),
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		HeadDDcl =.. [IDcl, Pred, Scope, no, 2, Obj, Obj],
		BodyDDcl =.. [DDcl, Pred, Scope],
		% lookup access to local, dynamic, (runtime) predicate declarations
		assertz('$lgt_pp_dcl_'((HeadDDcl:-BodyDDcl)))
	;	true
	).

'$lgt_generate_ic_local_idcl_clauses'(false, Complements, Obj, _, IDcl, DDcl) :-
	% no local (compile-time) predicate declarations
	(	'$lgt_compiler_flag'(dynamic_declarations, allow) ->
		HeadDDcl =.. [IDcl, Pred, Scope, no, 2, Obj, Obj],
		BodyDDcl =.. [DDcl, Pred, Scope],
		% lookup access to local, dynamic, (runtime) predicate declarations
		assertz('$lgt_pp_dcl_'((HeadDDcl:-BodyDDcl)))
	;	Complements == deny,
		\+ '$lgt_pp_implemented_protocol_'(_, _, _, _, _),
		\+ '$lgt_pp_imported_category_'(_, _, _, _, _, _),
		\+ '$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		% standalone class with no access to predicate declarations
		functor(HeadDDcl, IDcl, 6),
		% catchall clause to avoid lookup errors
		assertz('$lgt_pp_dcl_'((HeadDDcl:-fail)))
	;	true
	).



'$lgt_generate_ic_implements_idcl_clauses'(OIDcl, Rnm) :-
	'$lgt_pp_implemented_protocol_'(Ptc, Obj, _, PDcl, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [PDcl, Pred, Scope, Meta, Flags, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [PDcl, Pred, Scope2, Meta, Flags, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [PDcl, Pred, _, Meta, Flags, TCtn]
	),
	(	'$lgt_pp_predicate_alias_'(Ptc, _, _, _, _, _) ->
		Head =.. [OIDcl, Alias, Scope, Meta, Flags, Obj, TCtn],
		Rename =.. [Rnm, Ptc, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [OIDcl, Pred, Scope, Meta, Flags, Obj, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_implements_idcl_clauses'(_, _).



'$lgt_generate_ic_imports_idcl_clauses'(OIDcl, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, CDcl, _, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [CDcl, Pred, Scope, Meta, Flags, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [CDcl, Pred, Scope2, Meta, Flags, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup =.. [CDcl, Pred, _, Meta, Flags, TCtn]
	),
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [OIDcl, Alias, Scope, Meta, Flags, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [OIDcl, Pred, Scope, Meta, Flags, Obj, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_imports_idcl_clauses'(_, _).



'$lgt_generate_ic_specializes_idcl_clauses'(CIDcl, Rnm) :-
	'$lgt_pp_specialized_class_'(Super, Obj, _, _, _, _, SIDcl, _, _, _, RelationScope),
	(	RelationScope == (public) ->
		Lookup =.. [SIDcl, Pred, Scope, Meta, Flags, SCtn, TCtn]
	;	RelationScope == protected ->
		Lookup0 =.. [SIDcl, Pred, Scope2, Meta, Flags, SCtn, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope'(Scope2, Scope))
	;	Scope = p,
		Lookup0 =.. [SIDcl, Pred, Scope2, Meta, Flags, SCtn2, TCtn],
		Lookup = (Lookup0, '$lgt_filter_scope_container'(Scope2, SCtn2, Obj, SCtn))
	),
	(	'$lgt_pp_predicate_alias_'(Super, _, _, _, _, _) ->
		Head =.. [CIDcl, Alias, Scope, Meta, Flags, SCtn, TCtn],
		Rename =.. [Rnm, Super, Pred, Alias],
		assertz('$lgt_pp_dcl_'((Head :- Rename, Lookup)))
	;	Head =.. [CIDcl, Pred, Scope, Meta, Flags, SCtn, TCtn],
		assertz('$lgt_pp_dcl_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_specializes_idcl_clauses'(_, _).



% lookup of predicate definitions start at the instance itself
% (not at its classes as it's the case for predicate declarations)

'$lgt_generate_ic_def_clauses'(Obj, Def, IDef, DDef, Rnm, Complements) :-
	% some linking clauses depend on the existence of local predicate definitions
	(	'$lgt_pp_def_'(_) ->
		Local = true
	;	Local = false
	),
	(	Complements == allow ->
		% complementing categories are allowed to override local predicate definitions
		'$lgt_generate_ic_complements_def_clauses'(Obj, Def),
		'$lgt_generate_ic_local_def_clauses'(Local, Obj, Def, DDef)
	;	Complements == restrict ->
		% complementing categories can add to but not override local predicate definitions
		'$lgt_generate_ic_local_def_clauses'(Local, Obj, Def, DDef),
		'$lgt_generate_ic_complements_def_clauses'(Obj, Def)
	;	% Complements == deny,
		'$lgt_generate_ic_local_def_clauses'(Local, Obj, Def, DDef)
	),
	'$lgt_generate_ic_imports_def_clauses'(Def, Rnm),
	'$lgt_generate_ic_instantiates_def_clauses'(Def, Rnm),
	% add a catchall clause if necessary
	'$lgt_generate_object_catchall_def_clauses'(Local, Def),
	% generate linking clauses for accessing definitions when
	% we reach the class being compiled during a lookup from
	% a descendant instance
	'$lgt_generate_ic_idef_clauses'(Local, Obj, Def, IDef, DDef, Rnm, Complements).



'$lgt_generate_ic_complements_def_clauses'(Obj, Def) :-
	Head =.. [Def, Pred, ExCtx, Call, Obj, TCtn],
	Lookup = '$lgt_complemented_object'(Obj, Def, Pred, ExCtx, Call, TCtn),
	assertz('$lgt_pp_def_'((Head:-Lookup))).



'$lgt_generate_ic_local_def_clauses'(true, Obj, Def, DDef) :-
	% there are local (compile-time) predicate definitions
	'$lgt_execution_context_this_entity'(ExCtx, Obj, Obj),
	Head =.. [Def, Pred, ExCtx, Call, Obj, Obj],
	BodyDef =.. [Def, Pred, ExCtx, Call],
	% lookup access to local, static, predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDef))),
	BodyDDef =.. [DDef, Pred, ExCtx, Call],
	% lookup access to local, dynamic, (runtime) predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDDef))).

'$lgt_generate_ic_local_def_clauses'(false, Obj, Def, DDef) :-
	% no local (compile-time) predicate definitions
	'$lgt_execution_context_this_entity'(ExCtx, Obj, Obj),
	Head =.. [Def, Pred, ExCtx, Call, Obj, Obj],
	BodyDDef =.. [DDef, Pred, ExCtx, Call],
	% lookup access to local, dynamic, (runtime) predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDDef))).



'$lgt_generate_ic_imports_def_clauses'(ODef, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, _, CDef, _),
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, CExCtx, Obj, Ctg),
	Lookup =.. [CDef, Pred, CExCtx, Call, TCtn],
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [ODef, Alias, OExCtx, Call, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [ODef, Pred, OExCtx, Call, Obj, TCtn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_imports_def_clauses'(_, _).



'$lgt_generate_ic_instantiates_def_clauses'(ODef, Rnm) :-
	'$lgt_pp_instantiated_class_'(Class, Obj, _, _, _, _, _, CIDef, _, _, _),
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, CExCtx, Class, Class),
	Lookup =.. [CIDef, Pred, CExCtx, Call, SCtn, TCtn],
	(	'$lgt_pp_predicate_alias_'(Class, _, _, _, _, _) ->
		Head =.. [ODef, Alias, OExCtx, Call, SCtn, TCtn],
		Rename =.. [Rnm, Class, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [ODef, Pred, OExCtx, Call, SCtn, TCtn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_instantiates_def_clauses'(_, _).



% generates the definition linking clauses that are used
% when traversing specialization links in order to lookup
% a predicate definition for a descendant instance

'$lgt_generate_ic_idef_clauses'(Local, Obj, Def, IDef, DDef, Rnm, Complements) :-
	(	Complements == allow ->
		% complementing categories are allowed to override local predicate definitions
		'$lgt_generate_ic_complements_idef_clauses'(Obj, IDef),
		'$lgt_generate_ic_local_idef_clauses'(Local, Obj, Def, IDef, DDef)
	;	Complements == restrict ->
		% complementing categories can add to but not override local predicate definitions
		'$lgt_generate_ic_local_idef_clauses'(Local, Obj, Def, IDef, DDef),
		'$lgt_generate_ic_complements_idef_clauses'(Obj, IDef)
	;	% Complements == deny,
		'$lgt_generate_ic_local_idef_clauses'(Local, Obj, Def, IDef, DDef)
	),
	'$lgt_generate_ic_complements_idef_clauses'(Obj, IDef),
	'$lgt_generate_ic_local_idef_clauses'(Local, Obj, Def, IDef, DDef),
	'$lgt_generate_ic_imports_idef_clauses'(IDef, Rnm),
	'$lgt_generate_ic_specializes_idef_clauses'(IDef, Rnm).



'$lgt_generate_ic_complements_idef_clauses'(Obj, IDef) :-
	Head =.. [IDef, Pred, ExCtx, Call, Obj, TCtn],
	Lookup = '$lgt_complemented_object'(Obj, IDef, Pred, ExCtx, Call, TCtn),
	assertz('$lgt_pp_def_'((Head:-Lookup))).



'$lgt_generate_ic_local_idef_clauses'(true, Obj, Def, IDef, DDef) :-
	% there are local (compile-time) predicate definitions
	'$lgt_execution_context_this_entity'(ExCtx, Obj, Obj),
	Head =.. [IDef, Pred, ExCtx, Call, Obj, Obj],
	BodyDef =.. [Def, Pred, ExCtx, Call],
	% lookup access to local, static, predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDef))),
	BodyDDef =.. [DDef, Pred, ExCtx, Call],
	% lookup access to local, dynamic, (runtime) predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDDef))).

'$lgt_generate_ic_local_idef_clauses'(false, Obj, _, IDef, DDef) :-
	% no local (compile-time) predicate definitions
	'$lgt_execution_context_this_entity'(ExCtx, Obj, Obj),
	Head =.. [IDef, Pred, ExCtx, Call, Obj, Obj],
	BodyDDef =.. [DDef, Pred, ExCtx, Call],
	% lookup access to local, dynamic, (runtime) predicate definitions
	assertz('$lgt_pp_def_'((Head:-BodyDDef))).



'$lgt_generate_ic_imports_idef_clauses'(OIDef, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, _, CDef, _),
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, CExCtx, Obj, Ctg),
	Lookup =.. [CDef, Pred, CExCtx, Call, TCtn],
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [OIDef, Alias, OExCtx, Call, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [OIDef, Pred, OExCtx, Call, Obj, TCtn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_imports_idef_clauses'(_, _).



'$lgt_generate_ic_specializes_idef_clauses'(CIDef, Rnm) :-
	'$lgt_pp_specialized_class_'(Super, Class, _, _, _, _, _, SIDef, _, _, _),
	'$lgt_execution_context_update_this_entity'(CExCtx, Class, Class, SExCtx, Super, Super),
	Lookup =.. [SIDef, Pred, SExCtx, Call, SCtn, TCtn],
	(	'$lgt_pp_predicate_alias_'(Super, _, _, _, _, _) ->
		Head =.. [CIDef, Alias, CExCtx, Call, SCtn, TCtn],
		Rename =.. [Rnm, Super, Pred, Alias],
		assertz('$lgt_pp_def_'((Head :- Rename, Lookup)))
	;	Head =.. [CIDef, Pred, CExCtx, Call, SCtn, TCtn],
		assertz('$lgt_pp_def_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_specializes_idef_clauses'(_, _).



% we can have a root object where "super" have nowhere to go ...

'$lgt_generate_ic_super_clauses'(Obj, Super, _) :-
	\+ '$lgt_pp_imported_category_'(_, _, _, _, _, _),
	\+ '$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _),
	\+ ('$lgt_pp_instantiated_class_'(Class, _, _, _, _, _, _, _, _, _, _), Class \= Obj),
	functor(Head, Super, 5),
	assertz('$lgt_pp_super_'((Head:-fail))),
	!.

% ... or we may import some categories

'$lgt_generate_ic_super_clauses'(Obj, Super, Rnm) :-
	'$lgt_pp_imported_category_'(Ctg, Obj, _, _, CDef, _),
	% the entity in the object execution context is usually the object itself
	% but it can also be a complementing category; thus, the argument must be
	% left uninstantiated but it will be bound by the runtime
	'$lgt_execution_context_update_this_entity'(OExCtx, _, Obj, CExCtx, Obj, Ctg),
	Lookup =.. [CDef, Pred, CExCtx, Call, TCtn],
	(	'$lgt_pp_predicate_alias_'(Ctg, _, _, _, _, _) ->
		Head =.. [Super, Alias, OExCtx, Call, Obj, TCtn],
		Rename =.. [Rnm, Ctg, Pred, Alias],
		assertz('$lgt_pp_super_'((Head :- Rename, Lookup)))
	;	Head =.. [Super, Pred, OExCtx, Call, Obj, TCtn],
		assertz('$lgt_pp_super_'((Head:-Lookup)))
	),
	fail.

% ... or predicates can be redefined in instances...

'$lgt_generate_ic_super_clauses'(Obj, Super, Rnm) :-
	'$lgt_pp_instantiated_class_'(Class, Obj, _, _, _, _, _, CIDef, _, _, _),
	% we can ignore class self-instantiation, which is often used in reflective designs
	Class \= Obj,
	% the entity in the object execution context is usually the object itself
	% but it can also be a complementing category; thus, the argument must be
	% left uninstantiated but it will be bound by the runtime
	'$lgt_execution_context_update_this_entity'(OExCtx, _, Obj, CExCtx, Class, Class),
	Lookup =.. [CIDef, Pred, CExCtx, Call, SCtn, TCtn],
	% the following restriction allows us to distinguish the two "super" clauses that
	% are generated when an object both instantiates and specializes other objects
	'$lgt_execution_context'(OExCtx, _, _, Obj, Obj, _, _),
	(	'$lgt_pp_predicate_alias_'(Class, _, _, _, _, _) ->
		Head =.. [Super, Alias, OExCtx, Call, SCtn, TCtn],
		Rename =.. [Rnm, Class, Pred, Alias],
		assertz('$lgt_pp_super_'((Head :- Rename, Lookup)))
	;	Head =.. [Super, Pred, OExCtx, Call, SCtn, TCtn],
		assertz('$lgt_pp_super_'((Head:-Lookup)))
	),
	fail.

% ... or/and in subclasses...

'$lgt_generate_ic_super_clauses'(Class, Super, Rnm) :-
	'$lgt_pp_specialized_class_'(Superclass, Class, _, _, _, _, _, SIDef, _, _, _),
	% the entity in the object execution context is usually the class itself
	% but it can also be a complementing category; thus, the argument must be
	% left uninstantiated but it will be bound by the runtime
	'$lgt_execution_context_update_this_entity'(CExCtx, _, Class, SExCtx, Superclass, Superclass),
	Lookup =.. [SIDef, Pred, SExCtx, Call, SCtn, TCtn],
	(	'$lgt_pp_predicate_alias_'(Superclass, _, _, _, _, _) ->
		Head =.. [Super, Alias, CExCtx, Call, SCtn, TCtn],
		Rename =.. [Rnm, Superclass, Pred, Alias],
		assertz('$lgt_pp_super_'((Head :- Rename, Lookup)))
	;	Head =.. [Super, Pred, CExCtx, Call, SCtn, TCtn],
		assertz('$lgt_pp_super_'((Head:-Lookup)))
	),
	fail.

'$lgt_generate_ic_super_clauses'(_, _, _).



% '$lgt_compile_predicate_calls'(+atom)
%
% compiles predicate calls in entity clause rules and in initialization goals
%
% all predicate calls are compiled on this compiler second stage to take advantage
% of the information about declared and defined predicates collected on the first
% stage, thus making predicate declaration and definition order irrelevant; this
% allows us to deal with e.g. meta-predicate directives and redefined built-in
% predicates which may be textually defined in an entity after their calls

'$lgt_compile_predicate_calls'(runtime) :-
	% avoid querying the optimize flag for each compiled term
	'$lgt_compiler_flag'(optimize, Optimize),
	'$lgt_compile_predicate_calls_loop'(silent, Optimize).

'$lgt_compile_predicate_calls'(compile_time) :-
	% avoid querying the duplicated_clauses and optimize flags for each compiled term
	'$lgt_compiler_flag'(duplicated_clauses, DuplicatedClauses),
	'$lgt_compiler_flag'(optimize, Optimize),
	'$lgt_compile_predicate_calls_loop'(DuplicatedClauses, Optimize).


% compilation of auxiliary clauses can result in the
% creation of e.g. new auxiliary clauses or directives

'$lgt_compile_predicate_calls_loop'(DuplicatedClauses, Optimize) :-
	'$lgt_compile_predicate_calls'(DuplicatedClauses, Optimize),
	fail.

'$lgt_compile_predicate_calls_loop'(DuplicatedClauses, Optimize) :-
	(	(	'$lgt_pp_entity_aux_clause_'(_)
		;	'$lgt_pp_coinductive_'(_, _, _, _, _, _, _, _, _)
		;	'$lgt_pp_object_initialization_'(_, _, _)
		;	'$lgt_pp_entity_meta_directive_'(_, _, _)
		) ->
		'$lgt_compile_predicate_calls_loop'(DuplicatedClauses, Optimize)
	;	true
	).


'$lgt_compile_predicate_calls'(warning, Optimize) :-
	% user-defined terms; as SourceData may be nil, we cannot perform the
	% unification in the retract/1 goal otherwise we could skip terms
	retract('$lgt_pp_entity_term_'(Term, SourceData, _)),
	(	SourceData = sd(Original, _, _, OriginalFile, OriginalLines),
		% exclude directives
		Original \= (:- _),
		'$lgt_pp_entity_'(Type, Entity, _),
		'$lgt_internal_term_template'(Term, Template),
		'$lgt_pp_entity_term_'(Template, sd(Duplicate, _, _, File, Lines), _),
		'$lgt_variant'(Original, Duplicate) ->
		'$lgt_increment_compiling_warnings_counter',
		(	Original = (_ --> _) ->
			'$lgt_print_message'(
				warning(duplicated_clauses),
				duplicated_grammar_rule(File, Lines, Type, Entity, Original, OriginalFile, OriginalLines)
			)
		;	'$lgt_print_message'(
				warning(duplicated_clauses),
				duplicated_clause(File, Lines, Type, Entity, Original, OriginalFile, OriginalLines)
			)
		)
	;	true
	),
	'$lgt_compile_predicate_calls'(Term, SourceData, Optimize, TTerm),
	assertz('$lgt_pp_final_entity_term_'(TTerm, Lines)),
	fail.

'$lgt_compile_predicate_calls'(silent, Optimize) :-
	% user-defined terms
	retract('$lgt_pp_entity_term_'(Term, SourceData, Lines)),
	'$lgt_compile_predicate_calls'(Term, SourceData, Optimize, TTerm),
	assertz('$lgt_pp_final_entity_term_'(TTerm, Lines)),
	fail.

'$lgt_compile_predicate_calls'(_, _) :-
	retract('$lgt_pp_coinductive_'(Head, TestHead, HeadExCtx, TCHead, BodyExCtx, THead, DHead, _, _)),
	'$lgt_pp_defines_predicate_'(Head, _, _, _, _, _),
		'$lgt_add_coinductive_predicate_aux_clause'(Head, TestHead, HeadExCtx, TCHead, BodyExCtx, THead, DHead),
	fail.

'$lgt_compile_predicate_calls'(_, Optimize) :-
	% other auxiliary clauses
	retract('$lgt_pp_entity_aux_clause_'(Clause)),
	'$lgt_compile_predicate_calls'(Clause, nil, Optimize, TClause),
	assertz('$lgt_pp_final_entity_aux_clause_'(TClause)),
	fail.

'$lgt_compile_predicate_calls'(_, Optimize) :-
	% initialization/1 goals
	retract('$lgt_pp_object_initialization_'(Goal, SourceData, Lines)),
	'$lgt_compile_predicate_calls'(Goal, SourceData, Optimize, TGoal),
	assertz('$lgt_pp_final_object_initialization_'(TGoal, Lines)),
	fail.

'$lgt_compile_predicate_calls'(_, Optimize) :-
	% other initialization goals found on proprietary Prolog directives
	retract('$lgt_pp_entity_meta_directive_'(Directive, SourceData, _)),
	'$lgt_compile_predicate_calls'(Directive, SourceData, Optimize, TDirective),
	assertz('$lgt_pp_directive_'(TDirective)),
	fail.

'$lgt_compile_predicate_calls'(_, _).


% auxiliary predicate used when checking for duplicated clauses

'$lgt_internal_term_template'(srule(_,_,_), srule(_,_,_)).

'$lgt_internal_term_template'(dsrule(_,_,_,_), dsrule(_,_,_,_)).

'$lgt_internal_term_template'(drule(_,_,_,_), drule(_,_,_,_)).

'$lgt_internal_term_template'(ddrule(_,_,_,_,_), ddrule(_,_,_,_,_)).

'$lgt_internal_term_template'(fact(_,_), fact(_,_)).

'$lgt_internal_term_template'(dfact(_,_,_), dfact(_,_,_)).


% '$lgt_compile_predicate_calls'(+callable, +compound, +atom, -callable)

% entity term is final
'$lgt_compile_predicate_calls'({Term}, _, _, Term).

'$lgt_compile_predicate_calls'(fact(TFact,_), _, _, TFact).

% debug version of a predicate fact
'$lgt_compile_predicate_calls'(dfact(TFact,DHead,_), _, _, (TFact:-DHead)).

% static predicate rule
'$lgt_compile_predicate_calls'(srule(THead,Body,Ctx), SourceData, Optimize, TClause) :-
	'$lgt_add_source_data'(SourceData),
	catch(
		'$lgt_compile_body'(Body, rule, FBody, _, Ctx),
		Error,
		('$lgt_comp_ctx_head'(Ctx,Head), throw(error(Error,clause((Head:-Body)))))
	),
	(	Optimize == on ->
		'$lgt_simplify_goal'(FBody, SBody)
	;	SBody = FBody
	),
	(	SBody == true ->
		TClause = THead
	;	TClause = (THead:-SBody)
	).

% debug version of static predicate rule
'$lgt_compile_predicate_calls'(dsrule(THead,DHead,Body,Ctx), SourceData, _, (THead:-DHead,DBody)) :-
	'$lgt_add_source_data'(SourceData),
	catch(
		'$lgt_compile_body'(Body, rule, _, DBody, Ctx),
		Error,
		('$lgt_comp_ctx_head'(Ctx,Head), throw(error(Error,clause((Head:-Body)))))
	).

% dynamic predicate rule
'$lgt_compile_predicate_calls'(drule(THead,Nop,Body,Ctx), SourceData, Optimize, TClause) :-
	'$lgt_add_source_data'(SourceData),
	catch(
		'$lgt_compile_body'(Body, rule, TBody0, _, Ctx),
		Error,
		('$lgt_comp_ctx_head'(Ctx,Head), throw(error(Error,clause((Head:-Body)))))
	),
	(	Optimize == on ->
		'$lgt_simplify_goal'(TBody0, TBody)
	;	TBody = TBody0
	),
	(	TBody == true ->
		TClause = (THead:-Nop)
	;	TClause = (THead:-Nop,TBody)
	).

% debug version of dynamic predicate rule
'$lgt_compile_predicate_calls'(ddrule(THead,Nop,DHead,Body,Ctx), SourceData, _, (THead:-Nop,DHead,DBody)) :-
	'$lgt_add_source_data'(SourceData),
	catch(
		'$lgt_compile_body'(Body, rule, _, DBody, Ctx),
		Error,
		('$lgt_comp_ctx_head'(Ctx,Head), throw(error(Error,clause((Head:-Body)))))
	).

% initialization goal
'$lgt_compile_predicate_calls'(goal(Body,Ctx), SourceData, Optimize, TBody) :-
	'$lgt_add_source_data'(SourceData),
	catch(
		'$lgt_compile_body'(Body, directive, TBody0, _, Ctx),
		Error,
		throw(error(Error, directive(initialization(Body))))
	),
	(	Optimize == on ->
		'$lgt_simplify_goal'(TBody0, TBody)
	;	TBody = TBody0
	).

% debug version of initialization goal
'$lgt_compile_predicate_calls'(dgoal(Body,Ctx), SourceData, _, DBody) :-
	'$lgt_add_source_data'(SourceData),
	catch(
		'$lgt_compile_body'(Body, directive, _, DBody, Ctx),
		Error,
		throw(error(Error, directive(initialization(Body))))
	).

% supported Prolog meta-directives (specified in the adapter files)
'$lgt_compile_predicate_calls'(directive(Directive,Meta), SourceData, _, TDirective) :-
	'$lgt_add_source_data'(SourceData),
	Directive =.. [Functor| Args],
	Meta =.. [Functor| MArgs],
	'$lgt_pp_entity_'(_, Entity, Prefix),
	% MetaVars = [] as we're compiling a local call
	'$lgt_comp_ctx'(Ctx, _, _, Entity, Entity, Entity, Entity, Prefix, [], _, _, _, [], _, _),
	(	catch(
			'$lgt_compile_prolog_meta_arguments'(Args, MArgs, meta, Ctx, TArgs, DArgs),
			Error,
			throw(error(Error, directive(Directive)))
		) ->
		(	'$lgt_compiler_flag'(debug, on) ->
			TDirective =.. [Functor| DArgs]
		;	TDirective =.. [Functor| TArgs]
		)
	;	% the meta-directive template is not usable, report it as an error
		throw(error(domain_error(meta_directive_template, Meta), directive(Directive)))
	).


'$lgt_add_source_data'(nil) :-
	retractall('$lgt_pp_term_source_data_'(_, _, _, _, _)).

'$lgt_add_source_data'(sd(Term, VariableNames, Singletons, File, Lines)) :-
	retractall('$lgt_pp_term_source_data_'(_, _, _, _, _)),
	assertz('$lgt_pp_term_source_data_'(Term, VariableNames, Singletons, File, Lines)).


'$lgt_add_coinductive_predicate_aux_clause'(Head, TestHead, HeadExCtx, TCHead, BodyExCtx, THead, DHead) :-
	'$lgt_execution_context'(HeadExCtx, Entity, Sender, This, Self, MetaCallCtx, HeadStack),
	'$lgt_execution_context'(BodyExCtx, Entity, Sender, This, Self, MetaCallCtx, BodyStack),
	'$lgt_coinductive_success_hook'(Head, Hypothesis, HeadExCtx, HeadStack, BodyStack, Hook),
	(	'$lgt_compiler_flag'(debug, on) ->
		Header = '$lgt_debug'(rule(Entity, DHead, 0, nil, 0), BodyExCtx),
		If = '$lgt_debug'(goal(check_coinductive_success(TestHead, HeadStack), '$lgt_check_coinductive_success'(TestHead, HeadStack, Hypothesis)), BodyExCtx),
		Then = '$lgt_debug'(goal(coinductive_success_hook(Head, Hypothesis), Hook), BodyExCtx),
		Else = (
			'$lgt_debug'(goal(push_coinductive_hypothesis(TestHead, HeadStack, BodyStack), BodyStack = [Head| HeadStack]), BodyExCtx),
			'$lgt_debug'(goal(Head, THead), BodyExCtx)
		)
	;	Header = true,
		If = '$lgt_check_coinductive_success'(TestHead, HeadStack, Hypothesis),
		Then = Hook,
		Else = (BodyStack = [Head| HeadStack], THead)
	),
	(	'$lgt_prolog_built_in_predicate'('*->'(_, _)) ->
		% backend Prolog compiler supports the soft-cut control construct
		assertz('$lgt_pp_entity_aux_clause_'({(TCHead :- Header, ('*->'(If, Then); Else))}))
	;	'$lgt_prolog_built_in_predicate'(if(_, _, _)) ->
		% backend Prolog compiler supports the if/3 soft-cut built-in meta-predicate
		assertz('$lgt_pp_entity_aux_clause_'({(TCHead :- Header, if(If, Then, Else))}))
	;	% the adapter file for the backend Prolog compiler declares that coinduction
		% is supported but it seems to be missing the necessary declaration for the
		% soft-cut control construct or meta-predicate
		throw(resource_error(soft_cut_support))
	).


'$lgt_coinductive_success_hook'(Head, Hypothesis, ExCtx, HeadStack, BodyStack, Hook) :-
	% ensure zero performance penalties when defining coinductive predicates without a definition
	% for the coinductive success hook predicates
	(	'$lgt_pp_defines_predicate_'(coinductive_success_hook(Head,Hypothesis), _, ExCtx, THead, _, _),
		\+ \+ (
			'$lgt_pp_final_entity_term_'(THead, _)
		;	'$lgt_pp_final_entity_term_'((THead :- _), _)
		) ->
		% ... with at least one clause for this particular coinductive predicate head
		Hook = ((HeadStack = BodyStack), THead)
	;	% we only consider coinductive_success_hook/1 clauses if no coinductive_success_hook/2 clause applies
		'$lgt_pp_defines_predicate_'(coinductive_success_hook(Head), _, ExCtx, THead, _, _),
		\+ \+ (
			'$lgt_pp_final_entity_term_'(THead, _)
		;	'$lgt_pp_final_entity_term_'((THead :- _), _)
		) ->
		% ... with at least one clause for this particular coinductive predicate head
		Hook = ((HeadStack = BodyStack), THead)
	;	% no hook predicates defined or defined but with no clauses for this particular coinductive predicate head
		Hook = (HeadStack = BodyStack)
	).



% reports missing predicate directives

'$lgt_report_missing_directives'(_, _) :-
	'$lgt_compiler_flag'(missing_directives, silent),
	!.

% reports missing scope directives for dynamic predicates

'$lgt_report_missing_directives'(category, Entity) :-
	'$lgt_pp_dynamic_'(Head, Original, File, Lines),
	% declared dynamic predicate or non-terminal in a category are for objects
	functor(Head, Functor, Arity),
	\+ '$lgt_pp_public_'(Functor, Arity, _, _),
	\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
	\+ '$lgt_pp_private_'(Functor, Arity, _, _),
	% but missing corresponding scope directive
	\+ '$lgt_pp_implemented_protocol_'(_, _, _, _, _),
	\+ '$lgt_pp_extended_category_'(_, _, _, _, _, _),
	% nowhere from inheriting a predicate scope declaration
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_scope_directive(File, Lines, category, Entity, (dynamic)/1, Original)
	),
	fail.

% reports missing scope directives for multifile predicates

'$lgt_report_missing_directives'(Type, Entity) :-
	'$lgt_pp_multifile_'(Head, Original, File, Lines),
	% declared multifile predicate
	functor(Head, Functor, Arity),
	\+ '$lgt_pp_public_'(Functor, Arity, _, _),
	\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
	\+ '$lgt_pp_private_'(Functor, Arity, _, _),
	% but missing corresponding scope directive
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_scope_directive(File, Lines, Type, Entity, (multifile)/1, Original)
	),
	fail.

% reports missing meta_predicate/1 directives for meta-predicates

'$lgt_report_missing_directives'(Type, Entity) :-
	'$lgt_pp_missing_meta_predicate_directive_'(Head, File, Lines),
	functor(Head, Functor, Arity),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_defines_non_terminal_'(Functor, NonTerminalArity, Arity) ->
		'$lgt_print_message'(
			warning(missing_directives),
			missing_predicate_directive(File, Lines, Type, Entity, (meta_non_terminal)/1, Functor//NonTerminalArity)
		)
	;	'$lgt_print_message'(
			warning(missing_directives),
			missing_predicate_directive(File, Lines, Type, Entity, (meta_predicate)/1, Functor/Arity)
		)
	),
	fail.

% reports missing multifile/1 directives

'$lgt_report_missing_directives'(Type, Entity) :-
	'$lgt_pp_missing_multifile_directive_'(PI, File, Lines),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_predicate_directive(File, Lines, Type, Entity, (multifile)/1, PI)
	),
	fail.

% reports missing dynamic/1 directives

'$lgt_report_missing_directives'(Type, Entity) :-
	'$lgt_pp_missing_dynamic_directive_'(Head, File, Lines),
	functor(Head, Functor, Arity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_predicate_directive(File, Lines, Type, Entity, (dynamic)/1, Functor/Arity)
	),
	fail.

% reports missing discontiguous/1 directives

'$lgt_report_missing_directives'(Type, Entity) :-
	'$lgt_pp_missing_discontiguous_directive_'(Head, File, Lines),
	functor(Head, Functor, Arity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_predicate_directive(File, Lines, Type, Entity, (discontiguous)/1, Functor/Arity)
	),
	fail.

% reports missing scope directives for mode2 and info/2 directives

'$lgt_report_missing_directives'(Type, Entity) :-
	(	'$lgt_pp_mode_'(Mode, _, File, Lines),
		functor(Mode, Functor, Arity),
		Directive = (mode)/2
	;	'$lgt_pp_predicate_info_'(Functor/Arity, _, File, Lines),
		Directive = info/2
	),
	% documented predicate or non-terminal
	\+ '$lgt_pp_non_terminal_'(Functor, Arity, _),
	\+ '$lgt_pp_public_'(Functor, Arity, _, _),
	\+ '$lgt_pp_protected_'(Functor, Arity, _, _),
	\+ '$lgt_pp_private_'(Functor, Arity, _, _),
	% but missing scope directive
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_scope_directive(File, Lines, Type, Entity, Directive, Functor/Arity)
	),
	fail.

'$lgt_report_missing_directives'(Type, Entity) :-
	(	Type == object ->
		'$lgt_pp_referenced_object_'(Entity, File, Lines)
	;	Type == category ->
		'$lgt_pp_referenced_category_'(Entity, File, Lines)
	;	% Type == protocol
		fail
	),
	setof(Predicate, '$lgt_pp_missing_use_module_directive_'(Module, Predicate), Predicates),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(missing_directives),
		missing_predicate_directive(File, Lines, Type, Entity, (:- use_module(Module,Predicates)))
	),
	fail.

'$lgt_report_missing_directives'(_, _).



% '$lgt_report_unknown_predicate_call'(@compilation_mode, @callable, @lines)
%
% reports unknown predicates and non-terminals

'$lgt_report_unknown_predicate_call'(runtime, _, _).

'$lgt_report_unknown_predicate_call'(compile(_,_,_), Pred, Lines) :-
	'$lgt_compiler_flag'(unknown_predicates, Value),
	'$lgt_report_unknown_predicate_call_aux'(Value, Pred, Lines).


'$lgt_report_unknown_predicate_call_aux'(silent, _, _).

'$lgt_report_unknown_predicate_call_aux'(error, Functor/Arity, _) :-
	(	'$lgt_pp_calls_non_terminal_'(Functor, Arity2, Arity, _) ->
		throw(existence_error(non_terminal, Functor//Arity2))
	;	throw(existence_error(predicate, Functor/Arity))
	).

'$lgt_report_unknown_predicate_call_aux'(warning, Functor/Arity, Lines) :-
	% we may be compiling an auxiliary clause and thus unable
	% to use the '$lgt_source_file_context'/4 predicate
	(	'$lgt_source_file_context'(File, Lines, Type, Entity) ->
		true
	;	'$lgt_pp_file_paths_flags_'(_, _, File, _, _),
		'$lgt_pp_entity_'(Type, Entity, _)
	),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_calls_non_terminal_'(Functor, Arity2, Arity, _) ->
		'$lgt_print_message'(
			warning(unknown_predicates),
			unknown_non_terminal_called_but_not_defined(File, Lines, Type, Entity, Functor//Arity2)
		)
	;	'$lgt_print_message'(
			warning(unknown_predicates),
			unknown_predicate_called_but_not_defined(File, Lines, Type, Entity, Functor/Arity)
		)
	).



% '$lgt_report_undefined_predicate_call'(@compilation_mode, @callable, @lines)
%
% reports calls to declared, static, but undefined predicates and non-terminals

'$lgt_report_undefined_predicate_call'(runtime, _, _).

'$lgt_report_undefined_predicate_call'(compile(_,_,_), Pred, Lines) :-
	'$lgt_compiler_flag'(undefined_predicates, Value),
	'$lgt_report_undefined_predicate_call_aux'(Value, Pred, Lines).


'$lgt_report_undefined_predicate_call_aux'(silent, _, _).

'$lgt_report_undefined_predicate_call_aux'(error, Functor/Arity, _) :-
	(	'$lgt_pp_calls_non_terminal_'(Functor, Arity2, Arity, _) ->
		throw(existence_error(procedure, Functor//Arity2))
	;	throw(existence_error(procedure, Functor/Arity))
	).

'$lgt_report_undefined_predicate_call_aux'(warning, Functor/Arity, Lines) :-
	% we may be compiling an auxiliary clause and thus unable
	% to use the '$lgt_source_file_context'/4 predicate
	(	'$lgt_source_file_context'(File, Lines, Type, Entity) ->
		true
	;	'$lgt_pp_file_paths_flags_'(_, _, File, _, _),
		'$lgt_pp_entity_'(Type, Entity, _)
	),
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_calls_non_terminal_'(Functor, Arity2, Arity, _) ->
		'$lgt_print_message'(
			warning(undefined_predicates),
			declared_static_non_terminal_called_but_not_defined(File, Lines, Type, Entity, Functor//Arity2)
		)
	;	'$lgt_print_message'(
			warning(undefined_predicates),
			declared_static_predicate_called_but_not_defined(File, Lines, Type, Entity, Functor/Arity)
		)
	).



% '$lgt_report_non_portable_calls'(@entity_type, @entity_identifier)
%
% reports non-portable predicate and function calls in the body of object and category predicates

'$lgt_report_non_portable_calls'(protocol, _) :-
	!.

'$lgt_report_non_portable_calls'(_, _) :-
	'$lgt_compiler_flag'(portability, silent),
	!.

'$lgt_report_non_portable_calls'(Type, Entity) :-
	'$lgt_pp_non_portable_predicate_'(Head, File, Lines),
		functor(Head, Functor, Arity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			non_standard_predicate_call(File, Lines, Type, Entity, Functor/Arity)
		),
	fail.

'$lgt_report_non_portable_calls'(Type, Entity) :-
	'$lgt_pp_non_portable_function_'(Function, File, Lines),
		functor(Function, Functor, Arity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			non_standard_arithmetic_function_call(File, Lines, Type, Entity, Functor/Arity)
		),
	fail.

'$lgt_report_non_portable_calls'(_, _).



% '$lgt_report_missing_functions'(@entity_type, @entity_identifier)
%
% reports non-portable predicate and function calls in the body of object and category predicates

'$lgt_report_missing_functions'(protocol, _) :-
	!.

'$lgt_report_missing_functions'(_, _) :-
	'$lgt_compiler_flag'(portability, silent),
	!.

'$lgt_report_missing_functions'(Type, Entity) :-
	'$lgt_pp_missing_function_'(Function, File, Lines),
		functor(Function, Functor, Arity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(portability),
			missing_function(File, Lines, Type, Entity, Functor/Arity)
		),
	fail.

'$lgt_report_missing_functions'(_, _).



% '$lgt_report_predicates_called_as_non_terminals'(@entity_type, @entity_identifier)
%
% reports calls to predicates as non-terminals from grammar rules

'$lgt_report_predicates_called_as_non_terminals'(protocol, _) :-
	!.

'$lgt_report_predicates_called_as_non_terminals'(_, _) :-
	'$lgt_compiler_flag'(grammar_rules, silent),
	!.

'$lgt_report_predicates_called_as_non_terminals'(Type, Entity) :-
	'$lgt_pp_calls_non_terminal_'(Functor, Arity, ExtArity, Lines),
	\+ '$lgt_pp_defines_non_terminal_'(Functor, Arity, ExtArity),
	'$lgt_pp_defines_predicate_'(_, Functor/ExtArity, _, _, _, _),
	% actually require at least one clause to be defined as the predicate may be dynamic
	'$lgt_pp_number_of_clauses_rules_'(Functor, ExtArity, _, _),
	'$lgt_pp_file_paths_flags_'(_, _, File, _, _),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(grammar_rules),
		calls_predicate_as_non_terminal(File, Lines, Type, Entity, Functor/ExtArity)
	),
	fail.

'$lgt_report_predicates_called_as_non_terminals'(_, _).



% '$lgt_report_non_tail_recursive_predicates'(@entity_type, @entity_identifier)
%
% reports non-tail recursive predicate definitions

'$lgt_report_non_tail_recursive_predicates'(protocol, _) :-
	!.

'$lgt_report_non_tail_recursive_predicates'(Type, Entity) :-
	'$lgt_compiler_flag'(tail_recursive, warning),
	'$lgt_pp_non_tail_recursive_predicate_'(Functor, Arity, File, Lines),
	'$lgt_pp_predicate_recursive_calls_'(Functor, Arity, Count),
	Count =:= 1,
	'$lgt_increment_compiling_warnings_counter',
	(	'$lgt_pp_defines_non_terminal_'(Functor, Arity2, Arity) ->
		'$lgt_print_message'(
			warning(tail_recursive),
			non_tail_recursive_non_terminal(File, Lines, Type, Entity, Functor//Arity2)
		)
	;	'$lgt_print_message'(
			warning(tail_recursive),
			non_tail_recursive_predicate(File, Lines, Type, Entity, Functor/Arity)
		)
	),
	fail.

'$lgt_report_non_tail_recursive_predicates'(_, _).



% '$lgt_write_encoding_directive'(@stream, +atom)
%
% writes the encoding/1 directive (if supported in generated code);
% it must be the first term in the file

'$lgt_write_encoding_directive'(Stream, Path) :-
	(	'$lgt_prolog_feature'(encoding_directive, full),
		'$lgt_pp_file_encoding_'(Path, _, Encoding, _) ->
		'$lgt_write_compiled_term'(Stream, (:- encoding(Encoding)), runtime, Path, 1)
	;	true
	).



% '$lgt_write_entity_directives'(@stream, +atom)
%
% writes the compiled entity directives

'$lgt_write_entity_directives'(Stream, Path) :-
	'$lgt_pp_directive_'(Directive),
		'$lgt_write_compiled_term'(Stream, (:- Directive), runtime, Path, 1),
	fail.

'$lgt_write_entity_directives'(_, _).



% '$lgt_write_prolog_terms'(@stream, atom)
%
% writes any Prolog clauses that appear before an entity opening directive

'$lgt_write_prolog_terms'(Stream, Path) :-
	'$lgt_pp_prolog_term_'(Term, Line-_),
		'$lgt_write_compiled_term'(Stream, Term, user, Path, Line),
	fail.

'$lgt_write_prolog_terms'(_, _).



% '$lgt_write_entity_clauses'(@stream, +atom, +atom)
%
% writes Logtalk entity clauses

'$lgt_write_entity_clauses'(Stream, Path, _) :-
	'$lgt_pp_dcl_'(Clause),
		'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, _) :-
	'$lgt_pp_def_'(Clause),
		'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, _) :-
	'$lgt_pp_ddef_'(Clause),
		'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, _) :-
	'$lgt_pp_super_'(Clause),
		'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, Rnm) :-
	'$lgt_pp_predicate_alias_'(Entity, Pred, Alias, _, _, _),
		Clause =.. [Rnm, Entity, Pred, Alias],
		'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, Rnm) :-
	Catchall =.. [Rnm, _, Pred, Pred],
	'$lgt_write_compiled_term'(Stream, Catchall, runtime, Path, 1),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, _) :-
	'$lgt_pp_final_entity_term_'(Clause, Line-_),
		'$lgt_write_compiled_term'(Stream, Clause, user, Path, Line),
	fail.

'$lgt_write_entity_clauses'(Stream, Path, _) :-
	'$lgt_pp_final_entity_aux_clause_'(Clause),
		'$lgt_write_compiled_term'(Stream, Clause, aux, Path, 1),
	fail.

'$lgt_write_entity_clauses'(_, _, _).



% '$lgt_write_runtime_clauses'(@stream, +atom)
%
% writes the entity runtime multifile and dynamic directives and the entity
% runtime clauses for all defined entities

'$lgt_write_runtime_clauses'(Stream, Path) :-
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_current_protocol_'/5),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_current_category_'/6),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_current_object_'/11),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_entity_property_'/2),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_predicate_property_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_implements_protocol_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_imports_category_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_instantiates_class_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_specializes_class_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_extends_category_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_extends_object_'/3),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_extends_protocol_'/3),
	'$lgt_write_runtime_static_clauses'(Stream, Path, '$lgt_uses_predicate_'/5),
	'$lgt_write_runtime_static_clauses'(Stream, Path, '$lgt_use_module_predicate_'/5),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_loaded_file_'/7),
	'$lgt_write_runtime_dynamic_clauses'(Stream, Path, '$lgt_included_file_'/5).


'$lgt_write_runtime_dynamic_clauses'(Stream, Path, Functor/Arity) :-
	functor(Clause, Functor, Arity),
	(	\+ '$lgt_pp_runtime_clause_'(Clause) ->
		true
	;	'$lgt_write_compiled_term'(Stream, (:- multifile(Functor/Arity)), runtime, Path, 1),
		'$lgt_write_compiled_term'(Stream, (:- dynamic(Functor/Arity)), runtime, Path, 1),
		(	'$lgt_pp_runtime_clause_'(Clause),
			'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
			fail
		;	true
		)
	).


'$lgt_write_runtime_static_clauses'(Stream, Path, Functor/Arity) :-
	functor(Clause, Functor, Arity),
	(	\+ '$lgt_pp_runtime_clause_'(Clause) ->
		true
	;	'$lgt_write_compiled_term'(Stream, (:- multifile(Functor/Arity)), runtime, Path, 1),
		(	'$lgt_pp_runtime_clause_'(Clause),
			'$lgt_write_compiled_term'(Stream, Clause, runtime, Path, 1),
			fail
		;	true
		)
	).



% '$lgt_write_initialization_directive'(@stream, +atom)
%
% writes the initialization directive for the compiled source file,
% a conjunction of the initialization goals of the defined entities

'$lgt_write_initialization_directive'(Stream, Path) :-
	'$lgt_initialization_goal'(Goal),
	(	Goal == true ->
		true
	;	'$lgt_write_compiled_term'(Stream, (:- initialization(Goal)), runtime, Path, 1)
	).



% '$lgt_initialization_goal'(-callable)
%
% source file initialization goal constructed from object initialization
% directives and from source file initialization/1 directives if present

'$lgt_initialization_goal'(InitializationGoal) :-
	findall(
		Line-Goal,
		(	'$lgt_pp_file_entity_initialization_'(_, Goal, Line-_)
		;	'$lgt_pp_file_initialization_'(Goal, Line-_)
		),
		LineGoals
	),
	% ensure source file textual order for the initialization goals
	% (this assumes that the backend Prolog system provides access to
	% read term position...)
	keysort(LineGoals, SortedLineGoals),
	findall(
		Goal,
		'$lgt_member'(_-Goal, SortedLineGoals),
		Goals
	),
	'$lgt_list_to_conjunction'(Goals, InitializationGoal).



% converts a list of goals into a conjunction of goals

'$lgt_list_to_conjunction'([], true).

'$lgt_list_to_conjunction'([Goal| Goals], Conjunction) :-
	'$lgt_list_to_conjunction'(Goals, Goal, Conjunction).


'$lgt_list_to_conjunction'([], Conjunction, Conjunction).

'$lgt_list_to_conjunction'([Next| Goals], Goal, (Goal,Conjunction)) :-
	'$lgt_list_to_conjunction'(Goals, Next, Conjunction).



% converts a conjunction into a list of terms

'$lgt_conjunction_to_list'(Term, [Term]) :-
	var(Term),
	!.

'$lgt_conjunction_to_list'((Term, Conjunction), [Term| Terms]) :-
	!,
	'$lgt_conjunction_to_list'(Conjunction, Terms).

'$lgt_conjunction_to_list'(Term, [Term]).



% generates and asserts the initialization goal for the object being compiled

'$lgt_generate_file_object_initialization_goal' :-
	'$lgt_pp_entity_'(_, Object, Prefix),
	(	'$lgt_prolog_feature'(threads, supported),
		setof(Mutex, Head^File^Lines^'$lgt_pp_synchronized_'(Head, Mutex, File, Lines), Mutexes) ->
		Goal1 = '$lgt_create_mutexes'(Mutexes)
	;	Goal1 = true
	),
	(	'$lgt_pp_threaded_' ->
		Goal2 = '$lgt_init_object_message_queue'(Prefix)
	;	Goal2 = true
	),
	% an object may contain multiple initialization/1 directives
	(	bagof(ObjectInitGoal, Lines^'$lgt_pp_final_object_initialization_'(ObjectInitGoal, Lines), ObjectInitGoals) ->
		'$lgt_list_to_conjunction'(ObjectInitGoals, Goal3),
		'$lgt_remove_redundant_calls'((Goal1, Goal2, Goal3), Goal)
	;	'$lgt_remove_redundant_calls'((Goal1, Goal2), Goal)
	),
	(	Goal == true ->
		true
	;	'$lgt_pp_referenced_object_'(Object, _File, Lines),
		assertz('$lgt_pp_file_entity_initialization_'(Object, Goal, Lines))
	).



% generates and asserts the initialization goal for the category being compiled

'$lgt_generate_file_category_initialization_goal' :-
	(	'$lgt_prolog_feature'(threads, supported),
		setof(Mutex, Head^File^Lines^'$lgt_pp_synchronized_'(Head, Mutex, File, Lines), Mutexes) ->
		'$lgt_pp_referenced_category_'(Category, _File, Lines),
		assertz('$lgt_pp_file_entity_initialization_'(Category, '$lgt_create_mutexes'(Mutexes), Lines))
	;	true
	).



% '$lgt_assert_dynamic_entity'(+atom)
%
% adds a dynamically created entity to memory

'$lgt_assert_dynamic_entity'(object) :-
	'$lgt_pp_object_'(_, _, _, _, _, _, _, _, _, Rnm, _),
	'$lgt_assert_dynamic_entity'(object, Rnm),
	'$lgt_call_initialization_goal'.

'$lgt_assert_dynamic_entity'(protocol) :-
	'$lgt_pp_protocol_'(_, _, _, Rnm, _),
	'$lgt_assert_dynamic_entity'(protocol, Rnm),
	'$lgt_call_initialization_goal'.

'$lgt_assert_dynamic_entity'(category) :-
	'$lgt_pp_category_'(_, _, _, _, Rnm, _),
	'$lgt_assert_dynamic_entity'(category, Rnm),
	'$lgt_call_initialization_goal'.


'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_directive_'(dynamic(Functor/Arity)),
		functor(Pred, Functor, Arity),
		asserta(Pred),
		retract(Pred),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_dcl_'(Clause),
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_def_'(Clause),
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_ddef_'(Clause),
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_super_'(Clause),
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, Rnm) :-
	'$lgt_pp_predicate_alias_'(Entity, Pred, Alias, _, _, _),
		Clause =.. [Rnm, Entity, Pred, Alias],
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, Rnm) :-
	Catchall =.. [Rnm, _, Pred, Pred],
	'$lgt_assertz_entity_clause'(Catchall, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_final_entity_term_'(Clause, _),
		'$lgt_assertz_entity_clause'(Clause, user),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_final_entity_aux_clause_'(Clause),
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(Type, _) :-
	'$lgt_save_entity_runtime_clause'(Type),
	fail.

'$lgt_assert_dynamic_entity'(_, _) :-
	'$lgt_pp_runtime_clause_'(Clause),
		'$lgt_assertz_entity_clause'(Clause, aux),
	fail.

'$lgt_assert_dynamic_entity'(_, _).



% '$lgt_call_initialization_goal'
%
% calls any defined initialization goals for a dynamically created entity

'$lgt_call_initialization_goal' :-
	(	'$lgt_prolog_feature'(threads, supported),
		setof(Mutex, Head^File^Lines^'$lgt_pp_synchronized_'(Head, Mutex, File, Lines), Mutexes) ->
		'$lgt_create_mutexes'(Mutexes)
	;	true
	),
	(	'$lgt_pp_object_'(_, Prefix, _, _, _, _, _, _, _, _, _),
		'$lgt_pp_threaded_' ->
		'$lgt_init_object_message_queue'(Prefix)
	;	true
	),
	% an object may contain multiple initialization/1 directives
	(	bagof(Goal, Lines^'$lgt_pp_final_object_initialization_'(Goal, Lines), GoalList) ->
		'$lgt_list_to_conjunction'(GoalList, Goals),
		once(Goals)
	;	true
	),
	% complementing categories add a file initialization goal
	(	'$lgt_pp_file_initialization_'(InitializationGoal, _) ->
		once(InitializationGoal)
	;	true
	).



% '$lgt_construct_prototype_functors'(+object_identifier, -atom, -atom, -atom, -atom, -atom, -atom, -atom, -atom, -atom)
%
% constructs functors used in the compiled code of an object playing the role of a prototype

'$lgt_construct_prototype_functors'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm) :-
	(	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, Flags),
		Flags /\ 1 =:= 1 ->
		% loaded, built-in object
		true
	;	'$lgt_construct_entity_prefix'(Obj, Prefix),
		atom_concat(Prefix, '_dcl', Dcl),
		atom_concat(Prefix, '_def', Def),
		atom_concat(Prefix, '_super', Super),
		IDcl = Dcl,
		IDef = Def,
		atom_concat(Prefix, '_ddcl', DDcl),
		atom_concat(Prefix, '_ddef', DDef),
		atom_concat(Prefix, '_alias', Rnm)
	).



% '$lgt_construct_ic_functors'(+object_identifier, -atom, -atom, -atom, -atom, -atom, -atom, -atom, -atom, -atom)
%
% constructs functors used in the compiled code of an object playing the role of a class or an instance

'$lgt_construct_ic_functors'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm) :-
	(	'$lgt_current_object_'(Obj, Prefix, Dcl, Def, Super, IDcl, IDef, DDcl, DDef, Rnm, Flags),
		Flags /\ 1 =:= 1 ->
		% loaded, built-in object
		true
	;	'$lgt_construct_entity_prefix'(Obj, Prefix),
		atom_concat(Prefix, '_dcl', Dcl),
		atom_concat(Prefix, '_def', Def),
		atom_concat(Prefix, '_super', Super),
		atom_concat(Prefix, '_idcl', IDcl),
		atom_concat(Prefix, '_idef', IDef),
		atom_concat(Prefix, '_ddcl', DDcl),
		atom_concat(Prefix, '_ddef', DDef),
		atom_concat(Prefix, '_alias', Rnm)
	).



% '$lgt_construct_protocol_functors'(+protocol_identifier, -atom, -atom, -atom)
%
% constructs functors used in the compiled code of a protocol

'$lgt_construct_protocol_functors'(Ptc, Prefix, Dcl, Rnm) :-
	(	'$lgt_current_protocol_'(Ptc, Prefix, Dcl, Rnm, Flags),
		Flags /\ 1 =:= 1 ->
		% loaded, built-in protocol
		true
	;	'$lgt_construct_entity_prefix'(Ptc, Prefix),
		atom_concat(Prefix, '_dcl', Dcl),
		atom_concat(Prefix, '_alias', Rnm)
	).



% '$lgt_construct_category_functors'(+category_identifier, -atom, -atom, -atom, -atom)
%
% constructs functors used in the compiled code of a category

'$lgt_construct_category_functors'(Ctg, Prefix, Dcl, Def, Rnm) :-
	(	'$lgt_current_category_'(Ctg, Prefix, Dcl, Def, Rnm, Flags),
		Flags /\ 1 =:= 1 ->
		% loaded, built-in category
		true
	;	'$lgt_construct_entity_prefix'(Ctg, Prefix),
		atom_concat(Prefix, '_dcl', Dcl),
		atom_concat(Prefix, '_def', Def),
		atom_concat(Prefix, '_alias', Rnm)
	).



% '$lgt_entity_to_prefix'(@entity_identifier, -atom)
%
% converts an entity identifier into an entity prefix (used in the compiled code)
% note that objects, categories, and protocols share the same namespace

'$lgt_entity_to_prefix'(Entity, Prefix) :-
	(	'$lgt_current_object_'(Entity, Prefix, _, _, _, _, _, _, _, _, _) ->
		true
	;	'$lgt_current_protocol_'(Entity, Prefix, _, _, _) ->
		true
	;	'$lgt_current_category_'(Entity, Prefix, _, _, _, _) ->
		true
	;	'$lgt_construct_entity_prefix'(Entity, Prefix)
	).



% '$lgt_prefix_to_entity'(+atom, -entity_identifier)
%
% reverses the entity prefix used in the compiled code
% note that objects, categories, and protocols share the same namespace

'$lgt_prefix_to_entity'(Prefix, Entity) :-
	(	'$lgt_current_object_'(Entity, Prefix, _, _, _, _, _, _, _, _, _) ->
		true
	;	'$lgt_current_protocol_'(Entity, Prefix, _, _, _) ->
		true
	;	'$lgt_current_category_'(Entity, Prefix, _, _, _, _) ->
		true
	;	'$lgt_deconstruct_entity_prefix'(Prefix, Entity) ->
		true
	;	throw(representation_error(entity_prefix))
	).



% '$lgt_construct_entity_prefix'(@entity_identifier, -atom)
%
% constructs the entity prefix used in the compiled code from the entity identifier
%
% prefix = code prefix + entity functor + "#" + entity arity + "."

'$lgt_construct_entity_prefix'(Entity, Prefix) :-
	'$lgt_compiler_flag'(code_prefix, CodePrefix),
	% the functor code prefix can be used to hide internal predicates (by
	% defining it as '$' when using most backend Prolog compilers) and to
	% avoid conflicts with other predicates
	functor(Entity, Functor, Arity),
	atom_concat(CodePrefix, Functor, Prefix0),
	(	'$lgt_arity_#atom.'(Arity, ArityAtom) ->
		true
	;	number_codes(Arity, ArityCodes),
		atom_codes(ArityAtom0, ArityCodes),
		atom_concat('#', ArityAtom0, ArityAtom1),
		atom_concat(ArityAtom1, '.', ArityAtom)
	),
	atom_concat(Prefix0, ArityAtom, Prefix).


% avoid costly atom computations for the most common cases
'$lgt_arity_#atom.'(0, '#0.').
'$lgt_arity_#atom.'(1, '#1.').
'$lgt_arity_#atom.'(2, '#2.').
'$lgt_arity_#atom.'(3, '#3.').
'$lgt_arity_#atom.'(4, '#4.').
'$lgt_arity_#atom.'(5, '#5.').
'$lgt_arity_#atom.'(6, '#6.').
'$lgt_arity_#atom.'(7, '#7.').
'$lgt_arity_#atom.'(8, '#8.').
'$lgt_arity_#atom.'(9, '#9.').



% '$lgt_deconstruct_entity_prefix'(+atom, -entity_identifier)
%
% deconstructs the entity prefix used in the compiled code
% returning the corresponding entity identifier template

'$lgt_deconstruct_entity_prefix'(Prefix, Entity) :-
	% valid values of the code_prefix flag are single character atoms
	sub_atom(Prefix, 1, _, 0, Entity0),
	atom_concat(Entity1, '.', Entity0),
	% locate the rightmost #
	sub_atom(Entity1, Before, 1, After, '#'),
	Lines is Before + 1,
	sub_atom(Entity1, Lines, _, 0, Rest),
	\+ sub_atom(Rest, _, 1, _, '#'), !,
	sub_atom(Entity1, 0, Before, _, Functor),
	sub_atom(Entity1, _, After, 0, ArityAtom),
	atom_codes(ArityAtom, ArityCodes),
	number_codes(Arity, ArityCodes),
	functor(Entity, Functor, Arity).



% '$lgt_compile_aux_clauses'(@list(clause))
%
% compiles a list of auxiliary predicate clauses;
% used mainly in conjunction with term_expansion/2 and goal_expansion/2 hook predicates

'$lgt_compile_aux_clauses'([Clause| Clauses]) :-
	% avoid making a predicate discontiguous by accident by using a
	% compilation mode that ensures that the auxiliary clauses will
	% be written after the user clauses
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, compile(aux,_,_), _, '-'(0,0), _),
	'$lgt_compile_clause'(Clause, Ctx),
	'$lgt_compile_aux_clauses'(Clauses).

'$lgt_compile_aux_clauses'([]).



% '$lgt_entity_prefix'(?entity_identifier, ?atom)
%
% converts between entity identifiers and internal entity prefixes;
% used mainly in hook objects for processing proprietary directives

'$lgt_entity_prefix'(Entity, Prefix) :-
	(	var(Entity), var(Prefix) ->
		'$lgt_pp_entity_'(_, Entity, Prefix)
	;	callable(Entity) ->
		'$lgt_entity_to_prefix'(Entity, Prefix)
	;	atom(Prefix),
		'$lgt_prefix_to_entity'(Prefix, Entity)
	).



% '$lgt_compile_predicate_heads'(@list(callable), ?entity_identifier, -list(callable), @compilation_context)
% '$lgt_compile_predicate_heads'(@callable, ?entity_identifier, -callable, @term)
%
% compiles a single predicate head, a conjunction of predicate heads, or a list of
% predicate heads; used mainly in hook objects for processing proprietary directives
%
% the predicate heads are compiled in the context of the specified entity or in the context
% of the entity being compiled when the entity argument is not instantiated

'$lgt_compile_predicate_heads'(Heads, Entity, THeads, Ctx) :-
	'$lgt_check'(var_or_entity_identifier, Entity),
	'$lgt_entity_prefix'(Entity, Prefix),
	'$lgt_compile_predicate_heads_aux'(Heads, Prefix, THeads, Ctx).


'$lgt_compile_predicate_heads_aux'(Heads, _, _, _) :-
	var(Heads),
	throw(instantiation_error).

'$lgt_compile_predicate_heads_aux'([], _, [], _) :-
	!.

'$lgt_compile_predicate_heads_aux'([Head| Heads], Prefix, [THead| THeads], Ctx) :-
	!,
	'$lgt_compile_predicate_heads_aux'(Head, Prefix, THead, Ctx),
	'$lgt_compile_predicate_heads_aux'(Heads, Prefix, THeads, Ctx).

'$lgt_compile_predicate_heads_aux'((Head, Heads), Prefix, (THead, THeads), Ctx) :-
	!,
	'$lgt_compile_predicate_heads_aux'(Head, Prefix, THead, Ctx),
	'$lgt_compile_predicate_heads_aux'(Heads, Prefix, THeads, Ctx).

'$lgt_compile_predicate_heads_aux'(Head, Prefix, THead, Ctx) :-
	'$lgt_check'(callable, Head),
	functor(Head, Functor, Arity),
	'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity),
	functor(THead, TFunctor, TArity),
	'$lgt_unify_head_thead_arguments'(Head, THead, Ctx).



% '$lgt_decompile_predicate_heads'(+list(callable), ?entity_identifier, ?atom, -list(callable))
% '$lgt_decompile_predicate_heads'(+callable, ?entity_identifier, ?atom, -callable)
%
% decompiles the predicate heads used for compiled predicates;
%
% all the compiled predicate heads must refer to the same entity
% (which must be loaded) in order for this predicate to succeed

'$lgt_decompile_predicate_heads'(THeads, Entity, Type, Heads) :-
	'$lgt_check'(var_or_entity_identifier, Entity),
	'$lgt_decompile_predicate_heads'(THeads, Entity, Type, _, Heads).


'$lgt_decompile_predicate_heads'(THeads, _, _, _, _) :-
	var(THeads),
	throw(instantiation_error).

'$lgt_decompile_predicate_heads'([], _, _, _, []) :-
	!.

'$lgt_decompile_predicate_heads'([THead| THeads], Entity, Type, Prefix, [Head| Heads]) :-
	!,
	'$lgt_decompile_predicate_heads'(THead, Entity, Type, Prefix, Head),
	'$lgt_decompile_predicate_heads'(THeads, Entity, Type, Prefix, Heads).

'$lgt_decompile_predicate_heads'(':'(Module,THead), Entity, Type, Prefix, Head) :-
	atom(Module),
	'$lgt_user_module_qualification'(xx, ':'(Module,xx)),
	!,
	'$lgt_decompile_predicate_heads'(THead, Entity, Type, Prefix, Head).

'$lgt_decompile_predicate_heads'(THead, Entity, Type, Prefix, Head) :-
	callable(THead),
	functor(THead, TFunctor, TArity),
	(	var(Prefix) ->
		(	'$lgt_current_object_'(Entity, Prefix, _, _, _, _, _, _, _, _, _),
			Type = object
		;	'$lgt_current_category_'(Entity, Prefix, _, _, _, _),
			Type = category
		;	'$lgt_current_protocol_'(Entity, Prefix, _, _, _),
			Type = protocol
		)
	;	true
	),
	'$lgt_decompile_predicate_indicator'(Prefix, TFunctor/TArity, Functor/Arity),
	functor(Head, Functor, Arity),
	'$lgt_unify_head_thead_arguments'(Head, THead, _),
	!.



% '$lgt_compile_predicate_indicators'(+list(predicate_indicator), ?entity_identifier, -list(predicate_indicator))
% '$lgt_compile_predicate_indicators'(+predicate_indicator, ?entity_identifier, -predicate_indicator)
%
% compiles a single predicate indicator, a conjunction of predicate indicators, or a list
% of predicate indicators; used mainly in hook objects for processing proprietary directives
%
% the predicate indicators are compiled in the context of the specified entity or in the context
% of the entity being compiled when the entity argument is not instantiated

'$lgt_compile_predicate_indicators'(PIs, Entity, TPIs) :-
	'$lgt_check'(var_or_entity_identifier, Entity),
	'$lgt_entity_prefix'(Entity, Prefix),
	'$lgt_compile_predicate_indicators_aux'(PIs, Prefix, TPIs).


'$lgt_compile_predicate_indicators_aux'(PIs, _, _) :-
	var(PIs),
	throw(instantiation_error).

'$lgt_compile_predicate_indicators_aux'([], _, []) :-
	!.

'$lgt_compile_predicate_indicators_aux'([PI| PIs], Prefix, [TPI| TPIs]) :-
	!,
	'$lgt_compile_predicate_indicators_aux'(PI, Prefix, TPI),
	'$lgt_compile_predicate_indicators_aux'(PIs, Prefix, TPIs).

'$lgt_compile_predicate_indicators_aux'((PI, PIs), Prefix, (TPI, TPIs)) :-
	!,
	'$lgt_compile_predicate_indicators_aux'(PI, Prefix, TPI),
	'$lgt_compile_predicate_indicators_aux'(PIs, Prefix, TPIs).

'$lgt_compile_predicate_indicators_aux'(PI, Prefix, TFunctor/TArity) :-
	(	'$lgt_valid_predicate_indicator'(PI, Functor, Arity) ->
		'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity)
	;	'$lgt_valid_non_terminal_indicator'(PI, Functor, _, ExtArity) ->
		'$lgt_compile_predicate_indicator'(Prefix, Functor/ExtArity, TFunctor/TArity)
	;	throw(type_error(predicate_indicator, PI))
	).



% '$lgt_compile_predicate_indicator'(+atom, +predicate_indicator, -predicate_indicator)
%
% compiles the user predicate indicator using the encoding entity prefix + functor + # + arity

'$lgt_compile_predicate_indicator'(Prefix, Functor/Arity, TFunctor/TArity) :-
	atom_concat(Prefix, Functor, TFunctor0),
	(	'$lgt_arity_#atom'(Arity, ArityAtom) ->
		true
	;	number_codes(Arity, ArityCodes),
		atom_codes(ArityAtom0, ArityCodes),
		atom_concat('#', ArityAtom0, ArityAtom)
	),
	atom_concat(TFunctor0, ArityAtom, TFunctor),
	% add execution context argument
	TArity is Arity + 1.


% avoid costly atom computations for the most common cases
'$lgt_arity_#atom'(0, '#0').
'$lgt_arity_#atom'(1, '#1').
'$lgt_arity_#atom'(2, '#2').
'$lgt_arity_#atom'(3, '#3').
'$lgt_arity_#atom'(4, '#4').
'$lgt_arity_#atom'(5, '#5').
'$lgt_arity_#atom'(6, '#6').
'$lgt_arity_#atom'(7, '#7').
'$lgt_arity_#atom'(8, '#8').
'$lgt_arity_#atom'(9, '#9').



% '$lgt_decompile_predicate_indicator'(+atom, +predicate_indicator, -predicate_indicator)
%
% decompiles an internal predicate indicator used for a user predicate

'$lgt_decompile_predicate_indicator'(Prefix, TFunctor/TArity, Functor/Arity) :-
	atom_concat(Prefix, Predicate, TFunctor),
	% locate the rightmost # by looking for occurences left-to-right and
	% backtracking until the rest of the atom no longer contains a #
	sub_atom(Predicate, Before, 1, _, '#'),
	Parsed is Before + 1,
	sub_atom(Predicate, Parsed, _, 0, Rest),
	\+ sub_atom(Rest, _, 1, _, '#'),
	sub_atom(Predicate, 0, Before, _, Functor),
	% subtract execution context argument
	Arity is TArity - 1,
	Arity >= 0,
	!.



% '$lgt_decompile_predicate_indicators'(+list(predicate_indicator), ?entity_identifier, ?atom, -list(predicate_indicator))
% '$lgt_decompile_predicate_indicators'(+predicate_indicator, ?entity_identifier, ?atom, -predicate_indicator)
%
% reverses the predicate indicator used for a compiled predicate or a list of compiled predicates;
%
% all the compiled predicate indicators must refer to the same entity
% (which must be loaded) in order for this predicate to succeed

'$lgt_decompile_predicate_indicators'(TPIs, Entity, Type, PIs) :-
	'$lgt_check'(var_or_entity_identifier, Entity),
	'$lgt_decompile_predicate_indicators'(TPIs, Entity, Type, _, PIs).


'$lgt_decompile_predicate_indicators'(TPIs, _, _, _, _) :-
	var(TPIs),
	throw(instantiation_error).

'$lgt_decompile_predicate_indicators'([], _, _, _, []) :-
	!.

'$lgt_decompile_predicate_indicators'([TPI| TPIs], Entity, Type, Prefix, [PI| PIs]) :-
	!,
	'$lgt_decompile_predicate_indicators'(TPI, Entity, Type, Prefix, PI),
	'$lgt_decompile_predicate_indicators'(TPIs, Entity, Type, Prefix, PIs).

'$lgt_decompile_predicate_indicators'(':'(Module,TFunctor/TArity), Entity, Type, Prefix, Functor/Arity) :-
	atom(Module),
	'$lgt_user_module_qualification'(xx, ':'(Module,xx)),
	!,
	'$lgt_decompile_predicate_indicators'(TFunctor/TArity, Entity, Type, Prefix, Functor/Arity).

'$lgt_decompile_predicate_indicators'(TFunctor/TArity, Entity, Type, Prefix, Functor/Arity) :-
	(	var(Prefix) ->
		(	'$lgt_current_object_'(Entity, Prefix, _, _, _, _, _, _, _, _, _),
			Type = object
		;	'$lgt_current_category_'(Entity, Prefix, _, _, _, _),
			Type = category
		;	'$lgt_current_protocol_'(Entity, Prefix, _, _, _),
			Type = protocol
		)
	;	true
	),
	'$lgt_decompile_predicate_indicator'(Prefix, TFunctor/TArity, Functor/Arity),
	!.



% '$lgt_compile_hooks'(+callable)
%
% compiles the user-defined default compiler hooks
% (replacing any existing defined hooks)

'$lgt_compile_hooks'(HookEntity) :-
	'$lgt_comp_ctx'(Ctx, _, _, user, user, user, HookEntity, _, [], [], ExCtx, runtime, [], _, _),
	'$lgt_execution_context'(ExCtx, user, user, user, HookEntity, [], []),
	'$lgt_current_flag_'(events, Events),
	'$lgt_compile_message_to_object'(term_expansion(Term, ExpandedTerm), HookEntity, TermExpansionGoal, Events, Ctx),
	'$lgt_compile_message_to_object'(goal_expansion(Term, ExpandedTerm), HookEntity, GoalExpansionGoal, Events, Ctx),
	retractall('$lgt_hook_term_expansion_'(_, _)),
	assertz((
		'$lgt_hook_term_expansion_'(Term, ExpandedTerm) :-
			catch(TermExpansionGoal, Error, '$lgt_term_expansion_error'(HookEntity, Term, Error))
	)),
	retractall('$lgt_hook_goal_expansion_'(_, _)),
	assertz((
		'$lgt_hook_goal_expansion_'(Term, ExpandedTerm) :-
			catch(GoalExpansionGoal, Error, '$lgt_goal_expansion_error'(HookEntity, Term, Error))
	)).



% '$lgt_built_in_predicate'(@callable)
%
% checks if the argument is either a Logtalk or a Prolog built-in predicate

'$lgt_built_in_predicate'(Pred) :-
	'$lgt_logtalk_built_in_predicate'(Pred, _),
	!.

'$lgt_built_in_predicate'(Pred) :-
	'$lgt_predicate_property'(Pred, built_in),
	!.

'$lgt_built_in_predicate'(Pred) :-
	'$lgt_iso_predicate'(Pred),
	% hack for missing ISO standard predicate defined in the used adapter file
	!.



% '$lgt_prolog_built_in_predicate'(@callable)
%
% either host Prolog native built-ins or missing ISO built-ins
% that we have defined in the correspondent adapter file

'$lgt_prolog_built_in_predicate'(Pred) :-
	'$lgt_predicate_property'(Pred, built_in),
	% Logtalk built-in predicates may also have the property "built_in"
	% depending on the used backend Prolog compiler
	\+ '$lgt_logtalk_built_in_predicate'(Pred, _),
	!.

'$lgt_prolog_built_in_predicate'(Pred) :-
	% ISO Prolog built-in predicate (defined in the adapter files)
	'$lgt_iso_predicate'(Pred).



% '$lgt_prolog_built_in_database_predicate'(@callable)
%
% ISO Prolog standard and proprietary database predicates

'$lgt_prolog_built_in_database_predicate'(Term) :-
	'$lgt_iso_database_predicate'(Term),
	% ISO Prolog standard database predicate
	!.

'$lgt_prolog_built_in_database_predicate'(Term) :-
	'$lgt_prolog_database_predicate'(Term),
	% proprietary database predicate (declared in the adapter files)
	!.



% '$lgt_prolog_deprecated_built_in_predicate'(@callable, -callable)
%
% Prolog deprecated predicate that can be replaced by a call to a
% standard predicate; callers must check that the predicate is a
% built-in predicate that is not being locally redefined

'$lgt_prolog_deprecated_built_in_predicate'(current_predicate(Name, Template), current_predicate(Name/Arity)) :-
	callable(Template),
	Template \= ':'(_, _),
	functor(Template, Name, Arity).
'$lgt_prolog_deprecated_built_in_predicate'(fail_if(Pred), \+ Pred).
'$lgt_prolog_deprecated_built_in_predicate'(get0(Code), get_code(Code)).
'$lgt_prolog_deprecated_built_in_predicate'(get0(Stream, Code), get_code(Stream, Code)).
'$lgt_prolog_deprecated_built_in_predicate'(put(Code), put_code(Code)).
'$lgt_prolog_deprecated_built_in_predicate'(put(Stream, Code), put_code(Stream, Code)).
'$lgt_prolog_deprecated_built_in_predicate'(name(Atomic, Codes), Goal) :-
	(	number(Atomic) ->
		Goal = number_codes(Atomic, Codes)
	;	atom(Atomic),
		(	atom_length(Atomic, 1) ->
			Goal = char_code(Atomic, Code),
			Codes = [Code]
		;	Goal = atom_codes(Atomic, Codes)
		)
	).
'$lgt_prolog_deprecated_built_in_predicate'(not(Pred), \+ Pred).
'$lgt_prolog_deprecated_built_in_predicate'(otherwise, true).
'$lgt_prolog_deprecated_built_in_predicate'(prolog_flag(Flag, Value), current_prolog_flag(Flag, Value)) :-
	atom(Flag),
	'$lgt_iso_spec_flag'(Flag).
'$lgt_prolog_deprecated_built_in_predicate'(prolog_flag(Flag, Old, New), set_prolog_flag(Flag, New)) :-
	var(Old),
	atom(Flag),
	'$lgt_iso_spec_flag'(Flag).
'$lgt_prolog_deprecated_built_in_predicate'(on_exception(Error, Goal, Handler), catch(Goal, Error, Handler)).
'$lgt_prolog_deprecated_built_in_predicate'(raise_exception(Error), throw(Error)).
% Quintus Prolog predicates for arithmetic functions
'$lgt_prolog_deprecated_built_in_predicate'(sin(X, Y), Y is sin(X)).
'$lgt_prolog_deprecated_built_in_predicate'(cos(X, Y), Y is cos(X)).
'$lgt_prolog_deprecated_built_in_predicate'(tan(X, Y), Y is tan(X)).
'$lgt_prolog_deprecated_built_in_predicate'(log(X, Y), Y is log(X)).
'$lgt_prolog_deprecated_built_in_predicate'(pow(X, Y, Z), Z is X**Y).
'$lgt_prolog_deprecated_built_in_predicate'(ceiling(X, Y), Y is ceiling(X)).
'$lgt_prolog_deprecated_built_in_predicate'(floor(X, Y), Y is floor(X)).
'$lgt_prolog_deprecated_built_in_predicate'(round(X, Y), Y is round(X)).
'$lgt_prolog_deprecated_built_in_predicate'(sqrt(X, Y), Y is sqrt(X)).
'$lgt_prolog_deprecated_built_in_predicate'(acos(X, Y), Y is acos(X)).
'$lgt_prolog_deprecated_built_in_predicate'(asin(X, Y), Y is asin(X)).
'$lgt_prolog_deprecated_built_in_predicate'(atan(X, Y), Y is atan(X)).
'$lgt_prolog_deprecated_built_in_predicate'(atan2(X, Y, Z), Z is atan2(X, Y)).
'$lgt_prolog_deprecated_built_in_predicate'(sign(X, Y), Y is sign(X)).



% '$lgt_prolog_deprecated_built_in_predicate'(@callable)
%
% Prolog deprecated built-in predicate; callers must check that the
% predicate is a built-in predicate that is not being locally redefined

'$lgt_prolog_deprecated_built_in_predicate'(current_predicate(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(get(_)).
'$lgt_prolog_deprecated_built_in_predicate'(get(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(name(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(prolog_flag(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(prolog_flag(_, _, _)).
'$lgt_prolog_deprecated_built_in_predicate'(skip(_)).
'$lgt_prolog_deprecated_built_in_predicate'(skip(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(tab(_)).
'$lgt_prolog_deprecated_built_in_predicate'(tab(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(ttynl).
'$lgt_prolog_deprecated_built_in_predicate'(ttyflush).
'$lgt_prolog_deprecated_built_in_predicate'(ttyget0(_)).
'$lgt_prolog_deprecated_built_in_predicate'(ttyget(_)).
'$lgt_prolog_deprecated_built_in_predicate'(ttyskip(_)).
'$lgt_prolog_deprecated_built_in_predicate'(ttyput(_)).
'$lgt_prolog_deprecated_built_in_predicate'(ttytab(_)).
% DEC-10 Prolog and C-Prolog legacy predicates
'$lgt_prolog_deprecated_built_in_predicate'(simple(_)).
'$lgt_prolog_deprecated_built_in_predicate'(reconsult(_)).
'$lgt_prolog_deprecated_built_in_predicate'(display(_)).
'$lgt_prolog_deprecated_built_in_predicate'(unknown(_, _)).
'$lgt_prolog_deprecated_built_in_predicate'(fileerrors).
'$lgt_prolog_deprecated_built_in_predicate'(nofileerrors).
'$lgt_prolog_deprecated_built_in_predicate'(see(_)).
'$lgt_prolog_deprecated_built_in_predicate'(tell(_)).
'$lgt_prolog_deprecated_built_in_predicate'(append(_)).
'$lgt_prolog_deprecated_built_in_predicate'(seeing(_)).
'$lgt_prolog_deprecated_built_in_predicate'(telling(_)).
'$lgt_prolog_deprecated_built_in_predicate'(seen).
'$lgt_prolog_deprecated_built_in_predicate'(told).
% Quintus Prolog predicates for arithmetic functions
'$lgt_prolog_deprecated_built_in_predicate'(log10(_, _)).


% '$lgt_prolog_deprecated_built_in_function'(@callable, -callable)
%
% Prolog deprecated function that can be replaced by a call to a
% standard function; callers must check that the function is a
% built-in function

'$lgt_prolog_deprecated_built_in_function'(ceil(Float), ceiling(Float)).
'$lgt_prolog_deprecated_built_in_function'(integer(Float), round(Float)).



% '$lgt_prolog_deprecated_built_in_function'(@callable)
%
% Prolog deprecated built-in function; callers must check that the
% function is a built-in function

'$lgt_prolog_deprecated_built_in_function'(_) :-
	fail.



% Logtalk built-in methods
%
% '$lgt_built_in_method'(@callable, ?scope, ?callable, ?integer)

'$lgt_built_in_method'(Method, Scope, Meta, Flags) :-
	(	'$lgt_built_in_method_spec'(Method, Scope, Meta, Flags) ->
		true
	;	% check if call/2-N
		functor(Method, call, Arity),
		Arity > 1,
		Scope = p,
		functor(Meta, call, Arity),
		Closure is Arity - 1,
		arg(1, Meta, Closure),
		'$lgt_built_in_method_call_n_args'(Arity, Meta),
		Flags = 1
	).


'$lgt_built_in_method_call_n_args'(1, _) :-
	!.

'$lgt_built_in_method_call_n_args'(N, Meta) :-
	arg(N, Meta, *),
	N2 is N - 1,
	'$lgt_built_in_method_call_n_args'(N2, Meta).


% control constructs
'$lgt_built_in_method_spec'(_::_, p, '::'(*, *), 1).
'$lgt_built_in_method_spec'(::_, p, '::'(*), 1).
'$lgt_built_in_method_spec'([_], p, [*], 1).
'$lgt_built_in_method_spec'(^^_, p, '^^'(*), 1).
'$lgt_built_in_method_spec'(_<<_, p, '<<'(*, 0), 1).
'$lgt_built_in_method_spec'(_>>_, p, '>>'(*, 0), 1).
'$lgt_built_in_method_spec'(':'(_,_), p, ':'(*, 0), 1) :-
	'$lgt_prolog_feature'(modules, supported).
'$lgt_built_in_method_spec'({_}, p(p(p)), '{}'(0), 1).
'$lgt_built_in_method_spec'((_,_), p(p(p)), ','(0, 0), 1).
'$lgt_built_in_method_spec'((_;_), p(p(p)), ';'(0, 0), 1).
'$lgt_built_in_method_spec'((_->_), p(p(p)), '->'(0, 0), 1).
'$lgt_built_in_method_spec'('*->'(_,_), p(p(p)), '*->'(0, 0), 1) :-
	'$lgt_prolog_built_in_predicate'('*->'(_, _)).
% reflection methods
'$lgt_built_in_method_spec'(current_op(_,_,_), p(p(p)), current_op(*, *, (::)), 1).
'$lgt_built_in_method_spec'(current_predicate(_), p(p(p)), current_predicate((::)), 1).
'$lgt_built_in_method_spec'(predicate_property(_,_), p(p(p)), predicate_property((::), *), 1).
% database methods
'$lgt_built_in_method_spec'(abolish(_), p(p(p)), abolish((::)), 1).
'$lgt_built_in_method_spec'(asserta(_), p(p(p)), asserta((::)), 1).
'$lgt_built_in_method_spec'(assertz(_), p(p(p)), assertz((::)), 1).
'$lgt_built_in_method_spec'(clause(_,_), p(p(p)), clause((::), *), 1).
'$lgt_built_in_method_spec'(retract(_), p(p(p)), retract((::)), 1).
'$lgt_built_in_method_spec'(retractall(_), p(p(p)), retractall((::)), 1).
% term expansion methods
'$lgt_built_in_method_spec'(expand_term(_,_), p(p(p)), no, 1).
'$lgt_built_in_method_spec'(expand_goal(_,_), p(p(p)), no, 1).
% DCGs methods
'$lgt_built_in_method_spec'(phrase(_,_,_), p, phrase(2, *, *), 1).
'$lgt_built_in_method_spec'(phrase(_,_), p, phrase(2, *), 1).
% meta-calls plus logic and control methods
'$lgt_built_in_method_spec'(\+ _, p, \+ 0, 1).
'$lgt_built_in_method_spec'(call(_), p, call(0), 1).
'$lgt_built_in_method_spec'(once(_), p, once(0), 1).
'$lgt_built_in_method_spec'(ignore(_), p, ignore(0), 1).
'$lgt_built_in_method_spec'(!, p(p(p)), no, 1).
'$lgt_built_in_method_spec'(true, p(p(p)), no, 1).
'$lgt_built_in_method_spec'(fail, p(p(p)), no, 1).
'$lgt_built_in_method_spec'(false, p(p(p)), no, 1).
'$lgt_built_in_method_spec'(repeat, p(p(p)), no, 1).
% exception handling methods
'$lgt_built_in_method_spec'(catch(_,_,_), p, catch(0, *, 0), 1).
'$lgt_built_in_method_spec'(throw(_), p, no, 1).
% error predicates
'$lgt_built_in_method_spec'(instantiation_error, p, no, 1).
'$lgt_built_in_method_spec'(uninstantiation_error(_), p, no, 1).
'$lgt_built_in_method_spec'(type_error(_,_), p, no, 1).
'$lgt_built_in_method_spec'(domain_error(_,_), p, no, 1).
'$lgt_built_in_method_spec'(consistency_error(_,_,_), p, no, 1).
'$lgt_built_in_method_spec'(existence_error(_,_), p, no, 1).
'$lgt_built_in_method_spec'(permission_error(_,_,_), p, no, 1).
'$lgt_built_in_method_spec'(representation_error(_), p, no, 1).
'$lgt_built_in_method_spec'(evaluation_error(_), p, no, 1).
'$lgt_built_in_method_spec'(resource_error(_), p, no, 1).
'$lgt_built_in_method_spec'(syntax_error(_), p, no, 1).
'$lgt_built_in_method_spec'(system_error, p, no, 1).
% execution context methods
'$lgt_built_in_method_spec'(context(_), p, no, 1).
'$lgt_built_in_method_spec'(parameter(_,_), p, no, 1).
'$lgt_built_in_method_spec'(self(_), p, no, 1).
'$lgt_built_in_method_spec'(sender(_), p, no, 1).
'$lgt_built_in_method_spec'(this(_), p, no, 1).
% all solutions methods
'$lgt_built_in_method_spec'(bagof(_,_,_), p, bagof(*, ^, *), 1).
'$lgt_built_in_method_spec'(findall(_,_,_), p, findall(*, 0, *), 1).
'$lgt_built_in_method_spec'(findall(_,_,_,_), p, findall(*, 0, *, *), 1).
'$lgt_built_in_method_spec'(forall(_,_), p, forall(0, 0), 1).
'$lgt_built_in_method_spec'(setof(_,_,_), p, setof(*, ^, *), 1).



% Logtalk built-in error methods
%
% '$lgt_built_in_error_method'(@callable)

'$lgt_built_in_error_method'(instantiation_error).
'$lgt_built_in_error_method'(uninstantiation_error(_)).
'$lgt_built_in_error_method'(type_error(_, _)).
'$lgt_built_in_error_method'(domain_error(_, _)).
'$lgt_built_in_error_method'(consistency_error(_, _, _)).
'$lgt_built_in_error_method'(existence_error(_, _)).
'$lgt_built_in_error_method'(permission_error(_, _, _)).
'$lgt_built_in_error_method'(representation_error(_)).
'$lgt_built_in_error_method'(evaluation_error(_)).
'$lgt_built_in_error_method'(resource_error(_)).
'$lgt_built_in_error_method'(syntax_error(_)).
'$lgt_built_in_error_method'(system_error).



% Logtalk built-in meta-predicates
%
% '$lgt_logtalk_meta_predicate'(+callable, ?callable, ?atom)

'$lgt_logtalk_meta_predicate'(Pred, Meta, predicate) :-
	'$lgt_built_in_method'(Pred, _, Meta, _),
	Meta \== no.



% '$lgt_reserved_predicate_protocol'(?callable, ?atom)
%
% table of reserved predicate names and the built-in protocols
% where they are declared

'$lgt_reserved_predicate_protocol'(before(_, _, _), monitoring).
'$lgt_reserved_predicate_protocol'(after(_, _, _), monitoring).
'$lgt_reserved_predicate_protocol'(term_expansion(_, _), expanding).
'$lgt_reserved_predicate_protocol'(goal_expansion(_, _), expanding).
'$lgt_reserved_predicate_protocol'(forward(_), forwarding).



%'$lgt_logtalk_directive'(@callable)
%
% valid Logtalk directives; a common subset of Prolog module directives are
% also included as modules can be compiled as objects (but the specific case
% of the use_module/1 directive is handled at the Prolog adapter file level)

'$lgt_logtalk_directive'(Directive) :-
	'$lgt_logtalk_opening_directive'(Directive),
	!.

'$lgt_logtalk_directive'(Directive) :-
	'$lgt_logtalk_closing_directive'(Directive),
	!.

'$lgt_logtalk_directive'(Directive) :-
	'$lgt_logtalk_entity_directive'(Directive),
	!.

'$lgt_logtalk_directive'(Directive) :-
	'$lgt_logtalk_predicate_directive'(Directive),
	!.


% objects
'$lgt_logtalk_opening_directive'(object(_)).
'$lgt_logtalk_opening_directive'(object(_, _)).
'$lgt_logtalk_opening_directive'(object(_, _, _)).
'$lgt_logtalk_opening_directive'(object(_, _, _, _)).
'$lgt_logtalk_opening_directive'(object(_, _, _, _, _)).
% categories
'$lgt_logtalk_opening_directive'(category(_)).
'$lgt_logtalk_opening_directive'(category(_, _)).
'$lgt_logtalk_opening_directive'(category(_, _, _)).
'$lgt_logtalk_opening_directive'(category(_, _, _, _)).
% protocols
'$lgt_logtalk_opening_directive'(protocol(_)).
'$lgt_logtalk_opening_directive'(protocol(_, _)).
% Prolog module directives
'$lgt_logtalk_opening_directive'(module(_)).
'$lgt_logtalk_opening_directive'(module(_, _)).
% module/3 directives are currently not supported but must
% be recognized as entity opening directives
'$lgt_logtalk_opening_directive'(module(_, _, _)).


'$lgt_logtalk_closing_directive'(end_object).
'$lgt_logtalk_closing_directive'(end_category).
'$lgt_logtalk_closing_directive'(end_protocol).


'$lgt_logtalk_entity_directive'(built_in).
'$lgt_logtalk_entity_directive'(include(_)).
'$lgt_logtalk_entity_directive'(initialization(_)).
'$lgt_logtalk_entity_directive'((dynamic)).
'$lgt_logtalk_entity_directive'(op(_, _, _)).
'$lgt_logtalk_entity_directive'(info(_)).
'$lgt_logtalk_entity_directive'(threaded).
'$lgt_logtalk_entity_directive'(set_logtalk_flag(_, _)).
'$lgt_logtalk_entity_directive'(uses(_)).
'$lgt_logtalk_entity_directive'(use_module(_)).


'$lgt_logtalk_predicate_directive'(synchronized(_)).
'$lgt_logtalk_predicate_directive'(dynamic(_)).
'$lgt_logtalk_predicate_directive'(meta_predicate(_)).
'$lgt_logtalk_predicate_directive'(meta_non_terminal(_)).
'$lgt_logtalk_predicate_directive'(discontiguous(_)).
'$lgt_logtalk_predicate_directive'(public(_)).
'$lgt_logtalk_predicate_directive'(protected(_)).
'$lgt_logtalk_predicate_directive'(private(_)).
'$lgt_logtalk_predicate_directive'(mode(_, _)).
'$lgt_logtalk_predicate_directive'(info(_, _)).
'$lgt_logtalk_predicate_directive'(alias(_, _)).
'$lgt_logtalk_predicate_directive'(multifile(_)).
'$lgt_logtalk_predicate_directive'(coinductive(_)).
'$lgt_logtalk_predicate_directive'(uses(_, _)).
'$lgt_logtalk_predicate_directive'(use_module(_, _)).
% Prolog module directives that are recognized when compiling modules as objects
'$lgt_logtalk_predicate_directive'(export(_)).
'$lgt_logtalk_predicate_directive'(reexport(_, _)).


'$lgt_conditional_compilation_directive'(if(_)).
'$lgt_conditional_compilation_directive'(elif(_)).
'$lgt_conditional_compilation_directive'(else).
'$lgt_conditional_compilation_directive'(endif).


'$lgt_is_conditional_compilation_directive'((:- Directive)) :-
	nonvar(Directive),
	'$lgt_conditional_compilation_directive'(Directive).



% '$lgt_file_directive'(@callable)
%
% standard file-level directives (used for portability checking)

'$lgt_file_directive'(discontiguous(_)).
'$lgt_file_directive'(dynamic(_)).
'$lgt_file_directive'(multifile(_)).
'$lgt_file_directive'(encoding(_)).
'$lgt_file_directive'(include(_)).
'$lgt_file_directive'(use_module(_)).
'$lgt_file_directive'(use_module(_, _)).
'$lgt_file_directive'(ensure_loaded(_)).
'$lgt_file_directive'(set_prolog_flag(_, _)).
'$lgt_file_directive'(set_logtalk_flag(_, _)).
'$lgt_file_directive'(initialization(_)).
'$lgt_file_directive'(op(_, _, _)).



% utility predicates used during compilation of Logtalk entities to store and
% access compilation context information (represented by a compound term)

'$lgt_comp_ctx'(ctx(_, _, _, _, _, _, _, _, _, _, _, _, _, _)).

'$lgt_comp_ctx'(
	ctx(Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, Mode, Stack, Lines, Term),
	Head, HeadExCtx, Entity, Sender, This, Self, Prefix, MetaVars, MetaCallCtx, ExCtx, Mode, Stack, Lines, Term
).

% head of the clause being compiled
'$lgt_comp_ctx_head'(ctx(Head, _, _, _, _, _, _, _, _, _, _, _, _, _), Head).

% head execution context of the clause being compiled
'$lgt_comp_ctx_head_exec_ctx'(ctx(_, HeadExCtx, _, _, _, _, _, _, _, _, _, _, _, _), HeadExCtx).

% entity containing the clause being compiled (either a category or an object)
'$lgt_comp_ctx_entity'(ctx(_, _, Entity, _, _, _, _, _, _, _, _, _, _, _), Entity).

'$lgt_comp_ctx_sender'(ctx(_, _, _, Sender, _, _, _, _, _, _, _, _, _, _), Sender).

'$lgt_comp_ctx_this'(ctx(_, _, _, _, This, _, _, _, _, _, _, _, _, _), This).

'$lgt_comp_ctx_self'(ctx(_, _, _, _, _, Self, _, _, _, _, _, _, _, _), Self).

% entity prefix used to avoid predicate name conflicts
'$lgt_comp_ctx_prefix'(ctx(_, _, _, _, _, _, Prefix, _, _, _, _, _, _, _), Prefix).

'$lgt_comp_ctx_meta_vars'(ctx(_, _, _, _, _, _, _, MetaVars, _, _, _, _, _, _), MetaVars).

'$lgt_comp_ctx_meta_call_ctx'(ctx(_, _, _, _, _, _, _, _, MetaCallCtx, _, _, _, _, _), MetaCallCtx).

'$lgt_comp_ctx_exec_ctx'(ctx(_, _, _, _, _, _, _, _, _, ExCtx, _, _, _, _), ExCtx).

% compilation mode; possible values are "compile(user,_,_)", "compile(aux,_,_)", and "runtime"
'$lgt_comp_ctx_mode'(ctx(_, _, _, _, _, _, _, _, _, _, Mode, _, _, _), Mode).

% stack of coinductive hypothesis (ancestor goals)
'$lgt_comp_ctx_stack'(ctx(_, _, _, _, _, _, _, _, _, _, _, Stack, _, _), Stack).

% begin line and end line (a pair of integers) of the term being compiled
'$lgt_comp_ctx_lines'(ctx(_, _, _, _, _, _, _, _, _, _, _, _, Lines, _), Lines).

% term being compiled
'$lgt_comp_ctx_term'(ctx(_, _, _, _, _, _, _, _, _, _, _, _, _, Term), Term).



% utility predicates used to access execution context terms

'$lgt_execution_context'(c(This, Entity, r(Sender, Self, MetaCallContext, Stack)), Entity, Sender, This, Self, MetaCallContext, Stack).

% inheritance only requires updating "this" and "entity"
'$lgt_execution_context_update_this_entity'(c(OldThis, OldEntity, Rest), OldThis, OldEntity, c(NewThis, NewEntity, Rest), NewThis, NewEntity).

'$lgt_execution_context_this_entity'(c(This, Entity, _), This, Entity).



% '$lgt_term_template'(@callable, -callable)
%
% constructs a template for a callable term

'$lgt_term_template'(Term, Template) :-
	functor(Term, Functor, Arity),
	functor(Template, Functor, Arity).



% '$lgt_flatten_to_list'(+term, -list)
%
% flattens an item, a list of items, or a conjunction of items into a list

'$lgt_flatten_to_list'([A| B], [A| B]) :-
	!.

'$lgt_flatten_to_list'([], []) :-
	!.

'$lgt_flatten_to_list'((A, B), [A| BB]) :-
	!,
	'$lgt_flatten_to_list'(B, BB).

'$lgt_flatten_to_list'(A, [A]).



% '$lgt_valid_scope'(@nonvar).
%
% valid (user-level) scope

'$lgt_valid_scope'((private)).
'$lgt_valid_scope'(protected).
'$lgt_valid_scope'((public)).



% '$lgt_valid_predicate_indicator'(@term, -atom, -integer)
%
% valid predicate indicator

'$lgt_valid_predicate_indicator'(Functor/Arity, Functor, Arity) :-
	atom(Functor),
	integer(Arity),
	Arity >= 0.



% '$lgt_valid_non_terminal_indicator'(@term, -atom, -integer, -integer)
%
% valid grammar rule non-terminal indicator; the last argument is the
% arity of the corresponding predicate

'$lgt_valid_non_terminal_indicator'(Functor//Arity, Functor, Arity, ExtArity) :-
	atom(Functor),
	integer(Arity),
	Arity >= 0,
	ExtArity is Arity + 2.



% '$lgt_valid_predicate_or_non_terminal_indicator'(@term, -atom, -integer)
%
% valid predicate indicator or grammar rule indicator

'$lgt_valid_predicate_or_non_terminal_indicator'(Functor/Arity, Functor, Arity) :-
	atom(Functor),
	integer(Arity),
	Arity >= 0.

'$lgt_valid_predicate_or_non_terminal_indicator'(Functor//Arity, Functor, Arity) :-
	atom(Functor),
	integer(Arity),
	Arity >= 0.



% '$lgt_valid_info_key_value_pair'(@term, -atom, -integer)
%
% valid info/1-2 key-value pair

'$lgt_valid_info_key_value_pair'(Key is Value, Key, Value) :-
	atom(Key),
	nonvar(Value).



% '$lgt_check_entity_reference'(+atom, @term, -atom, -entity_identifier)

'$lgt_check_entity_reference'(object, Ref, Scope, Object) :-
	(	Ref = Scope::Object ->
		'$lgt_check'(scope, Scope),
		'$lgt_check'(object_identifier, Object)
	;	Ref = Object,
		Scope = (public),
		'$lgt_check'(object_identifier, Object)
	).

'$lgt_check_entity_reference'(protocol, Ref, Scope, Protocol) :-
	(	Ref = Scope::Protocol ->
		'$lgt_check'(scope, Scope),
		'$lgt_check'(protocol_identifier, Protocol)
	;	Ref = Protocol,
		Scope = (public),
		'$lgt_check'(protocol_identifier, Protocol)
	).

'$lgt_check_entity_reference'(category, Ref, Scope, Category) :-
	(	Ref = Scope::Category ->
		'$lgt_check'(scope, Scope),
		'$lgt_check'(category_identifier, Category)
	;	Ref = Category,
		Scope = (public),
		'$lgt_check'(category_identifier, Category)
	).



% '$lgt_check_closure'(@nonvar, @compilation_context)
%
% checks that a closure meta-argument is valid

'$lgt_check_closure'(Closure, _) :-
	var(Closure),
	!.

'$lgt_check_closure'(Free/Goal, Ctx) :-
	!,
	'$lgt_check_lambda_expression'(Free/Goal, Ctx).

'$lgt_check_closure'(Parameters>>Goal, Ctx) :-
	!,
	'$lgt_check_lambda_expression'(Parameters>>Goal, Ctx).

'$lgt_check_closure'({Closure}, _) :-
	!,
	'$lgt_check'(var_or_callable, Closure).

'$lgt_check_closure'(Object::Closure, _) :-
	!,
	'$lgt_check'(var_or_object_identifier, Object),
	'$lgt_check'(var_or_callable, Closure).

'$lgt_check_closure'(::Closure, _) :-
	!,
	'$lgt_check'(var_or_callable, Closure).

'$lgt_check_closure'(^^Closure, _) :-
	!,
	'$lgt_check'(var_or_callable, Closure).

'$lgt_check_closure'(Object<<Closure, _) :-
	!,
	'$lgt_check'(var_or_object_identifier, Object),
	'$lgt_check'(var_or_callable, Closure).

'$lgt_check_closure'(':'(Module, Closure), _) :-
	!,
	'$lgt_check'(var_or_module_identifier, Module),
	'$lgt_check'(var_or_callable, Closure).

'$lgt_check_closure'(Closure, _) :-
	\+ callable(Closure),
	throw(type_error(callable, Closure)).

'$lgt_check_closure'(_, _).



% '$lgt_check_lambda_expression'(@nonvar, @compilation_context)
%
% checks that a lambda expression is valid

'$lgt_check_lambda_expression'(Free/Parameters>>Goal, Ctx) :-
	!,
	% first, check for errors
	'$lgt_check'(var_or_curly_bracketed_term, Free),
	'$lgt_check'(list_or_partial_list, Parameters),
	'$lgt_check'(var_or_callable, Goal),
	% second, check for likely errors if compiling a source file
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		nonvar(Free),
		nonvar(Parameters),
		nonvar(Goal) ->
		'$lgt_check_lambda_expression_parameters'(Parameters, Free/Parameters>>Goal, Ctx),
		'$lgt_check_lambda_expression_unclassified_variables'(Free/Parameters>>Goal, Ctx),
		'$lgt_check_lambda_expression_mixed_up_variables'(Free/Parameters>>Goal, Ctx)
	;	true
	).

'$lgt_check_lambda_expression'(Free/Goal, Ctx) :-
	'$lgt_check'(var_or_curly_bracketed_term, Free),
	'$lgt_check'(var_or_callable, Goal),
	% second, check for likely errors if compiling a source file
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		nonvar(Free),
		nonvar(Goal) ->
		'$lgt_check_lambda_expression_unclassified_variables'(Free/Goal, Ctx)
	;	true
	).

'$lgt_check_lambda_expression'(Parameters>>Goal, Ctx) :-
	% first, check for errors
	'$lgt_check'(list_or_partial_list, Parameters),
	'$lgt_check'(var_or_callable, Goal),
	% second, check for likely errors if compiling a source file
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		nonvar(Parameters),
		nonvar(Goal) ->
		'$lgt_check_lambda_expression_parameters'(Parameters, Parameters>>Goal, Ctx),
		'$lgt_check_lambda_expression_unclassified_variables'(Parameters>>Goal, Ctx)
	;	true
	).


% '$lgt_check_lambda_expression_parameters'(@list, @callable, +compilation_context)

'$lgt_check_lambda_expression_parameters'(Parameters, Lambda, Ctx) :-
	term_variables(Parameters, Variables),
	'$lgt_comp_ctx_term'(Ctx, Clause),
	'$lgt_check_lambda_expression_parameter_variables'(Variables, Lambda, Clause, Ctx).


'$lgt_check_lambda_expression_parameter_variables'([], _, _, _).

'$lgt_check_lambda_expression_parameter_variables'([Variable| Variables], Lambda, Clause, Ctx) :-
	'$lgt_check_lambda_expression_parameter_variable'(Variable, Lambda, Clause, Ctx),
	'$lgt_check_lambda_expression_parameter_variables'(Variables, Lambda, Clause, Ctx).


'$lgt_check_lambda_expression_parameter_variable'(Variable, Lambda, Clause, _) :-
	'$lgt_count_variable_occurrences'(Lambda, Variable, InLambda),
	'$lgt_count_variable_occurrences'(Clause, Variable, InClause),
	(	InClause > InLambda,
		'$lgt_compiler_flag'(lambda_variables, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(lambda_variables),
			parameter_variable_used_elsewhere(File, Lines, Type, Entity, Lambda, Variable)
		)
	;	true
	).


'$lgt_contains_variable'(Term, Variable) :-
	Term == Variable.

'$lgt_contains_variable'(Term, Variable) :-
	compound(Term),
	functor(Term, _, Arity),
	'$lgt_between'(1, Arity, N),
	arg(N, Term, Argument),
	'$lgt_contains_variable'(Argument, Variable).


'$lgt_count_variable_occurrences'(Term, Variable, N) :-
	findall(1, '$lgt_contains_variable'(Term, Variable), L),
	'$lgt_length'(L, 0, N).


% each lambda goal variable should be either a lambda free variable or a lambda parameter

'$lgt_check_lambda_expression_unclassified_variables'(Free/Goal, Ctx) :-
	% take into account currying to avoid false positives
	'$lgt_check_lambda_expression_goal_variables'(Goal, GoalVars, Ctx),
	term_variables(Free, FreeVars),
	'$lgt_var_subtract'(GoalVars, FreeVars, UnclassifiedVars0),
	(	UnclassifiedVars0 \== [],
		'$lgt_compiler_flag'(lambda_variables, warning),
		% reinstate relation between term variables and their names
		'$lgt_comp_ctx_term'(Ctx, OriginalTerm),
		'$lgt_pp_term_source_data_'(OriginalTerm, VariableNames, Singletons, _, _),
		% ignore singleton (and anonymous) variables
		'$lgt_filter_singleton_variables'(UnclassifiedVars0, VariableNames, Singletons, UnclassifiedVars),
		UnclassifiedVars \== [] ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(lambda_variables),
			unclassified_variables_in_lambda_expression(File, Lines, Type, Entity, UnclassifiedVars, Free/Goal)
		)
	;	true
	).

'$lgt_check_lambda_expression_unclassified_variables'(Parameters>>Goal, Ctx) :-
	% take into account currying to avoid false positives
	'$lgt_check_lambda_expression_goal_variables'(Goal, GoalVars, Ctx),
	term_variables(Parameters, ParameterVars),
	'$lgt_var_subtract'(GoalVars, ParameterVars, UnclassifiedVars),
	(	UnclassifiedVars \== [],
		'$lgt_compiler_flag'(lambda_variables, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(lambda_variables),
			unclassified_variables_in_lambda_expression(File, Lines, Type, Entity, UnclassifiedVars, Parameters>>Goal)
		)
	;	true
	).


'$lgt_filter_singleton_variables'([], _, _, []).

'$lgt_filter_singleton_variables'([UnclassifiedVar0| UnclassifiedVars0], VariableNames, Singletons, [UnclassifiedVar0| UnclassifiedVars]) :-
	'$lgt_member'(_Name0=Variable0, VariableNames),
	Variable0 == UnclassifiedVar0,
	\+ (
		'$lgt_member'(Name1=Variable1, Singletons),
		Variable1 == UnclassifiedVar0,
		% parameter variables may be singletons in the clause but still need to be classified
		\+ '$lgt_parameter_variable_name'(Name1)
	),
	!,
	'$lgt_filter_singleton_variables'(UnclassifiedVars0, VariableNames, Singletons, UnclassifiedVars).

'$lgt_filter_singleton_variables'([_| UnclassifiedVars0], VariableNames, Singletons, UnclassifiedVars) :-
	'$lgt_filter_singleton_variables'(UnclassifiedVars0, VariableNames, Singletons, UnclassifiedVars).


'$lgt_check_lambda_expression_goal_variables'(Parameters>>Goal, UnqualifiedVars, Ctx) :-
	!,
	'$lgt_check_lambda_expression_goal_variables'(Goal, GoalVars, Ctx),
	term_variables(Parameters, ParameterVars),
	'$lgt_var_subtract'(GoalVars, ParameterVars, UnqualifiedVars).

'$lgt_check_lambda_expression_goal_variables'(Goal, UnqualifiedVars, Ctx) :-
	'$lgt_check_closure'(Goal, Ctx),
	term_variables(Goal, UnqualifiedVars).



% no lambda goal variable should be both a lambda free variable and a lambda parameter

'$lgt_check_lambda_expression_mixed_up_variables'(Free/Parameters>>Goal, _) :-
	term_variables(Free, FreeVars),
	term_variables(Parameters, ParameterVars),
	'$lgt_intersection'(FreeVars, ParameterVars, MixedUpVars),
	(	MixedUpVars \== [],
		'$lgt_compiler_flag'(lambda_variables, warning) ->
		'$lgt_source_file_context'(File, Lines, Type, Entity),
		'$lgt_increment_compiling_warnings_counter',
		'$lgt_print_message'(
			warning(lambda_variables),
			variables_with_dual_role_in_lambda_expression(File, Lines, Type, Entity, MixedUpVars, Free/Parameters>>Goal)
		)
	;	true
	).



% '$lgt_same_operator_class'(+atom, +atom)
%
% this utility predicate is used when defining new operators using op/3
% in order to know if there's an operator of the same class that should
% be backed up

'$lgt_same_operator_class'(fx, fx).
'$lgt_same_operator_class'(fx, fy).

'$lgt_same_operator_class'(fy, fx).
'$lgt_same_operator_class'(fy, fy).

'$lgt_same_operator_class'(xf, xf).
'$lgt_same_operator_class'(xf, yf).

'$lgt_same_operator_class'(yf, xf).
'$lgt_same_operator_class'(yf, yf).

'$lgt_same_operator_class'(xfx, xfx).
'$lgt_same_operator_class'(xfx, xfy).
'$lgt_same_operator_class'(xfx, yfx).

'$lgt_same_operator_class'(xfy, xfx).
'$lgt_same_operator_class'(xfy, xfy).
'$lgt_same_operator_class'(xfy, yfx).

'$lgt_same_operator_class'(yfx, xfx).
'$lgt_same_operator_class'(yfx, xfy).
'$lgt_same_operator_class'(yfx, yfx).



% '$lgt_valid_meta_predicate_template'(@term)

'$lgt_valid_meta_predicate_template'(Pred) :-
	callable(Pred),
	Pred =.. [_| Args],
	'$lgt_valid_meta_predicate_template_args'(Args).


'$lgt_valid_meta_predicate_template_args'([]).

'$lgt_valid_meta_predicate_template_args'([Arg| Args]) :-
	ground(Arg),
	'$lgt_valid_meta_predicate_template_arg'(Arg),
	'$lgt_valid_meta_predicate_template_args'(Args).


% meta-argument but not called
'$lgt_valid_meta_predicate_template_arg'((::)) :- !.
% non meta-argument
'$lgt_valid_meta_predicate_template_arg'(*) :- !.
% goal with possible existential variables qualification
'$lgt_valid_meta_predicate_template_arg'(^) :- !.
% goal or closure
'$lgt_valid_meta_predicate_template_arg'(Arg) :-
	integer(Arg), Arg >= 0.



% '$lgt_valid_mode_template'(@nonvar)

'$lgt_valid_mode_template'(Pred) :-
	Pred =.. [_| Args],
	'$lgt_valid_mode_template_args'(Args).


'$lgt_valid_mode_template_args'([]).

'$lgt_valid_mode_template_args'([Arg| Args]) :-
	(	ground(Arg) ->
		'$lgt_valid_mode_template_arg'(Arg)
	;	throw(instantiation_error)
	),
	'$lgt_valid_mode_template_args'(Args).



% '$lgt_valid_mode_template_arg'(@nonvar)

% unspecified argument, can be input, output, or both input and output
'$lgt_valid_mode_template_arg'((?)).
'$lgt_valid_mode_template_arg'('?'(_)).
% instantiated argument on predicate call, can be further instantiated by the predicate call
'$lgt_valid_mode_template_arg'((+)).
'$lgt_valid_mode_template_arg'('+'(_)).
% non-instantiated argument (i.e., a variable) on predicate call
'$lgt_valid_mode_template_arg'((-)).
'$lgt_valid_mode_template_arg'('-'(_)).
% not modified argument (i.e., not further instantiated) by the predicate call
'$lgt_valid_mode_template_arg'((@)).
'$lgt_valid_mode_template_arg'('@'(_)).
% ground argument
'$lgt_valid_mode_template_arg'((++)).
'$lgt_valid_mode_template_arg'('++'(_)).
% unbound argument
'$lgt_valid_mode_template_arg'((--)).
'$lgt_valid_mode_template_arg'('--'(_)).



% '$lgt_valid_number_of_proofs'(@nonvar)

% calling the predicate using the specified mode always fails
'$lgt_valid_number_of_proofs'(zero).
% calling the predicate using the specified mode always succeeds once
'$lgt_valid_number_of_proofs'(one).
% calling the predicate using the specified mode may succeed once or fail
'$lgt_valid_number_of_proofs'(zero_or_one).
% calling the predicate using the specified mode may fail or succeed multiple times
'$lgt_valid_number_of_proofs'(zero_or_more).
% calling the predicate using the specified mode always succeed at least once
'$lgt_valid_number_of_proofs'(one_or_more).
% calling the predicate using the specified mode either succeeds once or throws an error
'$lgt_valid_number_of_proofs'(zero_or_error).
% calling the predicate using the specified mode either fails or throws an error
'$lgt_valid_number_of_proofs'(one_or_error).
% calling the predicate using the specified mode either succeeds once or fails or throws an error
'$lgt_valid_number_of_proofs'(zero_or_one_or_error).
% calling the predicate using the specified mode may fail or succeed multiple times or throw an error
'$lgt_valid_number_of_proofs'(zero_or_more_or_error).
% calling the predicate using the specified mode may succeed one or more times or throw an error
'$lgt_valid_number_of_proofs'(one_or_more_or_error).
% calling the predicate using the specified mode throws an error
'$lgt_valid_number_of_proofs'(error).



% '$lgt_valid_predicate_property'(@nonvar)

% predicate scope (public, protected, or private)
'$lgt_valid_predicate_property'(scope(_)).
% public predicate
'$lgt_valid_predicate_property'((public)).
% protected predicate
'$lgt_valid_predicate_property'(protected).
% private predicate
'$lgt_valid_predicate_property'((private)).
% dynamic predicate
'$lgt_valid_predicate_property'((dynamic)).
% static predicate
'$lgt_valid_predicate_property'(static).
% predicate is defined in Logtalk source code
'$lgt_valid_predicate_property'(logtalk).
% predicate is defined in Prolog source code
'$lgt_valid_predicate_property'(prolog).
% predicate is defined in foreign source code (e.g., C)
'$lgt_valid_predicate_property'(foreign).

% entity containing the predicate scope directive
'$lgt_valid_predicate_property'(declared_in(_)).
% object or category containing the predicate definition
'$lgt_valid_predicate_property'(defined_in(_)).
% object or category containing the inherited but overridden predicate definition
'$lgt_valid_predicate_property'(redefined_from(_)).
% meta-predicate template
'$lgt_valid_predicate_property'(meta_predicate(_)).
% coinductive predicate template
'$lgt_valid_predicate_property'(coinductive(_)).
% built-in predicate
'$lgt_valid_predicate_property'(built_in).
% predicate is an alias of another predicate
'$lgt_valid_predicate_property'(alias_of(_)).
% entity where the predicate alias is declared
'$lgt_valid_predicate_property'(alias_declared_in(_)).
% clauses for the predicate can be defined within multiple entities
'$lgt_valid_predicate_property'((multifile)).
% predicate version of a non-terminal
'$lgt_valid_predicate_property'(non_terminal(_)).
% calls to the predicate are synchronized
'$lgt_valid_predicate_property'(synchronized).

% the remaining properties are available only when the entities are compiled with the "source_data" flag turned on

% mode/2 predicate information (predicates can have more than one mode)
'$lgt_valid_predicate_property'(mode(_, _)).
% info/2 predicate information
'$lgt_valid_predicate_property'(info(_)).
% number of predicate clauses
'$lgt_valid_predicate_property'(number_of_clauses(_)).
% number of predicate rules
'$lgt_valid_predicate_property'(number_of_rules(_)).
% entity containing the predicate scope directive plus declaration line
'$lgt_valid_predicate_property'(declared_in(_, _)).
% object or category containing the predicate definition plus definition line
'$lgt_valid_predicate_property'(defined_in(_, _)).
% object or category containing the inherited but overridden predicate definition plus definition line
'$lgt_valid_predicate_property'(redefined_from(_, _)).
% entity where the predicate alias is declared plus declaration line
'$lgt_valid_predicate_property'(alias_declared_in(_, _)).
% predicate is an auxiliary predicate
'$lgt_valid_predicate_property'(auxiliary).
% predicate definition is inlined
'$lgt_valid_predicate_property'(inline).
% predicate definition is recursive
'$lgt_valid_predicate_property'(recursive).



% '$lgt_valid_protocol_property'(@nonvar)

% built-in entity
'$lgt_valid_protocol_property'(built_in).
% dynamic entity (can be abolished at runtime)
'$lgt_valid_protocol_property'((dynamic)).
% static entity
'$lgt_valid_protocol_property'(static).
% entity compiled in debug mode
'$lgt_valid_protocol_property'(debugging).
% list of predicate indicators of public predicates declared in the entity
'$lgt_valid_protocol_property'(public(_)).
% list of predicate indicators of protected predicates declared in the entity
'$lgt_valid_protocol_property'(protected(_)).
% list of predicate indicators of private predicates declared in the entity
'$lgt_valid_protocol_property'(private(_)).
% list of declaration properties for a predicate declared in the entity
'$lgt_valid_protocol_property'(declares(_, _)).
% list of properties for a predicate alias declared in the entity
'$lgt_valid_protocol_property'(alias(_, _)).
% source data available for the entity
'$lgt_valid_protocol_property'(source_data).

% the remaining properties are available only when the entities are compiled with the "source_data" flag turned on

% list of pairs with user-defined protocol documentation
'$lgt_valid_protocol_property'(info(_)).
% source file absolute path
'$lgt_valid_protocol_property'(file(_)).
% source file basename and directory
'$lgt_valid_protocol_property'(file(_, _)).
% start and end lines in a source file
'$lgt_valid_protocol_property'(lines(_, _)).
% start and end lines in a source file of the entity opening directive
'$lgt_valid_protocol_property'(directive(_, _)).



% '$lgt_valid_category_property'(@nonvar)

% category properties include all protocol properties
'$lgt_valid_category_property'(Property) :-
	'$lgt_valid_protocol_property'(Property), !.
% messages sent from the object using the ::/2 control construct generate events
'$lgt_valid_category_property'(events).
% list of definition properties for a predicate defined in the category
'$lgt_valid_category_property'(defines(_, _)).
% list of definition properties for a multifile predicate defined in contributing entities
'$lgt_valid_category_property'(includes(_, _, _)).
% list of definition properties for a multifile predicate defined for other entities
'$lgt_valid_category_property'(provides(_, _, _)).
% list of calling properties for a predicate called in the entity
'$lgt_valid_category_property'(calls(_, _)).
% list of updating properties for a dynamic predicate updated in the entity
'$lgt_valid_category_property'(updates(_, _)).
% number of predicate clauses (including both user-defined and auxiliary clauses)
'$lgt_valid_category_property'(number_of_clauses(_)).
% number of predicate rules (including both user-defined and auxiliary clauses)
'$lgt_valid_category_property'(number_of_rules(_)).
% number of user-defined predicate clauses
'$lgt_valid_category_property'(number_of_user_clauses(_)).
% number of user-defined predicate rules
'$lgt_valid_category_property'(number_of_user_rules(_)).



% '$lgt_valid_object_property'(@nonvar)

% object properties include all category and protocol properties
'$lgt_valid_object_property'(Property) :-
	'$lgt_valid_category_property'(Property), !.
% object contains calls to the built-in multi-threading predicates
'$lgt_valid_object_property'(threaded).
% object allows the use of the <</2 debugging control construct
'$lgt_valid_object_property'(context_switching_calls).
% object supports dynamic declaration of new predicates
'$lgt_valid_object_property'(dynamic_declarations).
% object can be complemented by categories (old, deprecated, Logtalk 2.x property)
'$lgt_valid_object_property'(complements).
% object can be complemented by categories
'$lgt_valid_object_property'(complements(_)).
% object resulted from the compilation of a Prolog module
'$lgt_valid_object_property'(module).



% '$lgt_valid_flag'(@nonvar)
%
% true if the argument is a valid Logtalk flag name

% meta-lint compilation flag
'$lgt_valid_flag'(linter).
% lint compilation flags
'$lgt_valid_flag'(always_true_or_false_goals).
'$lgt_valid_flag'(arithmetic_expressions).
'$lgt_valid_flag'(catchall_catch).
'$lgt_valid_flag'(conditionals).
'$lgt_valid_flag'(deprecated).
'$lgt_valid_flag'(disjunctions).
'$lgt_valid_flag'(duplicated_clauses).
'$lgt_valid_flag'(duplicated_directives).
'$lgt_valid_flag'(encodings).
'$lgt_valid_flag'(general).
'$lgt_valid_flag'(grammar_rules).
'$lgt_valid_flag'(lambda_variables).
'$lgt_valid_flag'(left_recursion).
'$lgt_valid_flag'(missing_directives).
'$lgt_valid_flag'(naming).
'$lgt_valid_flag'(portability).
'$lgt_valid_flag'(redefined_built_ins).
'$lgt_valid_flag'(redefined_operators).
'$lgt_valid_flag'(singleton_variables).
'$lgt_valid_flag'(steadfastness).
'$lgt_valid_flag'(suspicious_calls).
'$lgt_valid_flag'(tail_recursive).
'$lgt_valid_flag'(trivial_goal_fails).
'$lgt_valid_flag'(undefined_predicates).
'$lgt_valid_flag'(unknown_entities).
'$lgt_valid_flag'(unknown_predicates).
% optional features compilation flags
'$lgt_valid_flag'(complements).
'$lgt_valid_flag'(context_switching_calls).
'$lgt_valid_flag'(dynamic_declarations).
'$lgt_valid_flag'(events).
% other compilation flags
'$lgt_valid_flag'(clean).
'$lgt_valid_flag'(code_prefix).
'$lgt_valid_flag'(debug).
'$lgt_valid_flag'(hook).
'$lgt_valid_flag'(optimize).
'$lgt_valid_flag'(relative_to).
'$lgt_valid_flag'(reload).
'$lgt_valid_flag'(report).
'$lgt_valid_flag'(scratch_directory).
'$lgt_valid_flag'(source_data).
% read-only compilation flags
'$lgt_valid_flag'(version_data).
% startup flags
'$lgt_valid_flag'(settings_file).
% backend Prolog compiler information
'$lgt_valid_flag'(prolog_compatible_version).
'$lgt_valid_flag'(prolog_dialect).
'$lgt_valid_flag'(prolog_version).
'$lgt_valid_flag'(underscore_variables).
% features requiring specific backend Prolog compiler support
'$lgt_valid_flag'(coinduction).
'$lgt_valid_flag'(encoding_directive).
'$lgt_valid_flag'(engines).
'$lgt_valid_flag'(modules).
'$lgt_valid_flag'(tabling).
'$lgt_valid_flag'(threads).
'$lgt_valid_flag'(unicode).
% backend Prolog compiler and loader options
'$lgt_valid_flag'(prolog_compiler).
'$lgt_valid_flag'(prolog_loader).
% internal flags
'$lgt_valid_flag'('$relative_to').



% '$lgt_read_only_flag'(@nonvar)
%
% true if the argument is a read only Logtalk flag name

% Logtalk version flag
'$lgt_read_only_flag'(version_data).
% startup flags
'$lgt_read_only_flag'(settings_file).
% backend Prolog compiler features
'$lgt_read_only_flag'(coinduction).
'$lgt_read_only_flag'(encoding_directive).
'$lgt_read_only_flag'(engines).
'$lgt_read_only_flag'(modules).
'$lgt_read_only_flag'(prolog_compatible_version).
'$lgt_read_only_flag'(prolog_dialect).
'$lgt_read_only_flag'(prolog_version).
'$lgt_read_only_flag'(tabling).
'$lgt_read_only_flag'(threads).
'$lgt_read_only_flag'(unicode).



% '$lgt_valid_flag_value'(@atom, @nonvar)

% meta-lint compilation flag

'$lgt_valid_flag_value'(linter, on) :- !.
'$lgt_valid_flag_value'(linter, default) :- !.
'$lgt_valid_flag_value'(linter, off) :- !.

% lint compilation flags

'$lgt_valid_flag_value'(always_true_or_false_goals, silent) :- !.
'$lgt_valid_flag_value'(always_true_or_false_goals, warning) :- !.

'$lgt_valid_flag_value'(arithmetic_expressions, silent) :- !.
'$lgt_valid_flag_value'(arithmetic_expressions, warning) :- !.

'$lgt_valid_flag_value'(catchall_catch, silent) :- !.
'$lgt_valid_flag_value'(catchall_catch, warning) :- !.

'$lgt_valid_flag_value'(conditionals, silent) :- !.
'$lgt_valid_flag_value'(conditionals, warning) :- !.

'$lgt_valid_flag_value'(deprecated, silent) :- !.
'$lgt_valid_flag_value'(deprecated, warning) :- !.

'$lgt_valid_flag_value'(disjunctions, silent) :- !.
'$lgt_valid_flag_value'(disjunctions, warning) :- !.

'$lgt_valid_flag_value'(duplicated_clauses, silent) :- !.
'$lgt_valid_flag_value'(duplicated_clauses, warning) :- !.

'$lgt_valid_flag_value'(duplicated_directives, silent) :- !.
'$lgt_valid_flag_value'(duplicated_directives, warning) :- !.

'$lgt_valid_flag_value'(encodings, silent) :- !.
'$lgt_valid_flag_value'(encodings, warning) :- !.

'$lgt_valid_flag_value'(general, silent) :- !.
'$lgt_valid_flag_value'(general, warning) :- !.

'$lgt_valid_flag_value'(grammar_rules, silent) :- !.
'$lgt_valid_flag_value'(grammar_rules, warning) :- !.

'$lgt_valid_flag_value'(lambda_variables, silent) :- !.
'$lgt_valid_flag_value'(lambda_variables, warning) :- !.

'$lgt_valid_flag_value'(left_recursion, silent) :- !.
'$lgt_valid_flag_value'(left_recursion, warning) :- !.

'$lgt_valid_flag_value'(missing_directives, silent) :- !.
'$lgt_valid_flag_value'(missing_directives, warning) :- !.

'$lgt_valid_flag_value'(naming, silent) :- !.
'$lgt_valid_flag_value'(naming, warning) :- !.

'$lgt_valid_flag_value'(portability, silent) :- !.
'$lgt_valid_flag_value'(portability, warning) :- !.

'$lgt_valid_flag_value'(redefined_built_ins, silent) :- !.
'$lgt_valid_flag_value'(redefined_built_ins, warning) :- !.

'$lgt_valid_flag_value'(redefined_operators, silent) :- !.
'$lgt_valid_flag_value'(redefined_operators, warning) :- !.

'$lgt_valid_flag_value'(singleton_variables, silent) :- !.
'$lgt_valid_flag_value'(singleton_variables, warning) :- !.

'$lgt_valid_flag_value'(steadfastness, silent) :- !.
'$lgt_valid_flag_value'(steadfastness, warning) :- !.

'$lgt_valid_flag_value'(suspicious_calls, silent) :- !.
'$lgt_valid_flag_value'(suspicious_calls, warning) :- !.

'$lgt_valid_flag_value'(tail_recursive, silent) :- !.
'$lgt_valid_flag_value'(tail_recursive, warning) :- !.

'$lgt_valid_flag_value'(trivial_goal_fails, silent) :- !.
'$lgt_valid_flag_value'(trivial_goal_fails, warning) :- !.

'$lgt_valid_flag_value'(underscore_variables, dont_care) :- !.
'$lgt_valid_flag_value'(underscore_variables, singletons) :- !.

'$lgt_valid_flag_value'(undefined_predicates, silent) :- !.
'$lgt_valid_flag_value'(undefined_predicates, warning) :- !.
'$lgt_valid_flag_value'(undefined_predicates, error) :- !.

'$lgt_valid_flag_value'(unknown_entities, silent) :- !.
'$lgt_valid_flag_value'(unknown_entities, warning) :- !.

'$lgt_valid_flag_value'(unknown_predicates, silent) :- !.
'$lgt_valid_flag_value'(unknown_predicates, warning) :- !.
'$lgt_valid_flag_value'(unknown_predicates, error) :- !.

% optional features compilation flags

'$lgt_valid_flag_value'(complements, allow) :- !.
'$lgt_valid_flag_value'(complements, restrict) :- !.
'$lgt_valid_flag_value'(complements, deny) :- !.

'$lgt_valid_flag_value'(context_switching_calls, allow) :- !.
'$lgt_valid_flag_value'(context_switching_calls, deny) :- !.

'$lgt_valid_flag_value'(dynamic_declarations, allow) :- !.
'$lgt_valid_flag_value'(dynamic_declarations, deny) :- !.

'$lgt_valid_flag_value'(events, allow) :- !.
'$lgt_valid_flag_value'(events, deny) :- !.

% other compilation flags

'$lgt_valid_flag_value'(clean, on) :- !.
'$lgt_valid_flag_value'(clean, off) :- !.

'$lgt_valid_flag_value'(code_prefix, Prefix) :-
	atom(Prefix),
	atom_length(Prefix, 1).

'$lgt_valid_flag_value'(debug, on) :- !.
'$lgt_valid_flag_value'(debug, off) :- !.

'$lgt_valid_flag_value'(hook, Obj) :-
	callable(Obj).

'$lgt_valid_flag_value'(optimize, on) :- !.
'$lgt_valid_flag_value'(optimize, off) :- !.

'$lgt_valid_flag_value'(relative_to, Directory) :-
	atom(Directory).

'$lgt_valid_flag_value'(reload, always) :- !.
'$lgt_valid_flag_value'(reload, changed) :- !.
'$lgt_valid_flag_value'(reload, skip) :- !.

'$lgt_valid_flag_value'(report, on) :- !.
'$lgt_valid_flag_value'(report, warnings) :- !.
'$lgt_valid_flag_value'(report, off) :- !.

'$lgt_valid_flag_value'(scratch_directory, Directory) :-
	callable(Directory).

'$lgt_valid_flag_value'(source_data, on) :- !.
'$lgt_valid_flag_value'(source_data, off) :- !.

% internal flags

'$lgt_valid_flag_value'('$relative_to', Directory) :-
	% internal flag; just for documenting value type
	atom(Directory).

% backend Prolog compiler and loader options

'$lgt_valid_flag_value'(prolog_compiler, Options) :-
	'$lgt_is_list'(Options).
'$lgt_valid_flag_value'(prolog_loader, Options) :-
	'$lgt_is_list'(Options).

% Logtalk version flag

'$lgt_valid_flag_value'(version_data, Version) :-
	compound(Version),
	functor(Version, logtalk, 4).

% startup flags

'$lgt_valid_flag_value'(settings_file, allow) :- !.
'$lgt_valid_flag_value'(settings_file, restrict) :- !.
'$lgt_valid_flag_value'(settings_file, deny) :- !.

'$lgt_valid_flag_value'(prolog_dialect, Dialect) :-
	atom(Dialect).
'$lgt_valid_flag_value'(prolog_version, Version) :-
	compound(Version),
	functor(Version, v, 3).
'$lgt_valid_flag_value'(prolog_compatible_version, Version) :-
	compound(Version),
	functor(Version, v, 3).

% backend Prolog compiler features

'$lgt_valid_flag_value'(coinduction, supported) :- !.
'$lgt_valid_flag_value'(coinduction, unsupported) :- !.

'$lgt_valid_flag_value'(encoding_directive, full) :- !.
'$lgt_valid_flag_value'(encoding_directive, source) :- !.
'$lgt_valid_flag_value'(encoding_directive, unsupported) :- !.

'$lgt_valid_flag_value'(engines, supported) :- !.
'$lgt_valid_flag_value'(engines, unsupported) :- !.

'$lgt_valid_flag_value'(modules, supported) :- !.
'$lgt_valid_flag_value'(modules, unsupported) :- !.

'$lgt_valid_flag_value'(tabling, supported) :- !.
'$lgt_valid_flag_value'(tabling, unsupported) :- !.

'$lgt_valid_flag_value'(threads, supported) :- !.
'$lgt_valid_flag_value'(threads, unsupported) :- !.

'$lgt_valid_flag_value'(unicode, full) :- !.
'$lgt_valid_flag_value'(unicode, bmp) :- !.
'$lgt_valid_flag_value'(unicode, unsupported) :- !.



% '$lgt_linter_flag'(?atom)

'$lgt_linter_flag'(always_true_or_false_goals).
'$lgt_linter_flag'(arithmetic_expressions).
'$lgt_linter_flag'(catchall_catch).
'$lgt_linter_flag'(conditionals).
'$lgt_linter_flag'(deprecated).
'$lgt_linter_flag'(disjunctions).
'$lgt_linter_flag'(duplicated_clauses).
'$lgt_linter_flag'(duplicated_directives).
'$lgt_linter_flag'(encodings).
'$lgt_linter_flag'(general).
'$lgt_linter_flag'(grammar_rules).
'$lgt_linter_flag'(lambda_variables).
'$lgt_linter_flag'(left_recursion).
'$lgt_linter_flag'(missing_directives).
'$lgt_linter_flag'(naming).
'$lgt_linter_flag'(portability).
'$lgt_linter_flag'(redefined_built_ins).
'$lgt_linter_flag'(redefined_operators).
'$lgt_linter_flag'(singleton_variables).
'$lgt_linter_flag'(steadfastness).
'$lgt_linter_flag'(suspicious_calls).
'$lgt_linter_flag'(tail_recursive).
'$lgt_linter_flag'(trivial_goal_fails).
'$lgt_linter_flag'(undefined_predicates).
'$lgt_linter_flag'(unknown_entities).
'$lgt_linter_flag'(unknown_predicates).



% '$lgt_valid_remark'(@term)
%
% valid predicate remark documentation on info/1-2 directives

'$lgt_valid_remark'(Topic - Text) :-
	atom(Topic),
	atom(Text).



% '$lgt_valid_predicate_allocation'(@nonvar)
%
% valid predicate allocation on info/2 directive

% predicate defined in the object containing its scope directive
'$lgt_valid_predicate_allocation'(container).
% predicate should be defined in the descendant objects
'$lgt_valid_predicate_allocation'(descendants).
% predicate should be defined in the class instances
'$lgt_valid_predicate_allocation'(instances).
% predicate should be defined in the class and its subclasses
'$lgt_valid_predicate_allocation'(classes).
% predicate should be defined in the class subclasses
'$lgt_valid_predicate_allocation'(subclasses).
% no restrictions on where the predicate should be defined
'$lgt_valid_predicate_allocation'(any).



% '$lgt_valid_predicate_redefinition'(@nonvar)
%
% valid predicate redefinition on info/2 directive

% predicate should not be redefined
'$lgt_valid_predicate_redefinition'(never).
% predicate can be freely redefined
'$lgt_valid_predicate_redefinition'(free).
% predicate redefinition must call the inherited definition
'$lgt_valid_predicate_redefinition'(specialize).
% predicate redefinition must call the inherited definition as the first body goal
'$lgt_valid_predicate_redefinition'(call_super_first).
% predicate redefinition must call the inherited definition as the last body goal
'$lgt_valid_predicate_redefinition'(call_super_last).



% '$lgt_valid_predicate_exception'(@term)
%
% valid predicate exception documentation on info/2 directive

'$lgt_valid_predicate_exception'(Description - Term) :-
	atom(Description),
	nonvar(Term).



% '$lgt_valid_predicate_call_example'(@term, +atom, +integer)
%
% valid predicate call example documentation on info/2 directive

'$lgt_valid_predicate_call_example'((Description - Call - {Bindings}), Functor, Arity) :-
	atom(Description),
	nonvar(Call),
	functor(Pred, Functor, Arity),
	Call = Pred,
	nonvar(Bindings),
	(	Bindings == no -> true
	;	Bindings == yes -> true
	;	Bindings == false -> true
	;	Bindings == true -> true
	;	'$lgt_valid_example_var_bindings'(Bindings)
	).


'$lgt_valid_example_var_bindings'((Binding, Bindings)) :-
	!,
	'$lgt_valid_example_var_binding'(Binding),
	'$lgt_valid_example_var_bindings'(Bindings).

'$lgt_valid_example_var_bindings'(Binding) :-
	'$lgt_valid_example_var_binding'(Binding).


'$lgt_valid_example_var_binding'(Binding) :-
	nonvar(Binding),
	Binding = (Var = _),
	var(Var).



% Logtalk built-in predicates
%
% '$lgt_logtalk_built_in_predicate'(?callable, ?callable)
%
% the second argument is either a meta-predicate template
% (when aplicable) or the atom "no"


% message-sending and context-switching control constructs
'$lgt_logtalk_built_in_predicate'(_ :: _, no).
'$lgt_logtalk_built_in_predicate'(_ << _, no).
% compiling and loading predicates
'$lgt_logtalk_built_in_predicate'(logtalk_compile(_,_,_), no).
'$lgt_logtalk_built_in_predicate'(logtalk_compile(_, _), no).
'$lgt_logtalk_built_in_predicate'(logtalk_load(_), no).
'$lgt_logtalk_built_in_predicate'(logtalk_load(_, _), no).
'$lgt_logtalk_built_in_predicate'(logtalk_make, no).
'$lgt_logtalk_built_in_predicate'(logtalk_make(_), no).
'$lgt_logtalk_built_in_predicate'(logtalk_load_context(_, _), no).
'$lgt_logtalk_built_in_predicate'(logtalk_library_path(_, _), no).
'$lgt_logtalk_built_in_predicate'(logtalk_make_target_action(_), no).
% entity properties
'$lgt_logtalk_built_in_predicate'(protocol_property(_, _), no).
'$lgt_logtalk_built_in_predicate'(category_property(_, _), no).
'$lgt_logtalk_built_in_predicate'(object_property(_, _), no).
% entity enumeration
'$lgt_logtalk_built_in_predicate'(current_protocol(_), no).
'$lgt_logtalk_built_in_predicate'(current_category(_), no).
'$lgt_logtalk_built_in_predicate'(current_object(_), no).
% entity creation predicates
'$lgt_logtalk_built_in_predicate'(create_object(_, _, _, _), no).
'$lgt_logtalk_built_in_predicate'(create_category(_, _, _, _), no).
'$lgt_logtalk_built_in_predicate'(create_protocol(_, _, _), no).
% entity abolishing predicates
'$lgt_logtalk_built_in_predicate'(abolish_object(_), no).
'$lgt_logtalk_built_in_predicate'(abolish_category(_), no).
'$lgt_logtalk_built_in_predicate'(abolish_protocol(_), no).
% entity relations
'$lgt_logtalk_built_in_predicate'(implements_protocol(_, _), no).
'$lgt_logtalk_built_in_predicate'(implements_protocol(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(imports_category(_, _), no).
'$lgt_logtalk_built_in_predicate'(imports_category(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(instantiates_class(_, _), no).
'$lgt_logtalk_built_in_predicate'(instantiates_class(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(specializes_class(_, _), no).
'$lgt_logtalk_built_in_predicate'(specializes_class(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(extends_protocol(_, _), no).
'$lgt_logtalk_built_in_predicate'(extends_protocol(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(extends_object(_, _), no).
'$lgt_logtalk_built_in_predicate'(extends_object(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(extends_category(_, _), no).
'$lgt_logtalk_built_in_predicate'(extends_category(_, _, _), no).
'$lgt_logtalk_built_in_predicate'(complements_object(_, _), no).
% protocol conformance
'$lgt_logtalk_built_in_predicate'(conforms_to_protocol(_, _), no).
'$lgt_logtalk_built_in_predicate'(conforms_to_protocol(_, _, _), no).
% events
'$lgt_logtalk_built_in_predicate'(abolish_events(_, _, _, _, _), no).
'$lgt_logtalk_built_in_predicate'(define_events(_, _, _, _, _), no).
'$lgt_logtalk_built_in_predicate'(current_event(_, _, _, _, _), no).
% flags
'$lgt_logtalk_built_in_predicate'(current_logtalk_flag(_, _), no).
'$lgt_logtalk_built_in_predicate'(set_logtalk_flag(_, _), no).
'$lgt_logtalk_built_in_predicate'(create_logtalk_flag(_, _, _), no).
% multi-threading predicates
'$lgt_logtalk_built_in_predicate'(threaded(_), threaded(0)).
'$lgt_logtalk_built_in_predicate'(threaded_call(_, _), threaded_call(0, *)).
'$lgt_logtalk_built_in_predicate'(threaded_call(_), threaded_call(0)).
'$lgt_logtalk_built_in_predicate'(threaded_once(_, _), threaded_once(0, *)).
'$lgt_logtalk_built_in_predicate'(threaded_once(_), threaded_once(0)).
'$lgt_logtalk_built_in_predicate'(threaded_ignore(_), threaded_ignore(0)).
'$lgt_logtalk_built_in_predicate'(threaded_exit(_, _), threaded_exit((::), *)).
'$lgt_logtalk_built_in_predicate'(threaded_exit(_), threaded_exit((::))).
'$lgt_logtalk_built_in_predicate'(threaded_peek(_, _), threaded_peek((::), *)).
'$lgt_logtalk_built_in_predicate'(threaded_peek(_), threaded_peek((::))).
'$lgt_logtalk_built_in_predicate'(threaded_cancel(_), threaded_cancel(*)).
'$lgt_logtalk_built_in_predicate'(threaded_wait(_), no).
'$lgt_logtalk_built_in_predicate'(threaded_notify(_), no).
% threaded engines predicates
'$lgt_logtalk_built_in_predicate'(threaded_engine_create(_, _, _), threaded_engine_create(*, 0, *)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_destroy(_), threaded_engine_destroy(*)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_self(_), threaded_engine_self(*)).
'$lgt_logtalk_built_in_predicate'(threaded_engine(_), threaded_engine(*)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_next(_, _), threaded_engine_next(*, *)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_next_reified(_, _), threaded_engine_next_reified(*, *)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_yield(_), threaded_engine_yield(*)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_post(_, _), threaded_engine_post(*, *)).
'$lgt_logtalk_built_in_predicate'(threaded_engine_fetch(_), threaded_engine_fetch(*)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  DCG rule conversion
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_dcg_rule'(@grammar_rule, -clause, @compilation_context)
%
% converts a grammar rule into a normal clause

'$lgt_dcg_rule'((RHead --> _), _, _) :-
	var(RHead),
	throw(instantiation_error).

'$lgt_dcg_rule'((RHead, _ --> _), _, _) :-
	var(RHead),
	throw(instantiation_error).

'$lgt_dcg_rule'((phrase(_), _ --> _), _, _) :-
	throw(permission_error(modify, built_in_non_terminal, phrase//1)).

'$lgt_dcg_rule'((NonTerminal, _ --> _), _, _) :-
	functor(NonTerminal, call, Arity),
	Arity >= 1,
	throw(permission_error(modify, built_in_non_terminal, call//Arity)).

'$lgt_dcg_rule'((Entity::NonTerminal, Terminals --> GRBody), (Entity::Head :- Body), Ctx) :-
	!,
	'$lgt_check'(object_identifier, Entity),
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Head),
	'$lgt_dcg_body'(GRBody, S0, S1, Goal1, Ctx),
	'$lgt_dcg_terminals'(Terminals, S, S1, Goal2),
	Body = (Goal1, Goal2).

'$lgt_dcg_rule'((':'(Module, NonTerminal), Terminals --> GRBody), (':'(Module, Head) :- Body), Ctx) :-
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Head),
	'$lgt_dcg_body'(GRBody, S0, S1, Goal1, Ctx),
	'$lgt_dcg_terminals'(Terminals, S, S1, Goal2),
	Body = (Goal1, Goal2).

'$lgt_dcg_rule'((NonTerminal, Terminals --> GRBody), _, Ctx) :-
	once((
		'$lgt_variant'(GRBody, NonTerminal)
	;	GRBody = (GRFirst, _),
		'$lgt_variant'(GRFirst, NonTerminal)
	)),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(left_recursion, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(left_recursion),
		left_recursion(File, Lines, Type, Entity, (NonTerminal, Terminals --> GRBody))
	),
	fail.

'$lgt_dcg_rule'((NonTerminal, Terminals --> GRBody), (Head :- Body), Ctx) :-
	!,
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Head),
	'$lgt_dcg_body'(GRBody, S0, S1, Goal1, Ctx),
	'$lgt_dcg_terminals'(Terminals, S, S1, Goal2),
	Body = (Goal1, Goal2),
	functor(NonTerminal, Functor, Arity),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		\+ '$lgt_pp_defines_non_terminal_'(Functor, Arity, _) ->
		ExtArity is Arity + 2,
		assertz('$lgt_pp_defines_non_terminal_'(Functor, Arity, ExtArity))
	;	true
	).

'$lgt_dcg_rule'((phrase(_) --> _), _, _) :-
	throw(permission_error(modify, built_in_non_terminal, phrase//1)).

'$lgt_dcg_rule'((NonTerminal --> _), _, _) :-
	functor(NonTerminal, call, Arity),
	Arity >= 1,
	throw(permission_error(modify, built_in_non_terminal, call//Arity)).

'$lgt_dcg_rule'((eos --> _), _, _) :-
	throw(permission_error(modify, built_in_non_terminal, eos//0)).

'$lgt_dcg_rule'((Entity::NonTerminal --> GRBody), (Entity::Head :- Body), Ctx) :-
	!,
	'$lgt_check'(object_identifier, Entity),
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Head),
	'$lgt_dcg_body'(GRBody, S0, S, Body, Ctx).

'$lgt_dcg_rule'((':'(Module, NonTerminal) --> GRBody), (':'(Module, Head) :- Body), Ctx) :-
	!,
	'$lgt_check'(module_identifier, Module),
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Head),
	'$lgt_dcg_body'(GRBody, S0, S, Body, Ctx).

'$lgt_dcg_rule'((NonTerminal --> GRBody), _, Ctx) :-
	once((
		'$lgt_variant'(GRBody, NonTerminal)
	;	GRBody = (GRFirst, _),
		'$lgt_variant'(GRFirst, NonTerminal)
	)),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(left_recursion, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(left_recursion),
		left_recursion(File, Lines, Type, Entity, (NonTerminal --> GRBody))
	),
	fail.

'$lgt_dcg_rule'((NonTerminal --> GRBody), (Head :- Body), Ctx) :-
	!,
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Head),
	'$lgt_dcg_body'(GRBody, S0, S, Body, Ctx),
	functor(NonTerminal, Functor, Arity),
	(	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
		\+ '$lgt_pp_defines_non_terminal_'(Functor, Arity, _) ->
		ExtArity is Arity + 2,
		assertz('$lgt_pp_defines_non_terminal_'(Functor, Arity, ExtArity))
	;	true
	).

'$lgt_dcg_rule'(Term, _, _) :-
	throw(type_error(grammar_rule, Term)).



% '$lgt_dcg_non_terminal'(+callable, @var, @var, -goal)
%
% translates a grammar goal non-terminal

'$lgt_dcg_non_terminal'(NonTerminal, _, _, _) :-
	'$lgt_check'(callable, NonTerminal),
	'$lgt_pp_protocol_'(_, _, _, _, _),
	% protocols cannot contain non-terminal definitions
	functor(NonTerminal, Functor, Arity),
	throw(permission_error(define, non_terminal, Functor//Arity)).

'$lgt_dcg_non_terminal'((_ ; _), _, _, _) :-
	throw(permission_error(modify, control_construct, (;)/2)).

'$lgt_dcg_non_terminal'((_ -> _), _, _, _) :-
	throw(permission_error(modify, control_construct, (->)/2)).

'$lgt_dcg_non_terminal'('*->'(_, _), _, _, _) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	throw(permission_error(modify, control_construct, (*->)/2)).

'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Goal) :-
	NonTerminal =.. NonTerminalUniv,
	'$lgt_append'(NonTerminalUniv, [S0, S], GoalUniv),
	Goal =.. GoalUniv.



% '$lgt_dcg_terminals'(+list, @var, @var, -goal)
%
% translates a list of terminals

'$lgt_dcg_terminals'(Terminals, S0, S, S0 = List) :-
	'$lgt_check'(list, Terminals),
	'$lgt_append'(Terminals, S, List).



% '$lgt_dcg_msg'(@dcgbody @object_identifier, @var, @var, -body)
%
% translates a grammar rule message to an object into a predicate message

'$lgt_dcg_msg'(Var, Obj, S0, S, phrase(Obj::Var, S0, S)) :-
	var(Var),
	!.

'$lgt_dcg_msg'('*->'(GRIf, GRThen), Obj, S0, S, '*->'(If, Then)) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_dcg_msg'(GRIf, Obj, S0, S1, If),
	'$lgt_dcg_msg'(GRThen, Obj, S1, S, Then).

'$lgt_dcg_msg'((GRIf -> GRThen), Obj, S0, S, (If -> Then)) :-
	!,
	'$lgt_dcg_msg'(GRIf, Obj, S0, S1, If),
	'$lgt_dcg_msg'(GRThen, Obj, S1, S, Then).

'$lgt_dcg_msg'((GREither; GROr), Obj, S0, S, (Either; Or)) :-
	!,
	'$lgt_dcg_msg'(GREither, Obj, S0, S, Either),
	'$lgt_dcg_msg'(GROr, Obj, S0, S, Or).

'$lgt_dcg_msg'((GRFirst, GRSecond), Obj, S0, S, (First, Second)) :-
	!,
	'$lgt_dcg_msg'(GRFirst, Obj, S0, S1, First),
	'$lgt_dcg_msg'(GRSecond, Obj, S1, S, Second).

'$lgt_dcg_msg'(!, _, S0, S, (!, (S0 = S))) :-
	!.

'$lgt_dcg_msg'(NonTerminal, Obj, S0, S, Obj::Pred) :-
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Pred).



% '$lgt_dcg_self_msg'(@dcgbody, @var, @var, -body, -body)
%
% translates a grammar rule message to an object into a predicate message

'$lgt_dcg_self_msg'(Var, S0, S, phrase(::Var, S0, S)) :-
	var(Var),
	!.

'$lgt_dcg_self_msg'('*->'(GRIf, GRThen), S0, S, '*->'(If, Then)) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_dcg_self_msg'(GRIf, S0, S1, If),
	'$lgt_dcg_self_msg'(GRThen, S1, S, Then).

'$lgt_dcg_self_msg'((GRIf -> GRThen), S0, S, (If -> Then)) :-
	!,
	'$lgt_dcg_self_msg'(GRIf, S0, S1, If),
	'$lgt_dcg_self_msg'(GRThen, S1, S, Then).

'$lgt_dcg_self_msg'((GREither; GROr), S0, S, (Either; Or)) :-
	!,
	'$lgt_dcg_self_msg'(GREither, S0, S, Either),
	'$lgt_dcg_self_msg'(GROr, S0, S, Or).

'$lgt_dcg_self_msg'((GRFirst, GRSecond), S0, S, (First, Second)) :-
	!,
	'$lgt_dcg_self_msg'(GRFirst, S0, S1, First),
	'$lgt_dcg_self_msg'(GRSecond, S1, S, Second).

'$lgt_dcg_self_msg'(!, S0, S, (!, (S0 = S))) :-
	!.

'$lgt_dcg_self_msg'(NonTerminal, S0, S, ::Pred) :-
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Pred).



% '$lgt_dcg_super_call'(@dcgbody, @var, @var, -body)
%
% translates a super call to a grammar rule in an ancestor entity

'$lgt_dcg_super_call'(Var, S0, S, phrase(^^Var, S0, S)) :-
	var(Var),
	!.

'$lgt_dcg_super_call'(NonTerminal, S0, S, ^^Pred) :-
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Pred).



% '$lgt_dcg_body'(@dcgbody, @var, @var, -body, @compilation_context)
%
% translates a grammar rule body into a Prolog clause body

'$lgt_dcg_body'(Var, S0, S, phrase(Var, S0, S), _) :-
	var(Var),
	!.

'$lgt_dcg_body'('$lgt_closure'(TFunctor, TArgs, ExCtx), S0, S, {TGoal}, _) :-
	% pre-compiled closure (note that the closure may be called from a mapping
	% predicate, which prevents us to use a difference list based solution to
	% avoid the calls to append/3 and =../2 as that would fix the extra arguments
	% in the goal on the first closure call and thus break the followup calls)
	!,
	'$lgt_append'(TArgs, [S0, S, ExCtx], FullArgs),
	TGoal =.. [TFunctor| FullArgs].

'$lgt_dcg_body'(Free/Parameters>>Lambda, S0, S, call(Free/Parameters>>Lambda, S0, S), Ctx) :-
	!,
	'$lgt_check_lambda_expression'(Free/Parameters>>Lambda, Ctx),
	(	\+ Parameters \= [_, _] ->
		true
	;	throw(representation_error(lambda_parameters))
	).

'$lgt_dcg_body'(Parameters>>Lambda, S0, S, call(Parameters>>Lambda, S0, S), Ctx) :-
	!,
	'$lgt_check_lambda_expression'(Parameters>>Lambda, Ctx),
	(	\+ Parameters \= [_, _] ->
		true
	;	throw(representation_error(lambda_parameters))
	).

'$lgt_dcg_body'(Free/Lambda, S0, S, call(Free/Lambda, S0, S), Ctx) :-
	!,
	'$lgt_check_lambda_expression'(Free/Lambda, Ctx).

'$lgt_dcg_body'(Obj::RGoal, S0, S, CGoal, _) :-
	!,
	'$lgt_dcg_msg'(RGoal, Obj, S0, S, CGoal).

'$lgt_dcg_body'(::RGoal, S0, S, CGoal, _) :-
	!,
	'$lgt_dcg_self_msg'(RGoal, S0, S, CGoal).

'$lgt_dcg_body'(^^RGoal, S0, S, CGoal, _) :-
	!,
	'$lgt_dcg_super_call'(RGoal, S0, S, CGoal).

'$lgt_dcg_body'(Obj<<RGoal, S0, S, Obj<<CGoal, Ctx) :-
	!,
	'$lgt_dcg_body'(RGoal, S0, S, CGoal, Ctx).

'$lgt_dcg_body'(':'(Module, RGoal), S0, S, CGoal, _) :-
	!,
	(	callable(RGoal) ->
		RGoal =.. RGoalUniv,
		'$lgt_append'(RGoalUniv, [S0, S], GoalUniv),
		Goal =.. GoalUniv,
		CGoal = ':'(Module, Goal)
	;	CGoal = call(':'(Module,RGoal), S0, S)
	).

'$lgt_dcg_body'((GRIfThen; GRElse), S0, S, (If -> Then; Else), Ctx) :-
	nonvar(GRIfThen),
	GRIfThen = (GRIf -> GRThen),
	!,
	'$lgt_dcg_body'(GRIf, S0, S1, If, Ctx),
	'$lgt_dcg_body'(GRThen, S1, S, Then, Ctx),
	'$lgt_dcg_body'(GRElse, S0, S, Else, Ctx).

'$lgt_dcg_body'((GRIfThen; GRElse), S0, S, ('*->'(If, Then); Else), Ctx) :-
	nonvar(GRIfThen),
	GRIfThen = '*->'(GRIf, GRThen),
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_dcg_body'(GRIf, S0, S1, If, Ctx),
	'$lgt_dcg_body'(GRThen, S1, S, Then, Ctx),
	'$lgt_dcg_body'(GRElse, S0, S, Else, Ctx).

'$lgt_dcg_body'((GREither; GROr), S0, S, (Either; Or), Ctx) :-
	!,
	'$lgt_dcg_body'(GREither, S0, S, Either0, Ctx),
	'$lgt_fix_disjunction_left_side'(Either0, Either),
	'$lgt_dcg_body'(GROr, S0, S, Or, Ctx).

'$lgt_dcg_body'('|'(GREither, GROr), S0, S, (Either; Or), Ctx) :-
	!,
	'$lgt_dcg_body'(GREither, S0, S, Either0, Ctx),
	'$lgt_fix_disjunction_left_side'(Either0, Either),
	'$lgt_dcg_body'(GROr, S0, S, Or, Ctx).

'$lgt_dcg_body'('*->'(GRIf, GRThen), _, _, _, Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	nonvar(GRIf),
	\+ functor(GRIf, {}, 1),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(grammar_rules, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(grammar_rules),
		unsound_construct_in_grammar_rule(File, Lines, Type, Entity, '*->'(GRIf, GRThen))
	),
	fail.

'$lgt_dcg_body'('*->'(GRIf, GRThen), S0, S, '*->'(If, Then), Ctx) :-
	'$lgt_predicate_property'('*->'(_, _), built_in),
	!,
	'$lgt_dcg_body'(GRIf, S0, S1, If, Ctx),
	'$lgt_dcg_body'(GRThen, S1, S, Then, Ctx).

'$lgt_dcg_body'((GRIf -> GRThen), _, _, _, Ctx) :-
	nonvar(GRIf),
	\+ functor(GRIf, {}, 1),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(grammar_rules, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(grammar_rules),
		unsound_construct_in_grammar_rule(File, Lines, Type, Entity, (GRIf -> GRThen))
	),
	fail.

'$lgt_dcg_body'((GRIf -> GRThen), S0, S, (If -> Then), Ctx) :-
	!,
	'$lgt_dcg_body'(GRIf, S0, S1, If, Ctx),
	'$lgt_dcg_body'(GRThen, S1, S, Then, Ctx).

'$lgt_dcg_body'((GRFirst, GRSecond), S0, S, (First, Second), Ctx) :-
	!,
	'$lgt_dcg_body'(GRFirst, S0, S1, First, Ctx),
	'$lgt_dcg_body'(GRSecond, S1, S, Second, Ctx).

'$lgt_dcg_body'(!, S0, S, (!, (S0 = S)), _) :-
	!.

'$lgt_dcg_body'('{}', S0, S, (S0 = S), _) :-
	!.

'$lgt_dcg_body'({Goal}, S0, S, (call(Goal), (S0 = S)), _) :-
	var(Goal),
	!.

'$lgt_dcg_body'({Goal}, S0, S, (Goal, (S0 = S)), _) :-
	!,
	'$lgt_check'(callable, Goal).

'$lgt_dcg_body'(\+ GRBody, _, _, _, Ctx) :-
	nonvar(GRBody),
	\+ functor(GRBody, {}, 1),
	'$lgt_comp_ctx_mode'(Ctx, compile(_,_,_)),
	'$lgt_compiler_flag'(grammar_rules, warning),
	'$lgt_source_file_context'(File, Lines, Type, Entity),
	'$lgt_increment_compiling_warnings_counter',
	'$lgt_print_message'(
		warning(grammar_rules),
		unsound_construct_in_grammar_rule(File, Lines, Type, Entity, \+ GRBody)
	),
	fail.

'$lgt_dcg_body'(\+ GRBody, S0, S, (\+ Goal, (S0 = S)), Ctx) :-
	!,
	'$lgt_dcg_body'(GRBody, S0, _, Goal, Ctx).

'$lgt_dcg_body'(catch(GRGoal, Catcher, GRRecovery), S0, S, catch(Goal, Catcher, Recovery), Ctx) :-
	!,
	'$lgt_dcg_body'(GRGoal, S0, S, Goal, Ctx),
	'$lgt_dcg_body'(GRRecovery, S0, S, Recovery, Ctx).

'$lgt_dcg_body'(phrase(GRBody), S0, S, phrase(GRBody, S0, S), _) :-
	!.

'$lgt_dcg_body'(eos, S0, S, (S0 = [], S = []), _) :-
	!.

'$lgt_dcg_body'(GRBody, S0, S, Goal, _) :-
	functor(GRBody, call, Arity),
	Arity >= 1,
	!,
	GRBody =.. [call, Closure| ExtraArgs],
	'$lgt_check'(var_or_callable, Closure),
	'$lgt_append'(ExtraArgs, [S0, S], FullArgs),
	% translate to the internal '$lgt_callN'/2 predicate instead of the call/N control
	% construct to avoid lint warnings about redundant uses of the control construct
	Goal = '$lgt_callN'(Closure, FullArgs).

'$lgt_dcg_body'([], S0, S, (S0 = S), _) :-
	!.

'$lgt_dcg_body'([T| Ts], S0, S, Goal, _) :-
	!,
	'$lgt_dcg_terminals'([T| Ts], S0, S, Goal).

'$lgt_dcg_body'(String, S0, S, Goal, _) :-
	'$lgt_string'(String),
	!,
	'$lgt_string_codes'(String, Codes),
	'$lgt_dcg_terminals'(Codes, S0, S, Goal).

'$lgt_dcg_body'(Alias, S0, S, Goal, Ctx) :-
	'$lgt_pp_uses_non_terminal_'(Obj, Original, Alias, Pred, PredAlias, Ctx, _, _),
	!,
	% we must register here otherwise the non-terminal alias information would be lost
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
	'$lgt_add_referenced_object_message'(Mode, Obj, Pred, PredAlias, Head),
	'$lgt_dcg_body'(Obj::Original, S0, S, Goal, Ctx).

'$lgt_dcg_body'(Alias, S0, S, Goal, Ctx) :-
	'$lgt_pp_use_module_non_terminal_'(Module, Original, Alias, Pred, PredAlias, Ctx, _, _),
	!,
	% we must register here otherwise the non-terminal alias information would be lost
	'$lgt_comp_ctx'(Ctx, Head, _, _, _, _, _, _, _, _, _, Mode, _, _, _),
	'$lgt_add_referenced_module_predicate'(Mode, Module, Pred, PredAlias, Head),
	'$lgt_dcg_body'(':'(Module, Original), S0, S, Goal, Ctx).

'$lgt_dcg_body'(NonTerminal, S0, S, Goal, Ctx) :-
	'$lgt_dcg_non_terminal'(NonTerminal, S0, S, Goal),
	functor(NonTerminal, Functor, Arity),
	'$lgt_comp_ctx'(Ctx, _, _, _, _, _, _, _, _, _, _, Mode, _, Lines, _),
	(	Mode = compile(_,_,_),
		\+ '$lgt_pp_calls_non_terminal_'(Functor, Arity, _, Lines) ->
		ExtArity is Arity + 2,
		assertz('$lgt_pp_calls_non_terminal_'(Functor, Arity, ExtArity, Lines))
	;	true
	).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  table of ISO Prolog specified built-in predicates
%
%  (used for portability checking)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_iso_spec_predicate'(?callable)

% control constructs
'$lgt_iso_spec_predicate'(true).
'$lgt_iso_spec_predicate'(fail).
'$lgt_iso_spec_predicate'(false).
'$lgt_iso_spec_predicate'(call(_)).
'$lgt_iso_spec_predicate'(call(_, _)).
'$lgt_iso_spec_predicate'(call(_, _, _)).
'$lgt_iso_spec_predicate'(call(_, _, _, _)).
'$lgt_iso_spec_predicate'(call(_, _, _, _, _)).
'$lgt_iso_spec_predicate'(call(_, _, _, _, _, _)).
'$lgt_iso_spec_predicate'(call(_, _, _, _, _, _, _)).
'$lgt_iso_spec_predicate'(call(_, _, _, _, _, _, _, _)).
'$lgt_iso_spec_predicate'(!).
'$lgt_iso_spec_predicate'((Goal; _)) :-
	(	var(Goal) ->
		true
	;	Goal \= '*->'(_, _)
	).
'$lgt_iso_spec_predicate'((_, _)).
'$lgt_iso_spec_predicate'((_ -> _)).
'$lgt_iso_spec_predicate'(catch(_, _, _)).
'$lgt_iso_spec_predicate'(throw(_)).
% term unification
'$lgt_iso_spec_predicate'((_ = _)).
'$lgt_iso_spec_predicate'((_ \= _)).
'$lgt_iso_spec_predicate'(unify_with_occurs_check(_, _)).
'$lgt_iso_spec_predicate'(subsumes_term(_, _)).
% term testing
'$lgt_iso_spec_predicate'(var(_)).
'$lgt_iso_spec_predicate'(nonvar(_)).
'$lgt_iso_spec_predicate'(atom(_)).
'$lgt_iso_spec_predicate'(atomic(_)).
'$lgt_iso_spec_predicate'(number(_)).
'$lgt_iso_spec_predicate'(integer(_)).
'$lgt_iso_spec_predicate'(float(_)).
'$lgt_iso_spec_predicate'(compound(_)).
'$lgt_iso_spec_predicate'(acyclic_term(_)).
'$lgt_iso_spec_predicate'(callable(_)).
'$lgt_iso_spec_predicate'(ground(_)).
% term comparison
'$lgt_iso_spec_predicate'((_ @=< _)).
'$lgt_iso_spec_predicate'((_ @< _)).
'$lgt_iso_spec_predicate'((_ @>= _)).
'$lgt_iso_spec_predicate'((_ @> _)).
'$lgt_iso_spec_predicate'((_ == _)).
'$lgt_iso_spec_predicate'((_ \== _)).
'$lgt_iso_spec_predicate'(compare(_, _, _)).
% term creation and decomposition
'$lgt_iso_spec_predicate'(functor(_, _, _)).
'$lgt_iso_spec_predicate'(arg(_, _, _)).
'$lgt_iso_spec_predicate'(_ =.. _).
'$lgt_iso_spec_predicate'(copy_term(_, _)).
'$lgt_iso_spec_predicate'(term_variables(_, _)).
% arithmetic evaluation
'$lgt_iso_spec_predicate'(_ is _).
% arithmetic comparison
'$lgt_iso_spec_predicate'((_ =< _)).
'$lgt_iso_spec_predicate'((_ < _)).
'$lgt_iso_spec_predicate'((_ >= _)).
'$lgt_iso_spec_predicate'((_ > _)).
'$lgt_iso_spec_predicate'((_ =:= _)).
'$lgt_iso_spec_predicate'((_ =\= _)).
% database
'$lgt_iso_spec_predicate'(clause(_, _)).
'$lgt_iso_spec_predicate'(current_predicate(_)).
'$lgt_iso_spec_predicate'(asserta(_)).
'$lgt_iso_spec_predicate'(assertz(_)).
'$lgt_iso_spec_predicate'(retract(_)).
'$lgt_iso_spec_predicate'(retractall(_)).
'$lgt_iso_spec_predicate'(abolish(_)).
% all solutions
'$lgt_iso_spec_predicate'(findall(_, _, _)).
'$lgt_iso_spec_predicate'(bagof(_, _, _)).
'$lgt_iso_spec_predicate'(setof(_, _, _)).
% stream selection and control
'$lgt_iso_spec_predicate'(current_input(_)).
'$lgt_iso_spec_predicate'(current_output(_)).
'$lgt_iso_spec_predicate'(set_input(_)).
'$lgt_iso_spec_predicate'(set_output(_)).
'$lgt_iso_spec_predicate'(open(_, _, _, _)).
'$lgt_iso_spec_predicate'(open(_, _, _)).
'$lgt_iso_spec_predicate'(close(_, _)).
'$lgt_iso_spec_predicate'(close(_)).
'$lgt_iso_spec_predicate'(flush_output(_)).
'$lgt_iso_spec_predicate'(flush_output).
'$lgt_iso_spec_predicate'(stream_property(_, _)).
'$lgt_iso_spec_predicate'(at_end_of_stream).
'$lgt_iso_spec_predicate'(at_end_of_stream(_)).
'$lgt_iso_spec_predicate'(set_stream_position(_, _)).
% character and byte input/output
'$lgt_iso_spec_predicate'(get_char(_, _)).
'$lgt_iso_spec_predicate'(get_char(_)).
'$lgt_iso_spec_predicate'(get_code(_, _)).
'$lgt_iso_spec_predicate'(get_code(_)).
'$lgt_iso_spec_predicate'(peek_char(_, _)).
'$lgt_iso_spec_predicate'(peek_char(_)).
'$lgt_iso_spec_predicate'(peek_code(_, _)).
'$lgt_iso_spec_predicate'(peek_code(_)).
'$lgt_iso_spec_predicate'(put_char(_, _)).
'$lgt_iso_spec_predicate'(put_char(_)).
'$lgt_iso_spec_predicate'(put_code(_, _)).
'$lgt_iso_spec_predicate'(put_code(_)).
'$lgt_iso_spec_predicate'(nl).
'$lgt_iso_spec_predicate'(nl(_)).
'$lgt_iso_spec_predicate'(get_byte(_, _)).
'$lgt_iso_spec_predicate'(get_byte(_)).
'$lgt_iso_spec_predicate'(peek_byte(_, _)).
'$lgt_iso_spec_predicate'(peek_byte(_)).
'$lgt_iso_spec_predicate'(put_byte(_, _)).
'$lgt_iso_spec_predicate'(put_byte(_)).
% term input/output
'$lgt_iso_spec_predicate'(read_term(_, _, _)).
'$lgt_iso_spec_predicate'(read_term(_, _)).
'$lgt_iso_spec_predicate'(read(_)).
'$lgt_iso_spec_predicate'(read(_, _)).
'$lgt_iso_spec_predicate'(write_term(_, _, _)).
'$lgt_iso_spec_predicate'(write_term(_, _)).
'$lgt_iso_spec_predicate'(write(_)).
'$lgt_iso_spec_predicate'(write(_, _)).
'$lgt_iso_spec_predicate'(writeq(_)).
'$lgt_iso_spec_predicate'(writeq(_, _)).
'$lgt_iso_spec_predicate'(write_canonical(_)).
'$lgt_iso_spec_predicate'(write_canonical(_, _)).
'$lgt_iso_spec_predicate'(op(_, _, _)).
'$lgt_iso_spec_predicate'(current_op(_, _, _)).
'$lgt_iso_spec_predicate'(char_conversion(_, _)).
'$lgt_iso_spec_predicate'(current_char_conversion(_, _)).
% logic and control
'$lgt_iso_spec_predicate'(\+ _).
'$lgt_iso_spec_predicate'(once(_)).
'$lgt_iso_spec_predicate'(repeat).
% atomic term processing
'$lgt_iso_spec_predicate'(atom_length(_, _)).
'$lgt_iso_spec_predicate'(atom_concat(_, _, _)).
'$lgt_iso_spec_predicate'(sub_atom(_, _, _, _, _)).
'$lgt_iso_spec_predicate'(atom_chars(_, _)).
'$lgt_iso_spec_predicate'(atom_codes(_, _)).
'$lgt_iso_spec_predicate'(char_code(_, _)).
'$lgt_iso_spec_predicate'(number_chars(_, _)).
'$lgt_iso_spec_predicate'(number_codes(_, _)).
% implementation defined hooks functions
'$lgt_iso_spec_predicate'(set_prolog_flag(_, _)).
'$lgt_iso_spec_predicate'(current_prolog_flag(_, _)).
'$lgt_iso_spec_predicate'(halt).
'$lgt_iso_spec_predicate'(halt(_)).
% sorting
'$lgt_iso_spec_predicate'(keysort(_, _)).
'$lgt_iso_spec_predicate'(sort(_, _)).

% the following predicates are not part of the ISO/IEC 13211-1 Prolog standard
% but can be found either on Core Revision standardization proposals or,
% more important, these predicates are or are becoming de facto standards

% term creation and decomposition
'$lgt_iso_spec_predicate'(numbervars(_, _, _)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  table of ISO Prolog specified arithmetic functions
%
%  (used by the linter for portability checking)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_iso_spec_function'(?callable)

'$lgt_iso_spec_function'(pi).

'$lgt_iso_spec_function'('+'(_)).
'$lgt_iso_spec_function'('-'(_)).
'$lgt_iso_spec_function'('+'(_, _)).
'$lgt_iso_spec_function'('-'(_, _)).
'$lgt_iso_spec_function'('*'(_, _)).
'$lgt_iso_spec_function'('/'(_, _)).
'$lgt_iso_spec_function'('//'(_, _)).
'$lgt_iso_spec_function'(rem(_, _)).
'$lgt_iso_spec_function'(mod(_, _)).
'$lgt_iso_spec_function'(div(_, _)).

'$lgt_iso_spec_function'('/\\'(_, _)).
'$lgt_iso_spec_function'('\\/'(_, _)).
'$lgt_iso_spec_function'('\\'(_)).
'$lgt_iso_spec_function'('<<'(_, _)).
'$lgt_iso_spec_function'('>>'(_, _)).
'$lgt_iso_spec_function'(xor(_, _)).

'$lgt_iso_spec_function'('**'(_, _)).
'$lgt_iso_spec_function'('^'(_, _)).

'$lgt_iso_spec_function'(abs(_)).
'$lgt_iso_spec_function'(sign(_)).

'$lgt_iso_spec_function'(sqrt(_)).

'$lgt_iso_spec_function'(acos(_)).
'$lgt_iso_spec_function'(asin(_)).
'$lgt_iso_spec_function'(atan(_)).
'$lgt_iso_spec_function'(atan2(_, _)).
'$lgt_iso_spec_function'(cos(_)).
'$lgt_iso_spec_function'(sin(_)).
'$lgt_iso_spec_function'(tan(_)).

'$lgt_iso_spec_function'(exp(_)).
'$lgt_iso_spec_function'(log(_)).

'$lgt_iso_spec_function'(float(_)).
'$lgt_iso_spec_function'(ceiling(_)).
'$lgt_iso_spec_function'(floor(_)).
'$lgt_iso_spec_function'(round(_)).
'$lgt_iso_spec_function'(truncate(_)).
'$lgt_iso_spec_function'(float_fractional_part(_)).
'$lgt_iso_spec_function'(float_integer_part(_)).

'$lgt_iso_spec_function'(max(_, _)).
'$lgt_iso_spec_function'(min(_, _)).

% the following functions are not part of the ISO/IEC 13211-1 Prolog standard
% but can be found either on Core Revision standardization proposals or,
% more important, these functions are or are becoming de facto standards

'$lgt_iso_spec_function'(e).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  auxiliary predicates checking for float and integer arithmetic
%  expressions (used for linter checks)
%
%  these checks also recognize de facto standard arithmetic constants
%  and functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_float_expression'(@term)

'$lgt_float_expression'(Exp) :-
	var(Exp),
	!,
	fail.
'$lgt_float_expression'(Exp) :-
	float(Exp),
	!.
% basic arithmetic functions
'$lgt_float_expression'(Exp1 + Exp2) :-
	(	'$lgt_float_expression'(Exp1) ->
		true
	;	'$lgt_float_expression'(Exp2)
	).
'$lgt_float_expression'(Exp1 - Exp2) :-
	(	'$lgt_float_expression'(Exp1) ->
		true
	;	'$lgt_float_expression'(Exp2)
	).
'$lgt_float_expression'(Exp1 * Exp2) :-
	(	'$lgt_float_expression'(Exp1) ->
		true
	;	'$lgt_float_expression'(Exp2)
	).
'$lgt_float_expression'(_ / _).
'$lgt_float_expression'(_ ** _).
% other functions
'$lgt_float_expression'(abs(Exp)) :-
	'$lgt_float_expression'(Exp).
'$lgt_float_expression'(sign(Exp)) :-
	'$lgt_float_expression'(Exp).
'$lgt_float_expression'(max(Exp1, Exp2)) :-
	(	'$lgt_float_expression'(Exp1) ->
		true
	;	'$lgt_float_expression'(Exp2)
	).
'$lgt_float_expression'(min(Exp1, Exp2)) :-
	(	'$lgt_float_expression'(Exp1) ->
		true
	;	'$lgt_float_expression'(Exp2)
	).
'$lgt_float_expression'(float_integer_part(_)).
'$lgt_float_expression'(float_fractional_part(_)).
'$lgt_float_expression'(sqrt(_)).
'$lgt_float_expression'(exp(_)).
'$lgt_float_expression'(log(_)).
'$lgt_float_expression'(log(_, _)).
'$lgt_float_expression'(log10(_)).
% trignometric functions
'$lgt_float_expression'(acos(_)).
'$lgt_float_expression'(asin(_)).
'$lgt_float_expression'(atan(_)).
'$lgt_float_expression'(atan2(_, _)).
'$lgt_float_expression'(cos(_)).
'$lgt_float_expression'(sin(_)).
'$lgt_float_expression'(tan(_)).
% hyperbolic functions
'$lgt_float_expression'(sinh(_)).
'$lgt_float_expression'(cosh(_)).
'$lgt_float_expression'(tanh(_)).
'$lgt_float_expression'(asinh(_)).
'$lgt_float_expression'(acosh(_)).
'$lgt_float_expression'(atanh(_)).
% float arithmetic constants
'$lgt_float_expression'(e).
'$lgt_float_expression'(pi).
'$lgt_float_expression'(epsilon).



% '$lgt_integer_expression'(@term)

'$lgt_integer_expression'(Exp) :-
	var(Exp),
	!,
	fail.
'$lgt_integer_expression'(Exp) :-
	integer(Exp),
	!.
% basic arithmetic functions
'$lgt_integer_expression'(Exp1 + Exp2) :-
	'$lgt_integer_expression'(Exp1),
	'$lgt_integer_expression'(Exp2).
'$lgt_integer_expression'(Exp1 - Exp2) :-
	'$lgt_integer_expression'(Exp1),
	'$lgt_integer_expression'(Exp2).
'$lgt_integer_expression'(Exp1 * Exp2) :-
	'$lgt_integer_expression'(Exp1),
	'$lgt_integer_expression'(Exp2).
'$lgt_integer_expression'(_ // _).
% other functions
'$lgt_integer_expression'(rem(_, _)).
'$lgt_integer_expression'(div(_, _)).
'$lgt_integer_expression'(mod(_, _)).
'$lgt_integer_expression'(gcd(_, _)).
'$lgt_integer_expression'(round(_)).
'$lgt_integer_expression'(truncate(_)).
'$lgt_integer_expression'(abs(Exp)) :-
	'$lgt_integer_expression'(Exp).
'$lgt_integer_expression'(sign(Exp)) :-
	'$lgt_integer_expression'(Exp).
'$lgt_integer_expression'(max(Exp1, Exp2)) :-
	'$lgt_integer_expression'(Exp1),
	'$lgt_integer_expression'(Exp2).
'$lgt_integer_expression'(min(Exp1, Exp2)) :-
	'$lgt_integer_expression'(Exp1),
	'$lgt_integer_expression'(Exp2).
% bitwise functions
'$lgt_integer_expression'(_ << _).
'$lgt_integer_expression'(_ >> _).
'$lgt_integer_expression'(_ /\ _).
'$lgt_integer_expression'(_ \/ _).
'$lgt_integer_expression'(xor(_, _)).
'$lgt_integer_expression'(\ _).
'$lgt_integer_expression'(lsb(_)).
'$lgt_integer_expression'(msb(_)).
'$lgt_integer_expression'(popcount(_)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  table of ISO Prolog operators
%
%  (used by the linter for portability checking)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_iso_spec_operator'(?atom, ?atom, ?integer)

'$lgt_iso_spec_operator'((:-),  xfx, 1200).
'$lgt_iso_spec_operator'((-->), xfx, 1200).

'$lgt_iso_spec_operator'((:-),   fx, 1200).
'$lgt_iso_spec_operator'((?-),   fx, 1200).

'$lgt_iso_spec_operator'((;),   xfy, 1100).
'$lgt_iso_spec_operator'((->),  xfy, 1050).
'$lgt_iso_spec_operator'((','), xfy, 1000).
'$lgt_iso_spec_operator'((\+),   fy,  900).

'$lgt_iso_spec_operator'((=),   xfx,  700).
'$lgt_iso_spec_operator'((\=),  xfx,  700).

'$lgt_iso_spec_operator'((==),  xfx,  700).
'$lgt_iso_spec_operator'((\==), xfx,  700).
'$lgt_iso_spec_operator'((@<),  xfx,  700).
'$lgt_iso_spec_operator'((@=<), xfx,  700).
'$lgt_iso_spec_operator'((@>),  xfx,  700).
'$lgt_iso_spec_operator'((@>=), xfx,  700).

'$lgt_iso_spec_operator'((=..), xfx,  700).

'$lgt_iso_spec_operator'((is),  xfx,  700).
'$lgt_iso_spec_operator'((=:=), xfx,  700).
'$lgt_iso_spec_operator'((=\=), xfx,  700).
'$lgt_iso_spec_operator'((<),   xfx,  700).
'$lgt_iso_spec_operator'((=<),  xfx,  700).
'$lgt_iso_spec_operator'((>),   xfx,  700).
'$lgt_iso_spec_operator'((>=),  xfx,  700).

'$lgt_iso_spec_operator'((:),   xfy,  600).

'$lgt_iso_spec_operator'((+),   yfx,  500).
'$lgt_iso_spec_operator'((-),   yfx,  500).
'$lgt_iso_spec_operator'((/\),  yfx,  500).
'$lgt_iso_spec_operator'((\/),  yfx,  500).

'$lgt_iso_spec_operator'((*),   yfx,  400).
'$lgt_iso_spec_operator'((/),   yfx,  400).
'$lgt_iso_spec_operator'((//),  yfx,  400).
'$lgt_iso_spec_operator'((rem), yfx,  400).
'$lgt_iso_spec_operator'((mod), yfx,  400).
'$lgt_iso_spec_operator'((<<),  yfx,  400).
'$lgt_iso_spec_operator'((>>),  yfx,  400).

'$lgt_iso_spec_operator'((**),  xfx,  200).

'$lgt_iso_spec_operator'((^),   xfy,  200).

'$lgt_iso_spec_operator'((+),    fy,  200).
'$lgt_iso_spec_operator'((-),    fy,  200).
'$lgt_iso_spec_operator'((\),    fy,  200).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  table of ISO Prolog specified flags
%
%  (used for portability checking)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% '$lgt_iso_spec_flag'(?atom)

'$lgt_iso_spec_flag'(bounded).
'$lgt_iso_spec_flag'(max_integer).
'$lgt_iso_spec_flag'(min_integer).
'$lgt_iso_spec_flag'(integer_rounding_function).
'$lgt_iso_spec_flag'(max_arity).
'$lgt_iso_spec_flag'(char_conversion).
'$lgt_iso_spec_flag'(debug).
'$lgt_iso_spec_flag'(double_quotes).
'$lgt_iso_spec_flag'(unknown).

% the following flags are not part of the ISO/IEC 13211-1 Prolog standard
% but can be found either on the Core Revision standardization proposal or,
% more important, these flags are de facto standard

'$lgt_iso_spec_flag'(dialect).
'$lgt_iso_spec_flag'(version_data).



% '$lgt_iso_spec_flag_value'(+atom, @nonvar)

'$lgt_iso_spec_flag_value'(bounded, true) :- !.
'$lgt_iso_spec_flag_value'(bounded, false) :- !.

'$lgt_iso_spec_flag_value'(max_integer, Value) :-
	integer(Value).

'$lgt_iso_spec_flag_value'(min_integer, Value) :-
	integer(Value).

'$lgt_iso_spec_flag_value'(integer_rounding_function, toward_zero) :- !.
'$lgt_iso_spec_flag_value'(integer_rounding_function, down) :- !.

'$lgt_iso_spec_flag_value'(max_arity, Value) :-
	integer(Value).

'$lgt_iso_spec_flag_value'(char_conversion, on) :- !.
'$lgt_iso_spec_flag_value'(char_conversion, off) :- !.

'$lgt_iso_spec_flag_value'(debug, on) :- !.
'$lgt_iso_spec_flag_value'(debug, off) :- !.

'$lgt_iso_spec_flag_value'(double_quotes, atom) :- !.
'$lgt_iso_spec_flag_value'(double_quotes, chars) :- !.
'$lgt_iso_spec_flag_value'(double_quotes, codes) :- !.

'$lgt_iso_spec_flag_value'(unknown, error) :- !.
'$lgt_iso_spec_flag_value'(unknown, warning) :- !.
'$lgt_iso_spec_flag_value'(unknown, fail) :- !.

'$lgt_iso_spec_flag_value'(dialect, Value) :-
	atom(Value).

'$lgt_iso_spec_flag_value'(version_data, Value) :-
	compound(Value).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  tables of ISO Prolog specified read and write term options
%
%  (used for portability checking)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% '$lgt_iso_spec_open_stream_option'(@nonvar)

'$lgt_iso_spec_open_stream_option'(type(Type)) :-
	(	var(Type) ->
		true
	;	Type == text ->
		true
	;	Type == binary
	).
'$lgt_iso_spec_open_stream_option'(reposition(Boolean)) :-
	(	var(Boolean) ->
		true
	;	Boolean == true ->
		true
	;	Boolean == false
	).
'$lgt_iso_spec_open_stream_option'(alias(Alias)) :-
	(	var(Alias) ->
		true
	;	atom(Alias)
	).
'$lgt_iso_spec_open_stream_option'(eof_action(Action)) :-
	(	var(Action) ->
		true
	;	Action == error ->
		true
	;	Action == eof_code ->
		true
	;	Action == reset
	).


% '$lgt_iso_spec_read_term_option'(@nonvar)

'$lgt_iso_spec_read_term_option'(variables(_)).
'$lgt_iso_spec_read_term_option'(variable_names(_)).
'$lgt_iso_spec_read_term_option'(singletons(_)).


% '$lgt_iso_spec_write_term_option'(@nonvar)

'$lgt_iso_spec_write_term_option'(quoted(Boolean)) :-
	(	var(Boolean) ->
		true
	;	Boolean == true ->
		true
	;	Boolean == false
	).
'$lgt_iso_spec_write_term_option'(ignore_ops(Boolean)) :-
	(	var(Boolean) ->
		true
	;	Boolean == true ->
		true
	;	Boolean == false
	).
'$lgt_iso_spec_write_term_option'(numbervars(Boolean)) :-
	(	var(Boolean) ->
		true
	;	Boolean == true ->
		true
	;	Boolean == false
	).
'$lgt_iso_spec_write_term_option'(variable_names(Pairs)) :-
	'$lgt_is_list'(Pairs),
	forall(
		'$lgt_member'(Pair, Pairs),
		(Pair = (Name = Variable), atom(Name), var(Variable))
	).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  table of ISO Prolog specified built-in database predicates
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_iso_database_predicate'(@callble)

'$lgt_iso_database_predicate'(abolish(_)).
'$lgt_iso_database_predicate'(asserta(_)).
'$lgt_iso_database_predicate'(assertz(_)).
'$lgt_iso_database_predicate'(clause(_, _)).
'$lgt_iso_database_predicate'(retract(_)).
'$lgt_iso_database_predicate'(retractall(_)).
'$lgt_iso_database_predicate'(current_predicate(_)).
'$lgt_iso_database_predicate'(predicate_property(_, _)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  table of Logtalk operators
%
%  (used for portability checking)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_logtalk_spec_operator'(?atom, ?atom, ?integer)

% message-sending operators
'$lgt_logtalk_spec_operator'((::), xfy, 600).
'$lgt_logtalk_spec_operator'((::),  fy, 600).
% "super" call operator
'$lgt_logtalk_spec_operator'((^^),  fy, 600).
% mode operators
'$lgt_logtalk_spec_operator'((?),   fy, 200).
'$lgt_logtalk_spec_operator'((@),   fy, 200).
'$lgt_logtalk_spec_operator'((++),  fy, 200).
'$lgt_logtalk_spec_operator'((--),  fy, 200).
% alias operator
'$lgt_logtalk_spec_operator'((as), xfx, 700).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Multi-threading support
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_init_object_message_queue'(+atom)
%
% creates a message queue for an object given its prefix
% (assume that any exception generated is due to the fact that the message
% queue already exists, which may happen when reloading threaded objects;
% there is no standard predicate for testing message queue existence)

'$lgt_init_object_message_queue'(ObjPrefix) :-
	catch(message_queue_create(_, [alias(ObjPrefix)]), _, true).



% '$lgt_threaded_wait_synch_ctg'(+mutex_identifier, @term, @object_identifier)
%
% calls to the threaded_wait/1 predicate from synchronized category predicates

'$lgt_threaded_wait_synch_ctg'(Mutex, Msg, This) :-
	'$lgt_current_object_'(This, Prefix, _, _, _, _, _, _, _, _, _),
	mutex_unlock(Mutex),
	'$lgt_threaded_wait'(Msg, Prefix),
	mutex_lock(Mutex).



% '$lgt_threaded_wait_synch'(+mutex_identifier, @term, +entity_prefix)
%
% calls to the threaded_wait/1 predicate from synchronized object predicates

'$lgt_threaded_wait_synch'(Mutex, Msg, Prefix) :-
	mutex_unlock(Mutex),
	'$lgt_threaded_wait'(Msg, Prefix),
	mutex_lock(Mutex).



% '$lgt_threaded_wait_ctg'(@term, @object_identifier)

'$lgt_threaded_wait_ctg'(Msg, This) :-
	'$lgt_current_object_'(This, Prefix, _, _, _, _, _, _, _, _, _),
	'$lgt_threaded_wait'(Msg, Prefix).



% '$lgt_threaded_wait'(@term, +entity_prefix)

'$lgt_threaded_wait'(Msg, Prefix) :-
	var(Msg),
	!,
	thread_get_message(Prefix, '$lgt_notification'(Msg)).

'$lgt_threaded_wait'([], _) :-
	!.

'$lgt_threaded_wait'([Msg| Msgs], Prefix) :-
	!,
	thread_get_message(Prefix, '$lgt_notification'(Msg)),
	'$lgt_threaded_wait'(Msgs, Prefix).

'$lgt_threaded_wait'(Msg, Prefix) :-
	thread_get_message(Prefix, '$lgt_notification'(Msg)).



% '$lgt_threaded_notify_ctg'(@term, @object_identifier)

'$lgt_threaded_notify_ctg'(Msg, This) :-
	'$lgt_current_object_'(This, Prefix, _, _, _, _, _, _, _, _, _),
	'$lgt_threaded_notify'(Msg, Prefix).



% '$lgt_threaded_notify'(@term, +entity_prefix)

'$lgt_threaded_notify'(Msg, Prefix) :-
	var(Msg),
	!,
	thread_send_message(Prefix, '$lgt_notification'(Msg)).

'$lgt_threaded_notify'([], _) :-
	!.

'$lgt_threaded_notify'([Msg| Msgs], Prefix) :-
	!,
	thread_send_message(Prefix, '$lgt_notification'(Msg)),
	'$lgt_threaded_notify'(Msgs, Prefix).

'$lgt_threaded_notify'(Msg, Prefix) :-
	thread_send_message(Prefix, '$lgt_notification'(Msg)).



% '$lgt_threaded_ignore'(@term, @callable, @execution_context)
%
% the thread is only created if the original goal is callable;
% this prevents programming errors going unnoticed

'$lgt_threaded_ignore'(Goal, TGoal, ExCtx) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_ignore(Goal), ExCtx)),
	thread_create(catch(TGoal, _, true), _, [detached(true)]).



% '$lgt_threaded_call'(@term, @callable, @execution_context)
%
% the thread is only created if the original goal is callable; this prevents
% programming errors going unnoticed until we try to retrieve the first answer

'$lgt_threaded_call'(Goal, TGoal, ExCtx) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_call(Goal), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	thread_create('$lgt_mt_non_det_goal'(Queue, Goal, TGoal, This, Self, []), Id, []),
	thread_send_message(Queue, '$lgt_thread_id'(call, Goal, This, Self, [], Id)).



% '$lgt_threaded_once'(@term, @callable, @execution_context)
%
% the thread is only created if the original goal is callable; this prevents
% programming errors going unnoticed until we try to retrieve the first answer

'$lgt_threaded_once'(Goal, TGoal, ExCtx) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_once(Goal), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	thread_create('$lgt_mt_det_goal'(Queue, Goal, TGoal, This, Self, []), Id, []),
	thread_send_message(Queue, '$lgt_thread_id'(once, Goal, This, Self, [], Id)).



% '$lgt_threaded_call_tagged'(@term, @callable, @execution_context, -nonvar)
%
% the thread is only created if the original goal is callable and the tag is unbound;
% this prevents programming errors going unnoticed until we try to retrieve the first answer

'$lgt_threaded_call_tagged'(Goal, TGoal, ExCtx, Tag) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_call(Goal, Tag), ExCtx)),
	'$lgt_check'(var, Tag, logtalk(threaded_call(Goal, Tag), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	'$lgt_new_threaded_tag'(Tag),
	thread_create('$lgt_mt_non_det_goal'(Queue, Goal, TGoal, This, Self, Tag), Id, []),
	thread_send_message(Queue, '$lgt_thread_id'(call, Goal, This, Self, Tag, Id)).



% '$lgt_threaded_once_tagged'(@term, @callable, @execution_context, -nonvar)
%
% the thread is only created if the original goal is callable and the tag is unbound;
% this programming errors going unnoticed until we try to retrieve the answer

'$lgt_threaded_once_tagged'(Goal, TGoal, ExCtx, Tag) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_once(Goal, Tag), ExCtx)),
	'$lgt_check'(var, Tag, logtalk(threaded_once(Goal, Tag), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	'$lgt_new_threaded_tag'(Tag),
	thread_create('$lgt_mt_det_goal'(Queue, Goal, TGoal, This, Self, Tag), Id, []),
	thread_send_message(Queue, '$lgt_thread_id'(once, Goal, This, Self, Tag, Id)).



% '$lgt_mt_det_goal'(+message_queue_identifier, +callable, +callable, +object_identifier, +object_identifier, @nonvar)
%
% processes a deterministic message received by an object's message queue

'$lgt_mt_det_goal'(Queue, Goal, TGoal, This, Self, Tag) :-
	thread_self(Id),
	(	catch(TGoal, Error, true) ->
		(	var(Error) ->
			thread_send_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, success, Id))
		;	thread_send_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, Error, Id))
		)
	;	thread_send_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, failure, Id))
	).



% '$lgt_mt_non_det_goal'(+atom, +callable, +callable, +object_identifier, +object_identifier, @nonvar)
%
% processes a non-deterministic message received by an object's message queue

'$lgt_mt_non_det_goal'(Queue, Goal, TGoal, This, Self, Tag) :-
	thread_self(Id),
	(	catch(TGoal, Error, true),
		(	var(Error) ->
			thread_send_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, success, Id)),
			thread_get_message(Message),
			(	Message == '$lgt_next' ->
				% backtrack to the catch(Goal, ...) to try to find an alternative solution
				fail
			;	% otherwise assume Message = '$lgt_exit' and terminate thread
				true
			)
		;	thread_send_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, Error, Id))
		)
	;	% no (more) solutions
		thread_send_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, failure, Id))
	).



% '$lgt_threaded_peek'(+callable, @execution_context)

'$lgt_threaded_peek'(Goal, ExCtx) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_peek(Goal), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	(	% first check if there is a thread running for proving the goal before proceeding
		thread_peek_message(Queue, '$lgt_thread_id'(_, Goal, This, Self, [], Id)) ->
		% answering thread exists; go ahead and peek a solution
		thread_peek_message(Queue, '$lgt_reply'(Goal, This, Self, [], _, Id))
	;	% answering thread don't exist; generate an exception
		throw(error(existence_error(thread, This), logtalk(threaded_peek(Goal), ExCtx)))
	).


% '$lgt_threaded_peek_tagged'(+callable, @execution_context, @nonvar)

'$lgt_threaded_peek_tagged'(Goal, ExCtx, Tag) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_peek(Goal, Tag), ExCtx)),
	'$lgt_check'(nonvar, Tag, logtalk(threaded_peek(Goal, Tag), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	(	% first check if there is a thread running for proving the goal before proceeding
		thread_peek_message(Queue, '$lgt_thread_id'(_, Goal, This, Self, Tag, Id)) ->
		% answering thread exists; go ahead and peek a solution
		thread_peek_message(Queue, '$lgt_reply'(Goal, This, Self, Tag, _, Id))
	;	% answering thread don't exist; generate an exception
		throw(error(existence_error(thread, This), logtalk(threaded_peek(Goal, Tag), ExCtx)))
	).



% '$lgt_threaded_cancel_tagged'(@nonvar, @execution_context)

'$lgt_threaded_cancel_tagged'(Tag, ExCtx) :-
	'$lgt_check'(nonvar, Tag, logtalk(threaded_cancel(Tag), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	(	thread_peek_message(Queue, '$lgt_thread_id'(_, _, This, Self, Tag, Id)) ->
		% answering thread exists; go ahead and cancel it
		thread_get_message(Queue, '$lgt_thread_id'(_, _, This, Self, Tag, Id)),
		% the thread may be suspended waiting for a request for an alternative proof; tell it to exit
		thread_send_message(Id, '$lgt_exit'),
		% but the thread may also be busy computing a solution; cancel it
		catch(thread_signal(Id, throw('$lgt_aborted')), _, true),
		thread_join(Id, _),
		% delete any thread reply that is pending retrievel
		forall(
			thread_peek_message(Queue, '$lgt_reply'(_, This, Self, Tag, _, Id)),
			thread_get_message(Queue, '$lgt_reply'(_, This, Self, Tag, _, Id))
		)
	;	% assume thread already canceled
		true
	).



% '$lgt_threaded_exit'(+callable, @execution_context)

'$lgt_threaded_exit'(Goal, ExCtx) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_exit(Goal), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	(	% first check if there is a thread running for proving the goal before proceeding
		thread_peek_message(Queue, '$lgt_thread_id'(Type, Goal, This, Self, [], Id)) ->
		% answering thread exists; go ahead and retrieve the solution(s)
		thread_get_message(Queue, '$lgt_thread_id'(Type, Goal, This, Self, [], Id)),
		(	Type == (once) ->
			setup_call_cleanup(
				true,
				'$lgt_mt_det_reply'(Queue, Goal, This, Self, [], Id),
				thread_join(Id, _)
			)
		;	setup_call_cleanup(
				true,
				'$lgt_mt_non_det_reply'(Queue, Goal, This, Self, [], Id),
				((	thread_property(Id, status(running)) ->
					% thread still running, suspended waiting for a request to an alternative proof; tell it to exit
					thread_send_message(Id, '$lgt_exit')
				;	true
				),
				thread_join(Id, _))
			)
		)
	;	% answering thread don't exist; generate an exception (failing is not an option as it could simply mean goal failure)
		throw(error(existence_error(thread, This), logtalk(threaded_exit(Goal), ExCtx)))
	).



% '$lgt_threaded_exit_tagged'(+callable, @execution_context, @nonvar)

'$lgt_threaded_exit_tagged'(Goal, ExCtx, Tag) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_exit(Goal, Tag), ExCtx)),
	'$lgt_check'(nonvar, Tag, logtalk(threaded_exit(Goal, Tag), ExCtx)),
	'$lgt_execution_context'(ExCtx, _, _, This, Self, _, _),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	(	% first check if there is a thread running for proving the goal before proceeding
		thread_peek_message(Queue, '$lgt_thread_id'(Type, Goal, This, Self, Tag, Id)) ->
		% answering thread exists; go ahead and retrieve the solution(s)
		thread_get_message(Queue, '$lgt_thread_id'(Type, Goal, This, Self, Tag, Id)),
		(	Type == (once) ->
			setup_call_cleanup(
				true,
				'$lgt_mt_det_reply'(Queue, Goal, This, Self, Tag, Id),
				thread_join(Id, _)
			)
		;	setup_call_cleanup(
				true,
				'$lgt_mt_non_det_reply'(Queue, Goal, This, Self, Tag, Id),
				((	thread_property(Id, status(running)) ->
					% thread still running, suspended waiting for a request to an alternative proof; tell it to exit
					thread_send_message(Id, '$lgt_exit')
				;	true
				),
				thread_join(Id, _))
			)
		)
	;	% answering thread don't exist; generate an exception (failing is not an option as it could simply mean goal failure)
		throw(error(existence_error(thread, This), logtalk(threaded_exit(Goal, Tag), ExCtx)))
	).



% return the solution found

'$lgt_mt_det_reply'(Queue, Goal, This, Self, Tag, Id) :-
	thread_get_message(Queue, '$lgt_reply'(Reply, This, Self, Tag, Result, Id)),
	(	Result == success ->
		Goal = Reply
	;	Result == failure ->
		fail
	;	throw(Result)
	).



% return current solution; on backtracking, ask working thread for and get from it the next solution

'$lgt_mt_non_det_reply'(Queue, Goal, This, Self, Tag, Id) :-
	thread_get_message(Queue, '$lgt_reply'(Reply, This, Self, Tag, Result, Id)),
	(	Result == success ->
		Goal = Reply
	;	Result == failure ->
		!,
		fail
	;	throw(Result)
	).

'$lgt_mt_non_det_reply'(Queue, Goal, This, Self, Tag, Id) :-
	catch(thread_send_message(Id, '$lgt_next'), _, fail),
	'$lgt_mt_non_det_reply'(Queue, Goal, This, Self, Tag, Id).



% '$lgt_threaded_engine_create'(@term, @term, @callable, +object_identifier, ?nonvar)
%
% the engine thread is only created if the original goal is callable; this prevents
% programming errors going unnoticed until we try to retrieve the first answer

'$lgt_threaded_engine_create'(AnswerTemplate, Goal, TGoal, ExCtx, Engine) :-
	'$lgt_check'(qualified_callable, Goal, logtalk(threaded_engine_create(AnswerTemplate, Goal, Engine), ExCtx)),
	with_mutex(
		'$lgt_engines',
		'$lgt_threaded_engine_create_protected'(AnswerTemplate, Goal, TGoal, ExCtx, Engine)
	).


'$lgt_threaded_engine_create_protected'(AnswerTemplate, Goal, TGoal, ExCtx, Engine) :-
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _),
	(	var(Engine) ->
		'$lgt_new_threaded_engine_tag'(Engine)
	;	'$lgt_current_engine_'(This, Engine, _, _) ->
		throw(error(permission_error(create, engine, Engine), logtalk(threaded_engine_create(AnswerTemplate, Goal, Engine), ExCtx)))
	;	true
	),
	'$lgt_current_object_'(This, ThisQueue, _, _, _, _, _, _, _, _, _),
	message_queue_create(TermQueue),
	thread_create('$lgt_mt_engine_goal'(ThisQueue, TermQueue, AnswerTemplate, TGoal, Engine, Id), Id, []),
	assertz('$lgt_current_engine_'(This, Engine, TermQueue, Id)).


% compute a solution for the engine goal and return it; note that the thread
% always terminates with a status of "true" when an exception occurs or there
% aren't any more solutions for the engine goal
%
% we use the object queue to store a '$lgt_engine_term_queue'/3 term with the
% engine name and the engine term queue to workaround random timing issues when
% accessing the '$lgt_current_engine_'/4 dynamic predicate that can result in
% unexpected errors

'$lgt_mt_engine_goal'(ThisQueue, TermQueue, Answer, Goal, Engine, Id) :-
	thread_send_message(ThisQueue, '$lgt_engine_term_queue'(Engine, TermQueue, Id)),
	(	setup_call_cleanup(true, catch(Goal, Error, true), Deterministic = true),
		(	var(Error) ->
			(	var(Deterministic) ->
				thread_send_message(ThisQueue, '$lgt_answer'(Engine, Id, Answer, success)),
				thread_get_message(Message),
				% if Message = '$lgt_next', backtrack to try to find an alternative solution
				Message == '$lgt_aborted'
			;	% no (more) solutions after this one
				thread_send_message(ThisQueue, '$lgt_answer'(Engine, Id, Answer, final))
			)
		;	Error == '$lgt_aborted' ->
			% we are destroying the engine
			true
		;	% engine goal error
			thread_send_message(ThisQueue, '$lgt_answer'(Engine, Id, _, error(Error)))
		)
	;	% no (more) solutions
		thread_send_message(ThisQueue, '$lgt_answer'(Engine, Id, _, failure))
	).



% '$lgt_current_engine'(@object_identifier, ?nonvar)
%
% we cannot compile threaded_engine/1 calls into '$lgt_current_engine_'/2 calls
% as the last two arguments would cause problems with bagof/3 and setof/3 calls

'$lgt_current_engine'(This, Engine) :-
	'$lgt_current_engine_'(This, Engine, _, _).



% '$lgt_threaded_engine_next'(@nonvar, ?term, @execution_context)
%
% blocks until an answer (either an engine goal solution or a solution
% posted by a call to threaded_engine_yield/1) becomes available

'$lgt_threaded_engine_next'(Engine, Answer, ExCtx) :-
	(	var(Engine) ->
		throw(error(instantiation_error, logtalk(threaded_engine_next(Engine, Answer), ExCtx)))
	;	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _),
		'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
		% first check if the engine is running
		'$lgt_current_engine_'(This, Engine, _, Id) ->
		% engine exists; go ahead and retrieve an answer
		'$lgt_mt_engine_reply'(Queue, Answer, Engine, Id, ExCtx)
	;	% engine does not exist
		throw(error(existence_error(engine, Engine), logtalk(threaded_engine_next(Engine, Answer), ExCtx)))
	).


% return current answer and start computing the next one
% if the engine goal succeeded non-deterministically
%
% after all solutions are consumed, or in case of error,
% ensure that the all next calls will fail

'$lgt_mt_engine_reply'(Queue, Answer, Engine, Id, ExCtx) :-
	thread_get_message(Queue, '$lgt_answer'(Engine, Id, Reply, Result)),
	(	Result == success ->
		thread_send_message(Id, '$lgt_next'),
		Answer = Reply
	;	Result == final ->
		thread_send_message(Queue, '$lgt_answer'(Engine, Id, '$lgt_done'(final), failure)),
		Answer = Reply
	;	Result == failure ->
		thread_send_message(Queue, '$lgt_answer'(Engine, Id, '$lgt_done'(failure), failure)),
		fail
	;	Result = error(Error),
		thread_send_message(Queue, '$lgt_answer'(Engine, Id, '$lgt_done'(error), failure)),
		throw(error(Error, logtalk(threaded_engine_next(Engine,Answer),ExCtx)))
	).



% '$lgt_threaded_engine_next_reified'(@nonvar, ?term, @execution_context)
%
% blocks until an answer (either an engine goal solution or a solution
% posted by a call to threaded_engine_yield/1) becomes available

'$lgt_threaded_engine_next_reified'(Engine, Answer, ExCtx) :-
	(	var(Engine) ->
		throw(error(instantiation_error, logtalk(threaded_engine_next_reified(Engine, Answer), ExCtx)))
	;	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _),
		'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
		% first check if the engine is running
		'$lgt_current_engine_'(This, Engine, _, Id) ->
		% engine exists; go ahead and retrieve an answer
		'$lgt_mt_engine_reply_reified'(Queue, Answer, Engine, Id)
	;	% engine does not exist
		throw(error(existence_error(engine, Engine), logtalk(threaded_engine_next_reified(Engine, Answer), ExCtx)))
	).


% return current answer and start computing the next one
% if the engine goal succeeded non-deterministically
%
% after all solutions are consumed, or in case of error,
% ensure that the all next calls will fail

'$lgt_mt_engine_reply_reified'(Queue, Answer, Engine, Id) :-
	thread_get_message(Queue, '$lgt_answer'(Engine, Id, Reply, Result)),
	(	Result == success ->
		thread_send_message(Id, '$lgt_next'),
		Answer = the(Reply)
	;	Result == final ->
		thread_send_message(Queue, '$lgt_answer'(Engine, Id, '$lgt_done'(final), failure)),
		Answer = the(Reply)
	;	Result == failure ->
		thread_send_message(Queue, '$lgt_answer'(Engine, Id, '$lgt_done'(failure), failure)),
		Answer = no
	;	Result = error(Error),
		thread_send_message(Queue, '$lgt_answer'(Engine, Id, '$lgt_done'(error), failure)),
		Answer = exception(Error)
	).



% '$lgt_threaded_engine_self'(@object_identifier, ?nonvar)
%
% fails if not called from within an engine

'$lgt_threaded_engine_self'(This, Engine) :-
	thread_self(Id),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	thread_peek_message(Queue, '$lgt_engine_term_queue'(Engine0, _, Id)),
	!,
	Engine = Engine0.



% '$lgt_threaded_engine_yield'(@term, @object_identifier)
%
% fails if not called from within an engine;
% blocks until the returned answer is consumed

'$lgt_threaded_engine_yield'(Answer, This) :-
	thread_self(Id),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	thread_peek_message(Queue, '$lgt_engine_term_queue'(Engine, _, Id)),
	thread_send_message(Queue, '$lgt_answer'(Engine, Id, Answer, success)),
	thread_get_message(_).



% '$lgt_threaded_engine_post'(@nonvar, @term, @execution_context)

'$lgt_threaded_engine_post'(Engine, Term, ExCtx) :-
	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _),
	(	var(Engine) ->
		throw(error(instantiation_error, logtalk(threaded_engine_post(Engine, Term), ExCtx)))
	;	% first check if the engine is running
		'$lgt_current_engine_'(This, Engine, TermQueue, _) ->
		% engine exists; go ahead and post the message in its mailbox
		thread_send_message(TermQueue, Term)
	;	% engine does not exist
		throw(error(existence_error(engine, Engine), logtalk(threaded_engine_post(Engine, Term), ExCtx)))
	).



% '$lgt_threaded_engine_fetch'(?term, @object_identifier)
%
% fails if not called from within an engine or if we are
% destroying a running engine

'$lgt_threaded_engine_fetch'(Term, This) :-
	thread_self(Id),
	'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
	(	% check if calling from within an engine
		thread_peek_message(Queue, '$lgt_engine_term_queue'(_, TermQueue, Id)) ->
		% engine exists; go ahead and retrieve a message from its mailbox
		thread_get_message(TermQueue, Term),
		Term \== '$lgt_aborted'
	;	% engine does not exist
		fail
	).



% '$lgt_threaded_engine_destroy'(@nonvar, @execution_context)
%
% when the engine thread is still running, we first put a throw/1 goal in the
% thread signal queue and then send messages to both the thread queue and the
% engine term queue to resume the engine goal if suspended waiting for either
% a request for the next solution or a term to be processed

'$lgt_threaded_engine_destroy'(Engine, ExCtx) :-
	with_mutex(
		'$lgt_engines',
		'$lgt_threaded_engine_destroy_protected'(Engine, ExCtx)
	).


'$lgt_threaded_engine_destroy_protected'(Engine, ExCtx) :-
	(	var(Engine) ->
		throw(error(instantiation_error, logtalk(threaded_engine_destroy(Engine), ExCtx)))
	;	'$lgt_execution_context'(ExCtx, _, _, This, _, _, _),
		'$lgt_current_object_'(This, Queue, _, _, _, _, _, _, _, _, _),
		retract('$lgt_current_engine_'(This, Engine, TermQueue, Id)) ->
		(	thread_property(Id, status(running)) ->
			% the engine thread may be suspended waiting for a client request to
			% compute the next solution; send it a '$lgt_aborted' term to prevent
			% further requests for backtracking into the next solution; note that
			% the engine thread and therefore its queue may no longer exist
			catch(thread_send_message(Id, '$lgt_aborted'), _, true),
			% send the '$lgt_aborted' term to the engine term queue to make any further
			% threaded_engine_fetch/1 calls fail; this queue is explicitly created and
			% destroyed and thus we can be sure it exists
			thread_send_message(TermQueue, '$lgt_aborted')
		;	true
		),
		% ensure that thread is terminated
		catch(thread_signal(Id, throw('$lgt_aborted')), _, true),
		thread_join(Id, _),
		message_queue_destroy(TermQueue),
		% remove any non-consumed answer
		(	thread_peek_message(Queue, '$lgt_answer'(Engine, Id, _, _)) ->
			thread_get_message(Queue, '$lgt_answer'(Engine, Id, _, _))
		;	true
		),
		% remove the answer that ensures threaded_engine_next/2 and threaded_engine_next_reified/2
		% return failures after consuming all solutions if present
		(	thread_peek_message(Queue, '$lgt_answer'(Engine, Id, _, failure)) ->
			thread_get_message(Queue, '$lgt_answer'(Engine, Id, _, failure))
		;	true
		),
		% remove the cache entry for the engine term queue handle
		(	thread_peek_message(Queue, '$lgt_engine_term_queue'(Engine, _, Id)) ->
			thread_get_message(Queue, '$lgt_engine_term_queue'(Engine, _, Id))
		;	true
		)
	;	% engine doesn't exist
		throw(error(existence_error(engine, Engine), logtalk(threaded_engine_destroy(Engine), ExCtx)))
	).



% '$lgt_threaded_and'(-var, +callable, +list)
%
% implements the threaded/1 built-in predicate when the argument is a conjunction
%
% each call uses a dedicated message queue that is destroyed after joining the
% threads that are created to run the individual goals

'$lgt_threaded_and'(Queue, MTGoals, Results) :-
	message_queue_create(Queue),
	call(MTGoals),
	'$lgt_mt_threaded_and_exit'(Queue, Results).



% '$lgt_threaded_or'(-var, +callable, +list)
%
% implements the threaded/1 built-in predicate when the argument is a disjunction
%
% each call uses a dedicated message queue that is destroyed after joining the
% threads that are created to run the individual goals

'$lgt_threaded_or'(Queue, MTGoals, Results) :-
	message_queue_create(Queue),
	call(MTGoals),
	'$lgt_mt_threaded_or_exit'(Queue, Results).



% '$lgt_threaded_goal'(+callable, -list(var), +message_queue_identifier, --thread_identifier)
%
% implements the call to an individual goal by the threaded/1 built-in predicate
%
% the at_exit/1 is used to ensure that the individual thread result is sent to
% the dedicated message queue of the parent threaded/1 predicate call

'$lgt_threaded_goal'(TGoal, TVars, Queue, Id) :-
	term_variables(TGoal, TVars),
	thread_create(
		'$lgt_mt_threaded_call'(TGoal, TVars, Queue, Id),
		Id,
		[at_exit('$lgt_mt_exit_handler'(Id, Queue))]
	).



% '$lgt_mt_threaded_call'(+callable, +list(var), +message_queue_identifier)
%
% proves an individual goal from a threaded/1 predicate call and sends the
% result back to the message queue associated to the call; assuming that
% the thread is not interrupted, its final status is set to "true"; if the
% thread is interrupted before sending its result, its at_exit/1 option goal
% ensures that a result is still sent to the parent threaded/1 predicate call
% dedicated message queue

'$lgt_mt_threaded_call'(TGoal, TVars, Queue, Id) :-
	(	call(TGoal) ->
		thread_send_message(Queue, '$lgt_result'(Id, true(TVars)))
	;	thread_send_message(Queue, '$lgt_result'(Id, false))
	).



% '$lgt_mt_exit_handler'(@thread_identifier, +message_queue_identifier)
%
% exit handler for threaded/1 individual thread calls; an error generated
% by the thread_send_message/2 call is interpreted as meaning that the
% master/parent thread queue no longer exists leading to the detaching of
% the worker thread

'$lgt_mt_exit_handler'(Id, Queue) :-
	(	thread_property(Id, status(exception(Error))) ->
		catch(thread_send_message(Queue, '$lgt_result'(Id, exception(Error))), _, thread_detach(Id))
	;	true
	).



% '$lgt_mt_threaded_and_exit'(+message_queue_identifier, +list)
%
% retrieves the result of proving a conjunction of goals using a threaded/1 predicate call
% by collecting the individual thread results posted to the master thread message queue
% until all individual goals succeeds or one them fails or throws an exception

'$lgt_mt_threaded_and_exit'(Queue, Results) :-
	thread_get_message(Queue, '$lgt_result'(Id, Result)),
	'$lgt_mt_threaded_and_exit'(Result, Id, Queue, Results).


'$lgt_mt_threaded_and_exit'(exception(Error), Id, Queue, Results) :-
	'$lgt_mt_threaded_and_record_result'(Results, Id, exception(Error)),
	'$lgt_mt_threaded_call_cancel'(Queue, Results),
	throw(Error).

'$lgt_mt_threaded_and_exit'(true(TVars), Id, Queue, Results) :-
	(	'$lgt_mt_threaded_and_add_result'(Results, Id, TVars, Continue) ->
		(	Continue == false ->
			% all thread goals succeeded
			'$lgt_mt_threaded_call_join'(Results, Queue)
		;	% some thread goal results are still pending
			'$lgt_mt_threaded_and_exit'(Queue, Results)
		)
	;	% adding a successful result can fail if the individual thread goals
		% are not independent (i.e., they share variables with the same or
		% partially the same role leading to unification failures)
		'$lgt_mt_threaded_and_exit'(false, Id, Queue, Results)
	).

'$lgt_mt_threaded_and_exit'(false, Id, Queue, Results) :-
	'$lgt_mt_threaded_and_record_result'(Results, Id, false),
	'$lgt_mt_threaded_call_cancel'(Queue, Results),
	fail.



% '$lgt_mt_threaded_and_add_result'(+list, +thread_identifier, +list, -atom)
%
% adds the result of proving a goal and checks if all other goals have succeeded

'$lgt_mt_threaded_and_add_result'([id(Id, TVars, true)| Results], Id, TVars, Continue) :-
	!,
	(	var(Continue) ->
		% we still don't know if there are any pending results
		'$lgt_mt_threaded_continue'(Results, Continue)
	;	true
	).

'$lgt_mt_threaded_and_add_result'([id(_, _, Done)| Results], Id, TVars, Continue) :-
	(	var(Done) ->
		% we found a thread whose result is still pending
		Continue = true
	;	% otherwise continue examining the remaining thread results
		true
	),
	'$lgt_mt_threaded_and_add_result'(Results, Id, TVars, Continue).



% '$lgt_mt_threaded_and_record_result'(+list, +thread_identifier, +callable)
%
% records a thread goal result

'$lgt_mt_threaded_and_record_result'([id(Id, _, Result)| _], Id, Result) :-
	!.

'$lgt_mt_threaded_and_record_result'([_| Results], Id, Result) :-
	'$lgt_mt_threaded_and_record_result'(Results, Id, Result).



% '$lgt_mt_threaded_or_exit'(+list)
%
% retrieves the result of proving a disjunction of goals using a threaded/1 predicate
% call by collecting the individual thread results posted to the call message queue
% until one of the individual goals succeeds or all goals fail or throw an exception

'$lgt_mt_threaded_or_exit'(Queue, Results) :-
	thread_get_message(Queue, '$lgt_result'(Id, Result)),
	'$lgt_mt_threaded_or_exit'(Result, Id, Queue, Results).


'$lgt_mt_threaded_or_exit'(exception(Error), Id, Queue, Results) :-
	'$lgt_mt_threaded_or_record_exception'(Results, Id, exception(Error), Continue),
	(	Continue == true ->
		% some thread goal results are still pending
		'$lgt_mt_threaded_or_exit'(Queue, Results)
	;	% no thread goal succeeded and at least one thread resulted in an exception
		'$lgt_mt_threaded_call_join'(Results, Queue),
		throw(Error)
	).

'$lgt_mt_threaded_or_exit'(true(TVars), Id, Queue, Results) :-
	'$lgt_mt_threaded_or_exit_unify'(Results, Id, TVars),
	'$lgt_mt_threaded_call_cancel'(Queue, Results).

'$lgt_mt_threaded_or_exit'(false, Id, Queue, Results) :-
	'$lgt_mt_threaded_or_record_failure'(Results, Id, Continue),
	(	Continue == true ->
		% some thread goal results are still pending
		'$lgt_mt_threaded_or_exit'(Queue, Results)
	;	% all goals terminated
		'$lgt_mt_threaded_call_join'(Results, Queue),
		(	'$lgt_member'(id(_, _, exception(Error)), Results) ->
			throw(Error)
		;	% all threads failed
			fail
		)
	).



% unifies the successful thread goal result with the original call

'$lgt_mt_threaded_or_exit_unify'([id(Id, TVars, true)| _], Id, TVars) :-
	!.

'$lgt_mt_threaded_or_exit_unify'([_| Results], Id, TVars) :-
	'$lgt_mt_threaded_or_exit_unify'(Results, Id, TVars).



% '$lgt_mt_threaded_or_record_exception'(+list, +thread_identifier, @nonvar, -atom)
%
% records a thread goal exception and checks if all other thread goals have
% failed or thrown exceptions

'$lgt_mt_threaded_or_record_exception'([id(Id, _, Result)| Results], Id, Exception, Continue) :-
	!,
	(	var(Result) ->
		Result = Exception
	;	% assume thread cancel exception; ignore it as the thread terminated
		% before receiving the signal with its result already recorded
		true
	),
	(	var(Continue) ->
		% we still don't know if there are any pending results
		'$lgt_mt_threaded_continue'(Results, Continue)
	;	true
	).

'$lgt_mt_threaded_or_record_exception'([id(_, _, Done)| Results], Id, Exception, Continue) :-
	(	var(Done) ->
		% we found a thread whose result is still pending
		Continue = true
	;	% otherwise continue examining the remaining thread results
		true
	),
	'$lgt_mt_threaded_or_record_exception'(Results, Id, Exception, Continue).



% '$lgt_mt_threaded_or_record_failure'(+list, +thread_identifier, -atom)
%
% records a thread goal failure and checks if all other thread goals have failed

'$lgt_mt_threaded_or_record_failure'([id(Id, _, false)| Results], Id, Continue) :-
	!,
	(	var(Continue) ->
		% we still don't know if there are any pending results
		'$lgt_mt_threaded_continue'(Results, Continue)
	;	true
	).

'$lgt_mt_threaded_or_record_failure'([id(_, _, Done)| Results], Id, Continue) :-
	(	var(Done) ->
		% we found a thread whose result is still pending
		Continue = true
	;	% otherwise continue examining the remaining thread results
		true
	),
	'$lgt_mt_threaded_or_record_failure'(Results, Id, Continue).



% '$lgt_mt_threaded_continue'(+list, -atom)
%
% checks if there are results still pending for a threaded/1 call

'$lgt_mt_threaded_continue'([], false).

'$lgt_mt_threaded_continue'([id(_, _, Done)| Results], Continue) :-
	(	var(Done) ->
		% we found a thread whose result is still pending
		Continue = true
	;	% otherwise continue looking for a thread with a still pending result
		'$lgt_mt_threaded_continue'(Results, Continue)
	).



% '$lgt_mt_threaded_call_cancel'(+message_queue_identifier, +list)
%
% aborts a threaded call by aborting and joining all individual threads;
% we must use catch/3 as some threads may already be terminated

'$lgt_mt_threaded_call_cancel'(Queue, Results) :-
	'$lgt_mt_threaded_call_abort'(Results, Queue),
	'$lgt_mt_threaded_call_join'(Results, Queue).



% '$lgt_mt_threaded_call_abort'(+list)
%
% signals individual threads to abort if their result is not yet registered;
% we must use catch/3 as some threads may no longer exist

'$lgt_mt_threaded_call_abort'([], _).

'$lgt_mt_threaded_call_abort'([id(Id, _, Result)| Ids], Queue) :-
	(	var(Result) ->
		catch(thread_signal(Id, throw('$lgt_aborted')), _, true)
	;	true
	),
	'$lgt_mt_threaded_call_abort'(Ids, Queue).



% '$lgt_mt_threaded_call_join'(+list, +message_queue_identifier)
%
% joins all individual threads; we must use catch/3 as some threads may no longer exist

'$lgt_mt_threaded_call_join'([], Queue) :-
	message_queue_destroy(Queue).

'$lgt_mt_threaded_call_join'([id(Id, _, _)| Results], Queue) :-
	catch(thread_join(Id, _), _, true),
	'$lgt_mt_threaded_call_join'(Results, Queue).



% '$lgt_new_threaded_tag'(-integer)
%
% generates a new multi-threading tag; used in the built-in asynchronous
% multi-threading predicates

'$lgt_new_threaded_tag'(New) :-
	with_mutex(
		'$lgt_threaded_tag',
		(	retract('$lgt_threaded_tag_counter_'(Old)),
			New is Old + 1,
			asserta('$lgt_threaded_tag_counter_'(New))
		)
	).



% '$lgt_new_threaded_engine_tag'(-integer)
%
% generates a new threading engine tag (already protected by the '$lgt_engines' mutex)

'$lgt_new_threaded_engine_tag'(New) :-
	retract('$lgt_threaded_engine_tag_counter_'(Old)), !,
	New is Old + 1,
	asserta('$lgt_threaded_engine_tag_counter_'(New)).



% '$lgt_create_mutexes'(+list(mutex_identifier))
%
% creates entity mutexes (called when loading an entity); we may
% be reloading an entity and the mutex may be already created

'$lgt_create_mutexes'([]).

'$lgt_create_mutexes'([Mutex| Mutexes]) :-
	(	mutex_property(_, alias(Mutex)) ->
		true
	;	mutex_create(_, [alias(Mutex)])
	),
	'$lgt_create_mutexes'(Mutexes).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  static binding supporting predicates
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% '$lgt_send_to_obj_static_binding'(@object_identifier, @callable, @object_identifier, -callable)
%
% static binding is only used for the (::)/2 control construct when the object receiving the
% message is static and the support for complementing categories is disallowed (unfortunately,
% allowing hot patching of an object would easily lead to inconsistencies as there isn't any
% portable solution for updating in-place the definition of patched object predicates that
% were already directly called due to the previous use of static binding)

'$lgt_send_to_obj_static_binding'(Obj, Pred, Call, Ctx) :-
	'$lgt_comp_ctx'(Ctx, _, _, _, _, This, _, _, _, _, CallerExCtx, _, _, _, _),
	(	'$lgt_send_to_obj_static_binding_'(Obj, Pred, CallerExCtx, Call) ->
		true
	;	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, ObjFlags),
		ObjFlags /\ 512 =\= 512,
		% object is not compiled in debug mode
		ObjFlags /\ 2 =:= 0,
		% object is static
		ObjFlags /\ 64 =\= 64,
		% complementing categories flag not set to "allow"
		'$lgt_term_template'(Pred, GPred),
		call(Dcl, GPred, p(p(p)), Meta, PredFlags, _, DclCtn), !,
		% get the execution context for meta-calls
		'$lgt_goal_meta_call_context'(Meta, GCallerExCtx, GThis, GMetaCallCtx),
		'$lgt_term_template'(Obj, GObj),
		'$lgt_execution_context'(GExCtx, _, GThis, GObj, GObj, GMetaCallCtx, []),
		call(Def, GPred, GExCtx, GCall, _, DefCtn), !,
		(	PredFlags /\ 2 =:= 0 ->
			% Type == static
			true
		;	% Type == (dynamic)
			GObj = DclCtn ->
			% local declaration
			true
		;	GObj = DefCtn
			% local definition
		),
		(	GObj \= DefCtn ->
			% inherited definition; complementing categories
			% flag must also not be set to "restrict"
			ObjFlags /\ 32 =\= 32
		;	% local definition
			true
		),
		% predicate definition found; use it only if it's safe
		'$lgt_static_binding_safe_paths'(GObj, DclCtn, DefCtn),
		(	Meta == no ->
			% cache only normal predicates
			assertz('$lgt_send_to_obj_static_binding_'(GObj, GPred, GCallerExCtx, GCall)),
			Obj = GObj, Pred = GPred, This = GThis, CallerExCtx = GCallerExCtx, Call = GCall
		;	% meta-predicates cannot be cached as they require translation of
			% the meta-arguments, which must succeed to allow static binding
			% (don't require the predicate and the meta-predicate template to
			% share the name as we may be using a predicate alias)
			Meta =.. [_| MArgs],
			Pred =.. [PredFunctor| Args],
			'$lgt_compile_static_binding_meta_arguments'(Args, MArgs, _, Ctx, TArgs),
			TPred =.. [PredFunctor| TArgs],
			Obj = GObj, TPred = GPred, This = GThis, CallerExCtx = GCallerExCtx, Call = GCall
		)
	).


'$lgt_compile_static_binding_meta_arguments'([], [], _, _, []).

'$lgt_compile_static_binding_meta_arguments'([Arg| Args], [MArg| MArgs], Caller, Ctx, [TArg| TArgs]) :-
	'$lgt_compile_static_binding_meta_argument'(MArg, Arg, Caller, Ctx, TArg),
	'$lgt_compile_static_binding_meta_arguments'(Args, MArgs, Caller, Ctx, TArgs).


'$lgt_compile_static_binding_meta_argument'((*), Arg, _, _, Arg) :-
	!.

'$lgt_compile_static_binding_meta_argument'(N, Closure, _, Ctx, {UserClosure}) :-
	integer(N),
	% goal or closure
	nonvar(Closure),
	(	Closure = Obj::UserClosure, Obj == user
	;	Closure = {UserClosure}
	;	'$lgt_comp_ctx_entity'(Ctx, Entity), Entity == user,
		\+ '$lgt_control_construct'(Closure),
		UserClosure = Closure
	),
	% goal or closure called in "user"
	!,
	'$lgt_check'(var_or_callable, UserClosure).

'$lgt_compile_static_binding_meta_argument'(N, Closure, _, Ctx, TClosure) :-
	integer(N), N > 0,
	% closure
	!,
	'$lgt_check'(var_or_callable, Closure),
	'$lgt_length'(ExtArgs, 0, N),
	'$lgt_extend_closure'(Closure, ExtArgs, Goal, Ctx),
	% compiling the meta-argument allows predicate cross-referencing information
	% to be collected even if the compilation result cannot be used
	'$lgt_compile_body'(Goal, meta, TGoal, _, Ctx),
	functor(TGoal, TFunctor, _),
	(	Goal == TGoal ->
		\+ '$lgt_control_construct'(TGoal),
		% either a built-in predicate or a predicate called in "user"
		TClosure = {Closure}
	;	sub_atom(TFunctor, 0, 5, _, '$lgt_') ->
		% in some backend Prolog systems, internal Logtalk compiler/runtime
		% predicates may be marked as built-in predicates
		fail
	;	'$lgt_built_in_predicate'(TGoal) ->
		\+ '$lgt_control_construct'(TGoal),
		% built-in predicates may result from goal-expansion during
		% compilation or from inlining of user predicate definitions
		'$lgt_built_in_goal_to_closure'(N, TGoal, TFunctor, TArgs),
		TClosure0 =.. [TFunctor| TArgs],
		TClosure = {TClosure0}
	;	'$lgt_user_goal_to_closure'(N, TGoal, TFunctor, TArgs, ExCtx) ->
		TClosure = '$lgt_closure'(TFunctor, TArgs, ExCtx)
	;	% runtime resolved meta-call
		fail
	).

'$lgt_compile_static_binding_meta_argument'(0, Goal, _, Ctx, {TGoal}) :-
	% the {}/1 construct signals a pre-compiled metacall
	'$lgt_compile_body'(Goal, meta, TGoal, _, Ctx).


'$lgt_built_in_goal_to_closure'(N, TGoal, TFunctor, TArgs) :-
	functor(TGoal, TFunctor, TArity),
	TGoal =.. [TFunctor| TAllArgs],
	% subtract the number of extra arguments
	Arity is TArity - N,
	Arity >= 0,
	% unify the compiled closure arguments from the compiled goal arguments
	'$lgt_length'(TArgs, 0, Arity),
	'$lgt_append'(TArgs, _, TAllArgs),
	!.


'$lgt_user_goal_to_closure'(N, TGoal, TFunctor, TArgs, ExCtx) :-
	functor(TGoal, TFunctor, TArity),
	TGoal =.. [TFunctor| TAllArgs],
	% subtract the number of extra arguments and the execution context argument
	Arity is TArity - N - 1,
	Arity >= 0,
	% unify the compiled closure arguments from the compiled goal arguments
	'$lgt_length'(TArgs, 0, Arity),
	'$lgt_append'(TArgs, _, TAllArgs),
	% unify the execution context argument using the compiled goal
	arg(TArity, TGoal, ExCtx),
	!.



% '$lgt_obj_super_call_static_binding'(@object_identifier, @callable, @execution_context, -callable)
%
% static binding for the (^^)/1 control construct (used within objects)

'$lgt_obj_super_call_static_binding'(Obj, Pred, ExCtx, Call) :-
	(	'$lgt_pp_imported_category_'(_, _, _, _, _, _),
		'$lgt_obj_super_call_static_binding_category'(Obj, Pred, ExCtx, Call) ->
		true
	;	'$lgt_pp_extended_object_'(_, _, _, _, _, _, _, _, _, _, _) ->
		'$lgt_obj_super_call_static_binding_prototype'(Obj, Pred, ExCtx, Call)
	;	'$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _),
		'$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		'$lgt_obj_super_call_static_binding_instance_class'(Obj, Pred, ExCtx, Call)
	;	'$lgt_pp_instantiated_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		'$lgt_obj_super_call_static_binding_instance'(Obj, Pred, ExCtx, Call)
	;	'$lgt_pp_specialized_class_'(_, _, _, _, _, _, _, _, _, _, _) ->
		'$lgt_obj_super_call_static_binding_class'(Obj, Pred, ExCtx, Call)
	;	fail
	).


'$lgt_obj_super_call_static_binding_category'(Obj, Alias, OExCtx, Call) :-
	% when working with parametric entities, we must connect the parameters
	% between related entities
	'$lgt_pp_runtime_clause_'('$lgt_imports_category_'(Obj, Ctg, _)),
	'$lgt_current_category_'(Ctg, _, Dcl, Def, _, _),
	% we may be aliasing the predicate
	(	'$lgt_pp_predicate_alias_'(Ctg, Pred, Alias, _, _, _) ->
		true
	;	Pred = Alias
	),
	% lookup predicate declaration
	call(Dcl, Pred, _, _, Flags, DclCtn), !,
	% the predicate must be static
	Flags /\ 2 =:= 0,
	% unify execution context arguments
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, CExCtx, Obj, Ctg),
	% lookup predicate definition
	call(Def, Pred, CExCtx, Call, DefCtn), !,
	% predicate definition found; use it only if it's safe
	'$lgt_static_binding_safe_paths'(Obj, DclCtn, DefCtn).


'$lgt_obj_super_call_static_binding_prototype'(Obj, Alias, OExCtx, Call) :-
	% when working with parametric entities, we must connect the parameters
	% between related entities
	'$lgt_pp_runtime_clause_'('$lgt_extends_object_'(Obj, Parent, RelationScope)),
	'$lgt_current_object_'(Parent, _, Dcl, Def, _, _, _, _, _, _, _),
	% we may be aliasing the predicate
	(	'$lgt_pp_predicate_alias_'(Parent, Pred, Alias, _, _, _) ->
		true
	;	Pred = Alias
	),
	% lookup predicate declaration
	(	RelationScope == (public) ->
		call(Dcl, Pred, Scope, _, Flags, SCtn, TCtn)
	;	RelationScope == protected ->
		call(Dcl, Pred, PredScope, _, Flags, SCtn, TCtn),
		'$lgt_filter_scope'(PredScope, Scope)
	;	Scope = p,
		call(Dcl, Pred, PredScope, _, Flags, SCtn0, TCtn),
		'$lgt_filter_scope_container'(PredScope, SCtn0, Obj, SCtn)
	), !,
	% check that the call is within scope (i.e., public or protected)
	(	Scope = p(_) ->
		true
	;	Obj = SCtn
	),
	% the predicate must be static
	Flags /\ 2 =:= 0,
	% unify execution context arguments
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, PExCtx, Parent, Parent),
	% lookup predicate definition
	call(Def, Pred, PExCtx, Call, _, DefCtn), !,
	% predicate definition found; use it only if it's safe
	'$lgt_static_binding_safe_paths'(Obj, TCtn, DefCtn).


'$lgt_obj_super_call_static_binding_instance'(Obj, Alias, OExCtx, Call) :-
	% when working with parametric entities, we must connect the parameters
	% between related entities
	'$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(Obj, Class, RelationScope)),
	'$lgt_current_object_'(Class, _, _, _, _, IDcl, IDef, _, _, _, _),
	% we may be aliasing the predicate
	(	'$lgt_pp_predicate_alias_'(Class, Pred, Alias, _, _, _) ->
		true
	;	Pred = Alias
	),
	% lookup predicate declaration
	(	RelationScope == (public) ->
		call(IDcl, Pred, Scope, _, Flags, SCtn, TCtn)
	;	RelationScope == protected ->
		call(IDcl, Pred, PredScope, _, Flags, SCtn, TCtn),
		'$lgt_filter_scope'(PredScope, Scope)
	;	Scope = p,
		call(IDcl, Pred, PredScope, _, Flags, SCtn0, TCtn),
		'$lgt_filter_scope_container'(PredScope, SCtn0, Obj, SCtn)
	), !,
	% check that the call is within scope (i.e., public or protected)
	(	Scope = p(_) ->
		true
	;	Obj = SCtn
	),
	% the predicate must be static
	Flags /\ 2 =:= 0,
	% unify execution context arguments
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, CExCtx, Class, Class),
	% lookup predicate definition
	call(IDef, Pred, CExCtx, Call, _, DefCtn), !,
	% predicate definition found; use it only if it's safe
	'$lgt_static_binding_safe_paths'(Obj, TCtn, DefCtn).


'$lgt_obj_super_call_static_binding_class'(Obj, Alias, OExCtx, Call) :-
	% when working with parametric entities, we must connect the parameters
	% between related entities
	'$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(Obj, Superclass, RelationScope)),
	'$lgt_current_object_'(Superclass, _, _, _, _, IDcl, IDef, _, _, _, _),
	% we may be aliasing the predicate
	(	'$lgt_pp_predicate_alias_'(Superclass, Pred, Alias, _, _, _) ->
		true
	;	Pred = Alias
	),
	% lookup predicate declaration
	(	RelationScope == (public) ->
		call(IDcl, Pred, Scope, _, Flags, SCtn, TCtn)
	;	RelationScope == protected ->
		call(IDcl, Pred, PredScope, _, Flags, SCtn, TCtn),
		'$lgt_filter_scope'(PredScope, Scope)
	;	Scope = p,
		call(IDcl, Pred, PredScope, _, Flags, SCtn0, TCtn),
		'$lgt_filter_scope_container'(PredScope, SCtn0, Obj, SCtn)
	), !,
	% check that the call is within scope (i.e., public or protected)
	(	Scope = p(_) ->
		true
	;	Obj = SCtn
	),
	% the predicate must be static
	Flags /\ 2 =:= 0,
	% unify execution context arguments
	'$lgt_execution_context_update_this_entity'(OExCtx, Obj, Obj, SExCtx, Superclass, Superclass),
	% lookup predicate definition
	call(IDef, Pred, SExCtx, Call, _, DefCtn), !,
	% predicate definition found; use it only if it's safe
	'$lgt_static_binding_safe_paths'(Obj, TCtn, DefCtn).


'$lgt_obj_super_call_static_binding_instance_class'(Obj, Pred, ExCtx, Call) :-
	(	'$lgt_obj_super_call_static_binding_instance'(Obj, Pred, ExCtx, ICall),
		'$lgt_obj_super_call_static_binding_class'(Obj, Pred, ExCtx, CCall) ->
		(	ICall == CCall ->
			Call = ICall
		;	'$lgt_execution_context'(ExCtx, _, _, _, Self, _, _),
			Call = (Obj = Self -> ICall; CCall)
		)
	;	'$lgt_obj_super_call_static_binding_instance'(Obj, Pred, ExCtx, Call) ->
		true
	;	'$lgt_obj_super_call_static_binding_class'(Obj, Pred, ExCtx, Call)
	).



% '$lgt_ctg_super_call_static_binding'(@category_identifier, @callable, @execution_context, -callable)
%
% static binding for the (^^)/1 control construct (used within categories)

'$lgt_ctg_super_call_static_binding'(Ctg, Alias, CExCtx, Call) :-
	% when working with parametric entities, we must connect the parameters
	% between related entities
	'$lgt_pp_runtime_clause_'('$lgt_extends_category_'(Ctg, ExtCtg, RelationScope)),
	'$lgt_current_category_'(ExtCtg, _, Dcl, Def, _, _),
	% we may be aliasing the predicate
	(	'$lgt_pp_predicate_alias_'(ExtCtg, Pred, Alias, _, _, _) ->
		true
	;	Pred = Alias
	),
	% lookup predicate declaration
	(	RelationScope == (public) ->
		call(Dcl, Pred, Scope, _, Flags, DclCtn)
	;	RelationScope == protected,
		call(Dcl, Pred, Scope0, _, Flags, DclCtn),
		'$lgt_filter_scope'(Scope0, Scope)
	), !,
	% check that the call is within scope
	Scope = p(_),
	% the predicate must be static
	Flags /\ 2 =:= 0,
	% unify execution context arguments
	'$lgt_execution_context_update_this_entity'(CExCtx, This, Ctg, EExCtx, This, ExtCtg),
	% lookup predicate definition
	call(Def, Pred, EExCtx, Call, DefCtn), !,
	% predicate definition found; use it only if it's safe
	'$lgt_static_binding_safe_paths'(Ctg, DclCtn, DefCtn).



% '$lgt_send_to_obj_db_msg_static_binding'(@category_identifier, @callable, -callable)
%
% static binding for selected database messages sent to an object

'$lgt_send_to_obj_db_msg_static_binding'(Obj, Head, THead) :-
	'$lgt_current_object_'(Obj, _, Dcl, Def, _, _, _, _, _, _, ObjFlags),
	% check that the object is not compiled in debug mode
	ObjFlags /\ 512 =\= 512,
	% check that the object is static
	ObjFlags /\ 2 =:= 0,
	call(Dcl, Head, Scope, _, PredFlags, SCtn, DCtn), !,
	% check that the call is within scope
	Scope = p(p(_)),
	% check that the predicate is dynamic
	PredFlags /\ 2 =:= 2,
	% check that we're acting on the same entity that declares the predicate dynamic
	SCtn = Obj,
	% lookup local predicate definition
	call(Def, Head, _, THead), !,
	% predicate definition found; use it only if it's safe
	'$lgt_static_binding_entity'(DCtn).



% '$lgt_static_binding_safe_paths'(@entity_identifier, @entity_identifier, @entity_identifier)
%
% all ancestor entities up to the starting point for both the declaration
% container and the definition container must be static-binding entities

'$lgt_static_binding_safe_paths'(Entity, DclEntity, DefEntity) :-
	(	DclEntity \= Entity ->
		'$lgt_static_binding_entity'(DclEntity)
	;	true
	),
	(	DefEntity \= Entity ->
		'$lgt_static_binding_entity'(DefEntity)
	;	true
	),
	'$lgt_static_binding_safe_declaration_ancestors'(Entity, DclEntity),
	'$lgt_static_binding_safe_definition_ancestors'(Entity, DefEntity).


'$lgt_static_binding_entity'(Entity) :-
	(	'$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, Flags) ->
		Flags /\ 64 =\= 64,
		Flags /\ 32 =\= 32
		% support for complementing categories is disabled
	;	'$lgt_current_protocol_'(Entity, _, _, _, Flags) ->
		true
	;	'$lgt_current_category_'(Entity, _, _, _, _, Flags)
	),
	Flags /\ 512 =\= 512,
	% entity is not compiled in debug mode
	Flags /\ 2 =:= 0.
	% entity is static


'$lgt_static_binding_entity'(object, Object) :-
	'$lgt_current_object_'(Object, _, _, _, _, _, _, _, _, _, Flags),
	Flags /\ 512 =\= 512,
	% object is not compiled in debug mode
	Flags /\ 2 =:= 0,
	% object is static
	Flags /\ 64 =\= 64,
	Flags /\ 32 =\= 32.
	% support for complementing categories is disallowed

'$lgt_static_binding_entity'(protocol, Protocol) :-
	'$lgt_current_protocol_'(Protocol, _, _, _, Flags),
	Flags /\ 512 =\= 512,
	% protocol is not compiled in debug mode
	Flags /\ 2 =:= 0.
	% protocol is static

'$lgt_static_binding_entity'(category, Category) :-
	'$lgt_current_category_'(Category, _, _, _, _, Flags),
	Flags /\ 512 =\= 512,
	% category is not compiled in debug mode
	Flags /\ 2 =:= 0.
	% category is static


'$lgt_static_binding_safe_declaration_ancestors'(Entity, DclEntity) :-
	(	Entity = DclEntity ->
		% local predicate declaration
		true
	;	% we add a third argument to properly handle class hierarchies if necessary
		'$lgt_static_binding_safe_declaration_ancestors'(Entity, DclEntity, _) ->
		% ensure no spurious choice-points
		true
	;	fail
	).

'$lgt_static_binding_safe_declaration_ancestors'(Entity, DclEntity, Kind) :-
	'$lgt_entity_ancestor'(Entity, Type, Ancestor, Kind, NextKind),
	(	'$lgt_static_binding_entity'(Type, Ancestor) ->
		(	Ancestor = DclEntity ->
			true
		;	% move up, implementing the same depth-first strategy used by the predicate
			% declaration lookup algorithm
			'$lgt_static_binding_safe_declaration_ancestors'(Ancestor, DclEntity, NextKind)
		)
	;	% ancestor can be later modified, rendering the static binding optimization invalid
		!,
		fail
	).


'$lgt_static_binding_safe_definition_ancestors'(Entity, DefEntity) :-
	(	Entity = DefEntity ->
		% local predicate definition
		true
	;	% we add a third argument to properly handle class hierarchies if necessary
		'$lgt_static_binding_safe_definition_ancestors'(Entity, DefEntity, _) ->
		% ensure no spurious choice-points
		true
	;	fail
	).

'$lgt_static_binding_safe_definition_ancestors'(Entity, DefEntity, Kind) :-
	'$lgt_entity_ancestor'(Entity, Type, Ancestor, Kind, NextKind),
	% protocols cannot contain predicate definitions
	Type \== protocol,
	(	'$lgt_static_binding_entity'(Type, Ancestor) ->
		(	Ancestor = DefEntity ->
			true
		;	% move up, implementing the same depth-first strategy used by the predicate
			% definition lookup algorithm
			'$lgt_static_binding_safe_definition_ancestors'(Ancestor, DefEntity, NextKind)
		)
	;	% ancestor can be later modified, rendering the static binding optimization invalid
		!,
		fail
	).


% entity ancestors are generated on backtracking in the same order
% used by the predicate declaration and definition lookup algorithms

'$lgt_entity_ancestor'(Entity, protocol, Protocol, Kind, Kind) :-
	'$lgt_implements_protocol_'(Entity, Protocol, _).

'$lgt_entity_ancestor'(Entity, protocol, Protocol, Kind, Kind) :-
	'$lgt_pp_runtime_clause_'('$lgt_implements_protocol_'(Entity, Protocol, _)).

'$lgt_entity_ancestor'(Entity, protocol, Protocol, protocol, protocol) :-
	'$lgt_extends_protocol_'(Entity, Protocol, _).

'$lgt_entity_ancestor'(Entity, protocol, Protocol, protocol, protocol) :-
	'$lgt_pp_runtime_clause_'('$lgt_extends_protocol_'(Entity, Protocol, _)).

'$lgt_entity_ancestor'(Entity, category, Category, category, category) :-
	'$lgt_extends_category_'(Entity, Category, _).

'$lgt_entity_ancestor'(Entity, category, Category, category, category) :-
	'$lgt_pp_runtime_clause_'('$lgt_extends_category_'(Entity, Category, _)).

'$lgt_entity_ancestor'(Entity, category, Category, Kind, Kind) :-
	'$lgt_imports_category_'(Entity, Category, _).

'$lgt_entity_ancestor'(Entity, category, Category, Kind, Kind) :-
	'$lgt_pp_runtime_clause_'('$lgt_imports_category_'(Entity, Category, _)).

'$lgt_entity_ancestor'(Entity, object, Parent, prototype, prototype) :-
	'$lgt_extends_object_'(Entity, Parent, _).

'$lgt_entity_ancestor'(Entity, object, Parent, prototype, prototype) :-
	'$lgt_pp_runtime_clause_'('$lgt_extends_object_'(Entity, Parent, _)).

'$lgt_entity_ancestor'(Entity, object, Class, instance, superclass) :-
	'$lgt_instantiates_class_'(Entity, Class, _).

'$lgt_entity_ancestor'(Entity, object, Class, instance, superclass) :-
	'$lgt_pp_runtime_clause_'('$lgt_instantiates_class_'(Entity, Class, _)).

'$lgt_entity_ancestor'(Entity, object, Superclass, superclass, superclass) :-
	'$lgt_specializes_class_'(Entity, Superclass, _).

'$lgt_entity_ancestor'(Entity, object, Superclass, superclass, superclass) :-
	'$lgt_pp_runtime_clause_'('$lgt_specializes_class_'(Entity, Superclass, _)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  utility predicates
%
% although usually provided as either built-in or library predicates by the
% backends, it's simpler and more portable to define our own versions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% '$lgt_length'(+list, +integer, -integer)
% '$lgt_length'(-list, +integer, +integer)

'$lgt_length'([], Length, Length) :-
	!.
'$lgt_length'([_| Tail], Length0, Length) :-
	Length1 is Length0 + 1,
	'$lgt_length'(Tail, Length1, Length).


'$lgt_append'([], List, List).
'$lgt_append'([Head| Tail], List, [Head| Tail2]) :-
	'$lgt_append'(Tail, List, Tail2).


'$lgt_member'(Head, [Head| _]).
'$lgt_member'(Head, [_| Tail]) :-
	'$lgt_member'(Head, Tail).


'$lgt_member_var'(V, [H| _]) :-
	V == H.
'$lgt_member_var'(V, [_| T]) :-
	nonvar(T),
	'$lgt_member_var'(V, T).


'$lgt_memberchk_var'(Element, [Head| Tail]) :-
	(	Element == Head ->
		true
	;	'$lgt_memberchk_var'(Element, Tail)
	).


'$lgt_sub_term_var'(SubTerm, Term) :-
	SubTerm == Term,
	!.
'$lgt_sub_term_var'(SubTerm, Term) :-
	compound(Term),
	functor(Term, _, Arity),
	'$lgt_between'(1, Arity, N),
	arg(N, Term, Argument),
	'$lgt_sub_term_var'(SubTerm, Argument),
	!.


'$lgt_anonymous_or_singleton_variable'(Variable, VariableNames, Singletons) :-
	(	'$lgt_member'(Name0=Variable0, Singletons),
		Variable0 == Variable,
		\+ '$lgt_parameter_variable_name'(Name0) ->
		true
	;	\+ (
			'$lgt_member'(_=Variable0, VariableNames),
			Variable0 == Variable
		)
	).


'$lgt_anonymous_or_singleton_variables'([], _, _).
'$lgt_anonymous_or_singleton_variables'([Variable| Variables], VariableNames, Singletons) :-
	'$lgt_anonymous_or_singleton_variable'(Variable, VariableNames, Singletons),
	'$lgt_anonymous_or_singleton_variables'(Variables, VariableNames, Singletons).



% find position-relevant argument pairs for =../2 lint checks where a relevant
% argument is either a bound argument or a named variable argument; the last
% argument returns the type of list (open or closed)
'$lgt_position_relevant_argument_pairs'([], _, _, [], closed, []).
'$lgt_position_relevant_argument_pairs'([Argument| Arguments], N, VariableNames, [N-Argument| Pairs], Type, Tail) :-
	once((
		nonvar(Argument)
	;	'$lgt_member'(_=Argument0, VariableNames),
		Argument0 == Argument
	)),
	!,
	(	var(Arguments) ->
		% open list
		Pairs = [],
		Type = open,
		Tail = Arguments
	;	M is N + 1,
		'$lgt_position_relevant_argument_pairs'(Arguments, M, VariableNames, Pairs, Type, Tail)
	).
'$lgt_position_relevant_argument_pairs'([_| Arguments], N, VariableNames, Pairs, Type, Tail) :-
	(	var(Arguments) ->
		% open list
		Pairs = [],
		Type = open,
		Tail = Arguments
	;	M is N + 1,
		'$lgt_position_relevant_argument_pairs'(Arguments, M, VariableNames, Pairs, Type, Tail)
	).


'$lgt_between'(Lower, Upper, N) :-
	Lower =< Upper,
	'$lgt_between_aux'(Lower, Upper, N).

'$lgt_between_aux'(Lower, _, Lower).
'$lgt_between_aux'(Lower, Upper, N) :-
	Lower < Upper,
	Next is Lower + 1,
	'$lgt_between_aux'(Next, Upper, N).


'$lgt_is_list_or_partial_list'(Var) :-
	var(Var),
	!.
'$lgt_is_list_or_partial_list'([]).
'$lgt_is_list_or_partial_list'([_| Tail]) :-
	'$lgt_is_list_or_partial_list'(Tail).


'$lgt_is_list'((-)) :-
	!,
	fail.
'$lgt_is_list'([]).
'$lgt_is_list'([_| Tail]) :-
	'$lgt_is_list'(Tail).


'$lgt_is_boolean'((-)) :-
	!,
	fail.
'$lgt_is_boolean'(true).
'$lgt_is_boolean'(false).


'$lgt_intersection'(_, [], []) :- !.
'$lgt_intersection'([], _, []) :- !.
'$lgt_intersection'([Head1| Tail1], List2, Intersection) :-
	(	'$lgt_memberchk_var'(Head1, List2) ->
		Intersection = [Head1| IntersectionRest],
		'$lgt_intersection'(Tail1, List2, IntersectionRest)
	;	'$lgt_intersection'(Tail1, List2, Intersection)
	).


'$lgt_var_subtract'([], _, []).
'$lgt_var_subtract'([Head| Tail], List, Rest) :-
	(	'$lgt_memberchk_var'(Head, List) ->
		'$lgt_var_subtract'(Tail, List, Rest)
	;	Rest = [Head| Tail2],
		'$lgt_var_subtract'(Tail, List, Tail2)
	).


'$lgt_sum_list'(List, Sum) :-
	'$lgt_sum_list'(List, 0, Sum).

'$lgt_sum_list'([], Sum, Sum).
'$lgt_sum_list'([Value| Values], Sum0, Sum) :-
	Sum1 is Sum0 + Value,
	'$lgt_sum_list'(Values, Sum1, Sum).


'$lgt_select'(Head, [Head| Tail], Tail).
'$lgt_select'(Head, [Head2| Tail], [Head2| Tail2]) :-
	'$lgt_select'(Head, Tail, Tail2).


% definition taken from the SWI-Prolog documentation
'$lgt_variant'(Term1, Term2) :-
	% avoid trouble in any shared variables
	copy_term(Term1, Term1Copy),
	copy_term(Term2, Term2Copy),
	% ground and compare the term copies
	numbervars(Term1Copy, 0, N),
	numbervars(Term2Copy, 0, N),
	Term1Copy == Term2Copy.


% variable aliasing occurs in a head of a clause
% when two or more arguments share variables
'$lgt_variable_aliasing'(Head) :-
	compound(Head),
	Head =.. [_| Arguments],
	'$lgt_select'(Argument1, Arguments, OtherArguments),
	'$lgt_member'(Argument2, OtherArguments),
	term_variables(Argument1, Variables1),
	term_variables(Argument2, Variables2),
	'$lgt_intersection'(Variables1, Variables2, [_| _]),
	% at least one variable in common
	!.


'$lgt_read_file_to_terms'(File, Directory, SourceFile, Terms, Mode) :-
	% check file specification and expand library notation or environment variable if used
	catch(
		'$lgt_check_and_expand_source_file'(File, ExpandedFile),
		error(FileError, _),
		throw(FileError)
	),
	% find the full file name as the extension may be missing
	(	'$lgt_source_file_name'(ExpandedFile, [], Directory, _, _, SourceFile),
		% avoid a loading loop by checking that the file name is different
		% from the name of the file containing the include/1 directive
		\+ '$lgt_pp_file_paths_flags_'(_, _, SourceFile, _, _),
		'$lgt_file_exists'(SourceFile) ->
		true
	;	'$lgt_source_file_name'(ExpandedFile, [], Directory, _, _, SourceFile),
		'$lgt_pp_file_paths_flags_'(_, _, SourceFile, _, _) ->
		throw(permission_error(include, file, File))
	;	throw(existence_error(file, File))
	),
	(	Mode = compile(_,_,_) ->
		'$lgt_print_message'(silent(compiling), compiling_file(SourceFile, []))
	;	true
	),
	catch(
		'$lgt_open'(SourceFile, read, Stream, []),
		error(OpenError, _),
		throw(OpenError)
	),
	% look for an encoding/1 directive that, when present, must be the first term on a source file
	catch(
		'$lgt_read_term'(Stream, Term, [variable_names(VariableNames), singletons(Singletons)], Lines),
		error(TermError, _),
		'$lgt_read_file_to_terms_error_handler'(Mode, SourceFile, Stream, TermError)
	),
	catch(
		'$lgt_check_for_encoding_directive'(Term, SourceFile, Lines, Stream, NewStream, [], _),
		FirstTermError,
		'$lgt_read_file_to_terms_error_handler'(Mode, SourceFile, Stream, FirstTermError)
	),
	% read the reamining terms
	catch(
		'$lgt_read_stream_to_terms'(Term, VariableNames, Singletons, Lines, SourceFile, NewStream, Terms, Mode),
		error(TermError, _),
		'$lgt_read_file_to_terms_error_handler'(Mode, SourceFile, NewStream, TermError)
	),
	'$lgt_close'(NewStream).


'$lgt_read_file_to_terms_error_handler'(runtime, _, Stream, Error) :-
	'$lgt_close'(Stream),
	throw(Error).

'$lgt_read_file_to_terms_error_handler'(compile(_,_,_), SourceFile, Stream, Error) :-
	'$lgt_pp_file_paths_flags_'(_, _, _, ObjectFile, _),
	(	'$lgt_stream_current_line_number'(Stream, Line) ->
		true
	;	Line = -1
	),
	'$lgt_close'(Stream),
	'$lgt_compiler_error_handler'(SourceFile, ObjectFile, Line-Line, Error).


'$lgt_read_stream_to_terms'(Term, VariableNames, Singletons, Lines, File, Stream, [Term-sd(VariableNames,Singletons,Lines)| Terms], Mode) :-
	var(Term),
	% delay the instantiation error
	!,
	'$lgt_read_term'(Stream, NextTerm, [variable_names(NextVariableNames), singletons(NextSingletons)], NextLines),
	'$lgt_read_stream_to_terms'(NextTerm, NextVariableNames, NextSingletons, NextLines, File, Stream, Terms, Mode).
'$lgt_read_stream_to_terms'(end_of_file, _, _, _, _, _, [], _) :-
	!.
'$lgt_read_stream_to_terms'((:- op(Priority, Specifier, Operators)), VariableNames, Singletons, Lines, File, Stream, [(:- op(Priority, Specifier, Operators))-sd(VariableNames,Singletons,Lines)| Terms], Mode) :-
	!,
	'$lgt_check'(operator_specification, op(Priority, Specifier, Operators)),
	(	'$lgt_pp_entity_'(_, _, _) ->
		'$lgt_activate_entity_operators'(Priority, Specifier, Operators, l, File, Lines, Mode)
	;	'$lgt_activate_file_operators'(Priority, Specifier, Operators, Mode)
	),
	'$lgt_read_term'(Stream, NextTerm, [variable_names(NextVariableNames), singletons(NextSingletons)], NextLines),
	'$lgt_read_stream_to_terms'(NextTerm, NextVariableNames, NextSingletons, NextLines, File, Stream, Terms, Mode).
'$lgt_read_stream_to_terms'(Term, VariableNames, Singletons, Lines, File, Stream, [Term-sd(VariableNames,Singletons,Lines)| Terms], Mode) :-
	'$lgt_report_singleton_variables'(Mode, Singletons, Term, File, Lines),
	'$lgt_read_term'(Stream, NextTerm, [variable_names(NextVariableNames), singletons(NextSingletons)], NextLines),
	'$lgt_read_stream_to_terms'(NextTerm, NextVariableNames, NextSingletons, NextLines, File, Stream, Terms, Mode).



% '$lgt_check'(+atom, @term, @callable)
%
% type-checking for built-in directive and predicate arguments

'$lgt_check'(var, Term, Context) :-
	(	var(Term) ->
		true
	;	throw(error(uninstantiation_error(Term), Context))
	).

'$lgt_check'(nonvar, Term, Context) :-
	(	nonvar(Term) ->
		true
	;	throw(error(instantiation_error, Context))
	).

'$lgt_check'(ground, Term, Context) :-
	(	ground(Term) ->
		true
	;	throw(error(instantiation_error, Context))
	).

'$lgt_check'(atom, Term, Context) :-
	(	atom(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(var_or_atom, Term, Context) :-
	(	var(Term) ->
		true
	;	atom(Term) ->
		true
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(boolean, Term, Context) :-
	(	Term == true ->
		true
	;	Term == false ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	atom(Term) ->
		throw(error(domain_error(boolean, Term), Context))
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(var_or_boolean, Term, Context) :-
	(	var(Term) ->
		true
	;	\+ atom(Term) ->
		throw(error(type_error(atom, Term), Context))
	;	Term \== true,
		Term \== false,
		throw(error(domain_error(boolean, Term), Context))
	).

'$lgt_check'(atom_or_string, Term, Context) :-
	(	atom(Term) ->
		true
	;	'$lgt_string'(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(atom_or_string, Term), Context))
	).

'$lgt_check'(integer, Term, Context) :-
	(	integer(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(integer, Term), Context))
	).

'$lgt_check'(var_or_integer, Term, Context) :-
	(	var(Term) ->
		true
	;	integer(Term) ->
		true
	;	throw(error(type_error(integer, Term), Context))
	).

'$lgt_check'(non_negative_integer, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	\+ integer(Term) ->
		throw(error(type_error(integer, Term), Context))
	;	Term < 0 ->
		throw(error(domain_error(not_less_than_zero, Term), Context))
	;	true
	).

'$lgt_check'(var_or_non_negative_integer, Term, Context) :-
	(	var(Term) ->
		true
	;	\+ integer(Term) ->
		throw(error(type_error(integer, Term), Context))
	;	Term < 0 ->
		throw(error(domain_error(not_less_than_zero, Term), Context))
	;	true
	).

'$lgt_check'(float, Term, Context) :-
	(	float(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(float, Term), Context))
	).

'$lgt_check'(atomic, Term, Context) :-
	(	atomic(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(atomic, Term), Context))
	).

'$lgt_check'(atomic_or_string, Term, Context) :-
	(	atomic(Term) ->
		true
	;	'$lgt_string'(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(atomic_or_string, Term), Context))
	).

'$lgt_check'(curly_bracketed_term, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	Term = {_} ->
		true
	;	Term == '{}' ->
		true
	;	throw(error(type_error(curly_bracketed_term, Term), Context))
	).

'$lgt_check'(var_or_curly_bracketed_term, Term, Context) :-
	(	var(Term) ->
		true
	;	Term = {_} ->
		true
	;	Term == '{}' ->
		true
	;	throw(error(type_error(curly_bracketed_term, Term), Context))
	).

'$lgt_check'(callable, Term, Context) :-
	(	callable(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(var_or_callable, Term, Context) :-
	(	var(Term) ->
		true
	;	callable(Term) ->
		true
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(qualified_callable, Term, Context) :-
	(	'$lgt_prolog_feature'(modules, supported) ->
		'$lgt_check'(qualified_callable_, Term, Context)
	;	'$lgt_check'(callable, Term, Context)
	).

'$lgt_check'(qualified_callable_, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	Term = ':'(Module, Goal) ->
		'$lgt_check'(module_identifier, Module, Context),
		'$lgt_check'(qualified_callable_, Goal, Context)
	;	callable(Term) ->
		true
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(clause, Term, Context) :-
	(	Term = (Head :- Body) ->
		'$lgt_check'(callable, Head, Context),
		'$lgt_check'(var_or_callable, Body, Context)
	;	callable(Term) ->
		true
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(list, Term, Context) :-
	'$lgt_check_list'(list, Term, Term, Context).

'$lgt_check'(list_or_partial_list, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_is_list_or_partial_list'(Term) ->
		true
	;	throw(error(type_error(list, Term), Context))
	).

'$lgt_check'(list(Type), Term, Context) :-
	'$lgt_check_list'(list(Type), Term, Term, Context),
	forall('$lgt_member'(Item, Term), '$lgt_check'(Type, Item, Context)).

'$lgt_check'(object, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_current_object_'(Term, _, _, _, _, _, _, _, _, _, _) ->
		true
	;	callable(Term) ->
		throw(error(existence_error(object, Term), Context))
	;	throw(error(type_error(object_identifier, Term), Context))
	).

'$lgt_check'(object_identifier, Term, Context) :-
	(	callable(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(object_identifier, Term), Context))
	).

'$lgt_check'(var_or_object_identifier, Term, Context) :-
	(	var(Term) ->
		true
	;	callable(Term) ->
		true
	;	throw(error(type_error(object_identifier, Term), Context))
	).

'$lgt_check'(protocol, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_current_protocol_'(Term, _, _, _, _) ->
		true
	;	atom(Term) ->
		throw(error(existence_error(protocol, Term), Context))
	;	throw(error(type_error(protocol_identifier, Term), Context))
	).

'$lgt_check'(protocol_identifier, Term, Context) :-
	(	atom(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(protocol_identifier, Term), Context))
	).

'$lgt_check'(var_or_protocol_identifier, Term, Context) :-
	(	var(Term) ->
		true
	;	atom(Term) ->
		true
	;	throw(error(type_error(protocol_identifier, Term), Context))
	).

'$lgt_check'(category, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_current_category_'(Term, _, _, _, _, _) ->
		true
	;	callable(Term) ->
		throw(error(existence_error(category, Term), Context))
	;	throw(error(type_error(category_identifier, Term), Context))
	).

'$lgt_check'(category_identifier, Term, Context) :-
	(	callable(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(category_identifier, Term), Context))
	).

'$lgt_check'(var_or_category_identifier, Term, Context) :-
	(	var(Term) ->
		true
	;	callable(Term) ->
		true
	;	throw(error(type_error(category_identifier, Term), Context))
	).

'$lgt_check'(entity_identifier, Term, Context) :-
	(	callable(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(entity_identifier, Term), Context))
	).

'$lgt_check'(var_or_entity_identifier, Term, Context) :-
	(	var(Term) ->
		true
	;	callable(Term) ->
		true
	;	throw(error(type_error(entity_identifier, Term), Context))
	).

'$lgt_check'(module_identifier, Term, Context) :-
	(	atom(Term) ->
		true
	;	var(Term) ->
		throw(error(instantiation_error, Context))
	;	throw(error(type_error(module_identifier, Term), Context))
	).

'$lgt_check'(var_or_module_identifier, Term, Context) :-
	(	var(Term) ->
		true
	;	atom(Term) ->
		true
	;	throw(error(type_error(module_identifier, Term), Context))
	).

'$lgt_check'(predicate_indicator, Term, Context) :-
	(	Term = Functor/Arity ->
		'$lgt_check'(atom, Functor, Context),
		'$lgt_check'(non_negative_integer, Arity, Context)
	;	throw(error(type_error(predicate_indicator, Term), Context))
	).

'$lgt_check'(var_or_predicate_indicator, Term, Context) :-
	(	var(Term) ->
		true
	;	Term = Functor/Arity ->
		'$lgt_check'(var_or_atom, Functor, Context),
		'$lgt_check'(var_or_non_negative_integer, Arity, Context)
	;	throw(error(type_error(predicate_indicator, Term), Context))
	).

'$lgt_check'(predicate_or_non_terminal_indicator, Term, Context) :-
	(	Term = Functor/Arity ->
		'$lgt_check'(atom, Functor, Context),
		'$lgt_check'(non_negative_integer, Arity, Context)
	;	Term = Functor//Arity ->
		'$lgt_check'(atom, Functor, Context),
		'$lgt_check'(non_negative_integer, Arity, Context)
	;	throw(error(type_error(predicate_indicator, Term), Context))
	).

'$lgt_check'(scope, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_valid_scope'(Term) ->
		true
	;	atom(Term) ->
		throw(error(domain_error(scope, Term), Context))
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(var_or_scope, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_valid_scope'(Term) ->
		true
	;	atom(Term) ->
		throw(error(domain_error(scope, Term), Context))
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(var_or_event, Term, Context) :-
	(	var(Term) ->
		true
	;	Term \== before,
		Term \== after ->
		throw(error(type_error(event, Term), Context))
	;	true
	).

'$lgt_check'(operator_specification, Term, Context) :-
	(	Term = op(Priority, Specifier, Operators) ->
		'$lgt_check'(operator_priority, Priority, Context),
		'$lgt_check'(operator_specifier, Specifier, Context),
		'$lgt_check'(operator_names, Operators, Context)
	;	throw(error(type_error(operator_specification, Term), Context))
	).

'$lgt_check'(operator_priority, Priority, Context) :-
	(	var(Priority) ->
		throw(error(instantiation_error, Context))
	;	\+ integer(Priority),
		throw(error(type_error(integer, Priority), Context))
	;	(Priority < 0; Priority > 1200) ->
		throw(error(domain_error(operator_priority, Priority), Context))
	;	true
	).

'$lgt_check'(var_or_operator_priority, Priority, Context) :-
	(	var(Priority) ->
		true
	;	'$lgt_check'(operator_priority, Priority, Context)
	).

'$lgt_check'(operator_specifier, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	\+ atom(Term) ->
		throw(error(type_error(atom, Term), Context))
	;	'$lgt_member'(Term, [fx, fy, xfx, xfy, yfx, xf, yf]) ->
		true
	;	throw(error(domain_error(operator_specifier, Term), Context))
	).

'$lgt_check'(var_or_operator_specifier, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_check'(operator_specifier, Term, Context)
	).

'$lgt_check'(operator_names, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	Term == (',') ->
		throw(error(permission_error(modify, operator, ','), Context))
	;	atom(Term) ->
		true
	;	\+ '$lgt_is_list'(Term) ->
		throw(type_error(list, Term))
	;	\+ ('$lgt_member'(Operator, Term), \+ '$lgt_check'(operator_name, Operator, Context))
	).

'$lgt_check'(operator_name, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	Term == (',') ->
		throw(error(permission_error(modify, operator, ','), Context))
	;	atom(Term) ->
		true
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(var_or_object_property, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_valid_object_property'(Term) ->
		true
	;	callable(Term) ->
		throw(error(domain_error(object_property, Term), Context))
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(var_or_category_property, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_valid_category_property'(Term) ->
		true
	;	callable(Term) ->
		throw(error(domain_error(category_property, Term), Context))
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(var_or_protocol_property, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_valid_protocol_property'(Term) ->
		true
	;	callable(Term) ->
		throw(error(domain_error(protocol_property, Term), Context))
	;	throw(error(type_error(callable, Term), Context))
	).

'$lgt_check'(flag, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_valid_flag'(Term) ->
		true
	;	'$lgt_user_defined_flag_'(Term, _, _) ->
		true
	;	atom(Term) ->
		throw(error(domain_error(flag, Term), Context))
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(var_or_flag, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_valid_flag'(Term) ->
		true
	;	'$lgt_user_defined_flag_'(Term, _, _) ->
		true
	;	atom(Term) ->
		throw(error(domain_error(flag, Term), Context))
	;	throw(error(type_error(atom, Term), Context))
	).

'$lgt_check'(read_write_flag, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	\+ atom(Term) ->
		throw(error(type_error(atom, Term), Context))
	;	\+ '$lgt_valid_flag'(Term),
		\+ '$lgt_user_defined_flag_'(Term, _, _) ->
		throw(error(domain_error(flag, Term), Context))
	;	'$lgt_read_only_flag'(Term) ->
		throw(error(permission_error(modify, flag, Term), Context))
	;	'$lgt_user_defined_flag_'(Term, read_only, _) ->
		throw(error(permission_error(modify, flag, Term), Context))
	;	true
	).

'$lgt_check'(var_or_read_write_flag, Term, Context) :-
	(	var(Term) ->
		true
	;	\+ atom(Term) ->
		throw(error(type_error(atom, Term), Context))
	;	\+ '$lgt_valid_flag'(Term),
		\+ '$lgt_user_defined_flag_'(Term, _, _) ->
		throw(error(domain_error(flag, Term), Context))
	;	'$lgt_read_only_flag'(Term) ->
		throw(error(permission_error(modify, flag, Term), Context))
	;	'$lgt_user_defined_flag_'(Term, read_only, _) ->
		throw(error(permission_error(modify, flag, Term), Context))
	;	true
	).

'$lgt_check'(flag_value, Term1+Term2, Context) :-
	(	var(Term2) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_valid_flag_value'(Term1, Term2) ->
		true
	;	'$lgt_user_defined_flag_'(Term1, _, Type),
		call(Type, Term2) ->
		true
	;	throw(error(domain_error(flag_value, Term1 + Term2), Context))
	).

'$lgt_check'(var_or_flag_value, Term1+Term2, Context) :-
	(	var(Term2) ->
		true
	;	'$lgt_valid_flag_value'(Term1, Term2) ->
		true
	;	'$lgt_user_defined_flag_'(Term1, _, Type),
		call(Type, Term2) ->
		true
	;	throw(error(domain_error(flag_value, Term1 + Term2), Context))
	).

'$lgt_check'(predicate_property, Term, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	'$lgt_valid_predicate_property'(Term) ->
		true
	;	'$lgt_prolog_predicate_property'(Term) ->
		true
	;	throw(error(domain_error(predicate_property, Term), Context))
	).

'$lgt_check'(var_or_predicate_property, Term, Context) :-
	(	var(Term) ->
		true
	;	'$lgt_valid_predicate_property'(Term) ->
		true
	;	'$lgt_prolog_predicate_property'(Term) ->
		true
	;	throw(error(domain_error(predicate_property, Term), Context))
	).

'$lgt_check'(key_value_info_pair, Term, Context) :-
	(	Term = (Key is Value) ->
		'$lgt_check'(atom, Key, Context),
		'$lgt_check'(nonvar, Value, Context)
	;	throw(error(type_error(key_value_info_pair, Term), Context))
	).


'$lgt_check_list'(Type, Term, Original, Context) :-
	(	var(Term) ->
		throw(error(instantiation_error, Context))
	;	Term == [] ->
		true
	;	Term = [_| Tail] ->
		'$lgt_check_list'(Type, Tail, Original, Context)
	;	throw(error(type_error(Type, Original), Context))
	).



% '$lgt_check'(+atom, @term)
%
% this simpler version of the predicate is mainly used when compiling source files

'$lgt_check'(Type, Term) :-
	catch('$lgt_check'(Type, Term, _), error(Error, _), throw(Error)).




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Logtalk startup initialization
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% dynamic entity counters initial definitions
%
% counters used when generating identifiers for dynamically created entities

'$lgt_dynamic_entity_counter_'(object,   o, 1).
'$lgt_dynamic_entity_counter_'(protocol, p, 1).
'$lgt_dynamic_entity_counter_'(category, c, 1).



% '$lgt_load_built_in_entities'(-atom)
%
% loads all built-in entities if not already loaded (when embedding
% Logtalk, the pre-compiled entities are loaded prior to this file)

'$lgt_load_built_in_entities'(ScratchDirectory) :-
	(	'$lgt_expand_library_alias'(scratch_directory, ScratchDirectory) ->
		% user override for the default scratch directory
		'$lgt_set_compiler_flag'(scratch_directory, ScratchDirectory)
	;	% use default scratch directory
		'$lgt_expand_library_alias'(logtalk_user, LogtalkUserDirectory),
		atom_concat(LogtalkUserDirectory, 'scratch/', ScratchDirectory)
	),
	'$lgt_load_built_in_entity'(expanding, protocol, 'expanding', ScratchDirectory),
	'$lgt_load_built_in_entity'(monitoring, protocol, 'monitoring', ScratchDirectory),
	'$lgt_load_built_in_entity'(forwarding, protocol, 'forwarding', ScratchDirectory),
	'$lgt_load_built_in_entity'(user, object, 'user', ScratchDirectory),
	'$lgt_load_built_in_entity'(logtalk, object, 'logtalk', ScratchDirectory),
	'$lgt_load_built_in_entity'(core_messages, category, 'core_messages', ScratchDirectory),
	% remember that all built-in entities are loaded and thus tokenization for
	% compiler and runtime error and warning messages is available
	assertz('$lgt_built_in_entities_loaded_').


'$lgt_load_built_in_entity'(Entity, Type, File, ScratchDirectory) :-
	(	Type == protocol,
		'$lgt_current_protocol_'(Entity, _, _, _, _) ->
		true
	;	Type == category,
		'$lgt_current_category_'(Entity, _, _, _, _, _) ->
		true
	;	Type == object,
		'$lgt_current_object_'(Entity, _, _, _, _, _, _, _, _, _, _) ->
		true
	;	% not an embedded entity; compile and load it
		logtalk_load(
			core(File),
			[	% we need a fixed code prefix as some of the entity predicates may need
				% to be called directly by the compiler/runtime
				code_prefix('$'),
				% delete the generated intermediate files as they may be non-portable
				% between backend Prolog compilers
				clean(on),
				% use a scratch directory where we expect to have writing permission
				scratch_directory(ScratchDirectory),
				% optimize entity code, allowing static binding to this entity resources
				optimize(on),
				% don't print any messages on the compilation and loading of these entities
				report(off),
				% prevent any attempts of logtalk_make(all) to reload this file
				reload(skip)
			]
		)
	).



% '$lgt_load_settings_file'(+atom, -callable)
%
% loads any settings file defined by the user; settings files are compiled
% and loaded silently, ignoring any errors;  the intermediate Prolog files
% are deleted using the clean/1 compiler flag in order to prevent problems
% when switching between backend Prolog compilers; returns the result from
% the loading attempt for printing after banner and default flags

'$lgt_load_settings_file'(ScratchDirectory, Result) :-
	'$lgt_default_flag'(settings_file, Value),
	Options = [
		% delete the generated intermediate file as it may be non-portable
		% between backend Prolog compilers
		clean(on),
		% use a scratch directory where we expect to have writing permission
		scratch_directory(ScratchDirectory),
		% optimize any entity code present, allowing static binding to
		% entity resources, and preventing their redefinition
		optimize(on), reload(skip),
		% don't print any compilation or loading messages
		report(off)
	],
	'$lgt_load_settings_file'(Value, Options, Result).


'$lgt_load_settings_file'(deny, _, disabled).

'$lgt_load_settings_file'(restrict, Options, Result) :-
	(	'$lgt_settings_file_search_directory'(restrict, Directory),
		'$lgt_load_settings_file_from_directory'(Directory, Options, Result) ->
		true
	;	% no settings file found
		Result = none(restrict)
	).

'$lgt_load_settings_file'(allow, Options, Result) :-
	(	'$lgt_settings_file_search_directory'(allow, Directory),
		'$lgt_load_settings_file_from_directory'(Directory, Options, Result) ->
		true
	;	% no settings file found
		Result = none(allow)
	).


'$lgt_settings_file_search_directory'(allow, Directory) :-
	'$lgt_expand_library_alias'(startup, Directory).

'$lgt_settings_file_search_directory'(allow, Directory) :-
	'$lgt_settings_file_search_directory'(restrict, Directory).

'$lgt_settings_file_search_directory'(restrict, Directory) :-
	'$lgt_expand_library_alias'(logtalk_user, Directory).

'$lgt_settings_file_search_directory'(restrict, Directory) :-
	'$lgt_expand_library_alias'(home, Directory).

'$lgt_settings_file_search_directory'(restrict, Directory) :-
	'$lgt_environment_variable'('COMSPEC', _),
	% Windows systems define this environment variable but not POSIX systems
	'$lgt_environment_variable'('APPDATA', APPDATA),
	atom_concat(APPDATA, '\\Logtalk\\', Directory).

'$lgt_settings_file_search_directory'(restrict, Directory) :-
	'$lgt_environment_variable'('XDG_CONFIG_HOME', XDG_CONFIG_HOME),
	atom_concat(XDG_CONFIG_HOME, '/logtalk/', Directory).

'$lgt_settings_file_search_directory'(restrict, Directory) :-
	'$lgt_expand_library_alias'(home, Home),
	atom_concat(Home, '.config/logtalk/', Directory).


'$lgt_load_settings_file_from_directory'(Directory, Options, Result) :-
	(	'$lgt_file_extension'(logtalk, Extension),
		% more than one possible extension may be listed in the used adapter file
		atom_concat(settings, Extension, SettingsFile),
		% construct full path to the possible settings file; directories resulting
		% from library alias expansion are guaranteed to end with a slash
		atom_concat(Directory, SettingsFile, SettingsPath),
		'$lgt_file_exists'(SettingsPath) ->
		% settings file found; compile and load it
		(	catch(logtalk_load(SettingsPath, Options), _, fail) ->
			Result = loaded(Directory)
		;	Result = error(Directory)
		)
	;	% no settings file in this directory
		fail
	).



% '$lgt_report_settings_file'(@nonvar)
%
% reports result of the attempt to load a settings file defined by the user

'$lgt_report_settings_file'(loaded(Path)) :-
	'$lgt_print_message'(comment(settings), loaded_settings_file(Path)).

'$lgt_report_settings_file'(disabled) :-
	'$lgt_print_message'(comment(settings), settings_file_disabled).

'$lgt_report_settings_file'(error(Path)) :-
	'$lgt_print_message'(error, error_loading_settings_file(Path)).

'$lgt_report_settings_file'(none(Flag)) :-
	'$lgt_print_message'(comment(settings), no_settings_file_found(Flag)).



% cache default and read-only compiler flags to improve the performance
% of the compiler by reducing the potential number of flag levels that
% need to be checked for finding the value of a flag in a given context
%
% although there should be no clauses for the '$lgt_current_flag_'/2
% predicate when this predicate is called at runtime initialization, a
% wrong file order when embedding Logtalk or a Logtalk application can
% falsify this assumption; therefore, we test for a flag definition
% before caching its default value

'$lgt_cache_compiler_flags' :-
	'$lgt_default_flag'(Name, Value),
	\+ '$lgt_current_flag_'(Name, _),
	assertz('$lgt_current_flag_'(Name, Value)),
	fail.

'$lgt_cache_compiler_flags' :-
	'$lgt_prolog_feature'(Name, Value),
	\+ '$lgt_current_flag_'(Name, _),
	assertz('$lgt_current_flag_'(Name, Value)),
	fail.

'$lgt_cache_compiler_flags' :-
	'$lgt_version_data'(VersionData),
	assertz('$lgt_current_flag_'(version_data, VersionData)).



% '$lgt_compile_default_hooks'
%
% compiles the default hooks specified on the backend adapter file or
% settings file for better performance when compiling source files

'$lgt_compile_default_hooks' :-
	(	'$lgt_compiler_flag'(hook, Hook) ->
		'$lgt_compile_hooks'(Hook)
	;	true
	).



% '$lgt_start_runtime_threading'
%
% initializes the engines mutex plus the asynchronous threaded calls mutex
% and tag counter support for backends supporting multi-threading programming
% (currently we use integers for the tag counter, which impose a limitation on
% the maximum number of tags on backends with bounded integers)

'$lgt_start_runtime_threading' :-
	(	'$lgt_prolog_feature'(engines, supported) ->
		mutex_create(_, [alias('$lgt_engines')]),
		(	current_prolog_flag(bounded, true) ->
			current_prolog_flag(min_integer, Min),
			assertz('$lgt_threaded_engine_tag_counter_'(Min))
		;	assertz('$lgt_threaded_engine_tag_counter_'(0))
		)
	;	true
	),
	(	'$lgt_prolog_feature'(threads, supported) ->
		mutex_create(_, [alias('$lgt_threaded_tag')]),
		(	current_prolog_flag(bounded, true) ->
			current_prolog_flag(min_integer, Min),
			assertz('$lgt_threaded_tag_counter_'(Min))
		;	assertz('$lgt_threaded_tag_counter_'(0))
		)
	;	true
	).



% '$lgt_check_prolog_version'
%
% checks for a compatible backend Prolog compiler version
%
% note, however, that an old and incompatible backend Prolog version may
% break Logtalk initialization before this checking predicate is called

'$lgt_check_prolog_version' :-
	'$lgt_prolog_feature'(prolog_version, Current),
	'$lgt_prolog_feature'(prolog_compatible_version, Check),
	functor(Check, Operator, 1),
	arg(1, Check, Compatible),
	(	call(Operator, Current, Compatible) ->
		true
	;	'$lgt_print_message'(
			warning(compatibility),
			possibly_incompatible_prolog_version(Current, Compatible)
		)
	).



% Logtalk runtime initialization
%
% when embedding Logtalk in a saved state created by a backend Prolog
% compiler, the runtime initialization may be triggered again when
% running the saved state; we use a dynamic predicate as a flag to
% prevent redoing this initialization
%
% we write the initialization/1 directive at the end of the file to
% avoid issues with backend Prolog compilers that fail to fully support
% ISO Prolog specified semantics for this directive

'$lgt_runtime_initialization' :-
	'$lgt_runtime_initialization_completed_',
	!.

'$lgt_runtime_initialization' :-
	'$lgt_cache_compiler_flags',
	'$lgt_load_built_in_entities'(ScratchDirectory),
	'$lgt_load_settings_file'(ScratchDirectory, Result),
	'$lgt_print_message'(banner, banner),
	'$lgt_print_message'(comment(settings), default_flags),
	'$lgt_compile_default_hooks',
	'$lgt_start_runtime_threading',
	'$lgt_report_settings_file'(Result),
	'$lgt_print_message'(comment(help), help),
	'$lgt_check_prolog_version',
	assertz('$lgt_runtime_initialization_completed_').


:- initialization('$lgt_runtime_initialization').




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  end!
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%