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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.15 - (hide annotations) (download)
Fri Mar 19 03:46:22 2004 UTC (20 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.14: +47 -2 lines
File MIME type: text/plain
New 'FormattingRuleAlias' implemented

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24