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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (show annotations) (download)
Sun Apr 25 07:06:50 2004 UTC (20 years, 6 months ago) by wakaba
Branch: MAIN
Branch point for: paragraph-200404
Changes since 1.15: +78 -47 lines
File MIME type: text/plain
LeafProp database module added; content_prop implemented; Media type property implemented

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24