Vitrail
is a text generator using context-free grammars, inspired by projects like Tracery, or Grammy.
It can be used on the spot by providing a grammar as a JSON file, and following the default syntax, and can also be used as a library.
I also have a Kotlin version here ;)
- Read grammar from JSON file
- Shared variables, available through all symbol expansions
- Customisable grammar syntax/operators (when used as a library)
- Potential for custom modifiers (when used as a library)
{
"root": [
"[character>subject]Let's {speak} about {subject}. Did you know that {subject} {verb} {object:s:capitalize}?"
],
"speak": [
"speak",
"babble",
"talk"
],
"character": [
"Alice",
"Bob",
"Eve"
],
"verb": [
"shared",
"ate",
"saw",
"destroyed",
"stole",
"lost"
],
"object": [
"the apple",
"the banana",
"the pancake",
"a cinnamon roll"
]
}
use vitrail::{
config::GrammarSyntax,
grammar::Grammar,
modifier::{
CapitalizeModifier,
PluralizeModifier,
},
};
let mut grammar = Grammar::from_json(
"test.json",
"any random seed",
GrammarSyntax::default(),
)
.with_modifier("capitalize".to_string(), &CapitalizeModifier {})
.with_modifier("s".to_string(), &PluralizeModifier {});
// Randomly expand the grammar 15 times
for _ in 0..15 {
println!("{}", &grammar.flatten());
}
Let's talk about Eve. Did you know that Eve stole The pancakes?
Let's speak about Bob. Did you know that Bob ate The pancakes?
Let's babble about Eve. Did you know that Eve shared The bananas?
Let's talk about Bob. Did you know that Bob saw The apples?
Let's babble about Bob. Did you know that Bob ate The pancakes?
Let's talk about Eve. Did you know that Eve lost A cinnamon rolls?
Let's speak about Bob. Did you know that Bob stole The bananas?
Let's speak about Bob. Did you know that Bob stole The bananas?
Let's talk about Bob. Did you know that Bob destroyed The bananas?
Let's babble about Bob. Did you know that Bob saw The apples?
Let's speak about Eve. Did you know that Eve saw The apples?
Let's talk about Bob. Did you know that Bob lost The bananas?
Let's babble about Alice. Did you know that Alice stole The bananas?
Let's babble about Alice. Did you know that Alice destroyed The bananas?
Let's babble about Eve. Did you know that Eve shared The bananas?
Looks up the rules corresponding to the symbol, and picks one randomly as a replacement
{
"root": "{subject} {verb} {object}",
"subject": [
"Alice",
"Bob"
],
"verb": [
"shares",
"eats",
"sees"
],
"object": [
"the apple",
"the banana"
]
}
Creates a variableName
symbol with a static value. The rules corresponding to symbol
determine the value of variableName
.
This value never changes once it's initialized.
{
"root": "[person>subject]{subject} {verb}. {subject} also {verb}",
"person": [
"..."
],
"...": []
}
In the example above, both subject
symbols will expand with the value coming from person
.
Note: The placement of the variable capture matters!
The algorithm expands symbols from left to right. This means that a symbol expansion captured inside a variable is only available for expressions on its right side.
Otherwise, placement is free.
Note (expanding on the previous note):
While placement is free for now, future plans involve allowing for more granularity regarding the scope of variables.
One possibility would be to declare captures inside a symbol to make it available only inside the scope of this symbol's expansion:
{ "root": "{[person>subject]part1}. {[person>subject]part2}" }In the example above,
subject
could have different values in the expansion ofpart1
andpart2
.Do keep this in mind when writing your grammars.
Applies a modification on the expanded value of a symbol.
Note: while a
Modifier
interface exists, and modifiers added to theGrammar
at runtime are applied, the library does not provide any modifier yet.
{
"root": "{subject:capitalize}",
"subject": [
"john"
]
}
Considering that the Grammar
has a modifier with the name capitalize
, that capitalizes the first letter of the value passed to it, the result of the example above would be:
"John"
Vitrail
is released under MIT License