From b00a024e1c2f32c4fd2cf68018aa21c000bb4d81 Mon Sep 17 00:00:00 2001 From: Stephen A Date: Sat, 24 Aug 2013 10:59:36 -0500 Subject: [PATCH 1/8] Fixed tests after resolving git conflict issues --- test/errors/core_test.clj | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/errors/core_test.clj b/test/errors/core_test.clj index db338cc..c7d1042 100644 --- a/test/errors/core_test.clj +++ b/test/errors/core_test.clj @@ -1,17 +1,23 @@ (ns errors.core_test (:use [clojure.test] [errors.core] - [errors.dictionaries])) + [errors.dictionaries] + [errors.messageobj])) (def simple-non-match-exception (java.lang.Exception. "Test Message")) (def get-pretty-message (ns-resolve 'errors.core 'get-pretty-message)) (def class-cast-exception (java.lang.ClassCastException. "oneType cannot be cast to anotherType")) +(def bigint-illegal-arg-ex (java.lang.IllegalArgumentException. "contains? not supported on type: clojure.lang.BigInt")) +(def best-approximation (ns-resolve 'errors.dictionaries 'best-approximation)) (deftest test-best-approximation (is (= "unrecognized type oneType" (best-approximation "oneType"))) + (is (= "a number" (best-approximation "clojure.lang.BigInt"))) + ;(is (= "unrecognized type clojure.lang.LittleInt" (best-approximation "clojure.lang.LittleInt"))) This triggers a classNotFoundException since there are periods in the type name ) (deftest test-get-pretty-message - (is (= "Test Message" (get-pretty-message simple-non-match-exception))) - (is (= "Attempted to use unrecognized type oneType, but unrecognized type anotherType was expected." (get-pretty-message class-cast-exception))) + (is (= "Test Message" (get-all-text (get-pretty-message simple-non-match-exception)))) + (is (= "Attempted to use unrecognized type oneType, but unrecognized type anotherType was expected." (get-all-text (get-pretty-message class-cast-exception)))) + (is (= "Function contains? does not allow a number as an argument" (get-all-text (get-pretty-message bigint-illegal-arg-ex)))) ) \ No newline at end of file From bad51560a53b341957697e5055c284cd1e65ed7f Mon Sep 17 00:00:00 2001 From: Stephen A Date: Sat, 24 Aug 2013 11:06:34 -0500 Subject: [PATCH 2/8] Reformatted messageobj.clj for readability --- src/errors/messageobj.clj | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/errors/messageobj.clj b/src/errors/messageobj.clj index 86b17b8..7ce2479 100644 --- a/src/errors/messageobj.clj +++ b/src/errors/messageobj.clj @@ -1,5 +1,5 @@ (ns errors.messageobj) - ;(:refer corefn/core :only [add-fisrt add-last])) + ;(:refer corefn/core :only [add-fisrt add-last])) ;; Functions related to a message object. Message object ;; is a vector of parts of a message (in order). Each ;; part is a hash map that contains the message text :msg, @@ -8,40 +8,40 @@ ;; A message pre-object doesn't have :start (defn make-msg-preobj-hash - "creates a hash map for a msg pre-object out of a msg and style" - ([msg style] (let [m (str msg)] - {:msg m :stylekey style :length (count m)})) - ([msg] (let [m (str msg)] - {:msg m :stylekey :reg :length (count m)}))) + "creates a hash map for a msg pre-object out of a msg and style" + ([msg style] (let [m (str msg)] + {:msg m :stylekey style :length (count m)})) + ([msg] (let [m (str msg)] + {:msg m :stylekey :reg :length (count m)}))) (defn- make-msg-preobj-hashes-helper [messages result] - (if (empty? messages) result - (let [next (second messages)] - (if (keyword? next) (recur (rest (rest messages)) - (conj result (make-msg-preobj-hash (first messages) next))) - (recur (rest messages) - (conj result (make-msg-preobj-hash (first messages)))))))) + (if (empty? messages) result + (let [next (second messages)] + (if (keyword? next) (recur (rest (rest messages)) + (conj result (make-msg-preobj-hash (first messages) next))) + (recur (rest messages) + (conj result (make-msg-preobj-hash (first messages)))))))) (defn make-preobj-hashes [& args] - "creates a vector of hash maps out of a vector that are strings, possibly followed by optional keywords" - (make-msg-preobj-hashes-helper args [])) + "creates a vector of hash maps out of a vector that are strings, possibly followed by optional keywords" + (make-msg-preobj-hashes-helper args [])) -;(defn make-preobj-hashes [messages] -; "creates a vector of hash maps out of a vector of vectors of msg + optional style" -; ;; apply is needed since messages contains vectors of 1 or 2 elements -; (map #(apply make-msg-preobj-hash %) messages)) + ;(defn make-preobj-hashes [messages] + ; "creates a vector of hash maps out of a vector of vectors of msg + optional style" + ; ;; apply is needed since messages contains vectors of 1 or 2 elements + ; (map #(apply make-msg-preobj-hash %) messages)) (defn make-obj [pre-obj] ; pre-obj is a vector of hashmaps "fills in the starting points of objects in the hash maps" (loop [hashes pre-obj start 0 res []] (if (empty? hashes) res - (recur (rest hashes) - (+ start (:length (first hashes))) - (conj res (assoc (first hashes) :start start)))))) + (recur (rest hashes) + (+ start (:length (first hashes))) + (conj res (assoc (first hashes) :start start)))))) (defn get-all-text [msg-obj] - "concatenate all text from a message object into a string" - (reduce #(str %1 (:msg %2)) "" msg-obj)) + "concatenate all text from a message object into a string" + (reduce #(str %1 (:msg %2)) "" msg-obj)) (defn make-mock-preobj [matches] "creates a test message pre-obj. Used for testing so that things don't break" From a7abf1fdc295d5b1bf2ed65c358f3a151689f40f Mon Sep 17 00:00:00 2001 From: Stephen A Date: Sun, 25 Aug 2013 10:14:09 -0500 Subject: [PATCH 3/8] Made note of new error message for uneven parameters in binding vectors --- src/errors/dictionaries.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/errors/dictionaries.clj b/src/errors/dictionaries.clj index 7d3bf78..90abd5a 100755 --- a/src/errors/dictionaries.clj +++ b/src/errors/dictionaries.clj @@ -167,6 +167,8 @@ :make-preobj (fn [matches] (make-preobj-hashes "There is an unmatched parameter in declaration of " (nth matches 1) :arg))} {:class IllegalArgumentException + ; TODO New message: + ; (nth matches 1) "'s must be pairs, you passed an odd number of parameters to " (nth matches 1) " on line " (nth matches 3) " in the file " (nth matches 2) :match #"(.*) requires an even number of forms in binding vector in (.*):(.*)" :make-preobj (fn [matches] (make-preobj-hashes "A parameter for a " (nth matches 1) " is missing a binding on line " From 1207c644ce273100047984f276da7d8bdb80c596 Mon Sep 17 00:00:00 2001 From: Stephen A Date: Sun, 25 Aug 2013 10:51:18 -0500 Subject: [PATCH 4/8] Added test for uneven binding vector --- test/errors/core_test.clj | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/errors/core_test.clj b/test/errors/core_test.clj index c7d1042..3603937 100644 --- a/test/errors/core_test.clj +++ b/test/errors/core_test.clj @@ -9,6 +9,11 @@ (def class-cast-exception (java.lang.ClassCastException. "oneType cannot be cast to anotherType")) (def bigint-illegal-arg-ex (java.lang.IllegalArgumentException. "contains? not supported on type: clojure.lang.BigInt")) (def best-approximation (ns-resolve 'errors.dictionaries 'best-approximation)) +(def incorrect-let-exception (try (eval (read-string "(let [l 1 s] (println l))")) (catch Exception e e))) + +(defn get-pretty-message-string [e] + (-> e get-pretty-message get-all-text) + ) (deftest test-best-approximation (is (= "unrecognized type oneType" (best-approximation "oneType"))) @@ -17,7 +22,8 @@ ) (deftest test-get-pretty-message - (is (= "Test Message" (get-all-text (get-pretty-message simple-non-match-exception)))) - (is (= "Attempted to use unrecognized type oneType, but unrecognized type anotherType was expected." (get-all-text (get-pretty-message class-cast-exception)))) - (is (= "Function contains? does not allow a number as an argument" (get-all-text (get-pretty-message bigint-illegal-arg-ex)))) + (is (= "Test Message" (get-pretty-message-string simple-non-match-exception))) + (is (= "Attempted to use unrecognized type oneType, but unrecognized type anotherType was expected." (get-pretty-message-string class-cast-exception))) + (is (= "Function contains? does not allow a number as an argument" (get-pretty-message-string bigint-illegal-arg-ex))) + (is (= "" (get-pretty-message-string incorrect-let-exception))) ) \ No newline at end of file From b0ebef64e6cd97c0710d261c2b6b35b26f133f39 Mon Sep 17 00:00:00 2001 From: Stephen A Date: Mon, 26 Aug 2013 14:50:12 -0500 Subject: [PATCH 5/8] Removed unused imports from project file. --- project.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/project.clj b/project.clj index 3c1a8bb..81e13d0 100755 --- a/project.clj +++ b/project.clj @@ -4,7 +4,6 @@ [org.clojure/clojure "1.5.0"] [clj-stacktrace "0.2.5"] [org.clojure/core.incubator "0.1.2"] - ;[trammel "0.7.0"] - ;[org.clojure/core.contracts "0.0.4"] [seesaw "1.4.3"]] - :main intro.core) + :main intro.core + ) From fb26483aaa19a0cdf723127ccd5dbe99353e06b2 Mon Sep 17 00:00:00 2001 From: Stephen A Date: Mon, 26 Aug 2013 14:51:11 -0500 Subject: [PATCH 6/8] Started refactoring mostly in error.core. --- src/errors/core.clj | 48 ++++++++++++++++++++++++++++++++------- src/errors/messageobj.clj | 3 ++- test/errors/core_test.clj | 2 +- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/errors/core.clj b/src/errors/core.clj index 8a53fe1..1babe14 100644 --- a/src/errors/core.clj +++ b/src/errors/core.clj @@ -7,6 +7,7 @@ [seesaw.core])) ;;(def ignore-nses #"(clojure|java)\..*") +;; Ignore stack trace entries beginning with user, clojure, or java (def ignore-nses #"(user|clojure|java)\..*") (defn- first-match [e message] @@ -20,15 +21,46 @@ message (if m m "")] ; converting an empty message from nil to "" (if-let [entry (first-match e message)] ((:make-preobj entry) (re-matches (:match entry) message)) - (make-preobj-hashes message)))) + (make-preobj-hashes message)))) +;; Returns true if a :trace-elems element is meaningful to the student +(defn- is-meaningful-elem? [elem] + (and + (:clojure elem) + (not (re-matches ignore-nses (:ns elem)))) + ) + +;; Takes in a single :trace-elems entry and produces a string for +;; our filtered stacktrace +(defn- create-error-str [err-elem] + (str + "\t" + (:ns err-elem) + "/" + (:fn err-elem) + " (" + (:file err-elem) + " line " + (:line err-elem) + ")" + ) + ) + +;; Creates the pre-object from a filtered stack trace +(defn- create-pre-obj [errstrs] + (make-preobj-hashes (str "\nSequence of function calls:\n" (join "\n" errstrs)) :causes) + ) + +;; Creates the full error object that is to be displayed +(defn- create-error-obj [e preobj] + (make-obj (concat (make-preobj-hashes "ERROR: " :err) + (get-pretty-message e) + preobj))) ;; All together: (defn prettify-exception [e] - (let [info (stacktrace/parse-exception e) - cljerrs (filter #(and (:clojure %) (not (re-matches ignore-nses (:ns %)))) - (:trace-elems info)) - errstrs (map #(str "\t" (:ns %) "/" (:fn %) " (" (:file %) " line " (:line %) ")") cljerrs)] - (show-error (make-obj (concat (make-preobj-hashes "ERROR: " :err) (get-pretty-message e) - (make-preobj-hashes (str "\nSequence of function calls:\n" (join "\n" errstrs)) :causes))) - e))) + (let [preobj (->> e stacktrace/parse-exception + (#(filter is-meaningful-elem? (:trace-elems %))) + (map create-error-str) + (create-pre-obj))] + (show-error (create-error-obj e preobj) e))) diff --git a/src/errors/messageobj.clj b/src/errors/messageobj.clj index 7ce2479..5ef7f0a 100644 --- a/src/errors/messageobj.clj +++ b/src/errors/messageobj.clj @@ -1,5 +1,5 @@ (ns errors.messageobj) - ;(:refer corefn/core :only [add-fisrt add-last])) + ;; Functions related to a message object. Message object ;; is a vector of parts of a message (in order). Each ;; part is a hash map that contains the message text :msg, @@ -19,6 +19,7 @@ (let [next (second messages)] (if (keyword? next) (recur (rest (rest messages)) (conj result (make-msg-preobj-hash (first messages) next))) + ;ELSE CASE (recur (rest messages) (conj result (make-msg-preobj-hash (first messages)))))))) diff --git a/test/errors/core_test.clj b/test/errors/core_test.clj index 3603937..fee6eb3 100644 --- a/test/errors/core_test.clj +++ b/test/errors/core_test.clj @@ -25,5 +25,5 @@ (is (= "Test Message" (get-pretty-message-string simple-non-match-exception))) (is (= "Attempted to use unrecognized type oneType, but unrecognized type anotherType was expected." (get-pretty-message-string class-cast-exception))) (is (= "Function contains? does not allow a number as an argument" (get-pretty-message-string bigint-illegal-arg-ex))) - (is (= "" (get-pretty-message-string incorrect-let-exception))) + (is (= "A parameter for a let is missing a binding on line in the file errors.core_test" (get-pretty-message-string incorrect-let-exception))) ) \ No newline at end of file From 87267d01e9946f5336b574564eb0d5184fe598da Mon Sep 17 00:00:00 2001 From: Stephen A Date: Mon, 26 Aug 2013 20:51:51 -0500 Subject: [PATCH 7/8] Mostly finished with errors.core refactoring. Mostly readability changes at this point --- src/errors/core.clj | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/errors/core.clj b/src/errors/core.clj index 1babe14..5ee90f4 100644 --- a/src/errors/core.clj +++ b/src/errors/core.clj @@ -6,26 +6,27 @@ [errors.messageobj] [seesaw.core])) -;;(def ignore-nses #"(clojure|java)\..*") ;; Ignore stack trace entries beginning with user, clojure, or java (def ignore-nses #"(user|clojure|java)\..*") +;; Gets the first match out of the error dictionary +;; based on the exception class and the message (defn- first-match [e message] - (println (str (class e) " " message)) ; debugging print + ;(println (str (class e) " " message)) ; debugging print (first (filter #(and (instance? (:class %) e) (re-matches (:match %) message)) error-dictionary))) ;; Putting together a message (perhaps should be moved to errors.dictionaries? ) (defn- get-pretty-message [e] (let [m (.getMessage e) - message (if m m "")] ; converting an empty message from nil to "" - (if-let [entry (first-match e message)] - ((:make-preobj entry) (re-matches (:match entry) message)) - (make-preobj-hashes message)))) + message (if m m "")] ; converting an empty message from nil to "" + (if-let [entry (first-match e message)] + ((:make-preobj entry) (re-matches (:match entry) message)) + (make-preobj-hashes message)))) ;; Returns true if a :trace-elems element is meaningful to the student (defn- is-meaningful-elem? [elem] - (and + (and (:clojure elem) (not (re-matches ignore-nses (:ns elem)))) ) From a70be3b888e0f957a6e7fb5b72311ab08e92ed91 Mon Sep 17 00:00:00 2001 From: Stephen A Date: Sat, 31 Aug 2013 19:05:37 -0500 Subject: [PATCH 8/8] Some minor changes --- src/errors/core.clj | 10 +++++----- src/errors/dictionaries.clj | 7 ++++--- src/intro/student.clj | 5 +---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/errors/core.clj b/src/errors/core.clj index 5ee90f4..f8f19eb 100644 --- a/src/errors/core.clj +++ b/src/errors/core.clj @@ -60,8 +60,8 @@ ;; All together: (defn prettify-exception [e] - (let [preobj (->> e stacktrace/parse-exception - (#(filter is-meaningful-elem? (:trace-elems %))) - (map create-error-str) - (create-pre-obj))] - (show-error (create-error-obj e preobj) e))) + (let [preobj (->> e stacktrace/parse-exception + (#(filter is-meaningful-elem? (:trace-elems %))) + (map create-error-str) + (create-pre-obj))] + (show-error (create-error-obj e preobj) e))) \ No newline at end of file diff --git a/src/errors/dictionaries.clj b/src/errors/dictionaries.clj index 90abd5a..4b8a33d 100755 --- a/src/errors/dictionaries.clj +++ b/src/errors/dictionaries.clj @@ -223,7 +223,8 @@ :match #"Unsupported binding form: (.*)" :make-preobj (fn [matches] (make-preobj-hashes "You cannot use " (nth matches 1) :arg " as a variable."))} - ;; Compilation errors + ;; Compilation errors + ;; These next two exceptions about wrong # of args will only be thrown by macros NOT functions {:class clojure.lang.Compiler$CompilerException :match #"(.+): Too many arguments to (.+), compiling:(.+)" ;:replace "Compilation error: too many arguments to $2 while compiling $3" @@ -239,11 +240,11 @@ {:class clojure.lang.Compiler$CompilerException :match #"(.+): EOF while reading, starting at line (.+), compiling:(.+)" :replace "Compilation error: end of file, starting at line $2, while compiling $3.\nProbabbly a non-closing parentheses or bracket." - :make-preobj make-mock-preobj} + :make-preobj make-mock-preobj} ; TODO Make preobj function {:class clojure.lang.Compiler$CompilerException :match #"(.+): Unmatched delimiter: (.+), compiling:(.+)" ;:replace "Compilation error: a closing $2 without a matching opening one while compiling $3." - :make-preobj make-mock-preobj} + :make-preobj make-mock-preobj} ; TODO Make preobj function {:class clojure.lang.Compiler$CompilerException :match #"(.+): Unable to resolve symbol: (.+) in this context, compiling:\((.+)\)" ;:replace "Compilation error: name $2 is undefined in this context, while compiling $3." diff --git a/src/intro/student.clj b/src/intro/student.clj index 421eada..5ec738f 100644 --- a/src/intro/student.clj +++ b/src/intro/student.clj @@ -1,7 +1,4 @@ -(ns intro.student - (:use [corefns.core] - [seesaw.core] - [turtle.core])) +(ns intro.student) ;; Testing compilation errors