3d06e5b432c9

Start documenting
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Fri, 03 Mar 2017 13:33:35 +0000
parents f0ec1842d603
children 2153d3a110dd
branches/tags (none)
files README.markdown docs/02-usage.markdown docs/03-reference.markdown docs/index.markdown src/chancery.lisp

Changes

--- 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))