Concise way to make a 0+ length list in Marpa grammar? - perl

I'm new to Marpa. I've tried a couple ways to describe a list of 0 or more terms in my grammar, and I want to avoid multiple parse trees.
My language will have exactly 1 component followed by 0+ subcomponents:
package => component-rule [subcomponent-rule ...]
What I tried first was this:
{ lhs => 'Package', rhs => [qw/component-rule subcomponents/] },
{ lhs => 'subcomponents', rhs => [qw/subcomponent-list/] },
{ lhs => 'subcomponent-list', rhs => [qw/subcomponent-rule/], action => 'do_subcomponent_list' },
{ lhs => 'subcomponent-list', rhs => [qw/subcomponent-list subcomponent-rule/], action => 'do_subcomponent_list' },
{ lhs => 'subcomponent-list', rhs => [qw//], action => 'do_subcomponent_empty_list' },
{ lhs => 'subcomponent-rule', rhs => [qw/subcomponent subcomponent-name/], action => 'do_subcomponent' },
(Full code at end of post.)
Here's my input:
$recce->read( 'component', );
$recce->read( 'String', 'MO Factory');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'Memory Wipe Station');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'DMO Tour Robot');
I get two parse trees, the first one with an undesirable undef, and the second one which I prefer. Both give the list back as inherently a tree.
$VAR1 = [
{
'Component' => 'MO Factory'
},
[
[
{
'Subcomponent' => undef
},
{
'Subcomponent' => 'Memory Wipe Station'
}
],
{
'Subcomponent' => 'DMO Tour Robot'
}
]
];
$VAR2 = [
{
'Component' => 'MO Factory'
},
[
{
'Subcomponent' => 'Memory Wipe Station'
},
{
'Subcomponent' => 'DMO Tour Robot'
}
]
];
The nullable rule for subcomponent-list was to allow the case of 0 subcomponents, but it introduces the null element on the front of a list of 1+ subcomponents, which is an alternate parse. (Marpa descends the cycle only once, thank goodness.)
My other idea was to make subcomponent-list non-nullable, and introduce an intermediate rule that is 0 or 1 subcomponent-lists:
{ lhs => 'subcomponents', rhs => [qw//] },
{ lhs => 'subcomponents', rhs => [qw/subcomponent-list/] },
This at least eliminated the multiple parse, but I still have a cycle, and a messy nested tree to compress.
Is there a more direct way to make a 0+ length list or otherwise make a symbol optional?
Full sample code:
#!/usr/bin/perl
use Marpa::R2;
use Data::Dumper;
my $grammar = Marpa::R2::Grammar->new(
{ start => 'Package',
actions => 'My_Actions',
default_action => 'do_what_I_mean',
rules => [
{ lhs => 'Package', rhs => [qw/component-rule subcomponents/] },
{ lhs => 'component-name', rhs => [qw/String/] },
{ lhs => 'component-rule', rhs => [qw/component component-name/], action => 'do_component' },
{ lhs => 'subcomponent-name', rhs => [qw/String/] },
{ lhs => 'subcomponent-rule', rhs => [qw/subcomponent subcomponent-name/], action => 'do_subcomponent' },
{ lhs => 'subcomponents', rhs => [qw//] },
{ lhs => 'subcomponents', rhs => [qw/subcomponent-list/] },
{ lhs => 'subcomponent-list', rhs => [qw/subcomponent-rule/], action => 'do_subcomponent_list' },
{ lhs => 'subcomponent-list', rhs => [qw/subcomponent-list subcomponent-rule/], action => 'do_subcomponent_list' },
# { lhs => 'subcomponent-list', rhs => [qw//], action => 'do_subcomponent_empty_list' },
# { lhs => 'subcomponent-list', rhs => [qw//], },
],
}
);
$grammar->precompute();
my $recce = Marpa::R2::Recognizer->new( { grammar => $grammar } );
$recce->read( 'component', );
$recce->read( 'String', 'MO Factory');
if (1) {
$recce->read( 'subcomponent', );
$recce->read( 'String', 'Memory Wipe Station');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'DMO Tour Robot');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'SMO Break Room');
}
my #values = ();
while ( defined( my $value_ref = $recce->value() ) ) {
push #values, ${$value_ref};
}
print "result is ",Dumper(#values),"\n";
sub My_Actions::do_what_I_mean {
print STDERR "do_what_I_mean\n";
# The first argument is the per-parse variable.
# At this stage, just throw it away
shift;
# Throw away any undef's
my #children = grep { defined } #_;
# Return what's left
return scalar #children > 1 ? \#children : shift #children;
}
sub My_Actions::do_component {
my ( undef, $t1 ) = #_;
print STDERR "do_component $t1\n";
my $href = { 'Component' => $t1 };
return $href;
}
sub My_Actions::do_subcomponent{
my ( undef, $t1 ) = #_;
print STDERR "do_subcomponent $t1\n";
my $href = { 'Subcomponent' => $t1 };
return $href;
}
sub My_Actions::do_subcomponent_empty_list
{
print STDERR "do_subcomponent_empty_list\n";
my $href = { 'Subcomponent' => undef };
return $href;
}
sub My_Actions::do_subcomponent_list{
# The first argument is the per-parse variable.
# At this stage, just throw it away
shift;
# Throw away any undef's
my #children = grep { defined } #_;
print STDERR "do_subcomponent_list size ",scalar(#children),"\n";
# Do this to collapse recursive trees to a list:
# #children = map { ref $_ eq "ARRAY" ? #{$_} : $_; } #children;
return scalar #children > 1 ? \#children : shift #children;
}

Specify a sequence rule with the min argument. The value may either be 0 (aka the * quantifier in regexes) or 1 (aka the + quantifier). You can do this by removing the subcomponents and subcomponent-list rules. Instead add:
{
lhs => 'subcomponents',
rhs => ['subcomponent-rule'],
min => 0,
action => 'do_subcomponent_list',
}
Your grammar then runs without further modifications.
Using sequence rules is preferable: No flattening has to take place, and the grammar should be more efficient.
Note that you are encouraged to use the Scanless Interface. The DSL abstracts nicely over this issue:
subcomponents ::= <subcomponent rule>* action => do_subcomponent_list

Related

odd number of elements in anonymous hash

I'm trying to understand this Perl code...
If there is one stream it works, if there are 2 or more streams it warns with odd number of elements in anonymous hash. It seems to return an array in that case. How do I add the array elements correctly to #streams? It appears to add correctly for the HASH case in the if clause. Is the else clause bunk?
my $x = $viewedProjectDataObj->{streams};
if (ref($x) eq 'HASH') {
push(#streams, $x->{id});
} elsif (ref($x) eq 'ARRAY') {
print "$x\n";
print "#$x\n";
my #array = #$x;
foreach my $obj (#array) {
print "in $obj\n";
print Dumper( $obj);
push(#streams, ($obj->{id}) );
}
}
print "streamcount " . #streams % 2;
print Dumper(#streams);
my $stream_defect_filter_spec = {
'streamIdList' => #streams,
'includeDefectInstances' => 'true',
'includeHistory' => 'true',
};
my #streamDefects = $WS->get_stream_defects($defectProxy, \#cids, $stream_defect_filter_spec);
print Dumper(#streamDefects);
I'm adding the next lines...
if ($defectSummary->{owner} eq "Various") {
foreach (#streamDefects) {
if (exists($_->{owner})) {
$defectSummary->{owner} = $_->{owner};
last;
}
}
}
my $diref = $streamDefects[0]->{defectInstances};
if ($diref) {
my $defectInstance;
if (ref($diref) eq 'HASH') {
$defectInstance = $diref;
} elsif (ref($diref) eq 'ARRAY') {
$defectInstance = #{$diref}[0];
} else {
die "Unable to handle $diref (".ref($diref).")";
}
It now errors with
Web API returned error code S:Server: calling getStreamDefects: No stream found
for name null.
$VAR1 = -1;
me
Can't use string ("-1") as a HASH ref while "strict refs" in use at xyz-handler.pl line 317.
some Dumper output
$VAR1 = {
'streamIdList' => [
{
'name' => 'asdfasdfadsfasdfa'
},
{
'name' => 'cpp-62bad47d63cfb25e76b29a4801c61d8d'
}
],
'includeDefectInstances' => 'true',
'includeHistory' => 'true'
};
The list assigned to a hash is a set of key/value pairs, which is why the number of elements must be even.
Because the => operator is little more than a comma, and the #streams array is flattened in the list, this
my $stream_defect_filter_spec = {
'streamIdList' => #streams,
'includeDefectInstances' => 'true',
'includeHistory' => 'true',
};
is equivalent to this
my $stream_defect_filter_spec = {
'streamIdList' => $streams[0],
$streams[1] => $streams[2],
$streams[3] => $streams[4],
...
'includeDefectInstances' => 'true',
'includeHistory' => 'true',
};
so I hope you can see that you will get the warning if you have an even number of elements in the array.
To fix things you need the value of the hash element to be an array reference, which is a scalar and won't upset the scheme of things
my $stream_defect_filter_spec = {
'streamIdList' => \#streams,
'includeDefectInstances' => 'true',
'includeHistory' => 'true',
};
that way you can access the array elements as
$stream_defect_filter_spec->{streamIdList}[0]
etc.
And by the way you can tidy up your code substantially by letting map do what it's good at:
if (ref $x eq 'HASH') {
push #streams, $x->{id};
}
elsif (ref $x eq 'ARRAY') {
push #streams, map $_->{id}, #$x;
}
The assignment in:
my $stream_defect_filter_spec = {
'streamIdList' => #streams, # <---- THIS ONE
'includeDefectInstances' => 'true',
'includeHistory' => 'true',
};
is not correct, you get hash keys from the 1 3 5th ... array element.
You probably want assign a reference to array, not the array itself:
'streamIdList' => \#streams,
example for the unwanted (as in your code):
use strict;
use warnings;
use Data::Dump;
my #z = qw(a b c x y z);
dd \#z;
my $q = {
'aa' => #z,
};
dd $q;
unwanted result:
["a", "b", "c", "x", "y", "z"]
Odd number of elements in anonymous hash at a line 12.
{ aa => "a", b => "c", x => "y", z => undef }
^-here
Example of assign a reference
use strict;
use warnings;
use Data::Dump;
my #z = qw(a b c x y z);
dd \#z;
my $q = {
'aa' => \#z,
};
dd $q;
produces:
["a", "b", "c", "x", "y", "z"]
{ aa => ["a", "b", "c", "x", "y", "z"] }
The difference is clearly visible.

unable to access hash of array element created using XML parser

My XML Parser looks as below:
$VAR1 = {
'Symmetrix' => {
'Masking_View' => {
'View_Info' => {
'Initiators' => {
'user_node_name' => [
'5001438001725614',
'5001438001725714'
],
'user_port_name' => [
'5001438001725614',
'5001438001725714'
],
'wwn' => [
'5001438001725614',
'5001438001725714'
]
},
'port_grpname' => 'PG_1E0_2E0'
}
},
'Symm_Info' => {
'symid' => '000295900074'
}
}
};
I am trying to pull element of wwn. But I'm not able to get through.
#!C:\Perl\bin
use strict;
use XML::Simple;
use Data::Dumper;
my $input_file = $ARGV[0];
my $detail_info = XMLin("$input_file");
# Loop through each view_info
$detail_info->{Symmetrix}{Masking_View}{View_Info} = [ $detail_info->{Symmetrix} {Masking_View}{View_Info} ] if ref ($detail_info->{Symmetrix}{Masking_View}{View_Info}) ne 'ARRAY';
foreach my $view_info (#{$detail_info-> {Symmetrix}{Masking_View}{View_Info}})
{
$view_info->{Initiators} = [$view_info->{Initiators}] if ref ($view_info-> {Initiators}) ne 'ARRAY';
foreach my $wwn (keys %{$view_info->{Initiators}})
{
my #flags = ();
push (#flags,"$wwn:$view_info->{Initiators}{$wwn}";
print #flags;
#"{$wwn->{wwn}}";
}
}
I am getting output as below;
{ARRAY(0x20c8904)}
I am looking for wwn element in single line of different line.
You're producing too much code to distinguish arrayref values from non-reference values. Strict mode requires you to set the ForceArray options, and thus you can be certain that every value is an arrayref, even if there's just one element in it.
use strictures;
use XML::Simple qw(:strict);
my $detail_info = XMLin($ARGV[0], ForceArray => 1, KeyAttr => []);
# $detail_info is {
# Symmetrix => [
# {
# name => 'Masking_View',
# View_Info => [
# {
# Initiators => [
# {
# user_node_name => [5001438001725614, 5001438001725714],
# user_port_name => [5001438001725614, 5001438001725714],
# wwn => [5001438001725614, 5001438001725714]
# }
# ],
# port_grpname => 'PG_1E0_2E0'
# }
# ]
# },
# {
# name => 'Symm_Info',
# symid => '000295900074'
# }
# ]
# }
my #flags;
for my $view_info (#{ $detail_info->{Symmetrix}[0]{View_Info} }) {
for my $initiator (#{ $view_info->{Initiators} }) {
push #flags, $initiator->{wwn};
}
}
# #flags is (
# [
# 5001438001725614,
# 5001438001725714
# ]
# )

Comparing and validating data structures

I have to check hashrefs like this one
{ foo => 65, bar => 20, baz => 15 }
against an arrayref of hashrefs expressing conditions like this
[
{ foo => { "<=" => 75 } },
{ bar => { "==" => 20 } },
{ baz => { ">=" => 5 } },
]
and return a true value if all conditions are fulfilled.
Neither of the two data structures is pre-determined. One is built from parsing a string in a database, the other from parsing user input.
In the case above, I would return true, but if I checked the hashref against
[
{ foo => { "<=" => 60 } },
{ bar => { "==" => 20 } },
{ baz => { ">=" => 5 } },
]
I would return false, because foo in the first hashref is not <= 60.
The question is: what's the best strategy for doing that?
I am thinking of
building a series of subrefs via eval
checking against the appropriate one among 5 different pre-built subrefs (one per case for >, <, <=, >= and ==)
Am I going down the wrong path altogether? and if not, what's the best, eval or pre-built functions?
I have looked into Params::Validate but I am concerned that it'd be a lot of overhead, and I'd have to build the callbacks anyway.
Use code references instead, and you will have ready to go validators. I simplified your condition-structure. There is no need to have an extra array level in there, unless you have duplicate hash keys, which I assume you don't.
The simplistic sub { $_[0] <= 75 } will simply compare the first value of the arguments. By default, the last value evaluated in the subroutine will be its return value.
use v5.10;
use strict;
use warnings;
my $in = { foo => 65, bar => 21, baz => 15 };
my $ref = {
foo => sub { $_[0] <= 75 } ,
bar => sub { $_[0] == 20 } ,
baz => sub { $_[0] >= 5 } ,
};
for my $key (keys %$in) {
if ($ref->{$key}($in->{$key})) {
say "$key : Valid";
} else {
say "$key : Invalid";
}
}
Output:
bar : Invalid
baz : Valid
foo : Valid
To build on TLP's answer, you can also easily create the anonymous subs from your existing array-of-hashes:
my $array_of_hashes = [
{ foo => { "<=" => 75 } },
{ bar => { "==" => 20 } },
{ baz => { ">=" => 5 } },
];
my $ref = {};
foreach my $entry ( #$array_of_hashes ) {
my ($key, $subhash) = %$entry;
my ($op, $num) = %$subhash;
$ref->{$key} = {
'<=' => sub { $_[0] <= $num },
'==' => sub { $_[0] == $num },
'>=' => sub { $_[0] >= $num },
}->{$op};
}
This assumes that you only have one check for each field in your original array-of-hashes. If you might have several, things get a bit more tricky, but you could always do something like this:
my $ref = {};
foreach my $entry ( #$array_of_hashes ) {
my ($key, $subhash) = %$entry;
my ($op, $num) = %$subhash;
my $chain = $ref->{$key} || sub {1};
$ref->{$key} = {
'<=' => sub { $_[0] <= $num and $chain->($_[0]) },
'==' => sub { $_[0] == $num and $chain->($_[0]) },
'>=' => sub { $_[0] >= $num and $chain->($_[0]) },
}->{$op} || $chain;
}
Ps. In case anyone's wondering just how this code can possibly work, the answer is: closures. Specifically, when those anonymous subs are created inside the loop, they retain references to the lexical variables $num and $chain, even after these variables go out of scope at the end of the current iteration of the loop. So, forever thereafter, those variables will be safely squirreled away, accessible only from the subroutine we've created.

How can I force list context in Template Toolkit with RDBO?

I have a TT plugin that does the trivial unique ids:
sub get_unique_uid_tt {
my ( $classname, $o ) = #_;
my %h;
foreach my $item ( #{$o} ) {
unless ( exists $h{ $item->id } ) {
$h{ $item->id } = 1;
}
}
return keys %h;
}
where the template call is simply:
[% Namespace.get_unique_uid_tt( data.users ) %]
and "data" is an RDB Object, users being one of its relationships. I have verified that the ".users" returns a list in Perl directly, whether the relationship has one or many elements.
However, it appears that TT returns the element for single-element lists, while properly returning lists for multiple element.
I looked this up and found that you can force list context with ".list":
[% Namespace.get_unique_uid_tt( data.users.list ) %]
This does not work as intended for single-element lists, as a Data::Dumper revealed:
$VAR1 = [
{
'value' => 1,
'key' => '__xrdbopriv_in_db'
},
{
'value' => bless(
... snip ...
),
'key' => 'db'
},
{
'value' => '1',
'key' => 'id'
}
];
instead of the expected
$VAR1 = [
bless( {
'__xrdbopriv_in_db' => 1,
'id' => '1',
'db' => ... snip ...
}, 'DataClass' )
];
Is there any other simple way in TT to get a list of objects, even on single-element lists? (One approach is to rewrite the function, but one that does not would be preferable)
Found this on the TT mailing list:
http://lists.template-toolkit.org/pipermail/templates/2009-December/011061.html
seems like TT's ".list" has trouble converting objects to lists in general, not just RDBOs.
The suggestion is make a vmethod:
$Template::Stash::LIST_OPS->{ as_list } = sub {
return ref( $_[0] ) eq 'ARRAY' ? shift : [shift];
};
I added this to my context object (same idea):
$context->define_vmethod(
'list',
'as_list',
sub {
return ref( $_[0] ) eq 'ARRAY' ? shift : [shift];
},
);
It's not quite what you're after, but could you alter the TT plugin to handle both lists and single items?
sub get_unique_uid_tt {
my ( $classname, $o ) = #_;
my %h;
if (ref $o eq 'ARRAY') {
foreach my $item ( #{$o} ) {
unless ( exists $h{ $item->id } ) {
$h{ $item->id } = 1;
}
}
}
else {
return ($o->id);
}
return keys %h;
}

RegSvr32 registering yet nothing actually registered

A rather odd experience. Using the latest PDK (v7.3) from ActiveState, I used perlctrl to build a COM DLL. Perlctrl ran without a hitch. OLEView read the typelib okay. RegSvr32 registered it okay. However ... there's no sign of it in registry, and anything that tries to use it fails. I hunted for the various UIDs using RegEdit and they're just not there.
The code is below. It's a wrapping of Lingua::ZH::WordSegmenter, but with the encoding changed to utf8 rather than gbk.
It's probably something obvious ...
package ZHWordSeg;
use strict;
use warnings;
use utf8;
use ws;
use Encode;
use constant STX => chr( 2 ); #[
use constant ETX => chr( 3 ); #]
use constant FS => chr( 28 ); #^
use constant RS => chr( 30 ); #~
use constant TAB_SEPARATOR => 0;
use constant CARET_SEPARATOR => 1;
use constant FS_SEPARATOR => 2;
use constant SPACE_SEPARATOR => 3;
use constant AS_ARRAY => 4;
use feature 'switch';
our $segmenter;
sub ZHWordSeg_Setup {
my $dic = shift;
my $dic_encoding = shift;
my $separator = shift;
my $verbose = shift;
$dic_encoding = 'utf8' unless defined( $dic_encoding );
$separator = " " unless defined( $separator );
$verbose = 0 unless defined( $verbose );
if ( defined( $dic ) ) {
$segmenter = ws->new( dic => $dic, dic_encoding => $dic_encoding, seperator => $separator, verbose => $verbose );
} else {
$segmenter = ws->new( dic_encoding => $dic_encoding, seperator => $separator, verbose => $verbose );
}
}
sub ZHWordSeg {
my $source = shift;
print STDERR $source;
my $sepcode = shift;
$source = encode("utf8",$source);
my $stringres = $segmenter->seg($source);
my #arrayres;
given ($sepcode) {
when (TAB_SEPARATOR) {
$stringres =~ tr/ /\t/;
return $stringres;
}
when (CARET_SEPARATOR) {
$stringres =~ tr/ /^/;
$stringres .= "^";
return $stringres;
}
when (FS_SEPARATOR) {
$stringres =~ s/ /FS/eg;
$stringres .= FS;
return $stringres;
}
when (SPACE_SEPARATOR) {
return $stringres;
}
default {
#arrayres = split( / /, $stringres );
return \#arrayres;
}
}
}
sub SetDictionary {
my ($source) = shift;
my $res = set_dic($source);
return $res;
}
1;
=pod
=begin PerlCtrl
%TypeLib = (
PackageName => 'ZHWordSeg',
DocString => 'Chinese word segmentation',
HelpContext => 1,
TypeLibGUID => '{F6C9BD66-7CA1-4610-B77F-E219A7122C18}', # do NOT edit this line
ControlGUID => '{45D47C6A-2B9A-4D62-9CFD-F18C95DC00C5}', # do NOT edit this line either
DispInterfaceIID=> '{007E4E7A-3B75-4DC3-864C-7746860941B3}', # or this one
ControlName => 'BOCWS',
ControlVer => 2, # increment if new object with same ProgID
# create new GUIDs as well
ProgID => 'ZHWordSeg.BOCWS',
LCID => 0,
DefaultMethod => 'ChineseWordSegmenter',
Methods => {
'ChineseWordSegmenter' => {
RetType => VT_VARIANT,
TotalParams => 2,
NumOptionalParams => 1,
ParamList =>
[ 'source' => VT_BSTR,
'sepcode' => VT_I4
]
},
'ChineseWordSegmenter_Setup' => {
RetType => VT_VARIANT,
TotalParams => 4,
NumOptionalParams => 4,
ParamList =>
[ 'dic' => VT_BSTR,
'dic_encoding' => VT_BSTR,
'separator' => VT_BSTR,
'verbose' => VT_BSTR
]
}
}, # end of 'Methods'
Properties => {
TAB_SEPARATOR => {
DocString => "Separate items with TAB (0x0)",
Type => VT_I4,
DispID => 3,
ReadOnly => 1,
},
CARET_SEPARATOR => {
DocString => "Separate items with ^ (0x1)",
Type => VT_I4,
DispID => 4,
ReadOnly => 1,
},
FS_SEPARATOR => {
DocString => "Separate items with ascii 28 (0x2)",
Type => VT_I4,
DispID => 5,
ReadOnly => 1,
},
SPACE_SEPARATOR => {
DocString => "Separate items with space (0x3)",
Type => VT_I4,
DispID => 6,
ReadOnly => 1,
},
AS_ARRAY => {
DocString => "Separate items as array (0x4)",
Type => VT_I4,
DispID => 7,
ReadOnly => 1,
}
}, # end of 'Properties'
); # end of %TypeLib
=end PerlCtrl
=cut
This is the .perlctrl file, in case it matters:
#!C:\Program Files\ActiveState Perl Dev Kit 7.3 Deployment\bin\lib\pai.exe
PAP-Version: 1.0
Packer: C:\Program Files\ActiveState Perl Dev Kit 7.3 Deployment\bin\perlctrl.exe
Script: ZHWordSeg.ctrl
Cwd: P:\BOCWS
Byref: 0
Clean: 0
Date: 2008-10-24 18:05:42
Debug: 127.0.0.1:2000
Dependent: 0
Dyndll: 0
Exe: BOCWS.dll
Force: 1
Gui: 0
Hostname: xi
No-Compress: 0
No-Gui: 0
No-Logo: 0
Runlib:
Shared: none
Singleton: 0
Tmpdir:
Verbose: 0
Version-Comments:
Version-CompanyName:
Version-FileDescription: Wrapper of Lingua::ZH::WordSegmenter.pm
Version-FileVersion: 1.0
Version-InternalName: ZHWordSeg
Version-LegalCopyright:
Version-LegalTrademarks:
Version-OriginalFilename: ZHWordSeg.ctrl
Version-ProductName: BOChineseWordSegmenter
Version-ProductVersion: 1.0
Warnings: 0
Xclude: 1
The only "solution" that I've found was suggested over on news:comp.os.ms-windows.programmer.win32
i am not a PDK user but from experience i can tell you, that you should check the DllRegister exports code and what it internally does, since this is what the regsvr32 calls and this code is creating the registry keys for your com server/proxy, etc, ...
You can track the registry for changes with sysinternal tools like regmon or procmon, just to make sure!
Ultimately, I gave up and went back to a previous version that worked and tweaked it.