;; Copyright (C) 2006 Dan Muresan
;;
;; This file is part of cpscm (http://www.omnigia.com/scheme/cpscm/home).
;;
;; cpscm is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
;; (at your option) any later version.
;;
;; cpscm is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with cpscm; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

(require-library 'cpscm) (import cpscm)

(define in-prelude? (make-parameter #f))

(define (symbol->cpscm x)
  (define (char-esc c)
    (case c
      ((#\. #\space)
       (s+ "_" (number->string (char->integer c) 16) "_"))
      (else (string c))))
  (define (str-esc str)
    (string-concatenate (map char-esc (string->list str))))
  (define (tr-prefix str pfx tr)
    (s+ tr (str-esc (string-drop str (string-length pfx)))))
  (define (tr-sym x)
    (define xstr (symbol->string x))
    (define (chk pfx+tr) (string-prefix? (car pfx+tr) xstr))
    (define (find-pfx)
      (find-tail
       chk `(,@(if (in-prelude?)
                   `(("%cpscm:" ,(s+ cpscm-name "__")))
                   `())
             (" %gs:" "g")
             ("" ,cpscm-name))))
    (apply tr-prefix xstr (car (find-pfx))))
  (unless (symbol? x) (error `(symbol->cpscm ,x)))
  (with-input-from-str (tr-sym x) read))

(define (atom->cpscm x)
  (cond
   ((pair? x) (error `(atom->cpscm ,x)))
   ((symbol? x) (symbol->cpscm x))
   (else x)))

(define (formals->cpscm formals)
  (cond
   ((null? formals) '())
   ((pair? formals)
    (cons (symbol->cpscm (car formals)) (formals->cpscm (cdr formals))))
   (else (symbol->cpscm formals))))

(define (lambda->cpscm formals body)
  (let* ((rbody (rewrite-body-int-defs body)) (sexp (car rbody))
         (ret
          (let ((lexp (sexp->cpscm sexp)))
            (if (and (trampoline?) (needs-trampoline? sexp))
                `(cpscm__trampoline ,lexp) lexp))))
    (or (null? (cdr rbody)) (error 'lambda->cpscm))
    `(lambda ,(formals->cpscm formals)
       ,ret)))

(define (top-sexp->cpscm sexp)
  (sexp->cpscm sexp #t))

(define/opt (sexp->cpscm sexp (top?))
  (define (do-eval sexp)
    ((if (computation? sexp) drive values)
     (sexp->cpscm sexp)))
  (define drive (if top? (cut list 'cpscm__drive <>) values))
  (define (do-red sexp)
    ((if (needs-trampoline? sexp) (cut list 'cpscm__reduce-trampoline <>)
         values)
     (sexp->cpscm sexp)))
  (wmatch
   sexp
   (('cpscm__trampoline . rest) (error '(sexp->cpscm invoked twice)))
   (('quote x) sexp)
   (('begin . prog)
    (receive (some last) (split-at-right prog 1)
      (drive `(begin ,@(map do-red some) ,(sexp->cpscm (car last))))))
   (('set! var val)
    `(set! ,(symbol->cpscm var) ,(do-eval val)))
   (('define (f . args) . body)
    (sexp->cpscm `(define ,f (lambda ,args . ,body))))
   (('define x val) `(define ,(symbol->cpscm x) ,(do-eval val)))
   (('lambda formals . body) (lambda->cpscm formals body))
   ((f . args) (drive (map sexp->cpscm sexp)))
   (_ (atom->cpscm sexp))))
          
(define (r5rs-defs)
  (call-with-input-file "../doc/r5rs_defs.txt" port->sexp-list))

(define (create-cpscm-prelude)
  (define aux-defs
    `((define append! append)  ;; TODO: optimize
      (define (append-reverse rev-head tail)  ;; TODO: optimize
        (append (reverse rev-head) tail))
      (define append-reverse! append-reverse)
      (define (reverse! l) (reverse l))  ;; TODO: optimize
      ))
  (define hof '(member memq memv assoc assq assv map for-each))
  (define ccfuns
    '(dynamic-wind call-with-values values call-with-current-continuation))
  (define (hof-def? def)
    (and (pair? (cadr def)) (memq (caadr def) hof)))
  (define (native-defs-1)
    `(         
      (define-record-type :cpscm__trampoline
        (cpscm__trampoline-create thunk) cpscm__trampoline?
        (text cpscm__trampoline-text cpscm__trampoline-set-text!)
        (thunk cpscm__trampoline-thunk))
      (define-record-type :cpscm__delay
        (cpscm__delay-create thunk) cpscm__delay?
        (thunk cpscm__delay-thunk))
      (define-syntax cpscm__trampoline
        (syntax-rules ()
          ((_ . rest) (let ((tr (cpscm__trampoline-create
                                 (lambda () . rest))))
                        (cpscm__trampoline-set-text! tr 'rest)
                        tr))))
      (define (cpscm__drive sexp)
        (cpscm__reduce-trampoline (cpscm__trampoline sexp)))
      (define (cpscm__reduce-trampoline sexp)
        (let loop ((cc sexp))
          (if (cpscm__trampoline? cc)
              (loop ((cpscm__trampoline-thunk cc))) cc)))
      (define (,(symbol->cpscm boolean-combinator) k test then else)
        (if test (then k) (else k)))
      (define (,(symbol->cpscm cpscm-delay) k thunk)
        (k (cpscm__delay-create thunk)))
      (define (,(symbol->cpscm 'promise?) k x)
        (k (cpscm__delay? x)))
      (define (,(symbol->cpscm 'force) k p)
        ((cpscm__delay-thunk p) k))
      (define ,(symbol->cpscm '%cpscm:normal-apply) apply)
      ))
  (parameterize
      ((in-prelude? #t) (strict-letrec? #f))
    (call-with-output-file "cpscm-drv.scm"
      (lambda (p)
        (define (wrnl x) (pretty-print x p))
        (define (tr-native-def sexp) sexp)
        (for-each wrnl (map tr-native-def (native-defs-1)))
        (for-each
         (lambda (d)
           (unless (memq d `(apply ,@ccfuns ,@hof force promise?))
             (wrnl `(define ,(symbol->cpscm d)
                      (lambda (k . args) (k (apply ,d args)))))))
         (r5rs-defs))
        (for-each
         wrnl
         `(,@(append
              (map (compose top-sexp->cpscm simplify-sexp expand-extra)
                   (cpscm-defs-cpsed))
              (prog->cpscm
                (append (filter hof-def? (r5rs-bootstrap-defs))
                        aux-defs (cpscm-int-defs))))
           (define ,(symbol->cpscm 'call/cc)
             ,(symbol->cpscm 'call-with-current-continuation))
           ))
        ))))

(define (prog->cpscm prog)
  (map top-sexp->cpscm (prog->is prog)))

(def-in-module scm2scm:compile
  (cpscm-compiler prog->cpscm pretty-print))

(module scm2scm () )
