:- module(loader, [consult/1, expand_goal/3, expand_term/2, file_load/2, load/1, predicate_property/2, prolog_load_context/2, strip_module/3, use_module/1, use_module/2 ]). :- use_module(library(error)). :- use_module(library(lists)). :- use_module(library(pairs)). create_file_load_context(Stream, Path, Evacuable) :- '$push_load_context'(Stream, Path), '$push_load_state_payload'(Evacuable). create_load_context(Stream, Evacuable) :- '$push_load_context'(Stream, ''), '$push_load_state_payload'(Evacuable). unload_evacuable(Evacuable) :- '$pop_load_state_payload'(Evacuable), '$pop_load_context'. file_load(Stream, Path) :- file_load(Stream, Path, _). file_load(Stream, Path, Evacuable) :- create_file_load_context(Stream, Path, Evacuable), catch(loader:load_loop(Stream, Evacuable), E, (loader:unload_evacuable(Evacuable), throw(E))), '$pop_load_context'. load(Stream) :- create_load_context(Stream, Evacuable), catch(loader:load_loop(Stream, Evacuable), E, (loader:unload_evacuable(Evacuable), throw(E))), '$pop_load_context'. load_loop(Stream, Evacuable) :- read_term(Stream, Term, [variable_names(VNs), singletons(Singletons)]), ( Term == end_of_file -> close(Stream), '$conclude_load'(Evacuable) ; var(Term) -> instantiation_error(load/1) ; expand_terms_and_goals(Term, Terms), !, ( var(Terms) -> instantiation_error(load/1) ; Terms = [_|_] -> compile_dispatch_or_clause_on_list(Terms, Evacuable, VNs) ; compile_dispatch_or_clause(Terms, Evacuable, VNs) ), load_loop(Stream, Evacuable) ). inner_meta_specs((:), HeadArg, InnerHeadArgs, InnerMetaSpecs) :- !, predicate_property(HeadArg, meta_predicate(InnerMetaSpecs)), HeadArg =.. [_ | InnerHeadArgs]. inner_meta_specs(N, HeadArg, InnerHeadArgs, InnerMetaSpecs) :- integer(N), N >= 0, HeadArg =.. [Functor | InnerHeadArgs], length(InnerHeadArgs1, N), append(InnerHeadArgs, InnerHeadArgs1, InnerHeadArgs0), CompleteHeadArg =.. [Functor | InnerHeadArgs0], predicate_property(CompleteHeadArg, meta_predicate(InnerMetaSpecs)). module_expanded_head_variables_([], _, HeadVars, HeadVars). module_expanded_head_variables_([HeadArg | HeadArgs], [MetaSpec | MetaSpecs], HeadVars, HeadVars0) :- ( ( MetaSpec == (:) ; integer(MetaSpec), MetaSpec >= 0 ) -> ( var(HeadArg) -> HeadVars = [HeadArg-HeadArg | HeadVars1], module_expanded_head_variables_(HeadArgs, MetaSpecs, HeadVars1, HeadVars0) ; inner_meta_specs(MetaSpec, HeadArg, InnerHeadArgs, InnerMetaSpecs) -> module_expanded_head_variables_(InnerHeadArgs, InnerMetaSpecs, HeadVars, HeadVars1), module_expanded_head_variables_(HeadArgs, MetaSpecs, HeadVars1, HeadVars0) ; module_expanded_head_variables_(HeadArgs, MetaSpecs, HeadVars, HeadVars0) ) ; module_expanded_head_variables_(HeadArgs, MetaSpecs, HeadVars, HeadVars0) ). module_expanded_head_variables(Head, MetaSpecs, HeadVars) :- ( var(Head) -> instantiation_error(load/1) ; predicate_property(Head, meta_predicate(MetaSpecs)), Head =.. [_ | HeadArgs] -> module_expanded_head_variables_(HeadArgs, MetaSpecs, HeadVars, []) ; HeadVars = [] ). expand_terms_and_goals(Term, Terms) :- expand_term(Term, Terms0), ( var(Terms0) -> instantiation_error(load/1) ; Terms0 = (Head1 :- Body0) -> ( var(Head1) -> instantiation_error(load/1) ; prolog_load_context(module, Target), module_expanded_head_variables(Head1, MetaSpecs, HeadVars), expand_goal(Body0, Target, Body1, HeadVars) ), Terms = (Head1 :- Body1) ; Terms = Terms0 ). expand_term(UnexpandedTerm, ExpandedTerm) :- user:term_expansion(UnexpandedTerm, ExpandedTerm). compile_dispatch_or_clause_on_list([], Evacuable, VNs). compile_dispatch_or_clause_on_list([Term | Terms], Evacuable, VNs) :- compile_dispatch_or_clause(Term, Evacuable, VNs), compile_dispatch_or_clause_on_list(Terms, Evacuable, VNs). compile_dispatch_or_clause(Term, Evacuable, VNs) :- ( var(Term) -> instantiation_error(load/1) ; compile_dispatch(Term, Evacuable, VNs) -> true ; compile_clause(Term, Evacuable, VNs) ). compile_dispatch((:- Declaration), Evacuable, _VNs) :- ( var(Declaration) -> instantiation_error(load/1) ; compile_declaration(Declaration, Evacuable) ). compile_dispatch(term_expansion(Term, Terms), Evacuable, VNs) :- '$add_term_expansion_clause'('$term_expansion'(Term, Terms), Evacuable, VNs). compile_dispatch((term_expansion(Term, Terms) :- Body), Evacuable, VNs) :- '$add_term_expansion_clause'(('$term_expansion'(Term, Terms) :- Body), Evacuable, VNs). compile_dispatch(user:term_expansion(Term, Terms), Evacuable, VNs) :- '$add_term_expansion_clause'('$term_expansion'(Term, Terms), Evacuable, VNs). compile_dispatch((user:term_expansion(Term, Terms) :- Body), Evacuable, VNs) :- '$add_term_expansion_clause'(('$term_expansion'(Term, Terms) :- Body), Evacuable, VNs). compile_dispatch(goal_expansion(Term, Terms), Evacuable, VNs) :- prolog_load_context(module, Target), '$add_goal_expansion_clause'(Target, goal_expansion(Term, Terms), Evacuable, VNs). compile_dispatch((goal_expansion(Term, Terms) :- Body), Evacuable, VNs) :- prolog_load_context(module, Target), '$add_goal_expansion_clause'(Target, (goal_expansion(Term, Terms) :- Body), Evacuable, VNs). compile_dispatch(Target:goal_expansion(Term, Terms), Evacuable, VNs) :- '$add_goal_expansion_clause'(Target, goal_expansion(Term, Terms), Evacuable, VNs). compile_dispatch((Target:goal_expansion(Term, Terms) :- Body), Evacuable, VNs) :- '$add_goal_expansion_clause'(Target, (goal_expansion(Term, Terms) :- Body), Evacuable, VNs). compile_declaration(use_module(Module), Evacuable) :- use_module(Module, [], Evacuable). compile_declaration(use_module(Module, Exports), Evacuable) :- ( Exports == [] -> '$remove_module_exports'(Module, Evacuable) % TODO: implement this. ; use_module(Module, Exports, Evacuable) ). compile_declaration(module(Module, Exports), Evacuable) :- ( atom(Module) -> '$declare_module'(Module, Exports, Evacuable) ; type_error(atom, Module, load/1) ). compile_declaration(dynamic(Name/Arity), Evacuable) :- must_be(atom, Name), must_be(integer, Arity), '$add_dynamic_predicate'(Name, Arity, Evacuable). compile_declaration(initialization(Goal), Evacuable) :- prolog_load_context(module, Module), '$compile_pending_predicates'(Evacuable), expand_goal(call(Goal), Module, call(ExpandedGoal)), call(ExpandedGoal). compile_clause(Clause, Evacuable, VNs) :- '$clause_to_evacuable'(Clause, Evacuable, VNs). prolog_load_context(source, Source) :- %% The absolute path name of the file being compiled. During %% loading of a PO file, the corresponding source file name is %% returned. '$prolog_lc_source'(Source). prolog_load_context(file, File) :- %% Outside included files (see Include Declarations) this is the %% same as the source key. In included files this is the absolute %% path name of the file being included. '$prolog_lc_file'(File). prolog_load_context(directory, Dir) :- %% The absolute path name of the directory of the file being %% compiled/loaded. In included files this is the directory of the %% file being included. '$prolog_lc_dir'(Dir). prolog_load_context(module, Module) :- %% The source module (see ref-mod-mne). This is useful for example %% if you are defining clauses for user:term_expansion/6 and need %% to access the source module at compile time. '$prolog_lc_module'(Module). prolog_load_context(stream, Stream) :- %% The stream being compiled or loaded from. '$prolog_lc_stream'(Stream). prolog_load_context(term_position, TermPosition) :- %% TermPosition represents the stream position of the last term read. '$prolog_lc_stream'(Stream), stream_property(Stream, position(TermPosition)). consult(Item) :- ( atom(Item) -> use_module(Item) ; type_error(atom, Item, consult/1) ). use_module(Module) :- '$push_load_state_payload'(Evacuable), use_module(Module, [], Evacuable). use_module(Module, Exports) :- '$push_load_state_payload'(Evacuable), ( Exports == [] -> '$remove_module_exports'(Module, Evacuable) ; use_module(Module, Exports, Evacuable) ). %% If use_module is invoked in an existing load context, use its %% directory. Otherwise, use the relative path of Path. load_context_path(Module, Path) :- ( prolog_load_context(directory, CurrentDir) -> atom_concat(CurrentDir, Path, Module) ; Module = Path ). use_module(Module, Exports, Evacuable) :- ( var(Module) -> instantiation_error(load/1) ; Module = library(Library) -> ( atom(Library) -> ( '$load_compiled_library'(Library, Evacuable) -> %% TODO: What about Exports? true ; '$load_library_as_stream'(Library, Stream, Path), file_load(Stream, Path, Subevacuable), '$use_module'(Evacuable, Subevacuable, Exports) ) ; var(Library) -> instantiation_error(load/1) ; type_error(atom, Library, load/1) ) ; atom(Module) -> load_context_path(Module, Path), open(Path, read, Stream), file_load(Stream, Path, Subevacuable), '$use_module'(Evacuable, Subevacuable, Exports) ; type_error(atom, Library, load/1) ). check_predicate_property(meta_predicate, Name, Arity, MetaPredicateTerm) :- must_be(atom, Name), must_be(integer, Arity), '$cpp_meta_predicate_property'(Name, Arity, MetaPredicateTerm). predicate_property(Callable, Property) :- ( var(Callable) -> instantiation_error(load/1) ; functor(Callable, Name, Arity), ( var(Property) -> true ; functor(Property, PropertyType, _) ), check_predicate_property(PropertyType, Name, Arity, Property) ). strip_module_(M0, G0, M1, G1) :- ( nonvar(G0), G0 = (MG1:G2) -> strip_module_(MG1, G2, M1, G1) ; M0 = M1, G0 = G1 ). strip_module(Goal, M, G) :- strip_module_(_, Goal, M, G). expand_subgoal(UnexpandedGoals, MS, Module, ExpandedGoals, HeadVars) :- ( var(UnexpandedGoals) -> UnexpandedGoals = ExpandedGoals ; user:goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1), ( Module \== user -> user:goal_expansion(UnexpandedGoals1, user, Goals) ; Goals = UnexpandedGoals1 ), ( inner_meta_specs(MS, Goals, _, MetaSpecs) -> expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) ; Goals = ExpandedGoals ) ; UnexpandedGoals = ExpandedGoals ). expand_module_name(ESG0, M, ESG) :- ( var(ESG0) -> ESG = M:ESG0 ; ESG0 = _:ESG1 -> ESG = ESG0 ; ESG = M:ESG0 ). expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars) :- ( ( MS == (:) ; integer(MS), MS >= 0 ) -> ( var(SG), pairs:same_key(SG, HeadVars, [_|_], _) -> expand_subgoal(SG, MS, M, ESG, HeadVars) ; expand_subgoal(SG, MS, M, ESG0, HeadVars), expand_module_name(ESG0, M, ESG) ), expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars) ; ESG = SG, expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars) ). expand_meta_predicate_subgoals([], _, _, [], _). expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :- Goals =.. [GoalFunctor | SubGoals], ( GoalFunctor == (:) -> false ; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars), ExpandedGoals =.. [GoalFunctor | ExpandedGoalList] ). expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- expand_goal(UnexpandedGoals, Module, ExpandedGoals, []), !. expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- ( var(UnexpandedGoals) -> UnexpandedGoals = ExpandedGoals ; user:goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1), ( Module \== user -> user:goal_expansion(UnexpandedGoals1, user, Goals) ; Goals = UnexpandedGoals1 ), ( Goals = (Goal0, Goals0) -> ( expand_goal(Goal0, Module, Goal1, HeadVars) -> expand_goal(Goals0, Module, Goals1, HeadVars), thread_goals(Goal1, ExpandedGoals, Goals1, (',')) ; expand_goal(Goals0, Module, Goals1, HeadVars), ExpandedGoals = (Goal0, Goals1) ) ; Goals = (Goals0 -> Goals1) -> expand_goal(Goals0, Module, ExpandedGoals0, HeadVars), expand_goal(Goals1, Module, ExpandedGoals1, HeadVars), ExpandedGoals = (ExpandedGoals0 -> ExpandedGoals1) ; Goals = (Goals0 ; Goals1) -> expand_goal(Goals0, Module, ExpandedGoals0, HeadVars), expand_goal(Goals1, Module, ExpandedGoals1, HeadVars), ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1) ; Goals = (\+ Goals0) -> expand_goal(Goals0, Module, Goals1, HeadVars), ExpandedGoals = (\+ Goals1) ; predicate_property(Goals, meta_predicate(MetaSpecs)) -> expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) ; thread_goals(Goals, ExpandedGoals, (',')) ; Goals = ExpandedGoals ) ). thread_goals(Goals0, Goals1, Functor) :- ( var(Goals0) -> Goals0 = Goals1 ; ( Goals0 = [G | Gs] -> ( Gs = [] -> Goals1 = G ; Goals1 =.. [Functor, G, Goals2], thread_goals(Gs, Goals2, Functor) ) ; Goals1 = Goals0 ) ). thread_goals(Goals0, Goals1, Hole, Functor) :- ( var(Goals0) -> Goals1 =.. [Functor, Goals0, Hole] ; ( Goals0 = [G | Gs] -> ( Gs == [] -> Goals1 =.. [Functor, G, Hole] ; Goals1 =.. [Functor, G, Goals2], thread_goals(Gs, Goals2, Hole, Functor) ) ; Goals1 =.. [Functor, Goals0, Hole] ) ).