From 85d66208b9d637dde7c6a71f8021bfb28b859ee6 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Fri, 21 Jul 2023 23:53:08 +0100 Subject: [PATCH] Much better error logging on rule execution. --- src/cljc/mw_engine/core.clj | 32 ++++++++++++++++++++++++-------- src/cljc/mw_engine/utils.clj | 8 ++++---- test/mw_engine/core_test.clj | 23 +++++++++-------------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/cljc/mw_engine/core.clj b/src/cljc/mw_engine/core.clj index ed7c48f..34e063b 100644 --- a/src/cljc/mw_engine/core.clj +++ b/src/cljc/mw_engine/core.clj @@ -58,7 +58,16 @@ ;; of the rule function itself. Yes, I know, this is obvious; but I'll confess ;; I didn't think of it before. [world cell rule] - (let [result (apply rule (list cell world))] + (let [result (try + (apply rule (list cell world)) + (catch Exception e + (l/warn e + (format + "Error in `apply-rule`: `%s` (%s) while executing rule `%s` on cell `%s`" + e + (.getMessage e) + (-> rule meta :lisp) + cell))))] (when result (merge result (meta rule))))) @@ -66,7 +75,18 @@ "Derive a cell from this `cell` of this `world` by applying these `rules`." [world cell rules] (or - (first (remove nil? (map #(apply-rule world cell %) rules))) + (first + (remove + nil? + (try + (map #(apply-rule world cell %) rules) + (catch Exception e + (l/warn e + (format + "Error in `apply-rules`: `%s` (%s) while executing rules on cell `%s`" + e + (.getMessage e) + cell)))))) cell)) (defn- transform-cell @@ -78,17 +98,13 @@ (apply-rules world cell rules) {:generation (+ (get-int-or-zero cell :generation) 1)}) (catch Exception e - (let [narrative (format "%s with message `%s` at generation %d when in state %s" + (let [narrative (format "Error in `transform-cell`: `%s` (%s) at generation %d when in state %s;" (-> e .getClass .getName) (.getMessage e) (:generation cell) (:state cell))] (l/warn e narrative) - (merge cell {:error narrative - :stacktrace ;; (remove #(starts-with? % "clojure.") - (map #(.toString %) (.getStackTrace e)) - ;;) - :state :error}))))) + cell)))) (defn transform-world "Return a world derived from this `world` by applying the production rules diff --git a/src/cljc/mw_engine/utils.clj b/src/cljc/mw_engine/utils.clj index c7e070b..684064e 100644 --- a/src/cljc/mw_engine/utils.clj +++ b/src/cljc/mw_engine/utils.clj @@ -138,16 +138,16 @@ :else 0)) (throw (Exception. "No map passed?")))) -(defn get-num +(defmacro get-num "Get the value of a property expected to be a number from a map; if not present (or not a number) return 0. * `map` a map; * `key` a symbol or keyword, presumed to be a key into the `map`." [map key] - (if (map? map) - (let [v (map key)] - (cond (and v (number? v)) v + `(if (map? ~map) + (let [~'v (~map ~key)] + (cond (and ~'v (number? ~'v)) ~'v :else 0)) (throw (Exception. "No map passed?")))) diff --git a/test/mw_engine/core_test.clj b/test/mw_engine/core_test.clj index 57a3d65..f6c056d 100644 --- a/test/mw_engine/core_test.clj +++ b/test/mw_engine/core_test.clj @@ -11,19 +11,13 @@ (cond (= (:state cell) :new) (merge cell {:state :grassland})))) - merge {:rule-type :production}) - pair (list afn "Test source")] + merge {:rule-type :production + :rule "Test source"})] (is (nil? (apply-rule nil {:state :water} afn)) "Rule shouldn't fire when state is wrong") - (is (nil? (apply-rule nil {:state :water} pair)) - "Rule shouldn't fire when state is wrong") (is (= (:state (apply-rule nil {:state :new} afn)) :grassland) - "Rule should fire when state is correct") - (is (= (:state (apply-rule nil {:state :new} pair)) :grassland) - "Rule should fire when state is correct") - (is (nil? (:rule (apply-rule nil {:state :new} afn))) - "No rule text if not provided") - (is (= (:rule (apply-rule nil {:state :new} pair)) "Test source") + "Rule should fire when state is correct") + (is (= (:rule (apply-rule nil {:state :new} afn)) "Test source") "Rule text cached on cell if provided")))) (deftest transform-world-tests @@ -34,10 +28,11 @@ (cond (= (:state cell) :new) (merge cell {:state :grassland})))) - merge {:rule-type :production}) + merge {:rule-type :production + :rule "Test source"}) world (make-world 3 3) - expected [[{:y 0, :state :grassland, :x 0, :generation 1} {:y 0, :state :grassland, :x 1, :generation 1} {:y 0, :state :grassland, :x 2, :generation 1}] - [{:y 1, :state :grassland, :x 0, :generation 1} {:y 1, :state :grassland, :x 1, :generation 1} {:y 1, :state :grassland, :x 2, :generation 1}] - [{:y 2, :state :grassland, :x 0, :generation 1} {:y 2, :state :grassland, :x 1, :generation 1} {:y 2, :state :grassland, :x 2, :generation 1}]] + expected [[[{:y 0, :state :grassland, :x 0, :rule-type :production, :rule "Test source", :generation 1} {:y 0, :state :grassland, :x 1, :rule-type :production, :rule "Test source", :generation 1} {:y 0, :state :grassland, :x 2, :rule-type :production, :rule "Test source", :generation 1}] + [{:y 1, :state :grassland, :x 0, :rule-type :production, :rule "Test source", :generation 1} {:y 1, :state :grassland, :x 1, :rule-type :production, :rule "Test source", :generation 1} {:y 1, :state :grassland, :x 2, :rule-type :production, :rule "Test source", :generation 1}] + [{:y 2, :state :grassland, :x 0, :rule-type :production, :rule "Test source", :generation 1} {:y 2, :state :grassland, :x 1, :rule-type :production, :rule "Test source", :generation 1} {:y 2, :state :grassland, :x 2, :rule-type :production, :rule "Test source", :generation 1}]]] actual (transform-world world (list afn))] (is (= actual expected))))) \ No newline at end of file