--- a/README.markdown Mon Feb 06 21:24:37 2017 +0000
+++ b/README.markdown Fri Mar 03 13:33:35 2017 +0000
@@ -8,7 +8,7 @@
\|
```
-Chancery is a library for procedurally generating text and stories in Common
+Chancery is a library for procedurally generating text and data in Common
Lisp. It's heavily inspired by [Tracery][].
[Tracery]: http://tracery.io/
@@ -20,3 +20,5 @@
Chancery focuses on simplicity, correctness, and usability. Performance is not
*terrible*, but is not a high priority.
+
+It is currently not thread-safe, but this may happen in the future.
--- a/docs/02-usage.markdown Mon Feb 06 21:24:37 2017 +0000
+++ b/docs/02-usage.markdown Fri Mar 03 13:33:35 2017 +0000
@@ -1,21 +1,131 @@
Usage
=====
-Chancery is ...
+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].
-[tutorial]: http://www.crystalcodepalace.com/traceryTut.html
+[Tracery]: http://www.crystalcodepalace.com/traceryTut.html
+[cfg]: https://en.wikipedia.org/wiki/Context-free_grammar
[TOC]
Rules
-----
-Concatenation
+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. 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)
+
+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)
+
+[zipf]: https://en.wikipedia.org/wiki/Zipf's_law
+
+Text Generation
+---------------
+
Modifiers
---------
Evaluation
----------
+Reader Macros
+-------------
--- a/docs/03-reference.markdown Mon Feb 06 21:24:37 2017 +0000
+++ b/docs/03-reference.markdown Fri Mar 03 13:33:35 2017 +0000
@@ -32,24 +32,23 @@
### `DEFINE-RULE` (macro)
- (DEFINE-RULE NAME &REST EXPRESSIONS)
+ (DEFINE-RULE NAME-AND-OPTIONS &REST EXPRESSIONS)
-Define a Chancery rule for the symbol `name`.
+### `DEFINE-STRING` (macro)
- Each expression in `expressions` can be any valid Chancery expression. When
- the rule is invoked one will be chosen at random and evaluated.
+ (DEFINE-STRING NAME-AND-OPTIONS &REST EXPRESSIONS)
- Examples:
+### `GEN` (macro)
- (define-rule name "Alice" "Bob" "Carol")
- (define-rule place "forest" "mountain")
- (define-rule emotion "happy" "sad")
+ (GEN EXPRESSION)
+
+Generate a single Chancery expression.
- (define-rule sentence
- (name "was" emotion :. ".")
- (name "went to the" place :. "."))
+### `GEN-STRING` (macro)
-
+ (GEN-STRING EXPRESSION)
+
+Generate a single Chancery string expression.
### `POS` (function)
@@ -57,12 +56,6 @@
Make `string` posessive by adding an apostrophe (and possibly an s).
-### `Q` (function)
-
- (Q STRING)
-
-Wrap `string` in quotation marks.
-
### `S` (function)
(S STRING)
--- a/docs/index.markdown Mon Feb 06 21:24:37 2017 +0000
+++ b/docs/index.markdown Fri Mar 03 13:33:35 2017 +0000
@@ -1,4 +1,4 @@
-Chancery is a library for procedurally generating text and stories in Common
+Chancery is a library for procedurally generating text and data in Common
Lisp. It's heavily inspired by [Tracery][].
[Tracery]: http://tracery.io/
--- a/src/chancery.lisp Mon Feb 06 21:24:37 2017 +0000
+++ b/src/chancery.lisp Fri Mar 03 13:33:35 2017 +0000
@@ -5,14 +5,19 @@
'(and symbol (not keyword)))
-(defmacro -<> (&rest forms)
+(defmacro -<> (expr &rest forms)
+ "Thread the given forms, with `<>` as a placeholder."
;; I am going to lose my fucking mind if I have to program lisp without
;; a threading macro, but I don't want to add another dep to this library, so
;; here we are.
- (if (null forms)
- '<>
- `(let ((<> ,(first forms)))
- (-<> ,@(rest forms)))))
+ `(let* ((<> ,expr)
+ ,@(mapcar (lambda (form)
+ (if (symbolp form)
+ `(<> (,form <>))
+ `(<> ,form)))
+ forms))
+ <>))
+
(defmacro assert-nonempty (place message)
`(assert (not (emptyp ,place)) (,place) ,message))