Skip to main content

Generative grammars for idea generation.

Project description

PrestoPlot

PyPI version GitHub Actions - CI Test coverage

A library and tool for text generation, inspired by Tracery.

PrestoPlot is a tool for idea generation, name generation, and other tomfoolery when you should otherwise be writing.

Goes best with the oracles from the PrestoPlot Oracles repository.

Install

PrestoPlot is available from PyPI:

pip install prestoplot

Usage

PrestoPlot may be invoked with the presto CLI script:

presto --help

The “oracle” consulted directly must include a Begin: stanza:

$ cat names.yaml
Begin:
  - "{Name}"

Name:
  - George
  - Martha

$ presto run names.yaml
George

Generative Grammars

The main feature right now is a generative grammar that uses a simple YAML-based language and Python f-string syntax to create “oracles” for idea generation.

The best way to learn the grammar is to look at examples. We’ll consider the YAML for generating a Pirate story, which begins like this:

include:
  - setup

Begin:
  - "{PiratesOracle}"

There is the Begin: stanza that we require to directly consult an oracle. This contains a list of strings that may be chosen from by the random generator. In this case, we have an f-string template that invokes PiratesOracle. We find that below:

PiratesOracle:
  - |
    {Setup}
    - {Letters.One}
    - {Letters.Two}
    - {Letters.Three}
    - {Letters.Four}
  - |
    {Setup}
    - {CutlassDagger.One}
    - {CutlassDagger.Two}
    - {CutlassDagger.Three}
    - {CutlassDagger.Four}

We see another list of strings. | followed by an indented new line means to treat what follows at that indentation level as a literal string, instead of YAML:

{Setup}
  - {Letters.One}
  - {Letters.Two}
  - {Letters.Three}
  - {Letters.Four}

So this is a string with a Markdown-style list, instead of a YAML list, all because of the |.

So here we see Setup invoked, and then Letters invoked four times. Letters is defined below:

Letters:
  - mode: pick
  - "Betrayal and treachery!"
  - "Captured {Nationality} charts, carefully copied, and used by the Royal Navy."
  - "Dolphins, seen frolicking in the bow-wake of a ship, perhaps leading it toward its goal."
  - "Flotsam and jetsam, washed ashore after a sea-battle."
  - "Fo’c’sle gossip blaming the ship’s misfortunes on a crewman who killed an albatross."
  - "Forged documents, implying that their bearer speaks for the Crown."
  - "Hidden reefs, which at low tide endanger any ship that passes over them."

We have another list, containing piratical thematic elements. mode: pick tells the generator to randomly pick from among them, then remove that option from consideration for future picks. The normal mode is reuse which allows list items to be re-used by the generator. Another mode, markov, tells the generator to build a Markov chain from the list, as with these name lists.

Going back to PiratesOracle, we see that Letters is invoked four times, each time with a new key. The values of the keys are important only to the reader. Each new key acts as a fresh seed for the random generator when working inside that stanza. For instance, if {Letters.One} picked the element "Captured {Nationality} charts, carefully copied, and used by the Royal Navy.", the value One provides the seed for picking a Nationality, say, English. Later, if {Letters.Two} encounters another element containing {Nationality}, the key Two will provide a different seed for picking a nationality the second time.

The plot thickens when we examine the include stanza, which includes the setup.yaml file next door. This file includes more files. We will next examine characters.yaml.

Inside of characters.yaml we find this fascinating set of stanzas:

Sex:
  - male
  - female

He:
  - >
    {'She' if Sex[key] == 'female' else 'He'}
his:
  - >
    {'her' if Sex[key] == 'female' else 'his'}
His:
  - >
    {'Her' if Sex[key] == 'female' else 'His'}
hero:
  - "{'heroine' if Sex[key] == 'female' else 'hero'}"

With this set of tools, we could write the following string:

That {hero.protag}! {He.protag} sure loves {his.protag} mom.

The long and short of it is that, depending on the sex of the protagonist, this will render either:

That heroine! She sure loves her mom.

or:

That hero! He sure loves his mom.

So here we see that inside of f-string syntax, we can use pythonic expressions, and the variable key contains the key from the outer scope: {He.protag} assigns the value "protag" to key. {Sex[key]} will reliably produce the same result for the same key (assuming the same initial seed).

Everything else is just YAML syntax and Python f-string expressions.

About

I wrote PrestoPlot to support idea generation and name generation for my pulp-inspired science fiction space opera series, Salvage of Empire:

When his brother-in-law threatens to reveal his terrible secret, Director Kolteo Ais must sacrifice everything he has worked for to save the Galactic Empire—and his marriage—from utter ruin.

CHANGES

0.5

  • Allow instances of random.Random() as seeds.

  • Rename ChangeLog to CHANGELOG.rst, include in long_description.

  • Changelog is now manually written, instead of derived from git logs.

0.4

  • Fixed major instability of markov generator in the presence of a seed.

0.3.4

  • Update packaging and requirements

0.3.3

  • Improved badge

  • Update README w/ build status and better lede

  • Update dev and test requirements

  • Allow customization of start key when rendering story

  • Add python 3.8 to tests

0.3.2

  • Remove more debug logging

0.3.1

  • Remove debug logging

  • Add extra whitespace

  • Add known third parties to isort cfg

  • Add twine dev dependency

0.3

  • Add msgpack-compiled test data

  • Add new more efficient storages

  • Improve runtests invocation

  • Tailor flake8 exclusions

  • Pin versions; add msgpack

  • Add some debug logging

  • Add pyyaml requirement

  • Revert “Use strictyaml instead of yaml”

  • Add dev and test requirements

  • Move prestplot package into src/

  • Add Travis CI integration

  • Add .isort.cfg

  • Use collections.abc in prep for python 3.8

  • Use strictyaml instead of yaml

  • Add requirements.txt

  • Use longer python environ specifiers for tox.ini

  • Add project URLs

0.2

  • A whole bunch of refactoring

  • Improve dev and test harness w/ dev requirements and test scripts

0.1.3

  • Add extended documentation on generative grammar syntax

  • Further README improvements

0.1.2

  • Improve README

0.1.1

  • Add --seed option to CLI for pre-seeding oracles

  • Add install/usage documentation to README

  • Add documentation to CLI

0.1

  • Add basic test to exercise render_story()

  • Fix email, summary

  • Remove future features from setup.cfg

  • Finish writing README

  • Fix link in README

  • Initial commit

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

prestoplot-0.5.2.tar.gz (38.6 kB view details)

Uploaded Source

File details

Details for the file prestoplot-0.5.2.tar.gz.

File metadata

  • Download URL: prestoplot-0.5.2.tar.gz
  • Upload date:
  • Size: 38.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.2

File hashes

Hashes for prestoplot-0.5.2.tar.gz
Algorithm Hash digest
SHA256 6871ca6c6a23a143550fd21d36882296fbaa45cffc90a57c208829c1609d8ef5
MD5 ee292bd09303ff6ce6eb509ac0d94b91
BLAKE2b-256 ef12aa642a30ad3197c024541f4c70f3001fed5b3992f39e0d6cbb9cb24d19ac

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page