rules specifies the finite state machines for filtering documentation.

rules.h

Copyright © 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License.

The following constants are used to mark special lines of documentation text and source code. The enumeration starts with value 1 because 0 indicates no mark.

enum {
    mergeBlank = 1, mergeCmd, mergeHtml, mergeTag, mergeGlobal, mergeLocal
};

Both input filters use the same set of states.

enum {
    docStart,
    docPrint, docCmd, docFile, docQuote, docPre, docHtml, docHtmlQuote,
    docNStates
};

Output states used to escape <, >, and & within <code>, <samp>, and <pre> elements:

enum {
    htmlDocStart,
    htmlDoc, htmlCode, htmlSamp,
    htmlPreStart, htmlPre, htmlPreStrong,
    htmlNStates
};

Output states used to enclose paragraphs in <p> elements:

enum {
    parStart,
    parOff,
    parPar, parParIn,
    parSkip,
    parNStates
};

Output states used by dictFilter to link C identifiers to their definitions:

enum {
    dictStart,
    dictCode, dictCodeSQuote, dictCodeDQuote,
    dictSamp, dictSampSQuote, dictSampDQuote,
    dictPre, dictPreSQuote, dictPreDQuote,
    dictNStates
};

sourceRules, docRules, htmlRules, and dictRules are arrays of filterRules, terminated by an element for which fromStr is a null pointer. The corresponding finite machines are coded by writing array initializers.

sourceTable, docTable, htmlTable, and dictTable are lookup tables for the same filterRules. They consist of linked lists indexed by the current state and the first character of fromStr. Each linked list is sorted by decreasing length, so the first match is the longest match.

extern filterRules *sourceTable[docNStates][NCHARS];
extern filterRules sourceRules[];

extern filterRules *docTable[docNStates][NCHARS];
extern filterRules docRules[];

extern filterRules *htmlTable[htmlNStates][NCHARS];
extern filterRules htmlRules[];

extern filterRules *parTable[parNStates][NCHARS];
extern filterRules parRules[];

extern filterRules *dictTable[dictNStates][NCHARS];
extern filterRules dictRules[];

rules.c

Copyright © 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License.

#include "root.h"
#include "file.h"
#include "filter.h"
#include "rules.h"

sourceTable

sourceTable and sourceRules implement the filter to be used for input of source code files.

filterRules *sourceTable[docNStates][NCHARS];

sourceRules

filterRules sourceRules[] =
{

Begin each line in state docStart, and move to state docPrint if a nonblank character is seen; blank characters are ' ' and '\t'. Blank lines are marked with mergeBlank. Note that the empty string "" matches only after all nontrivial prefixes fail to match.

    { " ", 0, docStart, docStart },
    { "\t", 0, docStart, docStart },
    { "\n", 0, docStart, docStart, mergeBlank },
    { "\n", 0, docPrint, docStart },
    { "", 0, docStart, docPrint },

If // or /* are the first nonblank characters on a line, then the line is a documentation tag, to be matched between the source code and documentation files.

Move from state docStart to state docPrint on these prefixes. Mark the prefixes //. and /*. with mergeGlobal, mark the prefixes //, and /*, with mergeLocal, and mark the prefixes // and /* with mergeTag.

    { "//.", 0, docStart, docPrint, mergeGlobal },
    { "/*.", 0, docStart, docPrint, mergeGlobal },
    { "//,", 0, docStart, docPrint, mergeLocal },
    { "/*,", 0, docStart, docPrint, mergeLocal },
    { "//", 0, docStart, docPrint, mergeTag },
    { "/*", 0, docStart, docPrint, mergeTag },

    { 0 }
};

docTable

docTable, sourceRules, and docRules implement the filter to be used for input of documentation .txt files.

filterRules *docTable[docNStates][NCHARS];

docRules

docRules are appended to sourceRules to initialize docTable.

filterRules docRules[] =
{

If //@ are the first nonblank characters on a line, then the line is a command, either to begin or end including a source file.

Move from state docStart to state docCmd on this prefix. Move to state docFile on the first nonblank character that follows, and mark it with mergeFile. If there are no nonblank characters, mark the return with mergeFile. In state docFile, mark the string .html with mergeHtml if found.

    { "//@", 0, docStart, docCmd },
    { " ", 0, docCmd, docCmd },
    { "\t", 0, docCmd, docCmd },
    { "", 0, docCmd, docFile, mergeCmd },
    { "\n", 0, docFile, docStart },
    { ".html\n", 0, docFile, docStart, mergeHtml },

Enclose quoted phrases in the HTML <samp> element, and remove the quotes. Move to state docQuote on an opening double quote, and move to state docPrint on a closing double quote. An escaped quote \" is output as a quote, and an escaped backslash \\ is output as a backslash. Quoted phrases may extend over multiple lines.

    { "\"", "<samp>", docStart, docQuote },
    { "\"", "<samp>", docPrint, docQuote },
    { "\"", "</samp>", docQuote, docPrint },
    { "\\\"", "\"", docStart, docPrint },
    { "\\\"", "\"", docPrint, docPrint },
    { "\\\"", "\"", docQuote, docQuote },
    { "\\\\", "\\", docStart, docPrint },
    { "\\\\", "\\", docPrint, docPrint },
    { "\\\\", "\\", docQuote, docQuote },

Move to state docPre within <pre> elements.

    { "<pre>", 0, docStart, docPre },
    { "<pre class=samp>", 0, docStart, docPre },
    { "<pre class=code>", 0, docStart, docPre },
    { "<pre>", 0, docPrint, docPre },
    { "<pre class=samp>", 0, docPrint, docPre },
    { "<pre class=code>", 0, docPrint, docPre },
    { "</pre>", 0, docPre, docPrint },

Recognize HTML element delimiters < and > so quoted file names aren't converted to <samp> elements.

    { "<", 0, docPrint, docHtml },
    { ">", 0, docHtml, docPrint },
    { "\"", 0, docHtml, docHtmlQuote },
    { "\"", 0, docHtmlQuote, docHtml },

    { 0 }
};

htmlTable

htmlTable and htmlRules implement the first filter to be used for output of documentation .html files. The purpose of this filter is to escape the special characters <, >, and & where necessary.

filterRules *htmlTable[htmlNStates][NCHARS];

htmlRules

filterRules htmlRules[] =
{

States for leading blanks:

    { " ", 0, htmlDocStart, htmlDocStart },
    { "\t", 0, htmlDocStart, htmlDocStart },
    { "\n", 0, htmlDocStart, htmlDocStart },
    { "\n", 0, htmlDoc, htmlDocStart },
    { "", 0, htmlDocStart, htmlDoc },

    { " ", 0, htmlPreStart, htmlPreStart },
    { "\t", 0, htmlPreStart, htmlPreStart },
    { "\n", 0, htmlPreStart, htmlPreStart },
    { "\n", 0, htmlPre, htmlPreStart },
    { "", 0, htmlPreStart, htmlPre },

Move to states htmlCode, htmlSamp, htmlPreStart within <code>, <samp>, <pre> elements, respectively.

    { "<code>", 0, htmlDoc, htmlCode },
    { "</code>", 0, htmlCode, htmlDoc },

    { "<samp>", 0, htmlDoc, htmlSamp },
    { "</samp>", 0, htmlSamp, htmlDoc },

    { "<pre>", 0, htmlDocStart, htmlPreStart },
    { "<pre class=code>", 0, htmlDocStart, htmlPreStart },
    { "<pre class=samp>", 0, htmlDocStart, htmlPreStart },
    { "</pre>\n", 0, htmlPre, htmlDocStart },

<strong> elements are allowed to enclose entire lines in <pre class=code> and <pre class=samp> elements, so that unmatched comments in source code can be highlighted.

    { "<strong>", 0, htmlPreStart, htmlPreStrong },
    { "</strong>\n", 0, htmlPreStrong, htmlPreStart },

Otherwise, escape <, >, and & within <code>, <samp>, and <pre> elements.

    { "<", "&lt;", htmlCode, htmlCode },
    { ">", "&gt;", htmlCode, htmlCode },
    { "&", "&amp;", htmlCode, htmlCode },

    { "<", "&lt;", htmlSamp, htmlSamp },
    { ">", "&gt;", htmlSamp, htmlSamp },
    { "&", "&amp;", htmlSamp, htmlSamp },

    { "<", "&lt;", htmlPre, htmlPre },
    { ">", "&gt;", htmlPre, htmlPre },
    { "&", "&amp;", htmlPre, htmlPre },

    { "<", "&lt;", htmlPreStrong, htmlPreStrong },
    { ">", "&gt;", htmlPreStrong, htmlPreStrong },
    { "&", "&amp;", htmlPreStrong, htmlPreStrong },

    { 0 }
};

parTable

parTable and parRules implement the second filter to be used for output of documentation .html files. The purpose of this filter is to automatically detect and markup paragraphs.

filterRules *parTable[parNStates][NCHARS];

parRules

filterRules parRules[] =
{
    { "<!-- par off -->", 0, parStart, parOff },
    { "<!-- par on -->", 0, parOff, parStart },

    { " ", 0, parStart, parStart },
    { "\t", 0, parStart, parStart },
    { "\n", 0, parStart, parStart },
    { "", "<p>\n", parStart, parPar },

    { " ", 0, parPar, parPar },
    { "\t", 0, parPar, parPar },
    { "\n", "</p>\n\n", parPar, parStart },
    { "", 0, parPar, parParIn },
    { "\n", 0, parParIn, parPar },

    { "<hr>", 0, parStart, parStart },

Rules for entering block-level elements:

    { "<pre", 0, parStart, parSkip },
    { "<ol", 0, parStart, parSkip },
    { "<ul", 0, parStart, parSkip },
    { "<table", 0, parStart, parSkip },
    { "<h1", 0, parStart, parSkip },
    { "<h2", 0, parStart, parSkip },
    { "<h3", 0, parStart, parSkip },
    { "<h4", 0, parStart, parSkip },
    { "<h5", 0, parStart, parSkip },
    { "<h6", 0, parStart, parSkip },

If already in a paragraph, first close the paragraph.

    { "<pre", "</p>\n<pre", parPar, parSkip },
    { "<ol", "</p>\n<ol", parPar, parSkip },
    { "<ul", "</p>\n<ul", parPar, parSkip },
    { "<table", "</p>\n<table", parPar, parSkip },

Rules for exiting block-level elements:

    { "</pre>", 0, parSkip, parStart },
    { "</ol>", 0, parSkip, parStart },
    { "</ul>", 0, parSkip, parStart },
    { "</table>", 0, parSkip, parStart },
    { "</h1>", 0, parSkip, parStart },
    { "</h2>", 0, parSkip, parStart },
    { "</h3>", 0, parSkip, parStart },
    { "</h4>", 0, parSkip, parStart },
    { "</h5>", 0, parSkip, parStart },
    { "</h6>", 0, parSkip, parStart },

    { 0 }
};

dictTable

dictTable and dictRules implement the third filter to be used for output of documentation .html files. The purpose of this filter is to link C identifiers to their definitions.

dictFilter, called by stateFilter when no rule applies, links C identifiers to their definitions in the dictCode or dictSamp states.

filterRules *dictTable[dictNStates][NCHARS];

dictRules

filterRules dictRules[] =
{

Rules for the <code> element: The dictCode state tracks when output text is within the <code> element, but not within single or double quotes.

    { "<code>", 0, dictStart, dictCode },
    { "</code>", 0, dictCode, dictStart },
    { "</code>", 0, dictCodeSQuote, dictStart },
    { "</code>", 0, dictCodeDQuote, dictStart },

    { "<strong>", 0, dictCode, dictCode },
    { "</strong>", 0, dictCode, dictCode },

    { "'", 0, dictCode, dictCodeSQuote },
    { "'", 0, dictCodeSQuote, dictCode },

    { "\"", 0, dictCode, dictCodeDQuote },
    { "\"", 0, dictCodeDQuote, dictCode },

    { "\\'", 0, dictCode, dictCode },
    { "\\'", 0, dictCodeSQuote, dictCodeSQuote },
    { "\\'", 0, dictCodeDQuote, dictCodeDQuote },

    { "\\\"", 0, dictCode, dictCode },
    { "\\\"", 0, dictCodeSQuote, dictCodeSQuote },
    { "\\\"", 0, dictCodeDQuote, dictCodeDQuote },

Rules for the <samp> element: The dictSamp state tracks when output text is within the <samp> element, but not within single or double quotes.

    { "<samp>", 0, dictStart, dictSamp },
    { "</samp>", 0, dictSamp, dictStart },
    { "</samp>", 0, dictSampSQuote, dictStart },
    { "</samp>", 0, dictSampDQuote, dictStart },

    { "<strong>", 0, dictSamp, dictSamp },
    { "</strong>", 0, dictSamp, dictSamp },

    { "'", 0, dictSamp, dictSampSQuote },
    { "'", 0, dictSampSQuote, dictSamp },

    { "\"", 0, dictSamp, dictSampDQuote },
    { "\"", 0, dictSampDQuote, dictSamp },

    { "\\'", 0, dictSamp, dictSamp },
    { "\\'", 0, dictSampSQuote, dictSampSQuote },
    { "\\'", 0, dictSampDQuote, dictSampDQuote },

    { "\\\"", 0, dictSamp, dictSamp },
    { "\\\"", 0, dictSampSQuote, dictSampSQuote },
    { "\\\"", 0, dictSampDQuote, dictSampDQuote },

Rules for the <pre> element: The dictPre state tracks when output text is within the <pre> element, but not within single or double quotes.

    { "<pre>", 0, dictStart, dictPre },
    { "<pre class=code>", 0, dictStart, dictPre },
    { "<pre class=samp>", 0, dictStart, dictPre },
    { "</pre>", 0, dictPre, dictStart },
    { "</pre>", 0, dictPreSQuote, dictStart },
    { "</pre>", 0, dictPreDQuote, dictStart },

    { "<strong>", 0, dictPre, dictPre },
    { "</strong>", 0, dictPre, dictPre },

    { "'", 0, dictPre, dictPreSQuote },
    { "'", 0, dictPreSQuote, dictPre },

    { "\"", 0, dictPre, dictPreDQuote },
    { "\"", 0, dictPreDQuote, dictPre },

    { "\\'", 0, dictPre, dictPre },
    { "\\'", 0, dictPreSQuote, dictPreSQuote },
    { "\\'", 0, dictPreDQuote, dictPreDQuote },

    { "\\\"", 0, dictPre, dictPre },
    { "\\\"", 0, dictPreSQuote, dictPreSQuote },
    { "\\\"", 0, dictPreDQuote, dictPreDQuote },

    { 0 }
};