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;