;; 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)

(module scm2lisp (symbol->lisp quoted->lisp sexp->lisp
                  create-lisp-prelude scm2lisp:compile)
  
(def-in-module in-prelude? (make-parameter #f))

(define (symbol->lisp x)
  ;; Replaces some characters with string escapes
  (define (char-esc c)
    (case c
      ((#\_) "__") ((#\space) "_x_") ((#\:) "_c_")
      ((#\^ #\.)
       (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))))
  (with-input-from-str (tr-sym x) read))

;; Converts formals from Scheme-style (x y . more) to (x y &rest more)
(define (scm-formals->lisp formals)
  (define (loop formals result)
    (cond
     ((null? formals) (reverse! result))
     ((pair? formals) (loop (cdr formals)
                            (cons (symbol->lisp (car formals)) result)))
     (else (loop '() `(,(symbol->lisp formals) &rest ,@result)))))
  (loop formals '()))

(define (atom->lisp x)
  (cond
   ((string? x) x)
   ((boolean? x) (if x 't 'cpscm__false))
   ((symbol? x) (symbol->lisp x))
   ((number? x) x)
   ((char? x) x)
   (else (error `(atom->lisp ,x)))))

(define (quoted->lisp x)
  (define orig `(quote ,x))
  (define (nil->list x)
    (wmatch x
            ((a b) (if (and (eq? a 'quote) (eq? b 'nil)) '() x))
            (_ x)))
  (cond ((null? x) '())
        ((symbol? x) orig)
        ((vector? x)
         `(quote ,(list->vector (second (quoted->lisp (vector->list x))))))
        ((pair? x)
         `(quote ,(improper-map
                   (compose unwrap-quote nil->list quoted->lisp) x)))
        (else (atom->lisp x))))

(define (lambda->lisp args body name)
  (let ((nbody (map sexp->lisp (rewrite-body-int-defs body))))
    ;; support for multi-sexp body broken for now
    (or (null? (cdr nbody)) (error 'lambda->lisp))
    `(lambda ,(scm-formals->lisp args)
       ,@(if (and (trampoline?) (needs-trampoline? (car nbody)))
             `((cpscm__trampoline (lambda () . ,nbody)))
             nbody))))

(define (declare-global sym val)
  `(cpscm__global ,(symbol->lisp sym) ,val))

(define (top-sexp->lisp sexp)
  (sexp->lisp sexp #f #t))

(define/opt (sexp->lisp sexp (name top?))
  (define (do-eval sexp)
    ((if (computation? sexp) drive values)
     (sexp->lisp sexp)))
  (define (drive lexp)
    (if top? `(cpscm__drive ,lexp (function error)) lexp))
  (define (do-red sexp)
    ((if (needs-trampoline? sexp)
         (cut list 'cpscm__reduce-trampoline <>) values)
     (sexp->lisp sexp)))
  (wmatch
   sexp
   (('quote x) (quoted->lisp x))
   (('begin . prog)
    (receive (some last) (split-at-right prog 1)
      (drive `(progn ,@(map do-red some) ,(sexp->lisp (car last))))))
   (('set! x val) `(setq ,(symbol->lisp x) ,(do-eval val)))
   (('lambda args . body) (lambda->lisp args body name))
   (('define (f . args) . body)
    (sexp->lisp `(define ,f (lambda ,args . ,body))))
   (('define x val) (declare-global x (do-eval val)))
   ((f . args)
    (drive `(funcall ,(sexp->lisp f) ,@(map sexp->lisp args))))
   (_ (atom->lisp sexp))))

(define (create-lisp-prelude)
  (parameterize
      ((in-prelude? #t) (strict-letrec? #f))
    (call-with-output-file "prelude.lsp"
      (lambda (p)
        (for-each
         (lambda (x) (pretty-print x p) (newline p) (newline p))
         (append
          (map (compose top-sexp->lisp simplify-sexp expand-extra)
               (cpscm-defs-cpsed))
          (prog->lisp (cpscm-defs))))))))

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

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

)
