;; Library of functions for the compiler
;; (c) 1996-7 Sebastian Good
;; (c) 1997-2011 PLT Scheme Inc

(module library mzscheme
  (require mzlib/unit
           mzlib/list
           mzlib/etc)

  (require syntax/zodiac-sig)

  (require "sig.rkt")

  (provide library@)
  (define-unit library@
      (import (prefix zodiac: zodiac^))
      (export compiler:library^)

      (define logical-inverse
	(lambda (fun)
	  (lambda (obj)
	    (not (fun obj)))))

      (define one-of
	(case-lambda
	 [(p1 p2) (lambda (obj)
		    (or (p1 obj) (p2 obj)))]
	 [preds 
	  (lambda (obj)
	    (ormap (lambda (p) (p obj)) preds))]))

      (define all-of
	(lambda preds
	  (lambda (obj)
	    (andmap (lambda (p) (p obj)) preds))))

      (define none-of
	(lambda preds
	  (logical-inverse (apply one-of preds))))

      (define vector-map ; modified by PAS, but looks to be unused
	(lambda (f vec)
	  (let* ([vec-len (vector-length vec)]
		 [x (make-vector vec-len)])
	    (let loop ((i 0))
	      (if (>= i vec-len)
		  x
		  (begin (vector-set! x i (f (vector-ref vec i)))
			 (loop (add1 i))))))))

      (define improper-map
	(lambda (f ilist)
	  (cond 
	   ((pair? ilist) (cons (f (car ilist)) (improper-map f (cdr ilist))))
	   ((null? ilist) null)
	   (else (f ilist)))))

      (define begin-map
	(lambda (non-tail tail list)
	  (if (null? list)
	      null
	      (let ([tail? (null? (cdr list))])
		(cons ((if tail? tail non-tail) (car list)) 
		      (begin-map non-tail tail (cdr list)))))))

      (define list-index
	(lambda (obj list)
	  (cond
	   [(null? list) (error 'list-index "~a not found int ~a" obj list)]
	   [(eq? obj (car list)) 0]
	   [else (add1 (list-index obj (cdr list)))])))

      (define list-last
	(lambda (list)
	  (if (null? list)
	      (error 'list-last "~a is empty!" list)
	      (let loop ([a list] [b (cdr list)])
		(if (null? b)
		    (car a)
		    (loop b (cdr b)))))))

      ;; Set operations

      ;; -----> Begin bit-vector implementation <-----
      #|
      (define set-next-index 0)
      (define index-vector (make-vector 100))
      (define singleton-vector (make-vector 100))
      (define index-table (make-hash-table))

      (define (index->object i) (vector-ref index-vector i))
      (define (object->index o) 
	(let ([i (hash-table-get index-table o (lambda () #f))])
	  (or i
	      (let ([i set-next-index])
		(set! set-next-index (add1 set-next-index))
		(unless (< i (vector-length index-vector))
		  (printf "grow ~a\n" i)
		  (let* ([old-iv index-vector]
			 [old-sv singleton-vector]
			 [old-size (vector-length index-vector)]
			 [new-size (* 2 old-size)])
		    (set! index-vector (make-vector new-size))
		    (set! singleton-vector (make-vector new-size))
		    (let loop ([n 0])
		      (unless (= n old-size)
			(vector-set! index-vector n (vector-ref old-iv n))
			(vector-set! singleton-vector n (vector-ref old-sv n))
			(loop (add1 n))))))
		(vector-set! index-vector i o)
		(vector-set! singleton-vector i (arithmetic-shift 1 i))
		(hash-table-put! index-table o i)
		i))))
      (define (object->singleton o) 
	(let ([i (object->index o)])
	  (vector-ref singleton-vector i)))
      (define (set->objects s)
	(letrec ([dloop ; double-search
		  (lambda (s i n d)
		    (if (zero? s)
			null
			(if (positive? (bitwise-and s i))
			    (if (= n 1)
				(cons (index->object d)
				      (dloop (arithmetic-shift s -1) 1 1 (add1 d)))		
				(let ([n/2 (quotient n 2)])
					; It's in d+n/2...d+n
				  (bloop (arithmetic-shift s (- n/2)) (arithmetic-shift i (- n/2)) n/2 (+ d n/2))))
			    (dloop s (bitwise-ior i (arithmetic-shift i n)) (* n 2) d))))]
		 [bloop 
		  (lambda (s i n d)
		    (if (= n 1)
			(cons (index->object d)
			      (dloop (arithmetic-shift s -1) 1 1 (add1 d)))		
			(let* ([n/2 (quotient n 2)]
			       [low_i (arithmetic-shift i (- n/2))])
			  (if (positive? (bitwise-and s low_i))
			      (bloop s low_i n/2 d)
			      (bloop (arithmetic-shift s (- n/2)) low_i n/2 (+ d n/2))))))])
	  (dloop s 1 1 0)))

      (define (set->list s) (reverse! (set->objects s))) ; something relies on the order
      (define empty-set 0)
      (define make-singleton-set object->singleton)
      (define (list->set l)
	(let loop ([l l][s 0])
	  (if (null? l)
	      s
	      (loop (cdr l) (set-union s (object->singleton (car l)))))))
      (define (set-memq? o s)
	(positive? (bitwise-and s (object->singleton o))))

      (define set-union bitwise-ior)
      (define set-intersect bitwise-and)
      (define (set-union-singleton s o) (set-union s (object->singleton o)))
      (define (set-minus s1 s2) (bitwise-and s1 (bitwise-not s2)))
      (define (set-subset? s1 s2) (zero? (bitwise-xor s1 (bitwise-and s1 s2))))
      (define set-empty? zero?)
      (define set? integer?) ; cheat
      |#
      ;; -----> End bit-vector implementation <------

      ;; -----> Begin list implementation <------

      (define-struct set (%m))
      (define empty-set (make-set null))
      (define make-singleton-set (compose make-set list))
      (define list->set
	(lambda (l)
					; (unless (list? l) (error 'list->set "~a not a list" l))
	  (make-set l)))
      (define set->list set-%m)
      (define set-memq?
	(lambda (obj set)
	  (memq obj (set->list set))))
      (define set-empty? (compose null? set->list))

      (define set-union ; O(|a|*|b|)
	(lambda (a b)
	  (let union ([a (set->list a)]
		      [b (set->list b)])
	    (cond
	     [(null? a) (list->set b)]
	     [(memq (car a) b) (union (cdr a) b)]
	     [else (union (cdr a) (cons (car a) b))]))))

      (define set-union-singleton
	(lambda (set obj)
	  (when (void? obj)
	    (error 'stop))
	  (if (memq obj (set->list set))
	      set
	      (list->set (cons obj (set->list set))))))

      (define set-minus ; O(|a|*|b|)
	(lambda (a b)
	  (let minus ([a (set->list a)]
		      [b (set->list b)]
		      [acc null])
	    (cond
	     [(null? a) (list->set acc)]
	     [(memq (car a) b) (minus (cdr a) b acc)]
	     [else (minus (cdr a) b (cons (car a) acc))]))))

      (define set-intersect ; O(|a|*|b|)
	(lambda (a b)
	  (if (or (set-empty? a)
		  (set-empty? b))
	      empty-set
	      (let intersect ([a (set->list a)]
			      [acc null])
		(cond
		 [(null? a) (list->set acc)]
		 [(set-memq? (car a) b) (intersect (cdr a) (cons (car a) acc))]
		 [else (intersect (cdr a) acc)])))))

      (define (set-subset? s1 s2)
	(if (eq? s1 s2)
	    #t
	    (let ([l1 (set->list s1)]
		  [l2 (set->list s2)])
	      (andmap (lambda (elt) (memq elt l2)) l1))))

      ;; -----> End list implementation <-----

      (define set-remove 
	(lambda (e s)
	  (set-minus s (make-singleton-set e))))

      (define improper-list->set
	(lambda (l)
	  (let loop ([l l][acc null])
	    (cond
	     [(null? l) (list->set acc)]
	     [(pair? l) (loop (cdr l) (cons (car l) acc))]
	     [else (list->set (cons l acc))]))))

      (define set-find
	(lambda (p s)
	  (let ([lst (set->list s)])
	    (let loop ([l lst])
	      (cond [(null? l) #f]
		    [(p (car l)) (car l)]
		    [else (loop (cdr l))])))))

      (define set-map
	(lambda (f s)
	  (list->set (map f (set->list s)))))

      (define set-filter
	(lambda (f s)
	  (list->set (filter f (set->list s)))))

      (define symbol-append
	(lambda s
	  (let loop ([str ""] [s s])
	    (if (null? s)
		(string->symbol str)
		(loop (string-append str (symbol->string (car s))) (cdr s))))))

      (define (remove-duplicates elts)
	(if (null? elts)
	    '()
	    (if (memq (car elts) (cdr elts))
		(remove-duplicates (cdr elts))
		(cons (car elts) (remove-duplicates (cdr elts))))))

					; end binder set ops

      (define compiler:formals->arity
	(lambda (f)
	  (let ([L (length (zodiac:arglist-vars f))])
	    (cond
	     [(zodiac:sym-arglist? f) (values 0 -1)]
	     [(zodiac:list-arglist? f) (values L L)]
	     [(zodiac:ilist-arglist? f) (values (- L 1) -1)]))))

      (define compiler:formals->arity*
	(lambda (fs)
	  (cond
	   [(null? fs) (values -1 0)]
	   [(null? (cdr fs)) (compiler:formals->arity (car fs))]
	   [else (let-values ([(a- a+) (compiler:formals->arity (car fs))]
			      [(b- b+) (compiler:formals->arity* (cdr fs))])
		   (values (min a- b-)
			   (if (or (negative? b+) (negative? a+))
			       -1
			       (max a+ b+))))])))
      
      (define compiler:gensym gensym)
      (define compiler:label-number 0)
      (define (compiler:reset-label-number!)
	(set! compiler:label-number 0))
      (define compiler:genlabel
	(lambda ()
	  (begin0 compiler:label-number
		  (set! compiler:label-number (add1 compiler:label-number)))))
      (define (compiler:get-label-number) compiler:label-number)

      (define re:bad-char (regexp "[][#+-.*/<=>!?:$%_&~^@;^(){}|\\,~\"`' \000-\040]"))
      (define re:starts-with-number (regexp "^[0-9]"))

      (define (compiler:clean-string s)
	(let ([s (regexp-replace* re:bad-char s "_")])
	  (if (regexp-match re:starts-with-number s)
	      (string-append "_" s)
	      s)))

      (define (protect-comment s)
	(string-append
	 (regexp-replace* "[*]/"
			  (regexp-replace* "/[*]" s "-")
			  "-")
	 " "))

      (define ns (make-namespace))

      (define (global-defined-value* v)
	(and v (namespace-variable-value v #t #f ns)))

      (define (kernel-modname? modname)
        (and modname
             (equal? ''#%kernel (let-values ([(name base) (module-path-index-split modname)])
                                  name))))))
