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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24