fble-0.5 (2025-07-13,fble-0.4-212-ga8f8ad0f)
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".
The primary design goals of fbld are:
It should be easy to parse, by both human and computer. There should not be weird corner cases, ambiguity, or complex resolution rules for parsing like there are for markdown, for example.
The main textual content should be easy to read from source by a human. It should not include excessive clutter from tags that overly distract from the contents of the text, as is the case with roff and xml, for example.
It should be easy to provide custom processing of the document. For example, conversion to html, latex, roff, or anything you might like.
It should support custom structure, allowing a user to define their own new kinds of structures and use those structures in their documents.
The original use cases motivating development of the fbld language are:
Document functions in code and automatically generate man pages from it.
Document command line usage and automatically generate both man page and help usage text from it.
Mark up README files, tutorials, and language specifications to add clarity to structure and make it possible to generate books, webpages, etc. with that information.
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.
You have to know what particular symbols mean to understand the structure
of a document. For example, ......
versus ////////
.
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.
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.
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]) ;
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 is used for marking up paragraphs or short lines of text. Whitespace is preserved in inline structured 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
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.
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.
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 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 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.
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.
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.
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.
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.
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.
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.
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
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]
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]
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.
@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.
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.
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.
The fbld markup language is still being developed. The following are open questions or things likely to change in future revisions of fbld.
Having all commands belong to a single global namespace doesn't scale. Some form of namespace management is desired.
It's not nice for the user to have to know all the right .fbld
files to
list on the command line in the right order. It seems like we ought to have
some form of import to import required command definitions automatically in
the right order.
Is it possible to have blank lines at the end of next line literal text? Should it be possible?