tame/src/current/include/preproc/symtable.xsl

943 lines
32 KiB
XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Generates a symbol table from fully a expanded (preprocessed) package
Copyright (C) 2016 R-T Specialty, LLC.
This file is part of TAME.
TAME is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
<http://www.gnu.org/licenses/>.
It is important that this table be generated after fully expanding all
templates, macros, etc; otherwise, the table may be incomplete.
The preproc:sym/@tex attribute is a TeX symbol used for typsetting. This
process is not responsible for generating defaults; that should be done by the
linker to ensure that there are no conflicts after all symbols are known.
Here are the recognized types:
rate - lv:rate block
gen - generator (c:*/@generates)
cgen - class generator (lv:classify/@yields)
class - classification (lv:classify/@as)
param - global param (lv:param)
lparam - local param (lv:function/lv:param)
const - global constant (lv:const; lv:enum/lv:item)
tpl - template (lv:template)
type - datatype (lv:typedef)
func - function
Dimensions (think 0=point, 1=line, 2=plane, etc):
0 - scalar
1 - vector
2 - matrix
...
Symbols from imported packages will be consumed and added to the output,
unless local; this has a similiar effect to including a C header file.
External symbols are denoted by preproc:sym/@src, which specifies the name of
the package from which it was imported. If an imported symbol conflicts with
another symbol (imported or otherwise), an error will be produced. Symbols
that are imported are implicitly marked as local.
Certain symbols will "polute" the symbol table of every package that imports
it, every package that imports that one, etc; this is for compatibility with
the old system and will hopefully be phased out eventually. Pollution will
reserve the symbol, but will not provide enough information about that symbol
to be useful, which will ensure that a package has to import the symbol
explicitly in order to actually make use of it.
-->
<xsl:stylesheet version="2.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:symtable="http://www.lovullo.com/tame/symtable"
xmlns:preproc="http://www.lovullo.com/rater/preproc"
xmlns:lv="http://www.lovullo.com/rater"
xmlns:c="http://www.lovullo.com/calc">
<xsl:include href="path.xsl" />
<xsl:include href="../../tame/src/symtable.xsl" />
<xsl:include href="../../tame/src/symtable/symbols.xsl" />
<!-- we will recurse through the entire tree rather than performing a series of
xpaths that are likely to hit the same nodes many times -->
<xsl:template match="*" mode="preproc:symtable" priority="1">
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<xsl:template match="text()" mode="preproc:symtable" priority="9">
<!-- output nothing -->
</xsl:template>
<!-- an alternative way to invoke the preproc:sym-discover template; useful for
use on variables -->
<xsl:template match="*" mode="preproc:sym-discover" as="element()">
<xsl:param name="orig-root" />
<xsl:call-template name="preproc:sym-discover">
<xsl:with-param name="orig-root" select="$orig-root" />
</xsl:call-template>
</xsl:template>
<xsl:template match="preproc:*" mode="preproc:sym-discover"
as="element()" priority="9">
<xsl:sequence select="." />
</xsl:template>
<!--
Other systems may contribute to a symbol table by invoking this template and
supplying the necessary preproc:symtable templates
TODO: This guy needs some refactoring
-->
<xsl:template name="preproc:sym-discover" as="element()">
<xsl:param name="orig-root" />
<xsl:variable name="this-pkg" as="element( lv:package )"
select="." />
<xsl:copy>
<xsl:sequence select="@*" />
<xsl:variable name="new">
<xsl:message>
<xsl:text>[preproc/symtable] discovering symbols...</xsl:text>
</xsl:message>
<preproc:syms>
<xsl:apply-templates mode="preproc:symtable">
<!-- we only need this param for the root children, so this is the only
template application that passes this -->
<xsl:with-param name="orig-root" select="$orig-root" />
</xsl:apply-templates>
</preproc:syms>
</xsl:variable>
<!-- gather a list of overrides -->
<xsl:variable name="overrides" select="
$new/preproc:syms/preproc:sym[ @override='true' ]
" />
<!-- check for duplicates -->
<xsl:variable name="symdup" as="element( preproc:sym )*"
select="symtable:find-duplicates( $new/preproc:syms )" />
<!-- overrides that override nothing may be the sign of a bug (expectation
of something that isn't there) -->
<xsl:for-each select="$overrides[ not( @name=$symdup/@name ) ]">
<xsl:message>
<xsl:text>[preproc/symtable] warning: symbol /</xsl:text>
<xsl:value-of select="$this-pkg/@name" />
<xsl:text>/</xsl:text>
<xsl:value-of select="@name" />
<xsl:text> has @override set, but does not override anything</xsl:text>
</xsl:message>
</xsl:for-each>
<!-- perform non-override duplicate checks (TODO: @ignore-dup is
intended to be temporary while map __head and __tail
generation conflicts are resolved) -->
<xsl:variable name="symdup-problems" as="element( preproc:sym )*" select="
$symdup[
not( @name = $overrides/@name
and @virtual = 'true' )
and not( @ignore-dup = 'true' ) ]
" />
<xsl:for-each select="$symdup-problems">
<xsl:variable name="dupname" select="@name" />
<xsl:choose>
<!-- attempt to override a non-virtual symbol -->
<xsl:when test="@name=$overrides/@name and not( @virtual='true' )">
<xsl:message>
<xsl:text>[preproc/symtable] error: cannot override non-virtual symbol /</xsl:text>
<xsl:value-of select="$this-pkg/@name" />
<xsl:text>/</xsl:text>
<xsl:value-of select="@name" />
</xsl:message>
</xsl:when>
<!-- just a plain duplicate -->
<xsl:otherwise>
<xsl:message>
<xsl:text>[preproc/symtable] error: duplicate symbol /</xsl:text>
<xsl:value-of select="$this-pkg/@name" />
<xsl:text>/</xsl:text>
<xsl:value-of select="@name" />
<xsl:text> (defined in ./</xsl:text>
<!-- output sources -->
<xsl:for-each select="
$new/preproc:syms/preproc:sym[
@name=$dupname
and @src
and not( @extern='true' )
and not( @name=$overrides/@name and @virtual='true' )
]
">
<xsl:if test="position() gt 1">
<xsl:text> and ./</xsl:text>
</xsl:if>
<xsl:value-of select="@src" />
</xsl:for-each>
<xsl:text>)</xsl:text>
<!-- if virtual, suggest override as an alternative solution -->
<xsl:if test="@virtual='true'">
<xsl:text>; did you forget @override?</xsl:text>
</xsl:if>
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- terminate if any duplicates are found, dumping documents for debugging -->
<xsl:if test="count( $symdup-problems ) gt 0">
<xsl:message>~~~~[begin document dump]~~~~</xsl:message>
<xsl:message select="$this-pkg" />
<xsl:message>~~~~[end document dump]~~~~</xsl:message>
<xsl:message>~~~~[begin symbol dump]~~~~</xsl:message>
<xsl:message select="$new" />
<xsl:message>~~~~[end symbol dump]~~~~</xsl:message>
<xsl:message terminate="yes">
<xsl:text>[preproc/symtable] fatal: aborting due to symbol errors</xsl:text>
</xsl:message>
</xsl:if>
<xsl:variable name="result" as="element( preproc:symtable )">
<preproc:symtable>
<!-- copy any existing symbols table -->
<preproc:syms>
<xsl:sequence select="preproc:symtable/preproc:sym" />
<xsl:sequence select="$new/preproc:syms/preproc:sym" />
</preproc:syms>
</preproc:symtable>
</xsl:variable>
<!-- output the symbols, checking for duplicates -->
<preproc:symtable>
<!-- validate imported externs -->
<xsl:variable name="extresults" as="element( preproc:syms )">
<xsl:call-template name="preproc:symtable-process-extern">
<xsl:with-param name="result" select="$result" />
</xsl:call-template>
</xsl:variable>
<!-- remove duplicates (if any) -->
<xsl:sequence select="
$extresults/preproc:sym[
not( @name=preceding-sibling::preproc:sym/@name )
]
, $extresults//preproc:error
" />
<!-- process symbols (except imported externs) -->
<xsl:variable name="newresult" as="element( preproc:syms )">
<xsl:call-template name="preproc:symtable-process-symbols">
<xsl:with-param name="extresults" select="$extresults" />
<xsl:with-param name="new" select="$new/preproc:syms" />
<xsl:with-param name="this-pkg" select="$this-pkg" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="dedup" as="element( preproc:sym )*"
select="$newresult/preproc:sym[
not(
(
@pollute='true'
and not( @type )
and (
@name=preceding-sibling::preproc:sym/@name
or @name=$newresult/preproc:sym[ @type ]/@name
)
)
or (
@local = 'true'
and @name = following-sibling::preproc:sym[
not( @local = 'true' )
]/@name
)
)
]" />
<xsl:apply-templates mode="preproc:symtable-complete"
select="$dedup">
<xsl:with-param name="syms" select="$dedup" />
</xsl:apply-templates>
</preproc:symtable>
<xsl:message>
<xsl:text>[preproc/symtable] done.</xsl:text>
</xsl:message>
<!-- 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
rid of them; discard any existing symbol table -->
<xsl:apply-templates mode="preproc:symtable-inject" />
</xsl:copy>
</xsl:template>
<xsl:template match="preproc:symtable" mode="preproc:symtable-inject"
priority="5">
<!-- strip old symbol table -->
</xsl:template>
<xsl:template match="*" mode="preproc:symtable-inject">
<xsl:sequence select="." />
</xsl:template>
<xsl:template name="preproc:symtable-process-extern"
as="element( preproc:syms )">
<xsl:param name="result" as="element( preproc:symtable )" />
<xsl:variable name="syms" as="element( preproc:syms )"
select="$result/preproc:syms" />
<preproc:syms>
<xsl:for-each select="$syms/preproc:sym[
@extern='true'
and @src
and not( @held ) ]">
<xsl:variable name="name" select="@name" />
<!-- our value may be concrete or an extern itself; we may also import
a package with a concrete definition -->
<xsl:variable name="ours" select="
$syms/preproc:sym[
@name=$name
and (
not( @src )
or ( @dtype and @src and not( @extern ) )
)
]
" />
<xsl:choose>
<!-- we have our own symbol; ensure the important values match the
expected (unless @dim has not yet been resolved) -->
<!-- XXX: the @dim='?' check leaves room for a dimension mismatch to
slip through; re-order checks (see package.xsl) as necessary to
ensure that this doesn't happen -->
<xsl:when test="$ours">
<xsl:if test="
not(
@type=$ours/@type
and @dtype=$ours/@dtype
and (
@dim=$ours/@dim
or $ours/@dim='?'
)
)
">
<preproc:error>
<xsl:text>extern mismatch: '</xsl:text>
<xsl:value-of select="@name" />
<xsl:text>' (imported from </xsl:text>
<xsl:value-of select="@src" />
<xsl:text>)</xsl:text>
<xsl:for-each select="@type, @dtype, @dim">
<xsl:variable name="aname" select="local-name()" />
<xsl:text>; </xsl:text>
<xsl:value-of select="local-name()" />
<xsl:text>=</xsl:text>
<xsl:value-of select="$ours/@*[ local-name() = $aname ]" />
<xsl:text>, </xsl:text>
<xsl:value-of select="." />
<xsl:text> expected</xsl:text>
</xsl:for-each>
</preproc:error>
</xsl:if>
<!-- N.B.: there could potentially be multiple matches -->
<!-- TODO: pollution should be removed and l:resolv-extern
in the linker should look up the package that should
include the resolved extern from the processing stack -->
<preproc:sym pollute="true">
<xsl:sequence select="$ours[1]/@*|*" />
</preproc:sym>
</xsl:when>
<!-- we do not have our own symbol matching this extern;
ignore this for now, as it may be handled by a future
template expansion -->
<xsl:otherwise>
<preproc:sym held="true">
<xsl:sequence select="@*" />
</preproc:sym>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</preproc:syms>
</xsl:template>
<xsl:function name="preproc:final-extern-check" as="element(preproc:error )*">
<xsl:param name="symtable" as="element( preproc:symtable )" />
<!-- any remaining unresolved externs at this point are bad -->
<xsl:for-each select="$symtable/preproc:sym[
@extern = 'true'
and @src ]">
<!-- since @missing may be provided, let's include the actual
symbol in the runlog for debugging -->
<xsl:message select="'[preproc] missing extern: ', @name" />
<preproc:error>
<xsl:choose>
<xsl:when test="@missing and not( @missing = '' )">
<xsl:value-of select="normalize-space( @missing )" />
</xsl:when>
<xsl:otherwise>
<xsl:text>unresolved extern '</xsl:text>
<xsl:value-of select="@name" />
<xsl:text>'</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text> (required by </xsl:text>
<xsl:value-of select="@src" />
<xsl:text>)</xsl:text>
</preproc:error>
</xsl:for-each>
</xsl:function>
<xsl:template name="preproc:symtable-process-symbols">
<xsl:param name="extresults" as="element( preproc:syms )" />
<xsl:param name="new" as="element( preproc:syms )" />
<xsl:param name="this-pkg" as="element( lv:package )" />
<preproc:syms>
<xsl:variable name="cursym" as="element( preproc:sym )*"
select="preproc:symtable/preproc:sym[
not( @held = 'true' ) ]" />
<xsl:sequence select="$cursym" />
<xsl:message>
<xsl:text>[preproc/symtable] processing symbol table...</xsl:text>
</xsl:message>
<xsl:for-each select="$new/preproc:sym[ not( @extern='true' and @src ) ]">
<xsl:variable name="name" select="@name" />
<xsl:variable name="src" select="@src" />
<xsl:variable name="dupall" select="
(
preceding-sibling::preproc:sym,
$cursym,
$extresults/preproc:sym
)[
@name=$name
]
" />
<xsl:variable name="dup" select="
$dupall[
not(
@src=$src
or ( not( @src ) and not( $src ) )
)
]
" />
<xsl:choose>
<xsl:when test="@pollute='true' and not( @type )">
<!-- we'll strip these out later -->
<xsl:sequence select="." />
</xsl:when>
<!-- note that dupall uses preceding-sibling, which will catch
duplicates in that case even if @override is not set -->
<xsl:when test="following-sibling::preproc:sym[ @name=$name and @override='true' ]">
<!-- overridden; we're obsolete :( -->
</xsl:when>
<!-- if we've gotten this far, then the override is good; clear it -->
<xsl:when test="@override='true'">
<xsl:copy>
<xsl:sequence select="@*[ not( name()='override' ) ], *" />
</xsl:copy>
</xsl:when>
<!-- if we have already imported the symbol as local, but this one
is non-local (exportable), then this one takes precedence -->
<xsl:when test="not( @local = 'true' )
and $dupall[ @local = 'true' ]
and not( $dupall[ not( @local = 'true' ) ] )">
<xsl:sequence select="." />
</xsl:when>
<xsl:when test="$dupall[ @type ]">
<!-- there is already a symbol of this name from the same package;
let's not add duplicates -->
</xsl:when>
<xsl:otherwise>
<!-- this symbol is good; use it -->
<xsl:sequence select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</preproc:syms>
</xsl:template>
<xsl:template match="preproc:symtable" mode="preproc:symtable" priority="9">
<!-- ignore existing symbol tables (for now at least) -->
</xsl:template>
<!-- do not re-import symbol tables that have already been imported -->
<xsl:template match="lv:import[
@package=root(.)/preproc:symtable/preproc:sym[
@dtype
]/@src
]"
mode="preproc:symtable" priority="9">
</xsl:template>
<xsl:template name="preproc:symimport" match="lv:import[ @package ]" mode="preproc:symtable" priority="8">
<xsl:param name="orig-root" />
<xsl:param name="package" select="@package" />
<xsl:param name="export" select="@export" />
<xsl:param name="no-extclass" select="@no-extclass" />
<xsl:param name="keep-classes" select="@keep-classes" />
<xsl:variable name="path" as="xs:string"
select="concat( $package, '.xmlo' )" />
<xsl:variable name="syms"
select="document( $path, $orig-root )/lv:*/preproc:symtable" />
<xsl:variable name="import-path" select="$package" />
<xsl:variable name="src-root" as="xs:string"
select="ancestor::lv:package/@__rootpath" />
<xsl:variable name="src-name" as="xs:string"
select="ancestor::lv:package/@name" />
<!-- if they're including a program package, do they realize what they're
doing!? -->
<!-- FIXME: @allow-nonpkg is no longer accurate terminology; change to
@allow-nonprg -->
<xsl:if test="
not( @allow-nonpkg = 'true' )
and $syms/parent::lv:package[ @program='true' ]
">
<xsl:message terminate="yes">
<xsl:text>[preproc/symtable] error: refusing to import non-package </xsl:text>
<xsl:value-of select="$import-path" />
<xsl:text>; use @allow-nonpkg to force (if you know what you are doing)</xsl:text>
</xsl:message>
</xsl:if>
<!-- to keep everything consistent and to simplify package equality
assertions, resolve relative paths -->
<xsl:variable name="import-default-path" select="$import-path" />
<xsl:message>
<xsl:text>[preproc/symtable] importing symbol table of </xsl:text>
<xsl:value-of select="$import-path" />
<xsl:text>...</xsl:text>
</xsl:message>
<!-- attempt to import symbols from the processed package -->
<xsl:if test="not( $syms )">
<xsl:message terminate="yes">
<xsl:text>[preproc/symtable] internal error: </xsl:text>
<xsl:text>failed to locate symbol table: </xsl:text>
<xsl:value-of select="$path" />
</xsl:message>
</xsl:if>
<!-- copy directly into symbol table, setting external source; local symbols
will not be imported -->
<xsl:for-each select="
$syms/preproc:sym[
(
not( @local='true' )
or @pollute='true'
or (
( @type='class' or @type='cgen' )
and $keep-classes='true'
)
)
and not( $no-extclass='true' and @extclass='true' )
]
">
<xsl:copy>
<xsl:choose>
<!-- pollution should have only the name and pollution status copied
over, which has the effect of reserving the symbol but not
providing enough information to actually make use of it; only
strip the data if this is a second-hand import -->
<!-- TODO: this list has gotten too large, but reducing it will require
refactoring other compilers and may reduce performance -->
<xsl:when test="@pollute='true'
and @local='true'
and not( @extern='true' )">
<xsl:sequence select="@name, @src, @pollute, @parent, @extclass" />
</xsl:when>
<!-- copy all the symbol information -->
<xsl:otherwise>
<xsl:sequence select="@*" />
</xsl:otherwise>
</xsl:choose>
<!-- all imported symbols are implicitly local (so including one package
will not include symbols down the entire hierarchy), unless the
symbol is explicitly marked global or @export was provided on the
import node -->
<xsl:if test="not( $export='true' )">
<xsl:attribute name="local" select="'true'" />
</xsl:if>
<!-- determine the relative path to the import -->
<xsl:attribute name="src">
<xsl:choose>
<!-- if no @src is set, then the answer is simple: the relative path is
the import path -->
<xsl:when test="not( @src )">
<xsl:value-of select="$import-default-path" />
</xsl:when>
<!-- otherwise, we need to merge the import path into the existing
relative path by prepending the import path (sans the package name
itself) onto the existing relative path and resolving relative
paths -->
<xsl:otherwise>
<xsl:sequence select="preproc:resolve-relative-import(
$src-root,
$src-name,
$import-path,
@src )" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<!-- children should always be copied, unless poluting -->
<xsl:if test="not( @pollute='true' and @local='true' )">
<xsl:sequence select="preproc:*" />
</xsl:if>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:template match="lv:rate" mode="preproc:symtable" priority="5">
<xsl:variable name="external" select="boolean( @external='true' )" />
<preproc:sym name="{@yields}" type="rate"
extclass="{$external}"
local="{@local}" dtype="float" dim="0" tex="{@sym}">
<xsl:if test="@preproc:yields-generated">
<xsl:attribute name="preproc:generated" select="'true'" />
</xsl:if>
</preproc:sym>
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<xsl:template match="lv:const" mode="preproc:symtable" priority="5">
<xsl:variable name="dim">
<xsl:choose>
<!-- TODO: matrix/vector predicate to support either type via
@values -->
<xsl:when test="./lv:set or @values">
<xsl:text>2</xsl:text>
</xsl:when>
<xsl:when test="./lv:item">
<xsl:text>1</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- TODO: remove magic support -->
<preproc:sym name="{@name}"
magic="{boolean( @magic='true' )}"
type="const" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}">
<!-- may or may not exist -->
<xsl:sequence select="@value" />
</preproc:sym>
<!-- for performance, we will not recurse any further; the rest are simply
data declarations -->
</xsl:template>
<xsl:template match="c:*[ @generates ]" mode="preproc:symtable" priority="5">
<!-- it's possible that templates generating rate blocks will cause nested
rate blocks, so only take the first ancestor -->
<xsl:variable name="parent" as="element( lv:rate )"
select="ancestor::lv:rate[1]" />
<xsl:variable name="dim" as="xs:integer"
select="if ( @dim ) then @dim else 1" />
<preproc:sym name="{@generates}"
parent="{$parent/@yields}"
type="gen" dtype="float" dim="{$dim}" desc="{@desc}" tex="{@sym}" />
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<!-- note the @dim value; this is determined later from its dependencies -->
<xsl:template match="lv:classify" mode="preproc:symtable" priority="5">
<xsl:variable name="external" select="boolean( @external='true' )" />
<xsl:variable name="terminate" select="boolean( @terminate='true' )" />
<preproc:sym name=":class:{@as}"
extclass="{$external}" terminate="{$terminate}"
type="class" dim="?" desc="{@desc}" yields="{@yields}"
orig-name="{@as}">
<!-- copy preprocessor metadata to symbol for easy reference -->
<xsl:sequence select="@preproc:*" />
</preproc:sym>
<!-- generator if @yields is provided (note that we also have a @yields above
to avoid scanning separate object files for such common information)
-->
<xsl:if test="@yields">
<preproc:sym name="{@yields}"
parent=":class:{@as}"
extclass="{$external}" terminate="{$terminate}"
type="cgen" dtype="boolean" dim="?" desc="{@desc}">
<xsl:if test="@preproc:yields-generated">
<xsl:attribute name="preproc:generated" select="'true'" />
</xsl:if>
<xsl:sequence select="@preproc:*" />
</preproc:sym>
</xsl:if>
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<xsl:template match="lv:typedef" mode="preproc:symtable" priority="5">
<!-- FIXME: this is a kluge -->
<xsl:variable name="dtype" as="xs:string?"
select="if ( lv:base-type ) then
@name
else
lv:enum/@type
, lv:union/lv:typedef[1]/lv:enum/@type" />
<xsl:if test="not( $dtype )">
<xsl:message terminate="yes">
<xsl:text>[preproc/symtable] internal error: </xsl:text>
<xsl:text>failed to resolve type primitve of `</xsl:text>
<xsl:value-of select="@name" />
<xsl:text>'</xsl:text>
</xsl:message>
</xsl:if>
<preproc:sym name="{@name}" dtype="{$dtype}"
type="type" dim="0" desc="{@desc}" />
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<xsl:template match="lv:typedef/lv:enum/lv:item" mode="preproc:symtable" priority="5">
<xsl:variable name="dtype" select="parent::lv:enum/@type" />
<preproc:sym name="{@name}" value="{@value}"
type="const" dtype="{$dtype}" dim="0" desc="{@desc}" />
</xsl:template>
<xsl:template match="lv:function" mode="preproc:symtable" priority="5">
<!-- default TeX symbol to the function name -->
<xsl:variable name="tex">
<xsl:choose>
<xsl:when test="@sym">
<xsl:value-of select="@sym" />
</xsl:when>
<xsl:otherwise>
<xsl:text>\textrm{</xsl:text>
<xsl:value-of select="@name" />
<xsl:text>}</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- TODO: determine return data type from tail -->
<!-- TODO: same for dim -->
<!-- functions can have circular dependencies (recursion) -->
<preproc:sym name="{@name}" type="func" dtype="float" dim="0" desc="{@desc}"
tex="{$tex}" allow-circular="true">
<!-- we need to include the argument order and symbol refs so that the
compiler knows how to call the function -->
<xsl:variable name="fname" select="@name" />
<xsl:for-each select="lv:param">
<preproc:sym-ref name=":{$fname}:{@name}" />
</xsl:for-each>
</preproc:sym>
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<!--
Function parameters are local to the function and are represented differently
in the symbol table than most other symbols. In particular:
- They have type lparam, not param
- Their name begins with a colon, which is normally invalid (ensuring that
there will be no naming conflicts)
- The name following the colon is the concatenation of the function name,
an underscore and the param name
-->
<xsl:template match="lv:function/lv:param" mode="preproc:symtable" priority="6">
<!-- determine number of dimensions -->
<xsl:variable name="dim">
<xsl:call-template name="preproc:param-dim" />
</xsl:variable>
<xsl:variable name="fname" select="parent::lv:function/@name" />
<preproc:sym name=":{$fname}:{@name}" parent="{$fname}" varname="{@name}"
type="lparam" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}">
<!-- may or may not be defined -->
<xsl:sequence select="@default" />
</preproc:sym>
</xsl:template>
<!--
Same concept as function params
-->
<xsl:template match="c:let/c:values/c:value" mode="preproc:symtable" priority="5">
<xsl:variable name="name" select="@name" />
<!-- determine number of dimensions -->
<xsl:variable name="dim">
<xsl:call-template name="preproc:param-dim" />
</xsl:variable>
<!-- the name is generated automatically by the preprocessor; the user cannot
set it -->
<xsl:variable name="lname" as="xs:string?"
select="ancestor::c:let[1]/@name" />
<!-- @lparent instead of @parent because the let does not actually exist
as a symbol -->
<preproc:sym name=":{$lname}:{@name}" local="true" varname="{@name}"
type="lparam" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}"
lparent="{$lname}" />
<xsl:apply-templates mode="preproc:symtable" />
</xsl:template>
<xsl:template match="lv:extern" mode="preproc:symtable" priority="5">
<preproc:sym desc="{@name} extern" extern="true" missing="{@missing}">
<!-- copy all the user-supplied params -->
<xsl:sequence select="@*" />
</preproc:sym>
</xsl:template>
<xsl:template match="preproc:sym[ @type='param' ]" mode="preproc:symtable-complete" priority="5">
<xsl:param name="syms" as="element( preproc:sym )*" />
<!-- attempt to derive type information from a typedef -->
<!-- TODO: also check symbol table after import (post-process) -->
<xsl:variable name="type" select="@dtype" />
<xsl:variable name="typedef" as="element( preproc:sym )?"
select="$syms[ @type = 'type'
and @name = $type ]" />
<xsl:if test="not( $typedef and $typedef/@dtype )">
<xsl:message terminate="yes">
<xsl:text>[preproc/symtable] internal error: </xsl:text>
<xsl:text>failed to resolve type: </xsl:text>
<xsl:value-of select="$type" />
</xsl:message>
</xsl:if>
<!-- complete datatype with primitive -->
<xsl:copy>
<xsl:sequence select="@*" />
<xsl:attribute name="dtype" select="$typedef/@dtype" />
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="preproc:symtable-complete" priority="1">
<!-- symbol does not need completion -->
<xsl:sequence select="." />
</xsl:template>
<!--
Determines param dimension from its string definition:
vector = 1-dimensional;
matrix = 2-dimensional;
otherwise, scalar = 0-dimensional
Other dimensions are certainly supported, but @set's syntax does not support
their specification.
-->
<xsl:template name="preproc:param-dim">
<xsl:choose>
<xsl:when test="@set = 'vector'">
<xsl:text>1</xsl:text>
</xsl:when>
<xsl:when test="@set = 'matrix'">
<xsl:text>2</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>