Another round of xmlo compilation performance enhancements

This reduces overall build times for one of our systems by ~50% by
addressing a lot of the low-hanging fruit for compilation of object
files.  There is much more work to be done, and the addition of maps added a
little bit of a mess that will be abstracted in future commits once I'm done
surveying the possible improvements that can be done.
master
Mike Gerwitz 2019-02-20 09:42:19 -05:00
commit c5a99e594d
6 changed files with 286 additions and 298 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 != ''">

View File

@ -26,6 +26,7 @@
-->
<stylesheet version="2.0"
xmlns="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:lv="http://www.lovullo.com/rater"
xmlns:c="http://www.lovullo.com/calc"
@ -57,12 +58,19 @@
<variable name="l:orig-root" as="document-node( element( lv:package ) )"
select="/" />
<variable name="l:orig-package" as="element( lv:package )"
select="$l:orig-root/lv:package" />
<variable name="l:process-empty" as="element( preproc:sym )*"
select="()" />
<variable name="l:stack-empty" as="element( preproc:sym )*"
select="()" />
<variable name="l:root-symtable-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge( for $sym in $l:orig-package/preproc:symtable/preproc:sym
return map{ string( $sym/@name ) : $sym } )" />
<template match="*" mode="l:link" priority="1">
<call-template name="log:error">
@ -106,7 +114,7 @@
<call-template name="log:debug">
<with-param name="name" select="'link'" />
<with-param name="msg">
<text>building dependency tree...</text>
<text>building dependency graph...</text>
</with-param>
</call-template>
@ -124,7 +132,7 @@
<call-template name="log:debug">
<with-param name="name" select="'link'" />
<with-param name="msg">
<text>resolving dependency tree...</text>
<text>resolving dependency graph...</text>
</with-param>
</call-template>
@ -134,7 +142,7 @@
<copy>
<copy-of select="@*" />
<!-- copy the dependency tree -->
<!-- copy the dependency graph -->
<copy-of select="$deps" />
<!-- if map data was provided, generate the map -->
@ -203,7 +211,7 @@
</variable>
<!-- start at the top of the table and begin processing each symbol
individually, generating a dependency tree as we go -->
individually, generating a dependency graph as we go -->
<variable name="result" as="element()+">
<call-template name="l:depgen-sym">
<with-param name="pending" select="$yields" />
@ -308,12 +316,8 @@
<!-- there is no reason (in the current implementation) that this
should _not_ have already been resolved in the package being
linked -->
<variable name="pkg" as="element( lv:package )" select="
$l:orig-root/lv:package" />
<variable name="resolv" as="element( preproc:sym )?"
select="$pkg/preproc:symtable/preproc:sym[
@name=$name ]" />
select="$l:root-symtable-map( $name )" />
<choose>
<!-- if this symbol is not external, then we have found it -->
@ -449,7 +453,7 @@
select="." />
<!-- perform circular dependency check and blow up if found (we cannot choose
a proper linking order without a correct dependency tree); the only
a proper linking order without a correct dependency graph); the only
exception is if the circular dependency is a function, since that simply
implies recursion, which we can handle just fine -->
<variable name="circ" as="element( preproc:sym )*"
@ -670,7 +674,7 @@
if ( @src and not( @src='' ) ) then
document( concat( @src, '.xmlo' ), $l:orig-root )/lv:*
else
$l:orig-root/lv:package
$l:orig-package
" />
<variable name="name" as="xs:string" select="@name" />
@ -1048,7 +1052,7 @@
if ( @src and not( @src='' ) ) then
document( concat( @src, '.xmlo' ), $l:orig-root )/lv:*
else
$l:orig-root/lv:package
$l:orig-package
" />
<variable name="name" select="@name" />

View File

@ -33,6 +33,8 @@
-->
<stylesheet version="1.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:ext="http://www.lovullo.com/ext"
xmlns:c="http://www.lovullo.com/calc"
@ -68,6 +70,11 @@
<template match="lv:package" mode="lvv:validate" priority="9">
<param name="symbol-map" />
<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 } )" />
<choose>
<when test="$prohibit-validation = 'true'">
<message>
@ -89,7 +96,10 @@
</message>
<!-- validate -->
<apply-templates mode="lvv:validate" />
<apply-templates mode="lvv:validate">
<with-param name="symtable-map" select="$symtable-map"
tunnel="yes" />
</apply-templates>
</otherwise>
</choose>
</template>
@ -119,6 +129,7 @@
</template>
<!-- XXX: Nothing is calling this! -->
<template name="lvv:symbol-chk">
<param name="root" />
<param name="symbol-map" />
@ -171,14 +182,13 @@
<template match="c:apply[@name]" mode="lvv:validate" priority="5">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="name" select="@name" />
<variable name="self" select="." />
<variable name="fsym" select="
root(.)/preproc:symtable/preproc:sym[
@type='func'
and @name=$name
]
" />
<variable name="fsym" as="element( preproc:sym )"
select="$symtable-map( $name )" />
<!-- ensure that a function is being applied -->
<if test="not( $fsym )">
@ -230,7 +240,12 @@
Validate that match @on's exist
-->
<template match="lv:classify[ @as ]//lv:match" mode="lvv:validate" priority="9">
<if test="not( @on=root(.)/preproc:symtable/preproc:sym[ @type ]/@name )">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="sym" as="element( preproc:sym )?"
select="$symtable-map( @on )" />
<if test="not( $sym )">
<call-template name="lvv:error">
<with-param name="desc" select="'Unknown match @on'" />
<with-param name="refnode" select="." />
@ -256,14 +271,13 @@
Validate that non-numeric value matches actually exist and are constants
-->
<template match="lv:match[@value]" mode="lvv:validate-match" priority="5">
<if test="
not( number( @value ) = @value )
and not(
@value=root(.)/preproc:symtable/preproc:sym[
@type='const'
]/@name
)
">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="sym" as="element( preproc:sym )?"
select="$symtable-map( @value )" />
<if test="not( number( @value ) = @value )
and not( $sym )">
<call-template name="lvv:error">
<with-param name="desc" select="'Unknown match value'" />
@ -338,6 +352,8 @@
</template>
<template match="c:*[@name or @of]" mode="lvv:validate" priority="2">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="name">
<choose>
<when test="@of">
@ -350,19 +366,6 @@
</choose>
</variable>
<!-- XXX: have to maintain this list! -->
<variable name="nodes" select="
root(.)//lv:*[
@name=$name
or @yields=$name
or @as=$name
]
, root(.)//c:*[
@generates=$name
]
, root(.)//c:values/c:value[ @name=$name ]
" />
<!-- locate function params/let vars -->
<variable name="fname" select="
ancestor::lv:function[
@ -385,16 +388,11 @@
<!-- if this name references a function parameter, then it takes
precedence (note that this consequently means that it masks any other
names that may be globally defined) -->
<variable name="sym" select="
if ( $fname ) then
root(.)/preproc:symtable/preproc:sym[
@name=concat( ':', $fname, ':', $name )
]
else
root(.)/preproc:symtable/preproc:sym[
@name=$name
]
" />
<variable name="sym" as="element( preproc:sym )?"
select="if ( $fname ) then
$symtable-map( concat( ':', $fname, ':', $name ) )
else
$symtable-map( $name )" />
<variable name="type" select="$sym/@dtype" />
@ -581,15 +579,12 @@
<template match="c:apply/c:arg[@name]" mode="lvv:validate" priority="5">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<!-- merely validate its existence -->
<variable name="fname" select="parent::c:apply/@name" />
<if test="not(
concat( ':', $fname, ':', @name ) = root(.)/preproc:symtable/preproc:sym[
@type='lparam'
and @parent=$fname
]/@name
)">
<if test="not( $symtable-map( concat( ':', $fname, ':', @name ) ) )">
<call-template name="lvv:error">
<with-param name="desc" select="'Unknown argument'" />
<with-param name="refnode" select="." />
@ -641,20 +636,21 @@
Checks for use of undefined classifications
-->
<template mode="lvv:validate" priority="2"
match="lv:rate/lv:class[
not( concat( ':class:', @ref ) = root(.)/preproc:symtable/preproc:sym[ @type='class' ]/@name )
]">
match="lv:rate/lv:class">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<call-template name="lvv:error">
<with-param name="desc" select="'Unknown classification'" />
<with-param name="refnode" select="." />
<with-param name="content">
<text>unknown classification '</text>
<if test="not( $symtable-map( concat( ':class:', @ref ) ) )">
<call-template name="lvv:error">
<with-param name="desc" select="'Unknown classification'" />
<with-param name="refnode" select="." />
<with-param name="content">
<text>unknown classification '</text>
<value-of select="@ref" />
<text>' referenced by </text>
<value-of select="ancestor::lv:rate/@yields" />
</with-param>
</call-template>
<text>' referenced by </text>
<value-of select="ancestor::lv:rate/@yields" />
</with-param>
</call-template>
</if>
</template>

View File

@ -25,6 +25,8 @@
-->
<stylesheet version="1.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:preproc="http://www.lovullo.com/rater/preproc"
xmlns:lv="http://www.lovullo.com/rater"
xmlns:t="http://www.lovullo.com/rater/apply-template"
@ -565,6 +567,16 @@
<param name="orig-root" as="element()" />
<param name="rpcount" select="0" />
<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 } )" />
<variable name="symdep-map" as="map( xs:string, element( preproc:sym-dep ) )"
select="map:merge(
for $sym-dep in preproc:sym-deps/preproc:sym-dep
return map{ string( $sym-dep/@name ) : $sym-dep } )" />
<!-- arbitrary; intended to prevent infinite recursion -->
<!-- TODO: same method as for templates; ensure changes, but do not create
arbitrary limit -->
@ -588,6 +600,8 @@
<apply-templates mode="preproc:resolv-syms">
<with-param name="orig-root" select="$orig-root" />
<with-param name="symtable-map" select="$symtable-map" tunnel="yes" />
<with-param name="symdep-map" select="$symdep-map" tunnel="yes" />
</apply-templates>
</copy>
</variable>
@ -637,67 +651,39 @@
</template>
<!--
Calculate symbol dimensions by taking the highest dimension of its
dependencies
If all dependencies are not yet resolved, then schedule a repass.
-->
<template match="preproc:sym[ not( @src ) and @dim='?' ]" mode="preproc:resolv-syms" priority="5">
<param name="orig-root" as="element()" />
<param name="symtable-map" as="map(*)" tunnel="yes" />
<param name="symdep-map" as="map(*)" tunnel="yes" />
<variable name="name" select="@name" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<variable name="deps" as="element( preproc:sym-dep )*" select="
$pkg/preproc:sym-deps/preproc:sym-dep[ @name=$name ]
" />
<variable name="deps" as="element( preproc:sym-dep )?"
select="$symdep-map( $name )" />
<variable name="depsyms-unresolv" as="element( preproc:sym )*" select="
$pkg/preproc:symtable/preproc:sym[
@name=$deps/preproc:sym-ref/@name
]
" />
<!-- TODO: make this fatal -->
<if test="empty( $deps ) and not( @no-deps = 'true' )">
<message select="concat( 'internal: failed to located dependencies for `',
$name, '''' )" />
</if>
<variable name="depsyms-resolv">
<for-each select="$depsyms-unresolv">
<choose>
<when test="not( @src )">
<sequence select="." />
</when>
<!-- look up complete symbol -->
<otherwise>
<variable name="name" select="@name" />
<variable name="sym" select="
document( concat( @src, '.xmlo' ), $orig-root )
/lv:package/preproc:symtable/preproc:sym[
@name=$name
]
" />
<if test="not( $sym )">
<message terminate="yes">
<text>[preproc] !!! failed to look up symbol `</text>
<value-of select="$name" />
<text>'</text>
</message>
</if>
<sequence select="$sym" />
</otherwise>
</choose>
</for-each>
</variable>
<variable name="depsyms" select="$depsyms-resolv/preproc:sym" />
<variable name="depsyms" as="element( preproc:sym )*"
select="for $ref in $deps/preproc:sym-ref
return $symtable-map( $ref/@name )" />
<choose>
<!-- unresolved dependency dimensions; defer until next pass -->
<when test="
$depsyms/@dim = '?'
">
<message>
<text>[preproc] deferring `</text>
<value-of select="$name" />
<text>' dimensions with unresolved dependencies</text>
</message>
<!-- schedule repass :x -->
<sequence select="." />
<preproc:repass src="preproc:sym resolv-syms"
@ -706,38 +692,12 @@
<!-- all dependencies are resolved; calculate dimensions -->
<otherwise>
<!-- sort dependencies so that the largest dimension is at the top -->
<variable name="maxset">
<for-each select="$depsyms">
<sort select="@dim" data-type="number" order="descending" />
<sequence select="." />
</for-each>
</variable>
<variable name="max" as="xs:double"
select="if ( empty( $depsyms ) ) then
0
else
max( $depsyms/@dim )" />
<variable name="max">
<choose>
<when test="count( $deps/preproc:sym-ref ) = 0">
<!-- no dependencies, unknown size, so it's a scalar -->
<text>0</text>
</when>
<otherwise>
<!-- largest value -->
<value-of select="$maxset/preproc:sym[1]/@dim" />
</otherwise>
</choose>
</variable>
<!-- failure? -->
<if test="not( $max ) or $max = ''">
<message terminate="yes">
<text>[preproc] !!! failed to determine dimensions of `</text>
<value-of select="$name" />
<text>'</text>
</message>
</if>
<!-- copy, substituting calculated dimensions -->
<copy>
<sequence select="@*" />
<attribute name="dim" select="$max" />

View File

@ -62,6 +62,7 @@
<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:symtable="http://www.lovullo.com/tame/symtable"
xmlns:preproc="http://www.lovullo.com/rater/preproc"
xmlns:lv="http://www.lovullo.com/rater"
@ -247,12 +248,12 @@
</variable>
<!-- remove duplicates (if any) -->
<sequence select="
$extresults/preproc:sym[
not( @name=preceding-sibling::preproc:sym/@name )
]
, $extresults//preproc:error
" />
<for-each-group select="$extresults/preproc:sym"
group-by="@name">
<sequence select="current-group()[ 1 ]" />
</for-each-group>
<sequence select="$extresults//preproc:error" />
<!-- process symbols (except imported externs) -->
<variable name="newresult" as="element( preproc:syms )">
@ -263,6 +264,24 @@
</call-template>
</variable>
<!-- contains duplicates -->
<variable name="new-seq-map" as="map( xs:string, element( preproc:sym )+ )"
select="map:merge(
for $sym in $newresult/preproc:sym
return map{ string( $sym/@name ) : $sym },
map{ 'duplicates' : 'combine' } )" />
<variable name="new-typed-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge(
for $sym in $newresult/preproc:sym[ @type ]
return map{ string( $sym/@name ) : $sym } )" />
<variable name="nonlocals-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge(
for $sym in $newresult/preproc:sym[ not( @local = 'true' ) ]
return map{ string( $sym/@name ) : $sym } )" />
<!-- TODO: revisit this logic -->
<variable name="dedup" as="element( preproc:sym )*"
select="$newresult/preproc:sym[
not(
@ -270,29 +289,27 @@
@pollute='true'
and not( @type )
and (
@name=preceding-sibling::preproc:sym/@name
or @name=$newresult/preproc:sym[ @type ]/@name
(
( count( $new-seq-map( @name ) ) gt 1 )
and @name=preceding-sibling::preproc:sym/@name
)
or exists( $new-typed-map( @name ) )
)
)
or (
@local = 'true'
and @name = following-sibling::preproc:sym[
not( @local = 'true' )
]/@name
and exists( $nonlocals-map( @name ) )
)
)
]" />
<apply-templates mode="preproc:symtable-complete"
select="$dedup">
<with-param name="syms" select="$dedup" />
</apply-templates>
</preproc:symtable>
<message>
<text>[preproc/symtable] done.</text>
</message>
<message select="'[preproc/symtable] done.'" />
<!-- copy all of the original elements after the symbol table; we're not
outputting them as we go, so we need to make sure that we don't get
@ -433,42 +450,59 @@
</function>
<!-- TODO: revisit this mess -->
<template name="preproc:symtable-process-symbols">
<param name="extresults" as="element( preproc:syms )" />
<param name="new" as="element( preproc:syms )" />
<param name="this-pkg" as="element( lv:package )" />
<message>
<text>[preproc/symtable] processing symbol table...</text>
</message>
<variable name="cursym" as="element( preproc:sym )*"
select="preproc:symtable/preproc:sym[
not( @held = 'true' ) ]" />
<variable name="cursym-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge(
for $sym in $cursym
return map{ string( $sym/@name ) : $sym } )" />
<variable name="extresults-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge(
for $sym in $extresults/preproc:sym
return map{ string( $sym/@name ) : $sym } )" />
<!-- contains duplicates -->
<variable name="new-seq-map" as="map( xs:string, element( preproc:sym )+ )"
select="map:merge(
for $sym in $new/preproc:sym
return map{ string( $sym/@name ) : $sym },
map{ 'duplicates' : 'combine' } )" />
<variable name="new-overrides-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge(
for $sym in $new/preproc:sym[ @override = 'true' ]
return map{ string( $sym/@name ) : $sym } )" />
<preproc:syms>
<variable name="cursym" as="element( preproc:sym )*"
select="preproc:symtable/preproc:sym[
not( @held = 'true' ) ]" />
<sequence select="$cursym" />
<message>
<text>[preproc/symtable] processing symbol table...</text>
</message>
<for-each select="$new/preproc:sym[ not( @extern='true' and @src ) ]">
<variable name="name" select="@name" />
<variable name="src" select="@src" />
<variable name="dupall" select="
(
preceding-sibling::preproc:sym,
$cursym,
$extresults/preproc:sym
)[
@name=$name
]
" />
<variable name="dup" select="
$dupall[
not(
@src=$src
or ( not( @src ) and not( $src ) )
)
]
" />
<variable name="dupall" as="element( preproc:sym )*"
select="$cursym-map( $name ),
$extresults-map( $name ),
if ( count( $new-seq-map( $name ) ) gt 1 ) then
preceding-sibling::preproc:sym[ @name = $name ]
else
()" />
<variable name="override" as="element( preproc:sym )?"
select="$new-overrides-map( @name )" />
<choose>
<when test="@pollute='true' and not( @type )">
@ -476,9 +510,7 @@
<sequence select="." />
</when>
<!-- note that dupall uses preceding-sibling, which will catch
duplicates in that case even if @override is not set -->
<when test="following-sibling::preproc:sym[ @name=$name and @override='true' ]">
<when test="exists( $override ) and not( $override is . )">
<!-- overridden; we're obsolete :( -->
</when>
@ -509,6 +541,8 @@
</choose>
</for-each>
</preproc:syms>
<message select="'[preproc/symtable] done processing symbol table.'" />
</template>