我正在尝试评估字符串中的中缀表达式。
一些用于评估我的代码的示例数据:
(def data {:Location "US-NY-Location1"
:Priority 3})
(def qual "(Location = \"US\")")
我希望将qual
字符串转换为这种形式并由 clojure 评估:
(= (:Location data) "US")
我编写了以下宏来实现这一点:
(defmacro parse-qual [[data-key op val] data-map]
`(~op ((keyword (str (quote ~data-key))) ~data-map) ~val))
和一个辅助函数:
(defn eval-qual [qual-str data]
(eval `(parse-qual ~(clojure.edn/read-string qual-str) ~data)))
(eval-qual qual data)
为我提供了预期的结果
这是我编写的第一个宏,我仍在努力解决所有引用和取消引用的问题。
我想知道是否有更有效的方法来实现上述目标?(甚至根本不需要宏)
如何扩展宏以处理嵌套表达式。处理像
((Location = "US") or (Priority > 2))
. 任何指针将不胜感激。我目前正在尝试tree-seq
解决这个问题。qual
如果字符串无效,我怎样才能使它更健壮和更优雅。
我还编写了parse-qual
宏的第二次迭代,如下所示:
(defmacro parse-qual-2 [qual-str data-map]
(let [[data-key op val] (clojure.edn/read-string qual-str)]
`(~op ((keyword (str (quote ~data-key))) ~data-map) ~val)))
并macroexpand
抛出以下内容:
playfield.core> (macroexpand `(parse-qual-2 qual data))
java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.String
我不知道如何调试它!
一些额外的信息:
macroexpand
REPL 上的 parse-qual 给了我以下信息:
playfield.core> (macroexpand
`(parse-qual ~(clojure.edn/read-string qual) data))
(= ((clojure.core/keyword (clojure.core/str (quote Location))) playfield.core/data) "US")
谢谢@Alan Thompson,我能够将其编写为如下函数,这也允许评估嵌套表达式。
(def qual "(Location = \"US\")")
(def qual2 "((Location = \"US\") or (Priority > 2))")
(def qual3 "(Priority > 2)")
(def qual4 "(((Location = \"US\") or (Priority > 2)) and (Active = true))")
(defn eval-qual-2 [qual-str data]
(let [[l op r] (clojure.edn/read-string qual-str)]
(cond
(and (seq? l)
(seq? r)) (eval (list op (list eval-qual-2 (str l) data) (list eval-qual-2 (str r) data)))
(seq? l) (eval (list op (list eval-qual-2 (str l) data) r))
(seq? r) (eval (list op (list (keyword l) data) (list eval-qual-2 (str r) data)))
:else (eval (list op (list (keyword l) data) r)))))
(eval-qual-2 qual data) ; => false
(eval-qual-2 qual2 data) ; => true
(eval-qual-2 qual3 data) ; => true
(eval-qual-2 qual3 data) ; => true