changeset 38:468a6cf8bf0b

big change wrt formulae compiles, crashes
author Henry S. Thompson <ht@markup.co.uk>
date Tue, 25 Apr 2017 22:17:12 +0100
parents ac3cd8de7a10
children 4c6a341e75da
files rect.xsl refs.xsl tokenise.xsl
diffstat 3 files changed, 96 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/rect.xsl	Tue Apr 25 18:30:04 2017 +0100
+++ b/rect.xsl	Tue Apr 25 22:17:12 2017 +0100
@@ -5,6 +5,7 @@
  
  <xsl:include href="a2n.xsl"/>
  <xsl:include href="n2a.xsl"/>
+ <xsl:include href="tokenise.xsl"/>
  
  <xsl:variable name="refs" select="collection()[2]/*"/>
  
@@ -40,6 +41,10 @@
          <t>
           <xsl:if test="$c/@e:class"><xsl:attribute name="c"><xsl:value-of select="substring($c/@e:class,1,1)"/></xsl:attribute></xsl:if>
           <xsl:if test="$c/@e:code"><xsl:attribute name="l"><xsl:value-of select="$c/@e:code"/></xsl:attribute></xsl:if>
+          <xsl:if test="$c/s:f">
+           <s:f>
+            <xsl:copy-of select="e:tokenise($c/s:f,$row,$col)"/>
+           </s:f></xsl:if>
           <xsl:value-of select="substring($c/@e:type,1,1)"/>
          </t>
         </xsl:if>
--- a/refs.xsl	Tue Apr 25 18:30:04 2017 +0100
+++ b/refs.xsl	Tue Apr 25 22:17:12 2017 +0100
@@ -1,28 +1,19 @@
 <?xml version='1.0'?>
 <!DOCTYPE xsl:stylesheet SYSTEM "../../../lib/xml/xsl.dtd" >
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:s="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:e="http://markup.co.uk/excel" exclude-result-prefixes="xs s e xf" xmlns="http://markup.co.uk/excel" xmlns:xf="http://www.w3.org/2005/xpath-functions">
+ 
+ <xsl:include href="a2n.xsl"/>
+ 
+ <xsl:param name="sheet-name"/>
 
  <xsl:template match="/">
   <refs sheetName="{$sheet-name}"><xsl:apply-templates select="//s:c"/></refs>
  </xsl:template>
  
- <xsl:template match="s:c[s:f]">
+ <xsl:template match="e:c[e:f]">
   <xsl:variable name="cr" select="e:cr(@r,0,0)"/>
-  <xsl:variable name="tokens" select="e:tokenise(s:f/.,$cr/e:r[1],$cr/e:r[2])"/>
-  <xsl:if test="@r='xxx'"><xsl:message><xsl:value-of select="s:f"/>|<xsl:value-of select="(analyze-string(s:f/.,$pat)/xf:match/xf:group)[3]/@nr"/></xsl:message>
+  <xsl:if test="@r='xxx'"><xsl:message><xsl:value-of select="count(e:f/*)"/>|</xsl:message>
   </xsl:if>
-  <xsl:if test="count($tokens)>0">
-   <xsl:variable name="singles" select="$tokens?1"/>
-   <!-- Note that we don't bother to treat external ranges as ranges,
-          since we're not going to try to detect cross-document refs -->
-   <xsl:variable name="ranges" select="$tokens?2"/>
-   <xsl:variable name="externals" select="$tokens?3"/>
-   <!-- Lost distinct-values filter, not sure it's really possible... -->
-   <ref c="{@r}">
-    <xsl:copy-of select="$singles"/>
-    <xsl:copy-of select="$ranges"/>
-    <xsl:copy-of select="$externals"/>
-   </ref></xsl:if>
  </xsl:template>
  
  <xsl:template match="s:c"/>
--- a/tokenise.xsl	Tue Apr 25 18:30:04 2017 +0100
+++ b/tokenise.xsl	Tue Apr 25 22:17:12 2017 +0100
@@ -3,8 +3,6 @@
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:s="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:e="http://markup.co.uk/excel" exclude-result-prefixes="xs s e xf" xmlns="http://markup.co.uk/excel" xmlns:xf="http://www.w3.org/2005/xpath-functions">
  <xsl:param name="sheet-number"/>
  <xsl:param name="xlDir"/>
- 
- <xsl:include href="a2n.xsl"/>
 
   <xsl:variable name="pat1">("[^"]*")|(\{[^}]+})|(,)|([^=\-+*/();:,.$&lt;>^!]+(?:\.[^=\-+*/();:,.$&lt;>^!]+)*\()|([)])|(^=|\()|((?:(?:'[^']+')|(?:\[[0-9]+\][^!]*)|(?:[a-zA-Z_][a-zA-Z0-9._]*)!))|(\$?[A-Z]+\$?[0-9]+)|([a-zA-Z_\\][a-zA-Z0-9._]*)|(.)</xsl:variable>
  <xsl:param name="pat" select="$pat1"/><!-- xsl:param for refinement debugging by passing in the pattern -->
@@ -14,12 +12,7 @@
  
  <xsl:function name="e:lookup" as="xs:string*">
   <xsl:param name="name" as="xs:string" required="yes"/>
-  <xsl:variable name="defn" select="$workbook/s:definedNames/s:definedName[@name=$name]"/>
-  <xsl:sequence select="let $prefix := concat($sheet-name,'!')
-                   return if ($defn and
-                              starts-with($defn,$prefix))
-                           then substring-after($defn,$prefix)
-                           else ()"/>
+  <xsl:value-of select="string($workbook/s:definedNames/s:definedName[@name=$name])"/>
  </xsl:function>
 
  <xsl:function name="e:tokenise" as="element(*)*">
@@ -32,15 +25,15 @@
        m: A constant matrix
        p: A close-paren
        q: A text (delimited by double quotes) 
-       r: A range reference
+       r: A range reference (two children, either e or s or u (unsupported))
        s: A single-cell reference
        v: A variable name [should only occur inside e]
        x: Amalgamated single characters not matched by anything else
  -->
   <xsl:param name="formula" as="xs:string" required="yes"/>
   <!-- The row and column number of the cell whence the formula came -->
-  <xsl:param name="row" required="yes" as="xs:int"/>
-  <xsl:param name="col" required="yes" as="xs:int"/>
+  <xsl:param name="row" required="yes" as="xs:integer"/>
+  <xsl:param name="col" required="yes" as="xs:integer"/>
   <xsl:sequence select="
      let $tokens := analyze-string($formula,$pat)/xf:match/xf:group
         return e:tok1($tokens,count($tokens),1,$row,$col,())"/>
@@ -48,10 +41,10 @@
  
  <xsl:function name="e:tok1" as="element(*)*">
   <xsl:param name="tokens" as="element(xf:group)*" required="yes"/>
-  <xsl:param name="n" required="yes" as="xs:int"/>
-  <xsl:param name="i" required="yes" as="xs:int"/>
-  <xsl:param name="row" required="yes" as="xs:int"/>
-  <xsl:param name="col" required="yes" as="xs:int"/>
+  <xsl:param name="n" required="yes" as="xs:integer"/>
+  <xsl:param name="i" required="yes" as="xs:integer"/>
+  <xsl:param name="row" required="yes" as="xs:integer"/>
+  <xsl:param name="col" required="yes" as="xs:integer"/>
   <xsl:param name="soFar" required="yes" as="element(*)*"/>
   <xsl:sequence select="
     if ($i gt $n)
@@ -63,12 +56,12 @@
             e:tok1($tokens,$n,$j,$row,$col,($soFar,$res))"/>
  </xsl:function>
  
- <xsl:function name="e:expand" as="element(*)*">
+ <xsl:function name="e:expand" as="array(*)">
   <xsl:param name="tokens" required="yes" as="element(xf:group)*"/>
-  <xsl:param name="i" required="yes" as="xs:int"/>
+  <xsl:param name="i" required="yes" as="xs:integer"/>
   <xsl:param name="local" required="yes" as="xs:boolean"/>
-  <xsl:param name="row" required="yes" as="xs:int"/>
-  <xsl:param name="col" required="yes" as="xs:int"/>
+  <xsl:param name="row" required="yes" as="xs:integer"/>
+  <xsl:param name="col" required="yes" as="xs:integer"/>
   <xsl:sequence select="
     let $t := $tokens[$i],
         $r := $tokens[$i + 1] return
@@ -87,16 +80,28 @@
      else if ($t/@nr=10) then e:amalgamate($tokens,$i+1,string($t))
      else if ($r[@nr=10 and .=':'])
        then (: a range, takes priority :)
-          e:range($tokens,$i,$ext,$row,$col)
-     else if ($t/@nr=8) then e:single($i,$ext,string($t))
+          [$i+2,e:range($tokens,$i,$local,$row,$col)]
+     else if ($t/@nr=8) then [$i+1,e:single(string($t),$row,$col)]
      else if ($t/@nr=9)
-       then if ($ext) then (: can't expand :) e:exp1($i,'v',string($t))
-       else e:tokenise(e:lookup(string($t)),$row,$col)
-     else (-- shouldn't ever get here --) ()"/>
+       then if ($local)
+            then let $sub := e:tokenise(e:lookup(string($t)),$row,$col) return
+              [$i+1,$sub]
+            else (: can't expand :) e:exp1($i,'v',string($t))
+     else (: shouldn't ever get here :) ()"/>
+ </xsl:function>
+ 
+ <xsl:function name="e:amalgamate" as="array(*)">
+  <xsl:param name="tokens" as="element(xf:group)*"/>
+  <xsl:param name="i" as="xs:integer"/>
+  <xsl:param name="soFar" as="xs:string"/>
+  <xsl:sequence select="if ($tokens[i]/@nr=10)
+                 then e:amalgamate($tokens,$i+1,concat($soFar,
+                                                       string($tokens[$i])))
+                 else [$i,$soFar]"/>
  </xsl:function>
  
  <xsl:function name="e:exp1" as="array(*)">
-  <xsl:param name="i" as="xs:int"/>
+  <xsl:param name="i" as="xs:integer"/>
   <xsl:param name="name" as="xs:string"/>
   <xsl:param name="val" as="xs:string"/>
   <xsl:variable name="elt">
@@ -107,32 +112,69 @@
   <xsl:sequence select="[$i+1,$elt]"/>
  </xsl:function>
  
- <xsl:function name="e:single" as="element(*)">
-  <xsl:param name="group" as="element(xf:group)"/>
+ <xsl:function name="e:single" as="element(e:s)">
+  <!-- I'm _guessing_ that external doesn't matter, i.e. that you
+       can copy an external relative ref and have it change -->
+  <xsl:param name="val" as="xs:string"/>
   <xsl:param name="row" as="xs:integer"/>
   <xsl:param name="col" as="xs:integer"/>
-  <xsl:param name="external" as="xs:boolean"/>
-  <xsl:variable name="val" select="if ($group/@nr=9) then e:lookup($group)
-                                             else string($group)"/>
-  <xsl:choose>
-   <xsl:when test="count($val)>0 or not($external)">
-    <xsl:sequence select="e:cr($val,$row,$col)"/>
-   </xsl:when>
-   <xsl:otherwise>
-    <v><xsl:value-of select="$group"/></v>
-   </xsl:otherwise>
-  </xsl:choose>  
+  <xsl:sequence select="e:cr($val,$row,$col)"/>  
  </xsl:function>
  
  <xsl:function name="e:range" as="element(e:r)">
-  <xsl:param name="l" as="element(e:s)" required="yes"/>
-  <xsl:param name="r" as="element(e:s)" required="yes"/>
-  <r><xsl:copy-of select="$l"/><xsl:copy-of select="$r"/></r>
+  <xsl:param name="tokens" as="element(xf:group)*"/>
+  <xsl:param name="i" as="xs:integer"/>
+  <xsl:param name="local" as="xs:boolean"/>
+  <xsl:param name="row" as="xs:integer"/>
+  <xsl:param name="col" as="xs:integer"/>
+  <xsl:variable name="l" select="$tokens[$i]"/>
+  <xsl:variable name="r" select="$tokens[$i+2]"/>
+  <xsl:variable name="lv" select="e:rPart($l,$local,$row,$col)"/>
+  <xsl:variable name="rv" select="e:rPart($r,$local,$row,$col)"/>
+  <r>
+   <xsl:copy-of select="($lv,$rv)"/>
+  </r>
+ </xsl:function>
+ 
+ <xsl:function name="e:rPart" as="element(*)">
+  <xsl:param name="g" as="element(xf:group)"/>
+  <xsl:param name="local" as="xs:boolean"/>
+  <xsl:param name="row" as="xs:integer"/>
+  <xsl:param name="col" as="xs:integer"/>
+  <xsl:sequence select="
+     if ($g/@nr=8) then e:single(string($g),$row,$col)
+     else if ($g/@nr=9)
+          then if ($local)
+               then let $tokens := e:tokenise(e:lookup(string($g)),
+                                                        $row,$col) return
+                      if (count($tokens)=1 and
+                          $tokens[local-name()='s' or 
+                                  (local-name()='e' and $tokens/e:s)])
+                      then $tokens
+                      else e:badRP(string($g),$g/@nr,$local,$row,$col,$tokens)
+           else e:var(string($g))
+     else e:badRP(string($g),$g/@nr,$local,$row,$col,())"/>
+ </xsl:function>
+ 
+ <xsl:function name="e:var" as="element(e:v)">
+  <xsl:param name="name" as="xs:string"/>
+  <v><xsl:value-of select="$name"/></v>
+ </xsl:function>
+ <xsl:function name="e:badRP" as="element(e:u)">
+  <xsl:param name="s" as="xs:string"/>
+  <xsl:param name="t" as="xs:integer"/>
+  <xsl:param name="local" as="xs:boolean"/>
+  <xsl:param name="row" as="xs:integer"/>
+  <xsl:param name="col" as="xs:integer"/>
+  <xsl:param name="toks" as="element(*)*"/>
+  <xsl:message terminate="yes">Bad range part in <xsl:value-of select="concat(e:n2a($col),$row)"/>: <xsl:value-of select="$s"/> of type <xsl:value-of select="$t"/> (<xsl:value-of select="if ($local) then 'local' else 'external'"/>: (<xsl:value-of select="string-join($toks,',')"/>)</xsl:message>
+  <u r="{concat(e:n2a($col),$row)}" s="{$s}" t="{$t}" local="{$local}">
+   <xsl:value-of select="string-join($toks,',')"/>
+  </u>
  </xsl:function>
  
  <xsl:function name="e:external" as="element(e:e)">
-  <xsl:param name="source" as="element(xf:group)" required="yes"/>
   <xsl:param name="ref" as="element(*)" required="yes"/>
-  <e s="{$source}"><xsl:sequence select="$ref"/></e>
+  <e><xsl:sequence select="$ref"/></e>
  </xsl:function>
 </xsl:stylesheet>