-
Notifications
You must be signed in to change notification settings - Fork 292
SimpleDoc language grammar
Note: This grammar was AI-generated by Claude Opus 5. 🤖
EBNF description of the docstring language accepted by the SimpleDoc package —
the format used by doc, multidoc, and document(String). It is a companion
to the Macaulay2 language grammar, which describes
the expression syntax that this language embeds.
Everything below is derived from the parser in
M2/Macaulay2/packages/SimpleDoc.m2; the function names appearing in the notes
(splitByIndent, markup, render, items, submenu, …) refer to that file.
A docstring is plain text, supplied either directly or as the name of a file to
read (doc reads the file when fileExists is true of its argument). It is
line-oriented and indentation-sensitive — it follows the off-side rule.
A section begins with a line holding a keyword and nothing else; the section's
body is the following, more deeply indented lines. Which keywords are legal
depends on the enclosing section, and is fixed by one of four keyword tables.
The language has two layers:
- an outer block layer, driven by indentation and the keyword tables, which determines the tree of sections; and
- an inner inline layer, in which prose is TeX and
@…@escapes to an arbitrary Macaulay2 expression.
The inline layer runs only inside certain sections; others take their bodies verbatim, or evaluate them as Macaulay2 code. The Inline Layer section lists which is which.
The terminals of the block grammar are whole lines, not characters. INDENT and
DEDENT are not produced by a lexer — they are a way of writing down the result
of splitByIndent. Unlike the corresponding tokens in most off-side-rule
languages, they carry two caveats: a single DEDENT may close several blocks at
once, and inconsistent indentation is never itself an error, so a misindented
line is silently reinterpreted rather than rejected. See
Block Structure.
Character-level productions are used only for the inline layer and for the parts of a line that have internal structure.
m2_expression means a complete Macaulay2 expression, as described by the
Macaulay2 language grammar. Wherever it appears,
the text really is handed to value.
source ::= line*
line ::= indentation content? line_end
line_end ::= "\n" | EOF
content ::= any_char* (* anything after the indentation *)
indentation ::= indent_ws*
blank_line ::= indent_ws* line_end
comment_line ::= strip_ws* "--" any_char*
indent_ws ::= " " | "\t" | "\r" (* what indent measurement recognises *)
strip_ws ::= [[:space:]] (* what comment matching and stripping use *)The two whitespace classes differ, and the difference is observable: a line beginning with a vertical tab or form feed has indent 0, with that character stripped from its text, so a line containing only a vertical tab is a non-blank line at indent 0 whose text is empty.
Preprocessing, before any parsing (toDoc, makeTextline):
- The source is split into lines.
- Every line matching
^[[:space:]]*--is deleted outright. - Each surviving line becomes a triple
(text, indent, linenum), wheretextis the line with leading and trailing whitespace stripped.
indent is the column of the first character that is not a space, tab, or
carriage return, computed by scanning the leading whitespace:
| Character | Effect on the running column |
|---|---|
" " |
column + 1 |
"\t" |
the smallest multiple of 8 strictly greater than the column |
"\r" |
column := 0 |
| anything else | stop; that column is the indent |
| end of line | indent is infinity — the line is blank
|
So "\tx", " \tx", and " \tx" all have indent 8, while
" \tx" has indent 16; " \r x" has indent 1. A line containing only
spaces, tabs, and carriage returns has indent infinity, which is what makes
blank lines behave specially rather than as indent 0.
keyword_line ::= keyword (* the entire stripped text of the line *)
keyword ::= (* a name drawn from the table in force; see below *)Dispatch is an exact hash lookup of the stripped text in the keyword table in
force, so a keyword line may hold nothing else: Headline is a keyword line,
Headline blah is an error. Keyword matching is case-sensitive.
splitByIndent divides a list of lines into consecutive blocks. It keeps a
running indent m, initially infinity, and for each line:
- if
m + 1 ≤ indent, the line continues the current block; - otherwise the line starts a new block, and
mis set to that line's indent.
INDENT body DEDENT in the productions below means "the lines of one such
block, after its first line".
The predicate is written m + 1 ≤ indent rather than indent > m because the
two differ when both are infinity, and that case matters: it is why blank
lines before the first block are absorbed rather than starting one.
The crucial consequence is that m only ever decreases: a less indented
line permanently lowers the nesting floor for everything that follows it. Given
these six lines,
a indent 2 -- starts a block, m := 2
b indent 4 -- continues a
c indent 2 -- starts a block, m := 2
d indent 0 -- starts a block, m := 0
e indent 2 -- continues d (!)
f indent 4 -- continues d
splitByIndent returns three blocks — a b, c, and d e f — not four. Once
d has pulled the floor down to 0, the indent-2 line e is a continuation
rather than a sibling of a and c. In practice this means a keyword indented
less than its siblings silently swallows them.
splitByIndent has two modes, and blank lines are the only thing that
distinguishes them.
-
Section splitting (the default) leaves blank lines at indent
infinity, so a blank line always continues the current block. Blank lines before the first block are discarded. -
Paragraph splitting, used by prose sections and nowhere else, treats a
blank line as indent
−1, so a blank line always ends the current block. This is what separates paragraphs.
Because comment lines are deleted before indentation is ever computed, a --
line can never break a block, whatever its indentation.
Four tables, each governing one context. The body column names the production used for the section's body; those productions are defined in Body Forms.
Legal at the top level of a docstring and inside Node (NodeFunctions).
| Keyword | Body | Inline layer |
|---|---|---|
Node |
node_body |
— |
Key |
key_list |
no — evaluated |
Headline |
single_line |
no |
Usage |
multi_line |
no |
Inputs |
item_list |
yes, in descriptions |
Outputs |
item_list |
yes, in descriptions |
Consequences |
consequences_body |
— |
Description |
description_body |
— |
Synopsis |
synopsis_body |
— |
Acknowledgement |
prose |
yes |
Contributors |
prose |
yes |
References |
prose |
yes |
Citation |
verbatim |
no — raw text |
Caveat |
prose |
yes |
SeeAlso |
key_list |
no — evaluated |
Subnodes |
submenu |
yes, per entry |
SourceCode |
key_list |
no — evaluated |
ExampleFiles |
file_list |
no |
Legal inside Synopsis (SynopsisFunctions).
| Keyword | Body | Notes |
|---|---|---|
Heading |
single_line |
|
BaseFunction |
single_line |
evaluated |
Usage |
multi_line |
same handler as the node keyword |
Inputs |
item_list |
same handler |
Outputs |
item_list |
same handler |
Consequences |
consequences_body |
same handler |
Description |
description_body |
same handler |
Synopsis is not itself a Synopsis keyword, so synopses do not nest.
Legal inside Description (DescriptionFunctions).
| Keyword | Body | Inline layer |
|---|---|---|
Text |
prose |
yes |
Tree |
menu |
yes, per entry |
Example |
example_block |
no — Macaulay2 input |
CannedExample |
verbatim |
no |
Pre |
verbatim |
no |
Code |
m2_code |
no — evaluated |
Legal inside Consequences (ConsequencesFunctions).
| Keyword | Body | Inline layer |
|---|---|---|
Item |
item_prose |
yes |
Item is the only one; anything else in a Consequences section is an error.
docstring ::= node+
| node_body
node ::= "Node" INDENT node_body DEDENTA docstring is either a sequence of Node sections, describing several
documentation nodes, or the body of a single unnamed node. Mixing the two —
some top-level sections Node and some not — is rejected by doc, though not
by the block parser itself. The test is applied to the parsed result rather
than to the source, and an empty docstring takes the second branch.
node_body ::= node_section*
node_section ::= "Node" INDENT node_body DEDENT
| "Key" INDENT key_list DEDENT
| "Headline" INDENT single_line DEDENT
| "Usage" INDENT multi_line DEDENT
| "Inputs" INDENT item_list DEDENT
| "Outputs" INDENT item_list DEDENT
| "Consequences" INDENT consequences_body DEDENT
| "Description" INDENT description_body DEDENT
| "Synopsis" INDENT synopsis_body DEDENT
| "Acknowledgement" INDENT prose DEDENT
| "Contributors" INDENT prose DEDENT
| "References" INDENT prose DEDENT
| "Citation" INDENT verbatim DEDENT
| "Caveat" INDENT prose DEDENT
| "SeeAlso" INDENT key_list DEDENT
| "Subnodes" INDENT submenu DEDENT
| "SourceCode" INDENT key_list DEDENT
| "ExampleFiles" INDENT file_list DEDENT
synopsis_body ::= synopsis_section*
synopsis_section ::= "Heading" INDENT single_line DEDENT
| "BaseFunction" INDENT single_line DEDENT
| "Usage" INDENT multi_line DEDENT
| "Inputs" INDENT item_list DEDENT
| "Outputs" INDENT item_list DEDENT
| "Consequences" INDENT consequences_body DEDENT
| "Description" INDENT description_body DEDENT
description_body ::= description_section*
description_section ::= "Text" INDENT prose DEDENT
| "Tree" INDENT menu DEDENT
| "Example" INDENT example_block DEDENT
| "CannedExample" INDENT verbatim DEDENT
| "Pre" INDENT verbatim DEDENT
| "Code" INDENT m2_code DEDENT
consequences_body ::= ("Item" INDENT item_prose DEDENT)*The * quantifiers are deliberate: an empty body is accepted almost everywhere.
Only Key, Headline, Heading, BaseFunction, Usage, and Synopsis
genuinely require content; the rest either yield an empty result or crash, as
listed under
Failure modes. A linter built from this
grammar should not reject an empty section outright.
Sections may appear in any order. Whether one may be repeated depends on which table it comes from:
- Among node keywords, only
Node,Description, andSynopsismay repeat. Those three contribute bare items; every other node keyword becomes an option ofdocument, which rejects a duplicate withoption X encountered twice. The block parser accepts the repetition either way, so the error surfaces later, atdocumenttime. - Inside
DescriptionandConsequences, every keyword repeats freely. ADescriptionis normally a sequence of alternatingTextandExamplesubsections, and severalItemsections are the usual way to write several consequences. - Inside
Synopsis, a repeated keyword is not an error either, but onlyDescriptionis cumulative — it is a bare item, whichSYNOPSISsplices into its body.Heading,BaseFunction,Usage,Inputs,Outputs, andConsequencesare options ofSYNOPSIS, an ordinary method with options, so for those the last occurrence silently wins.
A Description yields a sequence of items rather than a single item. At the
top level of a docstring toDoc's deepSplice flattens it immediately, and
inside a Synopsis SYNOPSIS splices it; inside an explicit Node it stays
nested until document calls fixup, which flattens it then. Either way,
several Description sections end up equivalent to one.
Node appears in its own body table, so nodes nest structurally. Nothing
consumes a nested node, so this is best regarded as an accident of the table
rather than a feature.
key_list ::= m2_expression* (* one per non-blank line *)
single_line ::= text_line
multi_line ::= text_line+ (* joined with newlines *)
verbatim ::= any_line* (* common indentation removed *)
m2_code ::= any_line* (* common indentation removed,
wrapped in ( ) and evaluated *)
example_block ::= example*
example ::= any_line more_indented_line*
file_list ::= any_line*
prose ::= "Tree" submenu
| paragraph (blank_line paragraph)*
item_prose ::= prose (* paragraph structure discarded *)
paragraph ::= inline_line* (* joined with single spaces *)
(* any_line — any line of the section, blank ones included
more_indented_line — a line indented more than the block's first line
text_line — any line, taken as a string
inline_line — any line, contributing to the paragraph's inline text *)-
key_list— used byKey,SeeAlso, andSourceCode. Blank lines are dropped and each remaining line is evaluated as a Macaulay2 expression, so a string key must carry its quotation marks.Keyrequires at least one entry. -
single_line— used byHeadline,Heading, andBaseFunction. The value is the first line; it is an error for any later line to be non-blank. Trailing blank lines are harmless, but a blank line immediately after the keyword is not: it becomes the value, and the intended text becomes an illegal second line.BaseFunction's line is then evaluated. -
multi_line— used byUsage. At least one line is required; the lines are joined with newlines, giving one usage per line. -
verbatim— used byPre,CannedExample, andCitation, which take their text raw with a common indentation removed.PreandCodesubtract the minimum indentation over the section;CannedExampleandCitationsubtract the indentation of the section's first line, as doesExample. Blank lines are kept. Indentation is rebuilt as spaces, so tabs are normalised to their column, and a line shallower than the reference is flushed left rather than kept relatively indented.The three differ sharply in what they produce, despite sharing a body form.
Prebecomes a preformatted block, andCitationis stored as a raw string.CannedExampleis wrapped inEXAMPLEand renders into the same<table class="examples">that a realExampledoes, but its content is a preformatted transcript rather than an example item, so it is never extracted into the package's example inputs and never run. -
m2_code— used byCode. The dedented lines are wrapped in parentheses and evaluated, so the body is one parenthesised Macaulay2 expression, not a statement sequence: two expressions on consecutive lines need a comma or a semicolon between them, exactly as inside( … ). Only the comma contributes two items —;sequences for effect and yields just the last value. The result is spliced into the surrounding list of documentation items, and both sequences and lists are flattened, the latter bydocument'sfixuprather than by SimpleDoc, so(PARA "a", PARA "b")and{PARA "a", PARA "b"}both contribute two items. -
example_block— used byExample. Each block found bysplitByIndentbecomes one example, so each line at the section's base indentation starts a new example and more deeply indented lines continue it. Indentation is measured against the section's first line. The text is not evaluated by SimpleDoc at all; it is captured and run later byinstallPackage. -
file_list— used byExampleFiles. Every line is taken literally as a file name, with no evaluation and, unlikekey_list, no dropping of blank lines. -
prose— used byText,Caveat,Acknowledgement,Contributors, andReferences. Blank lines separate paragraphs; the lines of a paragraph are joined with single spaces and passed through the inline layer as a unit. A paragraph that renders to a single hypertext container or paragraph is used as-is; anything else is wrapped in a paragraph. This is why@SUBSECTION "…"@alone on a line works while@SUBSECTION "…"@ taildoes not: the latter parses without complaint, but wheninstallPackagerenders the node it printswarning: … PARA, may not contain an element of type … HEADER2and then aborts withvalidation failed: PARA{HEADER2{…}, …}. As a special case, if the section's very first line is exactlyTree, the rest of the section is parsed as asubmenuinstead. -
item_prose— used byItem. Paragraphs are computed as forproseand then flattened: each paragraph's outermost wrapper is discarded and the pieces are concatenated. Blank lines therefore do not produce visible paragraph breaks inside anItem, and anItemconsisting of@SUBSECTION "S"@is reduced to the bare stringS. Use oneItemper paragraph.
Note that paragraph may be empty, and empty paragraphs are emitted rather than
discarded. The underlying rule is that markup prepends a synthetic blank line,
splits on blank lines, and emits an empty paragraph for any block consisting of
a blank line alone. So n leading or trailing blank lines yield n empty
paragraphs and an interior run of n yields n − 1. A body of nothing but
blank lines is the degenerate case: n of them yield n + 1, since the
synthetic line's own block is empty too.
The trailing case is the common one, and it is easy to miss: because blank lines
belong to the preceding section body, the ordinary habit of leaving a blank
line before the next keyword puts an empty paragraph at the end of nearly every
Text section.
item_list ::= item*
item ::= item_head description_line*
item_head ::= name? separator type? ("--" abbreviation)?
separator ::= ":" | "=>"
(* name — a string, or a symbol to evaluate; see below
type — m2_expression
abbreviation — free text, passed through the inline layer
description_line — a line indented more than the head *)Each block found by splitByIndent is one item: its first line is the head, and
the more deeply indented lines that follow are its description, joined with
spaces and passed through the inline layer. Unlike prose, a blank line does not
end an item description — it merely contributes an extra space.
The entire head, abbreviation included, is split on
[[:space:]]*(:|=>)[[:space:]]*, and the split must yield exactly two
parts. Both parts may be empty, so :ZZ (an anonymous value of type ZZ) and
n: are legal, but a head with no separator, or with a second one, is an error
— including a second one that only appears in the abbreviation, so
n:ZZ -- see: below does not parse.
- With
:, the name is kept as a string:n:ZZdocuments an argument namedn. - With
=>, the name is evaluated, yielding an optional argument:Limit => ZZdocuments the optionLimit. - The type is evaluated as Macaulay2 code. That is also why the
-- abbreviationtail survives: it is not stripped by SimpleDoc before evaluation, but Macaulay2's own lexer treats it as a comment. The abbreviation is separately recovered as the second---delimited fragment of the type text, so with two--s the remainder is dropped, and it is prepended to the description followed by"; "— which appears even when the description is empty. - If the type evaluates to a
Stringrather than a type, it is not treated as a type at all: it is joined to the description with", ", which is the supported idiom for describing a type in prose, as inn:"a list of things". - An abbreviation with no type before it is legal but rarely intended: in
n: -- small, the type text is-- small, which evaluates tonull, so the item is documented as having typenull.
The head is not checked for sensibility, so Matrix:a parses happily and
documents an argument named Matrix of the type given by the symbol a.
Subnodes, the Tree subsection of a Description, and a prose section whose
first line is Tree all share this sublanguage. Blank lines are discarded
throughout it.
submenu ::= entry*
entry ::= entry_line (INDENT submenu DEDENT)?
entry_line ::= ":" inline (* heading *)
| at_inline (* raw markup *)
| "* " entry_target (* link *)
| "> " entry_target (* subnode link *)
| "- " inline (* free text *)
| m2_expression (* documentation key *)
entry_target ::= at_inline
| m2_expression
at_inline ::= inline (* constrained to begin with "@" *)
menu ::= menu_section+
menu_section ::= heading (INDENT submenu DEDENT)?
heading ::= entry_lineThe alternatives are tried in the order written, so a line beginning with : or
@ is a heading or a raw markup line regardless of what follows; the prefix
forms are recognised by their first two characters, the space included, so
*matrix is a documentation key rather than a link.
-
*marks a link,>marks a link to a subnode, and-marks free text. -
*and>take a documentation key, evaluated and rendered with its headline, unless the text begins with@, in which case the whole rest of the line goes through the inline layer instead.-always uses the inline layer. - A line with none of these prefixes is a documentation key, rendered as after
*but without the wrapper described below. - Deeper indentation nests one menu inside another, to any depth.
Outside Subnodes, * and > are not cosmetic variants. They emit spans
of class link and subnode respectively, and installPackage harvests every
subnode span out of a node's Description, Acknowledgement, Contributors,
References, and Caveat, adding those tags to the documentation tree as
genuine children just as if they had been listed under Subnodes. A * link
in those sections is only a link.
Inside Subnodes the distinction is invisible to the tree builder, which
deep-selects every documentation tag in the section with no regard for class. A
key there becomes a child whichever prefix it carries, or none.
menu — the Tree subsection of a Description — differs from submenu in two
ways: it renders the first line of each block as a heading, and if the section's
first line begins with neither : nor @ it synthesises a Menu heading to put
the entries under. A prose section led by Tree uses plain submenu and does
neither; in particular its : lines get no heading element, so "heading" in the
submenu production above means only "not an entry".
A menu of exactly one entry is a special case: it is not wrapped in a UL. Be
warned that it is not uniform either — five of the six entry forms yield a
one-element list, while the bare-key form yields the link object itself, so a
consumer must handle three shapes in all. This is why both menu and
submenu's recursive call re-wrap a one-element result before use.
Within prose, item descriptions, and menu entries, text is a mixture of TeX and Macaulay2:
inline ::= (tex_run | at_block)*
at_block ::= "@" m2_expression "@"
tex_run ::= (any_char_except_at | "\\@")+- Text outside
@…@is TeX. It may use$…$and\(…\)for inline math and$$…$$and\[…\]for display math, which are left for KaTeX. Five constructs are converted to hypertext outright —{\bf …},{\em …},{\it …},{\tt …}, and\url{…}— and the text is also rewritten for typography:---becomes an em dash unconditionally, and then any remaining--with a word character immediately on both sides becomes an en dash. - Text inside
@…@is wrapped in parentheses and evaluated. It is therefore a full Macaulay2 expression, not a restricted markup dialect: surrounding spaces are harmless,@TT "a", TT "b"@is a sequence by virtue of the parentheses, and the hypertext constructors exported by theTextpackage (TO,TO2,TOH,TT,EM,HREF,UL, …) together with SimpleDoc's ownarXiv,wikipedia, andstacksProjecthelpers are all in scope. -
\@is an escaped literal@; it does not delimit a block, and the backslash is removed in both layers. - An unmatched
@is an error, including a single stray@at the end of a line.
The unit the inline layer operates on is the whole paragraph or the whole item
description, after its lines have been joined — not the individual line. So an
@…@ block may be broken across lines within one paragraph, but never across a
paragraph break, and never in a menu, whose entries are rendered one line at a
time.
Sections that do not use the inline layer fall into three groups:
| Treatment | Sections |
|---|---|
| Taken as a raw string |
Pre, CannedExample, Citation, ExampleFiles, Headline, Heading, Usage
|
| Evaluated while parsing |
Key, SeeAlso, SourceCode, Code, BaseFunction, item types, menu keys |
| Deferred to install time | Example |
Raised while parsing, with a line number as a line #N: prefix:
| Condition | Message |
|---|---|
Key with no non-blank lines |
expected at least one key |
Headline, Heading, or BaseFunction with no body, or with a second non-blank line |
expected single indented line after … |
Usage with no lines |
expected at least one indented line after Usage |
An item head without exactly one : or =>
|
expected line containing a colon or a double arrow |
Inputs or Outputs without Usage, inside a Node
|
Inputs or Outputs specified, but Usage not provided |
Raised while parsing, with the line number embedded in the message instead:
| Condition | Message |
|---|---|
| A keyword line not in the table in force | unrecognized keyword on line #N: "…"; expected: … |
An unmatched @ in a rendered line |
unmatched @ near line #N: |
The keyword error is the most helpful diagnostic SimpleDoc produces, since it also prints the full list of keywords legal in that context.
Raised with no line information at all:
| Condition | Message | Raised by |
|---|---|---|
Inputs or Outputs without Usage, in a docstring with no explicit Node
|
document: Inputs or Outputs specified, but Usage not provided |
document |
A Synopsis with no Usage at all |
Usage: expected content |
SYNOPSIS, during parsing |
A docstring mixing Node and non-Node top-level sections |
expected either a documentation node or a list of documentation nodes |
doc |
A node with no Key
|
missing Key |
document |
A node keyword other than Node, Description, or Synopsis used twice in one node |
option X encountered twice |
document |
The three Usage requirements are not one rule. nodeCheck runs from the
Node handler, so it never sees a docstring written without an explicit Node
keyword, and even inside a Node it only fires when Inputs or Outputs is
present. That case is not unchecked, though: document applies the same
requirement to every node later on, without a line number and with a
document: prefix. A Synopsis, by contrast, is expanded eagerly by
SYNOPSIS during parsing and requires Usage unconditionally — a Synopsis
containing only a Heading, or nothing at all, is an error.
The line #N in these messages is not always what a reader expects. For a
docstring read from a file, N is relative to that file. For an inline
doc /// … ///, it is an approximate absolute line in the .m2 file,
back-computed from the current row number — the source's own comment describes
it as "hopefully the doc line, but may be off by one". Line numbers are
assigned before comment lines are deleted, so they do count deleted -- lines.
Two diagnostics are further off. The "expected single indented line" error
reports the second body line rather than the offending one. The unmatched-@
error reports the line preceding the paragraph — the blank line that started
it, or the keyword line if it is the section's first paragraph — not the line
the @ is on.
Several malformed docstrings produce an internal Macaulay2 error rather than a SimpleDoc diagnostic. The known cases:
- An empty
CannedExampleorCitation, or an emptyTreesubsection of aDescription, raisesarray index 0 out of bounds. An emptyExampleraisesEXAMPLE: empty list of examples encountered. An emptySynopsisraisesUsage: expected content— the Static Checks rule above, since an emptySynopsishas noUsage. Apart from those and the keywords whose emptiness is diagnosed above (Key,Headline,Heading,BaseFunction,Usage), an empty body is silently accepted and degenerate, includingNode,Subnodes,Pre,Code,Inputs,Outputs,Consequences,Item,SeeAlso,SourceCode,ExampleFiles, a prose-leadingTree, every prose section, and aDescriptionoutside an explicitNode. - An empty
Descriptioninside an explicitNodealso raisesarray index 0 out of bounds, unless anInputsorOutputssection and aUsagesection both precede it.nodeCheckmakes two scans over the parsed items and both must short-circuit before reaching the empty sequence, so either one alone is not enough. - A blank first line in an
Example,CannedExample, orCitationraisesno method for binary operator : applied to …— the section takes its base indentation from that line, which isinfinity. - A failure inside an
@…@block is reported by re-evaluating the text after printingin the evaluation of: …to stderr. No other evaluated text gets this treatment: a failure in aKey,SeeAlso,SourceCode,Code, an item's type, or a menu key surfaces as a bare interpreter error naming neither the docstring line nor the offending section.
Comment lines vanish before indentation matters. Lines matching
^[[:space:]]*-- are deleted at the very start, so a comment can never break a
block, end a paragraph, or separate two examples, however it is indented. The
consequence authors actually hit is in Example: a whole-line -- comment is
removed from the example source, so it is never sent to Macaulay2 and never
appears in the transcript. An end-of-line -- inside prose, by contrast, is not
stripped and appears in the rendered text verbatim. TeX's typographic rewriting
does not save you: it converts a -- only when a word character sits
immediately on both sides, so foo--bar becomes an en dash but a trailing
-- comment is left alone. A trailing --- comment does get an em dash,
since --- is rewritten unconditionally.
The indentation floor only ever drops. See Block Structure. A section keyword accidentally indented one column less than its siblings will absorb them instead of standing beside them, and no error is reported.
Blank lines mean opposite things at the two levels. They are invisible when
splitting a section into subsections, significant when splitting prose into
paragraphs — where they produce empty paragraphs, including one at the end of
almost every Text section — and discarded entirely inside menus. A blank line
immediately after Headline, Heading, BaseFunction, Example,
CannedExample, or Citation is a hazard, since those forms take their value
or their base indentation from the section's first line.
Key requires quotation marks around string keys. Every line of a Key,
SeeAlso, or SourceCode section is evaluated, so "Macaulay2Doc :: matrix"
is a string key while matrix is a symbol.
SeeAlso drops blank lines but ExampleFiles does not. An ExampleFiles
section containing a blank line yields an empty file name.
Description splices, eventually. Its subsections end up inserted
individually into the enclosing node, so splitting a description across several
Description sections changes nothing — but inside an explicit Node the
flattening is deferred to document, so the intermediate parse tree still shows
a nested sequence.
Tabs are real tab stops. A tab advances to the next multiple of 8, so a line indented with a tab and a line indented with eight spaces are at the same level — but a tab following seven spaces advances only one column.
Adding a keyword means editing two files. Keywords that are not already
exported Macaulay2 symbols must also be registered for syntax highlighting in
M2/Macaulay2/packages/Style.m2, which currently lists Node, Synopsis,
CannedExample, Code, Example, Pre, Tree, and Item. That list is a
useful cross-check for anyone writing a highlighter.
Homepage | Projects | Packages | Documentation | Events | Google Group