+(define-compilation equal (x y)
+ (concat "(" (ls-compile x env fenv) " == " (ls-compile y env fenv) ")"))
+
+(define-compilation string (x)
+ (concat "String.fromCharCode(" (ls-compile x env fenv) ")"))
+
+(define-compilation stringp (x)
+ (concat "(typeof(" (ls-compile x env fenv) ") == \"string\")"))
+
+(define-compilation string-upcase (x)
+ (concat "(" (ls-compile x env fenv) ").toUpperCase()"))
+
+(define-compilation string-length (x)
+ (concat "(" (ls-compile x env fenv) ").length"))
+
+(define-compilation char (string index)
+ (concat "("
+ (ls-compile string env fenv)
+ ").charCodeAt("
+ (ls-compile index env fenv)
+ ")"))
+
+(define-compilation concat-two (string1 string2)
+ (concat "("
+ (ls-compile string1 env fenv)
+ ").concat("
+ (ls-compile string2 env fenv)
+ ")"))
+
+(define-compilation funcall (func &rest args)
+ (concat "("
+ (ls-compile func env fenv)
+ ")("
+ (join (mapcar (lambda (x)
+ (ls-compile x env fenv))
+ args)
+ ", ")
+ ")"))
+
+(define-compilation apply (func &rest args)
+ (if (null args)
+ (concat "(" (ls-compile func env fenv) ")()")
+ (let ((args (butlast args))
+ (last (car (last args))))
+ (concat "function(){" *newline*
+ "var f = " (ls-compile func env fenv) ";" *newline*
+ "var args = [" (join (mapcar (lambda (x)
+ (ls-compile x env fenv))
+ args)
+ ", ")
+ "];" *newline*
+ "var tail = (" (ls-compile last env fenv) ");" *newline*
+ "while (tail != false){" *newline*
+ " args.push(tail[0]);" *newline*
+ " args = args.slice(1);" *newline*
+ "}" *newline*
+ "return f.apply(this, args);" *newline*
+ "}" *newline*))))
+
+(define-compilation js-eval (string)
+ (concat "eval(" (ls-compile string env fenv) ")"))
+
+
+(define-compilation error (string)
+ (concat "(function (){ throw " (ls-compile string env fenv) ";" "return 0;})()"))
+
+(define-compilation new ()
+ "{}")
+
+(define-compilation get (object key)
+ (concat "(" (ls-compile object env fenv) ")[" (ls-compile key env fenv) "]"))
+
+(define-compilation set (object key value)
+ (concat "(("
+ (ls-compile object env fenv)
+ ")["
+ (ls-compile key env fenv) "]"
+ " = " (ls-compile value env fenv) ")"))
+
+(defun macrop (x)
+ (and (symbolp x) (eq (binding-type (lookup-function x *fenv*)) 'macro)))
+
+(defun ls-macroexpand-1 (form &optional env fenv)
+ (when (macrop (car form))
+ (let ((binding (lookup-function (car form) *env*)))
+ (if (eq (binding-type binding) 'macro)
+ (apply (eval (binding-translation binding)) (cdr form))
+ form))))
+
+(defun compile-funcall (function args env fenv)
+ (cond
+ ((symbolp function)
+ (concat (lookup-function-translation function fenv)
+ "("
+ (join (mapcar (lambda (x) (ls-compile x env fenv)) args)
+ ", ")
+ ")"))
+ ((and (listp function) (eq (car function) 'lambda))
+ (concat "(" (ls-compile function env fenv) ")("
+ (join (mapcar (lambda (x) (ls-compile x env fenv)) args)
+ ", ")
+ ")"))
+ (t
+ (error (concat "Invalid function designator " (symbol-name function))))))
+
+(defun ls-compile (sexp &optional env fenv)
+ (cond
+ ((symbolp sexp) (lookup-variable-translation sexp env))
+ ((integerp sexp) (integer-to-string sexp))
+ ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
+ ((listp sexp)
+ (if (assoc (car sexp) *compilations*)
+ (let ((comp (second (assoc (car sexp) *compilations*))))
+ (apply comp env fenv (cdr sexp)))
+ (if (macrop (car sexp))
+ (ls-compile (ls-macroexpand-1 sexp env fenv) env fenv)
+ (compile-funcall (car sexp) (cdr sexp) env fenv))))))
+
+(defun ls-compile-toplevel (sexp)
+ (setq *toplevel-compilations* nil)
+ (let ((code (ls-compile sexp)))
+ (prog1
+ (concat "/* " (princ-to-string sexp) " */"
+ (join (mapcar (lambda (x) (concat x ";" *newline*))
+ *toplevel-compilations*)
+ "")
+ code)
+ (setq *toplevel-compilations* nil))))
+
+#+common-lisp
+(progn
+ (defun read-whole-file (filename)
+ (with-open-file (in filename)
+ (let ((seq (make-array (file-length in) :element-type 'character)))
+ (read-sequence seq in)
+ seq)))
+
+ (defun ls-compile-file (filename output)
+ (setq *env* nil *fenv* nil)
+ (setq *compilation-unit-checks* nil)
+ (with-open-file (out output :direction :output :if-exists :supersede)
+ (let* ((source (read-whole-file filename))
+ (in (make-string-stream source)))
+ (loop
+ for x = (ls-read in)
+ until (eq x *eof*)
+ for compilation = (ls-compile-toplevel x)
+ when (plusp (length compilation))
+ do (write-line (concat compilation "; ") out))
+ (dolist (check *compilation-unit-checks*)
+ (funcall check))
+ (setq *compilation-unit-checks* nil))))
+
+ (defun bootstrap ()
+ (ls-compile-file "lispstrack.lisp" "lispstrack.js")))