Smarter string escaping. Fixes #114
[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 #-jscl
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 (defun escape-string (string)
486   (let ((index 0)
487         (size (length string))
488         (seen-single-quote nil)
489         (seen-double-quote nil))
490     (flet ((%escape-string (string escape-single-quote-p)
491              (let ((output "")
492                    (index 0))
493                (while (< index size)
494                  (let ((ch (char string index)))
495                    (when (char= ch #\\)
496                      (setq output (concat output "\\")))
497                    (when (and escape-single-quote-p (char= ch #\'))
498                      (setq output (concat output "\\")))
499                    (when (char= ch #\newline)
500                      (setq output (concat output "\\"))
501                      (setq ch #\n))
502                    (setq output (concat output (string ch))))
503                  (incf index))
504                output)))
505       ;; First, scan the string for single/double quotes
506       (while (< index size)
507         (let ((ch (char string index)))
508           (when (char= ch #\')
509             (setq seen-single-quote t))
510           (when (char= ch #\")
511             (setq seen-double-quote t)))
512         (incf index))
513       ;; Then pick the appropriate way to escape the quotes
514       (cond
515         ((not seen-single-quote)
516          (concat "'"   (%escape-string string nil) "'"))
517         ((not seen-double-quote)
518          (concat "\""  (%escape-string string nil) "\""))
519         (t (concat "'" (%escape-string string t)   "'"))))))
520
521 ;;; BOOTSTRAP MAGIC: We record the macro definitions as lists during
522 ;;; the bootstrap. Once everything is compiled, we want to dump the
523 ;;; whole global environment to the output file to reproduce it in the
524 ;;; run-time. However, the environment must contain expander functions
525 ;;; rather than lists. We do not know how to dump function objects
526 ;;; itself, so we mark the list definitions with this object and the
527 ;;; compiler will be called when this object has to be dumped.
528 ;;; Backquote/unquote does a similar magic, but this use is exclusive.
529 ;;;
530 ;;; Indeed, perhaps to compile the object other macros need to be
531 ;;; evaluated. For this reason we define a valid macro-function for
532 ;;; this symbol.
533 (defvar *magic-unquote-marker* (gensym "MAGIC-UNQUOTE"))
534 #-jscl
535 (setf (macro-function *magic-unquote-marker*)
536       (lambda (form &optional environment)
537         (declare (ignore environment))
538         (second form)))
539
540 (defvar *literal-table* nil)
541 (defvar *literal-counter* 0)
542
543 (defun genlit ()
544   (code "l" (incf *literal-counter*)))
545
546 (defun dump-symbol (symbol)
547   #-jscl
548   (let ((package (symbol-package symbol)))
549     (if (eq package (find-package "KEYWORD"))
550         (code "(new Symbol(" (dump-string (symbol-name symbol)) ", " (dump-string (package-name package)) "))")
551         (code "(new Symbol(" (dump-string (symbol-name symbol)) "))")))
552   #+jscl
553   (let ((package (symbol-package symbol)))
554     (if (null package)
555         (code "(new Symbol(" (dump-string (symbol-name symbol)) "))")
556         (ls-compile `(intern ,(symbol-name symbol) ,(package-name package))))))
557
558 (defun dump-cons (cons)
559   (let ((head (butlast cons))
560         (tail (last cons)))
561     (code "QIList("
562           (join-trailing (mapcar (lambda (x) (literal x t)) head) ",")
563           (literal (car tail) t)
564           ","
565           (literal (cdr tail) t)
566           ")")))
567
568 (defun dump-array (array)
569   (let ((elements (vector-to-list array)))
570     (concat "[" (join (mapcar #'literal elements) ", ") "]")))
571
572 (defun dump-string (string)
573   (code "make_lisp_string(" (escape-string string) ")"))
574
575 (defun literal (sexp &optional recursive)
576   (cond
577     ((integerp sexp) (integer-to-string sexp))
578     ((floatp sexp) (float-to-string sexp))
579     ((characterp sexp) (escape-string (string sexp)))
580     (t
581      (or (cdr (assoc sexp *literal-table* :test #'eql))
582          (let ((dumped (typecase sexp
583                          (symbol (dump-symbol sexp))
584                          (string (dump-string sexp))
585                          (cons
586                           ;; BOOTSTRAP MAGIC: See the root file
587                           ;; jscl.lisp and the function
588                           ;; `dump-global-environment' for futher
589                           ;; information.
590                           (if (eq (car sexp) *magic-unquote-marker*)
591                               (ls-compile (second sexp))
592                               (dump-cons sexp)))
593                          (array (dump-array sexp)))))
594            (if (and recursive (not (symbolp sexp)))
595                dumped
596                (let ((jsvar (genlit)))
597                  (push (cons sexp jsvar) *literal-table*)
598                  (toplevel-compilation (code "var " jsvar " = " dumped))
599                  (when (keywordp sexp)
600                    (toplevel-compilation (code jsvar ".value = " jsvar)))
601                  jsvar)))))))
602
603
604 (define-compilation quote (sexp)
605   (literal sexp))
606
607 (define-compilation %while (pred &rest body)
608   (js!selfcall
609     "while(" (ls-compile pred) " !== " (ls-compile nil) "){" *newline*
610     (indent (ls-compile-block body))
611     "}"
612     "return " (ls-compile nil) ";" *newline*))
613
614 (define-compilation function (x)
615   (cond
616     ((and (listp x) (eq (car x) 'lambda))
617      (compile-lambda (cadr x) (cddr x)))
618     ((and (listp x) (eq (car x) 'named-lambda))
619      ;; TODO: destructuring-bind now! Do error checking manually is
620      ;; very annoying.
621      (let ((name (cadr x))
622            (ll (caddr x))
623            (body (cdddr x)))
624        (compile-lambda ll body
625                        :name (symbol-name name)
626                        :block name)))
627     ((symbolp x)
628      (let ((b (lookup-in-lexenv x *environment* 'function)))
629        (if b
630            (binding-value b)
631            (ls-compile `(symbol-function ',x)))))))
632
633
634 (defun make-function-binding (fname)
635   (make-binding :name fname :type 'function :value (gvarname fname)))
636
637 (defun compile-function-definition (list)
638   (compile-lambda (car list) (cdr list)))
639
640 (defun translate-function (name)
641   (let ((b (lookup-in-lexenv name *environment* 'function)))
642     (and b (binding-value b))))
643
644 (define-compilation flet (definitions &rest body)
645   (let* ((fnames (mapcar #'car definitions))
646          (cfuncs (mapcar (lambda (def)
647                            (compile-lambda (cadr def)
648                                            `((block ,(car def)
649                                                ,@(cddr def)))))
650                          definitions))
651          (*environment*
652           (extend-lexenv (mapcar #'make-function-binding fnames)
653                          *environment*
654                          'function)))
655     (code "(function("
656           (join (mapcar #'translate-function fnames) ",")
657           "){" *newline*
658           (let ((body (ls-compile-block body t)))
659             (indent body))
660           "})(" (join cfuncs ",") ")")))
661
662 (define-compilation labels (definitions &rest body)
663   (let* ((fnames (mapcar #'car definitions))
664          (*environment*
665           (extend-lexenv (mapcar #'make-function-binding fnames)
666                          *environment*
667                          'function)))
668     (js!selfcall
669       (mapconcat (lambda (func)
670                    (code "var " (translate-function (car func))
671                          " = " (compile-lambda (cadr func)
672                                                `((block ,(car func) ,@(cddr func))))
673                          ";" *newline*))
674                  definitions)
675       (ls-compile-block body t))))
676
677
678 (defvar *compiling-file* nil)
679 (define-compilation eval-when-compile (&rest body)
680   (if *compiling-file*
681       (progn
682         (eval (cons 'progn body))
683         nil)
684       (ls-compile `(progn ,@body))))
685
686 (defmacro define-transformation (name args form)
687   `(define-compilation ,name ,args
688      (ls-compile ,form)))
689
690 (define-compilation progn (&rest body)
691   (if (null (cdr body))
692       (ls-compile (car body) *multiple-value-p*)
693       (code "("
694             (join
695              (remove-if #'null-or-empty-p
696                         (append
697                          (mapcar #'ls-compile (butlast body))
698                          (list (ls-compile (car (last body)) t))))
699                   ",")
700             ")")))
701
702 (defun special-variable-p (x)
703   (and (claimp x 'variable 'special) t))
704
705 ;;; Wrap CODE to restore the symbol values of the dynamic
706 ;;; bindings. BINDINGS is a list of pairs of the form
707 ;;; (SYMBOL . PLACE),  where PLACE is a Javascript variable
708 ;;; name to initialize the symbol value and where to stored
709 ;;; the old value.
710 (defun let-binding-wrapper (bindings body)
711   (when (null bindings)
712     (return-from let-binding-wrapper body))
713   (code
714    "try {" *newline*
715    (indent "var tmp;" *newline*
716            (mapconcat
717             (lambda (b)
718               (let ((s (ls-compile `(quote ,(car b)))))
719                 (code "tmp = " s ".value;" *newline*
720                       s ".value = " (cdr b) ";" *newline*
721                       (cdr b) " = tmp;" *newline*)))
722             bindings)
723            body *newline*)
724    "}" *newline*
725    "finally {"  *newline*
726    (indent
727     (mapconcat (lambda (b)
728                  (let ((s (ls-compile `(quote ,(car b)))))
729                    (code s ".value" " = " (cdr b) ";" *newline*)))
730                bindings))
731    "}" *newline*))
732
733 (define-compilation let (bindings &rest body)
734   (let* ((bindings (mapcar #'ensure-list bindings))
735          (variables (mapcar #'first bindings))
736          (cvalues (mapcar #'ls-compile (mapcar #'second bindings)))
737          (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
738          (dynamic-bindings))
739     (code "(function("
740           (join (mapcar (lambda (x)
741                           (if (special-variable-p x)
742                               (let ((v (gvarname x)))
743                                 (push (cons x v) dynamic-bindings)
744                                 v)
745                               (translate-variable x)))
746                         variables)
747                 ",")
748           "){" *newline*
749           (let ((body (ls-compile-block body t t)))
750             (indent (let-binding-wrapper dynamic-bindings body)))
751           "})(" (join cvalues ",") ")")))
752
753
754 ;;; Return the code to initialize BINDING, and push it extending the
755 ;;; current lexical environment if the variable is not special.
756 (defun let*-initialize-value (binding)
757   (let ((var (first binding))
758         (value (second binding)))
759     (if (special-variable-p var)
760         (code (ls-compile `(setq ,var ,value)) ";" *newline*)
761         (let* ((v (gvarname var))
762                (b (make-binding :name var :type 'variable :value v)))
763           (prog1 (code "var " v " = " (ls-compile value) ";" *newline*)
764             (push-to-lexenv b *environment* 'variable))))))
765
766 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
767 ;;; DOES NOT generate code to initialize the value of the symbols,
768 ;;; unlike let-binding-wrapper.
769 (defun let*-binding-wrapper (symbols body)
770   (when (null symbols)
771     (return-from let*-binding-wrapper body))
772   (let ((store (mapcar (lambda (s) (cons s (gvarname s)))
773                        (remove-if-not #'special-variable-p symbols))))
774     (code
775      "try {" *newline*
776      (indent
777       (mapconcat (lambda (b)
778                    (let ((s (ls-compile `(quote ,(car b)))))
779                      (code "var " (cdr b) " = " s ".value;" *newline*)))
780                  store)
781       body)
782      "}" *newline*
783      "finally {" *newline*
784      (indent
785       (mapconcat (lambda (b)
786                    (let ((s (ls-compile `(quote ,(car b)))))
787                      (code s ".value" " = " (cdr b) ";" *newline*)))
788                  store))
789      "}" *newline*)))
790
791 (define-compilation let* (bindings &rest body)
792   (let ((bindings (mapcar #'ensure-list bindings))
793         (*environment* (copy-lexenv *environment*)))
794     (js!selfcall
795       (let ((specials (remove-if-not #'special-variable-p (mapcar #'first bindings)))
796             (body (concat (mapconcat #'let*-initialize-value bindings)
797                           (ls-compile-block body t t))))
798         (let*-binding-wrapper specials body)))))
799
800
801 (define-compilation block (name &rest body)
802   ;; We use Javascript exceptions to implement non local control
803   ;; transfer. Exceptions has dynamic scoping, so we use a uniquely
804   ;; generated object to identify the block. The instance of a empty
805   ;; array is used to distinguish between nested dynamic Javascript
806   ;; exceptions. See https://github.com/davazp/jscl/issues/64 for
807   ;; futher details.
808   (let* ((idvar (gvarname name))
809          (b (make-binding :name name :type 'block :value idvar)))
810     (when *multiple-value-p*
811       (push 'multiple-value (binding-declarations b)))
812     (let* ((*environment* (extend-lexenv (list b) *environment* 'block))
813            (cbody (ls-compile-block body t)))
814       (if (member 'used (binding-declarations b))
815           (js!selfcall
816             "try {" *newline*
817             "var " idvar " = [];" *newline*
818             (indent cbody)
819             "}" *newline*
820             "catch (cf){" *newline*
821             "    if (cf.type == 'block' && cf.id == " idvar ")" *newline*
822             (if *multiple-value-p*
823                 "        return values.apply(this, forcemv(cf.values));"
824                 "        return cf.values;")
825             *newline*
826             "    else" *newline*
827             "        throw cf;" *newline*
828             "}" *newline*)
829           (js!selfcall cbody)))))
830
831 (define-compilation return-from (name &optional value)
832   (let* ((b (lookup-in-lexenv name *environment* 'block))
833          (multiple-value-p (member 'multiple-value (binding-declarations b))))
834     (when (null b)
835       (error "Return from unknown block `~S'." (symbol-name name)))
836     (push 'used (binding-declarations b))
837     ;; The binding value is the name of a variable, whose value is the
838     ;; unique identifier of the block as exception. We can't use the
839     ;; variable name itself, because it could not to be unique, so we
840     ;; capture it in a closure.
841     (js!selfcall
842       (when multiple-value-p (code "var values = mv;" *newline*))
843       "throw ({"
844       "type: 'block', "
845       "id: " (binding-value b) ", "
846       "values: " (ls-compile value multiple-value-p) ", "
847       "message: 'Return from unknown block " (symbol-name name) ".'"
848       "})")))
849
850 (define-compilation catch (id &rest body)
851   (js!selfcall
852     "var id = " (ls-compile id) ";" *newline*
853     "try {" *newline*
854     (indent (ls-compile-block body t)) *newline*
855     "}" *newline*
856     "catch (cf){" *newline*
857     "    if (cf.type == 'catch' && cf.id == id)" *newline*
858     (if *multiple-value-p*
859         "        return values.apply(this, forcemv(cf.values));"
860         "        return pv.apply(this, forcemv(cf.values));")
861     *newline*
862     "    else" *newline*
863     "        throw cf;" *newline*
864     "}" *newline*))
865
866 (define-compilation throw (id value)
867   (js!selfcall
868     "var values = mv;" *newline*
869     "throw ({"
870     "type: 'catch', "
871     "id: " (ls-compile id) ", "
872     "values: " (ls-compile value t) ", "
873     "message: 'Throw uncatched.'"
874     "})"))
875
876 (defun go-tag-p (x)
877   (or (integerp x) (symbolp x)))
878
879 (defun declare-tagbody-tags (tbidx body)
880   (let* ((go-tag-counter 0)
881          (bindings
882           (mapcar (lambda (label)
883                     (let ((tagidx (integer-to-string (incf go-tag-counter))))
884                       (make-binding :name label :type 'gotag :value (list tbidx tagidx))))
885                   (remove-if-not #'go-tag-p body))))
886     (extend-lexenv bindings *environment* 'gotag)))
887
888 (define-compilation tagbody (&rest body)
889   ;; Ignore the tagbody if it does not contain any go-tag. We do this
890   ;; because 1) it is easy and 2) many built-in forms expand to a
891   ;; implicit tagbody, so we save some space.
892   (unless (some #'go-tag-p body)
893     (return-from tagbody (ls-compile `(progn ,@body nil))))
894   ;; The translation assumes the first form in BODY is a label
895   (unless (go-tag-p (car body))
896     (push (gensym "START") body))
897   ;; Tagbody compilation
898   (let ((branch (gvarname 'branch))
899         (tbidx (gvarname 'tbidx)))
900     (let ((*environment* (declare-tagbody-tags tbidx body))
901           initag)
902       (let ((b (lookup-in-lexenv (first body) *environment* 'gotag)))
903         (setq initag (second (binding-value b))))
904       (js!selfcall
905         ;; TAGBODY branch to take
906         "var " branch " = " initag ";" *newline*
907         "var " tbidx " = [];" *newline*
908         "tbloop:" *newline*
909         "while (true) {" *newline*
910         (indent "try {" *newline*
911                 (indent (let ((content ""))
912                           (code "switch(" branch "){" *newline*
913                                 "case " initag ":" *newline*
914                                 (dolist (form (cdr body) content)
915                                   (concatf content
916                                     (if (not (go-tag-p form))
917                                         (indent (ls-compile form) ";" *newline*)
918                                         (let ((b (lookup-in-lexenv form *environment* 'gotag)))
919                                           (code "case " (second (binding-value b)) ":" *newline*)))))
920                                 "default:" *newline*
921                                 "    break tbloop;" *newline*
922                                 "}" *newline*)))
923                 "}" *newline*
924                 "catch (jump) {" *newline*
925                 "    if (jump.type == 'tagbody' && jump.id == " tbidx ")" *newline*
926                 "        " branch " = jump.label;" *newline*
927                 "    else" *newline*
928                 "        throw(jump);" *newline*
929                 "}" *newline*)
930         "}" *newline*
931         "return " (ls-compile nil) ";" *newline*))))
932
933 (define-compilation go (label)
934   (let ((b (lookup-in-lexenv label *environment* 'gotag))
935         (n (cond
936              ((symbolp label) (symbol-name label))
937              ((integerp label) (integer-to-string label)))))
938     (when (null b)
939       (error "Unknown tag `~S'" label))
940     (js!selfcall
941       "throw ({"
942       "type: 'tagbody', "
943       "id: " (first (binding-value b)) ", "
944       "label: " (second (binding-value b)) ", "
945       "message: 'Attempt to GO to non-existing tag " n "'"
946       "})" *newline*)))
947
948 (define-compilation unwind-protect (form &rest clean-up)
949   (js!selfcall
950     "var ret = " (ls-compile nil) ";" *newline*
951     "try {" *newline*
952     (indent "ret = " (ls-compile form) ";" *newline*)
953     "} finally {" *newline*
954     (indent (ls-compile-block clean-up))
955     "}" *newline*
956     "return ret;" *newline*))
957
958 (define-compilation multiple-value-call (func-form &rest forms)
959   (js!selfcall
960     "var func = " (ls-compile func-form) ";" *newline*
961     "var args = [" (if *multiple-value-p* "values" "pv") ", 0];" *newline*
962     "return "
963     (js!selfcall
964       "var values = mv;" *newline*
965       "var vs;" *newline*
966       (mapconcat (lambda (form)
967                    (code "vs = " (ls-compile form t) ";" *newline*
968                          "if (typeof vs === 'object' && 'multiple-value' in vs)" *newline*
969                          (indent "args = args.concat(vs);" *newline*)
970                          "else" *newline*
971                          (indent "args.push(vs);" *newline*)))
972                  forms)
973       "args[1] = args.length-2;" *newline*
974       "return func.apply(window, args);" *newline*) ";" *newline*))
975
976 (define-compilation multiple-value-prog1 (first-form &rest forms)
977   (js!selfcall
978     "var args = " (ls-compile first-form *multiple-value-p*) ";" *newline*
979     (ls-compile-block forms)
980     "return args;" *newline*))
981
982 (define-transformation backquote (form)
983   (bq-completely-process form))
984
985
986 ;;; Primitives
987
988 (defvar *builtins* nil)
989
990 (defmacro define-raw-builtin (name args &body body)
991   ;; Creates a new primitive function `name' with parameters args and
992   ;; @body. The body can access to the local environment through the
993   ;; variable *ENVIRONMENT*.
994   `(push (list ',name (lambda ,args (block ,name ,@body)))
995          *builtins*))
996
997 (defmacro define-builtin (name args &body body)
998   `(define-raw-builtin ,name ,args
999      (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg))) args)
1000        ,@body)))
1001
1002 ;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations.
1003 (defmacro type-check (decls &body body)
1004   `(js!selfcall
1005      ,@(mapcar (lambda (decl)
1006                  `(code "var " ,(first decl) " = " ,(third decl) ";" *newline*))
1007                decls)
1008      ,@(mapcar (lambda (decl)
1009                  `(code "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline*
1010                         (indent "throw 'The value ' + "
1011                                 ,(first decl)
1012                                 " + ' is not a type "
1013                                 ,(second decl)
1014                                 ".';"
1015                                 *newline*)))
1016                decls)
1017      (code "return " (progn ,@body) ";" *newline*)))
1018
1019 ;;; VARIABLE-ARITY compiles variable arity operations. ARGS stands for
1020 ;;; a variable which holds a list of forms. It will compile them and
1021 ;;; store the result in some Javascript variables. BODY is evaluated
1022 ;;; with ARGS bound to the list of these variables to generate the
1023 ;;; code which performs the transformation on these variables.
1024
1025 (defun variable-arity-call (args function)
1026   (unless (consp args)
1027     (error "ARGS must be a non-empty list"))
1028   (let ((counter 0)
1029         (fargs '())
1030         (prelude ""))
1031     (dolist (x args)
1032       (cond
1033         ((floatp x) (push (float-to-string x) fargs))
1034         ((numberp x) (push (integer-to-string x) fargs))
1035         (t (let ((v (code "x" (incf counter))))
1036              (push v fargs)
1037              (concatf prelude
1038                (code "var " v " = " (ls-compile x) ";" *newline*
1039                      "if (typeof " v " !== 'number') throw 'Not a number!';"
1040                      *newline*))))))
1041     (js!selfcall prelude (funcall function (reverse fargs)))))
1042
1043
1044 (defmacro variable-arity (args &body body)
1045   (unless (symbolp args)
1046     (error "`~S' is not a symbol." args))
1047   `(variable-arity-call ,args
1048                         (lambda (,args)
1049                           (code "return " ,@body ";" *newline*))))
1050
1051 (defun num-op-num (x op y)
1052   (type-check (("x" "number" x) ("y" "number" y))
1053     (code "x" op "y")))
1054
1055 (define-raw-builtin + (&rest numbers)
1056   (if (null numbers)
1057       "0"
1058       (variable-arity numbers
1059         (join numbers "+"))))
1060
1061 (define-raw-builtin - (x &rest others)
1062   (let ((args (cons x others)))
1063     (variable-arity args
1064       (if (null others)
1065           (concat "-" (car args))
1066           (join args "-")))))
1067
1068 (define-raw-builtin * (&rest numbers)
1069   (if (null numbers)
1070       "1"
1071       (variable-arity numbers
1072         (join numbers "*"))))
1073
1074 (define-raw-builtin / (x &rest others)
1075   (let ((args (cons x others)))
1076     (variable-arity args
1077       (if (null others)
1078           (concat "1 /" (car args))
1079           (join args "/")))))
1080
1081 (define-builtin mod (x y) (num-op-num x "%" y))
1082
1083
1084 (defun comparison-conjuntion (vars op)
1085   (cond
1086     ((null (cdr vars))
1087      "true")
1088     ((null (cddr vars))
1089      (concat (car vars) op (cadr vars)))
1090     (t
1091      (concat (car vars) op (cadr vars)
1092              " && "
1093              (comparison-conjuntion (cdr vars) op)))))
1094
1095 (defmacro define-builtin-comparison (op sym)
1096   `(define-raw-builtin ,op (x &rest args)
1097      (let ((args (cons x args)))
1098        (variable-arity args
1099          (js!bool (comparison-conjuntion args ,sym))))))
1100
1101 (define-builtin-comparison > ">")
1102 (define-builtin-comparison < "<")
1103 (define-builtin-comparison >= ">=")
1104 (define-builtin-comparison <= "<=")
1105 (define-builtin-comparison = "==")
1106 (define-builtin-comparison /= "!=")
1107
1108 (define-builtin numberp (x)
1109   (js!bool (code "(typeof (" x ") == \"number\")")))
1110
1111 (define-builtin floor (x)
1112   (type-check (("x" "number" x))
1113     "Math.floor(x)"))
1114
1115 (define-builtin expt (x y)
1116   (type-check (("x" "number" x)
1117                ("y" "number" y))
1118     "Math.pow(x, y)"))
1119
1120 (define-builtin float-to-string (x)
1121   (type-check (("x" "number" x))
1122     "make_lisp_string(x.toString())"))
1123
1124 (define-builtin cons (x y)
1125   (code "({car: " x ", cdr: " y "})"))
1126
1127 (define-builtin consp (x)
1128   (js!bool
1129    (js!selfcall
1130      "var tmp = " x ";" *newline*
1131      "return (typeof tmp == 'object' && 'car' in tmp);" *newline*)))
1132
1133 (define-builtin car (x)
1134   (js!selfcall
1135     "var tmp = " x ";" *newline*
1136     "return tmp === " (ls-compile nil)
1137     "? " (ls-compile nil)
1138     ": tmp.car;" *newline*))
1139
1140 (define-builtin cdr (x)
1141   (js!selfcall
1142     "var tmp = " x ";" *newline*
1143     "return tmp === " (ls-compile nil) "? "
1144     (ls-compile nil)
1145     ": tmp.cdr;" *newline*))
1146
1147 (define-builtin rplaca (x new)
1148   (type-check (("x" "object" x))
1149     (code "(x.car = " new ", x)")))
1150
1151 (define-builtin rplacd (x new)
1152   (type-check (("x" "object" x))
1153     (code "(x.cdr = " new ", x)")))
1154
1155 (define-builtin symbolp (x)
1156   (js!bool (code "(" x " instanceof Symbol)")))
1157
1158 (define-builtin make-symbol (name)
1159   (code "(new Symbol(" name "))"))
1160
1161 (define-builtin symbol-name (x)
1162   (code "(" x ").name"))
1163
1164 (define-builtin set (symbol value)
1165   (code "(" symbol ").value = " value))
1166
1167 (define-builtin fset (symbol value)
1168   (code "(" symbol ").fvalue = " value))
1169
1170 (define-builtin boundp (x)
1171   (js!bool (code "(" x ".value !== undefined)")))
1172
1173 (define-builtin fboundp (x)
1174   (js!bool (code "(" x ".fvalue !== undefined)")))
1175
1176 (define-builtin symbol-value (x)
1177   (js!selfcall
1178     "var symbol = " x ";" *newline*
1179     "var value = symbol.value;" *newline*
1180     "if (value === undefined) throw \"Variable `\" + xstring(symbol.name) + \"' is unbound.\";" *newline*
1181     "return value;" *newline*))
1182
1183 (define-builtin symbol-function (x)
1184   (js!selfcall
1185     "var symbol = " x ";" *newline*
1186     "var func = symbol.fvalue;" *newline*
1187     "if (func === undefined) throw \"Function `\" + xstring(symbol.name) + \"' is undefined.\";" *newline*
1188     "return func;" *newline*))
1189
1190 (define-builtin symbol-plist (x)
1191   (code "((" x ").plist || " (ls-compile nil) ")"))
1192
1193 (define-builtin lambda-code (x)
1194   (code "make_lisp_string((" x ").toString())"))
1195
1196 (define-builtin eq (x y)
1197   (js!bool (code "(" x " === " y ")")))
1198
1199 (define-builtin char-code (x)
1200   (type-check (("x" "string" x))
1201     "char_to_codepoint(x)"))
1202
1203 (define-builtin code-char (x)
1204   (type-check (("x" "number" x))
1205     "char_from_codepoint(x)"))
1206
1207 (define-builtin characterp (x)
1208   (js!bool
1209    (js!selfcall
1210      "var x = " x ";" *newline*
1211      "return (typeof(" x ") == \"string\") && (x.length == 1 || x.length == 2);")))
1212
1213 (define-builtin char-to-string (x)
1214   (js!selfcall
1215     "var r = [" x "];" *newline*
1216     "r.type = 'character';"
1217     "return r"))
1218
1219 (define-builtin char-upcase (x)
1220   (code "safe_char_upcase(" x ")"))
1221
1222 (define-builtin char-downcase (x)
1223   (code "safe_char_downcase(" x ")"))
1224
1225 (define-builtin stringp (x)
1226   (js!bool
1227    (js!selfcall
1228      "var x = " x ";" *newline*
1229      "return typeof(x) == 'object' && 'length' in x && x.type == 'character';")))
1230
1231 (define-builtin string-upcase (x)
1232   (code "make_lisp_string(xstring(" x ").toUpperCase())"))
1233
1234 (define-builtin string-length (x)
1235   (code x ".length"))
1236
1237 (define-raw-builtin slice (vector a &optional b)
1238   (js!selfcall
1239     "var vector = " (ls-compile vector) ";" *newline*
1240     "var a = " (ls-compile a) ";" *newline*
1241     "var b;" *newline*
1242     (when b (code "b = " (ls-compile b) ";" *newline*))
1243     "return vector.slice(a,b);" *newline*))
1244
1245 (define-builtin char (string index)
1246   (code string "[" index "]"))
1247
1248 (define-builtin concat-two (string1 string2)
1249   (js!selfcall
1250     "var r = " string1 ".concat(" string2 ");" *newline*
1251     "r.type = 'character';"
1252     "return r;" *newline*))
1253
1254 (define-raw-builtin funcall (func &rest args)
1255   (js!selfcall
1256     "var f = " (ls-compile func) ";" *newline*
1257     "return (typeof f === 'function'? f: f.fvalue)("
1258     (join (list* (if *multiple-value-p* "values" "pv")
1259                  (integer-to-string (length args))
1260                  (mapcar #'ls-compile args))
1261           ", ")
1262     ")"))
1263
1264 (define-raw-builtin apply (func &rest args)
1265   (if (null args)
1266       (code "(" (ls-compile func) ")()")
1267       (let ((args (butlast args))
1268             (last (car (last args))))
1269         (js!selfcall
1270           "var f = " (ls-compile func) ";" *newline*
1271           "var args = [" (join (list* (if *multiple-value-p* "values" "pv")
1272                                       (integer-to-string (length args))
1273                                       (mapcar #'ls-compile args))
1274                                ", ")
1275           "];" *newline*
1276           "var tail = (" (ls-compile last) ");" *newline*
1277           "while (tail != " (ls-compile nil) "){" *newline*
1278           "    args.push(tail.car);" *newline*
1279           "    args[1] += 1;" *newline*
1280           "    tail = tail.cdr;" *newline*
1281           "}" *newline*
1282           "return (typeof f === 'function'? f : f.fvalue).apply(this, args);" *newline*))))
1283
1284 (define-builtin js-eval (string)
1285   (if *multiple-value-p*
1286       (js!selfcall
1287         "var v = globalEval(xstring(" string "));" *newline*
1288         "return values.apply(this, forcemv(v));" *newline*)
1289       (code "globalEval(xstring(" string "))")))
1290
1291 (define-builtin %throw (string)
1292   (js!selfcall "throw " string ";" *newline*))
1293
1294 (define-builtin new () "{}")
1295
1296 (define-builtin objectp (x)
1297   (js!bool (code "(typeof (" x ") === 'object')")))
1298
1299 (define-builtin oget (object key)
1300   (js!selfcall
1301     "var tmp = " "(" object ")[xstring(" key ")];" *newline*
1302     "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*))
1303
1304 (define-builtin oset (object key value)
1305   (code "((" object ")[xstring(" key ")] = " value ")"))
1306
1307 (define-builtin in (key object)
1308   (js!bool (code "(xstring(" key ") in (" object "))")))
1309
1310 (define-builtin map-for-in (function object)
1311   (js!selfcall
1312    "var f = " function ";" *newline*
1313    "var g = (typeof f === 'function' ? f : f.fvalue);" *newline*
1314    "var o = " object ";" *newline*
1315    "for (var key in o){" *newline*
1316    (indent "g(" (if *multiple-value-p* "values" "pv") ", 1, o[key]);" *newline*)
1317    "}"
1318    " return " (ls-compile nil) ";" *newline*))
1319
1320 (define-builtin functionp (x)
1321   (js!bool (code "(typeof " x " == 'function')")))
1322
1323 (define-builtin write-string (x)
1324   (code "lisp.write(" x ")"))
1325
1326 (define-builtin make-array (n)
1327   (js!selfcall
1328     "var r = [];" *newline*
1329     "for (var i = 0; i < " n "; i++)" *newline*
1330     (indent "r.push(" (ls-compile nil) ");" *newline*)
1331     "return r;" *newline*))
1332
1333 ;;; FIXME: should take optional min-extension.
1334 ;;; FIXME: should use fill-pointer instead of the absolute end of array
1335 (define-builtin vector-push-extend (new vector)
1336   (js!selfcall
1337     "var v = " vector ";" *newline*
1338     "v.push(" new ");" *newline*
1339     "return v;"))
1340
1341 (define-builtin arrayp (x)
1342   (js!bool
1343    (js!selfcall
1344      "var x = " x ";" *newline*
1345      "return typeof x === 'object' && 'length' in x;")))
1346
1347 (define-builtin aref (array n)
1348   (js!selfcall
1349     "var x = " "(" array ")[" n "];" *newline*
1350     "if (x === undefined) throw 'Out of range';" *newline*
1351     "return x;" *newline*))
1352
1353 (define-builtin aset (array n value)
1354   (js!selfcall
1355     "var x = " array ";" *newline*
1356     "var i = " n ";" *newline*
1357     "if (i < 0 || i >= x.length) throw 'Out of range';" *newline*
1358     "return x[i] = " value ";" *newline*))
1359
1360 (define-builtin afind (value array)
1361   (js!selfcall
1362     "var v = " value ";" *newline*
1363     "var x = " array ";" *newline*
1364     "return x.indexOf(v);" *newline*))
1365
1366 (define-builtin aresize (array new-size)
1367   (js!selfcall
1368     "var x = " array ";" *newline*
1369     "var n = " new-size ";" *newline*
1370     "return x.length = n;" *newline*))
1371
1372 (define-builtin get-internal-real-time ()
1373   "(new Date()).getTime()")
1374
1375 (define-builtin values-array (array)
1376   (if *multiple-value-p*
1377       (code "values.apply(this, " array ")")
1378       (code "pv.apply(this, " array ")")))
1379
1380 (define-raw-builtin values (&rest args)
1381   (if *multiple-value-p*
1382       (code "values(" (join (mapcar #'ls-compile args) ", ") ")")
1383       (code "pv(" (join (mapcar #'ls-compile args) ", ") ")")))
1384
1385
1386 ;;; Javascript FFI
1387
1388 (define-compilation %js-vref (var)
1389   (code "js_to_lisp(" var ")"))
1390
1391 (define-compilation %js-vset (var val)
1392   (code "(" var " = lisp_to_js(" (ls-compile val) "))"))
1393
1394 (define-setf-expander %js-vref (var)
1395   (let ((new-value (gensym)))
1396     (unless (stringp var)
1397       (error "`~S' is not a string." var))
1398     (values nil
1399             (list var)
1400             (list new-value)
1401             `(%js-vset ,var ,new-value)
1402             `(%js-vref ,var))))
1403
1404
1405 #-jscl
1406 (defvar *macroexpander-cache*
1407   (make-hash-table :test #'eq))
1408
1409 (defun !macro-function (symbol)
1410   (unless (symbolp symbol)
1411     (error "`~S' is not a symbol." symbol))
1412   (let ((b (lookup-in-lexenv symbol *environment* 'function)))
1413     (if (and b (eq (binding-type b) 'macro))
1414         (let ((expander (binding-value b)))
1415           (cond
1416             #-jscl
1417             ((gethash b *macroexpander-cache*)
1418              (setq expander (gethash b *macroexpander-cache*)))
1419             ((listp expander)
1420              (let ((compiled (eval expander)))
1421                ;; The list representation are useful while
1422                ;; bootstrapping, as we can dump the definition of the
1423                ;; macros easily, but they are slow because we have to
1424                ;; evaluate them and compile them now and again. So, let
1425                ;; us replace the list representation version of the
1426                ;; function with the compiled one.
1427                ;;
1428                #+jscl (setf (binding-value b) compiled)
1429                #-jscl (setf (gethash b *macroexpander-cache*) compiled)
1430                (setq expander compiled))))
1431           expander)
1432         nil)))
1433
1434 (defun !macroexpand-1 (form)
1435   (cond
1436     ((symbolp form)
1437      (let ((b (lookup-in-lexenv form *environment* 'variable)))
1438        (if (and b (eq (binding-type b) 'macro))
1439            (values (binding-value b) t)
1440            (values form nil))))
1441     ((and (consp form) (symbolp (car form)))
1442      (let ((macrofun (!macro-function (car form))))
1443        (if macrofun
1444            (values (funcall macrofun (cdr form)) t)
1445            (values form nil))))
1446     (t
1447      (values form nil))))
1448
1449 (defun compile-funcall (function args)
1450   (let* ((values-funcs (if *multiple-value-p* "values" "pv"))
1451          (arglist (concat "(" (join (list* values-funcs
1452                                            (integer-to-string (length args))
1453                                            (mapcar #'ls-compile args)) ", ") ")")))
1454     (unless (or (symbolp function)
1455                 (and (consp function)
1456                      (eq (car function) 'lambda)))
1457       (error "Bad function designator `~S'" function))
1458     (cond
1459       ((translate-function function)
1460        (concat (translate-function function) arglist))
1461       ((and (symbolp function)
1462             #+jscl (eq (symbol-package function) (find-package "COMMON-LISP"))
1463             #-jscl t)
1464        (code (ls-compile `',function) ".fvalue" arglist))
1465       (t
1466        (code (ls-compile `#',function) arglist)))))
1467
1468 (defun ls-compile-block (sexps &optional return-last-p decls-allowed-p)
1469   (multiple-value-bind (sexps decls)
1470       (parse-body sexps :declarations decls-allowed-p)
1471     (declare (ignore decls))
1472     (if return-last-p
1473         (code (ls-compile-block (butlast sexps) nil decls-allowed-p)
1474               "return " (ls-compile (car (last sexps)) *multiple-value-p*) ";")
1475         (join-trailing
1476          (remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
1477          (concat ";" *newline*)))))
1478
1479 (defun ls-compile (sexp &optional multiple-value-p)
1480   (multiple-value-bind (sexp expandedp) (!macroexpand-1 sexp)
1481     (when expandedp
1482       (return-from ls-compile (ls-compile sexp multiple-value-p)))
1483     ;; The expression has been macroexpanded. Now compile it!
1484     (let ((*multiple-value-p* multiple-value-p))
1485       (cond
1486         ((symbolp sexp)
1487          (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
1488            (cond
1489              ((and b (not (member 'special (binding-declarations b))))
1490               (binding-value b))
1491              ((or (keywordp sexp)
1492                   (and b (member 'constant (binding-declarations b))))
1493               (code (ls-compile `',sexp) ".value"))
1494              (t
1495               (ls-compile `(symbol-value ',sexp))))))
1496         ((or (integerp sexp) (floatp sexp) (characterp sexp) (stringp sexp) (arrayp sexp))
1497          (literal sexp))
1498         ((listp sexp)
1499          (let ((name (car sexp))
1500                (args (cdr sexp)))
1501            (cond
1502              ;; Special forms
1503              ((assoc name *compilations*)
1504               (let ((comp (second (assoc name *compilations*))))
1505                 (apply comp args)))
1506              ;; Built-in functions
1507              ((and (assoc name *builtins*)
1508                    (not (claimp name 'function 'notinline)))
1509               (let ((comp (second (assoc name *builtins*))))
1510                 (apply comp args)))
1511              (t
1512               (compile-funcall name args)))))
1513         (t
1514          (error "How should I compile `~S'?" sexp))))))
1515
1516
1517 (defvar *compile-print-toplevels* nil)
1518
1519 (defun truncate-string (string &optional (width 60))
1520   (let ((n (or (position #\newline string)
1521                (min width (length string)))))
1522     (subseq string 0 n)))
1523
1524 (defun ls-compile-toplevel (sexp &optional multiple-value-p)
1525   (let ((*toplevel-compilations* nil))
1526     (cond
1527       ((and (consp sexp) (eq (car sexp) 'progn))
1528        (let ((subs (mapcar (lambda (s)
1529                              (ls-compile-toplevel s t))
1530                            (cdr sexp))))
1531          (join (remove-if #'null-or-empty-p subs))))
1532       (t
1533        (when *compile-print-toplevels*
1534          (let ((form-string (prin1-to-string sexp)))
1535            (format t "Compiling ~a..." (truncate-string form-string))))
1536        (let ((code (ls-compile sexp multiple-value-p)))
1537          (code (join-trailing (get-toplevel-compilations)
1538                               (code ";" *newline*))
1539                (when code
1540                  (code code ";" *newline*))))))))