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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.20 - (show annotations) (download)
Tue Sep 21 03:18:21 2004 UTC (20 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.19: +4 -3 lines
File MIME type: text/plain
%m--wikipage-obsolete rule added

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24