--- a/docs/01-installation.markdown Fri Nov 03 23:48:31 2017 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-Installation
-============
-
-Chancery is compatible with Quicklisp, but not *in* Quicklisp (yet?). You can
-clone the repository into your [Quicklisp local-projects][local] directory for
-now.
-
-[local]: https://www.quicklisp.org/beta/faq.html#local-project
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/01-usage.markdown Mon Dec 18 17:45:40 2017 -0500
@@ -0,0 +1,433 @@
+Usage
+=====
+
+Chancery is a library for procedurally generating text and data in Common
+Lisp. It's heavily inspired by [Tracery][], and is essentially just some Lispy
+syntactic sugar for writing [grammars][cfg].
+
+[Tracery]: http://www.crystalcodepalace.com/traceryTut.html
+[cfg]: https://en.wikipedia.org/wiki/Context-free_grammar
+
+[TOC]
+
+Rules
+-----
+
+Rules are the core construct Chancery uses to generate data, and can be created
+with `define-rule`:
+
+ :::lisp
+ (define-rule animal
+ "cat"
+ "dog"
+ "mouse")
+
+ (define-rule color
+ "black"
+ "white"
+ "brown"
+ "gray")
+
+Rules are compiled into vanilla Lisp functions, so you can call them like you
+would any other function.
+
+ :::lisp
+ (loop :repeat 10
+ :collect (list (color) (animal)))
+ ; =>
+ (("gray" "dog") ("white" "mouse") ("gray" "dog")
+ ("black" "mouse") ("gray" "cat") ("gray" "cat")
+ ("white" "mouse") ("white" "dog") ("gray" "mouse")
+ ("brown" "cat"))
+
+Basic rules select one of their body terms at random and evaluate it according
+to some special rules. Most kinds of objects (e.g. strings, keywords, numbers)
+evaluate to themselves, but there are a few exceptions.
+
+Symbols evaluate to `(funcall 'symbol)`, so rules can easily call other rules:
+
+ :::lisp
+ (define-rule cat-name
+ "fluffy"
+ "whiskers")
+
+ (define-rule dog-name
+ "spot"
+ "fido"
+ "lassie")
+
+ (define-rule animal-name
+ cat-name
+ dog-name)
+
+ (loop :repeat 10 :collect (animal-name))
+ ; =>
+ ("spot" "spot" "fido" "fluffy" "fido" "spot"
+ "fluffy" "spot" "spot" "lassie")
+
+Lists recursively evaluate their members and return the result as a fresh list:
+
+ :::lisp
+ (define-rule pet
+ (cat-name color :cat)
+ (dog-name color :dog))
+
+ (loop :repeat 5 :collect (pet))
+ ; =>
+ (("fluffy" "brown" :CAT) ("fluffy" "white" :CAT)
+ ("fido" "brown" :DOG) ("lassie" "white" :DOG)
+ ("fluffy" "black" :CAT))
+
+If you want to return a literal symbol or list from a rule you can `quote` it:
+
+ :::lisp
+ (define-rule foo
+ 'a
+ '(x y))
+
+ (loop :repeat 5 :collect (foo))
+ ; =>
+ (A (X Y) (X Y) A A)
+
+Text Generation
+---------------
+
+Grammars are often used to generate strings, and Chancery has a few extra bits
+of syntactic sugar to make it easy to generate readable text. `define-string`
+can be used to define a rule function that stringifies its result before
+returning it:
+
+ :::lisp
+ (define-string animal "cat" "dog" "mouse")
+ (define-string name "Alice" "Bob" "Carol")
+ (define-string place "mountain" "forest" "river")
+
+ (define-string story
+ (name "the" animal "went to the" place)
+ ("a friendly" animal "lived near a" place))
+
+ (loop :repeat 5 :collect (story))
+ ; =>
+ ; ("Alice the mouse went to the river"
+ ; "Bob the cat went to the forest"
+ ; "a friendly mouse lived near a river"
+ ; "a friendly cat lived near a forest"
+ ; "Bob the cat went to the river")
+
+`define-string` works the same way as `define-rule`, except that the evaluation
+of certain items is slightly different.
+
+Strings evaluate to themselves. `NIL` evaluates to an empty string. Other
+non-keyword symbols evaluate to procedure calls, as with `define-rule`.
+
+Lists evaluate their arguments and concatenate them with one `#\Space` between
+each. If you don't want a space between two items you can use the special
+symbol `:.` to suppress it:
+
+ :::lisp
+ (define-string foo "x" "y")
+
+ (define-string with-spaces
+ (foo foo foo))
+
+ (define-string without-spaces
+ (foo :. foo :. foo))
+
+ (with-spaces) ; => "x x y"
+ (without-spaces) ; => "xyx"
+
+The special form `(quote ...)` evaluates to its argument, *without* stringifying
+it. Note that this is one case where a `define-string` could return something
+that isn't a string, so use it with care.
+
+Everything else (except vectors, which we'll talk about shortly) evaluates to
+the result of calling `princ-to-string` on it.
+
+Distributions
+-------------
+
+By default each body term in a rule has an equal chance of being chosen.
+
+Chancery also includes support for weighting the terms so some will be chosen
+more often than others:
+
+ :::lisp
+ (define-rule (metal :distribution :weighted)
+ (10 :iron)
+ (5 :steel)
+ (2 :silver)
+ (1 :gold)
+ (1 :platinum))
+
+Weighting each term by hand can be tedious. Chancery can automatically
+calculate weights based on the order of the body terms according to [Zipf's
+law][zipf]:
+
+ :::lisp
+ (define-rule (armor-type :distribution :zipf)
+ :scale-mail
+ :chain-mail
+ :banded-mail
+ :plate-mail)
+
+The Zipf distribution can take an argument `exponent`, which is the exponent
+characterizing the distribution. The default is `1.0`, and larger exponents
+will result in the earlier terms being chosen more often:
+
+ :::lisp
+ (define-rule (foo :distribution :zipf)
+ :a :b :c :d :e)
+
+ (define-rule (bar :distribution (:zipf :exponent 3.0))
+ :a :b :c :d :e)
+
+ (define-rule (baz :distribution (:zipf :exponent 0.25))
+ :a :b :c :d :e)
+
+ (count :a (loop :repeat 1000 :collect (foo)))
+ ; => 413
+
+ (count :a (loop :repeat 1000 :collect (bar)))
+ ; => 831
+
+ (count :a (loop :repeat 1000 :collect (baz)))
+ ; => 257
+
+[zipf]: https://en.wikipedia.org/wiki/Zipf's_law
+
+Single Generation
+-----------------
+
+Sometimes it can be handy to evaluate a Chancery expression without defining an
+actual rule. The `generate` macro evaluates vanilla Chancery expressions, and
+`generate-string` evaluates Chancery string expressions:
+
+ :::lisp
+ (define-rule x 1 2 3)
+ (define-rule animal "cat" "dog" "mouse")
+
+ (generate :foo) ; => :FOO
+ (generate x) ; => 2
+ (generate (x x x)) ; => (2 2 1)
+
+ (generate-string :foo) ; => "FOO"
+ (generate-string x) ; => "1"
+ (generate-string ("fuzzy" animal)) ; => "fuzzy mouse"
+
+String Modifiers
+----------------
+
+Procedurally generating readable text can be tricky, so Chancery includes some
+syntax and a few helper functions to make your life easier.
+
+Inside a `define-string` vectors are evaluated specially. The first element of
+the vector is evaluated as usual in `define-string`. All remaining elements in
+the vector are treated as function names, and the result is threaded through
+each function in turn. Finally, the resulting value will be stringified. For
+example:
+
+ :::lisp
+ (define-rule animal "cat" "dog" "mouse")
+
+ (generate-string ("the" animal "ran"))
+ ; =>
+ ; "the cat ran"
+
+ (generate-string ("the" #(animal string-upcase) "ran"))
+ ; =>
+ ; "the MOUSE ran"
+
+Chancery defines a few helpful functions for generating English text:
+
+* `s` pluralizes its argument.
+* `a` adds an indefinite article ("a" or "an") to the front of its argument.
+* `cap` capitalizes the first letter of its argument.
+* `cap-all` capitalizes each word in its argument.)
+* `pos` makes its argument possessive by adding an apostrophe (and possibly an s).
+
+These are just normal Lisp functions that happen to be useful as modifiers:
+
+ :::lisp
+ (define-string animal "cat" "aardvark" "fly")
+
+ (loop :repeat 5
+ :collect (generate-string
+ ("a bunch of" #(animal s))))
+ ; =>
+ ; ("a bunch of flies" "a bunch of flies" "a bunch of aardvarks"
+ ; "a bunch of cats" "a bunch of aardvarks")
+
+ (loop :repeat 5
+ :collect (generate-string
+ (#(animal a cap) "is a good pet")))
+ ; =>
+ ; ("A cat is a good pet" "A fly is a good pet" "A fly is a good pet"
+ ; "A cat is a good pet" "An aardvark is a good pet")
+
+English is a messy language. Pull requests to improve these functions (or add
+more useful ones) are welcome.
+
+Evaluation
+----------
+
+Sometimes it can be useful to switch out of "Chancery evaluation" and back into
+normal Lisp evaluation. The `(eval ...)` special form can be used to do this in
+Chancery rules and string rules:
+
+ :::lisp
+ (define-rule treasure
+ :weapon
+ :armor
+ ((eval (random 100)) :gold))
+
+ (loop :repeat 5 :collect (treasure))
+ ; =>
+ ; ((93 :GOLD)
+ ; :WEAPON
+ ; (83 :GOLD)
+ ; :WEAPON
+ ; :ARMOR)
+
+ (define-string animal "cat" "dog")
+ (define-string story
+ ("The" animal "had" (eval (+ 2 (random 3))) "friends."))
+
+ (loop :repeat 5 :collect (story))
+ ; =>
+ ; ("The dog had 3 friends." "The cat had 2 friends." "The dog had 3 friends."
+ ; "The cat had 4 friends." "The dog had 2 friends.")
+
+You can think of `eval` and `generate` as two sides of the same coin, kind of
+like quasiquote's backquote and comma. `eval` flips from Chancery evaluation to
+Lisp evaluation, and `generate` flips in the other direction.
+
+Reader Macros
+-------------
+
+Chancery provides four reader macros you can use to make defining grammars
+even more concise. It uses [named-readtables][] to keep them safely stashed
+away unless you want them.
+
+The first reader macro is `[...]` to replace the standard vector `#(...)`
+syntax, to make string modifiers stand out more:
+
+ :::lisp
+ (named-readtables:in-readtable :chancery)
+
+ (define-string animal "cat" "dog")
+
+ (generate-string ("a few" [animal s]))
+ ; => "a few cats"
+
+The reader macro `!form` expands to `(eval form)`:
+
+ :::lisp
+ (named-readtables:in-readtable :chancery)
+
+ (define-rule treasure
+ :weapon
+ :armor
+ (!(random 100) :gold))
+
+ (loop :repeat 5 :collect (treasure))
+ ; =>
+ ; (:ARMOR (53 :GOLD) (49 :GOLD) :ARMOR :WEAPON)
+
+The reader macro `@form` expands to `(generate form)`, and `$form` expands to
+`(generate-string form)`. Note that none of th
+
+ :::lisp
+ (named-readtables:in-readtable :chancery)
+
+ (define-string animal "cat" "dog")
+
+ $("the" animal "ran")
+ ; => "the cat ran"
+
+Together these let you jump in and out of Chancery-style evaluation as needed:
+
+ :::lisp
+ (named-readtables:in-readtable :chancery)
+
+ (define-string animal "cat" "dog")
+
+ (define-string story
+ ("the" animal "went to sleep")
+ !(let ((pet (animal)))
+ $("the" !pet "was a good" !pet)))
+
+ (loop :repeat 4 :collect (story))
+ ; =>
+ ; ("the dog was a good dog" "the cat was a good cat"
+ ; "the dog was a good dog" "the cat went to sleep")
+
+This last example is a little tricky, but it helps to think of `$` and `@` as
+quasiquote's backquote, and `!` as comma. Flipping back and forth between Lisp
+and Chancery can let you define some really interesting grammars.
+
+
+
+
+
+[named-readtables]: https://common-lisp.net/project/named-readtables/
+
+Runtime Rule Creation
+---------------------
+
+All of the Chancery things we've been using so far have been macros. If you
+want to create rules at runtime you can use their function counterparts:
+
+* `create-rule` takes a list of expressions and options, and returns a rule
+ function.
+* `create-string` takes a list of expressions and options, and returns a string
+ function.
+* `invoke-generate` takes an expression and evaluates it.
+* `invoke-generate-string` takes an expression and evaluates it and stringifies
+ the result.
+
+For example:
+
+ (define-rule foo :a :b :c)
+ (foo)
+ ; versus
+ (funcall (create-rule (list :a :b :c)))
+
+ (define-string (bar :distribution :zipf)
+ ("one" foo)
+ ("two" [foo s]))
+ (bar)
+ ; versus
+ (funcall (create-string (list '("one" foo)
+ '("two" [foo s]))
+ :distribution :zipf))
+
+Tips
+----
+
+Chancery aims to be a very thin layer on top of Lisp. Chancery rules are just
+vanilla Lisp functions—you can `describe` them, `disassemble` them, `trace`
+them, `funcall` them, and anything else you might normally want to do with
+functions. Similarly, Chancery string modifiers are just unary functions—you
+can create your own, or even use built-in Lisp functions like `string-upcase`.
+
+Remember that you can flip back and forth between Chancery evaluation and normal
+Lisp evaluation. Sometimes it's easier to just do a quick `!(if foo then else)`
+than to make Yet Another Rule. Use your own judgement to determine when a rule
+is getting too hairy and needs to be split into simpler parts, just like you
+would for any other Lisp function.
+
+Examples
+--------
+
+If you want some less trivial examples than the ones seen here, you might want
+to take a look at some of the Twitter bots I've built with Chancery:
+
+* [@git\_commands](http://twitter.com/git_commands)
+ ([code](https://github.com/sjl/magitek/blob/master/src/robots/git-commands.lisp))
+ tweets procedurally-generated Git command summaries.
+* [@lisp\_talks](http://twitter.com/lisp_talks)
+ ([code](https://github.com/sjl/magitek/blob/master/src/robots/lisp-talks.lisp))
+ tweets random topics for Lisp talks.
+* [@rpg\_shopkeeper](http://twitter.com/rpg_shopkeeper)
+ ([code](https://github.com/sjl/magitek/blob/master/src/robots/rpg-shopkeeper.lisp))
+ generates random fantasy RPG items, and sells them for appropriate prices.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/02-reference.markdown Mon Dec 18 17:45:40 2017 -0500
@@ -0,0 +1,248 @@
+# API Reference
+
+The following is a list of all user-facing parts of Chancery.
+
+If there are backwards-incompatible changes to anything listed here, they will
+be noted in the changelog and the author will feel bad.
+
+Anything not listed here is subject to change at any time with no warning, so
+don't touch it.
+
+[TOC]
+
+## Package `CHANCERY`
+
+### `*RANDOM*` (variable)
+
+The random number generation function to use (default: `CL:RANDOM`).
+
+### `A` (function)
+
+ (A STRING)
+
+Add an indefinite article (a or an) to the front of `string`.
+
+### `CAP` (function)
+
+ (CAP STRING)
+
+Capitalize the first character of `string`.
+
+### `CAP-ALL` (function)
+
+ (CAP-ALL STRING)
+
+Capitalize each word of `string`.
+
+### `CREATE-RULE` (function)
+
+ (CREATE-RULE EXPRESSIONS &REST OPTIONS)
+
+Return a function that will return random elements of `expressions`.
+
+ `options` should be of the form:
+
+ (&key documentation (distribution :uniform) (arguments '()))
+
+ `documentation` will be used as a docstring for the resulting function.
+
+ `distribution` denotes the distribution of elements returned.
+
+ `arguments` is the arglist of the resulting function.
+
+ Examples:
+
+ (create-rule (list :blue :red :green))
+
+ (create-rule (list :copper :silver :gold :platinum)
+ :documentation "Return a random metal."
+ :distribution :zipf)
+
+ See the full documentation for more information.
+
+
+
+### `CREATE-STRING` (function)
+
+ (CREATE-STRING EXPRESSIONS &REST OPTIONS)
+
+Return a function that will return random stringified elements of `expressions`.
+
+ `options` should be of the form:
+
+ (&key documentation (distribution :uniform) (arguments '()))
+
+ `documentation` will be used as a docstring for the resulting function.
+
+ `distribution` denotes the distribution of elements returned.
+
+ `arguments` is the arglist of the resulting function.
+
+ Examples:
+
+ (create-string (list "white" "gray" "black"))
+
+ (create-string '((100 (color "cat"))
+ (100 (color "dog"))
+ (100 (color "dragon")))
+ :distribution :weighted)
+
+ See the full documentation for more information.
+
+
+
+### `DEFINE-RULE` (macro)
+
+ (DEFINE-RULE NAME-AND-OPTIONS &REST EXPRESSIONS)
+
+Define a function that will return random elements of `expressions`.
+
+ `name-and-options` should be of the form:
+
+ (name &key documentation (distribution :uniform) (arguments '()))
+
+ If no options are needed a bare symbol can be given.
+
+ `name` is the symbol under which the resulting function will be defined.
+
+ `documentation` will be used as a docstring for the resulting function.
+
+ `distribution` denotes the distribution of elements returned.
+
+ `arguments` is the arglist of the resulting function.
+
+ Examples:
+
+ (define-rule color
+ :blue
+ :green
+ :red)
+
+ (define-rule (metal :documentation "Return a random metal."
+ :distribution :zipf)
+ :copper
+ :silver
+ :gold
+ :platinum)
+
+ See the full documentation for more information.
+
+
+
+### `DEFINE-STRING` (macro)
+
+ (DEFINE-STRING NAME-AND-OPTIONS &REST EXPRESSIONS)
+
+Define a function that will return random stringified elements of `expressions`.
+
+ `name-and-options` should be of the form:
+
+ (name &key documentation (distribution :uniform) (arguments '()))
+
+ If no options are needed a bare symbol can be given.
+
+ `name` is the symbol under which the resulting function will be defined.
+
+ `documentation` will be used as a docstring for the resulting function.
+
+ `distribution` denotes the distribution of elements returned.
+
+ `arguments` is the arglist of the resulting function.
+
+ Examples:
+
+ (define-string color "white" "gray" "black")
+
+ (define-string (animal :distribution :weighted)
+ (100 (color "cat"))
+ (100 (color "dog"))
+ (100 (color "dragon")))
+
+ See the full documentation for more information.
+
+
+
+### `GENERATE` (macro)
+
+ (GENERATE EXPRESSION)
+
+Generate a single Chancery expression.
+
+ Example:
+
+ (define-rule x 1 2 3)
+
+ (generate (x x x))
+ ; => (1 3 1)
+
+
+
+### `GENERATE-STRING` (macro)
+
+ (GENERATE-STRING EXPRESSION)
+
+Generate and stringify a single Chancery string expression.
+
+ Example:
+
+ (define-string x 1 2 3)
+
+ (generate-string (x x x))
+ ; => "1 3 1"
+
+
+
+### `INVOKE-GENERATE` (function)
+
+ (INVOKE-GENERATE EXPRESSION)
+
+Generate a single Chancery expression.
+
+ THIS FUNCTION IS EXPERIMENTAL AND SUBJECT TO CHANGE IN THE FUTURE.
+
+ Because this is a function, not a macro, you'll need to do the quoting
+ yourself:
+
+ (define-rule x 1 2 3)
+
+ (generate (x x x))
+ ; => (1 3 3)
+
+ (invoke-generate '(x x x))
+ ; => (2 1 2)
+
+
+
+### `INVOKE-GENERATE-STRING` (function)
+
+ (INVOKE-GENERATE-STRING EXPRESSION)
+
+Generate and stringify a single Chancery expression.
+
+ THIS FUNCTION IS EXPERIMENTAL AND SUBJECT TO CHANGE IN THE FUTURE.
+
+ Because this is a function, not a macro, you'll need to do the quoting
+ yourself:
+
+ (define-string x 1 2 3)
+
+ (generate-string (x x x))
+ ; => "1 3 3"
+
+ (invoke-generate-string '(x x x))
+ ; => "2 1 2"
+
+
+
+### `POS` (function)
+
+ (POS STRING)
+
+Make `string` posessive by adding an apostrophe (and possibly an s).
+
+### `S` (function)
+
+ (S STRING)
+
+Pluralize `string`.
+
--- a/docs/02-usage.markdown Fri Nov 03 23:48:31 2017 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,433 +0,0 @@
-Usage
-=====
-
-Chancery is a library for procedurally generating text and data in Common
-Lisp. It's heavily inspired by [Tracery][], and is essentially just some Lispy
-syntactic sugar for writing [grammars][cfg].
-
-[Tracery]: http://www.crystalcodepalace.com/traceryTut.html
-[cfg]: https://en.wikipedia.org/wiki/Context-free_grammar
-
-[TOC]
-
-Rules
------
-
-Rules are the core construct Chancery uses to generate data, and can be created
-with `define-rule`:
-
- :::lisp
- (define-rule animal
- "cat"
- "dog"
- "mouse")
-
- (define-rule color
- "black"
- "white"
- "brown"
- "gray")
-
-Rules are compiled into vanilla Lisp functions, so you can call them like you
-would any other function.
-
- :::lisp
- (loop :repeat 10
- :collect (list (color) (animal)))
- ; =>
- (("gray" "dog") ("white" "mouse") ("gray" "dog")
- ("black" "mouse") ("gray" "cat") ("gray" "cat")
- ("white" "mouse") ("white" "dog") ("gray" "mouse")
- ("brown" "cat"))
-
-Basic rules select one of their body terms at random and evaluate it according
-to some special rules. Most kinds of objects (e.g. strings, keywords, numbers)
-evaluate to themselves, but there are a few exceptions.
-
-Symbols evaluate to `(funcall 'symbol)`, so rules can easily call other rules:
-
- :::lisp
- (define-rule cat-name
- "fluffy"
- "whiskers")
-
- (define-rule dog-name
- "spot"
- "fido"
- "lassie")
-
- (define-rule animal-name
- cat-name
- dog-name)
-
- (loop :repeat 10 :collect (animal-name))
- ; =>
- ("spot" "spot" "fido" "fluffy" "fido" "spot"
- "fluffy" "spot" "spot" "lassie")
-
-Lists recursively evaluate their members and return the result as a fresh list:
-
- :::lisp
- (define-rule pet
- (cat-name color :cat)
- (dog-name color :dog))
-
- (loop :repeat 5 :collect (pet))
- ; =>
- (("fluffy" "brown" :CAT) ("fluffy" "white" :CAT)
- ("fido" "brown" :DOG) ("lassie" "white" :DOG)
- ("fluffy" "black" :CAT))
-
-If you want to return a literal symbol or list from a rule you can `quote` it:
-
- :::lisp
- (define-rule foo
- 'a
- '(x y))
-
- (loop :repeat 5 :collect (foo))
- ; =>
- (A (X Y) (X Y) A A)
-
-Text Generation
----------------
-
-Grammars are often used to generate strings, and Chancery has a few extra bits
-of syntactic sugar to make it easy to generate readable text. `define-string`
-can be used to define a rule function that stringifies its result before
-returning it:
-
- :::lisp
- (define-string animal "cat" "dog" "mouse")
- (define-string name "Alice" "Bob" "Carol")
- (define-string place "mountain" "forest" "river")
-
- (define-string story
- (name "the" animal "went to the" place)
- ("a friendly" animal "lived near a" place))
-
- (loop :repeat 5 :collect (story))
- ; =>
- ; ("Alice the mouse went to the river"
- ; "Bob the cat went to the forest"
- ; "a friendly mouse lived near a river"
- ; "a friendly cat lived near a forest"
- ; "Bob the cat went to the river")
-
-`define-string` works the same way as `define-rule`, except that the evaluation
-of certain items is slightly different.
-
-Strings evaluate to themselves. `NIL` evaluates to an empty string. Other
-non-keyword symbols evaluate to procedure calls, as with `define-rule`.
-
-Lists evaluate their arguments and concatenate them with one `#\Space` between
-each. If you don't want a space between two items you can use the special
-symbol `:.` to suppress it:
-
- :::lisp
- (define-string foo "x" "y")
-
- (define-string with-spaces
- (foo foo foo))
-
- (define-string without-spaces
- (foo :. foo :. foo))
-
- (with-spaces) ; => "x x y"
- (without-spaces) ; => "xyx"
-
-The special form `(quote ...)` evaluates to its argument, *without* stringifying
-it. Note that this is one case where a `define-string` could return something
-that isn't a string, so use it with care.
-
-Everything else (except vectors, which we'll talk about shortly) evaluates to
-the result of calling `princ-to-string` on it.
-
-Distributions
--------------
-
-By default each body term in a rule has an equal chance of being chosen.
-
-Chancery also includes support for weighting the terms so some will be chosen
-more often than others:
-
- :::lisp
- (define-rule (metal :distribution :weighted)
- (10 :iron)
- (5 :steel)
- (2 :silver)
- (1 :gold)
- (1 :platinum))
-
-Weighting each term by hand can be tedious. Chancery can automatically
-calculate weights based on the order of the body terms according to [Zipf's
-law][zipf]:
-
- :::lisp
- (define-rule (armor-type :distribution :zipf)
- :scale-mail
- :chain-mail
- :banded-mail
- :plate-mail)
-
-The Zipf distribution can take an argument `exponent`, which is the exponent
-characterizing the distribution. The default is `1.0`, and larger exponents
-will result in the earlier terms being chosen more often:
-
- :::lisp
- (define-rule (foo :distribution :zipf)
- :a :b :c :d :e)
-
- (define-rule (bar :distribution (:zipf :exponent 3.0))
- :a :b :c :d :e)
-
- (define-rule (baz :distribution (:zipf :exponent 0.25))
- :a :b :c :d :e)
-
- (count :a (loop :repeat 1000 :collect (foo)))
- ; => 413
-
- (count :a (loop :repeat 1000 :collect (bar)))
- ; => 831
-
- (count :a (loop :repeat 1000 :collect (baz)))
- ; => 257
-
-[zipf]: https://en.wikipedia.org/wiki/Zipf's_law
-
-Single Generation
------------------
-
-Sometimes it can be handy to evaluate a Chancery expression without defining an
-actual rule. The `generate` macro evaluates vanilla Chancery expressions, and
-`generate-string` evaluates Chancery string expressions:
-
- :::lisp
- (define-rule x 1 2 3)
- (define-rule animal "cat" "dog" "mouse")
-
- (generate :foo) ; => :FOO
- (generate x) ; => 2
- (generate (x x x)) ; => (2 2 1)
-
- (generate-string :foo) ; => "FOO"
- (generate-string x) ; => "1"
- (generate-string ("fuzzy" animal)) ; => "fuzzy mouse"
-
-String Modifiers
-----------------
-
-Procedurally generating readable text can be tricky, so Chancery includes some
-syntax and a few helper functions to make your life easier.
-
-Inside a `define-string` vectors are evaluated specially. The first element of
-the vector is evaluated as usual in `define-string`. All remaining elements in
-the vector are treated as function names, and the result is threaded through
-each function in turn. Finally, the resulting value will be stringified. For
-example:
-
- :::lisp
- (define-rule animal "cat" "dog" "mouse")
-
- (generate-string ("the" animal "ran"))
- ; =>
- ; "the cat ran"
-
- (generate-string ("the" #(animal string-upcase) "ran"))
- ; =>
- ; "the MOUSE ran"
-
-Chancery defines a few helpful functions for generating English text:
-
-* `s` pluralizes its argument.
-* `a` adds an indefinite article ("a" or "an") to the front of its argument.
-* `cap` capitalizes the first letter of its argument.
-* `cap-all` capitalizes each word in its argument.)
-* `pos` makes its argument possessive by adding an apostrophe (and possibly an s).
-
-These are just normal Lisp functions that happen to be useful as modifiers:
-
- :::lisp
- (define-string animal "cat" "aardvark" "fly")
-
- (loop :repeat 5
- :collect (generate-string
- ("a bunch of" #(animal s))))
- ; =>
- ; ("a bunch of flies" "a bunch of flies" "a bunch of aardvarks"
- ; "a bunch of cats" "a bunch of aardvarks")
-
- (loop :repeat 5
- :collect (generate-string
- (#(animal a cap) "is a good pet")))
- ; =>
- ; ("A cat is a good pet" "A fly is a good pet" "A fly is a good pet"
- ; "A cat is a good pet" "An aardvark is a good pet")
-
-English is a messy language. Pull requests to improve these functions (or add
-more useful ones) are welcome.
-
-Evaluation
-----------
-
-Sometimes it can be useful to switch out of "Chancery evaluation" and back into
-normal Lisp evaluation. The `(eval ...)` special form can be used to do this in
-Chancery rules and string rules:
-
- :::lisp
- (define-rule treasure
- :weapon
- :armor
- ((eval (random 100)) :gold))
-
- (loop :repeat 5 :collect (treasure))
- ; =>
- ; ((93 :GOLD)
- ; :WEAPON
- ; (83 :GOLD)
- ; :WEAPON
- ; :ARMOR)
-
- (define-string animal "cat" "dog")
- (define-string story
- ("The" animal "had" (eval (+ 2 (random 3))) "friends."))
-
- (loop :repeat 5 :collect (story))
- ; =>
- ; ("The dog had 3 friends." "The cat had 2 friends." "The dog had 3 friends."
- ; "The cat had 4 friends." "The dog had 2 friends.")
-
-You can think of `eval` and `generate` as two sides of the same coin, kind of
-like quasiquote's backquote and comma. `eval` flips from Chancery evaluation to
-Lisp evaluation, and `generate` flips in the other direction.
-
-Reader Macros
--------------
-
-Chancery provides four reader macros you can use to make defining grammars
-even more concise. It uses [named-readtables][] to keep them safely stashed
-away unless you want them.
-
-The first reader macro is `[...]` to replace the standard vector `#(...)`
-syntax, to make string modifiers stand out more:
-
- :::lisp
- (named-readtables:in-readtable :chancery)
-
- (define-string animal "cat" "dog")
-
- (generate-string ("a few" [animal s]))
- ; => "a few cats"
-
-The reader macro `!form` expands to `(eval form)`:
-
- :::lisp
- (named-readtables:in-readtable :chancery)
-
- (define-rule treasure
- :weapon
- :armor
- (!(random 100) :gold))
-
- (loop :repeat 5 :collect (treasure))
- ; =>
- ; (:ARMOR (53 :GOLD) (49 :GOLD) :ARMOR :WEAPON)
-
-The reader macro `@form` expands to `(generate form)`, and `$form` expands to
-`(generate-string form)`. Note that none of th
-
- :::lisp
- (named-readtables:in-readtable :chancery)
-
- (define-string animal "cat" "dog")
-
- $("the" animal "ran")
- ; => "the cat ran"
-
-Together these let you jump in and out of Chancery-style evaluation as needed:
-
- :::lisp
- (named-readtables:in-readtable :chancery)
-
- (define-string animal "cat" "dog")
-
- (define-string story
- ("the" animal "went to sleep")
- !(let ((pet (animal)))
- $("the" !pet "was a good" !pet)))
-
- (loop :repeat 4 :collect (story))
- ; =>
- ; ("the dog was a good dog" "the cat was a good cat"
- ; "the dog was a good dog" "the cat went to sleep")
-
-This last example is a little tricky, but it helps to think of `$` and `@` as
-quasiquote's backquote, and `!` as comma. Flipping back and forth between Lisp
-and Chancery can let you define some really interesting grammars.
-
-
-
-
-
-[named-readtables]: https://common-lisp.net/project/named-readtables/
-
-Runtime Rule Creation
----------------------
-
-All of the Chancery things we've been using so far have been macros. If you
-want to create rules at runtime you can use their function counterparts:
-
-* `create-rule` takes a list of expressions and options, and returns a rule
- function.
-* `create-string` takes a list of expressions and options, and returns a string
- function.
-* `invoke-generate` takes an expression and evaluates it.
-* `invoke-generate-string` takes an expression and evaluates it and stringifies
- the result.
-
-For example:
-
- (define-rule foo :a :b :c)
- (foo)
- ; versus
- (funcall (create-rule (list :a :b :c)))
-
- (define-string (bar :distribution :zipf)
- ("one" foo)
- ("two" [foo s]))
- (bar)
- ; versus
- (funcall (create-string (list '("one" foo)
- '("two" [foo s]))
- :distribution :zipf))
-
-Tips
-----
-
-Chancery aims to be a very thin layer on top of Lisp. Chancery rules are just
-vanilla Lisp functions—you can `describe` them, `disassemble` them, `trace`
-them, `funcall` them, and anything else you might normally want to do with
-functions. Similarly, Chancery string modifiers are just unary functions—you
-can create your own, or even use built-in Lisp functions like `string-upcase`.
-
-Remember that you can flip back and forth between Chancery evaluation and normal
-Lisp evaluation. Sometimes it's easier to just do a quick `!(if foo then else)`
-than to make Yet Another Rule. Use your own judgement to determine when a rule
-is getting too hairy and needs to be split into simpler parts, just like you
-would for any other Lisp function.
-
-Examples
---------
-
-If you want some less trivial examples than the ones seen here, you might want
-to take a look at some of the Twitter bots I've built with Chancery:
-
-* [@git\_commands](http://twitter.com/git_commands)
- ([code](https://github.com/sjl/magitek/blob/master/src/robots/git-commands.lisp))
- tweets procedurally-generated Git command summaries.
-* [@lisp\_talks](http://twitter.com/lisp_talks)
- ([code](https://github.com/sjl/magitek/blob/master/src/robots/lisp-talks.lisp))
- tweets random topics for Lisp talks.
-* [@rpg\_shopkeeper](http://twitter.com/rpg_shopkeeper)
- ([code](https://github.com/sjl/magitek/blob/master/src/robots/rpg-shopkeeper.lisp))
- generates random fantasy RPG items, and sells them for appropriate prices.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/03-changelog.markdown Mon Dec 18 17:45:40 2017 -0500
@@ -0,0 +1,11 @@
+Changelog
+=========
+
+Here's the list of changes in each released version.
+
+[TOC]
+
+v1.0.0
+------
+
+Initial version.
--- a/docs/03-reference.markdown Fri Nov 03 23:48:31 2017 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,248 +0,0 @@
-# API Reference
-
-The following is a list of all user-facing parts of Chancery.
-
-If there are backwards-incompatible changes to anything listed here, they will
-be noted in the changelog and the author will feel bad.
-
-Anything not listed here is subject to change at any time with no warning, so
-don't touch it.
-
-[TOC]
-
-## Package `CHANCERY`
-
-### `*RANDOM*` (variable)
-
-The random number generation function to use (default: `CL:RANDOM`).
-
-### `A` (function)
-
- (A STRING)
-
-Add an indefinite article (a or an) to the front of `string`.
-
-### `CAP` (function)
-
- (CAP STRING)
-
-Capitalize the first character of `string`.
-
-### `CAP-ALL` (function)
-
- (CAP-ALL STRING)
-
-Capitalize each word of `string`.
-
-### `CREATE-RULE` (function)
-
- (CREATE-RULE EXPRESSIONS &REST OPTIONS)
-
-Return a function that will return random elements of `expressions`.
-
- `options` should be of the form:
-
- (&key documentation (distribution :uniform) (arguments '()))
-
- `documentation` will be used as a docstring for the resulting function.
-
- `distribution` denotes the distribution of elements returned.
-
- `arguments` is the arglist of the resulting function.
-
- Examples:
-
- (create-rule (list :blue :red :green))
-
- (create-rule (list :copper :silver :gold :platinum)
- :documentation "Return a random metal."
- :distribution :zipf)
-
- See the full documentation for more information.
-
-
-
-### `CREATE-STRING` (function)
-
- (CREATE-STRING EXPRESSIONS &REST OPTIONS)
-
-Return a function that will return random stringified elements of `expressions`.
-
- `options` should be of the form:
-
- (&key documentation (distribution :uniform) (arguments '()))
-
- `documentation` will be used as a docstring for the resulting function.
-
- `distribution` denotes the distribution of elements returned.
-
- `arguments` is the arglist of the resulting function.
-
- Examples:
-
- (create-string (list "white" "gray" "black"))
-
- (create-string '((100 (color "cat"))
- (100 (color "dog"))
- (100 (color "dragon")))
- :distribution :weighted)
-
- See the full documentation for more information.
-
-
-
-### `DEFINE-RULE` (macro)
-
- (DEFINE-RULE NAME-AND-OPTIONS &REST EXPRESSIONS)
-
-Define a function that will return random elements of `expressions`.
-
- `name-and-options` should be of the form:
-
- (name &key documentation (distribution :uniform) (arguments '()))
-
- If no options are needed a bare symbol can be given.
-
- `name` is the symbol under which the resulting function will be defined.
-
- `documentation` will be used as a docstring for the resulting function.
-
- `distribution` denotes the distribution of elements returned.
-
- `arguments` is the arglist of the resulting function.
-
- Examples:
-
- (define-rule color
- :blue
- :green
- :red)
-
- (define-rule (metal :documentation "Return a random metal."
- :distribution :zipf)
- :copper
- :silver
- :gold
- :platinum)
-
- See the full documentation for more information.
-
-
-
-### `DEFINE-STRING` (macro)
-
- (DEFINE-STRING NAME-AND-OPTIONS &REST EXPRESSIONS)
-
-Define a function that will return random stringified elements of `expressions`.
-
- `name-and-options` should be of the form:
-
- (name &key documentation (distribution :uniform) (arguments '()))
-
- If no options are needed a bare symbol can be given.
-
- `name` is the symbol under which the resulting function will be defined.
-
- `documentation` will be used as a docstring for the resulting function.
-
- `distribution` denotes the distribution of elements returned.
-
- `arguments` is the arglist of the resulting function.
-
- Examples:
-
- (define-string color "white" "gray" "black")
-
- (define-string (animal :distribution :weighted)
- (100 (color "cat"))
- (100 (color "dog"))
- (100 (color "dragon")))
-
- See the full documentation for more information.
-
-
-
-### `GENERATE` (macro)
-
- (GENERATE EXPRESSION)
-
-Generate a single Chancery expression.
-
- Example:
-
- (define-rule x 1 2 3)
-
- (generate (x x x))
- ; => (1 3 1)
-
-
-
-### `GENERATE-STRING` (macro)
-
- (GENERATE-STRING EXPRESSION)
-
-Generate and stringify a single Chancery string expression.
-
- Example:
-
- (define-string x 1 2 3)
-
- (generate-string (x x x))
- ; => "1 3 1"
-
-
-
-### `INVOKE-GENERATE` (function)
-
- (INVOKE-GENERATE EXPRESSION)
-
-Generate a single Chancery expression.
-
- THIS FUNCTION IS EXPERIMENTAL AND SUBJECT TO CHANGE IN THE FUTURE.
-
- Because this is a function, not a macro, you'll need to do the quoting
- yourself:
-
- (define-rule x 1 2 3)
-
- (generate (x x x))
- ; => (1 3 3)
-
- (invoke-generate '(x x x))
- ; => (2 1 2)
-
-
-
-### `INVOKE-GENERATE-STRING` (function)
-
- (INVOKE-GENERATE-STRING EXPRESSION)
-
-Generate and stringify a single Chancery expression.
-
- THIS FUNCTION IS EXPERIMENTAL AND SUBJECT TO CHANGE IN THE FUTURE.
-
- Because this is a function, not a macro, you'll need to do the quoting
- yourself:
-
- (define-string x 1 2 3)
-
- (generate-string (x x x))
- ; => "1 3 3"
-
- (invoke-generate-string '(x x x))
- ; => "2 1 2"
-
-
-
-### `POS` (function)
-
- (POS STRING)
-
-Make `string` posessive by adding an apostrophe (and possibly an s).
-
-### `S` (function)
-
- (S STRING)
-
-Pluralize `string`.
-
--- a/docs/04-changelog.markdown Fri Nov 03 23:48:31 2017 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-Changelog
-=========
-
-Here's the list of changes in each released version.
-
-[TOC]
-
-v1.0.0
-------
-
-Initial version.
--- a/docs/api.lisp Fri Nov 03 23:48:31 2017 -0400
+++ b/docs/api.lisp Mon Dec 18 17:45:40 2017 -0500
@@ -13,7 +13,7 @@
(d-api:generate-documentation
:chancery
- #p"docs/03-reference.markdown"
+ #p"docs/02-reference.markdown"
(list "CHANCERY")
*header*
:title "API Reference")
--- a/docs/index.markdown Fri Nov 03 23:48:31 2017 -0400
+++ b/docs/index.markdown Mon Dec 18 17:45:40 2017 -0500
@@ -1,7 +1,7 @@
Chancery is a library for procedurally generating text and data in Common
Lisp. It's heavily inspired by [Tracery][].
-[Tracery]: http://tracery.io/
+Chancery can be installed with [Quicklisp][]: `(ql:quickload :chancery)`
* **License:** MIT/X11
* **Documentation:** <https://sjl.bitbucket.io/chancery/>
@@ -12,3 +12,6 @@
*terrible*, but is not a high priority.
It is currently not thread-safe, but this may happen in the future.
+
+[Tracery]: http://tracery.io/
+[quicklisp]: https://quicklisp.org/