Begin to use symtable-map for template/macro passes
I wanted to get this committed before I continue because it required changes to the `expand-sequence` system---tunneling params cannot pass through functions, so this accepts a context to pass back to the calling system via the `eseq:expand-node` override. Otherwise, the key change here is the elimination of a preproc:symtable XPath within a `template/@match`, which was a huge performance problem with the preceding commits. This improves build times modestly, but there are more changes that this sets up for, so I'll keep going. DEV-15095main
parent
50e31f4616
commit
a8ef1b4fd1
|
@ -46,6 +46,25 @@
|
||||||
|
|
||||||
<include href="../../compiler/fragments.xsl" />
|
<include href="../../compiler/fragments.xsl" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Generate a symbol table map from the provided package.
|
||||||
|
|
||||||
|
This uses XSLT maps, which are backed by a hash table in Saxon. Note
|
||||||
|
that, while this does provide constant-time lookup (vs. a linear scan of
|
||||||
|
the entire symbol table), there is a cost associated with its
|
||||||
|
construction, so you must be sure that the savings afforded by the lookups
|
||||||
|
makes up for that cost.
|
||||||
|
-->
|
||||||
|
<function name="preproc:mk-symtable-map"
|
||||||
|
as="map( xs:string, element( preproc:sym ) )">
|
||||||
|
<!-- in practice this is lv:package or lv:rater -->
|
||||||
|
<param name="pkg" as="element()" />
|
||||||
|
|
||||||
|
<sequence select="map:merge(
|
||||||
|
for $sym in $pkg/preproc:symtable/preproc:sym
|
||||||
|
return map{ string( $sym/@name ) : $sym } )" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
|
||||||
<!-- begin preprocessing from an arbitrary node -->
|
<!-- begin preprocessing from an arbitrary node -->
|
||||||
<template name="preproc:pkg-compile" as="element( lv:package )"
|
<template name="preproc:pkg-compile" as="element( lv:package )"
|
||||||
|
@ -63,7 +82,10 @@
|
||||||
|
|
||||||
<!-- macro expansion -->
|
<!-- macro expansion -->
|
||||||
<variable name="stage1" as="element()+">
|
<variable name="stage1" as="element()+">
|
||||||
<apply-templates select="." mode="preproc:macropass" />
|
<apply-templates select="." mode="preproc:macropass">
|
||||||
|
<with-param name="symtable-map" tunnel="yes"
|
||||||
|
select="preproc:mk-symtable-map( . )" />
|
||||||
|
</apply-templates>
|
||||||
|
|
||||||
<message>
|
<message>
|
||||||
<text>[preproc] *macro pass complete; expanding...</text>
|
<text>[preproc] *macro pass complete; expanding...</text>
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
<stylesheet version="2.0"
|
<stylesheet version="2.0"
|
||||||
xmlns="http://www.w3.org/1999/XSL/Transform"
|
xmlns="http://www.w3.org/1999/XSL/Transform"
|
||||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
|
||||||
xmlns:preproc="http://www.lovullo.com/rater/preproc"
|
xmlns:preproc="http://www.lovullo.com/rater/preproc"
|
||||||
xmlns:lv="http://www.lovullo.com/rater"
|
xmlns:lv="http://www.lovullo.com/rater"
|
||||||
xmlns:t="http://www.lovullo.com/rater/apply-template"
|
xmlns:t="http://www.lovullo.com/rater/apply-template"
|
||||||
|
@ -105,6 +106,8 @@
|
||||||
TODO: This causes an extra pass; we'd like to avoid having to do that.
|
TODO: This causes an extra pass; we'd like to avoid having to do that.
|
||||||
-->
|
-->
|
||||||
<template match="t:*" mode="preproc:macros" priority="5">
|
<template match="t:*" mode="preproc:macros" priority="5">
|
||||||
|
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||||
|
|
||||||
<!-- TODO: debug flag
|
<!-- TODO: debug flag
|
||||||
<message>
|
<message>
|
||||||
<text>[preproc] expanding template shorthand for </text>
|
<text>[preproc] expanding template shorthand for </text>
|
||||||
|
@ -137,7 +140,7 @@
|
||||||
|
|
||||||
<!-- XXX: a large chunk of this is duplicate code; factor out -->
|
<!-- XXX: a large chunk of this is duplicate code; factor out -->
|
||||||
<variable name="tpl" as="element( lv:template )?"
|
<variable name="tpl" as="element( lv:template )?"
|
||||||
select="preproc:locate-template( $name, root( . ) )" />
|
select="preproc:locate-template( $symtable-map, $name, root( . ) )" />
|
||||||
|
|
||||||
<variable name="src-root" as="element( lv:package )"
|
<variable name="src-root" as="element( lv:package )"
|
||||||
select="if ( root(.)/lv:package ) then
|
select="if ( root(.)/lv:package ) then
|
||||||
|
@ -161,12 +164,12 @@
|
||||||
|
|
||||||
<function name="preproc:locate-template"
|
<function name="preproc:locate-template"
|
||||||
as="element( lv:template )?">
|
as="element( lv:template )?">
|
||||||
|
<param name="symtable-map" as="map(*)" />
|
||||||
<param name="name" as="xs:string" />
|
<param name="name" as="xs:string" />
|
||||||
<param name="root" as="element( lv:package )" />
|
<param name="root" as="element( lv:package )" />
|
||||||
|
|
||||||
<variable name="sym" as="element( preproc:sym )?"
|
<variable name="sym" as="element( preproc:sym )?"
|
||||||
select="$root/preproc:symtable/preproc:sym[
|
select="$symtable-map( $name )" />
|
||||||
@name = $name ]" />
|
|
||||||
|
|
||||||
<variable name="package" as="element( lv:package )?"
|
<variable name="package" as="element( lv:package )?"
|
||||||
select="if ( $sym/@src ) then
|
select="if ( $sym/@src ) then
|
||||||
|
@ -202,29 +205,23 @@
|
||||||
Note that if the attribute shorthand is used for params, one extra expansion
|
Note that if the attribute shorthand is used for params, one extra expansion
|
||||||
will have to occur, which is an additional performance hit.
|
will have to occur, which is an additional performance hit.
|
||||||
-->
|
-->
|
||||||
<template match="lv:apply-template[
|
<template mode="preproc:macros" priority="6"
|
||||||
@name=root(.)/preproc:symtable/preproc:sym/@name ]
|
match="lv:apply-template">
|
||||||
|lv:apply-template[ @name=root(.)//lv:template/@name ]"
|
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||||
mode="preproc:macros" priority="6">
|
|
||||||
|
|
||||||
<variable name="name" select="@name" />
|
<variable name="name" select="@name" />
|
||||||
<variable name="attrparams" select="@*[ not( local-name() = 'name' ) ]" />
|
<variable name="attrparams" select="@*[ not( local-name() = 'name' ) ]" />
|
||||||
|
|
||||||
<!-- used for type checking -->
|
<!-- used for type checking -->
|
||||||
<variable name="root" as="element( lv:package )"
|
<variable name="src-root" as="element( lv:package )"
|
||||||
select="if ( root( . ) instance of document-node() ) then
|
select="if ( root( . ) instance of document-node() ) then
|
||||||
root( . )/lv:package
|
root( . )/lv:package
|
||||||
else
|
else
|
||||||
root( . )" />
|
root( . )" />
|
||||||
|
|
||||||
<variable name="src-root" as="element( lv:package )"
|
|
||||||
select="if ( $root/lv:package ) then
|
|
||||||
$root/lv:package
|
|
||||||
else
|
|
||||||
$root" />
|
|
||||||
|
|
||||||
<variable name="tpl" as="element( lv:template )?"
|
<variable name="tpl" as="element( lv:template )?"
|
||||||
select="preproc:locate-template( $name, $root )" />
|
select="preproc:locate-template(
|
||||||
|
$symtable-map, $name, $src-root )" />
|
||||||
|
|
||||||
<choose>
|
<choose>
|
||||||
<when test="exists( $tpl ) and $attrparams">
|
<when test="exists( $tpl ) and $attrparams">
|
||||||
|
@ -254,7 +251,16 @@
|
||||||
</when>
|
</when>
|
||||||
|
|
||||||
<otherwise>
|
<otherwise>
|
||||||
<preproc:error>Undefined template <value-of select="$name" /></preproc:error>
|
<!-- keep this application around for later -->
|
||||||
|
<sequence select="." />
|
||||||
|
|
||||||
|
<!-- nothing we can do yet -->
|
||||||
|
<message>
|
||||||
|
<text>[preproc] deferring application of unknown template </text>
|
||||||
|
<value-of select="@name" />
|
||||||
|
</message>
|
||||||
|
|
||||||
|
<preproc:repass need-sym="{@name}" />
|
||||||
</otherwise>
|
</otherwise>
|
||||||
</choose>
|
</choose>
|
||||||
</template>
|
</template>
|
||||||
|
@ -546,24 +552,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This block is used when we attempt to apply a template that has not been
|
|
||||||
defined
|
|
||||||
-->
|
|
||||||
<template match="lv:apply-template" mode="preproc:macros" priority="5">
|
|
||||||
<!-- keep this application around for later -->
|
|
||||||
<sequence select="." />
|
|
||||||
|
|
||||||
<!-- nothing we can do yet -->
|
|
||||||
<message>
|
|
||||||
<text>[preproc] deferring application of unknown template </text>
|
|
||||||
<value-of select="@name" />
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<preproc:repass need-sym="{@name}" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<template match="lv:template/lv:param" mode="preproc:apply-template" priority="5">
|
<template match="lv:template/lv:param" mode="preproc:apply-template" priority="5">
|
||||||
<param name="first-child" select="false()" />
|
<param name="first-child" select="false()" />
|
||||||
|
|
||||||
|
@ -1512,7 +1500,11 @@
|
||||||
-->
|
-->
|
||||||
<template mode="preproc:macros" priority="5"
|
<template mode="preproc:macros" priority="5"
|
||||||
match="lv:expand-sequence">
|
match="lv:expand-sequence">
|
||||||
<sequence select="eseq:expand-step( . )" />
|
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||||
|
|
||||||
|
<!-- tunnels do not continue through functions, so store the symtable-map
|
||||||
|
as a context that will be passed back to us by eseq:expand-node -->
|
||||||
|
<sequence select="eseq:expand-step( $symtable-map, . )" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1570,10 +1562,16 @@
|
||||||
|
|
||||||
|
|
||||||
<function name="eseq:expand-node" as="node()*">
|
<function name="eseq:expand-node" as="node()*">
|
||||||
|
<!-- this is the symtable-map, as passed in via eseq:expand-step -->
|
||||||
|
<param name="context" as="map(*)" />
|
||||||
|
|
||||||
<param name="node" as="node()" />
|
<param name="node" as="node()" />
|
||||||
|
|
||||||
<apply-templates mode="preproc:macros"
|
<apply-templates mode="preproc:macros"
|
||||||
select="$node" />
|
select="$node">
|
||||||
|
<with-param name="symtable-map" tunnel="yes"
|
||||||
|
select="$context" />
|
||||||
|
</apply-templates>
|
||||||
</function>
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
generally unnecessary.
|
generally unnecessary.
|
||||||
|
|
||||||
Expansion sequences are initiated by invoking
|
Expansion sequences are initiated by invoking
|
||||||
@ttref{eseq:expand-step#1} on any arbitrary node containing any number
|
@ttref{eseq:expand-step#2} on any arbitrary node containing any number
|
||||||
of children to be expanded in order. Each call will proceed one
|
of children to be expanded in order. Each call will proceed one
|
||||||
step (detailed herein), eventually resulting in each node expanded
|
step (detailed herein), eventually resulting in each node expanded
|
||||||
and the expansion sequence node eliminated.
|
and the expansion sequence node eliminated.
|
||||||
|
@ -58,6 +58,7 @@
|
||||||
return an empty sequence in such a case.
|
return an empty sequence in such a case.
|
||||||
-->
|
-->
|
||||||
<function name="eseq:expand-step" as="node()*">
|
<function name="eseq:expand-step" as="node()*">
|
||||||
|
<param name="context" />
|
||||||
<param name="eseq" as="node()*" />
|
<param name="eseq" as="node()*" />
|
||||||
|
|
||||||
<variable name="count" as="xs:integer"
|
<variable name="count" as="xs:integer"
|
||||||
|
@ -73,7 +74,9 @@
|
||||||
<sequence select="$eseq[ position() lt $count ]" />
|
<sequence select="$eseq[ position() lt $count ]" />
|
||||||
|
|
||||||
<apply-templates mode="_eseq:expand"
|
<apply-templates mode="_eseq:expand"
|
||||||
select="$target" />
|
select="$target">
|
||||||
|
<with-param name="context" select="$context" />
|
||||||
|
</apply-templates>
|
||||||
</function>
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
@ -187,9 +190,11 @@
|
||||||
-->
|
-->
|
||||||
<template mode="_eseq:expand" as="node()+"
|
<template mode="_eseq:expand" as="node()+"
|
||||||
match="*[ node() ]">
|
match="*[ node() ]">
|
||||||
|
<param name="context" />
|
||||||
|
|
||||||
<choose>
|
<choose>
|
||||||
<when test="node()[1][ eseq:is-expandable( . ) ]">
|
<when test="node()[1][ eseq:is-expandable( . ) ]">
|
||||||
<sequence select="_eseq:expand-head( . )" />
|
<sequence select="_eseq:expand-head( $context, . )" />
|
||||||
</when>
|
</when>
|
||||||
|
|
||||||
<otherwise>
|
<otherwise>
|
||||||
|
@ -250,7 +255,7 @@
|
||||||
after all expansions are complete and the expansion sequence node
|
after all expansions are complete and the expansion sequence node
|
||||||
itself is eliminated (per the final match above), then the node that
|
itself is eliminated (per the final match above), then the node that
|
||||||
was last expanded and hoisted will be considered to be the expansion
|
was last expanded and hoisted will be considered to be the expansion
|
||||||
sequence by @ttref{eseq:expand-step#1}. This is true, but should not
|
sequence by @ttref{eseq:expand-step#2}. This is true, but should not
|
||||||
be a problem in practice: hoisting is intended to place nodes into
|
be a problem in practice: hoisting is intended to place nodes into
|
||||||
context for the caller; it is expected that the caller will
|
context for the caller; it is expected that the caller will
|
||||||
recognize when to invoke sequence expansion (likely on a pre-defined
|
recognize when to invoke sequence expansion (likely on a pre-defined
|
||||||
|
@ -286,9 +291,10 @@
|
||||||
function.
|
function.
|
||||||
|
|
||||||
Actual expansion is left to
|
Actual expansion is left to
|
||||||
@ref{eseq:expand-node#1,,@code{eseq:expand-node#1}}.
|
@ref{eseq:expand-node#2,,@code{eseq:expand-node#2}}.
|
||||||
-->
|
-->
|
||||||
<function name="_eseq:expand-head" as="element()">
|
<function name="_eseq:expand-head" as="element()">
|
||||||
|
<param name="context" />
|
||||||
<param name="eseq" as="element()" />
|
<param name="eseq" as="element()" />
|
||||||
|
|
||||||
<variable name="head" as="node()"
|
<variable name="head" as="node()"
|
||||||
|
@ -300,7 +306,7 @@
|
||||||
<for-each select="$eseq">
|
<for-each select="$eseq">
|
||||||
<copy>
|
<copy>
|
||||||
<sequence select="$eseq/@*,
|
<sequence select="$eseq/@*,
|
||||||
eseq:expand-node( $head ),
|
eseq:expand-node( $context, $head ),
|
||||||
$head/following-sibling::node()" />
|
$head/following-sibling::node()" />
|
||||||
</copy>
|
</copy>
|
||||||
</for-each>
|
</for-each>
|
||||||
|
@ -314,7 +320,7 @@
|
||||||
|
|
||||||
Its default behavior is an important consideration: what if
|
Its default behavior is an important consideration: what if
|
||||||
@ttref{eseq:is-expandable#1} is overridden but the implementation
|
@ttref{eseq:is-expandable#1} is overridden but the implementation
|
||||||
forgets to override @ttref{eseq:expand-node#1}? If the default
|
forgets to override @ttref{eseq:expand-node#2}? If the default
|
||||||
behavior were to simply echo back the node, it seems likely that we
|
behavior were to simply echo back the node, it seems likely that we
|
||||||
would never finish processing, since the very node that matched the
|
would never finish processing, since the very node that matched the
|
||||||
predicate to begin with would remain unchanged.
|
predicate to begin with would remain unchanged.
|
||||||
|
@ -341,6 +347,7 @@
|
||||||
prevent infinite recursion/iteration.
|
prevent infinite recursion/iteration.
|
||||||
-->
|
-->
|
||||||
<function name="eseq:expand-node" as="node()*">
|
<function name="eseq:expand-node" as="node()*">
|
||||||
|
<param name="context" />
|
||||||
<param name="node" as="node()" />
|
<param name="node" as="node()" />
|
||||||
|
|
||||||
<eseq:expand-error>
|
<eseq:expand-error>
|
||||||
|
@ -352,7 +359,7 @@
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
The return type of @ttref{eseq:expand-node#1} produces an interesting
|
The return type of @ttref{eseq:expand-node#2} produces an interesting
|
||||||
concept. Consider what may happen after an expansion:
|
concept. Consider what may happen after an expansion:
|
||||||
|
|
||||||
@enumerate
|
@enumerate
|
||||||
|
@ -383,7 +390,7 @@
|
||||||
continue processing; the expansion may have yielded additional
|
continue processing; the expansion may have yielded additional
|
||||||
symbols that must be added to the symbol table, for example. The
|
symbols that must be added to the symbol table, for example. The
|
||||||
process will be continued on the next call to
|
process will be continued on the next call to
|
||||||
@ttref{eseq:expand-step#1}.
|
@ttref{eseq:expand-step#2}.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue