Re: [xsl] I love programs that output programs

Subject: Re: [xsl] I love programs that output programs
From: "Dimitre Novatchev dnovatchev@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 17 Jan 2025 21:41:21 -0000
Another note: People may want to generate not xslt-code, but pure XPath
code (and evaluate it with xsl:evaluate in XSLT, or using fn:load-xquery
module, if they don't have an xslt-capable host.
https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-load-x
query-module

On Fri, Jan 17, 2025 at 1:36b/PM Dimitre Novatchev <dnovatchev@xxxxxxxxx>
wrote:

> Hi Roger,
>
> >         <axsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
> version="3.0">
> >               <axsl:template match="/">
> >                  <axsl:for-each select="/passwds/passwd">
> >                       <axsl:variable name="passwd" select="."/>
> >                       <xsl:for-each select="/validation_tests/test">
> >                           <axsl:choose>
> >                               <axsl:when test="{xpath}">
> >                                   <axsl:message>line <axsl:value-of
> select="position()"/>, <xsl:value-of select="message"/>: <axsl:value-of
> select="$passwd"/></axsl:message>
> >                               </axsl:when>
> >                           </axsl:choose>
> >                       </xsl:for-each>
> >                   </axsl:for-each>
> >               </axsl:template>
> >           </axsl:stylesheet>
>
> This seems rather unreadable to me, and I would definitely want to avoid
> having to debug two different transformations - too-inconvenient.
>
> What I would do instead:
>
>    1. Generate the new XSLT code as a string. This is more readable,
>    because no  <xsl:namespace-alias> directive is needed and we will be
using
>    just/only the wellknown "xsl:" prefix.
>    2. Call the standard XPath fn:transform, passing to it as a parameter
>    the generated string.
>    https://www.w3.org/TR/xpath-functions-31/#func-transform
>    3. This can be combined with the "fill-in the blanks" approach, where
>    the skeleton for the generated code is contained in an external file,
and
>    only its (marked as) variable parts are dynamically replaced with
>    calculated values.
>
> Also, this approach has the advantage that the xslt code generation can be
> coded not only in XSLT, but even in pure XPath 3.
>
> As they say, "just my 2c." :)
>
> Thanks,
> Dimitre
>
>
>
> On Fri, Jan 17, 2025 at 12:22b/PM Roger L Costello costello@xxxxxxxxx <
> xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
>> Hi Folks,
>>
>> Recently I was reading Brian Kernighan's new book on AWK, and it showed a
>> wicked cool AWK program that outputs an AWK program. Below is the AWK
>> program, followed by the equivalent XSLT program.
>>
>> The task is to run some checks on a password file. Here is a sample
>> password file:
>>
>> root:qyxRi2uVjrg:0:2::/:
>> jd:1L./v6iblzzNE:9:1:John Doe:/usr/jd:
>> jt:otxs1oToyvMQ:15:1:Jim Thomson:/usr/jt:
>> uucp:xutIBs2hKtcls:48:1:uucp daemon:/usr/lib/uucp:uucico
>> sm:xNqy//GDc8FFg:170:2:Sally Smith:/usr/sm:
>> a!f:aiopaIAjfaI:21:1:Fake Person:/usr/a!f:
>> jv::1:Jules Verne:/usr/jv:
>> ah:dsjkdAJ:34:1:Alexander Hamilton
>>
>> Fields within each line are delimited by a colon.
>>
>> The first field is the username. It should be alphanumeric. Check each
>> line to confirm that its username is alphanumeric.
>> The second field is an encrypted version of the password. The field
>> should not be empty. Check each line to confirm that it's not empty.
>> I won't discuss the other fields.
>> The third check confirms that each line has 7 fields.
>>
>>
>>
-----------------------------------------------------------------------------
---------------
>>           AWK Version
>>
>>
-----------------------------------------------------------------------------
---------------
>> Here's an AWK expression to check whether a line does not have 7 fields:
>>
>> NF != 7
>>
>> [NF = Number of Fields, NF is a built-in function]
>>
>> Here's an AWK expression to check whether the second field is empty:
>>
>> $2 == ""
>>
>> [$1 denotes the first field, $2 denotes the second field, etc.]
>>
>> Here's an AWK expression to check whether the first field is not
>> alphanumeric:
>>
>> $1 ~ /[^A-Za-z0-9]/
>>
>> [tilda means "matches"; the stuff between slashes is a regex]
>>
>> Put the expressions into a file, along with a description:
>>
>> NF != 7                 does not have 7 fields
>> $2 == ""                no password
>> $1 ~ /[^A-Za-z0-9]/     nonalphanumeric user id
>>
>> The following AWK program inputs those expressions and outputs a program
>> that applies the expressions to each line of the password file:
>>
>> BEGIN { FS = "\t+" }
>> { printf("%s {\n\tprintf(\"line %%d, %s: %%s\\n\",NR,$0) }\n", $1, $2) }
>>
>> [FS = Field Separator. NR = Row Number, $0 denotes the entire line. If
>> you know the C printf statement you can probably figure out what that
>> printf is doing--it is outputting a printf]
>>
>> Run the AWK program; it generates this AWK program:
>>
>> NF != 7 {
>>         printf("line %d, does not have 7 fields: %s\n",NR,$0) }
>> $2 == "" {
>>         printf("line %d, no password: %s\n",NR,$0) }
>> $1 ~ /[^A-Za-z0-9]/ {
>>         printf("line %d, nonalphanumeric user id: %s\n",NR,$0) }
>>
>> Now run that generated AWK program against the password file. It reveals
>> errors in the password file:
>>
>> line 6, nonalphanumeric user id: a!f:aiopaIAjfaI:21:1:Fake
>> Person:/usr/a!f:
>> line 7, does not have 7 fields: jv::1:Jules Verne:/usr/jv:
>> line 7, no password: jv::1:Jules Verne:/usr/jv:
>> line 8, does not have 7 fields: ah:dsjkdAJ:34:1:Alexander Hamilton
>>
>>
>>
-----------------------------------------------------------------------------
---------------
>>           XSLT/XPath Version
>>
>>
-----------------------------------------------------------------------------
---------------
>> Format the password file as XML:
>>
>> <passwds>
>>     <passwd>root:qyxRi2uVjrg:0:2::/:</passwd>
>>     <passwd>jd:1L./v6iblzzNE:9:1:John Doe:/usr/jd:</passwd>
>>     <passwd>jt:otxs1oToyvMQ:15:1:Jim Thomson:/usr/jt:</passwd>
>>     <passwd>uucp:xutIBs2hKtcls:48:1:uucp
>> daemon:/usr/lib/uucp:uucico</passwd>
>>     <passwd>sm:xNqy//GDc8FFg:170:2:Sally Smith:/usr/sm:</passwd>
>>     <passwd>a!f:aiopaIAjfaI:21:1:Fake Person:/usr/a!f:</passwd>
>>     <passwd>jv::1:Jules Verne:/usr/jv:</passwd>
>>     <passwd>ah:dsjkdAJ:34:1:Alexander Hamilton</passwd>
>> </passwds>
>>
>> Create XPath expressions for the three checks and put the XPath
>> expressions into a file:
>>
>> <validation_tests>
>>     <test>
>>         <xpath>count(tokenize(.,':')) ne 7</xpath>
>>         <message>does not have 7 fields</message>
>>     </test>
>>     <test>
>>         <xpath>tokenize(.,':')[2] eq ''</xpath>
>>         <message>no password</message>
>>     </test>
>>     <test>
>>         <xpath>string-length(translate(tokenize(., ':')[1],
>> 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', '')) ne
>> 0</xpath>
>>         <message>nonalphanumeric user id</message>
>>     </test>
>> </validation_tests>
>>
>> This XSLT program inputs the XPath expressions file and outputs an XSLT
>> program that applies the XPath expressions:
>>
>> <xsl:stylesheet         xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>>                 xmlns:axsl="http://www.w3.org/1999/XSL/Transform/alias";
>>                 version="3.0">
>>
>>     <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
>>
>>     <xsl:template match="/">
>>         <axsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>> version="3.0">
>>             <axsl:template match="/">
>>                 <axsl:for-each select="/passwds/passwd">
>>                     <axsl:variable name="passwd" select="."/>
>>                     <xsl:for-each select="/validation_tests/test">
>>                         <axsl:choose>
>>                             <axsl:when test="{xpath}">
>>                                 <axsl:message>line <axsl:value-of
>> select="position()"/>, <xsl:value-of select="message"/>: <axsl:value-of
>> select="$passwd"/></axsl:message>
>>                             </axsl:when>
>>                         </axsl:choose>
>>                     </xsl:for-each>
>>                 </axsl:for-each>
>>             </axsl:template>
>>         </axsl:stylesheet>
>>     </xsl:template>
>> </xsl:stylesheet>
>>
>> Here is the XSLT it generates:
>>
>> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>> version="3.0">
>>     <xsl:template match="/">
>>         <xsl:for-each select="/passwds/passwd">
>>             <xsl:variable name="passwd" select="."/>
>>             <xsl:choose>
>>                 <xsl:when test="count(tokenize(., ':')) ne 7">
>>                     <xsl:message>line <xsl:value-of
>> select="position()"/>, does not have 7 fields: <xsl:value-of
>> select="$passwd"/></xsl:message>
>>                 </xsl:when>
>>             </xsl:choose>
>>             <xsl:choose>
>>                 <xsl:when test="tokenize(., ':')[2] eq ''">
>>                     <xsl:message>line <xsl:value-of
>> select="position()"/>, no password: <xsl:value-of
>> select="$passwd"/></xsl:message>
>>                 </xsl:when>
>>             </xsl:choose>
>>             <xsl:choose>
>>                 <xsl:when test="string-length(translate(tokenize(.,
>> ':')[1], 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
>> '')) ne 0">
>>                     <xsl:message>line <xsl:value-of
>> select="position()"/>, nonalphanumeric user id: <xsl:value-of
>> select="$passwd"/></xsl:message>
>>                 </xsl:when>
>>             </xsl:choose>
>>         </xsl:for-each>
>>     </xsl:template>
>> </xsl:stylesheet>
>>
>> Run the generated XSLT program against the password file; these messages
>> are displayed:
>>
>> line 6, nonalphanumeric user id: a!f:aiopaIAjfaI:21:1:Fake
>> Person:/usr/a!f:
>> line 7, does not have 7 fields: jv::1:Jules Verne:/usr/jv:
>> line 7, no password: jv::1:Jules Verne:/usr/jv:
>> line 8, does not have 7 fields: ah:dsjkdAJ:34:1:Alexander Hamilton
>>
>>
>>
>
>
>


--
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
To avoid situations in which you might make mistakes may be the
biggest mistake of all
------------------------------------
Quality means doing it right when no one is looking.
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play
-------------------------------------
To achieve the impossible dream, try going to sleep.
-------------------------------------
Facts do not cease to exist because they are ignored.
-------------------------------------
Typing monkeys will write all Shakespeare's works in 200yrs.Will they write
all patents, too? :)
-------------------------------------
Sanity is madness put to good use.
-------------------------------------
I finally figured out the only reason to be alive is to enjoy it.

Current Thread