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

Contents of /markup/tool/mkdtds.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations) (download)
Fri Oct 24 13:37:38 2003 UTC (21 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.1: +103 -7 lines
File MIME type: text/plain
Support of full-DTD importing

1 wakaba 1.1 use strict;
2     {require SuikaWiki::Markup::SuikaWikiConfig20::Parser;
3    
4     my $parser = new SuikaWiki::Markup::SuikaWikiConfig20::Parser;
5     local $/ = undef;
6     my $src = $parser->parse_text (scalar <>);
7     my $Info = {};
8    
9     for my $src ($src->get_attribute ('ModuleSet')
10     || $src->get_attribute ('DocumentType')) {
11     for (qw/ID Copyright BaseURI/) {
12     $Info->{$_} = $src->get_attribute_value ($_);
13     }
14     $Info->{Name} = $src->get_attribute_value ('Name')
15     .' '.$src->get_attribute_value ('Version');
16     $Info->{ns} = $src->get_attribute ('Namespace');
17     }
18    
19     for (@{$src->child_nodes}) {
20     if ($_->local_name eq 'Attribute') {
21     attrib_module ($_, $Info);
22     } elsif ($_->local_name eq 'Datatype') {
23     datatype_module ($_, $Info);
24     } elsif ($_->local_name eq 'Notation') {
25     notation_module ($_, $Info);
26     } elsif ($_->local_name eq 'Module') {
27     submodule ($_, $Info);
28     } elsif ($_->local_name eq 'Model') {
29     model_module ($_, $Info);
30 wakaba 1.2 $Info->{has_model} = 1;
31 wakaba 1.1 } elsif ($_->local_name eq 'Driver') {
32     dtd_driver ($_, $Info);
33     }
34     }
35    
36     if (ref $src->get_attribute ('ModuleSet')) {
37     qname_module ($src->get_attribute ('ModuleSet'), $Info);
38     }
39     exit}
40    
41     sub submodule_id_of ($$;%) {
42     my ($src, $Info, %opt) = @_;
43     my $id = $src->get_attribute_value ('ID') || $opt{default};
44     unless ($id) {
45     die "$0: Submodule identifier not specified";
46     }
47     $id;
48     }
49     sub xml_datatype_of ($$;%) {
50     my ($src, $Info, %opt) = @_;
51     my $type = $src->get_attribute_value ('XMLType') || $opt{default};
52     $type =~ s/\s+//g;
53     $type;
54     }
55 wakaba 1.2 sub system_id_of ($$;%) {
56     my ($src, $Info, %opt) = @_;
57     my $sysid = $src->get_attribute_value ('SYSTEM');
58     if ($sysid =~ /<([^>]+)>/) {
59     return $1;
60     } else {
61     return $opt{base}.($sysid || $opt{default});
62     }
63     }
64 wakaba 1.1 sub external_id_of ($$;%) {
65     my ($src, $Info, %opt) = @_;
66 wakaba 1.2 my $sysid = system_id_of ($src, $Info, %opt);
67 wakaba 1.1 my $pubid = $src->get_attribute_value ('PUBLIC');
68     if ($pubid) {
69     if ($sysid) {
70     return qq(PUBLIC "$pubid"\n\t "$sysid");
71     } else {
72     return qq(PUBLIC "$pubid");
73     }
74     } else {
75     return qq(SYSTEM "$sysid");
76     }
77     }
78     sub name_of ($$;%) {
79     my ($src, $Info, %opt) = @_;
80     unless (ref $src) {require Carp; Carp::croak ('$src undefined')}
81     my $name = $src->get_attribute_value ($opt{key} || 'Name');
82     if ($name =~ /^:(.+)/) { ## Global namespace
83     return $1;
84     } elsif ($name =~ /([^:]+):(.+)/) { ## Named space
85     return $1.($opt{delim}||'.').$2;
86     } else { ## Default namespace
87     return $Info->{ID}.($opt{delim}||'.').$name;
88     }
89     }
90     sub local_name_of ($$;%) {
91     my ($src, $Info, %opt) = @_;
92     my $name = $src->get_attribute_value ($opt{key} || 'Name');
93     if ($name =~ /^:(.+)/) { ## Global namespace
94     return $1;
95     } elsif ($name =~ /[^:]+:(.+)/) { ## Named space
96     return $1;
97     } else { ## Default namespace
98     return $name;
99     }
100     }
101     sub set_name_of ($$;%) {
102     my ($src, $Info, %opt) = @_;
103     my $name = $src->get_attribute_value ($opt{key} || 'Name');
104     if ($name =~ /^:.+/) { ## Global namespace
105     return 'XHTML';
106     } elsif ($name =~ /([^:]+):.+/) { ## Named space
107     return $1;
108     } else { ## Default namespace
109     return $Info->{ID};
110     }
111     }
112     sub class_name_of ($$;%) {
113     my ($src, $Info, %opt) = @_;
114     my $name = name_of ($src, $Info, %opt);
115     unless ($name =~ /\.(class|mix|content|datatype)$/) {
116     $name .= '.class';
117     }
118     $name;
119     }
120     sub convert_content_model ($$;%) {
121     my ($src, $Info, %opt) = @_;
122     my $model = $src->get_attribute_value ($opt{key} || 'Content') || $opt{default};
123     $model =~ s/\s//g;
124     my $nonsymbol = qr/[^%#?,\$;()+*|:]/;
125     $model =~ s/(?<![%#.])((?:\$|\b)$nonsymbol+(?::$nonsymbol+)?|\$?:$nonsymbol+|"[^"]+")/get_model_token ($1, $Info)/ge;
126     $model;
127     }
128 wakaba 1.2 sub sparalit ($) {
129     my $s = paralit (shift);
130     $s =~ s/&/&#x26;/g;
131     $s =~ s/%/&#x25;/g;
132     $s;
133     }
134 wakaba 1.1 sub paralit ($) {
135     my $s = shift;
136     if ($s =~ /"/) {
137     if ($s =~ /'/) {
138     $s =~ s/'/&#x27;/g;
139     return qq("$s");
140     } else {
141     return qq('$s');
142     }
143     } else {
144     return qq("$s");
145     }
146     }
147     sub description ($$;%) {
148     my ($src, $Info, %opt) = @_;
149     my $desc = $src->get_attribute_value ('Description');
150     $desc =~ s/\n/\n /g;
151     unless ($desc) {
152     $desc = {
153     load_module => {
154     AttributeModule => q/Common Attributes Module/,
155     DatatypeModule => q/Datatypes Module/,
156     NotationModule => q/Notation Module/,
157     QNameModule => q/QName Module/,
158     },
159     }->{$opt{context}}->{$opt{id} || $src->get_attribute_value ($opt{id_key}||'ID')};
160     }
161     $desc = qq(<!-- $desc -->\n) if $desc;
162     $desc;
163     }
164    
165 wakaba 1.2 sub entity_declaration ($$;%) {
166     my ($src, $Info, %opt) = @_;
167     my $val;
168     if ($src->get_attribute_value ('ID')
169     || $src->get_attribute_value ('SYSTEM')
170     || $src->get_attribute_value ('PUBLIC')) {
171     $val = "\n\t".external_id_of ($src, $Info, default => $src->get_attribute_value ('ID'));
172     } elsif (ref $src->get_attribute ('Declaration')) {
173     $val = "\n\t".sparalit submodule_declarations ($src->get_attribute ('Declaration'), $Info);
174     } else {
175     $val = paralit $src->get_attribute_value ('EntityValue');
176     }
177     my $s = <<EOH;
178     @{[description ($src, $Info)]}<!ENTITY @{[$opt{param}?'% ':'']}@{[$src->get_attribute_value ('Name')]} $val>
179    
180     EOH
181     $s;
182     }
183 wakaba 1.1
184     sub dtd_driver ($$) {
185     my ($src, $Info) = @_;
186     my $s = '';
187     my %s;
188     my @module_set;
189     for my $src (@{$src->child_nodes}) {
190     if ($src->local_name eq 'Module') {
191     $s .= dtd_driver_load_module ($src, $Info);
192 wakaba 1.2 } elsif ($src->local_name eq 'DTD') {
193     $s .= dtd_driver_load_dtd ($src, $Info);
194 wakaba 1.1 } elsif ($src->local_name eq 'ModuleSet') {
195     push @module_set, $src;
196     } elsif ($src->local_name =~ /^(?:QName|Attribute|Datatype|Notation)Module/) {
197     $s{$src->local_name} .= dtd_driver_load_module ($src, $Info);
198     } elsif ($src->local_name eq 'IfModuleSet') {
199     $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
200     $s .= submodule_declarations ($src, $Info);
201     $s .= qq(]]>\n);
202     } elsif ($src->local_name eq 'GeneralEntity') {
203 wakaba 1.2 $s .= entity_declaration ($src, $Info, param => 0);
204 wakaba 1.1 } elsif ($src->local_name eq 'ParameterEntity') {
205 wakaba 1.2 $s .= entity_declaration ($src, $Info, param => 1);
206 wakaba 1.1 }
207     }
208    
209 wakaba 1.2 $s{ModelModule} = $src->get_attribute_value ('NoModelModule') ? '' :
210     $Info->{has_model} ? <<EOH : '';
211 wakaba 1.1 <!-- Document Model module -->
212     <!ENTITY % $Info->{ID}-model.module "INCLUDE">
213     <![%$Info->{ID}-model.module;[
214     <!ENTITY % $Info->{ID}-model.decl
215     'SYSTEM "$Info->{ID}-model.mod"'>
216     <!ENTITY % $Info->{ID}-model.mod %$Info->{ID}-model.decl;>
217     %$Info->{ID}-model.mod;]]>
218    
219     EOH
220    
221     $s = dtd_driver_module_sets (\@module_set, $Info)
222     . $s{QNameModule}.$s{DatatypeModule}.$s{NotationModule}.$s{AttributeModule}
223     . $s{ModelModule}
224     .$s;
225     make_dtd ($src, $Info, $src->get_attribute_value ('ID'), $s);
226     }
227    
228     sub dtd_driver_module_sets ($$) {
229     my ($srces, $Info) = @_;
230     my @src = map {{src => $_}} @$srces;
231     my $s = qq(<!-- Switchers to include/ignore each vocabulary -->\n);
232     for my $module_set (@src) {
233     $module_set->{ID} = $module_set->{src}->get_attribute_value ('ID') || 'XHTML';
234     $s .= qq(<!ENTITY % $module_set->{ID}.module "@{[$module_set->{src}->get_attribute_value ('Default') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
235     }
236     $s .= qq(\n<!-- Namespace names -->\n);
237     for my $module_set (@src) {
238     $module_set->{ns} = $module_set->{src}->get_attribute ('Namespace');
239     $s .= qq(<!ENTITY % $module_set->{ID}.xmlns "@{[$module_set->{ns}->get_attribute_value ('Name')]}">\n);
240     }
241     $s .= qq(\n<!-- Base URIs for the relavant DTD modules -->\n);
242     for my $module_set (@src) {
243     $s .= qq(<!ENTITY % $module_set->{ID}.sysid.base "@{[$module_set->{src}->get_attribute_value ('BaseURI')]}">\n);
244     }
245     $s .= qq(\n<!-- Namespace prefix -->\n);
246     $s .= qq(<!ENTITY % NS.prefixed "@{[$Info->{ns}->get_attribute_value ('UsePrefix') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
247     for my $module_set (@src) {
248     $s .= qq(<!ENTITY % $module_set->{ID}.prefix "@{[$module_set->{ns}->get_attribute_value ('DefaultPrefix')]}">\n);
249     $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);
250     }
251     $s .= qq(\n<!-- a URI reference -->\n<!ENTITY % URI.datatype "CDATA">\n);
252     $s .= qq(\n<!-- Placefolders for foreign namespace declarations -->\n);
253     for my $module_set (@src) {
254     $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.extra.attrib "">\n);
255     }
256     $s .= qq(\n<!-- Namespace declarations -->\n);
257     for my $module_set (@src) {
258     $s .= qq(<![%$module_set->{ID}.module;[
259     <![%$module_set->{ID}.prefixed;[
260     <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
261     "xmlns:%$module_set->{ID}.prefix; %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
262     ]]>
263     <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
264     "xmlns %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
265     ]]>
266     <!ENTITY % $module_set->{ID}.xmlns.decl.attrib "">\n\n);
267     }
268     $s .= qq(\n<!ENTITY % NS.decl.attrib
269     ").join ("\n\t", (map {qq(%$_->{ID}.xmlns.decl.attrib;)} @src),
270     map {qq(%$_->{ID}.xmlns.extra.attrib;)} @src).qq(">\n);
271     $s .= qq(\n);
272     for my $module_set (@src) {
273     $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.attrib "%NS.decl.attrib;">\n);
274     }
275     $s .= qq(\n\n);
276     $s;
277     }
278    
279     sub dtd_driver_load_module ($$) {
280     my ($src, $Info) = @_;
281     my $module_name = name_of ($src, $Info, key => 'ID');
282     my $module_hyphen_name = name_of ($src, $Info, key => 'ID', delim => '-');
283     my $module_set_name = set_name_of ($src, $Info, key => 'ID');
284     my $module_id = local_name_of ($src, $Info, key => 'ID');
285    
286     my $s .= <<EOH;
287     @{[description ($src, $Info, context => 'load_module', id => $src->local_name)]}<![%$module_set_name.module;[
288 wakaba 1.2 <!ENTITY % $module_name.module "@{[$src->get_attribute_value ('Default') >= 0 ? 'INCLUDE' : 'IGNORE']}">
289 wakaba 1.1 <![%$module_name.module;[
290     @{[submodule_declarations ($src, $Info)]}<!ENTITY % $module_name.decl
291     @{[paralit external_id_of ($src, $Info, default => qq($module_hyphen_name.mod), base => qq(%$module_set_name.sysid.base;))]}>
292     <!ENTITY % $module_name.mod %$module_name.decl;>
293     %$module_name.mod;]]>
294     ]]>
295    
296     EOH
297     $s;
298     }
299 wakaba 1.2
300     sub dtd_driver_load_dtd ($$) {
301     my ($src, $Info) = @_;
302     my $module_set_name = $src->get_attribute_value ('ID');
303    
304     my $s .= <<EOH;
305     @{[description ($src, $Info)]}<![%$module_set_name.module;[
306     @{[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;))]}">
307     @{[do{
308     my $pubid = $src->get_attribute_value ('PUBLIC');
309     if ($pubid) {
310     qq(<!ENTITY % $module_set_name.dtd.fpi "$pubid">\n<!ENTITY % $module_set_name.dtd.fpi.defined "INCLUDE">\n);
311     } else {
312     qq(<!ENTITY % $module_set_name.dtd.fpi "">\n<!ENTITY % $module_set_name.dtd.fpi.defined "IGNORE">\n);
313     }
314     }]}
315     <![%$module_set_name.dtd.fpi.defined;[
316     <!ENTITY % $module_set_name.dtd.decl
317     'PUBLIC "%$module_set_name.dtd.fpi;"
318     "%$module_set_name.dtd.sysid;"'>
319     ]]>
320     <!ENTITY % $module_set_name.dtd.decl
321     'SYSTEM "%$module_set_name.dtd.sysid;"'>
322     <!ENTITY % $module_set_name.dtd %$module_set_name.dtd.decl;>
323     %$module_set_name.dtd;]]>
324    
325     EOH
326     $s;
327     }
328    
329 wakaba 1.1 sub model_module ($$) {
330     my ($src, $Info) = @_;
331     my $s = '';
332     for my $src (@{$src->child_nodes}) {
333     if ($src->local_name eq 'Class') {
334 wakaba 1.2 $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
335 wakaba 1.1 } elsif ($src->local_name eq 'Content') {
336     $s .= element_content_def ($src, $Info);
337     }
338     }
339     make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'model'), $s);
340     }
341    
342     sub datatype_module ($$) {
343     my ($src, $Info) = @_;
344     my $s = '';
345     for my $src (@{$src->child_nodes}) {
346     if ($src->local_name eq 'Type') {
347     $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % @{[name_of ($src, $Info)]}.datatype "@{[xml_datatype_of ($src, $Info, default => 'CDATA')]}">\n\n);
348     }
349     }
350     make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'datatype'), $s);
351     }
352    
353     sub notation_module ($$) {
354     my ($src, $Info) = @_;
355     my $s = '';
356     for my $src (@{$src->child_nodes}) {
357     if ($src->local_name eq 'Notation') {
358     $s .= qq(@{[get_desc ($src, $Info)]}<!NOTATION @{[name_of ($src, $Info)]} @{[external_id_of ($src, $Info)]}>\n\n);
359     }
360     }
361     make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'notation'), $s);
362     }
363    
364     sub qname_module ($$) {
365     my ($src, $Info) = @_;
366     my $ID = $Info->{ID};
367     my $ns = $src->get_attribute ('Namespace');
368     my $s = <<EOH;
369     <!ENTITY % NS.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
370     q(INCLUDE):q(IGNORE)]}">
371 wakaba 1.2
372     <!-- 1. Declare conditional section keyword, used to activate namespace prefixing. -->
373 wakaba 1.1 <!ENTITY % $ID.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
374     q(INCLUDE):
375     $ns->get_attribute_value ('UsePrefix')==-1?
376     q(IGNORE):
377     q(%NS.prefixed;)]}">
378 wakaba 1.2
379     <!-- 2. Declare a parameter entity containing the namespace name. -->
380 wakaba 1.1 <!ENTITY % $ID.xmlns "@{[$ns->get_attribute_value ('Name')]}">
381 wakaba 1.2
382     <!-- 3. Declare parameter entities containing the default namespace prefix
383     string to use when prefixing is enabled. -->
384 wakaba 1.1 <!ENTITY % $ID.prefix "@{[$ns->get_attribute_value ('DefaultPrefix')]}">
385 wakaba 1.2
386     <!-- 4. Declare parameter entities containing the colonized prefix
387     used when prefixing is active, an empty string when it is not. -->
388 wakaba 1.1 <![%$ID.prefixed;[
389     <!ENTITY % $ID.pfx "%$ID.prefix;:">
390     ]]>
391     <!ENTITY % $ID.pfx "">
392 wakaba 1.2
393     <!-- declare qualified name extensions here -->
394     <!ENTITY % ${ID}-qname-extra.mod "">
395     %${ID}-qname-extra.mod;
396    
397     <!-- 5. May be redeclared to contain any foreign namespace declaration
398     attributes for namespaces embedded in XML. -->
399 wakaba 1.1 <!ENTITY % $ID.xmlns.extra.attrib "">
400    
401     <![%$ID.prefixed;[
402     <!ENTITY % $ID.xmlns.decl.attrib
403     "xmlns:%$ID.prefix; %URI.datatype; #FIXED '%$ID.xmlns;'">
404     ]]>
405     <!ENTITY % $ID.xmlns.decl.attrib
406     "xmlns %URI.datatype; #FIXED '%$ID.xmlns;'">
407    
408     <![%$ID.prefixed;[
409     <!ENTITY % NS.decl.attrib
410     "%$ID.xmlns.decl.attrib;
411     %$ID.xmlns.extra.attrib;">
412     ]]>
413     <!ENTITY % NS.decl.attrib
414     "%$ID.xmlns.extra.attrib;">
415    
416 wakaba 1.2 <!-- Declare a parameter entity containing all XML namespace declaration
417     attributes used, including a default xmlns declaration when prefixing
418     is inactive. -->
419 wakaba 1.1 <![%$ID.prefixed;[
420     <!ENTITY % $ID.xmlns.attrib
421     "%NS.decl.attrib;">
422     ]]>
423     <!ENTITY % $ID.xmlns.attrib
424     "%$ID.xmlns.decl.attrib;
425     %NS.decl.attrib;">
426    
427 wakaba 1.2 <!-- 6. Declare parameter entities used to provide namespace-qualified
428     names for all element types and global attribute names. -->
429 wakaba 1.1 EOH
430     for my $lname (keys %{$Info->{QName}}) {
431     $s .= qq(<!ENTITY % $Info->{ID}.$lname.qname "%$Info->{ID}.pfx;$lname">\n);
432     }
433     $s .= qq(\n);
434     for my $lname (keys %{$Info->{QNameA}}) {
435     $s .= qq(<!ENTITY % $Info->{ID}.$lname.attrib.qname "%$Info->{ID}.prefix;:$lname">\n);
436     }
437     $s .= qq(\n);
438     for my $lname (keys %{$Info->{QNameB}}) {
439     $s .= qq(<!ENTITY % $Info->{ID}.$lname.attribute.qname "%$Info->{ID}.pfx;$lname">\n);
440     }
441     make_module ($src, $Info, 'qname', $s);
442     }
443    
444     sub get_name ($$;$) {
445     my ($src, $Info, $key) = @_;
446     my $name = $src->get_attribute_value ($key || 'Name');
447     if ($name =~ /^:(.+)/) {
448     $name = $1;
449     } elsif ($name =~ /([^:]+):(.+)/) {
450     $name = qq($1.$2);
451     } else {
452     $name = qq($Info->{ID}.$name);
453     }
454     $name;
455     }
456    
457     sub get_qname ($$) {
458     my ($src, $Info) = @_;
459     my $name = $src->get_attribute_value ('Name');
460     if ($name =~ /"([^"]+)"/) {
461     $name = qq($1);
462     } elsif ($name =~ /^:(.+)/) {
463     $name = qq(%$1.qname;);
464     } elsif ($name =~ /([^:]+):(.+)/) {
465     $name = qq(%$1.$2.qname;);
466     } elsif ($name =~ /\{([^{}]+)\}/) {
467     $Info->{QNameB}->{$1} = 1;
468     $name = qq(%$Info->{ID}.$1.attribute.qname;);
469     } else {
470     $Info->{QNameA}->{$name} = 1;
471     $name = qq(%$Info->{ID}.$name.attrib.qname;);
472     }
473     $name;
474     }
475    
476     sub get_atype ($$) {
477     my ($src, $Info) = @_;
478     my $name = $src->get_attribute_value ('Type');
479     if ($name =~ /^:(.+)/) {
480     $name = qq(%$1.datatype;);
481     } elsif ($name =~ /([^:]+):(.+)/) {
482     $name = qq(%$1.$2.datatype;);
483     } elsif ($name =~ /"([^"]+)"/) {
484     $name = qq($1);
485     } else {
486     $name = qq(%$Info->{ID}.$name.datatype;);
487     }
488     $name;
489     }
490    
491     sub get_adefault ($$) {
492     my ($src, $Info) = @_;
493     my $name = $src->get_attribute_value ('Default');
494     if (defined $name) {
495     } else {
496     $name = qq(#IMPLIED);
497     }
498     $name;
499     }
500    
501     sub get_desc ($$) {
502     my ($src, $Info) = @_;
503     my $desc = $src->get_attribute_value ('Description');
504     $desc =~ s/\n/\n /g;
505     $desc = qq(<!-- $desc -->\n) if $desc;
506     $desc;
507     }
508    
509     sub attset_def ($$) {
510     my ($src, $Info) = @_;
511     my $name = get_name ($src, $Info);
512     my $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % $name.attrib\n\t);
513     my @s;
514     if ($name eq qq($Info->{ID}.common)) {
515     push @s, qq(%$Info->{ID}.common.extra.attrib;);
516     push @s, qq(%$Info->{ID}.xmlns.attrib;);
517     }
518     for my $src (@{$src->child_nodes}) {
519     ## Attribute Definition
520     if ($src->local_name eq 'Attribute') {
521     push @s, attrib_def ($src, $Info);
522     ## Reference to Attribute Definition
523     } elsif ($src->local_name eq 'ref') {
524     push @s, attrib_ref ($src, $Info);
525     } elsif ($src->local_name eq 'REF') {
526     push @s, attrib_REF ($src, $Info);
527     }
528     }
529     $s .= paralit join "\n\t", @s;
530     $s .= qq(>\n\n);
531     $s;
532     }
533    
534     sub attrib_module ($$) {
535     my ($src, $Info) = @_;
536     my $s = <<EOH;
537     <!ENTITY % $Info->{ID}.common.extra.attrib "">
538    
539     EOH
540     my $output_common = 0;
541     for my $src (@{$src->child_nodes}) {
542     ## Attributes Set
543     if ($src->local_name eq 'Attribute' or $src->local_name eq 'AttributeSet') {
544     $s .= attset_def ($src, $Info);
545     $output_common = 1 if get_name ($src, $Info) eq qq($Info->{ID}.common);
546     }
547     }
548     unless ($output_common) {
549     $s .= <<EOH;
550     <!ENTITY % $Info->{ID}.common.attrib
551     "%$Info->{ID}.common.extra.attrib;
552     %$Info->{ID}.xmlns.attrib;">
553    
554     EOH
555     }
556     make_module ($src, $Info, ($src->get_attribute_value ('ID') || 'attribs'), $s);
557     }
558    
559     sub attrib_def ($$) {
560     my ($src, $Info) = @_;
561     my $s = qq(@{[get_qname ($src, $Info)]} @{[get_atype ($src, $Info)]} @{[get_adefault ($src, $Info)]});
562     $s;
563     }
564    
565     sub attrib_ref ($$) {
566     my ($src, $Info) = @_;
567     my $name = $src->value;
568     if ($name =~ /^:(.+)/) {
569     $name = $1;
570     } elsif ($name =~ /([^:]+):(.+)/) {
571     $name = qq($1.$2);
572     } else {
573     $name = qq($Info->{ID}.$name);
574     }
575     qq(%$name.attrib;);
576     }
577    
578     sub attrib_REF ($$) {
579     my ($src, $Info) = @_;
580     {
581     'xml:base' => q<xml:base %URI.datatype; #IMPLIED>,
582     'xml:lang' => q<xml:lang %LanguageCode.datatype; #IMPLIED>,
583     'xml:space' => q<xml:space (default|preserve) #IMPLIED>,
584     }->{$src->value};
585     }
586    
587     sub submodule ($$) {
588     my ($src, $Info) = @_;
589     my $s = submodule_declarations ($src, $Info);
590     make_module ($src, $Info, $src->get_attribute_value ('ID'), $s);
591     }
592    
593     sub submodule_declarations ($$) {
594     my ($src, $Info) = @_;
595     my $s = '';
596     for my $src (@{$src->child_nodes}) {
597     if ($src->local_name eq 'Element') {
598     $s .= element_def ($src, $Info);
599     } elsif ($src->local_name eq 'Attribute') {
600     $s .= attlist_def ($src, $Info);
601     } elsif ($src->local_name eq 'AttributeSet') {
602     $s .= attset_def ($src, $Info);
603 wakaba 1.2 } elsif ($src->local_name eq 'Class') {
604     $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
605 wakaba 1.1 } elsif ($src->local_name eq 'Content') {
606     $s .= element_content_def ($src, $Info);
607     } elsif ($src->local_name eq 'IfModuleSet') {
608     $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
609     $s .= submodule_declarations ($src, $Info);
610     $s .= qq(]]>\n);
611     } elsif ($src->local_name eq 'ElementSwitch') {
612     $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.element "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
613     } elsif ($src->local_name eq 'AttributeSwitch') {
614     $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.attlist "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
615 wakaba 1.2 } elsif ($src->local_name eq 'ModuleSwitch') {
616     $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.module "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
617     } elsif ($src->local_name eq 'GeneralEntity') {
618     $s .= entity_declaration ($src, $Info, param => 0);
619 wakaba 1.1 } elsif ($src->local_name eq 'ParameterEntity') {
620 wakaba 1.2 $s .= entity_declaration ($src, $Info, param => 1);
621 wakaba 1.1 }
622     }
623     $s;
624     }
625    
626     sub element_content_def ($$) {
627     my ($src, $Info) = @_;
628     qq(<!ENTITY % @{[name_of ($src, $Info, key => 'ElementType')]}.content @{[paralit convert_content_model ($src, $Info, default => 'EMPTY')]}>\n);
629     }
630    
631     sub element_def ($$) {
632     my ($src, $Info) = @_;
633     my $name = get_name ($src, $Info);
634     my $mname = $name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name);
635     $Info->{QName}->{$1} = 1 if $name =~ /^\Q$Info->{ID}\E\.(.+)/;
636     my $s = <<EOH;
637     @{[get_desc ($src, $Info)]}<!ENTITY % $mname.element "INCLUDE">
638     <![%$mname.element;[
639     <!ENTITY % $name.content @{[paralit convert_content_model ($src, $Info, default => 'EMPTY')]}>
640     <!ELEMENT %$name.qname; %$name.content;>
641     ]]>
642     EOH
643     $s .= attlist_def (scalar $src->get_attribute ('Attribute', make_new_node => 1), $Info, $mname);
644     $s;
645     }
646    
647     sub get_model_token ($$) {
648     my ($name, $Info) = @_;
649     my $suffix = '.qname';
650     if ($name =~ s/^\$//) {
651     $suffix = $name =~ /\.(?:mix|class|content|datatype)$/ ? '' : '.class';
652     }
653     if ($name =~ /^:(.+)/) {
654     $name = qq(%$1$suffix;);
655     } elsif ($name =~ /([^:]+):(.+)/) {
656     $name = qq(%$1.$2$suffix;);
657     } elsif ($name =~ /"([^"]+)"/) {
658     $name = qq($1);
659     } else {
660     $name = qq(%$Info->{ID}.$name$suffix;);
661     }
662     $name;
663     }
664    
665     sub attlist_def ($$;$) {
666     my ($src, $Info, $name) = @_;
667     $name ||= get_name ($src, $Info, 'ElementType');
668     my $mname = get_name ($src, $Info);
669     $mname = ($name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name))
670     if $mname eq "$Info->{ID}.";
671     $Info->{QName}->{$1} = 1 if $name =~ /^\Q$Info->{ID}\E\.(.+)/;
672     my $s = qq(@{[description ($src, $Info)]}<!ENTITY % $mname.attlist "INCLUDE">
673     <![%$mname.attlist;[
674     <!ATTLIST %$name.qname;);
675     for my $src (@{$src->child_nodes}) {
676     ## Attribute Definition
677     if ($src->local_name eq 'Attribute') {
678     $s .= "\n\t". attrib_def ($src, $Info);
679     ## Reference to Attribute Definition
680     } elsif ($src->local_name eq 'ref') {
681     $s .= "\n\t". attrib_ref ($src, $Info);
682     } elsif ($src->local_name eq 'REF') {
683     $s .= "\n\t". attrib_REF ($src, $Info);
684     }
685     }
686     if ($_[2]) {
687     $s .= qq(\n\t%$Info->{ID}.common.attrib;);
688     }
689     $s .= qq(>
690     ]]>
691    
692     );
693     $s;
694     }
695    
696     sub make_module ($$$$) {
697     my ($src, $Info, $id, $s) = @_;
698     my $name = $src->get_attribute_value ('Name')
699     || {attribs => q/Common Attributes/,
700     datatype => q/Datatypes/,
701     model => q/Document Model/,
702     qname => q/QName/,
703     struct => q/Structual/,
704     }->{$id}
705     || $id;
706 wakaba 1.2 return unless $s;
707 wakaba 1.1
708     my $r = <<EOH;
709     <!-- $Info->{Name} : $name Module
710    
711     Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}
712     Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
713     (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]]}
714    
715     SYSTEM "$Info->{BaseURI}$Info->{ID}-$id.mod"
716     -->
717    
718     EOH
719    
720     $r .= $s;
721    
722     $r .= qq(\n<!-- end of $Info->{ID}-$id.mod -->\n);
723    
724     my $file = qq"$Info->{ID}-$id.mod";
725     open FILE, '>', $file or die "$0: $file: $!";
726     print FILE $r;
727     close FILE;
728     print STDERR "$0: $file created\n";
729     }
730    
731     sub make_dtd ($$$$) {
732     my ($src, $Info, $id, $s) = @_;
733     $id = "-$id" if $id;
734    
735     my $r = <<EOH;
736     <!-- $Info->{Name} : Document Type Definition
737    
738     Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}
739     Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
740     (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]]}
741    
742     SYSTEM "$Info->{BaseURI}$Info->{ID}$id.dtd"
743     -->
744    
745     EOH
746    
747     $r .= $s;
748    
749     $r .= qq(\n<!-- end of $Info->{ID}$id.dtd -->\n);
750    
751     my $file = qq"$Info->{ID}$id.dtd";
752     open FILE, '>', $file or die "$0: $file: $!";
753     print FILE $r;
754     close FILE;
755     print STDERR "$0: $file created\n";
756     }
757    
758    
759     =head1 NAME
760    
761     mkdtds.pl --- Moduralized XML Document Type Definition Generator
762    
763     =head1 DESCRIPTION
764    
765     This script can be used to generate XML DTD modules and driver
766     which is interoperable with XHTML DTD modules.
767    
768     =head1 USAGE
769    
770     $ perl mkdtds.pl driver.dds
771     mkdtds.pl: driver.dtd created
772     mkdtds.pl: driver-model.mod created
773    
774     $ perl mkdtds.pl moduleset.dms
775     mkdtds.pl: moduleset-datatype.mod created
776     mkdtds.pl: moduleset-attrib.mod created
777     mkdtds.pl: moduleset-module1.mod created
778    
779     =head1 DTD SOURCE FORMAT
780    
781     (((See examples on <http://suika.fam.cx/gate/cvs/markup/>)))
782    
783     =head1 REQUIRED MODULE
784    
785     This script uses SuikaWiki::Markup::SuikaWikiConfig20 and
786     SuikaWiki::Markup::SuikaWikiConfig20::Parser.
787     Please get it from <http://suika.fam.cx/gate/cvs/suikawiki/script/lib/>
788     and put into your lib directory.
789    
790     =head1 AUTHOR
791    
792     Wakaba <w@suika.fam.cx>
793    
794     =head1 LICENSE
795    
796     Copyright 2003 Wakaba <w@suika.fam.cx>
797    
798     This program is free software; you can redistribute it and/or
799     modify it under the same terms as Perl itself.
800    
801     Note that author claims no right about DTD modules generated by this script.
802     Author(s) of DTD modules should be explicily state their license terms.
803    
804     =cut

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24