#!/usr/bin/perl -w use strict; use Message::Util::QName::Filter { d => q, dis2pm => q, DISCore => q, DISLang => q, DISPerl => q, disPerl => q, DOMCore => q, DOMMain => q, DOMXML => q, lang => q, Perl => q, license => q, ManakaiDOM => q, MDOMX => q, owl => q, rdf => q, rdfs => q, TreeCore => q<>, }; use Getopt::Long; use Pod::Usage; use Storable; my %Opt; GetOptions ( 'for=s' => \$Opt{For}, 'help' => \$Opt{help}, 'module-name=s' => \$Opt{module_name}, 'module-uri=s' => \$Opt{module_uri}, 'verbose!' => $Opt{verbose}, ) or pod2usage (2); pod2usage ({-exitval => 0, -verbose => 1}) if $Opt{help}; $Opt{file_name} = shift; pod2usage ({-exitval => 2, -verbose => 0}) unless $Opt{file_name}; pod2usage (2) if not $Opt{module_uri} and not $Opt{module_name}; BEGIN { require 'manakai/genlib.pl'; require 'manakai/dis.pl'; } our $State = retrieve ($Opt{file_name}) or die "$0: $Opt{file_name}: Cannot load"; eval q{ sub impl_msg ($;%) { warn shift () . "\n"; } } unless $Opt{verbose}; sub perl_change_package (%) { my %opt = @_; my $fn = $opt{full_name}; impl_err (qq<$fn: Bad package name>) unless $fn; unless ($fn eq $State->{ExpandedURI q}) { my $r = dispm_package_declarations (%opt); $State->{ExpandedURI q} = $fn; $State->{ExpandedURI q}->{$fn} = -1; return $r . perl_statement qq; } else { return ''; } } # perl_change_package =item $code = dispm_package_declarations (%opt) Generates a code fragment that declares what is required in the current package, including import statements for character classes. =cut sub dispm_package_declarations (%) { my %opt = @_; my $pack_name = $State->{ExpandedURI q}; my $pack = $State->{ExpandedURI q}->{$pack_name}; my $r = ''; my @xml_class; for (keys %{$pack->{ExpandedURI q}||{}}) { my $val = $pack->{ExpandedURI q}->{$_}; next if not ref $val and $val <= 0; if (/^InXML/) { push @xml_class, $_; $pack->{ExpandedURI q}->{$_} = -1; } else { valid_err (qq<"$_": Unknown character class>, node => ref $val ? $val : $opt{node}); } } if (@xml_class) { $State->{Module}->{$State->{module}} ->{ExpandedURI q} ->{'Char::Class::XML'} = 1; $r .= perl_statement 'Char::Class::XML->import ('. perl_list (@xml_class).')'; } $r; } # dispm_package_declarations =item $code = dispm_perl_throws (%opt) Generates a code to throw an exception. =cut sub dispm_perl_throws (%) { my %opt = @_; my $x = $opt{class_resource} || $State->{Type}->{$opt{class}}; my $r = 'report '; unless (defined $x->{Name}) { $opt{class} = dis_typeforuris_to_uri ($opt{class}, $opt{class_for}, %opt); $x = $State->{Type}->{$opt{class}}; } valid_err (qq is not defined>, node => $opt{node}) unless defined $x->{Name}; if ($x->{ExpandedURI q} and { ExpandedURI q => 1, ExpandedURI q => 1, }->{$x->{ExpandedURI q}}) { $opt{type} = $opt{type_resource}->{Name} unless defined $opt{type}; valid_err qq{Exception code must be specified}, node => $opt{type_resource}->{src} || $opt{node} unless defined $opt{type}; $opt{subtype} = $opt{subtype_resource}->{NameURI} || $opt{subtype_resource}->{URI} unless defined $opt{subtype}; $opt{xparam}->{ExpandedURI q} = $opt{subtype} if defined $opt{subtype}; $r .= $x->{ExpandedURI q} . ' ' . perl_list -type => $opt{type}, -object => perl_code_literal ('$self'), %{$opt{xparam} || {}}; } else { no warnings 'uninitialized'; valid_err (qq{Resource <$opt{class}> [<$x->{ExpandedURI q}>] }. q. q, node => $opt{node}); } return $r; } # dispm_perl_throw my $RegQNameChar = qr/[^\s<>"'\\\[\]\{\},=]/; { use re 'eval'; my $RegBlockContent; $RegBlockContent = qr/(?>[^{}\\]*)(?>(?>[^{}\\]+|\\.|\{(??{$RegBlockContent})\})*)/s; ## Defined by genlib.pl but overridden. sub perl_code ($;%) { my ($s, %opt) = @_; valid_err q, node => $opt{node} unless defined $s; local $State->{Namespace} = $State->{Module}->{$opt{resource}->{parentModule}}->{nsBinding}; $s =~ s[(?]+)>|\b(null|true|false)\b][ my ($q, $l) = ($1, $2); my $r; if (defined $q) { if ($q =~ /\}/) { valid_warn qq" has a "}" - it might be a typo>; } if ($q =~ /=$/) { valid_warn qq" ends with a "=" - >. q{should "=" be used place of "=>"?}; } if ($q =~ s/^(.+?):://) { my $et = dis_qname_to_uri ($1, %opt, use_default_namespace => ExpandedURI q); if ($et eq ExpandedURI q) { ## QName constant $r = perl_literal (dis_qname_to_uri ($q, use_default_namespace => 1, %opt)); } elsif ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$et}) { ## Method call my ($clsq, $mtdq) = split /\s*\.\s*/, $q, 2; my $clsu = dis_typeforqnames_to_uri ($clsq, use_default_namespace => 1, %opt); my $cls = $State->{Type}->{$clsu}; my $clsp = $cls->{ExpandedURI q}; if ($cls->{ExpandedURI q} and { ExpandedURI q => 1, ExpandedURI q => 1, }->{$cls->{ExpandedURI q}}) { valid_err q<"disPerl:ClassM" cannot be used for interface methods>, node => $opt{node} if $et eq ExpandedURI q; $clsp = ''; } else { valid_err qq must be defined>, node => $opt{node} unless defined $clsp; $State->{Module}->{$State->{module}} ->{ExpandedURI q} ->{$State->{Module}->{$cls->{parentModule}} ->{ExpandedURI q}} = 1; } if ($mtdq =~ /:/) { valid_err qq<$mtdq: Prefixed method name not supported>, node => $opt{node}; } else { my $mtd; for (values %{$cls->{ExpandedURI q}}) { if (defined $_->{Name} and $_->{Name} eq $mtdq) { $mtd = $_; last; } } valid_err qq. q, node => $mtd->{src} || $opt{node} if not defined $mtd or not defined $mtd->{ExpandedURI q}; $r = ' ' . ($clsp ? $clsp . { ExpandedURI q => '::', ExpandedURI q => '::', ExpandedURI q => '::', ExpandedURI q => '->', }->{$et} : '') . $mtd->{ExpandedURI q} . ' '; } } elsif ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$et}) { ## Perl package name my $uri = dis_typeforqnames_to_uri ($q, use_default_namespace => 1, %opt); if (defined $State->{Type}->{$uri}->{Name} and defined $State->{Type}->{$uri} ->{ExpandedURI q}) { $r = $State->{Type}->{$uri}->{ExpandedURI q}; if ({ ExpandedURI q => 1, ExpandedURI q => 1, }->{$et}) { $r = perl_literal $r; } } else { valid_err qq must be defined>, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { ## CODE constant my ($nm); $q =~ s/^\s+//; if ($q =~ s/^((?>(?!::).)+)//) { $nm = $1; } else { valid_err qq<"$q": Code name required>, node => $opt{node}; } $q =~ s/^::\s*//; my $param = dispm_parse_param (\$q, %opt, ExpandedURI q => 1, use_default_namespace => ''); my $uri = dis_typeforqnames_to_uri ($nm, use_default_namespace => 1, %opt); if (defined $State->{Type}->{$uri}->{Name} and dis_resource_ctype_match (ExpandedURI q, $State->{Type}->{$uri}, %opt)) { local $State->{ExpandedURI q} = $param; ## ISSUE: It might be required to check loop referring $r = dispm_get_code (%opt, resource => $State->{Type}->{$uri}, For => [keys %{$State->{Type}->{$uri} ->{For}}]->[0], is_inline => 1, ExpandedURI q => $param, ExpandedURI q => ExpandedURI q); for (grep {/^\$/} keys %$param) { $r =~ s/\Q$_\E\b/ $param->{$_} /g; } } else { valid_err qq must be defined>, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { if ($q =~ /^((?>(?!\.)$RegQNameChar)*)\.($RegQNameChar+)$/o) { my ($cls, $constn) = ($1, $2); if (length $cls) { my $clsu = dis_typeforqnames_to_uri ($cls, %opt, use_default_namespace => 1, node => $_); $cls = $State->{Type}->{$clsu}; valid_err qq must be defined>, node => $_ unless defined $cls->{Name}; } else { $cls = $State->{ExpandedURI q}; valid_err q, node => $_ unless defined $cls->{Name}; } my $const = $cls->{ExpandedURI q}->{$constn}; valid_err qq. qq{"$cls->{Name}" (<$cls->{URI}>)}, node => $_ unless defined $const->{Name}; $r = dispm_const_value (resource => $const); } else { valid_err qq<"$q": Syntax error>, node => $opt{node}; } } else { valid_err qq<"$et": Unknown element type>, node => $opt{node}; } } else { valid_err qq<"<$q>": Element type must be specified>, node => $opt{node}; } } else { $r = {true => 1, false => 0, null => 'undef'}->{$l}; } $r; ]ge; ## TODO: Ensure Message::Util::Error imported if "try"ing. ## ISSUE: __FILE__ & __LINE__ will break if multiline substition happens. $s =~ s{ \b__($RegQNameChar+) (?:\{($RegBlockContent)\})? __\b }{ my ($name, $data) = ($1, $2); my $r; my $et = dis_qname_to_uri ($name, %opt, use_default_namespace => ExpandedURI q); if ($et eq ExpandedURI q) { ## Deep Method Call $r = '{'.perl_statement ('local $Error::Depth = $Error::Depth + 1'). perl_code ($data) . '}'; } elsif ({ ExpandedURI q => 1, ExpandedURI q => 1, }->{$et}) { ## Raising an Exception or Warning if ($data =~ s/^ \s* ((?>(?! ::|\.)$RegQNameChar)+) \s* (?: \. \s* ((?>(?! ::|\.)$RegQNameChar)+) \s* (?: \. \s* ((?>(>! ::|\.)$RegQNameChar)+) \s* )? )? (?: ::\s* | $)//ox) { my ($q, $constq, $subtypeq) = ($1, $2, $3); $q =~ s/\|\|/::/g; my $clsuri; my $cls; my $consturi; my $const; my $subtypeuri; my $subtype; if (defined $constq and not defined $subtypeq) { $clsuri = dis_typeforqnames_to_uri ($q, use_default_namespace => 1, %opt); $cls = $State->{Type}->{$clsuri}; valid_err qq{Exception/warning class definition for }. qq{<$clsuri> is required}, node => $opt{node} unless defined $cls->{Name}; my ($consttq, $constfq) = split /\|\|/, $constq, 2; if (defined $constfq) { if ($consttq !~ /:/) { valid_err qq<"$constq": Unprefixed exception code QName must >. q, node => $opt{node}; } else { $consturi = dis_typeforqnames_to_uri ($consttq.'::'.$constfq, use_default_namespace => 1, %opt); } } else { if ($consttq !~ /:/) { $consturi = $consttq; CONSTCLS: { for (values %{$cls->{ExpandedURI q}}) { if (defined $_->{Name} and $_->{Name} eq $consturi) { $const = $_; last CONSTCLS; } } valid_err qq{Exception/warning code "$consturi" must be }. qq{defined in the exception/warning class }. qq{<$clsuri>}, node => $opt{node}; } } else { $consturi = dis_typeforqnames_to_uri ($consttq.'::'.$constfq, use_default_namespace => 1, %opt); } } unless ($const) { CONSTCLS: { for (values %{$cls->{ExpandedURI q}}) { if (defined $_->{Name} and $_->{URI} and $_->{URI} eq $consturi) { $const = $_; last CONSTCLS; } } valid_err qq{Exception/warning code <$consturi> must be }. qq{defined in the exception/warning class }. qq{<$clsuri>}, node => $opt{node}; } } } else { ## By code/subtype QName $subtypeq = $q unless defined $constq; $subtypeuri = dis_typeforqnames_to_uri ($subtypeq, use_default_namespace => 1, %opt); $subtype = $State->{Type}->{$subtypeuri}; valid_err qq{Exception/warning code/subtype <$subtypeuri> must }. qq{be defined}, node => $opt{node} unless defined $subtype->{Name} and defined $subtype->{ExpandedURI q}; if ($subtype->{ExpandedURI q} eq ExpandedURI q) { $const = $subtype->{ExpandedURI q}; $cls = $subtype->{ExpandedURI q}; } elsif ($subtype->{ExpandedURI q} eq ExpandedURI q) { $const = $subtype; $subtype = undef; $cls = $const->{ExpandedURI q}; } else { valid_err qq{Type of <$subtypeuri> must be either }. q{"ManakaiDOM:Const" or }. q{"ManakaiDOM:ExceptionOrWarningSubType"}, node => $opt{node}; } } ## Parameter my %xparam; while ($data =~ s/^\s*($RegQNameChar+)\s*//) { my $pnameuri = dis_qname_to_uri ($1, use_default_namespace => 1, %opt); if (defined $xparam{$pnameuri}) { valid_err qq is already specified>, node => $opt{node}; } if ($data =~ s/^=>\s*'([^']*)'\s*//) { ## String $xparam{$pnameuri} = $1; } elsif ($data =~ s/^=>\s*\{($RegBlockContent)\}\s*//) { ## Code $xparam{$pnameuri} = perl_code_literal ($1); } elsif ($data =~ /^,|$/) { ## Boolean $xparam{$pnameuri} = 1; } else { valid_err qq<<$pnameuri>: Parameter value is expected>, node => $opt{node}; } $data =~ s/^\,\s*// or last; } valid_err qq<"$data": Broken exception parameter specification>, node => $opt{node} if length $data; for ( ExpandedURI q, ExpandedURI q, ExpandedURI q, ExpandedURI q, ) { $xparam{$_} = $opt{$_} if defined $opt{$_}; } $r = dispm_perl_throws (%opt, class_resource => $cls, class_for => $opt{For}, type_resource => $const, subtype_resource => $subtype, xparam => \%xparam); } else { valid_err qq, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { my ($nm); $data =~ s/^\s+//; if ($data =~ s/^((?>(?!::).)+)//) { $nm = $1; } else { valid_err q, node => $opt{node}; } $data =~ s/^::\s*//; my $param = dispm_parse_param (\$data, %opt, use_default_namespace => '', ExpandedURI q => 1); my $uri = dis_typeforqnames_to_uri ($nm, use_default_namespace => 1, %opt); if (defined $State->{Type}->{$uri}->{Name} and dis_resource_ctype_match (ExpandedURI q, $State->{Type}->{$uri}, %opt)) { local $State->{ExpandedURI q} = $param; ## ISSUE: It might be required to detect a loop $r = dispm_get_code (%opt, resource => $State->{Type}->{$uri}, For => [keys %{$State->{Type}->{$uri} ->{For}}]->[0], ExpandedURI q => $param, ExpandedURI q => ExpandedURI q); for (grep {/^\$/} keys %$param) { $r =~ s/\Q$_\E\b/ $param->{$_} /g; } valid_err qq is empty>, node => $opt{node} unless length $r; $r = "\n{\n$r\n}\n"; } else { valid_err qq must be defined>, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { my $method = $opt{ExpandedURI q}; valid_err q cannot be used here>, node => $opt{node} unless defined $method->{Name}; PARAM: { for my $param (@{$method->{ExpandedURI q}||[]}) { if ($data eq $param->{ExpandedURI q}) { ## NOTE: property is not ## checked for this element. my $nm = dispm_get_code (%opt, resource => $State->{Type} ->{$param->{ExpandedURI q}}, ExpandedURI q => ExpandedURI q, ExpandedURI q => 1, ExpandedURI q => $param->{ExpandedURI q}); if (defined $nm) { $nm =~ s[\$INPUT\b][\$$param->{ExpandedURI q} ]g; $r = $nm; } else { $r = ''; } last PARAM; } } valid_err q, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { if ($data =~ s/^\s*IS\s*\{($RegBlockContent)\}::\s*//o) { my $v = dis_qname_to_uri ($1, use_default_namespace => 1, %opt); if ($State->{ExpandedURI q}->{$v}) { $r = perl_code ($data, %opt); } } else { valid_err qq, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { if ($data =~ s/^((?>(?!::).)*)::\s*//) { my @For = ($opt{For} || ExpandedURI q, @{$opt{'For+'} || []}); V: for (split /\s*\|\s*/, $1) { my $for = dis_qname_to_uri ($_, %opt, use_default_namespace => 1, node => $opt{node}); for (@For) { if (dis_uri_for_match ($for, $_, %opt)) { $r = perl_code ($data, %opt); last V; } } } } else { valid_err (qq block: "$data">, node => $opt{node}); } } elsif ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$et}) { $r = qq<__${name}__>; valid_err (q. qq>, node => $opt{node}) if length $data; } else { valid_err qq not supported>, node => $opt{node}; } $r; }goex; ## Checks \p character classes while ($s =~ /\\p{([^{}]+)}/gs) { my $name = $1; $State->{ExpandedURI q} ->{$State->{ExpandedURI q}} ->{ExpandedURI q} ->{$name} ||= $opt{node} || 1; } $s; } } =item {%param} = dispm_parse_param (\$paramspec, %opt) Parses parameter specification and returns it as a reference to hash. =cut sub dispm_parse_param ($%) { my ($src, %opt) = @_; my %param; while ($$src =~ s/^ ## Parameter name (\$? $RegQNameChar+)\s* (?: =>? \s* ## Parameter value ( ## Bare string $RegQNameChar+ | ## Quoted string '(?>[^'\\]*)' ## ISSUE: escape mechanism required? ) \s*)? (?:,\s*|$)//ox) { my ($n, $v) = ($1, $2); if (defined $v) { if ($v =~ /^'/) { $v = substr ($v, 1, length ($v) - 2); } else { # } } else { $v = 1; } if ($n =~ /^\$/) { $param{$n} = $v; } else { $param{dis_qname_to_uri ($n, %opt)} = $v; } } if ($opt{ExpandedURI q} and length $$src) { valid_err qq, node => $opt{node}; } \%param; } # dispm_parse_param ## Defined in genlib.pl but overridden. sub perl_code_source ($%) { my ($s, %opt) = @_; my $npk = [qw/Name QName Label/]; my $f1 = sprintf q Node <%s> [Chunk #%d]>, $opt{file} || $State->{Module}->{$opt{resource}->{parentModule}}->{FileName}, $opt{path} || ($opt{resource}->{src} ? $opt{resource}->{src}->node_path (key => $npk) : $opt{node} ? $opt{node}->node_path (key => $npk) : 'x:unknown ()'), ++($State->{ExpandedURI q} ||= 0); my $f2 = sprintf q [Chunk #%d]>, $opt{file} || $State->{Module}->{$State->{module}}->{URI}, ++($State->{ExpandedURI q} ||= 0); $f1 =~ s/"/\"/g; $f2 =~ s/"/\"/g; sprintf qq<\n#line %d "%s"\n%s\n#line 1 "%s"\n>, $opt{line} || 1, $f1, $s, $f2; } =item $code = dispm_get_code (resource => $res, %opt) Generates a Perl code fragment from resource(s). =cut sub dispm_get_code (%) { my %opt = @_; if (($opt{ExpandedURI q} and defined $opt{resource}->{Name}) or ($opt{resource}->{ExpandedURI q} and { ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$opt{resource}->{ExpandedURI q}}) or (dis_resource_ctype_match ([ExpandedURI q, ExpandedURI q], $opt{resource}, %opt, node => $opt{resource}->{src}))) { local $State->{Namespace} = $State->{Module}->{$opt{resource}->{parentModule}}->{nsBinding} if defined $opt{resource}->{Name}; my $key = $opt{ExpandedURI q} || ExpandedURI q; my $n = dis_get_attr_node (%opt, parent => $opt{resource}->{src}, name => {uri => $key}, ContentType => ExpandedURI q) || dis_get_attr_node (%opt, parent => $opt{resource}->{src}, name => {uri => $key}, ContentType => ExpandedURI q); if ($n) { return disperl_to_perl (%opt, node => $n); } $n = dis_get_attr_node (%opt, parent => $opt{resource}->{src}, name => {uri => $key}, ContentType => ExpandedURI q); if ($n) { my $code = ''; for (@{dis_get_elements_nodes (%opt, parent => $n, name => 'require')}) { $code .= perl_statement 'require ' . $_->value; } my $v = $n->value; valid_err q, node => $n unless defined $v; $code .= perl_code ($v, %opt, node => $n); if ($opt{is_inline} and dis_resource_ctype_match ([ExpandedURI q], $opt{resource}, %opt, node => $opt{resource}->{src})) { $code =~ s/\n/\x20/g; return $code; } else { return perl_code_source ($code, %opt, node => $n); } } return undef; } else { impl_err ("Bad resource for dispm_get_code: ". $opt{resource}->{ExpandedURI q}, node => $opt{resource}->{src}); } } # dispm_get_code =item $code = dispm_get_value (%opt) Gets value property and returns it as a Perl code fragment. =cut sub dispm_get_value (%) { my %opt = @_; my $key = $opt{ExpandedURI q} || ExpandedURI q; my $vt = $opt{ExpandedURI q} || ExpandedURI q; local $State->{Namespace} = $State->{Module}->{$opt{resource}->{parentModule}}->{nsBinding} if defined $opt{resource}->{Name}; my $n = $opt{node} ? [$opt{node}] : dis_get_elements_nodes (%opt, parent => $opt{resource}->{src}, name => {uri => $key}); for my $n (@$n) { my $t = dis_get_attr_node (%opt, parent => $n, name => 'ContentType'); my $type; if ($t) { $type = dis_qname_to_uri ($t->value, %opt, node => $t); } else { $type = ExpandedURI q; } valid_err (qq is not defined>, node => $t || $n) unless defined $State->{Type}->{$type}->{Name}; if (dis_uri_ctype_match (ExpandedURI q, $type, %opt)) { ## ISSUE: Is some pre-process required? return $n->value; } elsif (dis_uri_ctype_match (ExpandedURI q, $type, %opt)) { return perl_literal $n->value; } elsif (dis_uri_ctype_match (ExpandedURI q, $type, %opt)) { ## NOTE: This might not be a valid Perl code fragment. return $n->value; } } ## No explicit value specified if ($opt{ExpandedURI q}) { if (dis_uri_ctype_match (ExpandedURI q, $vt, %opt)) { return q<"">; } } return undef; } # dispm_get_value =item $code = dispm_const_value (resource => $const, %opt) Returns a code fragment corresponding to the vaue of C<$const>. =cut sub dispm_const_value (%) { my %opt = @_; my $for = [keys %{$opt{resource}->{For}}]->[0]; my $value = dispm_get_value (%opt, ExpandedURI q => ExpandedURI q, ExpandedURI q => $opt{resource} ->{ExpandedURI q}, For => $for); valid_err q, node => $opt{resource}->{src} unless defined $value; return $value; } # dispm_const_value =item $code = dispm_const_value_sub (resource => $const, %opt) Returns a code fragment to declare and define a constant function corresponding to the definition of C<$const>. =cut sub dispm_const_value_sub (%) { my %opt = @_; my $value = dispm_const_value (%opt); return perl_sub (name => $opt{resource}->{ExpandedURI q}, prototype => '', code => $value); } # dispm_const_value_sub =item $code = dispm_const_group (resource => $const_group, %opt) Returns a code fragment to define a constant value group. =cut sub dispm_const_group (%) { my %opt = @_; my $name = $opt{resource}->{ExpandedURI q}; for my $cg (values %{$opt{resource}->{ExpandedURI q}}) { if (defined $cg->{ExpandedURI q}) { valid_err (qq{"$name"."$cg->{ExpandedURI q}": }. qq{Nesting constant group not supported}, node => $cg->{src}); } } my $result = ''; my @cname; if (length $name) { if (defined $opt{ExpandedURI q}->{$name}) { valid_err qq, node => $opt{resource}->{src}; } $opt{ExpandedURI q}->{$name} = \@cname; } for my $cv (values %{$opt{resource}->{ExpandedURI q}}) { next unless defined $cv->{ExpandedURI q}; #$result .= dispm_const_value_sub (%opt, resource => $cv); push @cname, $cv->{ExpandedURI q}; } return $result; } # dispm_const_group =item $code = disperl_to_perl (node => $node, %opt) Converts a C node to a Perl code fragment. =cut sub disperl_to_perl (%) { my %opt = @_; my $code = ''; for (@{$opt{node}->child_nodes}) { next unless $_->node_type eq '#element'; next unless dis_node_for_match ($_, $opt{For}, %opt); my $et = dis_element_type_to_uri ($_->local_name, %opt, node => $_); if ($et eq ExpandedURI q) { my $cn = $_->value; if ($cn =~ /^((?>(?!\.)$RegQNameChar)*)\.($RegQNameChar+)$/o) { my ($cls, $constn) = ($1, $2); if (length $cls) { my $clsu = dis_typeforqnames_to_uri ($cls, %opt, use_default_namespace => 1, node => $_); $cls = $State->{Type}->{$clsu}; valid_err qq must be defined>, node => $_ unless defined $cls->{Name}; } else { $cls = $State->{ExpandedURI q}; valid_err q, node => $_ unless defined $cls->{Name}; } my $const = $cls->{ExpandedURI q}->{$constn}; valid_err qq. qq{"$cls->{Name}" (<$cls->{URI}>)}, node => $_ unless defined $const->{Name}; $code .= perl_statement perl_assign perl_var (type => '$', local_name => 'r') => dispm_const_value (resource => $const); } else { valid_err q, node => $_; } } elsif ($et eq ExpandedURI q) { my $v = dispm_get_value (%opt, node => $_); $code .= perl_statement perl_assign perl_var (type => '$', local_name => 'r') => $v; } elsif ($et eq ExpandedURI q or $et eq ExpandedURI q) { my $uri = dis_qname_to_uri ($_->value, %opt, node => $_, use_default_namespace => 1); $code .= perl_statement perl_assign perl_var (type => '$', local_name => 'r') => '$self->{'.(ExpandedURI q). '}->{'.(perl_literal $uri).'}'; if ($et eq ExpandedURI q) { $code .= perl_if 'defined $r', perl_code (q{ ($r)}, %opt, node => $_); } } elsif ($et eq ExpandedURI q) { my $uri = dis_qname_to_uri ($_->value, %opt, node => $_, use_default_namespace => 1); my $chk = dis_get_attr_node (%opt, parent => $_, name => 'CheckReadOnly'); if ($chk and $chk->value) { my $for1 = $opt{For} || ExpandedURI q; unless (dis_uri_for_match (ExpandedURI q, $for1, node => $_)) { $for1 = ExpandedURI q; } $code .= perl_if q[$self->{].(perl_literal ExpandedURI q). q[}->{].(perl_literal ExpandedURI q). q[}], perl_statement dispm_perl_throws (%opt, class_for => $for1, class => ExpandedURI q, type => 'NO_MODIFICATION_ALLOWED_ERR', subtype => ExpandedURI q); } $code .= perl_statement perl_assign '$self->{'.(ExpandedURI q). '}->{'.(perl_literal $uri).'}' => perl_var (type => '$', local_name => 'given'); } elsif ($et eq ExpandedURI q) { my $memref = $_->value; my $mem = dispm_memref_to_resource ($memref, %opt, node => $_, return_returner => 1, use_default_type_resource => $State->{ExpandedURI q}, ## ISSUE: Reference in a resource that is ## referred from another resource might ## not be interpreted correctly. ); ## ISSUE: It might be required to detect a loop $code .= dispm_get_code (%opt, resource => $mem, For => [keys %{$mem->{For}}]->[0], ExpandedURI q => ExpandedURI q); } elsif ($et eq ExpandedURI q) { my $cprop = dis_get_attr_node (%opt, parent => $_, name => {uri => ExpandedURI q}); my $propvalue; if ($cprop) { my $cpropuri = dis_qname_to_uri ($cprop->value, use_default_namespace => 1, %opt, node => $cprop); my $prop; if ($opt{ExpandedURI q}) { if (ref $opt{ExpandedURI q} eq 'HASH') { $prop = $opt{ExpandedURI q}; if (defined $prop->{$cpropuri}) { $propvalue = $prop->{$cpropuri}; } else { $propvalue = ''; } } else { $prop = dis_get_attr_node (%opt, parent => $opt{ExpandedURI q}, name => {uri => $cpropuri}); if ($prop) { $propvalue = $prop->value; } else { $propvalue = ''; } } } else { valid_err q, node => $_; } } else { valid_err q, node => $_; } my $selcase; for my $case (@{$_->child_nodes}) { next unless $case->node_type eq '#element'; next unless dis_node_for_match ($case, $opt{For}, %opt); my $et = dis_element_type_to_uri ($case->local_name, %opt, node => $case); if ($et eq ExpandedURI q) { my $val = dis_get_attr_node (%opt, parent => $case, name => 'Value', ContentType => ExpandedURI q, defaultContentType => ExpandedURI q); if ($val and $val->value eq $propvalue) { $selcase = $case; last; } elsif ($propvalue eq '' and (not $val or not $val->value)) { $selcase = $case; last; } } elsif ($et eq ExpandedURI q) { $selcase = $case; last; } elsif ({ ExpandedURI q => 1, }->{$et}) { # } else { valid_err qq not allowed here>, node => $case; } } if ($selcase) { my $lcode = perl_code ($selcase->value, %opt, node => $selcase); if ($opt{is_inline}) { $code .= $lcode; } else { $code .= perl_code_source ($lcode, %opt, node => $selcase); } } } elsif ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$et}) { # } else { valid_err qq not supported>, node => $opt{node}; } } my $val = $opt{node}->value; if (defined $val and length $val) { my $lcode = perl_code ($val, %opt); if ($opt{is_inline}) { $code .= $lcode; } else { $code .= perl_code_source ($lcode, %opt); } } return $code; } # disperl_to_perl =item $res = dispm_memref_to_resource ($memref, %opt) Converts a C (a reference to a class member, i.e. either method, attribute, attribute getter or attribute setter) to a resource. =cut sub dispm_memref_to_resource ($%) { my ($memref, %opt) = @_; my ($clsq, $memq) = split /\./, $memref, 2; unless (defined $memq) { valid_err qq<"$memref": Member name required>. node => $opt{node}; } elsif ($memq =~ /:/) { valid_err qq<"$memref": Prefixed member name not supported>, node => $opt{node}; } ## Class my $cls; my $clsuri; if ($clsq eq '') { if (defined $opt{use_default_type_resource}->{Name}) { $cls = $opt{use_default_type_resource}; $clsuri = $cls->{URI}; } elsif ($opt{use_default_type}) { $clsuri = $opt{use_default_type}; } else { $clsuri = dis_typeforqnames_to_uri ($clsq, use_default_namespace => 1, %opt); } } else { $clsuri = dis_typeforqnames_to_uri ($clsq, use_default_namespace => 1, %opt); } unless ($cls) { $cls = $State->{Type}->{$clsuri}; valid_err qq must be defined>, node => $opt{node} unless defined $cls->{Name}; } ## Method or attribute my $memname = $memq; my $mem; for (values %{$cls->{ExpandedURI q}||{}}) { if (defined $_->{Name} and $_->{Name} eq $memname) { $mem = $_; last; } } if ($mem) { if ($opt{return_method_returner}) { if (defined $mem->{ExpandedURI q}->{Name}) { $mem = $mem->{ExpandedURI q}; } elsif (defined $mem->{ExpandedURI q}->{Name}) { $mem = $mem->{ExpandedURI q}; } else { valid_err qq{Neither "return" nor "getter" is defined for }. qq{the class "$cls->{Name}" <$cls->{URI}>}, node => $opt{node}; } } } elsif ($memname =~ s/^([gs]et)(?=.)//) { my $gs = $1; $memname = lcfirst $memname; my $memp; for (values %{$cls->{ExpandedURI q}||{}}) { if (defined $_->{Name} and $_->{Name} eq $memname) { $memp = $_; last; } } if ($memp) { if ($gs eq 'set') { $mem = $memp->{ExpandedURI q}; unless (defined $mem->{Name}) { valid_err qq{Setter for "$memp->{Name}" <$memp->{URI}> is not defined}, node => $opt{node}; } } else { $mem = $memp->{ExpandedURI q}; unless (defined $mem->{Name}) { valid_err qq{Getter for "$memp->{Name}" <$memp->{URI}> is not defined}, node => $opt{node}; } } } } valid_err qq is not defined>, node => $opt{node} unless defined $mem->{Name}; return $mem; } # dispm_memref_to_resource =item $hash = dispm_collect_hash_prop_value ($resource, $propuri, %opt) Get property values from a resource and its superclasses (Cs - Cs are not checked). =cut ## TODO: Loop test might be required sub dispm_collect_hash_prop_value ($$%) { my ($res, $propu, %opt) = @_; my %r; for (@{$res->{ISA}||[]}) { %r = (%{dispm_collect_hash_prop_value ($State->{Type}->{$_}, $propu, %opt)}, %r); } %r = (%r, %{$res->{$propu}||{}}); \%r; } # dispm_collect_hash_prop_value ## Outputed module and "For" my $mf = dis_get_module_uri (module_name => $Opt{module_name}, module_uri => $Opt{module_uri}, For => $Opt{For}); $State->{DefaultFor} = $mf->{For}; $State->{module} = $mf->{module}; our $result = ''; valid_err (qq{Perl module <$State->{module}> not defined for <$State->{DefaultFor}>}, node => $State->{Module}->{$State->{module}}->{src}) unless $State->{Module}->{$State->{module}} ->{ExpandedURI q}; $State->{ExpandedURI q} = 'main'; my $header = "#!/usr/bin/perl \n"; $header .= perl_comment q . "\n" . q<"> . $Opt{file_name} . q<" at > . rfc3339_date (time) . qq<.\n> . q; $header .= perl_comment qq{Module <$State->{module}>}; $header .= perl_comment qq{For <$State->{DefaultFor}>}; $header .= perl_statement q; $header .= perl_change_package (full_name => $State->{Module}->{$State->{module}} ->{ExpandedURI q}); $header .= perl_statement perl_assign perl_var (type => '$', local_name => 'VERSION', scope => 'our') => perl_literal version_date time; ## -- Classes my %opt; for my $pack (values %{$State->{Module}->{$State->{module}} ->{ExpandedURI q}||{}}) { next unless defined $pack->{Name}; if ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$pack->{ExpandedURI q}}) { ## Package name and version $result .= perl_change_package (full_name => $pack->{ExpandedURI q}); $result .= perl_statement perl_assign perl_var (type => '$', local_name => 'VERSION', scope => 'our') => perl_literal version_date time; ## Inheritance ## TODO: IF "isa" should be expanded my $isa = $pack->{ExpandedURI q} || []; for (@$isa) { $State->{Module}->{$State->{module}} ->{ExpandedURI q}->{$_} ||= 1; } for my $uri (@{$pack->{ISA}||[]}, @{$pack->{Implement}||[]}) { my $pack = $State->{Type}->{$uri}; if (defined $pack->{ExpandedURI q}) { push @$isa, $pack->{ExpandedURI q}; } else { impl_msg ("Inheriting package name for <$uri> not defined", node => $pack->{src}) if $Opt{verbose}; } } $isa = array_uniq $isa; $result .= perl_inherit $isa; $State->{ExpandedURI q}->{$_} ||= $pack->{src} || 1 for @$isa; ## Role my $role = dispm_collect_hash_prop_value ($pack, ExpandedURI q, %opt); my $feature; for (values %$role) { my $roleres = $State->{Type}->{$_->{Role}}; my $compatres; $compatres = $State->{Type}->{$_->{compat}} if defined $_->{compat}; valid_err qq{Perl package name for interface <$_->{Role}> must be defined}, node => $roleres->{src} unless defined $roleres->{ExpandedURI q}; valid_err qq{Perl package name for class <$_->{compat}> must be defined}, node => $compatres->{src} if $compatres and not defined $compatres->{ExpandedURI q}; if ({ dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, }->{$_->{Role}}) { unless ($feature) { $feature = {}; for (keys %{dispm_collect_hash_prop_value ($pack, ExpandedURI q, %opt)}) { my $f = $State->{Type}->{$_}; my $version = $f->{ExpandedURI q}; $version = '' unless defined $version; for (keys %{$f->{ExpandedURI q}}) { $feature->{$_}->{$version} = length $version ? $f->{ExpandedURI q} ? 0 : 1 : 1; } } } my %f = ( packageName => $pack->{ExpandedURI q}, feature => $feature, ); $result .= perl_statement (($compatres ? perl_var (type => '$', package => $compatres ->{ExpandedURI q}, local_name => 'Class'). '{'.(perl_literal ($f{packageName})).'} = ' : ''). perl_var (type => '$', package => $roleres ->{ExpandedURI q}, local_name => 'Class'). '{'.(perl_literal ($f{packageName})).'} = '. perl_literal \%f); } elsif ({ dis_typeforuris_to_uri (ExpandedURI q, ExpandedURI q, %opt) => 1, }->{$_->{Role}}) { $result .= perl_statement 'push @org::w3c::dom::DOMImplementationSourceList, '. perl_literal ($pack->{ExpandedURI q}); } else { valid_err qq{Role <$_->{Role}> not supported}, $_->{node}; } } ## Members if ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$pack->{ExpandedURI q}}) { local $State->{ExpandedURI q} = $pack; local $opt{ExpandedURI q} = $pack->{ExpandedURI q}; for my $method (values %{$pack->{ExpandedURI q}}) { next unless defined $method->{Name}; next unless length $method->{ExpandedURI q}; if ($method->{ExpandedURI q} eq ExpandedURI q) { local $opt{ExpandedURI q} = $method->{ExpandedURI q}; local $opt{ExpandedURI q} = $method; my $proto = '$'; my @param = ('$self'); my $param_norm = ''; my $param_opt = 0; my $for = [keys %{$method->{For}}]->[0]; for my $param (@{$method->{ExpandedURI q}||[]}) { my $atype = $param->{ExpandedURI q}; if ($param->{ExpandedURI q}) { $proto .= ';' unless $param_opt; $param_opt++; } if (dis_uri_ctype_match (ExpandedURI q, $atype, %opt)) { $proto .= '@'; push @param, '@'.$param->{ExpandedURI q}; } elsif (dis_uri_ctype_match (ExpandedURI q, $atype, %opt)) { $proto .= '%'; push @param, '%'.$param->{ExpandedURI q}; } else { $proto .= '$'; push @param, '$'.$param->{ExpandedURI q}; } my $nin = dis_get_attr_node (%opt, parent => $param->{ExpandedURI q}, name => {uri => ExpandedURI q}, ); if ($nin and $nin->value) { ## No input normalizing } else { my $nm = dispm_get_code (%opt, resource => $State->{Type}->{$atype}, ExpandedURI q => ExpandedURI q, For => $for, ExpandedURI q => 1, ExpandedURI q => $param->{ExpandedURI q}); if (defined $nm) { $nm =~ s/\$INPUT\b/$param[-1] /g; ## NOTE: "Perl:Array" or "Perl:Hash" is not supported. $param_norm .= $nm; } } } my $code = dispm_get_code (%opt, resource => $method->{ExpandedURI q}, For => $for, ExpandedURI q => ExpandedURI q); if (defined $code) { my $my = perl_statement ('my ('.join (", ", @param). ') = @_'); my $return = defined $method->{ExpandedURI q}->{Name} ? $method->{ExpandedURI q} : undef; if ($return->{ExpandedURI q} ? 1 : 0) { my $default = dispm_get_value (%opt, resource => $return, ExpandedURI q => ExpandedURI q, ExpandedURI q => 1, ExpandedURI q => $return->{ExpandedURI q}); $code = $my . $param_norm . perl_statement (defined $default ? 'my $r = '.$default : 'my $r'). $code . "\n" . perl_statement ('$r'); } else { $code = $my . $code; } } else { ## Code not defined my $for1 = $for; unless (dis_uri_for_match (ExpandedURI q, $for, node => $method->{src})) { $for1 = ExpandedURI q; } $code = perl_statement 'my $self = shift;'; $code .= perl_statement dispm_perl_throws class => ExpandedURI q, class_for => $for1, type => 'NOT_SUPPORTED_ERR', subtype => ExpandedURI q, xparam => { ExpandedURI q => $pack->{ExpandedURI q}, ExpandedURI q => $method->{ExpandedURI q}, }; } $result .= perl_sub (name => $method->{ExpandedURI q}, code => $code, prototype => $proto); } elsif ($method->{ExpandedURI q} eq ExpandedURI q) { local $opt{ExpandedURI q} = $method->{ExpandedURI q}; my $getter = $method->{ExpandedURI q}; valid_err qq{Getter for attribute "$method->{Name}" must be }. q{defined}, node => $method->{src} unless $getter; my $setter = defined $method->{ExpandedURI q}->{Name} ? $method->{ExpandedURI q} : undef; my $for = [keys %{$method->{For}}]->[0]; my $for1 = $for; unless (dis_uri_for_match (ExpandedURI q, $for, node => $method->{src})) { $for1 = ExpandedURI q; } local $opt{ExpandedURI q} = 'get'; my $get_code = dispm_get_code (resource => $getter, For => $for, ExpandedURI q => ExpandedURI q); if (defined $get_code) { my $default = dispm_get_value (%opt, resource => $getter, ExpandedURI q => ExpandedURI q, ExpandedURI q => 1, ExpandedURI q => $getter->{ExpandedURI q}); $get_code = perl_statement (defined $default ? 'my $r = '.$default : 'my $r'). $get_code. "\n" . perl_statement ('$r'); } else { ## Get code not defined $get_code = perl_statement dispm_perl_throws class => ExpandedURI q, class_for => $for1, type => 'NOT_SUPPORTED_ERR', subtype => ExpandedURI q, xparam => { ExpandedURI q => $pack->{ExpandedURI q}, ExpandedURI q => $method->{ExpandedURI q}, ExpandedURI q => 'get', }; } if ($setter) { local $opt{ExpandedURI q} = 'set'; my $set_code = dispm_get_code (%opt, resource => $setter, For => $for, ExpandedURI q => ExpandedURI q); if (defined $set_code) { my $nm = dispm_get_code (%opt, resource => $State->{Type} ->{$setter->{ExpandedURI q}}, ExpandedURI q => ExpandedURI q, For => $for, ExpandedURI q => 1); if (defined $nm) { $nm =~ s/\$INPUT\b/\$given /g; } else { $nm = ''; } my $default = dispm_get_value (%opt, resource => $setter, ExpandedURI q => ExpandedURI q, ExpandedURI q => 1, ExpandedURI q => $getter->{ExpandedURI q}); $set_code = $nm . perl_statement (defined $default ? 'my $r = '.$default : 'my $r'). $set_code. "\n" . perl_statement ('$r'); } else { ## Set code not defined $set_code = perl_statement dispm_perl_throws class => ExpandedURI q, class_for => $for1, type => 'NOT_SUPPORTED_ERR', subtype => ExpandedURI q, xparam => { ExpandedURI q => $pack->{ExpandedURI q}, ExpandedURI q => $method->{ExpandedURI q}, ExpandedURI q => 'set', }; } $get_code = perl_if '@_ == 2', perl_statement ('my ($self, $given) = @_'). $set_code, perl_statement ('my ($self) = @_'). $get_code; } else { $get_code = perl_statement ('my ($self) = @_'). $get_code; } $result .= perl_sub (name => $method->{ExpandedURI q}, prototype => $setter ? '$;$' : '$', code => $get_code); } } # package method for my $cg (values %{$pack->{ExpandedURI q}}) { next unless defined $cg->{Name}; $result .= dispm_const_group (resource => $cg); } # package const group for my $cv (values %{$pack->{ExpandedURI q}}) { next unless defined $cv->{Name}; $result .= dispm_const_value_sub (resource => $cv); } # package const value } } # root object } $result .= dispm_package_declarations; my $begin = ''; for (keys %{$State->{Module}->{$State->{module}} ->{ExpandedURI q}||{}}) { next if $_ eq $State->{Module}->{$State->{module}} ->{ExpandedURI q}; $begin .= perl_statement ('require ' . $_); $State->{ExpandedURI q}->{$_} = -1; } $result = $begin . $result if $begin; my @ref; for (keys %{$State->{ExpandedURI q}||{}}) { my $v = $State->{ExpandedURI q}->{$_}; if (ref $v or $v >= 0) { push @ref, $_; } } $result .= "for (" . join (", ", map {'$'.$_.'::'} @ref) . ") {}" if @ref; $result = $header . $result . perl_statement 1; output_result $result; 1;