File indexing completed on 2024-05-19 15:11:19

0001 # 
0002 # KDOM IDL parser
0003 #
0004 # Copyright (C) 2005 Nikolas Zimmermann <wildfox@kde.org>
0005 # 
0006 # This file is part of the KDE project
0007 # 
0008 # This library is free software; you can redistribute it and/or
0009 # modify it under the terms of the GNU Library General Public
0010 # License as published by the Free Software Foundation; either
0011 # version 2 of the License, or (at your option) any later version.
0012 # 
0013 # This library is distributed in the hope that it will be useful,
0014 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016 # Library General Public License for more details.
0017 # 
0018 # You should have received a copy of the GNU Library General Public License
0019 # aint with this library; see the file COPYING.LIB.  If not, write to
0020 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021 # Boston, MA 02110-1301, USA.
0022 # 
0023 
0024 package IDLParser;
0025 
0026 use IPC::Open2;
0027 use IDLStructure;
0028 
0029 use constant MODE_UNDEF    => 0; # Default mode.
0030 
0031 use constant MODE_MODULE  => 10; # 'module' section
0032 use constant MODE_INTERFACE  => 11; # 'interface' section
0033 use constant MODE_EXCEPTION  => 12; # 'exception' section
0034 use constant MODE_ALIAS    => 13; # 'alias' section
0035 
0036 # Helper variables
0037 my @temporaryContent = "";
0038 
0039 my $parseMode = MODE_UNDEF;
0040 my $preservedParseMode = MODE_UNDEF;
0041 
0042 my $beQuiet; # Should not display anything on STDOUT?
0043 my $document = 0; # Will hold the resulting 'idlDocument'
0044 
0045 # Default Constructor
0046 sub new
0047 {
0048     my $object = shift;
0049     my $reference = { };
0050 
0051     $document = 0;
0052     $beQuiet = shift;
0053 
0054     bless($reference, $object);
0055     return $reference;
0056 }
0057 
0058 # Returns the parsed 'idlDocument'
0059 sub Parse
0060 {
0061     my $object = shift;
0062     my $fileName = shift;
0063     my $defines = shift;
0064     my $preprocessor = shift;
0065 
0066     if (!$preprocessor) {
0067         $preprocessor = "/usr/bin/gcc -E -P -x c++";
0068     }
0069 
0070     if (!$defines) {
0071         $defines = "";
0072     }
0073 
0074     print " | *** Starting to parse $fileName...\n |\n" unless $beQuiet;
0075 
0076     open2(\*PP_OUT, \*PP_IN, split(' ', $preprocessor), (map { "-D$_" } split(' ', $defines)), $fileName);
0077     close PP_IN;
0078     my @documentContent = <PP_OUT>;
0079     close PP_OUT;
0080 
0081     my $dataAvailable = 0;
0082 
0083     # Simple IDL Parser (tm)
0084     foreach (@documentContent) {
0085         my $newParseMode = $object->DetermineParseMode($_);
0086 
0087         if ($newParseMode ne MODE_UNDEF) {
0088             if ($dataAvailable eq 0) {
0089                 $dataAvailable = 1; # Start node building...
0090             } else {
0091                 $object->ProcessSection();
0092             }
0093         }
0094 
0095         # Update detected data stream mode...
0096         if ($newParseMode ne MODE_UNDEF) {
0097             $parseMode = $newParseMode;
0098         }
0099 
0100         push(@temporaryContent, $_);
0101     }
0102 
0103     # Check if there is anything remaining to parse...
0104     if (($parseMode ne MODE_UNDEF) and ($#temporaryContent > 0)) {
0105         $object->ProcessSection();
0106     }
0107 
0108     print " | *** Finished parsing!\n" unless $beQuiet;
0109  
0110     $document->fileName($fileName);
0111 
0112     return $document;
0113 }
0114 
0115 sub ParseModule
0116 {
0117     my $object = shift;
0118     my $dataNode = shift;
0119 
0120     print " |- Trying to parse module...\n" unless $beQuiet;
0121 
0122     my $data = join("", @temporaryContent);
0123     $data =~ /$IDLStructure::moduleSelector/;
0124 
0125     my $moduleName = (defined($1) ? $1 : die("Parsing error!\nSource:\n$data\n)"));
0126     $dataNode->module($moduleName);
0127 
0128     print "  |----> Module; NAME \"$moduleName\"\n |-\n |\n" unless $beQuiet;
0129 }
0130 
0131 sub dumpExtendedAttributes
0132 {
0133     my $padStr = shift;
0134     my $attrs = shift;
0135 
0136     if (!%{$attrs}) {
0137         return "";
0138     }
0139 
0140     my @temp;
0141     while (($name, $value) = each(%{$attrs})) {
0142         push(@temp, "$name=$value");
0143     }
0144 
0145     return $padStr . "[" . join(", ", @temp) . "]";
0146 }
0147 
0148 sub parseExtendedAttributes
0149 {
0150     my $str = shift;
0151     $str =~ s/\[\s*(.*?)\s*\]/$1/g;
0152 
0153     my %attrs = ();
0154 
0155     foreach my $value (split(/\s*,\s*/, $str)) {
0156         ($name,$value) = split(/\s*=\s*/, $value, 2);
0157 
0158         # Attributes with no value are set to be true
0159         $value = 1 unless defined $value;
0160         $attrs{$name} = $value;
0161     }
0162 
0163     return \%attrs;
0164 }
0165 
0166 sub ParseInterface
0167 {
0168     my $object = shift;
0169     my $dataNode = shift;
0170     my $sectionName = shift;
0171 
0172     my $data = join("", @temporaryContent);
0173 
0174     # Look for end-of-interface mark
0175     $data =~ /};/g;
0176     $data = substr($data, index($data, $sectionName), pos($data) - length($data));
0177 
0178     $data =~ s/[\n\r]/ /g;
0179 
0180     # Beginning of the regexp parsing magic
0181     if ($sectionName eq "exception") {
0182         print " |- Trying to parse exception...\n" unless $beQuiet;
0183 
0184         my $exceptionName = "";
0185         my $exceptionData = "";
0186         my $exceptionDataName = "";
0187         my $exceptionDataType = "";
0188 
0189         # Match identifier of the exception, and enclosed data...
0190         $data =~ /$IDLStructure::exceptionSelector/;
0191         $exceptionName = (defined($1) ? $1 : die("Parsing error!\nSource:\n$data\n)"));
0192         $exceptionData = (defined($2) ? $2 : die("Parsing error!\nSource:\n$data\n)"));
0193 
0194         ('' =~ /^/); # Reset variables needed for regexp matching
0195 
0196         # ... parse enclosed data (get. name & type)
0197         $exceptionData =~ /$IDLStructure::exceptionSubSelector/;
0198         $exceptionDataType = (defined($1) ? $1 : die("Parsing error!\nSource:\n$data\n)"));
0199         $exceptionDataName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$data\n)"));
0200 
0201         # Fill in domClass datastructure
0202         $dataNode->name($exceptionName);
0203 
0204         my $newDataNode = new domAttribute();
0205         $newDataNode->type("readonly attribute");
0206         $newDataNode->signature(new domSignature());
0207 
0208         $newDataNode->signature->name($exceptionDataName);
0209         $newDataNode->signature->type($exceptionDataType);
0210 
0211         my $arrayRef = $dataNode->attributes;
0212         push(@$arrayRef, $newDataNode);
0213 
0214         print "  |----> Exception; NAME \"$exceptionName\" DATA TYPE \"$exceptionDataType\" DATA NAME \"$exceptionDataName\"\n |-\n |\n" unless $beQuiet;
0215     } elsif ($sectionName eq "interface") {
0216         print " |- Trying to parse interface...\n" unless $beQuiet;
0217 
0218         my $interfaceName = "";
0219         my $interfaceData = "";
0220 
0221         # Match identifier of the interface, and enclosed data...
0222         $data =~ /$IDLStructure::interfaceSelector/;
0223 
0224         $interfaceExtendedAttributes = (defined($1) ? $1 : " "); chop($interfaceExtendedAttributes);
0225         $interfaceName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$data\n)"));
0226         $interfaceBase = (defined($3) ? $3 : "");
0227         $interfaceData = (defined($4) ? $4 : die("Parsing error!\nSource:\n$data\n)"));
0228 
0229         # Fill in known parts of the domClass datastructure now...
0230         $dataNode->name($interfaceName);
0231         $dataNode->extendedAttributes(parseExtendedAttributes($interfaceExtendedAttributes));
0232 
0233         # Inheritance detection
0234         my @interfaceParents = split(/,/, $interfaceBase);
0235         foreach(@interfaceParents) {
0236             my $line = $_;
0237             $line =~ s/\s*//g;
0238 
0239             my $arrayRef = $dataNode->parents;
0240             push(@$arrayRef, $line);
0241         }
0242 
0243         $interfaceData =~ s/[\n\r]/ /g;
0244         my @interfaceMethods = split(/;/, $interfaceData);
0245 
0246         foreach my $line (@interfaceMethods) {
0247             if ($line =~ /attribute/) {
0248                 $line =~ /$IDLStructure::interfaceAttributeSelector/;
0249 
0250                 my $attributeType = (defined($1) ? $1 : die("Parsing error!\nSource:\n$line\n)"));
0251                 my $attributeExtendedAttributes = (defined($2) ? $2 : " "); chop($attributeExtendedAttributes);
0252 
0253                 my $attributeDataType = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
0254                 my $attributeDataName = (defined($4) ? $4 : die("Parsing error!\nSource:\n$line\n)"));
0255   
0256                 ('' =~ /^/); # Reset variables needed for regexp matching
0257 
0258                 $line =~ /$IDLStructure::getterRaisesSelector/;
0259                 my $getterException = (defined($1) ? $1 : "");
0260 
0261                 $line =~ /$IDLStructure::setterRaisesSelector/;
0262                 my $setterException = (defined($1) ? $1 : "");
0263 
0264                 my $newDataNode = new domAttribute();
0265                 $newDataNode->type($attributeType);
0266                 $newDataNode->signature(new domSignature());
0267 
0268                 $newDataNode->signature->name($attributeDataName);
0269                 $newDataNode->signature->type($attributeDataType);
0270                 $newDataNode->signature->extendedAttributes(parseExtendedAttributes($attributeExtendedAttributes));
0271 
0272                 my $arrayRef = $dataNode->attributes;
0273                 push(@$arrayRef, $newDataNode);
0274 
0275                 print "  |  |>  Attribute; TYPE \"$attributeType\" DATA NAME \"$attributeDataName\" DATA TYPE \"$attributeDataType\" GET EXCEPTION? \"$getterException\" SET EXCEPTION? \"$setterException\"" .
0276                     dumpExtendedAttributes("\n  |                 ", $newDataNode->signature->extendedAttributes) . "\n" unless $beQuiet;
0277 
0278                 $getterException =~ s/\s+//g;
0279                 $setterException =~ s/\s+//g;
0280                 @{$newDataNode->getterExceptions} = split(/,/, $getterException);
0281                 @{$newDataNode->setterExceptions} = split(/,/, $setterException);
0282             } elsif (($line !~ s/^\s*$//g) and ($line !~ /^\s*const/)) {
0283                 $line =~ /$IDLStructure::interfaceMethodSelector/ or die "Parsing error!\nSource:\n$line\n)";
0284 
0285                 my $methodExtendedAttributes = (defined($1) ? $1 : " "); chop($methodExtendedAttributes);
0286                 my $methodType = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
0287                 my $methodName = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
0288                 my $methodSignature = (defined($4) ? $4 : die("Parsing error!\nSource:\n$line\n)"));
0289 
0290                 ('' =~ /^/); # Reset variables needed for regexp matching
0291 
0292                 $line =~ /$IDLStructure::raisesSelector/;
0293                 my $methodException = (defined($1) ? $1 : "");
0294 
0295                 my $newDataNode = new domFunction();
0296 
0297                 $newDataNode->signature(new domSignature());
0298                 $newDataNode->signature->name($methodName);
0299                 $newDataNode->signature->type($methodType);
0300                 $newDataNode->signature->extendedAttributes(parseExtendedAttributes($methodExtendedAttributes));
0301 
0302                 print "  |  |-  Method; TYPE \"$methodType\" NAME \"$methodName\" EXCEPTION? \"$methodException\"" .
0303                     dumpExtendedAttributes("\n  |              ", $newDataNode->signature->extendedAttributes) . "\n" unless $beQuiet;
0304 
0305                 $methodException =~ s/\s+//g;
0306                 @{$newDataNode->raisesExceptions} = split(/,/, $methodException);
0307 
0308                 my @params = split(/,/, $methodSignature);
0309                 foreach(@params) {
0310                     my $line = $_;
0311 
0312                     $line =~ /$IDLStructure::interfaceParameterSelector/;
0313                     my $paramExtendedAttributes = (defined($1) ? $1 : " "); chop($paramExtendedAttributes);
0314                     my $paramType = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
0315                     my $paramName = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
0316 
0317                     my $paramDataNode = new domSignature();
0318                     $paramDataNode->name($paramName);
0319                     $paramDataNode->type($paramType);
0320                     $paramDataNode->extendedAttributes(parseExtendedAttributes($paramExtendedAttributes));
0321 
0322                     my $arrayRef = $newDataNode->parameters;
0323                     push(@$arrayRef, $paramDataNode);
0324 
0325                     print "  |   |>  Param; TYPE \"$paramType\" NAME \"$paramName\"" . 
0326                         dumpExtendedAttributes("\n  |              ", $paramDataNode->extendedAttributes) . "\n" unless $beQuiet;          
0327                 }
0328 
0329                 my $arrayRef = $dataNode->functions;
0330                 push(@$arrayRef, $newDataNode);
0331             } elsif ($line =~ /^\s*const/) {
0332                 $line =~ /$IDLStructure::constantSelector/;
0333                 my $constType = (defined($1) ? $1 : die("Parsing error!\nSource:\n$line\n)"));
0334                 my $constName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
0335                 my $constValue = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
0336 
0337                 my $newDataNode = new domConstant();
0338                 $newDataNode->name($constName);
0339                 $newDataNode->type($constType);
0340                 $newDataNode->value($constValue);
0341 
0342                 my $arrayRef = $dataNode->constants;
0343                 push(@$arrayRef, $newDataNode);
0344 
0345                 print "  |   |>  Constant; TYPE \"$constType\" NAME \"$constName\" VALUE \"$constValue\"\n" unless $beQuiet;
0346             }
0347         }
0348 
0349         print "  |----> Interface; NAME \"$interfaceName\"" .
0350             dumpExtendedAttributes("\n  |                 ", $dataNode->extendedAttributes) . "\n |-\n |\n" unless $beQuiet;
0351     }
0352 }
0353 
0354 # Internal helper
0355 sub DetermineParseMode
0356 {
0357     my $object = shift;  
0358     my $line = shift;
0359 
0360     my $mode = MODE_UNDEF;
0361     if ($_ =~ /module/) {
0362         $mode = MODE_MODULE;
0363     } elsif ($_ =~ /interface/) {
0364         $mode = MODE_INTERFACE;
0365     } elsif ($_ =~ /exception/) {
0366         $mode = MODE_EXCEPTION;
0367     } elsif ($_ =~ /alias/) {
0368         $mode = MODE_ALIAS;
0369     }
0370 
0371     return $mode;
0372 }
0373 
0374 # Internal helper
0375 sub ProcessSection
0376 {
0377     my $object = shift;
0378   
0379     if ($parseMode eq MODE_MODULE) {
0380         die ("Two modules in one file! Fatal error!\n") if ($document ne 0);
0381         $document = new idlDocument();
0382         $object->ParseModule($document);
0383     } elsif ($parseMode eq MODE_INTERFACE) {
0384         my $node = new domClass();
0385         $object->ParseInterface($node, "interface");
0386     
0387         die ("No module specified! Fatal Error!\n") if ($document eq 0);
0388         my $arrayRef = $document->classes;
0389         push(@$arrayRef, $node);
0390     } elsif($parseMode eq MODE_EXCEPTION) {
0391         my $node = new domClass();
0392         $object->ParseInterface($node, "exception");
0393 
0394         die ("No module specified! Fatal Error!\n") if ($document eq 0);
0395         my $arrayRef = $document->classes;
0396         push(@$arrayRef, $node);
0397     } elsif($parseMode eq MODE_ALIAS) {
0398         print " |- Trying to parse alias...\n" unless $beQuiet;
0399     
0400         my $line = join("", @temporaryContent);
0401         $line =~ /$IDLStructure::aliasSelector/;
0402 
0403         my $interfaceName = (defined($1) ? $1 : die("Parsing error!\nSource:\n$line\n)"));
0404         my $wrapperName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
0405     
0406         print "  |----> Alias; INTERFACE \"$interfaceName\" WRAPPER \"$wrapperName\"\n |-\n |\n" unless $beQuiet;
0407 
0408         # FIXME: Check if alias is already in aliases
0409         my $aliases = $document->aliases;
0410         $aliases->{$interfaceName} = $wrapperName;
0411     }
0412 
0413     @temporaryContent = "";
0414 }
0415 
0416 1;