/[pub]/suikawiki/script/bin/mkplugin2.pl
Suika

Contents of /suikawiki/script/bin/mkplugin2.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.18 - (show annotations) (download)
Thu Jun 3 06:38:48 2004 UTC (20 years, 5 months ago) by wakaba
Branch: MAIN
CVS Tags: release-3-0-0
Changes since 1.17: +19 -7 lines
File MIME type: text/plain
Static output of stylesheet implemented; Use of simple HTML serializer (new to manakai) if text/html output

1 #!/usr/bin/perl
2 use strict;
3 our $VERSION = do{my @r=(q$Revision: 1.17 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 require Message::Markup::SuikaWikiConfig20::Parser;
5
6 {
7 my $src = '';
8 my $srcfile = shift;
9 open SRC, $srcfile or die "$0: $!"; {
10 local $/ = undef;
11 $src = <SRC>;
12 } close SRC;
13
14 sub literal ($) {
15 my $s = shift;
16 if (ref ($s) eq 'ARRAY') {
17 q<[> . list (@$s) . q<]>;
18 } elsif (ref ($s) eq 'HASH') {
19 q<{> . hash (%$s) . q<}>;
20 } elsif (ref ($s) eq 'bare') {
21 $$s;
22 } else {
23 $s =~ s/([#\\])/\\$1/g;
24 q<q#> . $s . q<#>;
25 }
26 }
27 sub list (@) {
28 join ', ', map {literal $_} @_;
29 }
30 sub hash (%) {
31 my $i = 0;
32 list map {($i++ % 2) ? $_ : do {my $s = $_; $s =~ s/(?<=.)-/_/; $s}} @_;
33 }
34 sub n11n ($) {
35 my $s = shift;
36 $s =~ s/\s+/ /g;
37 $s;
38 }
39 sub m13ed_val_list ($$) {
40 my ($src, $key) = @_;
41 my @r;
42 for (@{$src->child_nodes}) {
43 if ($_->local_name eq $key) {
44 push @r, [scalar $_->inner_text,
45 scalar $_->get_attribute ('lang', make_new_node => 1)
46 ->inner_text,
47 scalar $_->get_attribute ('script', make_new_node => 1)
48 ->inner_text];
49 }
50 }
51 list @r;
52 }
53 sub barecode ($) {
54 bless \$_[0], 'bare';
55 }
56 sub code ($$) {
57 my ($Info, $code) = @_;
58 for (keys %{$Info->{const}}) {
59 $code =~ s/\$$_\b/$Info->{const}->{$_}/ge;
60 }
61 $code =~ s/__FUNCPACK__/$Info->{module_name}/g;
62 $code =~ s{<Q:([^:]+):([^>]+)>}{
63 if ($Info->{Namespace}->{$1}) {
64 literal $Info->{Namespace}->{$1}.$2;
65 } else {
66 warn qq(Namespace prefix "$1" not defined);
67 literal $2;
68 }
69 }ge;
70
71 $Info->{-message_error_used} = 1 if $code =~ /\buse\s+Message::Util::Error\b/;
72 if (not $Info->{-message_error_used} and
73 ($code =~ /\btry\s*\{/ or $code =~ /\bcatch\s*\{/)) {
74 warn "Message::Util::Error MUST be 'use'd before 'try'...'catch' block used";
75 }
76 $code;
77 }
78 sub change_package ($$) {
79 my ($Info, $pack) = @_;
80 unless ($Info->{current_package} eq $pack) {
81 $Info->{current_package} = $pack;
82 return qq{package $pack;\n\n};
83 } else {
84 return '';
85 }
86 }
87 sub quoted_string ($) {
88 my $s = shift;
89 $s =~ s/([\\"])/\\$1/g;
90 '"'.$s.'"';
91 }
92 sub line ($;%) {
93 my ($Info, %opt) = @_;
94
95 unless ($opt{file}) {
96 if ($opt{reset}) {
97 $opt{file} = sprintf '(WikiPlugin module %s, chunk %d)',
98 $Info->{Name},
99 ++$Info->{chunk_count};
100 } elsif ($opt{realfile}) {
101 $opt{file} = sprintf '(WikiPlugin module %s, chunk from %s)',
102 $Info->{Name},
103 $opt{realfile};
104 } else {
105 $opt{file} = sprintf '(WikiPlugin module source %s, block %s)',
106 $Info->{source_file},
107 $opt{node_path};
108 }
109 }
110
111 $opt{file} =~ s/"/''/g;
112 sprintf '%s#line %d "%s"%s', "\n", $opt{line_no} || 1, $opt{file}, "\n";
113 }
114 sub literal_or_code ($$) {
115 my ($Info, $s) = @_;
116 substr ($s, 0, 1) ne '{' ? literal ($s)
117 : code ($Info, substr ($s, 1, length ($s) - 2));
118 }
119
120 my $parser = Message::Markup::SuikaWikiConfig20::Parser->new;
121 my $plugins = $parser->parse_text ($src);
122 my $meta = $plugins->get_attribute ('Plugin')
123 or die "$0: Required 'Plugin' section not found";
124 my %Info = (provide => {},
125 Name => n11n $meta->get_attribute ('Name')->value);
126 $Info{source_file} = $srcfile;
127 $Info{name_literal} = literal $Info{Name};
128 my @date = gmtime;
129 $Info{LastModified} = sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
130 $date[5] + 1900, $date[4] + 1, @date[3,2,1,0];
131 $Info{Version} = sprintf '%04d.%02d%02d.%02d%02d',
132 $date[5] + 1900, $date[4] + 1, @date[3,2,1];
133 $Info{InterfaceVersion} = '2.9.1';
134 $Info{mkpluginVersion} = '2.'.$VERSION;
135 $Info{module_name} = q#SuikaWiki::Plugin::plugin#;
136 $Info{module_name} = random_module_name (\%Info, $Info{Name});
137
138 print <<EOH;
139 use strict;
140 @{[change_package \%Info, 'SuikaWiki::Plugin::Registry']}
141 our \%Info;
142 \$Info{$Info{name_literal}}->{Name} = $Info{name_literal};
143 EOH
144 for (qw/Version InterfaceVersion mkpluginVersion module_name/) {
145 print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = @{[literal $Info{$_}]};\n};
146 }
147 for (qw/LastModified Date.RCS/) {
148 $Info{$_} = n11n $meta->get_attribute ($_,make_new_node=>1)->value;
149 next unless length $Info{$_};
150 print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = } . literal $Info{$_};
151 print ";\n";
152 }
153 for (qw/RequiredPlugin RequiredModule/) {
154 $Info{$_} = $meta->get_attribute ($_);
155 next unless ref $Info{$_}; $Info{$_} = $Info{$_}->value;
156 print qq{\$Info{$Info{name_literal}}->{$_} = [};
157 print join ', ', map {literal $_} @{$Info{$_}};
158 print "];\n";
159 }
160 for (qw/Description License RelatedWikiPage RelatedURI/) {
161 my $r = m13ed_val_list $meta, $_;
162 next unless $r;
163 print qq{\$Info{$Info{name_literal}}->{$_} = [$r];\n};
164 }
165
166 print qq{\$Info{$Info{name_literal}}->{Author} = [} .( list map {
167 [
168 [ barecode m13ed_val_list ($_, 'Name') ],
169 [ $_->get_attribute ('Mail', make_new_node => 1)->value ],
170 [ $_->get_attribute ('URI', make_new_node => 1)->value ],
171 ]
172 } grep { $_->local_name eq 'Author' } @{$meta->child_nodes}
173 ). qq{];\n};
174
175 for (@{$meta->get_attribute ('Namespace', make_new_node => 1)->child_nodes}) {
176 $Info{Namespace}->{$_->local_name} = $_->value;
177 }
178
179 my $use = $meta->get_attribute ('Use');
180 if (ref $use) {
181 print change_package \%Info, $Info{module_name};
182 print line \%Info, node_path => 'Plugin/Use';
183 print code \%Info, $use->inner_text;
184 print line \%Info, reset => 1;
185 }
186
187 for (@{$plugins->child_nodes}) {
188 if ($_->local_name eq 'FormattingRule') {
189 print "\n", make_rule ($_, \%Info);
190 } elsif ($_->local_name eq 'ViewDefinition') {
191 print "\n", make_viewdef ($_, \%Info);
192 } elsif ($_->local_name eq 'ViewFragment') {
193 print "\n", make_viewfragment ($_, \%Info);
194 } elsif ($_->local_name eq 'Function') {
195 print "\n", make_function ($_, \%Info);
196 } elsif ($_->local_name eq 'Resource') {
197 print "\n", make_resdef ($_, \%Info);
198 } elsif ($_->local_name eq 'PluginConst') {
199 register_plugin_const ($_, \%Info);
200 } elsif ($_->local_name eq 'Format') {
201 print "\n", make_format ($_, \%Info);
202 } elsif ($_->local_name eq 'FormattingRuleAlias') {
203 print "\n", make_rule_alias ($_, \%Info);
204 # Parameter
205 # PluginCategory
206 }
207 }
208
209 print change_package \%Info, q(SuikaWiki::Plugin::Registry);
210 print qq{\$Info{$Info{name_literal}}->{provide} = } . literal $Info{provide};
211 print qq{;\n};
212
213 print "\n1;\n";
214 exit;
215 }
216
217 sub make_format ($$) {
218 my ($src, $Info) = @_;
219 my $module_name = 'SuikaWiki::Format::Definition::'.$src->get_attribute_value ('ModuleName');
220 my $r = change_package $Info, $module_name;
221 local $Info->{-message_error_used} = 0;
222 $r .= qq{our \@ISA;\n};
223 if (my $isa = $src->get_attribute_value ('Inherit')) {
224 for (@$isa) {
225 $r .= qq{push \@ISA, @{[literal 'SuikaWiki::Format::Definition::'.$_]};\n};
226 }
227 } else {
228 $r .= qq{push \@ISA, 'SuikaWiki::Format::Definition::template';\n};
229 }
230 if (my $name = $src->get_attribute_value ('Name')) {
231 $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'MAGIC:'.$name.'/'.$src->get_attribute_value ('Version', default => '').'##']}} = '$module_name';\n};
232 }
233 if (my $type = $src->get_attribute_value ('Type')) {
234 $type .= join '', map {
235 ';'. $_->local_name .'='. quoted_string $_->inner_text
236 } sort {
237 $a->local_name cmp $b->local_name
238 } @{$src->get_attribute ('Type')->child_nodes};
239 $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'IMT:'.$type.'##']}} = '$module_name';\n};
240 }
241
242 my $convert = line $Info, line_no => __LINE__ + 2, realfile => __FILE__;
243 $convert .= <<'EOH';
244 our $Converter;
245 sub convert ($$;%) {
246 my ($self, $source, %opt) = @_;
247 my $t = SuikaWiki::Format::Definition->serialize_media_type (%opt);
248 my $converter = $Converter->{$t->{_}};
249 return ($converter->{$opt{return_type} or 'Main'} or
250 CORE::die "Buggy implementation: $t->{_}/@{[$opt{return_type} or 'Main']} not defined")
251 ->($self, $source, \%opt)
252 if $converter;
253 $self->SUPER::convert ($source, %opt);
254 }
255 EOH
256
257 for (@{$src->child_nodes}) {
258 if ($_->local_name eq 'Converter') {
259 if ($convert) {
260 $r .= $convert;
261 $r .= line $Info, reset => 1;
262 undef $convert;
263 }
264 $r .= make_format_converter ($_, $Info);
265 } elsif ($_->local_name eq 'WikiForm') {
266 $r .= q(sub wikiform {)."\n".q(my ($self, $source, %opt) = @_;);
267 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/WikiForm);
268 $r .= code $Info, $_->get_attribute_value ('Main');
269 $r .= line $Info, reset => 1;
270 $r .= qq(}\n);
271 } elsif ($_->local_name eq 'HeadSummary') {
272 $r .= q(sub headsummary {)."\n".q(my ($self, $source, %opt) = @_;);
273 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/HeadSummary);
274 $r .= code $Info, $_->get_attribute_value ('Main');
275 $r .= line $Info, reset => 1;
276 $r .= qq(}\n);
277 } elsif ($_->local_name eq 'NextIndex') {
278 my $name = $_->get_attribute_value ('Name', default => '');
279 $r .= q(sub next_index_for_).$name
280 . q( {)."\n".q(my ($self, $source, %opt) = @_;)
281 . line $Info, node_path => qq(Format[module-name()=$module_name]/NextIndex[Name=$name]);
282 $r .= code $Info, $_->get_attribute_value ('Main');
283 $r .= line $Info, reset => 1;
284 $r .= qq(}\n);
285 } elsif ({qw/content_written 1 content_removed 1 content_type_changed_from 1/}
286 ->{my $node_name = $_->local_name}) {
287 $r .= q(sub ).$node_name
288 . q( {)."\n".q(my ($self, %opt) = @_;)
289 . line $Info, node_path => qq(Format[module-name()=$module_name]/$node_name]);
290 $r .= code $Info, $_->get_attribute_value ('Main');
291 $r .= line $Info, reset => 1;
292 $r .= qq(}\n);
293 } elsif ($_->local_name eq 'Use') {
294 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/Use);
295 $r .= code $Info, $_->inner_text;
296 }
297 }
298 $r;
299 }
300
301 sub make_format_converter ($$) {
302 my ($src, $Info) = @_;
303 my %def;
304 for (qw/Type Name Version TypeURIReference IsFragment IsPlaceholder/) {
305 $def{$_} = $src->get_attribute_value ($_);
306 delete $def{$_} unless defined $def{$_};
307 }
308 $def{Type_param} = {map {$_->local_name => $_->value}
309 @{$src->get_attribute ('Type', make_new_node => 1)
310 ->child_nodes}};
311 my $type = serialize_media_type ($Info,
312 Type => $def{Type},
313 Type_param => $def{Type_param},
314 Name => $def{Name},
315 Version => $def{Version},
316 URIReference => $def{TypeURIReference},
317 IsFragment => $def{IsFragment},
318 IsPlaceholder => $def{IsPlaceholder});
319 $def{serialized_type} = $type->{_};
320
321 for (qw/Main ToString ToOctetStream/) {
322 my $def = $src->get_attribute_value ($_);
323 next unless $def;
324 $def{$_} = line ($Info, node_path => '//Converter/'.$_)
325 . $def
326 . line ($Info, reset => 1);
327 if ($def{$_} =~ /\$r\b/) {
328 $def{$_} = 'my $r;'."\n".$def{$_}."\n".'$r';
329 }
330 $def{$_} = barecode code $Info,
331 'sub {my ($self, $source, $opt) = @_;'
332 . $def{$_} . '}';
333 }
334
335 my $r = list %def;
336 if ($type->{Type}) {
337 $r = qq{\$Converter->{@{[literal $type->{Type}]}} = {$r};\n};
338 $r .= qq{\$Converter->{@{[literal $type->{Magic}]}} = \$Converter->{@{[literal $type->{Type}]}};\n}
339 if $type->{Magic};
340 $r .= qq{\$Converter->{@{[literal $type->{URIReference}]}} = \$Converter->{@{[literal $type->{Type}]}};\n}
341 if $type->{URIReference};
342 } elsif ($type->{Magic}) {
343 $r = qq{\$Converter->{@{[literal $type->{Magic}]}} = {$r};\n};
344 $r .= qq{\$Converter->{@{[literal $type->{URIReference}]}} = \$Converter->{@{[literal $type->{Magic}]}};\n}
345 if $type->{URIReference};
346 } elsif ($type->{URIReference}) {
347 $r = qq{\$Converter->{@{[literal $type->{URIReference}]}} = {$r};\n};
348 } else {
349 $r = 'BEGIN { die "Invalid Syntax of Converter: Type or Name or TypeURIReference property required" }';
350 }
351 $r;
352 }
353
354 sub serialize_media_type ($%) {
355 my ($Info, %opt) = @_;
356 my %return;
357 if ($opt{Type}) {
358 $return{Type} = 'IMT:'.$opt{Type};
359 if ($opt{Type_param}) {
360 $return{Type} .= join '', map {my $s;
361 ';'. $_ .'="'
362 . (($s = $opt{Type_param}->{$_}) =~ s/([\\"])/\\$1/g, $s)
363 . '"'
364 } sort {
365 $a cmp $b
366 } keys %{$opt{Type_param}};
367 }
368 }
369 if ($opt{Magic}) {
370 $return{Magic} = 'MAGIC:'.$opt{Magic};
371 } elsif ($opt{Name}) {
372 $return{Name} = 'MAGIC:'.$opt{Name}.'/*';
373 $return{Magic} = 'MAGIC:'.$opt{Name}.'/'.$opt{Version} if $opt{Version};
374 }
375 if ($opt{URIReference}) {
376 $return{URIReference} = $opt{URIReference};
377 }
378 my $flag = '##';
379 $flag .= 'f' if $opt{IsFragment};
380 $flag .= 'p' if $opt{IsPlaceholder};
381 for (qw/URIReference Type Magic Name/) {
382 $return{$_} .= $flag if $return{$_};
383 }
384 $return{_} = $return{URIReference} || $return{Type}
385 || $return{Magic} || $return{Name};
386 \%return;
387 }
388
389
390 sub make_function ($$) {
391 my ($src, $Info) = @_;
392 ## TODO: support of ARGV property
393 my $name;
394 my $r = <<EOH;
395 @{[change_package $Info, $Info->{module_name}]}
396 sub @{[$name = $src->get_attribute_value ('Name')]} {
397 @{[line $Info, node_path => "Function[Name='$name']/Main"]}@{[
398 code $Info, $src->get_attribute_value ('Main')
399 ]}
400 }
401 @{[line $Info, reset => 1]}
402 EOH
403 }
404
405 sub register_plugin_const ($$) {
406 my ($src, $Info) = @_;
407 for (@{$src->child_nodes}) {
408 $Info->{const}->{$_->local_name} = literal_or_code $Info, $_->value;
409 }
410 }
411
412 sub make_resdef ($$) {
413 my ($src, $Info) = @_;
414 my $r = change_package $Info, 'SuikaWiki::Plugin::Resource';
415 local $Info->{-message_error_used} = 0;
416 $r .= qq{our \$BaseResource;\n};
417 for (@{$src->child_nodes}) {
418 if ($_->node_type eq '#element') {
419 my $lang = literal ($_->get_attribute_value ('lang') || 'und');
420 my $script = literal $_->get_attribute_value ('script');
421 my $name = literal $_->local_name;
422 my $val = literal n11n $_->value;
423 $r .= qq{\$BaseResource->{$lang}->{$script}->{$name} = $val;\n};
424 }
425 }
426 $r;
427 }
428
429 sub make_viewfragment ($$) {
430 my ($src, $Info) = @_;
431 my $r = '';
432 my $body = <<EOH;
433 {
434 Main => @{[literal $src->get_attribute_value ('Formatting')]},
435 Order => @{[0+$src->get_attribute_value ('Order')]},
436 Description => [@{[m13ed_val_list $src, 'Description']}],
437 };
438 EOH
439 ## Recommended format
440 my $name = $src->get_attribute_value ('Template');
441 if (ref ($name) and @$name > 1) {
442 $r .= qq({my \$def = $body;\n);
443 for (@$name) {
444 my $name = $_; $name =~ tr/-/_/;
445 $r .= qq(push \@{\$SuikaWiki::View::Implementation::TemplateFragment{@{[literal $name]}}}, \$def;\n);
446 push @{$Info->{provide}->{viewfragment}}, {Name => $name};
447 }
448 $r .= qq(}\n);
449 } else { ## Obsoleted format
450 $name = ref $name ? $name->[0] : $src->get_attribute_value ('Name');
451 $name =~ tr/-/_/;
452 $r .= qq(push \@{\$SuikaWiki::View::Implementation::TemplateFragment{@{[literal $name]}}}, $body);
453 push @{$Info->{provide}->{viewfragment}}, {Name => $name};
454 }
455 $r;
456 }
457
458 sub make_viewdef ($$) {
459 my ($src, $Info) = @_;
460 my $ViewProp = {};
461 my $r = '';
462 $ViewProp->{Name} = n11n $src->get_attribute_value ('Mode');
463 $ViewProp->{Name} =~ s/(?<=.)-/_/g;
464 $ViewProp->{pack_name} = random_module_name ($Info, $ViewProp->{Name});
465
466 $ViewProp->{condition_stringified} = hash
467 mode => $ViewProp->{Name},
468 map {($_->local_name => $_->value)}
469 @{$src->get_attribute ('Condition',make_new_node=>1)->child_nodes};
470
471 $r .= <<EOH;
472 push \@SuikaWiki::View::Implementation::CommonViewDefs, {
473 condition => {$ViewProp->{condition_stringified}},
474 object_class => q#$ViewProp->{pack_name}#,
475 };
476 @{[change_package $Info, $ViewProp->{pack_name}]}
477 our \@ISA = q#SuikaWiki::View::template#;
478 EOH
479 local $Info->{-message_error_used} = 0;
480 my $use = $src->get_attribute ('Use');
481 if (ref $use) {
482 $r .= line $Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/Use";
483 $r .= code $Info, $use->inner_text;
484 $r .= "\n\n";
485 }
486
487 for (@{$src->child_nodes}) {
488 if ($_->local_name eq 'template') {
489 $r .= make_view_template_method ($_, $Info, $ViewProp);
490 } elsif ($_->local_name eq 'method') {
491 my $method_name = $_->get_attribute_value ('Name');
492 $r .= ({
493 main => q(sub main ($$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
494 main_pre => q(sub main_pre ($$$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
495 main_post => q(sub main_post ($$$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
496 }->{$method_name}
497 ||qq(sub @{[$method_name]} {\n))
498 . line ($Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/method[Name='$method_name']")
499 . code ($Info, $_->value)
500 . qq(}\n)
501 . line ($Info, reset => 1);
502 }
503 }
504 my $prop = {Name => $ViewProp->{Name},
505 Description => barecode m13ed_val_list $_, 'Description'};
506 push @{$Info->{provide}->{viewdef}}, $prop;
507 $r;
508 }
509
510 sub make_view_template_method ($$) {
511 my ($src, $Info, $ViewProp) = @_;
512 my $media_type = $src->get_attribute_value
513 ('media-type',
514 default => q<application/octet-stream>);
515 my $r = <<EOH;
516
517 sub main (\$\$\$) {
518 my (\$self, \$opt, \$opt2) = \@_;
519 require SuikaWiki::Output::HTTP;
520 \$opt2->{output} = SuikaWiki::Output::HTTP->new
521 (wiki => \$self->{view}->{wiki},
522 view => \$self->{view}, viewobj => \$self);
523 for (\@{\$self->{view}->{wiki}->{var}->{client}->{used_for_negotiate}},
524 'Accept-Language') {
525 \$opt2->{output}->add_negotiate_header_field (\$_);
526 }
527
528 \$opt2->{template} = @{[literal $src->get_attribute ('body', make_new_node => 1)->inner_text]};
529 \$opt2->{o} = bless {
530 ## SuikaWiki 3 WikiPlugin interface
531 wiki => \$self->{view}->{wiki},
532 plugin => \$self->{view}->{wiki}->{plugin},
533 var => {},
534 }, 'SuikaWiki::Plugin';
535 @{[do{my $x=$src->get_attribute('http-status-code',make_new_node=>1)->inner_text;
536 $x?q{$opt2->{output}->{status_code} = }.(0 + $x).q{;}:q{}}]}
537 @{[do{my $x=$src->get_attribute('http-status-phrase',make_new_node=>1)->inner_text;
538 $x?q{$opt2->{output}->{status_phrase} = }.literal($x).q{;}:q{}}]}
539 \$opt2->{output}->{entity}->{media_type} = @{[literal $media_type]};
540
541 @{[($src->get_attribute ('use-media-type-charset',make_new_node=>1)
542 ->inner_text || 0) ?
543 q{$opt2->{output}->{entity}->{charset} = $self->{view}->{wiki}->{config}->{charset}->{output};}:
544 q{}]}
545 @{[do{my $x = $src->get_attribute ('expires',make_new_node=>1)->inner_text;
546 if ($x =~ /%%(\w+)%%/) {
547 qq{\$opt2->{output}->set_expires (%{\$self->{view}->{wiki}->{config}->{entity}->{expires}->{@{[literal $1]}}});};
548 } else {
549 qq{\$opt2->{output}->set_expires (delta => @{[0 + $x]});};
550 }
551 }]}
552 \$opt2->{output}->{entity}->{body_is_octet_stream} = @{[
553 $src->get_attribute ('body')->get_attribute_value ('is-octet-stream', default_value => 0)
554 or 0
555 ]};
556
557 \$self->{view}->{wiki}->init_db;
558 \$self->main_pre (\$opt, \$opt2);
559
560 @{[$media_type eq 'text/html' ? q{require Message::Markup::XML::Serialize::HTML;} : '']}
561 use Message::Util::Error;
562 try {
563 \$opt2->{output}->{entity}->{body}
564 = @{[$media_type eq 'text/html' ? q{Message::Markup::XML::Serialize::HTML::html_simple} : '']}
565 (SuikaWiki::Plugin->formatter ('view')
566 ->replace (\$opt2->{template}, param => \$opt2->{o}));
567 } \$self->{view}->{wiki}->{config}->{catch}->{ @{[
568 $ViewProp->{Name} eq '-error' ? 'formatter_view_error'
569 : 'formatter_view' ]} };
570 \$opt2->{output}->output (output => 'http-cgi');
571
572 \$self->main_post (\$opt, \$opt2);
573 }
574 EOH
575 }
576
577 sub make_rule ($$) {
578 my ($src, $Info) = @_;
579 my $type = $src->get_attribute ('Category', make_new_node => 1)->value || [];
580 my $name = $src->get_attribute ('Name', make_new_node => 1)->value;
581 $name =~ s/(?<=.)-/_/g;
582
583 my $reg_block;
584 $reg_block = qr/[^{}]*(?>[^{}]+|{(??{$reg_block})})*/;
585 my %code;
586 for my $codename ([qw/Formatting main/], [qw/After after/],
587 [qw/Before before/], [qw/Pre pre/], [qw/Post post/],
588 [qw/Attribute attr/]) {
589 my $main = code $Info, $src->get_attribute_value ($codename->[0]);
590 next unless $main;
591 $main = line ($Info, node_path =>
592 "FormattingRule[name()='@{[list $type]}/$name']/".$codename->[0])
593 . $main;
594
595 if ( $main =~ /\$f\b/
596 or $main =~ /\$rule_name\b/
597 or $main =~ /\$[opr]\b/
598 or $main =~ /[%\$]opt\b/
599 or $main =~ /\$param_(?:name|value)\n/) {
600 if ($codename->[0] ne 'Attribute') {
601 $main = q{my ($f, $rule_name, $p, $o, %opt) = @_;}."\n".$main;
602 } else {
603 $main = q{my ($f, $rule_name, $p, $o, $param_name => $param_value, %opt) = @_;}."\n".$main;
604 }
605 }
606 if ($main =~ /\$r\b/) {
607 warn qq(Rule @{[list $type]}/$name: Use of \$r is deprecated);
608 $main = q{my $r = '';} . "\n" . $main . "\n"
609 . q{$p->{-parent}->append_node ($r, node_or_text => 1);};
610 }
611 $main =~ s{__ATTR(TEXT|NODE)?:%(\w+|{$reg_block})(?:->{($reg_block)})?__;}
612 {($1 eq 'TEXT' ? '$p->{'.literal_or_code($Info, $2)
613 .'} = do { my $r = ' : '')
614 .'$f->parse_attr ($p=>'.literal_or_code($Info, $2).', $o, '
615 .($3?'-parent => '.$3.', ':'')
616 .($1?'-non_parsed_to_node => 1, ':'')
617 .'%opt)'
618 .($1 eq 'TEXT' ? '; ref $r?$r->inner_text:$r}'
619 : '')
620 .';'}ge;
621 $code{$codename->[1]} = barecode "sub {$main}";
622 }
623
624 my $main = literal {
625 Description => [barecode m13ed_val_list $src, 'Description'],
626 Parameter => {do {
627 my @r;
628 for (@{$src->child_nodes}) {
629 if ($_->local_name eq 'Parameter') {
630 push @r, $_->get_attribute_value ('Name')
631 => {Type => $_->get_attribute_value ('Type'),
632 Default => $_->get_attribute_value ('Default'),
633 Description => [barecode m13ed_val_list $_, 'Description']};
634 }
635 }
636 @r;
637 }},
638 %code,
639 };
640 $main .= line $Info, reset => 1;
641
642
643 my $amain = <<EOH;
644 {
645 main => sub {$main},
646 @{[line ($Info, reset => 1)]}
647 Description => [@{[m13ed_val_list $src, 'Description']}],
648 Parameter => {@{[do{
649 }]}},
650 }
651 EOH
652 my $r = change_package $Info, $Info->{module_name};
653 local $Info->{-message_error_used} = 0;
654 if (@$type == 1) {
655 $type->[0] =~ tr/-/_/;
656 $r .= qq{\$SuikaWiki::Plugin::Rule{$type->[0]}->{$name} = $main;\n};
657 push @{$Info->{provide}->{rule}->{$type->[0]}}, $name;
658 } else {
659 $r .= qq({my \$def = $main;\n);
660 for my $type (@$type) {
661 $type =~ tr/-/_/;
662 $r .= qq{\$SuikaWiki::Plugin::Rule{$type}->{$name} = \$def;\n};
663 push @{$Info->{provide}->{rule}->{$type}}, $name;
664 }
665 $r .= qq(};\n);
666 }
667 $r;
668 }
669
670 =item FormattingRuleAlias
671
672 Generating an alias name for a formatting rule that is already loaded.
673 Example:
674
675 FormattingRuleAlias:
676 @Category[list]:
677 category-1
678 category-2
679 ...
680 @Name: new-rule-name
681 @Reference:
682 @@Category: one-of-category
683 @@Name: one-of-name
684
685 associates C<(I<category-1>, I<new-rule-name>)>,
686 C<(I<category-2>, I<new-rule-name>)>, ...
687 with C<(I<one-of-category>, I<one-of-name>)>.
688
689 =cut
690
691 sub make_rule_alias ($$) {
692 my ($src, $Info) = @_;
693 my $type = $src->get_attribute ('Category', make_new_node => 1)->value || [];
694 my $name = $src->get_attribute ('Name', make_new_node => 1)->value;
695
696 my $ref = $src->get_attribute ('Reference', make_new_node => 1);
697 my $c = $ref->get_attribute_value ('Category');
698 my $n = $ref->get_attribute_value ('Name');
699
700 s/(?<=.)-/_/g for $n, $name;
701 tr/-/_/ for $c, @$type;
702
703 my $def = qq{\$SuikaWiki::Plugin::Rule{@{[literal $c]}}->{@{[literal $n]}} or warn qq{Formatting rule "$c/$n" not defined}};
704
705 my $r = change_package $Info, $Info->{module_name};
706 for my $type (@$type) {
707 $r .= qq{\$SuikaWiki::Plugin::Rule{@{[literal $type]}}->{@{[literal $name]}} = $def;\n};
708 push @{$Info->{provide}->{rule}->{$type}}, $name;
709 }
710 $r;
711 }
712
713
714 sub random_module_name ($;$) {
715 my ($Info, $subname) = @_;
716 $subname =~ s/[^0-9A-Za-z_:]//g;
717 my @date = gmtime;
718 my @rand = ('A'..'Z','a'..'z',0..9,'_');
719 sprintf '%s::%s%s%s', $Info->{module_name}, $subname,
720 sprintf ('%02d%02d%02d%02d%02d%02d', @date[5,4,3,2,1,0]),
721 join ('', @rand[rand@rand,rand@rand,rand@rand,rand@rand]);
722 }
723
724 =head1 NAME
725
726 mkplugin2.pl - SuikaWiki: WikiPlugin Generator
727
728 =head1 SYNOPSIS
729
730 mkplugin2.pl pluginsrc.wp2 > plugin.pm
731
732 =head1 DESCRIPTION
733
734 C<mkplugin2.pl> generates WikiPlugin module as a Perl module file
735 from WikiPlugin source description. WikiPlugin source description
736 is described in SuikaWikiConfig/2.0 format and it contains
737 definitions of wiki constructions (such as formatting rules and
738 WikiView definitions) as both machine understandable code and
739 human readable documentation. For more information, see
740 <http://suika.fam.cx/~wakaba/-temp/wiki/wiki?WikiPlugin>.
741
742 This script is part of SuikaWiki.
743
744 =head1 HISTORY AND COMPATIBILITY
745
746 C<mkplugin2.pl> introduced as part of SuikaWiki 3.
747 It converts SuikaWiki 3 WikiPlugin source descriptions
748 (in SuikaWikiConfig/2.0) into Perl modules with SuikaWiki 3 interface.
749
750 SuikaWiki 2 has C<mkplugin.pl>. It also converts WikiPlugin
751 source descriptions into Perl modules. But it support
752 SuikaWiki 2 format of WikiPlugin source description that differs from
753 SuikaWiki 3 format. Wiki programming interface (not limited to
754 WikiPlugin related one) of SuikaWiki 3 also incompatible with that
755 of SuikaWiki 2 so that it is impossible to use SuikaWiki 2 WikiPlugin
756 module with SuikaWiki 3 and vice versa.
757
758 =head1 SEE ALSO
759
760 C<SuikaWiki::Plugin>, SuikaWiki:WikiPlugin
761 <http://suika.fam.cx/~wakaba/-temp/wiki/wiki?WikiPlugin>.
762
763 =head1 LICENSE
764
765 Copyright 2003-2004 Wakaba <w@suika.fam.cx>. All rights reserved.
766
767 This program is free software; you can redistribute it and/or
768 modify it under the same terms as Perl itself.
769
770 =cut
771
772 1; # $Date: 2004/05/01 03:55:05 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24