;;;Some tools for defining vaidators for Yenta parameters.
;;;Validators are used to check the correctness of values for parameters.
;;;Validators should be procedures of one argument that return #t, #f or error-string that explains
;;;why an argument fails to satisfy the requirements.

;;;When writing validators, make sure you check for all conditions you expect the argument to obey.
;;;For example, if you want it to be an odd integer, don't just set the validator to odd? 
;;;because when the user tries to set your parameter to some string (odd? string) will give
;;;a scheme error.  To avoid that use (validate-all interger? odd?)

;;;Procedure for combining validators.
;;;Checks whether AND of all its arguments applied to a value is true.
(define (validate-all . validators) 
  (define (val-all value validators)
    (if (null? validators)
	#t
	(let ((cur ((car validators) value)))
	  (cond ((string? cur) cur)
		(else (and cur (val-all value (cdr validators))))))))
  (lambda (value) (val-all value validators)))

;;;Procedure for combining validators.
;;;Checks whether OR of all its arguments applied to a value is true.
(define (validate-one . validators) 
  (define (val-one value validators)
    (if (null? validators)
	#f
	(let ((cur ((car validators) value)))
	  (if (eq? #t cur)
	      #t     
	      (val-one value (cdr validators))))))
  (lambda (value) (val-one value validators)))

;;;some useful procedures
(define (nonneg-int? x)
  (and (integer? x) (>= x 0)))
(define (pathname? name)
  (and (string? name) (if (stat name) #t #f)))
(define (yenta-id? string) (string? string)) ;%%%should be changed later

;;;It is useful for validators to return an error string instead of #f.
;;;The following procedure takes a symbol - name of the boolean procedure and creates
;;;procedure validator:<name> that returns errror string instead of false.

(define (create-validator name)
  (eval `(define ,(create-name "validator:" name)
	   (lambda (x)
	     (if (,name x) #t (format #f "value ~S does not pass test ~S" x ,name))))))

;;;here we create some useful validators
;;; feel free to add procedures to the list below
(for-each create-validator
	  '(number?
	    integer?
	    nonneg-integer?
	    string?
	    symbol?
	    list?
	    pathname?
	    yenta-id?
	    odd?			;should probably be defined as AND of integer? and odd?
	    even?			;the same here
	    positive?			;the same here (should pass number?)
	    negative?			;the same here
	    ))

;;;here is the escape validator.
;;;use it when you don't want to bother with validator
(define (validator:none x) #t)
