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
]
rust
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]>
.
rust
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.
rust
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
].
rust
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.
rust
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.
rust
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