310 lines
11 KiB
Prolog
310 lines
11 KiB
Prolog
:- op(400, yfx, /).
|
|
|
|
:- module(builtins, [(=)/2, (+)/2, (**)/2, (*)/2, (-)/2, (/)/2, (/\)/2,
|
|
(\/)/2, (is)/2, (xor)/2, (div)/2, (//)/2, (rdiv)/2, (<<)/2,
|
|
(>>)/2, (mod)/2, (rem)/2, (>)/2, (<)/2, (=\=)/2, (=:=)/2,
|
|
(-)/1, (>=)/2, (=<)/2, (,)/2, (->)/2, (;)/2, (=..)/2, (==)/2,
|
|
(\==)/2, (@=<)/2, (@>=)/2, (@<)/2, (@>)/2, (=@=)/2, (\=@=)/2,
|
|
(:)/2, call_with_inference_limit/3, catch/3,
|
|
current_prolog_flag/2, set_prolog_flag/2,
|
|
setup_call_cleanup/3, throw/1, true/0, false/0]).
|
|
|
|
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
|
|
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
|
|
instructions are unchanged. */
|
|
:- op(700, fx, non_counted_backtracking).
|
|
|
|
% arithmetic operators.
|
|
:- op(700, xfx, is).
|
|
:- op(500, yfx, +).
|
|
:- op(500, yfx, -).
|
|
:- op(400, yfx, *).
|
|
:- op(200, xfy, **).
|
|
:- op(500, yfx, /\).
|
|
:- op(500, yfx, \/).
|
|
:- op(500, yfx, xor).
|
|
:- op(400, yfx, div).
|
|
:- op(400, yfx, //).
|
|
:- op(400, yfx, rdiv).
|
|
:- op(400, yfx, <<).
|
|
:- op(400, yfx, >>).
|
|
:- op(400, yfx, mod).
|
|
:- op(400, yfx, rem).
|
|
:- op(200, fy, -).
|
|
|
|
% arithmetic comparison operators.
|
|
:- op(700, xfx, >).
|
|
:- op(700, xfx, <).
|
|
:- op(700, xfx, =\=).
|
|
:- op(700, xfx, =:=).
|
|
:- op(700, xfx, >=).
|
|
:- op(700, xfx, =<).
|
|
|
|
% control.
|
|
:- op(700, xfx, =).
|
|
:- op(900, fy, \+).
|
|
:- op(700, xfx, =..).
|
|
|
|
% conditional operators.
|
|
:- op(1050, xfy, ->).
|
|
:- op(1100, xfy, ;).
|
|
|
|
% term comparison.
|
|
:- op(700, xfx, ==).
|
|
:- op(700, xfx, \==).
|
|
:- op(700, xfx, @=<).
|
|
:- op(700, xfx, @>=).
|
|
:- op(700, xfx, @<).
|
|
:- op(700, xfx, @>).
|
|
:- op(700, xfx, =@=).
|
|
:- op(700, xfx, \=@=).
|
|
|
|
% module resolution operator.
|
|
:- op(600, xfy, :).
|
|
|
|
% the maximum arity flag. needs to be replaced with current_prolog_flag(max_arity, MAX_ARITY).
|
|
max_arity(63).
|
|
|
|
% unify.
|
|
X = X.
|
|
|
|
true.
|
|
|
|
false :- '$fail'.
|
|
|
|
% flags.
|
|
|
|
current_prolog_flag(Flag, false) :- Flag == bounded, !.
|
|
current_prolog_flag(bounded, false).
|
|
current_prolog_flag(Flag, down) :- Flag == integer_rounding_function, !.
|
|
current_prolog_flag(integer_rounding_function, down).
|
|
current_prolog_flag(Flag, Value) :- Flag == double_quotes, !, '$get_double_quotes'(Value).
|
|
current_prolog_flag(double_quotes, Value) :- '$get_double_quotes'(Value).
|
|
current_prolog_flag(Flag, _) :- Flag == max_integer, !, '$fail'.
|
|
current_prolog_flag(Flag, _) :- Flag == min_integer, !, '$fail'.
|
|
current_prolog_flag(Flag, _) :-
|
|
atom(Flag),
|
|
throw(error(domain_error(prolog_flag, Flag), current_prolog_flag/2)). % 8.17.2.3 b
|
|
current_prolog_flag(Flag, _) :-
|
|
nonvar(Flag),
|
|
throw(error(type_error(atom, Flag), current_prolog_flag/2)). % 8.17.2.3 a
|
|
|
|
set_prolog_flag(Flag, Value) :-
|
|
(var(Flag) ; var(Value)),
|
|
throw(error(instantiation_error, set_prolog_flag/2)). % 8.17.1.3 a, b
|
|
set_prolog_flag(bounded, false) :- !. % 7.11.1.1
|
|
set_prolog_flag(bounded, true) :- !, '$fail'. % 7.11.1.1
|
|
set_prolog_flag(bounded, Value) :-
|
|
throw(error(domain_error(flag_value, bounded + Value), set_prolog_flag/2)). % 8.17.1.3 e
|
|
set_prolog_flag(max_integer, Value) :- integer(Value), !, '$fail'. % 7.11.1.2
|
|
set_prolog_flag(max_integer, Value) :-
|
|
throw(error(domain_error(flag_value, max_integer + Value), set_prolog_flag/2)). % 8.17.1.3 e
|
|
set_prolog_flag(min_integer, Value) :- integer(Value), !, '$fail'. % 7.11.1.3
|
|
set_prolog_flag(min_integer, Value) :-
|
|
throw(error(domain_error(flag_value, min_integer + Value), set_prolog_flag/2)). % 8.17.1.3 e
|
|
set_prolog_flag(integer_rounding_function, down) :- !. % 7.11.1.4
|
|
set_prolog_flag(integer_rounding_function, Value) :-
|
|
throw(error(domain_error(flag_value, integer_rounding_function + Value),
|
|
set_prolog_flag/2)). % 8.17.1.3 e
|
|
set_prolog_flag(double_quotes, chars) :-
|
|
!, '$set_double_quotes'(chars). % 7.11.2.5, list of one-char atoms.
|
|
set_prolog_flag(double_quotes, atom) :-
|
|
!, '$set_double_quotes'(atom). % 7.11.2.5, list of one-char atoms.
|
|
set_prolog_flag(double_quotes, Value) :-
|
|
throw(error(domain_error(flag_value, double_quotes + Value),
|
|
set_prolog_flag/2)). % 8.17.1.3 e
|
|
set_prolog_flag(Flag, _) :-
|
|
atom(Flag),
|
|
throw(error(domain_error(prolog_flag, Flag), set_prolog_flag/2)). % 8.17.1.3 d
|
|
set_prolog_flag(Flag, _) :-
|
|
throw(error(type_error(atom, Flag), set_prolog_flag/2)). % 8.17.1.3 c
|
|
|
|
% control operators.
|
|
|
|
','(G1, G2) :- '$get_b_value'(B), ','(G1, G2, B).
|
|
|
|
:- non_counted_backtracking (,)/3.
|
|
','(!, ','(G1, G2), B) :- '$set_cp'(B), ','(G1, G2, B).
|
|
','(!, !, B) :- '$set_cp'(B).
|
|
','(!, G, B) :- '$set_cp'(B), G.
|
|
','(G, ','(G2, G3), B) :- !, G, ','(G2, G3, B).
|
|
','(G, !, B) :- !, G, '$set_cp'(B).
|
|
','(G1, G2, _) :- G1, G2.
|
|
|
|
;(G1, G2) :- '$get_b_value'(B), ;(G1, G2, B).
|
|
|
|
:- non_counted_backtracking (;)/3.
|
|
;(G1, G4, B) :- compound(G1), G1 = ->(G2, G3), (G2 -> G3 ; '$set_cp'(B), G4).
|
|
;(G1, G2, B) :- G1 == !, '$set_cp'(B), call(G2).
|
|
;(G1, G2, B) :- G2 == !, call(G2), '$set_cp'(B).
|
|
;(G, _, _) :- G.
|
|
;(_, G, _) :- G.
|
|
|
|
G1 -> G2 :- '$get_b_value'(B), ->(G1, G2, B).
|
|
|
|
:- non_counted_backtracking (->)/3.
|
|
->(G1, G2, B) :- G2 == !, call(G1), !, '$set_cp'(B).
|
|
->(G1, G2, B) :- call(G1), '$set_cp'(B), call(G2).
|
|
|
|
% arg.
|
|
|
|
/* Here is the old, SWI Prolog-imitative arg/3. It has been superseded by an ISO Prolog
|
|
* compliant arg/3 implemented in Rust.
|
|
|
|
arg(N, Functor, Arg) :- var(N), !, functor(Functor, _, Arity), arg_(N, 1, Arity, Functor, Arg).
|
|
arg(N, Functor, Arg) :- integer(N), !, functor(Functor, _, Arity), '$get_arg'(N, Functor, Arg).
|
|
arg(N, Functor, Arg) :- throw(error(type_error(integer, N), arg/3)).
|
|
|
|
arg_(N, N, N, Functor, Arg) :- !, '$get_arg'(N, Functor, Arg).
|
|
arg_(N, N, Arity, Functor, Arg) :- '$get_arg'(N, Functor, Arg).
|
|
arg_(N, N0, Arity, Functor, Arg) :- N0 < Arity, N1 is N0 + 1, arg_(N, N1, Arity, Functor, Arg).
|
|
|
|
*/
|
|
|
|
% univ.
|
|
|
|
\+ Goal :- call(Goal), !, false.
|
|
\+ _.
|
|
|
|
univ_errors(Term, List, N) :-
|
|
'$skip_max_list'(N, -1, List, R),
|
|
( var(R) -> ( var(Term), throw(error(instantiation_error, (=..)/2)) % 8.5.3.3 a)
|
|
; true )
|
|
; R \== [] -> throw(error(type_error(list, List), (=..)/2)) % 8.5.3.3 b)
|
|
; List = [H|T] -> ( var(H), var(Term), % R == [] => List is a proper list.
|
|
throw(error(instantiation_error, (=..)/2)) % 8.5.3.3 c)
|
|
; T \== [], nonvar(H), \+ atom(H),
|
|
throw(error(type_error(atom, H), (=..)/2)) % 8.5.3.3 d)
|
|
; compound(H), T == [],
|
|
throw(error(type_error(atomic, H), (=..)/2)) % 8.5.3.3 e)
|
|
; var(Term), max_arity(M), N - 1 > M,
|
|
throw(error(representation_error(max_arity), (=..)/2)) % 8.5.3.3 g)
|
|
; true )
|
|
; var(Term) -> throw(error(domain_error(non_empty_list, List), (=..)/2)) % 8.5.3.3 f)
|
|
; true ).
|
|
|
|
Term =.. List :- univ_errors(Term, List, N), univ_worker(Term, List, N).
|
|
|
|
univ_worker(Term, List, _) :- atomic(Term), !, List = [Term].
|
|
univ_worker(Term, [Name|Args], N) :-
|
|
var(Term), !,
|
|
Arity is N-1,
|
|
functor(Term, Name, Arity),
|
|
'$get_args'(Args, Term, 1, Arity).
|
|
univ_worker(Term, List, _) :-
|
|
functor(Term, Name, Arity),
|
|
'$get_args'(Args, Term, 1, Arity),
|
|
List = [Name|Args].
|
|
|
|
'$get_args'(Args, _, _, 0) :-
|
|
!, Args = [].
|
|
'$get_args'([Arg], Func, N, N) :-
|
|
!, arg(N, Func, Arg).
|
|
'$get_args'([Arg|Args], Func, I0, N) :-
|
|
arg(I0, Func, Arg),
|
|
I1 is I0 + 1,
|
|
'$get_args'(Args, Func, I1, N).
|
|
|
|
% setup_call_cleanup.
|
|
|
|
setup_call_cleanup(S, G, C) :- '$get_b_value'(B),
|
|
S, '$set_cp_by_default'(B), '$get_current_block'(Bb),
|
|
( '$call_with_default_policy'(var(C)) -> throw(error(instantiation_error, setup_call_cleanup/3))
|
|
; '$call_with_default_policy'(scc_helper(C, G, Bb)) ).
|
|
|
|
:- non_counted_backtracking scc_helper/3.
|
|
scc_helper(C, G, Bb) :-
|
|
'$get_cp'(Cp), '$install_scc_cleaner'(C, NBb), call(G),
|
|
( '$check_cp'(Cp) -> '$reset_block'(Bb),
|
|
'$call_with_default_policy'(run_cleaners_without_handling(Cp))
|
|
; '$call_with_default_policy'(true)
|
|
; '$reset_block'(NBb), '$fail').
|
|
scc_helper(_, _, Bb) :-
|
|
'$reset_block'(Bb), '$get_ball'(Ball),
|
|
'$call_with_default_policy'(run_cleaners_with_handling),
|
|
'$erase_ball',
|
|
'$call_with_default_policy'(throw(Ball)).
|
|
scc_helper(_, _, _) :-
|
|
'$get_cp'(Cp),
|
|
'$call_with_default_policy'(run_cleaners_without_handling(Cp)),
|
|
'$fail'.
|
|
|
|
:- non_counted_backtracking run_cleaners_with_handling/0.
|
|
run_cleaners_with_handling :-
|
|
'$get_scc_cleaner'(C), '$get_level'(B),
|
|
'$call_with_default_policy'(catch(C, _, true)),
|
|
'$set_cp_by_default'(B),
|
|
'$call_with_default_policy'(run_cleaners_with_handling).
|
|
run_cleaners_with_handling :-
|
|
'$restore_cut_policy'.
|
|
|
|
:- non_counted_backtracking run_cleaners_without_handling/1.
|
|
run_cleaners_without_handling(Cp) :-
|
|
'$get_scc_cleaner'(C), '$get_level'(B), C, '$set_cp_by_default'(B),
|
|
'$call_with_default_policy'(run_cleaners_without_handling(Cp)).
|
|
run_cleaners_without_handling(Cp) :-
|
|
'$set_cp_by_default'(Cp), '$restore_cut_policy'.
|
|
|
|
% call_with_inference_limit
|
|
|
|
call_with_inference_limit(G, L, R) :-
|
|
'$get_current_block'(Bb),
|
|
'$get_b_value'(B),
|
|
'$call_with_default_policy'(call_with_inference_limit(G, L, R, Bb, B)),
|
|
'$remove_call_policy_check'(B).
|
|
|
|
:- non_counted_backtracking call_with_inference_limit/5.
|
|
call_with_inference_limit(G, L, R, Bb, B) :-
|
|
'$install_new_block'(NBb),
|
|
'$install_inference_counter'(B, L, Count0),
|
|
call(G),
|
|
'$inference_level'(R, B),
|
|
'$remove_inference_counter'(B, Count1),
|
|
'$call_with_default_policy'(is(Diff, L - (Count1 - Count0))),
|
|
'$call_with_default_policy'(end_block(B, Bb, NBb, Diff)).
|
|
call_with_inference_limit(_, _, R, Bb, B) :-
|
|
'$reset_block'(Bb),
|
|
'$remove_inference_counter'(B, _),
|
|
( '$get_ball'(Ball), '$get_level'(Cp), '$set_cp_by_default'(Cp)
|
|
; '$remove_call_policy_check'(B), '$fail' ),
|
|
'$erase_ball',
|
|
'$call_with_default_policy'(handle_ile(B, Ball, R)).
|
|
|
|
:- non_counted_backtracking end_block/4.
|
|
end_block(_, Bb, NBb, L) :-
|
|
'$clean_up_block'(NBb),
|
|
'$reset_block'(Bb).
|
|
end_block(B, Bb, NBb, L) :-
|
|
'$install_inference_counter'(B, L, _),
|
|
'$reset_block'(NBb),
|
|
'$fail'.
|
|
|
|
:- non_counted_backtracking handle_ile/3.
|
|
handle_ile(B, inference_limit_exceeded(B), inference_limit_exceeded) :- !.
|
|
handle_ile(B, E, _) :-
|
|
'$remove_call_policy_check'(B),
|
|
'$call_with_default_policy'(throw(E)).
|
|
|
|
% exceptions.
|
|
|
|
catch(G,C,R) :- '$get_current_block'(Bb), '$call_with_default_policy'(catch(G,C,R,Bb)).
|
|
|
|
:- non_counted_backtracking catch/4.
|
|
catch(G,C,R,Bb) :-
|
|
'$install_new_block'(NBb), call(G),
|
|
'$call_with_default_policy'(end_block(Bb, NBb)).
|
|
catch(G,C,R,Bb) :-
|
|
'$reset_block'(Bb),
|
|
'$get_ball'(Ball),
|
|
'$call_with_default_policy'(handle_ball(Ball, C, R)).
|
|
|
|
:- non_counted_backtracking end_block/2.
|
|
end_block(Bb, NBb) :- '$clean_up_block'(NBb), '$reset_block'(Bb).
|
|
end_block(Bb, NBb) :- '$reset_block'(NBb), '$fail'.
|
|
|
|
:- non_counted_backtracking handle_ball/3.
|
|
handle_ball(C, C, R) :- !, '$erase_ball', call(R).
|
|
handle_ball(_, _, _) :- '$unwind_stack'.
|
|
|
|
throw(Ball) :- '$set_ball'(Ball), '$unwind_stack'.
|