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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.18 - (hide annotations) (download)
Thu Jun 3 06:38:48 2004 UTC (20 years, 5 months ago) by wakaba
Branch: MAIN
CVS Tags: release-3-0-0
Changes since 1.17: +19 -7 lines
File MIME type: text/plain
Static output of stylesheet implemented; Use of simple HTML serializer (new to manakai) if text/html output

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24