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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (hide annotations) (download)
Thu Mar 11 04:04:06 2004 UTC (20 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.13: +51 -1 lines
File MIME type: text/plain
New

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24