diff --git a/cherry/src/cherry/compiler.cljc b/cherry/src/cherry/compiler.cljc index e9fbcb8..4c29e29 100644 --- a/cherry/src/cherry/compiler.cljc +++ b/cherry/src/cherry/compiler.cljc @@ -23,7 +23,7 @@ [squint.compiler-common :as cc :refer [#?(:cljs Exception) #?(:cljs format) *aliases* *imported-vars* *public-vars* *repl* comma-list emit emit-args emit-infix - emit-special emit-wrap escape-jsx expr-env infix-operator? prefix-unary? + emit-special emit-return escape-jsx expr-env infix-operator? prefix-unary? statement suffix-unary?]]) #?(:cljs (:require-macros [cherry.resource :as resource]))) @@ -35,7 +35,7 @@ (defmethod emit #?(:clj clojure.lang.Keyword :cljs Keyword) [expr env] (swap! *imported-vars* update "cherry-cljs/lib/cljs_core.js" (fnil conj #{}) 'keyword) - (emit-wrap (str (format "keyword(%s)" (pr-str (subs (str expr) 1)))) env)) + (emit-return (str (format "keyword(%s)" (pr-str (subs (str expr) 1)))) env)) (def special-forms (set ['var '. 'if 'funcall 'fn 'fn* 'quote 'set! 'return 'delete 'new 'do 'aget 'while @@ -110,7 +110,7 @@ (repeat statement-separator)))) (defmethod emit-special 'quote [_ env [_ form]] - (emit-wrap (emit form (expr-env (assoc env :quote true))) env)) + (emit-return (emit form (expr-env (assoc env :quote true))) env)) (defmethod emit-special 'deftype* [_ env [_ t fields pmasks body]] (let [fields (map munge fields)] @@ -154,7 +154,7 @@ (emit test env) (emit then env) (emit else env))) - (emit-wrap env)) + (emit-return env)) (str (format "if (truth_(%s)) {\n" (emit test (assoc env :context :expr))) (emit then env) @@ -266,17 +266,17 @@ (symbol "") tag-name) tag-name (emit tag-name (expr-env (dissoc env :jsx)))] - (emit-wrap (format "<%s%s>%s" + (emit-return (format "<%s%s>%s" tag-name (jsx-attrs attrs env) (let [env (expr-env env)] (str/join " " (map #(emit % env) elts))) tag-name) env)) (if (::js (meta expr)) - (emit-wrap (format "[%s]" + (emit-return (format "[%s]" (str/join ", " (emit-args env expr))) env) (do (swap! *imported-vars* update "cherry-cljs/lib/cljs_core.js" (fnil conj #{}) 'vector) - (emit-wrap (format "vector(%s)" + (emit-return (format "vector(%s)" (str/join ", " (emit-args env expr))) env))))) #?(:cljs (derive PersistentArrayMap ::map)) @@ -302,13 +302,13 @@ (escape-jsx (-> (if map-fn (format "%s(%s)" map-fn keys) (format "({ %s })" keys)) - (emit-wrap env)) env*))) + (emit-return env)) env*))) (defmethod emit #?(:clj clojure.lang.PersistentHashSet :cljs PersistentHashSet) [expr env] (swap! *imported-vars* update "cherry-cljs/lib/cljs_core.js" (fnil conj #{}) 'hash_set) - (emit-wrap (format "%s%s" "hash_set" + (emit-return (format "%s%s" "hash_set" (comma-list (emit-args env expr))) env)) (defn transpile-form [f] diff --git a/compiler-common/src/squint/compiler_common.cljc b/compiler-common/src/squint/compiler_common.cljc index 8baa36a..f154d75 100644 --- a/compiler-common/src/squint/compiler_common.cljc +++ b/compiler-common/src/squint/compiler_common.cljc @@ -19,7 +19,7 @@ template substitutions)) -(defn emit-wrap [s env] +(defn emit-return [s env] (if (= :return (:context env)) (format "return %s;" s) s)) @@ -66,7 +66,7 @@ (str/replace #"\$$" "")))) (defmethod emit nil [_ env] - (emit-wrap "null" env)) + (emit-return "null" env)) #?(:clj (derive #?(:clj java.lang.Integer) ::number)) #?(:clj (derive #?(:clj java.lang.Long) ::number)) @@ -85,7 +85,7 @@ (defmethod emit ::number [expr env] (-> (str expr) - (emit-wrap env) + (emit-return env) (emit-repl env) (escape-jsx env))) @@ -93,14 +93,14 @@ (-> (if (and (:jsx env) (not (:jsx-attr env))) expr - (emit-wrap (pr-str expr) env)) + (emit-return (pr-str expr) env)) (emit-repl env))) (defmethod emit #?(:clj java.lang.Boolean :cljs js/Boolean) [^String expr env] (-> (if (:jsx-attr env) (escape-jsx expr env) (str expr)) - (emit-wrap env) + (emit-return env) (emit-repl env))) #?(:clj (defmethod emit #?(:clj java.util.regex.Pattern) [expr _env] @@ -109,7 +109,7 @@ (defmethod emit :default [expr env] ;; RegExp case moved here: ;; References to the global RegExp object prevents optimization of regular expressions. - (emit-wrap (str expr) env)) + (emit-return (str expr) env)) (def prefix-unary-operators '#{!}) @@ -148,7 +148,7 @@ '+ "+"}] (str "(" (str/join (str " " (or (substitutions operator) operator) " ") (emit-args env args)) ")")) - (emit-wrap enc-env))) + (emit-return enc-env))) (emit-repl enc-env))))) (def core-vars (atom #{})) @@ -164,7 +164,7 @@ (defmethod emit #?(:clj clojure.lang.Symbol :cljs Symbol) [expr env] (if (:quote env) - (emit-wrap (escape-jsx (emit (list 'cljs.core/symbol + (emit-return (escape-jsx (emit (list 'cljs.core/symbol (str expr)) (dissoc env :quote))env) env) @@ -195,7 +195,7 @@ (let [m (munged-name expr)] (str (when *repl* (str (munge *cljs-ns*) ".")) m)))))] - (-> (emit-wrap (escape-jsx (str expr) env) + (-> (emit-return (escape-jsx (str expr) env) env) (emit-repl env)))))) @@ -350,7 +350,7 @@ (emit-var more env))) (defn js-await [env more] - (-> (emit-wrap (wrap-await (emit more (expr-env env))) env) + (-> (emit-return (wrap-await (emit more (expr-env env))) env) (emit-repl env))) (defmethod emit-special 'js/await [_ env [_await more]] @@ -483,13 +483,13 @@ (defn emit-method [env obj method args] (let [eenv (expr-env env)] - (emit-wrap (str (emit obj eenv) "." + (emit-return (str (emit obj eenv) "." (str method) (comma-list (emit-args env args))) env))) (defn emit-aget [env var idxs] - (emit-wrap (apply str + (emit-return (apply str (emit var (expr-env env)) (interleave (repeat "[") (emit-args env idxs) (repeat "]"))) env)) @@ -517,18 +517,18 @@ (defmethod emit-special 'set! [_type env [_set! var val & more]] (assert (or (nil? more) (even? (count more)))) (let [eenv (expr-env env)] - (emit-wrap (str (emit var eenv) " = " (emit val eenv) statement-separator + (emit-return (str (emit var eenv) " = " (emit val eenv) statement-separator #_(when more (str (emit (cons 'set! more) env)))) env))) (defmethod emit-special 'new [_type env [_new class & args]] - (emit-wrap (str "new " (emit class (expr-env env)) (comma-list (emit-args env args))) env)) + (emit-return (str "new " (emit class (expr-env env)) (comma-list (emit-args env args))) env)) (defmethod emit-special 'dec [_type env [_ var]] - (emit-wrap (str "(" (emit var (assoc env :context :expr)) " - " 1 ")") env)) + (emit-return (str "(" (emit var (assoc env :context :expr)) " - " 1 ")") env)) (defmethod emit-special 'inc [_type env [_ var]] - (emit-wrap (str "(" (emit var (assoc env :context :expr)) " + " 1 ")") env)) + (emit-return (str "(" (emit var (assoc env :context :expr)) " + " 1 ")") env)) #_(defmethod emit-special 'defined? [_type env [_ var]] (str "typeof " (emit var env) " !== \"undefined\" && " (emit var env) " !== null")) @@ -536,11 +536,14 @@ #_(defmethod emit-special '? [_type env [_ test then else]] (str (emit test env) " ? " (emit then env) " : " (emit else env))) +(defn wrap-parens [s] + (str "(" s ")")) + (defmethod emit-special 'and [_type env [_ & more]] - (emit-wrap (apply str (interpose " && " (emit-args env more))) env)) + (emit-return (wrap-parens (apply str (interpose " && " (emit-args env more)))) env)) (defmethod emit-special 'or [_type env [_ & more]] - (emit-wrap (apply str (interpose " || " (emit-args env more))) env)) + (emit-return (wrap-parens (apply str (interpose " || " (emit-args env more)))) env)) (defmethod emit-special 'while [_type env [_while test & body]] (str "while (" (emit test) ") { \n" @@ -605,7 +608,7 @@ break;}" body) (let [signature (first expr) body (rest expr)] (str (emit-function env nil signature body)))) - (emit-wrap env)))) + (emit-return env)))) (defmethod emit-special 'fn* [_type env [_fn & sigs :as expr]] (let [async? (:async (meta expr))] @@ -653,7 +656,7 @@ break;}" body) "}\n"))) (not= :statement (:context env)) (wrap-iife)) - (emit-wrap outer-env))))) + (emit-return outer-env))))) #_(defmethod emit-special 'funcall [_type env [fname & args :as _expr]] (-> (emit-wrap (str @@ -676,11 +679,11 @@ break;}" body) cherry+interop? (and cherry? (= "js" ns))] - (-> (emit-wrap (str + (-> (emit-return (str (emit fname (expr-env env)) ;; this is needed when calling keywords, symbols, etc. We could ;; optimize this later by inferring that we're not directly - ;; calling a `function`. + ;; calling a `function`. (when (and cherry? (not cherry+interop?)) ".call") (comma-list (emit-args env (if cherry? diff --git a/squint/CHANGELOG.md b/squint/CHANGELOG.md index db8e43f..21851f6 100644 --- a/squint/CHANGELOG.md +++ b/squint/CHANGELOG.md @@ -2,6 +2,10 @@ [Squint](https://github.com/squint-cljs/squint): ClojureScript syntax to JavaScript compiler +## 0.0.7 + +[#274](https://github.com/squint-cljs/squint/issues/274): fix logic precedence by wrapping in parens + ## 0.0.6 Add preliminary Node.js API in `node.js` diff --git a/squint/src/squint/compiler.cljc b/squint/src/squint/compiler.cljc index adcdc2f..01b3c04 100644 --- a/squint/src/squint/compiler.cljc +++ b/squint/src/squint/compiler.cljc @@ -16,7 +16,7 @@ [squint.compiler-common :as cc :refer [#?(:cljs Exception) #?(:cljs format) *aliases* *cljs-ns* *excluded-core-vars* *imported-vars* *public-vars* *repl* - comma-list emit emit-args emit-infix emit-repl emit-special emit-wrap escape-jsx + comma-list emit emit-args emit-infix emit-repl emit-special emit-return escape-jsx expr-env infix-operator? prefix-unary? statement suffix-unary?]] [squint.internal.deftype :as deftype] [squint.internal.destructure :refer [core-let]] @@ -28,7 +28,7 @@ (defmethod emit #?(:clj clojure.lang.Keyword :cljs Keyword) [expr env] - (-> (emit-wrap (str (pr-str (subs (str expr) 1))) env) + (-> (emit-return (str (pr-str (subs (str expr) 1))) env) (emit-repl env))) (def special-forms (set ['var '. 'if 'funcall 'fn 'fn* 'quote 'set! @@ -100,13 +100,13 @@ (str (emit arg) operator)) (defmethod emit-special 'quote [_ env [_ form]] - (emit-wrap (emit form (expr-env (assoc env :quote true))) env)) + (emit-return (emit form (expr-env (assoc env :quote true))) env)) (defmethod emit-special 'not [_ env [_ form]] - (emit-wrap (str "!" (emit form (expr-env env))) env)) + (emit-return (str "!" (emit form (expr-env env))) env)) (defmethod emit-special 'js/typeof [_ env [_ form]] - (emit-wrap (str "typeof " (emit form (expr-env env))) env)) + (emit-return (str "typeof " (emit form (expr-env env))) env)) (defmethod emit-special 'letfn* [_ env [_ form & body]] (let [bindings (take-nth 2 form) @@ -118,7 +118,7 @@ (emit let env))) (defmethod emit-special 'quote [_ env [_ form]] - (emit-wrap (emit form (expr-env (assoc env :quote true))) env)) + (emit-return (emit form (expr-env (assoc env :quote true))) env)) #_(defmethod emit-special 'let* [_type enc-env [_let bindings & body]] (emit-let enc-env bindings body false)) @@ -167,7 +167,7 @@ (emit test env) (emit then env) (emit else env))) - (emit-wrap env)) + (emit-return env)) (str (format "if (%s) {\n" (emit test (assoc env :context :expr))) (emit then env) @@ -288,14 +288,14 @@ (symbol "") tag-name) tag-name (emit tag-name (expr-env (dissoc env :jsx)))] - (emit-wrap (format "<%s%s>%s" + (emit-return (format "<%s%s>%s" tag-name (jsx-attrs attrs env) (let [env (expr-env env)] (str/join " " (map #(emit % env) elts))) tag-name) env)) - (-> (emit-wrap (format "[%s]" + (-> (emit-return (format "[%s]" (str/join ", " (emit-args env expr))) env) (emit-repl env)))) @@ -314,13 +314,13 @@ (emit (val pair) expr-env))) keys (str/join ", " (map mk-pair (seq expr)))] (escape-jsx (-> (format "({ %s })" keys) - (emit-wrap env)) + (emit-return env)) env*))) (defmethod emit #?(:clj clojure.lang.PersistentHashSet :cljs PersistentHashSet) [expr env] - (emit-wrap + (emit-return (format "new Set([%s])" (str/join ", " (emit-args (expr-env env) expr))) env)) diff --git a/squint/test/squint/compiler_test.cljs b/squint/test/squint/compiler_test.cljs index c0b58cb..a9daca4 100644 --- a/squint/test/squint/compiler_test.cljs +++ b/squint/test/squint/compiler_test.cljs @@ -1214,6 +1214,9 @@ (is (= 2 (jsv! '(do (defn foo [a b] (and a b)) (foo 1 2))))) (is (= 1 (jsv! '(do (defn foo [a b] (or a b)) (foo 1 2)))))) +(deftest logic-precedence + (is (false? (jsv! '(and (or true false) false))))) + (deftest multiple-arity-infix (is (true? (jsv! '(> 5 4 3 2 1)))) (is (true? (jsv! '(> 5 4 3))))