Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
nginx
GitHub Repository: nginx/nginx.org
Path: blob/main/tools/xslscript.pl
1 views
1
#!/usr/bin/perl
2
3
# (C) Maxim Dounin
4
# (C) Nginx, Inc.
5
6
# Convert from XSLScript to XSLT.
7
#
8
# Originally XSLScript was written by Paul Tchistopolskii. It is believed
9
# to be mostly identical to XSLT, but uses shorter syntax. Original
10
# implementation has major Java dependency, no longer supported and hard
11
# to find.
12
#
13
# This code doesn't pretend to be a full replacement, but rather an attempt
14
# to provide functionality needed for nginx documentation.
15
16
###############################################################################
17
18
use warnings;
19
use strict;
20
21
use Parse::RecDescent;
22
use Getopt::Long;
23
use Data::Dumper qw/Dumper/;
24
25
###############################################################################
26
27
my $dump = 0;
28
my $output;
29
30
GetOptions(
31
"output|o=s" => \$output,
32
"trace!" => \$::RD_TRACE,
33
"hint!" => \$::RD_HINT,
34
"dump!" => \$dump,
35
)
36
or die "oops\n";
37
38
###############################################################################
39
40
my $grammar = <<'EOF';
41
42
# XSLScript grammar, reconstructed
43
44
startrule : <skip:""> item(s) eofile
45
{ $return = $item{'item(s)'}; 1 }
46
47
item : "<!--" <commit> comment
48
| "!!" <commit> exclam_double
49
| "!{" <commit> exclam_xpath
50
| "!" name <commit> params
51
{ $return = [
52
"X:call-template", "name", $item{name}, [],
53
$item{params}
54
]; 1 }
55
| "<%" <commit> space instruction space "%>"
56
{ $return = $item{instruction}; 1 }
57
| "<" name attrs space ">" <commit> item(s?) "</" name ">"
58
{ $return = [ "tag", $item{name}, $item{attrs}, $item{'item(s?)'} ]; 1 }
59
| "<" <commit> name attrs space "/>"
60
{ $return = [ "tag", $item{name}, $item{attrs} ]; 1 }
61
| "X:variable" space <commit> xvariable
62
| "X:var" space <commit> xvariable
63
| "X:template" space <commit> xtemplate
64
| "X:if" space <commit> xif
65
| "X:param" space <commit> xparam
66
| "X:for-each" space <commit> xforeach
67
| "X:sort" space <commit> xsort
68
| "X:when" space <commit> xwhen
69
| "X:attribute" space <commit> xattribute
70
| "X:output" space <commit> xoutput
71
| "X:copy-of" space <commit> xcopyof
72
| instruction space <commit> attrs body
73
{ $return = [ $item{instruction}, $item{attrs}, $item{body} ]; 1 }
74
| space_notempty
75
| text
76
| <error>
77
78
# list of simple instructions
79
80
instruction : "X:stylesheet"
81
| "X:transform"
82
| "X:attribute-set"
83
| "X:element"
84
| "X:apply-templates"
85
| "X:choose"
86
| "X:otherwise"
87
| "X:value-of"
88
| "X:apply-imports"
89
| "X:number"
90
| "X:include"
91
| "X:import"
92
| "X:strip-space"
93
| "X:preserve-space"
94
| "X:copy"
95
| "X:text"
96
| "X:comment"
97
| "X:processing-instruction"
98
| "X:decimal-format"
99
| "X:namespace-alias"
100
| "X:key"
101
| "X:fallback"
102
| "X:message"
103
104
# comments, <!-- ... -->
105
# not sure if it's something to be interpreted specially
106
# likely an artifact of our dump process
107
108
comment : /((?!-->).)*/ms "-->"
109
{ $return = "<!--" . $item[1] . "-->"; 1 }
110
111
# special chars: ', ", {, }, \
112
# if used in text, they needs to be escaped with backslash
113
114
text : quoted | unreserved | "'" | "\"" | "{"
115
quoted : "\\" special
116
{ $return = $item{special}; 1; }
117
special : "'" | "\"" | "\\" | "{" | "}"
118
unreserved : /[^'"\\{}<\s]+\s*/ms
119
120
# whitespace
121
122
space : /\s*/ms
123
space_notempty : /\s+/ms
124
125
# shortcuts:
126
#
127
# !! for X:apply-templates
128
# !{xpath-expression} for X:value-of select="xpath-expression";
129
# !foo() for X:call-template name="foo"
130
131
# !root (path = { !{ substring($DIRNAME, 2) } })
132
# !root (path = "substring-after($path, '/')")
133
134
exclam_double : space value(?) params(?) attrs space ";"
135
{ $return = [
136
"X:apply-templates", "select", $item{'value(?)'}[0],
137
$item{attrs}, $item{'params(?)'}[0]
138
]; 1 }
139
140
exclam_xpath : xpath "}"
141
{ $return = [
142
"X:value-of", "select", $item{xpath}, []
143
]; 1 }
144
xpath : /("[^"]*"|'[^']*'|[^}'"])*/ms
145
146
# instruction attributes
147
# name="value"
148
149
attrs : attr(s?)
150
attr : space name space "=" space value
151
{ $return = $item{name} . "=" . $item{value}; }
152
name : /[a-z0-9_:-]+/i
153
value : /"[^"]*"/
154
155
# template parameters
156
# ( bar="init", baz={markup} )
157
158
params : space "(" param(s? /\s*,\s*/) ")" space
159
{ $return = $item[3]; 1 }
160
param : space name space "=" space value space
161
{ $return = [
162
"X:with-param",
163
"select", $item{value},
164
"name", $item{name},
165
[]
166
]; 1 }
167
| space name space "=" <commit> space "{" item(s) "}"
168
{ $return = [
169
"X:with-param", "name", $item{name}, [],
170
$item{'item(s)'}
171
]; 1 }
172
| space name
173
{ $return = [
174
"X:param", "name", $item{name}, []
175
]; 1 }
176
177
# instruction body
178
# ";" for empty body, "{ ... }" otherwise
179
180
body : space ";"
181
{ $return = ""; }
182
| space "{" <commit> item(s?) "}" (space ";")(?)
183
{ $return = $item{'item(s?)'}; 1 }
184
185
# special handling of some instructions
186
# X:if attribute is test=
187
188
xif : value body space "else" <commit> body
189
{ $return = [
190
"X:choose", [], [
191
[ "X:when", "test", $item[1], [], $item[2] ],
192
[ "X:otherwise", [], $item[6] ]
193
]
194
]; 1 }
195
| value attrs body
196
{ $return = [
197
"X:if", "test", $item{value}, $item{attrs}, $item{body},
198
]; 1 }
199
| attrs body
200
{ $return = [
201
"X:if", $item{attrs}, $item{body},
202
]; 1 }
203
| <error>
204
205
# X:template name(params) = "match" {
206
# X:template name( bar="init", baz={markup} ) = "match" mode="some" {
207
208
xtemplate : name(?) params(?) space
209
(space "=" space value)(?) attrs body
210
{ $return = [
211
"X:template",
212
"name", $item{'name(?)'}[0],
213
"match", $item[4][0],
214
$item{attrs},
215
[ ($item[2][0] ? @{$item[2][0]} : ()), @{$item{body}} ]
216
]; 1 }
217
218
# X:var LINK = "/article/@link";
219
# X:var year = { ... }
220
# semicolon is optional
221
222
xvariable : name space "=" space value attrs body
223
{ $return = [
224
"X:variable",
225
"select", $item{value},
226
"name", $item{name},
227
$item{attrs}, $item{body}
228
]; 1 }
229
| name space "=" space attrs body
230
{ $return = [
231
"X:variable",
232
"name", $item{name},
233
$item{attrs}, $item{body}
234
]; 1 }
235
| name space "=" space value
236
{ $return = [
237
"X:variable",
238
"select", $item{value},
239
"name", $item{name},
240
[]
241
]; 1 }
242
| name space "="
243
{ $return = [
244
"X:variable",
245
"name", $item{name},
246
[]
247
]; 1 }
248
| <error>
249
250
# X:param XML = "'../xml'";
251
# X:param YEAR;
252
253
xparam : name space "=" space value attrs body
254
{ $return = [
255
"X:param",
256
"select", $item{value},
257
"name", $item{name},
258
$item{attrs}, $item{body}
259
]; 1 }
260
| name attrs body
261
{ $return = [
262
"X:param", "name", $item{name},
263
$item{attrs}, $item{body}
264
]; 1 }
265
266
# X:for-each "section[@id and @name]" { ... }
267
# X:for-each "link", X:sort "@id" {
268
269
xforeach : value attrs body
270
{ $return = [
271
"X:for-each", "select", $item{value}, $item{attrs}, $item{body}
272
]; 1 }
273
| value attrs space
274
"," space "X:sort" <commit> space value attrs body
275
{ $return = [
276
"X:for-each", "select", $item[1], $item[2], [
277
[ "X:sort", "select", $item[9], $item[10] ],
278
@{$item{body}}
279
]
280
]; 1 }
281
282
# X:sort select
283
# X:sort "@id"
284
285
xsort : value attrs body
286
{ $return = [
287
"X:sort", "select", $item{value}, $item{attrs}, $item{body}
288
]; 1 }
289
290
# X:when "position() = 1" { ... }
291
292
xwhen : value attrs body
293
{ $return = [
294
"X:when", "test", $item{value}, $item{attrs}, $item{body}
295
]; 1 }
296
297
# X:attribute "href" { ... }
298
299
xattribute : value attrs body
300
{ $return = [
301
"X:attribute", "name", $item{value}, $item{attrs}, $item{body}
302
]; 1 }
303
304
# X:output
305
# semicolon is optional
306
307
xoutput : attrs body(?)
308
{ $return = [
309
"X:output", undef, undef, $item{attrs}, $item{body}
310
]; 1 }
311
312
# "X:copy-of"
313
# semicolon is optional
314
315
xcopyof : value attrs body(?)
316
{ $return = [
317
"X:copy-of", "select", $item{value}, $item{attrs}, $item{body}
318
]; 1 }
319
320
# eof
321
322
eofile : /^\Z/
323
324
EOF
325
326
###############################################################################
327
328
sub format_tree {
329
my ($tree, $indent) = @_;
330
my $s = '';
331
332
if (!defined $indent) {
333
$indent = 0;
334
$s .= '<?xml version="1.0" encoding="utf-8"?>' . "\n";
335
}
336
337
my $space = " " x $indent;
338
339
foreach my $el (@{$tree}) {
340
if (!defined $el) {
341
warn "Undefined element in output.\n";
342
$s .= $space . "(undef)" . "\n";
343
next;
344
}
345
346
if (not ref($el) && defined $el) {
347
if ($el =~ /^<!--(.*)-->$/s) {
348
my $comment = $1;
349
$comment =~ s/--/../sg;
350
$el = "<!--" . $comment . "-->";
351
}
352
353
$s .= $el;
354
next;
355
}
356
357
die if ref($el) ne 'ARRAY';
358
359
my $tag = $el->[0];
360
361
if ($tag eq 'tag') {
362
my (undef, $name, $attrs, $body) = @{$el};
363
364
$s .= "<" . join(" ", $name, @{$attrs});
365
if ($body) {
366
$s .= ">" . format_tree($body, $indent + 1)
367
. "</$name>";
368
} else {
369
$s .= "/>";
370
}
371
372
next;
373
}
374
375
if ($tag =~ m/^X:(.*)/) {
376
my $name = "xsl:" . $1;
377
my (undef, @a) = @{$el};
378
my @attrs;
379
380
while (@a) {
381
last if ref($a[0]) eq 'ARRAY';
382
my $name = shift @a;
383
my $value = shift @a;
384
next unless defined $value;
385
$value = '"' . $value . '"'
386
unless $value =~ /^"/;
387
push @attrs, "$name=$value";
388
}
389
390
if ($name eq "xsl:stylesheet") {
391
push @attrs, 'xmlns:xsl="http://www.w3.org/1999/XSL/Transform"';
392
push @attrs, 'version="1.0"';
393
}
394
395
my ($attrs, $body) = @a;
396
$attrs = [ @{$attrs}, @attrs ];
397
398
$s .= "<" . join(" ", $name, @{$attrs});
399
400
if ($body && scalar @{$body} > 0) {
401
$s .= ">" . format_tree($body, $indent + 1)
402
. "</$name>";
403
} else {
404
$s .= "/>";
405
}
406
407
next;
408
}
409
410
$s .= format_tree($el, $indent + 1);
411
}
412
413
return $s;
414
}
415
416
###############################################################################
417
418
my $parser = Parse::RecDescent->new($grammar)
419
or die "Failed to create parser.\n";
420
421
my $lines;
422
423
{
424
local $/;
425
$lines = <>;
426
}
427
428
my $tree = $parser->startrule($lines)
429
or die "Failed to parse $ARGV.\n";
430
my $formatted = format_tree($tree);
431
432
if (defined $output) {
433
open STDOUT, ">", $output
434
or die "Can't open $output: $!\n";
435
}
436
437
if ($dump) {
438
print Dumper($tree);
439
exit(0);
440
}
441
442
print $formatted;
443
444
###############################################################################
445
446