Permalink
Browse files

Merge pull request #12 from christophermaier/try-catch

Add support for try / catch / finally
  • Loading branch information...
2 parents 254adb4 + 41ac62a commit 50203535c41ac0868ff09fc2ad21800d75fb5aab @arohner committed Jul 16, 2011
Showing with 100 additions and 1 deletion.
  1. +26 −0 README.markdown
  2. +31 −1 src/com/reasonr/scriptjure.clj
  3. +43 −0 test/test_scriptjure.clj
View
@@ -94,6 +94,32 @@ Returns a javascript if statement. Like Clojure, true-form and false-form take o
bar(y);
}"
+**try / catch / finally**
+
+ (try expr* catch-clause? finally-clause?)
+ catch-clause -> (catch e expr*)
+ finally-clause -> (finally expr*)
+
+Returns a JavaScript `try` / `catch` / `finally` block. All non-`catch` and non-`finally` forms within a `try` form are executed in an implicit `do` statement. The `catch` clause (if present) generates an unconditional `catch` block (multiple conditional `catch` blocks are not supported at this time), with `e` bound to the exception object. The `finally` clause (if present) is used to generate a `finally` block. All expressions in the `catch` and `finally` clauses are executed in implicit `do` statements.
+
+ (js (try
+ (set! x 5)
+ (catch e
+ (print (+ "BOOM: " e)))
+ (finally
+ (print "saved!"))))
+ => "try{
+ x = 5;
+ }
+ catch(e){
+ print((\"BOOM: \" + e));
+ }
+ finally{
+ print(\"saved!\");
+ }"
+
+An Exception will be thrown if there are no `catch` or `finally` clauses, or if there are more than one of either.
+
**return**
(return value)
@@ -80,7 +80,7 @@
(defmethod emit :default [expr]
(str expr))
-(def special-forms (set ['var '. '.. 'if 'funcall 'fn 'quote 'set! 'return 'delete 'new 'do 'aget 'while 'doseq 'str 'inc! 'dec! 'dec 'inc 'defined? 'and 'or '?]))
+(def special-forms (set ['var '. '.. 'if 'funcall 'fn 'quote 'set! 'return 'delete 'new 'do 'aget 'while 'doseq 'str 'inc! 'dec! 'dec 'inc 'defined? 'and 'or '? 'try]))
(def prefix-unary-operators (set ['!]))
@@ -252,6 +252,36 @@
body (rest expr)]
(str (emit-function nil signature body))))))
+(defmethod emit-special 'try [type [try & body :as expression]]
+ (let [try-body (remove #(contains? #{'catch 'finally} (first %))
+ body)
+ catch-clause (filter #(= 'catch (first %))
+ body)
+ finally-clause (filter #(= 'finally (first %))
+ body)]
+ (cond
+ (and (empty? catch-clause)
+ (empty? finally-clause))
+ (throw (new Exception (str "Must supply a catch or finally clause (or both) in a try statement! " expression)))
+
+ (> (count catch-clause) 1)
+ (throw (new Exception (str "Multiple catch clauses in a try statement are not currently supported! " expression)))
+
+ (> (count finally-clause) 1)
+ (throw (new Exception (str "Cannot supply more than one finally clause in a try statement! " expression)))
+
+ :true (str "try{\n"
+ (emit-do try-body)
+ "}\n"
+ (if-let [[_ exception & catch-body] (first catch-clause)]
+ (str "catch(" (emit exception) "){\n"
+ (emit-do catch-body)
+ "}\n"))
+ (if-let [[_ & finally-body] (first finally-clause)]
+ (str "finally{\n"
+ (emit-do finally-body)
+ "}\n"))))))
+
(declare emit-custom custom-form?)
(derive clojure.lang.Cons ::list)
View
@@ -177,4 +177,47 @@
(deftest custom-form-use
(is (= (js (prn-hw "custom"))) "alert(\"hello world custom\")"))
+(deftest test-try
+ (testing "Normal cases for try / catch / finally"
+ (is (= (strip-whitespace (js (try (set! x 5)
+ (catch e (print (+ "BOOM: " e)))
+ (finally (print "saved!")))))
+ "try{ x = 5; } catch(e){ print((\"BOOM: \" + e)); } finally{ print(\"saved!\"); }")
+ "Try with catch and finally is OK")
+ (is (= (strip-whitespace (js (try (set! x 5)
+ (catch e (print (+ "BOOM: " e))))))
+ "try{ x = 5; } catch(e){ print((\"BOOM: \" + e)); }")
+ "Try with just a catch clause is OK")
+ (is (= (strip-whitespace (js (try (set! x 5)
+ (finally (print "saved!")))))
+ "try{ x = 5; } finally{ print(\"saved!\"); }")
+ "Try with just a finally clause is OK")
+ (is (= (strip-whitespace (js (try (set! x 5)
+ (print "doin' stuff")
+ (print "doin' more stuff")
+ (catch e
+ (print (+ "BOOM: " e))
+ (print "ouch"))
+ (finally (print "saved!")
+ (print "yippee!")))))
+ "try{ x = 5; print(\"doin' stuff\"); print(\"doin' more stuff\"); } catch(e){ print((\"BOOM: \" + e)); print(\"ouch\"); } finally{ print(\"saved!\"); print(\"yippee!\"); }")
+ "Try, catch, and finally all use implicit 'do' for multiple statements"))
+ (testing "Exceptional cases for try / catch / finally"
+ (is (thrown-with-msg? Exception
+ #"Must supply a catch or finally clause \(or both\) in a try statement! \(try \(set! x 5\)\)"
+ (js (try (set! x 5))))
+ "Try with no catch and no finally should throw an exception")
+ (is (thrown-with-msg? Exception
+ #"Multiple catch clauses in a try statement are not currently supported! \(try \(set! x 5\) \(catch e \(print \"foo\"\)\) \(catch ee \(print \"bar\"\)\)\)"
+ (js (try (set! x 5)
+ (catch e (print "foo"))
+ (catch ee (print "bar")))))
+ "Multiple catch clauses are not supported")
+ (is (thrown-with-msg? Exception
+ #"Cannot supply more than one finally clause in a try statement! \(try \(set! x 5\) \(finally \(print \"foo\"\)\) \(finally \(print \"bar\"\)\)\)"
+ (js (try (set! x 5)
+ (finally (print "foo"))
+ (finally (print "bar")))))
+ "Cannot supply more than one finally clause")))
+
(run-tests)

0 comments on commit 5020353

Please sign in to comment.