Fork of https://github.com/google/lisp-koans so that I could go through them. THIS CONTAINS ANSWERS.

condition-handlers.lsp 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. ;; Copyright 2013 Google Inc.
  2. ;;
  3. ;; Licensed under the Apache License, Version 2.0 (the "License");
  4. ;; you may not use this file except in compliance with the License.
  5. ;; You may obtain a copy of the License at
  6. ;;
  7. ;; http://www.apache.org/licenses/LICENSE-2.0
  8. ;;
  9. ;; Unless required by applicable law or agreed to in writing, software
  10. ;; distributed under the License is distributed on an "AS IS" BASIS,
  11. ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. ;; See the License for the specific language governing permissions and
  13. ;; limitations under the License.
  14. "Common lisp conditions are much like CLOS classes.
  15. They are used to handle exceptional situations, and separate
  16. error handling code from normal operational code."
  17. (define-condition minimal-error-cond (error) ())
  18. (define-condition minimal-warning-cond (warning) ())
  19. (define-test test-conditions-derive-from-types
  20. "conditions inherit from base types"
  21. (true-or-false? t (typep (make-condition 'minimal-error-cond)
  22. 'minimal-error-cond))
  23. (true-or-false? t (typep (make-condition 'minimal-error-cond)
  24. 'error))
  25. (true-or-false? nil (typep (make-condition 'minimal-error-cond)
  26. 'warning))
  27. (true-or-false? t (typep (make-condition 'minimal-warning-cond)
  28. 'minimal-warning-cond))
  29. (true-or-false? nil (typep (make-condition 'minimal-warning-cond)
  30. 'error))
  31. (true-or-false? t (typep (make-condition 'minimal-warning-cond)
  32. 'warning)))
  33. ;; ----
  34. (define-condition my-div-by-zero-error (error) ())
  35. (define-condition my-non-number-args-error (error) ())
  36. (defun my-divide (num denom)
  37. (if (or (not (numberp num))
  38. (not (numberp denom)))
  39. (error 'my-non-number-args-error))
  40. (if (= 0 denom)
  41. (error 'my-div-by-zero-error)
  42. (/ num denom)))
  43. (define-test assert-error-thrown
  44. "assert-error checks that the right error is thrown"
  45. (assert-equal 3 (my-divide 6 2))
  46. (assert-error 'my-div-by-zero-error (my-divide 6 0))
  47. (assert-error 'my-non-number-args-error (my-divide 6 "zero")))
  48. (define-test test-handle-errors
  49. "the handler case is like a case statement which can capture errors
  50. and warnings, and execute appropriate forms in those conditions."
  51. (assert-equal 3
  52. (handler-case (my-divide 6 2)
  53. (my-div-by-zero-error (condition) :zero-div-error)
  54. (my-non-number-args-error (condition) :bad-args)))
  55. (assert-equal :zero-div-error
  56. (handler-case (my-divide 6 0)
  57. (my-div-by-zero-error (condition) :zero-div-error)
  58. (my-non-number-args-error (condition) :bad-args)))
  59. (assert-equal :bad-args
  60. (handler-case (my-divide 6 "woops")
  61. (my-div-by-zero-error (condition) :zero-div-error)
  62. (my-non-number-args-error (condition) :bad-args))))
  63. ;; ----
  64. "conditions, as CLOS objects, can have slots, some of which have special
  65. meanings. Common Lisp the Language Chapter 29 for more details.
  66. http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node312.html"
  67. ; This error condition is more than a signal. It carries data in two slots.
  68. ; the "original-line" slot and the "reason" slot. Both slots have a defined
  69. ; :initarg, which they will use to set themselves, if available. If not,
  70. ; they have a default form (:initform). They also both provide reader functions
  71. (define-condition logline-parse-error (error)
  72. ((original-line :initarg :original-line :initform "line not given" :reader original-line)
  73. (reason :initarg :reason :initform "no-reason" :reader reason)))
  74. ;; This function is designed to take loglines, and report what type they are.
  75. ;; It can also throw errors, like div-by-zero above, but the errors now carry some
  76. ;; additional information carried within the error itself.
  77. (defun get-logline-type (in-line)
  78. (if (not (typep in-line 'string))
  79. ;; if the in-line isn't a string, throw a logline-parse-error, and set the :reason and :original-line
  80. (error 'logline-parse-error :original-line in-line :reason :bad-type-reason))
  81. (cond
  82. ((equal 0 (search "TIMESTAMP" in-line)) :timestamp-logline-type)
  83. ((if (equal 0 (search "HTTP" in-line)) :http-logline-type))
  84. ;; if we don't recognize the first token, throw a logline-parse-error, and set the :reason and :original-line
  85. (t (error 'logline-parse-error :original-line in-line :reason :unknown-token-reason))))
  86. (define-test test-errors-have-slots
  87. (assert-equal :timestamp-logline-type
  88. (handler-case (get-logline-type "TIMESTAMP y13m01d03")
  89. (logline-parse-error (condition) (list (reason condition) (original-line condition)))))
  90. (assert-equal :http-logline-type
  91. (handler-case (get-logline-type "HTTP access 128.0.0.100")
  92. (logline-parse-error (condition) (list (reason condition) (original-line condition)))))
  93. (assert-equal
  94. (handler-case (get-logline-type "bogus logline")
  95. (logline-parse-error (condition) (list (reason condition) (original-line condition)))))
  96. (assert-equal ____
  97. (handler-case (get-logline-type 5555)
  98. (logline-parse-error (condition) (list (reason condition) (original-line condition))))))