/[suikacvs]/markup/tool/mkdtds.pl
Suika

Contents of /markup/tool/mkdtds.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (hide annotations) (download)
Tue Jan 13 11:17:20 2004 UTC (20 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.2: +237 -64 lines
File MIME type: text/plain
Use new version of SuikaWikiConfig/2.0 parser

1 wakaba 1.3 #!/usr/bin/perl
2 wakaba 1.1 use strict;
3 wakaba 1.3 our $SCRIPT_NAME = 'mkdtds';
4     our $VERSION = do{my @r=(q$Revision: 1.26 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
5     {require Message::Markup::SuikaWikiConfig20::Parser;
6 wakaba 1.1
7 wakaba 1.3 my $parser = new Message::Markup::SuikaWikiConfig20::Parser;
8 wakaba 1.1 local $/ = undef;
9     my $src = $parser->parse_text (scalar <>);
10     my $Info = {};
11    
12     for my $src ($src->get_attribute ('ModuleSet')
13     || $src->get_attribute ('DocumentType')) {
14 wakaba 1.3 for (qw/ID Copyright BaseURI Description Version/) {
15 wakaba 1.1 $Info->{$_} = $src->get_attribute_value ($_);
16     }
17 wakaba 1.3 $Info->{Name} = $src->get_attribute_value ('Name');
18     $Info->{Name} .= ' ' . $Info->{Version} if length $Info->{Version};
19 wakaba 1.1 $Info->{ns} = $src->get_attribute ('Namespace');
20     }
21    
22     for (@{$src->child_nodes}) {
23     if ($_->local_name eq 'Attribute') {
24     attrib_module ($_, $Info);
25     } elsif ($_->local_name eq 'Datatype') {
26     datatype_module ($_, $Info);
27     } elsif ($_->local_name eq 'Notation') {
28     notation_module ($_, $Info);
29     } elsif ($_->local_name eq 'Module') {
30     submodule ($_, $Info);
31     } elsif ($_->local_name eq 'Model') {
32     model_module ($_, $Info);
33 wakaba 1.2 $Info->{has_model} = 1;
34 wakaba 1.1 } elsif ($_->local_name eq 'Driver') {
35     dtd_driver ($_, $Info);
36     }
37     }
38    
39     if (ref $src->get_attribute ('ModuleSet')) {
40     qname_module ($src->get_attribute ('ModuleSet'), $Info);
41     }
42     exit}
43    
44 wakaba 1.3
45     sub make_paragraphs ($;%) {
46     my ($para, %opt) = @_;
47     join "\n\n", map {
48     my $s = $_;
49     $s =~ s/\n+$//g;
50     $s =~ s/\n/\n$opt{indent}/g;
51     $opt{indent}.$s;
52     } grep {length} @$para;
53     }
54    
55     sub dot_padding ($%) {
56     my ($s, %opt) = @_;
57     if ($opt{length} - length $s > 0) {
58     return $s . ( ($opt{dot} or q(.)) x ($opt{length} - length $s) );
59     } else {
60     return $s;
61     }
62     }
63    
64 wakaba 1.1 sub submodule_id_of ($$;%) {
65     my ($src, $Info, %opt) = @_;
66     my $id = $src->get_attribute_value ('ID') || $opt{default};
67     unless ($id) {
68     die "$0: Submodule identifier not specified";
69     }
70     $id;
71     }
72     sub xml_datatype_of ($$;%) {
73     my ($src, $Info, %opt) = @_;
74     my $type = $src->get_attribute_value ('XMLType') || $opt{default};
75     $type =~ s/\s+//g;
76     $type;
77     }
78 wakaba 1.2 sub system_id_of ($$;%) {
79     my ($src, $Info, %opt) = @_;
80     my $sysid = $src->get_attribute_value ('SYSTEM');
81     if ($sysid =~ /<([^>]+)>/) {
82     return $1;
83     } else {
84     return $opt{base}.($sysid || $opt{default});
85     }
86     }
87 wakaba 1.1 sub external_id_of ($$;%) {
88     my ($src, $Info, %opt) = @_;
89 wakaba 1.2 my $sysid = system_id_of ($src, $Info, %opt);
90 wakaba 1.1 my $pubid = $src->get_attribute_value ('PUBLIC');
91     if ($pubid) {
92     if ($sysid) {
93     return qq(PUBLIC "$pubid"\n\t "$sysid");
94     } else {
95     return qq(PUBLIC "$pubid");
96     }
97     } else {
98     return qq(SYSTEM "$sysid");
99     }
100     }
101     sub name_of ($$;%) {
102     my ($src, $Info, %opt) = @_;
103     unless (ref $src) {require Carp; Carp::croak ('$src undefined')}
104     my $name = $src->get_attribute_value ($opt{key} || 'Name');
105     if ($name =~ /^:(.+)/) { ## Global namespace
106     return $1;
107     } elsif ($name =~ /([^:]+):(.+)/) { ## Named space
108     return $1.($opt{delim}||'.').$2;
109     } else { ## Default namespace
110     return $Info->{ID}.($opt{delim}||'.').$name;
111     }
112     }
113     sub local_name_of ($$;%) {
114     my ($src, $Info, %opt) = @_;
115     my $name = $src->get_attribute_value ($opt{key} || 'Name');
116     if ($name =~ /^:(.+)/) { ## Global namespace
117     return $1;
118     } elsif ($name =~ /[^:]+:(.+)/) { ## Named space
119     return $1;
120     } else { ## Default namespace
121     return $name;
122     }
123     }
124     sub set_name_of ($$;%) {
125     my ($src, $Info, %opt) = @_;
126     my $name = $src->get_attribute_value ($opt{key} || 'Name');
127     if ($name =~ /^:.+/) { ## Global namespace
128     return 'XHTML';
129     } elsif ($name =~ /([^:]+):.+/) { ## Named space
130     return $1;
131     } else { ## Default namespace
132     return $Info->{ID};
133     }
134     }
135     sub class_name_of ($$;%) {
136     my ($src, $Info, %opt) = @_;
137     my $name = name_of ($src, $Info, %opt);
138     unless ($name =~ /\.(class|mix|content|datatype)$/) {
139     $name .= '.class';
140     }
141     $name;
142     }
143     sub convert_content_model ($$;%) {
144     my ($src, $Info, %opt) = @_;
145     my $model = $src->get_attribute_value ($opt{key} || 'Content') || $opt{default};
146     $model =~ s/\s//g;
147     my $nonsymbol = qr/[^%#?,\$;()+*|:]/;
148     $model =~ s/(?<![%#.])((?:\$|\b)$nonsymbol+(?::$nonsymbol+)?|\$?:$nonsymbol+|"[^"]+")/get_model_token ($1, $Info)/ge;
149     $model;
150     }
151 wakaba 1.2 sub sparalit ($) {
152     my $s = paralit (shift);
153     $s =~ s/&/&#x26;/g;
154     $s =~ s/%/&#x25;/g;
155     $s;
156     }
157 wakaba 1.1 sub paralit ($) {
158     my $s = shift;
159     if ($s =~ /"/) {
160     if ($s =~ /'/) {
161     $s =~ s/'/&#x27;/g;
162     return qq("$s");
163     } else {
164     return qq('$s');
165     }
166     } else {
167     return qq("$s");
168     }
169     }
170     sub description ($$;%) {
171     my ($src, $Info, %opt) = @_;
172     my $desc = $src->get_attribute_value ('Description');
173     $desc =~ s/\n/\n /g;
174     unless ($desc) {
175     $desc = {
176     load_module => {
177     AttributeModule => q/Common Attributes Module/,
178     DatatypeModule => q/Datatypes Module/,
179     NotationModule => q/Notation Module/,
180     QNameModule => q/QName Module/,
181     },
182     }->{$opt{context}}->{$opt{id} || $src->get_attribute_value ($opt{id_key}||'ID')};
183     }
184     $desc = qq(<!-- $desc -->\n) if $desc;
185     $desc;
186     }
187 wakaba 1.3 sub xml_condition_section ($$;%) {
188     my ($condition, $content, %opt) = @_;
189     qq(<![%$condition;[\n)
190     . $content
191     . qq(<!-- end of $condition -->]]>\n);
192     }
193     sub xml_parameter_ENTITY ($%) {
194     my ($name, %opt) = @_;
195     qq(<!ENTITY % $name @{[paralit $opt{value}]}>\n);
196     }
197 wakaba 1.1
198 wakaba 1.2 sub entity_declaration ($$;%) {
199     my ($src, $Info, %opt) = @_;
200     my $val;
201     if ($src->get_attribute_value ('ID')
202     || $src->get_attribute_value ('SYSTEM')
203     || $src->get_attribute_value ('PUBLIC')) {
204     $val = "\n\t".external_id_of ($src, $Info, default => $src->get_attribute_value ('ID'));
205     } elsif (ref $src->get_attribute ('Declaration')) {
206     $val = "\n\t".sparalit submodule_declarations ($src->get_attribute ('Declaration'), $Info);
207     } else {
208     $val = paralit $src->get_attribute_value ('EntityValue');
209     }
210     my $s = <<EOH;
211     @{[description ($src, $Info)]}<!ENTITY @{[$opt{param}?'% ':'']}@{[$src->get_attribute_value ('Name')]} $val>
212    
213     EOH
214     $s;
215     }
216 wakaba 1.1
217     sub dtd_driver ($$) {
218     my ($src, $Info) = @_;
219     my $s = '';
220     my %s;
221     my @module_set;
222     for my $src (@{$src->child_nodes}) {
223     if ($src->local_name eq 'Module') {
224     $s .= dtd_driver_load_module ($src, $Info);
225 wakaba 1.2 } elsif ($src->local_name eq 'DTD') {
226     $s .= dtd_driver_load_dtd ($src, $Info);
227 wakaba 1.1 } elsif ($src->local_name eq 'ModuleSet') {
228     push @module_set, $src;
229     } elsif ($src->local_name =~ /^(?:QName|Attribute|Datatype|Notation)Module/) {
230     $s{$src->local_name} .= dtd_driver_load_module ($src, $Info);
231     } elsif ($src->local_name eq 'IfModuleSet') {
232     $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
233     $s .= submodule_declarations ($src, $Info);
234     $s .= qq(]]>\n);
235     } elsif ($src->local_name eq 'GeneralEntity') {
236 wakaba 1.2 $s .= entity_declaration ($src, $Info, param => 0);
237 wakaba 1.1 } elsif ($src->local_name eq 'ParameterEntity') {
238 wakaba 1.2 $s .= entity_declaration ($src, $Info, param => 1);
239 wakaba 1.1 }
240     }
241    
242 wakaba 1.2 $s{ModelModule} = $src->get_attribute_value ('NoModelModule') ? '' :
243     $Info->{has_model} ? <<EOH : '';
244 wakaba 1.1 <!-- Document Model module -->
245     <!ENTITY % $Info->{ID}-model.module "INCLUDE">
246     <![%$Info->{ID}-model.module;[
247     <!ENTITY % $Info->{ID}-model.decl
248     'SYSTEM "$Info->{ID}-model.mod"'>
249     <!ENTITY % $Info->{ID}-model.mod %$Info->{ID}-model.decl;>
250     %$Info->{ID}-model.mod;]]>
251    
252     EOH
253    
254     $s = dtd_driver_module_sets (\@module_set, $Info)
255     . $s{QNameModule}.$s{DatatypeModule}.$s{NotationModule}.$s{AttributeModule}
256     . $s{ModelModule}
257     .$s;
258     make_dtd ($src, $Info, $src->get_attribute_value ('ID'), $s);
259     }
260    
261     sub dtd_driver_module_sets ($$) {
262     my ($srces, $Info) = @_;
263     my @src = map {{src => $_}} @$srces;
264     my $s = qq(<!-- Switchers to include/ignore each vocabulary -->\n);
265     for my $module_set (@src) {
266     $module_set->{ID} = $module_set->{src}->get_attribute_value ('ID') || 'XHTML';
267     $s .= qq(<!ENTITY % $module_set->{ID}.module "@{[$module_set->{src}->get_attribute_value ('Default') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
268     }
269     $s .= qq(\n<!-- Namespace names -->\n);
270     for my $module_set (@src) {
271     $module_set->{ns} = $module_set->{src}->get_attribute ('Namespace');
272     $s .= qq(<!ENTITY % $module_set->{ID}.xmlns "@{[$module_set->{ns}->get_attribute_value ('Name')]}">\n);
273     }
274     $s .= qq(\n<!-- Base URIs for the relavant DTD modules -->\n);
275     for my $module_set (@src) {
276     $s .= qq(<!ENTITY % $module_set->{ID}.sysid.base "@{[$module_set->{src}->get_attribute_value ('BaseURI')]}">\n);
277     }
278     $s .= qq(\n<!-- Namespace prefix -->\n);
279     $s .= qq(<!ENTITY % NS.prefixed "@{[$Info->{ns}->get_attribute_value ('UsePrefix') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
280     for my $module_set (@src) {
281     $s .= qq(<!ENTITY % $module_set->{ID}.prefix "@{[$module_set->{ns}->get_attribute_value ('DefaultPrefix')]}">\n);
282     $s .= qq(<!ENTITY % $module_set->{ID}.prefixed "@{[$module_set->{ns}->get_attribute_value ('UsePrefix') > 0 ? 'INCLUDE' : $module_set->{ns}->get_attribute_value ('UsePrefix') < 0 ? 'IGNORE' : '%NS.prefixed;']}">\n);
283     }
284     $s .= qq(\n<!-- a URI reference -->\n<!ENTITY % URI.datatype "CDATA">\n);
285     $s .= qq(\n<!-- Placefolders for foreign namespace declarations -->\n);
286     for my $module_set (@src) {
287     $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.extra.attrib "">\n);
288     }
289     $s .= qq(\n<!-- Namespace declarations -->\n);
290     for my $module_set (@src) {
291     $s .= qq(<![%$module_set->{ID}.module;[
292     <![%$module_set->{ID}.prefixed;[
293     <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
294     "xmlns:%$module_set->{ID}.prefix; %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
295     ]]>
296     <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
297     "xmlns %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
298     ]]>
299     <!ENTITY % $module_set->{ID}.xmlns.decl.attrib "">\n\n);
300     }
301     $s .= qq(\n<!ENTITY % NS.decl.attrib
302     ").join ("\n\t", (map {qq(%$_->{ID}.xmlns.decl.attrib;)} @src),
303     map {qq(%$_->{ID}.xmlns.extra.attrib;)} @src).qq(">\n);
304     $s .= qq(\n);
305     for my $module_set (@src) {
306     $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.attrib "%NS.decl.attrib;">\n);
307     }
308     $s .= qq(\n\n);
309     $s;
310     }
311    
312     sub dtd_driver_load_module ($$) {
313     my ($src, $Info) = @_;
314     my $module_name = name_of ($src, $Info, key => 'ID');
315     my $module_hyphen_name = name_of ($src, $Info, key => 'ID', delim => '-');
316     my $module_set_name = set_name_of ($src, $Info, key => 'ID');
317     my $module_id = local_name_of ($src, $Info, key => 'ID');
318    
319     my $s .= <<EOH;
320     @{[description ($src, $Info, context => 'load_module', id => $src->local_name)]}<![%$module_set_name.module;[
321 wakaba 1.2 <!ENTITY % $module_name.module "@{[$src->get_attribute_value ('Default') >= 0 ? 'INCLUDE' : 'IGNORE']}">
322 wakaba 1.1 <![%$module_name.module;[
323     @{[submodule_declarations ($src, $Info)]}<!ENTITY % $module_name.decl
324     @{[paralit external_id_of ($src, $Info, default => qq($module_hyphen_name.mod), base => qq(%$module_set_name.sysid.base;))]}>
325     <!ENTITY % $module_name.mod %$module_name.decl;>
326     %$module_name.mod;]]>
327     ]]>
328    
329     EOH
330     $s;
331     }
332 wakaba 1.2
333     sub dtd_driver_load_dtd ($$) {
334     my ($src, $Info) = @_;
335     my $module_set_name = $src->get_attribute_value ('ID');
336    
337     my $s .= <<EOH;
338     @{[description ($src, $Info)]}<![%$module_set_name.module;[
339     @{[submodule_declarations ($src, $Info)]}<!ENTITY % $module_set_name.dtd.sysid "@{[system_id_of ($src, $Info, default => $src->get_attribute_value ('ID').'.dtd', base => qq(%$module_set_name.sysid.base;))]}">
340     @{[do{
341     my $pubid = $src->get_attribute_value ('PUBLIC');
342     if ($pubid) {
343     qq(<!ENTITY % $module_set_name.dtd.fpi "$pubid">\n<!ENTITY % $module_set_name.dtd.fpi.defined "INCLUDE">\n);
344     } else {
345     qq(<!ENTITY % $module_set_name.dtd.fpi "">\n<!ENTITY % $module_set_name.dtd.fpi.defined "IGNORE">\n);
346     }
347     }]}
348     <![%$module_set_name.dtd.fpi.defined;[
349     <!ENTITY % $module_set_name.dtd.decl
350     'PUBLIC "%$module_set_name.dtd.fpi;"
351     "%$module_set_name.dtd.sysid;"'>
352     ]]>
353     <!ENTITY % $module_set_name.dtd.decl
354     'SYSTEM "%$module_set_name.dtd.sysid;"'>
355     <!ENTITY % $module_set_name.dtd %$module_set_name.dtd.decl;>
356     %$module_set_name.dtd;]]>
357    
358     EOH
359     $s;
360     }
361    
362 wakaba 1.1 sub model_module ($$) {
363     my ($src, $Info) = @_;
364     my $s = '';
365     for my $src (@{$src->child_nodes}) {
366     if ($src->local_name eq 'Class') {
367 wakaba 1.2 $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
368 wakaba 1.1 } elsif ($src->local_name eq 'Content') {
369     $s .= element_content_def ($src, $Info);
370     }
371     }
372     make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'model'), $s);
373     }
374    
375     sub datatype_module ($$) {
376     my ($src, $Info) = @_;
377     my $s = '';
378     for my $src (@{$src->child_nodes}) {
379     if ($src->local_name eq 'Type') {
380     $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % @{[name_of ($src, $Info)]}.datatype "@{[xml_datatype_of ($src, $Info, default => 'CDATA')]}">\n\n);
381     }
382     }
383     make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'datatype'), $s);
384     }
385    
386     sub notation_module ($$) {
387     my ($src, $Info) = @_;
388     my $s = '';
389     for my $src (@{$src->child_nodes}) {
390     if ($src->local_name eq 'Notation') {
391     $s .= qq(@{[get_desc ($src, $Info)]}<!NOTATION @{[name_of ($src, $Info)]} @{[external_id_of ($src, $Info)]}>\n\n);
392     }
393     }
394     make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'notation'), $s);
395     }
396    
397     sub qname_module ($$) {
398     my ($src, $Info) = @_;
399     my $ID = $Info->{ID};
400     my $ns = $src->get_attribute ('Namespace');
401     my $s = <<EOH;
402     <!ENTITY % NS.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
403     q(INCLUDE):q(IGNORE)]}">
404 wakaba 1.2
405 wakaba 1.3 <!-- Section A: XML Namespace Framework :::::::::::::::::::::::::: -->
406    
407     <!-- 1. Declare conditional section keyword, used to activate namespace
408     prefixing. -->
409 wakaba 1.1 <!ENTITY % $ID.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
410     q(INCLUDE):
411     $ns->get_attribute_value ('UsePrefix')==-1?
412     q(IGNORE):
413     q(%NS.prefixed;)]}">
414 wakaba 1.2
415     <!-- 2. Declare a parameter entity containing the namespace name. -->
416 wakaba 1.1 <!ENTITY % $ID.xmlns "@{[$ns->get_attribute_value ('Name')]}">
417 wakaba 1.2
418     <!-- 3. Declare parameter entities containing the default namespace prefix
419     string to use when prefixing is enabled. -->
420 wakaba 1.1 <!ENTITY % $ID.prefix "@{[$ns->get_attribute_value ('DefaultPrefix')]}">
421 wakaba 1.2
422     <!-- 4. Declare parameter entities containing the colonized prefix
423     used when prefixing is active, an empty string when it is not. -->
424 wakaba 1.1 <![%$ID.prefixed;[
425     <!ENTITY % $ID.pfx "%$ID.prefix;:">
426     ]]>
427     <!ENTITY % $ID.pfx "">
428 wakaba 1.2
429     <!-- declare qualified name extensions here -->
430     <!ENTITY % ${ID}-qname-extra.mod "">
431     %${ID}-qname-extra.mod;
432    
433 wakaba 1.3 <!-- 5. This parameter entity may be redeclared to contain any foreign
434     namespace declaration attributes for namespaces embedded. -->
435 wakaba 1.1 <!ENTITY % $ID.xmlns.extra.attrib "">
436    
437     <![%$ID.prefixed;[
438     <!ENTITY % $ID.xmlns.decl.attrib
439     "xmlns:%$ID.prefix; %URI.datatype; #FIXED '%$ID.xmlns;'">
440     ]]>
441     <!ENTITY % $ID.xmlns.decl.attrib
442     "xmlns %URI.datatype; #FIXED '%$ID.xmlns;'">
443    
444     <![%$ID.prefixed;[
445     <!ENTITY % NS.decl.attrib
446     "%$ID.xmlns.decl.attrib;
447     %$ID.xmlns.extra.attrib;">
448     ]]>
449     <!ENTITY % NS.decl.attrib
450     "%$ID.xmlns.extra.attrib;">
451    
452 wakaba 1.2 <!-- Declare a parameter entity containing all XML namespace declaration
453     attributes used, including a default xmlns declaration when prefixing
454     is inactive. -->
455 wakaba 1.1 <![%$ID.prefixed;[
456     <!ENTITY % $ID.xmlns.attrib
457     "%NS.decl.attrib;">
458     ]]>
459     <!ENTITY % $ID.xmlns.attrib
460     "%$ID.xmlns.decl.attrib;
461     %NS.decl.attrib;">
462    
463 wakaba 1.3 <!-- Section B: Qualified Names :::::::::::::::::::::::::::::::::: -->
464    
465     <!-- placeholder for qualified name redeclarations -->
466     <!ENTITY % ${ID}-qname-extra.mod "">
467     %${ID}-qname-extra.mod;
468    
469 wakaba 1.2 <!-- 6. Declare parameter entities used to provide namespace-qualified
470     names for all element types and global attribute names. -->
471 wakaba 1.1 EOH
472 wakaba 1.3 for my $lname (sort keys %{$Info->{QName}}) {
473     $s .= qq(<!ENTITY % )
474     . (dot_padding qq($Info->{ID}.$lname.qname),
475     length => 15 + length ($Info->{ID}), dot => ' ')
476     . qq( "%$Info->{ID}.pfx;$lname">\n);
477 wakaba 1.1 }
478     $s .= qq(\n);
479 wakaba 1.3 for my $lname (sort keys %{$Info->{QNameA}}) {
480     $s .= qq(<!ENTITY % )
481     . (dot_padding qq($Info->{ID}.$lname.attrib.qname),
482     length => 15 + length ($Info->{ID}), dot => ' ')
483     . qq( "%$Info->{ID}.prefix;:$lname">\n);
484 wakaba 1.1 }
485     $s .= qq(\n);
486 wakaba 1.3 for my $lname (sort keys %{$Info->{QNameB}}) {
487     $s .= qq(<!ENTITY % )
488     . (dot_padding qq($Info->{ID}.$lname.attribute.qname),
489     length => 15 + length ($Info->{ID}), dot => ' ')
490     . qq( "%$Info->{ID}.pfx;$lname">\n);
491 wakaba 1.1 }
492 wakaba 1.3 make_module ($src->get_attribute ('QName', make_new_node => 1), $Info, 'qname', $s);
493 wakaba 1.1 }
494    
495     sub get_name ($$;$) {
496     my ($src, $Info, $key) = @_;
497     my $name = $src->get_attribute_value ($key || 'Name');
498     if ($name =~ /^:(.+)/) {
499     $name = $1;
500     } elsif ($name =~ /([^:]+):(.+)/) {
501     $name = qq($1.$2);
502     } else {
503     $name = qq($Info->{ID}.$name);
504     }
505     $name;
506     }
507    
508     sub get_qname ($$) {
509     my ($src, $Info) = @_;
510     my $name = $src->get_attribute_value ('Name');
511     if ($name =~ /"([^"]+)"/) {
512     $name = qq($1);
513     } elsif ($name =~ /^:(.+)/) {
514     $name = qq(%$1.qname;);
515     } elsif ($name =~ /([^:]+):(.+)/) {
516     $name = qq(%$1.$2.qname;);
517     } elsif ($name =~ /\{([^{}]+)\}/) {
518     $Info->{QNameB}->{$1} = 1;
519     $name = qq(%$Info->{ID}.$1.attribute.qname;);
520     } else {
521     $Info->{QNameA}->{$name} = 1;
522     $name = qq(%$Info->{ID}.$name.attrib.qname;);
523     }
524     $name;
525     }
526    
527     sub get_atype ($$) {
528     my ($src, $Info) = @_;
529     my $name = $src->get_attribute_value ('Type');
530     if ($name =~ /^:(.+)/) {
531     $name = qq(%$1.datatype;);
532     } elsif ($name =~ /([^:]+):(.+)/) {
533     $name = qq(%$1.$2.datatype;);
534     } elsif ($name =~ /"([^"]+)"/) {
535     $name = qq($1);
536     } else {
537     $name = qq(%$Info->{ID}.$name.datatype;);
538     }
539     $name;
540     }
541    
542     sub get_adefault ($$) {
543     my ($src, $Info) = @_;
544     my $name = $src->get_attribute_value ('Default');
545     if (defined $name) {
546     } else {
547     $name = qq(#IMPLIED);
548     }
549     $name;
550     }
551    
552 wakaba 1.3 sub get_desc ($$;%) {
553     my ($src, $Info, %opt) = @_;
554 wakaba 1.1 my $desc = $src->get_attribute_value ('Description');
555     $desc =~ s/\n/\n /g;
556 wakaba 1.3 if (length $desc) {
557     $desc = qq($opt{prefix}$desc);
558     $desc .= q( ) if $opt{padding_length};
559     $desc = q(<!-- ).(dot_padding $desc, length => $opt{padding_length},
560     dot => $opt{padding_dot}).qq( -->\n);
561     }
562 wakaba 1.1 $desc;
563     }
564    
565     sub attset_def ($$) {
566     my ($src, $Info) = @_;
567     my $name = get_name ($src, $Info);
568     my $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % $name.attrib\n\t);
569     my @s;
570     if ($name eq qq($Info->{ID}.common)) {
571     push @s, qq(%$Info->{ID}.common.extra.attrib;);
572     push @s, qq(%$Info->{ID}.xmlns.attrib;);
573     }
574     for my $src (@{$src->child_nodes}) {
575     ## Attribute Definition
576     if ($src->local_name eq 'Attribute') {
577     push @s, attrib_def ($src, $Info);
578     ## Reference to Attribute Definition
579     } elsif ($src->local_name eq 'ref') {
580     push @s, attrib_ref ($src, $Info);
581     } elsif ($src->local_name eq 'REF') {
582     push @s, attrib_REF ($src, $Info);
583     }
584     }
585     $s .= paralit join "\n\t", @s;
586     $s .= qq(>\n\n);
587     $s;
588     }
589    
590     sub attrib_module ($$) {
591     my ($src, $Info) = @_;
592     my $s = <<EOH;
593     <!ENTITY % $Info->{ID}.common.extra.attrib "">
594    
595     EOH
596     my $output_common = 0;
597     for my $src (@{$src->child_nodes}) {
598     ## Attributes Set
599     if ($src->local_name eq 'Attribute' or $src->local_name eq 'AttributeSet') {
600     $s .= attset_def ($src, $Info);
601     $output_common = 1 if get_name ($src, $Info) eq qq($Info->{ID}.common);
602     }
603     }
604     unless ($output_common) {
605     $s .= <<EOH;
606     <!ENTITY % $Info->{ID}.common.attrib
607     "%$Info->{ID}.common.extra.attrib;
608     %$Info->{ID}.xmlns.attrib;">
609    
610     EOH
611     }
612     make_module ($src, $Info, ($src->get_attribute_value ('ID') || 'attribs'), $s);
613     }
614    
615     sub attrib_def ($$) {
616     my ($src, $Info) = @_;
617     my $s = qq(@{[get_qname ($src, $Info)]} @{[get_atype ($src, $Info)]} @{[get_adefault ($src, $Info)]});
618     $s;
619     }
620    
621     sub attrib_ref ($$) {
622     my ($src, $Info) = @_;
623     my $name = $src->value;
624     if ($name =~ /^:(.+)/) {
625     $name = $1;
626     } elsif ($name =~ /([^:]+):(.+)/) {
627     $name = qq($1.$2);
628     } else {
629     $name = qq($Info->{ID}.$name);
630     }
631     qq(%$name.attrib;);
632     }
633    
634     sub attrib_REF ($$) {
635     my ($src, $Info) = @_;
636     {
637     'xml:base' => q<xml:base %URI.datatype; #IMPLIED>,
638     'xml:lang' => q<xml:lang %LanguageCode.datatype; #IMPLIED>,
639     'xml:space' => q<xml:space (default|preserve) #IMPLIED>,
640     }->{$src->value};
641     }
642    
643     sub submodule ($$) {
644     my ($src, $Info) = @_;
645 wakaba 1.3 local $Info->{elements} = [];
646 wakaba 1.1 my $s = submodule_declarations ($src, $Info);
647     make_module ($src, $Info, $src->get_attribute_value ('ID'), $s);
648     }
649    
650     sub submodule_declarations ($$) {
651     my ($src, $Info) = @_;
652     my $s = '';
653     for my $src (@{$src->child_nodes}) {
654     if ($src->local_name eq 'Element') {
655     $s .= element_def ($src, $Info);
656     } elsif ($src->local_name eq 'Attribute') {
657     $s .= attlist_def ($src, $Info);
658     } elsif ($src->local_name eq 'AttributeSet') {
659     $s .= attset_def ($src, $Info);
660 wakaba 1.2 } elsif ($src->local_name eq 'Class') {
661     $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
662 wakaba 1.1 } elsif ($src->local_name eq 'Content') {
663     $s .= element_content_def ($src, $Info);
664     } elsif ($src->local_name eq 'IfModuleSet') {
665     $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
666     $s .= submodule_declarations ($src, $Info);
667 wakaba 1.3 $s .= qq(<!-- end of -->]]>\n);
668 wakaba 1.1 } elsif ($src->local_name eq 'ElementSwitch') {
669     $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.element "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
670     } elsif ($src->local_name eq 'AttributeSwitch') {
671     $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.attlist "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
672 wakaba 1.2 } elsif ($src->local_name eq 'ModuleSwitch') {
673     $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.module "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
674     } elsif ($src->local_name eq 'GeneralEntity') {
675     $s .= entity_declaration ($src, $Info, param => 0);
676 wakaba 1.1 } elsif ($src->local_name eq 'ParameterEntity') {
677 wakaba 1.2 $s .= entity_declaration ($src, $Info, param => 1);
678 wakaba 1.1 }
679     }
680     $s;
681     }
682    
683     sub element_content_def ($$) {
684     my ($src, $Info) = @_;
685     qq(<!ENTITY % @{[name_of ($src, $Info, key => 'ElementType')]}.content @{[paralit convert_content_model ($src, $Info, default => 'EMPTY')]}>\n);
686     }
687    
688     sub element_def ($$) {
689     my ($src, $Info) = @_;
690     my $name = get_name ($src, $Info);
691     my $mname = $name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name);
692 wakaba 1.3 my $short_name = $name;
693     if ($name =~ /^\Q$Info->{ID}\E\.(.+)/) {
694     $Info->{QName}->{$1} = 1;
695     push @{$Info->{elements}}, $1;
696     $short_name = $1;
697     }
698     my $s = get_desc $src, $Info, prefix => qq($short_name: ),
699     padding_length => 51, padding_dot => q(.);
700     $s .= "\n";
701     $s .= xml_parameter_ENTITY qq($mname.element), value => 'INCLUDE';
702     $s .= xml_condition_section (qq($mname.element) =>
703     xml_parameter_ENTITY
704     (qq($name.content),
705     value => convert_content_model ($src, $Info, default => 'EMPTY'))
706     . xml_parameter_ENTITY (qq($name.qname), value => $short_name)
707     . qq(<!ELEMENT %$name.qname; %$name.content;>\n));
708     $s .= "\n";
709 wakaba 1.1 $s .= attlist_def (scalar $src->get_attribute ('Attribute', make_new_node => 1), $Info, $mname);
710     $s;
711     }
712    
713     sub get_model_token ($$) {
714     my ($name, $Info) = @_;
715     my $suffix = '.qname';
716     if ($name =~ s/^\$//) {
717     $suffix = $name =~ /\.(?:mix|class|content|datatype)$/ ? '' : '.class';
718     }
719     if ($name =~ /^:(.+)/) {
720     $name = qq(%$1$suffix;);
721     } elsif ($name =~ /([^:]+):(.+)/) {
722     $name = qq(%$1.$2$suffix;);
723     } elsif ($name =~ /"([^"]+)"/) {
724     $name = qq($1);
725     } else {
726     $name = qq(%$Info->{ID}.$name$suffix;);
727     }
728     $name;
729     }
730    
731     sub attlist_def ($$;$) {
732     my ($src, $Info, $name) = @_;
733     $name ||= get_name ($src, $Info, 'ElementType');
734     my $mname = get_name ($src, $Info);
735     $mname = ($name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name))
736     if $mname eq "$Info->{ID}.";
737     $Info->{QName}->{$1} = 1 if $name =~ /^\Q$Info->{ID}\E\.(.+)/;
738 wakaba 1.3 my $s = qq(<!ATTLIST %$name.qname;);
739 wakaba 1.1 for my $src (@{$src->child_nodes}) {
740     ## Attribute Definition
741     if ($src->local_name eq 'Attribute') {
742     $s .= "\n\t". attrib_def ($src, $Info);
743     ## Reference to Attribute Definition
744     } elsif ($src->local_name eq 'ref') {
745     $s .= "\n\t". attrib_ref ($src, $Info);
746     } elsif ($src->local_name eq 'REF') {
747     $s .= "\n\t". attrib_REF ($src, $Info);
748     }
749     }
750     if ($_[2]) {
751     $s .= qq(\n\t%$Info->{ID}.common.attrib;);
752     }
753 wakaba 1.3 $s .= qq(>\n);
754     qq(@{[description ($src, $Info)]}<!ENTITY % $mname.attlist "INCLUDE">\n)
755     . xml_condition_section (qq($mname.attlist) => $s)
756     . "\n";
757 wakaba 1.1 }
758    
759 wakaba 1.3 sub make_module ($$$$;%) {
760     my ($src, $Info, $id, $s, %opt) = @_;
761 wakaba 1.1 my $name = $src->get_attribute_value ('Name')
762 wakaba 1.3 || {arch => q/Base Architecture/,
763     attribs => q/Common Attributes/,
764     blkphras => q/Block Phrasal/,
765     blkpres => q/Block Presentation/,
766     blkstruct => q/Block Structural/,
767     charent => q/Character Entities/,
768 wakaba 1.1 datatype => q/Datatypes/,
769 wakaba 1.3 framework => q/Modular Framework/,
770     inlphras => q/Inline Phrasal/,
771     inlpres => q/Inline Presentation/,
772     inlstruct => q/Inline Structural/,
773     legacy => q/Legacy Markup/,
774     list => q/Lists/,
775     meta => q/Metainformation/,
776 wakaba 1.1 model => q/Document Model/,
777 wakaba 1.3 notations => q/Notations/,
778     pres => q/Presentation/,
779     qname => q/QName (Qualified Name)/,
780     struct => q/Document Structure/,
781     text => q/Text/,
782 wakaba 1.1 }->{$id}
783     || $id;
784 wakaba 1.2 return unless $s;
785 wakaba 1.1
786     my $r = <<EOH;
787 wakaba 1.3 <!-- ...................................................................... -->
788     <!-- @{[do{
789     my $s = qq($Info->{Name} $name Module );
790     if (70 - length $s > 0) {
791     $s = dot_padding $s, length => 70, dot => q(.);
792     } else {
793     $s = qq( $name Module );
794     $s = qq($Info->{Name}\n ) . dot_padding $s, length => 70, dot => q(.);
795     }
796     $s;
797     }]} -->
798     <!-- file: $Info->{ID}-$id.mod
799 wakaba 1.1
800 wakaba 1.3 $Info->{Description}
801     Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}, All Rights Reserved.
802 wakaba 1.1 Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
803 wakaba 1.3 (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]
804     ]} (Generated by $SCRIPT_NAME/$VERSION)
805    
806     This DTD module is identified by the SYSTEM identifier:
807 wakaba 1.1
808 wakaba 1.3 SYSTEM "$Info->{BaseURI}$Info->{ID}-$id.mod"
809    
810     ...................................................................... -->
811    
812     EOH
813     ## TODO: Support PUBLIC identifier.
814 wakaba 1.1
815 wakaba 1.3 ## Module description
816     my @para = ({
817     arch => (join "\n",
818     q!This optional module includes declarations that enable to be used!,
819     q!as a base architecture according to the 'Architectural Forms Definition!,
820     q!Requirements' (Annex A.3, ISO/IEC 10744, 2nd edition). For more!,
821     q!information on use of architectural forms, see the HyTime web site at!,
822     q!<http://www.hytime.org/>.!),
823     attribs => q/This module declares many of the common attributes./,
824     blkphras => qq/This module declares the element types and their attributes used\n/.
825     q/to support block-level phrasal markup./,
826     blkpres => qq/This module declares the element types and their attributes used\n/.
827     q/to support block-level presentational markup./,
828     blkstruct => qq/This module declares the element types and their attributes used\n/.
829     q/to support block-level structural markup./,
830     charent => q/This module declares the set of character entities./,
831     datatype => q/This module defines containers for the datatypes./,
832     framework => qq/This module imstantiates the modules needed to support\n/.
833     q/the modularization model./,
834     inlphras => qq/This module declares the element types and their attributes used\n/.
835     q/to support inline phrasal markup./,
836     inlpres => qq/This module declares the element types and their attributes used\n/.
837     q/to support inline presentational markup./,
838     inlstruct => qq/This module declares the element types and their attributes used\n/.
839     q/to support inline structural markup./,
840     legacy => q/This module declares additional markup that is considered obsolete./,
841     list => qq/This module declares the list-oriented element types\n/.
842     q/and their attributes./,
843     meta => qq/This module declares the element types and their attributes\n/.
844     q/to support metainformation markup./,
845     model => qq/This model describes the groupings of element types that\n/.
846     q/make up common content models./,
847     pres => qq/This module declares the element types and their attributes used\n/.
848     q/to support presentational markup./,
849     qname => (join "\n",
850     q!This module is contained in two parts, labeled Section 'A' and 'B':!,
851     q!!,
852     q! Section A declares parameter entities to support namespace-qualified!,
853     q! names, namespace declarations, and name prefixing.!,
854     q!!,
855     q! Section B declares parameter entities used to provide namespace-qualified!,
856     q! names for all element types and global attribute names.!),
857     struct => qq/This module defines the major structural element types and\n/.
858     q/their attributes./,
859     }->{$id}, $src->get_attribute_value ('Description'));
860     unshift @para, ' '.join ', ', sort @{$Info->{elements}||[]} if @{$Info->{elements}||[]};
861     if (@para) {
862     $r .= <<EOH;
863     <!-- $name
864    
865     @{[make_paragraphs \@para, indent => ' ']}
866     -->
867    
868 wakaba 1.1 EOH
869 wakaba 1.3 }
870 wakaba 1.1
871     $r .= $s;
872    
873     $r .= qq(\n<!-- end of $Info->{ID}-$id.mod -->\n);
874    
875     my $file = qq"$Info->{ID}-$id.mod";
876     open FILE, '>', $file or die "$0: $file: $!";
877     print FILE $r;
878     close FILE;
879     print STDERR "$0: $file created\n";
880     }
881    
882     sub make_dtd ($$$$) {
883     my ($src, $Info, $id, $s) = @_;
884     $id = "-$id" if $id;
885    
886     my $r = <<EOH;
887 wakaba 1.3 <!-- ....................................................................... -->
888     <!-- @{[ dot_padding "$Info->{Name} DTD ", length => 71, dot => q(.) ]} -->
889     <!-- file: $Info->{ID}.dtd
890     -->
891    
892     <!-- $Info->{Name} DTD
893    
894     $Info->{Description}
895    
896     Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}, All Rights Reserved.
897    
898     Permission to use, copy, modify and distribute this DTD and its
899     accompanying documentation for any purpose and without fee is hereby
900     granted in perpetuity, provided that the above copyright notice and
901     this paragraph appear in all copies. The copyright holders make no
902     representation about the suitability of the DTD for any purpose.
903    
904     It is provided "as is" without expressed or implied warranty.
905    
906     Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
907     (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]]}
908    
909     -->
910     <!-- This is the driver file for the $Info->{Name} DTD.
911    
912     This DTD is identified by the SYSTEM identifier:
913    
914 wakaba 1.1 SYSTEM "$Info->{BaseURI}$Info->{ID}$id.dtd"
915 wakaba 1.3 -->
916    
917 wakaba 1.1 EOH
918    
919     $r .= $s;
920    
921     $r .= qq(\n<!-- end of $Info->{ID}$id.dtd -->\n);
922    
923     my $file = qq"$Info->{ID}$id.dtd";
924     open FILE, '>', $file or die "$0: $file: $!";
925     print FILE $r;
926     close FILE;
927     print STDERR "$0: $file created\n";
928     }
929    
930    
931     =head1 NAME
932    
933 wakaba 1.3 mkdtds.pl - Modularized XML Document Type Definition (DTD) Generator
934 wakaba 1.1
935     =head1 DESCRIPTION
936    
937 wakaba 1.3 This script generates XML DTD module implementations and/or DTD drivers,
938     that can be used with modularized XHTML DTDs.
939 wakaba 1.1
940     =head1 USAGE
941    
942     $ perl mkdtds.pl driver.dds
943     mkdtds.pl: driver.dtd created
944     mkdtds.pl: driver-model.mod created
945    
946     $ perl mkdtds.pl moduleset.dms
947     mkdtds.pl: moduleset-datatype.mod created
948     mkdtds.pl: moduleset-attrib.mod created
949     mkdtds.pl: moduleset-module1.mod created
950    
951     =head1 DTD SOURCE FORMAT
952    
953     (((See examples on <http://suika.fam.cx/gate/cvs/markup/>)))
954    
955 wakaba 1.3 =head1 REQUIRED MODULES
956 wakaba 1.1
957 wakaba 1.3 This script uses C<Message::Markup::SuikaWikiConfig20::Node> and
958     C<Message::Markup::SuikaWikiConfig20::Parser>. Please retrive it from
959     <http://suika.fam.cx/gate/cvs/messaging/manakai/lib/Message/Markup/SuikaWikiConfig20/>
960     and put into your C<lib> directory.
961 wakaba 1.1
962     =head1 AUTHOR
963    
964     Wakaba <w@suika.fam.cx>
965    
966     =head1 LICENSE
967    
968 wakaba 1.3 Copyright 2003-2004 Wakaba <w@suika.fam.cx>
969 wakaba 1.1
970     This program is free software; you can redistribute it and/or
971     modify it under the same terms as Perl itself.
972    
973 wakaba 1.3 Note that author claims no copyright with regard to DTD modules/drivers generated
974     by this script. Author(s) of DTD modules/drivers should explicily state their
975     license terms in them and their documentation (if any).
976 wakaba 1.1
977     =cut

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24