Алексей Валиков - Технология XSLT
Декларация типа документа вынесена во внешний файл gemini.dtd.
Листинг 6.14. Файл gemini.dtd<!ELEMENT vertices (vertex*)>
<!ELEMENT vertex EMPTY>
<!ATTLIST vertex
name ID #REQUIRED
connects IDREFS #REQUIRED>
При обработке этого документа функция id будет очень полезна для выбора элементов соединенных вершин. Действительно, функция id, которой будет передано значение атрибута connects (в котором через пробелы перечислены вершины, смежные данной), возвратит множество, состоящее из элементов с перечисленными уникальными идентификаторами. Так, например, функция id('tau upsilon') возвратит множество, состоящее из двух элементов с атрибутами name, равными tau и upsilon соответственно.
Более того, функция id может быть вычислена и от множества узлов. В этом случае ее значением будет объединение множеств, полученных в результате выполнения функции от строкового значения каждого узла переданного множества. Например, id(id('tau upsilon')/@connects) возвратит множество, состоящее из вершин с именами alpha, beta, delta, epsilon, theta, iota и kappa — множество вершин, смежных с вершинами tau и upsilon.
Приведем пример преобразования, которое в каждый элемент vertex добавляет комментарий, в котором перечислены имена вершин, достижимых из текущей, не более чем за два шага.
Для того чтобы найти множество вершин, достижимых за один шаг (иначе говоря, смежных), мы воспользуемся выражением вида id(@connects), для выборки множества вершин, достижимых из текущей за два шага — выражением id(id(@connects)/@connects). Таким образом, множество вершин, достижимых не более чем за два шага, будет вычисляться как
id(@connects)|id(id(@connects)/@connects)
Листинг 6.15. Преобразование gemini.xsl<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output doctype-system="gemini.dtd"/>
<xsl:template match="vertices">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="vertex">
<vertex name="{@name}" connects="{@connects}">
<xsl:comment>
<xsl:for-each select="id(@connects)|id([email protected])/@connects)">
<xsl:text> </xsl:text>
<xsl:value-of select="@name"/>
</xsl:for-each>
</xsl:comment>
</vertex>
</xsl:template>
</xsl:stylesheet>
Листинг 6.16. Выходящий документ<!DOCTYPE vertices SYSTEM "gemini.dtd">
<vertices>
<vertex name="alpha" connects="tau">
<!-- alpha epsilon theta iota tau-->
</vertex>
<vertex name="beta" connects="upsilon">
<!-- beta delta iota kappa upsilon-->
</vertex>
<vertex name="gamma" connects="zeta">
<!-- gamma delta zeta-->
</vertex>
<vertex name="delta" connects="zeta lambda upsilon">
<!-- beta gamma delta zeta iota kappa lambda xi upsilon-->
</vertex>
<vertex name="epsilon" connects="nu mu tau">
<!-- alpha epsilon theta iota mu nu tau-->
</vertex>
<vertex name="zeta" connects="delta gamma">
<!-- gamma delta zeta lambda upsilon-->
</vertex>
<vertex name="theta" connects="tau">
<!-- alpha epsilon theta iota tau-->
</vertex>
<vertex name="iota" connects="tau upsilon">
<!-- alpha beta delta epsilon theta iota kappa tau upsilon-->
</vertex>
<vertex name="kappa" connects="upsilon">
<!-- beta delta iota kappa upsilon-->
</vertex>
<vertex name="lambda" connects="delta xi">
<!-- delta zeta lambda xi upsilon-->
</vertex>
<vertex name="mu" connects="epsilon">
<!-- epsilon mu nu tau-->
</vertex>
<vertex name="nu" connects="epsilon">
<!-- epsilon mu nu tau-->
</vertex>
<vertex name="xi" connects="lambda">
<!-- delta lambda xi-->
</vertex>
<vertex name="tau" connects="alpha theta iota epsilon">
<!-- alpha epsilon theta iota mu nu tau upsilon-->
</vertex>
<vertex name="upsilon" connects="beta iota kappa delta">
<!-- beta delta zeta iota kappa lambda tau upsilon-->
</vertex>
</vertices>
Базовые продукции XPath
В этом разделе мы приведем базовые синтаксические правила языка XPath. Со многими из них мы уже встречались в правилах более высокого уровня, некоторые определены в спецификации для того, чтобы облегчить реализацию лексического разбора XPath-выражений в различных процессорах.
Литералы — это строковые значения, заключенные в одинарные или двойные кавычки. В литералах нельзя использовать символ кавычек, в которые они заключены. Кроме этого, поскольку XPath-выражения чаще всего используются в атрибутах элементов, в них нельзя использовать символы "<" и "&" — они должны заменяться на сущности. Литералам соответствует продукция Literal, определяемая в виде:
[XP29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
XPath использует десятичную систему счисления. Наборы цифр, соответствующие правилу Digits, могут состоять из цифр от 0 до 9:
[XP31] Digits ::= [0-9]+
Число в XPath состоит из последовательности цифр, которые могут быть разделены точкой, причем точка может стоять как в начале числа (.5), так и в конце (5.). Числу соответствует EBNF-правило Number:
[XP30] Number ::= Digits ('.' Digits?)? | '.' Digits
Оператору умножения соответствует символ "*" и синтаксическое правило MultiplyOperator:
[XP34] MultiplyOperator ::= '*'
Именам переменных, которые используются в XPath, предшествует символ "$". Сами же имена должны удовлетворять продукции QName, которую мы рассматривали в разделе "Расширенные имена".
[XP36] VariableReference ::= '$' QName
Продукция NodeType, использованная в тесте узла (см. раздел "Тесты узлов" данной главы, продукция [XP7]), определяет типы узлов, которые можно проверить при тесте — comment (комментарий), text (текстовый узел), processing-instruction (узел инструкции по обработке) и node (узел любого типа). NodeType записывается следующим образом:
[XP38] NodeType ::= 'comment'
| 'text'
| 'processing-instruction'
| 'node'
Другая конструкция, NameTest, которая также используется в тесте узла, проверяет узлы базового типа оси на соответствие определенному имени. EBNF-правило NameTest имеет следующий синтаксис:
[ХР37] NameTest ::= '*' | NCName ':' '*' | QName
Имя функции в XPath может быть любым корректным XML-именем за исключением тех имен, которые используются для обозначения типов узлов. Правило FunctionName имеет вид:
[XP35] FunctionName ::= QName - NodeType
В целях удобочитаемости, в выражениях можно использовать пробельное пространство. Ему соответствует EBNF-правило ExprWhiteSpace:
[XP39] ExprWhitespace ::= S
Разбор XPath-выражений
Хотя синтаксис языка XPath укладывается в тридцать с небольшим синтаксических правил, реализация интерпретатора XPath-выражений может быть довольно непростой задачей. Для того чтобы хоть как-то упростить ее, в XPath определяются так называемые токены выражения (англ. expression token). Токены — это единицы, из которых состоит выражение. Будучи сами очень простыми, они выстраиваются в более сложные конструкции, образуя, в итоге, выражения.
Примером токенов являются операторы, которым соответствуют продукции Operator и OperatorName:
[XP33] OperatorName ::= 'and' | 'or' | 'mod* | 'div'
[XP32] Operator ::= OperatorName
| MultiplyOperator
| '/' | '//' | '|' | '+' | '-'
| '=' | '!=' | '<' | '>' | '<=' | '>='
Продукция самого токена выражения имеет вид:
[ХР28] ExprToken ::= '(' | ')' | '[' | ']'
| ' . ' | ' .. ' | '@' | ' | ':: '
| NameTest
| NodeType
| Operator
| FunctionName
| AxisName
| Literal
| Number
| VariableReference
При разборе XPath-выражения оно сначала разбивается на отдельные токены, а затем из них организуются более сложные структуры. При разбивке выражения на отдельные токены, следует всегда выбирать токен с самым длинным строковым представлением.
Помимо этого, для того чтобы грамматика XPath-выражений была однозначной, в спецификации языка приводятся следующие правила разбора.