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