Skip to content

Commit 25395d5

Browse files
committed
feat: ensure capture all instances of rule env and rule form meta
1 parent 29bc7ec commit 25395d5

File tree

5 files changed

+38
-42
lines changed

5 files changed

+38
-42
lines changed

clj-kondo/clj-kondo.exports/clara/rules/hooks/clara_rules.clj_kondo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@
410410
(api/token-node 'def)
411411
var-name
412412
(api/vector-node
413-
children)))
413+
children)))
414414
merge {:clj-kondo/ignore [:clojure-lsp/unused-public-var]})]
415415
{:node new-node}))
416416

src/main/clojure/clara/rules.clj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,19 +256,23 @@
256256
See the [rule authoring documentation](http://www.clara-rules.org/docs/rules/) for details."
257257
[rule-name & body]
258258
(let [doc (if (string? (first body)) (first body) nil)
259-
rule (dsl/build-rule rule-name body (meta &form)) ;;; Full rule LHS + RHS
260-
rule-action (dsl/build-rule-action rule-name body (meta &form)) ;;; Only the RHS
259+
rule (dsl/build-rule rule-name body &env (meta &form)) ;;; Full rule LHS + RHS
260+
rule-action (dsl/build-rule-action rule-name body &env (meta &form)) ;;; Only the RHS
261261
rule-node (com/build-rule-node rule-action) ;;; The Node of the RHS
262262
{:keys [bindings production]} rule-node
263263
rule-handler (com/compile-action-handler rule-name bindings
264264
(:rhs production)
265265
(:env production))
266+
[rule-args & rule-body] (drop 2 rule-handler)
266267
name-with-meta (vary-meta rule-name assoc :rule true :doc doc)
267268
handler-name (symbol (name (ns-name *ns*)) (name rule-name))] ;;; The compiled RHS
268269
`(defn ~name-with-meta
269270
([]
270271
(assoc ~rule :handler '~handler-name))
271-
(~@(drop 2 rule-handler)))))
272+
([~@(take 1 rule-args)]
273+
(~rule-name '?__token__ {}))
274+
([~@rule-args]
275+
~@rule-body))))
272276

273277
(defmacro defquery
274278
"Defines a query and stores it in the given var. For instance, a simple query that accepts no
@@ -283,7 +287,7 @@
283287
[name & body]
284288
(let [doc (if (string? (first body)) (first body) nil)]
285289
`(def ~(vary-meta name assoc :query true :doc doc)
286-
~(dsl/build-query name body (meta &form)))))
290+
~(dsl/build-query name body &env (meta &form)))))
287291

288292
(defmacro defhierarchy
289293
"Defines a hierarchy and stores it in the given var. For instance, a simple hierarchy that adds

src/main/clojure/clara/rules/compiler.clj

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -396,16 +396,17 @@
396396
binding-keys)
397397

398398
;; The destructured environment, if any.
399-
destructured-env (if (> (count env) 0)
400-
{:keys (mapv #(symbol (name %)) (keys env))}
401-
'?__env__)]
402-
`(fn ~action-name [~'?__token__ ~destructured-env]
399+
destructured-env (if (pos? (count env))
400+
{:keys (mapv (comp symbol name) (keys env)) :as '?__env__}
401+
'?__env__)
402+
destructured-bindings (if (pos? (count token-binding-keys))
403+
{{:keys (mapv (comp symbol name) token-binding-keys)} :bindings
404+
:as '?__token__}
405+
'?__token__)]
406+
`(fn ~action-name [~destructured-bindings ~destructured-env]
403407
;; similar to test nodes, nothing in the contract of an RHS enforces that bound variables must be used.
404408
;; similarly we will not bind anything in this event, and thus the let block would be superfluous.
405-
~(if (seq token-binding-keys)
406-
`(let [{:keys [~@(map (comp symbol name) token-binding-keys)]} (:bindings ~'?__token__)]
407-
~rhs)
408-
rhs))))
409+
~rhs)))
409410

410411
(defn compile-action
411412
"Compile the right-hand-side action of a rule, returning a function to execute it."

src/main/clojure/clara/rules/dsl.clj

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,7 @@
220220
(defn parse-rule*
221221
"Creates a rule from the DSL syntax using the given environment map. *ns*
222222
should be bound to the namespace the rule is meant to be defined in."
223-
([lhs rhs properties env]
224-
(parse-rule* lhs rhs properties env {}))
225-
([lhs rhs properties env rule-meta]
223+
([lhs rhs properties rule-env rule-meta]
226224
(let [conditions (into [] (for [expr lhs]
227225
(parse-expression expr rule-meta)))
228226

@@ -235,7 +233,7 @@
235233
assoc :file *file*))}
236234

237235
symbols (set (filter symbol? (com/flatten-expression (concat lhs rhs))))
238-
matching-env (into {} (for [sym (keys env)
236+
matching-env (into {} (for [sym (keys rule-env)
239237
:when (symbols sym)]
240238
[(keyword (name sym)) sym]))]
241239

@@ -245,14 +243,12 @@
245243
(seq properties) (assoc :props properties)
246244

247245
;; Add the environment, if given.
248-
(seq env) (assoc :env matching-env)))))
246+
(seq rule-env) (assoc :env matching-env)))))
249247

250248
(defn parse-rule-action*
251249
"Creates a rule action from the DSL syntax using the given environment map. *ns*
252250
should be bound to the namespace the rule is meant to be defined in."
253-
([lhs rhs properties env]
254-
(parse-rule-action* lhs rhs properties env {}))
255-
([lhs rhs properties env rule-meta]
251+
([lhs rhs properties rule-env rule-meta]
256252
(let [conditions (into [] (for [expr lhs]
257253
(parse-expression expr rule-meta)))
258254

@@ -262,7 +258,7 @@
262258
:rhs (vary-meta rhs assoc :file *file*)}
263259

264260
symbols (set (filter symbol? (com/flatten-expression (concat lhs rhs))))
265-
matching-env (into {} (for [sym (keys env)
261+
matching-env (into {} (for [sym (keys rule-env)
266262
:when (symbols sym)]
267263
[(keyword (name sym)) sym]))]
268264

@@ -272,13 +268,11 @@
272268
(seq properties) (assoc :props properties)
273269

274270
;; Add the environment, if given.
275-
(seq env) (assoc :env matching-env)))))
271+
(seq rule-env) (assoc :env matching-env)))))
276272

277273
(defn parse-query*
278274
"Creates a query from the DSL syntax using the given environment map."
279-
([params lhs env]
280-
(parse-query* params lhs env {}))
281-
([params lhs env query-meta]
275+
([params lhs query-env query-meta]
282276
(let [conditions (into [] (for [expr lhs]
283277
(parse-expression expr query-meta)))
284278

@@ -288,19 +282,19 @@
288282

289283
symbols (set (filter symbol? (com/flatten-expression lhs)))
290284
matching-env (into {}
291-
(for [sym (keys env)
285+
(for [sym (keys query-env)
292286
:when (symbols sym)]
293287
[(keyword (name sym)) sym]))]
294288

295289
(cond-> query
296-
(seq env) (assoc :env matching-env)))))
290+
(seq query-env) (assoc :env matching-env)))))
297291

298292
(defmacro parse-rule
299293
"Macro used to dynamically create a new rule using the DSL syntax."
300294
([lhs rhs]
301-
(parse-rule* lhs rhs nil &env))
295+
(parse-rule* lhs rhs nil &env (meta &form)))
302296
([lhs rhs properties]
303-
(parse-rule* lhs rhs properties &env)))
297+
(parse-rule* lhs rhs properties &env (meta &form))))
304298

305299
;;; added to clojure.core in 1.9
306300
(defn- qualified-keyword?
@@ -315,44 +309,41 @@
315309

316310
(defn build-rule
317311
"Function used to parse and build a rule using the DSL syntax."
318-
([name body] (build-rule name body {}))
319-
([name body form-meta]
312+
([name body rule-env rule-meta]
320313
(let [doc (if (string? (first body)) (first body) nil)
321314
body (if doc (rest body) body)
322315
properties (if (map? (first body)) (first body) nil)
323316
definition (if properties (rest body) body)
324317
{:keys [lhs rhs]} (split-lhs-rhs definition)]
325-
(cond-> (parse-rule* lhs rhs properties {} form-meta)
318+
(cond-> (parse-rule* lhs rhs properties rule-env rule-meta)
326319

327320
name (assoc :name (production-name name))
328321
doc (assoc :doc doc)))))
329322

330323
(defn build-rule-action
331324
"Function used to parse and build a rule action using the DSL syntax."
332-
([name body] (build-rule-action name body {}))
333-
([name body form-meta]
325+
([name body rule-env rule-meta]
334326
(let [doc (if (string? (first body)) (first body) nil)
335327
body (if doc (rest body) body)
336328
properties (if (map? (first body)) (first body) nil)
337329
definition (if properties (rest body) body)
338330
{:keys [lhs rhs]} (split-lhs-rhs definition)]
339-
(cond-> (parse-rule-action* lhs rhs properties {} form-meta)
331+
(cond-> (parse-rule-action* lhs rhs properties rule-env rule-meta)
340332

341333
name (assoc :name (production-name name))
342334
doc (assoc :doc doc)))))
343335

344336
(defmacro parse-query
345337
"Macro used to dynamically create a new rule using the DSL syntax."
346338
[params lhs]
347-
(parse-query* params lhs &env))
339+
(parse-query* params lhs &env (meta &form)))
348340

349341
(defn build-query
350342
"Function used to parse and build a query using the DSL syntax."
351-
([name body] (build-query name body {}))
352-
([name body form-meta]
343+
([name body env form-meta]
353344
(let [doc (if (string? (first body)) (first body) nil)
354345
binding (if doc (second body) (first body))
355346
definition (if doc (drop 2 body) (rest body))]
356-
(cond-> (parse-query* binding definition {} form-meta)
347+
(cond-> (parse-query* binding definition env form-meta)
357348
name (assoc :name (production-name name))
358349
doc (assoc :doc doc)))))

src/main/clojure/clara/tools/testing_utils.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@
3131
(partition 2)
3232
(into {}
3333
(map (fn [[rule-name [lhs rhs props]]]
34-
[rule-name (assoc (dsl/parse-rule* lhs rhs props {}) :name (str rule-name))]))))
34+
[rule-name (assoc (dsl/parse-rule* lhs rhs props &env (meta &form)) :name (str rule-name))]))))
3535

3636
sym->query (->> params
3737
:queries
3838
(partition 2)
3939
(into {}
4040
(map (fn [[query-name [params lhs]]]
41-
[query-name (assoc (dsl/parse-query* params lhs {}) :name (str query-name))]))))
41+
[query-name (assoc (dsl/parse-query* params lhs &env (meta &form)) :name (str query-name))]))))
4242

4343
production-syms->productions (fn [p-syms]
4444
(map (fn [s]

0 commit comments

Comments
 (0)