From: Lady <redacted> Date: Sat, 19 Oct 2024 19:30:35 +0000 (-0400) Subject: Support multiple documents per file X-Git-Tag: 0.3.0~4 X-Git-Url: https://git.ladys.computer/LesML/commitdiff_plain/80c75b24c821e3d8a1d5d1a460304f8c8d35179c?ds=inline Support multiple documents per file Documents may begin with either `#!lesml` or `##`. --- diff --git a/README.markdown b/README.markdown index 7df7fde..406f611 100644 --- a/README.markdown +++ b/README.markdown @@ -43,6 +43,13 @@ Following the shebang line, document metadata may be provided in the The body of the document begins after the last line which begins with the string `%%`, or after the shebang line if none exists. +Multiple documents can be catenated into a single file; a new document + is begun on any line which starts with `#!lesml` or `##`. +Documents in the later case inherit the latest preceding `#!lesml` + declaration. +`##` may be followed by other text; this is treated as an interdocument + comment. + Documents are broken into paragraphs by blank lines. Empty paragraphs are ignored. Non·empty paragraphs are classified as follows :— @@ -55,7 +62,6 @@ Non·empty paragraphs are classified as follows :— | Character | Codepoint | Unicode Name | | --------- | --------- | ------------ | - | `#` | `U+0023` | `NUMBER SIGN` | | `*` | `U+002A` | `ASTERISK` | | `-` | `U+002D` | `HYPHEN-MINUS` | | `.` | `U+002E` | `FULL STOP` | diff --git a/parser.xslt b/parser.xslt index f1ade04..d35d53b 100644 --- a/parser.xslt +++ b/parser.xslt @@ -12,7 +12,7 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v 2 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>. --> <!DOCTYPE transform [ - <!ENTITY section-break '#*-.=_~·․‥…⁂⋯─━┄┅┈┉╌╍═╴╶╸╺☙❧ ・*-.=_~'> + <!ENTITY section-break '*-.=_~·․‥…⁂⋯─━┄┅┈┉╌╍═╴╶╸╺☙❧ ・*-.=_~'> ]> <transform xmlns="http://www.w3.org/1999/XSL/Transform" @@ -129,7 +129,6 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one <value-of select="$first-line"/> </if> </variable> - <variable name="noshebang" select="$lines[position()>1 or not(starts-with(., '#!lesml') or starts-with(., '##'))]"/> <variable name="params-string"> <choose> <when test="starts-with($shebang, '#!lesml@')"> @@ -141,120 +140,141 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one </choose> </variable> <variable name="params-fragment"> - <html:dl> - <if test="starts-with($shebang, '#!lesml@') and contains($shebang, '$')"> - <html:div> - <html:dt> - <text> LANG </text> - </html:dt> - <html:dd> - <value-of select="substring-before(substring-after($shebang, '#!lesml@'), '$')"/> - </html:dd> - </html:div> - </if> - <for-each select="exslstr:tokenize($params-string)"> - <choose> - <when test="contains(., '=')"> + <choose> + <when test="$shebang!=''"> + <html:dl> + <if test="starts-with($shebang, '#!lesml@') and contains($shebang, '$')"> <html:div> <html:dt> - <value-of select="substring-before(., '=')"/> + <text> LANG </text> </html:dt> <html:dd> - <value-of select="substring-after(., '=')"/> + <value-of select="substring-before(substring-after($shebang, '#!lesml@'), '$')"/> </html:dd> </html:div> - </when> - <otherwise> - <html:div> - <html:dt> - <value-of select="."/> - </html:dt> - <html:dd/> - </html:div> - </otherwise> - </choose> - </for-each> - </html:dl> + </if> + <for-each select="exslstr:tokenize($params-string)"> + <choose> + <when test="contains(., '=')"> + <html:div> + <html:dt> + <value-of select="substring-before(., '=')"/> + </html:dt> + <html:dd> + <value-of select="substring-after(., '=')"/> + </html:dd> + </html:div> + </when> + <otherwise> + <html:div> + <html:dt> + <value-of select="."/> + </html:dt> + <html:dd/> + </html:div> + </otherwise> + </choose> + </for-each> + </html:dl> + </when> + <when test="$parent-params"> + <copy-of select="$parent-params"/> + </when> + <otherwise> + <html:dl/> + </otherwise> + </choose> </variable> <variable name="params" select="exsl:node-set($params-fragment)/*"/> - <variable name="record-separators" select="$noshebang[starts-with(., '%%')]"/> - <html:article> - <for-each select="$params/html:div/html:dt[string()=' LANG ']"> - <attribute name="lang"> - <value-of select="following-sibling::html:dd"/> - </attribute> - <attribute name="xml:lang"> - <value-of select="following-sibling::html:dd"/> - </attribute> - </for-each> - <for-each select="$params/html:div/html:dt[string()='profile']"> - <attribute name="data-lesml-profile"> - <value-of select="following-sibling::html:dd"/> - </attribute> - </for-each> - <if test="count($record-separators)>1"> - <html:footer class="head"> - <for-each select="$record-separators"> - <variable name="position" select="position()"/> - <variable name="prev-separator" select="$record-separators[($position)-1]"/> - <variable name="fields" select="$noshebang[following-sibling::*[generate-id()=generate-id(current())] and (not($prev-separator) or preceding-sibling::*[generate-id()=generate-id($prev-separator)])]"/> - <if test="$fields"> - <html:dl> - <for-each select="$fields"> - <choose> - <when test="starts-with(., ' ') and $fields[generate-id()=generate-id(current()/preceding-sibling::*[1])]"/> - <otherwise> - <variable name="next" select="following-sibling::*[not(starts-with(., ' '))]"/> - <html:div> - <html:dt> - <value-of select="normalize-space(substring-before(., ':'))"/> - </html:dt> - <html:dd> - <variable name="firstline"> - <choose> - <when test="contains(., ':')"> - <value-of select="normalize-space(substring-after(., ':'))"/> - </when> - <otherwise> - <value-of select="normalize-space(.)"/> - </otherwise> - </choose> - </variable> - <choose> - <when test="substring($firstline, string-length($firstline))='\' and following-sibling::*[position()=1 and starts-with(., ' ')]"> - <value-of select="substring($firstline, 1, string-length($firstline)-1)"/> - </when> - <otherwise> - <value-of select="$firstline"/> - </otherwise> - </choose> - <for-each select="following-sibling::*[starts-with(., ' ') and not(preceding-sibling::*[generate-id()=generate-id($next)])]"> - <variable name="nextline" select="normalize-space(.)"/> + <variable name="noshebang" select="$lines[position()>1 or not(starts-with(., '#!lesml') or starts-with(., '##'))]"/> + <variable name="docsep" select="$noshebang[starts-with(., '#!lesml') or starts-with(., '##')][1]"/> + <variable name="doclines" select="$noshebang[not($docsep) or following-sibling::*[generate-id()=generate-id($docsep)]]"/> + <if test="$shebang!='' or $doclines[normalize-space()!='']"> + <variable name="record-separators" select="$doclines[starts-with(., '%%')]"/> + <html:article> + <for-each select="$params/html:div/html:dt[string()=' LANG ']"> + <attribute name="lang"> + <value-of select="following-sibling::html:dd"/> + </attribute> + <attribute name="xml:lang"> + <value-of select="following-sibling::html:dd"/> + </attribute> + </for-each> + <for-each select="$params/html:div/html:dt[string()='profile']"> + <attribute name="data-lesml-profile"> + <value-of select="following-sibling::html:dd"/> + </attribute> + </for-each> + <if test="$record-separators[preceding-sibling::*[normalize-space()!='']]"> + <html:footer class="head"> + <for-each select="$record-separators"> + <variable name="position" select="position()"/> + <variable name="prev-separator" select="$record-separators[($position)-1]"/> + <variable name="fields" select="$noshebang[following-sibling::*[generate-id()=generate-id(current())] and (not($prev-separator) or preceding-sibling::*[generate-id()=generate-id($prev-separator)])]"/> + <if test="$fields"> + <html:dl> + <for-each select="$fields"> + <choose> + <when test="starts-with(., ' ') and $fields[generate-id()=generate-id(current()/preceding-sibling::*[1])]"/> + <otherwise> + <variable name="next" select="following-sibling::*[not(starts-with(., ' '))]"/> + <html:div> + <html:dt> + <value-of select="normalize-space(substring-before(., ':'))"/> + </html:dt> + <html:dd> + <variable name="firstline"> + <choose> + <when test="contains(., ':')"> + <value-of select="normalize-space(substring-after(., ':'))"/> + </when> + <otherwise> + <value-of select="normalize-space(.)"/> + </otherwise> + </choose> + </variable> <choose> - <when test="substring($nextline, string-length($nextline))='\' and following-sibling::*[position()=1 and starts-with(., ' ')]"> - <value-of select="substring($nextline, 1, string-length($nextline)-1)"/> + <when test="substring($firstline, string-length($firstline))='\' and following-sibling::*[position()=1 and starts-with(., ' ')]"> + <value-of select="substring($firstline, 1, string-length($firstline)-1)"/> </when> <otherwise> - <value-of select="$nextline"/> + <value-of select="$firstline"/> </otherwise> </choose> - </for-each> - </html:dd> - </html:div> - </otherwise> - </choose> - </for-each> - </html:dl> - </if> - </for-each> - </html:footer> - </if> - <html:div class="body"> - <call-template name="LesML:paragraphize"> - <with-param name="lines" select="$noshebang[not($record-separators) or preceding-sibling::*[generate-id()=generate-id($record-separators[last()])]]"/> - </call-template> - </html:div> - </html:article> + <for-each select="following-sibling::*[starts-with(., ' ') and not(preceding-sibling::*[generate-id()=generate-id($next)])]"> + <variable name="nextline" select="normalize-space(.)"/> + <choose> + <when test="substring($nextline, string-length($nextline))='\' and following-sibling::*[position()=1 and starts-with(., ' ')]"> + <value-of select="substring($nextline, 1, string-length($nextline)-1)"/> + </when> + <otherwise> + <value-of select="$nextline"/> + </otherwise> + </choose> + </for-each> + </html:dd> + </html:div> + </otherwise> + </choose> + </for-each> + </html:dl> + </if> + </for-each> + </html:footer> + </if> + <html:div class="body"> + <call-template name="LesML:paragraphize"> + <with-param name="lines" select="$doclines[not($record-separators) or preceding-sibling::*[generate-id()=generate-id($record-separators[last()])]]"/> + </call-template> + </html:div> + </html:article> + </if> + <if test="$docsep"> + <call-template name="LesML:parse"> + <with-param name="lines" select="$docsep|$lines[preceding-sibling::*[generate-id()=generate-id($docsep)]]"/> + <with-param name="parent-params" select="$params"/> + </call-template> + </if> </template> <template name="LesML:paragraphize"> <param name="lines" select="/.."/>