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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.17 - (show annotations) (download)
Sat May 1 03:55:05 2004 UTC (20 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.16: +10 -3 lines
File MIME type: text/plain
Warn if namespace not defined

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24