123 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Prolog
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Prolog
		
	
	
		
			Executable File
		
	
	
	
	
| #! /usr/local/bin/yap -L -- $*
 | |
| # .
 | |
| % -*- Mode: Prolog -*-
 | |
| 
 | |
| /**
 | |
|   * @defgroup YAPU YAPU: A Compact Unit Testing Script
 | |
|   * @ingroup YAPLibrary
 | |
|   *
 | |
|   * Unit tests are useful in testing a procedure or a function. Therefore, several
 | |
|   * Prolog implementations extend the language with support for unit testing. Examples
 | |
|   * include SWI Prolog's pl-unit, and the Logtalk unit checker. These packages
 | |
|   * include sophisticated machinery.
 | |
|   *
 | |
|   * YAPU is a simple script designed to enable large scale generation of unit tests
 | |
|   * as text files. It uses on a tab separated format, as follows:
 | |
|   *
 | |
|   * Name	Arity
 | |
|   * Arg1	Arg2	ArgN	Answer
 | |
|   * ....
 | |
|   *
 | |
|   * 
 | |
| */
 | |
| 
 | |
| :- use_module( library( lineutils ) ).
 | |
| :- use_module( library( lists ) ).
 | |
| 
 | |
| :- yap_flag(write_strings, on).
 | |
| 
 | |
| :- initialization( main ).
 | |
| 
 | |
| :- dynamic test/5, exists/2.
 | |
| 
 | |
| main :-
 | |
| 	unix(argv([Inp])),
 | |
| 	file_filter(Inp, user_output, test ).
 | |
| 
 | |
| test(Inp, "") :-
 | |
| 	Inp == [0'%|_], %0'
 | |
| 	!, fail.
 | |
| 
 | |
| test(Inp, "+") :-
 | |
| 	\+ test(_A,_Name,_N,_AN,_G),
 | |
| 	split(Inp, "	", [Name, Arity|_]), !,
 | |
| 	atom_codes(A, Name),
 | |
| 	number_codes(N, Arity),
 | |
| 	functor(G, A, N),
 | |
| 	assert(test(A,Name,N,A/N,G)).
 | |
| test(Inp, "~n") :-
 | |
| 	split(Inp, "	", ["end"|_]), !,
 | |
| 	retract(test(A,_Name,N,A/N,_G)).
 | |
| test(Inp, ".") :-
 | |
| trace,
 | |
| 	test( _, Name, Ar, _, _),
 | |
| 	fields(Inp, "	", [_|L]), !,
 | |
| 	args(L, Ar, Sol, Text, []),
 | |
| 	append([" query( ", Name, "( ( ", Text, " ) ), ( "|Sol], P1),
 | |
| 	append(P1, " ) ). ", Codes ),
 | |
| 	atom_to_term( Codes, Go, Bindings ),
 | |
| 	cmd( Go, Bindings).
 | |
| 
 | |
| args(Args, 0, Args) -->
 | |
| 	!.
 | |
| args([A|L], Ar, Args) -->
 | |
| 	add_arg(A, Ar),
 | |
| 	{ Ar1 is Ar-1 },
 | |
| 	( { Ar1 =:= 0 } -> [] ; " ) , ( " ),
 | |
| 	args(L, Ar1, Args).
 | |
| 
 | |
| add_arg(A, Ar) -->
 | |
| 	blank(A), !,
 | |
| 	{ exists(Ar, S) },
 | |
| 	S.
 | |
| add_arg(A, Ar) -->
 | |
| 	A,
 | |
| 	{ retract(exists(Ar,_)), fail ; assert(exists(Ar, A)) }.
 | |
| 
 | |
| cmd( query( Q, Sol ), Bs ) :-
 | |
| 	findall( X, catch( run(X, Q, Sol, Bs), T, X = throw( T ) ), Ls),
 | |
| 	match( Ls, Sol ).
 | |
| 
 | |
| run(Bs, Q, Sol, Bs) :-
 | |
| 	T = t( Sol ),
 | |
| 	call(Q),
 | |
| 	count_solutions( T ).
 | |
| 
 | |
| count_solutions(T) :-
 | |
| 	arg(1, T, [_|L]),
 | |
| 	( var( L ) -> true ; nb_setarg(1, T, L ) ).
 | |
| 
 | |
| % matches to a variable
 | |
| % should always be the first rule.
 | |
| match( [_|_], Any) :- var(Any), !.
 | |
| match( [], fail) :- !.
 | |
| match( [throw( Error0 )], ErrorF) :-
 | |
| 	Error0 = error(ISO0, _),
 | |
| 	ErrorF = error(ISOF, _),
 | |
| 	ISO0 =@= ISOF,
 | |
| 	!.
 | |
| match( Bs, BFs) :-
 | |
| 	length( Bs, Sz),
 | |
| 	length( BFs, Sz), !,
 | |
| 	maplist( match_bs, Bs, BFs ).
 | |
| match_bs( B0, B1 ) :-
 | |
| 	msort(B0, K0),
 | |
| 	msort(B1, K1),
 | |
| 	intersect(K0, K1, I0, I1),
 | |
| 	I0 =@= I1.
 | |
| match_bs( Bs, BFs) :-
 | |
| 	line_count( filter_input, Line ),
 | |
| 	format(user_error, 'line ~d failed: ~    nExpected ~w.~n        Got ~w.~n',
 | |
| 	       [Line, BFs, Bs] ).
 | |
| 
 | |
| intersect([], [_|_], [], []).
 | |
| intersect([_|_], [], [], []).
 | |
| intersect([B=V1|B1s], [B=V2|B2s], [V1|V1s], [V2|V2s]) :- !,
 | |
| 	intersect(B1s, B2s, V1s, V2s).
 | |
| intersect([_B1=V1|B1s], [B2=V2|B2s], V1s, V2s) :-
 | |
| 	V1 @< V2, !,
 | |
| 	intersect(B1s, [B2=V2|B2s], V1s, V2s).
 | |
| intersect([_B1=_V1|B1s], [B2=V2|B2s], V1s, V2s) :-
 | |
| 	intersect(B1s, [B2=V2|B2s], V1s, V2s).
 | |
| 
 |