#!/usr/bin/perl -w use strict; use Message::Util::QName::Filter { d => q, dis2pm => q, disPerl => q, DOMCore => q, DOMMain => q, lang => q, Perl => q, license => q, ManakaiDOM => q, MDOMX => q, owl => q, rdf => q, rdfs => 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"; our $result = ''; 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}) { $State->{ExpandedURI q} = $fn; return perl_statement qq; } else { return ''; } } # perl_change_package =item $code = dispm_perl_throws (%opt) Generates a code to throw an exception. =cut sub dispm_perl_throws (%) { my %opt = @_; my $x = $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}}) { $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 { 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[<(\w[^<>]+)>|\b(null|true|false)\b][ my ($q, $l) = ($1, $2); my $r; if (defined $q) { if ($q =~ /\}/) { valid_warn qq<"<$q>" has a "}" - it might be a typo>; } 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 ($et eq ExpandedURI q or $et eq ExpandedURI q) { ## 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 => '->'}->{$et} : '') . $mtd->{ExpandedURI q} . ' '; } } elsif ($et eq ExpandedURI q) { ## 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 = perl_literal ($State->{Type}->{$uri} ->{ExpandedURI q}); } else { valid_err qq must be defined>, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { ## CODE constant my $uri = dis_typeforqnames_to_uri ($q, use_default_namespace => 1, %opt); if (defined $State->{Type}->{$uri}->{Name} and dis_resource_ctype_match (ExpandedURI q, $State->{Type}->{$uri}, %opt)) { ## 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); } else { valid_err qq must be defined>, 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__([A-Z]+) (?:\{($RegBlockContent)\})? __\b }{ my ($name, $data) = ($1, $2); my $r; my $et = dis_qname_to_uri ($name, %opt, use_default_namespace => ExpandedURI q); if ($name eq 'XINT') { ## Inserting point of the for-internal code if (defined $data) { if ($data =~ /^{($RegBlockContent)}$/o) { $data = $1; my $name = $1 if $data =~ s/^\s*(\w+)\s*(?:$|:\s*)// or valid_err qq, node => $opt{node}; #local $Status->{preprocess_variable} # = {%{$Status->{preprocess_variable}||{}}}; while ($data =~ /\G(\S+)\s*(?:=>\s*(\S+)\s*)?(?:,\s*|$)/g) { my ($n, $v) = ($1, defined $2 ? $2 : 1); for ($n, $v) { s/^'([^']+)'$/$1/; ## ISSUE: Doesn't support quoted-' } #$Status->{preprocess_variable}->{$n} = $v; } valid_err q unless $opt{internal}; $r = perl_comment ("INT: $name"). $opt{internal}->($name); } elsif ($data =~ s/^SP://) { $r = '___'.$data; } else { $r = perl_internal_name $data; } } else { valid_err q unless $opt{internal}; $r = $opt{internal}->(); } } elsif ($name eq 'DEEP') { ## Deep Method Call $r = '{'.perl_statement ('local $Error::Depth = $Error::Depth + 1'). perl_code ($data) . '}'; } elsif ($name eq 'XEXCEPTION' or $name eq 'XWARNING') { ## Raising an Exception or Warning if ($data =~ s/^\s*(\w+)\s*\.\s*(\w+)\s*(?:\.\s*([\w:]+)\s*)?(?:::\s*|$)//) { $r = perl_exception (level => $name, class => $1, type => $2, subtype => $3, param => perl_code $data); } else { valid_err qq, node => $opt{node}; } } elsif ($et eq ExpandedURI q) { my ($nm, %param); $data =~ s/^\s+//; if ($data =~ s/^((?>(?!::).)+)//) { $nm = $1; } else { valid_err q, node => $opt{node}; } $data =~ s/^::\s*//; while ($data =~ s/^(\S+)\s*=>\s*(\S+)\s*(?:,\s*|$)//) { $param{$1} = $2; } valid_err qq, node => $opt{node} if length $data; 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)) { ## 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]); for (grep {/^\$/} keys %param) { $r =~ s/\Q$_\E\b/ $param{$_} /g; } $r = "\n{\n$r\n}\n"; } else { valid_err qq must be defined>, node => $opt{node}; } } elsif ($name eq 'XPACKAGE' and $data) { if ($data eq 'Global') { #$r = $ManakaiDOMModulePrefix; } else { valid_err qq; } } elsif ($name eq 'XREQUIRE') { #$r = perl_statement (q. perl_package_name name => $data); } elsif ($name eq 'XWHEN') { if ($data =~ s/^\s*IS\s*\{($RegBlockContent)\}::\s*//o) { my $v = $1; if ($v =~ /^\s*'([^']+)'\s*$/) { ## ISSUE: Doesn't support quoted-' if ($State->{preprocess_variable}->{$1}) { $r = perl_code ($data, %opt); } else { $r = perl_comment ($data); } } else { valid_err qq, node => $opt{node}; } } else { valid_err qq, node => $opt{node}; } } elsif ($name eq 'FILE' or $name eq 'LINE' or $name eq 'PACKAGE') { $r = qq<__${name}__>; } else { $r = $&; #valid_err qq; } $r; }goex; $s; } } ## 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}))) { my $key = $opt{ExpandedURI q} || ExpandedURI q; my $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; } $code .= perl_code ($n->value, %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; my $n = 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)) { ## 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 to declare and define a constant function corresponding to the definition 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 perl_sub (name => $opt{resource}->{ExpandedURI q}, prototype => '', code => $value); } # dispm_const_value =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 (%opt, resource => $cv); push @cname, $cv->{ExpandedURI q}; } return $result; } # dispm_const_group ## 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}; 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'; $result .= "#!/usr/bin/perl \n"; $result .= perl_comment q . "\n" . q<"> . $Opt{file_name} . q<" at > . rfc3339_date (time) . qq<.\n> . q; $result .= perl_comment qq{Module <$State->{module}>}; $result .= perl_comment qq{For <$State->{DefaultFor}>}; $result .= perl_statement q; $result .= perl_change_package (full_name => $State->{Module}->{$State->{module}} ->{ExpandedURI q}); $result .= perl_statement perl_assign perl_var (type => '$', local_name => 'VERSION', scope => 'our') => perl_literal version_date time; ## -- Classes 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 = []; 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; $result .= '$' . $_ . "::;\n" for @$isa; ## Members if ({ ExpandedURI q => 1, ExpandedURI q => 1, ExpandedURI q => 1, }->{$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) { my $proto = '$'; my @param = ('self'); my $param_norm = ''; my $param_opt = 0; my $for = [keys %{$method->{For}}]->[0]; for my $param (@{$method->{ExpandedURI q}||[]}) { if ($param->{ExpandedURI q}) { $proto .= ';' unless $param_opt; $param_opt++; } $proto .= '$'; push @param, $param->{ExpandedURI q}; my $nm = dispm_get_code (resource => $State->{Type} ->{$param->{ExpandedURI q}}, ExpandedURI q => ExpandedURI q, For => $for, ExpandedURI q => 1); if (defined $nm) { $nm =~ s/\$INPUT\b/\$$param[-1] /g; $param_norm .= $nm; } } my $code = dispm_get_code (resource => $method->{ExpandedURI q}, For => $for); if (defined $code) { my $my = perl_statement ('my ('.join (", ", map {"\$$_"} @param). ') = @_'); my $return = defined $method->{ExpandedURI q}->{Name} ? $method->{ExpandedURI q} : undef; if ($return->{ExpandedURI q} ? 1 : 0) { my $default = dispm_get_value (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 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) { 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; } my $get_code = dispm_get_code (resource => $getter, For => $for); if (defined $get_code) { my $default = dispm_get_value (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) { my $set_code = dispm_get_code (resource => $setter, For => $for); if (defined $set_code) { my $nm = dispm_get_code (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 (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 (resource => $cv); } # package const value } } # root object } for (keys %{$State->{Module}->{$State->{module}} ->{ExpandedURI q}||{}}) { next if $_ eq $State->{Module}->{$State->{module}} ->{ExpandedURI q}; $result .= perl_statement ('require ' . $_); } $result .= perl_statement 1; output_result $result; 1;