diff --git a/CMakeLists.txt b/CMakeLists.txt index e6fa787..7b7ad86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,6 +143,14 @@ set(INSTALL_TESTS_DIR ${SWIPL_INSTALL_PREFIX}/test) endif() +if(NOT SWIPL_PKG_NAME) + if(SWIPL_INSTALL_DIR STREQUAL "swi-prolog") + set(SWIPL_PKG_NAME ${SWIPL_INSTALL_DIR}) + else() + set(SWIPL_PKG_NAME "swipl") + endif() +endif() + if(MSVC) add_compile_options(/W3) else() diff --git a/VERSION b/VERSION index e624caf..e72091d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.29 +8.1.30 diff --git a/boot/bags.pl b/boot/bags.pl index 20a0ff9..acee1fe 100644 --- a/boot/bags.pl +++ b/boot/bags.pl @@ -3,8 +3,9 @@ Author: Jan Wielemaker E-mail: J.Wielemaker@vu.nl WWW: http://www.swi-prolog.org - Copyright (c) 1985-2014, University of Amsterdam, + Copyright (c) 1985-2020, University of Amsterdam, VU University Amsterdam + CWI, Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without @@ -180,11 +181,27 @@ ( Vars == v -> findall(Templ, Goal, List), List \== [] - ; findall(Vars-Templ, Goal, Answers), - bind_bagof_keys(Answers,_), + ; alloc_bind_key_list(Vars, VDict), + findall(Vars-Templ, Goal, Answers), + bind_bagof_keys(Answers, VDict), keysort(Answers, Sorted), pick(Sorted, Vars, List) ). + +%! alloc_bind_key_list(+Vars, -VDict) is det. +% +% Pre-allocate the variable dictionary used by bind_bagof_keys/2. By +% pre-allocating this list all variables bound become references from +% the `Vars` of each answer to this dictionary. If we do not +% preallocate we create a huge reference chain from VDict through each +% of the answers, causing serious slowdown in the subsequent keysort. +% +% The slowdown was discovered by Jan Burse. + +alloc_bind_key_list(Vars, VDict) :- + functor(Vars, _, Count), + length(List, Count), + '$append'(List, _, VDict). %! bind_bagof_keys(+VarsTemplPairs, -SharedVars) % @@ -243,13 +260,14 @@ -> findall(Templ, Goal, Answers), Answers \== [], sort(Answers, List) - ; findall(Vars-Templ, Goal, Answers), + ; alloc_bind_key_list(Vars, VDict), + findall(Vars-Templ, Goal, Answers), ( ground(Answers) - -> sort(Answers,Sorted), - pick(Sorted,Vars,List) - ; bind_bagof_keys(Answers,_VDict), + -> sort(Answers, Sorted), + pick(Sorted, Vars, List) + ; bind_bagof_keys(Answers, VDict), sort(Answers, Sorted), pick(Sorted, Vars, Listu), - sort(Listu,List) % Listu ordering may be nixed by Vars + sort(Listu, List) % Listu ordering may be nixed by Vars ) ). diff --git a/boot/init.pl b/boot/init.pl index cb16ed5..d949007 100644 --- a/boot/init.pl +++ b/boot/init.pl @@ -3761,6 +3761,11 @@ -> true ; '$type_error'(callable, X) ). +'$must_be'(acyclic, X) :- !, + ( acyclic_term(X) + -> true + ; '$domain_error'(acyclic_term, X) + ). '$must_be'(oneof(Type, Domain, List), X) :- !, '$must_be'(Type, X), ( memberchk(X, List) diff --git a/boot/tabling.pl b/boot/tabling.pl index 968c1dc..ace2f49 100644 --- a/boot/tabling.pl +++ b/boot/tabling.pl @@ -55,7 +55,9 @@ start_tabling/3, % +Closure, +Wrapper, :Worker start_subsumptive_tabling/3,% +Closure, +Wrapper, :Worker - moded_start_tabling/5, % +Closure, +Wrapper, :Worker, :Variant, ?ModeArgs + start_abstract_tabling/3, % +Closure, +Wrapper, :Worker + start_moded_tabling/5, % +Closure, +Wrapper, :Worker, + % :Variant, ?ModeArgs '$tbl_answer'/4, % +Trie, -Return, -ModeArgs, -Delay @@ -74,7 +76,8 @@ not_exists(0), tabled_call(0), start_tabling(+, +, 0), - start_tabling(+, +, 0, +, ?), + start_abstract_tabling(+, +, 0), + start_moded_tabling(+, +, 0, +, ?), current_table(:, -), abolish_table_subgoals(:), '$wfs_call'(0, :). @@ -271,22 +274,18 @@ prolog_listen(untable, untable_reconsult). -%! start_tabling(:Wrapper, :Implementation) -% -% Execute Implementation using tabling. This predicate should not -% be called directly. The table/1 directive causes a predicate to -% be translated into a renamed implementation and a wrapper that -% involves this predicate. -% -% @compat This interface may change or disappear without notice -% from future versions. - '$wrap_tabled'(Head, Options) :- get_dict(mode, Options, subsumptive), !, set_pattributes(Head, Options), '$wrap_predicate'(Head, table, Closure, Wrapped, start_subsumptive_tabling(Closure, Head, Wrapped)). +'$wrap_tabled'(Head, Options) :- + get_dict(subgoal_abstract, Options, _Abstract), + !, + set_pattributes(Head, Options), + '$wrap_predicate'(Head, table, Closure, Wrapped, + start_abstract_tabling(Closure, Head, Wrapped)). '$wrap_tabled'(Head, Options) :- !, set_pattributes(Head, Options), @@ -311,12 +310,28 @@ tabled_attribute(dynamic). tabled_attribute(tshared). tabled_attribute(max_answers). -tabled_attribute(abstract_subgoal). +tabled_attribute(subgoal_abstract). tabled_attribute(answer_abstract). +%! start_tabling(:Closure, :Wrapper, :Implementation) +% +% Execute Implementation using tabling. This predicate should not be +% called directly. The table/1 directive causes a predicate to be +% translated into a renamed implementation and a wrapper that involves +% this predicate. +% +% @arg Closure is the wrapper closure to find the predicate quickly. +% It is also allowed to pass nothing. In that cases the predicate is +% looked up using Wrapper. We suggest to pass `0` in this case. +% +% @compat This interface may change or disappear without notice +% from future versions. start_tabling(Closure, Wrapper, Worker) :- '$tbl_variant_table'(Closure, Wrapper, Trie, Status, Skeleton), + start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton). + +start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) :- tdebug(deadlock, 'Got table ~p, status ~p', [Trie, Status]), ( Status == complete -> trie_gen_compiled(Trie, Skeleton) @@ -342,7 +357,6 @@ tdebug(schedule, 'Leader ~p done, status = ~p', [Goal, LStatus]), done_leader(LStatus, Fresh, Skeleton, Clause). - %! restart_tabling(+Closure, +Wrapper, +Worker) % % We were aborted due to a deadlock. Simply retry. We sleep a very @@ -357,8 +371,13 @@ sleep(0.000001), start_tabling(Closure, Wrapper, Worker). - -%! start_subsumptive_tabling(:Wrapper, :Implementation) +restart_abstract_tabling(Closure, Wrapper, Worker) :- + tdebug(user_goal(Wrapper, Goal)), + tdebug(deadlock, 'Deadlock running ~p; retrying', [Goal]), + sleep(0.000001), + start_abstract_tabling(Closure, Wrapper, Worker). + +%! start_subsumptive_tabling(:Closure, :Wrapper, :Implementation) % % (*) We should __not__ use trie_gen_compiled/2 here as this will % enumerate all answers while '$tbl_answer_update_dl'/2 uses the @@ -404,6 +423,77 @@ [GenSkeleton+Skeleton]). unify_subsumptive(X,X). + +%! start_abstract_tabling(:Closure, :Wrapper, :Worker) +% +% Deal with ``table p/1 as subgoal_abstract(N)``. This is a merge +% between variant and subsumptive tabling. If the goal is not +% abstracted this is simple variant tabling. If the goal is abstracted +% we must solve the more general goal and use answers from the +% abstract table. +% +% Wrapper is e.g., user:p(s(s(s(X))),Y) +% Worker is e.g., call((p/2)(s(s(s(X))),Y)) + +start_abstract_tabling(Closure, Wrapper, Worker) :- + '$tbl_abstract_table'(Closure, Wrapper, Trie, _Abstract, Status, Skeleton), + tdebug(abstract, 'Wrapper=~p, Worker=~p, Skel=~p', + [Wrapper, Worker, Skeleton]), + ( is_most_general_term(Skeleton) % TBD: Fill and test Abstract + -> start_tabling_2(Closure, Wrapper, Worker, Trie, Status, Skeleton) + ; Status == complete + -> '$tbl_answer_update_dl'(Trie, Skeleton) + ; functor(Status, fresh, 2) + -> '$tbl_table_status'(Trie, _, GenWrapper, GenSkeleton), + abstract_worker(Worker, GenWrapper, GenWorker), + catch(create_abstract_table(Trie, Status, Skeleton, GenSkeleton, GenWrapper, + GenWorker), + deadlock, + restart_abstract_tabling(Closure, Wrapper, Worker)) + ; Status == invalid + -> '$tbl_table_status'(Trie, _, GenWrapper, GenSkeleton), + reeval(ATrie, GenWrapper, GenSkeleton), + Wrapper = GenWrapper, + '$tbl_answer_update_dl'(ATrie, Skeleton) + ; shift(call_info(GenSkeleton, Skeleton, Status)), + unify_subsumptive(Skeleton, GenSkeleton) + ). + +create_abstract_table(Trie, Fresh, Skeleton, GenSkeleton, Wrapper, Worker) :- + tdebug(Fresh = fresh(SCC, WorkList)), + tdebug(wl_goal(WorkList, Goal, _)), + tdebug(schedule, 'Created component ~d for ~p', [SCC, Goal]), + setup_call_catcher_cleanup( + '$idg_set_current'(OldCurrent, Trie), + run_leader(GenSkeleton, Worker, Fresh, LStatus, _Clause), + Catcher, + finished_leader(OldCurrent, Catcher, Fresh, Wrapper)), + tdebug(schedule, 'Leader ~p done, status = ~p', [Goal, LStatus]), + Skeleton = GenSkeleton, + done_abstract_leader(LStatus, Fresh, GenSkeleton, Trie). + +abstract_worker(_:call(Term), _M:GenWrapper, call(GenTerm)) :- + functor(Term, Closure, _), + GenWrapper =.. [_|Args], + GenTerm =.. [Closure|Args]. + +:- '$hide'((done_abstract_leader/4)). + +done_abstract_leader(complete, _Fresh, Skeleton, Trie) :- + !, + '$tbl_answer_update_dl'(Trie, Skeleton). +done_abstract_leader(final, fresh(SCC, _Worklist), Skeleton, Trie) :- + !, + '$tbl_free_component'(SCC), + '$tbl_answer_update_dl'(Trie, Skeleton). +done_abstract_leader(_,_,_,_). + +%! done_leader(+Status, +Fresh, +Skeleton, -Clause) +% +% Called on completion of a table. Possibly destroys the component and +% generates the answers from the complete table. The last cases deals +% with leaders that are merged into a higher SCC (and thus no longer a +% leader). :- '$hide'((done_leader/4, finished_leader/4)). @@ -507,7 +597,7 @@ dependency(SrcSkeleton, Continuation, Skeleton, WorkList, AllDelays)) ). -%! moded_start_tabling(+Closure, :Wrapper, :Implementation, +Variant, +ModeArgs) +%! start_moded_tabling(+Closure, :Wrapper, :Implementation, +Variant, +ModeArgs) % % As start_tabling/2, but in addition separates the data stored in the % answer trie in the Variant and ModeArgs. @@ -516,12 +606,12 @@ '$set_predicate_attribute'(Head, tabled, true), '$wrap_predicate'(Head, table, Closure, Wrapped, ( ModeTest, - moded_start_tabling(Closure, Head, Wrapped, + start_moded_tabling(Closure, Head, Wrapped, WrapperNoModes, ModeArgs) )). -moded_start_tabling(Closure, Wrapper, Worker, WrapperNoModes, ModeArgs) :- +start_moded_tabling(Closure, Wrapper, Worker, WrapperNoModes, ModeArgs) :- '$tbl_moded_variant_table'(Closure, WrapperNoModes, Trie, Status, Skeleton), ( Status == complete -> moded_gen_answer(Trie, Skeleton, ModeArgs) @@ -699,6 +789,8 @@ %! tnot(:Goal) % % Tabled negation. +% +% (*): Only variant tabling is allowed under tnot/1. tnot(Goal0) :- '$tnot_implementation'(Goal0, Goal), % verifies Goal is tabled @@ -713,7 +805,9 @@ ; negation_suspend(Goal, Skeleton, Status) ) ; tdebug(tnot, 'tnot: ~p: fresh', [Goal]), - ( call(Goal), + ( '$wrapped_implementation'(Goal, table, Implementation), % see (*) + functor(Implementation, Closure, _), + start_tabling(Closure, Goal, Implementation), fail ; '$tbl_existing_variant_table'(_, Goal, Trie, NewStatus, NewSkeleton), tdebug(tnot, 'tnot: fresh ~p now ~p', [Goal, NewStatus]), @@ -901,10 +995,13 @@ abolish_table_subgoals(SubGoal0) :- '$tbl_implementation'(SubGoal0, M:SubGoal), !, - forall(( '$tbl_variant_table'(VariantTrie), - trie_gen(VariantTrie, M:SubGoal, Trie) - ), - '$tbl_destroy_table'(Trie)). + '$must_be'(acyclic, SubGoal), + ( '$tbl_variant_table'(VariantTrie), + trie_gen(VariantTrie, M:SubGoal, Trie), + '$tbl_destroy_table'(Trie), + fail + ; true + ). abolish_table_subgoals(_). %! abolish_module_tables(+Module) is det. @@ -1015,11 +1112,13 @@ system:term_expansion/2. wrappers(Spec, M) --> - { tabling_defaults([ table_incremental-(incremental=true), - table_shared-(tshared=true), - table_subsumptive-((mode)=subsumptive) - ], - #{}, Defaults) + { tabling_defaults( + [ (table_incremental=true) - (incremental=true), + (table_shared=true) - (tshared=true), + (table_subsumptive=true) - ((mode)=subsumptive), + call(subgoal_size_restraint(Level)) - (subgoal_abstract=Level) + ], + #{}, Defaults) }, wrappers(Spec, M, Defaults). @@ -1102,13 +1201,24 @@ tabling_defaults([], Dict, Dict). -tabling_defaults([Flag-(Opt=Value)|T], Dict0, Dict) :- - ( current_prolog_flag(Flag, true) +tabling_defaults([Condition-(Opt=Value)|T], Dict0, Dict) :- + ( tabling_default(Condition) -> Dict1 = Dict0.put(Opt,Value) ; Dict1 = Dict0 ), tabling_defaults(T, Dict1, Dict). +tabling_default(Flag=FValue) :- + !, + current_prolog_flag(Flag, FValue). +tabling_default(call(Term)) :- + call(Term). + +% Called from wrappers//2. + +subgoal_size_restraint(Level) :- + current_prolog_flag(max_table_subgoal_size_action, abstract), + current_prolog_flag(max_table_subgoal_size, Level). %! table_options(+Options, +OptDictIn, -OptDictOut) % @@ -1145,9 +1255,9 @@ table_options(max_answers(Count), Opts0, Opts1) :- !, restraint(max_answers, Count, Opts0, Opts1). -table_options(abstract_subgoal(Size), Opts0, Opts1) :- - !, - restraint(abstract_subgoal, Size, Opts0, Opts1). +table_options(subgoal_abstract(Size), Opts0, Opts1) :- + !, + restraint(subgoal_abstract, Size, Opts0, Opts1). table_options(answer_abstract(Size), Opts0, Opts1) :- !, restraint(answer_abstract, Size, Opts0, Opts1). diff --git a/cmake/PackageSelection.cmake b/cmake/PackageSelection.cmake index fd66be7..286ae56 100644 --- a/cmake/PackageSelection.cmake +++ b/cmake/PackageSelection.cmake @@ -264,6 +264,7 @@ endif() if(NOT MULTI_THREADED) swipl_del_package(swipl-win "requires multi-threading") + swipl_del_package(tipc "requires multi-threading") endif() if(INSTALL_DOCUMENTATION) diff --git a/library/apply.pl b/library/apply.pl index 7f581c2..4527fd6 100644 --- a/library/apply.pl +++ b/library/apply.pl @@ -178,11 +178,23 @@ * MAPLIST/2... * *******************************/ -%! maplist(:Goal, ?List) -% -% True if Goal can successfully be applied on all elements of -% List. Arguments are reordered to gain performance as well as to -% make the predicate deterministic under normal circumstances. +%! maplist(:Goal, ?List1). +%! maplist(:Goal, ?List1, ?List2). +%! maplist(:Goal, ?List1, ?List2, ?List3). +%! maplist(:Goal, ?List1, ?List2, ?List3, ?List4). +% +% True if Goal is successfully applied on all matching elements of the +% list. The maplist family of predicates is defined as: +% +% ``` +% maplist(P, [X11,...,X1n], ..., [Xm1,...,Xmn]) :- +% P(X11, ..., Xm1), +% ... +% P(X1n, ..., Xmn). +% ``` +% +% This family of predicates is deterministic iff Goal is deterministic +% and List1 is a proper list, i.e., a list that ends in `[]`. maplist(Goal, List) :- maplist_(List, Goal). @@ -192,10 +204,6 @@ call(Goal, Elem), maplist_(Tail, Goal). -%! maplist(:Goal, ?List1, ?List2) -% -% As maplist/2, operating on pairs of elements from two lists. - maplist(Goal, List1, List2) :- maplist_(List1, List2, Goal). @@ -204,10 +212,6 @@ call(Goal, Elem1, Elem2), maplist_(Tail1, Tail2, Goal). -%! maplist(:Goal, ?List1, ?List2, ?List3) -% -% As maplist/2, operating on triples of elements from three lists. - maplist(Goal, List1, List2, List3) :- maplist_(List1, List2, List3, Goal). @@ -215,12 +219,6 @@ maplist_([Elem1|Tail1], [Elem2|Tail2], [Elem3|Tail3], Goal) :- call(Goal, Elem1, Elem2, Elem3), maplist_(Tail1, Tail2, Tail3, Goal). - - -%! maplist(:Goal, ?List1, ?List2, ?List3, ?List4) -% -% As maplist/2, operating on quadruples of elements from four -% lists. maplist(Goal, List1, List2, List3, List4) :- maplist_(List1, List2, List3, List4, Goal). diff --git a/library/dialect/xsb/machine.pl b/library/dialect/xsb/machine.pl index b892f08..b7ad5f4 100644 --- a/library/dialect/xsb/machine.pl +++ b/library/dialect/xsb/machine.pl @@ -3,7 +3,8 @@ Author: Jan Wielemaker E-mail: J.Wielemaker@vu.nl WWW: http://www.swi-prolog.org - Copyright (c) 2019, VU University Amsterdam + Copyright (c) 2019-2020, VU University Amsterdam + CWI, Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without @@ -37,6 +38,7 @@ trimcore/0, abolish_table_info/0, + close_open_tables/1, % ? str_cat/3, @@ -79,6 +81,13 @@ % Undocumented in the XSB manual. abolish_table_info. + +%! close_open_tables(?Arg) +% +% Undocumented in the XSB manual. Tables are always closed on +% exceptions, so it is unclear what this should do? + +close_open_tables(_). %! str_cat(+Atom1, +Atom2, -Atom3) diff --git a/library/dialect/xsb/source.pl b/library/dialect/xsb/source.pl index a1968a0..9d2c647 100644 --- a/library/dialect/xsb/source.pl +++ b/library/dialect/xsb/source.pl @@ -34,11 +34,10 @@ */ :- module(xsb_source, []). -:- autoload(library(apply),[convlist/3,partition/4]). -:- autoload(library(debug),[debug/3]). -:- autoload(library(error),[instantiation_error/1]). -:- autoload(library(lists),[append/3,flatten/2]). - +:- autoload(library(apply), [convlist/3,partition/4]). +:- autoload(library(debug), [debug/3]). +:- autoload(library(error), [instantiation_error/1]). +:- autoload(library(occurs), [sub_term/2]). /** Support XSB source .P files @@ -67,7 +66,7 @@ file_name_extension(Path, 'P', File), include_options(File, Include), compiler_options(COptions), - append(Include, COptions, Extra), + '$append'(Include, COptions, Extra), xsb_directives(File, Directives), directive_exports(Directives, Public, Directives1), ( Public == [] @@ -81,15 +80,14 @@ (:- use_module(library(tables))) | Out2 ], - append(Extra, More, Out2), + '$append'(Extra, More, Out2), ( nonvar(Module) -> setup_call_cleanup( '$set_source_module'(OldM, Module), - convlist(head_directive(File), Directives1, More0), + phrase(head_directives(Directives1, File), More), '$set_source_module'(OldM)) - ; convlist(head_directive(File), Directives1, More0) + ; phrase(head_directives(Directives1, File), More) ), - flatten(More0, More), debug(xsb(header), '~p: directives: ~p', [File, More]). include_options(File, Option) :- @@ -142,21 +140,64 @@ export_decl(PI) --> [PI]. -%! head_directive(+File, +Directive, -PrefixedDirective) is semidet. - -head_directive(File, import(from(Preds, From)), - (:- xsb_import(Preds, From))) :- - assertz(xsb:moved_directive(File, import(from(Preds, From)))). -head_directive(File, table(Preds as XSBOptions), Clauses) :- - ignored_table_options(XSBOptions, Options), - ( Options == true - -> expand_term((:- table(Preds)), Clauses) - ; expand_term((:- table(Preds as Options)), Clauses) - ), - assertz(xsb:moved_directive(File, table(Preds as XSBOptions))). -head_directive(File, table(Preds), Clauses) :- - expand_term((:- table(Preds)), Clauses), - assertz(xsb:moved_directive(File, table(Preds))). +%! head_directives(+Directives, +File)// is det. +%! head_directives_s(+Directives, +State)// is det. + +head_directives(Directives, File) --> + { current_prolog_flag(max_table_subgoal_size_action, Action), + ( current_prolog_flag(max_table_subgoal_size, Size) + -> true + ; Size = -1 + ) + }, + head_directives_s(Directives, + #{file: File, + max_table_subgoal_size_action: Action, + max_table_subgoal_size:Size + }). + + +head_directives_s([], _) --> []. +head_directives_s([H|T], State0) --> + { update_state(H, State0, State) }, + !, + head_directives_s(T, State). +head_directives_s([H|T], State) --> + head_directive(H, State), + head_directives_s(T, State). + +update_state(set_prolog_flag(max_table_subgoal_size_action, Action), + State0, State) :- + State = State0.put(max_table_subgoal_size_action, Action). +update_state(set_prolog_flag(max_table_subgoal_size, Size), + State0, State) :- + State = State0.put(max_table_subgoal_size, Size). + +%! head_directive(+Directive, +State)// is det. + +head_directive(import(from(Preds, From)), State) --> + !, + { assertz(xsb:moved_directive(State.file, import(from(Preds, From)))) + }, + [ (:- xsb_import(Preds, From)) ]. +head_directive(table(Preds as XSBOptions), State) --> + !, + { ignored_table_options(XSBOptions, Options), + table_clauses(Preds, Options, Clauses, State), + assertz(xsb:moved_directive(State.file, table(Preds as XSBOptions))) + }, + seq(Clauses). +head_directive(table(Preds), State) --> + !, + { table_clauses(Preds, true, Clauses, State), + assertz(xsb:moved_directive(State.file, table(Preds))) + }, + seq(Clauses). +head_directive(_, _) --> + []. + +seq([]) --> []. +seq([H|T]) --> [H], seq(T). ignored_table_options((A0,B0), Conj) :- !, @@ -177,12 +218,30 @@ supported_table_option(shared). supported_table_option(private). supported_table_option(max_answers(_)). -supported_table_option(abstract_subgoal(_)). +supported_table_option(subgoal_abstract(_)). supported_table_option(answer_abstract(_)). mkconj(true, X, X) :- !. mkconj(X, true, X) :- !. mkconj(X, Y, (X,Y)) :- !. + +table_clauses(Preds, Options0, Clauses, State) :- + add_defaults(Options0, Options, State), + ( Options == true + -> expand_term((:- table(Preds)), Clauses) + ; expand_term((:- table(Preds as Options)), Clauses) + ). + +add_defaults(Opts, Opts, _) :- + sub_term(subgoal_abstract(_), Opts), + !. +add_defaults(Opts0, Opts, State) :- + #{max_table_subgoal_size_action:abstract, + max_table_subgoal_size:Size} :< State, + Size >= 0, + !, + mkconj(Opts0, subgoal_abstract(Size), Opts). +add_defaults(Opts, Opts, _). %! xsb_directives(+File, -Directives) is semidet. % diff --git a/library/dialect/xsb/tables.pl b/library/dialect/xsb/tables.pl index 6b027b7..2fd443e 100644 --- a/library/dialect/xsb/tables.pl +++ b/library/dialect/xsb/tables.pl @@ -269,6 +269,9 @@ comma_list(tnot(M:G), M) --> !, [tnot(G)]. +comma_list(system:G, _) --> + !, + [G]. comma_list(G, _) --> [G]. diff --git a/library/dialect/xsb/timed_call.pl b/library/dialect/xsb/timed_call.pl index 7ec1c93..310b743 100644 --- a/library/dialect/xsb/timed_call.pl +++ b/library/dialect/xsb/timed_call.pl @@ -105,6 +105,7 @@ '$timed_call_nested'(Goal, M:Options) :- memberchk(max(MaxInterval, MaxHandler), Options), + !, run_max_goal(Goal, MaxInterval, MaxHandler, M:Options). '$timed_call_nested'(Goal, M:Options) :- memberchk(repeating(RepInterval, RepHandler), Options), diff --git a/library/dialect/xsb.pl b/library/dialect/xsb.pl index 43508db..f5fc3cc 100644 --- a/library/dialect/xsb.pl +++ b/library/dialect/xsb.pl @@ -47,6 +47,7 @@ compiler_options/1, % +Options xsb_import/2, % +Preds, From + xsb_set_prolog_flag/2, % +Flag, +Value fail_if/1, % :Goal @@ -179,6 +180,8 @@ xsb_mapped_predicate(expand_file_name(File, Expanded), xsb_expand_file_name(File, Expanded)). +xsb_mapped_predicate(set_prolog_flag(Flag, Value), + xsb_set_prolog_flag(Flag, Value)). xsb_mapped_predicate(abolish_module_tables(UserMod), abolish_module_tables(user)) :- UserMod == usermod. @@ -312,6 +315,21 @@ !. map_module(XSB, Module) :- assertz(mapped__module(XSB, Module)). + + +%! xsb_set_prolog_flag(+Flag, +Value) +% +% Map some XSB Prolog flags to their SWI-Prolog's equivalents. + +xsb_set_prolog_flag(unify_with_occurs_check, XSBVal) :- + !, + map_bool(XSBVal, Val), + set_prolog_flag(occurs_check, Val). +xsb_set_prolog_flag(Flag, Value) :- + set_prolog_flag(Flag, Value). + +map_bool(on, true). +map_bool(off, false). /******************************* diff --git a/library/help.pl b/library/help.pl index a21e2ca..f0a5134 100644 --- a/library/help.pl +++ b/library/help.pl @@ -168,13 +168,13 @@ help_html(Matches, How, HTML) :- phrase(html(html([ head([]), body([ \match_type(How), - \man_pages(Matches, - [ no_manual(fail), - links(false), - link_source(false), - navtree(false), - server(false) - ]) + dl(\man_pages(Matches, + [ no_manual(fail), + links(false), + link_source(false), + navtree(false), + server(false) + ])) ]) ])), Tokens), diff --git a/library/listing.pl b/library/listing.pl index de67871..081849f 100644 --- a/library/listing.pl +++ b/library/listing.pl @@ -52,7 +52,7 @@ :- autoload(library(option),[option/2,option/3,meta_options/3]). :- autoload(library(prolog_clause),[clause_info/5]). -:- set_prolog_flag(generate_debug_info, false). +%:- set_prolog_flag(generate_debug_info, false). :- module_transparent listing/0. @@ -663,23 +663,17 @@ portray_body(Body, BodyIndent, indent, RightPri, Out, Options) ), full_stop(Out). -do_portray_clause(Out, (:-use_module(File, Imports)), Options) :- - length(Imports, Len), - Len > 3, - !, +do_portray_clause(Out, (:-Directive), Options) :- + wrapped_list_directive(Directive), + !, + Directive =.. [Name, Arg, List], option(indent(LeftMargin), Options, 0), indent(Out, LeftMargin), - ListIndent is LeftMargin+14, - format(Out, ':- use_module(~q,', [File]), - portray_list(Imports, ListIndent, Out, Options), - write(Out, ').\n'). -do_portray_clause(Out, (:-module(Module, Exports)), Options) :- - !, - option(indent(LeftMargin), Options, 0), - indent(Out, LeftMargin), - ModuleIndent is LeftMargin+10, - format(Out, ':- module(~q,', [Module]), - portray_list(Exports, ModuleIndent, Out, Options), + format(Out, ':- ~q(', [Name]), + line_position(Out, Indent), + format(Out, '~q,', [Arg]), + nlindent(Out, Indent), + portray_list(List, Indent, Out, Options), write(Out, ').\n'). do_portray_clause(Out, (:-Directive), Options) :- !, @@ -702,6 +696,9 @@ '$put_token'(Out, '.'), nl(Out). +wrapped_list_directive(module(_,_)). +%wrapped_list_directive(use_module(_,_)). +%wrapped_list_directive(autoload(_,_)). %! portray_body(+Term, +Indent, +DoIndent, +Priority, +Out, +Options) % @@ -959,7 +956,6 @@ !, write(Out, []). portray_list(List, Indent, Out, Options) :- - nlindent(Out, Indent), write(Out, '[ '), EIndent is Indent + 2, portray_list_elements(List, EIndent, Out, Options), diff --git a/library/prolog_colour.pl b/library/prolog_colour.pl index 9a824a5..fe5e5f3 100644 --- a/library/prolog_colour.pl +++ b/library/prolog_colour.pl @@ -1959,14 +1959,14 @@ valid_decl_option(Option, Which), !, functor(Option, Name, _), - colour_item(decl_option(Name), TB, Pos), - ( Pos = term_position(_,_,_,_,[ArgPos]) - -> ( arg(1, Option, Value), + ( Pos = term_position(_,_,FF,FT,[ArgPos]) + -> colour_item(decl_option(Name), TB, FF-FT), + ( arg(1, Option, Value), nonneg_or_false(Value) -> colourise_term_arg(Value, TB, ArgPos) ; colour_item(type_error(decl_option_value(Which)), TB, ArgPos) ) - ; true + ; colour_item(decl_option(Name), TB, Pos) ). colourise_decl_options(_, Which, TB, Pos) :- colour_item(type_error(decl_option(Which)), TB, Pos). diff --git a/library/prolog_deps.pl b/library/prolog_deps.pl new file mode 100755 index 0000000..db9559b --- /dev/null +++ b/library/prolog_deps.pl @@ -0,0 +1,510 @@ +/* Part of SWI-Prolog + + Author: Jan Wielemaker + E-mail: J.Wielemaker@vu.nl + WWW: http://www.swi-prolog.org + Copyright (c) 2020, VU University Amsterdam + CWI, Amsterdam + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +:- module(prolog_deps, + [ file_autoload_directives/3, % +File, -Directives, +Options + file_auto_import/2 % +File, +Options + ]). +:- use_module(library(apply), [convlist/3, maplist/3]). +:- use_module(library(filesex), [copy_file/2]). +:- use_module(library(lists), [select/3, subtract/3, append/3, member/2]). +:- use_module(library(option), [option/2, option/3]). +:- use_module(library(pairs), [group_pairs_by_key/2]). +:- use_module(library(pprint), [print_term/2]). +:- use_module(library(prolog_code), [pi_head/2]). +:- use_module(library(prolog_source), + [ file_name_on_path/2, + path_segments_atom/2, + prolog_open_source/2, + prolog_read_source_term/4, + prolog_close_source/1 + ]). +:- use_module(library(prolog_xref), + [ xref_source/1, + xref_module/2, + xref_called/4, + xref_defined/3, + xref_built_in/1 + ]). +:- use_module(library(readutil), [read_file_to_string/3]). +:- use_module(library(solution_sequences), [distinct/2]). + +/** Compute file dependencies + +This module computes file dependencies for _modules_ as a set of +directives. +*/ + + +%! file_autoload_directives(+File, -Directives, +Options) is det. +% +% Compute the dependencies as autoload/2 directives. Options +% +% - missing(+Bool) +% If `true` (default `false`), only generate directives +% for called predicates that are not already imported. +% +% - directive(+Directive) +% Directive to use for adding dependencies. Defined +% options are: +% +% - use_autoload/2 +% (Default). This uses use_module/2 for files that +% cannot be imported using use_autoload/2. +% - use_autoload/1 +% This uses use_module/1 for files that cannot be +% imported using use_autoload/1. +% - use_module/2 +% - use_module/1 +% +% - update(Old) +% Updated an existing set of directives. The returned +% set of Directive starts with copies of Old. If a +% member of Old is autoload/2 or use_module/2, new +% dependencies are added at the end of this list. +% New dependent files are added after the modified +% copies of Old. Declared dependencies are never +% removed, even if no proof of usage is found. +% +% If no directive(+Directive) option is provided a +% default is determined from the given directives. + +file_autoload_directives(File, Directives, Options) :- + xref_source(File), + findall(Head, distinct(Head, undefined(File, Head, Options)), Missing), + convlist(missing_autoload(File), Missing, Pairs), + keysort(Pairs, Pairs1), + group_pairs_by_key(Pairs1, Grouped), + directives(Grouped, Directives, Options). + +%! undefined(+File, -Callable, +Options) +% +% Callable is called in File, but no definition can be found. If +% File is not a module file we consider other files that are not +% module files. + +undefined(File, Undef, Options) :- + xref_module(File, _), + !, + xref_called_cond(File, Undef, Cond), + \+ ( available(File, Undef, How, Options), + How \== plain_file + ), + included_if_defined(Cond, Undef), + Undef \= (_:_). +undefined(File, Undef, Options) :- + xref_called_cond(File, Undef, Cond), + \+ available(File, Undef, _, Options), + included_if_defined(Cond, Undef), + Undef \= (_:_). + +%! included_if_defined(+Condition, +Callable) is semidet. + +included_if_defined(true, _) :- !. +included_if_defined(false, _) :- !, fail. +included_if_defined(fail, _) :- !, fail. +included_if_defined(current_predicate(Name/Arity), Callable) :- + \+ functor(Callable, Name, Arity), + !. +included_if_defined(\+ Cond, Callable) :- + !, + \+ included_if_defined(Cond, Callable). +included_if_defined((A,B), Callable) :- + !, + included_if_defined(A, Callable), + included_if_defined(B, Callable). +included_if_defined((A;B), Callable) :- + !, + ( included_if_defined(A, Callable) + ; included_if_defined(B, Callable) + ). + +xref_called_cond(Source, Callable, Cond) :- + xref_called(Source, Callable, By, Cond), + By \= Callable. % recursive calls + +%! available(+File, +Callable, -HowDefined, +Options) +% +% True if Callable is available in File. + +available(File, Called, How, Options) :- + xref_defined(File, Called, How0), + ( How0 = imported(_) + -> option(missing(true), Options) + ; true + ), + !, + How = How0. +available(_, Called, How, _) :- + built_in_predicate(Called), + !, + How = builtin. +available(_, Called, How, _) :- + Called = _:_, + defined(_, Called), + !, + How = module_qualified. +available(_, M:G, How, _) :- + defined(ExportFile, G), + xref_module(ExportFile, M), + !, + How = module_overruled. +available(_, Called, How, _) :- + defined(ExportFile, Called), + \+ xref_module(ExportFile, _), + !, + How == plain_file. + +%! built_in_predicate(+Callable) +% +% True if Callable is a built-in + +built_in_predicate(Goal) :- + strip_module(Goal, _, Plain), + xref_built_in(Plain). + +%! defined(?File, ?Callable) +% +% True if Callable is defined in File and not imported. + +defined(File, Callable) :- + xref_defined(File, Callable, How), + How \= imported(_). + + + /******************************* + * GENERATE OUTPUT * + *******************************/ + +missing_autoload(_Src, Head, File-Head) :- + predicate_property(Head, autoload(File)), + !. +missing_autoload(Src, Head, From-Head) :- + xref_defined(Src, Head, imported(From)), + !. +missing_autoload(_Src, Head, _) :- + pi_head(PI, Head), + print_message(warning, + error(existence_error(procedure, PI), _)), + fail. + +%! directives(+FileAndHeads, -Directives, +Options) is det. +% +% Assemble the final set of directives. Uses the option update(Old). + +directives(FileAndHeads, Directives, Options) :- + option(update(Old), Options, []), + phrase(update_directives(Old, FileAndHeads, RestDeps), Directives, Rest), + update_style(Old, Options, Options1), + maplist(directive(Options1), RestDeps, Rest0), + sort(Rest0, Rest). + +update_directives([], Deps, Deps) --> + []. +update_directives([:-(H)|T], Deps0, Deps) --> + { update_directive(H, Deps0, Deps1, Directive) }, + !, + [ :-(Directive) ], + update_directives(T, Deps1, Deps). +update_directives([H|T], Deps0, Deps) --> + [ H ], + update_directives(T, Deps0, Deps). + +update_directive(Dir0, Deps0, Deps, Dir) :- + directive_file(Dir0, FileSpec), + absolute_file_name(FileSpec, File, + [ file_type(prolog), + file_errors(fail), + access(read) + ]), + select(DepFile-Heads, Deps0, Deps), + same_dep_file(DepFile, File), + !, + ( Dir0 =.. [Pred,File,Imports] + -> maplist(pi_head, PIs, Heads), + subtract(PIs, Imports, New), + append(Imports, New, NewImports), + Dir =.. [Pred,File,NewImports] + ; Dir = Dir0 + ). + +directive_file(use_module(File), File). +directive_file(use_module(File,_), File). +directive_file(autoload(File), File). +directive_file(autoload(File,_), File). + +same_dep_file(File, File) :- + !. +same_dep_file(Dep, _File) :- + exists_file(Dep), + !, + fail. +same_dep_file(Dep, File) :- + user:prolog_file_type(Ext, prolog), + file_name_extension(Dep, Ext, DepFile), + same_file(DepFile, File), + !. + + +%! update_style(+OldDirectives, +Options0, -Options) +% +% Determine the directive to use for new dependencies. This +% establishes a default based on existing dependencies. + +update_style(_Old, Options, Options) :- + option(directive(_), Options), + !. +update_style(Old, Options, [directive(autoload/2)|Options]) :- + memberchk((:- autoload(_,_)), Old), + !. +update_style(Old, Options, [directive(autoload/1)|Options]) :- + memberchk((:- autoload(_)), Old), + !. +update_style(Old, Options, [directive(use_module/2)|Options]) :- + memberchk((:- use_module(_,_)), Old), + !. +update_style(Old, Options, [directive(use_module/1)|Options]) :- + memberchk((:- use_module(_)), Old), + !. +update_style(_, Options, Options). + + +%! directive(+Options, +FileAndHeads, -Directive) +% +% Create a directive to import Heads from File. + +directive(Options, File-Heads, Directive) :- + file_name_extension(File, pl, LibFile), + file_name_on_path(LibFile, Lib0), + segments(Lib0, Lib), + maplist(pi_head, PIs, Heads), + make_directive(Lib, PIs, Directive, Options). + +segments(Term0, Term) :- + Term0 =.. [Alias,Atom], + path_segments_atom(Segments, Atom), + format(atom(Atom), '~q', [Segments]), + !, + Term =.. [Alias,Segments]. +segments(FilePL, File) :- + atom(FilePL), + file_name_extension(File, pl, FilePL), + !. +segments(Term, Term). + +:- multifile + prolog:no_autoload_module/1. + +make_directive(Lib, Import, (:- use_module(Lib, Import)), Options) :- + option(directive(use_module/2), Options, use_autoload/2), + !. +make_directive(Lib, _Import, (:- use_module(Lib)), Options) :- + option(directive(use_module/1), Options, use_autoload/2), + !. +make_directive(Lib, _Import, (:- use_module(Lib)), Options) :- + option(directive(use_autoload/1), Options, use_autoload/2), + prolog:no_autoload_module(Lib), + !. +make_directive(Lib, Import, (:- use_module(Lib, Import)), _) :- + prolog:no_autoload_module(Lib), + !. +make_directive(Lib, _Import, (:- autoload(Lib)), Options) :- + option(directive(use_autoload/1), Options, use_autoload/2), + !. +make_directive(Lib, Import, (:- autoload(Lib, Import)), _). + + + /******************************* + * REPLACE * + *******************************/ + +%! file_auto_import(+File, +Options) +% +% Update the autoload/2 directives for File. This predicate __modifies +% the file in place__. Defined options are: +% +% - backup(+Extension) +% Create a backup of File using Extension. + +file_auto_import(File, Options) :- + absolute_file_name(File, Path, + [ file_type(prolog), + access(read) + ]), + file_autoload_directives(Path, Directives, Options), + ( option(backup(Ext), Options) + -> file_name_extension(Path, Ext, Old), + copy_file(Path, Old) + ; true + ), + Edit = _{import:Directives, done:_}, + ( has_import(Path) + -> edit_file(Old, Path, Edit.put(replace,true)) + ; edit_file(Old, Path, Edit.put(new,true)) + ). + +has_import(InFile) :- + setup_call_cleanup( + prolog_open_source(InFile, In), + ( repeat, + prolog_read_source_term(In, Term, _Expanded, []), + ( Term == end_of_file + -> ! + ; true + ) + ), + prolog_close_source(In)), + nonvar(Term), + import_directive(Term), + !. + +import_directive((:- use_module(_))). +import_directive((:- use_module(_, _))). + +%! rewrite_term(+In, -Keep, -OutList, +Options) is semidet. + +rewrite_term(Never,_,_,_) :- + never_rewrite(Never), + !, + fail. +rewrite_term(Import,false,[],Options) :- + Options.done == true, + !, + import_directive(Import). +rewrite_term(In,false,Directives,Options) :- + import_directive(In), + !, + append(Options.import, [nl], Directives), + Options.done = true. +rewrite_term(In,true,Directives,Options) :- + In = (:- module(_,_)), + Options.get(new) == true, + !, + append(Options.import, [nl], Directives), + Options.done = true. + +never_rewrite((:- use_module(_, []))). + +edit_file(InFile, OutFile, Options) :- + read_file_to_string(InFile, String, []), + setup_call_cleanup( + prolog_open_source(InFile, In), + setup_call_cleanup( + open(OutFile, write, Out), + rewrite(In, Out, String, Options), + close(Out)), + prolog_close_source(In)). + +rewrite(In, Out, String, Options) :- + prolog_read_source_term( + In, Term, _Expanded, + [ term_position(StartPos), + subterm_positions(TermPos), + comments(Comments) + ]), + stream_position_data(char_count, StartPos, StartChar), + copy_comments(Comments, StartChar, String, Out), + ( Term == end_of_file + -> true + ; ( nonvar(Term), + rewrite_term(Term, Keep, List, Options) + -> ( Keep == true + -> copy_term_string(TermPos, String, Out) + ; true + ), + forall(member(T, List), + output_term(Out, T)), + ( append(_, [nl], List) + -> skip_blanks(In) + ; true + ) + ; copy_term_string(TermPos, String, Out) + ), + rewrite(In, Out, String, Options) + ). + +output_term(Out, nl) :- + !, + nl(Out). +output_term(Out, Term) :- + print_term(Term, [output(Out)]), + format(Out, '.~n', []). + +copy_comments([Pos-H|T], StartChar, String, Out) :- + stream_position_data(char_count, Pos, Start), + Start < StartChar, + !, + string_length(H, Len), + sub_string(String, Start, Len, _, Comment), + End is Start+Len+1, + layout_after(End, String, Layout), + format(Out, '~s~s', [Comment, Layout]), + copy_comments(T, StartChar, String, Out). +copy_comments(_, _, _, _). + +copy_term_string(TermPos, String, Out) :- + arg(1, TermPos, Start), + arg(2, TermPos, End), + Len is End - Start, + sub_string(String, Start, Len, _, TermString), + End1 is End + 1, + full_stop_after(End1, String, Layout), + format(Out, '~s~s', [TermString, Layout]). + +layout_after(Index, String, [H|T]) :- + string_code(Index, String, H), + code_type(H, space), + !, + Index2 is Index+1, + layout_after(Index2, String, T). +layout_after(_, _, []). + +full_stop_after(Index, String, [H|T]) :- + string_code(Index, String, H), + Index2 is Index+1, + ( code_type(H, space) + -> !, full_stop_after(Index2, String, T) + ; H == 0'. + -> !, layout_after(Index2, String, T) + ). +full_stop_after(_, _, []). + +skip_blanks(In) :- + peek_code(In, C), + code_type(C, space), + !, + get_code(In, _), + skip_blanks(In). +skip_blanks(_). diff --git a/library/sandbox.pl b/library/sandbox.pl index 6cea772..3379f47 100644 --- a/library/sandbox.pl +++ b/library/sandbox.pl @@ -563,6 +563,7 @@ safe_primitive(copy_term(_,_)). safe_primitive(system:duplicate_term(_,_)). safe_primitive(system:copy_term_nat(_,_)). +safe_primitive(system:size_abstract_term(_,_,_)). safe_primitive(numbervars(_,_,_)). safe_primitive(system:numbervars(_,_,_,_)). safe_primitive(subsumes_term(_,_)). @@ -1235,6 +1236,10 @@ % tabling safe_prolog_flag(max_answers_for_subgoal,_). safe_prolog_flag(max_answers_for_subgoal_action,_). +safe_prolog_flag(max_table_answer_size,_). +safe_prolog_flag(max_table_answer_size_action,_). +safe_prolog_flag(max_table_subgoal_size,_). +safe_prolog_flag(max_table_subgoal_size_action,_). %! prolog:sandbox_allowed_expansion(:G) is det. diff --git a/library/solution_sequences.pl b/library/solution_sequences.pl index 6b8700b..14bdb75 100644 --- a/library/solution_sequences.pl +++ b/library/solution_sequences.pl @@ -271,15 +271,17 @@ %! order_by(+Spec, :Goal) % -% Order solutions according to Spec. Spec is a list of terms, -% where each element is one of. The ordering of solutions of Goal -% that only differ in variables that are _not_ shared with Spec is -% not changed. +% Order solutions according to Spec. Spec is a list of terms, where +% each element is one of. The ordering of solutions of Goal that only +% differ in variables that are _not_ shared with Spec is not changed. % % - asc(Term) % Order solution according to ascending Term % - desc(Term) % Order solution according to descending Term +% +% This predicate is based on findall/3 and (thus) variables in answers +% are _copied_. order_by(Spec, Goal) :- must_be(list, Spec), diff --git a/man/foreign.doc b/man/foreign.doc index d27d61b..8792e87 100644 --- a/man/foreign.doc +++ b/man/foreign.doc @@ -779,6 +779,10 @@ Returns non-zero if \arg{term} is a compound term using the list constructor. See also PL_is_list() and PL_skip_list(). + \cfunction{int}{PL_is_dict}{term_t} +Returns non-zero if \arg{term} is a dict. See also PL_put_dict() +and PL_get_dict_key(). + \cfunction{int}{PL_is_atomic}{term_t} Returns non-zero if \arg{term} is atomic (not a variable or compound). @@ -965,12 +969,19 @@ If \arg{t} is an atom, the system will look up or create the corresponding module and assign an opaque pointer to it over {\em module}. + \cfunction{int}{PL_get_arg}{size_t index, term_t +t, term_t -a} If \arg{t} is compound and index is between 1 and arity (inclusive), assign \arg{a} with a term reference to the argument. + \cfunction{int}{_PL_get_arg}{size_t index, term_t +t, term_t -a} Same as PL_get_arg(), but no checking is performed, neither whether \arg{t} is actually a term nor whether \arg{index} is a valid argument index. + +\cfunction{int}{PL_get_dict_key}{atom_t key, term_t +dict, term_t -value} +If \arg{dict} is a dict, get the associated value in \arg{value}. Fails +silently if \arg{key} does not appear in \arg{dict} and with an +exception if \arg{dict} is not a dict. \end{description} @@ -1344,6 +1355,18 @@ Note that \arg{l} can be redefined within a {\tt PL_cons_list} call as shown here because operationally its old value is consumed before its new value is set. + +\cfunction{int}{PL_put_dict}{term_t -h, atom_t tag, size_t len, + const atom_t *keys, term_t values} +Create a dict from a \arg{tag} and vector of atom-value pairs and put +the result in \arg{h}. The dict's key is set by \arg{tag}, which may +be \const{0} to leave the tag unbound. The \arg{keys} vector is a vector +of atoms of at least \arg{len} long. The \arg{values} is a term vector +allocated using PL_new_term_refs() of at least \arg{len} long. This +function returns \const{TRUE} on success, \const{FALSE} on a resource +error (leaving a resource error exception in the environment), +\const{-1} if some key or the \arg{tag} is invalid and \const{-2} if +there are duplicate keys. \end{description} @@ -2905,6 +2928,33 @@ mp_set_memory_functions() in the GMP documentation. The action returns \const{FALSE} if there is no GMP support or GMP is already initialised. \end{description} + +\cfunction{unsigned int}{PL_version}{int key} +Query version information. This function may be called before +PL_initialise(). If the key is unknown the function returns 0. +See \secref{abi-versions} for a more in-depth discussion on +binary compatibility. Defined keys are: + + \begin{description} + \termitem{PL_VERSION_SYSTEM}{} +SWI-Prolog version as $10,000 \times major + 100 \times minor + patch$. + \termitem{PL_VERSION_FLI}{} +Incremented if the foreign interface defined in this chapter changes in +a way that breaks backward compatibility. + \termitem{PL_VERSION_REC}{} +Incremented if the binary representation of terms as used by +PL_record_external() and fast_write/2 changes. + \termitem{PL_VERSION_QLF}{} +Incremented if the QLF file format changes. + \termitem{PL_VERSION_QLF_LOAD}{} +Represents the oldest loadable QLF file format version. + \termitem{PL_VERSION_VM}{} +A hash that represents the VM instructions and their arguments. + \termitem{PL_VERSION_BUILT_IN}{} +A hash that represents the names, arities and properties of all +built-in predicates defined in C. If this function is called +before PL_initialise() it returns 0. + \end{description} \end{description} \subsection{Querying Prolog} diff --git a/man/main.doc b/man/main.doc index 4c3bc22..ddb3368 100644 --- a/man/main.doc +++ b/man/main.doc @@ -59,9 +59,9 @@ \newcommand{\vmajor}{8} \newcommand{\vminor}{1} -\newcommand{\vpatch}{29} +\newcommand{\vpatch}{30} \newcommand{\vtag}{} -\newcommand{\vmonth}{April} +\newcommand{\vmonth}{May} \newcommand{\vyear}{2020} #ifdef BOOK diff --git a/man/overview.doc b/man/overview.doc index 5e7022b..a958cab 100644 --- a/man/overview.doc +++ b/man/overview.doc @@ -351,6 +351,10 @@ \cmdlineoptionitem{--version}{} When given as the only option, it summarises the version and the architecture identifier. Also available as \cmdlineoption{-v}. + + \cmdlineoptionitem{--abi_version}{} +Print a key (string) that represents the binary compatibility on +a number of aspects. See \secref{abi-versions}. \end{description} @@ -1182,6 +1186,11 @@ \begin{description} + \prologflagitem{abi_version}{dict}{r} +The flag value is a dict with keys that describe the version of +the various Application Binary Interface (ABI) components. See +\secref{abi-versions} for details. + \prologflagitem{access_level}{atom}{rw} This flag defines a normal `user' view (\const{user}, default) or a `system' view. In system view all system code is fully accessible as if @@ -3502,3 +3511,66 @@ start with a dollar (\chr{\$}) sign. \input{bit64.tex} + +\section{Binary compatibility} \label{sec:abi-versions} + +\index{compatibility,binary}% +\index{ABI,compatibility}% +SWI-Prolog first of all attempts to maintain \jargon{source code} +compatibility between versions. Data and programs can often be +represented in binary form. This touches a number of interfaces +with varying degrees of compatibility. The relevant version numbers +and signatures are made available by PL_version(), the +\cmdlineoption{abi_version} and the Prolog flag +\prologflag{abi_version}. + +\begin{description} + \definition{Foreign extensions} +Dynamically loadable foreign extensions have the usual dependencies on +the architecture, ABI model of the (C) compiler, dynamic link library +format, etc. They also depend on the backward compatibility of the PL_* +API functions provided lib \file{libswipl}. + +A compatible API allows distribution of foreign extensions in binary +form, notably for platforms on which compilation is complicated (e.g., +Windows). This compatibility is therefore high on the priority list, but +must infrequently be compromised. + +PL_version(): \const{PL_VERSION_FLI}, \prologflag{abi_version} key: +\const{foreign_interface} + + \definition{Binary terms} +Terms may be represented in binary format using PL_record_external() and +fast_write/2. As these formats are used for storing binary terms in +databases or communicate terms between Prolog processes in binary form, +great care is taken to maintain compatibility. + +PL_version(): \const{PL_VERSION_REC}, \prologflag{abi_version} key: +\const{record} + + \definition{QLF files} +QLF files (see qcompile/1) are binary representation of Prolog file or +module. They represent clauses as sequences of \jargon{virtual machine} +(VM) instructions. Their compatibility relies on the QLF file format and +the ABI of the VM. Some care is taken to maintain compatibility. + +PL_version(): \const{PL_VERSION_QLF}, \const{PL_VERSION_QLF_LOAD} and +\const{PL_VERSION_VM}, \prologflag{abi_version} key: \const{qlf}, +\const{qlf_min_load}, \const{vmi} + + \definition{Saved states} +Saved states (see \cmdlineoption{-c} and qsave_program/2) is a zip file +that contains the entire Prolog database using the same representation +as QLF files. A saved state may contain additional resources, such as +foreign extensions, data files, etc. In addition to the dependency +concerns of QLF files, built-in and core library predicates may call +\emph{internal} foreign predicates. The interface between the public +built-ins and internal foreign predicates changes frequently. Patch +level releases in the \emph{stable branch} will as much as possible +maintain compatibility. + +The relevant ABI version keys are the same as for QLF files with one +addition: PL_version(): \const{PL_VERSION_BUILT_IN}, \prologflag{abi_version} +key: \const{built_in} +\end{description} + diff --git a/man/tabling.doc b/man/tabling.doc index bcfe676..de0eca1 100644 --- a/man/tabling.doc +++ b/man/tabling.doc @@ -840,7 +840,7 @@ \subsection{Restraint subgoal size} \label{sec:tabling-restraint-subgoal} -Using the \term{abstract_subgoal}{Size} attribute, a tabled subgoal that +Using the \term{subgoal_abstract}{Size} attribute, a tabled subgoal that that is too large is \jargon{abstracted} by replacing compound subterms of the goal with variables. In a nutshell, a goal \term{p}{s(s(s(s(s(0)))))} is converted into the semantically equivalent diff --git a/packages/clib/uri.c b/packages/clib/uri.c index b53d722..0edf58d 100644 --- a/packages/clib/uri.c +++ b/packages/clib/uri.c @@ -917,6 +917,10 @@ /** uri_query_components(+QueryString, -ValueList) is det. + +(*) We must malloc() because the individual query components use the +text ring buffer, so with more query components then the ring size the +original text gets corrupted. */ static foreign_t @@ -924,8 +928,10 @@ { pl_wchar_t *s; size_t len; - if ( PL_get_wchars(string, &len, &s, CVT_ATOM|CVT_STRING|CVT_LIST) ) - { return unify_query_string_components(list, len, s); + if ( PL_get_wchars(string, &len, &s, CVT_ATOM|CVT_STRING|CVT_LIST|BUF_MALLOC) ) + { int rc = unify_query_string_components(list, len, s); + PL_free(s); /* See (*) */ + return rc; } else if ( PL_is_list(list) ) { term_t tail = PL_copy_term_ref(list); term_t head = PL_new_term_ref(); diff --git a/packages/http/CMakeLists.txt b/packages/http/CMakeLists.txt index 9b5694f..88e1ba8 100644 --- a/packages/http/CMakeLists.txt +++ b/packages/http/CMakeLists.txt @@ -11,6 +11,8 @@ ${JQUERYFILE} PATHS /usr/share/javascript/jquery NO_DEFAULT_PATH) + # Deal with snap creation + string(REPLACE "-sdk/current/" "/current/" JQUERYDIR "${JQUERYDIR}") endif() if(JQUERYDIR AND EXISTS ${JQUERYDIR}/${JQUERYFILE}) diff --git a/packages/jpl/CMakeLists.txt b/packages/jpl/CMakeLists.txt index 53e28bc..0292d15 100644 --- a/packages/jpl/CMakeLists.txt +++ b/packages/jpl/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.5) project(swipl-jpl) +set(JPL_VERSION 7.6.0) #message(${CMAKE_CURRENT_SOURCE_DIR}) # /packages/jpl #message(${PROJECT_SOURCE_DIR}) # /packages/jpl diff --git a/packages/jpl/docs/DeploymentLinux.md b/packages/jpl/docs/DeploymentLinux.md index 768b1e8..c478e54 100644 --- a/packages/jpl/docs/DeploymentLinux.md +++ b/packages/jpl/docs/DeploymentLinux.md @@ -1,28 +1,77 @@ # Deploying for users - on Linux -Recall that to use JPL under linux one must have the following in place: +To use JPL under Linux one must have the following in place (here showing the locations under Linux Mint distribution under packages `swi-prolog-nox` and `swi-prolog-java`): -* C Native JPL Library `libjpl.so`, generally found at `/usr/lib/swi-prolog/lib/amd64/libjpl.so` -* Java API Jar file `jpl.jar` in the Java `CLASSPATH`, generally accessible from `/usr/share/java/jpl.jar`. -* Prolog API as an SWI source module `jpl.pl` at `$SWI_HOME_DIR/library`, generally accessible from `/usr/lib/swi-prolog/library/jpl.pl` - -JPL is **generally distributed with official Linux**. For example, in Ubuntu-based systems, JPL is provided via package `swi-prolog-java`. That package includes the C library `libjpl.so`, the Java API `jpl.jar`, the Prolog module `jpl.pl` as well as all documentation associated. +* A SWIPL install containing the core SWI system, which includes: + * A SWIPL home directory (`SWI_HOME_DIR`), located at `/usr/lib/swi-prolog/` and containing the whole SWIPL core system, including a booting `.prc` file (`$SWI_HOME_DIR/boot64.prc`). + * The "home" of SWIPL can be queried via [`current_prolog_flag(home, H)`](https://www.swi-prolog.org/pldoc/man?predicate=current_prolog_flag/2) + * The C Native core SWIPL library `/usr/lib/libswipl.so`. + * The set of C Native SWIPL libraries for each package (files `.so`), located in `/usr/lib/swi-prolog/lib/amd64/` +* The JPL package, which includes: + * The C Native JPL Library `libjpl.so` at `$SWI_HOME_DIR/lib/amd64/libjpl.so`. + * The Java API JAR file `jpl.jar` in `$SWI_HOME_DIR/lib/jpl.jar`. + * The Prolog API as an SWIPL source module `jpl.pl` at `$SWI_HOME_DIR/library`. + +## Which SWIPL version to (not) use? + +JPL is **generally distributed with official Linux**. For example, in Ubuntu-based systems, JPL is provided via package `swi-prolog-java`. That package includes the C library `libjpl.so`, the Java API `jpl.jar`, the Prolog module `jpl.pl` as well as all associated documentation. However, the **official packages are often out-of-date**. For Debian-based systems (Debian, Ubuntu, Mint, ...) you can get the latest stable and development versions via [this PPAs](http://www.swi-prolog.org/build/PPA.txt) provided directly by SWI-Prolog. -To use JPL, use either SWI stable version 7.6.4 (available in standard Linux repos) or compile and install 8.1.x from SWI-devel repo. -* **Note:** The official stable SWI 8.0.x versions (as of Jan 2020) have issues with the `libswipl.so/dll/dylib` library and makes JPL crash; see [issue](https://github.com/ssardina-research/packages-jpl/issues/21). It has been fixed in the git repo but will only show up with versions 8.1.x. +Use either SWIPL stable version 7.6.4 (available in standard Linux repos) or compile & install 8.1.x from [SWI-devel repo](https://github.com/SWI-Prolog/swipl-devel) using CMAKE. + * **Note:** The official stable SWI 8.0.x versions (as of Jan 2020) have issues with the `libswipl.so/dll/dylib` library and makes JPL crash; see [issue](https://github.com/ssardina-research/packages-jpl/issues/21). It has been fixed in the git repo but will only show up with versions 8.1.x. + +## Configuring environment variables -Finally, to be **able to use JPL** you may need to make sure that: +When embeeding SWIPL into a Java, one may needs to "tell" the Java application the right information, via environment variables, so that SWIPL is initialized properly and can find all resources. -* Extend environment library `LD_PRELOAD` for system to pre-load `libswipl.so`: - * `export LD_PRELOAD=libswipl.so:$LD_PRELOAD` - * Check [this post](https://answers.ros.org/question/132411/unable-to-load-existing-owl-in-semantic-map-editor/) and [this one](https://blog.cryptomilk.org/2014/07/21/what-is-preloading/) about library preloading. - * Also, check [this](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=690734) and [this](https://github.com/yuce/pyswip/issues/10) posts. -* Extend environment variable `LD_LIBRARY_PATH` to point to the directory where `libjpl.so` is located: - * `export LD_LIBRARY_PATH=/usr/lib/swi-prolog/lib/amd64/` -* Make sure `jpl.jar` is in your Java CLASSPATH. This is generally automatically done if environment variable `SWI_HOME_DIR` is correctly set to `/usr/lib/swi-prolog/`. +In general, if the Java application will use the default executable of the system (i.e., the one that runs when executing `swipl`), then you only need to set-up `CLASSPATH` to include `jpl.jar` and possibly `LD_PRELOAD` to point to your active SWIPL `libswipl.so` library to avoid run-time errors. The executable has a pointer to the right information to initialize; see [here](https://www.swi-prolog.org/FAQ/FindResources.html). -To install JPL from scratch (e.g., to compiling it in order to develop it further) please refer to the [Developing JPL](Developing-JPL) guide. +However, if your Java application will use an SWIPL & JPL version that is _not_ the exectuable default. + +### Using stable distribution versions of SWIPL + +If the **Linux distribution install** of SWIPL & JPL is not the executable default but the one to be used, set-up the following environment variables: + + SWI_HOME_DIR=/usr/lib/swi-prolog/ # if default binary not pointing to this version + LD_LIBRARY_PATH=/usr/lib/swi-prolog/lib/amd64/ # to find all .so, including libjpl.so + CLASSPATH=/usr/lib/swi-prolog/lib/jpl.jar + LD_PRELOAD=/usr/lib/libswipl.so # see below for explanation + +or in one line (for IDE Run configurations, for example): + + CLASSPATH=/usr/lib/swi-prolog/lib/jpl.jar;LD_LIBRARY_PATH=/usr/lib/swi-prolog/lib/amd64/;LD_PRELOAD=/usr/lib/libswipl.so;SWI_HOME_DIR=/usr/lib/swi-prolog/ + +Notice that, in this case, library `libswipl.so` will be found automatically, as it is located in the standard system-wide library dir `/usr/lib`. + +### Using locally compiled and installed version of SWIPL + +Alternatively, if you have **compiled and installed** an SWIPL system, say, under directory `/usr/local/swipl-git/`, then the SWIPL home will be `/usr/local/swipl-git/lib/swipl/`, the executable binary will be `/usr/local/swipl-git/lib/swipl/bin/x86_64-linux/swipl` and the environment variables should be set-up as follows: + + SWI_HOME_DIR=/usr/local/swipl-git/lib/swipl/ # if binary exec not pointing to this SWIPL + LD_LIBRARY_PATH=/usr/local/swipl-git/lib/swipl/lib/x86_64-linux/ # to find all .so, including libjpl.so + CLASSPATH=/usr/local/swipl-git/lib/swipl/lib/jpl.jar + LD_PRELOAD=/usr/local/swipl-git/lib/swipl/lib/x86_64-linux/libswipl.so # see below for explanation + +or in one line (for IDE Run configurations, for example): + + CLASSPATH=/usr/local/swipl-git/lib/swipl/lib/jpl.jar;LD_LIBRARY_PATH=/usr/local/swipl-git/lib/swipl/lib/x86_64-linux/;LD_PRELOAD=/usr/local/swipl-git/lib/swipl/lib/x86_64-linux/libswipl.so;SWI_HOME_DIR=/usr/local/swipl-git/lib/swipl/ + +## Using the development tree of SWI+JPL that is yet not installed? + +If you want to run your application against a development tree of SWIPL & JPL that is not yet installed, then refer to [Developing JPL](TutorialDeveloping.md) guide. + ## Troubleshooting + +### Pre-loading `libswipl.so` + +The [libray preloading](https://blog.cryptomilk.org/2014/07/21/what-is-preloading/) of `libswipl.so` is often necessary to avoid the following run-time error (check [this](https://answers.ros.org/question/132411/unable-to-load-existing-owl-in-semantic-map-editor/), [this](https://github.com/yuce/pyswip/issues/10) and [this](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=690734) posts): + + ERROR: /usr/lib/swi-prolog/library/process.pl:53: + /usr/lib/swi-prolog/library/process.pl:53: Initialization goal raised exception: + '$open_shared_object'/3: /usr/lib/swi-prolog/lib/amd64/process.so: undefined symbol: Sfilefunctions + +### [FATAL ERROR: Could not find system resources]. + +It is not finding the correct `libswipl.so`. diff --git a/packages/jpl/docs/JavaApiOverview.md b/packages/jpl/docs/JavaApiOverview.md index 17d51bb..b9c00e3 100644 --- a/packages/jpl/docs/JavaApiOverview.md +++ b/packages/jpl/docs/JavaApiOverview.md @@ -98,7 +98,7 @@ `Term` instances are never changed by any activity within the Prolog engine: indeed; it doesn't know of their existence. -The `Term` class is abstract, so it cannot be directly instantiated; to create a Term, create an instance of one of its subclasses. +The `Term` class is abstract, so it cannot be directly instantiated; to create a Term, create an instance of one of its subclasses, which are the ones accounting for the various [data types in SWI-Prolog](https://www.swi-prolog.org/datatypes.html). ### Atoms @@ -235,30 +235,59 @@ public Term arg0(int i); ``` -#### Lists as compound terms - -As usual in Prolog, lists are just compound terms with function `[|]` and two arguments: the _head_ element and the _tail_ list. For example: - -```prolog -?- A = [1,2,3,4], A =.. [X|Y]. -A = [1, 2, 3, 4], -X = '[|]', -Y = [1, [2, 3, 4]]. -```` - -We can build list compound terms in two ways. First, by converting an Array of terms (`Term[]`) into a Compound (list) term using utility `Util.termArrayToList`: - -```java -Term list = Util.termArrayToList(new Term[] - { new Integer(1), new Variable("B"), new Atom("c") }); +### Lists + +In SWI-Prolog a list is either: + +* An empty list `[]`. In JPL, the empty list is the constant `JPL.LIST_NIL` and is a final object of class `Atom`. (Observe on the SWI side, from SWI-Prolog 7+, the empty list is not an atom but a reserved word.) +* A `Compound` term with functor `[|]` and two arguments where the second one is itself a list. On the Prolog side: + + ?- A = [1,2,3,4], A =.. [X|Y]. + A = [1, 2, 3, 4], + X = '[|]', + Y = [1, [2, 3, 4]]. + +While one can build non-empty lists by creating `Compound` terms, it can become really cumbersome, as the second argument always has to be another lits. + +So, class `Term` provides several _static_ methods to conveniently build non-empty lists, namely: + +```java +public static Term textToTerm(String text) +public static Term termArrayToList(Term[] terms) +public static Term stringArrayToList(String[] a) +public static Term intArrayToList(int[] a) +public static Term intArrayArrayToList(int[][] a) +``` + +Method `textToTerm(String text)` can actually build _any_ term form its String representation, including list terms: + +```java +Term list = Util.textToTerm("[1, B, [p(g), g(1)], c]"); +``` + +A second tool is `termArrayToList(Term[])`, which builds a list from an Array of Terms (`Term[]`) (the corresponding functors `[|]` are added automatically). For example: + +```java +Term list = Term.termArrayToList(new Term[] // list [1, B, hello] + { new Integer(1), new Variable("B"), new Atom("hello") }); ``` -The second way is by converting a String representing a list into a term via `Util.textToTerm`: - -```java -Term list = Util.textToTerm("[1, B, c]"); -``` - +Finally, one can build specific data-type lists using: +* `stringArrayToList(String[] a)`: builds a list of atoms from an Array of Strings; +* `intArrayToList(int[] a)`: builds a list of integers; +* `intArrayArrayToList(int[][] a)`: builds a list of lists of integers. + +On the other direction, the following methods in `Term` class transform a list Term into another Java type: + +```java +public static String toString(Term t) +public static Term[] listToTermArray(Term t) +public static String[] atomListToStringArray(Term t) +``` + +When it comes to non-empty, compound, list terms, the behavior of `toString()` will depend on boolean `JPL.LIST_TOSTRING_TEXTUAL`: +* If True, lists will be represented as String in the usual Prolog textual representation `[e1, e2, e3, ...., en]`. Note a space wil be added after each comma always. +* If False, lists will be represented in infix notation with the list pair functor `[|]`, e.g., `[|](1, [|](2, [|](3, '[]')))` for list `[1, 2, 3]`. @@ -348,6 +377,14 @@ ``` If the query has no solutions, this method returns `null`; otherwise, a non-null return indicates success. If the query is ground (i.e. contains no variables), the returned map will be empty (i.e. will contain no bindings). + +If the query is non-ground (i.e., it includes variables), then bindings are retrieved by name, e.g.: + +``` +Map m = org.jpl7.Query.oneSolution("statistics(heap, X)"); +long heapsize = m.get("X"); +``` + ### Obtaining all solutions @@ -457,15 +494,6 @@ ## Gotchas -### Variables are named - -Instances of `org.jpl7.Variable` have names, and bindings are retrieved by name, e.g. - -``` -Map m = org.jpl7.Query.oneSolution("statistics(heap, X)"); -long heapsize = m.get("X"); -``` - ### Argument numbering The `Term[]` args of a `Compound` are indexed (like all Java arrays) from zero, whereas in Prolog the args of a structure are conventionally numbered from one. diff --git a/packages/jpl/docs/ReleaseNotes.md b/packages/jpl/docs/ReleaseNotes.md index 620ca28..4c60e4c 100644 --- a/packages/jpl/docs/ReleaseNotes.md +++ b/packages/jpl/docs/ReleaseNotes.md @@ -4,7 +4,11 @@ * Version [JPL 7.x](https://jpl7.org/) uses SWI Prolog V7 and has modernise JPL's APIs significantly. - * Current version []7.4.0](https://jpl7.org/ReleaseNotes740.jsp) works with SWI versions V7.4.x to V7.7.x. It has a new implemention of JRefs as blobs to address Java objects from Prolog. + * Version [7.6.0](https://jpl7.org/ReleaseNotes760.jsp) works with SWI versions V8.1.x+. It adds Rationals as a data type and has several term methods refactored into class Term. + + * Version [7.5.0](https://jpl7.org/ReleaseNotes750.jsp) works with SWI versions V7.4.x to V7.7.x. It has a new implemention of iterators ans solution methods in Queries and better handling of quoted terms. + + * Version [7.4.0](https://jpl7.org/ReleaseNotes740.jsp) works with SWI versions V7.4.x to V7.7.x. It has a new implemention of JRefs as blobs to address Java objects from Prolog. * Versions [JPL 3.x,y](http://www.swi-prolog.org/packages/jpl/) implemented many changes and worked with SWI V5.2.0 or later (it used multi-threading FLI calls not available in older versions) and Java 2 runtime. It was tested with Microsoft Visual C/C++ 5 under Windows NT 4.0 (SP6a). diff --git a/packages/jpl/docs/ReleaseNotes750.md b/packages/jpl/docs/ReleaseNotes750.md index a379fa9..8d0d8bb 100644 --- a/packages/jpl/docs/ReleaseNotes750.md +++ b/packages/jpl/docs/ReleaseNotes750.md @@ -15,8 +15,8 @@ ## Versions up to 7.4 - Complete overhawl from version 3.x -- Check release notes for [7.4.0](https://jpl7.org/ReleaseNotes740.jsp). -- Check release notes for [7.0.1](https://jpl7.org/ReleaseNotes701.jsp). +- Check release notes for [7.4.0](ReleaseNotes740). +- Check release notes for [7.0.1](ReleaseNotes701). diff --git a/packages/jpl/docs/ReleaseNotes760.md b/packages/jpl/docs/ReleaseNotes760.md new file mode 100644 index 0000000..19f5fe1 --- /dev/null +++ b/packages/jpl/docs/ReleaseNotes760.md @@ -0,0 +1,25 @@ +# Release Notes - 7.6.0 + +- New `org.jpl7.Rational` type to handle [SWI rationals](https://www.swi-prolog.org/pldoc/man?section=rational). +- Refactored several methods dealing with JPL terms, from `org.jpl7.Util` to `org.jpl7.Term`: + - `Term textToTerm(String text)` + - `String[] atomListToStringArray(Term t)` + - `static Term intArrayArrayToList(int[][] a)` + - `Term intArrayToList(int[] a)` + - `boolean isList(Term term)` + - `int listToLength(Term term)` + - `Term[] listToTermArray(Term t)` + - `Term stringArrayToList(String[] a)` + - `Term termArrayToList(Term[] terms)` +- Added a textual mode for `Term.toString()` to convert non-empty lists in Prolog textual style `[e2, e2, ..., en]` instead of the pre-fix functor-based style `'[|]'(e1, '[|]'(e2, '[|]'(...,'[|]'(en,'[]')..)`. + - This textual mode is used when `JPL.LIST_TOSTRING_TEXTUAL` is True (default is True); otherwise default pre-fix style is used. +- Added specific section for lists in documentation. + +## Internal + +- More direct and simpler `Term.textToTerm(String text)` without using +`getSolutionWithVarNames` and by renaming anonymous Variable terms to give them the textual name. +- Migrated unit testing from JUnit3 to JUnit4. + - Refactored the unit testing test suite; all test files subclass of `org.jpl7.java.test.junit.JPLTest`. +- Modified init arguments and CMAKE configuration for SWI embeded engine unit testing to fix issue with engine not loading libraries. No more use of `libswipl.dll` as first argument; all packages available in unit tetsing now. +- Added some static versions of instance methods in class `Term`. \ No newline at end of file diff --git a/packages/jpl/docs/TutorialDeveloping.md b/packages/jpl/docs/TutorialDeveloping.md index ac3e218..bec0b93 100644 --- a/packages/jpl/docs/TutorialDeveloping.md +++ b/packages/jpl/docs/TutorialDeveloping.md @@ -178,3 +178,55 @@ [ssardina@Thinkpad-X1 build]$ ``` + +## Using non-installed development tree of SWIPL & JPL in Java application + +Suppose you are developing JPL as above, in this case at `/home/ssardina/git/soft/prolog/swipl-devel.git` + +However, you have a Java application X besides JPL itself and want to test that application under the current SWIPL+JPL development. There are several things that need to be set-up for your application to correctly access the SWIPL+JPL under development. + +First, we need to **setup various environment variables** so that the correct SWIPL+JPL is initialized and used: + + SWI_HOME_DIR=/home/ssardina/git/soft/prolog/swipl-devel.git/build/home + SWI_EXEC_FILE=/home/ssardina/git/soft/prolog/swipl-devel.git/build/src/swipl + SWIPL_BOOT_FILE=/home/ssardina/git/soft/prolog/swipl-devel.git/build/home/boot.prc + LD_LIBRARY_PATH=/home/ssardina/git/soft/prolog/swipl-devel.git/build/packages/jpl + LD_PRELOAD=/home/ssardina/git/soft/prolog/swipl-devel.git/build/src/libswipl.so + CLASSPATH=/home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/out/artifacts/jpl_jar/ + +The `CLASSPATH` needs to point to the `jpl.jar` file that needs to be us used in case `jpl.pl` requires it (to access JAVA from Prolog). + +In one line (to copy into the usual Run-configuration of IDE): + + LD_LIBRARY_PATH=/home/ssardina/git/soft/prolog/swipl-devel.git/build/packages/jpl;SWI_HOME_DIR=/home/ssardina/git/soft/prolog/swipl-devel.git/build/home;SWIPL_BOOT_FILE=/home/ssardina/git/soft/prolog/swipl-devel.git/build/home/boot.prc;CLASSPATH=/home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/out/artifacts/jpl_jar/;LD_PRELOAD=/home/ssardina/git/soft/prolog/swipl-devel.git/build/src/libswipl.so;SWI_EXEC_FILE=/home/ssardina/git/soft/prolog/swipl-devel.git/build/src/swipl + + +Second, we need to tell our applicatoin X development to use the development JPL Java, and NOT any other `jpl.jar` that may be in the system (e.g., due to a distribution installation). In our example, under IntelliJ, we define a library `jpl` in the project of the application pointing to directory: + + /home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/out/production/jpl + +where all the classes for the currente devel JPL are located (if we are using IntelliJ). Then provded that library to use for your application/module for compilation and runtime. + +Third, and finally, you need to explicitly initialize the SWIPL JPL engine in your Java application to with the right home, executable, and most importantly `-F swipl` so that the initialization file `swipl.rc` in charge of setting up all the search paths: + +```java +String init_swi_config = + String.format("%s -x %s -F swipl --home=%s -g true -q", + System.getenv("SWI_EXEC_FILE"), # irrelevant for Windows + System.getenv("SWIPL_BOOT_FILE"), + System.getenv("SWI_HOME_DIR")); +JPL.setDefaultInitArgs(init_swi_config.split("\\s+")); # initialize SWIPL engine +JPL.init() +``` + +Because SWIPL tries to "guess" the location of the binary and boot file, in most cases, it also suffies to do: + +```java +String init_swi_config = + String.format("dummy --home=%s -g true -q", + System.getenv("SWI_HOME_DIR")); +JPL.setDefaultInitArgs(init_swi_config.split("\\s+")); # initialize SWIPL engine +``` + + + diff --git a/packages/jpl/docs/TutorialGettingStarted.md b/packages/jpl/docs/TutorialGettingStarted.md index 63403b5..65ba1f2 100644 --- a/packages/jpl/docs/TutorialGettingStarted.md +++ b/packages/jpl/docs/TutorialGettingStarted.md @@ -14,9 +14,47 @@ You may wish to load this database into an interactive Prolog session to experiment with the predicates in this database before experimenting with JPL. -## Initializing The Prolog engine +## Initializing the Prolog engine -Although the `org.jpl7.JPL` class provides a number of methods for initializing the Prolog engine from within Java, their use is not usually necessary: Prolog will be automatically initialised with default parameters at the first attempt to use it. +Although the `org.jpl7.JPL` class provides a number of methods for initializing the Prolog engine from within Java, their use is not usually necessary: Prolog will be _automatically initialised_ with default parameters at the first attempt to use it. + +By default, the SWIPL install accessible via the _default executable_ (i.e., the one that runs when one types `swipl`) will be initialized. If one, instead, _requires to use another install_ of SWIPL+JPL (e.g., the one under development or an alternative one installed in `/usr/local`), then we need to set-up a few variables to make sure the intended SWIPL resources are found. For example, suppose we intend that our Java-based application uses the SWIPL framework locally installed in `/usr/local/swipl/`. Then: + + SWI_HOME_DIR=/usr/local/swipl-git/lib/swipl/ # root of SWIPL install + LD_LIBRARY_PATH=/usr/local/swipl-git/lib/swipl/lib/x86_64-linux/ # all .so nativ libs + LD_PRELOAD=/usr/local/swipl-git/lib/swipl/lib/x86_64-linux/libswipl.so + CLASSPATH=/usr/local/swipl-git/lib/swipl/lib/jpl.jar + +From here, [SWIPL will try to find/guess the resources](https://www.swi-prolog.org/FAQ/FindResources.html) and set-up the engine correctly. + +Note also that you need to tell your development environment (e.g., IntelliJ or ECLIPSE) to use the correct `jpl.jar` (rather than one coming with the default active SWIPL system) or even the place where the JPL compiled classes are located if you intend to use a development (but uninstall) version of JPL. + +Sometimes one needs to _explicitly initialize_ the engine (before a query triggers defaul initialization); for example, if one wants to use an SWIPL & JPL under a development tree that is not yet installed. Assuming that development is happening under `/home/ssardina/git/soft/prolog/swipl-devel.git`, the following code template will perform a comprehensive, and quiety, initialization (before any query is performed) (see [CLI options](https://www.swi-prolog.org/pldoc/man?section=cmdline) available): + +```java +String init_swi_config = + String.format("%s -x %s -F swipl --home=%s -g true -q", + System.getenv("SWI_EXEC_FILE"), # irrelevant for Windows + System.getenv("SWIPL_BOOT_FILE"), + System.getenv("SWI_HOME_DIR")); +JPL.setDefaultInitArgs(init_swi_config.split("\\s+")); # initialize SWIPL engine +JPL.init() +``` + +The option `-F swipl` will cause the script `$SWI_HOME_DIR/swipl.rc` to be loaded which will set-up all the search paths relative to the install. The above code will require the following environment variables: + + SWI_HOME_DIR=/home/ssardina/git/soft/prolog/swipl-devel.git/build/home + SWI_EXEC_FILE=/home/ssardina/git/soft/prolog/swipl-devel.git/build/src/swipl + SWIPL_BOOT_FILE=/home/ssardina/git/soft/prolog/swipl-devel.git/build/home/boot.prc + +Because SWIPL tries to "guess" the location of the binary and boot file, in most cases, it also suffies to do: + +```java +String init_swi_config = + String.format("dummy --home=%s -g true -q", + System.getenv("SWI_HOME_DIR")); +JPL.setDefaultInitArgs(init_swi_config.split("\\s+")); # initialize SWIPL engine +``` diff --git a/packages/jpl/docs/TutorialMultithreaded.md b/packages/jpl/docs/TutorialMultithreaded.md index 2280e62..a2c8205 100644 --- a/packages/jpl/docs/TutorialMultithreaded.md +++ b/packages/jpl/docs/TutorialMultithreaded.md @@ -72,7 +72,7 @@ // process solution... } while (query1.hasMoreSolutions()) { // finish processing query1 - Map solution = query2.nextSolution(); + Map solution = query1.nextSolution(); // process solution... } ``` @@ -149,6 +149,6 @@ [Queries from multi-threaded applications](https://jpl7.org/JavaApiOverview#queries-from-multi-threaded-applications) -as well in this related issue at Github: https://github.com/SWI-Prolog/packages-jpl/issues/15 +as well in this related [issue #15](https://github.com/SWI-Prolog/packages-jpl/issues/15) at Github: diff --git a/packages/jpl/docs/index.md b/packages/jpl/docs/index.md index d147566..e184084 100644 --- a/packages/jpl/docs/index.md +++ b/packages/jpl/docs/index.md @@ -22,6 +22,14 @@ * minimum impact deployability: runtime support for Prolog+Java apps must be a position-independent, self-sufficient filestore tree, requiring no changes to registries, system libraries, system configuration files etc; * minimum dependency deployability: as with JVMs, the Prolog+Java runtime support must depend upon nothing which cannot be taken for granted in healthy OS installations; and * minimum vulnerability deployability: the Prolog+Java runtime support must be immune to legitimate variations in its environment (PATH settings, other applications and libraries including other Prolog+Java apps, etc.). + +### Historical perspective + +JPL was originally dreamed to be independent of Prolog implementation, mapping only the classic Prolog term model to and from Java, and its Java-side implementation deliberately resisted adoption of post Java 1.4 features (some nice, but none irresistible), thereby maintaining compatibility with (now very) old JVMs which some deployment environments might be stuck with. + +Innovations in SWI Prolog 7 prompted a JPL overhaul (from 3.0.3 to 7.0.1) which changed the public API and warranted a rebrand (as "JPL7", in a nod to SWIPL7). + +(Summary taken from discussion in [this issue](https://github.com/SWI-Prolog/packages-jpl/issues/46).) ## About this page diff --git a/packages/jpl/examples/java/Test/Test.java b/packages/jpl/examples/java/Test/Test.java index b4e87f0..d18fedd 100644 --- a/packages/jpl/examples/java/Test/Test.java +++ b/packages/jpl/examples/java/Test/Test.java @@ -178,7 +178,7 @@ Term Y = solutions[0].get("Y"); if (X != Y) { System.out.println(t7 + " failed:"); - System.out.println(Util.toString(solutions[0])); + System.out.println(Util.subsToString(solutions[0])); System.out.println("\tThe variables to which X and Y are bound in the first solution should be identical."); // System.exit(1); } @@ -186,13 +186,13 @@ Y = solutions[1].get("Y"); if (X == Y) { System.out.println(t7 + " failed:"); - System.out.println(Util.toString(solutions[1])); + System.out.println(Util.subsToString(solutions[1])); System.out.println("\tThe variables to which X and Y are bound in the second solution should be distinct."); // System.exit(1); } if (X.equals(Y)) { System.out.println(t7 + " failed:"); - System.out.println(Util.toString(solutions[1])); + System.out.println(Util.subsToString(solutions[1])); System.out.println("\tThe variables to which X and Y are bound in the second solution should not be \"equal\"."); // System.exit(1); } @@ -215,7 +215,7 @@ } // corresponds with Prolog List: [a-a,a-b] - static Term test_9_solution = Util.termArrayToList(new Term[] { new Compound("-", new Term[] { a, a }), new Compound("-", new Term[] { a, b }) }); + static Term test_9_solution = Term.termArrayToList(new Term[] { new Compound("-", new Term[] { a, a }), new Compound("-", new Term[] { a, b }) }); static void test_9() { System.out.print("test 9..."); diff --git a/packages/jpl/jpl.pl b/packages/jpl/jpl.pl index ad4aecd..febc5e0 100644 --- a/packages/jpl/jpl.pl +++ b/packages/jpl/jpl.pl @@ -1218,7 +1218,7 @@ % % == % ?- jpl_pl_lib_version(V). -% V = '7.4.0-alpha'. +% V = '7.6.0-alpha'. % == jpl_pl_lib_version(VersionString) :- @@ -1240,7 +1240,7 @@ % Status = alpha. % == -jpl_pl_lib_version(7, 4, 0, alpha). % jref as blob +jpl_pl_lib_version(7, 6, 0, alpha). % jref as blob %! jpl_c_lib_version(-Version) % diff --git a/packages/jpl/src/c/jpl.c b/packages/jpl/src/c/jpl.c index 74a0a08..53bddee 100644 --- a/packages/jpl/src/c/jpl.c +++ b/packages/jpl/src/c/jpl.c @@ -55,9 +55,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* update this to distinguish releases of this C library: */ -#define JPL_C_LIB_VERSION "7.4.0-alpha" +#define JPL_C_LIB_VERSION "7.6.0-alpha" #define JPL_C_LIB_VERSION_MAJOR 7 -#define JPL_C_LIB_VERSION_MINOR 4 +#define JPL_C_LIB_VERSION_MINOR 6 #define JPL_C_LIB_VERSION_PATCH 0 #define JPL_C_LIB_VERSION_STATUS "alpha" diff --git a/packages/jpl/src/java/CMakeLists.txt b/packages/jpl/src/java/CMakeLists.txt index 66f729b..fb87284 100644 --- a/packages/jpl/src/java/CMakeLists.txt +++ b/packages/jpl/src/java/CMakeLists.txt @@ -89,9 +89,12 @@ set(CMAKE_JAVA_COMPILE_FLAGS) endif() + add_jar(jpl_jar SOURCES ${CLS} ${FLI} - OUTPUT_NAME jpl) + OUTPUT_NAME jpl + MANIFEST META-INF/MANIFEST.MF + VERSION ${JPL_VERSION}) # VERSION creates jar with the -jar format and a symblic to it. if(JUNIT_JAR) add_jar(jpltest diff --git a/packages/jpl/src/java/META-INF/MANIFEST.MF b/packages/jpl/src/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5627584 --- /dev/null +++ b/packages/jpl/src/java/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Name: org.jpl7 +Implementation-Title: JPL +Implementation-Version: 7.6.0 +Implementation-Vendor: SWI-Prolog diff --git a/packages/jpl/src/java/org/jpl7/Atom.java b/packages/jpl/src/java/org/jpl7/Atom.java index 17bcbca..2656df9 100644 --- a/packages/jpl/src/java/org/jpl7/Atom.java +++ b/packages/jpl/src/java/org/jpl7/Atom.java @@ -177,7 +177,7 @@ * @return string representation of an Atom */ public String toString() { - return (JPL.isSimpleName(name) ? name : "'" + name + "'"); + return (JPL.isSimpleName(name) || isListNil() ? name : "'" + name + "'"); } /** diff --git a/packages/jpl/src/java/org/jpl7/Compound.java b/packages/jpl/src/java/org/jpl7/Compound.java index e642be2..accb4ef 100644 --- a/packages/jpl/src/java/org/jpl7/Compound.java +++ b/packages/jpl/src/java/org/jpl7/Compound.java @@ -227,6 +227,10 @@ /** * whether this Term denotes (syntax-specifically) a list cell + * see this does not check the shape of the second argument to check if it is a list + * + * So a list pair may not be a list itself and hence isList() will return false + * as the second argument many not be a list * * @return whether this Term denotes (syntax-specifically) a list cell */ @@ -281,16 +285,38 @@ } - - /** - * a prefix functional representation of a Compound of the form name(arg1,...), where 'name' is quoted iff necessary - * (to be valid Prolog soutce text) and each argument is represented according to its toString() method. + /** + * + * a prefix functional representation of a Compound of the form name(arg1,...), + * where 'name' is quoted iff necessary + * (to be valid Prolog source text) and each argument is represented according to its + * toString() method. + * + * Lists are represented depending on JPL.LIST_TOSTRING_TEXTUAL * * @return string representation of an Compound */ public String toString() { - return JPL.quotedName(name) + (args.length > 0 ? "(" + Term.toString(args) + ")" : ""); - } + if (isListPair() && JPL.LIST_TOSTRING_TEXTUAL) { // output nice Prolog [.,.,.,.,] format, not prefix format + String head_str = arg(1).toString(); // must be at least 1 arg in compound + String tail_str; + if (arg(2).isListNil()) { + return String.format("[%s]", head_str); + } else { + tail_str = arg(2).toString(); + StringBuilder builder = new StringBuilder(tail_str); + if (arg(2).isListPair()) { + builder.deleteCharAt(builder.length()-1); // remove closing ] + builder.deleteCharAt(0); // remove opening [ + } + return String.format("[%s, %s]", head_str, builder.toString()); + } + } // just return lists in pre-fix as '[|]'(head, '[|]'(head, '[|]'(head, .....) + return JPL.quotedName(name) + + (args.length > 0 ? "(" + Term.toString(args) + ")" : ""); + } + + /** * the type of this term, as jpl.fli.Prolog.COMPOUND diff --git a/packages/jpl/src/java/org/jpl7/JPL.java b/packages/jpl/src/java/org/jpl7/JPL.java index a97cef8..9cebbd6 100644 --- a/packages/jpl/src/java/org/jpl7/JPL.java +++ b/packages/jpl/src/java/org/jpl7/JPL.java @@ -76,6 +76,8 @@ public static String LIST_PAIR = LIST_PAIR_MODERN; // default unless/until // setTraditional() is // called successfully + // whether we want lists to be printed in pre-fix '[|]'(head, tails) or textual [....] format + public static boolean LIST_TOSTRING_TEXTUAL = true; private static String nativeLibraryName = "jpl"; private static String nativeLibraryDir = null; diff --git a/packages/jpl/src/java/org/jpl7/Query.java b/packages/jpl/src/java/org/jpl7/Query.java index 962a841..6a3d169 100644 --- a/packages/jpl/src/java/org/jpl7/Query.java +++ b/packages/jpl/src/java/org/jpl7/Query.java @@ -166,7 +166,7 @@ * @throws JPLException if term provided is not of right sort Atom or Compound */ public Query(String text) { - this(Util.textToTerm(text)); + this(Term.textToTerm(text)); } @@ -205,7 +205,7 @@ * the arguments of this Query's goal */ private static Term buildQueryTerm(String text, Term[] args) { - Term t = Util.textToTerm(text); + Term t = Term.textToTerm(text); if (t instanceof Atom) { return new Compound(text, args); } else { @@ -436,7 +436,7 @@ Term exception_term = Term.getTerm(new HashMap(), exception_term_t); close(); throw new PrologException(exception_term); - } else { + } else { // no more solution, close the query close(); return false; } @@ -456,7 +456,7 @@ /** * Used to be used only by Utils.textToTerm but code has been integrated there already - * @return + * @return a substituion from variable names to terms */ @Deprecated public final Map getSubstWithNameVars() { @@ -563,7 +563,7 @@ String BindingVarName = ((Variable) BindingVarTerm).name; // its textual name ("Bindings" in example) Term TermVar = args[args.length - 2]; // the Query's last arg: a variable binding list; type Variable - String TermVarName = ((Variable) TermVar).name; // its textual name ("Bindings" in example) +// String TermVarName = ((Variable) TermVar).name; // its textual name ("Bindings" in example) // SECOND, we store what B was bound to as JPL Compound list instance BindingVarValue // If B was bound to ['X' = _1, 'Y' = _2] in the current solution diff --git a/packages/jpl/src/java/org/jpl7/Term.java b/packages/jpl/src/java/org/jpl7/Term.java index 58900e0..c358c68 100644 --- a/packages/jpl/src/java/org/jpl7/Term.java +++ b/packages/jpl/src/java/org/jpl7/Term.java @@ -60,7 +60,7 @@ * returns the i-th (1+) argument of a Term; * * defined only for Compound - * + * * @param i the index of argument to return * @return the i-th argument of a (Compound) Term * @throws JPLException if Term is not a Compound @@ -105,10 +105,24 @@ throw new JPLException("atomType() is undefined for " + this.typeName()); } - - /** - * Converts a Prolog source text to a corresponding JPL Term (in which each Variable has the appropriate name from - * the source text). + /** + * The name of an Atom, Compound or Variable. + * + * @return the name of an Atom, Compound or Variable. + * @throws JPLException + * if this Term is not an Atom, Compound or Variable. + */ + public String name() { // overridden in Atom, Compound, Variable + throw new JPLException("name() is undefined for " + this.typeName()); + } + + public void setName(String name) { + throw new JPLException("name() is undefined for " + this.typeName()); + } + + /** + * Converts a Prolog source text to a corresponding JPL Term + * (in which each Variable has the appropriate name from the source text). * * @param text A Prolog source text denoting a term * @return Term a JPL Term equivalent to the given source text @@ -131,12 +145,9 @@ // it might be better to use PL_chars_to_term() Query q = new Query(new Compound("atom_to_term", new Term[] { new Atom(text), new Variable("T"), new Variable("NVdict") })); + q.open(); - - // quer is open, check if there is a solution if (q.hasMoreSolutions()) { - // cannot use this bc it gives anonymous vars, we lose the names - // If we use this we will get T mmapped to Term mother(maria, _1, _2) but we want mother(maria, X, Y) Map s = q.nextSolution(); // New way of doing it (April 2020) @@ -148,23 +159,65 @@ // Since the Term bound to T will use exactl the Variables _1, _2, etc, by changing their names // the term T will now use Variables X, Y, etc as wanted! //Term T = s.get("T"); - Term[] VarsMapList = Util.listToTermArray(s.get("NVdict")); // this is a Compound list of mappings + Term[] VarsMapList = Term.listToTermArray(s.get("NVdict")); // this is a Compound list of mappings for (Term map : VarsMapList) { // map represents 'X' = _1 - String VarName = map.arg(1).name(); // extract the symbolic name - Variable Var = (Variable) map.arg(2); // extract Variable object - Var.setName(VarName); + String VarName = map.arg(1).name(); // extract the symbolic name of the variable + Variable Var = (Variable) map.arg(2); // extract the Variable object + Var.setName(VarName); // set the name of the Variable object to the symbolic name } // Old way of doing it (before April 2020) - extremely involved! //Map s = q.getSolutionWithVarNames(); + + // We MUST close the query explicitly or otherwise it will remain at the top of stack because we + // just did q.nextSolution() and there may be more q.close(); + return (Term) s.get("T"); } else { + q.close(); // redundant as q.hasMoreSolutions() would have closed it, but .... return null; } } + /** + * Converts a Prolog source text to a corresponding JPL Term (in which each Variable has the appropriate name from + * the source text), replacing successive occurrences of ? in the text by the corresponding element of Term[] + * params. (New in JPL 3.0.4) + * + * Throws PrologException containing error(syntax_error(_),_) if text is invalid. + * + * @param text A Prolog source text denoting a term + * @param params parameters to be injected in each ? + * @return Term a JPL Term equivalent to the given source text + */ + public static Term textParamsToTerm(String text, Term[] params) { + return Term.textToTerm(text).putParams(params); + } + + + /** + * returns the value (as an int) of an Integer or Float + * + * @return the value (as an int) of an Integer or Float + * @throws JPLException + * if this Term is a Compound, Atom or Variable + */ + public int intValue() { + throw new JPLException("intValue() is undefined for " + this.typeName()); + } + + /** + * The (long) value of a Float or Integer. + * + * @return the (long) value of a Float or Integer. + * @throws JPLException + * if this Term is not a Float or Integer. + */ + public long longValue() { // overridden in Integer, GFloat + throw new JPLException("longValue() is undefined for " + this.typeName()); + } /** * the value (as a java.math.BigInteger) of an Integer, whether or not it is big @@ -199,47 +252,6 @@ throw new JPLException("floatValue() is undefined for " + this.typeName()); } - /** - * This method computes a substitution from a Term (the current object). - * The bindings Map varnames_to_Terms maps names of Variables to Terms. - * Thus, a substitution is as it is in mathematical logic, a sequence of the form \sigma = {t_0/x_0, ..., t_n/x_n}. - * Once the substitution is computed, the substitution should satisfy - * - * \sigma T = t - * - * where T is the Term from which the substitution is computed, and t is the term_t which results from the Prolog - * query. - *

- * - * A second Map, vars_to_Vars, is required: this table holds the Variables that occur (thus far) in the unified term. - * The Variable instances in this table are guaranteed to be unique and are keyed on Strings which are Prolog internal - * representations of the variables. - * - * @param varnames_to_Terms - * table holding Term substitutions, keyed on names of Variables. - * @param vars_to_Vars - * A Map holding the Variables that occur thus far in the term; keyed by internal (Prolog) string rep. - */ - protected void getSubst(Map varnames_to_Terms, Map vars_to_Vars) { - // overridden in Compound, Variable - } - - /** - * Just calls computeSubstitution for each Term in the array. - * - * @param varnames_to_Terms - * a Map from variable names to Terms (what each variable string is to be replaced by) - * @param vars_to_Vars - * a Map from Prolog variables (which may be bounded in the engine) to JPL Variables (which are Java objects) - * @param args - * an array of Terms to which the substitution is to be applied - */ - protected static void getSubsts(Map varnames_to_Terms, Map vars_to_Vars, - Term[] args) { - for (int i = 0; i < args.length; ++i) { - args[i].getSubst(varnames_to_Terms, vars_to_Vars); - } - } /** * create and return a org.jpl7.Term representation of the given Prolog term @@ -256,89 +268,89 @@ Int64Holder hInt64; ObjectHolder hObject; switch (Prolog.term_type(term)) { - case Prolog.VARIABLE: // 1 - for (Iterator i = vars_to_Vars.keySet().iterator(); i.hasNext();) { - term_t varX = (term_t) i.next(); // a previously seen Prolog - // variable - if (Prolog.compare(varX, term) == 0) { // identical Prolog - // variables? - return (Term) vars_to_Vars.get(varX); // return the - // associated JPL - // Variable + case Prolog.VARIABLE: // 1 + for (Iterator i = vars_to_Vars.keySet().iterator(); i.hasNext();) { + term_t varX = (term_t) i.next(); // a previously seen Prolog + // variable + if (Prolog.compare(varX, term) == 0) { // identical Prolog + // variables? + return (Term) vars_to_Vars.get(varX); // return the + // associated JPL + // Variable + } } - } - // otherwise, the Prolog variable in term has not been seen before - Variable Var = new Variable(); // allocate a new (sequentially - // named) Variable to represent it - Var.term_ = term; // this should become redundant... - vars_to_Vars.put(term, Var); // use Hashtable(var,null), but only - // need set(var) - return Var; - case Prolog.ATOM: // 2 - hString = new StringHolder(); - Prolog.get_atom_chars(term, hString); // ignore return val; assume - // success... - return new Atom(hString.value, "text"); - case Prolog.STRING: // 5 - hString = new StringHolder(); - Prolog.get_string_chars(term, hString); // ignore return val; assume - // success... - return new Atom(hString.value, "string"); - case Prolog.INTEGER: // 3 - hInt64 = new Int64Holder(); - if (Prolog.get_integer(term, hInt64)) { // assume it fails if Prolog - // integer is bigger than a - // Java long... - return new org.jpl7.Integer(hInt64.value); - } else { + // otherwise, the Prolog variable in term has not been seen before + Variable Var = new Variable(); // allocate a new (sequentially + // named) Variable to represent it + Var.term_ = term; // this should become redundant... + vars_to_Vars.put(term, Var); // use Hashtable(var,null), but only + // need set(var) + return Var; + case Prolog.ATOM: // 2 hString = new StringHolder(); - if (Prolog.get_integer_big(term, hString)) { + Prolog.get_atom_chars(term, hString); // ignore return val; assume + // success... + return new Atom(hString.value, "text"); + case Prolog.STRING: // 5 + hString = new StringHolder(); + Prolog.get_string_chars(term, hString); // ignore return val; assume + // success... + return new Atom(hString.value, "string"); + case Prolog.INTEGER: // 3 + hInt64 = new Int64Holder(); + if (Prolog.get_integer(term, hInt64)) { // assume it fails if Prolog + // integer is bigger than a + // Java long... + return new org.jpl7.Integer(hInt64.value); + } else { + hString = new StringHolder(); + if (Prolog.get_integer_big(term, hString)) { + // System.out.println("bigint = " + hString.value); + return new org.jpl7.Integer(new java.math.BigInteger(hString.value)); + } else { + return new org.jpl7.Integer(-3); // arbitrary + } + } + case Prolog.RATIONAL: // 4 + hString = new StringHolder(); + if (Prolog.get_rational(term, hString)) { // System.out.println("bigint = " + hString.value); - return new org.jpl7.Integer(new java.math.BigInteger(hString.value)); + return new org.jpl7.Rational(hString.value); } else { return new org.jpl7.Integer(-3); // arbitrary } - } - case Prolog.RATIONAL: // 4 - hString = new StringHolder(); - if (Prolog.get_rational(term, hString)) { - // System.out.println("bigint = " + hString.value); - return new org.jpl7.Rational(hString.value); - } else { - return new org.jpl7.Integer(-3); // arbitrary - } - case Prolog.FLOAT: // 5 - DoubleHolder hFloatValue = new DoubleHolder(); - Prolog.get_float(term, hFloatValue); // assume it succeeds... - return new org.jpl7.Float(hFloatValue.value); - case Prolog.COMPOUND: // 6 - case Prolog.LIST_PAIR: // 9 - hString = new StringHolder(); - hInt = new IntHolder(); - Prolog.get_name_arity(term, hString, hInt); // assume it succeeds - Term args[] = new Term[hInt.value]; - // term_t term1 = Prolog.new_term_refs(hArity.value); - for (int i = 1; i <= hInt.value; i++) { - term_t termi = Prolog.new_term_ref(); - Prolog.get_arg(i, term, termi); - args[i - 1] = Term.getTerm(vars_to_Vars, termi); - } - return new Compound(hString.value, args); - case Prolog.LIST_NIL: // 7 - return JPL.LIST_NIL; - case Prolog.BLOB: // 8 - hObject = new ObjectHolder(); - if (Prolog.get_jref_object(term, hObject)) { - if (hObject.value == null) { - return JPL.JNULL; + case Prolog.FLOAT: // 5 + DoubleHolder hFloatValue = new DoubleHolder(); + Prolog.get_float(term, hFloatValue); // assume it succeeds... + return new org.jpl7.Float(hFloatValue.value); + case Prolog.COMPOUND: // 6 + case Prolog.LIST_PAIR: // 9 + hString = new StringHolder(); + hInt = new IntHolder(); + Prolog.get_name_arity(term, hString, hInt); // assume it succeeds + Term args[] = new Term[hInt.value]; + // term_t term1 = Prolog.new_term_refs(hArity.value); + for (int i = 1; i <= hInt.value; i++) { + term_t termi = Prolog.new_term_ref(); + Prolog.get_arg(i, term, termi); + args[i - 1] = Term.getTerm(vars_to_Vars, termi); + } + return new Compound(hString.value, args); + case Prolog.LIST_NIL: // 7 + return JPL.LIST_NIL; + case Prolog.BLOB: // 8 + hObject = new ObjectHolder(); + if (Prolog.get_jref_object(term, hObject)) { + if (hObject.value == null) { + return JPL.JNULL; + } else { + return new JRef(hObject.value); + } } else { - return new JRef(hObject.value); + throw new JPLException("unsupported blob type passed from Prolog"); } - } else { - throw new JPLException("unsupported blob type passed from Prolog"); - } - default: // should never happen - throw new JPLException("unknown term type=" + Prolog.term_type(term)); + default: // should never happen + throw new JPLException("unknown term type=" + Prolog.term_type(term)); } } @@ -358,7 +370,7 @@ * if this Term is a Variable */ public boolean hasFunctor(String name, int arity) { // overridden in - // Atom, Compound + // Atom, Compound return false; } @@ -413,16 +425,7 @@ return false; } - /** - * returns the value (as an int) of an Integer or Float - * - * @return the value (as an int) of an Integer or Float - * @throws JPLException - * if this Term is a Compound, Atom or Variable - */ - public int intValue() { - throw new JPLException("intValue() is undefined for " + this.typeName()); - } + /** * whether this Term is an Atom (of any type) @@ -543,34 +546,7 @@ return false; } - /** - * Tests whether this Term denotes an empty list within the current syntax ("traditional" or "modern"). - * - * @return whether this Term denotes an empty list within the current syntax ("traditional" or "modern"). - * @see org.jpl7.JPL#getSyntax() - */ - public boolean isListNil() { // overridden in Atom - return false; - } - - /** - * Tests whether this Term is a list pair within the current syntax ("traditional" or "modern"). - * - * @return whether this Term is a list pair within the current syntax ("traditional" or "modern"). - */ - public boolean isListPair() { // overridden in Compound - return false; - } - - - /** - * Tests whether this Term is a list (empty or list pair) - * - * @return whether this Term is a list, empty or list pair - */ - public boolean isList() { // overridden in Compound - return (isListNil() || isListPair()); - } + /** * Tests whether this Term is a Variable. @@ -581,58 +557,8 @@ return this instanceof Variable; } - /** - * @return the Object which this JRef references - * @deprecated Use {@link JRef#object()} - */ - @Deprecated - public Object jrefToObject() { // overridden in Compound and JRef - throw new JPLException("term is neither a JRef nor a Compound representing @(null)"); - } - - /** - * The length of this list, iff it is one, else an exception is thrown. - * - * @throws JPLException if the term is not a list - * @return the length (as an int) of this list, iff it is one. - * @deprecated Use {@link Util#listToLength(Term)} - */ - @Deprecated - public final int listLength() { - if (this.isListPair()) { // was .hasFunctor(".", 2) - return 1 + this.arg(2).listLength(); // TODO eliminate recursion - } else if (this.isListNil()) { // was .hasFunctor("[]", 0) - return 0; - } else { - throw new JPLException("term is not a list"); - } - } - - /** - * The (long) value of a Float or Integer. - * - * @return the (long) value of a Float or Integer. - * @throws JPLException - * if this Term is not a Float or Integer. - */ - public long longValue() { // overridden in Integer, GFloat - throw new JPLException("longValue() is undefined for " + this.typeName()); - } - - /** - * The name of an Atom, Compound or Variable. - * - * @return the name of an Atom, Compound or Variable. - * @throws JPLException - * if this Term is not an Atom, Compound or Variable. - */ - public String name() { // overridden in Atom, Compound, Variable - throw new JPLException("name() is undefined for " + this.typeName()); - } - - public void setName(String name) { - throw new JPLException("name() is undefined for " + this.typeName()); - } + + /** * The Object which this org.jpl7.JRef refers to, iff this Term is a JRef or just JPL.JNULL. * @@ -641,21 +567,21 @@ */ public Object object() { // overridden in JRef if (this == JPL.JNULL) - return null; - else - throw new JPLException("this term is not a JRef"); - } - - /** - * Returns the JREF term for an object - * - * @param object object of interest + return null; + else + throw new JPLException("this term is not a JRef"); + } + + /** + * Returns the JREF term for an object + * + * @param object object of interest * @return a new JRef which references object, or @(null) if object == null. * @throws JPLException * if object is a String. * @deprecated Use {@link JPL#newJRef} */ - @Deprecated + @Deprecated public static final Term objectToJRef(Object object) { if (object == null) { return JPL.JNULL; @@ -733,18 +659,18 @@ protected Term putParams1(IntHolder next, Term[] ps) { switch (this.type()) { - case Prolog.COMPOUND: - return new Compound(this.name(), putParams2(this.args(), next, ps)); - case Prolog.ATOM: - if (!this.name().equals("?")) { + case Prolog.COMPOUND: + return new Compound(this.name(), putParams2(this.args(), next, ps)); + case Prolog.ATOM: + if (!this.name().equals("?")) { + return this; + } else if (next.value >= ps.length) { + throw new JPLException("fewer actual params than formal params"); + } else { + return ps[next.value++]; + } + default: return this; - } else if (next.value >= ps.length) { - throw new JPLException("fewer actual params than formal params"); - } else { - return ps[next.value++]; - } - default: - return this; } } @@ -800,31 +726,19 @@ * if this Term is not a JRef * @deprecated Use {@link JRef#object()} */ - @Deprecated + @Deprecated public Object ref() { // overridden in JRef throw new JPLException("this Term is not a JRef"); } - /** - * returns an array of Terms whose elements are the respective members of this list, iff it is a list. - * - * @throws JPLException - * if this Term is not a proper list. - * @return an array of Terms whose elements are the respective members of this list, iff it is a list. - */ - public final Term[] toTermArray() { - try { - int len = Util.listToLength(this); // exception if not a well formed list - Term[] ts = new Term[len]; - Term t = this; - for (int i = 0; i < len; i++) { // no need to check functor (it's a list) - ts[i] = t.arg(1); - t = t.arg(2); - } - return ts; - } catch (JPLException e) { - throw new JPLException("term is not a proper list"); - } + + /** + * @return the Object which this JRef references + * @deprecated Use {@link JRef#object()} + */ + @Deprecated + public Object jrefToObject() { // overridden in Compound and JRef + throw new JPLException("term is neither a JRef nor a Compound representing @(null)"); } // ==================================================================/ @@ -841,6 +755,49 @@ // Variable with which the term was unified. // ==================================================================/ + + /** + * This method computes a substitution from a Term (the current object). + * The bindings Map varnames_to_Terms maps names of Variables to Terms. + * Thus, a substitution is as it is in mathematical logic, a sequence of the form \sigma = {t_0/x_0, ..., t_n/x_n}. + * Once the substitution is computed, the substitution should satisfy + * + * \sigma T = t + * + * where T is the Term from which the substitution is computed, and t is the term_t which results from the Prolog + * query. + *

+ * + * A second Map, vars_to_Vars, is required: this table holds the Variables that occur (thus far) in the unified term. + * The Variable instances in this table are guaranteed to be unique and are keyed on Strings which are Prolog internal + * representations of the variables. + * + * @param varnames_to_Terms + * table holding Term substitutions, keyed on names of Variables. + * @param vars_to_Vars + * A Map holding the Variables that occur thus far in the term; keyed by internal (Prolog) string rep. + */ + protected void getSubst(Map varnames_to_Terms, Map vars_to_Vars) { + // overridden in Compound, Variable + } + + /** + * Just calls computeSubstitution for each Term in the array. + * + * @param varnames_to_Terms + * a Map from variable names to Terms (what each variable string is to be replaced by) + * @param vars_to_Vars + * a Map from Prolog variables (which may be bounded in the engine) to JPL Variables (which are Java objects) + * @param args + * an array of Terms to which the substitution is to be applied + */ + protected static void getSubsts(Map varnames_to_Terms, Map vars_to_Vars, + Term[] args) { + for (int i = 0; i < args.length; ++i) { + args[i].getSubst(varnames_to_Terms, vars_to_Vars); + } + } + /** * This method is used (by Compound.equals) to determine whether two Term arrays are pairwise equal, where two * Terms are equal if they satisfy the equals predicate (defined differently in each Term subclass). @@ -896,4 +853,213 @@ */ public abstract String typeName(); -} + + + + + // ==================================================================/ + // LIST METHODS + // + // All tools to deal specifically with lists that could be either + // empty list JPL.LIST_NIL or Compound terms with functor [|]. + // ==================================================================/ + + /** + * Tests whether this Term denotes an empty list within the current syntax ("traditional" or "modern"). + * + * @return whether this Term denotes an empty list within the current syntax ("traditional" or "modern"). + * @see org.jpl7.JPL#getSyntax() + */ + public boolean isListNil() { // overridden in Atom + return false; + } + + /** + * Tests whether this Term is a list pair within the current + * syntax ("traditional" or "modern"). + * + * Note that a list pair may not be a list itself and hence isList() will return false + * as the second argument many not be a list + * + * @return whether this Term is a list pair within the current syntax ("traditional" or "modern"). + */ + public boolean isListPair() { // overridden in Compound + return false; + } + + /** + * whether the Term represents a proper list + * + * @return whether the Term represents a proper list + */ + public final boolean isList() { + return isList(this); + } + + /** + * whether the Term represents a proper list + * + * @param term the term to check if it is a list + * @return whether the Term represents a proper list + */ + public static final boolean isList(Term term) { + return listLength(term) >= 0; + } + + + + /** + * @param term any Term + * @return the length of the proper list which the Term represents, else -1 + */ + public static int listLength(Term term) { + int length = 0; + Term head = term; + while (head.isListPair()) { + length++; + head = head.arg(2); + } + return (head.isListNil() ? length : -1); + } + + /** + * @return the length of the proper list which the Term represents, else -1 + */ + public int listLength() { + return Term.listLength(this); + } + + /** + * Converts an array of String to a corresponding JPL list of atoms + * + * @param a An array of String objects + * @return Term a JPL list of atoms corresponding to the given String array + */ + public static Term stringArrayToList(String[] a) { + Term list = JPL.LIST_NIL; + for (int i = a.length - 1; i >= 0; i--) { + list = new Compound(JPL.LIST_PAIR, new Term[] { new Atom(a[i]), list }); + } + return list; + } + + /** + * Converts an array of int to a corresponding JPL list + * + * @param a + * An array of int values + * @return Term a JPL list corresponding to the given int array + */ + public static Term intArrayToList(int[] a) { + Term list = JPL.LIST_NIL; // was new Atom("[]"); + for (int i = a.length - 1; i >= 0; i--) { + list = new Compound(JPL.LIST_PAIR, new Term[] { new org.jpl7.Integer(a[i]), list }); + } + return list; + } + + /** + * Converts an array of arrays of int to a corresponding JPL list of lists + * + * @param a + * An array of arrays of int values + * @return Term a JPL list of lists corresponding to the given int array of arrays + */ + public static Term intArrayArrayToList(int[][] a) { + Term list = JPL.LIST_NIL; // was new Atom("[]"); + for (int i = a.length - 1; i >= 0; i--) { + list = new Compound(JPL.LIST_PAIR, new Term[] { intArrayToList(a[i]), list }); + } + return list; + } + + + /** + * Converts an array of Terms to a JPL representation of a Prolog list of terms whose members correspond to the + * respective array elements. + * + * @param terms + * An array of Term + * @return Term a list of the array elements + */ + public static Term termArrayToList(Term[] terms) { + Term list = JPL.LIST_NIL; // was new Atom("[]") + for (int i = terms.length - 1; i >= 0; --i) { + list = new Compound(JPL.LIST_PAIR, new Term[] { terms[i], list }); + } + return list; + } + + + /** + * Converts a term representing a list of atoms into an array of Strings, each element + * in the array being a String for the corresponding atom + * e.g., [a, b, 1 + * + * + * @param t a term representing a list of atoms + * @return an array of Strings, each element, null if t is not a list of atoms + */ + public static String[] atomListToStringArray(Term t) { + int n = Term.listLength(t); + String[] a; + if (n < 0) { + return null; + } else { + a = new String[n]; + } + int i = 0; + Term head = t; + while (head.isListPair()) { + Term x = head.arg(1); + if (x.isAtom()) { + a[i++] = x.name(); + } else { + return null; + } + head = head.arg(2); + } + return (head.isListNil() ? a : null); + } + + + + /** + * converts a proper list to an array of terms, else throws an exception + * + * @throws JPLException if the term passed is not itself a Prolog list term + * @return an array of terms whose successive elements are the corresponding members of the list (if it is a list) + */ + public static Term[] listToTermArray(Term t) { + int len = Term.listLength(t); // exception if not a well formed list + if (len < 0) { + throw new JPLException("term is not a proper list"); + } + Term[] ts = new Term[len]; + for (int i = 0; i < len; i++) { // no need to check functor (it's a list) + ts[i] = t.arg(1); + t = t.arg(2); + } + return ts; + } + + /** + * converts a proper list to an array of terms, else throws an exception + * + * @throws JPLException if the term passed is not itself a Prolog list term + * @return an array of terms whose successive elements are the corresponding members of the list (if it is a list) + */ + public final Term[] listToTermArray() { + return Term.listToTermArray(this); + } + + /** + * @deprecated Use org.jpl7.Term.termArrayToList(Term[] terms) + */ + @Deprecated + public final Term[] toTermArray() { + return listToTermArray(); + } + + +} \ No newline at end of file diff --git a/packages/jpl/src/java/org/jpl7/Util.java b/packages/jpl/src/java/org/jpl7/Util.java index 92623e0..c3f6f0f 100644 --- a/packages/jpl/src/java/org/jpl7/Util.java +++ b/packages/jpl/src/java/org/jpl7/Util.java @@ -46,13 +46,11 @@ * @param terms * An array of Term * @return Term a list of the array elements - */ + * @deprecated Use org.jpl7.Term.termArrayToList(Term[] terms) + */ + @Deprecated public static Term termArrayToList(Term[] terms) { - Term list = JPL.LIST_NIL; // was new Atom("[]") - for (int i = terms.length - 1; i >= 0; --i) { - list = new Compound(JPL.LIST_PAIR, new Term[] { terms[i], list }); - } - return list; + return Term.termArrayToList(terms); } /** @@ -62,7 +60,7 @@ * A Map from variable names to Terms. * @return String A String representation of the variable bindings */ - public static String toString(Map varnames_to_Terms) { + public static String subsToString(Map varnames_to_Terms) { if (varnames_to_Terms == null) { return "[no solution]"; } else { @@ -74,6 +72,14 @@ } return s; } + } + + /** + * @deprecated Use org.jpl7.Util.subsToString(Map) + */ + @Deprecated + public static String toString(Map varnames_to_Terms) { + return subsToString(varnames_to_Terms); } /** @@ -94,31 +100,25 @@ // the cast to Variable is necessary to access the (protected) // .term_ field vars_to_Vars.put(((Variable) nvs.arg(1).arg(2)).term_, new Variable(nvs.arg(1).arg(1).name())); // map - // the - // Prolog - // variable - // to - // a - // new, - // named - // Variable + // the + // Prolog + // variable + // to + // a + // new, + // named + // Variable nvs = nvs.arg(2); // advance to next list cell } // maybe oughta check that nvs is [] ? return vars_to_Vars; } catch (java.lang.ClassCastException e) { // nvs is not of the expected - // structure + // structure return null; } } /** - * Converts a Prolog source text to a corresponding JPL Term (in which each Variable has the appropriate name from - * the source text). - * - * @param text A Prolog source text denoting a term - * @return Term a JPL Term equivalent to the given source text - * @throws PrologException containing error(syntax_error(_),_) if text is invalid as a term. * @deprecated Use org.jpl7.Term.textToTerm(String text) */ @Deprecated @@ -142,84 +142,55 @@ } /** - * Converts an array of String to a corresponding JPL list - * - * @param a - * An array of String objects - * @return Term a JPL list corresponding to the given String array - */ + * @deprecated Use org.jpl7.Term.stringArrayToList(String[] a) + */ + @Deprecated public static Term stringArrayToList(String[] a) { - Term list = JPL.LIST_NIL; - for (int i = a.length - 1; i >= 0; i--) { - list = new Compound(JPL.LIST_PAIR, new Term[] { new Atom(a[i]), list }); - } - return list; - } - - /** - * Converts an array of int to a corresponding JPL list - * - * @param a - * An array of int values - * @return Term a JPL list corresponding to the given int array - */ + return Term.stringArrayToList(a); + } + + /** + * @deprecated Use org.jpl7.Term.intArrayToList(int[] a) + */ + @Deprecated public static Term intArrayToList(int[] a) { - Term list = JPL.LIST_NIL; // was new Atom("[]"); - for (int i = a.length - 1; i >= 0; i--) { - list = new Compound(JPL.LIST_PAIR, new Term[] { new org.jpl7.Integer(a[i]), list }); - } - return list; - } - - /** - * Converts an array of arrays of int to a corresponding JPL list of lists - * - * @param a - * An array of arrays of int values - * @return Term a JPL list of lists corresponding to the given int array of arrays - */ + return Term.intArrayToList(a); + } + + /** + * @deprecated Use org.jpl7.Term.intArrayArrayToList(int[][] a) + */ + @Deprecated public static Term intArrayArrayToList(int[][] a) { - Term list = JPL.LIST_NIL; // was new Atom("[]"); - for (int i = a.length - 1; i >= 0; i--) { - list = new Compound(JPL.LIST_PAIR, new Term[] { intArrayToList(a[i]), list }); - } - return list; - } - - /** - * whether the Term represents a proper list - * - * @param term the term to check if it is a list - * @return whether the Term represents a proper list - */ + return Term.intArrayArrayToList(a); + } + + /** + * @deprecated Use {@link Term#isList(Term)} + */ + @Deprecated public static final boolean isList(Term term) { - return listToLength(term) >= 0; - } + return Term.isList(term); + } + /** * @param term any Term * @return the length of the proper list which the Term represents, else -1 - */ + * @deprecated Use {@link Term#listLength(Term)} + */ + @Deprecated public static int listToLength(Term term) { - int length = 0; - Term head = term; - while (head.isListPair()) { - length++; - head = head.arg(2); - } - return (head.isListNil() ? length : -1); - } - - /** - * converts a proper list to an array of terms, else throws an exception - * - * @param t a list term - * @throws JPLException if the term passed is not itself a Prolog list term - * @return an array of terms whose successive elements are the corresponding members of the list (if it is a list) - */ + return Term.listLength(term); + } + + /** + * @deprecated Use {@link Term#listToTermArray(Term)} + */ + @Deprecated public static Term[] listToTermArray(Term t) { try { - int len = Util.listToLength(t); // exception if not a list + int len = Term.listLength(t); // exception if not a list Term[] ts = new Term[len]; for (int i = 0; i < len; i++) { ts[i] = t.arg(1); @@ -231,8 +202,12 @@ } } + /** + * @deprecated Use {@link Term#atomListToStringArray(Term)} + */ + @Deprecated public static String[] atomListToStringArray(Term t) { - int n = listToLength(t); + int n = Term.listLength(t); String[] a; if (n < 0) { return null; diff --git a/packages/jpl/src/java/org/jpl7/Version.java b/packages/jpl/src/java/org/jpl7/Version.java index 20a39a2..f82d937 100644 --- a/packages/jpl/src/java/org/jpl7/Version.java +++ b/packages/jpl/src/java/org/jpl7/Version.java @@ -3,7 +3,7 @@ class Version { public final int major = 7; - public final int minor = 4; // jref as blob + public final int minor = 6; // jref as blob public final int patch = 0; public final String status = "alpha"; } diff --git a/packages/jpl/src/java/org/jpl7/test/junit/DataManagement.java b/packages/jpl/src/java/org/jpl7/test/junit/DataManagement.java index e01c8a5..febf7e5 100644 --- a/packages/jpl/src/java/org/jpl7/test/junit/DataManagement.java +++ b/packages/jpl/src/java/org/jpl7/test/junit/DataManagement.java @@ -30,7 +30,7 @@ public static void setUp() { setUpClass(); - Query.hasSolution(String.format("consult('%s/test_quoted_module.pl')", test_dir)); // only because we call e.g. jpl_pl_syntax/1 below + Query.hasSolution(String.format("consult('%s/test_quoted_module.pl')", test_dir)); // .pl file to be used } @@ -62,7 +62,7 @@ name = "existence_error(procedure, '/'(pepe, 1))"; // name = "'/'(pepe, 1)"; name = "'$c_call_prolog'"; - t = Util.textToTerm(name); + t = Term.textToTerm(name); assertEquals("matching text-term", name, t.toString()); } @@ -166,7 +166,7 @@ public void testUtilListToTermArray1() { String goal = "T = [a,b,c]"; Term list = Query.oneSolution(goal).get("T"); - Term[] array = Util.listToTermArray(list); + Term[] array = Term.listToTermArray(list); assertTrue(array[2].isAtom() && array[2].name().equals("c")); } @@ -174,7 +174,7 @@ public void testTermToTermArray1() { String goal = "T = [a,b,c]"; Term list = Query.oneSolution(goal).get("T"); - Term[] array = list.toTermArray(); + Term[] array = list.listToTermArray(); assertTrue(array[2].isAtom() && array[2].name().equals("c")); } @@ -183,7 +183,7 @@ @Test public void testTextToTerm1() { String text = "fred(B,p(A))"; - Term t = Util.textToTerm(text); + Term t = Term.textToTerm(text); assertTrue("Util.textToTerm() converts \"fred(B,p(A))\" to a corresponding Term", t.hasFunctor("fred", 2) && t.arg(1).isVariable() && t.arg(1).name().equals("B") && t.arg(2).hasFunctor("p", 1) && t.arg(2).arg(1).isVariable() @@ -196,9 +196,9 @@ public void testTextToTerm2() { String text1 = "fred(?,2,?)"; String text2 = "[first(x,y),A]"; - Term plist = Util.textToTerm(text2); - Term[] ps = plist.toTermArray(); - Term t = Util.textToTerm(text1).putParams(ps); + Term plist = Term.textToTerm(text2); + Term[] ps = plist.listToTermArray(); + Term t = Term.textToTerm(text1).putParams(ps); assertTrue("fred(?,2,?) .putParams( [first(x,y),A] )", t.hasFunctor("fred", 3) && t.arg(1).hasFunctor("first", 2) && t.arg(1).arg(1).hasFunctor("x", 0) && t.arg(1).arg(2).hasFunctor("y", 0) && t.arg(2).hasFunctor(2, 0) && t.arg(3).isVariable() diff --git a/packages/jpl/src/java/org/jpl7/test/junit/ListTest.java b/packages/jpl/src/java/org/jpl7/test/junit/ListTest.java index 21dd1c3..51bf393 100644 --- a/packages/jpl/src/java/org/jpl7/test/junit/ListTest.java +++ b/packages/jpl/src/java/org/jpl7/test/junit/ListTest.java @@ -2,8 +2,6 @@ import org.jpl7.*; import org.jpl7.Integer; -import org.jpl7.fli.Prolog; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; @@ -51,76 +49,26 @@ /////////////////////////////////////////////////////////////////////////////// + private static Term[] terms_pair_integers = new Term[]{new Integer(1), new Integer(2)}; + private static Term[] terms_integers = new Term[]{new Integer(1), new Integer(2), new Integer(3)}; + private static Term[] terms_atoms = new Term[]{new Atom("a"), new Atom("b"), new Atom("c")}; + + // Several list terms of numbers + private static Term list_empty = Term.textToTerm("[]"); + private static Term list_unit = Term.textToTerm("[1]"); + private static Term list_unit_complex = Term.textToTerm("[[1,2,3]]"); + private static Term list_simple = Term.textToTerm("[1,2,3]"); + private static Term list_complex = Term.textToTerm("[1, [1,2,3], 3]"); + + private static Term[] lists_many = new Term[]{list_empty, list_unit, list_unit_complex, list_simple, list_complex}; + private static Term[] lists_many_noempty = new Term[]{list_unit, list_unit_complex, list_simple, list_complex}; + + /////////////////////////////////////////////////////////////////////////////// // TESTS /////////////////////////////////////////////////////////////////////////////// - - @Test - public void testArrayToList1() { - Term l2 = Util.termArrayToList( - new Term[]{new Atom("a"), new Atom("b"), new Atom("c"), new Atom("d"), new Atom("e")}); - Query q9 = new Query(new Compound("append", new Term[]{new Variable("Xs"), new Variable("Ys"), l2})); - assertTrue("append(Xs,Ys,[a,b,c,d,e]) has 6 solutions", q9.allSolutions().length == 6); - } - - @Test - public void testArrayToList2() { - String goal = "append(Xs,Ys,[a,b,c,d,e])"; - assertTrue(goal + " has 6 solutions", Query.allSolutions(goal).length == 6); - } - - @Test - public void testArrayToList3() { - final String[] expectedSolutions = { "a", "b", "c", "d", "e"}; - - Term l2 = Util.termArrayToList( - new Term[] { new Atom("a"), new Atom("b"), new Atom("c"), new Atom("d"), new Atom("e") }); - Query query = new Query(new Compound("member", new Term[] { new Variable("X"), l2 })); - - Map[] sol = query.allSolutions(); - for (int i = 0; i < sol.length; i++) { - assertEquals(expectedSolutions[i], sol[i].get("X").toString()); - } - } - - @Test - public void testArrayToList4() { - final String[] expectedSolutionsX = { "[]", "[a]", "[a, b]", "[a, b, c]"}; - final String[] expectedSolutionsY = { "[a, b, c]", "[b, c]", "[c]", "[]"}; - - Term l2 = Util.termArrayToList( - new Term[] { new Atom("a"), new Atom("b"), new Atom("c") }); - Query query = new Query(new Compound("append", new Term[] { new Variable("X"), new Variable("Y"), l2 })); - - Map[] sol = query.allSolutions(); - for (int i = 0; i < sol.length; i++) { - - String ListX = Arrays.toString(Util.atomListToStringArray(sol[i].get("X"))); - String ListY = Arrays.toString(Util.atomListToStringArray(sol[i].get("Y"))); - - - assertEquals("Bad X in append(X, Y, [a, b, c])", expectedSolutionsX[i], ListX); - assertEquals("Bad Y in append(X, Y, [a, b, c])", expectedSolutionsY[i], ListY); - - } - } - - - - - - - - @Test - public void testLength1() { - Query q5 = new Query(new Compound("length", new Term[]{new Variable("Zs"), new Integer(2)})); - Term zs = q5.oneSolution().get("Zs"); - assertTrue("length(Zs,2) binds Zs to a list of two distinct variables " + zs.toString(), - zs.isListPair() && zs.arg(1).isVariable() && zs.arg(2).isListPair() && zs.arg(2).arg(1).isVariable() - && zs.arg(2).arg(2).isListNil() && !zs.arg(1).name().equals(zs.arg(2).arg(1).name())); - } @Test public void testListNil1() { @@ -134,6 +82,71 @@ } } + + @Test + public void testListNil2() { + Term x; + + x = Query.oneSolution("X = []").get("X"); + assertTrue("term should be empty list", x.isListNil()); + assertTrue("Util.isList on empty list", Term.isList(x)); + assertTrue("term is not a ListPair", !x.isListPair()); + + x = Query.oneSolution("X = [1, 2, 3]").get("X"); + assertTrue("term should NOT be empty list", !x.isListNil()); + assertTrue("Util.isList on non-empty list", Term.isList(x)); + assertTrue("term is not a ListPair", x.isListPair()); + } + + @Test + public void testIsList() { + final String[] options = { "[]", "[1]", "[1,2,3]", "[1, [a, b, c], 2]", "[[1,2,3]]"}; + + Term x; + for (String opt : options) { + x = Query.oneSolution(String.format("X = %s", opt)).get("X"); + assertTrue("term should be empty list - Util", Term.isList(x)); + assertTrue("term should be empty list - Util", x.isList()); + } + } + + + + @Test + public void testIsPairList() { + final String[] options = { "[]", "[1]", "[1,2,3]", "[1, [a, b, c], 2]", "[[1,2,3]]"}; + + Term t; + for (Term t2 : lists_many_noempty) { + String msg = String.format("term %s should be a pair list", t2.toString()); + assertTrue(msg, t2.isListPair()); + } + + t = new Compound(JPL.LIST_PAIR, terms_pair_integers); + assertTrue("term is a pair list (even though second arg is not a list)", t.isListPair()); + + + assertTrue("empty list term is not a list pair", !JPL.LIST_NIL.isListPair()); + + t = new Compound(JPL.LIST_PAIR, terms_integers); + assertTrue("term is not a pair list, has more than two arguments", !t.isListPair()); + + t = new Compound("hello", terms_integers); + assertTrue("term is not a pair list, not JPL.PAIR_LIST functor", !t.isListPair()); + + } + + @Test + public void testIsPairList2() { + Term t = new Compound(JPL.LIST_PAIR, + new Term[]{new Integer(1), new Integer(2)}); + + String msg = String.format("term %s should be a pair list", t.toString()); + assertTrue(msg, t.isListPair()); + + assertEquals("[1, 2]", t.toString()); + } + @Test public void testListCons1() { Term x = Query.oneSolution("X = [a]").get("X"); @@ -142,6 +155,83 @@ } else { assertTrue("list constructor is [|]/2", x.isCompound() && x.name().equals("[|]")); } + } + + + + + + @Test + public void testArrayToList1() { + Term l = Term.termArrayToList( + new Term[]{new Atom("a"), new Atom("b"), new Atom("c"), + new Atom("d"), new Atom("e")}); + Query q = new Query(new Compound("append", + new Term[]{new Variable("Xs"), new Variable("Ys"), l})); + + assertTrue("append(Xs,Ys,[a,b,c,d,e]) has 6 solutions", q.allSolutions().length == 6); + } + + + @Test + public void testArrayToList2() { + final String[] expectedSolutions = { "a", "b", "c", "d", "e"}; + + Term l = Term.termArrayToList( + new Term[] { new Atom("a"), new Atom("b"), new Atom("c"), + new Atom("d"), new Atom("e") }); + Query query = new Query(new Compound("member", + new Term[] { new Variable("X"), l })); + + Map[] sol = query.allSolutions(); + for (int i = 0; i < sol.length; i++) { + assertEquals(expectedSolutions[i], sol[i].get("X").toString()); + } + } + + @Test + public void testArrayToList3() { + final String[] expectedSolutionsX = { "[]", "[a]", "[a, b]", "[a, b, c]"}; + final String[] expectedSolutionsY = { "[a, b, c]", "[b, c]", "[c]", "[]"}; + + Term l = Term.termArrayToList( + new Term[] { new Atom("a"), new Atom("b"), new Atom("c") }); + Query q = new Query(new Compound("append", // append(X, Y, [a, b, c]) + new Term[] { new Variable("X"), new Variable("Y"), l })); + + Map[] sol = q.allSolutions(); + for (int i = 0; i < sol.length; i++) { + + String ListX = Arrays.toString(Term.atomListToStringArray(sol[i].get("X"))); + String ListY = Arrays.toString(Term.atomListToStringArray(sol[i].get("Y"))); + + + assertEquals("Bad X in append(X, Y, [a, b, c])", expectedSolutionsX[i], ListX); + assertEquals("Bad Y in append(X, Y, [a, b, c])", expectedSolutionsY[i], ListY); + + } + } + + + @Test + public void testStringToList() { + String goal = "append(Xs,Ys,[a,b,c,d,e])"; + assertTrue(goal + " has 6 solutions", Query.allSolutions(goal).length == 6); + } + + + + + + @Test + public void testLength1() { + Query q5 = new Query(new Compound("length", new Term[]{new Variable("Zs"), new Integer(2)})); + Term zs = q5.oneSolution().get("Zs"); + assertTrue("length(Zs,2) binds Zs to a list of two distinct variables " + zs.toString(), + zs.isListPair() && zs.arg(1).isVariable() && + zs.arg(2).isListPair() && zs.arg(2).arg(1).isVariable() + && zs.arg(2).arg(2).isListNil() && + !zs.arg(1).name().equals(zs.arg(2).arg(1).name())); } @Test @@ -188,4 +278,30 @@ // } + @Test + public void test_textToTerm_and_toString() { + final String[] options = { "[]", "[1,2,3]", "[1]", "[1,g(2,3,5),[1,2,3],abc,[1],a,[],b]", "[[1,2,3]]" }; + final String[] options2 = { "[]", "[1, 2, 3]", "[1]", "[1, g(2, 3, 5), [1, 2, 3], abc, [1], a, [], b]", "[[1, 2, 3]]" }; + + Term t; + Term s; + String msg; + String opt, opt2; + for (int i = 0; i < options.length; i++) { + opt = options[i]; + msg = String.format("test Term.textToTerm on: %s", opt); + + t = Term.textToTerm(opt); + s = Query.oneSolution(String.format("X = %s", opt)).get("X"); + assertTrue(msg, t.isList()); + assertEquals(t, s); + + opt2 = options2[i]; + msg = String.format("test Term.toString() on: %s", opt); + assertEquals(msg, opt2, s.toString()); + + } + } + + } diff --git a/packages/jpl/src/java/org/jpl7/test/junit/QueryBuilder.java b/packages/jpl/src/java/org/jpl7/test/junit/QueryBuilder.java index aa057b5..49dd107 100644 --- a/packages/jpl/src/java/org/jpl7/test/junit/QueryBuilder.java +++ b/packages/jpl/src/java/org/jpl7/test/junit/QueryBuilder.java @@ -55,7 +55,7 @@ @Test public void testTerm1() { - Term args = Util.textToTerm("[1,2,3,4,5]"); + Term args = Term.textToTerm("[1,2,3,4,5]"); Term t = new Compound("member", new Term[] { new Integer(1), args } ); Query q = new Query(t); assertTrue("Query should have succeded, but it did not!", q.hasSolution()); @@ -99,7 +99,7 @@ @Test public void testString3() { - Term[] args = new Term[] { new Integer(1), Util.textToTerm("[1,2,3,4,5]") }; + Term[] args = new Term[] { new Integer(1), Term.textToTerm("[1,2,3,4,5]") }; Query q = new Query("member(?, ?)", args); assertTrue("Query should have succeded, but it did not!", q.hasSolution()); @@ -107,7 +107,7 @@ @Test public void testString4() { - Term[] args = new Term[] { new Integer(1), Util.textToTerm("[1,2,3,4,5]") }; + Term[] args = new Term[] { new Integer(1), Term.textToTerm("[1,2,3,4,5]") }; Query q = new Query("member", args); assertTrue("Query should have succeded, but it did not!", q.hasSolution()); @@ -154,7 +154,7 @@ // Error in number of placeholder matching arguments (too many terms) @Test public void testStringErr3() { - Term[] args = new Term[] { new Integer(1), Util.textToTerm("[1,2,3,4,5]") }; + Term[] args = new Term[] { new Integer(1), Term.textToTerm("[1,2,3,4,5]") }; try { Query q = new Query("member(?, ?, ?)", args); @@ -170,7 +170,7 @@ // Error in number of placeholder matching arguments (too many terms) @Test public void testStringErr4() { - Term[] args = new Term[] { new Integer(1), Util.textToTerm("[1,2,3,4,5]") }; + Term[] args = new Term[] { new Integer(1), Term.textToTerm("[1,2,3,4,5]") }; try { Query q = new Query("member(?)", args); diff --git a/packages/pengines/pengines_io.pl b/packages/pengines/pengines_io.pl index 1cbda43..4ead632 100644 --- a/packages/pengines/pengines_io.pl +++ b/packages/pengines/pengines_io.pl @@ -529,8 +529,15 @@ list_clauses([]). list_clauses([H|T]) :- - portray_clause(H), + ( system_undefined(H) + -> true + ; portray_clause(H) + ), list_clauses(T). + +system_undefined((undefined :- tnot(undefined))). +system_undefined((answer_count_restraint :- tnot(answer_count_restraint))). +system_undefined((radial_restraint :- tnot(radial_restraint))). dict_bindings(Dict, Bindings) :- dict_pairs(Dict, _Tag, Pairs), diff --git a/packages/semweb/doc/rdfdb.md b/packages/semweb/doc/rdfdb.md index 53ec4d6..31770d7 100644 --- a/packages/semweb/doc/rdfdb.md +++ b/packages/semweb/doc/rdfdb.md @@ -70,7 +70,6 @@ * [[rdf_retractall/3]] * [[rdf_retractall/4]] * [[rdf_update/4]] - * [[rdf_update/5]] ## Update view, transactions and snapshots {#semweb-update-view} diff --git a/packages/semweb/rdf11.pl b/packages/semweb/rdf11.pl index 26ae83f..c30b0ef 100644 --- a/packages/semweb/rdf11.pl +++ b/packages/semweb/rdf11.pl @@ -364,7 +364,7 @@ %! rdf_update(+S, +P, +O, ++Action) is det. %! rdf_update(+S, +P, +O, +G, ++Action) is det. % -% Replaces one of the three fields on the matching triples +% Replaces one of the three (four) fields on the matching triples % depending on Action: % % * subject(Resource) @@ -376,14 +376,14 @@ % literal(Value). % * graph(Graph) % Moves the triple from its current named graph to Graph. -% This only works with rdf_update/4 and will throw an error when -% used with rdf_update/3. -% -% The argument matching the action must be ground. If this -% argument is equivalent to the current value, no action is -% performed. Otherwise, the requested action is performed on all -% matching triples. For example, all resources typed `rdfs:Class` -% can be changed to `owl:Class` using +% This only works with rdf_update/5 and throws an error when +% used with rdf_update/4. +% +% The argument matching Action must be ground. If this argument is +% equivalent to the current value, no action is performed. Otherwise, +% the requested action is performed on all matching triples. For +% example, all resources typed `rdfs:Class` can be changed to +% `owl:Class` using % % ``` % ?- rdf_update(_, rdf:type, rdfs:'Class', diff --git a/packages/semweb/rdf_db.pl b/packages/semweb/rdf_db.pl index 59cf290..cb1845b 100644 --- a/packages/semweb/rdf_db.pl +++ b/packages/semweb/rdf_db.pl @@ -567,9 +567,10 @@ % useful to remove all triples coming from a loaded file. See also % rdf_unload/1. -%! rdf_update(+Subject, +Predicate, +Object, +Action) is det. -% -% Replaces one of the three fields on the matching triples +%! rdf_update(+Subject, +Predicate, +Object, ++Action) is det. +%! rdf_update(+Subject, +Predicate, +Object, +Graph, ++Action) is det +% +% Replaces one of the three (four) fields on the matching triples % depending on Action: % % * subject(Resource) @@ -581,10 +582,8 @@ % literal(Value). % * graph(Graph) % Moves the triple from its current named graph to Graph. - -%! rdf_update(+Subject, +Predicate, +Object, +Graph, +Action) is det -% -% As rdf_update/4 but allows for specifying the graph. +% This only works with rdf_update/5 and throws an error when +% used with rdf_update/4. /******************************* diff --git a/packages/sgml/parser.c b/packages/sgml/parser.c index bb2a0c1..8f1d402 100644 --- a/packages/sgml/parser.c +++ b/packages/sgml/parser.c @@ -3,8 +3,9 @@ Author: Jan Wielemaker E-mail: J.Wielemaker@vu.nl WWW: http://www.swi-prolog.org - Copyright (c) 2000-2015, University of Amsterdam + Copyright (c) 2000-2020, University of Amsterdam VU University Amsterdam + CWI, Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without @@ -2727,8 +2728,8 @@ validate_completeness(dtd_parser *p, sgml_environment *env) { if ( !complete(env) ) { wchar_t buf[MAXNMLEN+50]; - - swprintf(buf, MAXNMLEN+50, L"Incomplete element: <%s>", + buf[MAXNMLEN+49] = 0; + swprintf(buf, MAXNMLEN+49, L"Incomplete element: <%s>", env->element->name->name); gripe(p, ERC_VALIDATE, buf); /* TBD: expected */ @@ -5481,12 +5482,13 @@ static wchar_t * format_location(wchar_t *s, size_t len, dtd_srcloc *l) { int first = TRUE; - wchar_t *e = &s[len]; - + wchar_t *e = &s[len-1]; + + assert(len > 0); if ( !l || l->type == IN_NONE || len == 0 ) return s; - e[-1] = L'\0'; + *e = L'\0'; for( ; l && l->type != IN_NONE; l = l->parent, first = FALSE ) { if ( !first ) @@ -5587,6 +5589,7 @@ int dtdmode = FALSE; void *freeme = NULL; + buf[MAX_MESSAGE_LEN] = 0; va_start(args, e); memset(&error, 0, sizeof(error)); diff --git a/packages/swipl-win/Preferences.cpp b/packages/swipl-win/Preferences.cpp index a41216b..4fd3a34 100644 --- a/packages/swipl-win/Preferences.cpp +++ b/packages/swipl-win/Preferences.cpp @@ -51,10 +51,12 @@ /** get configured values, with reasonable defaults */ Preferences::Preferences(QObject *parent) : - QSettings("SWI-Prolog", "pqConsole", parent) + QSettings("swi-prolog", "pqConsole", parent) { qDebug() << "Loading preferences from " << fileName(); - console_font = value("console_font", QFont("courier", 12)).value(); + QFont default_font("Monospace"); + default_font.setStyleHint(QFont::Monospace); + console_font = value("console_font", default_font).value(); wrapMode = static_cast(value("wrapMode", ConsoleEditBase::WidgetWidth).toInt()); console_out_fore = value("console_out_fore", 0).toInt(); diff --git a/packages/xpce/Defaults.user b/packages/xpce/Defaults.user index 30908eb..7f57a14 100644 --- a/packages/xpce/Defaults.user +++ b/packages/xpce/Defaults.user @@ -76,9 +76,15 @@ ! to use tabs (@on). PceEmacs detects body_indentation and ! indent_tabs from the first clause when editing an existing file. -!emacs_prolog_mode.body_indentation: 4 -!emacs_prolog_mode.cond_indentation: 4 -!emacs_prolog_mode.indent_tabs: @off +!emacs_prolog_mode.body_indentation: 4 +!emacs_prolog_mode.cond_indentation: 4 +!emacs_prolog_mode.indent_tabs: @off + +! How to add a new set of dependencies for ->update_dependencies +! (^c^d). Possible values are autoload/1, autoload/2, use_module/1 +! and use_module/2. + +!emacs_prolog_mode.dependency_directive: autoload/2 ! Comment column for M-; This may be refined by mode (e.g. emacs_prolog_mode, ! etc.) diff --git a/packages/xpce/INFO b/packages/xpce/INFO deleted file mode 100644 index 85eda34..0000000 --- a/packages/xpce/INFO +++ /dev/null @@ -1,75 +0,0 @@ - XPCE: a portable GUI toolkit - ============================ - -Author: -======= - - Jan Wielemaker jan@swi.psy.uva.nl - Anjo Anjewierden anjo@swi.psy.uva.nl - SWI, - University of Amsterdam - Roetersstraat 15 - 1018 WB Amsterdam - The Netherlands - http://www.swi.psy.uva.nl - -URL: -==== - - xpce-bugs@swi.psy.uva.nl Bug reports - xpce-request@swi.psy.uva.nl General info and mailing list - xpce@swi.psy.uva.nl Mailing list - http://swi-prolog.org/packages/xpce Project Home Page - ftp://swi.psy.uva.nl/xpce Various public resources - -OS Platforms: -============= - - Unix/X11 (most brands, including 64-bit platforms) - Win32 (Windows 95/98/ME, NT/2000/XP, Intel) - -Hosting languages -================= - - SWI-Prolog http://www.swi-prolog.org - - Ports to the following have existed and are in not too bad - shape. - - Quintus Prolog http://www.sics.se/quintus/ - SICStus Prolog http://www.sics.se/ - - Considering the costs of maintaining portable Prolog - code given the current status of the standard, we will only - produce versions for other Prolog systems if this situation - improves or we get hold of sufficient resources to maintain - portability. - -Documentation -============= - - # Programming in XPCE/Prolog - Userguide to get you started. Explains the interface, object - model as well as creating new classes. Provides `technique' - sections, explaining how commonly encountered problems may - be solved using XPCE. - - # Course Notes - Document explaining roughly the same material as ``Programming - in XPCE/Prolog'' in a condensed form. - - # Reference Manual - Available using the built-in online help-system. Includes - various fully dynamic viewpoints to the reference material. - - -Licenses -======== - - * XPCE is distributed as free software with sufficient escapes - to generate non-free applications. The license conditions are - in the source-files. An overall discussion on the license - issues can be found with SWI-Prolog, which is distributed under - exactly these conditions. For details, see: - - http://www.swi-prolog.org/license.html diff --git a/packages/xpce/INSTALL.md b/packages/xpce/INSTALL.md deleted file mode 100644 index 99fb56d..0000000 --- a/packages/xpce/INSTALL.md +++ /dev/null @@ -1,232 +0,0 @@ -# Installing XPCE/Prolog from source - -Normally, xpce is provided as `packages/xpce` in the SWI-Prolog sources. -It is build as part of SWI-Prolog. See -http://www.swi-prolog.org/build/index.txt for building SWI-Prolog and -xpce. - -The documentation below is in part out of date, but left as a reference. -Configuration, compilation and installation is based on the GNU autoconf -package. - -## Required tools - - - GNU-Make - Non-gnu versions of make will fail. - - - GCC or clang - Necessary on most machines, though you might get away with - another ANSI C compiler. - - - libXpm - The X11 XPM (XPixMap) format libary for handling coloured and - masked images. For any popular Unix platform you should be - able to find this package. For the Windows built we provide - a port at https://github.com/SWI-Prolog/libXpm.git - - - libjpeg - The JPEG group library for handling JPEG images. - - - Freetype - Not really required, but if present the system will use the - Freetype library to realise scalable and antialiased fonts. - Its presence is detected by looking for the program xft-config, - which is also used to determine compile and link flags. If - you want to build WITHOUT XFT and you have the library installed, - set the environment variable XFTCONFIG=false. - - -For all other required tools, both the GNU, BSD and System-V versions -are supposed to work properly. - -## Bluffers Installation Guide - -To install XPCE/SWI-Prolog from the sources: - - 1. Build SWI-Prolog according to the instructions and install it. - 2. Determine an installation prefix (normally use the same as for - SWI-Prolog, we use `linux' in this example). - 3. Run the following commands - - % mkdir linux - % cd linux - % ../src/configure - % make - % make install - -# Detailed Installation Guide - -We start at the SWI-Prolog installation, as this needs some special -considerations on some platforms. - - -## Choosing the build-directory - -You can place the directory for building XPCE/Prolog anywhere. A good -choice is /usr/local/src or, if you are installing as a private user, -$HOME/src. Unpack the Prolog and XPCE archives from the same directory: - - % gunzip < pl-.tar.gz | tar xvfB - - % gunzip < xpce-.tar.gz | tar xvfB - - -NOTES: - - - Some versions of tar hate reading from a pipe. In that case - use `gunzip file.tar.gz' followed by `tar xvf file.tar' to extract - the archives - - - If you are using GNU-tar, `tar zxvf file.tar.gz' is easier. - - -## Choosing a build sub-directory - -For easy cleanup or building for multiple directories, both SWI-Prolog -and XPCE are normally built in a directory next to the src directory. -The name is not important. Good examples are `sunos' `linux', etc. In -the examples below, we use `linux'. - - -## The Destination Prefix (configure --prefix=dir) - -GNU autoconf-based packages accept the flag --prefix=

to specify -the destination. The installation will use the following subdirectories: - -``` - bin For making *links* to the executables - man/man1 For installing the manual pages - include For installing the SWI-Prolog.h header file - lib/pl- For installing libs, executables, etc. - lib For installing public shared objects (if any) -``` - -The default prefix is /usr/local. If you choose another one (assume -/home/projects/bigmoney), do (if the directory structure is not yet -available): - - % cd /home/projects/bigmoney - % mkdir bin man man/man1 include lib - -Make sure the binary directory is in your PATH! - -## Preparing SWI-Prolog - - % cd pl- - % mkdir linux - % cd linux - % ../src/configure - -## Shared libraries or not? - -By default, the installation atempts to build SWI-Prolog and load xpce -as a shared object (.so file on most Unix machines) into Prolog. This -installation is better to maintain and prepares SWI-Prolog for loading -custom extensions written in C in a well supported manner. - -Now, shared libraries are used on all systems and the the static -alternative is no longer actively maintained. When porting to a new OS, -please check the shared library facilities of the SWI-Prolog plld tool -and make sure it runs on your environment. - -## Preparing XPCE - -XPCE extracts most information about the configuration from SWI-Prolog, -so it is generally much easier. Most of the configure run is simple, but -configure needs to find out where the X11 libraries and include files -are and you may have multiple. Normally, it will first try -/usr/include/X11 and /usr/lib. If you have only one version of X11 -around, generally configure will be able to find it. - -If you have multiple copies of X11 around, you have to decide which to -use. If you are compiling for local usage, use the one most of your -local packages use to improve resource sharing. Otherwise, use the one -distributed with the OS or known to be most commonly in use by your user -community. For example, our system has the MIT X11R6 libs and includes -in /usr/lib and /usr/include/X11 and the OpenWindows versions in -/usr/openwin/lib and /usr/openwin/include/X11. For local usage, we -configure without options to use the X11R6 version, used by most of the -other X11 software we run locally. For distribution, we configure using: - - --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib - -## Checking Program versions - -Just to make sure your programs are accessible and of the right version, -try: - - % swipl -v - SWI-Prolog version 5.3.9 for i686-linux - % make -v - GNU Make 3.80 - ... - % gcc -v - gcc -v - Reading specs from /usr/lib/gcc-lib/i586-suse-linux/3.3.1/specs - ... - gcc version 3.3.1 (SuSE Linux) - -Now, run configure: - - % cd xpce- - % mkdir linux - % ../src/configure - -## Building the library - -Now, make the XPCE library: - - % make xpce - -If you have configured for using shared libraries, the -fPIC flag should -be passed to the compiler. If this is not the case, please look closely -at the steps above. If all goes well, the compilation should finish with -few warnings, resulting in the library libXPCE.a - - -## Building the interface: - -With write premission to the Prolog home directory, now do the following -to make the interface. - - % make pl-itf - -This command will run either `make pl-shared' or `make pl-static', -depending on whether SWI-Prolog handles shared objects. The first builds -the pl2xpce.so shared object, and the second builds an XPCE executable -holding both the Prolog kernel and the XPCE library called `xpce'. - -Both versions will put various things in the SWI-Prolog home directory -to make XPCE known to Prolog: - - * A link to the xpce build-directory - * A swipl.rc script to register the xpce library - * A Makefile to recompile the XPCE/Prolog quick-load-files - -## Testing XPCE - -Now, if you have build for a shared object, xpce is a dynamically -loadable Prolog library, so to test it simply do: - - % swipl - ?- manpce. - -which should start the manual tools. - -## Installing the XPCE library - -Finally, to install the XPCE libraries, do: - - % make install - -Which will: - - - Create a directory xpce- in the Prolog home and - copy the Prolog libraries, manual data and other resources - into this directory. - - - In the Prolog home, make a link from xpce to xpce- - - - run `make' in the Prolog home directory to make quick-load - versions of some large and frequently used library packages. - - - in $exec_prefix/bin, make links to the xpce and xpce-client - executables diff --git a/packages/xpce/cmake/XPCESources.cmake b/packages/xpce/cmake/XPCESources.cmake index 76eb82a..378c357 100644 --- a/packages/xpce/cmake/XPCESources.cmake +++ b/packages/xpce/cmake/XPCESources.cmake @@ -193,7 +193,8 @@ javascript_mode.pl language_mode.pl latex_mode.pl logtalk_mode.pl man_mode.pl outline_mode.pl prolog_mode.pl prompt.pl rdf_mode.pl script_mode.pl server.pl sgml_mode.pl shell.pl swi_prolog.pl - text_mode.pl turtle_mode.pl window.pl yaml_mode.pl cmake_mode.pl) + text_mode.pl turtle_mode.pl window.pl yaml_mode.pl cmake_mode.pl + help_buffer.pl) set(XPCE_DATA_prolog_lib_english pce_messages.pl) @@ -386,4 +387,4 @@ set(XPCE_QLF_emacs emacs/window.pl emacs/buffer.pl emacs/application.pl emacs/buffer_menu.pl emacs/server.pl emacs/history.pl emacs/fundamental_mode.pl emacs/language_mode.pl emacs/outline_mode.pl - emacs/bookmarks.pl) + emacs/bookmarks.pl emacs/help_buffer.pl) diff --git a/packages/xpce/install-sh b/packages/xpce/install-sh deleted file mode 100755 index ab74c88..0000000 --- a/packages/xpce/install-sh +++ /dev/null @@ -1,238 +0,0 @@ -#!/bin/sh -# -# install - install a program, script, or datafile -# This comes from X11R5. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. -# - - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -tranformbasename="" -transform_arg="" -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd="" -chgrpcmd="" -stripcmd="" -rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -dst="" -dir_arg="" - -while [ x"$1" != x ]; do - case $1 in - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; - - *) if [ x"$src" = x ] - then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done - -if [ x"$src" = x ] -then - echo "install: no input file specified" - exit 1 -else - true -fi - -if [ x"$dir_arg" != x ]; then - dst=$src - src="" - - if [ -d $dst ]; then - instcmd=: - else - instcmd=mkdir - fi -else - -# Waiting for this to be detected by the "$instcmd $src $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f $src -o -d $src ] - then - true - else - echo "install: $src does not exist" - exit 1 - fi - - if [ x"$dst" = x ] - then - echo "install: no destination specified" - exit 1 - else - true - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d $dst ] - then - dst="$dst"/`basename $src` - else - true - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $mkdirprog "${pathcomp}" - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - -if [ x"$dir_arg" != x ] -then - $doit $instcmd $dst && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi -else - -# If we're going to rename the final executable, determine the name now. - - if [ x"$transformarg" = x ] - then - dstfile=`basename $dst` - else - dstfile=`basename $dst $transformbasename | - sed $transformarg`$transformbasename - fi - -# don't allow the sed command to completely eliminate the filename - - if [ x"$dstfile" = x ] - then - dstfile=`basename $dst` - else - true - fi - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Move or copy the file name to the temp name - - $doit $instcmd $src $dsttmp && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && - -# Now rename the file to the real destination. - - $doit $rmcmd -f $dstdir/$dstfile && - $doit $mvcmd $dsttmp $dstdir/$dstfile - -fi && - - -exit 0 diff --git a/packages/xpce/man/reference/class/application.doc b/packages/xpce/man/reference/class/application.doc index 06d6df6..0f36966 100644 Binary files a/packages/xpce/man/reference/class/application.doc and b/packages/xpce/man/reference/class/application.doc differ diff --git a/packages/xpce/newversion b/packages/xpce/newversion deleted file mode 100755 index ef7671a..0000000 --- a/packages/xpce/newversion +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -function confirm () -{ while true; do - echo -n "$1 " - read answer - case "$answer" in - y*) return 0 - ;; - n*) return 1 - ;; - *) - echo "Please answer yes or no" - ;; - esac - done -} - -version=`cat VERSION` -versiondate=`date +"%B %Y"` -major=`echo $version | sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` -minor=`echo $version | sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` -patch=`echo $version | sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` - -tmp=.tmp$$ - -f=src/Makefile.in -sed "s/^PCEVERSIONDATE=.*/PCEVERSIONDATE=$versiondate/" $f > $tmp -if cmp $f $tmp; then - rm -f $tmp -else - cp $tmp $f - echo "Updated PCEVERSIONDATE in $f" -fi - -f=src/h/interface.h -sed "s/^#define PCE_VERSION.*/#define PCE_VERSION "\""$version, $versiondate"\"/ $f > $tmp -if cmp $f $tmp; then - rm -f $tmp -else - cp $tmp $f - echo "Updated #define PCE_VERSION in $f" -fi - -rm -f $tmp diff --git a/packages/xpce/prolog/lib/doc/html.pl b/packages/xpce/prolog/lib/doc/html.pl index 406adcd..8734726 100644 --- a/packages/xpce/prolog/lib/doc/html.pl +++ b/packages/xpce/prolog/lib/doc/html.pl @@ -3,7 +3,7 @@ Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ - Copyright (c) 2000-2011, University of Amsterdam + Copyright (c) 2000-2020, University of Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without @@ -104,7 +104,9 @@ @br ]. element(div, A, C) --> %
- element(p, A, C). % TBD: Should force clearance of + element(p, A, C). % TBD: Should force clearance of +element(span, _, C) --> + seq(C). % shape-graphicals! element(br, _, _) --> %
[ @br, @@ -326,7 +328,8 @@ ] ). element(a, Attr, Content) --> % > ... - { memberchk(name=Label, Attr) + { memberchk(id=Label, Attr) + ; memberchk(name=Label, Attr) }, !, [ \anchor(Label, Content) @@ -360,7 +363,7 @@ % Preformatted output element(pre, _, Content) --> %
-    [ @br,
+    [ \parskip,
       \group([ \setfont(fixed, @on),
                \pre(Content)
              ]),
@@ -381,6 +384,8 @@
 element(meta, _, _) -->                 % 
     [].
 element(link, _, _) -->                 % 
+    [].
+element(script, _, _) -->               %