/[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 - (hide 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 wakaba 1.1 #!/usr/bin/perl
2     use strict;
3 wakaba 1.17 our $VERSION = do{my @r=(q$Revision: 1.16 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.5 require Message::Markup::SuikaWikiConfig20::Parser;
5 wakaba 1.1
6 wakaba 1.2 {
7 wakaba 1.1 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 wakaba 1.2 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 wakaba 1.1 }
34     sub n11n ($) {
35     my $s = shift;
36     $s =~ s/\s+/ /g;
37     $s;
38     }
39 wakaba 1.2 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 wakaba 1.11 $code =~ s/\$$_\b/$Info->{const}->{$_}/ge;
60 wakaba 1.2 }
61     $code =~ s/__FUNCPACK__/$Info->{module_name}/g;
62 wakaba 1.17 $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 wakaba 1.16
71 wakaba 1.13 $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 wakaba 1.2 $code;
77     }
78 wakaba 1.5 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 wakaba 1.6 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 wakaba 1.7 $opt{file} = sprintf '(WikiPlugin module source %s, block %s)',
106     $Info->{source_file},
107 wakaba 1.6 $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 wakaba 1.9 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 wakaba 1.1
120 wakaba 1.5 my $parser = Message::Markup::SuikaWikiConfig20::Parser->new;
121 wakaba 1.1 my $plugins = $parser->parse_text ($src);
122     my $meta = $plugins->get_attribute ('Plugin')
123     or die "$0: Required 'Plugin' section not found";
124 wakaba 1.2 my %Info = (provide => {},
125     Name => n11n $meta->get_attribute ('Name')->value);
126 wakaba 1.7 $Info{source_file} = $srcfile;
127 wakaba 1.1 $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 wakaba 1.2 $Info{InterfaceVersion} = '2.9.1';
134     $Info{mkpluginVersion} = '2.'.$VERSION;
135 wakaba 1.1 $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 wakaba 1.5 @{[change_package \%Info, 'SuikaWiki::Plugin::Registry']}
141 wakaba 1.1 our \%Info;
142     \$Info{$Info{name_literal}}->{Name} = $Info{name_literal};
143     EOH
144 wakaba 1.5 for (qw/Version InterfaceVersion mkpluginVersion module_name/) {
145 wakaba 1.13 print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = @{[literal $Info{$_}]};\n};
146 wakaba 1.2 }
147 wakaba 1.13 for (qw/LastModified Date.RCS/) {
148 wakaba 1.1 $Info{$_} = n11n $meta->get_attribute ($_,make_new_node=>1)->value;
149     next unless length $Info{$_};
150 wakaba 1.13 print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = } . literal $Info{$_};
151 wakaba 1.1 print ";\n";
152     }
153 wakaba 1.2 for (qw/RequiredPlugin RequiredModule/) {
154 wakaba 1.1 $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 wakaba 1.2 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 wakaba 1.1
175 wakaba 1.16 for (@{$meta->get_attribute ('Namespace', make_new_node => 1)->child_nodes}) {
176     $Info{Namespace}->{$_->local_name} = $_->value;
177     }
178    
179 wakaba 1.5 my $use = $meta->get_attribute ('Use');
180     if (ref $use) {
181     print change_package \%Info, $Info{module_name};
182 wakaba 1.6 print line \%Info, node_path => 'Plugin/Use';
183 wakaba 1.13 print code \%Info, $use->inner_text;
184 wakaba 1.6 print line \%Info, reset => 1;
185 wakaba 1.5 }
186    
187 wakaba 1.1 for (@{$plugins->child_nodes}) {
188 wakaba 1.2 if ($_->local_name eq 'FormattingRule') {
189     print "\n", make_rule ($_, \%Info);
190     } elsif ($_->local_name eq 'ViewDefinition') {
191 wakaba 1.1 print "\n", make_viewdef ($_, \%Info);
192     } elsif ($_->local_name eq 'ViewFragment') {
193     print "\n", make_viewfragment ($_, \%Info);
194 wakaba 1.2 } 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 wakaba 1.5 } elsif ($_->local_name eq 'Format') {
201     print "\n", make_format ($_, \%Info);
202 wakaba 1.15 } elsif ($_->local_name eq 'FormattingRuleAlias') {
203     print "\n", make_rule_alias ($_, \%Info);
204 wakaba 1.9 # Parameter
205     # PluginCategory
206 wakaba 1.1 }
207     }
208    
209 wakaba 1.5 print change_package \%Info, q(SuikaWiki::Plugin::Registry);
210 wakaba 1.2 print qq{\$Info{$Info{name_literal}}->{provide} = } . literal $Info{provide};
211     print qq{;\n};
212    
213 wakaba 1.1 print "\n1;\n";
214     exit;
215 wakaba 1.2 }
216    
217 wakaba 1.5 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 wakaba 1.13 local $Info->{-message_error_used} = 0;
222 wakaba 1.5 $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 wakaba 1.16 $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'MAGIC:'.$name.'/'.$src->get_attribute_value ('Version', default => '').'##']}} = '$module_name';\n};
232 wakaba 1.5 }
233     if (my $type = $src->get_attribute_value ('Type')) {
234 wakaba 1.10 $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 wakaba 1.16 $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'IMT:'.$type.'##']}} = '$module_name';\n};
240 wakaba 1.5 }
241    
242 wakaba 1.8 my $convert = line $Info, line_no => __LINE__ + 2, realfile => __FILE__;
243     $convert .= <<'EOH';
244 wakaba 1.5 our $Converter;
245     sub convert ($$;%) {
246     my ($self, $source, %opt) = @_;
247 wakaba 1.16 my $t = SuikaWiki::Format::Definition->serialize_media_type (%opt);
248     my $converter = $Converter->{$t->{_}};
249 wakaba 1.12 return ($converter->{$opt{return_type} or 'Main'} or
250 wakaba 1.16 CORE::die "Buggy implementation: $t->{_}/@{[$opt{return_type} or 'Main']} not defined")
251 wakaba 1.12 ->($self, $source, \%opt)
252     if $converter;
253 wakaba 1.5 $self->SUPER::convert ($source, %opt);
254     }
255     EOH
256    
257     for (@{$src->child_nodes}) {
258     if ($_->local_name eq 'Converter') {
259 wakaba 1.8 if ($convert) {
260     $r .= $convert;
261     $r .= line $Info, reset => 1;
262     undef $convert;
263     }
264 wakaba 1.5 $r .= make_format_converter ($_, $Info);
265 wakaba 1.10 } 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 wakaba 1.11 } 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 wakaba 1.10 } 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 wakaba 1.5 } elsif ($_->local_name eq 'Use') {
286 wakaba 1.6 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/Use);
287 wakaba 1.13 $r .= code $Info, $_->inner_text;
288 wakaba 1.5 }
289     }
290     $r;
291     }
292    
293     sub make_format_converter ($$) {
294     my ($src, $Info) = @_;
295     my %def;
296 wakaba 1.16 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 wakaba 1.5
313 wakaba 1.12 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 wakaba 1.5
327     my $r = list %def;
328 wakaba 1.16 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 wakaba 1.9 } else {
341 wakaba 1.16 $r = 'BEGIN { die "Invalid Syntax of Converter: Type or Name or TypeURIReference property required" }';
342 wakaba 1.5 }
343     $r;
344     }
345    
346 wakaba 1.16 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 wakaba 1.2 sub make_function ($$) {
383     my ($src, $Info) = @_;
384     ## TODO: support of ARGV property
385 wakaba 1.6 my $name;
386 wakaba 1.2 my $r = <<EOH;
387 wakaba 1.5 @{[change_package $Info, $Info->{module_name}]}
388 wakaba 1.6 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 wakaba 1.9 ]}
392 wakaba 1.2 }
393 wakaba 1.9 @{[line $Info, reset => 1]}
394 wakaba 1.2 EOH
395     }
396    
397     sub register_plugin_const ($$) {
398     my ($src, $Info) = @_;
399     for (@{$src->child_nodes}) {
400 wakaba 1.11 $Info->{const}->{$_->local_name} = literal_or_code $Info, $_->value;
401 wakaba 1.2 }
402     }
403    
404     sub make_resdef ($$) {
405     my ($src, $Info) = @_;
406 wakaba 1.5 my $r = change_package $Info, 'SuikaWiki::Plugin::Resource';
407 wakaba 1.13 local $Info->{-message_error_used} = 0;
408 wakaba 1.5 $r .= qq{our \$BaseResource;\n};
409 wakaba 1.2 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 wakaba 1.1
421     sub make_viewfragment ($$) {
422     my ($src, $Info) = @_;
423     my $r = '';
424 wakaba 1.4 my $body = <<EOH;
425     {
426 wakaba 1.2 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 wakaba 1.4 ## 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 wakaba 1.1 $r;
448     }
449    
450     sub make_viewdef ($$) {
451     my ($src, $Info) = @_;
452     my $ViewProp = {};
453     my $r = '';
454 wakaba 1.6 $ViewProp->{Name} = n11n $src->get_attribute_value ('Mode');
455 wakaba 1.10 $ViewProp->{Name} =~ s/(?<=.)-/_/g;
456 wakaba 1.1 $ViewProp->{pack_name} = random_module_name ($Info, $ViewProp->{Name});
457    
458 wakaba 1.2 $ViewProp->{condition_stringified} = hash
459 wakaba 1.1 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 wakaba 1.5 @{[change_package $Info, $ViewProp->{pack_name}]}
469 wakaba 1.1 our \@ISA = q#SuikaWiki::View::template#;
470     EOH
471 wakaba 1.13 local $Info->{-message_error_used} = 0;
472 wakaba 1.11 my $use = $src->get_attribute ('Use');
473     if (ref $use) {
474     $r .= line $Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/Use";
475 wakaba 1.13 $r .= code $Info, $use->inner_text;
476     $r .= "\n\n";
477 wakaba 1.11 }
478    
479 wakaba 1.1 for (@{$src->child_nodes}) {
480 wakaba 1.2 if ($_->local_name eq 'template') {
481 wakaba 1.8 $r .= make_view_template_method ($_, $Info, $ViewProp);
482 wakaba 1.2 } elsif ($_->local_name eq 'method') {
483 wakaba 1.6 my $method_name = $_->get_attribute_value ('Name');
484 wakaba 1.1 $r .= ({
485 wakaba 1.8 main => q(sub main ($$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
486 wakaba 1.1 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 wakaba 1.6 }->{$method_name}
489     ||qq(sub @{[$method_name]} {\n))
490     . line ($Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/method[Name='$method_name']")
491 wakaba 1.2 . code ($Info, $_->value)
492 wakaba 1.9 . qq(}\n)
493     . line ($Info, reset => 1);
494 wakaba 1.1 }
495     }
496 wakaba 1.2 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 wakaba 1.8 my ($src, $Info, $ViewProp) = @_;
504 wakaba 1.2 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 wakaba 1.8 $x?q{$opt2->{output}->{status_phrase} = }.literal($x).q{;}:q{}}]}
528 wakaba 1.2 \$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 wakaba 1.12 \$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 wakaba 1.2
547     \$self->{view}->{wiki}->init_db;
548     \$self->main_pre (\$opt, \$opt2);
549    
550 wakaba 1.8 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 wakaba 1.2 \$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 wakaba 1.5 $name =~ s/(?<=.)-/_/g;
570    
571     my $reg_block;
572     $reg_block = qr/[^{}]*(?>[^{}]+|{(??{$reg_block})})*/;
573 wakaba 1.10 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 wakaba 1.11 if ( $main =~ /\$f\b/
584 wakaba 1.10 or $main =~ /\$rule_name\b/
585     or $main =~ /\$[opr]\b/
586 wakaba 1.11 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 wakaba 1.10 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 wakaba 1.2 {
633 wakaba 1.5 main => sub {$main},
634 wakaba 1.9 @{[line ($Info, reset => 1)]}
635 wakaba 1.2 Description => [@{[m13ed_val_list $src, 'Description']}],
636     Parameter => {@{[do{
637     }]}},
638     }
639     EOH
640 wakaba 1.5 my $r = change_package $Info, $Info->{module_name};
641 wakaba 1.13 local $Info->{-message_error_used} = 0;
642 wakaba 1.2 if (@$type == 1) {
643     $type->[0] =~ tr/-/_/;
644 wakaba 1.5 $r .= qq{\$SuikaWiki::Plugin::Rule{$type->[0]}->{$name} = $main;\n};
645 wakaba 1.2 push @{$Info->{provide}->{rule}->{$type->[0]}}, $name;
646     } else {
647 wakaba 1.5 $r .= qq({my \$def = $main;\n);
648 wakaba 1.2 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 wakaba 1.1 $r;
656     }
657    
658 wakaba 1.15 =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 wakaba 1.1
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 wakaba 1.2 sprintf '%s::%s%s%s', $Info->{module_name}, $subname,
708 wakaba 1.1 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 wakaba 1.14
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 wakaba 1.17 1; # $Date: 2004/04/25 07:06:50 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24