8 releases

0.2.1 Apr 24, 2021
0.2.0 Sep 13, 2020
0.1.4 Sep 13, 2020
0.1.3 Aug 21, 2020
0.1.0 Jun 17, 2016

#656 in Text processing

MIT/Apache

63KB
989 lines

tracery

Rust implementation of the tracery generative grammar language.

Crates.io MIT licensed Documentation Coverage Status

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

Usage of the library can be divided into two areas: creation of grammars and the generation of output strings.

Grammar Creation

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.

The grammar! macro

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" ]
}?;

From a map/iterator

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

From a JSON string

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

Generating output strings

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

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

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

Dependencies

~2.6–3.5MB
~70K SLoC