[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/emacsql a164ecd9d3 374/427: Implement operator precedence
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/emacsql a164ecd9d3 374/427: Implement operator precedence handling |
Date: |
Tue, 13 Dec 2022 03:00:12 -0500 (EST) |
branch: elpa/emacsql
commit a164ecd9d31dbd45a646244906ac077d5a331419
Author: Ákos Kiss <ak@coram.pub>
Commit: Ákos Kiss <ak@coram.pub>
Implement operator precedence handling
---
emacsql-compiler.el | 181 ++++++++++++++++++++++++++++------------
tests/emacsql-compiler-tests.el | 17 +++-
2 files changed, 143 insertions(+), 55 deletions(-)
diff --git a/emacsql-compiler.el b/emacsql-compiler.el
index 5e7b9e0577..b1aae9ce63 100644
--- a/emacsql-compiler.el
+++ b/emacsql-compiler.el
@@ -261,7 +261,85 @@ Only use within `emacsql-with-params'!"
(vector (format "(%s)" (mapconcat #'scalar vector ", ")))
(otherwise (emacsql-error "Invalid vector: %S" vector)))))
-(defun emacsql--*expr (expr)
+(defmacro emacsql--generate-op-lookup (operator-name
+ operator-argument-count
+ operator-precedence-groups)
+ `(cond
+ ,@(cl-loop
+ for precedence-value from 1
+ for precedence-group in (reverse operator-precedence-groups)
+ append (cl-loop
+ for (op-name arity sql-format-string) in precedence-group
+ for sql-name = (upcase (symbol-name op-name))
+ for format-string = (or sql-format-string
+ (pcase arity
+ (:unary (cl-concatenate 'string
sql-name " %s"))
+ (:binary (cl-concatenate 'string
+ "%s "
sql-name " %s"))))
+ collect (list `(and (eq ,operator-name (quote ,op-name))
+ ,(if (eq arity :unary)
+ `(eql ,operator-argument-count 1)
+ `(>= ,operator-argument-count 2)))
+ `(list ,format-string ,arity ,precedence-value))))
+ (t (list nil nil nil))))
+
+
+(defun emacsql--get-op (op-name argument-count)
+ (emacsql--generate-op-lookup
+ op-name
+ argument-count
+ (((~ :unary "~%s"))
+ ((collate :binary))
+ ((|| :binary))
+ ((* :binary) (/ :binary) (% :binary))
+ ((+ :unary "+%s") (- :unary "-%s"))
+ ((+ :binary) (- :binary))
+ ((& :binary) (| :binary) (<< :binary) (>> :binary))
+ ((escape :unary "%s ESCAPE"))
+ ((< :binary) (<= :binary) (> :binary) (>= :binary))
+
+ (;;TODO? (between :binary) (not-between :binary)
+ (is :binary) (is-not :binary "%s IS NOT %s")
+ (match :binary) (not-match :binary "%s NOT MATCH %s")
+ (like :binary) (not-like :binary "%s NOT LIKE %s")
+ (in :binary) (not-in :binary "%s NOT IN %s")
+ (isnull :unary "%s ISNULL") (notnull :unary "%s NOTNULL")
+ (= :binary) (== :binary)
+ (!= :binary) (<> :binary)
+ (glob :binary) (not-glob :binary "%s NOT GLOB %s")
+ (regexp :binary) (not-regexp :binary "%s NOT REGEXP %s"))
+
+ ((not :unary))
+ ((and :binary))
+ ((or :binary)))))
+
+(defun emacsql--expand-format-string (op format-string arity argument-count)
+ (when format-string
+ (cond
+ ((and (eq arity :unary) (eql argument-count 1)) format-string)
+ ((and (eq arity :binary) (>= argument-count 2))
+ (cl-loop with acc = format-string
+ for i from 2 below argument-count
+ collect "%s" into rest-args
+ do (setf acc (apply #'format acc format-string rest-args))
+ finally (return acc)))
+ (t (emacsql-error "Wrong number of operands for %s" op)))))
+
+(defun emacsql--get-op-info (op argument-count parent-precedence-value)
+ (cl-destructuring-bind (format-string arity precedence-value)
+ (emacsql--get-op op argument-count)
+ (let ((expanded-format-string (emacsql--expand-format-string op
+ format-string
+ arity
+
argument-count)))
+ (cl-values (cond
+ ((null format-string) nil)
+ ((>= parent-precedence-value
+ precedence-value) (format "(%s)"
expanded-format-string))
+ (t expanded-format-string))
+ precedence-value))))
+
+(defun emacsql--*expr (expr &optional parent-precedence-value)
"Expand EXPR recursively."
(emacsql-with-params ""
(cond
@@ -269,59 +347,54 @@ Only use within `emacsql-with-params'!"
((vectorp expr) (svector expr))
((atom expr) (param expr))
((cl-destructuring-bind (op . args) expr
- (cl-flet ((recur (n) (combine (emacsql--*expr (nth n args))))
- (nops (op)
- (emacsql-error "Wrong number of operands for %s" op)))
- (cl-case op
- ;; Special cases <= >=
- ((<= >=)
- (cl-case (length args)
- (2 (format "%s %s %s" (recur 0) op (recur 1)))
- (3 (format "%s BETWEEN %s AND %s"
- (recur 1)
- (recur (if (eq op '>=) 2 0))
- (recur (if (eq op '>=) 0 2))))
- (otherwise (nops op))))
- ;; Special case -
- ((-)
- (cl-case (length args)
- (1 (format "-(%s)" (recur 0)))
- (2 (format "%s - %s" (recur 0) (recur 1)))
- (otherwise (nops op))))
- ;; Unary
- ((not)
- (format "NOT %s" (recur 0)))
- ((notnull)
- (format "%s NOTNULL" (recur 0)))
- ((isnull)
- (format "%s ISNULL" (recur 0)))
- ;; Ordering
- ((asc desc)
- (format "%s %s" (recur 0) (upcase (symbol-name op))))
- ;; Special case quote
- ((quote) (let ((arg (nth 0 args)))
- (if (stringp arg)
- (raw arg)
- (scalar arg))))
- ;; Special case funcall
- ((funcall)
- (format "%s(%s)" (recur 0)
- (cond
- ((and (= 2 (length args))
- (eq '* (nth 1 args)))
- "*")
- ((and (= 3 (length args))
- (eq :distinct (nth 1 args))
- (format "DISTINCT %s" (recur 2))))
- ((mapconcat
- #'recur (cl-loop for i from 1 below (length args)
- collect i)
- ", ")))))
- ;; Guess
- (otherwise
- (mapconcat
- #'recur (cl-loop for i from 0 below (length args) collect i)
- (format " %s " (upcase (symbol-name op))))))))))))
+ (cl-multiple-value-bind (format-string precedence-value)
+ (emacsql--get-op-info op
+ (length args)
+ (or parent-precedence-value 0))
+ (cl-flet ((recur (n) (combine (emacsql--*expr (nth n args)
+ (or precedence-value
0))))
+ (nops (op)
+ (emacsql-error "Wrong number of operands for %s"
op)))
+ (cl-case op
+ ;; Special cases <= >=
+ ((<= >=)
+ (cl-case (length args)
+ (2 (format format-string (recur 0) (recur 1)))
+ (3 (format "%s BETWEEN %s AND %s"
+ (recur 1)
+ (recur (if (eq op '>=) 2 0))
+ (recur (if (eq op '>=) 0 2))))
+ (otherwise (nops op))))
+ ;; Ordering
+ ((asc desc)
+ (format "%s %s" (recur 0) (upcase (symbol-name op))))
+ ;; Special case quote
+ ((quote) (let ((arg (nth 0 args)))
+ (if (stringp arg)
+ (raw arg)
+ (scalar arg))))
+ ;; Special case funcall
+ ((funcall)
+ (format "%s(%s)" (recur 0)
+ (cond
+ ((and (= 2 (length args))
+ (eq '* (nth 1 args)))
+ "*")
+ ((and (= 3 (length args))
+ (eq :distinct (nth 1 args))
+ (format "DISTINCT %s" (recur 2))))
+ ((mapconcat
+ #'recur (cl-loop for i from 1 below (length args)
+ collect i)
+ ", ")))))
+ ;; Guess
+ (otherwise
+ (let ((arg-indices (cl-loop for i from 0 below (length args)
collect i)))
+ (if format-string
+ (apply #'format format-string (mapcar #'recur
arg-indices))
+ (mapconcat
+ #'recur (cl-loop for i from 0 below (length args) collect
i)
+ (format " %s " (upcase (symbol-name op)))))))))))))))
(defun emacsql--*idents (idents)
"Read in a vector of IDENTS identifiers, or just an single identifier."
diff --git a/tests/emacsql-compiler-tests.el b/tests/emacsql-compiler-tests.el
index cb92ee04d6..79ec6b41e6 100644
--- a/tests/emacsql-compiler-tests.el
+++ b/tests/emacsql-compiler-tests.el
@@ -159,7 +159,7 @@
([:order-by [$i1]] '(bar)
"ORDER BY bar;")
([:order-by (- foo)] '()
- "ORDER BY -(foo);")
+ "ORDER BY -foo;")
([:order-by [(asc a) (desc (/ b 2))]] '()
"ORDER BY a ASC, b / 2 DESC;")))
@@ -192,6 +192,8 @@
([:where (and $i1 $i2 $i3)] '(a b c)
"WHERE a AND b AND c;")
([:where (is foo (not nil))] '()
+ "WHERE foo IS (NOT NULL);")
+ ([:where (is-not foo nil)] '()
"WHERE foo IS NOT NULL;")
([:where (= attrib :name)] '()
"WHERE attrib = ':name';")))
@@ -229,6 +231,19 @@
([:select (funcall count :distinct x)] '()
"SELECT count(DISTINCT x);")))
+(ert-deftest emacsql-precedence ()
+ (emacsql-tests-with-queries
+ ([:select (<< (not (is x nil)) 4)] '()
+ "SELECT (NOT x IS NULL) << 4;")
+ ([:select (* 3 (+ (/ 14 2) (- 5 3)))] '()
+ "SELECT 3 * (14 / 2 + (5 - 3));")
+ ([:select (- (|| (~ x) y))] '()
+ "SELECT -~x || y;")
+ ([:select (funcall length (|| (* x x) (* y y) (* z z)))] '()
+ "SELECT length((x * x) || (y * y) || (z * z));")
+ ([:select (and (+ (<= x y) 1) (>= y x))] '()
+ "SELECT (x <= y) + 1 AND y >= x;")))
+
(provide 'emacsql-compiler-tests)
;;; emacsql-tests.el ends here
- [nongnu] elpa/emacsql 3b70e8f5dd 366/427: Add support for NUL characters in strings (fixes #42), (continued)
- [nongnu] elpa/emacsql 3b70e8f5dd 366/427: Add support for NUL characters in strings (fixes #42), ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 75ac0448a5 364/427: Add support for DISTINCT in aggregate functions (#41), ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 8c46fb2c1e 286/427: Drop argument count check since it's wrong., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 641338533c 331/427: Just build SQLite on first connection., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 56a2882936 333/427: Update README for Melpa changes., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 1b37570bf5 352/427: Fix up the Makefile., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 2e9e2d6ba1 390/427: Fix SQL truncated when print-level or print-length are changed, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 50b139443b 414/427: make: Fix compiling and cleaning tests, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 4fe4413994 420/427: Re-indent some data so that machine and human can agree, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 62d3915737 363/427: Update to SQLite 3.22.0 and remove download system (#40), ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql a164ecd9d3 374/427: Implement operator precedence handling,
ELPA Syncer <=
- [nongnu] elpa/emacsql 214b46f547 372/427: Update documentation for building SQLite backend (#46), ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 5f30787890 375/427: Add precedence handling for "between" syntax, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 60b99760c5 371/427: Extend the compiler search to a configurable list (#45), ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 373975cbcc 387/427: Remove types from cl-defgeneric arguments, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 9dca599616 383/427: Fix whitespace, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 2fa32d2ab7 395/427: Create new log buffer if existing one isn't live anymore, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 374726060d 385/427: Fix buffer struct allocation size (fixes #91), ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql d5c37d905d 379/427: Put emacs-version in path for out-of-package binary, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 1ce92a6f4a 397/427: No longer require emacsql-psql in emacsql-pg, ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql d03c1a606f 392/427: Include optional .config.mk in Makefile, ELPA Syncer, 2022/12/13