3dc2f1ca2ff155b96342f6a5e74154747e70e5e4
[jscl.git] / src / compiler.lisp
1 ;;; compiler.lisp --- 
2
3 ;; copyright (C) 2012, 2013 David Vazquez
4 ;; Copyright (C) 2012 Raimon Grau
5
6 ;; JSCL 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 ;; JSCL 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 JSCL.  If not, see <http://www.gnu.org/licenses/>.
18
19 ;;;; Compiler
20
21 ;;; Translate the Lisp code to Javascript. It will compile the special
22 ;;; forms. Some primitive functions are compiled as special forms
23 ;;; too. The respective real functions are defined in the target (see
24 ;;; the beginning of this file) as well as some primitive functions.
25
26 (defun code (&rest args)
27   (mapconcat (lambda (arg)
28                (cond
29                  ((null arg) "")
30                  ((integerp arg) (integer-to-string arg))
31                  ((floatp arg) (float-to-string arg))
32                  ((stringp arg) arg)
33                  (t (error "Unknown argument `~S'." arg))))
34              args))
35
36 ;;; Wrap X with a Javascript code to convert the result from
37 ;;; Javascript generalized booleans to T or NIL.
38 (defun js!bool (x)
39   (code "(" x "?" (ls-compile t) ": " (ls-compile nil) ")"))
40
41 ;;; Concatenate the arguments and wrap them with a self-calling
42 ;;; Javascript anonymous function. It is used to make some Javascript
43 ;;; statements valid expressions and provide a private scope as well.
44 ;;; It could be defined as function, but we could do some
45 ;;; preprocessing in the future.
46 (defmacro js!selfcall (&body body)
47   `(code "(function(){" *newline* (indent ,@body) "})()"))
48
49 ;;; Like CODE, but prefix each line with four spaces. Two versions
50 ;;; of this function are available, because the Ecmalisp version is
51 ;;; very slow and bootstraping was annoying.
52
53 #+jscl
54 (defun indent (&rest string)
55   (let ((input (apply #'code string)))
56     (let ((output "")
57           (index 0)
58           (size (length input)))
59       (when (plusp (length input)) (concatf output "    "))
60       (while (< index size)
61         (let ((str
62                (if (and (char= (char input index) #\newline)
63                         (< index (1- size))
64                         (not (char= (char input (1+ index)) #\newline)))
65                    (concat (string #\newline) "    ")
66                    (string (char input index)))))
67           (concatf output str))
68         (incf index))
69       output)))
70
71 #+common-lisp
72 (defun indent (&rest string)
73   (with-output-to-string (*standard-output*)
74     (with-input-from-string (input (apply #'code string))
75       (loop
76          for line = (read-line input nil)
77          while line
78          do (write-string "    ")
79          do (write-line line)))))
80
81
82 ;;; A Form can return a multiple values object calling VALUES, like
83 ;;; values(arg1, arg2, ...). It will work in any context, as well as
84 ;;; returning an individual object. However, if the special variable
85 ;;; `*multiple-value-p*' is NIL, is granted that only the primary
86 ;;; value will be used, so we can optimize to avoid the VALUES
87 ;;; function call.
88 (defvar *multiple-value-p* nil)
89
90 ;;; Environment
91
92 (def!struct binding
93   name
94   type
95   value
96   declarations)
97
98 (def!struct lexenv
99   variable
100   function
101   block
102   gotag)
103
104 (defun lookup-in-lexenv (name lexenv namespace)
105   (find name (ecase namespace
106                 (variable (lexenv-variable lexenv))
107                 (function (lexenv-function lexenv))
108                 (block    (lexenv-block    lexenv))
109                 (gotag    (lexenv-gotag    lexenv)))
110         :key #'binding-name))
111
112 (defun push-to-lexenv (binding lexenv namespace)
113   (ecase namespace
114     (variable (push binding (lexenv-variable lexenv)))
115     (function (push binding (lexenv-function lexenv)))
116     (block    (push binding (lexenv-block    lexenv)))
117     (gotag    (push binding (lexenv-gotag    lexenv)))))
118
119 (defun extend-lexenv (bindings lexenv namespace)
120   (let ((env (copy-lexenv lexenv)))
121     (dolist (binding (reverse bindings) env)
122       (push-to-lexenv binding env namespace))))
123
124
125 (defvar *environment* (make-lexenv))
126
127 (defvar *variable-counter* 0)
128
129 (defun gvarname (symbol)
130   (declare (ignore symbol))
131   (code "v" (incf *variable-counter*)))
132
133 (defun translate-variable (symbol)
134   (awhen (lookup-in-lexenv symbol *environment* 'variable)
135     (binding-value it)))
136
137 (defun extend-local-env (args)
138   (let ((new (copy-lexenv *environment*)))
139     (dolist (symbol args new)
140       (let ((b (make-binding :name symbol :type 'variable :value (gvarname symbol))))
141         (push-to-lexenv b new 'variable)))))
142
143 ;;; Toplevel compilations
144 (defvar *toplevel-compilations* nil)
145
146 (defun toplevel-compilation (string)
147   (push string *toplevel-compilations*))
148
149 (defun null-or-empty-p (x)
150   (zerop (length x)))
151
152 (defun get-toplevel-compilations ()
153   (reverse (remove-if #'null-or-empty-p *toplevel-compilations*)))
154
155 (defun %compile-defmacro (name lambda)
156   (toplevel-compilation (ls-compile `',name))
157   (let ((binding (make-binding :name name :type 'macro :value lambda)))
158     (push-to-lexenv binding  *environment* 'function))
159   name)
160
161 (defun global-binding (name type namespace)
162   (or (lookup-in-lexenv name *environment* namespace)
163       (let ((b (make-binding :name name :type type :value nil)))
164         (push-to-lexenv b *environment* namespace)
165         b)))
166
167 (defun claimp (symbol namespace claim)
168   (let ((b (lookup-in-lexenv symbol *environment* namespace)))
169     (and b (member claim (binding-declarations b)))))
170
171 (defun !proclaim (decl)
172   (case (car decl)
173     (special
174      (dolist (name (cdr decl))
175        (let ((b (global-binding name 'variable 'variable)))
176          (push 'special (binding-declarations b)))))
177     (notinline
178      (dolist (name (cdr decl))
179        (let ((b (global-binding name 'function 'function)))
180          (push 'notinline (binding-declarations b)))))
181     (constant
182      (dolist (name (cdr decl))
183        (let ((b (global-binding name 'variable 'variable)))
184          (push 'constant (binding-declarations b)))))))
185
186 #+jscl
187 (fset 'proclaim #'!proclaim)
188
189 (defun %define-symbol-macro (name expansion)
190   (let ((b (make-binding :name name :type 'macro :value expansion)))
191     (push-to-lexenv b *environment* 'variable)
192     name))
193
194 #+jscl
195 (defmacro define-symbol-macro (name expansion)
196   `(%define-symbol-macro ',name ',expansion))
197
198
199 ;;; Special forms
200
201 (defvar *compilations* nil)
202
203 (defmacro define-compilation (name args &body body)
204   ;; Creates a new primitive `name' with parameters args and
205   ;; @body. The body can access to the local environment through the
206   ;; variable *ENVIRONMENT*.
207   `(push (list ',name (lambda ,args (block ,name ,@body)))
208          *compilations*))
209
210 (define-compilation if (condition true false)
211   (code "(" (ls-compile condition) " !== " (ls-compile nil)
212         " ? " (ls-compile true *multiple-value-p*)
213         " : " (ls-compile false *multiple-value-p*)
214         ")"))
215
216 (defvar *ll-keywords* '(&optional &rest &key))
217
218 (defun list-until-keyword (list)
219   (if (or (null list) (member (car list) *ll-keywords*))
220       nil
221       (cons (car list) (list-until-keyword (cdr list)))))
222
223 (defun ll-section (keyword ll)
224   (list-until-keyword (cdr (member keyword ll))))
225
226 (defun ll-required-arguments (ll)
227   (list-until-keyword ll))
228
229 (defun ll-optional-arguments-canonical (ll)
230   (mapcar #'ensure-list (ll-section '&optional ll)))
231
232 (defun ll-optional-arguments (ll)
233   (mapcar #'car (ll-optional-arguments-canonical ll)))
234
235 (defun ll-rest-argument (ll)
236   (let ((rest (ll-section '&rest ll)))
237     (when (cdr rest)
238       (error "Bad lambda-list `~S'." ll))
239     (car rest)))
240
241 (defun ll-keyword-arguments-canonical (ll)
242   (flet ((canonicalize (keyarg)
243            ;; Build a canonical keyword argument descriptor, filling
244            ;; the optional fields. The result is a list of the form
245            ;; ((keyword-name var) init-form).
246            (let ((arg (ensure-list keyarg)))
247              (cons (if (listp (car arg))
248                        (car arg)
249                        (list (intern (symbol-name (car arg)) "KEYWORD") (car arg)))
250                    (cdr arg)))))
251     (mapcar #'canonicalize (ll-section '&key ll))))
252
253 (defun ll-keyword-arguments (ll)
254   (mapcar (lambda (keyarg) (second (first keyarg)))
255           (ll-keyword-arguments-canonical ll)))
256
257 (defun ll-svars (lambda-list)
258   (let ((args
259          (append
260           (ll-keyword-arguments-canonical lambda-list)
261           (ll-optional-arguments-canonical lambda-list))))
262     (remove nil (mapcar #'third args))))
263
264 (defun lambda-name/docstring-wrapper (name docstring &rest strs)
265   (if (or name docstring)
266       (js!selfcall
267         "var func = " (join strs) ";" *newline*
268         (when name
269           (code "func.fname = '" (escape-string name) "';" *newline*))
270         (when docstring
271           (code "func.docstring = '" (escape-string docstring) "';" *newline*))
272         "return func;" *newline*)
273       (apply #'code strs)))
274
275 (defun lambda-check-argument-count
276     (n-required-arguments n-optional-arguments rest-p)
277   ;; Note: Remember that we assume that the number of arguments of a
278   ;; call is at least 1 (the values argument).
279   (let ((min n-required-arguments)
280         (max (if rest-p 'n/a (+ n-required-arguments n-optional-arguments))))
281     (block nil
282       ;; Special case: a positive exact number of arguments.
283       (when (and (< 0 min) (eql min max))
284         (return (code "checkArgs(nargs, " min ");" *newline*)))
285       ;; General case:
286       (code
287        (when (< 0 min)
288          (code "checkArgsAtLeast(nargs, " min ");" *newline*))
289        (when (numberp max)
290          (code "checkArgsAtMost(nargs, " max ");" *newline*))))))
291
292 (defun compile-lambda-optional (ll)
293   (let* ((optional-arguments (ll-optional-arguments-canonical ll))
294          (n-required-arguments (length (ll-required-arguments ll)))
295          (n-optional-arguments (length optional-arguments)))
296     (when optional-arguments
297       (code "switch(nargs){" *newline*
298             (let ((cases nil)
299                   (idx 0))
300               (progn
301                 (while (< idx n-optional-arguments)
302                   (let ((arg (nth idx optional-arguments)))
303                     (push (code "case " (+ idx n-required-arguments) ":" *newline*
304                                 (indent (translate-variable (car arg))
305                                         "="
306                                         (ls-compile (cadr arg)) ";" *newline*)
307                                 (when (third arg)
308                                   (indent (translate-variable (third arg))
309                                           "="
310                                           (ls-compile nil)
311                                           ";" *newline*)))
312                           cases)
313                     (incf idx)))
314                 (push (code "default: break;" *newline*) cases)
315                 (join (reverse cases))))
316             "}" *newline*))))
317
318 (defun compile-lambda-rest (ll)
319   (let ((n-required-arguments (length (ll-required-arguments ll)))
320         (n-optional-arguments (length (ll-optional-arguments ll)))
321         (rest-argument (ll-rest-argument ll)))
322     (when rest-argument
323       (let ((js!rest (translate-variable rest-argument)))
324         (code "var " js!rest "= " (ls-compile nil) ";" *newline*
325               "for (var i = nargs-1; i>=" (+ n-required-arguments n-optional-arguments)
326               "; i--)" *newline*
327               (indent js!rest " = {car: arguments[i+2], cdr: " js!rest "};" *newline*))))))
328
329 (defun compile-lambda-parse-keywords (ll)
330   (let ((n-required-arguments
331          (length (ll-required-arguments ll)))
332         (n-optional-arguments
333          (length (ll-optional-arguments ll)))
334         (keyword-arguments
335          (ll-keyword-arguments-canonical ll)))
336     (code
337      ;; Declare variables
338      (mapconcat (lambda (arg)
339                   (let ((var (second (car arg))))
340                     (code "var " (translate-variable var) "; " *newline*
341                           (when (third arg)
342                             (code "var " (translate-variable (third arg))
343                                   " = " (ls-compile nil)
344                                   ";" *newline*)))))
345                 keyword-arguments)
346      ;; Parse keywords
347      (flet ((parse-keyword (keyarg)
348               ;; ((keyword-name var) init-form)
349               (code "for (i=" (+ n-required-arguments n-optional-arguments)
350                     "; i<nargs; i+=2){" *newline*
351                     (indent
352                      "if (arguments[i+2] === " (ls-compile (caar keyarg)) "){" *newline*
353                      (indent (translate-variable (cadr (car keyarg)))
354                              " = arguments[i+3];"
355                              *newline*
356                              (let ((svar (third keyarg)))
357                                (when svar
358                                  (code (translate-variable svar) " = " (ls-compile t) ";" *newline*)))
359                              "break;" *newline*)
360                      "}" *newline*)
361                     "}" *newline*
362                     ;; Default value
363                     "if (i == nargs){" *newline*
364                     (indent (translate-variable (cadr (car keyarg))) " = " (ls-compile (cadr keyarg)) ";" *newline*)
365                     "}" *newline*)))
366        (when keyword-arguments
367          (code "var i;" *newline*
368                (mapconcat #'parse-keyword keyword-arguments))))
369      ;; Check for unknown keywords
370      (when keyword-arguments
371        (code "for (i=" (+ n-required-arguments n-optional-arguments)
372              "; i<nargs; i+=2){" *newline*
373              (indent "if ("
374                      (join (mapcar (lambda (x)
375                                      (concat "arguments[i+2] !== " (ls-compile (caar x))))
376                                    keyword-arguments)
377                            " && ")
378                      ")" *newline*
379                      (indent
380                       "throw 'Unknown keyword argument ' + xstring(arguments[i].name);" *newline*))
381              "}" *newline*)))))
382
383 (defun parse-lambda-list (ll)
384   (values (ll-required-arguments ll)
385           (ll-optional-arguments ll)
386           (ll-keyword-arguments  ll)
387           (ll-rest-argument      ll)))
388
389 ;;; Process BODY for declarations and/or docstrings. Return as
390 ;;; multiple values the BODY without docstrings or declarations, the
391 ;;; list of declaration forms and the docstring.
392 (defun parse-body (body &key declarations docstring)
393   (let ((value-declarations)
394         (value-docstring))
395     ;; Parse declarations
396     (when declarations
397       (do* ((rest body (cdr rest))
398             (form (car rest) (car rest)))
399            ((or (atom form) (not (eq (car form) 'declare)))
400             (setf body rest))
401         (push form value-declarations)))
402     ;; Parse docstring
403     (when (and docstring
404                (stringp (car body))
405                (not (null (cdr body))))
406       (setq value-docstring (car body))
407       (setq body (cdr body)))
408     (values body value-declarations value-docstring)))
409
410 ;;; Compile a lambda function with lambda list LL and body BODY. If
411 ;;; NAME is given, it should be a constant string and it will become
412 ;;; the name of the function. If BLOCK is non-NIL, a named block is
413 ;;; created around the body. NOTE: No block (even anonymous) is
414 ;;; created if BLOCk is NIL.
415 (defun compile-lambda (ll body &key name block)
416   (multiple-value-bind (required-arguments
417                         optional-arguments
418                         keyword-arguments
419                         rest-argument)
420       (parse-lambda-list ll)
421     (multiple-value-bind (body decls documentation)
422         (parse-body body :declarations t :docstring t)
423       (declare (ignore decls))
424       (let ((n-required-arguments (length required-arguments))
425             (n-optional-arguments (length optional-arguments))
426             (*environment* (extend-local-env
427                             (append (ensure-list rest-argument)
428                                     required-arguments
429                                     optional-arguments
430                                     keyword-arguments
431                                     (ll-svars ll)))))
432         (lambda-name/docstring-wrapper name documentation
433          "(function ("
434          (join (list* "values"
435                       "nargs"
436                       (mapcar #'translate-variable
437                               (append required-arguments optional-arguments)))
438                ",")
439          "){" *newline*
440          (indent
441           ;; Check number of arguments
442           (lambda-check-argument-count n-required-arguments
443                                        n-optional-arguments
444                                        (or rest-argument keyword-arguments))
445                                         (compile-lambda-optional ll)
446                                         (compile-lambda-rest ll)
447                                         (compile-lambda-parse-keywords ll)
448                                         (let ((*multiple-value-p* t))
449                                           (if block
450                                               (ls-compile-block `((block ,block ,@body)) t)
451                                               (ls-compile-block body t))))
452          "})")))))
453
454
455 (defun setq-pair (var val)
456   (let ((b (lookup-in-lexenv var *environment* 'variable)))
457     (cond
458       ((and b
459             (eq (binding-type b) 'variable)
460             (not (member 'special (binding-declarations b)))
461             (not (member 'constant (binding-declarations b))))
462        (code (binding-value b) " = " (ls-compile val)))
463       ((and b (eq (binding-type b) 'macro))
464        (ls-compile `(setf ,var ,val)))
465       (t
466        (ls-compile `(set ',var ,val))))))
467
468
469 (define-compilation setq (&rest pairs)
470   (let ((result ""))
471     (while t
472       (cond
473         ((null pairs) (return))
474         ((null (cdr pairs))
475          (error "Odd pairs in SETQ"))
476         (t
477          (concatf result
478            (concat (setq-pair (car pairs) (cadr pairs))
479                    (if (null (cddr pairs)) "" ", ")))
480          (setq pairs (cddr pairs)))))
481     (code "(" result ")")))
482
483
484 ;;; Compilation of literals an object dumping
485
486 (defun escape-string (string)
487   (let ((output "")
488         (index 0)
489         (size (length string)))
490     (while (< index size)
491       (let ((ch (char string index)))
492         (when (or (char= ch #\") (char= ch #\\))
493           (setq output (concat output "\\")))
494         (when (or (char= ch #\newline))
495           (setq output (concat output "\\"))
496           (setq ch #\n))
497         (setq output (concat output (string ch))))
498       (incf index))
499     output))
500
501 (defvar *literal-table* nil)
502 (defvar *literal-counter* 0)
503
504 ;;; BOOTSTRAP MAGIC: During bootstrap, we record the macro definitions
505 ;;; as lists. Once everything is compiled, we want to dump the whole
506 ;;; global environment to the output file to reproduce it in the
507 ;;; run-time. However, the environment must contain expander functions
508 ;;; rather than lists. We do not know how to dump function objects
509 ;;; itself, so we mark the definitions with this object and the
510 ;;; compiler will be called when this object has to be dumped.
511 ;;; Backquote/unquote does a similar magic, but this use is exclusive.
512 (defvar *magic-unquote-marker* (gensym "MAGIC-UNQUOTE"))
513
514 (defun genlit ()
515   (code "l" (incf *literal-counter*)))
516
517 (defun dump-symbol (symbol)
518   #+common-lisp
519   (let ((package (symbol-package symbol)))
520     (if (eq package (find-package "KEYWORD"))
521         (code "(new Symbol(" (dump-string (symbol-name symbol)) ", "
522               (dump-string (package-name package)) "))")
523         (code "(new Symbol(" (dump-string (symbol-name symbol)) "))")))
524   #+jscl
525   (let ((package (symbol-package symbol)))
526     (if (null package)
527         (code "(new Symbol(" (dump-string (symbol-name symbol)) "))")
528         (ls-compile `(intern ,(symbol-name symbol) ,(package-name package))))))
529
530 (defun dump-cons (cons)
531   (let ((head (butlast cons))
532         (tail (last cons)))
533     (code "QIList("
534           (join-trailing (mapcar (lambda (x) (literal x t)) head) ",")
535           (literal (car tail) t)
536           ","
537           (literal (cdr tail) t)
538           ")")))
539
540 (defun dump-array (array)
541   (let ((elements (vector-to-list array)))
542     (concat "[" (join (mapcar #'literal elements) ", ") "]")))
543
544 (defun dump-string (string)
545   (code "make_lisp_string(\"" (escape-string string) "\")"))
546
547 (defun literal (sexp &optional recursive)
548   (cond
549     ((integerp sexp) (integer-to-string sexp))
550     ((floatp sexp) (float-to-string sexp))
551     ((characterp sexp) (code "\"" (escape-string (string sexp)) "\""))
552     (t
553      (or (cdr (assoc sexp *literal-table* :test #'equal))
554          (let ((dumped (typecase sexp
555                          (symbol (dump-symbol sexp))
556                          (string (dump-string sexp))
557                          (cons
558                           (if (eq (car sexp) *magic-unquote-marker*)
559                               (ls-compile (cdr sexp))
560                               (dump-cons sexp)))
561                          (array (dump-array sexp)))))
562            (if (and recursive (not (symbolp sexp)))
563                dumped
564                (let ((jsvar (genlit)))
565                  (push (cons sexp jsvar) *literal-table*)
566                  (toplevel-compilation (code "var " jsvar " = " dumped))
567                  jsvar)))))))
568
569
570 (define-compilation quote (sexp)
571   (literal sexp))
572
573 (define-compilation %while (pred &rest body)
574   (js!selfcall
575     "while(" (ls-compile pred) " !== " (ls-compile nil) "){" *newline*
576     (indent (ls-compile-block body))
577     "}"
578     "return " (ls-compile nil) ";" *newline*))
579
580 (define-compilation function (x)
581   (cond
582     ((and (listp x) (eq (car x) 'lambda))
583      (compile-lambda (cadr x) (cddr x)))
584     ((and (listp x) (eq (car x) 'named-lambda))
585      ;; TODO: destructuring-bind now! Do error checking manually is
586      ;; very annoying.
587      (let ((name (cadr x))
588            (ll (caddr x))
589            (body (cdddr x)))
590        (compile-lambda ll body
591                        :name (symbol-name name)
592                        :block name)))
593     ((symbolp x)
594      (let ((b (lookup-in-lexenv x *environment* 'function)))
595        (if b
596            (binding-value b)
597            (ls-compile `(symbol-function ',x)))))))
598
599
600 (defun make-function-binding (fname)
601   (make-binding :name fname :type 'function :value (gvarname fname)))
602
603 (defun compile-function-definition (list)
604   (compile-lambda (car list) (cdr list)))
605
606 (defun translate-function (name)
607   (let ((b (lookup-in-lexenv name *environment* 'function)))
608     (and b (binding-value b))))
609
610 (define-compilation flet (definitions &rest body)
611   (let* ((fnames (mapcar #'car definitions))
612          (cfuncs (mapcar (lambda (def)
613                            (compile-lambda (cadr def)
614                                            `((block ,(car def)
615                                                ,@(cddr def)))))
616                          definitions))
617          (*environment*
618           (extend-lexenv (mapcar #'make-function-binding fnames)
619                          *environment*
620                          'function)))
621     (code "(function("
622           (join (mapcar #'translate-function fnames) ",")
623           "){" *newline*
624           (let ((body (ls-compile-block body t)))
625             (indent body))
626           "})(" (join cfuncs ",") ")")))
627
628 (define-compilation labels (definitions &rest body)
629   (let* ((fnames (mapcar #'car definitions))
630          (*environment*
631           (extend-lexenv (mapcar #'make-function-binding fnames)
632                          *environment*
633                          'function)))
634     (js!selfcall
635       (mapconcat (lambda (func)
636                    (code "var " (translate-function (car func))
637                          " = " (compile-lambda (cadr func)
638                                                `((block ,(car func) ,@(cddr func))))
639                          ";" *newline*))
640                  definitions)
641       (ls-compile-block body t))))
642
643
644 (defvar *compiling-file* nil)
645 (define-compilation eval-when-compile (&rest body)
646   (if *compiling-file*
647       (progn
648         (eval (cons 'progn body))
649         nil)
650       (ls-compile `(progn ,@body))))
651
652 (defmacro define-transformation (name args form)
653   `(define-compilation ,name ,args
654      (ls-compile ,form)))
655
656 (define-compilation progn (&rest body)
657   (if (null (cdr body))
658       (ls-compile (car body) *multiple-value-p*)
659       (js!selfcall (ls-compile-block body t))))
660
661 (defun special-variable-p (x)
662   (and (claimp x 'variable 'special) t))
663
664 ;;; Wrap CODE to restore the symbol values of the dynamic
665 ;;; bindings. BINDINGS is a list of pairs of the form
666 ;;; (SYMBOL . PLACE),  where PLACE is a Javascript variable
667 ;;; name to initialize the symbol value and where to stored
668 ;;; the old value.
669 (defun let-binding-wrapper (bindings body)
670   (when (null bindings)
671     (return-from let-binding-wrapper body))
672   (code
673    "try {" *newline*
674    (indent "var tmp;" *newline*
675            (mapconcat
676             (lambda (b)
677               (let ((s (ls-compile `(quote ,(car b)))))
678                 (code "tmp = " s ".value;" *newline*
679                       s ".value = " (cdr b) ";" *newline*
680                       (cdr b) " = tmp;" *newline*)))
681             bindings)
682            body *newline*)
683    "}" *newline*
684    "finally {"  *newline*
685    (indent
686     (mapconcat (lambda (b)
687                  (let ((s (ls-compile `(quote ,(car b)))))
688                    (code s ".value" " = " (cdr b) ";" *newline*)))
689                bindings))
690    "}" *newline*))
691
692 (define-compilation let (bindings &rest body)
693   (let* ((bindings (mapcar #'ensure-list bindings))
694          (variables (mapcar #'first bindings))
695          (cvalues (mapcar #'ls-compile (mapcar #'second bindings)))
696          (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
697          (dynamic-bindings))
698     (code "(function("
699           (join (mapcar (lambda (x)
700                           (if (special-variable-p x)
701                               (let ((v (gvarname x)))
702                                 (push (cons x v) dynamic-bindings)
703                                 v)
704                               (translate-variable x)))
705                         variables)
706                 ",")
707           "){" *newline*
708           (let ((body (ls-compile-block body t)))
709             (indent (let-binding-wrapper dynamic-bindings body)))
710           "})(" (join cvalues ",") ")")))
711
712
713 ;;; Return the code to initialize BINDING, and push it extending the
714 ;;; current lexical environment if the variable is not special.
715 (defun let*-initialize-value (binding)
716   (let ((var (first binding))
717         (value (second binding)))
718     (if (special-variable-p var)
719         (code (ls-compile `(setq ,var ,value)) ";" *newline*)
720         (let* ((v (gvarname var))
721                (b (make-binding :name var :type 'variable :value v)))
722           (prog1 (code "var " v " = " (ls-compile value) ";" *newline*)
723             (push-to-lexenv b *environment* 'variable))))))
724
725 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
726 ;;; DOES NOT generate code to initialize the value of the symbols,
727 ;;; unlike let-binding-wrapper.
728 (defun let*-binding-wrapper (symbols body)
729   (when (null symbols)
730     (return-from let*-binding-wrapper body))
731   (let ((store (mapcar (lambda (s) (cons s (gvarname s)))
732                        (remove-if-not #'special-variable-p symbols))))
733     (code
734      "try {" *newline*
735      (indent
736       (mapconcat (lambda (b)
737                    (let ((s (ls-compile `(quote ,(car b)))))
738                      (code "var " (cdr b) " = " s ".value;" *newline*)))
739                  store)
740       body)
741      "}" *newline*
742      "finally {" *newline*
743      (indent
744       (mapconcat (lambda (b)
745                    (let ((s (ls-compile `(quote ,(car b)))))
746                      (code s ".value" " = " (cdr b) ";" *newline*)))
747                  store))
748      "}" *newline*)))
749
750 (define-compilation let* (bindings &rest body)
751   (let ((bindings (mapcar #'ensure-list bindings))
752         (*environment* (copy-lexenv *environment*)))
753     (js!selfcall
754       (let ((specials (remove-if-not #'special-variable-p (mapcar #'first bindings)))
755             (body (concat (mapconcat #'let*-initialize-value bindings)
756                           (ls-compile-block body t))))
757         (let*-binding-wrapper specials body)))))
758
759
760 (define-compilation block (name &rest body)
761   ;; We use Javascript exceptions to implement non local control
762   ;; transfer. Exceptions has dynamic scoping, so we use a uniquely
763   ;; generated object to identify the block. The instance of a empty
764   ;; array is used to distinguish between nested dynamic Javascript
765   ;; exceptions. See https://github.com/davazp/jscl/issues/64 for
766   ;; futher details.
767   (let* ((idvar (gvarname name))
768          (b (make-binding :name name :type 'block :value idvar)))
769     (when *multiple-value-p*
770       (push 'multiple-value (binding-declarations b)))
771     (let* ((*environment* (extend-lexenv (list b) *environment* 'block))
772            (cbody (ls-compile-block body t)))
773       (if (member 'used (binding-declarations b))
774           (js!selfcall
775             "try {" *newline*
776             "var " idvar " = [];" *newline*
777             (indent cbody)
778             "}" *newline*
779             "catch (cf){" *newline*
780             "    if (cf.type == 'block' && cf.id == " idvar ")" *newline*
781             (if *multiple-value-p*
782                 "        return values.apply(this, forcemv(cf.values));"
783                 "        return cf.values;")
784             *newline*
785             "    else" *newline*
786             "        throw cf;" *newline*
787             "}" *newline*)
788           (js!selfcall cbody)))))
789
790 (define-compilation return-from (name &optional value)
791   (let* ((b (lookup-in-lexenv name *environment* 'block))
792          (multiple-value-p (member 'multiple-value (binding-declarations b))))
793     (when (null b)
794       (error "Return from unknown block `~S'." (symbol-name name)))
795     (push 'used (binding-declarations b))
796     ;; The binding value is the name of a variable, whose value is the
797     ;; unique identifier of the block as exception. We can't use the
798     ;; variable name itself, because it could not to be unique, so we
799     ;; capture it in a closure.
800     (js!selfcall
801       (when multiple-value-p (code "var values = mv;" *newline*))
802       "throw ({"
803       "type: 'block', "
804       "id: " (binding-value b) ", "
805       "values: " (ls-compile value multiple-value-p) ", "
806       "message: 'Return from unknown block " (symbol-name name) ".'"
807       "})")))
808
809 (define-compilation catch (id &rest body)
810   (js!selfcall
811     "var id = " (ls-compile id) ";" *newline*
812     "try {" *newline*
813     (indent (ls-compile-block body t)) *newline*
814     "}" *newline*
815     "catch (cf){" *newline*
816     "    if (cf.type == 'catch' && cf.id == id)" *newline*
817     (if *multiple-value-p*
818         "        return values.apply(this, forcemv(cf.values));"
819         "        return pv.apply(this, forcemv(cf.values));")
820     *newline*
821     "    else" *newline*
822     "        throw cf;" *newline*
823     "}" *newline*))
824
825 (define-compilation throw (id value)
826   (js!selfcall
827     "var values = mv;" *newline*
828     "throw ({"
829     "type: 'catch', "
830     "id: " (ls-compile id) ", "
831     "values: " (ls-compile value t) ", "
832     "message: 'Throw uncatched.'"
833     "})"))
834
835 (defun go-tag-p (x)
836   (or (integerp x) (symbolp x)))
837
838 (defun declare-tagbody-tags (tbidx body)
839   (let* ((go-tag-counter 0)
840          (bindings
841           (mapcar (lambda (label)
842                     (let ((tagidx (integer-to-string (incf go-tag-counter))))
843                       (make-binding :name label :type 'gotag :value (list tbidx tagidx))))
844                   (remove-if-not #'go-tag-p body))))
845     (extend-lexenv bindings *environment* 'gotag)))
846
847 (define-compilation tagbody (&rest body)
848   ;; Ignore the tagbody if it does not contain any go-tag. We do this
849   ;; because 1) it is easy and 2) many built-in forms expand to a
850   ;; implicit tagbody, so we save some space.
851   (unless (some #'go-tag-p body)
852     (return-from tagbody (ls-compile `(progn ,@body nil))))
853   ;; The translation assumes the first form in BODY is a label
854   (unless (go-tag-p (car body))
855     (push (gensym "START") body))
856   ;; Tagbody compilation
857   (let ((branch (gvarname 'branch))
858         (tbidx (gvarname 'tbidx)))
859     (let ((*environment* (declare-tagbody-tags tbidx body))
860           initag)
861       (let ((b (lookup-in-lexenv (first body) *environment* 'gotag)))
862         (setq initag (second (binding-value b))))
863       (js!selfcall
864         ;; TAGBODY branch to take
865         "var " branch " = " initag ";" *newline*
866         "var " tbidx " = [];" *newline*
867         "tbloop:" *newline*
868         "while (true) {" *newline*
869         (indent "try {" *newline*
870                 (indent (let ((content ""))
871                           (code "switch(" branch "){" *newline*
872                                 "case " initag ":" *newline*
873                                 (dolist (form (cdr body) content)
874                                   (concatf content
875                                     (if (not (go-tag-p form))
876                                         (indent (ls-compile form) ";" *newline*)
877                                         (let ((b (lookup-in-lexenv form *environment* 'gotag)))
878                                           (code "case " (second (binding-value b)) ":" *newline*)))))
879                                 "default:" *newline*
880                                 "    break tbloop;" *newline*
881                                 "}" *newline*)))
882                 "}" *newline*
883                 "catch (jump) {" *newline*
884                 "    if (jump.type == 'tagbody' && jump.id == " tbidx ")" *newline*
885                 "        " branch " = jump.label;" *newline*
886                 "    else" *newline*
887                 "        throw(jump);" *newline*
888                 "}" *newline*)
889         "}" *newline*
890         "return " (ls-compile nil) ";" *newline*))))
891
892 (define-compilation go (label)
893   (let ((b (lookup-in-lexenv label *environment* 'gotag))
894         (n (cond
895              ((symbolp label) (symbol-name label))
896              ((integerp label) (integer-to-string label)))))
897     (when (null b)
898       (error "Unknown tag `~S'" label))
899     (js!selfcall
900       "throw ({"
901       "type: 'tagbody', "
902       "id: " (first (binding-value b)) ", "
903       "label: " (second (binding-value b)) ", "
904       "message: 'Attempt to GO to non-existing tag " n "'"
905       "})" *newline*)))
906
907 (define-compilation unwind-protect (form &rest clean-up)
908   (js!selfcall
909     "var ret = " (ls-compile nil) ";" *newline*
910     "try {" *newline*
911     (indent "ret = " (ls-compile form) ";" *newline*)
912     "} finally {" *newline*
913     (indent (ls-compile-block clean-up))
914     "}" *newline*
915     "return ret;" *newline*))
916
917 (define-compilation multiple-value-call (func-form &rest forms)
918   (js!selfcall
919     "var func = " (ls-compile func-form) ";" *newline*
920     "var args = [" (if *multiple-value-p* "values" "pv") ", 0];" *newline*
921     "return "
922     (js!selfcall
923       "var values = mv;" *newline*
924       "var vs;" *newline*
925       (mapconcat (lambda (form)
926                    (code "vs = " (ls-compile form t) ";" *newline*
927                          "if (typeof vs === 'object' && 'multiple-value' in vs)" *newline*
928                          (indent "args = args.concat(vs);" *newline*)
929                          "else" *newline*
930                          (indent "args.push(vs);" *newline*)))
931                  forms)
932       "args[1] = args.length-2;" *newline*
933       "return func.apply(window, args);" *newline*) ";" *newline*))
934
935 (define-compilation multiple-value-prog1 (first-form &rest forms)
936   (js!selfcall
937     "var args = " (ls-compile first-form *multiple-value-p*) ";" *newline*
938     (ls-compile-block forms)
939     "return args;" *newline*))
940
941
942 ;;; Backquote implementation.
943 ;;;
944 ;;;    Author: Guy L. Steele Jr.     Date: 27 December 1985
945 ;;;    Tested under Symbolics Common Lisp and Lucid Common Lisp.
946 ;;;    This software is in the public domain.
947
948 ;;;    The following are unique tokens used during processing.
949 ;;;    They need not be symbols; they need not even be atoms.
950 (defvar *comma* 'unquote)
951 (defvar *comma-atsign* 'unquote-splicing)
952
953 (defvar *bq-list* (make-symbol "BQ-LIST"))
954 (defvar *bq-append* (make-symbol "BQ-APPEND"))
955 (defvar *bq-list** (make-symbol "BQ-LIST*"))
956 (defvar *bq-nconc* (make-symbol "BQ-NCONC"))
957 (defvar *bq-clobberable* (make-symbol "BQ-CLOBBERABLE"))
958 (defvar *bq-quote* (make-symbol "BQ-QUOTE"))
959 (defvar *bq-quote-nil* (list *bq-quote* nil))
960
961 ;;; BACKQUOTE is an ordinary macro (not a read-macro) that processes
962 ;;; the expression foo, looking for occurrences of #:COMMA,
963 ;;; #:COMMA-ATSIGN, and #:COMMA-DOT.  It constructs code in strict
964 ;;; accordance with the rules on pages 349-350 of the first edition
965 ;;; (pages 528-529 of this second edition).  It then optionally
966 ;;; applies a code simplifier.
967
968 ;;; If the value of *BQ-SIMPLIFY* is non-NIL, then BACKQUOTE
969 ;;; processing applies the code simplifier.  If the value is NIL,
970 ;;; then the code resulting from BACKQUOTE is exactly that
971 ;;; specified by the official rules.
972 (defparameter *bq-simplify* t)
973
974 (defmacro backquote (x)
975   (bq-completely-process x))
976
977 ;;; Backquote processing proceeds in three stages:
978 ;;;
979 ;;; (1) BQ-PROCESS applies the rules to remove occurrences of
980 ;;; #:COMMA, #:COMMA-ATSIGN, and #:COMMA-DOT corresponding to
981 ;;; this level of BACKQUOTE.  (It also causes embedded calls to
982 ;;; BACKQUOTE to be expanded so that nesting is properly handled.)
983 ;;; Code is produced that is expressed in terms of functions
984 ;;; #:BQ-LIST, #:BQ-APPEND, and #:BQ-CLOBBERABLE.  This is done
985 ;;; so that the simplifier will simplify only list construction
986 ;;; functions actually generated by BACKQUOTE and will not involve
987 ;;; any user code in the simplification.  #:BQ-LIST means LIST,
988 ;;; #:BQ-APPEND means APPEND, and #:BQ-CLOBBERABLE means IDENTITY
989 ;;; but indicates places where "%." was used and where NCONC may
990 ;;; therefore be introduced by the simplifier for efficiency.
991 ;;;
992 ;;; (2) BQ-SIMPLIFY, if used, rewrites the code produced by
993 ;;; BQ-PROCESS to produce equivalent but faster code.  The
994 ;;; additional functions #:BQ-LIST* and #:BQ-NCONC may be
995 ;;; introduced into the code.
996 ;;;
997 ;;; (3) BQ-REMOVE-TOKENS goes through the code and replaces
998 ;;; #:BQ-LIST with LIST, #:BQ-APPEND with APPEND, and so on.
999 ;;; #:BQ-CLOBBERABLE is simply eliminated (a call to it being
1000 ;;; replaced by its argument).  #:BQ-LIST* is replaced by either
1001 ;;; LIST* or CONS (the latter is used in the two-argument case,
1002 ;;; purely to make the resulting code a tad more readable).
1003
1004 (defun bq-completely-process (x)
1005   (let ((raw-result (bq-process x)))
1006     (bq-remove-tokens (if *bq-simplify*
1007                           (bq-simplify raw-result)
1008                           raw-result))))
1009
1010 (defun bq-process (x)
1011   (cond ((atom x)
1012          (list *bq-quote* x))
1013         ((eq (car x) 'backquote)
1014          (bq-process (bq-completely-process (cadr x))))
1015         ((eq (car x) *comma*) (cadr x))
1016         ((eq (car x) *comma-atsign*)
1017          (error ",@~S after `" (cadr x)))
1018         ;; ((eq (car x) *comma-dot*)
1019         ;;  ;; (error ",.~S after `" (cadr x))
1020         ;;  (error "ill-formed"))
1021         (t (do ((p x (cdr p))
1022                 (q '() (cons (bracket (car p)) q)))
1023                ((atom p)
1024                 (cons *bq-append*
1025                       (nreconc q (list (list *bq-quote* p)))))
1026              (when (eq (car p) *comma*)
1027                (unless (null (cddr p))
1028                  (error "Malformed ,~S" p))
1029                (return (cons *bq-append*
1030                              (nreconc q (list (cadr p))))))
1031              (when (eq (car p) *comma-atsign*)
1032                (error "Dotted ,@~S" p))
1033              ;; (when (eq (car p) *comma-dot*)
1034              ;;   ;; (error "Dotted ,.~S" p)
1035              ;;   (error "Dotted"))
1036              ))))
1037
1038 ;;; This implements the bracket operator of the formal rules.
1039 (defun bracket (x)
1040   (cond ((atom x)
1041          (list *bq-list* (bq-process x)))
1042         ((eq (car x) *comma*)
1043          (list *bq-list* (cadr x)))
1044         ((eq (car x) *comma-atsign*)
1045          (cadr x))
1046         ;; ((eq (car x) *comma-dot*)
1047         ;;  (list *bq-clobberable* (cadr x)))
1048         (t (list *bq-list* (bq-process x)))))
1049
1050 ;;; This auxiliary function is like MAPCAR but has two extra
1051 ;;; purposes: (1) it handles dotted lists; (2) it tries to make
1052 ;;; the result share with the argument x as much as possible.
1053 (defun maptree (fn x)
1054   (if (atom x)
1055       (funcall fn x)
1056       (let ((a (funcall fn (car x)))
1057             (d (maptree fn (cdr x))))
1058         (if (and (eql a (car x)) (eql d (cdr x)))
1059             x
1060             (cons a d)))))
1061
1062 ;;; This predicate is true of a form that when read looked
1063 ;;; like %@foo or %.foo.
1064 (defun bq-splicing-frob (x)
1065   (and (consp x)
1066        (or (eq (car x) *comma-atsign*)
1067            ;; (eq (car x) *comma-dot*)
1068            )))
1069
1070 ;;; This predicate is true of a form that when read
1071 ;;; looked like %@foo or %.foo or just plain %foo.
1072 (defun bq-frob (x)
1073   (and (consp x)
1074        (or (eq (car x) *comma*)
1075            (eq (car x) *comma-atsign*)
1076            ;; (eq (car x) *comma-dot*)
1077            )))
1078
1079 ;;; The simplifier essentially looks for calls to #:BQ-APPEND and
1080 ;;; tries to simplify them.  The arguments to #:BQ-APPEND are
1081 ;;; processed from right to left, building up a replacement form.
1082 ;;; At each step a number of special cases are handled that,
1083 ;;; loosely speaking, look like this:
1084 ;;;
1085 ;;;  (APPEND (LIST a b c) foo) => (LIST* a b c foo)
1086 ;;;       provided a, b, c are not splicing frobs
1087 ;;;  (APPEND (LIST* a b c) foo) => (LIST* a b (APPEND c foo))
1088 ;;;       provided a, b, c are not splicing frobs
1089 ;;;  (APPEND (QUOTE (x)) foo) => (LIST* (QUOTE x) foo)
1090 ;;;  (APPEND (CLOBBERABLE x) foo) => (NCONC x foo)
1091 (defun bq-simplify (x)
1092   (if (atom x)
1093       x
1094       (let ((x (if (eq (car x) *bq-quote*)
1095                    x
1096                    (maptree #'bq-simplify x))))
1097         (if (not (eq (car x) *bq-append*))
1098             x
1099             (bq-simplify-args x)))))
1100
1101 (defun bq-simplify-args (x)
1102   (do ((args (reverse (cdr x)) (cdr args))
1103        (result
1104          nil
1105          (cond ((atom (car args))
1106                 (bq-attach-append *bq-append* (car args) result))
1107                ((and (eq (caar args) *bq-list*)
1108                      (notany #'bq-splicing-frob (cdar args)))
1109                 (bq-attach-conses (cdar args) result))
1110                ((and (eq (caar args) *bq-list**)
1111                      (notany #'bq-splicing-frob (cdar args)))
1112                 (bq-attach-conses
1113                   (reverse (cdr (reverse (cdar args))))
1114                   (bq-attach-append *bq-append*
1115                                     (car (last (car args)))
1116                                     result)))
1117                ((and (eq (caar args) *bq-quote*)
1118                      (consp (cadar args))
1119                      (not (bq-frob (cadar args)))
1120                      (null (cddar args)))
1121                 (bq-attach-conses (list (list *bq-quote*
1122                                               (caadar args)))
1123                                   result))
1124                ((eq (caar args) *bq-clobberable*)
1125                 (bq-attach-append *bq-nconc* (cadar args) result))
1126                (t (bq-attach-append *bq-append*
1127                                     (car args)
1128                                     result)))))
1129       ((null args) result)))
1130
1131 (defun null-or-quoted (x)
1132   (or (null x) (and (consp x) (eq (car x) *bq-quote*))))
1133
1134 ;;; When BQ-ATTACH-APPEND is called, the OP should be #:BQ-APPEND
1135 ;;; or #:BQ-NCONC.  This produces a form (op item result) but
1136 ;;; some simplifications are done on the fly:
1137 ;;;
1138 ;;;  (op '(a b c) '(d e f g)) => '(a b c d e f g)
1139 ;;;  (op item 'nil) => item, provided item is not a splicable frob
1140 ;;;  (op item 'nil) => (op item), if item is a splicable frob
1141 ;;;  (op item (op a b c)) => (op item a b c)
1142 (defun bq-attach-append (op item result)
1143   (cond ((and (null-or-quoted item) (null-or-quoted result))
1144          (list *bq-quote* (append (cadr item) (cadr result))))
1145         ((or (null result) (equal result *bq-quote-nil*))
1146          (if (bq-splicing-frob item) (list op item) item))
1147         ((and (consp result) (eq (car result) op))
1148          (list* (car result) item (cdr result)))
1149         (t (list op item result))))
1150
1151 ;;; The effect of BQ-ATTACH-CONSES is to produce a form as if by
1152 ;;; `(LIST* ,@items ,result) but some simplifications are done
1153 ;;; on the fly.
1154 ;;;
1155 ;;;  (LIST* 'a 'b 'c 'd) => '(a b c . d)
1156 ;;;  (LIST* a b c 'nil) => (LIST a b c)
1157 ;;;  (LIST* a b c (LIST* d e f g)) => (LIST* a b c d e f g)
1158 ;;;  (LIST* a b c (LIST d e f g)) => (LIST a b c d e f g)
1159 (defun bq-attach-conses (items result)
1160   (cond ((and (every #'null-or-quoted items)
1161               (null-or-quoted result))
1162          (list *bq-quote*
1163                (append (mapcar #'cadr items) (cadr result))))
1164         ((or (null result) (equal result *bq-quote-nil*))
1165          (cons *bq-list* items))
1166         ((and (consp result)
1167               (or (eq (car result) *bq-list*)
1168                   (eq (car result) *bq-list**)))
1169          (cons (car result) (append items (cdr result))))
1170         (t (cons *bq-list** (append items (list result))))))
1171
1172 ;;; Removes funny tokens and changes (#:BQ-LIST* a b) into
1173 ;;; (CONS a b) instead of (LIST* a b), purely for readability.
1174 (defun bq-remove-tokens (x)
1175   (cond ((eq x *bq-list*) 'list)
1176         ((eq x *bq-append*) 'append)
1177         ((eq x *bq-nconc*) 'nconc)
1178         ((eq x *bq-list**) 'list*)
1179         ((eq x *bq-quote*) 'quote)
1180         ((atom x) x)
1181         ((eq (car x) *bq-clobberable*)
1182          (bq-remove-tokens (cadr x)))
1183         ((and (eq (car x) *bq-list**)
1184               (consp (cddr x))
1185               (null (cdddr x)))
1186          (cons 'cons (maptree #'bq-remove-tokens (cdr x))))
1187         (t (maptree #'bq-remove-tokens x))))
1188
1189 (define-transformation backquote (form)
1190   (bq-completely-process form))
1191
1192
1193 ;;; Primitives
1194
1195 (defvar *builtins* nil)
1196
1197 (defmacro define-raw-builtin (name args &body body)
1198   ;; Creates a new primitive function `name' with parameters args and
1199   ;; @body. The body can access to the local environment through the
1200   ;; variable *ENVIRONMENT*.
1201   `(push (list ',name (lambda ,args (block ,name ,@body)))
1202          *builtins*))
1203
1204 (defmacro define-builtin (name args &body body)
1205   `(define-raw-builtin ,name ,args
1206      (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg))) args)
1207        ,@body)))
1208
1209 ;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations.
1210 (defmacro type-check (decls &body body)
1211   `(js!selfcall
1212      ,@(mapcar (lambda (decl)
1213                  `(code "var " ,(first decl) " = " ,(third decl) ";" *newline*))
1214                decls)
1215      ,@(mapcar (lambda (decl)
1216                  `(code "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline*
1217                         (indent "throw 'The value ' + "
1218                                 ,(first decl)
1219                                 " + ' is not a type "
1220                                 ,(second decl)
1221                                 ".';"
1222                                 *newline*)))
1223                decls)
1224      (code "return " (progn ,@body) ";" *newline*)))
1225
1226 ;;; VARIABLE-ARITY compiles variable arity operations. ARGS stands for
1227 ;;; a variable which holds a list of forms. It will compile them and
1228 ;;; store the result in some Javascript variables. BODY is evaluated
1229 ;;; with ARGS bound to the list of these variables to generate the
1230 ;;; code which performs the transformation on these variables.
1231
1232 (defun variable-arity-call (args function)
1233   (unless (consp args)
1234     (error "ARGS must be a non-empty list"))
1235   (let ((counter 0)
1236         (fargs '())
1237         (prelude ""))
1238     (dolist (x args)
1239       (cond
1240         ((floatp x) (push (float-to-string x) fargs))
1241         ((numberp x) (push (integer-to-string x) fargs))
1242         (t (let ((v (code "x" (incf counter))))
1243              (push v fargs)
1244              (concatf prelude
1245                (code "var " v " = " (ls-compile x) ";" *newline*
1246                      "if (typeof " v " !== 'number') throw 'Not a number!';"
1247                      *newline*))))))
1248     (js!selfcall prelude (funcall function (reverse fargs)))))
1249
1250
1251 (defmacro variable-arity (args &body body)
1252   (unless (symbolp args)
1253     (error "`~S' is not a symbol." args))
1254   `(variable-arity-call ,args
1255                         (lambda (,args)
1256                           (code "return " ,@body ";" *newline*))))
1257
1258 (defun num-op-num (x op y)
1259   (type-check (("x" "number" x) ("y" "number" y))
1260     (code "x" op "y")))
1261
1262 (define-raw-builtin + (&rest numbers)
1263   (if (null numbers)
1264       "0"
1265       (variable-arity numbers
1266         (join numbers "+"))))
1267
1268 (define-raw-builtin - (x &rest others)
1269   (let ((args (cons x others)))
1270     (variable-arity args
1271       (if (null others)
1272           (concat "-" (car args))
1273           (join args "-")))))
1274
1275 (define-raw-builtin * (&rest numbers)
1276   (if (null numbers)
1277       "1"
1278       (variable-arity numbers
1279         (join numbers "*"))))
1280
1281 (define-raw-builtin / (x &rest others)
1282   (let ((args (cons x others)))
1283     (variable-arity args
1284       (if (null others)
1285           (concat "1 /" (car args))
1286           (join args "/")))))
1287
1288 (define-builtin mod (x y) (num-op-num x "%" y))
1289
1290
1291 (defun comparison-conjuntion (vars op)
1292   (cond
1293     ((null (cdr vars))
1294      "true")
1295     ((null (cddr vars))
1296      (concat (car vars) op (cadr vars)))
1297     (t
1298      (concat (car vars) op (cadr vars)
1299              " && "
1300              (comparison-conjuntion (cdr vars) op)))))
1301
1302 (defmacro define-builtin-comparison (op sym)
1303   `(define-raw-builtin ,op (x &rest args)
1304      (let ((args (cons x args)))
1305        (variable-arity args
1306          (js!bool (comparison-conjuntion args ,sym))))))
1307
1308 (define-builtin-comparison > ">")
1309 (define-builtin-comparison < "<")
1310 (define-builtin-comparison >= ">=")
1311 (define-builtin-comparison <= "<=")
1312 (define-builtin-comparison = "==")
1313
1314 (define-builtin numberp (x)
1315   (js!bool (code "(typeof (" x ") == \"number\")")))
1316
1317 (define-builtin floor (x)
1318   (type-check (("x" "number" x))
1319     "Math.floor(x)"))
1320
1321 (define-builtin expt (x y)
1322   (type-check (("x" "number" x)
1323                ("y" "number" y))
1324     "Math.pow(x, y)"))
1325
1326 (define-builtin float-to-string (x)
1327   (type-check (("x" "number" x))
1328     "make_lisp_string(x.toString())"))
1329
1330 (define-builtin cons (x y)
1331   (code "({car: " x ", cdr: " y "})"))
1332
1333 (define-builtin consp (x)
1334   (js!bool
1335    (js!selfcall
1336      "var tmp = " x ";" *newline*
1337      "return (typeof tmp == 'object' && 'car' in tmp);" *newline*)))
1338
1339 (define-builtin car (x)
1340   (js!selfcall
1341     "var tmp = " x ";" *newline*
1342     "return tmp === " (ls-compile nil)
1343     "? " (ls-compile nil)
1344     ": tmp.car;" *newline*))
1345
1346 (define-builtin cdr (x)
1347   (js!selfcall
1348     "var tmp = " x ";" *newline*
1349     "return tmp === " (ls-compile nil) "? "
1350     (ls-compile nil)
1351     ": tmp.cdr;" *newline*))
1352
1353 (define-builtin rplaca (x new)
1354   (type-check (("x" "object" x))
1355     (code "(x.car = " new ", x)")))
1356
1357 (define-builtin rplacd (x new)
1358   (type-check (("x" "object" x))
1359     (code "(x.cdr = " new ", x)")))
1360
1361 (define-builtin symbolp (x)
1362   (js!bool (code "(" x " instanceof Symbol)")))
1363
1364 (define-builtin make-symbol (name)
1365   (code "(new Symbol(" name "))"))
1366
1367 (define-builtin symbol-name (x)
1368   (code "(" x ").name"))
1369
1370 (define-builtin set (symbol value)
1371   (code "(" symbol ").value = " value))
1372
1373 (define-builtin fset (symbol value)
1374   (code "(" symbol ").fvalue = " value))
1375
1376 (define-builtin boundp (x)
1377   (js!bool (code "(" x ".value !== undefined)")))
1378
1379 (define-builtin symbol-value (x)
1380   (js!selfcall
1381     "var symbol = " x ";" *newline*
1382     "var value = symbol.value;" *newline*
1383     "if (value === undefined) throw \"Variable `\" + xstring(symbol.name) + \"' is unbound.\";" *newline*
1384     "return value;" *newline*))
1385
1386 (define-builtin symbol-function (x)
1387   (js!selfcall
1388     "var symbol = " x ";" *newline*
1389     "var func = symbol.fvalue;" *newline*
1390     "if (func === undefined) throw \"Function `\" + xstring(symbol.name) + \"' is undefined.\";" *newline*
1391     "return func;" *newline*))
1392
1393 (define-builtin symbol-plist (x)
1394   (code "((" x ").plist || " (ls-compile nil) ")"))
1395
1396 (define-builtin lambda-code (x)
1397   (code "make_lisp_string((" x ").toString())"))
1398
1399 (define-builtin eq (x y)
1400   (js!bool (code "(" x " === " y ")")))
1401
1402 (define-builtin char-code (x)
1403   (type-check (("x" "string" x))
1404     "x.charCodeAt(0)"))
1405
1406 (define-builtin code-char (x)
1407   (type-check (("x" "number" x))
1408     "String.fromCharCode(x)"))
1409
1410 (define-builtin characterp (x)
1411   (js!bool
1412    (js!selfcall
1413      "var x = " x ";" *newline*
1414      "return (typeof(" x ") == \"string\") && x.length == 1;")))
1415
1416 (define-builtin char-to-string (x)
1417   (js!selfcall
1418     "var r = [" x "];" *newline*
1419     "r.type = 'character';"
1420     "return r"))
1421
1422 (define-builtin char-upcase (x)
1423   (code x ".toUpperCase()"))
1424
1425 (define-builtin char-downcase (x)
1426   (code x ".toLowerCase()"))
1427
1428 (define-builtin stringp (x)
1429   (js!bool
1430    (js!selfcall
1431      "var x = " x ";" *newline*
1432      "return typeof(x) == 'object' && 'length' in x && x.type == 'character';")))
1433
1434 (define-builtin string-upcase (x)
1435   (code "make_lisp_string(xstring(" x ").toUpperCase())"))
1436
1437 (define-builtin string-length (x)
1438   (code x ".length"))
1439
1440 (define-raw-builtin slice (vector a &optional b)
1441   (js!selfcall
1442     "var vector = " (ls-compile vector) ";" *newline*
1443     "var a = " (ls-compile a) ";" *newline*
1444     "var b;" *newline*
1445     (when b (code "b = " (ls-compile b) ";" *newline*))
1446     "return vector.slice(a,b);" *newline*))
1447
1448 (define-builtin char (string index)
1449   (code string "[" index "]"))
1450
1451 (define-builtin concat-two (string1 string2)
1452   (js!selfcall
1453     "var r = " string1 ".concat(" string2 ");" *newline*
1454     "r.type = 'character';"
1455     "return r;" *newline*))
1456
1457 (define-raw-builtin funcall (func &rest args)
1458   (js!selfcall
1459     "var f = " (ls-compile func) ";" *newline*
1460     "return (typeof f === 'function'? f: f.fvalue)("
1461     (join (list* (if *multiple-value-p* "values" "pv")
1462                  (integer-to-string (length args))
1463                  (mapcar #'ls-compile args))
1464           ", ")
1465     ")"))
1466
1467 (define-raw-builtin apply (func &rest args)
1468   (if (null args)
1469       (code "(" (ls-compile func) ")()")
1470       (let ((args (butlast args))
1471             (last (car (last args))))
1472         (js!selfcall
1473           "var f = " (ls-compile func) ";" *newline*
1474           "var args = [" (join (list* (if *multiple-value-p* "values" "pv")
1475                                       (integer-to-string (length args))
1476                                       (mapcar #'ls-compile args))
1477                                ", ")
1478           "];" *newline*
1479           "var tail = (" (ls-compile last) ");" *newline*
1480           "while (tail != " (ls-compile nil) "){" *newline*
1481           "    args.push(tail.car);" *newline*
1482           "    args[1] += 1;" *newline*
1483           "    tail = tail.cdr;" *newline*
1484           "}" *newline*
1485           "return (typeof f === 'function'? f : f.fvalue).apply(this, args);" *newline*))))
1486
1487 (define-builtin js-eval (string)
1488   (if *multiple-value-p*
1489       (js!selfcall
1490         "var v = globalEval(xstring(" string "));" *newline*
1491         "return values.apply(this, forcemv(v));" *newline*)
1492       (code "globalEval(xstring(" string "))")))
1493
1494 (define-builtin %throw (string)
1495   (js!selfcall "throw " string ";" *newline*))
1496
1497 (define-builtin new () "{}")
1498
1499 (define-builtin objectp (x)
1500   (js!bool (code "(typeof (" x ") === 'object')")))
1501
1502 (define-builtin oget (object key)
1503   (js!selfcall
1504     "var tmp = " "(" object ")[xstring(" key ")];" *newline*
1505     "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*))
1506
1507 (define-builtin oset (object key value)
1508   (code "((" object ")[xstring(" key ")] = " value ")"))
1509
1510 (define-builtin in (key object)
1511   (js!bool (code "(xstring(" key ") in (" object "))")))
1512
1513 (define-builtin functionp (x)
1514   (js!bool (code "(typeof " x " == 'function')")))
1515
1516 (define-builtin write-string (x)
1517   (code "lisp.write(" x ")"))
1518
1519 (define-builtin make-array (n)
1520   (js!selfcall
1521     "var r = [];" *newline*
1522     "for (var i = 0; i < " n "; i++)" *newline*
1523     (indent "r.push(" (ls-compile nil) ");" *newline*)
1524     "return r;" *newline*))
1525
1526 (define-builtin arrayp (x)
1527   (js!bool
1528    (js!selfcall
1529      "var x = " x ";" *newline*
1530      "return typeof x === 'object' && 'length' in x;")))
1531
1532 (define-builtin aref (array n)
1533   (js!selfcall
1534     "var x = " "(" array ")[" n "];" *newline*
1535     "if (x === undefined) throw 'Out of range';" *newline*
1536     "return x;" *newline*))
1537
1538 (define-builtin aset (array n value)
1539   (js!selfcall
1540     "var x = " array ";" *newline*
1541     "var i = " n ";" *newline*
1542     "if (i < 0 || i >= x.length) throw 'Out of range';" *newline*
1543     "return x[i] = " value ";" *newline*))
1544
1545 (define-builtin get-internal-real-time ()
1546   "(new Date()).getTime()")
1547
1548 (define-builtin values-array (array)
1549   (if *multiple-value-p*
1550       (code "values.apply(this, " array ")")
1551       (code "pv.apply(this, " array ")")))
1552
1553 (define-raw-builtin values (&rest args)
1554   (if *multiple-value-p*
1555       (code "values(" (join (mapcar #'ls-compile args) ", ") ")")
1556       (code "pv(" (join (mapcar #'ls-compile args) ", ") ")")))
1557
1558
1559 ;;; Javascript FFI
1560
1561 (define-compilation %js-vref (var)
1562   (code "js_to_lisp(" var ")"))
1563
1564 (define-compilation %js-vset (var val)
1565   (code "(" var " = lisp_to_js(" (ls-compile val) "))"))
1566
1567 (define-setf-expander %js-vref (var)
1568   (let ((new-value (gensym)))
1569     (unless (stringp var)
1570       (error "`~S' is not a string." var))
1571     (values nil
1572             (list var)
1573             (list new-value)
1574             `(%js-vset ,var ,new-value)
1575             `(%js-vref ,var))))
1576
1577
1578 #+common-lisp
1579 (defvar *macroexpander-cache*
1580   (make-hash-table :test #'eq))
1581
1582 (defun !macro-function (symbol)
1583   (unless (symbolp symbol)
1584     (error "`~S' is not a symbol." symbol))
1585   (let ((b (lookup-in-lexenv symbol *environment* 'function)))
1586     (if (and b (eq (binding-type b) 'macro))
1587         (let ((expander (binding-value b)))
1588           (cond
1589             #+common-lisp
1590             ((gethash b *macroexpander-cache*)
1591              (setq expander (gethash b *macroexpander-cache*)))
1592             ((listp expander)
1593              (let ((compiled (eval expander)))
1594                ;; The list representation are useful while
1595                ;; bootstrapping, as we can dump the definition of the
1596                ;; macros easily, but they are slow because we have to
1597                ;; evaluate them and compile them now and again. So, let
1598                ;; us replace the list representation version of the
1599                ;; function with the compiled one.
1600                ;;
1601                #+jscl (setf (binding-value b) compiled)
1602                #+common-lisp (setf (gethash b *macroexpander-cache*) compiled)
1603                (setq expander compiled))))
1604           expander)
1605         nil)))
1606
1607 (defun !macroexpand-1 (form)
1608   (cond
1609     ((symbolp form)
1610      (let ((b (lookup-in-lexenv form *environment* 'variable)))
1611        (if (and b (eq (binding-type b) 'macro))
1612            (values (binding-value b) t)
1613            (values form nil))))
1614     ((and (consp form) (symbolp (car form)))
1615      (let ((macrofun (!macro-function (car form))))
1616        (if macrofun
1617            (values (apply macrofun (cdr form)) t)
1618            (values form nil))))
1619     (t
1620      (values form nil))))
1621
1622 (defun compile-funcall (function args)
1623   (let* ((values-funcs (if *multiple-value-p* "values" "pv"))
1624          (arglist (concat "(" (join (list* values-funcs
1625                                            (integer-to-string (length args))
1626                                            (mapcar #'ls-compile args)) ", ") ")")))
1627     (unless (or (symbolp function)
1628                 (and (consp function)
1629                      (eq (car function) 'lambda)))
1630       (error "Bad function designator `~S'" function))
1631     (cond
1632       ((translate-function function)
1633        (concat (translate-function function) arglist))
1634       ((and (symbolp function)
1635             #+jscl (eq (symbol-package function) (find-package "COMMON-LISP"))
1636             #+common-lisp t)
1637        (code (ls-compile `',function) ".fvalue" arglist))
1638       (t
1639        (code (ls-compile `#',function) arglist)))))
1640
1641 (defun ls-compile-block (sexps &optional return-last-p)
1642   (if return-last-p
1643       (code (ls-compile-block (butlast sexps))
1644             "return " (ls-compile (car (last sexps)) *multiple-value-p*) ";")
1645       (join-trailing
1646        (remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
1647        (concat ";" *newline*))))
1648
1649 (defun ls-compile (sexp &optional multiple-value-p)
1650   (multiple-value-bind (sexp expandedp) (!macroexpand-1 sexp)
1651     (when expandedp
1652       (return-from ls-compile (ls-compile sexp multiple-value-p)))
1653     ;; The expression has been macroexpanded. Now compile it!
1654     (let ((*multiple-value-p* multiple-value-p))
1655       (cond
1656         ((symbolp sexp)
1657          (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
1658            (cond
1659              ((and b (not (member 'special (binding-declarations b))))
1660               (binding-value b))
1661              ((or (keywordp sexp)
1662                   (and b (member 'constant (binding-declarations b))))
1663               (code (ls-compile `',sexp) ".value"))
1664              (t
1665               (ls-compile `(symbol-value ',sexp))))))
1666         ((or (integerp sexp) (floatp sexp) (characterp sexp) (stringp sexp) (arrayp sexp))
1667          (literal sexp))
1668         ((listp sexp)
1669          (let ((name (car sexp))
1670                (args (cdr sexp)))
1671            (cond
1672              ;; Special forms
1673              ((assoc name *compilations*)
1674               (let ((comp (second (assoc name *compilations*))))
1675                 (apply comp args)))
1676              ;; Built-in functions
1677              ((and (assoc name *builtins*)
1678                    (not (claimp name 'function 'notinline)))
1679               (let ((comp (second (assoc name *builtins*))))
1680                 (apply comp args)))
1681              (t
1682               (compile-funcall name args)))))
1683         (t
1684          (error "How should I compile `~S'?" sexp))))))
1685
1686
1687 (defvar *compile-print-toplevels* nil)
1688
1689 (defun truncate-string (string &optional (width 60))
1690   (let ((n (or (position #\newline string)
1691                (min width (length string)))))
1692     (subseq string 0 n)))
1693
1694 (defun ls-compile-toplevel (sexp &optional multiple-value-p)
1695   (let ((*toplevel-compilations* nil))
1696     (cond
1697       ((and (consp sexp) (eq (car sexp) 'progn))
1698        (let ((subs (mapcar (lambda (s)
1699                              (ls-compile-toplevel s t))
1700                            (cdr sexp))))
1701          (join (remove-if #'null-or-empty-p subs))))
1702       (t
1703        (when *compile-print-toplevels*
1704          (let ((form-string (prin1-to-string sexp)))
1705            (format t "Compiling ~a..." (truncate-string form-string))))
1706        (let ((code (ls-compile sexp multiple-value-p)))
1707          (code (join-trailing (get-toplevel-compilations)
1708                               (code ";" *newline*))
1709                (when code
1710                  (code code ";" *newline*))))))))