Fbld

fble-0.5 (2025-07-13,fble-0.4-212-ga8f8ad0f)

Introduction

Fbld is a lightweight markup language for structured text. It aims to balance the readibilty of markup languages like asciidoc and markdown with the extensibility and clarity of structure of markup languages like xml.

Note: Fbld should not be confused with the predecessor to fble, which was also called fbld. We reuse the name fbld in this case to suggest the term "Fable Doc Language".

Goals

The primary design goals of fbld are:

Use Cases

The original use cases motivating development of the fbld language are:

Alternatives

There are many alternatives for document markup. Each of the ones I tried had their own issues. Some of the issues are subtle and quite subjective. Here are a list of alternatives I considered before deciding to develop my own markup language.

Markdown
Asciidoc
XML
roff
tcl
doxygen

Example

This section provides an example of fbld markup text which will be referenced through the remainder of the fbld specification. It's a contrived document explaining the risks of using malloc:

@title Malloc Reminder

It's @emph[really] important to check the result of the @code[malloc]
function in C, in case it returns @NULL. See @link[the malloc man
page][https://manpages.org/malloc] for more details.

@section[The Risks of Malloc]
 It's possible that @code[malloc] is unable to allocate memory. If that's
 the case, it will return @NULL. If you try to access the returned pointer,
 bad things can happen.

 So you should always check the result of @code[malloc] for @NULL.

We have discussed that it's important to check the result of @code[malloc]
for @NULL.

Fbld documents are a mix of plain text and commands introduce with the @ character. The syntax and interpretation of these commands are described in detail in the following sections.

Document Structure

Internally, fbld documents are represented as a mix of plain text and commands.

A command has a name and zero or more arguments. For example, @NULL is a command with name NULL and no arguments. @emph[really] is a command with name emph and single argument really, and @link[the malloc man page][https://manpages.org/malloc] is a command with name link and two arguments.

Arguments to commands are fbld markup which can themselves have additional commands in them.

Abstract Syntax

Here's an abstract syntax for fbld document structure:

Text: A string of plain text characters.

Command:
    command (name : Text) (args : [Markup])
  ;

Markup:
    plain (text : Text)
  | command (command : Command)
  | sequence (markups : [Markup])
  ;

Document Syntax

The syntax of fbld leverages whitespace and other punctuation to reduce the need for brackets around command arguments everywhere. The goal of the syntax is to make fbld documents easy to read while still providing clear rules for forming commands and arguments to commands.

There are two main kinds of syntax in fbld: inline structured text and block structured text.

Inline Structured Text

Inline structured text is used for marking up paragraphs or short lines of text. Whitespace is preserved in inline structured text.

Plain Text

Inline structured text interleaves plain text and commands. Plain text is any text that comes between commands. It can be written directly. For example:

Hello, how are you?

The following escape sequences are supported in plain text:

\\
\
\@
@
\[
[
\]
]
\n
Newline

Any other character following a backslash is considered reserved for future use and is not currently allowed. The escape sequences make it possible to include @ in plain text, and make it possible to include [, ], and newline in plain text where they may not otherwise be allowed. For example:

My email is ruhler\@degralder.com.

Command

An inline command starts with the character @ followed by a command name made up of one or more characters from the set [a-zA-Z0-9_]. Arguments to commands are provided following the command name.

Inline Args

The standard way of supplying arguments to a command is using inline args. An inline arg is inline structured text in square brackets following a command or other inline arguments to a command.

For example, the following @emph command has a single inline argument:

@emph[You must check for @NULL!]

Inline Literal Args

Inline literal args are used to pass literal text as an argument. A literal inline arg is literal text in braces following a command or other inline arguments to a command. This is primarily useful for writing plain text with @ characters without having to escape them. For example:

@email{ruhler@degralder.com}

There is no processing of escape sequences in inline literal args. The characters are read directly. Braces nest inside the literal argument, so you can include close braces in the literal text as long as there is a matching, properly nested open brace.

For example, the following inline literal argument specifies an fble function type, which includes both @ and } characters:

@emph{(Bool@) { Bool@; }}

The argument to the command in the following example includes the backslash characters:

@emph{The escape sequences in fbld are: \\, \@, \[, \], and \n.}

Block Structured Text

Block structured text is used for marking up larger sections of document spanning multiple lines or paragraphs. It provides a variety concrete syntax options for specifying arguments to commands. Documents are parsed as block structured text.

Block Commands

Commands are introduced in block structured text using the @ symbol, as in inline structured text, except the commands must start at the first column of text. For example, here is a sequence of four commands in block structured text:

@title[My Favorite Document]
@author[Richard]{ruhler@degralder.com}
@date[November 3rd]
@abstract[A study of my @emph{most} favoriate document in the world]

Block commands take inline arguments and inline literal arguments just like commands in inline structured text. Blank lines between commands act as delimiters.

Implicit Block Command

If a line of block structured text starts with plain text rather than a block command, it and following lines of text up to the next blank line are interpreted as inline structured text and passed as a single argument to an implicit command named .block.

From the malloc example above, we had:

It's possible that @code[malloc] is unable to allocate memory. If that's
the case, it will return @NULL. If you try to access the returned pointer,
bad things can happen.

So you should always check the result of @code[malloc] for @NULL.

This is parsed as if it were instead written:

@.block[It's possible that @code[malloc] is unable to allocate memory. If that's
the case, it will return @NULL. If you try to access the returned pointer,
bad things can happen.\n]
@.block[So you should always check the result of @code[malloc] for @NULL.\n]

This makes it possible to identify and format paragraphs when processing fbld marked up text, without the clutter of an explicit paragraph tag for each paragraph.

The newline on the last line of inline structured text forming the implicit block command is included in the argument passed to the implicit block command.

Explicit Implicit Block Command

Occasionally it happens that you want to write an implicit block command that starts with an inline command. For example:

@l{malloc} is a very interesting function to call, because it could be
that it returns @NULL.

In this case, the command @l is intended as inline structured text of the paragraph, not as a separate block command. To parse this as an implicit block command, use two @ characters at the start of the line. The extra @ indicates the following text should be parsed as an implicit block command, not including the first @ character.

In this case, for example:

Pay attention to the next paragraph of this document.

@@l{malloc} is a very interesting function to call, because it could be
that it returns @NULL.

Same Line Arg

Text on the same line as a block command is parsed as inline structured text and passed as an additional argument to the block command. For example, the command:

@title[My Favorite Document]

Could equivalently be written as:

@title My Favorite Document

This reduces some of the clutter of brackets in block structured text.

The same line arg can contain inline commands, and need not be the first argument to the block command. For example:

@callout[warning] You @emph{must} check for @NULL!

Is equivalent to:

@callout[warning][You @emph{must} check for @NULL!]

The trailing newline at the end of the same line argument is not included in the argument to the block command.

If there is no space on the same line after the command and any inline arguments, no same line argument is passed to the command.

Next Line Arg

If the line following a block command starts with a single space, it indicates the start of a next line argument. The next line argument includes all the following lines at the same level of indentation, with the leading space character stripped from the argument.

For example, the following block structured text has two invocations of the @example command, each with a single next line argument.

@example
 This is an example of next line argument text.

 That spans multiple paragraphs

@example
 Here is a second example block command.

The next line argument is parsed as block structured text.

Next line argument ends at the first line of text indented at the same level or less than the original block command.

Notice that, because of next line arguments, block commands end up indented in practice, even though we said they have to start at the beginning of the line. This is because the indent is stripped away from the lines before the block command gets processed.

Literal Next Line Arg

If the block command ends with a space character followed by a single @ character, it is assumed to have a next line argument that is interpreted as literal text. This is convenient for writing large blocks of literal text such as code blocks.

For example:

@code @
 Here is a bunch of text not interpreted with @ and other stuff
 that wouldn't parse as fbld text.

Continuation

If the line immediately following a block command starts with an @ character not associated with another command, it continues the arguments being passed to the block command. This allows you to provide additional inline arguments, same line arguments, and next line arguments.

For example:

@foo[first] second
@[third][fourth] fifth
 @sixth
@ seventh
@bar

Is equivalent to:

@foo[first][second][third][fourth][fifth][@sixth][seventh]
@bar

Same Line Final Arg

If a same line argument ends with @@, then the text following the command at the same indent level is parsed as block structured text and provided as the final argument to the block command.

For example:

@foo First argument @@
@bar

Is equivalent to:

@foo[First argument][@bar]

Next Line Final Arg

If the line following a block command is the text @@, then the text following the command at the same indent level is parsed as block structured text and provided as the final argument to the block command.

For example:

@foo First argument
@@
@bar

Is equivalent to:

@foo[First argument][@bar]

Document Processing

An fbld document is a mix of plain text and commands. To process a document, the commands are evaluated to plain text with the results all concatenated together.

There are a small number of builtin commands which make it possible for a user to define their own commands. These user-defined commands can be collected into separate files, with different implementations for different target backends. For example, one implementation of the commands could be used to convert the document to html when processed, another to convert the document to markdown.

Builtin Commands

@head[str]

Returns the first character of the given string after evaluation. Returns an empty string if the given string is empty.

@tail[str]

Returns all but the first character of the given string after evaluation. Returns an empty string if the given string is empty.

@ifeq[a][b][then][else]

Compares strings a and b after evaluation. If they are equal, returns the result of evaluating the then branch. Otherwise returns the results of evaluating the else branch.

@ifneq[a][b][then][else]

Compares strings a and b after evaluation. If they are not equal, returns the result of evaluating the then branch. Otherwise returns the results of evaluating the else branch.

@define[name][params][def][body]

Defines a new command @name for use in the body. params is a space separated list of parameters for the new command. def defines the value of the command, which will be evaluated in a context where args are bound to the parameters.

For example:

@define[ShowPair][a b][The pair is: @a, @b][@ShowPair[hello][there]]
@let[name][def][body]

Defines a new variable @name for use in the body. def defines the value of the variable, which will be evaluated in the context of the let command.

@eval[arg]

Evaluates arg in the current environment. Any unevaluated commands in arg that are now defined in the environment will be evaluated.

@plain[f][body]

Applies command @f[...] to each occurrence of plain text in body, then evaluates the result.

The command f ought to satisfy the property: @f[xy] = @f[x]@f[y]

@error[msg]

Reports msg as an error processing the fbld file.

Evaluation

Commands do not have any side effects. When a command is encountered in an fbld document, it is substituted for the result of its evaluation according to the rules described in the section on builtin commands.

If a command is encountered in an fbld document that has not been defined, the command is left as is in the document, without processing any of the arguments to the command.

Invocation

The fbld program is used to process an fbld document, which can be split across multiple files. These files are concatenated in order to form the document to be processed. The file is parsed into an fbld document as block structured text. The commands in the document are processed as described above. The resulting text is concatenated together and output, assuming there are no unprocessed commands left after evaluation.

The most common way to split a document into separate files is to put related command definitions into a separate file ending with @@ as a final arg separator. The next file in the list of files will then be interpreted as the body of the last command definition in the previous file.

To Do

The fbld markup language is still being developed. The following are open questions or things likely to change in future revisions of fbld.