Procedural Footnote Language
Procedural Footnote Language (PFL) is a system of adding structured footnote objects to a text file. Through a system of tags/delimiters and footnote definitions, complex logic can be created, generating either a static document or using user input. The original language specification was created by Daniel Myers.
Introduction
In the course of composing email or other text documents it may be desirable to add comments or other notes to clarify a statement when those additions may not fit the syntax of the statement, or may not significantly add to the reader’s understanding of the statement, and where the use of a parenthetical comment would adversely affect the readability of the statement. In such a situation footnotes are one method of including the additional information with minimal negative impact.
Language Description
The PFL Document
A document complying with the PFL 1.0 format consists of plain ASCII text and is divided into two sections: the BODY and the FOOTNOTES.
The BODY Section
The BODY section consists of zero or more ASCII characters with an undefined structure, and containing zero or more footnote delimiters. A footnote delimiter consists of a positive integer enclosed in square brackets. As the square bracket is used in PFL as a delimiter to indicate a footnote, any square brackets in the body of a document must be “escaped out” by enclosing them in square brackets. All footnote delimiters in the BODY section must have a corresponding footnote in the FOOTNOTE section.
Example: This is body text containing a footnote tag[1]
Example: [[]This is body text[1] enclosed in square brackets.[]]
The FOOTNOTES Section
The FOOTNOTES section consists of the PFL document identifier followed by zero or more footnotes in PFL format. The PFL document identifier is the text “PFL” followed by the version number, enclosed in square brackets, on a line by itself. Any text between the PFL document identifier and the first PFL footnote is ignored.
Example: [PFL1.0]
The proper syntax of a PFL footnote is a number enclosed by square brackets followed by a space followed by 1 or more ASCII characters. There must be no text between the start of the line and the start of the footnote. As with the body text any square brackets in the text portion of a PFL footnote must be escaped out by enclosing them in square brackets. All footnotes in the FOOTNOTE section must have a corresponding delimiter in the BODY section. The numbers of the footnotes in the FOOTNOTES section must be in sequential order and may not skip any values.
Example: [1] Hi Sherry!
Footnotes may also have one or two optional parameters. A first parameter will indicate the maximum number of times a footnote is to be evaluated, with any further delimiters referencing that footnote being ignored.
Example: The footnote [1:3] Wubba [1]
would parse as Wubba Wubba Wubba
A second parameter will indicate the minimum index value for the footnote to be evaluated. The footnote will not be evaluated before this index is reached. Note that a delimiter referencing the footnote will advance the index for that footnote, but will not add to the number of times the footnote has been evaluated unless the index is greater or equal to the first parameter.
Markers
There are a number of optional markers. These markers all take the form of one or more ASCII characters enclosed in square brackets.
[HS]
(honorable salutation) – This marker may occur in either the BODY or FOOTNOTES sections of a PFL document and is the equivalent to a delimiter pointing to a footnote that contains the text “Hi Sherry!”. Note that if the[HS]
marker appears at the start of a line in the FOOTNOTES section, it is taken as a special “Hi Sherry!” footnote and will generate aUFA
error if a[HS]
marker does not also appear elsewhere in the document.[PFLEND]
(PFL End of Document) – Indicates the end of the FOOTNOTES section of a PFL document. This marker may only be used at the end of the FOOTNOTES section. All text in a document after the[PFLEND]
marker will be ignored.[[]
(“Escaped” Leading Square Bracket) – used when a open square bracket is desired anywhere in the document. This marker may not be used at the start of a line in the FOOTNOTES section of a document.[]]
(“Escaped” Trailing Square Bracket) – used when a close square bracket is desired anywhere in the document. This marker may not be used at the start of a line in the FOOTNOTES section of a document.
Functions
PFL Defines a number of built in functions of a mathematical or logical nature. Each function is evaluated by the parser. The functions may be nested as required. Parameters enclosed in angle brackets are optional.
[ABC]
- returnsABCDEFGHIJKLMNOPQRSTUVWXYZ
[ADD:x:y]
- returns the valuex + y
[AND:condition 1:condition 2]
- returnstrue
if both conditions aretrue
, andfalse
otherwise[ASCII:hex]
- returns the ASCII character for a given hexadecimal value[BEEP]
- system beep[DATE]
- returns the current date[FALSE]
- returnsfalse
[GT:x:y]
- returnstrue
ifx
is greater thany
, andfalse
otherwise[HEX:character]
- returns the hexadecimal value of an ASCII character[IF:condition:x<:y>]
- Evaluates the condition and branches to footnotex
if the result istrue
, otherwise branches to footnotey
. A footnote used as the condition will always evaluate astrue
.[INDEX:x]
- returns the number of times that footnotex
has been evaluated by the parser[INPUT]
- pauses execution, reads a line of input fromSTDIN
, and returns the result (new in v1.0.2)[IS:x:y]
- returnstrue
ifx
is equal toy
, andfalse
otherwise[LEN:text1]
- returns the number of characters intext1
[LT:x:y]
- returnstrue
ifx
is less thany
, andfalse
otherwise[NOT:condition]
- returnstrue
if the condition isfalse
, andfalse
otherwise[OR:condition 1:condition 2]
- returnstrue
if either condition istrue
(or both), andfalse
otherwise[ORD:x]
- returns the ordinal value ofx
(i.e."first"
for1
,"second"
for2
, etc…)[PRIME:x]
- returnstrue
ifx
is a prime number, andfalse
otherwise (new in v1.0.1)[RET]
- returns a carriage return[SPACE]
- returns a space[SUB:x:y]
- returns the valuex – y
[TAB]
- returns a tab[TIME]
- returns the current time[TRUE]
- returnstrue
[USERDATA:parameter]
- returns various data on the user, if it is available. If the parameter is omitted then it will returntrue
if the user data is available, andfalse
otherwise.[USERDATA:FULLNAME]
- returns the full name of the user in the format“firstname lastname”
[USERDATA:FIRSTNAME]
- returns the first name of the user[USERDATA:LASTNAME]
- returns the last name of the user[USERDATA:INITIAL]
- returns the middle initial of the user[USERDATA:COMPANY]
- returns the company name of the user[USERDATA:STREET]
- returns the street address of the user[USERDATA:CITY]
- returns the city of the user[USERDATA:REGION]
- returns the region of the user[USERDATA:POSTAL]
- returns the postal zone of the user[USERDATA:COUNTRY]
- returns the country of the user[USERDATA:LANGUAGE]
- returns the preferred language of the user
[VER]
- returns the PFL version of the parser[XOR:condition 1:condition 2]
- returnstrue
if either condition istrue
(but not both) , andfalse
otherwise[ZEN]
- doesn’t return anything
Possible Errors
The following are potential errors that may be encountered when parsing a PFL document.
FSE
- Footnote Sequence Error: The footnote numbers in the FOOTNOTES section are not in sequential order.IFA
- Improper Footnote Alert: A footnote was found in the FOOTNOTES section that was not properly formed.MDA
- Malformed Delimiter Alert: A delimiter was found in either the BODY or the FOOTNOTES section that was not properly formed. This may be caused by square brackets that have not been properly escaped.MFA
- Missing Footnote Alert: There is a delimiter in the BODY section of the document that has no corresponding footnote in the FOOTNOTES section.NOT
- Not a PFL Document: No PFL document identifier was found.TMI
- Too Much Information: There was a buffer overflow while parsing the document, possibly due to a circular reference.UFA
- Unassigned Footnote Alert: There is a footnote in the FOOTNOTES section of the document that has no corresponding delimiter in the BODY section.UPM
- Unexpected PFLEND Marker: A [PFLEND] marker was encountered in the BODY section of the document.UVN
- Unrecognized Version Number: The version of PFL specified in the PFL document identifier was not recognized by the parser.
Examples
Hello World
[1] [PFL1.0] [1] Hello, world! [PFLEND]
Adding Two Numbers
[1][2][3] [PFL1.0] [1:0:2] [INPUT] [2:0:2] [INPUT] [3] [ADD:[1]:[2]] [PFLEND]
Primality Test
[1][2] [PFL1.0] [1:0:2] [INPUT] [2] [PRIME:[1]] [PFLEND]
99 Bottles of Beer
[1][2] [PFL1.0] [1:98] [SUB:100:[INDEX:1]] bottles of beer on the wall, [SUB:100:[INDEX:1]] bottles of beer.[RET]Take one down, pass it around, [IF:[GT:[SUB:99:[INDEX:1]]:1]:[SUB:99:[INDEX:1]] bottles:1 bottle] of beer on the wall.[RET][RET][1] [2] 1 bottle of beer on the wall, 1 bottle of beer.[RET]Take one down and pass it around, no more bottles of beer on the wall.[RET][RET]No more bottles of beer on the wall, no more bottles of beer.[RET]Go to the store and buy some more, 99 bottles of beer on the wall.[RET] [PFLEND]
Computational class
Procedural Footnote Language is Turing complete. Arbitrary control flow is easy (it can be constructed from IF
and recursion), so the only issue is data storage. This can be accomplished using pairs of footnotes with no text to represent counters: incrementing the counter is done by expanding the first footnote in the pair, decrementing the counter is done by expanding the second footnote in the pair, and a zero test is accomplished via comparing the INDEX
of the two counters to see if they're the same. Together with the arbitrary control flow, increment, decrement, and zero test are enough to create a Minsky machine.
Note that in practice, PFL interpreters may well have a maximum limit on the length of time for which the program can run, unless they implement tail recursion. Thus, implementations which do not use tail-recursion optimisation may fail to be Turing-complete, even though the language itself is conceptually Turing complete. (Likewise, INDEX
is the only way to persistently store data, so implementations which do not have unbounded INDEX
counters are bounded-storage machines.)