Turn tests into examples that can both be tested and turned into docs

master
Magnar Sveen 14 years ago
parent 754dd0d1fb
commit ec27b47648
  1. 61
      bang.el
  2. 7
      create-docs.sh
  3. 144
      docs.md
  4. 31
      examples-to-docs.el
  5. 19
      examples-to-tests.el
  6. 69
      examples.el
  7. 2
      run-tests.sh
  8. 94
      tests.el

@ -27,7 +27,12 @@
(defalias '!map 'mapcar)
(defun !map (fn list)
"Returns a new list consisting of the result of applying FN to the items in list."
(mapcar fn list))
(defmacro !!map (form list)
"Anaphoric form of `!map'."
`(!map (lambda (it) ,form) ,list))
(defun !reduce-from (fn initial-value list)
@ -36,16 +41,13 @@ first item in LIST, then applying FN to that result and the 2nd
item, etc. If LIST contains no items, returns INITIAL-VALUE and
FN is not called."
(let ((acc initial-value))
(while list
(setq acc (funcall fn acc (car list)))
(setq list (cdr list)))
acc))
(while list
(setq acc (funcall fn acc (car list)))
(setq list (cdr list)))
acc))
(defmacro !!reduce-from (form initial-value list)
"Anaphoric form of `!reduce'. Returns the result of applying
FORM to INITIAL-VALUE and the first item in LIST, then applying
FORM to that result and the 2nd item, etc. If INITIAL-VALUE
contains no items, returns INITIAL-VALUE and FORM is not called."
"Anaphoric form of `!reduce-from'."
`(let ((!--list ,list)
(!--acc ,initial-value))
(while !--list
@ -66,12 +68,7 @@ LIST has only 1 item, it is returned and FN is not called."
(funcall fn)))
(defmacro !!reduce (form list)
"Returns the result of applying FORM to the first 2 items in LIST,
then applying FORM to that result and the 3rd item, etc. If
LIST contains no items, FORM must accept no arguments as
well, and reduce returns the result of calling FORM with no
arguments. If LIST has only 1 item, it is returned and FORM
is not called."
"Anaphoric form of `!reduce'."
(if (eval list)
`(!!reduce-from ,form ,(car (eval list)) ',(cdr (eval list)))
`(let (acc it) ,form)))
@ -86,7 +83,7 @@ is not called."
(nreverse result)))
(defmacro !!filter (form list)
"Returns a new list of the items in LIST for which FORM returns a non-nil value."
"Anaphoric form of `!filter'."
`(let ((!--list ,list)
(!--result '()))
(while !--list
@ -101,7 +98,7 @@ is not called."
(!!filter (not (funcall fn it)) list))
(defmacro !!remove (form list)
"Returns a new list of the items in LIST for which FORM returns nil."
"Anaphoric form of `!remove'."
`(!!filter (not ,form) ,list))
(defun !concat (&rest lists)
@ -115,11 +112,15 @@ Thus function FN should return a collection."
(apply '!concat (!map fn list)))
(defmacro !!mapcat (form list)
"Returns the result of applying concat to the result of applying map to FORM and LIST.
Thus function FORM should return a collection."
"Anaphoric form of `!mapcat'."
`(apply '!concat (!!map ,form ,list)))
(defalias '!partial 'apply-partially)
(defun !partial (fn &rest args)
"Takes a function FN and fewer than the normal arguments to FN,
and returns a fn that takes a variable number of additional ARGS.
When called, the returned function calls FN with args +
additional args."
(apply 'apply-partially fn args))
(defun !uniq (list)
"Return a new list with all duplicates removed.
@ -143,16 +144,18 @@ or with `!compare-fn' if that's non-nil."
"Return whether LIST contains ELEMENT.
The test for equality is done with `equal',
or with `!compare-fn' if that's non-nil."
(cond
((null !compare-fn) (member element list))
((eq !compare-fn 'eq) (memq element list))
((eq !compare-fn 'eql) (memql element list))
(t
(let ((lst list))
(while (and lst
(not (funcall !compare-fn element (car lst))))
(setq lst (cdr lst)))
lst))))
(not
(null
(cond
((null !compare-fn) (member element list))
((eq !compare-fn 'eq) (memq element list))
((eq !compare-fn 'eql) (memql element list))
(t
(let ((lst list))
(while (and lst
(not (funcall !compare-fn element (car lst))))
(setq lst (cdr lst)))
lst))))))
(defvar !compare-fn nil
"Tests for equality use this function or `equal' if this is nil.

@ -0,0 +1,7 @@
#!/usr/bin/env bash
if [ -z "$EMACS" ] ; then
EMACS="emacs"
fi
$EMACS -batch -l examples-to-docs.el -l bang.el -l examples.el -f create-docs-file

@ -0,0 +1,144 @@
## !map `(fn list)`
Returns a new list consisting of the result of applying FN to the items in list.
```cl
(!map (lambda (num) (* num num)) (quote (1 2 3 4))) ;; => (quote (1 4 9 16))
(!map (quote square) (quote (1 2 3 4))) ;; => (quote (1 4 9 16))
(!!map (* it it) (quote (1 2 3 4))) ;; => (quote (1 4 9 16))
```
## !reduce-from `(fn initial-value list)`
Returns the result of applying FN to INITIAL-VALUE and the
first item in LIST, then applying FN to that result and the 2nd
item, etc. If LIST contains no items, returns INITIAL-VALUE and
FN is not called.
```cl
(!reduce-from (quote +) 7 (quote nil)) ;; => 7
(!reduce-from (quote +) 7 (quote (1))) ;; => 8
(!reduce-from (quote +) 7 (quote (1 2))) ;; => 10
(!!reduce-from (+ acc it) 7 (quote (1 2 3))) ;; => 13
```
## !reduce `(fn list)`
Returns the result of applying FN to the first 2 items in LIST,
then applying FN to that result and the 3rd item, etc. If LIST
contains no items, FN must accept no arguments as well, and
reduce returns the result of calling FN with no arguments. If
LIST has only 1 item, it is returned and FN is not called.
```cl
(!reduce (quote +) (quote nil)) ;; => 0
(!reduce (quote +) (quote (1))) ;; => 1
(!reduce (quote +) (quote (1 2))) ;; => 3
(!reduce (lambda (memo item) (format %s-%s memo item)) (quote (1 2 3))) ;; => 1-2-3
(!!reduce (format %s-%s acc it) (quote (1 2 3))) ;; => 1-2-3
(!!reduce (format %s-%s acc it) (quote nil)) ;; => nil-nil
```
## !filter `(fn list)`
Returns a new list of the items in LIST for which FN returns a non-nil value.
```cl
(!filter (lambda (num) (= 0 (% num 2))) (quote (1 2 3 4))) ;; => (quote (2 4))
(!filter (quote even?) (quote (1 2 3 4))) ;; => (quote (2 4))
(!!filter (= 0 (% it 2)) (quote (1 2 3 4))) ;; => (quote (2 4))
```
## !remove `(fn list)`
Returns a new list of the items in LIST for which FN returns nil.
```cl
(!remove (lambda (num) (= 0 (% num 2))) (quote (1 2 3 4))) ;; => (quote (1 3))
(!remove (quote even?) (quote (1 2 3 4))) ;; => (quote (1 3))
(!!remove (= 0 (% it 2)) (quote (1 2 3 4))) ;; => (quote (1 3))
```
## !concat `(&rest lists)`
Returns a new list with the concatenation of the elements in
the supplied LISTS.
```cl
(!concat) ;; => nil
(!concat (quote (1))) ;; => (quote (1))
(!concat (quote (1)) (quote (2))) ;; => (quote (1 2))
(!concat (quote (1)) (quote (2 3)) (quote (4))) ;; => (quote (1 2 3 4))
```
## !mapcat `(fn list)`
Returns the result of applying concat to the result of applying map to FN and LIST.
Thus function FN should return a collection.
```cl
(!mapcat (quote list) (quote (1 2 3))) ;; => (quote (1 2 3))
(!mapcat (lambda (item) (list 0 item)) (quote (1 2 3))) ;; => (quote (0 1 0 2 0 3))
(!!mapcat (list 0 it) (quote (1 2 3))) ;; => (quote (0 1 0 2 0 3))
```
## !partial `(fn &rest args)`
Takes a function FN and fewer than the normal arguments to FN,
and returns a fn that takes a variable number of additional ARGS.
When called, the returned function calls FN with args +
additional args.
```cl
(funcall (!partial (quote +) 5) 3) ;; => 8
(funcall (!partial (quote +) 5 2) 3) ;; => 10
```
## !difference `(list list2)`
Return a new list with only the members of LIST that are not in LIST2.
The test for equality is done with `equal',
or with `!compare-fn' if that's non-nil.
```cl
(!difference (quote nil) (quote nil)) ;; => (quote nil)
(!difference (quote (1 2 3)) (quote (4 5 6))) ;; => (quote (1 2 3))
(!difference (quote (1 2 3 4)) (quote (3 4 5 6))) ;; => (quote (1 2))
```
## !intersection `(list list2)`
Return a new list containing only the elements that are members of both LIST and LIST2.
The test for equality is done with `equal',
or with `!compare-fn' if that's non-nil.
```cl
(!intersection (quote nil) (quote nil)) ;; => (quote nil)
(!intersection (quote (1 2 3)) (quote (4 5 6))) ;; => (quote nil)
(!intersection (quote (1 2 3 4)) (quote (3 4 5 6))) ;; => (quote (3 4))
```
## !uniq `(list)`
Return a new list with all duplicates removed.
The test for equality is done with `equal',
or with `!compare-fn' if that's non-nil.
```cl
(!uniq (quote nil)) ;; => (quote nil)
(!uniq (quote (1 2 2 4))) ;; => (quote (1 2 4))
```
## !contains? `(list element)`
Return whether LIST contains ELEMENT.
The test for equality is done with `equal',
or with `!compare-fn' if that's non-nil.
```cl
(!contains? (quote (1 2 3)) 1) ;; => t
(!contains? (quote (1 2 3)) 2) ;; => t
(!contains? (quote nil) (quote nil)) ;; => nil
(!contains? (quote nil) 1) ;; => nil
(!contains? (quote (1 2 4)) 3) ;; => nil
```

@ -0,0 +1,31 @@
(defvar functions '())
(defun example-to-string (example)
(let ((actual (car example))
(expected (cadr (cdr example))))
(format "%s ;; => %s" actual expected)))
(defun examples-to-strings (examples)
(let (result)
(while examples
(setq result (cons (example-to-string examples) result))
(setq examples (cddr (cdr examples))))
(nreverse result)))
(defmacro defexamples (cmd &rest examples)
`(add-to-list 'functions (list
',cmd ;; command name
(cadr (symbol-function ',cmd)) ;; signature
(car (cddr (symbol-function ',cmd))) ;; docstring
(examples-to-strings ',examples)))) ;; examples
(defun function-to-md (function)
(let ((command-name (car function))
(signature (cadr function))
(docstring (cadr (cdr function)))
(examples (mapconcat 'identity (cadr (cddr function)) "\n")))
(format "## %s `%s`\n\n%s\n\n```cl\n%s\n```\n" command-name signature docstring examples)))
(defun create-docs-file ()
(with-temp-file "./docs.md"
(insert (mapconcat 'function-to-md (nreverse functions) "\n"))))

@ -0,0 +1,19 @@
(require 'ert)
(defun examples-to-should-1 (examples)
(let ((actual (car examples))
(expected (cadr (cdr examples))))
`(should (equal ,actual ,expected))))
(defun examples-to-should (examples)
(let (result)
(while examples
(setq result (cons (examples-to-should-1 examples) result))
(setq examples (cddr (cdr examples))))
(nreverse result)))
(defmacro defexamples (cmd &rest examples)
`(ert-deftest ,cmd ()
,@(examples-to-should examples)))
(provide 'examples-to-tests)

@ -0,0 +1,69 @@
(require 'bang)
(defun even? (num) (= 0 (% num 2)))
(defun square (num) (* num num))
(defexamples !map
(!map (lambda (num) (* num num)) '(1 2 3 4)) => '(1 4 9 16)
(!map 'square '(1 2 3 4)) => '(1 4 9 16)
(!!map (* it it) '(1 2 3 4)) => '(1 4 9 16))
(defexamples !reduce-from
(!reduce-from '+ 7 '()) => 7
(!reduce-from '+ 7 '(1)) => 8
(!reduce-from '+ 7 '(1 2)) => 10
(!!reduce-from (+ acc it) 7 '(1 2 3)) => 13)
(defexamples !reduce
(!reduce '+ '()) => 0
(!reduce '+ '(1)) => 1
(!reduce '+ '(1 2)) => 3
(!reduce (lambda (memo item) (format "%s-%s" memo item)) '(1 2 3)) => "1-2-3"
(!!reduce (format "%s-%s" acc it) '(1 2 3)) => "1-2-3"
(!!reduce (format "%s-%s" acc it) '()) => "nil-nil")
(defexamples !filter
(!filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) => '(2 4)
(!filter 'even? '(1 2 3 4)) => '(2 4)
(!!filter (= 0 (% it 2)) '(1 2 3 4)) => '(2 4))
(defexamples !remove
(!remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) => '(1 3)
(!remove 'even? '(1 2 3 4)) => '(1 3)
(!!remove (= 0 (% it 2)) '(1 2 3 4)) => '(1 3))
(defexamples !concat
(!concat) => nil
(!concat '(1)) => '(1)
(!concat '(1) '(2)) => '(1 2)
(!concat '(1) '(2 3) '(4)) => '(1 2 3 4))
(defexamples !mapcat
(!mapcat 'list '(1 2 3)) => '(1 2 3)
(!mapcat (lambda (item) (list 0 item)) '(1 2 3)) => '(0 1 0 2 0 3)
(!!mapcat (list 0 it) '(1 2 3)) => '(0 1 0 2 0 3))
(defexamples !partial
(funcall (!partial '+ 5) 3) => 8
(funcall (!partial '+ 5 2) 3) => 10)
(defexamples !difference
(!difference '() '()) => '()
(!difference '(1 2 3) '(4 5 6)) => '(1 2 3)
(!difference '(1 2 3 4) '(3 4 5 6)) => '(1 2))
(defexamples !intersection
(!intersection '() '()) => '()
(!intersection '(1 2 3) '(4 5 6)) => '()
(!intersection '(1 2 3 4) '(3 4 5 6)) => '(3 4))
(defexamples !uniq
(!uniq '()) => '()
(!uniq '(1 2 2 4)) => '(1 2 4))
(defexamples !contains?
(!contains? '(1 2 3) 1) => t
(!contains? '(1 2 3) 2) => t
(!contains? '() '()) => nil
(!contains? '() 1) => nil
(!contains? '(1 2 4) 3) => nil)

@ -4,4 +4,4 @@ if [ -z "$EMACS" ] ; then
EMACS="emacs"
fi
$EMACS -batch -l ert.el -l bang.el -l tests.el -f ert-run-tests-batch-and-exit
$EMACS -batch -l ert.el -l examples-to-tests.el -l bang.el -l examples.el -f ert-run-tests-batch-and-exit

@ -1,94 +0,0 @@
(require 'ert)
(require 'bang)
(defun even? (num) (= 0 (% num 2)))
(defun square (num) (* num num))
(ert-deftest map ()
"`!map' returns a new list with the results of calling the function on each element."
(should (equal (!map (lambda (num) (* num num)) '(1 2 3 4)) '(1 4 9 16)))
(should (equal (!map 'square '(1 2 3 4)) '(1 4 9 16)))
(should (equal (!!map (* it it) '(1 2 3 4)) '(1 4 9 16)))
)
(ert-deftest reduce-from ()
"`!reduce-from' takes a list and an initial value, and applies the function over them to create one result"
(should (equal (!reduce '+ '()) 0))
(should (equal (!reduce '+ '(1)) 1))
(should (equal (!reduce '+ '(1 2)) 3))
(should (equal (!reduce-from '+ 7 '()) 7))
(should (equal (!reduce-from '+ 7 '(1)) 8))
(should (equal (!reduce-from '+ 7 '(1 2)) 10))
(should (equal (!!reduce-from (+ acc it) 7 '(1 2 3)) 13))
)
(ert-deftest reduce ()
"`!reduce' takes a list and applies the function over the elements to create one result"
(should (equal (!reduce (lambda (memo item) (format "%s-%s" memo item)) '(1 2 3)) "1-2-3"))
(should (equal (!!reduce (format "%s-%s" acc it) '(1 2 3)) "1-2-3"))
(should (equal (!!reduce (format "%s-%s" acc it) '()) "nil-nil"))
)
(ert-deftest filter ()
"`!filter' returns a new list of only those elements where the predicate was non-nil."
(should (equal (!filter (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) '(2 4)))
(should (equal (!filter 'even? '(1 2 3 4)) '(2 4)))
(should (equal (!!filter (= 0 (% it 2)) '(1 2 3 4)) '(2 4)))
)
(ert-deftest remove ()
"`!remove' returns a new list of only those elements where the predicate was nil."
(should (equal (!remove (lambda (num) (= 0 (% num 2))) '(1 2 3 4)) '(1 3)))
(should (equal (!remove 'even? '(1 2 3 4)) '(1 3)))
(should (equal (!!remove (= 0 (% it 2)) '(1 2 3 4)) '(1 3)))
)
(ert-deftest concat ()
"`!concat' returns the concatenation of the elements in the supplied lists"
(should (equal (!concat) nil))
(should (equal (!concat '(1)) '(1)))
(should (equal (!concat '(1) '(2)) '(1 2)))
(should (equal (!concat '(1) '(2 3) '(4)) '(1 2 3 4)))
)
(ert-deftest mapcat ()
"`!mapcat' applies the function to all elements of the list and then concatenates the result"
(should (equal (!mapcat 'list '(1 2 3)) '(1 2 3)))
(should (equal (!mapcat (lambda (item) (list 0 item)) '(1 2 3)) '(0 1 0 2 0 3)))
(should (equal (!!mapcat (list 0 it) '(1 2 3)) '(0 1 0 2 0 3)))
)
(ert-deftest partial ()
"`!partial' returns a function like fn where the first arguments are filled in"
(should (equal (funcall (!partial '+ 5) 3) 8))
(should (equal (funcall (!partial '+ 5 2) 3) 10))
)
(ert-deftest difference ()
"`!difference' returns a new list of only elements in list1 that are not in list2."
(should (equal (!difference '() '()) '()))
(should (equal (!difference '(1 2 3) '(4 5 6)) '(1 2 3)))
(should (equal (!difference '(1 2 3 4) '(3 4 5 6)) '(1 2)))
)
(ert-deftest intersection ()
"`!intersection' returns a new list of only elements that are in both given lists."
(should (equal (!intersection '() '()) '()))
(should (equal (!intersection '(1 2 3) '(4 5 6)) '()))
(should (equal (!intersection '(1 2 3 4) '(3 4 5 6)) '(3 4)))
)
(ert-deftest uniq ()
"`!uniq' returns a new list of only unique elements."
(should (equal (!uniq '()) '()))
(should (equal (!uniq '(1 2 2 4)) '(1 2 4)))
)
(ert-deftest contains? ()
"`!contains?' returns t if the list contains the element."
(should (!contains? '(1 2 3) 1))
(should (!contains? '(1 2 3) 2))
(should (not (!contains? '() '())))
(should (not (!contains? '() 1)))
(should (not (!contains? '(1 2 4) 3)))
)
Loading…
Cancel
Save