SkillAgentSearch skills...

Tracery

Powerful extensible content generation library inspired by Tracery.io

Install / Use

/learn @BenziAhamed/Tracery
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Tracery - powerful content generation

Contents

Introduction

Tracery is a content generation library originally created by @GalaxyKate; you can find more information at Tracery.io

This implementation, while heavily inspired by the original, has more features added.

The content generation in Tracery works based on an input set of rules. The rules determine how content should be generated.

Installation

Manual

  • Clone or download this repository
  • To work with the playground, open Playgrounds/Tracery.playground
  • The project builds iOS and macOS framework targets, which can be linked to your projects

Cocoapods

You want to add pod 'Tracery', '~> 0.0.2' similar to the following to your Podfile:

target 'MyApp' do
  pod 'Tracery', '~> 0.0.2'
end

Then run a pod install inside your terminal, or from CocoaPods.app.

Alternatively to give it a test run, run the command:

pod try Tracery

top


Swift Package Manager

Use Swift Package Manager support in Xcode 11 (File > Swift Packages > Add Package Dependency...) to add the Swift package to your targets. Or add Tracery to your Package.swift file with the following:

.package(url: "https://github.com/BenziAhamed/Tracery.git", from: "0.0.2")

Then, add Tracery as a dependency to your target(s):

.target(name: "App", dependencies: [..., "Tracery"])

Basic usage

import Tracery

// create a new Tracery engine

var t = Tracery {[
    "msg" : "hello world"
]}

t.expand("well #msg#")

// output: well hello world

We create an instance of the Tracery engine passing along a dictionary of rules. The keys to this dictionary are the rule names, and the value for each key represents the expansion of the rule.

The we use Tracery to expand instances of specified rules

Notice we provide as input a template string, which contains #msg#, that is the rule we wish to expand inside # marks. Tracery evaluates the template, recognizes a rule, and replaces it with its expansion.

We can have multiple rules:

t = Tracery {[
    "name": "jack",
    "age": "10",
    "msg": "#name# is #age# years old",
]}

t.expand("#msg#") // jack is 10 years old

Notice how we specify to expand #msg#, which then triggers the expansion of #name# and #age# rules? Tracery can recursively expand rules until no further expansion is possible.

A rule can have multiple candidates expansions.

t = Tracery {[
    "name": ["jack", "john", "jacob"], // we can specify multiple values here
    "age": "10",
    "msg": "#name# is #age# years old",
    ]}

t.expand("#msg#")

// jacob is 10 years old  <- name is randomly picked

t.expand("#msg#")

// jack is 10 years old   <- name is randomly picked

t.expand("#name# #name#")

// will print out two different random names

In the snippet above, whenever Tracery sees the rule #name#, it will pick out one of the candidate values; in this example, name could be "jack" "john" or "jacob"

This is what allows content generation. By specifying various candidates for each rule, every time expand is invoked yields a different result.

Let us try to build a sentence based on a popular nursery rhyme.

t = Tracery {[
    "boy" : ["jack", "john"],
    "girl" : ["jill", "jenny"],
    "sentence": "#boy# and #girl# went up the hill."
]}

t.expand("#sentence#")

// output: john and jenny went up the hill

So we get the first part of the sentence, what if we wanted to add in a second line so that our final output becomes:

"john and jenny went up the hill, john fell down, and so did jenny too"

// the following will fail
// to produce the correct output
t.expand("#boy# and #girl# went up the hill, #boy# fell down, and so did #girl#")

// sample output:
// jack and jenny went up the hill, john fell down, and so did jill

The problem is that any occurence of a #rule# will be replaced by one of its candidate values. So when we write #boy# twice, it may get replaced with entirely different names.

In order to remember values, we can use tags.

top


Tags

Tags allow to persist the result of a rule expansion to a temporary variable.

t = Tracery {[
    "boy" : ["jack", "john"],
    "girl" : ["jill", "jenny"],
    "sentence": "[b:#boy#][g:#girl#] #b# and #g# went up the hill, #b# fell down, and so did #g#"
]}

t.expand("#sentence#")

// output: jack and jill went up the hill, jack fell down, and so did jill

Tags are created using the format [tagName:tagValue]. In the above snippet we first create two tags, b and g to hold values of boy and girl names respectively. Later on we can use #b# and #g# as if they were new rules and we Tracery will recall their stored values as required for substitution.

Tags can also simply contain a value, or a group of values. Tags can also appear inside #rules#. Tags are variable, they can be set any number of times.

top


Simple story

Here is a more complex example that generates a short story.

t = Tracery {[

    "name": ["Arjun","Yuuma","Darcy","Mia","Chiaki","Izzi","Azra","Lina"],
    "animal": ["unicorn","raven","sparrow","scorpion","coyote","eagle","owl","lizard","zebra","duck","kitten"],
    "mood": ["vexed","indignant","impassioned","wistful","astute","courteous"],
    "story": ["#hero# traveled with her pet #heroPet#.  #hero# was never #mood#, for the #heroPet# was always too #mood#."],
    "origin": ["#[hero:#name#][heroPet:#animal#]story#"]
]}

t.expand("#origin#")

// sample output:
// Darcy traveled with her pet unicorn. Darcy was never vexed, for the unicorn was always too indignant.

top


Random numbers

Here's another example to generate a random number:

t.expand("[d:0,1,2,3,4,5,6,7,8,9] random 5-digit number: #d##d##d##d##d#")

// sample output:
// random 5-digit number: 68233

In

If a tag name matches a rule, the tag will take precedence and will always be evaluated.

Now that we have the hang of things, we will look at rule modifiers.

top


Modifiers

When expanding a rule, sometimes we may need to capitalize its output, or transform it in some way. The Tracery engine allows for defining rule extensions.

One kind of rule extension is known as a modifier.

import Tracery

var t = Tracery {[
    "city": "new york"
]}

// add a bunch of modifiers
t.add(modifier: "caps") { return $0.uppercased() }
t.add(modifier: "title") { return $0.capitalized }
t.add(modifier: "reverse") { return String($0.characters.reversed()) }

t.expand("#city.caps#")

// output: NEW YORK

t.expand("#city.title#")

// output: New York

t.expand("#city.reverse#")

// output: kroy wen

The power of modifiers lies in the fact that they can be chained.

t.expand("#city.reverse.caps#")

// output: KROY WREN

t.expand("There once was a man named #city.reverse.title#, who came from the city of #city.title#.")
// output: There once was a man named Kroy Wen, who came from the city of New York.

The original implementation at Tracery.io a couple of has modifiers that allows prefixing a/an to words, pluralization, caps etc. The library follows another approach and provides customization endopints so that one can add as many modifiers as required.

The next rule expansion option is the ability to add custom rule methods.

top


Methods

While modifiers would receive as input the current candidate value of a rule, methods can be used to define modifiers that can accept parameters.

Methods are written and called in the same way as modifiers.

import Tracery

var t = Tracery {[
    "name": ["Jack Hamilton", "Manny D'souza", "Rihan Khan"]
]}

t.add(method: "prefix") { input, args in
    return args[0] + input
}

t.add(method: "suffix") { input, args in
    return input + args[0]
}

t.expand("#name.prefix(Mr. )#") // Mr. Jack Hamilton

And like modifiers, they can be chained. In fact, any type of rule extension can be chained.

t.expand("#name.prefix(Mr. ).suffix( woke up tired.)#") // Mr. Rihan Khan woke up tired.

The power of methods come from the fact that arguments to the method can themselves be rules (or tags). Tracery will expand these and pass in the correct value to the method.

t = Tracery {[
    "count": [1,2,3,4],
    "name": ["jack", "joy", "jason"]
]}

t.add(method: "repeat") { input, args in
    let count = Int(args[0]) ?? 1
    return String(repeating: input, count: count)
}

// repeat a randomly selected name, a random number of times
t.expand("#name.repeat(#count#)")

// repeat a tag's value 3 ti
View on GitHub
GitHub Stars48
CategoryContent
Updated3y ago
Forks9

Languages

Swift

Security Score

80/100

Audited on Nov 29, 2022

No findings