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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.21 - (show annotations) (download)
Mon Nov 8 09:57:49 2004 UTC (19 years, 6 months ago) by wakaba
Branch: MAIN
CVS Tags: suikawiki3-redirect, HEAD
Branch point for: helowiki, helowiki-2005
Changes since 1.20: +3 -2 lines
File MIME type: text/plain
Committed

1 #!/usr/bin/perl
2 use strict;
3 our $VERSION = do{my @r=(q$Revision: 1.20 $=~/\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 local $Error::Depth = $Error::Depth + 1;
261 $self->SUPER::convert ($source, %opt);
262 }
263 EOH
264
265 for (@{$src->child_nodes}) {
266 if ($_->local_name eq 'Converter') {
267 if ($convert) {
268 $r .= $convert;
269 $r .= line $Info, reset => 1;
270 undef $convert;
271 }
272 $r .= make_format_converter ($_, $Info);
273 } elsif ($_->local_name eq 'WikiForm') {
274 $r .= q(sub wikiform {)."\n".q(my ($self, $source, %opt) = @_;);
275 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/WikiForm);
276 $r .= code $Info, $_->get_attribute_value ('Main');
277 $r .= line $Info, reset => 1;
278 $r .= qq(}\n);
279 } elsif ($_->local_name eq 'HeadSummary') {
280 $r .= q(sub headsummary {)."\n".q(my ($self, $source, %opt) = @_;);
281 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/HeadSummary);
282 $r .= code $Info, $_->get_attribute_value ('Main');
283 $r .= line $Info, reset => 1;
284 $r .= qq(}\n);
285 } elsif ($_->local_name eq 'NextIndex') {
286 my $name = $_->get_attribute_value ('Name', default => '');
287 $r .= q(sub next_index_for_).$name
288 . q( {)."\n".q(my ($self, $source, %opt) = @_;)
289 . line $Info, node_path => qq(Format[module-name()=$module_name]/NextIndex[Name=$name]);
290 $r .= code $Info, $_->get_attribute_value ('Main');
291 $r .= line $Info, reset => 1;
292 $r .= qq(}\n);
293 } elsif ({qw/content_written 1 content_removed 1 content_type_changed_from 1
294 content_prop_modified 1/}
295 ->{my $node_name = $_->local_name}) {
296 $r .= q(sub ).$node_name
297 . q( {)."\n".q(my ($self, %opt) = @_;)
298 . line $Info, node_path => qq(Format[module-name()=$module_name]/$node_name]);
299 $r .= code $Info, $_->get_attribute_value ('Main');
300 $r .= line $Info, reset => 1;
301 $r .= qq(}\n);
302 } elsif ($_->local_name eq 'Use') {
303 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/Use);
304 $r .= code $Info, $_->inner_text;
305 } elsif ($_->local_name eq 'Prop') {
306 $r .= q<sub prop ($$;%) { my ($self, $name, %opt) = @_;>;
307 my $f = 0;
308 for my $prop (@{$_->child_nodes}) {
309 if ($prop->local_name =~ /^([^:]+):([^:]+)$/) {
310 $r .= qq{if (\$name eq @{[literal expanded_uri $Info, $1, $2]}) { return @{[literal $prop->value]} } els};
311 $f = 1;
312 }
313 }
314 $r .= q<e {> if $f;
315 $r .= q{$self->SUPER::prop ($name, %opt);};
316 $r .= q<}> if $f;
317 $r .= qq<\n}\n>;
318 }
319 }
320 $r;
321 }
322
323 sub make_format_converter ($$) {
324 my ($src, $Info) = @_;
325 my %def;
326 for (qw/Type Name Version TypeURIReference IsFragment IsPlaceholder/) {
327 $def{$_} = $src->get_attribute_value ($_);
328 delete $def{$_} unless defined $def{$_};
329 }
330 $def{Type_param} = {map {$_->local_name => $_->value}
331 @{$src->get_attribute ('Type', make_new_node => 1)
332 ->child_nodes}};
333 my $type = serialize_media_type ($Info,
334 Type => $def{Type},
335 Type_param => $def{Type_param},
336 Name => $def{Name},
337 Version => $def{Version},
338 URIReference => $def{TypeURIReference},
339 IsFragment => $def{IsFragment},
340 IsPlaceholder => $def{IsPlaceholder});
341 $def{serialized_type} = $type->{_};
342
343 for (qw/Main ToString ToOctetStream/) {
344 my $def = $src->get_attribute_value ($_);
345 next unless $def;
346 $def{$_} = line ($Info, node_path => '//Converter/'.$_)
347 . $def
348 . line ($Info, reset => 1);
349 if ($def{$_} =~ /\$r\b/) {
350 $def{$_} = 'my $r;'."\n".$def{$_}."\n".'$r';
351 }
352 $def{$_} = barecode code $Info,
353 'sub {my ($self, $source, $opt) = @_;'
354 . $def{$_} . '}';
355 }
356
357 my $r = list %def;
358 if ($type->{Type}) {
359 $r = qq{\$Converter->{@{[literal $type->{Type}]}} = {$r};\n};
360 $r .= qq{\$Converter->{@{[literal $type->{Magic}]}} = \$Converter->{@{[literal $type->{Type}]}};\n}
361 if $type->{Magic};
362 $r .= qq{\$Converter->{@{[literal $type->{URIReference}]}} = \$Converter->{@{[literal $type->{Type}]}};\n}
363 if $type->{URIReference};
364 } elsif ($type->{Magic}) {
365 $r = qq{\$Converter->{@{[literal $type->{Magic}]}} = {$r};\n};
366 $r .= qq{\$Converter->{@{[literal $type->{URIReference}]}} = \$Converter->{@{[literal $type->{Magic}]}};\n}
367 if $type->{URIReference};
368 } elsif ($type->{URIReference}) {
369 $r = qq{\$Converter->{@{[literal $type->{URIReference}]}} = {$r};\n};
370 } else {
371 $r = 'BEGIN { die "Invalid Syntax of Converter: Type or Name or TypeURIReference property required" }';
372 }
373 $r;
374 }
375
376 sub serialize_media_type ($%) {
377 my ($Info, %opt) = @_;
378 my %return;
379 if ($opt{Type}) {
380 $return{Type} = 'IMT:'.$opt{Type};
381 if ($opt{Type_param}) {
382 $return{Type} .= join '', map {my $s;
383 ';'. $_ .'="'
384 . (($s = $opt{Type_param}->{$_}) =~ s/([\\"])/\\$1/g, $s)
385 . '"'
386 } sort {
387 $a cmp $b
388 } keys %{$opt{Type_param}};
389 }
390 }
391 if ($opt{Magic}) {
392 $return{Magic} = 'MAGIC:'.$opt{Magic};
393 } elsif ($opt{Name}) {
394 $return{Name} = 'MAGIC:'.$opt{Name}.'/*';
395 $return{Magic} = 'MAGIC:'.$opt{Name}.'/'.$opt{Version} if $opt{Version};
396 }
397 if ($opt{URIReference}) {
398 $return{URIReference} = $opt{URIReference};
399 }
400 my $flag = '##';
401 $flag .= 'f' if $opt{IsFragment};
402 $flag .= 'p' if $opt{IsPlaceholder};
403 for (qw/URIReference Type Magic Name/) {
404 $return{$_} .= $flag if $return{$_};
405 }
406 $return{_} = $return{URIReference} || $return{Type}
407 || $return{Magic} || $return{Name};
408 \%return;
409 }
410
411
412 sub make_function ($$) {
413 my ($src, $Info) = @_;
414 ## TODO: support of ARGV property
415 my $name;
416 my $r = <<EOH;
417 @{[change_package $Info, $Info->{module_name}]}
418 sub @{[$name = $src->get_attribute_value ('Name')]} {
419 @{[line $Info, node_path => "Function[Name='$name']/Main"]}@{[
420 code $Info, $src->get_attribute_value ('Main')
421 ]}
422 }
423 @{[line $Info, reset => 1]}
424 EOH
425 }
426
427 sub register_plugin_const ($$) {
428 my ($src, $Info) = @_;
429 for (@{$src->child_nodes}) {
430 $Info->{const}->{$_->local_name} = literal_or_code $Info, $_->value;
431 }
432 }
433
434 sub make_resdef ($$) {
435 my ($src, $Info) = @_;
436 my $r = change_package $Info, 'SuikaWiki::Plugin::Resource';
437 local $Info->{-message_error_used} = 0;
438 $r .= qq{our \$BaseResource;\n};
439 for (@{$src->child_nodes}) {
440 if ($_->node_type eq '#element') {
441 my $lang = literal ($_->get_attribute_value ('lang') || 'und');
442 my $script = literal $_->get_attribute_value ('script');
443 my $name = literal $_->local_name;
444 my $val = literal n11n $_->value;
445 $r .= qq{\$BaseResource->{$lang}->{$script}->{$name} = $val;\n};
446 }
447 }
448 $r;
449 }
450
451 sub make_viewfragment ($$) {
452 my ($src, $Info) = @_;
453 my $r = '';
454 my $body = <<EOH;
455 {
456 Main => @{[literal $src->get_attribute_value ('Formatting')]},
457 Order => @{[0+$src->get_attribute_value ('Order')]},
458 Description => [@{[m13ed_val_list $src, 'Description']}],
459 };
460 EOH
461 ## Recommended format
462 my $name = $src->get_attribute_value ('Template');
463 if (ref ($name) and @$name > 1) {
464 $r .= qq({my \$def = $body;\n);
465 for (@$name) {
466 my $name = $_; $name =~ tr/-/_/;
467 $r .= qq(push \@{\$SuikaWiki::View::Implementation::TemplateFragment{@{[literal $name]}}}, \$def;\n);
468 push @{$Info->{provide}->{viewfragment}}, {Name => $name};
469 }
470 $r .= qq(}\n);
471 } else { ## Obsoleted format
472 $name = ref $name ? $name->[0] : $src->get_attribute_value ('Name');
473 $name =~ tr/-/_/;
474 $r .= qq(push \@{\$SuikaWiki::View::Implementation::TemplateFragment{@{[literal $name]}}}, $body);
475 push @{$Info->{provide}->{viewfragment}}, {Name => $name};
476 }
477 $r;
478 }
479
480 sub make_viewdef ($$) {
481 my ($src, $Info) = @_;
482 my $ViewProp = {};
483 my $r = '';
484 $ViewProp->{Name} = n11n $src->get_attribute_value ('Mode');
485 $ViewProp->{Name} =~ s/(?<=.)-/_/g;
486 $ViewProp->{pack_name} = random_module_name ($Info, $ViewProp->{Name});
487
488 $ViewProp->{condition_stringified} = hash
489 mode => $ViewProp->{Name},
490 map {($_->local_name => $_->value)}
491 @{$src->get_attribute ('Condition',make_new_node=>1)->child_nodes};
492
493 $r .= <<EOH;
494 push \@SuikaWiki::View::Implementation::CommonViewDefs, {
495 condition => {$ViewProp->{condition_stringified}},
496 object_class => q#$ViewProp->{pack_name}#,
497 };
498 @{[change_package $Info, $ViewProp->{pack_name}]}
499 our \@ISA = q#SuikaWiki::View::template#;
500 EOH
501 local $Info->{-message_error_used} = 0;
502 my $use = $src->get_attribute ('Use');
503 if (ref $use) {
504 $r .= line $Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/Use";
505 $r .= code $Info, $use->inner_text;
506 $r .= "\n\n";
507 }
508
509 for (@{$src->child_nodes}) {
510 if ($_->local_name eq 'template') {
511 $r .= make_view_template_method ($_, $Info, $ViewProp);
512 } elsif ($_->local_name eq 'method') {
513 my $method_name = $_->get_attribute_value ('Name');
514 $r .= ({
515 main => q(sub main ($$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
516 main_pre => q(sub main_pre ($$$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
517 main_post => q(sub main_post ($$$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
518 }->{$method_name}
519 ||qq(sub @{[$method_name]} {\n))
520 . line ($Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/method[Name='$method_name']")
521 . code ($Info, $_->value)
522 . qq(}\n)
523 . line ($Info, reset => 1);
524 }
525 }
526 my $prop = {Name => $ViewProp->{Name},
527 Description => barecode m13ed_val_list $_, 'Description'};
528 push @{$Info->{provide}->{viewdef}}, $prop;
529 $r;
530 }
531
532 sub make_view_template_method ($$) {
533 my ($src, $Info, $ViewProp) = @_;
534 my $media_type = $src->get_attribute_value
535 ('media-type',
536 default => q<application/octet-stream>);
537 my $r = <<EOH;
538
539 sub main (\$\$\$) {
540 my (\$self, \$opt, \$opt2) = \@_;
541 require SuikaWiki::Output::HTTP;
542 \$opt2->{output} = SuikaWiki::Output::HTTP->new
543 (wiki => \$self->{view}->{wiki},
544 view => \$self->{view}, viewobj => \$self);
545 for (\@{\$self->{view}->{wiki}->{var}->{client}->{used_for_negotiate}},
546 'Accept-Language') {
547 \$opt2->{output}->add_negotiate_header_field (\$_);
548 }
549
550 \$opt2->{template} = @{[literal $src->get_attribute ('body', make_new_node => 1)->inner_text]};
551 \$opt2->{o} = bless {
552 ## SuikaWiki 3 WikiPlugin interface
553 wiki => \$self->{view}->{wiki},
554 plugin => \$self->{view}->{wiki}->{plugin},
555 var => {},
556 }, 'SuikaWiki::Plugin';
557 @{[do{my $x=$src->get_attribute('http-status-code',make_new_node=>1)->inner_text;
558 $x?q{$opt2->{output}->{status_code} = }.(0 + $x).q{;}:q{}}]}
559 @{[do{my $x=$src->get_attribute('http-status-phrase',make_new_node=>1)->inner_text;
560 $x?q{$opt2->{output}->{status_phrase} = }.literal($x).q{;}:q{}}]}
561 \$opt2->{output}->{entity}->{media_type} = @{[literal $media_type]};
562
563 @{[($src->get_attribute ('use-media-type-charset',make_new_node=>1)
564 ->inner_text || 0) ?
565 q{$opt2->{output}->{entity}->{charset} = $self->{view}->{wiki}->{config}->{charset}->{output};}:
566 q{}]}
567 @{[do{my $x = $src->get_attribute ('expires',make_new_node=>1)->inner_text;
568 if ($x =~ /%%(\w+)%%/) {
569 qq{\$opt2->{output}->set_expires (%{\$self->{view}->{wiki}->{config}->{entity}->{expires}->{@{[literal $1]}}});};
570 } else {
571 qq{\$opt2->{output}->set_expires (delta => @{[0 + $x]});};
572 }
573 }]}
574 \$opt2->{output}->{entity}->{body_is_octet_stream} = @{[
575 $src->get_attribute ('body')->get_attribute_value ('is-octet-stream', default_value => 0)
576 or 0
577 ]};
578
579 \$self->{view}->{wiki}->init_db;
580 \$self->main_pre (\$opt, \$opt2);
581
582 @{[$media_type eq 'text/html' ? q{require Message::Markup::XML::Serialize::HTML;} : '']}
583 use Message::Util::Error;
584 try {
585 \$opt2->{output}->{entity}->{body}
586 = @{[$media_type eq 'text/html' ? q{Message::Markup::XML::Serialize::HTML::html_simple} : '']}
587 (SuikaWiki::Plugin->formatter ('view')
588 ->replace (\$opt2->{template}, param => \$opt2->{o}));
589 } \$self->{view}->{wiki}->{config}->{catch}->{ @{[
590 $ViewProp->{Name} eq '-error' ? 'formatter_view_error'
591 : 'formatter_view' ]} };
592 \$opt2->{output}->output (output => 'http-cgi');
593
594 \$self->main_post (\$opt, \$opt2);
595 }
596 EOH
597 }
598
599 sub make_rule ($$) {
600 my ($src, $Info) = @_;
601 my $type = $src->get_attribute ('Category', make_new_node => 1)->value || [];
602 my $name = $src->get_attribute ('Name', make_new_node => 1)->value;
603 $name =~ s/(?<=.)-/_/g;
604
605 my $reg_block;
606 $reg_block = qr/[^{}]*(?>[^{}]+|{(??{$reg_block})})*/;
607 my %code;
608 for my $codename ([qw/Formatting main/], [qw/After after/],
609 [qw/Before before/], [qw/Pre pre/], [qw/Post post/],
610 [qw/Attribute attr/]) {
611 my $main = code $Info, $src->get_attribute_value ($codename->[0]);
612 next unless $main;
613 $main = line ($Info, node_path =>
614 "FormattingRule[name()='@{[list $type]}/$name']/".$codename->[0])
615 . $main;
616
617 if ( $main =~ /\$f\b/
618 or $main =~ /\$rule_name\b/
619 or $main =~ /\$[opr]\b/
620 or $main =~ /[%\$]opt\b/
621 or $main =~ /\$param_(?:name|value)\n/) {
622 if ($codename->[0] ne 'Attribute') {
623 $main = q{my ($f, $rule_name, $p, $o, %opt) = @_;}."\n".$main;
624 } else {
625 $main = q{my ($f, $rule_name, $p, $o, $param_name => $param_value, %opt) = @_;}."\n".$main;
626 }
627 }
628 if ($main =~ /\$r\b/) {
629 warn qq(Rule @{[list $type]}/$name: Use of \$r is deprecated);
630 $main = q{my $r = '';} . "\n" . $main . "\n"
631 . q{$p->{-parent}->append_node ($r, node_or_text => 1);};
632 }
633 $main =~ s{__ATTR(TEXT|NODE)?:%(\w+|{$reg_block})(?:->{($reg_block)})?__;}
634 {($1 eq 'TEXT' ? '$p->{'.literal_or_code($Info, $2)
635 .'} = do { my $r = ' : '')
636 .'$f->parse_attr ($p=>'.literal_or_code($Info, $2).', $o, '
637 .($3?'-parent => '.$3.', ':'')
638 .($1?'-non_parsed_to_node => 1, ':'')
639 .'%opt)'
640 .($1 eq 'TEXT' ? '; ref $r?$r->inner_text:$r}'
641 : '')
642 .';'}ge;
643 $code{$codename->[1]} = barecode "sub {$main}";
644 }
645
646 my $main = literal {
647 Description => [barecode m13ed_val_list $src, 'Description'],
648 Parameter => {do {
649 my @r;
650 for (@{$src->child_nodes}) {
651 if ($_->local_name eq 'Parameter') {
652 push @r, $_->get_attribute_value ('Name')
653 => {Type => $_->get_attribute_value ('Type'),
654 Default => $_->get_attribute_value ('Default'),
655 Description => [barecode m13ed_val_list $_, 'Description']};
656 }
657 }
658 @r;
659 }},
660 %code,
661 };
662 $main .= line $Info, reset => 1;
663
664
665 my $amain = <<EOH;
666 {
667 main => sub {$main},
668 @{[line ($Info, reset => 1)]}
669 Description => [@{[m13ed_val_list $src, 'Description']}],
670 Parameter => {@{[do{
671 }]}},
672 }
673 EOH
674 my $r = change_package $Info, $Info->{module_name};
675 local $Info->{-message_error_used} = 0;
676 if (@$type == 1) {
677 $type->[0] =~ tr/-/_/;
678 $r .= qq{\$SuikaWiki::Plugin::Rule{$type->[0]}->{$name} = $main;\n};
679 push @{$Info->{provide}->{rule}->{$type->[0]}}, $name;
680 } else {
681 $r .= qq({my \$def = $main;\n);
682 for my $type (@$type) {
683 $type =~ tr/-/_/;
684 $r .= qq{\$SuikaWiki::Plugin::Rule{$type}->{$name} = \$def;\n};
685 push @{$Info->{provide}->{rule}->{$type}}, $name;
686 }
687 $r .= qq(};\n);
688 }
689 $r;
690 }
691
692 =item FormattingRuleAlias
693
694 Generating an alias name for a formatting rule that is already loaded.
695 Example:
696
697 FormattingRuleAlias:
698 @Category[list]:
699 category-1
700 category-2
701 ...
702 @Name: new-rule-name
703 @Reference:
704 @@Category: one-of-category
705 @@Name: one-of-name
706
707 associates C<(I<category-1>, I<new-rule-name>)>,
708 C<(I<category-2>, I<new-rule-name>)>, ...
709 with C<(I<one-of-category>, I<one-of-name>)>.
710
711 =cut
712
713 sub make_rule_alias ($$) {
714 my ($src, $Info) = @_;
715 my $type = $src->get_attribute ('Category', make_new_node => 1)->value || [];
716 my $name = $src->get_attribute ('Name', make_new_node => 1)->value;
717
718 my $ref = $src->get_attribute ('Reference', make_new_node => 1);
719 my $c = $ref->get_attribute_value ('Category');
720 my $n = $ref->get_attribute_value ('Name');
721
722 s/(?<=.)-/_/g for $n, $name;
723 tr/-/_/ for $c, @$type;
724
725 my $def = qq{\$SuikaWiki::Plugin::Rule{@{[literal $c]}}->{@{[literal $n]}} or warn qq{Formatting rule "$c/$n" not defined}};
726
727 my $r = change_package $Info, $Info->{module_name};
728 for my $type (@$type) {
729 $r .= qq{\$SuikaWiki::Plugin::Rule{@{[literal $type]}}->{@{[literal $name]}} = $def;\n};
730 push @{$Info->{provide}->{rule}->{$type}}, $name;
731 }
732 $r;
733 }
734
735
736 sub random_module_name ($;$) {
737 my ($Info, $subname) = @_;
738 $subname =~ s/[^0-9A-Za-z_:]//g;
739 my @date = gmtime;
740 my @rand = ('A'..'Z','a'..'z',0..9,'_');
741 sprintf '%s::%s%s%s', $Info->{module_name}, $subname,
742 sprintf ('%02d%02d%02d%02d%02d%02d', @date[5,4,3,2,1,0]),
743 join ('', @rand[rand@rand,rand@rand,rand@rand,rand@rand]);
744 }
745
746 =head1 NAME
747
748 mkplugin2.pl - SuikaWiki: WikiPlugin Generator
749
750 =head1 SYNOPSIS
751
752 mkplugin2.pl pluginsrc.wp2 > plugin.pm
753
754 =head1 DESCRIPTION
755
756 C<mkplugin2.pl> generates WikiPlugin module as a Perl module file
757 from WikiPlugin source description. WikiPlugin source description
758 is described in SuikaWikiConfig/2.0 format and it contains
759 definitions of wiki constructions (such as formatting rules and
760 WikiView definitions) as both machine understandable code and
761 human readable documentation. For more information, see
762 <http://suika.fam.cx/~wakaba/-temp/wiki/wiki?WikiPlugin>.
763
764 This script is part of SuikaWiki.
765
766 =head1 HISTORY AND COMPATIBILITY
767
768 C<mkplugin2.pl> introduced as part of SuikaWiki 3.
769 It converts SuikaWiki 3 WikiPlugin source descriptions
770 (in SuikaWikiConfig/2.0) into Perl modules with SuikaWiki 3 interface.
771
772 SuikaWiki 2 has C<mkplugin.pl>. It also converts WikiPlugin
773 source descriptions into Perl modules. But it support
774 SuikaWiki 2 format of WikiPlugin source description that differs from
775 SuikaWiki 3 format. Wiki programming interface (not limited to
776 WikiPlugin related one) of SuikaWiki 3 also incompatible with that
777 of SuikaWiki 2 so that it is impossible to use SuikaWiki 2 WikiPlugin
778 module with SuikaWiki 3 and vice versa.
779
780 =head1 SEE ALSO
781
782 C<SuikaWiki::Plugin>, SuikaWiki:WikiPlugin
783 <http://suika.fam.cx/~wakaba/-temp/wiki/wiki?WikiPlugin>.
784
785 =head1 LICENSE
786
787 Copyright 2003-2004 Wakaba <w@suika.fam.cx>. All rights reserved.
788
789 This program is free software; you can redistribute it and/or
790 modify it under the same terms as Perl itself.
791
792 =cut
793
794 1; # $Date: 2004/09/21 03:18:21 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24