Implement TAGBODY and GO special forms
[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 #+ecmalisp
25 (progn
26   (eval-when-compile
27     (%compile-defmacro 'defmacro
28                        '(lambda (name args &rest body)
29                          `(eval-when-compile
30                             (%compile-defmacro ',name
31                                                '(lambda ,(mapcar (lambda (x)
32                                                                    (if (eq x '&body)
33                                                                        '&rest
34                                                                        x))
35                                                                  args)
36                                                  ,@body))))))
37
38   (defmacro %defvar (name value)
39     `(progn
40        (eval-when-compile
41          (%compile-defvar ',name))
42        (setq ,name ,value)))
43
44   (defmacro defvar (name &optional value)
45     `(%defvar ,name ,value))
46
47   (defmacro named-lambda (name args &rest body)
48     (let ((x (gensym "FN")))
49       `(let ((,x (lambda ,args ,@body)))
50          (set ,x "fname" ,name)
51          ,x)))
52
53   (defmacro %defun (name args &rest body)
54     `(progn
55        (eval-when-compile
56          (%compile-defun ',name))
57        (fsetq ,name (named-lambda ,(symbol-name name) ,args
58                       (block ,name ,@body)))))
59
60   (defmacro defun (name args &rest body)
61     `(%defun ,name ,args ,@body))
62
63   (defvar *package* (new))
64
65   (defvar nil (make-symbol "NIL"))
66   (set *package* "NIL" nil)
67
68   (defvar t (make-symbol "T"))
69   (set *package* "T" t)
70
71   (defun null (x)
72     (eq x nil))
73
74   (defmacro return (&optional value)
75     `(return-from nil ,value))
76
77   (defmacro while (condition &body body)
78     `(block nil (%while ,condition ,@body)))
79
80   (defun internp (name)
81     (in name *package*))
82
83   (defun intern (name)
84     (if (internp name)
85         (get *package* name)
86         (set *package* name (make-symbol name))))
87
88   (defun find-symbol (name)
89     (get *package* name))
90
91   (defvar *gensym-counter* 0)
92   (defun gensym (&optional (prefix "G"))
93     (setq *gensym-counter* (+ *gensym-counter* 1))
94     (make-symbol (concat-two prefix (integer-to-string *gensym-counter*))))
95
96   ;; Basic functions
97   (defun = (x y) (= x y))
98   (defun + (x y) (+ x y))
99   (defun - (x y) (- x y))
100   (defun * (x y) (* x y))
101   (defun / (x y) (/ x y))
102   (defun 1+ (x) (+ x 1))
103   (defun 1- (x) (- x 1))
104   (defun zerop (x) (= x 0))
105   (defun truncate (x y) (floor (/ x y)))
106
107   (defun eql (x y) (eq x y))
108
109   (defun not (x) (if x nil t))
110
111   (defun cons (x y ) (cons x y))
112   (defun consp (x) (consp x))
113   (defun car (x) (car x))
114   (defun cdr (x) (cdr x))
115   (defun caar (x) (car (car x)))
116   (defun cadr (x) (car (cdr x)))
117   (defun cdar (x) (cdr (car x)))
118   (defun cddr (x) (cdr (cdr x)))
119   (defun caddr (x) (car (cdr (cdr x))))
120   (defun cdddr (x) (cdr (cdr (cdr x))))
121   (defun cadddr (x) (car (cdr (cdr (cdr x)))))
122   (defun first (x) (car x))
123   (defun second (x) (cadr x))
124   (defun third (x) (caddr x))
125   (defun fourth (x) (cadddr x))
126
127   (defun list (&rest args) args)
128   (defun atom (x)
129     (not (consp x)))
130
131   ;; Basic macros
132
133   (defmacro incf (x &optional (delta 1))
134     `(setq ,x (+ ,x ,delta)))
135
136   (defmacro decf (x &optional (delta 1))
137     `(setq ,x (- ,x ,delta)))
138
139   (defmacro push (x place)
140     `(setq ,place (cons ,x ,place)))
141
142   (defmacro when (condition &body body)
143     `(if ,condition (progn ,@body) nil))
144
145   (defmacro unless (condition &body body)
146     `(if ,condition nil (progn ,@body)))
147
148   (defmacro dolist (iter &body body)
149     (let ((var (first iter))
150           (g!list (gensym)))
151       `(block nil
152          (let ((,g!list ,(second iter))
153                (,var nil))
154            (%while ,g!list
155                    (setq ,var (car ,g!list))
156                    ,@body
157                    (setq ,g!list (cdr ,g!list)))
158            ,(third iter)))))
159
160   (defmacro dotimes (iter &body body)
161     (let ((g!to (gensym))
162           (var (first iter))
163           (to (second iter))
164           (result (third iter)))
165       `(block nil
166          (let ((,var 0)
167                (,g!to ,to))
168            (%while (< ,var ,g!to)
169                    ,@body
170                    (incf ,var))
171            ,result))))
172
173   (defmacro cond (&rest clausules)
174     (if (null clausules)
175         nil
176         (if (eq (caar clausules) t)
177             `(progn ,@(cdar clausules))
178             `(if ,(caar clausules)
179                  (progn ,@(cdar clausules))
180                  (cond ,@(cdr clausules))))))
181
182   (defmacro case (form &rest clausules)
183     (let ((!form (gensym)))
184       `(let ((,!form ,form))
185          (cond
186            ,@(mapcar (lambda (clausule)
187                        (if (eq (car clausule) t)
188                            clausule
189                            `((eql ,!form ',(car clausule))
190                              ,@(cdr clausule))))
191                      clausules)))))
192
193   (defmacro ecase (form &rest clausules)
194     `(case ,form
195        ,@(append
196           clausules
197           `((t
198              (error "ECASE expression failed."))))))
199
200   (defmacro and (&rest forms)
201     (cond
202       ((null forms)
203        t)
204       ((null (cdr forms))
205        (car forms))
206       (t
207        `(if ,(car forms)
208             (and ,@(cdr forms))
209             nil))))
210
211   (defmacro or (&rest forms)
212     (cond
213       ((null forms)
214        nil)
215       ((null (cdr forms))
216        (car forms))
217       (t
218        (let ((g (gensym)))
219          `(let ((,g ,(car forms)))
220             (if ,g ,g (or ,@(cdr forms))))))))
221
222   (defmacro prog1 (form &body body)
223     (let ((value (gensym)))
224       `(let ((,value ,form))
225          ,@body
226          ,value))))
227
228 ;;; This couple of helper functions will be defined in both Common
229 ;;; Lisp and in Ecmalisp.
230 (defun ensure-list (x)
231   (if (listp x)
232       x
233       (list x)))
234
235 (defun !reduce (func list initial)
236   (if (null list)
237       initial
238       (!reduce func
239                (cdr list)
240                (funcall func initial (car list)))))
241
242 ;;; Go on growing the Lisp language in Ecmalisp, with more high
243 ;;; level utilities as well as correct versions of other
244 ;;; constructions.
245 #+ecmalisp
246 (progn
247   (defmacro defun (name args &body body)
248     `(progn
249        (%defun ,name ,args ,@body)
250        ',name))
251
252   (defmacro defvar (name &optional value)
253     `(progn
254        (%defvar ,name ,value)
255        ',name))
256
257   (defun append-two (list1 list2)
258     (if (null list1)
259         list2
260         (cons (car list1)
261               (append (cdr list1) list2))))
262
263   (defun append (&rest lists)
264     (!reduce #'append-two lists '()))
265
266   (defun reverse-aux (list acc)
267     (if (null list)
268         acc
269         (reverse-aux (cdr list) (cons (car list) acc))))
270
271   (defun reverse (list)
272     (reverse-aux list '()))
273
274   (defun list-length (list)
275     (let ((l 0))
276       (while (not (null list))
277         (incf l)
278         (setq list (cdr list)))
279       l))
280
281   (defun length (seq)
282     (if (stringp seq)
283         (string-length seq)
284         (list-length seq)))
285
286   (defun concat-two (s1 s2)
287     (concat-two s1 s2))
288
289   (defun mapcar (func list)
290     (if (null list)
291         '()
292         (cons (funcall func (car list))
293               (mapcar func (cdr list)))))
294
295   (defun identity (x) x)
296
297   (defun copy-list (x)
298     (mapcar #'identity x))
299
300   (defun code-char (x) x)
301   (defun char-code (x) x)
302   (defun char= (x y) (= x y))
303
304   (defun integerp (x)
305     (and (numberp x) (= (floor x) x)))
306
307   (defun plusp (x) (< 0 x))
308   (defun minusp (x) (< x 0))
309
310   (defun listp (x)
311     (or (consp x) (null x)))
312
313   (defun nth (n list)
314     (cond
315       ((null list) list)
316       ((zerop n) (car list))
317       (t (nth (1- n) (cdr list)))))
318
319   (defun last (x)
320     (if (consp (cdr x))
321         (last (cdr x))
322         x))
323
324   (defun butlast (x)
325     (and (consp (cdr x))
326          (cons (car x) (butlast (cdr x)))))
327
328   (defun member (x list)
329     (cond
330       ((null list)
331        nil)
332       ((eql x (car list))
333        list)
334       (t
335        (member x (cdr list)))))
336
337   (defun remove (x list)
338     (cond
339       ((null list)
340        nil)
341       ((eql x (car list))
342        (remove x (cdr list)))
343       (t
344        (cons (car list) (remove x (cdr list))))))
345
346   (defun remove-if (func list)
347     (cond
348       ((null list)
349        nil)
350       ((funcall func (car list))
351        (remove-if func (cdr list)))
352       (t
353        (cons (car list) (remove-if func (cdr list))))))
354
355   (defun remove-if-not (func list)
356     (cond
357       ((null list)
358        nil)
359       ((funcall func (car list))
360        (cons (car list) (remove-if-not func (cdr list))))
361       (t
362        (remove-if-not func (cdr list)))))
363
364   (defun digit-char-p (x)
365     (if (and (<= #\0 x) (<= x #\9))
366         (- x #\0)
367         nil))
368
369   (defun subseq (seq a &optional b)
370     (cond
371       ((stringp seq)
372        (if b
373            (slice seq a b)
374            (slice seq a)))
375       (t
376        (error "Unsupported argument."))))
377
378   (defun parse-integer (string)
379     (let ((value 0)
380           (index 0)
381           (size (length string)))
382       (while (< index size)
383         (setq value (+ (* value 10) (digit-char-p (char string index))))
384         (incf index))
385       value))
386
387   (defun some (function seq)
388     (cond
389       ((stringp seq)
390        (let ((index 0)
391              (size (length seq)))
392          (while (< index size)
393            (when (funcall function (char seq index))
394              (return-from some t))
395            (incf index))
396          nil))
397       ((listp seq)
398        (dolist (x seq nil)
399          (when (funcall function x)
400            (return t))))
401       (t
402        (error "Unknown sequence."))))
403
404   (defun every (function seq)
405     (cond
406       ((stringp seq)
407        (let ((index 0)
408              (size (length seq)))
409          (while (< index size)
410            (unless (funcall function (char seq index))
411              (return-from every nil))
412            (incf index))
413          t))
414       ((listp seq)
415        (dolist (x seq t)
416          (unless (funcall function x)
417            (return))))
418       (t
419        (error "Unknown sequence."))))
420
421   (defun assoc (x alist)
422     (while alist
423       (if (eql x (caar alist))
424           (return)
425           (setq alist (cdr alist))))
426     (car alist))
427
428   (defun string= (s1 s2)
429     (equal s1 s2)))
430
431
432 ;;; The compiler offers some primitives and special forms which are
433 ;;; not found in Common Lisp, for instance, while. So, we grow Common
434 ;;; Lisp a bit to it can execute the rest of the file.
435 #+common-lisp
436 (progn
437   (defmacro while (condition &body body)
438     `(do ()
439          ((not ,condition))
440        ,@body))
441
442   (defmacro eval-when-compile (&body body)
443     `(eval-when (:compile-toplevel :load-toplevel :execute)
444        ,@body))
445
446   (defun concat-two (s1 s2)
447     (concatenate 'string s1 s2))
448
449   (defun setcar (cons new)
450     (setf (car cons) new))
451   (defun setcdr (cons new)
452     (setf (cdr cons) new)))
453
454 ;;; At this point, no matter if Common Lisp or ecmalisp is compiling
455 ;;; from here, this code will compile on both. We define some helper
456 ;;; functions now for string manipulation and so on. They will be
457 ;;; useful in the compiler, mostly.
458
459 (defvar *newline* (string (code-char 10)))
460
461 (defun concat (&rest strs)
462   (!reduce #'concat-two strs ""))
463
464 (defmacro concatf (variable &body form)
465   `(setq ,variable (concat ,variable (progn ,@form))))
466
467 ;;; Concatenate a list of strings, with a separator
468 (defun join (list &optional (separator ""))
469   (cond
470     ((null list)
471      "")
472     ((null (cdr list))
473      (car list))
474     (t
475      (concat (car list)
476              separator
477              (join (cdr list) separator)))))
478
479 (defun join-trailing (list &optional (separator ""))
480   (if (null list)
481       ""
482       (concat (car list) separator (join-trailing (cdr list) separator))))
483
484 ;;; Like CONCAT, but prefix each line with four spaces.
485 (defun indent (&rest string)
486   (let ((input (join string)))
487     (let ((output "")
488           (index 0)
489           (size (length input)))
490       (when (plusp size)
491         (setq output "    "))
492       (while (< index size)
493         (setq output
494               (concat output
495                       (if (and (char= (char input index) #\newline)
496                                (< index (1- size))
497                                (not (char= (char input (1+ index)) #\newline)))
498                           (concat (string #\newline) "    ")
499                           (subseq input index (1+ index)))))
500         (incf index))
501       output)))
502
503 (defun integer-to-string (x)
504   (cond
505     ((zerop x)
506      "0")
507     ((minusp x)
508      (concat "-" (integer-to-string (- 0 x))))
509     (t
510      (let ((digits nil))
511        (while (not (zerop x))
512          (push (mod x 10) digits)
513          (setq x (truncate x 10)))
514        (join (mapcar (lambda (d) (string (char "0123456789" d)))
515                      digits))))))
516
517
518 (defun js!selfcall (&rest args)
519   (concat "(function(){" *newline* (apply #'indent args) "})()"))
520
521
522 ;;; Printer
523
524 #+ecmalisp
525 (progn
526   (defun prin1-to-string (form)
527     (cond
528       ((symbolp form) (symbol-name form))
529       ((integerp form) (integer-to-string form))
530       ((stringp form) (concat "\"" (escape-string form) "\""))
531       ((functionp form)
532        (let ((name (get form "fname")))
533          (if name
534              (concat "#<FUNCTION " name ">")
535              (concat "#<FUNCTION>"))))
536       ((listp form)
537        (concat "("
538                (join-trailing (mapcar #'prin1-to-string (butlast form)) " ")
539                (let ((last (last form)))
540                  (if (null (cdr last))
541                      (prin1-to-string (car last))
542                      (concat (prin1-to-string (car last)) " . " (prin1-to-string (cdr last)))))
543                ")"))))
544
545   (defun write-line (x)
546     (write-string x)
547     (write-string *newline*)
548     x)
549
550   (defun print (x)
551     (write-line (prin1-to-string x))
552     x))
553
554
555 ;;;; Reader
556
557 ;;; The Lisp reader, parse strings and return Lisp objects. The main
558 ;;; entry points are `ls-read' and `ls-read-from-string'.
559
560 (defun make-string-stream (string)
561   (cons string 0))
562
563 (defun %peek-char (stream)
564   (and (< (cdr stream) (length (car stream)))
565        (char (car stream) (cdr stream))))
566
567 (defun %read-char (stream)
568   (and (< (cdr stream) (length (car stream)))
569        (prog1 (char (car stream) (cdr stream))
570          (setcdr stream (1+ (cdr stream))))))
571
572 (defun whitespacep (ch)
573   (or (char= ch #\space) (char= ch #\newline) (char= ch #\tab)))
574
575 (defun skip-whitespaces (stream)
576   (let (ch)
577     (setq ch (%peek-char stream))
578     (while (and ch (whitespacep ch))
579       (%read-char stream)
580       (setq ch (%peek-char stream)))))
581
582 (defun terminalp (ch)
583   (or (null ch) (whitespacep ch) (char= #\) ch) (char= #\( ch)))
584
585 (defun read-until (stream func)
586   (let ((string "")
587         (ch))
588     (setq ch (%peek-char stream))
589     (while (and ch (not (funcall func ch)))
590       (setq string (concat string (string ch)))
591       (%read-char stream)
592       (setq ch (%peek-char stream)))
593     string))
594
595 (defun skip-whitespaces-and-comments (stream)
596   (let (ch)
597     (skip-whitespaces stream)
598     (setq ch (%peek-char stream))
599     (while (and ch (char= ch #\;))
600       (read-until stream (lambda (x) (char= x #\newline)))
601       (skip-whitespaces stream)
602       (setq ch (%peek-char stream)))))
603
604 (defun %read-list (stream)
605   (skip-whitespaces-and-comments stream)
606   (let ((ch (%peek-char stream)))
607     (cond
608       ((null ch)
609        (error "Unspected EOF"))
610       ((char= ch #\))
611        (%read-char stream)
612        nil)
613       ((char= ch #\.)
614        (%read-char stream)
615        (prog1 (ls-read stream)
616          (skip-whitespaces-and-comments stream)
617          (unless (char= (%read-char stream) #\))
618            (error "')' was expected."))))
619       (t
620        (cons (ls-read stream) (%read-list stream))))))
621
622 (defun read-string (stream)
623   (let ((string "")
624         (ch nil))
625     (setq ch (%read-char stream))
626     (while (not (eql ch #\"))
627       (when (null ch)
628         (error "Unexpected EOF"))
629       (when (eql ch #\\)
630         (setq ch (%read-char stream)))
631       (setq string (concat string (string ch)))
632       (setq ch (%read-char stream)))
633     string))
634
635 (defun read-sharp (stream)
636   (%read-char stream)
637   (ecase (%read-char stream)
638     (#\'
639      (list 'function (ls-read stream)))
640     (#\\
641      (let ((cname
642             (concat (string (%read-char stream))
643                     (read-until stream #'terminalp))))
644        (cond
645          ((string= cname "space") (char-code #\space))
646          ((string= cname "tab") (char-code #\tab))
647          ((string= cname "newline") (char-code #\newline))
648          (t (char-code (char cname 0))))))
649     (#\+
650      (let ((feature (read-until stream #'terminalp)))
651        (cond
652          ((string= feature "common-lisp")
653           (ls-read stream)              ;ignore
654           (ls-read stream))
655          ((string= feature "ecmalisp")
656           (ls-read stream))
657          (t
658           (error "Unknown reader form.")))))))
659
660 (defvar *eof* (make-symbol "EOF"))
661 (defun ls-read (stream)
662   (skip-whitespaces-and-comments stream)
663   (let ((ch (%peek-char stream)))
664     (cond
665       ((null ch)
666        *eof*)
667       ((char= ch #\()
668        (%read-char stream)
669        (%read-list stream))
670       ((char= ch #\')
671        (%read-char stream)
672        (list 'quote (ls-read stream)))
673       ((char= ch #\`)
674        (%read-char stream)
675        (list 'backquote (ls-read stream)))
676       ((char= ch #\")
677        (%read-char stream)
678        (read-string stream))
679       ((char= ch #\,)
680        (%read-char stream)
681        (if (eql (%peek-char stream) #\@)
682            (progn (%read-char stream) (list 'unquote-splicing (ls-read stream)))
683            (list 'unquote (ls-read stream))))
684       ((char= ch #\#)
685        (read-sharp stream))
686       (t
687        (let ((string (read-until stream #'terminalp)))
688          (if (every #'digit-char-p string)
689              (parse-integer string)
690              (intern (string-upcase string))))))))
691
692 (defun ls-read-from-string (string)
693   (ls-read (make-string-stream string)))
694
695
696 ;;;; Compiler
697
698 ;;; Translate the Lisp code to Javascript. It will compile the special
699 ;;; forms. Some primitive functions are compiled as special forms
700 ;;; too. The respective real functions are defined in the target (see
701 ;;; the beginning of this file) as well as some primitive functions.
702
703 (defvar *compilation-unit-checks* '())
704
705 (defun make-binding (name type translation declared)
706   (list name type translation declared))
707
708 (defun binding-name (b) (first b))
709 (defun binding-type (b) (second b))
710 (defun binding-translation (b) (third b))
711 (defun binding-declared (b)
712   (and b (fourth b)))
713 (defun mark-binding-as-declared (b)
714   (setcar (cdddr b) t))
715
716 (defun make-lexenv ()
717   (list nil nil nil nil))
718
719 (defun copy-lexenv (lexenv)
720   (copy-list lexenv))
721
722 (defun push-to-lexenv (binding lexenv namespace)
723   (ecase namespace
724     (variable
725      (setcar lexenv (cons binding (car lexenv))))
726     (function
727      (setcar (cdr lexenv) (cons binding (cadr lexenv))))
728     (block
729      (setcar (cddr lexenv) (cons binding (caddr lexenv))))
730     (gotag
731      (setcar (cdddr lexenv) (cons binding (cadddr lexenv))))))
732
733 (defun extend-lexenv (bindings lexenv namespace)
734   (let ((env (copy-lexenv lexenv)))
735     (dolist (binding (reverse bindings) env)
736       (push-to-lexenv binding env namespace))))
737
738 (defun lookup-in-lexenv (name lexenv namespace)
739   (assoc name (ecase namespace
740                 (variable (first lexenv))
741                 (function (second lexenv))
742                 (block (third lexenv))
743                 (gotag (fourth lexenv)))))
744
745 (defvar *environment* (make-lexenv))
746
747 (defun clear-undeclared-global-bindings ()
748   (let ((variables (first *environment*))
749         (functions (second *environment*)))
750     (setq *environment* (list variables functions (third *environment*)))))
751
752
753 (defvar *variable-counter* 0)
754 (defun gvarname (symbol)
755   (concat "v" (integer-to-string (incf *variable-counter*))))
756
757 (defun lookup-variable (symbol env)
758   (or (lookup-in-lexenv symbol env 'variable)
759       (lookup-in-lexenv symbol *environment* 'variable)
760       (let ((name (symbol-name symbol))
761             (binding (make-binding symbol 'variable (gvarname symbol) nil)))
762         (push-to-lexenv binding *environment* 'variable)
763         (push (lambda ()
764                 (unless (lookup-in-lexenv symbol *environment* 'variable)
765                   (error (concat "Undefined variable `" name "'"))))
766               *compilation-unit-checks*)
767         binding)))
768
769 (defun lookup-variable-translation (symbol env)
770   (binding-translation (lookup-variable symbol env)))
771
772 (defun extend-local-env (args env)
773   (let ((new (copy-lexenv env)))
774     (dolist (symbol args new)
775       (let ((b (make-binding symbol 'variable (gvarname symbol) t)))
776         (push-to-lexenv b new 'variable)))))
777
778 (defvar *function-counter* 0)
779 (defun lookup-function (symbol env)
780   (or (lookup-in-lexenv symbol env 'function)
781       (lookup-in-lexenv symbol *environment* 'function)
782       (let ((name (symbol-name symbol))
783             (binding
784              (make-binding symbol
785                            'function
786                            (concat "f" (integer-to-string (incf *function-counter*)))
787                            nil)))
788         (push-to-lexenv binding *environment* 'function)
789         (push (lambda ()
790                 (unless (binding-declared (lookup-in-lexenv symbol *environment* 'function))
791                   (error (concat "Undefined function `" name "'"))))
792               *compilation-unit-checks*)
793         binding)))
794
795 (defun lookup-function-translation (symbol env)
796   (binding-translation (lookup-function symbol env)))
797
798 (defvar *toplevel-compilations* nil)
799
800 (defun %compile-defvar (name)
801   (let ((b (lookup-variable name *environment*)))
802     (mark-binding-as-declared b)
803     (push (concat "var " (binding-translation b)) *toplevel-compilations*)))
804
805 (defun %compile-defun (name)
806   (let ((b (lookup-function name *environment*)))
807     (mark-binding-as-declared b)
808     (push (concat "var " (binding-translation b)) *toplevel-compilations*)))
809
810 (defun %compile-defmacro (name lambda)
811   (push-to-lexenv (make-binding name 'macro lambda t) *environment* 'function))
812
813 (defvar *compilations* nil)
814
815 (defun ls-compile-block (sexps env)
816   (join-trailing
817    (remove-if (lambda (x)
818                 (or (null x)
819                     (and (stringp x)
820                          (zerop (length x)))))
821               (mapcar (lambda (x) (ls-compile x env))  sexps))
822    (concat ";" *newline*)))
823
824 (defmacro define-compilation (name args &body body)
825   ;; Creates a new primitive `name' with parameters args and
826   ;; @body. The body can access to the local environment through the
827   ;; variable ENV.
828   `(push (list ',name (lambda (env ,@args) (block ,name ,@body)))
829          *compilations*))
830
831 (define-compilation if (condition true false)
832   (concat "("
833           (ls-compile condition env) " !== " (ls-compile nil)
834           " ? "
835           (ls-compile true env)
836           " : "
837           (ls-compile false env)
838           ")"))
839
840
841 (defvar *lambda-list-keywords* '(&optional &rest))
842
843 (defun list-until-keyword (list)
844   (if (or (null list) (member (car list) *lambda-list-keywords*))
845       nil
846       (cons (car list) (list-until-keyword (cdr list)))))
847
848 (defun lambda-list-required-arguments (lambda-list)
849   (list-until-keyword lambda-list))
850
851 (defun lambda-list-optional-arguments-with-default (lambda-list)
852   (mapcar #'ensure-list (list-until-keyword (cdr (member '&optional lambda-list)))))
853
854 (defun lambda-list-optional-arguments (lambda-list)
855   (mapcar #'car (lambda-list-optional-arguments-with-default lambda-list)))
856
857 (defun lambda-list-rest-argument (lambda-list)
858   (let ((rest (list-until-keyword (cdr (member '&rest lambda-list)))))
859     (when (cdr rest)
860       (error "Bad lambda-list"))
861     (car rest)))
862
863 (define-compilation lambda (lambda-list &rest body)
864   (let ((required-arguments (lambda-list-required-arguments lambda-list))
865         (optional-arguments (lambda-list-optional-arguments lambda-list))
866         (rest-argument (lambda-list-rest-argument lambda-list)))
867     (let ((n-required-arguments (length required-arguments))
868           (n-optional-arguments (length optional-arguments))
869           (new-env (extend-local-env
870                     (append (ensure-list rest-argument)
871                             required-arguments
872                             optional-arguments)
873                     env)))
874       (concat "(function ("
875               (join (mapcar (lambda (x)
876                               (lookup-variable-translation x new-env))
877                             (append required-arguments optional-arguments))
878                     ",")
879               "){" *newline*
880               ;; Check number of arguments
881               (indent
882                (if required-arguments
883                    (concat "if (arguments.length < " (integer-to-string n-required-arguments)
884                            ") throw 'too few arguments';" *newline*)
885                    "")
886                (if (not rest-argument)
887                    (concat "if (arguments.length > "
888                            (integer-to-string (+ n-required-arguments n-optional-arguments))
889                            ") throw 'too many arguments';" *newline*)
890                    "")
891                ;; Optional arguments
892                (if optional-arguments
893                    (concat "switch(arguments.length){" *newline*
894                            (let ((optional-and-defaults
895                                   (lambda-list-optional-arguments-with-default lambda-list))
896                                  (cases nil)
897                                  (idx 0))
898                              (progn
899                                (while (< idx n-optional-arguments)
900                                  (let ((arg (nth idx optional-and-defaults)))
901                                    (push (concat "case "
902                                                  (integer-to-string (+ idx n-required-arguments)) ":" *newline*
903                                                  (lookup-variable-translation (car arg) new-env)
904                                                  "="
905                                                  (ls-compile (cadr arg) new-env)
906                                                  ";" *newline*)
907                                          cases)
908                                    (incf idx)))
909                                     (push (concat "default: break;" *newline*) cases)
910                                     (join (reverse cases))))
911                            "}" *newline*)
912                    "")
913                ;; &rest/&body argument
914                (if rest-argument
915                    (let ((js!rest (lookup-variable-translation rest-argument new-env)))
916                      (concat "var " js!rest "= " (ls-compile nil) ";" *newline*
917                              "for (var i = arguments.length-1; i>="
918                              (integer-to-string (+ n-required-arguments n-optional-arguments))
919                              "; i--)" *newline*
920                              (indent js!rest " = "
921                                      "{car: arguments[i], cdr: ") js!rest "};"
922                                      *newline*))
923                    "")
924                ;; Body
925                (concat (ls-compile-block (butlast body) new-env)
926                        "return " (ls-compile (car (last body)) new-env) ";")) *newline*
927               "})"))))
928
929 (define-compilation fsetq (var val)
930   (concat (lookup-function-translation var env)
931           " = "
932           (ls-compile val env)))
933
934 (define-compilation setq (var val)
935   (concat (lookup-variable-translation var env)
936           " = "
937            (ls-compile val env)))
938
939 ;;; Literals
940 (defun escape-string (string)
941   (let ((output "")
942         (index 0)
943         (size (length string)))
944     (while (< index size)
945       (let ((ch (char string index)))
946         (when (or (char= ch #\") (char= ch #\\))
947           (setq output (concat output "\\")))
948         (when (or (char= ch #\newline))
949           (setq output (concat output "\\"))
950           (setq ch #\n))
951         (setq output (concat output (string ch))))
952       (incf index))
953     output))
954
955 (defun literal->js (sexp)
956   (cond
957     ((integerp sexp) (integer-to-string sexp))
958     ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
959     ((symbolp sexp) (ls-compile `(intern ,(escape-string (symbol-name sexp))) *environment*))
960     ((consp sexp) (concat "{car: "
961                           (literal->js (car sexp))
962                           ", cdr: "
963                           (literal->js (cdr sexp)) "}"))))
964
965 (defvar *literal-counter* 0)
966 (defun literal (form)
967   (let ((var (concat "l" (integer-to-string (incf *literal-counter*)))))
968     (push (concat "var " var " = " (literal->js form)) *toplevel-compilations*)
969     var))
970
971 (define-compilation quote (sexp)
972   (literal sexp))
973
974 (define-compilation %while (pred &rest body)
975   (concat "(function(){" *newline*
976           (indent "while(" (ls-compile pred env) " !== " (ls-compile nil) "){" *newline*
977                   (indent (ls-compile-block body env))
978                   "}"
979                   "return " (ls-compile nil) ";" *newline*)
980           "})()"))
981
982 (define-compilation function (x)
983   (cond
984     ((and (listp x) (eq (car x) 'lambda))
985      (ls-compile x env))
986     ((symbolp x)
987      (lookup-function-translation x env))))
988
989 (define-compilation eval-when-compile (&rest body)
990   (eval (cons 'progn body))
991   "")
992
993 (defmacro define-transformation (name args form)
994   `(define-compilation ,name ,args
995      (ls-compile ,form env)))
996
997 (define-compilation progn (&rest body)
998   (concat "(function(){" *newline*
999           (indent (ls-compile-block (butlast body) env)
1000                   "return " (ls-compile (car (last body)) env) ";" *newline*)
1001           "})()"))
1002
1003 (define-compilation let (bindings &rest body)
1004   (let ((bindings (mapcar #'ensure-list bindings)))
1005     (let ((variables (mapcar #'first bindings))
1006           (values    (mapcar #'second bindings)))
1007       (let ((new-env (extend-local-env variables env)))
1008         (concat "(function("
1009                 (join (mapcar (lambda (x)
1010                                 (lookup-variable-translation x new-env))
1011                               variables)
1012                       ",")
1013                 "){" *newline*
1014                 (indent (ls-compile-block (butlast body) new-env)
1015                         "return " (ls-compile (car (last body)) new-env)
1016                         ";" *newline*)
1017                 "})(" (join (mapcar (lambda (x) (ls-compile x env))
1018                                     values)
1019                             ",")
1020                 ")")))))
1021
1022
1023 (defvar *block-counter* 0)
1024
1025 (define-compilation block (name &rest body)
1026   (let ((tr (integer-to-string (incf *block-counter*))))
1027     (let ((b (make-binding name 'block tr t)))
1028       (concat "(function(){" *newline*
1029               (indent "try {" *newline*
1030                       (indent "return " (ls-compile `(progn ,@body)
1031                                                     (extend-lexenv (list b) env 'block))
1032                               ";" *newline*)
1033                       "}" *newline*
1034                       "catch (cf){" *newline*
1035                       "    if (cf.type == 'block' && cf.id == " tr ")" *newline*
1036                       "        return cf.value;" *newline*
1037                       "    else" *newline*
1038                       "        throw cf;" *newline*
1039                       "}" *newline*)
1040               "})()"))))
1041
1042 (define-compilation return-from (name &optional value)
1043   (let ((b (lookup-in-lexenv name env 'block)))
1044     (if b
1045         (concat "(function(){ throw ({"
1046                 "type: 'block', "
1047                 "id: " (binding-translation b) ", "
1048                 "value: " (ls-compile value env) ", "
1049                 "message: 'Return from unknown block " (symbol-name name) ".'"
1050                 "})})()")
1051         (error (concat "Unknown block `" (symbol-name name) "'.")))))
1052
1053
1054 (define-compilation catch (id &rest body)
1055   (concat "(function(){" *newline*
1056           (indent "var id = " (ls-compile id env) ";" *newline*
1057                   "try {" *newline*
1058                   (indent "return " (ls-compile `(progn ,@body))
1059                           ";" *newline*)
1060                   "}" *newline*
1061                   "catch (cf){" *newline*
1062                   "    if (cf.type == 'catch' && cf.id == id)" *newline*
1063                   "        return cf.value;" *newline*
1064                   "    else" *newline*
1065                   "        throw cf;" *newline*
1066                   "}" *newline*)
1067           "})()"))
1068
1069 (define-compilation throw (id &optional value)
1070   (concat "(function(){ throw ({"
1071           "type: 'catch', "
1072           "id: " (ls-compile id env) ", "
1073           "value: " (ls-compile value env) ", "
1074           "message: 'Throw uncatched.'"
1075           "})})()"))
1076
1077
1078 (defvar *tagbody-counter* 0)
1079 (defvar *go-tag-counter* 0)
1080
1081 (defun go-tag-p (x)
1082   (or (integerp x) (symbolp x)))
1083
1084 (defun declare-tagbody-tags (env tbidx body)
1085   (let ((bindings
1086          (mapcar (lambda (label)
1087                    (let ((tagidx (integer-to-string (incf *go-tag-counter*))))
1088                      (make-binding label 'gotag (list tbidx tagidx) t)))
1089                  (remove-if-not #'go-tag-p body))))
1090     (extend-lexenv bindings env 'gotag)))
1091
1092 (define-compilation tagbody (&rest body)
1093   ;; Ignore the tagbody if it does not contain any go-tag. We do this
1094   ;; because 1) it is easy and 2) many built-in forms expand to a
1095   ;; implicit tagbody, so we save some space.
1096   (unless (some #'go-tag-p body)
1097     (return-from tagbody (ls-compile `(progn ,@body nil) env)))
1098   ;; The translation assumes the first form in BODY is a label
1099   (unless (go-tag-p (car body))
1100     (push (gensym "START") body))
1101   ;; Tagbody compilation
1102   (let ((tbidx (integer-to-string *tagbody-counter*)))
1103     (let ((env (declare-tagbody-tags env tbidx body))
1104           initag)
1105       (let ((b (lookup-in-lexenv (first body) env 'gotag)))
1106         (setq initag (second (binding-translation b))))
1107       (js!selfcall
1108         "var tagbody_" tbidx " = " initag ";" *newline*
1109         "tbloop:" *newline*
1110         "while (true) {" *newline*
1111         (indent "try {" *newline*
1112                 (indent (let ((content ""))
1113                           (concat "switch(tagbody_" tbidx "){" *newline*
1114                                   "case " initag ":" *newline*
1115                                   (dolist (form (cdr body) content)
1116                                     (concatf content
1117                                       (if (not (go-tag-p form))
1118                                           (indent (ls-compile form env) ";" *newline*)
1119                                           (let ((b (lookup-in-lexenv form env 'gotag)))
1120                                             (concat "case " (second (binding-translation b)) ":" *newline*)))))
1121                                   "default:" *newline*
1122                                   "    break tbloop;" *newline*
1123                                   "}" *newline*)))
1124                 "}" *newline*
1125                 "catch (jump) {" *newline*
1126                 "    if (jump.type == 'tagbody' && jump.id == " tbidx ")" *newline*
1127                 "        tagbody_" tbidx " = jump.label;" *newline*
1128                 "    else" *newline*
1129                 "        throw(jump);" *newline*
1130                 "}" *newline*)
1131         "}" *newline*
1132         "return " (ls-compile nil) ";" *newline*))))
1133
1134 (define-compilation go (label)
1135   (let ((b (lookup-in-lexenv label env 'gotag))
1136         (n (cond
1137              ((symbolp label) (symbol-name label))
1138              ((integerp label) (integer-to-string label)))))
1139     (if b
1140         (js!selfcall
1141          (concat "throw ({"
1142                  "type: 'tagbody', "
1143                  "id: " (first (binding-translation b)) ", "
1144                  "label: " (second (binding-translation b)) ", "
1145                  "message: 'Attempt to GO to non-existing tag " n "'"
1146                  "})" *newline*))
1147         (error (concat "Unknown tag `" n "'.")))))
1148
1149
1150 (define-compilation unwind-protect (form &rest clean-up)
1151   (concat "(function(){" *newline*
1152           (indent "var ret = " (ls-compile nil) ";" *newline*
1153                   "try {" *newline*
1154                   (indent "ret = " (ls-compile form env) ";" *newline*)
1155                   "} finally {" *newline*
1156                   (indent (ls-compile-block clean-up env))
1157                   "}" *newline*
1158                   "return ret;" *newline*)
1159           "})()"))
1160
1161
1162 ;;; A little backquote implementation without optimizations of any
1163 ;;; kind for ecmalisp.
1164 (defun backquote-expand-1 (form)
1165   (cond
1166     ((symbolp form)
1167      (list 'quote form))
1168     ((atom form)
1169      form)
1170     ((eq (car form) 'unquote)
1171      (car form))
1172     ((eq (car form) 'backquote)
1173      (backquote-expand-1 (backquote-expand-1 (cadr form))))
1174     (t
1175      (cons 'append
1176            (mapcar (lambda (s)
1177                      (cond
1178                        ((and (listp s) (eq (car s) 'unquote))
1179                         (list 'list (cadr s)))
1180                        ((and (listp s) (eq (car s) 'unquote-splicing))
1181                         (cadr s))
1182                        (t
1183                         (list 'list (backquote-expand-1 s)))))
1184                    form)))))
1185
1186 (defun backquote-expand (form)
1187   (if (and (listp form) (eq (car form) 'backquote))
1188       (backquote-expand-1 (cadr form))
1189       form))
1190
1191 (defmacro backquote (form)
1192   (backquote-expand-1 form))
1193
1194 (define-transformation backquote (form)
1195   (backquote-expand-1 form))
1196
1197 ;;; Primitives
1198
1199 (defmacro define-builtin (name args &body body)
1200   `(define-compilation ,name ,args
1201      (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg env))) args)
1202        ,@body)))
1203
1204 (defun compile-bool (x)
1205   (concat "(" x "?" (ls-compile t) ": " (ls-compile nil) ")"))
1206
1207 ;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations.
1208 (defmacro type-check (decls &body body)
1209   `(concat "(function(){" *newline*
1210            (indent ,@(mapcar (lambda (decl)
1211                                `(concat "var " ,(first decl) " = " ,(third decl) ";" *newline*))
1212                              decls)
1213
1214                    ,@(mapcar (lambda (decl)
1215                                `(concat "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline*
1216                                         (indent "throw 'The value ' + "
1217                                                 ,(first decl)
1218                                                 " + ' is not a type "
1219                                                 ,(second decl)
1220                                                 ".';"
1221                                                 *newline*)))
1222                              decls)
1223                    (concat "return " (progn ,@body) ";" *newline*))
1224            "})()"))
1225
1226 (defun num-op-num (x op y)
1227   (type-check (("x" "number" x) ("y" "number" y))
1228     (concat "x" op "y")))
1229
1230 (define-builtin + (x y) (num-op-num x "+" y))
1231 (define-builtin - (x y) (num-op-num x "-" y))
1232 (define-builtin * (x y) (num-op-num x "*" y))
1233 (define-builtin / (x y) (num-op-num x "/" y))
1234
1235 (define-builtin mod (x y) (num-op-num x "%" y))
1236
1237 (define-builtin < (x y)  (compile-bool (num-op-num x "<" y)))
1238 (define-builtin > (x y)  (compile-bool (num-op-num x ">" y)))
1239 (define-builtin = (x y)  (compile-bool (num-op-num x "==" y)))
1240 (define-builtin <= (x y) (compile-bool (num-op-num x "<=" y)))
1241 (define-builtin >= (x y) (compile-bool (num-op-num x ">=" y)))
1242
1243 (define-builtin numberp (x)
1244   (compile-bool (concat "(typeof (" x ") == \"number\")")))
1245
1246 (define-builtin floor (x)
1247   (type-check (("x" "number" x))
1248     "Math.floor(x)"))
1249
1250 (define-builtin cons (x y) (concat "({car: " x ", cdr: " y "})"))
1251 (define-builtin consp (x)
1252   (compile-bool
1253    (concat "(function(){" *newline*
1254            (indent "var tmp = " x ";" *newline*
1255                    "return (typeof tmp == 'object' && 'car' in tmp);" *newline*)
1256            "})()")))
1257
1258 (define-builtin car (x)
1259   (concat "(function(){" *newline*
1260           (indent "var tmp = " x ";" *newline*
1261                   "return tmp === " (ls-compile nil)
1262                   "? " (ls-compile nil)
1263                   ": tmp.car;" *newline*)
1264           "})()"))
1265
1266 (define-builtin cdr (x)
1267   (concat "(function(){" *newline*
1268           (indent "var tmp = " x ";" *newline*
1269                   "return tmp === " (ls-compile nil) "? "
1270                   (ls-compile nil)
1271                   ": tmp.cdr;" *newline*)
1272           "})()"))
1273
1274 (define-builtin setcar (x new)
1275   (type-check (("x" "object" x))
1276     (concat "(x.car = " new ")")))
1277
1278 (define-builtin setcdr (x new)
1279   (type-check (("x" "object" x))
1280     (concat "(x.cdr = " new ")")))
1281
1282 (define-builtin symbolp (x)
1283   (compile-bool
1284    (concat "(function(){" *newline*
1285            (indent "var tmp = " x ";" *newline*
1286                    "return (typeof tmp == 'object' && 'name' in tmp);" *newline*)
1287            "})()")))
1288
1289 (define-builtin make-symbol (name)
1290   (type-check (("name" "string" name))
1291     "({name: name})"))
1292
1293 (define-builtin symbol-name (x)
1294   (concat "(" x ").name"))
1295
1296 (define-builtin eq    (x y) (compile-bool (concat "(" x " === " y ")")))
1297 (define-builtin equal (x y) (compile-bool (concat "(" x  " == " y ")")))
1298
1299 (define-builtin string (x)
1300   (type-check (("x" "number" x))
1301     "String.fromCharCode(x)"))
1302
1303 (define-builtin stringp (x)
1304   (compile-bool (concat "(typeof(" x ") == \"string\")")))
1305
1306 (define-builtin string-upcase (x)
1307   (type-check (("x" "string" x))
1308     "x.toUpperCase()"))
1309
1310 (define-builtin string-length (x)
1311   (type-check (("x" "string" x))
1312     "x.length"))
1313
1314 (define-compilation slice (string a &optional b)
1315   (concat "(function(){" *newline*
1316           (indent "var str = " (ls-compile string env) ";" *newline*
1317                   "var a = " (ls-compile a env) ";" *newline*
1318                   "var b;" *newline*
1319                   (if b
1320                       (concat "b = " (ls-compile b env) ";" *newline*)
1321                       "")
1322                   "return str.slice(a,b);" *newline*)
1323           "})()"))
1324
1325 (define-builtin char (string index)
1326   (type-check (("string" "string" string)
1327                ("index" "number" index))
1328     "string.charCodeAt(index)"))
1329
1330 (define-builtin concat-two (string1 string2)
1331   (type-check (("string1" "string" string1)
1332                ("string2" "string" string2))
1333     "string1.concat(string2)"))
1334
1335 (define-compilation funcall (func &rest args)
1336   (concat "(" (ls-compile func env) ")("
1337           (join (mapcar (lambda (x)
1338                           (ls-compile x env))
1339                         args)
1340                 ", ")
1341           ")"))
1342
1343 (define-compilation apply (func &rest args)
1344   (if (null args)
1345       (concat "(" (ls-compile func env) ")()")
1346       (let ((args (butlast args))
1347             (last (car (last args))))
1348         (concat "(function(){" *newline*
1349                 (indent "var f = " (ls-compile func env) ";" *newline*
1350                         "var args = [" (join (mapcar (lambda (x)
1351                                                        (ls-compile x env))
1352                                                      args)
1353                                              ", ")
1354                         "];" *newline*
1355                         "var tail = (" (ls-compile last env) ");" *newline*
1356                         (indent "while (tail != " (ls-compile nil) "){" *newline*
1357                                 "    args.push(tail.car);" *newline*
1358                                 "    tail = tail.cdr;" *newline*
1359                                 "}" *newline*
1360                                 "return f.apply(this, args);" *newline*)
1361                         "})()")))))
1362
1363 (define-builtin js-eval (string)
1364   (type-check (("string" "string" string))
1365     "eval.apply(window, [string])"))
1366
1367 (define-builtin error (string)
1368   (concat "(function (){ throw " string "; })()"))
1369
1370 (define-builtin new () "{}")
1371
1372 (define-builtin get (object key)
1373   (concat "(function(){" *newline*
1374           (indent "var tmp = " "(" object ")[" key "];" *newline*
1375                   "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*)
1376           "})()"))
1377
1378 (define-builtin set (object key value)
1379   (concat "((" object ")[" key "] = " value ")"))
1380
1381 (define-builtin in (key object)
1382   (compile-bool (concat "((" key ") in (" object "))")))
1383
1384 (define-builtin functionp (x)
1385   (compile-bool (concat "(typeof " x " == 'function')")))
1386
1387 (define-builtin write-string (x)
1388   (type-check (("x" "string" x))
1389     "lisp.write(x)"))
1390
1391 (defun macrop (x)
1392   (and (symbolp x) (eq (binding-type (lookup-function x *environment*)) 'macro)))
1393
1394 (defun ls-macroexpand-1 (form env)
1395   (if (macrop (car form))
1396       (let ((binding (lookup-function (car form) *environment*)))
1397         (if (eq (binding-type binding) 'macro)
1398             (apply (eval (binding-translation binding)) (cdr form))
1399             form))
1400       form))
1401
1402 (defun compile-funcall (function args env)
1403   (cond
1404     ((symbolp function)
1405      (concat (lookup-function-translation function env)
1406              "("
1407              (join (mapcar (lambda (x) (ls-compile x env)) args)
1408                    ", ")
1409              ")"))
1410     ((and (listp function) (eq (car function) 'lambda))
1411      (concat "(" (ls-compile function env) ")("
1412              (join (mapcar (lambda (x) (ls-compile x env)) args)
1413                    ", ")
1414              ")"))
1415     (t
1416      (error (concat "Invalid function designator " (symbol-name function))))))
1417
1418 (defun ls-compile (sexp &optional (env (make-lexenv)))
1419   (cond
1420     ((symbolp sexp) (lookup-variable-translation sexp env))
1421     ((integerp sexp) (integer-to-string sexp))
1422     ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
1423     ((listp sexp)
1424      (if (assoc (car sexp) *compilations*)
1425          (let ((comp (second (assoc (car sexp) *compilations*))))
1426            (apply comp env (cdr sexp)))
1427          (if (macrop (car sexp))
1428              (ls-compile (ls-macroexpand-1 sexp env) env)
1429              (compile-funcall (car sexp) (cdr sexp) env))))))
1430
1431 (defun ls-compile-toplevel (sexp)
1432   (setq *toplevel-compilations* nil)
1433   (let ((code (ls-compile sexp)))
1434     (prog1
1435         (concat (join (mapcar (lambda (x) (concat x ";" *newline*))
1436                               *toplevel-compilations*))
1437                 code)
1438       (setq *toplevel-compilations* nil))))
1439
1440
1441 ;;; Once we have the compiler, we define the runtime environment and
1442 ;;; interactive development (eval), which works calling the compiler
1443 ;;; and evaluating the Javascript result globally.
1444
1445 #+ecmalisp
1446 (progn
1447   (defmacro with-compilation-unit (&body body)
1448     `(prog1
1449          (progn
1450            (setq *compilation-unit-checks* nil)
1451            (clear-undeclared-global-bindings)
1452            ,@body)
1453        (dolist (check *compilation-unit-checks*)
1454          (funcall check))))
1455
1456   (defun eval (x)
1457     (let ((code
1458            (with-compilation-unit
1459                (ls-compile-toplevel x))))
1460       (js-eval code)))
1461
1462   ;; Set the initial global environment to be equal to the host global
1463   ;; environment at this point of the compilation.
1464   (eval-when-compile
1465     (let ((tmp (ls-compile
1466                 `(progn
1467                    (setq *environment* ',*environment*)
1468                    (setq *variable-counter* ',*variable-counter*)
1469                    (setq *function-counter* ',*function-counter*)
1470                    (setq *literal-counter* ',*literal-counter*)
1471                    (setq *gensym-counter* ',*gensym-counter*)
1472                    (setq *block-counter* ',*block-counter*)))))
1473       (setq *toplevel-compilations*
1474             (append *toplevel-compilations* (list tmp)))))
1475
1476   (js-eval
1477    (concat "var lisp = {};"
1478            "lisp.read = " (lookup-function-translation 'ls-read-from-string nil) ";" *newline*
1479            "lisp.print = " (lookup-function-translation 'prin1-to-string nil) ";" *newline*
1480            "lisp.eval = " (lookup-function-translation 'eval nil) ";" *newline*
1481            "lisp.compile = " (lookup-function-translation 'ls-compile-toplevel nil) ";" *newline*
1482            "lisp.evalString = function(str){" *newline*
1483            "   return lisp.eval(lisp.read(str));" *newline*
1484            "}" *newline*
1485            "lisp.compileString = function(str){" *newline*
1486            "   return lisp.compile(lisp.read(str));" *newline*
1487            "}" *newline*)))
1488
1489
1490 ;;; Finally, we provide a couple of functions to easily bootstrap
1491 ;;; this. It just calls the compiler with this file as input.
1492
1493 #+common-lisp
1494 (progn
1495   (defun read-whole-file (filename)
1496     (with-open-file (in filename)
1497       (let ((seq (make-array (file-length in) :element-type 'character)))
1498         (read-sequence seq in)
1499         seq)))
1500
1501   (defun ls-compile-file (filename output)
1502     (setq *compilation-unit-checks* nil)
1503     (with-open-file (out output :direction :output :if-exists :supersede)
1504       (let* ((source (read-whole-file filename))
1505              (in (make-string-stream source)))
1506         (loop
1507            for x = (ls-read in)
1508            until (eq x *eof*)
1509            for compilation = (ls-compile-toplevel x)
1510            when (plusp (length compilation))
1511            do (write-line (concat compilation "; ") out))
1512         (dolist (check *compilation-unit-checks*)
1513           (funcall check))
1514         (setq *compilation-unit-checks* nil))))
1515
1516   (defun bootstrap ()
1517     (setq *environment* (make-lexenv))
1518     (setq *variable-counter* 0
1519           *gensym-counter* 0
1520           *function-counter* 0
1521           *literal-counter* 0
1522           *block-counter* 0)
1523     (ls-compile-file "ecmalisp.lisp" "ecmalisp.js")))