Sharpee
Chord / Project & Files / Multi-file stories

Multi-file stories

As a story grows, split it across files. A .story file imports a .chord fragment, and the compiler splices that fragment in at the import line, exactly as if you had pasted its contents there.

story
  title: Harbor
  authors:
    You
  id: harbor

create the Lighthouse
  a room

  A tall lighthouse by the sea.

import "regions/harbor"

The fragment lives beside the story as regions/harbor.chord:

## Harbor's rooms, kept in their own file.

create the pier
  a room
  north to the Lighthouse

  A wooden pier over cold water.

You write import "regions/harbor" with no extension: .chord is assumed, so the file on disk is regions/harbor.chord. Paths are story-rooted: every import, in the main file or inside a fragment, is resolved from the .story file's directory.

An import is a paste

Splicing happens before anything is checked, so a fragment shares one namespace with the main story. The pier's north to the Lighthouse exit reaches a room declared in the main file; a fragment freely references anything the story defines, and the story references anything the fragment adds. The import's position is the arbitration position for anything order-sensitive: the fragment behaves as if written on the import line.

Because it is a plain paste, there are no new rules to learn: ordering, name collisions, and cross-references all work exactly as they would in one file.

What a fragment may hold

A .chord fragment holds any complete declaration the story does (rooms, people, sequences, define phrasebook blocks, ## comments) — including grammar alterations, so a story can keep its extend action and remove from action blocks in a grammar-tweaks.chord of their own — with one exception:

  • No story header. The story block lives only in the main .story file, so the main file is never importable.

A fragment must hold whole declarations: you cannot split a single room or rule across two files.

Imports nest

A fragment may carry import lines of its own. Each is a paste at its position inside the fragment, so the assembled order is depth-first at every import line — the same one rule, applied again. Paths stay story-rooted inside a fragment: regions/market.chord writes import "npcs/teisha", not a path relative to itself.

## regions/market.chord

import "npcs/teisha"

create the Grocery Stall
  a room

A fragment reached twice along two paths is pasted twice and collides as an ordinary duplicate declaration — there is no import-once rule. A chain that returns to a file already being spliced (a.chord → b.chord → a.chord) is analysis.import-cycle on the import line that closes it, with the chain in the message; that import is dropped and the splice continues.

Diagnostics

SituationDiagnostic
import "gone" and gone.chord is not foundanalysis.import-unresolved
Fragment contains a story headeranalysis.import-fragment-story
An import chain returns to a file already being splicedanalysis.import-cycle
Fragment does not parse into whole declarationsanalysis.import-fragment-content
import without a quoted file nameparse.import-form

The import-fragment-* messages point into the fragment file (prefixed with its name); import-unresolved points at the import line in your main story.