0.1.9 • Published 4 months ago

fantasy-timeline-parser-beta v0.1.9

Weekly downloads
-
License
GPLv3
Repository
github
Last release
4 months ago

FantasyTimelineParser

Version License: GPL v3

This is a TypeScript parser for FantasyTimeline, a descriptive language designed for writers and worldbuilders to quickly create a rough timeline for their setting.

Idea and motivation

I wanted to learn more about worldbuilding and create a simple, rough timeline for my world, but I couldn't find an easy tool for the job.

My requirements for such a tool were:

  • Complete offline usage
  • Should be code-based (definition)
    • Easy to read without visualization
    • Changes can be easily tracked via Git
  • Support for different calendar systems
  • Elements on the timeline
    • Can be a single moment (day)
    • Can span a time range (start and end day)
    • Can be grouped into larger eras
  • A visual representation

So I decided to create my own tool (and language). This parser converts a FantasyTimeline (.ft) definition file into a JavaScript/TypeScript object. From there, other tools (see Future Plans at the bottom) can be used to generate a visual representation.

Installation

npm install fantasy-timeline-parser

Language structure

The language consists of two parts:

  1. Header Section – Defines the structure of the timeline.
  2. Element Section – Defines events and eras that occur on the timeline.

These sections are separated by a line with three dashes (---). The result after parsing is a JavaScript object of type FantasyTimeline.

Header section

The header section consists of sections and properties that define the structure of the timeline. A section is marked using square brackets ([ ]), while properties are written as an identifier followed by a colon (:) and a value.

Sections can have multiple opening and closing brackets, indicating subsections, with the number of brackets defining the subsection level. There is no explicit "end section" definition, any property or subsection that appears after a section automatically belongs to it, similar to chapters in a book. A subsection of level x always belongs to the preceding section or subsection of level x-1.

Each property and section must be placed on its own separate line.

General Settings

The first two properties, name and description, are not tied to any specific section and must be defined first. They serve only for visual representation and completeness.

PropertyDescriptionDefault valueRestrictions
nameThe name of the timelineUntitled
descriptionA small description for the timeline

Example:

name: Middle-earth
description: A timeline of Middle-earth, a world by J.R.R Tolkien

Age Settings

Next, there are settings that define the range and units of the timeline.

These settings can either be defined for a single age using the [Year Settings] section or for multiple ages, where the timeline is divided into different ages (like in Lord of the Rings). In this case, the [Ages] section and its subsections are used. Only one of these options should be defined in any given timeline.

Year Settings

All properties are defined within the [Year Settings] section.

PropertyDescriptionDefault valueRestrictions
unitBeforeZeroThe unit for parsing and display for years before year zeroBCneed to start with a letter
unitAfterZeroThe unit for parsing and display for years after year zeroADneed to start with a letter
minYearThe first year in this timeline-1000can either be just a number (-1000) or the number and the unit (1000 BC)
maxYearThe last year (inclusive) in this timeline1000can either be just a number (1000) or the number and the unit (1000 AD)
includeYearZeroIf the timeline should include a year 0 between negative years and positive yearsfalsetrue or false

Example:

[Year Settings]
unitAfterZero: AD
minYear: 2000
maxYear: 2025

Ages

The [Ages] section replaces the standard [Year Settings] section. To define different ages, create a subsection with the age name as the value. For example, a subsection might look like [[The Third Age]]. Each age has the same properties as the Year Settings, but with slight differences in the default values.

PropertyDescriptionDefault valueRestrictions
unitBeforeZeroThe unit for parsing and display for years before year zerobefore <AGE_NAME>need to start with a letter
unitAfterZero or unitThe unit for parsing and display for years after year zeroafter <AGE_NAME>need to start with a letter
minYearThe first year in this timeline1can either be just a number (1) or the number and the unit (1 <AGE_UNIT>)
maxYearThe last year (inclusive) in this timeline1000can either be just a number (1000) or the number and the unit (1000 <AGE_UNIT>)
includeYearZeroIf the timeline should include a year 0 between negative years and positive yearsfalsetrue or false

Additionally, each unit of an age (both before and after year zero) can only exist once in the entire timeline. If a unit is repeated, it will not be possible to deterministically parse dates later on.

Example:

[Ages]

[[Third Age]]
unit: T.A.
minYear: 1601
maxYear: 3021

[[Fourth Age]]
unit: Fo.A.
maxYear: 172

Calendar Settings

The [Calendar Settings] section defines the calendar (months and days) for a year. It can be defined using static values for staticDaysPerMonth and monthsPerYear, resulting in all months having the same number of days. Alternatively, an array (daysPerMonth) can be used to specify the number of days in each individual month.

The default calendar generated by the parser is similar to the Gregorian calendar, except without leap years (February always has 28 days).

PropertyDescriptionDefault valueRestrictions
daysPerMonthA list of days of each month of the year.31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31start and end with []; each value separated by a comma (,); only numbers allowed
staticDaysPerMonthA single number indicating the number of days in each month.30a number
monthsPerYearA single number indicating the number of months in each year.12a number
monthNamesA list of the names of all months.January, February, March, ...start and end with []; each value separated by a comma (,); each month name needs to start with a letter
dateFormatThe date format wor parsing and displaying dates.d/m/yeither d/m/y or m/d/y

!WARNING Leap years/days are currently not supported. They may be added in the future if a suitable solution is found (either by the creator or someone else).

Tags

The tags section defines tags that can be assigned to elements. Similar to other assignments, a tag is the same as a property. The key for a tag can be anything, as there are no predefined tag keys; they can all be defined by the user. The value of the tag should represent a color, either as a hex value or a written name.

Example:

[Tags]
tagName1: red
tagName2: #00ff00

These tag names can later be used in the element section to tag specific elements. Any word starting with a hashtag (#) in the description of a timeline element will be extracted and converted into a tag. The hashtag will not be removed from the description, but it will still appear.

Element section

The element section consists of two types of elements: eras and events.

Eras

An era (or period, epoch, etc.) is a group of multiple elements (both eras and events). Eras can be nested indefinitely.

Eras are defined similarly to sections in the header section. They start and end with the same number of square brackets [ ], with the era's name in the middle (e.g., [The Lord of the Rings] or [[The Return of the King]]). As with the header sections, everything following the opening of an era belongs to that era. Also, like header sections, an era of depth x can only follow an era of depth x-1.

However, unlike in the header section, an era can be closed prematurely with an "end section" tag of the same depth as the start (e.g., start with [LotR] and end with [End]). This is mainly for keeping elements organized.

These end section tags are optional because starting a new era of the same depth x or lower will automatically close all eras of depth x or higher. While this may sound complicated, examples will make it clearer.

In addition to the start, end, and child elements, eras can also have descriptions. After the start of an era, every line until the next element starts will be added to the era's description.

Example:

[The Hobbit]
All events happening during and in the book "The Hobbit"

...

[End]

Events

Events are individual occurrences on the timeline. They consist of a date or date range (start and end date), a name, and, like an era, they can (but don't have to) include a description.

An event starts with a > followed by the date (or date range), a colon (:), and then the name of the event on the same line.

Similar to an era, the lines following the event, until the next element starts, will make up the event's description.

Example:

> 3.12.2015: Cool event
This is a description
Even on multiline
Parsing dates

The date in an event can be defined in multiple ways. Each date needs to have a year (and a unit, if using multiple ages) and can have a month, as number of name, and can have a day. Dates will be parsed depending on the date format, so they can either be in d/m/y (default) or m/d/y order.

Here are some examples (all of them assume our Gregorian calendar with d/m/y format):

  • 3.12.2015 -> The 3rd of December 2015
  • 3. December 2015 -> The 3rd of December 2015
  • 12.2015 -> December 2015 -> Starts on the 1st of December and ends on the 31st (implicit range)
  • December 2015 -> December 2015 -> Starts on the 1st of December and ends on the 31st (implicit range)
  • 2015 -> Starts on the 1st of January and ends on the 31st of December (implicit range)
  • 2015 AD -> 2015 -> Starts on the 1st of January and ends on the 31st of December (implicit range)
  • 2015 BC -> -2015 -> Starts on the 1st of January and ends on the 31st of December in the year 2015 BC (implicit range)

Here are some examples with m/d/y format:

  • 3.12.2015 -> The 12th of March 2015
  • 3. December 2015 -> The 3rd of December 2015
  • 12.2015 -> December 2015 -> Starts on the 1st of December and ends on the 31st (implicit range)

In a single-age calendar, it is always assumed that a date without a unit means the unit is unitAfterZero (the positive one). However, this does not apply to a multi-age calendar, and therefore, the year unit must be explicitly defined for every date.

Each date can also include an optional fuzzy marker in the form of a ~ at the beginning of the date. This marker indicates that the date is approximate or circa. While it does not affect the date parsing, it can be used later for a more informative visual representation.

  • ~December 2015 -> circa December 2015 -> Still starts at the 1st of December and ends on the 31st (implicit range)
Parsing date ranges

Date ranges are similar to single dates but consist of an explicit start and end date. These two dates are separated by a dash (-) and are parsed in the same way as individual dates.

Here are some examples (all assuming the Gregorian calendar):

  • 3.12.2015 - 4.9.2018 -> From the 3rd of December 2015 until the 4th of September 2018
  • December 2015 - ~2018 -> Starts on the 1st of December 2015 and ends approximately on the 31st of December 2018

The start date must always be earlier than or the same as the end date. Date ranges can also span across multiple ages, for example:

  • 3.12.2015 First Age - 4.9.2018 Second Age

!WARNING Parsing of dates from events can involve year units and month names, so there are restrictions in place to ensure unique parsing. While it's possible to trick the system and get more than one parse result per date, this should only occur if intentionally done. In normal usage, it should work fine. If issues arise, feel free to create an issue, and it will be looked into.

Comments

The language supports simple comments. Anything on a line after a double slash (//) is considered a comment and will be ignored until the end of line during the parsing process.

Example

Here is a small extract from the larger "Middle-earth" example found in the examples folder.

name: Middle-earth
description: A timeline of Middle-earth, a world by J.R.R Tolkien (only parts of the Third and Fourth Age)

[Ages]

[[Third Age]]
unit: T.A.
minYear: 1601
maxYear: 3021

[[Fourth Age]]
unit: Fo.A.
maxYear: 172

> 1. March 2931 T.A.: Aragorn, son of Arathorn, Heir of Isildur, is born.

> 15. March 2941 T.A.: Thorin Oakenshield meets with Gandalf at the Prancing Pony in Bree
They agree on setting out on the Quest of Erebor.

> 6. April 2941 T.A.: Gandalf visits Bag End but Bilbo Baggins is missing
He talks with Holman Greenhand and realises Bilbo is the right person for the Quest for Erebor.

[The Hobbit]
All events happening during and in the book "The Hobbit"

> 25. April 2941 T.A.: Gandalf visits Bag End looking for somebody to take part in an adventure but Bilbo rejects the offer.

> 26. April 2941 T.A.: Thirteen dwarves and Gandalf arrive at Bag End
Bilbo learns of the Lonely Mountain and Smaug and agrees to accompany the dwarves and Gandalf on their adventure.

> 27. April 2941 T.A.: Bilbo starts together with the dwarves and Gandalf his journey to the Lonely Mountain

> 1. July 2941 T.A.: Bilbo meets Gollum under the Misty Mountains and finds the One Ring

> 22. September 2941 T.A.: Thorin and the company arrive in Lake-town

> 1. October 2941 T.A.: Smaug launches an attack on Esgaroth and is killed by Bard the Bowman

> 10. October 2941 T.A.: Battle of Five Armies is fought
Thorin Oakenshield, Fíli and Kíli are slain. Beorn kills Bolg son of Azog.

> 1. May 2942 T.A.: Bilbo and Gandalf reach Rivendell after their adventures at the Lonely Mountain

> 22. June 2942 T.A.: Bilbo Baggins returns to Bag End after the Quest of Erebor and learns he has been declared dead
[End]

// Even tho this happens during the time of "The Hobbit", (as far as I know) it is not mentioned in the book
// so it is not in the section
> 1. May 2942 T.A.: Sauron returns to Mordor

> 2951 T.A.: Sauron reveals himself in Mordor and starts to rebuild Barad-dûr

> 2953 T.A.: Last meeting of the White Council

> 2954 T.A.: Orodruin re-awakens

Future plans

Once the parser is completed, the next step will be to create a Vue component to visually display the parsed timelines. Afterward, a VSCode extension for .ft files will be developed, featuring syntax highlighting and error display, along with integrating the Vue component into a VSCode webview.

Credits and inspiration

As mentioned in the motivation section, several other tools inspired the creation of this tool. These are listed in alphabetical order:

Disclaimer

I am not confident in my english writing, so some of these texts are improved by AI. They are not completely written from AI by analyzing the code, they are created by taking my own text and "improving" it. I also proof read each and every "improved" text, to check if any information got lost or the AI invented some new things.

0.1.9

4 months ago

0.1.8

4 months ago

0.1.7

4 months ago

0.1.6

4 months ago

0.1.5

4 months ago

0.1.4

4 months ago

0.1.3

4 months ago

0.1.2

4 months ago

0.1.1

4 months ago

0.1.0

4 months ago