forked from google/lisp-koans
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lisp-koans.lisp
133 lines (109 loc) · 5.07 KB
/
lisp-koans.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
;;; 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.
(defpackage #:lisp-koans.core
(:use #:common-lisp
#:lisp-koans.test)
(:export #:main))
(in-package :lisp-koans.core)
(defvar *all-koan-groups*
(with-open-file (in #p".koans")
(with-standard-io-syntax (read in))))
(defvar *collected-results* nil)
;;; Functions for loading koans
(defun package-name-from-group-name (group-name)
(format nil "LISP-KOANS.KOANS.~A" group-name))
(defun load-koan-group-named (dirname koan-group-name)
(let* ((koan-name (string-downcase (string koan-group-name)))
(koan-file-name (concatenate 'string koan-name ".lisp"))
(koan-package-name (package-name-from-group-name koan-group-name)))
(unless (find-package koan-package-name)
(make-package koan-package-name
:use '(#:common-lisp #:lisp-koans.test)))
(let ((*package* (find-package koan-package-name)))
(load (concatenate 'string dirname "/" koan-file-name)))))
(defun load-all-koans (dirname)
(loop for koan-group-name in *all-koan-groups*
do (load-koan-group-named dirname koan-group-name)))
;;; Functions for executing koans
(defun execute-koans ()
(loop for koan-group-name in *all-koan-groups*
for package-name = (package-name-from-group-name koan-group-name)
for kg-results = (run-koans package-name)
collect (list koan-group-name kg-results) into results
do (print-koan-group-progress koan-group-name kg-results)
while (every (lambda (x) (eq x :pass)) (second (first kg-results)))
finally (setf *collected-results* results)))
;;; Functions for printing progress
(defun print-koan-group-progress (name results)
(format t "~%Thinking about ~A~%" name)
(dolist (result (reverse results))
(destructuring-bind (test-name results) result
(let ((format-control (if (every (lambda (x) (equalp :pass x)) results)
" [32m~A has expanded your awareness.~%[0m"
" [31m~A requires more meditation.~%[0m")))
(format t format-control test-name)))))
;;; Functions for processing results
(defun n-passed-koans-overall (collected-results)
(flet ((all-asserts-passed-in-koan-p (result)
(every (lambda (x) (eq :pass x)) (second result))))
(loop for kg in collected-results
sum (count-if #'all-asserts-passed-in-koan-p (second kg)))))
(defun any-assert-non-pass-p ()
(dolist (k-group-result *collected-results*)
(dolist (result (second k-group-result))
(dolist (one-assert (second result))
(when (not (equal one-assert :pass))
(return-from any-assert-non-pass-p one-assert))))))
;;; Functions for printing results
(defun koan-status-message (koan-status)
(cond ((find :incomplete koan-status) "[1m[33mA koan is incomplete.[0m")
((find :fail koan-status) "[1m[31mA koan is incorrect.[0m")
((find :error koan-status) "[1m[31mA koan signaled an error.[0m")
(t (format nil "[1mLast koan status: ~A.[0m" koan-status))))
(defun print-next-suggestion-message (dirname)
(let ((filename (caar (last *collected-results*)))
(koan-name (caaadr (car (last (last *collected-results*)))))
(koan-status (reverse (cadaar (cdar (last (last *collected-results*)))))))
(format t "~&You have not yet reached enlightenment.
~A
[1mPlease meditate on the following code:[0m
File \"~A/~(~A~).lisp\"
Koan \"~A\"
Current koan assert status is \"~A\"~%~%"
(koan-status-message koan-status) dirname filename koan-name koan-status)))
(defun print-completion-message ()
(format t "
*********************************************************
That was the last one, well done! ENLIGHTENMENT IS YOURS!
*********************************************************
If you demand greater challenge, take a look at extra-credit.lisp
Or, let the student become the teacher:
Write and submit your own improvements to https://github.com/google/lisp-koans!~%
"))
(defun print-progress-message ()
(format t "You are now ~A/~A koans and ~A/~A lessons closer to reaching enlightenment.~%~%"
(n-passed-koans-overall *collected-results*)
(test-total-count)
(1- (length *collected-results*))
(length *all-koan-groups*)))
(defun output-advice (dirname)
(cond ((any-assert-non-pass-p)
(print-next-suggestion-message dirname)
(print-progress-message))
(t (print-completion-message))))
;;; Main
(defun main (&optional (dirname "koans"))
(load-all-koans dirname)
(execute-koans)
(output-advice dirname))