80e2b77675b1921ae326b278c8fa8fceb85d5784
[jscl.git] / ecmalisp.lisp
1 ;;; ecmalisp.lisp ---
2
3 ;; Copyright (C) 2012, 2013 David Vazquez
4 ;; Copyright (C) 2012 Raimon Grau
5
6 ;; This program is free software: you can redistribute it and/or
7 ;; modify it under the terms of the GNU General Public License as
8 ;; published by the Free Software Foundation, either version 3 of the
9 ;; License, or (at your option) any later version.
10 ;;
11 ;; This program is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ;; General Public License for more details.
15 ;;
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 ;;; This code is executed when ecmalisp compiles this file
20 ;;; itself. The compiler provides compilation of some special forms,
21 ;;; as well as funcalls and macroexpansion, but no functions. So, we
22 ;;; define the Lisp world from scratch. This code has to define enough
23 ;;; language to the compiler to be able to run.
24
25 #+ecmalisp
26 (progn
27   (eval-when-compile
28     (%compile-defmacro 'defmacro
29                        '(function
30                          (lambda (name args &rest body)
31                           `(eval-when-compile
32                              (%compile-defmacro ',name
33                                                 '(function
34                                                   (lambda ,(mapcar #'(lambda (x)
35                                                                        (if (eq x '&body)
36                                                                            '&rest
37                                                                            x))
38                                                                    args)
39                                                    ,@body))))))))
40
41   (defmacro declaim (&rest decls)
42     `(eval-when-compile
43        ,@(mapcar (lambda (decl) `(!proclaim ',decl)) decls)))
44
45   (declaim (constant nil t) (special t nil))
46   (setq nil 'nil)
47   (js-vset "nil" nil)
48   (setq t 't)
49
50   (defmacro lambda (args &body body)
51     `(function (lambda ,args ,@body)))
52
53   (defmacro when (condition &body body)
54     `(if ,condition (progn ,@body) nil))
55
56   (defmacro unless (condition &body body)
57     `(if ,condition nil (progn ,@body)))
58
59   (defmacro defvar (name value &optional docstring)
60     `(progn
61        (declaim (special ,name))
62        (unless (boundp ',name) (setq ,name ,value))
63        ,@(when (stringp docstring) `((oset ',name "vardoc" ,docstring)))
64        ',name))
65
66   (defmacro defparameter (name value &optional docstring)
67     `(progn
68        (setq ,name ,value)
69        ,@(when (stringp docstring) `((oset ',name "vardoc" ,docstring)))
70        ',name))
71
72   (defmacro named-lambda (name args &rest body)
73     (let ((x (gensym "FN")))
74       `(let ((,x (lambda ,args ,@body)))
75          (oset ,x "fname" ,name)
76          ,x)))
77
78   (defmacro defun (name args &rest body)
79     `(progn
80        (fset ',name
81              (named-lambda ,(symbol-name name) ,args
82                ,@(if (and (stringp (car body)) (not (null (cdr body))))
83                      `(,(car body) (block ,name ,@(cdr body)))
84                      `((block ,name ,@body)))))
85        ',name))
86
87   (defun null (x)
88     (eq x nil))
89
90   (defmacro return (&optional value)
91     `(return-from nil ,value))
92
93   (defmacro while (condition &body body)
94     `(block nil (%while ,condition ,@body)))
95
96   (defvar *gensym-counter* 0)
97   (defun gensym (&optional (prefix "G"))
98     (setq *gensym-counter* (+ *gensym-counter* 1))
99     (make-symbol (concat-two prefix (integer-to-string *gensym-counter*))))
100
101   (defun boundp (x)
102     (boundp x))
103
104   ;; Basic functions
105   (defun = (x y) (= x y))
106   (defun * (x y) (* x y))
107   (defun / (x y) (/ x y))
108   (defun 1+ (x) (+ x 1))
109   (defun 1- (x) (- x 1))
110   (defun zerop (x) (= x 0))
111   (defun truncate (x y) (floor (/ x y)))
112
113   (defun eql (x y) (eq x y))
114
115   (defun not (x) (if x nil t))
116
117   (defun cons (x y ) (cons x y))
118   (defun consp (x) (consp x))
119
120   (defun car (x)
121     "Return the CAR part of a cons, or NIL if X is null."
122     (car x))
123
124   (defun cdr (x) (cdr x))
125   (defun caar (x) (car (car x)))
126   (defun cadr (x) (car (cdr x)))
127   (defun cdar (x) (cdr (car x)))
128   (defun cddr (x) (cdr (cdr x)))
129   (defun caddr (x) (car (cdr (cdr x))))
130   (defun cdddr (x) (cdr (cdr (cdr x))))
131   (defun cadddr (x) (car (cdr (cdr (cdr x)))))
132   (defun first (x) (car x))
133   (defun second (x) (cadr x))
134   (defun third (x) (caddr x))
135   (defun fourth (x) (cadddr x))
136
137   (defun list (&rest args) args)
138   (defun atom (x)
139     (not (consp x)))
140
141   ;; Basic macros
142
143   (defmacro incf (x &optional (delta 1))
144     `(setq ,x (+ ,x ,delta)))
145
146   (defmacro decf (x &optional (delta 1))
147     `(setq ,x (- ,x ,delta)))
148
149   (defmacro push (x place)
150     `(setq ,place (cons ,x ,place)))
151
152   (defmacro dolist (iter &body body)
153     (let ((var (first iter))
154           (g!list (gensym)))
155       `(block nil
156          (let ((,g!list ,(second iter))
157                (,var nil))
158            (%while ,g!list
159                    (setq ,var (car ,g!list))
160                    (tagbody ,@body)
161                    (setq ,g!list (cdr ,g!list)))
162            ,(third iter)))))
163
164   (defmacro dotimes (iter &body body)
165     (let ((g!to (gensym))
166           (var (first iter))
167           (to (second iter))
168           (result (third iter)))
169       `(block nil
170          (let ((,var 0)
171                (,g!to ,to))
172            (%while (< ,var ,g!to)
173                    (tagbody ,@body)
174                    (incf ,var))
175            ,result))))
176
177   (defmacro cond (&rest clausules)
178     (if (null clausules)
179         nil
180         (if (eq (caar clausules) t)
181             `(progn ,@(cdar clausules))
182             `(if ,(caar clausules)
183                  (progn ,@(cdar clausules))
184                  (cond ,@(cdr clausules))))))
185
186   (defmacro case (form &rest clausules)
187     (let ((!form (gensym)))
188       `(let ((,!form ,form))
189          (cond
190            ,@(mapcar (lambda (clausule)
191                        (if (eq (car clausule) t)
192                            clausule
193                            `((eql ,!form ',(car clausule))
194                              ,@(cdr clausule))))
195                      clausules)))))
196
197   (defmacro ecase (form &rest clausules)
198     `(case ,form
199        ,@(append
200           clausules
201           `((t
202              (error "ECASE expression failed."))))))
203
204   (defmacro and (&rest forms)
205     (cond
206       ((null forms)
207        t)
208       ((null (cdr forms))
209        (car forms))
210       (t
211        `(if ,(car forms)
212             (and ,@(cdr forms))
213             nil))))
214
215   (defmacro or (&rest forms)
216     (cond
217       ((null forms)
218        nil)
219       ((null (cdr forms))
220        (car forms))
221       (t
222        (let ((g (gensym)))
223          `(let ((,g ,(car forms)))
224             (if ,g ,g (or ,@(cdr forms))))))))
225
226   (defmacro prog1 (form &body body)
227     (let ((value (gensym)))
228       `(let ((,value ,form))
229          ,@body
230          ,value)))
231
232   (defmacro prog2 (form1 result &body body)
233     `(prog1 (progn ,form1 ,result) ,@body)))
234
235
236 ;;; This couple of helper functions will be defined in both Common
237 ;;; Lisp and in Ecmalisp.
238 (defun ensure-list (x)
239   (if (listp x)
240       x
241       (list x)))
242
243 (defun !reduce (func list initial)
244   (if (null list)
245       initial
246       (!reduce func
247                (cdr list)
248                (funcall func initial (car list)))))
249
250 ;;; Go on growing the Lisp language in Ecmalisp, with more high
251 ;;; level utilities as well as correct versions of other
252 ;;; constructions.
253 #+ecmalisp
254 (progn
255   (defun + (&rest args)
256     (let ((r 0))
257       (dolist (x args r)
258         (incf r x))))
259
260   (defun - (x &rest others)
261     (if (null others)
262         (- x)
263         (let ((r x))
264           (dolist (y others r)
265             (decf r y)))))
266
267   (defun append-two (list1 list2)
268     (if (null list1)
269         list2
270         (cons (car list1)
271               (append (cdr list1) list2))))
272
273   (defun append (&rest lists)
274     (!reduce #'append-two lists '()))
275
276   (defun revappend (list1 list2)
277     (while list1
278       (push (car list1) list2)
279       (setq list1 (cdr list1)))
280     list2)
281
282   (defun reverse (list)
283     (revappend list '()))
284
285   (defmacro psetq (&rest pairs)
286     (let ( ;; For each pair, we store here a list of the form
287           ;; (VARIABLE GENSYM VALUE).
288           (assignments '()))
289       (while t
290         (cond
291           ((null pairs) (return))
292           ((null (cdr pairs))
293            (error "Odd paris in PSETQ"))
294           (t
295            (let ((variable (car pairs))
296                  (value (cadr pairs)))
297              (push `(,variable ,(gensym) ,value)  assignments)
298              (setq pairs (cddr pairs))))))
299       (setq assignments (reverse assignments))
300       ;;
301       `(let ,(mapcar #'cdr assignments)
302          (setq ,@(!reduce #'append (mapcar #'butlast assignments) '())))))
303
304   (defmacro do (varlist endlist &body body)
305     `(block nil
306        (let ,(mapcar (lambda (x) (list (first x) (second x))) varlist)
307          (while t
308            (when ,(car endlist)
309              (return (progn ,(cdr endlist))))
310            (tagbody ,@body)
311            (psetq
312             ,@(apply #'append
313                      (mapcar (lambda (v)
314                                (and (consp (cddr v))
315                                     (list (first v) (third v))))
316                              varlist)))))))
317
318   (defmacro do* (varlist endlist &body body)
319     `(block nil
320        (let* ,(mapcar (lambda (x) (list (first x) (second x))) varlist)
321          (while t
322            (when ,(car endlist)
323              (return (progn ,(cdr endlist))))
324            (tagbody ,@body)
325            (setq
326             ,@(apply #'append
327                      (mapcar (lambda (v)
328                                (and (consp (cddr v))
329                                     (list (first v) (third v))))
330                              varlist)))))))
331
332   (defun list-length (list)
333     (let ((l 0))
334       (while (not (null list))
335         (incf l)
336         (setq list (cdr list)))
337       l))
338
339   (defun length (seq)
340     (cond
341       ((stringp seq)
342        (string-length seq))
343       ((arrayp seq)
344        (oget seq "length"))
345       ((listp seq)
346        (list-length seq))))
347
348   (defun concat-two (s1 s2)
349     (concat-two s1 s2))
350
351   (defun mapcar (func list)
352     (if (null list)
353         '()
354         (cons (funcall func (car list))
355               (mapcar func (cdr list)))))
356
357   (defun identity (x) x)
358
359   (defun copy-list (x)
360     (mapcar #'identity x))
361
362   (defun code-char (x) x)
363   (defun char-code (x) x)
364   (defun char= (x y) (= x y))
365
366   (defun integerp (x)
367     (and (numberp x) (= (floor x) x)))
368
369   (defun plusp (x) (< 0 x))
370   (defun minusp (x) (< x 0))
371
372   (defun listp (x)
373     (or (consp x) (null x)))
374
375   (defun nthcdr (n list)
376     (while (and (plusp n) list)
377       (setq n (1- n))
378       (setq list (cdr list)))
379     list)
380
381   (defun nth (n list)
382     (car (nthcdr n list)))
383
384   (defun last (x)
385     (while (consp (cdr x))
386       (setq x (cdr x)))
387     x)
388
389   (defun butlast (x)
390     (and (consp (cdr x))
391          (cons (car x) (butlast (cdr x)))))
392
393   (defun member (x list)
394     (while list
395       (when (eql x (car list))
396         (return list))
397       (setq list (cdr list))))
398
399   (defun remove (x list)
400     (cond
401       ((null list)
402        nil)
403       ((eql x (car list))
404        (remove x (cdr list)))
405       (t
406        (cons (car list) (remove x (cdr list))))))
407
408   (defun remove-if (func list)
409     (cond
410       ((null list)
411        nil)
412       ((funcall func (car list))
413        (remove-if func (cdr list)))
414       (t
415        (cons (car list) (remove-if func (cdr list))))))
416
417   (defun remove-if-not (func list)
418     (cond
419       ((null list)
420        nil)
421       ((funcall func (car list))
422        (cons (car list) (remove-if-not func (cdr list))))
423       (t
424        (remove-if-not func (cdr list)))))
425
426   (defun digit-char-p (x)
427     (if (and (<= #\0 x) (<= x #\9))
428         (- x #\0)
429         nil))
430
431   (defun subseq (seq a &optional b)
432     (cond
433       ((stringp seq)
434        (if b
435            (slice seq a b)
436            (slice seq a)))
437       (t
438        (error "Unsupported argument."))))
439
440   (defun parse-integer (string)
441     (block nil
442       (let ((value 0)
443             (index 0)
444             (size (length string))
445             (sign 1))
446         (when (zerop size) (return (values nil 0)))
447         ;; Optional sign
448         (case (char string 0)
449           (#\+ (incf index))
450           (#\- (setq sign -1)
451                (incf index)))
452         ;; First digit
453         (unless (and (< index size)
454                      (setq value (digit-char-p (char string index))))
455           (values nil index))
456         (incf index)
457         ;; Other digits
458         (while (< index size)
459           (let ((digit (digit-char-p (char string index))))
460             (unless digit (return))
461             (setq value (+ (* value 10) digit))
462             (incf index)))
463         (if (or (= index size)
464                 (char= (char string index) #\space))
465             (values (* sign value) index)
466             (values nil index)))))
467
468   (defun some (function seq)
469     (cond
470       ((stringp seq)
471        (let ((index 0)
472              (size (length seq)))
473          (while (< index size)
474            (when (funcall function (char seq index))
475              (return-from some t))
476            (incf index))
477          nil))
478       ((listp seq)
479        (dolist (x seq nil)
480          (when (funcall function x)
481            (return t))))
482       (t
483        (error "Unknown sequence."))))
484
485   (defun every (function seq)
486     (cond
487       ((stringp seq)
488        (let ((index 0)
489              (size (length seq)))
490          (while (< index size)
491            (unless (funcall function (char seq index))
492              (return-from every nil))
493            (incf index))
494          t))
495       ((listp seq)
496        (dolist (x seq t)
497          (unless (funcall function x)
498            (return))))
499       (t
500        (error "Unknown sequence."))))
501
502   (defun assoc (x alist)
503     (while alist
504       (if (eql x (caar alist))
505           (return)
506           (setq alist (cdr alist))))
507     (car alist))
508
509   (defun string (x)
510     (cond ((stringp x) x)
511           ((symbolp x) (symbol-name x))
512           (t (char-to-string x))))
513
514   (defun string= (s1 s2)
515     (equal s1 s2))
516
517   (defun fdefinition (x)
518     (cond
519       ((functionp x)
520        x)
521       ((symbolp x)
522        (symbol-function x))
523       (t
524        (error "Invalid function"))))
525
526   (defun disassemble (function)
527     (write-line (lambda-code (fdefinition function)))
528     nil)
529
530   (defun documentation (x type)
531     "Return the documentation of X. TYPE must be the symbol VARIABLE or FUNCTION."
532     (ecase type
533       (function
534        (let ((func (fdefinition x)))
535          (oget func "docstring")))
536       (variable
537        (unless (symbolp x)
538          (error "Wrong argument type! it should be a symbol"))
539        (oget x "vardoc"))))
540
541   ;; Packages
542
543   (defvar *package-list* nil)
544
545   (defun list-all-packages ()
546     *package-list*)
547
548   (defun make-package (name &optional use)
549     (let ((package (new))
550           (use (mapcar #'find-package-or-fail use)))
551       (oset package "packageName" name)
552       (oset package "symbols" (new))
553       (oset package "exports" (new))
554       (oset package "use" use)
555       (push package *package-list*)
556       package))
557
558   (defun packagep (x)
559     (and (objectp x) (in "symbols" x)))
560
561   (defun find-package (package-designator)
562     (when (packagep package-designator)
563       (return-from find-package package-designator))
564     (let ((name (string package-designator)))
565       (dolist (package *package-list*)
566         (when (string= (package-name package) name)
567           (return package)))))
568
569   (defun find-package-or-fail (package-designator)
570     (or (find-package package-designator)
571         (error "Package unknown.")))
572
573   (defun package-name (package-designator)
574     (let ((package (find-package-or-fail package-designator)))
575       (oget package "packageName")))
576
577   (defun %package-symbols (package-designator)
578     (let ((package (find-package-or-fail package-designator)))
579       (oget package "symbols")))
580
581   (defun package-use-list (package-designator)
582     (let ((package (find-package-or-fail package-designator)))
583       (oget package "use")))
584
585   (defun %package-external-symbols (package-designator)
586     (let ((package (find-package-or-fail package-designator)))
587       (oget package "exports")))
588
589   (defvar *common-lisp-package*
590     (make-package "CL"))
591
592   (defvar *user-package*
593     (make-package "CL-USER" (list *common-lisp-package*)))
594
595   (defvar *keyword-package*
596     (make-package "KEYWORD"))
597
598   (defun keywordp (x)
599     (and (symbolp x) (eq (symbol-package x) *keyword-package*)))
600
601   (defvar *package* *common-lisp-package*)
602
603   (defmacro in-package (package-designator)
604     `(eval-when-compile
605        (setq *package* (find-package-or-fail ,package-designator))))
606
607   ;; This function is used internally to initialize the CL package
608   ;; with the symbols built during bootstrap.
609   (defun %intern-symbol (symbol)
610     (let ((symbols (%package-symbols *common-lisp-package*)))
611       (oset symbol "package" *common-lisp-package*)
612       (oset symbols (symbol-name symbol) symbol)))
613
614   (defun %find-symbol (name package)
615     (let ((package (find-package-or-fail package)))
616       (let ((symbols (%package-symbols package)))
617         (if (in name symbols)
618             (cons (oget symbols name) t)
619             (dolist (used (package-use-list package) (cons nil nil))
620               (let ((exports (%package-external-symbols used)))
621                 (when (in name exports)
622                   (return-from %find-symbol
623                     (cons (oget exports name) t)))))))))
624
625   (defun find-symbol (name &optional (package *package*))
626     (car (%find-symbol name package)))
627
628   (defun intern (name &optional (package *package*))
629     (let ((package (find-package-or-fail package)))
630       (let ((result (%find-symbol name package)))
631         (if (cdr result)
632             (car result)
633             (let ((symbols (%package-symbols package)))
634               (oget symbols name)
635               (let ((symbol (make-symbol name)))
636                 (oset symbol "package" package)
637                 (when (eq package *keyword-package*)
638                   (oset symbol "value" symbol)
639                   (export (list symbol) package))
640                 (oset symbols name symbol)))))))
641
642   (defun symbol-package (symbol)
643     (unless (symbolp symbol)
644       (error "it is not a symbol"))
645     (oget symbol "package"))
646
647   (defun export (symbols &optional (package *package*))
648     (let ((exports (%package-external-symbols package)))
649       (dolist (symb symbols t)
650         (oset exports (symbol-name symb) symb))))
651
652   (defun get-universal-time ()
653     (+ (get-unix-time) 2208988800)))
654
655
656 ;;; The compiler offers some primitives and special forms which are
657 ;;; not found in Common Lisp, for instance, while. So, we grow Common
658 ;;; Lisp a bit to it can execute the rest of the file.
659 #+common-lisp
660 (progn
661   (defmacro while (condition &body body)
662     `(do ()
663          ((not ,condition))
664        ,@body))
665
666   (defmacro eval-when-compile (&body body)
667     `(eval-when (:compile-toplevel :load-toplevel :execute)
668        ,@body))
669
670   (defun concat-two (s1 s2)
671     (concatenate 'string s1 s2))
672
673   (defun aset (array idx value)
674     (setf (aref array idx) value)))
675
676 ;;; At this point, no matter if Common Lisp or ecmalisp is compiling
677 ;;; from here, this code will compile on both. We define some helper
678 ;;; functions now for string manipulation and so on. They will be
679 ;;; useful in the compiler, mostly.
680
681 (defvar *newline* (string (code-char 10)))
682
683 (defun concat (&rest strs)
684   (!reduce #'concat-two strs ""))
685
686 (defmacro concatf (variable &body form)
687   `(setq ,variable (concat ,variable (progn ,@form))))
688
689 ;;; Concatenate a list of strings, with a separator
690 (defun join (list &optional (separator ""))
691   (cond
692     ((null list)
693      "")
694     ((null (cdr list))
695      (car list))
696     (t
697      (concat (car list)
698              separator
699              (join (cdr list) separator)))))
700
701 (defun join-trailing (list &optional (separator ""))
702   (if (null list)
703       ""
704       (concat (car list) separator (join-trailing (cdr list) separator))))
705
706 (defun mapconcat (func list)
707   (join (mapcar func list)))
708
709 (defun vector-to-list (vector)
710   (let ((list nil)
711         (size (length vector)))
712     (dotimes (i size (reverse list))
713       (push (aref vector i) list))))
714
715 (defun list-to-vector (list)
716   (let ((v (make-array (length list)))
717         (i 0))
718     (dolist (x list v)
719       (aset v i x)
720       (incf i))))
721
722 #+ecmalisp
723 (progn
724   (defun values-list (list)
725     (values-array (list-to-vector list)))
726
727   (defun values (&rest args)
728     (values-list args))
729
730   (defmacro multiple-value-bind (variables value-from &body body)
731     `(multiple-value-call (lambda (&optional ,@variables &rest ,(gensym))
732                             ,@body)
733        ,value-from))
734
735   (defmacro multiple-value-list (value-from)
736     `(multiple-value-call #'list ,value-from)))
737
738
739 ;;; Like CONCAT, but prefix each line with four spaces. Two versions
740 ;;; of this function are available, because the Ecmalisp version is
741 ;;; very slow and bootstraping was annoying.
742
743 #+ecmalisp
744 (defun indent (&rest string)
745   (let ((input (join string)))
746     (let ((output "")
747           (index 0)
748           (size (length input)))
749       (when (plusp (length input)) (concatf output "    "))
750       (while (< index size)
751         (let ((str
752                (if (and (char= (char input index) #\newline)
753                         (< index (1- size))
754                         (not (char= (char input (1+ index)) #\newline)))
755                    (concat (string #\newline) "    ")
756                    (string (char input index)))))
757           (concatf output str))
758         (incf index))
759       output)))
760
761 #+common-lisp
762 (defun indent (&rest string)
763   (with-output-to-string (*standard-output*)
764     (with-input-from-string (input (join string))
765       (loop
766          for line = (read-line input nil)
767          while line
768          do (write-string "    ")
769          do (write-line line)))))
770
771
772 (defun integer-to-string (x)
773   (cond
774     ((zerop x)
775      "0")
776     ((minusp x)
777      (concat "-" (integer-to-string (- 0 x))))
778     (t
779      (let ((digits nil))
780        (while (not (zerop x))
781          (push (mod x 10) digits)
782          (setq x (truncate x 10)))
783        (join (mapcar (lambda (d) (string (char "0123456789" d)))
784                      digits))))))
785
786
787 ;;; Wrap X with a Javascript code to convert the result from
788 ;;; Javascript generalized booleans to T or NIL.
789 (defun js!bool (x)
790   (concat "(" x "?" (ls-compile t) ": " (ls-compile nil) ")"))
791
792 ;;; Concatenate the arguments and wrap them with a self-calling
793 ;;; Javascript anonymous function. It is used to make some Javascript
794 ;;; statements valid expressions and provide a private scope as well.
795 ;;; It could be defined as function, but we could do some
796 ;;; preprocessing in the future.
797 (defmacro js!selfcall (&body body)
798   `(concat "(function(){" *newline* (indent ,@body) "})()"))
799
800
801 ;;; Printer
802
803 #+ecmalisp
804 (progn
805   (defun prin1-to-string (form)
806     (cond
807       ((symbolp form)
808        (if (cdr (%find-symbol (symbol-name form) *package*))
809            (symbol-name form)
810            (let ((package (symbol-package form))
811                  (name (symbol-name form)))
812              (concat (cond
813                        ((null package) "#")
814                        ((eq package (find-package "KEYWORD")) "")
815                        (t (package-name package)))
816                      ":" name))))
817       ((integerp form) (integer-to-string form))
818       ((stringp form) (concat "\"" (escape-string form) "\""))
819       ((functionp form)
820        (let ((name (oget form "fname")))
821          (if name
822              (concat "#<FUNCTION " name ">")
823              (concat "#<FUNCTION>"))))
824       ((listp form)
825        (concat "("
826                (join-trailing (mapcar #'prin1-to-string (butlast form)) " ")
827                (let ((last (last form)))
828                  (if (null (cdr last))
829                      (prin1-to-string (car last))
830                      (concat (prin1-to-string (car last)) " . " (prin1-to-string (cdr last)))))
831                ")"))
832       ((arrayp form)
833        (concat "#" (prin1-to-string (vector-to-list form))))
834       ((packagep form)
835        (concat "#<PACKAGE " (package-name form) ">"))))
836
837   (defun write-line (x)
838     (write-string x)
839     (write-string *newline*)
840     x)
841
842   (defun warn (string)
843     (write-string "WARNING: ")
844     (write-line string))
845
846   (defun print (x)
847     (write-line (prin1-to-string x))
848     x))
849
850
851 ;;;; Reader
852
853 ;;; The Lisp reader, parse strings and return Lisp objects. The main
854 ;;; entry points are `ls-read' and `ls-read-from-string'.
855
856 (defun make-string-stream (string)
857   (cons string 0))
858
859 (defun %peek-char (stream)
860   (and (< (cdr stream) (length (car stream)))
861        (char (car stream) (cdr stream))))
862
863 (defun %read-char (stream)
864   (and (< (cdr stream) (length (car stream)))
865        (prog1 (char (car stream) (cdr stream))
866          (rplacd stream (1+ (cdr stream))))))
867
868 (defun whitespacep (ch)
869   (or (char= ch #\space) (char= ch #\newline) (char= ch #\tab)))
870
871 (defun skip-whitespaces (stream)
872   (let (ch)
873     (setq ch (%peek-char stream))
874     (while (and ch (whitespacep ch))
875       (%read-char stream)
876       (setq ch (%peek-char stream)))))
877
878 (defun terminalp (ch)
879   (or (null ch) (whitespacep ch) (char= #\) ch) (char= #\( ch)))
880
881 (defun read-until (stream func)
882   (let ((string "")
883         (ch))
884     (setq ch (%peek-char stream))
885     (while (and ch (not (funcall func ch)))
886       (setq string (concat string (string ch)))
887       (%read-char stream)
888       (setq ch (%peek-char stream)))
889     string))
890
891 (defun skip-whitespaces-and-comments (stream)
892   (let (ch)
893     (skip-whitespaces stream)
894     (setq ch (%peek-char stream))
895     (while (and ch (char= ch #\;))
896       (read-until stream (lambda (x) (char= x #\newline)))
897       (skip-whitespaces stream)
898       (setq ch (%peek-char stream)))))
899
900 (defun %read-list (stream)
901   (skip-whitespaces-and-comments stream)
902   (let ((ch (%peek-char stream)))
903     (cond
904       ((null ch)
905        (error "Unspected EOF"))
906       ((char= ch #\))
907        (%read-char stream)
908        nil)
909       ((char= ch #\.)
910        (%read-char stream)
911        (prog1 (ls-read stream)
912          (skip-whitespaces-and-comments stream)
913          (unless (char= (%read-char stream) #\))
914            (error "')' was expected."))))
915       (t
916        (cons (ls-read stream) (%read-list stream))))))
917
918 (defun read-string (stream)
919   (let ((string "")
920         (ch nil))
921     (setq ch (%read-char stream))
922     (while (not (eql ch #\"))
923       (when (null ch)
924         (error "Unexpected EOF"))
925       (when (eql ch #\\)
926         (setq ch (%read-char stream)))
927       (setq string (concat string (string ch)))
928       (setq ch (%read-char stream)))
929     string))
930
931 (defun read-sharp (stream)
932   (%read-char stream)
933   (ecase (%read-char stream)
934     (#\'
935      (list 'function (ls-read stream)))
936     (#\( (list-to-vector (%read-list stream)))
937     (#\: (make-symbol (string-upcase (read-until stream #'terminalp))))
938     (#\\
939      (let ((cname
940             (concat (string (%read-char stream))
941                     (read-until stream #'terminalp))))
942        (cond
943          ((string= cname "space") (char-code #\space))
944          ((string= cname "tab") (char-code #\tab))
945          ((string= cname "newline") (char-code #\newline))
946          (t (char-code (char cname 0))))))
947     (#\+
948      (let ((feature (read-until stream #'terminalp)))
949        (cond
950          ((string= feature "common-lisp")
951           (ls-read stream)              ;ignore
952           (ls-read stream))
953          ((string= feature "ecmalisp")
954           (ls-read stream))
955          (t
956           (error "Unknown reader form.")))))))
957
958 ;;; Parse a string of the form NAME, PACKAGE:NAME or
959 ;;; PACKAGE::NAME and return the name. If the string is of the
960 ;;; form 1) or 3), but the symbol does not exist, it will be created
961 ;;; and interned in that package.
962 (defun read-symbol (string)
963   (let ((size (length string))
964         package name internalp index)
965     (setq index 0)
966     (while (and (< index size)
967                 (not (char= (char string index) #\:)))
968       (incf index))
969     (cond
970       ;; No package prefix
971       ((= index size)
972        (setq name string)
973        (setq package *package*)
974        (setq internalp t))
975       (t
976        ;; Package prefix
977        (if (zerop index)
978            (setq package "KEYWORD")
979            (setq package (string-upcase (subseq string 0 index))))
980        (incf index)
981        (when (char= (char string index) #\:)
982          (setq internalp t)
983          (incf index))
984        (setq name (subseq string index))))
985     ;; Canonalize symbol name and package
986     (setq name (string-upcase name))
987     (setq package (find-package package))
988     ;; TODO: PACKAGE:SYMBOL should signal error if SYMBOL is not an
989     ;; external symbol from PACKAGE.
990     (if (or internalp (eq package (find-package "KEYWORD")))
991         (intern name package)
992         (find-symbol name package))))
993
994 (defvar *eof* (gensym))
995 (defun ls-read (stream)
996   (skip-whitespaces-and-comments stream)
997   (let ((ch (%peek-char stream)))
998     (cond
999       ((or (null ch) (char= ch #\)))
1000        *eof*)
1001       ((char= ch #\()
1002        (%read-char stream)
1003        (%read-list stream))
1004       ((char= ch #\')
1005        (%read-char stream)
1006        (list 'quote (ls-read stream)))
1007       ((char= ch #\`)
1008        (%read-char stream)
1009        (list 'backquote (ls-read stream)))
1010       ((char= ch #\")
1011        (%read-char stream)
1012        (read-string stream))
1013       ((char= ch #\,)
1014        (%read-char stream)
1015        (if (eql (%peek-char stream) #\@)
1016            (progn (%read-char stream) (list 'unquote-splicing (ls-read stream)))
1017            (list 'unquote (ls-read stream))))
1018       ((char= ch #\#)
1019        (read-sharp stream))
1020       (t
1021        (let ((string (read-until stream #'terminalp)))
1022          (if (every #'digit-char-p string)
1023              (parse-integer string)
1024              (read-symbol string)))))))
1025
1026 (defun ls-read-from-string (string)
1027   (ls-read (make-string-stream string)))
1028
1029
1030 ;;;; Compiler
1031
1032 ;;; Translate the Lisp code to Javascript. It will compile the special
1033 ;;; forms. Some primitive functions are compiled as special forms
1034 ;;; too. The respective real functions are defined in the target (see
1035 ;;; the beginning of this file) as well as some primitive functions.
1036
1037 ;;; A Form can return a multiple values object calling VALUES, like
1038 ;;; values(arg1, arg2, ...). It will work in any context, as well as
1039 ;;; returning an individual object. However, if the special variable
1040 ;;; `*multiple-value-p*' is NIL, is granted that only the primary
1041 ;;; value will be used, so we can optimize to avoid the VALUES
1042 ;;; function call.
1043 (defvar *multiple-value-p* nil)
1044
1045
1046 (defun make-binding (name type value &optional declarations)
1047   (list name type value declarations))
1048
1049 (defun binding-name (b) (first b))
1050 (defun binding-type (b) (second b))
1051 (defun binding-value (b) (third b))
1052 (defun binding-declarations (b) (fourth b))
1053
1054 (defun set-binding-value (b value)
1055   (rplaca (cddr b) value))
1056
1057 (defun set-binding-declarations (b value)
1058   (rplaca (cdddr b) value))
1059
1060 (defun push-binding-declaration (decl b)
1061   (set-binding-declarations b (cons decl (binding-declarations b))))
1062
1063
1064 (defun make-lexenv ()
1065   (list nil nil nil nil))
1066
1067 (defun copy-lexenv (lexenv)
1068   (copy-list lexenv))
1069
1070 (defun push-to-lexenv (binding lexenv namespace)
1071   (ecase namespace
1072     (variable   (rplaca        lexenv  (cons binding (car lexenv))))
1073     (function   (rplaca   (cdr lexenv) (cons binding (cadr lexenv))))
1074     (block      (rplaca  (cddr lexenv) (cons binding (caddr lexenv))))
1075     (gotag      (rplaca (cdddr lexenv) (cons binding (cadddr lexenv))))))
1076
1077 (defun extend-lexenv (bindings lexenv namespace)
1078   (let ((env (copy-lexenv lexenv)))
1079     (dolist (binding (reverse bindings) env)
1080       (push-to-lexenv binding env namespace))))
1081
1082 (defun lookup-in-lexenv (name lexenv namespace)
1083   (assoc name (ecase namespace
1084                 (variable (first lexenv))
1085                 (function (second lexenv))
1086                 (block (third lexenv))
1087                 (gotag (fourth lexenv)))))
1088
1089 (defvar *environment* (make-lexenv))
1090
1091 (defvar *variable-counter* 0)
1092 (defun gvarname (symbol)
1093   (concat "v" (integer-to-string (incf *variable-counter*))))
1094
1095 (defun translate-variable (symbol)
1096   (binding-value (lookup-in-lexenv symbol *environment* 'variable)))
1097
1098 (defun extend-local-env (args)
1099   (let ((new (copy-lexenv *environment*)))
1100     (dolist (symbol args new)
1101       (let ((b (make-binding symbol 'variable (gvarname symbol))))
1102         (push-to-lexenv b new 'variable)))))
1103
1104 ;;; Toplevel compilations
1105 (defvar *toplevel-compilations* nil)
1106
1107 (defun toplevel-compilation (string)
1108   (push string *toplevel-compilations*))
1109
1110 (defun null-or-empty-p (x)
1111   (zerop (length x)))
1112
1113 (defun get-toplevel-compilations ()
1114   (reverse (remove-if #'null-or-empty-p *toplevel-compilations*)))
1115
1116 (defun %compile-defmacro (name lambda)
1117   (toplevel-compilation (ls-compile `',name))
1118   (push-to-lexenv (make-binding name 'macro lambda) *environment* 'function)
1119   name)
1120
1121 (defun global-binding (name type namespace)
1122   (or (lookup-in-lexenv name *environment* namespace)
1123       (let ((b (make-binding name type nil)))
1124         (push-to-lexenv b *environment* namespace)
1125         b)))
1126
1127 (defun claimp (symbol namespace claim)
1128   (let ((b (lookup-in-lexenv symbol *environment* namespace)))
1129     (and b (member claim (binding-declarations b)))))
1130
1131 (defun !proclaim (decl)
1132   (case (car decl)
1133     (special
1134      (dolist (name (cdr decl))
1135        (let ((b (global-binding name 'variable 'variable)))
1136          (push-binding-declaration 'special b))))
1137     (notinline
1138      (dolist (name (cdr decl))
1139        (let ((b (global-binding name 'function 'function)))
1140          (push-binding-declaration 'notinline b))))
1141     (constant
1142      (dolist (name (cdr decl))
1143        (let ((b (global-binding name 'variable 'variable)))
1144          (push-binding-declaration 'constant b))))))
1145
1146 #+ecmalisp
1147 (fset 'proclaim #'!proclaim)
1148
1149 ;;; Special forms
1150
1151 (defvar *compilations* nil)
1152
1153 (defmacro define-compilation (name args &body body)
1154   ;; Creates a new primitive `name' with parameters args and
1155   ;; @body. The body can access to the local environment through the
1156   ;; variable *ENVIRONMENT*.
1157   `(push (list ',name (lambda ,args (block ,name ,@body)))
1158          *compilations*))
1159
1160 (define-compilation if (condition true false)
1161   (concat "(" (ls-compile condition) " !== " (ls-compile nil)
1162           " ? " (ls-compile true *multiple-value-p*)
1163           " : " (ls-compile false *multiple-value-p*)
1164           ")"))
1165
1166 (defvar *lambda-list-keywords* '(&optional &rest))
1167
1168 (defun list-until-keyword (list)
1169   (if (or (null list) (member (car list) *lambda-list-keywords*))
1170       nil
1171       (cons (car list) (list-until-keyword (cdr list)))))
1172
1173 (defun lambda-list-required-arguments (lambda-list)
1174   (list-until-keyword lambda-list))
1175
1176 (defun lambda-list-optional-arguments-with-default (lambda-list)
1177   (mapcar #'ensure-list (list-until-keyword (cdr (member '&optional lambda-list)))))
1178
1179 (defun lambda-list-optional-arguments (lambda-list)
1180   (mapcar #'car (lambda-list-optional-arguments-with-default lambda-list)))
1181
1182 (defun lambda-list-rest-argument (lambda-list)
1183   (let ((rest (list-until-keyword (cdr (member '&rest lambda-list)))))
1184     (when (cdr rest)
1185       (error "Bad lambda-list"))
1186     (car rest)))
1187
1188 (defun lambda-docstring-wrapper (docstring &rest strs)
1189   (if docstring
1190       (js!selfcall
1191         "var func = " (join strs) ";" *newline*
1192         "func.docstring = '" docstring "';" *newline*
1193         "return func;" *newline*)
1194       (join strs)))
1195
1196 (defun lambda-check-argument-count
1197     (n-required-arguments n-optional-arguments rest-p)
1198   ;; Note: Remember that we assume that the number of arguments of a
1199   ;; call is at least 1 (the values argument).
1200   (let ((min (1+ n-required-arguments))
1201         (max (if rest-p 'n/a (+ 1 n-required-arguments n-optional-arguments))))
1202     (block nil
1203       ;; Special case: a positive exact number of arguments.
1204       (when (and (< 1 min) (eql min max))
1205         (return (concat "checkArgs(arguments, " (integer-to-string min) ");" *newline*)))
1206       ;; General case:
1207       (concat
1208        (if (< 1 min)
1209            (concat "checkArgsAtLeast(arguments, " (integer-to-string min) ");" *newline*)
1210            "")
1211        (if (numberp max)
1212            (concat "checkArgsAtMost(arguments, " (integer-to-string max) ");" *newline*)
1213            "")))))
1214
1215 (defun compile-lambda (lambda-list body)
1216   (let ((required-arguments (lambda-list-required-arguments lambda-list))
1217         (optional-arguments (lambda-list-optional-arguments lambda-list))
1218         (rest-argument (lambda-list-rest-argument lambda-list))
1219         documentation)
1220     ;; Get the documentation string for the lambda function
1221     (when (and (stringp (car body))
1222                (not (null (cdr body))))
1223       (setq documentation (car body))
1224       (setq body (cdr body)))
1225     (let ((n-required-arguments (length required-arguments))
1226           (n-optional-arguments (length optional-arguments))
1227           (*environment* (extend-local-env
1228                           (append (ensure-list rest-argument)
1229                                   required-arguments
1230                                   optional-arguments))))
1231       (lambda-docstring-wrapper
1232        documentation
1233        "(function ("
1234        (join (cons "values"
1235                    (mapcar #'translate-variable
1236                            (append required-arguments optional-arguments)))
1237              ",")
1238        "){" *newline*
1239        (indent
1240         ;; Check number of arguments
1241         (lambda-check-argument-count n-required-arguments
1242                                      n-optional-arguments
1243                                      rest-argument)
1244         ;; Optional arguments
1245         (if optional-arguments
1246             (concat "switch(arguments.length-1){" *newline*
1247                     (let ((optional-and-defaults
1248                            (lambda-list-optional-arguments-with-default lambda-list))
1249                           (cases nil)
1250                           (idx 0))
1251                       (progn
1252                         (while (< idx n-optional-arguments)
1253                           (let ((arg (nth idx optional-and-defaults)))
1254                             (push (concat "case "
1255                                           (integer-to-string (+ idx n-required-arguments)) ":" *newline*
1256                                           (translate-variable (car arg))
1257                                           "="
1258                                           (ls-compile (cadr arg))
1259                                           ";" *newline*)
1260                                   cases)
1261                             (incf idx)))
1262                         (push (concat "default: break;" *newline*) cases)
1263                         (join (reverse cases))))
1264                     "}" *newline*)
1265             "")
1266         ;; &rest/&body argument
1267         (if rest-argument
1268             (let ((js!rest (translate-variable rest-argument)))
1269               (concat "var " js!rest "= " (ls-compile nil) ";" *newline*
1270                       "for (var i = arguments.length-1; i>="
1271                       (integer-to-string (+ 1 n-required-arguments n-optional-arguments))
1272                       "; i--)" *newline*
1273                       (indent js!rest " = "
1274                               "{car: arguments[i], cdr: ") js!rest "};"
1275                       *newline*))
1276             "")
1277         ;; Body
1278         (let ((*multiple-value-p* t)) (ls-compile-block body t)))
1279        "})"))))
1280
1281
1282 (defun setq-pair (var val)
1283   (let ((b (lookup-in-lexenv var *environment* 'variable)))
1284     (if (and (eq (binding-type b) 'variable)
1285              (not (member 'special (binding-declarations b)))
1286              (not (member 'constant (binding-declarations b))))
1287         (concat (binding-value b) " = " (ls-compile val))
1288         (ls-compile `(set ',var ,val)))))
1289
1290 (define-compilation setq (&rest pairs)
1291   (let ((result ""))
1292     (while t
1293       (cond
1294         ((null pairs) (return))
1295         ((null (cdr pairs))
1296          (error "Odd paris in SETQ"))
1297         (t
1298          (concatf result
1299            (concat (setq-pair (car pairs) (cadr pairs))
1300                    (if (null (cddr pairs)) "" ", ")))
1301          (setq pairs (cddr pairs)))))
1302     (concat "(" result ")")))
1303
1304 ;;; FFI Variable accessors
1305 (define-compilation js-vref (var)
1306   var)
1307
1308 (define-compilation js-vset (var val)
1309   (concat "(" var " = " (ls-compile val) ")"))
1310
1311
1312
1313 ;;; Literals
1314 (defun escape-string (string)
1315   (let ((output "")
1316         (index 0)
1317         (size (length string)))
1318     (while (< index size)
1319       (let ((ch (char string index)))
1320         (when (or (char= ch #\") (char= ch #\\))
1321           (setq output (concat output "\\")))
1322         (when (or (char= ch #\newline))
1323           (setq output (concat output "\\"))
1324           (setq ch #\n))
1325         (setq output (concat output (string ch))))
1326       (incf index))
1327     output))
1328
1329
1330 (defvar *literal-symbols* nil)
1331 (defvar *literal-counter* 0)
1332
1333 (defun genlit ()
1334   (concat "l" (integer-to-string (incf *literal-counter*))))
1335
1336 (defun literal (sexp &optional recursive)
1337   (cond
1338     ((integerp sexp) (integer-to-string sexp))
1339     ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
1340     ((symbolp sexp)
1341      (or (cdr (assoc sexp *literal-symbols*))
1342          (let ((v (genlit))
1343                (s #+common-lisp (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
1344                   #+ecmalisp
1345                   (let ((package (symbol-package sexp)))
1346                     (if (null package)
1347                         (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
1348                         (ls-compile `(intern ,(symbol-name sexp) ,(package-name package)))))))
1349            (push (cons sexp v) *literal-symbols*)
1350            (toplevel-compilation (concat "var " v " = " s))
1351            v)))
1352     ((consp sexp)
1353      (let* ((head (butlast sexp))
1354             (tail (last sexp))
1355             (c (concat "QIList("
1356                        (join-trailing (mapcar (lambda (x) (literal x t)) head) ",")
1357                        (literal (car tail) t)
1358                        ","
1359                        (literal (cdr tail) t)
1360                        ")")))
1361        (if recursive
1362            c
1363            (let ((v (genlit)))
1364              (toplevel-compilation (concat "var " v " = " c))
1365              v))))
1366     ((arrayp sexp)
1367      (let ((elements (vector-to-list sexp)))
1368        (let ((c (concat "[" (join (mapcar #'literal elements) ", ") "]")))
1369          (if recursive
1370              c
1371              (let ((v (genlit)))
1372                (toplevel-compilation (concat "var " v " = " c))
1373                v)))))))
1374
1375 (define-compilation quote (sexp)
1376   (literal sexp))
1377
1378 (define-compilation %while (pred &rest body)
1379   (js!selfcall
1380     "while(" (ls-compile pred) " !== " (ls-compile nil) "){" *newline*
1381     (indent (ls-compile-block body))
1382     "}"
1383     "return " (ls-compile nil) ";" *newline*))
1384
1385 (define-compilation function (x)
1386   (cond
1387     ((and (listp x) (eq (car x) 'lambda))
1388      (compile-lambda (cadr x) (cddr x)))
1389     ((symbolp x)
1390      (ls-compile `(symbol-function ',x)))))
1391
1392 (defvar *compiling-file* nil)
1393 (define-compilation eval-when-compile (&rest body)
1394   (if *compiling-file*
1395       (progn
1396         (eval (cons 'progn body))
1397         nil)
1398       (ls-compile `(progn ,@body))))
1399
1400 (defmacro define-transformation (name args form)
1401   `(define-compilation ,name ,args
1402      (ls-compile ,form)))
1403
1404 (define-compilation progn (&rest body)
1405   (if (null (cdr body))
1406       (ls-compile (car body) *multiple-value-p*)
1407       (js!selfcall (ls-compile-block body t))))
1408
1409 (defun special-variable-p (x)
1410   (and (claimp x 'variable 'special) t))
1411
1412 ;;; Wrap CODE to restore the symbol values of the dynamic
1413 ;;; bindings. BINDINGS is a list of pairs of the form
1414 ;;; (SYMBOL . PLACE),  where PLACE is a Javascript variable
1415 ;;; name to initialize the symbol value and where to stored
1416 ;;; the old value.
1417 (defun let-binding-wrapper (bindings body)
1418   (when (null bindings)
1419     (return-from let-binding-wrapper body))
1420   (concat
1421    "try {" *newline*
1422    (indent "var tmp;" *newline*
1423            (mapconcat
1424             (lambda (b)
1425               (let ((s (ls-compile `(quote ,(car b)))))
1426                 (concat "tmp = " s ".value;" *newline*
1427                         s ".value = " (cdr b) ";" *newline*
1428                         (cdr b) " = tmp;" *newline*)))
1429             bindings)
1430            body *newline*)
1431    "}" *newline*
1432    "finally {"  *newline*
1433    (indent
1434     (mapconcat (lambda (b)
1435                  (let ((s (ls-compile `(quote ,(car b)))))
1436                    (concat s ".value" " = " (cdr b) ";" *newline*)))
1437                bindings))
1438    "}" *newline*))
1439
1440 (define-compilation let (bindings &rest body)
1441   (let* ((bindings (mapcar #'ensure-list bindings))
1442          (variables (mapcar #'first bindings))
1443          (cvalues (mapcar #'ls-compile (mapcar #'second bindings)))
1444          (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
1445          (dynamic-bindings))
1446     (concat "(function("
1447             (join (mapcar (lambda (x)
1448                             (if (special-variable-p x)
1449                                 (let ((v (gvarname x)))
1450                                   (push (cons x v) dynamic-bindings)
1451                                   v)
1452                                 (translate-variable x)))
1453                           variables)
1454                   ",")
1455             "){" *newline*
1456             (let ((body (ls-compile-block body t)))
1457               (indent (let-binding-wrapper dynamic-bindings body)))
1458             "})(" (join cvalues ",") ")")))
1459
1460
1461 ;;; Return the code to initialize BINDING, and push it extending the
1462 ;;; current lexical environment if the variable is not special.
1463 (defun let*-initialize-value (binding)
1464   (let ((var (first binding))
1465         (value (second binding)))
1466     (if (special-variable-p var)
1467         (concat (ls-compile `(setq ,var ,value)) ";" *newline*)
1468         (let* ((v (gvarname var))
1469                (b (make-binding var 'variable v)))
1470           (prog1 (concat "var " v " = " (ls-compile value) ";" *newline*)
1471             (push-to-lexenv b *environment* 'variable))))))
1472
1473 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
1474 ;;; DOES NOT generate code to initialize the value of the symbols,
1475 ;;; unlike let-binding-wrapper.
1476 (defun let*-binding-wrapper (symbols body)
1477   (when (null symbols)
1478     (return-from let*-binding-wrapper body))
1479   (let ((store (mapcar (lambda (s) (cons s (gvarname s)))
1480                        (remove-if-not #'special-variable-p symbols))))
1481     (concat
1482      "try {" *newline*
1483      (indent
1484       (mapconcat (lambda (b)
1485                    (let ((s (ls-compile `(quote ,(car b)))))
1486                      (concat "var " (cdr b) " = " s ".value;" *newline*)))
1487                  store)
1488       body)
1489      "}" *newline*
1490      "finally {" *newline*
1491      (indent
1492       (mapconcat (lambda (b)
1493                    (let ((s (ls-compile `(quote ,(car b)))))
1494                      (concat s ".value" " = " (cdr b) ";" *newline*)))
1495                  store))
1496      "}" *newline*)))
1497
1498 (define-compilation let* (bindings &rest body)
1499   (let ((bindings (mapcar #'ensure-list bindings))
1500         (*environment* (copy-lexenv *environment*)))
1501     (js!selfcall
1502       (let ((specials (remove-if-not #'special-variable-p (mapcar #'first bindings)))
1503             (body (concat (mapconcat #'let*-initialize-value bindings)
1504                           (ls-compile-block body t))))
1505         (let*-binding-wrapper specials body)))))
1506
1507
1508 (defvar *block-counter* 0)
1509
1510 (define-compilation block (name &rest body)
1511   (let* ((tr (integer-to-string (incf *block-counter*)))
1512          (b (make-binding name 'block tr)))
1513     (when *multiple-value-p*
1514       (push-binding-declaration 'multiple-value b))
1515     (let* ((*environment* (extend-lexenv (list b) *environment* 'block))
1516            (cbody (ls-compile-block body t)))
1517       (if (member 'used (binding-declarations b))
1518           (js!selfcall
1519             "try {" *newline*
1520             (indent cbody)
1521             "}" *newline*
1522             "catch (cf){" *newline*
1523             "    if (cf.type == 'block' && cf.id == " tr ")" *newline*
1524             (if *multiple-value-p*
1525                 "        return values.apply(this, forcemv(cf.values));"
1526                 "        return cf.values;")
1527             *newline*
1528             "    else" *newline*
1529             "        throw cf;" *newline*
1530             "}" *newline*)
1531           (js!selfcall cbody)))))
1532
1533 (define-compilation return-from (name &optional value)
1534   (let* ((b (lookup-in-lexenv name *environment* 'block))
1535          (multiple-value-p (member 'multiple-value (binding-declarations b))))
1536     (when (null b)
1537       (error (concat "Unknown block `" (symbol-name name) "'.")))
1538     (push-binding-declaration 'used b)
1539     (js!selfcall
1540       (if multiple-value-p
1541           (concat "var values = mv;" *newline*)
1542           "")
1543       "throw ({"
1544       "type: 'block', "
1545       "id: " (binding-value b) ", "
1546       "values: " (ls-compile value multiple-value-p) ", "
1547       "message: 'Return from unknown block " (symbol-name name) ".'"
1548       "})")))
1549
1550 (define-compilation catch (id &rest body)
1551   (js!selfcall
1552     "var id = " (ls-compile id) ";" *newline*
1553     "try {" *newline*
1554     (indent (ls-compile-block body t)) *newline*
1555     "}" *newline*
1556     "catch (cf){" *newline*
1557     "    if (cf.type == 'catch' && cf.id == id)" *newline*
1558     (if *multiple-value-p*
1559         "        return values.apply(this, forcemv(cf.values));"
1560         "        return pv.apply(this, forcemv(cf.values));")
1561     *newline*
1562     "    else" *newline*
1563     "        throw cf;" *newline*
1564     "}" *newline*))
1565
1566 (define-compilation throw (id value)
1567   (js!selfcall
1568     "var values = mv;" *newline*
1569     "throw ({"
1570     "type: 'catch', "
1571     "id: " (ls-compile id) ", "
1572     "values: " (ls-compile value t) ", "
1573     "message: 'Throw uncatched.'"
1574     "})"))
1575
1576
1577 (defvar *tagbody-counter* 0)
1578 (defvar *go-tag-counter* 0)
1579
1580 (defun go-tag-p (x)
1581   (or (integerp x) (symbolp x)))
1582
1583 (defun declare-tagbody-tags (tbidx body)
1584   (let ((bindings
1585          (mapcar (lambda (label)
1586                    (let ((tagidx (integer-to-string (incf *go-tag-counter*))))
1587                      (make-binding label 'gotag (list tbidx tagidx))))
1588                  (remove-if-not #'go-tag-p body))))
1589     (extend-lexenv bindings *environment* 'gotag)))
1590
1591 (define-compilation tagbody (&rest body)
1592   ;; Ignore the tagbody if it does not contain any go-tag. We do this
1593   ;; because 1) it is easy and 2) many built-in forms expand to a
1594   ;; implicit tagbody, so we save some space.
1595   (unless (some #'go-tag-p body)
1596     (return-from tagbody (ls-compile `(progn ,@body nil))))
1597   ;; The translation assumes the first form in BODY is a label
1598   (unless (go-tag-p (car body))
1599     (push (gensym "START") body))
1600   ;; Tagbody compilation
1601   (let ((tbidx (integer-to-string *tagbody-counter*)))
1602     (let ((*environment* (declare-tagbody-tags tbidx body))
1603           initag)
1604       (let ((b (lookup-in-lexenv (first body) *environment* 'gotag)))
1605         (setq initag (second (binding-value b))))
1606       (js!selfcall
1607         "var tagbody_" tbidx " = " initag ";" *newline*
1608         "tbloop:" *newline*
1609         "while (true) {" *newline*
1610         (indent "try {" *newline*
1611                 (indent (let ((content ""))
1612                           (concat "switch(tagbody_" tbidx "){" *newline*
1613                                   "case " initag ":" *newline*
1614                                   (dolist (form (cdr body) content)
1615                                     (concatf content
1616                                       (if (not (go-tag-p form))
1617                                           (indent (ls-compile form) ";" *newline*)
1618                                           (let ((b (lookup-in-lexenv form *environment* 'gotag)))
1619                                             (concat "case " (second (binding-value b)) ":" *newline*)))))
1620                                   "default:" *newline*
1621                                   "    break tbloop;" *newline*
1622                                   "}" *newline*)))
1623                 "}" *newline*
1624                 "catch (jump) {" *newline*
1625                 "    if (jump.type == 'tagbody' && jump.id == " tbidx ")" *newline*
1626                 "        tagbody_" tbidx " = jump.label;" *newline*
1627                 "    else" *newline*
1628                 "        throw(jump);" *newline*
1629                 "}" *newline*)
1630         "}" *newline*
1631         "return " (ls-compile nil) ";" *newline*))))
1632
1633 (define-compilation go (label)
1634   (let ((b (lookup-in-lexenv label *environment* 'gotag))
1635         (n (cond
1636              ((symbolp label) (symbol-name label))
1637              ((integerp label) (integer-to-string label)))))
1638     (if b
1639         (js!selfcall
1640           "throw ({"
1641           "type: 'tagbody', "
1642           "id: " (first (binding-value b)) ", "
1643           "label: " (second (binding-value b)) ", "
1644           "message: 'Attempt to GO to non-existing tag " n "'"
1645           "})" *newline*)
1646         (error (concat "Unknown tag `" n "'.")))))
1647
1648 (define-compilation unwind-protect (form &rest clean-up)
1649   (js!selfcall
1650     "var ret = " (ls-compile nil) ";" *newline*
1651     "try {" *newline*
1652     (indent "ret = " (ls-compile form) ";" *newline*)
1653     "} finally {" *newline*
1654     (indent (ls-compile-block clean-up))
1655     "}" *newline*
1656     "return ret;" *newline*))
1657
1658 (define-compilation multiple-value-call (func-form &rest forms)
1659   (js!selfcall
1660     "var func = " (ls-compile func-form) ";" *newline*
1661     "var args = [" (if *multiple-value-p* "values" "pv") "];" *newline*
1662     "return "
1663     (js!selfcall
1664       "var values = mv;" *newline*
1665       "var vs;" *newline*
1666       (mapconcat (lambda (form)
1667                    (concat "vs = " (ls-compile form t) ";" *newline*
1668                            "if (typeof vs === 'object' && 'multiple-value' in vs)" *newline*
1669                            (indent "args = args.concat(vs);" *newline*)
1670                            "else" *newline*
1671                            (indent "args.push(vs);" *newline*)))
1672                  forms)
1673       "return func.apply(window, args);" *newline*) ";" *newline*))
1674
1675 (define-compilation multiple-value-prog1 (first-form &rest forms)
1676   (js!selfcall
1677     "var args = " (ls-compile first-form *multiple-value-p*) ";" *newline*
1678     (ls-compile-block forms)
1679     "return args;" *newline*))
1680
1681
1682
1683 ;;; A little backquote implementation without optimizations of any
1684 ;;; kind for ecmalisp.
1685 (defun backquote-expand-1 (form)
1686   (cond
1687     ((symbolp form)
1688      (list 'quote form))
1689     ((atom form)
1690      form)
1691     ((eq (car form) 'unquote)
1692      (car form))
1693     ((eq (car form) 'backquote)
1694      (backquote-expand-1 (backquote-expand-1 (cadr form))))
1695     (t
1696      (cons 'append
1697            (mapcar (lambda (s)
1698                      (cond
1699                        ((and (listp s) (eq (car s) 'unquote))
1700                         (list 'list (cadr s)))
1701                        ((and (listp s) (eq (car s) 'unquote-splicing))
1702                         (cadr s))
1703                        (t
1704                         (list 'list (backquote-expand-1 s)))))
1705                    form)))))
1706
1707 (defun backquote-expand (form)
1708   (if (and (listp form) (eq (car form) 'backquote))
1709       (backquote-expand-1 (cadr form))
1710       form))
1711
1712 (defmacro backquote (form)
1713   (backquote-expand-1 form))
1714
1715 (define-transformation backquote (form)
1716   (backquote-expand-1 form))
1717
1718 ;;; Primitives
1719
1720 (defvar *builtins* nil)
1721
1722 (defmacro define-raw-builtin (name args &body body)
1723   ;; Creates a new primitive function `name' with parameters args and
1724   ;; @body. The body can access to the local environment through the
1725   ;; variable *ENVIRONMENT*.
1726   `(push (list ',name (lambda ,args (block ,name ,@body)))
1727          *builtins*))
1728
1729 (defmacro define-builtin (name args &body body)
1730   `(progn
1731      (define-raw-builtin ,name ,args
1732        (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg))) args)
1733          ,@body))))
1734
1735 ;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations.
1736 (defmacro type-check (decls &body body)
1737   `(js!selfcall
1738      ,@(mapcar (lambda (decl)
1739                    `(concat "var " ,(first decl) " = " ,(third decl) ";" *newline*))
1740                  decls)
1741      ,@(mapcar (lambda (decl)
1742                  `(concat "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline*
1743                           (indent "throw 'The value ' + "
1744                                   ,(first decl)
1745                                   " + ' is not a type "
1746                                   ,(second decl)
1747                                   ".';"
1748                                   *newline*)))
1749                decls)
1750      (concat "return " (progn ,@body) ";" *newline*)))
1751
1752 ;;; VARIABLE-ARITY compiles variable arity operations. ARGS stands for
1753 ;;; a variable which holds a list of forms. It will compile them and
1754 ;;; store the result in some Javascript variables. BODY is evaluated
1755 ;;; with ARGS bound to the list of these variables to generate the
1756 ;;; code which performs the transformation on these variables.
1757
1758 (defun variable-arity-call (args function)
1759   (unless (consp args)
1760     (error "ARGS must be a non-empty list"))
1761   (let ((counter 0)
1762         (variables '())
1763         (prelude ""))
1764     (dolist (x args)
1765       (let ((v (concat "x" (integer-to-string (incf counter)))))
1766         (push v variables)
1767         (concatf prelude
1768                  (concat "var " v " = " (ls-compile x) ";" *newline*
1769                          "if (typeof " v " !== 'number') throw 'Not a number!';"
1770                          *newline*))))
1771     (js!selfcall prelude (funcall function (reverse variables)))))
1772
1773
1774 (defmacro variable-arity (args &body body)
1775   (unless (symbolp args)
1776     (error "Bad usage of VARIABLE-ARITY, you must pass a symbol"))
1777   `(variable-arity-call ,args
1778                         (lambda (,args)
1779                           (concat "return " ,@body ";" *newline*))))
1780
1781 (defun num-op-num (x op y)
1782   (type-check (("x" "number" x) ("y" "number" y))
1783     (concat "x" op "y")))
1784
1785 (define-raw-builtin + (&rest numbers)
1786   (if (null numbers)
1787       "0"
1788       (variable-arity numbers
1789         (join numbers "+"))))
1790
1791 (define-raw-builtin - (x &rest others)
1792   (let ((args (cons x others)))
1793     (variable-arity args
1794       (if (null others)
1795           (concat "-" (car args))
1796           (join args "-")))))
1797
1798 (define-raw-builtin * (&rest numbers)
1799   (if (null numbers)
1800       "1"
1801       (variable-arity numbers
1802         (join numbers "*"))))
1803
1804 (define-raw-builtin / (x &rest others)
1805   (let ((args (cons x others)))
1806     (variable-arity args
1807       (if (null others)
1808           (concat "1 /" (car args))
1809           (join args "/")))))
1810
1811 (define-builtin mod (x y) (num-op-num x "%" y))
1812
1813
1814 (defun comparison-conjuntion (vars op)
1815   (cond
1816     ((null (cdr vars))
1817      "true")
1818     ((null (cddr vars))
1819      (concat (car vars) op (cadr vars)))
1820     (t
1821      (concat (car vars) op (cadr vars)
1822              " && "
1823              (comparison-conjuntion (cdr vars) op)))))
1824
1825 (defmacro define-builtin-comparison (op sym)
1826   `(define-raw-builtin ,op (x &rest args)
1827      (let ((args (cons x args)))
1828        (variable-arity args
1829          (js!bool (comparison-conjuntion args ,sym))))))
1830
1831 (define-builtin-comparison > ">")
1832 (define-builtin-comparison < "<")
1833 (define-builtin-comparison >= ">=")
1834 (define-builtin-comparison <= "<=")
1835 (define-builtin-comparison = "==")
1836
1837 (define-builtin numberp (x)
1838   (js!bool (concat "(typeof (" x ") == \"number\")")))
1839
1840 (define-builtin floor (x)
1841   (type-check (("x" "number" x))
1842     "Math.floor(x)"))
1843
1844 (define-builtin cons (x y)
1845   (concat "({car: " x ", cdr: " y "})"))
1846
1847 (define-builtin consp (x)
1848   (js!bool
1849    (js!selfcall
1850      "var tmp = " x ";" *newline*
1851      "return (typeof tmp == 'object' && 'car' in tmp);" *newline*)))
1852
1853 (define-builtin car (x)
1854   (js!selfcall
1855     "var tmp = " x ";" *newline*
1856     "return tmp === " (ls-compile nil)
1857     "? " (ls-compile nil)
1858     ": tmp.car;" *newline*))
1859
1860 (define-builtin cdr (x)
1861   (js!selfcall
1862     "var tmp = " x ";" *newline*
1863     "return tmp === " (ls-compile nil) "? "
1864     (ls-compile nil)
1865     ": tmp.cdr;" *newline*))
1866
1867 (define-builtin rplaca (x new)
1868   (type-check (("x" "object" x))
1869     (concat "(x.car = " new ", x)")))
1870
1871 (define-builtin rplacd (x new)
1872   (type-check (("x" "object" x))
1873     (concat "(x.cdr = " new ", x)")))
1874
1875 (define-builtin symbolp (x)
1876   (js!bool
1877    (js!selfcall
1878      "var tmp = " x ";" *newline*
1879      "return (typeof tmp == 'object' && 'name' in tmp);" *newline*)))
1880
1881 (define-builtin make-symbol (name)
1882   (type-check (("name" "string" name))
1883     "({name: name})"))
1884
1885 (define-builtin symbol-name (x)
1886   (concat "(" x ").name"))
1887
1888 (define-builtin set (symbol value)
1889   (concat "(" symbol ").value = " value))
1890
1891 (define-builtin fset (symbol value)
1892   (concat "(" symbol ").fvalue = " value))
1893
1894 (define-builtin boundp (x)
1895   (js!bool (concat "(" x ".value !== undefined)")))
1896
1897 (define-builtin symbol-value (x)
1898   (js!selfcall
1899     "var symbol = " x ";" *newline*
1900     "var value = symbol.value;" *newline*
1901     "if (value === undefined) throw \"Variable `\" + symbol.name + \"' is unbound.\";" *newline*
1902     "return value;" *newline*))
1903
1904 (define-builtin symbol-function (x)
1905   (js!selfcall
1906     "var symbol = " x ";" *newline*
1907     "var func = symbol.fvalue;" *newline*
1908     "if (func === undefined) throw \"Function `\" + symbol.name + \"' is undefined.\";" *newline*
1909     "return func;" *newline*))
1910
1911 (define-builtin symbol-plist (x)
1912   (concat "((" x ").plist || " (ls-compile nil) ")"))
1913
1914 (define-builtin lambda-code (x)
1915   (concat "(" x ").toString()"))
1916
1917 (define-builtin eq    (x y) (js!bool (concat "(" x " === " y ")")))
1918 (define-builtin equal (x y) (js!bool (concat "(" x  " == " y ")")))
1919
1920 (define-builtin char-to-string (x)
1921   (type-check (("x" "number" x))
1922     "String.fromCharCode(x)"))
1923
1924 (define-builtin stringp (x)
1925   (js!bool (concat "(typeof(" x ") == \"string\")")))
1926
1927 (define-builtin string-upcase (x)
1928   (type-check (("x" "string" x))
1929     "x.toUpperCase()"))
1930
1931 (define-builtin string-length (x)
1932   (type-check (("x" "string" x))
1933     "x.length"))
1934
1935 (define-raw-builtin slice (string a &optional b)
1936   (js!selfcall
1937     "var str = " (ls-compile string) ";" *newline*
1938     "var a = " (ls-compile a) ";" *newline*
1939     "var b;" *newline*
1940     (if b
1941         (concat "b = " (ls-compile b) ";" *newline*)
1942         "")
1943     "return str.slice(a,b);" *newline*))
1944
1945 (define-builtin char (string index)
1946   (type-check (("string" "string" string)
1947                ("index" "number" index))
1948     "string.charCodeAt(index)"))
1949
1950 (define-builtin concat-two (string1 string2)
1951   (type-check (("string1" "string" string1)
1952                ("string2" "string" string2))
1953     "string1.concat(string2)"))
1954
1955 (define-raw-builtin funcall (func &rest args)
1956   (concat "(" (ls-compile func) ")("
1957           (join (cons (if *multiple-value-p* "values" "pv")
1958                       (mapcar #'ls-compile args))
1959                 ", ")
1960           ")"))
1961
1962 (define-raw-builtin apply (func &rest args)
1963   (if (null args)
1964       (concat "(" (ls-compile func) ")()")
1965       (let ((args (butlast args))
1966             (last (car (last args))))
1967         (js!selfcall
1968           "var f = " (ls-compile func) ";" *newline*
1969           "var args = [" (join (cons (if *multiple-value-p* "values" "pv")
1970                                      (mapcar #'ls-compile args))
1971                                ", ")
1972           "];" *newline*
1973           "var tail = (" (ls-compile last) ");" *newline*
1974           "while (tail != " (ls-compile nil) "){" *newline*
1975           "    args.push(tail.car);" *newline*
1976           "    tail = tail.cdr;" *newline*
1977           "}" *newline*
1978           "return f.apply(this, args);" *newline*))))
1979
1980 (define-builtin js-eval (string)
1981   (type-check (("string" "string" string))
1982     (if *multiple-value-p*
1983         (js!selfcall
1984           "var v = eval.apply(window, [string]);" *newline*
1985           "if (typeof v !== 'object' || !('multiple-value' in v)){" *newline*
1986           (indent "v = [v];" *newline*
1987                   "v['multiple-value'] = true;" *newline*)
1988           "}" *newline*
1989           "return values.apply(this, v);" *newline*)
1990         "eval.apply(window, [string])")))
1991
1992 (define-builtin error (string)
1993   (js!selfcall "throw " string ";" *newline*))
1994
1995 (define-builtin new () "{}")
1996
1997 (define-builtin objectp (x)
1998   (js!bool (concat "(typeof (" x ") === 'object')")))
1999
2000 (define-builtin oget (object key)
2001   (js!selfcall
2002     "var tmp = " "(" object ")[" key "];" *newline*
2003     "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*))
2004
2005 (define-builtin oset (object key value)
2006   (concat "((" object ")[" key "] = " value ")"))
2007
2008 (define-builtin in (key object)
2009   (js!bool (concat "((" key ") in (" object "))")))
2010
2011 (define-builtin functionp (x)
2012   (js!bool (concat "(typeof " x " == 'function')")))
2013
2014 (define-builtin write-string (x)
2015   (type-check (("x" "string" x))
2016     "lisp.write(x)"))
2017
2018 (define-builtin make-array (n)
2019   (js!selfcall
2020     "var r = [];" *newline*
2021     "for (var i = 0; i < " n "; i++)" *newline*
2022     (indent "r.push(" (ls-compile nil) ");" *newline*)
2023     "return r;" *newline*))
2024
2025 (define-builtin arrayp (x)
2026   (js!bool
2027    (js!selfcall
2028      "var x = " x ";" *newline*
2029      "return typeof x === 'object' && 'length' in x;")))
2030
2031 (define-builtin aref (array n)
2032   (js!selfcall
2033     "var x = " "(" array ")[" n "];" *newline*
2034     "if (x === undefined) throw 'Out of range';" *newline*
2035     "return x;" *newline*))
2036
2037 (define-builtin aset (array n value)
2038   (js!selfcall
2039     "var x = " array ";" *newline*
2040     "var i = " n ";" *newline*
2041     "if (i < 0 || i >= x.length) throw 'Out of range';" *newline*
2042     "return x[i] = " value ";" *newline*))
2043
2044 (define-builtin get-unix-time ()
2045   (concat "(Math.round(new Date() / 1000))"))
2046
2047 (define-builtin values-array (array)
2048   (if *multiple-value-p*
2049       (concat "values.apply(this, " array ")")
2050       (concat "pv.apply(this, " array ")")))
2051
2052 (define-raw-builtin values (&rest args)
2053   (if *multiple-value-p*
2054       (concat "values(" (join (mapcar #'ls-compile args) ", ") ")")
2055       (concat "pv(" (join (mapcar #'ls-compile args) ", ") ")")))
2056
2057 (defun macro (x)
2058   (and (symbolp x)
2059        (let ((b (lookup-in-lexenv x *environment* 'function)))
2060          (and (eq (binding-type b) 'macro)
2061               b))))
2062
2063 (defun ls-macroexpand-1 (form)
2064   (let ((macro-binding (macro (car form))))
2065     (if macro-binding
2066         (let ((expander (binding-value macro-binding)))
2067           (when (listp expander)
2068             (let ((compiled (eval expander)))
2069               ;; The list representation are useful while
2070               ;; bootstrapping, as we can dump the definition of the
2071               ;; macros easily, but they are slow because we have to
2072               ;; evaluate them and compile them now and again. So, let
2073               ;; us replace the list representation version of the
2074               ;; function with the compiled one.
2075               ;;
2076               #+ecmalisp (set-binding-value macro-binding compiled)
2077               (setq expander compiled)))
2078           (apply expander (cdr form)))
2079         form)))
2080
2081 (defun compile-funcall (function args)
2082   (let ((values-funcs (if *multiple-value-p* "values" "pv")))
2083     (if (and (symbolp function)
2084              #+ecmalisp (eq (symbol-package function) (find-package "COMMON-LISP"))
2085              #+common-lisp t)
2086         (concat (ls-compile `',function) ".fvalue("
2087                 (join (cons values-funcs (mapcar #'ls-compile args))
2088                       ", ")
2089                 ")")
2090         (concat (ls-compile `#',function) "("
2091                 (join (cons values-funcs (mapcar #'ls-compile args))
2092                       ", ")
2093                 ")"))))
2094
2095 (defun ls-compile-block (sexps &optional return-last-p)
2096   (if return-last-p
2097       (concat (ls-compile-block (butlast sexps))
2098               "return " (ls-compile (car (last sexps)) *multiple-value-p*) ";")
2099       (join-trailing
2100        (remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
2101        (concat ";" *newline*))))
2102
2103 (defun ls-compile (sexp &optional multiple-value-p)
2104   (let ((*multiple-value-p* multiple-value-p))
2105     (cond
2106       ((symbolp sexp)
2107        (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
2108          (cond
2109            ((and b (not (member 'special (binding-declarations b))))
2110             (binding-value b))
2111            ((or (keywordp sexp)
2112                 (member 'constant (binding-declarations b)))
2113             (concat (ls-compile `',sexp) ".value"))
2114            (t
2115             (ls-compile `(symbol-value ',sexp))))))
2116       ((integerp sexp) (integer-to-string sexp))
2117       ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
2118       ((arrayp sexp) (literal sexp))
2119       ((listp sexp)
2120        (let ((name (car sexp))
2121              (args (cdr sexp)))
2122          (cond
2123            ;; Special forms
2124            ((assoc name *compilations*)
2125             (let ((comp (second (assoc name *compilations*))))
2126               (apply comp args)))
2127            ;; Built-in functions
2128            ((and (assoc name *builtins*)
2129                  (not (claimp name 'function 'notinline)))
2130             (let ((comp (second (assoc name *builtins*))))
2131               (apply comp args)))
2132            (t
2133             (if (macro name)
2134                 (ls-compile (ls-macroexpand-1 sexp) multiple-value-p)
2135                 (compile-funcall name args))))))
2136       (t
2137        (error "How should I compile this?")))))
2138
2139 (defun ls-compile-toplevel (sexp &optional multiple-value-p)
2140   (let ((*toplevel-compilations* nil))
2141     (cond
2142       ((and (consp sexp) (eq (car sexp) 'progn))
2143        (let ((subs (mapcar (lambda (s)
2144                              (ls-compile-toplevel s t))
2145                            (cdr sexp))))
2146          (join (remove-if #'null-or-empty-p subs))))
2147       (t
2148        (let ((code (ls-compile sexp multiple-value-p)))
2149          (concat (join-trailing (get-toplevel-compilations)
2150                                 (concat ";" *newline*))
2151                  (if code
2152                      (concat code ";" *newline*)
2153                      "")))))))
2154
2155
2156 ;;; Once we have the compiler, we define the runtime environment and
2157 ;;; interactive development (eval), which works calling the compiler
2158 ;;; and evaluating the Javascript result globally.
2159
2160 #+ecmalisp
2161 (progn
2162   (defun eval (x)
2163     (js-eval (ls-compile-toplevel x t)))
2164
2165   (export '(&rest &optional &body * *gensym-counter* *package* + - / 1+ 1- < <= =
2166             = > >= and append apply aref arrayp aset assoc atom block boundp
2167             boundp butlast caar cadddr caddr cadr car car case catch cdar cdddr
2168             cddr cdr cdr char char-code char= code-char cond cons consp copy-list
2169             decf declaim defparameter defun defmacro defvar digit-char-p
2170             disassemble do do* documentation dolist dotimes ecase eq eql equal
2171             error eval every export fdefinition find-package find-symbol first
2172             fourth fset funcall function functionp gensym get-universal-time go
2173             identity if in-package incf integerp integerp intern keywordp lambda
2174             last length let let* list-all-packages list listp make-array
2175             make-package make-symbol mapcar member minusp mod multiple-value-bind
2176             multiple-value-call multiple-value-list multiple-value-prog1 nil not
2177             nth nthcdr null numberp or package-name package-use-list packagep
2178             plusp prin1-to-string print proclaim prog1 prog2 progn psetq push
2179             quote remove remove-if remove-if-not return return-from revappend
2180             reverse rplaca rplacd second set setq some string-upcase string
2181             string= stringp subseq symbol-function symbol-name symbol-package
2182             symbol-plist symbol-value symbolp t tagbody third throw truncate
2183             unless unwind-protect values values-list variable warn when write-line
2184             write-string zerop))
2185
2186   (setq *package* *user-package*)
2187
2188   (js-eval "var lisp")
2189   (js-vset "lisp" (new))
2190   (js-vset "lisp.read" #'ls-read-from-string)
2191   (js-vset "lisp.print" #'prin1-to-string)
2192   (js-vset "lisp.eval" #'eval)
2193   (js-vset "lisp.compile" (lambda (s) (ls-compile-toplevel s t)))
2194   (js-vset "lisp.evalString" (lambda (str) (eval (ls-read-from-string str))))
2195   (js-vset "lisp.compileString" (lambda (str) (ls-compile-toplevel (ls-read-from-string str) t)))
2196
2197   ;; Set the initial global environment to be equal to the host global
2198   ;; environment at this point of the compilation.
2199   (eval-when-compile
2200     (toplevel-compilation
2201      (ls-compile
2202       `(progn
2203          ,@(mapcar (lambda (s) `(%intern-symbol (js-vref ,(cdr s))))
2204                    *literal-symbols*)
2205          (setq *literal-symbols* ',*literal-symbols*)
2206          (setq *environment* ',*environment*)
2207          (setq *variable-counter* ,*variable-counter*)
2208          (setq *gensym-counter* ,*gensym-counter*)
2209          (setq *block-counter* ,*block-counter*)))))
2210
2211   (eval-when-compile
2212     (toplevel-compilation
2213      (ls-compile
2214       `(setq *literal-counter* ,*literal-counter*)))))
2215
2216
2217 ;;; Finally, we provide a couple of functions to easily bootstrap
2218 ;;; this. It just calls the compiler with this file as input.
2219
2220 #+common-lisp
2221 (progn
2222   (defun read-whole-file (filename)
2223     (with-open-file (in filename)
2224       (let ((seq (make-array (file-length in) :element-type 'character)))
2225         (read-sequence seq in)
2226         seq)))
2227
2228   (defun ls-compile-file (filename output)
2229     (let ((*compiling-file* t))
2230       (with-open-file (out output :direction :output :if-exists :supersede)
2231         (write-string (read-whole-file "prelude.js") out)
2232         (let* ((source (read-whole-file filename))
2233                (in (make-string-stream source)))
2234           (loop
2235              for x = (ls-read in)
2236              until (eq x *eof*)
2237              for compilation = (ls-compile-toplevel x)
2238              when (plusp (length compilation))
2239              do (write-string compilation out))))))
2240
2241   (defun bootstrap ()
2242     (setq *environment* (make-lexenv))
2243     (setq *literal-symbols* nil)
2244     (setq *variable-counter* 0
2245           *gensym-counter* 0
2246           *literal-counter* 0
2247           *block-counter* 0)
2248     (ls-compile-file "ecmalisp.lisp" "ecmalisp.js")))