comparison xsls.pl @ 4:eb959dce273b

XSLScript: tree construction and printing. In its current form the script is able to process at least article.xsl, and produces a result with a minimal non-significant diff with article.xslt produced with original Java code.
author Maxim Dounin <mdounin@mdounin.ru>
date Fri, 21 Feb 2014 14:26:14 +0400
parents 0fa786e03744
children 2d6764d9980b
comparison
equal deleted inserted replaced
3:0fa786e03744 4:eb959dce273b
39 { $return = $item[1]; 1 } 39 { $return = $item[1]; 1 }
40 40
41 item : "<!--" <commit> comment 41 item : "<!--" <commit> comment
42 | "!!" <commit> exclam_double 42 | "!!" <commit> exclam_double
43 | "!{" <commit> exclam_xpath 43 | "!{" <commit> exclam_xpath
44 | "!" name <commit> exclam_name 44 | "!" name <commit> params
45 { $return = [
46 "X:call-template", "name", $item{name}, [],
47 $item{params}
48 ]; 1 }
45 | "<%" <commit> instruction "%>" 49 | "<%" <commit> instruction "%>"
46 { $return = $item{instruction}; 1 } 50 { $return = $item{instruction}; 1 }
47 | "<" name attrs ">" <commit> item(s?) "</" name ">" 51 | "<" name attrs ">" <commit> item(s?) "</" name ">"
48 { $return = ::format_tag($item{name}, $item{attrs}, $item[6]); 1 } 52 { $return = [ "tag", $item{name}, $item{attrs}, $item[6] ]; 1 }
49 | "<" <commit> name attrs "/" ">" 53 | "<" <commit> name attrs "/" ">"
50 { $return = ::format_tag($item{name}, $item{attrs}); 1 } 54 { $return = [ "tag", $item{name}, $item{attrs} ]; 1 }
51 | "X:variable" <commit> xvariable 55 | "X:variable" <commit> xvariable
52 | "X:var" <commit> xvariable 56 | "X:var" <commit> xvariable
53 | "X:template" <commit> xtemplate 57 | "X:template" <commit> xtemplate
54 | "X:if" <commit> xif 58 | "X:if" <commit> xif
55 | "X:param" <commit> xparam 59 | "X:param" <commit> xparam
58 | "X:when" <commit> xwhen 62 | "X:when" <commit> xwhen
59 | "X:attribute" <commit> xattribute 63 | "X:attribute" <commit> xattribute
60 | "X:output" <commit> xoutput 64 | "X:output" <commit> xoutput
61 | "X:copy-of" <commit> xcopyof 65 | "X:copy-of" <commit> xcopyof
62 | instruction <commit> attrs body 66 | instruction <commit> attrs body
63 { $return = ::format_instruction( 67 { $return = [ $item{instruction}, $item{attrs}, $item{body} ]; 1 }
64 $item{instruction}, $item{attrs}, $item{body});
65 1 }
66 | text 68 | text
67 | <error> 69 | <error>
68 70
69 # list of simple instructions 71 # list of simple instructions
70 72
116 118
117 # !root (path = { !{ substring($DIRNAME, 2) } }) 119 # !root (path = { !{ substring($DIRNAME, 2) } })
118 # !root (path = "substring-after($path, '/')") 120 # !root (path = "substring-after($path, '/')")
119 121
120 exclam_double : value(?) params(?) attrs ";" 122 exclam_double : value(?) params(?) attrs ";"
121 { $return = ::format_apply($item{value}, $item{params}, $item{attrs}); 1 } 123 { $return = [
122 124 "X:apply-templates", "select", $item{value}, $item{attrs},
123 exclam_xpath : xpath(s?) "}" 125 $item{params}
124 xpath : /[^}'"]+/ 126 ]; 1 }
125 | /"[^"]*"/ 127
126 | /'[^']*'/ 128 exclam_xpath : xpath "}"
127 129 { $return = [
128 exclam_name : params 130 "X:value-of", "select", $item{xpath}, []
131 ]; 1 }
132 xpath : /("[^"]*"|'[^']*'|[^}'"])*/ms
129 133
130 # instruction attributes 134 # instruction attributes
131 # name="value" 135 # name="value"
132 136
133 attrs : attr(s?) 137 attrs : attr(s?)
134 attr : name "=" value 138 attr : name "=" value
139 { $return = $item{name} . "=" . $item{value}; }
135 name : /[a-z0-9_:-]+/i 140 name : /[a-z0-9_:-]+/i
136 value : /"[^"]*"/ 141 value : /"[^"]*"/
137 142
138 # template parameters 143 # template parameters
139 # ( bar="init", baz={markup} ) 144 # ( bar="init", baz={markup} )
140 145
141 params : "(" param(s /,/) ")" 146 params : "(" param(s /,/) ")"
142 { $return = $item[2]; 1 } 147 { $return = $item[2]; 1 }
143 param : name "=" value 148 param : name "=" value
149 { $return = [
150 "X:with-param",
151 "select", $item{value},
152 "name", $item{name},
153 []
154 ]; 1 }
144 | name "=" <commit> "{" item(s) "}" 155 | name "=" <commit> "{" item(s) "}"
156 { $return = [
157 "X:with-param", "name", $item{name}, [],
158 $item{item}
159 ]; 1 }
145 | name 160 | name
161 { $return = [
162 "X:with-param", "name", $item{name}, []
163 ]; 1 }
146 164
147 # instruction body 165 # instruction body
148 # ";" for empty body, "{ ... }" otherwise 166 # ";" for empty body, "{ ... }" otherwise
149 167
150 body : ";" 168 body : ";"
151 { $return = ""; } 169 { $return = ""; }
152 | "{" <commit> item(s?) "}" 170 | "{" <commit> item(s?) "}"
153 { $return = join("\n", @{$item[3]}); 1 } 171 { $return = $item[3]; 1 }
154 172
155 # special handling of some instructions 173 # special handling of some instructions
156 # X:if attribute is test= 174 # X:if attribute is test=
157 175
158 xif : value(?) attrs body "else" <commit> body 176 xif : value body "else" <commit> body
159 | value(?) attrs body 177 { $return = [
178 "X:choose", [], [
179 [ "X:when", "test", $item[1], [], $item[2] ],
180 [ "X:otherwise", [], $item[5] ]
181 ]
182 ]; 1 }
183 | value attrs body
184 { $return = [
185 "X:if", "test", $item[1], $item{attrs}, $item{body},
186 ]; 1 }
187 | attrs body
188 { $return = [
189 "X:if", $item{attrs}, $item{body},
190 ]; 1 }
160 | <error> 191 | <error>
161 192
162 # X:template name(params) = "match" { 193 # X:template name(params) = "match" {
163 # X:template name( bar="init", baz={markup} ) = "match" mode="some" { 194 # X:template name( bar="init", baz={markup} ) = "match" mode="some" {
164 195
165 xtemplate : name(?) params(?) ( "=" value )(?) 196 xtemplate : name(?) params(?) ( "=" value )(?) attrs body
166 attrs body 197 { $return = [
198 "X:template", "name", $item[1][0], "match", $item[3][0],
199 $item{attrs},
200 [ ($item{params} ? @{$item{params}} : ()), @{$item{body}} ]
201 ]; 1 }
167 202
168 # X:var LINK = "/article/@link"; 203 # X:var LINK = "/article/@link";
169 # X:var year = { ... } 204 # X:var year = { ... }
170 # semicolon is optional 205 # semicolon is optional
171 206
172 xvariable : name "=" value(?) attrs body 207 xvariable : name "=" value attrs body
173 | name "=" value(?) 208 { $return = [
209 "X:variable",
210 "select", $item{value},
211 "name", $item{name},
212 $item{attrs}, $item{body}
213 ]; 1 }
214 | name "=" attrs body
215 { $return = [
216 "X:variable",
217 "name", $item{name},
218 $item{attrs}, $item{body}
219 ]; 1 }
220 | name "=" value
221 { $return = [
222 "X:variable",
223 "select", $item{value},
224 "name", $item{name},
225 []
226 ]; 1 }
227 | name "="
228 { $return = [
229 "X:variable",
230 "name", $item{name},
231 []
232 ]; 1 }
174 | <error> 233 | <error>
175 234
176 # X:param XML = "'../xml'"; 235 # X:param XML = "'../xml'";
177 # X:param YEAR; 236 # X:param YEAR;
178 237
179 xparam : name ("=" value)(?) attrs body 238 xparam : name "=" value attrs body
239 { $return = [
240 "X:param",
241 "select", $item{value},
242 "name", $item{name},
243 $item{attrs}, $item{body}
244 ]; 1 }
245 | name attrs body
246 { $return = [
247 "X:param", "name", $item{name},
248 $item{attrs}, $item{body}
249 ]; 1 }
180 250
181 # X:for-each "section[@id and @name]" { ... } 251 # X:for-each "section[@id and @name]" { ... }
182 # X:for-each "link", X:sort "@id" { 252 # X:for-each "link", X:sort "@id" {
183 253
184 xforeach : value attrs body 254 xforeach : value attrs body
185 { $return = ::format_instruction_value( 255 { $return = [
186 "X:for-each", "select", $item{value}, $item{attrs}, $item{body}); 256 "X:for-each", "select", $item{value}, $item{attrs}, $item{body}
187 1 } 257 ]; 1 }
188 | value attrs "," "X:sort" <commit> value attrs body 258 | value attrs "," "X:sort" <commit> value attrs body
259 { $return = [
260 "X:for-each", "select", $item[1], $item[2], [
261 [ "X:sort", "select", $item[6], $item[7] ],
262 @{$item{body}}
263 ]
264 ]; 1 }
189 265
190 # X:sort select 266 # X:sort select
191 # X:sort "@id" 267 # X:sort "@id"
192 268
193 xsort : value attrs body 269 xsort : value attrs body
194 { $return = ::format_instruction_value( 270 { $return = [
195 "X:sort", "select", $item{value}, $item{attrs}, $item{body}); 271 "X:sort", "select", $item{value}, $item{attrs}, $item{body}
196 1 } 272 ]; 1 }
197 273
198 # X:when "position() = 1" { ... } 274 # X:when "position() = 1" { ... }
199 275
200 xwhen : value attrs body 276 xwhen : value attrs body
201 { $return = ::format_instruction_value( 277 { $return = [
202 "X:when", "test", $item{value}, $item{attrs}, $item{body}); 278 "X:when", "test", $item{value}, $item{attrs}, $item{body}
203 1 } 279 ]; 1 }
204 280
205 # X:attribute "href" { ... } 281 # X:attribute "href" { ... }
206 282
207 xattribute : value attrs body 283 xattribute : value attrs body
208 { $return = ::format_instruction_value( 284 { $return = [
209 "X:attribute", "name", $item{value}, $item{attrs}, $item{body}); 285 "X:attribute", "name", $item{value}, $item{attrs}, $item{body}
210 1 } 286 ]; 1 }
211 287
212 # X:output 288 # X:output
213 # semicolon is optional 289 # semicolon is optional
214 290
215 xoutput : attrs body(?) 291 xoutput : attrs body(?)
216 { $return = ::format_instruction( 292 { $return = [
217 "X:output", $item{attrs}, $item{body}); 293 "X:output", undef, undef, $item{attrs}, $item{body}
218 1 } 294 ]; 1 }
219 295
220 # "X:copy-of" 296 # "X:copy-of"
221 # semicolon is optional 297 # semicolon is optional
222 298
223 xcopyof : value attrs body(?) 299 xcopyof : value attrs body(?)
224 { $return = ::format_instruction_value( 300 { $return = [
225 "X:copy-of", "select", $item{value}, $item{attrs}, $item{body}); 301 "X:copy-of", "select", $item{value}, $item{attrs}, $item{body}
226 1 } 302 ]; 1 }
227 303
228 # eof 304 # eof
229 305
230 eofile : /^\Z/ 306 eofile : /^\Z/
231 307
232 EOF 308 EOF
233 309
234 ############################################################################### 310 ###############################################################################
235 311
236 # helper formatting functions, used by grammar 312 sub format_tree {
237 313 my ($tree, $indent) = @_;
238 sub format_instruction { 314 my $s = '';
239 my ($instruction, $attrs, $body) = @_; 315
240 my $s = "<"; 316 $indent ||= 0;
241 317 my $space = " " x $indent;
242 $instruction =~ s/^X:/xsl:/; 318
243 319 foreach my $el (@{$tree}) {
244 $s .= join(" ", $instruction, @{$attrs}); 320 if (!defined $el) {
245 321 $s .= $space . "(undef)" . "\n";
246 if ($body) { 322 next;
247 $s .= ">\n" . $body . "\n</" . $instruction . ">\n"; 323 }
248 } else { 324
249 $s .= "/>\n"; 325 if (not ref($el) && defined $el) {
326 $s .= $space . $el . "\n";
327 next;
328 }
329
330 die if ref($el) ne 'ARRAY';
331
332 my $tag = $el->[0];
333
334 if ($tag eq 'tag') {
335 my (undef, $name, $attrs, $body) = @{$el};
336
337 $s .= $space . "<" . join(" ", $name, @{$attrs});
338 if ($body) {
339 $s .= ">\n";
340 $s .= format_tree($body, $indent + 1);
341 $s .= $space . "</$name>\n";
342 } else {
343 $s .= "/>\n";
344 }
345
346 next;
347 }
348
349 if ($tag =~ m/^X:(.*)/) {
350 my $name = "xsl:" . $1;
351 my (undef, @a) = @{$el};
352 my @attrs;
353
354 while (@a) {
355 last if ref($a[0]) eq 'ARRAY';
356 my $name = shift @a;
357 my $value = shift @a;
358 next unless defined $value;
359 $value = '"' . $value . '"'
360 unless $value =~ /^"/;
361 push @attrs, "$name=$value";
362 }
363
364 if ($name eq "xsl:stylesheet") {
365 $s .= '<?xml version="1.0" encoding="utf-8"?>' . "\n";
366 push @attrs, 'xmlns:xsl="http://www.w3.org/1999/XSL/Transform"';
367 push @attrs, 'version="1.0"';
368 }
369
370 my ($attrs, $body) = @a;
371 $attrs = [ @{$attrs}, @attrs ];
372
373 $s .= $space . "<" . join(" ", $name, @{$attrs});
374
375 if ($body) {
376 $s .= ">\n";
377 $s .= format_tree($body, $indent + 1);
378 $s .= $space . "</$name>\n";
379 } else {
380 $s .= "/>\n\n";
381 }
382
383 next;
384 }
385
386 $s .= format_tree($el, $indent + 1);
250 } 387 }
251 388
252 return $s; 389 return $s;
253 }
254
255 sub format_instruction_value {
256 my ($instruction, $name, $value, $attrs, $body) = @_;
257 my $s = "<";
258
259 if ($value) {
260 push(@{$attrs}, "$name=$value");
261 }
262
263 return format_instruction($instruction, $attrs, $body);
264 }
265
266 sub format_tag {
267 my ($tag, $attrs, $body) = @_;
268 my $s = "\n<";
269
270 $s .= join(" ", $tag, @{$attrs});
271
272 $body = join("\n", @{$body});
273
274 if ($body) {
275 $s .= ">" . $body . "\n</" . $tag . ">\n";
276 } else {
277 $s .= "/>\n";
278 }
279
280 return $s;
281 }
282
283 sub format_apply {
284 my ($select, $params, $attrs) = @_;
285 my $s = "\n<";
286 my $tag = "xsl:apply-templates";
287
288 if ($select) {
289 unshift "select=$select", @{$attrs};
290 }
291
292 $s .= join(" ", $tag, @{$attrs});
293
294 if ($params) {
295 $params = join("\n", @{$params});
296 $s .= ">\n" . $params . "\n</" . $tag . ">\n";
297 } else {
298 $s .= "/>\n";
299 }
300 } 390 }
301 391
302 ############################################################################### 392 ###############################################################################
303 393
304 my $parser = Parse::RecDescent->new($grammar) 394 my $parser = Parse::RecDescent->new($grammar)
315 or die "Failed to parse $ARGV.\n"; 405 or die "Failed to parse $ARGV.\n";
316 406
317 #print Dumper($tree); 407 #print Dumper($tree);
318 #print join("\n", @{$tree}); 408 #print join("\n", @{$tree});
319 409
320 ############################################################################### 410 print format_tree($tree);
321 ############################################################################### 411
412 ###############################################################################