fragment: Iterate over document and use symtable map

Same concept as previous commits: rather than iterating over the symbol
table and scanning the tree for the matching node, iterate over the document
and look up from a symbol map: O(n²) => O(n).

This gives a respectable performance boost to compilation of certain
packages (best improving packages with many classifications or rate blocks).

* src/current/compiler/fragments.xsl (@xmlns:xs, @xmlns:map): New namespace
    declarations.
  (preproc:compile-fragments): Generate `preproc:fragment' nodes and match
    on document rather than symbols.
    [lv:package]: Generate map and tunnel it.
* src/current/compiler/js.xsl (compile)[lv:classify, lv:match]: Use
    symtable-map.
  (compile-class-condition)[lv:rate]: Likewise.
  (compile-cmatch)[lv:rate]: Likewise.
master
Mike Gerwitz 2019-02-20 00:26:32 -05:00
parent dae1990a00
commit 16749a9a45
2 changed files with 103 additions and 109 deletions

View File

@ -19,139 +19,130 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see
<http://www.gnu.org/licenses/>.
This stylesheet should be included by whatever is doing the processing and is
responsible for outputting the generated code in whatever manner is
appropriate (inline JS, a file, etc).
-->
<stylesheet version="2.0"
xmlns="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:lv="http://www.lovullo.com/rater"
xmlns:c="http://www.lovullo.com/calc"
xmlns:preproc="http://www.lovullo.com/rater/preproc">
<template match="lv:package" mode="preproc:compile-fragments">
<template mode="preproc:compile-fragments" priority="9"
match="lv:package">
<variable name="symtable-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge(
for $sym in preproc:symtable/preproc:sym
return map{ string( $sym/@name ) : $sym } )" />
<copy>
<sequence select="@*, *" />
<!-- compile each fragment in the symbol table -->
<preproc:fragments>
<for-each select="preproc:symtable/preproc:sym">
<variable name="result">
<apply-templates select="." mode="preproc:compile-fragments" />
</variable>
<if test="$result != ''">
<preproc:fragment id="{@name}">
<value-of select="$result" />
</preproc:fragment>
</if>
</for-each>
<apply-templates mode="preproc:compile-fragments">
<with-param name="symtable-map" select="$symtable-map"
tunnel="yes" />
</apply-templates>
</preproc:fragments>
</copy>
</template>
<template match="preproc:sym[ @src ]" mode="preproc:compile-fragments" priority="9">
<!-- do not compile external symbols -->
</template>
<template mode="preproc:compile-fragments" priority="5"
match="lv:rate">
<preproc:fragment id="{@yields}">
<apply-templates mode="compile" select="." />
</preproc:fragment>
<template match="preproc:sym" mode="preproc:compile-fragments" priority="1">
<message terminate="yes">
<text>[jsc] fatal: unknown symbol type for `</text>
<value-of select="@name" />
<text>': </text>
<value-of select="@type" />
</message>
<apply-templates mode="preproc:compile-fragments" />
</template>
<template match="preproc:sym[ @type='rate' ]" mode="preproc:compile-fragments" priority="5">
<template mode="preproc:compile-fragments" priority="5"
match="lv:classify">
<preproc:fragment id=":class:{@as}">
<apply-templates select="." mode="compile" />
</preproc:fragment>
<apply-templates mode="preproc:compile-fragments" />
</template>
<template mode="preproc:compile-fragments" priority="5"
match="lv:function">
<preproc:fragment id="{@name}">
<apply-templates select="." mode="compile" />
</preproc:fragment>
<apply-templates mode="preproc:compile-fragments" />
</template>
<template mode="preproc:compile-fragments" priority="7"
match="lv:function/lv:param">
<!-- ignore -->
</template>
<template mode="preproc:compile-fragments" priority="5"
match="lv:param">
<variable name="name" select="@name" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<!-- could be one of two places -->
<apply-templates mode="compile" select="
$pkg/lv:rate[ @yields=$name ]
, $pkg/lv:rate-group/lv:rate[ @yields=$name ]
" />
</template>
<template match="preproc:sym[ @type='gen' ]" mode="preproc:compile-fragments" priority="5">
<!-- compiled by above -->
<preproc:fragment id="{@name}">
<apply-templates select="." mode="compile" />
</preproc:fragment>
</template>
<template match="preproc:sym[ @type='class' ]" mode="preproc:compile-fragments" priority="5">
<!-- name is prefixed with :class: -->
<variable name="as" select="substring-after( @name, ':class:' )" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<apply-templates select="$pkg/lv:classify[ @as=$as ]" mode="compile" />
</template>
<template match="preproc:sym[ @type='cgen' ]" mode="preproc:compile-fragments" priority="5">
<!-- compiled by above -->
<template mode="preproc:compile-fragments" priority="5"
match="lv:typedef">
<preproc:fragment id="{@name}">
<apply-templates mode="compile" select="." />
</preproc:fragment>
<apply-templates mode="preproc:compile-fragments" />
</template>
<template match="preproc:sym[ @type='func' ]" mode="preproc:compile-fragments" priority="5">
<variable name="name" select="@name" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<apply-templates select="$pkg/lv:function[ @name=$name ]" mode="compile" />
<template mode="preproc:compile-fragments" priority="5"
match="lv:const|lv:item">
<preproc:fragment id="{@name}">
<apply-templates mode="compile" select=".">
<with-param name="as-const" select="true()" />
</apply-templates>
</preproc:fragment>
<apply-templates mode="preproc:compile-fragments" />
</template>
<template match="preproc:sym[ @type='param' ]" mode="preproc:compile-fragments" priority="5">
<variable name="name" select="@name" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<apply-templates select="$pkg/lv:param[ @name=$name ]" mode="compile" />
<template mode="preproc:compile-fragments" priority="5"
match="lv:meta/lv:prop">
<preproc:fragment id=":meta:{@name}">
<apply-templates mode="compile" select="." />
</preproc:fragment>
<apply-templates mode="preproc:compile-fragments" />
</template>
<template match="preproc:sym[ @type='type' ]" mode="preproc:compile-fragments" priority="5">
<variable name="name" select="@name" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<!-- a typedef can stand on its own or exist within another typedef -->
<apply-templates mode="compile" select="
$pkg/lv:typedef[ @name=$name ]
, $pkg//lv:typedef//lv:typedef[ @name=$name ]
" />
<template mode="preproc:compile-fragments" priority="7"
match="lv:template">
<!-- don't process template bodies, since they are not yet expanded -->
</template>
<template match="preproc:sym[ @type='const' ]" mode="preproc:compile-fragments" priority="5">
<variable name="name" select="@name" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<apply-templates mode="compile"
select="$pkg/lv:const[ @name=$name ],
$pkg/lv:typedef//lv:item[ @name=$name ]">
<with-param name="as-const" select="true()" />
</apply-templates>
<template mode="preproc:compile-fragments" priority="7"
match="preproc:*">
<!-- we don't compile this stuff -->
</template>
<template match="preproc:sym[ @type='tpl' ]" mode="preproc:compile-fragments" priority="5">
<!-- templates are for the preprocessor only -->
</template>
<template match="preproc:sym[ @type='lparam' ]" mode="preproc:compile-fragments" priority="5">
<!-- they're local and therefore compiled as part of the containing block -->
</template>
<template match="preproc:sym[ @type='meta' ]"
mode="preproc:compile-fragments" priority="5">
<variable name="name" select="substring-after( @name, ':meta:' )" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<variable name="node" as="element( lv:prop )"
select="$pkg/lv:meta/lv:prop[ @name=$name ]" />
<apply-templates mode="compile"
select="$node" />
<template mode="preproc:compile-fragments" priority="1"
match="node()">
<apply-templates mode="preproc:compile-fragments" />
</template>
</stylesheet>

View File

@ -551,6 +551,7 @@
@return generated classification expression
-->
<template match="lv:classify" mode="compile">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<param name="noclass" />
<param name="result-set" />
<param name="ignores" />
@ -569,7 +570,7 @@
<text>'] = (function(){var result,tmp; </text>
</if>
<!-- locate classification criteria (since lv:any and lv:all are split
<!-- locate classification predicates (since lv:any and lv:all are split
into their own classifications, matching on any depth ensures we get
into any preproc:* nodes as well) -->
<variable name="criteria" as="element( lv:match )*"
@ -578,8 +579,8 @@
or not( @on=$ignores/@ref ) ]" />
<variable name="criteria-syms" as="element( preproc:sym )*"
select="root(.)/preproc:symtable/preproc:sym[
@name = $criteria/@on ]" />
select="for $match in $criteria
return $symtable-map( $match/@on )" />
<variable name="dest">
<choose>
@ -674,7 +675,7 @@
</if>
<variable name="sym"
select="root(.)/preproc:symtable/preproc:sym[ @name=$self/@yields ]" />
select="$symtable-map( $self/@yields )" />
<!-- if we are not any type of set, then yield the value of the first
index (note the $criteria check; see above); note that we do not do
@ -733,6 +734,8 @@
@return generated match code
-->
<template match="lv:match" mode="compile" priority="1">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<!-- default to all matches being required -->
<param name="operator" select="'&amp;&amp;'" />
<param name="yields" select="../@yields" />
@ -741,7 +744,7 @@
<variable name="name" select="@on" />
<variable name="sym-on" as="element( preproc:sym )"
select="root(.)/preproc:symtable/preproc:sym[ @name = $name ]" />
select="$symtable-map( $name )" />
<text> tmp = </text>
@ -837,8 +840,8 @@
<choose>
<when test="@value">
<variable name="value" select="@value" />
<variable name="sym"
select="root(.)/preproc:symtable/preproc:sym[ @name=$value ]" />
<variable name="sym" as="element( preproc:sym )?"
select="$symtable-map( $value )" />
<choose>
<!-- value unavailable (TODO: vector/matrix support) -->
@ -1191,6 +1194,8 @@
</template>
<template match="lv:rate" mode="compile-class-condition">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="rate" select="." />
<!-- Generate expression for class list (leave the @no check to the cmatch
@ -1220,11 +1225,8 @@
<variable name="ref" select="@ref" />
<if test="
root(.)/preproc:symtable/preproc:sym[
@name=concat( ':class:', $ref )
]/@preproc:generated='true'
">
<if test="$symtable-map( concat( ':class:', $ref ) )
/@preproc:generated='true'">
<text>gen</text>
</if>
@ -1244,6 +1246,8 @@
<template match="lv:rate" mode="compile-cmatch">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="root" select="root(.)" />
<!-- generate cmatch call that will generate the cmatch set -->
@ -1255,6 +1259,7 @@
<text>args['</text>
<call-template name="compiler:get-class-yield">
<with-param name="symtable-map" select="$symtable-map" />
<with-param name="name" select="@ref" />
<with-param name="search" select="$root" />
</call-template>
@ -1268,6 +1273,7 @@
<text>args['</text>
<call-template name="compiler:get-class-yield">
<with-param name="symtable-map" select="$symtable-map" />
<with-param name="name" select="@ref" />
<with-param name="search" select="$root" />
</call-template>
@ -1278,16 +1284,13 @@
<template name="compiler:get-class-yield">
<param name="symtable-map" as="map(*)" />
<param name="name" />
<param name="search" />
<variable name="yields">
<value-of select="
root(.)/preproc:symtable/preproc:sym[
@name=concat( ':class:', $name )
]/@yields
" />
</variable>
<variable name="yields"
select="$symtable-map(
concat( ':class:', $name ) )/@yields" />
<choose>
<when test="$yields != ''">