0
votes

I have this XML

<math>
 <mrow>
  <mi>XY</mi>
  <mrow>
   <mrow>
    <mo>(</mo>
    <mrow>
     <mfrac>
      <mn>5</mn>
      <mrow>
       <mn>10</mn>
      </mrow>
     </mfrac>
    </mrow>
    <mo>)</mo>
   </mrow>
  </mrow>
  <msup>
   <mn>2</mn>
  </msup>
 </mrow>
</math>
<br/>
<math>
 <mrow>
  <mi>x</mi>
  <msub>
   <mn>1</mn>
  </msub>
  <msup>
   <mn>2</mn>
  </msup>
 </mrow>
</math>

The following XSL performs a near-identity transformation: it copies everything through, except that it has special handling for msub elements and msup elements. If we have both an msub and an msup in either order, we want to produce a single msupsub element with both of them (and the preceding element, which is the base of the sub- and superscripts). If we have just one, then we produce an msub or msup but bring the immediately preceding sibling in as the first child.

<xsl:stylesheet version="2.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" encoding="UTF-8" indent="yes" />

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="msup">
    <xsl:choose>
      <xsl:when test="preceding-sibling::msub">
        <msubsup>
          <xsl:apply-templates 
            select="preceding-sibling::*[2]" />
          <xsl:apply-templates 
            select="preceding-sibling::msub[1]/child::*" />
          <xsl:apply-templates />
        </msubsup>
      </xsl:when>
      <xsl:when test="following-sibling::msub">

      </xsl:when>
      <xsl:otherwise>
        <msup>
          <xsl:apply-templates 
            select="preceding-sibling::*[1]" />
          <xsl:apply-templates />
        </msup>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="msub">
    <xsl:choose>
      <xsl:when test="preceding-sibling::msup">
        <msupsub>
          <xsl:apply-templates 
            select="preceding-sibling::*[2]" />
          <xsl:apply-templates 
            select="preceding-sibling::msup[1]/child::*" />
          <xsl:apply-templates />
        </msupsub>
      </xsl:when>
      <xsl:when test="following-sibling::msup">

      </xsl:when>
      <xsl:otherwise>
        <msub>
          <xsl:apply-templates 
            select="preceding-sibling::*[1]" />
          <xsl:apply-templates />
        </msub>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

The output I'm currently getting is (roughly):

<xml>
  <math display="block">
    <mrow>
      <mi>lakshmi</mi>
      <mrow>
        <mrow>
          <mo>(</mo>
          <mrow>
            <mfrac>
              <mn>5</mn>
              <mrow>
                <mn>10</mn>
              </mrow>
            </mfrac>
          </mrow>
          <mo>)</mo>
        </mrow>
      </mrow>
      <msup>
        <mrow>
          <mrow>
            <mo>(</mo>
            <mrow>
              <mfrac>
                <mn>5</mn>
                <mrow>
                  <mn>10</mn>
                </mrow>
              </mfrac>
            </mrow>
            <mo>)</mo>
          </mrow>
        </mrow>
        <mn>2</mn>
      </msup>
    </mrow>
    </math><br><math display="block">
    <mrow>
      <mi>x</mi>
      <msubsup>
        <mi>x</mi>
        <mn>1</mn>
        <mn>2</mn>
      </msubsup>
    </mrow>
  </math>
</xml>

But in the output above, the element immediately preceding the subscript and/or superscript (the nested mrow in the first math expression, the mi in the second), is being copied through both before and inside the subscript (and/or superscript).

We would like to exclude the preceding-sibling of 'msubsup' and 'msup'. That is, we need output like this, instead of what's shown above:

<xml>
  <math display="block">
    <mrow>
      <mi>lakshmi</mi>
      <msup>
        <mrow>
          <mrow>
            <mo>(</mo>
            <mrow>
              <mfrac>
                <mn>5</mn>
                <mrow>
                  <mn>10</mn>
                </mrow>
              </mfrac>
            </mrow>
            <mo>)</mo>
          </mrow>
        </mrow>
        <mn>2</mn>
      </msup>
    </mrow>
    </math><br><math display="block">
    <mrow>
      <msubsup>
        <mi>x</mi>
        <mn>1</mn>
        <mn>2</mn>
      </msubsup>
    </mrow>
  </math>
</xml>

I want to know how to match the preceding-sibling of specific in template. If it is possible to clarify. Thanks for advance.

1
What does 'preceding sibling of specific' mean? - user207421
@EJP I believe OP means "the preceding-sibling of a specific element type". That would make the question make sense. But of course I could be wrong. - C. M. Sperberg-McQueen

1 Answers

0
votes

You have several issues here, I think.

  • I guess that you really only want to merge adjacent pairs of msub and msup, not non-adjacent pairs.

    So the first test in the template for msub should NOT be

    preceding-sibling::msub
    

    but

    preceding-sibling::*[1]/self::msub
    
  • If you intend to be producing MathML (and it sure looks as if you were trying to) from this not-quite-MathML input, you don't want an element named msupsub but one named msubsup.

And now for the issue you actually raise in your question: how do you keep the base of the subscript and/or superscript from appearing twice?

If it were me, I would start by splitting your existing two templates, each with a three-way choice, into six templates, so that each template will be (much) shorter, and I'd add explicit priorities to allow me to keep the match patterns simple:

<template match="msup" priority="10"> ... </>
<template match="msup
   [preceding-sibling::*[1]/self::msub]"
   priority="20"> ... </>
<template match="msup
   [following-sibling::*[1]/self::msub]"
   priority="20"/>

<template match="msub" priority="10"> ... </>
<template match="msub
   [preceding-sibling::*[1]/self::msup]"
   priority="20"> ... </>
<template match="msub
   [following-sibling::*[1]/self::msup]"
   priority="20"/>

This does two things, at least for me. First, it makes it easy to see that this code doesn't know what to do if it sees a sequence of siblings named mi, msub, msup, msub, msup, or mi, msub, msub. If both of those are guaranteed not to happen, and you can trust that guarantee, it doesn't need to know. (Really? you can trust your upstream data producer? feeling lucky?)

Second, it constitutes most but not all of a list of the kinds of elements that should not simply be copied through to the output by the default identity-transform template at the start of the stylesheet. We have superscripts and subscripts that occur alone, or that occur as the first of a pair, or that occur as the second of a pair. What we don't have in the stylesheet is a template for the base elements, elements which occur before a subscript or a superscript.

Add that, and you're done.

<template match="*[not(self::msub or self::msup)]
  [following-sibling::*[1][self::msub or self::msup]"/>