123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- ;; Copyright 2013 Google Inc.
- ;;
- ;; Licensed under the Apache License, Version 2.0 (the "License");
- ;; you may not use this file except in compliance with the License.
- ;; You may obtain a copy of the License at
- ;;
- ;; http://www.apache.org/licenses/LICENSE-2.0
- ;;
- ;; Unless required by applicable law or agreed to in writing, software
- ;; distributed under the License is distributed on an "AS IS" BASIS,
- ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ;; See the License for the specific language governing permissions and
- ;; limitations under the License.
- (in-package :cl-user)
- ;; lisp-unit defines the modules for loading / executing koans
- (load "lisp-unit.lsp")
- (defpackage :lisp-koans
- (:use :common-lisp)
- (:use :lisp-unit)
- (:use :sb-ext))
- (in-package :lisp-koans)
- ;; .koans file controls which files in *koan-dir-name* are loaded as
- ;; koans to complete
- (defvar *koan-dir-name* "koans")
- (with-open-file (in #P".koans")
- (with-standard-io-syntax
- (defvar *all-koans-groups* (read in))))
- ;; set *print-koan-progress* to t to list all completed koans before summary
- (defvar *print-koan-progress* t)
- ;; debug-print directives
- (defvar *dp-loading* nil)
- ;; Global state used to hold results of loading and processing koans
- (defvar *n-total-koans* 0)
- (defvar *collected-results* nil)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Functions for loading koans ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defun load-koan-group-named (koan-group-name)
- ;; Creates a package for the koan-group based on koan-group-name.
- ;; Loads a lisp file at *koan-dir-name* / koan-group-name .lsp
- ;; Adds all the koans from that file to the package.
- (let ((koan-file-name (concatenate 'string (string-downcase (string koan-group-name)) ".lsp")))
- (if *dp-loading* (format t "start loading ~A ~%" koan-file-name))
- (in-package :lisp-koans)
- (make-package koan-group-name
- :use '(:common-lisp :lisp-unit :sb-ext))
- (setf *package* (find-package koan-group-name))
- (load (concatenate 'string *koan-dir-name* "/" koan-file-name))
- (incf *n-total-koans* (length (list-tests)))
- (in-package :lisp-koans)
- (if *dp-loading* (format t "done loading ~A ~%" koan-file-name))))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Functions for executing koans ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defun run-koan-group-named (koan-group-name)
- ;; Executes the koan group, using run-koans defined in lisp-unit
- ;; returning a test-results object.
- (if *dp-loading* (format t "start running ~A ~%" koan-group-name))
- (run-koans koan-group-name))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Functions for printing progress ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defun print-one-koan-status (k-result)
- (let ((koan-name (first k-result))
- (all-pass-p (every
- #'(lambda (x) (equalp :pass x))
- (second k-result))))
- (if all-pass-p
- (format t "~A has expanded your awareness.~%" koan-name)
- (format t "~A has damaged your karma.~%" koan-name))))
- (defun print-koan-group-progress (kg-name kg-results)
- (format t "~%Thinking about ~A~%" kg-name)
- (dolist (k-result (reverse kg-results))
- (format t " ")
- (print-one-koan-status k-result))
- (format t "~%"))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Functions for processing results ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defun any-assert-non-pass-p ()
- (dolist (k-group-result *collected-results*)
- (dolist (koan-result (second k-group-result))
- (dolist (one-assert (second koan-result))
- (if (not (equal one-assert :pass))
- (return-from any-assert-non-pass-p one-assert)))))
- nil)
- (defun get-error-filename (collected-results)
- (first (first (last collected-results))))
- (defun get-error-koan-name (collected-results)
- (first (first (second (first (last (last collected-results)))))))
- (defun get-error-koan-status (collected-results)
- (second (first (second (first (last (last collected-results)))))))
- (defun koan-status-message (koan-status)
- (if (find :incomplete koan-status)
- (return-from koan-status-message
- " A koan is incomplete.~%"))
- (if (find :fail koan-status)
- (return-from koan-status-message
- " A koan is incorrect.~%"))
- (if (find :error koan-status)
- (return-from koan-status-message
- " A koan threw an error.~%"))
- (format t " last koan status: ~A~%" koan-status)
- "")
- (defun print-next-suggestion-message ()
- (let ((filename (get-error-filename *collected-results*))
- (koan-name (get-error-koan-name *collected-results*))
- (koan-status (get-error-koan-status *collected-results*)))
- (format t "You have not yet reached enlightenment ...~%")
- (format t (koan-status-message koan-status))
- (format t "~%")
- (format t "Please meditate on the following code:~%")
- (format t " File \"~A/~A.lsp\"~%" *koan-dir-name* (string-downcase filename))
- (format t " Koan \"~A\"~%" koan-name)
- (format t " Current koan assert status is \"~A\"~%" (reverse koan-status))))
- (defun print-completion-message ()
- (format t "That was the last one, well done!~%")
- (format t "If you want more, take a look at extra-credit.lsp~%"))
- (defun n-completed-koans (collected-results)
- (loop for kg in collected-results
- sum (length (second kg)) into partial-sum
- finally (return partial-sum)))
- (defun all-asserts-passed-in-koan-p (koan-result)
- (equal
- (length (second koan-result))
- (count :pass (second koan-result))))
- (defun n-passed-koans-in-group (kg)
- (loop for k in (second kg)
- counting (all-asserts-passed-in-koan-p k) into partial-sum
- finally (return partial-sum)))
- (defun n-passed-koans-overall (collected-results)
- (loop for kg in collected-results
- sum (n-passed-koans-in-group kg) into partial-sum
- finally (return partial-sum)))
- (defun print-progress-message ()
- (format t "You are now ~A/~A koans and ~A/~A lessons away from reaching enlightenment~%~%"
- (n-passed-koans-overall *collected-results*)
- *n-total-koans*
- (- (length *collected-results*) 1)
- (length *all-koans-groups*)))
- ;;;;;;;;;;
- ;; Main ;;
- ;;;;;;;;;;
- ;; Load all the koans before testing any, and
- ;; count how many total koans there are.
- (loop for koan-group-name in *all-koans-groups*
- do
- (load-koan-group-named koan-group-name))
- ;; Run through the koans until reaching the end condition.
- ;; Store the results in *collected-results*
- (setf *collected-results*
- (loop for koan-group-name in *all-koans-groups*
- for kg-results = (run-koan-group-named koan-group-name)
- collect (list koan-group-name kg-results)
- do (if *print-koan-progress*
- (print-koan-group-progress koan-group-name kg-results))
- ;; *proceed-after-failure* is defined in lisp-unit
- until (and (not *proceed-after-failure*) (any-non-pass-p kg-results))))
- ;; Output advice to the learner
- (if (any-assert-non-pass-p)
- (progn
- (print-next-suggestion-message)
- (format t "~%")
- (print-progress-message))
- (print-completion-message))
|