Целочисленный оценщик XSLT, как реализовать n-арную сумму и умножение?

Я пытаюсь реализовать небольшой целочисленный оценщик. Как бы то ни было, XML-документы, которые он обрабатывает, имеют выражение и список varDefсо значениями для возможных переменных.

XSLT преобразует этот XML-документ в другой документ с результатом.

Это XML-схема для XML-документов:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    xmlns:ej3="http://procesadores.ejemplo.com/Ej3"
    targetNamespace="http://procesadores.ejemplo.com/Ej3" 
    elementFormDefault="qualified"> 

    <element name="documento">
        <complexType>
            <sequence>
                <element ref="ej3:expr"/>
                <element ref="ej3:varDef" maxOccurs="unbounded" minOccurs="0"/>
            </sequence>
        </complexType>
    </element>

    <element name="expr" abstract="true"/>

    <element name="suma" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="resta" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="mult" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="div" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="mod" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="opuesto" type="ej3:expUnaria" substitutionGroup="ej3:expr"/>
    <element name="abs" type="ej3:expUnaria" substitutionGroup="ej3:expr"/>

    <element name="var" type="ej3:tipoNombreVar" substitutionGroup="ej3:expr"/>
    <element name="cons" type="integer" substitutionGroup="ej3:expr"/>

    <element name="varDef">
        <complexType>
            <simpleContent>
                <extension base="int">
                    <attribute name="nombre" type="ej3:tipoNombreVar"/>
                </extension>
            </simpleContent>
        </complexType>
    </element>

    <complexType name="expUnaria">
        <sequence>
            <element ref="ej3:expr" minOccurs="1" maxOccurs="1"/>
        </sequence>
    </complexType>

    <complexType name="expBinaria">
        <sequence>
            <element ref="ej3:expr" minOccurs="2" maxOccurs="2"/>
        </sequence>
    </complexType>

    <complexType name="expNaria">
        <sequence>
            <element ref="ej3:expr" minOccurs="0" maxOccurs="unbounded"/>
        </sequence>
    </complexType>


    <simpleType name="tipoNombreVar">
        <restriction base="string">
            <pattern value="[a-zA-Z][a-zA-Z0-9]*"/>
        </restriction>
    </simpleType>
</schema>

А это XSLT-документ:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ej3="http://procesadores.ejemplo.com/Ej3"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:strip-space elements="ej3:*"/>

    <xsl:output
        method="xml"
        indent="yes"
        encoding="utf-8"/>

    <xsl:key name="defVariables" match="ej3:varDef" use="@nombre"/>

    <xsl:template match="/ej3:documento"> 
        <cons><xsl:apply-templates select="*[not(local-name()='varDef')]"/></cons>
    </xsl:template>

    <xsl:template match="ej3:suma">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 + $s2"/>
    </xsl:template>

    <xsl:template match="ej3:resta">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 - $s2"/>
    </xsl:template>

    <xsl:template match="ej3:mult">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 * $s2"/>
    </xsl:template>

    <xsl:template match="ej3:div">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>

        <xsl:choose>
            <xsl:when test="$s2 = 0">
                <xsl:value-of select="$s1 div $s2"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="xs:integer($s1 div $s2)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="ej3:mod">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 mod $s2"/>
    </xsl:template>

    <xsl:template match="ej3:opuesto">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:value-of select="- $s1"/>
    </xsl:template>

    <xsl:template match="ej3:abs">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:value-of select="abs($s1)"/>
    </xsl:template>

    <xsl:template match="ej3:var">
        <xsl:value-of select="key('defVariables',.)"/>
    </xsl:template>    

    <xsl:template match="ej3:cons">
        <test><xsl:value-of select="."/></test>
    </xsl:template>

</xsl:stylesheet>

Это все работает, как я и ожидал. Но я хочу сделать suma(сумма) и mult n-арные операторы. То есть что-то вроде этого:

<suma>
    <cons>1</cons>
    <cons>2</cons>
    <cons>3</cons>
</suma>

должны быть в состоянии быть оценены. Чтобы это работало, мне нужно изменить suma xsl:template, но я не совсем уверен, как это сделать. Я пробовал несколько вещей, но тот факт, что я должен как-то оценивать детей, прежде чем добавлять их, затрудняет поиск решения.

Не подскажете, как этого добиться?

Обратите внимание, что я хочу, чтобы операнды sum и mult работали таким образом, поэтому решение, основанное на функции xpath sum(), не будет работать для mult.


person José D.    schedule 30.03.2015    source источник


Ответы (1)


Я хотел бы убедиться, что ваши шаблоны возвращают последовательности xs:integer, например. сдача

<xsl:template match="ej3:cons">
    <test><xsl:value-of select="."/></test>
</xsl:template>

to

<xsl:template match="ej3:cons">
    <xsl:sequence select="xs:integer(.)"/>
</xsl:template>

тогда вы можете использовать

<xsl:template match="suma">
  <xsl:variable name="operands" as="xs:integer+">
    <xsl:apply-templates select="*"/>
  </xsl:variable>
  <xsl:sequence select="sum($operands)"/>
</xsl:template>

Для умножения вы можете использовать функцию (необходимо объявить префикс mf, привязанный к некоторому пространству имен)

<xsl:function name="mf:multiply" as="xs:integer">
  <xsl:param name="operands" as="xs:integer+"/>
  <xsl:sequence select="if (not(exists($operands[2])))
                        then $operands[1]
                        else $operands[1] * mf:multiply($operands[position() gt 1])"/>
</xsl:function>

а затем использовать это в

<xsl:template match="multa">
  <xsl:variable name="operands" as="xs:integer+">
    <xsl:apply-templates select="*"/>
  </xsl:variable>
  <xsl:sequence select="mf:multiply($operands)"/>
</xsl:template>

Вот пример:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:function name="mf:multiply" as="xs:integer">
  <xsl:param name="operands" as="xs:integer+"/>
  <xsl:sequence select="if (not(exists($operands[2])))
                        then $operands[1]
                        else $operands[1] * mf:multiply($operands[position() gt 1])"/>
</xsl:function>

<xsl:template match="expression">
  <result>
    <xsl:apply-templates/>
  </result>
</xsl:template>

<xsl:template match="cons">
    <xsl:sequence select="xs:integer(.)"/>
</xsl:template>

<xsl:template match="suma">
  <xsl:variable name="operands" as="xs:integer+">
    <xsl:apply-templates select="*"/>
  </xsl:variable>
  <xsl:sequence select="sum($operands)"/>
</xsl:template>

<xsl:template match="multa">
  <xsl:variable name="operands" as="xs:integer+">
    <xsl:apply-templates select="*"/>
  </xsl:variable>
  <xsl:sequence select="mf:multiply($operands)"/>
</xsl:template>

</xsl:stylesheet>

При вводе примера

<expression>
  <suma>
    <cons>1</cons>
    <cons>2</cons>
    <cons>3</cons>
    <multa>
      <cons>1</cons>
      <cons>2</cons>
      <cons>3</cons>
    </multa>
  </suma>
</expression>

Я получаю вывод <result>12</result>.

person Martin Honnen    schedule 31.03.2015
comment
Как я могу использовать этот подход для реализации n-арного умножения? - person José D.; 31.03.2015
comment
@Trollkemada, я отредактировал ответ с некоторым предложением, он должен помочь реализовать его, хотя он не проверен и введен только непосредственно в редакторе SO. - person Martin Honnen; 31.03.2015
comment
И mult, и sum объединяют результат в виде строки, это проблема, с которой я сталкиваюсь, когда пытаюсь. Я тестировал в некоторых онлайн-тестерах xslt с тем же результатом. - person José D.; 31.03.2015
comment
@Trollkemada, я внес некоторые изменения и добавил короткий, но полный пример, который должен выполнить эту работу. Если у вас все еще есть проблемы, отредактируйте свой вопрос и предоставьте входные XML-документы некоторых примеров выражений. - person Martin Honnen; 31.03.2015
comment
И обратите внимание, что использование xsl:sequence вместо xsl:value-of необходимо. - person Martin Honnen; 31.03.2015