# HG changeset patch # User Steve Losh # Date 1488548015 0 # Node ID 3d06e5b432c9eaaecd1b45ff91543ce61d70bab3 # Parent f0ec1842d603db147f547a046274bd14083e67ac Start documenting diff -r f0ec1842d603 -r 3d06e5b432c9 README.markdown --- 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. diff -r f0ec1842d603 -r 3d06e5b432c9 docs/02-usage.markdown --- 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 +------------- diff -r f0ec1842d603 -r 3d06e5b432c9 docs/03-reference.markdown --- 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) diff -r f0ec1842d603 -r 3d06e5b432c9 docs/index.markdown --- 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/ diff -r f0ec1842d603 -r 3d06e5b432c9 src/chancery.lisp --- 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))