Mercurial > hg > ooxml
comparison tokenise.xsl @ 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 |
comparison
equal
deleted
inserted
replaced
37:ac3cd8de7a10 | 38:468a6cf8bf0b |
---|---|
1 <?xml version='1.0'?> | 1 <?xml version='1.0'?> |
2 <!DOCTYPE xsl:stylesheet SYSTEM "../../../lib/xml/xsl.dtd" > | 2 <!DOCTYPE xsl:stylesheet SYSTEM "../../../lib/xml/xsl.dtd" > |
3 <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"> | 3 <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"> |
4 <xsl:param name="sheet-number"/> | 4 <xsl:param name="sheet-number"/> |
5 <xsl:param name="xlDir"/> | 5 <xsl:param name="xlDir"/> |
6 | |
7 <xsl:include href="a2n.xsl"/> | |
8 | 6 |
9 <xsl:variable name="pat1">("[^"]*")|(\{[^}]+})|(,)|([^=\-+*/();:,.$<>^!]+(?:\.[^=\-+*/();:,.$<>^!]+)*\()|([)])|(^=|\()|((?:(?:'[^']+')|(?:\[[0-9]+\][^!]*)|(?:[a-zA-Z_][a-zA-Z0-9._]*)!))|(\$?[A-Z]+\$?[0-9]+)|([a-zA-Z_\\][a-zA-Z0-9._]*)|(.)</xsl:variable> | 7 <xsl:variable name="pat1">("[^"]*")|(\{[^}]+})|(,)|([^=\-+*/();:,.$<>^!]+(?:\.[^=\-+*/();:,.$<>^!]+)*\()|([)])|(^=|\()|((?:(?:'[^']+')|(?:\[[0-9]+\][^!]*)|(?:[a-zA-Z_][a-zA-Z0-9._]*)!))|(\$?[A-Z]+\$?[0-9]+)|([a-zA-Z_\\][a-zA-Z0-9._]*)|(.)</xsl:variable> |
10 <xsl:param name="pat" select="$pat1"/><!-- xsl:param for refinement debugging by passing in the pattern --> | 8 <xsl:param name="pat" select="$pat1"/><!-- xsl:param for refinement debugging by passing in the pattern --> |
11 | 9 |
12 <xsl:variable name="workbook" select="document(concat($xlDir,'/workbook.xml'))/*"/> | 10 <xsl:variable name="workbook" select="document(concat($xlDir,'/workbook.xml'))/*"/> |
13 <xsl:variable name="sheet-name" select="$workbook/s:sheets/s:sheet[@sheetId=$sheet-number]/@name"/> | 11 <xsl:variable name="sheet-name" select="$workbook/s:sheets/s:sheet[@sheetId=$sheet-number]/@name"/> |
14 | 12 |
15 <xsl:function name="e:lookup" as="xs:string*"> | 13 <xsl:function name="e:lookup" as="xs:string*"> |
16 <xsl:param name="name" as="xs:string" required="yes"/> | 14 <xsl:param name="name" as="xs:string" required="yes"/> |
17 <xsl:variable name="defn" select="$workbook/s:definedNames/s:definedName[@name=$name]"/> | 15 <xsl:value-of select="string($workbook/s:definedNames/s:definedName[@name=$name])"/> |
18 <xsl:sequence select="let $prefix := concat($sheet-name,'!') | |
19 return if ($defn and | |
20 starts-with($defn,$prefix)) | |
21 then substring-after($defn,$prefix) | |
22 else ()"/> | |
23 </xsl:function> | 16 </xsl:function> |
24 | 17 |
25 <xsl:function name="e:tokenise" as="element(*)*"> | 18 <xsl:function name="e:tokenise" as="element(*)*"> |
26 <!-- Tokenise a formula, recursively wrt variables | 19 <!-- Tokenise a formula, recursively wrt variables |
27 Output is composed of e:* as follows: | 20 Output is composed of e:* as follows: |
30 f: A function name followed by an opening parenthesis | 23 f: A function name followed by an opening parenthesis |
31 l: The beginning of the formula or an opening paren | 24 l: The beginning of the formula or an opening paren |
32 m: A constant matrix | 25 m: A constant matrix |
33 p: A close-paren | 26 p: A close-paren |
34 q: A text (delimited by double quotes) | 27 q: A text (delimited by double quotes) |
35 r: A range reference | 28 r: A range reference (two children, either e or s or u (unsupported)) |
36 s: A single-cell reference | 29 s: A single-cell reference |
37 v: A variable name [should only occur inside e] | 30 v: A variable name [should only occur inside e] |
38 x: Amalgamated single characters not matched by anything else | 31 x: Amalgamated single characters not matched by anything else |
39 --> | 32 --> |
40 <xsl:param name="formula" as="xs:string" required="yes"/> | 33 <xsl:param name="formula" as="xs:string" required="yes"/> |
41 <!-- The row and column number of the cell whence the formula came --> | 34 <!-- The row and column number of the cell whence the formula came --> |
42 <xsl:param name="row" required="yes" as="xs:int"/> | 35 <xsl:param name="row" required="yes" as="xs:integer"/> |
43 <xsl:param name="col" required="yes" as="xs:int"/> | 36 <xsl:param name="col" required="yes" as="xs:integer"/> |
44 <xsl:sequence select=" | 37 <xsl:sequence select=" |
45 let $tokens := analyze-string($formula,$pat)/xf:match/xf:group | 38 let $tokens := analyze-string($formula,$pat)/xf:match/xf:group |
46 return e:tok1($tokens,count($tokens),1,$row,$col,())"/> | 39 return e:tok1($tokens,count($tokens),1,$row,$col,())"/> |
47 </xsl:function> | 40 </xsl:function> |
48 | 41 |
49 <xsl:function name="e:tok1" as="element(*)*"> | 42 <xsl:function name="e:tok1" as="element(*)*"> |
50 <xsl:param name="tokens" as="element(xf:group)*" required="yes"/> | 43 <xsl:param name="tokens" as="element(xf:group)*" required="yes"/> |
51 <xsl:param name="n" required="yes" as="xs:int"/> | 44 <xsl:param name="n" required="yes" as="xs:integer"/> |
52 <xsl:param name="i" required="yes" as="xs:int"/> | 45 <xsl:param name="i" required="yes" as="xs:integer"/> |
53 <xsl:param name="row" required="yes" as="xs:int"/> | 46 <xsl:param name="row" required="yes" as="xs:integer"/> |
54 <xsl:param name="col" required="yes" as="xs:int"/> | 47 <xsl:param name="col" required="yes" as="xs:integer"/> |
55 <xsl:param name="soFar" required="yes" as="element(*)*"/> | 48 <xsl:param name="soFar" required="yes" as="element(*)*"/> |
56 <xsl:sequence select=" | 49 <xsl:sequence select=" |
57 if ($i gt $n) | 50 if ($i gt $n) |
58 then $soFar | 51 then $soFar |
59 else | 52 else |
61 $j := $next?1, | 54 $j := $next?1, |
62 $res := $next?2 return | 55 $res := $next?2 return |
63 e:tok1($tokens,$n,$j,$row,$col,($soFar,$res))"/> | 56 e:tok1($tokens,$n,$j,$row,$col,($soFar,$res))"/> |
64 </xsl:function> | 57 </xsl:function> |
65 | 58 |
66 <xsl:function name="e:expand" as="element(*)*"> | 59 <xsl:function name="e:expand" as="array(*)"> |
67 <xsl:param name="tokens" required="yes" as="element(xf:group)*"/> | 60 <xsl:param name="tokens" required="yes" as="element(xf:group)*"/> |
68 <xsl:param name="i" required="yes" as="xs:int"/> | 61 <xsl:param name="i" required="yes" as="xs:integer"/> |
69 <xsl:param name="local" required="yes" as="xs:boolean"/> | 62 <xsl:param name="local" required="yes" as="xs:boolean"/> |
70 <xsl:param name="row" required="yes" as="xs:int"/> | 63 <xsl:param name="row" required="yes" as="xs:integer"/> |
71 <xsl:param name="col" required="yes" as="xs:int"/> | 64 <xsl:param name="col" required="yes" as="xs:integer"/> |
72 <xsl:sequence select=" | 65 <xsl:sequence select=" |
73 let $t := $tokens[$i], | 66 let $t := $tokens[$i], |
74 $r := $tokens[$i + 1] return | 67 $r := $tokens[$i + 1] return |
75 if ($t/@nr=1) then e:exp1($i,'q',string($t)) | 68 if ($t/@nr=1) then e:exp1($i,'q',string($t)) |
76 else if ($t/@nr=2) then e:exp1($i,'m',string($t)) | 69 else if ($t/@nr=2) then e:exp1($i,'m',string($t)) |
85 else let $ext := e:expand($tokens,$i+1,false(),$row,$col) return | 78 else let $ext := e:expand($tokens,$i+1,false(),$row,$col) return |
86 [$ext?1,e:external($ext?2)] | 79 [$ext?1,e:external($ext?2)] |
87 else if ($t/@nr=10) then e:amalgamate($tokens,$i+1,string($t)) | 80 else if ($t/@nr=10) then e:amalgamate($tokens,$i+1,string($t)) |
88 else if ($r[@nr=10 and .=':']) | 81 else if ($r[@nr=10 and .=':']) |
89 then (: a range, takes priority :) | 82 then (: a range, takes priority :) |
90 e:range($tokens,$i,$ext,$row,$col) | 83 [$i+2,e:range($tokens,$i,$local,$row,$col)] |
91 else if ($t/@nr=8) then e:single($i,$ext,string($t)) | 84 else if ($t/@nr=8) then [$i+1,e:single(string($t),$row,$col)] |
92 else if ($t/@nr=9) | 85 else if ($t/@nr=9) |
93 then if ($ext) then (: can't expand :) e:exp1($i,'v',string($t)) | 86 then if ($local) |
94 else e:tokenise(e:lookup(string($t)),$row,$col) | 87 then let $sub := e:tokenise(e:lookup(string($t)),$row,$col) return |
95 else (-- shouldn't ever get here --) ()"/> | 88 [$i+1,$sub] |
89 else (: can't expand :) e:exp1($i,'v',string($t)) | |
90 else (: shouldn't ever get here :) ()"/> | |
91 </xsl:function> | |
92 | |
93 <xsl:function name="e:amalgamate" as="array(*)"> | |
94 <xsl:param name="tokens" as="element(xf:group)*"/> | |
95 <xsl:param name="i" as="xs:integer"/> | |
96 <xsl:param name="soFar" as="xs:string"/> | |
97 <xsl:sequence select="if ($tokens[i]/@nr=10) | |
98 then e:amalgamate($tokens,$i+1,concat($soFar, | |
99 string($tokens[$i]))) | |
100 else [$i,$soFar]"/> | |
96 </xsl:function> | 101 </xsl:function> |
97 | 102 |
98 <xsl:function name="e:exp1" as="array(*)"> | 103 <xsl:function name="e:exp1" as="array(*)"> |
99 <xsl:param name="i" as="xs:int"/> | 104 <xsl:param name="i" as="xs:integer"/> |
100 <xsl:param name="name" as="xs:string"/> | 105 <xsl:param name="name" as="xs:string"/> |
101 <xsl:param name="val" as="xs:string"/> | 106 <xsl:param name="val" as="xs:string"/> |
102 <xsl:variable name="elt"> | 107 <xsl:variable name="elt"> |
103 <xsl:element name="{$name}" namespace="http://markup.co.uk/excel"> | 108 <xsl:element name="{$name}" namespace="http://markup.co.uk/excel"> |
104 <xsl:value-of select="$val"/> | 109 <xsl:value-of select="$val"/> |
105 </xsl:element> | 110 </xsl:element> |
106 </xsl:variable> | 111 </xsl:variable> |
107 <xsl:sequence select="[$i+1,$elt]"/> | 112 <xsl:sequence select="[$i+1,$elt]"/> |
108 </xsl:function> | 113 </xsl:function> |
109 | 114 |
110 <xsl:function name="e:single" as="element(*)"> | 115 <xsl:function name="e:single" as="element(e:s)"> |
111 <xsl:param name="group" as="element(xf:group)"/> | 116 <!-- I'm _guessing_ that external doesn't matter, i.e. that you |
117 can copy an external relative ref and have it change --> | |
118 <xsl:param name="val" as="xs:string"/> | |
112 <xsl:param name="row" as="xs:integer"/> | 119 <xsl:param name="row" as="xs:integer"/> |
113 <xsl:param name="col" as="xs:integer"/> | 120 <xsl:param name="col" as="xs:integer"/> |
114 <xsl:param name="external" as="xs:boolean"/> | 121 <xsl:sequence select="e:cr($val,$row,$col)"/> |
115 <xsl:variable name="val" select="if ($group/@nr=9) then e:lookup($group) | |
116 else string($group)"/> | |
117 <xsl:choose> | |
118 <xsl:when test="count($val)>0 or not($external)"> | |
119 <xsl:sequence select="e:cr($val,$row,$col)"/> | |
120 </xsl:when> | |
121 <xsl:otherwise> | |
122 <v><xsl:value-of select="$group"/></v> | |
123 </xsl:otherwise> | |
124 </xsl:choose> | |
125 </xsl:function> | 122 </xsl:function> |
126 | 123 |
127 <xsl:function name="e:range" as="element(e:r)"> | 124 <xsl:function name="e:range" as="element(e:r)"> |
128 <xsl:param name="l" as="element(e:s)" required="yes"/> | 125 <xsl:param name="tokens" as="element(xf:group)*"/> |
129 <xsl:param name="r" as="element(e:s)" required="yes"/> | 126 <xsl:param name="i" as="xs:integer"/> |
130 <r><xsl:copy-of select="$l"/><xsl:copy-of select="$r"/></r> | 127 <xsl:param name="local" as="xs:boolean"/> |
128 <xsl:param name="row" as="xs:integer"/> | |
129 <xsl:param name="col" as="xs:integer"/> | |
130 <xsl:variable name="l" select="$tokens[$i]"/> | |
131 <xsl:variable name="r" select="$tokens[$i+2]"/> | |
132 <xsl:variable name="lv" select="e:rPart($l,$local,$row,$col)"/> | |
133 <xsl:variable name="rv" select="e:rPart($r,$local,$row,$col)"/> | |
134 <r> | |
135 <xsl:copy-of select="($lv,$rv)"/> | |
136 </r> | |
137 </xsl:function> | |
138 | |
139 <xsl:function name="e:rPart" as="element(*)"> | |
140 <xsl:param name="g" as="element(xf:group)"/> | |
141 <xsl:param name="local" as="xs:boolean"/> | |
142 <xsl:param name="row" as="xs:integer"/> | |
143 <xsl:param name="col" as="xs:integer"/> | |
144 <xsl:sequence select=" | |
145 if ($g/@nr=8) then e:single(string($g),$row,$col) | |
146 else if ($g/@nr=9) | |
147 then if ($local) | |
148 then let $tokens := e:tokenise(e:lookup(string($g)), | |
149 $row,$col) return | |
150 if (count($tokens)=1 and | |
151 $tokens[local-name()='s' or | |
152 (local-name()='e' and $tokens/e:s)]) | |
153 then $tokens | |
154 else e:badRP(string($g),$g/@nr,$local,$row,$col,$tokens) | |
155 else e:var(string($g)) | |
156 else e:badRP(string($g),$g/@nr,$local,$row,$col,())"/> | |
157 </xsl:function> | |
158 | |
159 <xsl:function name="e:var" as="element(e:v)"> | |
160 <xsl:param name="name" as="xs:string"/> | |
161 <v><xsl:value-of select="$name"/></v> | |
162 </xsl:function> | |
163 <xsl:function name="e:badRP" as="element(e:u)"> | |
164 <xsl:param name="s" as="xs:string"/> | |
165 <xsl:param name="t" as="xs:integer"/> | |
166 <xsl:param name="local" as="xs:boolean"/> | |
167 <xsl:param name="row" as="xs:integer"/> | |
168 <xsl:param name="col" as="xs:integer"/> | |
169 <xsl:param name="toks" as="element(*)*"/> | |
170 <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> | |
171 <u r="{concat(e:n2a($col),$row)}" s="{$s}" t="{$t}" local="{$local}"> | |
172 <xsl:value-of select="string-join($toks,',')"/> | |
173 </u> | |
131 </xsl:function> | 174 </xsl:function> |
132 | 175 |
133 <xsl:function name="e:external" as="element(e:e)"> | 176 <xsl:function name="e:external" as="element(e:e)"> |
134 <xsl:param name="source" as="element(xf:group)" required="yes"/> | |
135 <xsl:param name="ref" as="element(*)" required="yes"/> | 177 <xsl:param name="ref" as="element(*)" required="yes"/> |
136 <e s="{$source}"><xsl:sequence select="$ref"/></e> | 178 <e><xsl:sequence select="$ref"/></e> |
137 </xsl:function> | 179 </xsl:function> |
138 </xsl:stylesheet> | 180 </xsl:stylesheet> |