From 54c6a5dec031de59dad385d4ba2ccda09a70ec2a Mon Sep 17 00:00:00 2001 From: Carlo Zancanaro Date: Thu, 20 Jun 2013 15:49:55 +1000 Subject: Fix compiling of n-ary operators (0 & 1 args), add operator aliases n-ary operators with no arguments would break everything (compiling to "()"). This should no longer be the case. Additionally, unary operators (ie. negation) now compile properly in the one arg case. Operator aliases allow us to change how we refer to things like the "~" regex operator in postgresql. At the moment it's aliased as "$". Additionally: booleans now compile to upper case. --- src/clojure_sql/compiler.clj | 45 +++++++++++++++++++++++++++++++++----------- src/clojure_sql/core.clj | 4 ++++ src/clojure_sql/writer.clj | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/clojure_sql/compiler.clj b/src/clojure_sql/compiler.clj index 9ba0c3a..c7f907f 100644 --- a/src/clojure_sql/compiler.clj +++ b/src/clojure_sql/compiler.clj @@ -5,6 +5,12 @@ [clojure-sql.util :as u :refer [named?]] [clojure-sql.writer :as w :refer [return lift p-lift sequence do-m tell >>]])) +(defn no-args-operator-error [op] + (throw (ex-info (str "Argument called with no args doesn't have identity value: " op) + {:operator op}))) + + + (defn add-parentheses [s] (str \( s \))) @@ -39,10 +45,18 @@ ;; Utility functions for the compile-* functions ;; ============================================================== +(def ^:private boolean? (some-fn true? false?)) +(def ^:private regex? (partial instance? (class #""))) +(def ^:private operator-name (some-fn (comp {"$" "~"} name) name)) + (def quote? (comp boolean '#{"quote"} name)) -(def unary? (comp boolean '#{"not" "exists"} name)) -(def binary? (comp boolean '#{"=" "<" ">" "<=" ">=" "is" "in"} name)) -(def operator? (comp boolean '#{"and" "or" "+" "-" "/" "*"} name)) +(def unary? (comp boolean '#{"not" "exists" "-"} name)) +(def binary? (comp boolean '#{"=" "<" ">" "<=" ">=" "is" "in" "like" "~"} name)) +(def n-ary? (comp boolean '#{"and" "or" "+" "-" "/" "*"} name)) +(def operator-identity (comp {"and" true + "or" false + "+" 0 + "*" 1} name)) ;; ============================================================== @@ -64,24 +78,31 @@ (defmulti compile-expression-sequential (fn [db _] db)) (defmethod compile-expression-sequential :default [db ex] (let [compile-exprs #(map (partial compile-expression db) %) - op (name (first ex)) + op-name (operator-name (first ex)) num-args (dec (count ex))] - (-> (condp u/funcall (first ex) + (-> (condp u/funcall op-name quote? (do (assert (= num-args 1) "`quote` must only take one argument") (if (sequential? (second ex)) (compile-expression-list db (second ex)) (>> (tell (second ex)) (return "?")))) - unary? (do (assert (= num-args 1) (str "Unary operator `" op "` must take one argument")) + n-ary? (do-m :let [[op & exprs] (compile-exprs ex)] + vals <- (apply sequence (interpose op exprs)) + (condp = (count vals) + 0 (if-let [id (operator-identity op-name)] + (compile-expression db id) + (no-args-operator-error (name (first ex)))) + 1 (if (unary? op-name) + (do-m compiled-op <- op + (return (str compiled-op (first vals))))) + (return (string/join " " vals)))) + unary? (do (assert (= num-args 1) (str "Unary operator `" (first ex) "` must take one argument")) (do-m :let [exprs (compile-exprs ex)] vals <- (apply sequence exprs) (return (string/join "" vals)))) - binary? (do (assert (= num-args 2) (str "Binary operator `" op "` must take two arguments")) + binary? (do (assert (= num-args 2) (str "Binary operator `" (first ex) "` must take two arguments")) (do-m :let [[op left right] (compile-exprs ex)] vals <- (sequence left op right) (return (string/join " " vals)))) - operator? (do-m :let [[op & exprs] (compile-exprs ex)] - vals <- (apply sequence (interpose op exprs)) - (return (string/join " " vals))) (do-m :let [fn-name (function-name db (first ex)) exprs (compile-exprs (rest ex))] vals <- (apply sequence exprs) @@ -92,12 +113,14 @@ (defmulti compile-expression (fn [db _] db)) (defmethod compile-expression :default [db ex] (condp u/funcall ex + boolean? (return (string/upper-case (str ex))) query? ($add-parentheses (compile-query db ex)) nil? (return "NULL") vector? (return (str (table-name db (first ex)) \. (field-name db (second ex)))) keyword? (return (field-name db ex)) + regex? (>> (tell (str ex)) (return "?")) string? (>> (tell ex) (return "?")) ;;(sql-string db ex) - symbol? (return (string/upper-case (name ex))) + symbol? (return (string/upper-case (operator-name ex))) sequential? (compile-expression-sequential db ex) (return ex))) diff --git a/src/clojure_sql/core.clj b/src/clojure_sql/core.clj index 72438a6..bd4b431 100644 --- a/src/clojure_sql/core.clj +++ b/src/clojure_sql/core.clj @@ -26,3 +26,7 @@ (def group #'d/group) (def having #'d/having) (def sort #'d/sort) + + +(-> (table :x) + (select `($ (- :a) #"bloo"))) diff --git a/src/clojure_sql/writer.clj b/src/clojure_sql/writer.clj index 5f4250b..176cc67 100644 --- a/src/clojure_sql/writer.clj +++ b/src/clojure_sql/writer.clj @@ -53,7 +53,7 @@ (if (= (first args) :let) `(let ~(second args) ~(apply do-m* (nnext args)))) - (if (= (name (second args)) "<-") + (if (every? #(% (second args)) [symbol? #(= (name %) "<-")]) (let [[var <- val & others] args] `(bind ~val (fn [~var] -- cgit v1.2.3