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