Rust implementation of the tracery generative grammar language.
This library is a Rust port/implementation of tracery, the generative grammar language designed and created by Kate Compton. Given a set of rules written in the tracery syntax, it will use them to procedurally generate strings of text. For more information about the tracery language, see Language Concepts.
Usage of the library can be divided into two areas: creation of grammars and the generation of output strings.
Grammars can be created using the grammar!
macro, from an any iterable
rust object of strings and associated lists of strings, or for compatibility
with the original tracery, from a string representing a JSON map.
Accepts input in the form "key" => [ "list", "of", "rules" ]
or, in the
case of a key having only one rule, "key" => "rule"
. Equivalent to
manually building a map and then calling Grammar::from_map
use tracery::grammar;
let g = grammar! {
"origin" => "#tool# is #description#!",
"tool" => "tracery",
"description" => [ "fun", "awesome" ]
}?;
A grammar can be created from any object implementing, essentially,
IntoIterator<Item = (Into<String>, Into<Vec<Into<String>>)>
. For example,
HashMap<String, Vec<String>>
or BTreeMap<&str, &[&str]>
.
let map = hashmap! {
"origin" => vec![ "#tool# is #description#!" ],
"tool" => vec![ "tracery" ],
"description" => vec![ "fun", "awesome" ]
};
let g = tracery::from_map(map)?;
For compatibility with the original tracery, a Grammar can be created from a
string representing a JSON object. This feature is controlled by the
tracery_json
feature, which is enabled by default. It can be turned off if
you do not require this functionality.
let json = r##"{
"origin": [ "#tool# is #description#!" ],
"tool": [ "tracery" ],
"description": [ "fun", "awesome" ]
}"##;
let g = tracery::from_json(json)?;
There are two methods for getting a generated output string from a created
Grammar: execute
and flatten
. Generally, execute
should be
preferred if possible.
execute
takes two parameters: the rule to expand and an RNG to use
during generation. The RNG can be any type implementing rand::Rng
.
use tracery::grammar;
let mut g = grammar! {
"origin" => "#tool# is #description#!",
"tool" => "tracery",
"description" => [ "fun", "awesome" ]
}?;
// Generate an output (either "tracery is fun!" or "tracery is awesome!")
let key = String::from("origin");
let output = g.execute(&key, &mut rand::thread_rng())?;
execute
generates its output using the Grammar in-place. Since Grammars
are allowed to modify their own rule stacks, execute
must take a &mut self
reference. This means that any modifications made during an execution
will persist in the Grammar.
use tracery::grammar;
// This time, origin has a side-effect: it creates the rule 'aside'
let mut g = grammar! {
"origin" => "#[aside:Rust is, too]tool# is #description#!",
"tool" => "tracery",
"description" => [ "fun", "awesome" ]
}?;
// Generate an output (either "tracery is fun!" or "tracery is awesome!")
let key = String::from("origin");
let output = g.execute(&key, &mut rand::thread_rng())?;
// The previous call to execute created the 'aside' rule
let key = String::from("aside");
// Generates the string "Rust is, too"
let output = g.execute(&key, &mut rand::thread_rng())?;
flatten
, unlike execute
, always operates on the default rule of the
Grammar ("origin" by default), but like execute
, takes an instance of
rand::Rng
to use during generation. In addition, flatten
creates a
clone of the Grammar to use during generation, then discards it, which means
that any side-effects that occur will be discarded when it's done.
use tracery::grammar;
let g = grammar! {
"origin" => "#tool# is #description#!",
"tool" => "tracery",
"description" => [ "fun", "awesome" ]
}?;
// Generate an output (either "tracery is fun!" or "tracery is awesome!")
let output = g.flatten(&mut rand::thread_rng())?;
License: MIT/Apache-2.0