This is an ASCII-compatible grammar for drafting and collating file-folder hierarchies in a single text file. The idea is simple: give structure to code documentation using a written pseudo-filesystem while retaining editability in text editors. Intelligent editors can take advantage of this structure to match documentation to the corresponding source, displaying it easily. The extension for these files is ‘.sli’.
Actual SLI files are arbitrary, and have no direct correspondence with any particular construct in the source code or the project repository. ADP 1 provides a protocol for placing SLI files inside the
doc/ subfolder of a repository.
Similar to tarballs, SLI docs consist of a series of records. Each line that does not begin with a hard tab (
\t) denotes the head of a record. Hard tabs prefix every line that contiguously forms a record’s contents. Customarily, a single tab is used for this, but the number of tabs does not matter. A record ends at the beginning of another record or at the EOF. The format of the record head is simply a relative pathname separated with forward slashes (
/). A good convention is to start record paths with the project name.
For maximum compatibility with computers and human fingers, SLI files are restricted to be ASCII only. Files should end with a single newline, per Unix custom. Additionally, hard tab characters and newlines are insignificant to the composed output; both are are part of the grammar, and newlines may be inserted wherever necessary to display well on older terminals. However, through an exhaustive list of escape sequences in Appendix A, full output of Unicode in composition is supported.
File extensions are used to denote the ‘kind’ of expression being documented in a given file. These are the supported kinds of expressions:
.struct– struct body
.class– class body
.sub– subroutine, function, or method
.var– variable or data member
.const– constant (static or preprocessor)
For complex expressions that often include nontrivial children, such as classes, their contents can be placed in a subfolder of the same basename as the
.class file sans the extension. So,
foo.class’s members could be
foo/baz.var, for example. For the special constructor and destructor methods in classes, use
foo/~@.sub, respectively. Getter methods are enclosed in angle brackets (e.g.
foo/<myvar>.sub), while setter methods are enclosed the same but inverted (
foo/>myvar<.sub). Operator overloads use the operator’s syntax literal as the name (e.g.,
Often it is informative to list attributes or properties of a function subroutine or other expression to inform the user. For example, a function may be marked as pure, or marked to say it does not return. It could also be documented that it does not throw. To tag a file, simply list the tags at the beginning of the file with each tag in square brackets. These are the known tags:
[pure]– pure function. this implies
[nothrow]– will NOT throw exceptions, even from deep within its call graph.
[noreturn]– does not return control flow at all.
[noalloc]– does not allocate or free any memory from the heap.
[new]– allocates memory specifically in service of the caller.
[threadsafe]– safe for use with multithreading.
[packed]– only for
.class. members are tightly byte-packed.
[zerofill]– only for
.struct. zerofilling members for init is well-behaved.
[preproc]– only for
.const. expression is resolved by
It is a common need to document a function’s parameters and return values, a variable’s valid values, or some combination thereof. Attributes are written as their own records with a special form; the file path remains the same, and a colon is appended to separate the path from the attribute portion, similar to the notation for file offsets or source code line numbers.
Denoted by the pound sign (
Denoted like shell arguments or regex groups, using a dollar sign and a number in decimal (
$0 is used to represent member variables in class instances and is unused outside such context, so all parameters begin with
Denoted by a single asterisk (
*), following any other attributes if present, e.g.
foo/bla.sub:$1*. These records list out the constraints for a piece of data or code to be considered “valid”; for data, this entails specific restrictions on permitted values, shallow and deep, while for code this entails constraints beyond which the subroutine may exhibit undefined behaviour. This can include things like “such pointer must not be NULL” and “must point to a valid X object allocated with xyzallocator function”. RFC 2119 is useful for specifying normative terminology to these ends.
\\– literal backslash
\h??– a single byte in hexadecimal
\0– NUL byte (shorthand for \h00)
\r– carriage return
\n– line feed
\t– horizontal tabulation
\u????– a single Unicode codepoint, within the Basic Multilingual Plane
\U????????– a single Unicode codepoint from any plane