]> Lady’s Gitweb - Shushe/commitdiff
Add a parser for Pipe‐Separated Jar format current 1.1.1
authorLady <redacted>
Mon, 30 Mar 2026 01:56:10 +0000 (21:56 -0400)
committerLady <redacted>
Mon, 30 Mar 2026 04:27:06 +0000 (00:27 -0400)
This is a bespoke format which is basically a T·S·V that can be broken
across multiple lines for readability.

README.markdown
magic/psj [new file with mode: 0644]
parsers/psj.xslt [new file with mode: 0644]

index 658866e02a7d9e78c026c567b4f98472e7cef217..994df1fc0120609d5e26f66ed1fc5fc4591cde37 100644 (file)
@@ -491,6 +491,10 @@ Parsers are used to convert plaintext files into X·M·L trees, as well
 - **`parsers/plain.xslt`:**
   Wraps `text/plain` contents in a `<html:pre>` element.
 
 - **`parsers/plain.xslt`:**
   Wraps `text/plain` contents in a `<html:pre>` element.
 
+- **`parsers/psj.xslt`:**
+  Converts `text/pipe-separated-jar` contents into an `<html:table>`
+    element.
+
 - **`parsers/record-jar.xslt`:**
   Converts `text/record-jar` contents into a `<html:div>` of
     `<html:dl>` elements (one for each record).
 - **`parsers/record-jar.xslt`:**
   Converts `text/record-jar` contents into a `<html:div>` of
     `<html:dl>` elements (one for each record).
diff --git a/magic/psj b/magic/psj
new file mode 100644 (file)
index 0000000..373fe06
--- /dev/null
+++ b/magic/psj
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2024, 2026 Lady <https://www.ladys.computer/about/#lady>
+# SPDX-License-Identifier: CC0-1.0
+
+0  string  %?psj     PSJ text
+!:mime text/pipe-separated-jar
+!:strength + 100
+
+0     byte    0xEF
+>1    byte    0xBB
+>>2   byte    0xBF
+>>>3  string  %?psj  PSJ text
+!:mime text/pipe-separated-jar
+!:strength + 170
diff --git a/parsers/psj.xslt b/parsers/psj.xslt
new file mode 100644 (file)
index 0000000..36f8347
--- /dev/null
@@ -0,0 +1,268 @@
+<?xml version="1.0"?>
+<!--
+SPDX-FileCopyrightText: 2026 Lady <https://www.ladys.computer/about/#lady>
+SPDX-License-Identifier: MPL-2.0
+-->
+<!--
+⁌ ⛩📰 书社 ∷ parsers/psj.xslt
+
+© 2026 Lady [@ Ladys Computer].
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+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 xhtml 'http://www.w3.org/1999/xhtml'>
+]>
+<!--
+This is an alterative to the T·S·V format which allows rows to take up
+  multiple lines and separates columns with backslash‐escapable `|`s.
+
+Roughly speaking, the syntax is as follows :—
+
+- The file should begin with `%?psj`.
+
+- Rows are terminated by lines which begin `%%`.
+  Any extra content on the line following the `%%` is treated as a
+    comment.
+  The final row must be terminated by `%%`, just like any other row.
+
+- Rows which consist only of whitespace (including newlines) are
+    ignored.
+
+- Trailing whitespace in row lines is removed.
+
+- If (after trimming) a line in a row ends in an odd number of
+    backslashes, the final one is dropped.
+  This enables a row line to end in a space by following the space
+    with a backslash.
+
+- Newlines in the row are then removed.
+
+- Rows are broken into columns via `|` characters.
+  These can be escaped with backslashes.
+  Backslashes can also escape themselves.
+
+- Whitespace is trimmed and collapsed in each column.
+
+- Empty columns at the end of each row are removed.
+  Then, each row is expanded with empty columns to the width of the
+    widest row.
+
+- The first row is treated as a header and remaining rows as the body.
+-->
+<transform
+       xmlns="http://www.w3.org/1999/XSL/Transform"
+       xmlns:exsl="http://exslt.org/common"
+       xmlns:exslmath="http://exslt.org/math"
+       xmlns:exslset="http://exslt.org/sets"
+       xmlns:exslstr="http://exslt.org/strings"
+       xmlns:html="&xhtml;"
+       xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+       extension-element-prefixes="exsl exslmath exslset exslstr"
+       version="1.0"
+>
+       <import href="../lib/split.xslt"/>
+       <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:psj.xslt</书社:id>
+       <template name="书社:psj-fill-empty-cols">
+               <param name="header-row" select="/.."/>
+               <param name="cols-to-fill" select="0"/>
+               <if test="$cols-to-fill>0">
+                       <element name="td" namespace="&xhtml;">
+                               <if test="$header-row">
+                                       <variable name="header" select="$header-row/*[count($header-row/*)-$cols-to-fill+1]"/>
+                                       <if test="$header">
+                                               <attribute name="data-psj-header">
+                                                       <value-of select="$header"/>
+                                               </attribute>
+                                       </if>
+                               </if>
+                       </element>
+                       <call-template name="书社:psj-fill-empty-cols">
+                               <with-param name="header-row" select="$header-row"/>
+                               <with-param name="cols-to-fill" select="($cols-to-fill)-1"/>
+                       </call-template>
+               </if>
+       </template>
+       <template match="html:script[@type='text/pipe-separated-jar']">
+               <variable name="lines" select="exslstr:tokenize(., '&#xA;&#xD;')[not(normalize-space(.)='')]"/>
+               <variable name="shero" select="$lines[1][starts-with(., '%?psj')]"/>
+               <element name="table" namespace="&xhtml;">
+                       <if test="$shero and $shero!='%?psj'">
+                               <comment>
+                                       <value-of select="substring-after($shero, '%?psj')"/>
+                               </comment>
+                       </if>
+                       <variable name="firstline" select="$lines[$shero and position()=2 or not($shero) and position()=1]"/>
+                       <if test="starts-with($firstline, '%%') and $firstline!='%%'">
+                               <comment>
+                                       <value-of select="substring-after($firstline, '%%')"/>
+                               </comment>
+                       </if>
+                       <variable name="rows-fragment">
+                               <for-each select="$firstline/following-sibling::*[starts-with(., '%%')]">
+                                       <variable name="end" select="."/>
+                                       <variable name="start" select="(preceding-sibling::*[starts-with(., '%%')][1]|$shero)[last()]"/>
+                                       <variable name="row-lines" select="exslset:intersection(exslset:trailing($end/preceding-sibling::*, $start), $lines)"/>
+                                       <if test="$row-lines[normalize-space(.)!='']">
+                                               <variable name="row">
+                                                       <for-each select="$row-lines">
+                                                               <variable name="line" select="normalize-space(.)"/>
+                                                               <choose>
+                                                                       <when test="substring($line, string-length($line))='\'">
+                                                                               <variable name="nobackslash" select="translate($line, '\', '')"/>
+                                                                               <variable name="onlybackslash" select="exslstr:tokenize($line, $nobackslash)[last()]"/>
+                                                                               <choose>
+                                                                                       <when test="string-length($onlybackslash) mod 2=1">
+                                                                                               <value-of select="substring($line, 1, string-length($line)-1)"/>
+                                                                                       </when>
+                                                                                       <otherwise>
+                                                                                               <value-of select="$line"/>
+                                                                                       </otherwise>
+                                                                               </choose>
+                                                                       </when>
+                                                                       <otherwise>
+                                                                               <value-of select="$line"/>
+                                                                       </otherwise>
+                                                               </choose>
+                                                       </for-each>
+                                               </variable>
+                                               <variable name="cols-fragment">
+                                                       <call-template name="书社:split">
+                                                               <with-param name="source" select="$row"/>
+                                                               <with-param name="separator" select="'|'"/>
+                                                       </call-template>
+                                               </variable>
+                                               <variable name="unescaped-cols-fragment">
+                                                       <for-each select="exsl:node-set($cols-fragment)/node()">
+                                                               <variable name="is-last" select="position()=last()"/>
+                                                               <html:span>
+                                                                       <variable name="nodoubles-fragment">
+                                                                               <call-template name="书社:split">
+                                                                                       <with-param name="source" select="string()"/>
+                                                                                       <with-param name="separator" select="'\\'"/>
+                                                                               </call-template>
+                                                                       </variable>
+                                                                       <for-each select="exsl:node-set($nodoubles-fragment)/node()">
+                                                                               <choose>
+                                                                                       <when test="position()!=last()">
+                                                                                               <value-of select="."/>
+                                                                                               <text>\</text>
+                                                                                       </when>
+                                                                                       <otherwise>
+                                                                                               <choose>
+                                                                                                       <when test="not($is-last) and substring(., string-length(.))='\'">
+                                                                                                               <value-of select="substring(., 1, string-length(.)-1)"/>
+                                                                                                               <processing-instruction name="书社-psj-not-finished"/>
+                                                                                                       </when>
+                                                                                                       <otherwise>
+                                                                                                               <value-of select="."/>
+                                                                                                       </otherwise>
+                                                                                               </choose>
+                                                                                       </otherwise>
+                                                                               </choose>
+                                                                       </for-each>
+                                                               </html:span>
+                                                       </for-each>
+                                               </variable>
+                                               <variable name="unescaped-cols" select="exsl:node-set($unescaped-cols-fragment)/node()"/>
+                                               <variable name="start-cols" select="$unescaped-cols[not(preceding-sibling::*[position()=1 and processing-instruction()])]"/>
+                                               <html:tr>
+                                                       <for-each select="$start-cols">
+                                                               <variable name="next" select="exslset:intersection($start-cols, following-sibling::*)[1]"/>
+                                                               <variable name="text">
+                                                                       <value-of select="text()"/>
+                                                                       <for-each select="exslset:leading(following-sibling::*, $next)">
+                                                                               <text>|</text>
+                                                                               <value-of select="text()"/>
+                                                                       </for-each>
+                                                               </variable>
+                                                               <html:td>
+                                                                       <value-of select="normalize-space($text)"/>
+                                                               </html:td>
+                                                       </for-each>
+                                               </html:tr>
+                                       </if>
+                                       <if test="substring-after(., '%%')!=''">
+                                               <comment>
+                                                       <value-of select="substring-after(., '%%')"/>
+                                               </comment>
+                                       </if>
+                               </for-each>
+                       </variable>
+                       <variable name="rows" select="exsl:node-set($rows-fragment)/node()"/>
+                       <variable name="lengths-fragment">
+                               <for-each select="$rows[self::*]">
+                                       <html:span>
+                                               <variable name="last-col" select="*[not(following-sibling::*[.!=''])][1]"/>
+                                               <choose>
+                                                       <when test="$last-col">
+                                                               <value-of select="1+count($last-col/preceding-sibling::*)"/>
+                                                       </when>
+                                                       <otherwise>
+                                                               <text>0</text>
+                                                       </otherwise>
+                                               </choose>
+                                       </html:span>
+                               </for-each>
+                       </variable>
+                       <variable name="length" select="exslmath:max(exsl:node-set($lengths-fragment)/*)"/>
+                       <variable name="first-row" select="$rows[1]"/>
+                       <for-each select="exslset:leading($rows, $first-row)">
+                               <copy-of select="."/>
+                       </for-each>
+                       <if test="$first-row">
+                               <element name="thead" namespace="&xhtml;">
+                                       <element name="tr" namespace="&xhtml;">
+                                               <for-each select="$first-row/*">
+                                                       <choose>
+                                                               <when test=".!=''">
+                                                                       <element name="th" namespace="&xhtml;">
+                                                                               <attribute name="scope">
+                                                                                       <text>col</text>
+                                                                               </attribute>
+                                                                               <value-of select="."/>
+                                                                       </element>
+                                                               </when>
+                                                               <otherwise>
+                                                                       <element name="td" namespace="&xhtml;"/>
+                                                               </otherwise>
+                                                       </choose>
+                                               </for-each>
+                                               <call-template name="书社:psj-fill-empty-cols">
+                                                       <with-param name="cols-to-fill" select="($length)-count($first-row/*)"/>
+                                               </call-template>
+                                       </element>
+                               </element>
+                       </if>
+                       <element name="tbody" namespace="&xhtml;">
+                               <for-each select="$first-row/following-sibling::node()">
+                                       <choose>
+                                               <when test="self::*">
+                                                       <element name="tr" namespace="&xhtml;">
+                                                               <for-each select="*">
+                                                                       <element name="td" namespace="&xhtml;">
+                                                                               <variable name="header" select="$first-row/*[count(current()/preceding-sibling::*)+1]"/>
+                                                                               <if test="$header!=''">
+                                                                                       <attribute name="data-psj-header">
+                                                                                               <value-of select="$header"/>
+                                                                                       </attribute>
+                                                                               </if>
+                                                                               <value-of select="."/>
+                                                                       </element>
+                                                               </for-each>
+                                                               <call-template name="书社:psj-fill-empty-cols">
+                                                                       <with-param name="header-row" select="$first-row"/>
+                                                                       <with-param name="cols-to-fill" select="($length)-count(*)"/>
+                                                               </call-template>
+                                                       </element>
+                                               </when>
+                                               <otherwise>
+                                                       <copy-of select="."/>
+                                               </otherwise>
+                                       </choose>
+                               </for-each>
+                       </element>
+               </element>
+       </template>
+</transform>
This page took 0.381753 seconds and 4 git commands to generate.