/[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 - (hide 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 wakaba 1.1 #!/usr/bin/perl
2     use strict;
3 wakaba 1.16 our $VERSION = do{my @r=(q$Revision: 1.15 $=~/\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.16 $code =~ s/<Q:([^:]+):([^>]+)>/literal $Info->{Namespace}->{$1}.$2/ge;
63    
64 wakaba 1.13 $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 wakaba 1.2 $code;
70     }
71 wakaba 1.5 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 wakaba 1.6 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 wakaba 1.7 $opt{file} = sprintf '(WikiPlugin module source %s, block %s)',
99     $Info->{source_file},
100 wakaba 1.6 $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 wakaba 1.9 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 wakaba 1.1
113 wakaba 1.5 my $parser = Message::Markup::SuikaWikiConfig20::Parser->new;
114 wakaba 1.1 my $plugins = $parser->parse_text ($src);
115     my $meta = $plugins->get_attribute ('Plugin')
116     or die "$0: Required 'Plugin' section not found";
117 wakaba 1.2 my %Info = (provide => {},
118     Name => n11n $meta->get_attribute ('Name')->value);
119 wakaba 1.7 $Info{source_file} = $srcfile;
120 wakaba 1.1 $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 wakaba 1.2 $Info{InterfaceVersion} = '2.9.1';
127     $Info{mkpluginVersion} = '2.'.$VERSION;
128 wakaba 1.1 $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 wakaba 1.5 @{[change_package \%Info, 'SuikaWiki::Plugin::Registry']}
134 wakaba 1.1 our \%Info;
135     \$Info{$Info{name_literal}}->{Name} = $Info{name_literal};
136     EOH
137 wakaba 1.5 for (qw/Version InterfaceVersion mkpluginVersion module_name/) {
138 wakaba 1.13 print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = @{[literal $Info{$_}]};\n};
139 wakaba 1.2 }
140 wakaba 1.13 for (qw/LastModified Date.RCS/) {
141 wakaba 1.1 $Info{$_} = n11n $meta->get_attribute ($_,make_new_node=>1)->value;
142     next unless length $Info{$_};
143 wakaba 1.13 print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = } . literal $Info{$_};
144 wakaba 1.1 print ";\n";
145     }
146 wakaba 1.2 for (qw/RequiredPlugin RequiredModule/) {
147 wakaba 1.1 $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 wakaba 1.2 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 wakaba 1.1
168 wakaba 1.16 for (@{$meta->get_attribute ('Namespace', make_new_node => 1)->child_nodes}) {
169     $Info{Namespace}->{$_->local_name} = $_->value;
170     }
171    
172 wakaba 1.5 my $use = $meta->get_attribute ('Use');
173     if (ref $use) {
174     print change_package \%Info, $Info{module_name};
175 wakaba 1.6 print line \%Info, node_path => 'Plugin/Use';
176 wakaba 1.13 print code \%Info, $use->inner_text;
177 wakaba 1.6 print line \%Info, reset => 1;
178 wakaba 1.5 }
179    
180 wakaba 1.1 for (@{$plugins->child_nodes}) {
181 wakaba 1.2 if ($_->local_name eq 'FormattingRule') {
182     print "\n", make_rule ($_, \%Info);
183     } elsif ($_->local_name eq 'ViewDefinition') {
184 wakaba 1.1 print "\n", make_viewdef ($_, \%Info);
185     } elsif ($_->local_name eq 'ViewFragment') {
186     print "\n", make_viewfragment ($_, \%Info);
187 wakaba 1.2 } 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 wakaba 1.5 } elsif ($_->local_name eq 'Format') {
194     print "\n", make_format ($_, \%Info);
195 wakaba 1.15 } elsif ($_->local_name eq 'FormattingRuleAlias') {
196     print "\n", make_rule_alias ($_, \%Info);
197 wakaba 1.9 # Parameter
198     # PluginCategory
199 wakaba 1.1 }
200     }
201    
202 wakaba 1.5 print change_package \%Info, q(SuikaWiki::Plugin::Registry);
203 wakaba 1.2 print qq{\$Info{$Info{name_literal}}->{provide} = } . literal $Info{provide};
204     print qq{;\n};
205    
206 wakaba 1.1 print "\n1;\n";
207     exit;
208 wakaba 1.2 }
209    
210 wakaba 1.5 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 wakaba 1.13 local $Info->{-message_error_used} = 0;
215 wakaba 1.5 $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 wakaba 1.16 $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'MAGIC:'.$name.'/'.$src->get_attribute_value ('Version', default => '').'##']}} = '$module_name';\n};
225 wakaba 1.5 }
226     if (my $type = $src->get_attribute_value ('Type')) {
227 wakaba 1.10 $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 wakaba 1.16 $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'IMT:'.$type.'##']}} = '$module_name';\n};
233 wakaba 1.5 }
234    
235 wakaba 1.8 my $convert = line $Info, line_no => __LINE__ + 2, realfile => __FILE__;
236     $convert .= <<'EOH';
237 wakaba 1.5 our $Converter;
238     sub convert ($$;%) {
239     my ($self, $source, %opt) = @_;
240 wakaba 1.16 my $t = SuikaWiki::Format::Definition->serialize_media_type (%opt);
241     my $converter = $Converter->{$t->{_}};
242 wakaba 1.12 return ($converter->{$opt{return_type} or 'Main'} or
243 wakaba 1.16 CORE::die "Buggy implementation: $t->{_}/@{[$opt{return_type} or 'Main']} not defined")
244 wakaba 1.12 ->($self, $source, \%opt)
245     if $converter;
246 wakaba 1.5 $self->SUPER::convert ($source, %opt);
247     }
248     EOH
249    
250     for (@{$src->child_nodes}) {
251     if ($_->local_name eq 'Converter') {
252 wakaba 1.8 if ($convert) {
253     $r .= $convert;
254     $r .= line $Info, reset => 1;
255     undef $convert;
256     }
257 wakaba 1.5 $r .= make_format_converter ($_, $Info);
258 wakaba 1.10 } 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 wakaba 1.11 } 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 wakaba 1.10 } 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 wakaba 1.5 } elsif ($_->local_name eq 'Use') {
279 wakaba 1.6 $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/Use);
280 wakaba 1.13 $r .= code $Info, $_->inner_text;
281 wakaba 1.5 }
282     }
283     $r;
284     }
285    
286     sub make_format_converter ($$) {
287     my ($src, $Info) = @_;
288     my %def;
289 wakaba 1.16 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 wakaba 1.5
306 wakaba 1.12 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 wakaba 1.5
320     my $r = list %def;
321 wakaba 1.16 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 wakaba 1.9 } else {
334 wakaba 1.16 $r = 'BEGIN { die "Invalid Syntax of Converter: Type or Name or TypeURIReference property required" }';
335 wakaba 1.5 }
336     $r;
337     }
338    
339 wakaba 1.16 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 wakaba 1.2 sub make_function ($$) {
376     my ($src, $Info) = @_;
377     ## TODO: support of ARGV property
378 wakaba 1.6 my $name;
379 wakaba 1.2 my $r = <<EOH;
380 wakaba 1.5 @{[change_package $Info, $Info->{module_name}]}
381 wakaba 1.6 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 wakaba 1.9 ]}
385 wakaba 1.2 }
386 wakaba 1.9 @{[line $Info, reset => 1]}
387 wakaba 1.2 EOH
388     }
389    
390     sub register_plugin_const ($$) {
391     my ($src, $Info) = @_;
392     for (@{$src->child_nodes}) {
393 wakaba 1.11 $Info->{const}->{$_->local_name} = literal_or_code $Info, $_->value;
394 wakaba 1.2 }
395     }
396    
397     sub make_resdef ($$) {
398     my ($src, $Info) = @_;
399 wakaba 1.5 my $r = change_package $Info, 'SuikaWiki::Plugin::Resource';
400 wakaba 1.13 local $Info->{-message_error_used} = 0;
401 wakaba 1.5 $r .= qq{our \$BaseResource;\n};
402 wakaba 1.2 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 wakaba 1.1
414     sub make_viewfragment ($$) {
415     my ($src, $Info) = @_;
416     my $r = '';
417 wakaba 1.4 my $body = <<EOH;
418     {
419 wakaba 1.2 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 wakaba 1.4 ## 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 wakaba 1.1 $r;
441     }
442    
443     sub make_viewdef ($$) {
444     my ($src, $Info) = @_;
445     my $ViewProp = {};
446     my $r = '';
447 wakaba 1.6 $ViewProp->{Name} = n11n $src->get_attribute_value ('Mode');
448 wakaba 1.10 $ViewProp->{Name} =~ s/(?<=.)-/_/g;
449 wakaba 1.1 $ViewProp->{pack_name} = random_module_name ($Info, $ViewProp->{Name});
450    
451 wakaba 1.2 $ViewProp->{condition_stringified} = hash
452 wakaba 1.1 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 wakaba 1.5 @{[change_package $Info, $ViewProp->{pack_name}]}
462 wakaba 1.1 our \@ISA = q#SuikaWiki::View::template#;
463     EOH
464 wakaba 1.13 local $Info->{-message_error_used} = 0;
465 wakaba 1.11 my $use = $src->get_attribute ('Use');
466     if (ref $use) {
467     $r .= line $Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/Use";
468 wakaba 1.13 $r .= code $Info, $use->inner_text;
469     $r .= "\n\n";
470 wakaba 1.11 }
471    
472 wakaba 1.1 for (@{$src->child_nodes}) {
473 wakaba 1.2 if ($_->local_name eq 'template') {
474 wakaba 1.8 $r .= make_view_template_method ($_, $Info, $ViewProp);
475 wakaba 1.2 } elsif ($_->local_name eq 'method') {
476 wakaba 1.6 my $method_name = $_->get_attribute_value ('Name');
477 wakaba 1.1 $r .= ({
478 wakaba 1.8 main => q(sub main ($$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
479 wakaba 1.1 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 wakaba 1.6 }->{$method_name}
482     ||qq(sub @{[$method_name]} {\n))
483     . line ($Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/method[Name='$method_name']")
484 wakaba 1.2 . code ($Info, $_->value)
485 wakaba 1.9 . qq(}\n)
486     . line ($Info, reset => 1);
487 wakaba 1.1 }
488     }
489 wakaba 1.2 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 wakaba 1.8 my ($src, $Info, $ViewProp) = @_;
497 wakaba 1.2 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 wakaba 1.8 $x?q{$opt2->{output}->{status_phrase} = }.literal($x).q{;}:q{}}]}
521 wakaba 1.2 \$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 wakaba 1.12 \$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 wakaba 1.2
540     \$self->{view}->{wiki}->init_db;
541     \$self->main_pre (\$opt, \$opt2);
542    
543 wakaba 1.8 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 wakaba 1.2 \$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 wakaba 1.5 $name =~ s/(?<=.)-/_/g;
563    
564     my $reg_block;
565     $reg_block = qr/[^{}]*(?>[^{}]+|{(??{$reg_block})})*/;
566 wakaba 1.10 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 wakaba 1.11 if ( $main =~ /\$f\b/
577 wakaba 1.10 or $main =~ /\$rule_name\b/
578     or $main =~ /\$[opr]\b/
579 wakaba 1.11 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 wakaba 1.10 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 wakaba 1.2 {
626 wakaba 1.5 main => sub {$main},
627 wakaba 1.9 @{[line ($Info, reset => 1)]}
628 wakaba 1.2 Description => [@{[m13ed_val_list $src, 'Description']}],
629     Parameter => {@{[do{
630     }]}},
631     }
632     EOH
633 wakaba 1.5 my $r = change_package $Info, $Info->{module_name};
634 wakaba 1.13 local $Info->{-message_error_used} = 0;
635 wakaba 1.2 if (@$type == 1) {
636     $type->[0] =~ tr/-/_/;
637 wakaba 1.5 $r .= qq{\$SuikaWiki::Plugin::Rule{$type->[0]}->{$name} = $main;\n};
638 wakaba 1.2 push @{$Info->{provide}->{rule}->{$type->[0]}}, $name;
639     } else {
640 wakaba 1.5 $r .= qq({my \$def = $main;\n);
641 wakaba 1.2 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 wakaba 1.1 $r;
649     }
650    
651 wakaba 1.15 =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 wakaba 1.1
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 wakaba 1.2 sprintf '%s::%s%s%s', $Info->{module_name}, $subname,
701 wakaba 1.1 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 wakaba 1.14
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 wakaba 1.16 1; # $Date: 2004/03/19 03:46:22 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24