;; 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 scm2js (symbol->js sexp->js quoted->js create-js-prelude scm2js:compile) (def-in-module in-prelude? (make-parameter #f)) ;; Generates JS code that destructures a list according to lambda list (define (js-bind-formals jsarg args) (define (loop args result) (cond ((null? args) result) ((pair? args) (let ((var (symbol->js (car args)))) (loop (cdr args) (s+ result "var " var "=" jsarg ".car; " jsarg "=" jsarg ".cdr;\n")))) (else (s+ result "var " (symbol->js args) "=" jsarg ";\n")))) (loop args "")) (define (list->js args) (define (loop args result) (cond ((null? args) (s+ result (__cpscm "nil"))) ((pair? args) (loop (cdr args) (s+ result (sexp->js (car args)) ", "))) (else (s+ result (sexp->js args))))) (s+ (loop args (s+ (__cpscm "list") " (")) ")")) (define (symbol->js x) ;; Replaces some characters with string escapes (define (char-esc c) (case c ((#\_) "__") ((#\-) "_d_") ((#\?) "_p_") ((#\!) "_b_") ((#\=) "_e_") ((#\<) "_l_") ((#\>) "_r_") ((#\/) "_w_") ((#\space) "_x_") ((#\:) "_c_") ((#\+ #\* #\^ #\$ #\% #\& #\@ #\~ #\.) (s+ "_" (number->string (char->integer c) 16) "_")) (else (string c)))) (define (fix-first l) (if (string=? (car l) "__") (cons "_u_" (cdr l)) l)) (define (str-esc str) (string-concatenate (fix-first (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)))) (tr-sym x)) (define (string->js str) (with-output-to-string (cut write str))) (define (atom->js x) (cond ((string? x) (string->js x)) ((boolean? x) (if x "true" "false")) ((symbol? x) (symbol->js x)) ((number? x) (number->string x)) ((char? x) (s+ "(cpscm__char (" (number->string (char->integer x)) "))")) (else (with-output-to-string (cut display x))))) (define (quoted-list->js l) (define (loop l res) (if (pair? l) (loop (cdr l) (s+ res (quoted->js (car l)) ", ")) (s+ res (quoted->js l)))) (s+ (loop l (s+ (__cpscm "list")" (")) ")")) (define (quoted->js x) (cond ((pair? x) (quoted-list->js x)) ((null? x) (__cpscm "nil")) ((symbol? x) (s+ (__cpscm "sym") " (" (string->js (symbol->string x)) ")")) ((vector? x) (s+ "[" (string-join (map quoted->js (vector->list x)) ", ") "]")) (else (atom->js x)))) (define (lambda->js args body name) (let* ((rbody (rewrite-body-int-defs body)) (nbody (s+ (sexp->js (car rbody)) ";"))) ;; support for multi-sexp body broken for now (or (null? (cdr rbody)) (error 'lambda->js)) (s+ "function " (if name name "") "(__args) {\n" (js-bind-formals "__args" args) "return " (if (and (trampoline?) (needs-trampoline? (car rbody))) (s+ "new cpscm__Trampoline (function () {\nreturn " nbody " });") nbody) "\n}\n"))) (define (top-sexp->js sexp) (sexp->js sexp #f #t)) (define/opt (sexp->js sexp (name top?)) (define (maybe-native->js x) (if (string? x) x (sexp->js x))) (define (do-eval sexp) (s+ ((if (computation? sexp) drive values) (sexp->js sexp)))) (define drive (if top? (cut string-append "cpscm__drive (" <> ")") values)) (define (do-red sexp) ((if (needs-trampoline? sexp) (cut string-append "cpscm__reduce_d_trampoline (" <> ")") values) (sexp->js sexp))) (wmatch sexp (('quote x) (quoted->js x)) (('begin . prog) (receive (some last) (split-at-right prog 1) (s+ (drive (s+ "(" (string-join (map do-red some) ", ") ", " (sexp->js (car last)) ")")) (if top? ";\n\n" "")))) (('set! x val) (s+ "(" (atom->js x) " = " (do-eval val) ")")) (('lambda args . body) (lambda->js args body name)) (('define (f . args) . body) (sexp->js `(define ,f (lambda ,args . ,body)))) (('define x val) (s+ "var " (symbol->js x) " = " (do-eval val) ";\n")) (('%cpscm:native . args) (s+ "(" (string-join (map maybe-native->js args)) ")")) ((f . args) (s+ (drive (s+ "(" (sexp->js f) ") " "(" (list->js args) ")")) (if top? ";\n\n" ""))) (_ (atom->js sexp)))) (define (create-js-prelude) (parameterize ((in-prelude? #t) (strict-letrec? #f)) (call-with-output-file "prelude.js" (lambda (p) (display (s+ (string-concatenate (append (map (compose top-sexp->js simplify-sexp expand-extra) (cpscm-defs-cpsed)) (prog->js-list (cpscm-defs))))) p))))) (define (prog->js-list prog) (map top-sexp->js (prog->is prog))) (def-in-module scm2js:compile (cpscm-compiler prog->js-list display)) ;; To compile test-case: ;; (with-output-to-file "t.js" (cut display (sexp->js (simplify-sexp (def->cps def))))) (define js-test-cases '( (define (f x) (call/cc (lambda (cc) (cc 10)))) (define (f x) (define (hnd m ek) (display "in fc") (ek 10)) (with/fc hnd (lambda () (dynamic-wind (lambda () (display "dwb")) (lambda () (car 1)) (lambda () (display "dwa")))))) (define (f x) (call-with-values (lambda () (values 10 20)) (lambda (a b) (+ a b)))) (define (f x) (dynamic-wind (lambda () (display "B")) (lambda () (error 1) (display "T")) (lambda () (display "A")))) )) )