3 ;;; javascript name conversion
5 (defvar *special-chars
*
14 (defun string-chars (string)
15 (coerce string
'list
))
17 (defun constant-string-p (string)
18 (let ((len (length string
))
19 (constant-chars '(#\
+ #\
*)))
21 (member (char string
0) constant-chars
)
22 (member (char string
(1- len
)) constant-chars
))))
24 (defun first-uppercase-p (string)
25 (and (> (length string
) 1)
26 (member (char string
0) '(#\
+ #\
*))))
28 (defun symbol-to-js (symbol)
29 (when (symbolp symbol
)
30 (setf symbol
(symbol-name symbol
)))
34 (cond ((constant-string-p symbol
)
36 symbol
(subseq symbol
1 (1- (length symbol
)))))
37 ((first-uppercase-p symbol
)
39 symbol
(subseq symbol
1))))
41 (push (if (and lowercase
(not all-uppercase
))
45 (dotimes (i (length symbol
))
46 (let ((c (char symbol i
)))
49 (setf lowercase
(not lowercase
)))
50 ((assoc c
*special-chars
*)
51 (dolist (i (coerce (cdr (assoc c
*special-chars
*)) 'list
))
54 (coerce (nreverse res
) 'string
)))
102 ;;; js language types
104 (defclass statement
()
105 ((value :initarg
:value
:accessor value
)))
107 (defclass expression
(statement)
112 (defun special-append-to-last (form elt
)
113 (flet ((special-append (form elt
)
114 (let ((len (length form
)))
116 (member (char form
(1- len
))
119 (concatenate 'string form elt
)))))
120 (cond ((stringp form
)
121 (special-append form elt
))
123 (let ((last (last form
)))
124 (if (stringp (car last
))
125 (rplaca last
(special-append (car last
) elt
))
126 (append-to-last (car last
) elt
))
128 (t (error "unsupported form ~S" form
)))))
130 (defun dwim-join (value-string-lists max-length
132 join-before join-after
133 white-space
(separator " ")
134 (append-to-last #'append-to-last
)
137 (format t
"value-string-lists: ~S~%" value-string-lists
)
143 (setf join-before
""))
145 ;;; collect single value-string-lists until line full
147 (do* ((string-lists value-string-lists
(cdr string-lists
))
148 (string-list (car string-lists
) (car string-lists
))
151 (white-space (or white-space
(make-string (length start
) :initial-element
#\Space
)))
158 (funcall append-to-last
(first res
) end
)))
162 (format t
"string-list: ~S~%" string-list
)
165 (unless (null (cdr string-lists
))
166 (funcall append-to-last string-list join-after
)))
168 (if (and collect
(= (length string-list
) 1))
171 (format t
"cur-elt: ~S line-length ~D, max-length ~D, string: ~S~%"
173 (+ (length (first string-list
))
178 (< (+ (length (first string-list
))
179 (length cur-elt
)) max-length
))
181 (concatenate 'string cur-elt
182 (if cur-empty
"" (concatenate 'string separator join-before
))
187 (setf cur-elt
(concatenate 'string white-space
188 join-before
(first string-list
))
194 (setf cur-elt white-space
196 (setf res
(nconc (nreverse
197 (cons (concatenate 'string
198 cur-elt
(if (null res
)
201 (mapcar #'(lambda (x) (concatenate 'string white-space x
))
202 (cdr string-list
)))) res
))
203 (setf cur-elt white-space cur-empty t
)))))
205 (defmethod js-to-strings ((expression expression
) start-pos
)
206 (list (princ-to-string (value expression
))))
208 (defmethod js-to-statement-strings ((expression expression
) start-pos
)
209 (js-to-strings expression start-pos
))
211 (defmethod js-to-statement-strings ((statement statement
) start-pos
)
212 (list (princ-to-string (value statement
))))
216 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
217 (defvar *js-compiler-macros
* (make-hash-table)
218 "*JS-COMPILER-MACROS* is a hash-table containing the functions corresponding
219 to javascript special forms, indexed by their name. Javascript special
220 forms are compiler macros for JS expressions."))
222 (defmacro define-js-compiler-macro
(name lambda-list
&rest body
)
223 "Define a javascript compiler macro NAME. Arguments are destructured
224 according to LAMBDA-LIST. The resulting JS language types are appended
225 to the ongoing javascript compilation."
226 (let ((js-name (intern (concatenate 'string
"JS-" (symbol-name name
)))))
227 `(progn (defun ,js-name
,lambda-list
,@body
)
228 (setf (gethash ',name
*js-compiler-macros
*) #',js-name
))))
230 (defun js-compiler-macro-form-p (form)
231 (when (gethash (car form
) *js-compiler-macros
*)
234 (defun js-get-compiler-macro (name)
235 (gethash name
*js-compiler-macros
*))
239 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
240 (defvar *js-macro-toplevel
* (make-hash-table)
241 "Toplevel of macro expansion, holds all the toplevel javascript macros.")
242 (defvar *js-macro-env
* (list *js-macro-toplevel
*)
243 "Current macro environment."))
245 (defun lookup-macro (name)
246 "Lookup the macro NAME in the current macro expansion
247 environment. Returns the macro and the parent macro environment of
249 (do ((env *js-macro-env
* (cdr env
)))
251 (let ((val (gethash name
(car env
))))
253 (return-from lookup-macro
254 (values val
(or (cdr env
)
255 (list *js-macro-toplevel
*))))))))
257 (defmacro defjsmacro
(name args
&rest body
)
258 "Define a javascript macro, and store it in the toplevel macro environment."
259 (when (gethash name
*js-compiler-macros
*)
260 (warn "Redefining compiler macro ~S" name
)
261 (remhash name
*js-compiler-macros
*))
262 (let ((lambda-list (gensym)))
263 `(setf (gethash ',name
*js-macro-toplevel
*)
264 #'(lambda (&rest
,lambda-list
)
265 (destructuring-bind ,args
,lambda-list
,@body
)))))
267 (defun js-expand-form (expr)
268 "Expand a javascript form."
270 (multiple-value-bind (js-macro macro-env
)
273 (js-expand-form (let ((*js-macro-env
* macro-env
))
277 ((js-compiler-macro-form-p expr
) expr
)
279 ((equal (first expr
) 'quote
) expr
)
281 (t (let ((js-macro (lookup-macro (car expr
))))
283 (js-expand-form (apply js-macro
(cdr expr
)))
288 (defmacro defjsliteral
(name string
)
289 "Define a Javascript literal that will expand to STRING."
290 `(define-js-compiler-macro ,name
() (make-instance 'expression
:value
,string
)))
292 (defjsliteral this
"this")
293 (defjsliteral t
"true")
294 (defjsliteral nil
"null")
295 (defjsliteral false
"false")
296 (defjsliteral undefined
"undefined")
298 (defmacro defjskeyword
(name string
)
299 "Define a Javascript keyword that will expand to STRING."
300 `(define-js-compiler-macro ,name
() (make-instance 'statement
:value
,string
)))
302 (defjskeyword break
"break")
303 (defjskeyword continue
"continue")
307 (defclass array-literal
(expression)
308 ((values :initarg
:values
:accessor array-values
)))
310 (define-js-compiler-macro array
(&rest values
)
311 (make-instance 'array-literal
312 :values
(mapcar #'js-compile-to-expression values
)))
314 (defjsmacro list
(&rest values
)
317 (defmethod js-to-strings ((array array-literal
) start-pos
)
318 (let ((value-string-lists
319 (mapcar #'(lambda (x) (js-to-strings x
(+ start-pos
2)))
320 (array-values array
)))
321 (max-length (- 80 start-pos
2)))
322 (dwim-join value-string-lists max-length
323 :start
"[ " :end
" ]"
326 (defclass js-aref
(expression)
327 ((array :initarg
:array
328 :accessor aref-array
)
329 (index :initarg
:index
330 :accessor aref-index
)))
332 (define-js-compiler-macro aref
(array &rest coords
)
333 (make-instance 'js-aref
334 :array
(js-compile-to-expression array
)
335 :index
(mapcar #'js-compile-to-expression coords
)))
337 (defmethod js-to-strings ((aref js-aref
) start-pos
)
338 (dwim-join (cons (js-to-strings (aref-array aref
) start-pos
)
339 (mapcar #'(lambda (x) (dwim-join (list (js-to-strings x
(+ start-pos
2)))
341 :start
"[" :end
"]"))
343 (- 80 start-pos
2) :separator
""
348 (defclass string-literal
(expression)
351 (defmethod js-to-strings ((string string-literal
) start-pos
)
352 (declare (ignore start-pos
))
353 (list (prin1-to-string (value string
))))
357 (defclass number-literal
(expression)
362 (defclass js-variable
(expression)
365 (defmethod js-to-strings ((v js-variable
) start-form
)
366 (list (symbol-to-js (value v
))))
368 ;;; arithmetic operators
370 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
372 (defparameter *op-precedence-hash
* (make-hash-table))
374 (defparameter *op-precedences
*
394 ;;; generate the operator precedences from *OP-PRECEDENCES*
395 (let ((precedence 1))
396 (dolist (ops *op-precedences
*)
398 (setf (gethash op
*op-precedence-hash
*) precedence
))
401 (defun js-convert-op-name (op)
410 (defclass op-form
(expression)
411 ((operator :initarg
:operator
:accessor operator
)
412 (args :initarg
:args
:accessor op-args
)))
414 (defun op-form-p (form)
416 (not (js-compiler-macro-form-p form
))
417 (not (null (gethash (first form
) *op-precedence-hash
*)))))
419 (defun klammer (string-list)
420 (prepend-to-first string-list
"(")
421 (append-to-last string-list
")")
424 (defmethod expression-precedence ((expression expression
))
427 (defmethod expression-precedence ((form op-form
))
428 (gethash (operator form
) *op-precedence-hash
*))
430 (defmethod js-to-strings ((form op-form
) start-pos
)
431 (let* ((precedence (expression-precedence form
))
433 (mapcar #'(lambda (x)
434 (let ((string-list (js-to-strings x
(+ start-pos
2))))
435 (if (>= (expression-precedence x
) precedence
)
436 (klammer string-list
)
439 (max-length (- 80 start-pos
2))
440 (op-string (format nil
"~A " (operator form
))))
441 (dwim-join value-string-lists max-length
:join-before op-string
)))
443 (defjsmacro 1-
(form)
446 (defjsmacro 1+ (form)
449 (defclass one-op
(expression)
450 ((pre-p :initarg
:pre-p
452 :accessor one-op-pre-p
)
456 (defmethod js-to-strings ((one-op one-op
) start-pos
)
457 (let* ((value (value one-op
))
458 (value-strings (js-to-strings value start-pos
)))
459 (when (typep value
'op-form
)
460 (setf value-strings
(klammer value-strings
)))
461 (if (one-op-pre-p one-op
)
462 (prepend-to-first value-strings
464 (append-to-last value-strings
467 (define-js-compiler-macro incf
(x)
468 (make-instance 'one-op
:pre-p t
:op
"++"
469 :value
(js-compile-to-expression x
)))
470 (define-js-compiler-macro ++ (x)
471 (make-instance 'one-op
:pre-p nil
:op
"++"
472 :value
(js-compile-to-expression x
)))
473 (define-js-compiler-macro decf
(x)
474 (make-instance 'one-op
:pre-p t
:op
"--"
475 :value
(js-compile-to-expression x
)))
476 (define-js-compiler-macro --
(x)
477 (make-instance 'one-op
:pre-p nil
:op
"--"
478 :value
(js-compile-to-expression x
)))
481 (define-js-compiler-macro not
(x)
482 (let ((value (js-compile-to-expression x
)))
483 (if (typep value
'op-form
)
484 (let ((new-op (case (operator value
)
495 (make-instance 'op-form
:operator new-op
496 :args
(op-args value
))
497 (make-instance 'one-op
:pre-p t
:op
"!"
499 (make-instance 'one-op
:pre-p t
:op
"!"
504 (defclass function-call
(expression)
505 ((function :initarg
:function
:accessor f-function
)
506 (args :initarg
:args
:accessor f-args
)))
508 (defun funcall-form-p (form)
510 (not (op-form-p form
))
511 (not (js-compiler-macro-form-p form
))))
513 (defmethod js-to-strings ((form function-call
) start-pos
)
514 (let ((value-string-lists
515 (mapcar #'(lambda (x) (js-to-strings x
(+ start-pos
2)))
517 (max-length (- 80 start-pos
2)))
518 (dwim-join value-string-lists max-length
519 :start
(format nil
"~A(" (symbol-to-js (f-function form
)))
523 (defclass method-call
(expression)
524 ((method :initarg
:method
:accessor m-method
)
525 (args :initarg
:args
:accessor m-args
)))
527 (defun method-call-p (form)
528 (and (funcall-form-p form
)
529 (eql (char (symbol-name (first form
)) 0) #\.
)))
533 (defclass js-body
(expression)
534 ((stmts :initarg
:stmts
:accessor b-stmts
)
535 (indent :initarg
:indent
:initform
"" :accessor b-indent
)))
537 (define-js-compiler-macro progn
(&rest body
)
538 (make-instance 'js-body
539 :stmts
(mapcar #'js-compile-to-statement body
)))
541 (defmethod js-to-statement-strings ((body js-body
) start-pos
)
542 (dwim-join (mapcar #'(lambda (x) (js-to-statement-strings x
(+ start-pos
2)))
546 :append-to-last
#'special-append-to-last
547 :start
(b-indent body
) :collect nil
550 (defmethod js-to-strings ((body js-body
) start-pos
)
551 (dwim-join (mapcar #'(lambda (x) (js-to-strings x
(+ start-pos
2)))
554 :append-to-last
#'special-append-to-last
556 :start
(b-indent body
)))
558 (defclass js-sub-body
(js-body)
561 (defmethod js-to-statement-strings ((body js-sub-body
) start-pos
)
562 (nconc (list "{") (call-next-method) (list "}")))
564 (dwim-join (mapcar #'(lambda (x) (js-to-statement-strings x
(+ start-pos
2)))
567 :start
(format nil
"{~% ")
568 :end
(format nil
"~%}")
569 :white-space
" " :collect nil
)
571 (defmethod expression-precedence ((body js-body
))
572 (if (= (length (b-stmts body
)) 1)
573 (expression-precedence (first (b-stmts body
)))
574 (gethash 'comma
*op-precedence-hash
*)))
576 ;;; function definition
578 (defclass js-defun
(expression)
579 ((name :initarg
:name
:accessor d-name
)
580 (args :initarg
:args
:accessor d-args
)
581 (body :initarg
:body
:accessor d-body
)))
583 (define-js-compiler-macro defun
(name args
&rest body
)
584 (make-instance 'js-defun
585 :name
(js-compile-to-symbol name
)
586 :args
(mapcar #'js-compile-to-symbol args
)
587 :body
(make-instance 'js-body
589 :stmts
(mapcar #'js-compile-to-statement body
))))
591 (defmethod js-to-strings ((defun js-defun) start-pos
)
592 (let ((fun-header (dwim-join (mapcar #'(lambda (x) (list (symbol-to-js x
)))
595 :start
(format nil
"function ~A("
596 (symbol-to-js (d-name defun
)))
597 :end
") {" :join-after
","))
598 (fun-body (js-to-statement-strings (d-body defun
) (+ start-pos
2))))
599 (nconc fun-header fun-body
(list "}"))))
601 (defmethod js-to-statement-strings ((defun js-defun) start-pos
)
602 (js-to-strings defun
start-pos))
604 (defjsmacro lambda
(args &rest body
)
605 `(defun :||
,args
,@body
))
609 (defclass js-object
(expression)
610 ((slots :initarg
:slots
613 (define-js-compiler-macro create
(&rest args
)
614 (make-instance 'js-object
615 :slots
(loop for
(name val
) on args by
#'cddr
616 collect
(list (js-compile-to-symbol name
)
617 (js-compile-to-expression val
)))))
619 ;;; XXX so ist das noch nicht korrekt
620 (defmethod js-to-strings ((object js-object
) start-pos
)
621 (let ((value-string-lists
622 (mapcar #'(lambda (slot)
623 (dwim-join (list (js-to-strings (second slot
) (+ start-pos
4)))
625 :start
(concatenate 'string
(symbol-to-js (first slot
)) " : ")
626 :white-space
" ")) (o-slots object
)))
627 (max-length (- 80 start-pos
2)))
628 (dwim-join value-string-lists max-length
629 :start
(format nil
"{~% ")
630 :end
(format nil
"~%} ")
635 (defclass js-slot-value
(expression)
636 ((object :initarg
:object
641 (define-js-compiler-macro slot-value
(obj slot
)
642 (make-instance 'js-slot-value
:object
(js-compile-to-expression obj
)
643 :slot
(js-compile-to-symbol slot
)))
645 (defmethod js-to-strings ((sv js-slot-value
) start-pos
)
646 (append-to-last (js-to-strings (sv-object sv
) start-pos
)
647 (format nil
".~A" (symbol-to-js (sv-slot sv
)))))
649 (defjsmacro with-slots
(slots object
&rest body
)
650 `(symbol-macrolet ,(mapcar #'(lambda (slot)
651 `(,slot
'(slot-value ,object
',slot
)))
657 (define-js-compiler-macro macrolet
(macros &rest body
)
658 (let* ((macro-env (make-hash-table))
659 (*js-macro-env
* (cons macro-env
*js-macro-env
*)))
660 (dolist (macro macros
)
661 (destructuring-bind (name arglist
&rest body
) macro
662 (setf (gethash name macro-env
)
663 (compile nil
`(lambda ,arglist
,@body
)))))
664 (js-compile `(progn ,@body
))))
666 (defjsmacro symbol-macrolet
(macros &rest body
)
667 `(macrolet ,(mapcar #'(lambda (macro)
668 `(,(first macro
) () ,@(rest macro
))) macros
)
673 (defjsmacro lisp
(&rest forms
)
674 (eval (cons 'progn forms
)))
678 (defclass js-if
(expression)
679 ((test :initarg
:test
686 (define-js-compiler-macro if
(test then
&optional else
)
687 (make-instance 'js-if
:test
(js-compile-to-expression test
)
688 :then
(js-compile-to-body then
:indent
" ")
690 (js-compile-to-body else
:indent
" "))))
692 (defmethod js-to-statement-strings ((if js-if
) start-pos
)
693 (let ((if-strings (dwim-join (list (js-to-strings (if-test if
) 0))
697 (then-strings (js-to-statement-strings (if-then if
) (+ start-pos
2)))
698 (else-strings (when (if-else if
)
699 (js-to-statement-strings (if-else if
)
701 (nconc if-strings then-strings
(if else-strings
702 (nconc (list "} else {") else-strings
(list "}"))
705 (defmethod expression-precedence ((if js-if
))
706 (gethash 'if
*op-precedence-hash
*))
708 (defmethod js-to-strings ((if js-if
) start-pos
)
709 (assert (typep (if-then if
) 'expression
))
711 (assert (typep (if-else if
) 'expression
)))
712 (dwim-join (list (append-to-last (js-to-strings (if-test if
) start-pos
) " ?")
713 (let* ((new-then (make-instance 'js-body
714 :stmts
(b-stmts (if-then if
))
716 (res (js-to-strings new-then start-pos
)))
717 (if (>= (expression-precedence (if-then if
))
718 (expression-precedence if
))
723 (let* ((new-else (make-instance 'js-body
724 :stmts
(b-stmts (if-else if
))
726 (res (js-to-strings new-else start-pos
)))
727 (if (>= (expression-precedence (if-else if
))
728 (expression-precedence if
))
735 (defjsmacro when
(test &rest body
)
736 `(if ,test
(progn ,@body
)))
738 (defjsmacro unless
(test &rest body
)
739 `(if (not ,test
) (progn ,@body
)))
741 ;;; single keyword expressions and statements
743 (defmacro define-js-single-op
(name &optional
(superclass 'expression
))
744 (let ((js-name (intern (concatenate 'string
"JS-" (symbol-name name
)) #.
*package
*)))
746 (defclass ,js-name
(,superclass
)
748 (define-js-compiler-macro ,name
(value)
749 (make-instance ',js-name
:value
(js-compile-to-expression value
)))
750 (defmethod js-to-strings ((,name
,js-name
) start-pos
)
751 (dwim-join (list (js-to-strings (value ,name
) (+ start-pos
2)))
753 :start
,(concatenate 'string
(string-downcase (symbol-name name
)) " ")
754 :white-space
" ")))))
757 (define-js-single-op return statement
)
758 (define-js-single-op throw statement
)
759 (define-js-single-op delete
)
760 (define-js-single-op void
)
761 (define-js-single-op typeof
)
762 (define-js-single-op instanceof
)
763 (define-js-single-op new
)
767 (defclass js-setf
(expression)
768 ((lhs :initarg
:lhs
:accessor setf-lhs
)
769 (rhsides :initarg
:rhsides
:accessor setf-rhsides
)))
771 (define-js-compiler-macro setf
(&rest args
)
772 (let ((assignments (loop for
(lhs rhs
) on args by
#'cddr
773 for rexpr
= (js-compile-to-expression rhs
)
774 for lexpr
= (js-compile-to-expression lhs
)
775 collect
(make-instance 'js-setf
:lhs lexpr
776 :rhsides
(list rexpr
)))))
777 (if (= (length assignments
) 1)
779 (make-instance 'js-body
:indent
"" :stmts assignments
))))
781 (defmethod js-to-strings ((setf js-setf
) start-pos
)
782 (dwim-join (cons (js-to-strings (setf-lhs setf
) start-pos
)
783 (mapcar #'(lambda (x) (js-to-strings x start-pos
)) (setf-rhsides setf
)))
787 (defmethod expression-precedence ((setf js-setf
))
788 (gethash '= *op-precedence-hash
*))
792 (defclass js-defvar
(statement)
793 ((names :initarg
:names
:accessor var-names
)
794 (value :initarg
:value
:accessor var-value
)))
796 (define-js-compiler-macro defvar
(name &optional value
)
797 (make-instance 'js-defvar
:names
(list (js-compile-to-symbol name
))
798 :value
(when value
(js-compile-to-expression value
))))
800 (defmethod js-to-statement-strings ((defvar js-defvar
) start-pos
)
801 (dwim-join (nconc (mapcar #'(lambda (x) (list (symbol-to-js x
))) (var-names defvar
))
802 (when (var-value defvar
)
803 (list (js-to-strings (var-value defvar
) start-pos
))))
806 :start
"var " :end
";"))
810 (define-js-compiler-macro let
(decls &rest body
)
811 (let ((single-defvar (make-instance 'js-defvar
812 :names
(mapcar #'js-compile-to-symbol
813 (remove-if-not #'atom decls
))
815 (defvars (mapcar #'(lambda (decl)
816 (let ((name (first decl
))
817 (value (second decl
)))
818 (make-instance 'js-defvar
819 :names
(list (js-compile-to-symbol name
))
820 :value
(js-compile-to-expression value
))))
821 (remove-if #'atom decls
))))
822 (make-instance 'js-sub-body
824 :stmts
(nconc (when (var-names single-defvar
) (list single-defvar
))
826 (mapcar #'js-compile-to-statement body
)))))
830 (defclass js-for
(statement)
831 ((vars :initarg
:vars
:accessor for-vars
)
832 (steps :initarg
:steps
:accessor for-steps
)
833 (check :initarg
:check
:accessor for-check
)
834 (body :initarg
:body
:accessor for-body
)))
836 (defun make-for-vars (decls)
837 (loop for decl in decls
838 for var
= (if (atom decl
) decl
(first decl
))
839 for init
= (if (atom decl
) nil
(second decl
))
840 collect
(make-instance 'js-defvar
:names
(list (js-compile-to-symbol var
))
841 :value
(js-compile-to-expression init
))))
843 (defun make-for-steps (decls)
844 (loop for decl in decls
845 when
(= (length decl
) 3)
846 collect
(js-compile-to-expression (third decl
))))
848 (define-js-compiler-macro do
(decls termination
&rest body
)
849 (let ((vars (make-for-vars decls
))
850 (steps (make-for-steps decls
))
851 (check (js-compile-to-expression (list 'not
(first termination
))))
852 (body (js-compile-to-body (cons 'progn body
) :indent
" ")))
853 (make-instance 'js-for
859 (defun strings-length (string-list)
860 (reduce #'max
(mapcar #'length string-list
) :initial-value most-negative-fixnum
))
862 (defmethod js-to-statement-strings ((for js-for
) start-pos
)
863 (let* ((init (dwim-join (mapcar #'(lambda (x)
864 (dwim-join (list (list (symbol-to-js (first (var-names x
))))
865 (js-to-strings (var-value x
)
871 :start
"var " :join-after
","))
873 (init-len (strings-length init
))
874 (check (js-to-strings (for-check for
) (+ start-pos
2)))
876 (check-len (strings-length check
))
877 (steps (dwim-join (mapcar #'(lambda (x)
878 (js-to-strings x
(- start-pos
2)))
882 (header (dwim-join (list init check steps
)
884 :start
"for (" :end
") {"
886 (body (js-to-statement-strings (for-body for
) (+ start-pos
2))))
887 (nconc header body
(list "}"))))
889 (let ((fun-header (dwim-join (mapcar #'(lambda (x) (list (symbol-to-js x
)))
892 :start
(format nil
"function ~A("
893 (symbol-to-js (d-name defun
)))
894 :end
") {" :join-after
","))
895 (fun-body (js-to-statement-strings (d-body defun
) (+ start-pos
2))))
896 (nconc fun-header fun-body
(list "}"))))
898 (defclass for-each
(statement)
899 ((name :initarg
:name
:accessor fe-name
)
900 (value :initarg
:value
:accessor fe-value
)
901 (body :initarg
:value
:accessor fe-body
)))
903 (define-js-compiler-macro do-each
(decl &rest body
)
904 (make-instance 'for-each
:name
(js-compile-to-symbol (first decl
))
905 :value
(js-compile-to-expression (second decl
))
906 :body
(js-compile-to-body (cons 'progn body
) :indent
" ")))
908 (defmethod js-to-statement-strings ((fe for-each
) start-pos
)
909 (let ((header (dwim-join (list (list (symbol-to-js (fe-name fe
)) " in ")
910 (js-to-strings (fe-value fe
) (+ start-pos
2)))
913 (body (js-to-statement-strings (fe-body fe
) (+ start-pos
2))))
914 (nconc header body
(list "}"))))
916 (defclass js-while
(statement)
917 ((check :initarg
:check
:accessor while-check
)
918 (body :initarg
:body
:accessor while-body
)))
920 (define-js-compiler-macro while
(check &rest body
)
921 (make-instance 'js-while
922 :check
(js-compile-to-expression check
)
923 :body
(js-compile-to-body (cons 'progn body
) :indent
" ")))
925 (defmethod js-to-statement-strings ((while js-while
) start-pos
)
926 (let ((header (dwim-join (list (js-to-strings (while-check while
) (+ start-pos
2)))
930 (body (js-to-statement-strings (while-body while
) (+ start-pos
2))))
931 (nconc header body
(list "}"))))
935 (defclass js-with
(statement)
936 ((obj :initarg
:obj
:accessor with-obj
)
937 (body :initarg
:body
:accessor with-body
)))
939 (define-js-compiler-macro with
(statement &rest body
)
940 (make-instance 'js-with
941 :obj
(js-compile-to-expression (first statement
))
942 :body
(js-compile-to-body (cons 'progn body
) :indent
" ")))
944 (defmethod js-to-statement-strings ((with js-with
) start-pos
)
945 (nconc (dwim-join (list (js-to-strings (with-obj with
) (+ start-pos
2)))
947 :start
"with (" :end
") {")
948 (js-to-statement-strings (with-body with
) (+ start-pos
2))
953 (defclass js-case
(statement)
954 ((value :initarg
:value
:accessor case-value
)
955 (clauses :initarg
:clauses
:accessor case-clauses
)))
957 ;;; XXX DEFAULT exporten
958 (define-js-compiler-macro case
(value &rest clauses
)
959 (let ((clauses (mapcar #'(lambda (clause)
960 (let ((val (first clause
))
962 (list (if (eql val
'default
)
964 (js-compile-to-expression val
))
965 (js-compile-to-body (cons 'progn body
) :indent
" "))))
967 (check (js-compile-to-expression value
)))
968 (make-instance 'js-case
:value check
971 (defmethod js-to-statement-strings ((case js-case
) start-pos
)
972 (let ((body (mapcan #'(lambda (clause)
973 (let ((val (car clause
))
974 (body (second clause
)))
975 (dwim-join (list (if (eql val
'default
)
977 (js-to-strings val
(+ start-pos
2)))
978 (js-to-statement-strings body
(+ start-pos
2)))
980 :start
(if (eql val
'default
) " default" " case ")
982 :join-after
":"))) (case-clauses case
))))
984 (format t
"body: ~S~%" body
)
985 (nconc (dwim-join (list (js-to-strings (case-value case
) (+ start-pos
2)))
987 :start
"switch (" :end
") {")
993 (defclass js-try
(statement)
994 ((body :initarg
:body
:accessor try-body
)
995 (catch :initarg
:catch
:accessor try-catch
)
996 (finally :initarg
:finally
:accessor try-finally
)))
998 (define-js-compiler-macro try
(body clauses
)
999 (let ((body (js-compile-to-body body
:indent
" "))
1000 (catch (cdr (assoc :catch clauses
)))
1001 (finally (cdr (assoc :finally clauses
))))
1002 (make-instance 'js-try
1004 :catch
(when catch
(list (js-compile-to-symbol (first catch
))
1005 (js-compile-to-body (cons 'progn
(cdr catch
))
1007 :finally
(when finally
(js-compile-to-body finally
:indent
" ")))))
1009 (defmethod js-to-statement-strings ((try js-try
) start-pos
)
1010 (let* ((catch (try-catch try
))
1011 (finally (try-finally try
))
1012 (catch-list (when catch
1013 (dwim-join (list (list (symbol-to-js (first catch
)))
1014 (js-to-strings (second catch
) (+ start-pos
2)))
1018 (finally-list (when finally
1019 (dwim-join (list (js-to-strings finally
(+ start-pos
2)))
1021 :start
"finally {"))))
1022 (nconc (dwim-join (list (js-to-statement-strings (try-body try
) (+ start-pos
2)))
1031 (defclass regex
(expression)
1034 (define-js-compiler-macro regex
(regex)
1035 (make-instance 'regex
:value
(string regex
)))
1037 ;;; conditional compilation
1040 ((test :initarg
:test
:accessor cc-if-test
)
1041 (body :initarg
:body
:accessor cc-if-body
)))
1043 (defmethod js-to-statement-strings ((cc cc-if
) start-pos
)
1044 (nconc (list (format nil
"/*@if ~A" (cc-if-test cc
)))
1045 (mapcan #'(lambda (x) (js-to-strings x start-pos
)) (cc-if-body cc
))
1048 (define-js-compiler-macro cc-if
(test &rest body
)
1049 (make-instance 'cc-if
:test test
1050 :body
(mapcar #'js-compile body
)))
1054 (defun js-compile (form)
1055 (setf form
(js-expand-form form
))
1056 (cond ((stringp form
)
1057 (make-instance 'string-literal
:value form
))
1059 (make-instance 'number-literal
:value form
))
1061 (let ((c-macro (js-get-compiler-macro form
)))
1064 (make-instance 'js-variable
:value form
))))
1066 (eql (first form
) 'quote
))
1069 (js-compile-list form
))
1070 (t (error "Unknown atomar expression ~S" form
))))
1072 (defun js-compile-list (form)
1073 (let* ((name (car form
))
1075 (js-form (js-get-compiler-macro name
)))
1077 (apply js-form args
))
1080 (make-instance 'op-form
1081 :operator
(js-convert-op-name (first form
))
1082 :args
(mapcar #'js-compile-to-expression
(rest form
))))
1084 ((method-call-p form
)
1085 (make-instance 'method-call
1086 :method
(first form
)
1087 :args
(mapcar #'js-compile-to-expression
(rest form
))))
1089 ((funcall-form-p form
)
1090 (make-instance 'function-call
1091 :function
(first form
)
1092 :args
(mapcar #'js-compile-to-expression
(rest form
))))
1094 (t (error "Unknown form ~S" form
)))))
1096 (defun js-compile-to-expression (form)
1097 (let ((res (js-compile form
)))
1098 (assert (typep res
'expression
))
1101 (defun js-compile-to-symbol (form)
1102 (let ((res (js-compile form
)))
1103 (when (typep res
'js-variable
)
1104 (setf res
(value res
)))
1105 (assert (symbolp res
))
1108 (defun js-compile-to-statement (form)
1109 (let ((res (js-compile form
)))
1110 (assert (typep res
'statement
))
1113 (defun js-compile-to-body (form &key
(indent ""))
1114 (let ((res (js-compile-to-statement form
)))
1115 (if (typep res
'js-body
)
1116 (progn (setf (b-indent res
) indent
)
1118 (make-instance 'js-body
1120 :stmts
(list res
)))))