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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24