About Terrace

Terrace is a tiny system for storing data in human-friendly text files.

It allows you to mix all sorts of text and data formats together in an intuitive way without getting caught up in syntax pomp and ceremony.

title An Essay on Rockets
date 2022-02-22
by John Doe

heading The ideal rocket equation

equation LaTeX
  \Delta v=v_{\text{e}}\ln {\frac {m_{0}}{m_{f}}}=I_{\text{sp}}g_{0}\ln {\frac {m_{0}}{m_{f}}}
  
markdown
  A fundamental equation in astrophysics, the *ideal rocket equation* describes the total potential velocity of an idealized vehicle propelled by expelling some of its own mass.
  
...

You can use Terrace to write documents, web pages, configuration files, data storage, or whatever else you come up with!

This page is written using Terrace!

Background

Terrace was originally envisioned out of frustration at the massive usability gulf for embedding custom blocks in simple, human-friendly markup formats such as Markdown, and capable but complex block-based systems such as Project Gutenberg and Editor.js.

The Complexity Problem

A simple markdown document:

# My Page Title

Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.

1. Magna id sint consequat quis esse tempor.
2. Veniam eu id esse occaecat eu.
3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.

[Put my-custom-component here somehow]

Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.

That same document in a simplified Editor.js schema:

[
  {
    type: "header",
    data: {
      level: 1,
      text: "My Page Title"
    }
  },
  {
    type: "paragraph",
    data: {
      text: "Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad."
    }
  },
  {
    type: "list",
    data: {
      style: "ordered",
      items: [
        "Magna id sint consequat quis esse tempor.",
        "Veniam eu id esse occaecat eu.",
        "Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua."
      ]
    }
  },
  {
    type: "my-custom-component",
    data: {
      option1: "value",
      option2: "value",
      text: "Text"
    }
  },
  {
    type: "paragraph",
    data: {
      text: "Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim."
    }
  }
]

MDX & MDC

Other attempts, such as MDX and MDC have been made to solve this problem, allowing you to incorporate complex content inside markup documents.

However, they suffer from a number of problems:

  1. Tightly coupled to the JavaScript ecosystem
  2. Lock you out of other markup formats, such as AsciiDoc
  3. Difficult to parse - Markdown was not intended as a general-purpose syntax
  4. Data can't be extracted from markup without rendering the entire document to HTML
  5. Ugly. Just look at this:
# My Page Title

Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.

1. Magna id sint consequat quis esse tempor.
2. Veniam eu id esse occaecat eu.
3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.

[MDX Example]
<MyCustomComponent option1={{'value'}} option2={{'value'}}>
  Text
</MyCustomComponent>

[MDC Example]
::MyCustomComponent{option1="value", option2="value"}
  Text
::


Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.

Terrace Prototype

I first attempted to solve this problem using an S-Expression-based syntax for defining blocks.

(heading
  (level 1)
  My Page Title
)

(markdown
  Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
  
  1. Magna id sint consequat quis esse tempor.
  2. Veniam eu id esse occaecat eu.
  3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
)

(my-custom-component
  (option1 value)
  (option2 value)
  Text
)

(markdown
  Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.
)

While quite flexible and clean-looking, problems became apparent with handling whitespace and escaping parenthesis in text. The parser quickly became much more extensive than initially envisioned.

Nevertheless, it proved the concept that cleanly combining functional blocks and markup text was quite reasonable.

Not long after, I stumbled upon Tree Notation by Brek Yunits. Playing around with it, the potential of just getting rid of the parenthesis was quite apparent. Simpler syntax for humans to write, and far less work required for parsing & escaping since the only control characters are newlines and spaces:

heading My Page Title
  level 1
  
markdown
  Occaecat fugiat est ullamco aliqua ea dolor nostrud. Reprehenderit nisi minim sit commodo deserunt ullamco deserunt aute ex exercitation Lorem deserunt ad.
  
  1. Magna id sint consequat quis esse tempor.
  2. Veniam eu id esse occaecat eu.
  3. Minim commodo nostrud eiusmod excepteur ea sint in enim esse aliqua.
  
my-custom-block
  option1 value
  option2 value
  Text
  
markdown
  Dolor adipisicing amet aliquip nulla occaecat Lorem excepteur veniam. Voluptate pariatur sint anim tempor aliquip in. Id nulla est irure officia occaecat enim.

Ultimately though, using Tree Notation still proved too messy. After working with the grammar system and spreadsheet-style data model for a good while, my gut said all this use case needed was a simple line-based parser.

I started Terrace to implement a similar syntax, but with the following goals:

  1. Make as few decisions as possible for the user
  2. Be as fast, tiny, and light on resources as possible

Six or seven parser rewrites later, it finally works mostly how I envisioned it. :)

Development Goals

Terrace development is guided by the following goals.

  • Tiny, primitive core - no “framework”
    • Use the most boring patterns possible for implementing the core library.
    • Avoid dependencies
    • Easily port Terrace in any language or environment
  • Avoid dynamic allocation
    • Since we can work directly off of string slices without the need for ASTs and escaping systems, might as well avoid dynamic allocations altogether!
  • Minimize assumptions
    • Let people use Terrace as a building block for all sorts of languages

Make note of this before submitting change requests. If your changes don't fit these goals, they may be denied. In which case, you could try building a separate library on top of the Terrace cor.

Maintained by the Terrace Team. Find an issue? Let us know!

Site contents licensed under the CC BY 3.0 license
All code examples licensed under the MIT license