Bracery
Procedural text generator, somewhat compatible with Tracery
Install / Use
/learn @ihh/BraceryREADME
Bracery
Bracery (bracery.org) is a small language for procedural text generation. Its purpose is to enable quick and fluid writing of text with random elements, allowing for user extensions that may include calls to a server (e.g. for synonyms, rhymes, or other functions).
Bracery aims...
- to keep out of the writer's way, looking mostly like a markup language and close to plain text;
- to be easy to work with, presenting simply whether you're a casual user or an experienced programmer;
- to avoid plundering the keyboard for syntax, especially common punctuation, like quotes;
- to allow real programming, with variables, functions, and lists, but without forcing the writer to learn (or care) about all that;
- to be usable offline by default, but also readily connectable to online generative text servers;
- to be secure running random code from the internet, including limits on recursion and network/CPU usage;
- to be compatible as much as possible with previous work, especially @galaxykate's Tracery.
Bracery combines a few different tricks well-known to computational linguistics and adjacent fields, such as variable manipulation syntax from Tracery, alternations from regular expressions, natural language processing from the compromise library (and, optionally, rhymes and phonemes from RiTa), parsing algorithms from bioinformatics, and lists from Scheme.
Procedural text repository
A writable repository of Bracery scripts is available at bracery.org. Edit Bracery in the web browser; save and share immediately; deploy as a Twitter bot with a single click.
Bracery can also be run from the command line, in the browser, etc.
The Bracery command-line client will use the bracery.org server to resolve symbol definitions if the -w switch is specified from the command line, e.g.
bin/bracery -w -e '~common_animal'
will generate a sample from this page.
You can also type bin/bracery -wr to enter text interactively from the command-line
and have expansions returned by the server
(or, alternatively, just point your web browser at bracery.org, click "Edit", and type into the box).
The micro-wiki repository is serverless, built using AWS Lambda functions available in the lambda/ directory.
Programmer's tl;dr
If you're a programmer, or not, you may want the summary:
Bracery is a text markup language
oriented around probabilistic context-free grammars
that uses dollars signs $ for variables (as in $x),
equals for assignment ($x=value),
curly-brace delimiters ($x={longer value}),
square-bracket alternations ([first option|another option|third option]),
ampersands for built-in function names (&add, &subtract, &plural, etc.),
conditionals (&if),
lists (&map, &join, &reduce),
access to several parsers (&eval, "e, &syntax, &parse),
and other language-oriented features;
with an engine that runs asynchronously and can (optionally) connect to an online wiki for procedural content,
where each page is its own symbol in the grammar.
Usage
The following Bracery code generates lines like
how goes it, magician of Middle Earth
and well met, magus of the world
[hello|well met|how goes it|greetings], [wizard|witch|mage|magus|magician|sorcerer|enchanter] of [earthsea|Earth|Middle Earth|the planet|the world]
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example1?demo=true&reset=true&enableParse=true&eval=%5Bhello%7Cwell%20met%7Chow%20goes%20it%7Cgreetings%5D%2C%20%5Bwizard%7Cwitch%7Cmage%7Cmagus%7Cmagician%7Csorcerer%7Cenchanter%5D%20of%20%5Bearthsea%7CEarth%7CMiddle%20Earth%7Cthe%20planet%7Cthe%20world%5D">Try this</a> </em>
Here's the same example, but using variables to keep track of the choices:
$greetings=[hello|well met|how goes it|greetings]
$wizard=[wizard|witch|mage|magus|magician|sorcerer|enchanter]
$earthsea=[earthsea|Earth|Middle Earth|the planet|the world]
$greetings, $wizard of $earthsea
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example2?demo=true&reset=true&enableParse=true&eval=%24greetings%3D%5Bhello%7Cwell%20met%7Chow%20goes%20it%7Cgreetings%5D%0A%24wizard%3D%5Bwizard%7Cwitch%7Cmage%7Cmagus%7Cmagician%7Csorcerer%7Cenchanter%5D%0A%24earthsea%3D%5Bearthsea%7CEarth%7CMiddle%20Earth%7Cthe%20planet%7Cthe%20world%5D%0A%24greetings%2C%20%24wizard%20of%20%24earthsea">Try this</a> </em>
You can also use variables to store Bracery code itself, for later expansion:
$greetings="e{[hello|well met|how goes it|greetings]}
$wizard="e{[wizard|witch|mage|magus|magician|sorcerer|enchanter]}
$earthsea="e{[earthsea|Earth|Middle Earth|the planet|the world]}
&eval{$greetings}, &eval{$wizard} of &eval{$earthsea}
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example3?demo=true&reset=true&enableParse=true&eval=%24greetings%3D%26quote%7B%5Bhello%7Cwell%20met%7Chow%20goes%20it%7Cgreetings%5D%7D%0A%0A%24wizard%3D%26quote%7B%5Bwizard%7Cwitch%7Cmage%7Cmagus%7Cmagician%7Csorcerer%7Cenchanter%5D%7D%0A%0A%24earthsea%3D%26quote%7B%5Bearthsea%7CEarth%7CMiddle%20Earth%7Cthe%20planet%7Cthe%20world%5D%7D%0A%0A%26eval%7B%24greetings%7D%2C%20%26eval%7B%24wizard%7D%20of%20%26eval%7B%24earthsea%7D">Try this</a> </em>
The above example uses dynamic evaluation. Here's the same code with some syntactic sugar for the way variables are assigned and expanded:
[greetings=>hello|well met|how goes it|greetings]
[wizard=>wizard|witch|mage|magus|magician|sorcerer|enchanter]
[earthsea=>earthsea|Earth|Middle Earth|the planet|the world]
#greetings#, #wizard# of #earthsea#
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example4?demo=true&reset=true&enableParse=true&eval=%5Bgreetings%3D%3Ehello%7Cwell%20met%7Chow%20goes%20it%7Cgreetings%5D%0A%5Bwizard%3D%3Ewizard%7Cwitch%7Cmage%7Cmagus%7Cmagician%7Csorcerer%7Cenchanter%5D%0A%5Bearthsea%3D%3Eearthsea%7CEarth%7CMiddle%20Earth%7Cthe%20planet%7Cthe%20world%5D%0A%23greetings%23%2C%20%23wizard%23%20of%20%23earthsea%23">Try this</a> </em>
Programmers may recognize this kind of thing too (lambdas):
$greetings=[hello|well met|how goes it|greetings]
$wizard=[wizard|witch|mage|magus|magician|sorcerer|enchanter]
$earthsea=[earthsea|Earth|Middle Earth|the planet|the world]
$sentence=&function{$name}{$greetings, $name}
&$sentence{$wizard of $earthsea}
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example5?demo=true&reset=true&enableParse=true&eval=%24greetings%3D%5Bhello%7Cwell%20met%7Chow%20goes%20it%7Cgreetings%5D%0A%24wizard%3D%5Bwizard%7Cwitch%7Cmage%7Cmagus%7Cmagician%7Csorcerer%7Cenchanter%5D%0A%24earthsea%3D%5Bearthsea%7CEarth%7CMiddle%20Earth%7Cthe%20planet%7Cthe%20world%5D%0A%0A%24sentence%3D%26function%7B%24name%7D%7B%24greetings%2C%20%24name%7D%0A%0A%26%24sentence%7B%24wizard%20of%20%24earthsea%7D">Try this</a> </em>
And maybe this as well (lists):
$greetings=[hello|well met|how goes it|greetings]
$wizard=[wizard|witch|mage|magus|magician|sorcerer|enchanter]
$earthsea=[earthsea|Earth|Middle Earth|the planet|the world]
$sentence={$greetings, $wizard of $earthsea}
&join{&shuffle{&split{$sentence}}}
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example6?demo=true&reset=true&enableParse=true&eval=%24greetings%3D%5Bhello%7Cwell%20met%7Chow%20goes%20it%7Cgreetings%5D%0A%24wizard%3D%5Bwizard%7Cwitch%7Cmage%7Cmagus%7Cmagician%7Csorcerer%7Cenchanter%5D%0A%24earthsea%3D%5Bearthsea%7CEarth%7CMiddle%20Earth%7Cthe%20planet%7Cthe%20world%5D%0A%0A%24sentence%3D%7B%24greetings%2C%20%24wizard%20of%20%24earthsea%7D%0A%0A%26join%7B%26shuffle%7B%26split%7B%24sentence%7D%7D%7D">Try this</a> </em>
which gives jumbled-up output like
witch the hello, of world
There is a built-in (limited) rhyming engine, using RiTa or the CMU Pronouncing Dictionary for text-to-phoneme conversion. If you supply two grammar templates, Bracery will make a (limited) effort to find two expansions that rhyme:
&rhyme{The [stupid|foolish|hungry] [cat|dog], }{the [lonely|fateful] [hat|log]}
<!--DEMO--> <em> <a style="float:right;" href="https://bracery.org/guest/readme_example7?demo=true&reset=true&enableParse=true&eval=%26rhyme%7BThe%20%5Bstupid%7Cfoolish%7Chungry%5D%20%5Bcat%7Cdog%5D%2C%20%7D%7Bthe%20%5Blonely%7Cfateful%5D%20%5Bhat%7Clog%5D%7D">Try this</a> </em>
Note that the rhyming algorithm is essentially "brute-force": Bracery generates a few different possibilities, and preferentially selects the ones that rhyme. So, this could take a while if the rhyme templates are complex, and it won't always work (and it will tend to work best if you choose templates that you know will contain some rhymes, as in this exampl
