;;;# The `CASE' statement
;;;t \index{CASE}
+;;;t \index{SWITCH}
;;;t \index{switch}
; (CASE case-value clause*)
;
-; clause ::= (value body)
+; clause ::= (value body) | ((value*) body) | t-clause
; case-value ::= a ParenScript expression
; value ::= a ParenScript expression
+; t-clause ::= {t | otherwise | default} body
; body ::= a list of ParenScript statements
;;; The Lisp `CASE' form is transformed to a `switch' statement in
;;; JavaScript. Note that `CASE' is not an expression in
-;;; ParenScript. The default case is not named `T' in ParenScript, but
-;;; `DEFAULT' instead.
+;;; ParenScript.
(case (aref blorg i)
- (1 (alert "one"))
+ ((1 "one") (alert "one"))
(2 (alert "two"))
- (default (alert "default clause")))
+ (t (alert "default clause")))
=> switch (blorg[i]) {
- case 1: alert('one');
- case 2: alert('two');
+ case 1: ;
+ case 'one':
+ alert('one');
+ break;
+ case 2:
+ alert('two');
+ break;
default: alert('default clause');
}
+; (SWITCH case-value clause*)
+; clause ::= (value body) | (default body)
+
+;;; The `SWITCH' form is the equivalent to a javascript switch statement.
+;;; No break statements are inserted, and the default case is named `DEFAULT'.
+;;; The `CASE' form should be prefered in most cases.
+
+(switch (aref blorg i)
+ (1 (alert "If I get here"))
+ (2 (alert "I also get here"))
+ (default (alert "I always get here")))
+ => switch (blorg[i]) {
+ case 1: alert('If I get here');
+ case 2: alert('I also get here');
+ default: alert('I always get here');
+ }
+
+
;;;# The `WITH' statement
;;;t \index{WITH}
;;;t \index{dynamic scope}
;;; case
-(defjsclass js-case (statement)
+(defjsclass js-switch (statement)
((value :initarg :value :accessor case-value)
(clauses :initarg :clauses :accessor case-clauses)))
-(define-js-compiler-macro case (value &rest clauses)
+(define-js-compiler-macro switch (value &rest clauses)
(let ((clauses (mapcar #'(lambda (clause)
(let ((val (first clause))
(body (cdr clause)))
(js-compile-to-body (cons 'progn body) :indent " "))))
clauses))
(check (js-compile-to-expression value)))
- (make-instance 'js-case :value check
+ (make-instance 'js-switch :value check
:clauses clauses)))
-(defmethod js-to-statement-strings ((case js-case) start-pos)
+(defmethod js-to-statement-strings ((case js-switch) start-pos)
(let ((body (mapcan #'(lambda (clause)
(let ((val (car clause))
(body (second clause)))
body
(list "}"))))
+(defjsmacro case (value &rest clauses)
+ (labels ((make-clause (val body more)
+ (cond ((listp val)
+ (append (mapcar #'list (butlast val))
+ (make-clause (first (last val)) body more)))
+ ((member val '(t otherwise))
+ (make-clause 'default body more))
+ (more `((,val ,@body break)))
+ (t `((,val ,@body))))))
+ `(switch ,value ,@(mapcon #'(lambda (x)
+ (make-clause (car (first x))
+ (cdr (first x))
+ (rest x)))
+ clauses))))
+
;;; throw catch
(defjsclass js-try (statement)
(in-package :js-test)
;;Generates automatic tests from the reference
-(defparameter +reference-file+ (make-pathname :name "reference"
- :type "lisp"
- :defaults *load-truename*))
+(defparameter +reference-file+ (merge-pathnames
+ (make-pathname :directory '(:relative :back "docs"))
+ (make-pathname :name "reference"
+ :type "lisp"
+ :defaults *load-truename*)))
+
+
(defparameter +generate-file+ (make-pathname :name "reference-tests"
:type "lisp"
:defaults *load-truename*))
(= 2 heading-count)) ;requires cl-interpol reader
(format t "Skipping regex-test two~&"))
((and lisp-part javascript-part)
- (format out-stream "(test-ps-js ~a-~a ~% ~a ~% \"~a\")~%~%"
+ (format out-stream "(test-ps-js ~a-~a~% ~a~% \"~a\")~%~%"
heading heading-count
(trim-whitespace lisp-part)
(strip-indentation javascript-part js-indent-width)))
(test-ps-js the-case-statement-1
(case (aref blorg i)
- (1 (alert "one"))
+ ((1 "one") (alert "one"))
(2 (alert "two"))
- (default (alert "default clause")))
+ (t (alert "default clause")))
"switch (blorg[i]) {
- case 1: alert('one');
- case 2: alert('two');
+ case 1: ;
+ case 'one':
+ alert('one');
+ break;
+ case 2:
+ alert('two');
+ break;
default: alert('default clause');
}")
+(test-ps-js the-case-statement-2
+ (switch (aref blorg i)
+ (1 (alert "If I get here"))
+ (2 (alert "I also get here"))
+ (default (alert "I always get here")))
+ "switch (blorg[i]) {
+ case 1: alert('If I get here');
+ case 2: alert('I also get here');
+ default: alert('I always get here');
+}")
+
(test-ps-js the-with-statement-1
(with ((create :foo "foo" :i "i"))
(alert (+ "i is now intermediary scoped: " i)))
(test-ps-js buggy-slot-value-two
(slot-value foo (get-slot-name))
"foo[getSlotName()]")
+
+(test-ps-js old-case-is-now-switch
+ ;; Switch was "case" before, but that was very non-lispish.
+ ;; For example, this code makes three messages and not one
+ ;; which may have been expected. This is because a switch
+ ;; statment must have a break statement for it to return
+ ;; after the alert. Otherwise it continues on the next
+ ;; clause.
+ (switch (aref blorg i)
+ (1 (alert "one"))
+ (2 (alert "two"))
+ (default (alert "default clause")))
+ "switch (blorg[i]) {
+ case 1: alert('one');
+ case 2: alert('two');
+ default: alert('default clause');
+ }")
+
+(test-ps-js lisp-like-case
+ (case (aref blorg i)
+ (1 (alert "one"))
+ (2 (alert "two"))
+ (default (alert "default clause")))
+ "switch (blorg[i]) {
+ case 1:
+ alert('one');
+ break;
+ case 2:
+ alert('two');
+ break;
+ default: alert('default clause');
+ }")
+
+
+(test-ps-js even-lispier-case
+ (case (aref blorg i)
+ ((1 2) (alert "Below three"))
+ (3 (alert "Three"))
+ (t (alert "Something else")))
+ "switch (blorg[i]) {
+ case 1: ;
+ case 2:
+ alert('Below three');
+ break;
+ case 3:
+ alert('Three');
+ break;
+ default: alert('Something else');
+ }")
+
+(test-ps-js otherwise-case
+ (case (aref blorg i)
+ (1 (alert "one"))
+ (otherwise (alert "default clause")))
+ "switch (blorg[i]) {
+ case 1:
+ alert('one');
+ break;
+ default: alert('default clause');
+ }")