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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1 by wakaba, Sun Oct 5 11:11:13 2003 UTC revision 1.16 by wakaba, Sun Apr 25 07:06:50 2004 UTC
# Line 1  Line 1 
1  #!/usr/bin/perl  #!/usr/bin/perl
2  use strict;  use strict;
3  require SuikaWiki::Markup::SuikaWikiConfig20::Parser;  our $VERSION = do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4    require Message::Markup::SuikaWikiConfig20::Parser;
5    
6    {
7  my $src = '';  my $src = '';
8  my $srcfile = shift;  my $srcfile = shift;
9  open SRC, $srcfile or die "$0: $!"; {  open SRC, $srcfile or die "$0: $!"; {
# Line 11  open SRC, $srcfile or die "$0: $!"; { Line 13  open SRC, $srcfile or die "$0: $!"; {
13    
14  sub literal ($) {  sub literal ($) {
15    my $s = shift;    my $s = shift;
16    $s =~ s/([#\\])/\\$1/g;    if (ref ($s) eq 'ARRAY') {
17    q<q#> . $s . q<#>;      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  }  }
34  sub n11n ($) {  sub n11n ($) {
35    my $s = shift;    my $s = shift;
36    $s =~ s/\s+/ /g;    $s =~ s/\s+/ /g;
37    $s;    $s;
38  }  }
39    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        $code =~ s/\$$_\b/$Info->{const}->{$_}/ge;
60      }
61      $code =~ s/__FUNCPACK__/$Info->{module_name}/g;
62      $code =~ s/<Q:([^:]+):([^>]+)>/literal $Info->{Namespace}->{$1}.$2/ge;
63      
64      $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      $code;
70    }
71    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    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          $opt{file} = sprintf '(WikiPlugin module source %s, block %s)',
99                               $Info->{source_file},
100                               $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    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    
113  my $parser = SuikaWiki::Markup::SuikaWikiConfig20::Parser->new;  my $parser = Message::Markup::SuikaWikiConfig20::Parser->new;
114  my $plugins = $parser->parse_text ($src);  my $plugins = $parser->parse_text ($src);
115  my $meta = $plugins->get_attribute ('Plugin')  my $meta = $plugins->get_attribute ('Plugin')
116            or die "$0: Required 'Plugin' section not found";            or die "$0: Required 'Plugin' section not found";
117  my %Info = (Name => n11n $meta->get_attribute ('Name')->value);  my %Info = (provide => {},
118                Name => n11n $meta->get_attribute ('Name')->value);
119    $Info{source_file} = $srcfile;
120  $Info{name_literal} = literal $Info{Name};  $Info{name_literal} = literal $Info{Name};
121  my @date = gmtime;  my @date = gmtime;
122  $Info{LastModified} = sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',  $Info{LastModified} = sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
123                                $date[5] + 1900, $date[4] + 1, @date[3,2,1,0];                                $date[5] + 1900, $date[4] + 1, @date[3,2,1,0];
124  $Info{Version} = sprintf '%04d.%02d%02d.%02d%02d',  $Info{Version} = sprintf '%04d.%02d%02d.%02d%02d',
125                           $date[5] + 1900, $date[4] + 1, @date[3,2,1];                           $date[5] + 1900, $date[4] + 1, @date[3,2,1];
126    $Info{InterfaceVersion} = '2.9.1';
127    $Info{mkpluginVersion} = '2.'.$VERSION;
128  $Info{module_name} = q#SuikaWiki::Plugin::plugin#;  $Info{module_name} = q#SuikaWiki::Plugin::plugin#;
129  $Info{module_name} = random_module_name (\%Info, $Info{Name});  $Info{module_name} = random_module_name (\%Info, $Info{Name});
130    
131  print <<EOH;  print <<EOH;
132  use strict;  use strict;
133  package SuikaWiki::Plugin::Registry;  @{[change_package \%Info, 'SuikaWiki::Plugin::Registry']}
134  our \%Info;  our \%Info;
135  \$Info{$Info{name_literal}}->{Name} = $Info{name_literal};  \$Info{$Info{name_literal}}->{Name} = $Info{name_literal};
 \$Info{$Info{name_literal}}->{Version} = q#$Info{Version}#;  
136  EOH  EOH
137  for (qw/Description LastModified License/) {  for (qw/Version InterfaceVersion mkpluginVersion module_name/) {
138      print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = @{[literal $Info{$_}]};\n};
139    }
140    for (qw/LastModified Date.RCS/) {
141    $Info{$_} = n11n $meta->get_attribute ($_,make_new_node=>1)->value;    $Info{$_} = n11n $meta->get_attribute ($_,make_new_node=>1)->value;
142    next unless length $Info{$_};    next unless length $Info{$_};
143    print qq{\$Info{$Info{name_literal}}->{$_} = } . literal $Info{$_};    print qq{\$Info{$Info{name_literal}}->{@{[literal $_]}} = } . literal $Info{$_};
144    print ";\n";    print ";\n";
145  }  }
146  for (qw/Author RelatedURI RelatedWikiPage RequiredPlugin RequiredModule/) {  for (qw/RequiredPlugin RequiredModule/) {
147    $Info{$_} = $meta->get_attribute ($_);    $Info{$_} = $meta->get_attribute ($_);
148    next unless ref $Info{$_}; $Info{$_} = $Info{$_}->value;    next unless ref $Info{$_}; $Info{$_} = $Info{$_}->value;
149    print qq{\$Info{$Info{name_literal}}->{$_} = [};    print qq{\$Info{$Info{name_literal}}->{$_} = [};
150    print join ', ', map {literal $_} @{$Info{$_}};    print join ', ', map {literal $_} @{$Info{$_}};
151    print "];\n";    print "];\n";
152  }  }
153    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    
168    for (@{$meta->get_attribute ('Namespace', make_new_node => 1)->child_nodes}) {
169      $Info{Namespace}->{$_->local_name} = $_->value;
170    }
171    
172    my $use = $meta->get_attribute ('Use');
173    if (ref $use) {
174      print change_package \%Info, $Info{module_name};
175      print line \%Info, node_path => 'Plugin/Use';
176      print code \%Info, $use->inner_text;
177      print line \%Info, reset => 1;
178    }
179    
180  for (@{$plugins->child_nodes}) {  for (@{$plugins->child_nodes}) {
181    if ($_->local_name eq 'ViewDefinition') {    if ($_->local_name eq 'FormattingRule') {
182        print "\n", make_rule ($_, \%Info);
183      } elsif ($_->local_name eq 'ViewDefinition') {
184      print "\n", make_viewdef ($_, \%Info);      print "\n", make_viewdef ($_, \%Info);
185    } elsif ($_->local_name eq 'ViewFragment') {    } elsif ($_->local_name eq 'ViewFragment') {
186      print "\n", make_viewfragment ($_, \%Info);      print "\n", make_viewfragment ($_, \%Info);
187      } 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      } elsif ($_->local_name eq 'Format') {
194        print "\n", make_format ($_, \%Info);
195      } elsif ($_->local_name eq 'FormattingRuleAlias') {
196        print "\n", make_rule_alias ($_, \%Info);
197    # Parameter
198    # PluginCategory
199    }    }
200  }  }
201    
202    print change_package \%Info, q(SuikaWiki::Plugin::Registry);
203    print qq{\$Info{$Info{name_literal}}->{provide} = } . literal $Info{provide};
204    print qq{;\n};
205    
206  print "\n1;\n";  print "\n1;\n";
207  exit;  exit;
208    }
209    
210    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      local $Info->{-message_error_used} = 0;  
215      $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        $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'MAGIC:'.$name.'/'.$src->get_attribute_value ('Version', default => '').'##']}} = '$module_name';\n};
225      }
226      if (my $type = $src->get_attribute_value ('Type')) {
227        $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        $r .= qq{\$SuikaWiki::Format::Definition::Class{@{[literal 'IMT:'.$type.'##']}} = '$module_name';\n};
233      }
234      
235      my $convert = line $Info, line_no => __LINE__ + 2, realfile => __FILE__;
236      $convert .= <<'EOH';
237    our $Converter;
238    sub convert ($$;%) {
239      my ($self, $source, %opt) = @_;
240      my $t = SuikaWiki::Format::Definition->serialize_media_type (%opt);
241      my $converter = $Converter->{$t->{_}};
242      return ($converter->{$opt{return_type} or 'Main'} or
243              CORE::die "Buggy implementation: $t->{_}/@{[$opt{return_type} or 'Main']} not defined")
244             ->($self, $source, \%opt)
245        if $converter;
246      $self->SUPER::convert ($source, %opt);
247    }
248    EOH
249      
250      for (@{$src->child_nodes}) {
251        if ($_->local_name eq 'Converter') {
252          if ($convert) {
253            $r .= $convert;
254            $r .= line $Info, reset => 1;
255            undef $convert;
256          }
257          $r .= make_format_converter ($_, $Info);
258        } 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        } 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        } 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        } elsif ($_->local_name eq 'Use') {
279          $r .= line $Info, node_path => qq(Format[module-name()=$module_name]/Use);
280          $r .= code $Info, $_->inner_text;
281        }
282      }
283      $r;
284    }
285    
286    sub make_format_converter ($$) {
287      my ($src, $Info) = @_;
288      my %def;
289      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      
306      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      
320      my $r = list %def;
321      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      } else {
334        $r = 'BEGIN { die "Invalid Syntax of Converter: Type or Name or TypeURIReference property required" }';
335      }
336      $r;
337    }
338    
339    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    sub make_function ($$) {
376      my ($src, $Info) = @_;
377      ## TODO: support of ARGV property
378      my $name;
379      my $r = <<EOH;
380    @{[change_package $Info, $Info->{module_name}]}
381    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    ]}
385    }
386    @{[line $Info, reset => 1]}
387    EOH
388    }
389    
390    sub register_plugin_const ($$) {
391      my ($src, $Info) = @_;
392      for (@{$src->child_nodes}) {
393        $Info->{const}->{$_->local_name} = literal_or_code $Info, $_->value;
394      }
395    }
396    
397    sub make_resdef ($$) {
398      my ($src, $Info) = @_;
399      my $r = change_package $Info, 'SuikaWiki::Plugin::Resource';
400      local $Info->{-message_error_used} = 0;  
401      $r .= qq{our \$BaseResource;\n};
402      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    
414  sub make_viewfragment ($$) {  sub make_viewfragment ($$) {
415    my ($src, $Info) = @_;    my ($src, $Info) = @_;
416    my $r = '';    my $r = '';
417    for (@{$src->child_nodes}) {    my $body = <<EOH;
418      ## TODO: use SuikaWiki2 interface    {
419      $r .= qq(SuikaWiki::View->template (@{[literal $_->local_name]})->add_line (@{[literal $_->value]});\n);      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      ## 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    $r;    $r;
441  }  }
# Line 80  sub make_viewdef ($$) { Line 444  sub make_viewdef ($$) {
444    my ($src, $Info) = @_;    my ($src, $Info) = @_;
445    my $ViewProp = {};    my $ViewProp = {};
446    my $r = '';    my $r = '';
447    $ViewProp->{Name} = n11n $src->get_attribute ('Mode')->value;    $ViewProp->{Name} = n11n $src->get_attribute_value ('Mode');
448      $ViewProp->{Name} =~ s/(?<=.)-/_/g;
449    $ViewProp->{pack_name} = random_module_name ($Info, $ViewProp->{Name});    $ViewProp->{pack_name} = random_module_name ($Info, $ViewProp->{Name});
450        
451    $ViewProp->{condition_stringified} = join ', ', map {literal $_}    $ViewProp->{condition_stringified} = hash
452      mode => $ViewProp->{Name},      mode => $ViewProp->{Name},
453      map {($_->local_name => $_->value)}      map {($_->local_name => $_->value)}
454        @{$src->get_attribute ('Condition',make_new_node=>1)->child_nodes};        @{$src->get_attribute ('Condition',make_new_node=>1)->child_nodes};
# Line 93  push \@SuikaWiki::View::Implementation:: Line 458  push \@SuikaWiki::View::Implementation::
458    condition => {$ViewProp->{condition_stringified}},    condition => {$ViewProp->{condition_stringified}},
459    object_class => q#$ViewProp->{pack_name}#,    object_class => q#$ViewProp->{pack_name}#,
460  };  };
461  package $ViewProp->{pack_name};  @{[change_package $Info, $ViewProp->{pack_name}]}
462  our \@ISA = q#SuikaWiki::View::template#;  our \@ISA = q#SuikaWiki::View::template#;
463  EOH  EOH
464      local $Info->{-message_error_used} = 0;  
465      my $use = $src->get_attribute ('Use');
466      if (ref $use) {
467        $r .= line $Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/Use";
468        $r .= code $Info, $use->inner_text;
469        $r .= "\n\n";
470      }
471      
472    for (@{$src->child_nodes}) {    for (@{$src->child_nodes}) {
473      if ($_->local_name eq 'method') {      if ($_->local_name eq 'template') {
474          $r .= make_view_template_method ($_, $Info, $ViewProp);
475        } elsif ($_->local_name eq 'method') {
476          my $method_name = $_->get_attribute_value ('Name');
477        $r .= ({        $r .= ({
478                main => q(sub main ($$) {)."\n".q(my ($self, $opt) = @_;)."\n",                main => q(sub main ($$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",
479                main_pre => q(sub main_pre ($$$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",                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",                                    main_post => q(sub main_post ($$$) {)."\n".q(my ($self, $opt, $opt2) = @_;)."\n",                    
481               }->{$_->get_attribute ('Name')->value}               }->{$method_name}
482               ||qq(sub @{[$_->get_attribute ('Name')->value]} {\n))               ||qq(sub @{[$method_name]} {\n))
483           . $_->inner_text           . line ($Info, node_path => "ViewDefinition[Mode='$ViewProp->{Name}']/method[Name='$method_name']")
484           . qq(\n}\n);           . code ($Info, $_->value)
485             . qq(}\n)
486             . line ($Info, reset => 1);
487        }
488      }
489      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      my ($src, $Info, $ViewProp) = @_;
497      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         $x?q{$opt2->{output}->{status_phrase} = }.literal($x).q{;}:q{}}]}
521      \$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      \$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      
540      \$self->{view}->{wiki}->init_db;
541      \$self->main_pre (\$opt, \$opt2);
542      
543      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      \$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      $name =~ s/(?<=.)-/_/g;
563      
564      my $reg_block;
565      $reg_block = qr/[^{}]*(?>[^{}]+|{(??{$reg_block})})*/;
566      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        if ( $main =~ /\$f\b/
577          or $main =~ /\$rule_name\b/
578          or $main =~ /\$[opr]\b/
579          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        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    {
626      main => sub {$main},
627    @{[line ($Info, reset => 1)]}
628      Description => [@{[m13ed_val_list $src, 'Description']}],
629      Parameter => {@{[do{
630      }]}},
631    }
632    EOH
633      my $r = change_package $Info, $Info->{module_name};
634      local $Info->{-message_error_used} = 0;  
635      if (@$type == 1) {
636        $type->[0] =~ tr/-/_/;
637        $r .= qq{\$SuikaWiki::Plugin::Rule{$type->[0]}->{$name} = $main;\n};
638        push @{$Info->{provide}->{rule}->{$type->[0]}}, $name;
639      } else {
640        $r .= qq({my \$def = $main;\n);
641        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      $r;
649    }
650    
651    =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;    $r;
692  }  }
# Line 117  sub random_module_name ($;$) { Line 697  sub random_module_name ($;$) {
697    $subname =~ s/[^0-9A-Za-z_:]//g;    $subname =~ s/[^0-9A-Za-z_:]//g;
698    my @date = gmtime;    my @date = gmtime;
699    my @rand = ('A'..'Z','a'..'z',0..9,'_');    my @rand = ('A'..'Z','a'..'z',0..9,'_');
700    sprintf '%s::%s%s%s', $Info{module_name}, $subname,    sprintf '%s::%s%s%s', $Info->{module_name}, $subname,
701      sprintf ('%02d%02d%02d%02d%02d%02d', @date[5,4,3,2,1,0]),      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]);      join ('', @rand[rand@rand,rand@rand,rand@rand,rand@rand]);
703  }  }
704    
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    1; # $Date$

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.16

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24