/*******************************************************************
 *
 * A Lisp compiler, written in Prolog
 *
 * (builtin_lisp_functions.pl)
 *
 * (c) Neil Smith, 2001
 *
 * This program provides some built-in functionality for the 
 * Lisp compiler.  It requires that the file lisp_compiler.pl has 
 * already been successfully compiled.
 *
 *******************************************************************/



:- ensure_loaded(library(writef)).
:- use_module(library(lists)).


first(List, Result):-
	once((	List = [Result|_]
	    ;	lisp_error_description(first_not_cons, ErrMsg),
			throw(error(first_not_cons(List), context(first, ErrMsg))) )).

rest(List, Result):-
	once((	List = [_|Result]
	    ;	lisp_error_description(rest_not_cons, ErrMsg),
			throw(error(first_not_cons(List), context(first, ErrMsg)))	)).

cons(Item, List, Result):-
	Result = [Item|List].

null(Item, Result):-
		Item = []
	->	Result = t
	;	Result = [].

eq(Item1, Item2, Result):-
		Item1 == Item2
	->	Result = t
	;	Result = [].

equalp(Item1, Item2, Result):-
		Item1 = Item2
	->	Result = t
	;	Result = [].


plus(Num1, Num2, Result):-
	Result is Num1 + Num2.
minus(Num1, Num2, Result):-
	Result is Num1 - Num2.
times(Num1, Num2, Result):-
	Result is Num1 * Num2.
divide(Num1, Num2, Result):-
	Result is Num1 / Num2.


lisp_not(Boolean, Result):-
		Boolean = []
	->	Result = t
	;	Result = [].

or(Bool1, Bool2, Result):-
		once(Bool1 \= [] ; Bool2 \= [])
	->	Result = t
	;	Result = [].

and(Bool1, Bool2, Result):-
		(Bool1 \= [] , Bool2 \= [])
	->	Result = t
	;	Result = [].


lisp_apply(FunctionObject, Arguments, Result):-
		% FunctionObject = closure(FormalArgs, Body, Environment)
		FunctionObject = closure(FormalArgs, Body, ClosureEnvironment, Environment)
	->	% zip_with(FormalArgs, Arguments, [Arg, Val, binding(Arg, [Val|_])]^true, Bindings),
		maplist(make_binding, FormalArgs, Arguments, Bindings),
		append(ClosureEnvironment, Environment, ActualEnvironment),
		% apply(Body, [[Bindings|Environment], Result])
		apply(Body, [[Bindings|ActualEnvironment], Result])
	;	FunctionObject = function(FunctionName), 
		append(Arguments, [Result], ArgumentsResult),
		Function =.. [FunctionName|ArgumentsResult],
		call(Function).


extract_variable_value([Val|Vals], FoundVal, Hole):-
		var(Vals)
	->	FoundVal = Val,
		Hole = Vals
	;	extract_variable_value(Vals, FoundVal, Hole).


lisp_call(Function, Result):-
	apply(Function, [Result]).



/*
show_special:-
		setof(sv(Var, Value), special_var(Var, Value), SVs)
	->	format('Variable ~tValue~n~n'),
		maplist((sv(Var2, Value2), format('~w :~t~w~n', [Var2, Value2])), SVs)
	;	format('No special variables~n').
*/

show_special:-
	setof(sv(Var, Value), special_var(Var, Value), SVs),
	!,
	format('Variable ~tValue~20|~n~n'),
	% maplist((sv(Var2, Value2), format('~w :~t~w~n', [Var2, Value2])), SVs).
	maplist(show_one_special, SVs).
show_special:-
	format('No special variables~n').

show_one_special(sv(Var, Value)):-
	format('~w :~t~w~20|~n', [Var, Value]).