Parsing Twee: Part 1: It’s All Passages

Parsing Twee

Twee is a human-readable format for saving the “code” of a Twine story. It predates Twine by a few years and has been a format used by many to create Twine-compatible projects for a decade.

It’s All Passages

Since many people come to Twee from Twine, it is easier to think in terms of its visual presentation.

In Twine, all content is contained within passages. These are different entries in the visual editor in the form of boxes.

Twee shares this same idea. However, as it is not as visual, passages are represented through two areas: its header and content.

Passage Header

:: Start

A passage header begins with two colons. These signal that a passage header is starting that will extend to the end of the line.

After the the two colons is the name of the passage. In most examples, the full passage header will contain these two elements, the starting colons and the name of the passage.

Optionally, passage headers can also contain tags and metadata. Depending on the story format, tags can be used as a way organize passages or even toward more programmatic ends. The metadata, on the other hand, is for Twine compatibility. It encodes extra information such as where to place the passage visually and what its size should be in the visual editor, should it ever be opened

:: An overgrown path [forest spooky] {"position":"600,400","size":"100,200"}

Passage Content

:: Start
Some text

Passages can contain any valid UTF-8 content. They may contain text in the form of macros, comma-separated values, or even other encoded formats like JSON.

For the point-of-view of Twee, the content of most passages is not important. An author has added the content and the “job” of Twee, if it can be said to have one, is to simply store it.

Special Passages

Everything in Twee is part of a passage. This means that any special information about the story itself is also part of passages. This is why special passage names are important.

  • StoryTitle: The project’s name
  • StoryData: Metadata about the project including ifid and other optional information such as story format, its version, and the starting node of the story (when not Start)
  • Start: The first passage in the story

Special Tags

While tags can be used in a number of different ways with Twee, two names are reserved.

  • script: Signifies that the passage’s contents are JavaScript code
  • stylesheet: Signifies that the passage’s contents are CSS style

All Projects Begin with Start

:: Start
It starts with one thing 

The special passage name of Start is important, as it is the first passage in the story unless specified by optional data in the StoryData passage.

When writing a project in Twee, it should begin with Start. (Unless changed via the StoryData passage, of course.)

From Twine 2 (with love)

Twine 2 does not automatically provide the ability to work with Twee. For that, the story formats Entwee and Entweedle can be used. These work to “export” a story from Twine 2 into Twee.

:: StoryTitle
Twee Example

:: UserScript[script]

:: UserStylesheet[stylesheet]

:: Start
It starts with one thing 

When using Entweedle, the names UserScript and UserStylesheet are used for passages that contain the Story JavaScript and Story Stylesheet data from Twine.

As Twee looks for the script and stylesheet tags, these are treated as if they were any other script or stylesheet passages.

Parsing Twee

Consider the following code from Extwee (my own Twee compiler).

// Check if there are extra content in the files
// If so, cut it all out for the parser
if(fileContents[0] != ':' && fileContents[1] != ':') {
let firstPassagePos = fileContents.indexOf('::');
fileContents = fileContents.slice(firstPassagePos, fileContents.length);
// Split the file based on the passage sigil (::) preceeded by a newline
let parsingPassages = fileContents.split('\n::');
// Check if any passages exist
if(parsingPassages == 0) {
throw new Error("No passages were found!");

The code looks for the first instance of the double-colon usage (signifying that there is a passage header) and then discards all data before it. (fileContents contains the text content of some file passed to the function to parse.)

Passages are then split using the knowledge that a new-line character will appear before a double-colon usage. The resulting array contains the passages (if any were found).

The most basic parsing of Twee is not much more than the above. Test for double-colon usage, read the data, and then prepare for the harder steps of dealing with the optional tags, metadata, and retaining any content found in the passage. (Greater details can be found in examining the TweeParser.js file.)