Home | History | Annotate | Download | only in scripts
      1 # 
      2 # KDOM IDL parser
      3 #
      4 # Copyright (C) 2005 Nikolas Zimmermann <wildfox (at] kde.org>
      5 # 
      6 # This library is free software; you can redistribute it and/or
      7 # modify it under the terms of the GNU Library General Public
      8 # License as published by the Free Software Foundation; either
      9 # version 2 of the License, or (at your option) any later version.
     10 # 
     11 # This library is distributed in the hope that it will be useful,
     12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14 # Library General Public License for more details.
     15 # 
     16 # You should have received a copy of the GNU Library General Public License
     17 # aint with this library; see the file COPYING.LIB.  If not, write to
     18 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19 # Boston, MA 02110-1301, USA.
     20 # 
     21 
     22 package IDLParser;
     23 
     24 use IPC::Open2;
     25 use IDLStructure;
     26 
     27 use constant MODE_UNDEF    => 0; # Default mode.
     28 
     29 use constant MODE_MODULE  => 10; # 'module' section
     30 use constant MODE_INTERFACE  => 11; # 'interface' section
     31 use constant MODE_EXCEPTION  => 12; # 'exception' section
     32 use constant MODE_ALIAS    => 13; # 'alias' section
     33 
     34 # Helper variables
     35 my @temporaryContent = "";
     36 
     37 my $parseMode = MODE_UNDEF;
     38 my $preservedParseMode = MODE_UNDEF;
     39 
     40 my $beQuiet; # Should not display anything on STDOUT?
     41 my $document = 0; # Will hold the resulting 'idlDocument'
     42 my $parentsOnly = 0; # If 1, parse only enough to populate parents list
     43 
     44 # Default Constructor
     45 sub new
     46 {
     47     my $object = shift;
     48     my $reference = { };
     49 
     50     $document = 0;
     51     $beQuiet = shift;
     52 
     53     bless($reference, $object);
     54     return $reference;
     55 }
     56 
     57 # Returns the parsed 'idlDocument'
     58 sub Parse
     59 {
     60     my $object = shift;
     61     my $fileName = shift;
     62     my $defines = shift;
     63     my $preprocessor = shift;
     64     $parentsOnly = shift;
     65 
     66     if (!$preprocessor) {
     67         $preprocessor = "/usr/bin/gcc -E -P -x c++";
     68     }
     69 
     70     if (!$defines) {
     71         $defines = "";
     72     }
     73 
     74     print " | *** Starting to parse $fileName...\n |\n" unless $beQuiet;
     75 
     76     open2(\*PP_OUT, \*PP_IN, split(' ', $preprocessor), (map { "-D$_" } split(' ', $defines)), $fileName);
     77     close PP_IN;
     78     my @documentContent = <PP_OUT>;
     79     close PP_OUT;
     80 
     81     my $dataAvailable = 0;
     82 
     83     # Simple IDL Parser (tm)
     84     foreach (@documentContent) {
     85         my $newParseMode = $object->DetermineParseMode($_);
     86 
     87         if ($newParseMode ne MODE_UNDEF) {
     88             if ($dataAvailable eq 0) {
     89                 $dataAvailable = 1; # Start node building...
     90             } else {
     91                 $object->ProcessSection();
     92             }
     93         }
     94 
     95         # Update detected data stream mode...
     96         if ($newParseMode ne MODE_UNDEF) {
     97             $parseMode = $newParseMode;
     98         }
     99 
    100         push(@temporaryContent, $_);
    101     }
    102 
    103     # Check if there is anything remaining to parse...
    104     if (($parseMode ne MODE_UNDEF) and ($#temporaryContent > 0)) {
    105         $object->ProcessSection();
    106     }
    107 
    108     print " | *** Finished parsing!\n" unless $beQuiet;
    109  
    110     $document->fileName($fileName);
    111 
    112     return $document;
    113 }
    114 
    115 sub ParseModule
    116 {
    117     my $object = shift;
    118     my $dataNode = shift;
    119 
    120     print " |- Trying to parse module...\n" unless $beQuiet;
    121 
    122     my $data = join("", @temporaryContent);
    123     $data =~ /$IDLStructure::moduleSelector/;
    124 
    125     my $moduleName = (defined($1) ? $1 : die("Parsing error!\nSource:\n$data\n)"));
    126     $dataNode->module($moduleName);
    127 
    128     print "  |----> Module; NAME \"$moduleName\"\n |-\n |\n" unless $beQuiet;
    129 }
    130 
    131 sub dumpExtendedAttributes
    132 {
    133     my $padStr = shift;
    134     my $attrs = shift;
    135 
    136     if (!%{$attrs}) {
    137         return "";
    138     }
    139 
    140     my @temp;
    141     while (($name, $value) = each(%{$attrs})) {
    142         push(@temp, "$name=$value");
    143     }
    144 
    145     return $padStr . "[" . join(", ", @temp) . "]";
    146 }
    147 
    148 sub parseExtendedAttributes
    149 {
    150     my $str = shift;
    151     $str =~ s/\[\s*(.*?)\s*\]/$1/g;
    152 
    153     my %attrs = ();
    154 
    155     foreach my $value (split(/\s*,\s*/, $str)) {
    156         ($name,$value) = split(/\s*=\s*/, $value, 2);
    157 
    158         # Attributes with no value are set to be true
    159         $value = 1 unless defined $value;
    160         $attrs{$name} = $value;
    161         die("Invalid extended attribute name: '$name'\n") if $name =~ /\s/;
    162     }
    163 
    164     return \%attrs;
    165 }
    166 
    167 sub ParseInterface
    168 {
    169     my $object = shift;
    170     my $dataNode = shift;
    171     my $sectionName = shift;
    172 
    173     my $data = join("", @temporaryContent);
    174 
    175     # Look for end-of-interface mark
    176     $data =~ /};/g;
    177     $data = substr($data, index($data, $sectionName), pos($data) - length($data));
    178 
    179     $data =~ s/[\n\r]/ /g;
    180 
    181     # Beginning of the regexp parsing magic
    182     if ($sectionName eq "exception") {
    183         print " |- Trying to parse exception...\n" unless $beQuiet;
    184 
    185         my $exceptionName = "";
    186         my $exceptionData = "";
    187         my $exceptionDataName = "";
    188         my $exceptionDataType = "";
    189 
    190         # Match identifier of the exception, and enclosed data...
    191         $data =~ /$IDLStructure::exceptionSelector/;
    192         $exceptionName = (defined($1) ? $1 : die("Parsing error!\nSource:\n$data\n)"));
    193         $exceptionData = (defined($2) ? $2 : die("Parsing error!\nSource:\n$data\n)"));
    194 
    195         ('' =~ /^/); # Reset variables needed for regexp matching
    196 
    197         # ... parse enclosed data (get. name & type)
    198         $exceptionData =~ /$IDLStructure::exceptionSubSelector/;
    199         $exceptionDataType = (defined($1) ? $1 : die("Parsing error!\nSource:\n$data\n)"));
    200         $exceptionDataName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$data\n)"));
    201 
    202         # Fill in domClass datastructure
    203         $dataNode->name($exceptionName);
    204 
    205         my $newDataNode = new domAttribute();
    206         $newDataNode->type("readonly attribute");
    207         $newDataNode->signature(new domSignature());
    208 
    209         $newDataNode->signature->name($exceptionDataName);
    210         $newDataNode->signature->type($exceptionDataType);
    211 
    212         my $arrayRef = $dataNode->attributes;
    213         push(@$arrayRef, $newDataNode);
    214 
    215         print "  |----> Exception; NAME \"$exceptionName\" DATA TYPE \"$exceptionDataType\" DATA NAME \"$exceptionDataName\"\n |-\n |\n" unless $beQuiet;
    216     } elsif ($sectionName eq "interface") {
    217         print " |- Trying to parse interface...\n" unless $beQuiet;
    218 
    219         my $interfaceName = "";
    220         my $interfaceData = "";
    221 
    222         # Match identifier of the interface, and enclosed data...
    223         $data =~ /$IDLStructure::interfaceSelector/;
    224 
    225         $interfaceExtendedAttributes = (defined($1) ? $1 : " "); chop($interfaceExtendedAttributes);
    226         $interfaceName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$data\n)"));
    227         $interfaceBase = (defined($3) ? $3 : "");
    228         $interfaceData = (defined($4) ? $4 : die("Parsing error!\nSource:\n$data\n)"));
    229 
    230         # Fill in known parts of the domClass datastructure now...
    231         $dataNode->name($interfaceName);
    232         $dataNode->extendedAttributes(parseExtendedAttributes($interfaceExtendedAttributes));
    233 
    234         # Inheritance detection
    235         my @interfaceParents = split(/,/, $interfaceBase);
    236         foreach(@interfaceParents) {
    237             my $line = $_;
    238             $line =~ s/\s*//g;
    239 
    240             my $arrayRef = $dataNode->parents;
    241             push(@$arrayRef, $line);
    242         }
    243 
    244         return if $parentsOnly;
    245 
    246         $interfaceData =~ s/[\n\r]/ /g;
    247         my @interfaceMethods = split(/;/, $interfaceData);
    248 
    249         foreach my $line (@interfaceMethods) {
    250             if ($line =~ /attribute/) {
    251                 $line =~ /$IDLStructure::interfaceAttributeSelector/;
    252 
    253                 my $attributeType = (defined($1) ? $1 : die("Parsing error!\nSource:\n$line\n)"));
    254                 my $attributeExtendedAttributes = (defined($2) ? $2 : " "); chop($attributeExtendedAttributes);
    255 
    256                 my $attributeDataType = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
    257                 my $attributeDataName = (defined($4) ? $4 : die("Parsing error!\nSource:\n$line\n)"));
    258   
    259                 ('' =~ /^/); # Reset variables needed for regexp matching
    260 
    261                 $line =~ /$IDLStructure::getterRaisesSelector/;
    262                 my $getterException = (defined($1) ? $1 : "");
    263 
    264                 $line =~ /$IDLStructure::setterRaisesSelector/;
    265                 my $setterException = (defined($1) ? $1 : "");
    266 
    267                 my $newDataNode = new domAttribute();
    268                 $newDataNode->type($attributeType);
    269                 $newDataNode->signature(new domSignature());
    270 
    271                 $newDataNode->signature->name($attributeDataName);
    272                 $newDataNode->signature->type($attributeDataType);
    273                 $newDataNode->signature->extendedAttributes(parseExtendedAttributes($attributeExtendedAttributes));
    274 
    275                 my $arrayRef = $dataNode->attributes;
    276                 push(@$arrayRef, $newDataNode);
    277 
    278                 print "  |  |>  Attribute; TYPE \"$attributeType\" DATA NAME \"$attributeDataName\" DATA TYPE \"$attributeDataType\" GET EXCEPTION? \"$getterException\" SET EXCEPTION? \"$setterException\"" .
    279                     dumpExtendedAttributes("\n  |                 ", $newDataNode->signature->extendedAttributes) . "\n" unless $beQuiet;
    280 
    281                 $getterException =~ s/\s+//g;
    282                 $setterException =~ s/\s+//g;
    283                 @{$newDataNode->getterExceptions} = split(/,/, $getterException);
    284                 @{$newDataNode->setterExceptions} = split(/,/, $setterException);
    285             } elsif (($line !~ s/^\s*$//g) and ($line !~ /^\s*const/)) {
    286                 $line =~ /$IDLStructure::interfaceMethodSelector/ or die "Parsing error!\nSource:\n$line\n)";
    287 
    288                 my $methodExtendedAttributes = (defined($1) ? $1 : " "); chop($methodExtendedAttributes);
    289                 my $methodType = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
    290                 my $methodName = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
    291                 my $methodSignature = (defined($4) ? $4 : die("Parsing error!\nSource:\n$line\n)"));
    292 
    293                 ('' =~ /^/); # Reset variables needed for regexp matching
    294 
    295                 $line =~ /$IDLStructure::raisesSelector/;
    296                 my $methodException = (defined($1) ? $1 : "");
    297 
    298                 my $newDataNode = new domFunction();
    299 
    300                 $newDataNode->signature(new domSignature());
    301                 $newDataNode->signature->name($methodName);
    302                 $newDataNode->signature->type($methodType);
    303                 $newDataNode->signature->extendedAttributes(parseExtendedAttributes($methodExtendedAttributes));
    304 
    305                 print "  |  |-  Method; TYPE \"$methodType\" NAME \"$methodName\" EXCEPTION? \"$methodException\"" .
    306                     dumpExtendedAttributes("\n  |              ", $newDataNode->signature->extendedAttributes) . "\n" unless $beQuiet;
    307 
    308                 $methodException =~ s/\s+//g;
    309                 @{$newDataNode->raisesExceptions} = split(/,/, $methodException);
    310 
    311                 # Split arguments at commas but only if the comma
    312                 # is not within attribute brackets, expressed here
    313                 # as being followed by a ']' without a preceding '['.
    314                 # Note that this assumes that attributes don't nest.
    315                 my @params = split(/,(?![^[]*\])/, $methodSignature);
    316                 foreach(@params) {
    317                     my $line = $_;
    318 
    319                     $line =~ /$IDLStructure::interfaceParameterSelector/;
    320                     my $paramExtendedAttributes = (defined($1) ? $1 : " "); chop($paramExtendedAttributes);
    321                     my $paramType = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
    322                     my $paramName = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
    323 
    324                     my $paramDataNode = new domSignature();
    325                     $paramDataNode->name($paramName);
    326                     $paramDataNode->type($paramType);
    327                     $paramDataNode->extendedAttributes(parseExtendedAttributes($paramExtendedAttributes));
    328 
    329                     my $arrayRef = $newDataNode->parameters;
    330                     push(@$arrayRef, $paramDataNode);
    331 
    332                     print "  |   |>  Param; TYPE \"$paramType\" NAME \"$paramName\"" . 
    333                         dumpExtendedAttributes("\n  |              ", $paramDataNode->extendedAttributes) . "\n" unless $beQuiet;          
    334                 }
    335 
    336                 my $arrayRef = $dataNode->functions;
    337                 push(@$arrayRef, $newDataNode);
    338             } elsif ($line =~ /^\s*const/) {
    339                 $line =~ /$IDLStructure::constantSelector/;
    340                 my $constType = (defined($1) ? $1 : die("Parsing error!\nSource:\n$line\n)"));
    341                 my $constName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
    342                 my $constValue = (defined($3) ? $3 : die("Parsing error!\nSource:\n$line\n)"));
    343 
    344                 my $newDataNode = new domConstant();
    345                 $newDataNode->name($constName);
    346                 $newDataNode->type($constType);
    347                 $newDataNode->value($constValue);
    348 
    349                 my $arrayRef = $dataNode->constants;
    350                 push(@$arrayRef, $newDataNode);
    351 
    352                 print "  |   |>  Constant; TYPE \"$constType\" NAME \"$constName\" VALUE \"$constValue\"\n" unless $beQuiet;
    353             }
    354         }
    355 
    356         print "  |----> Interface; NAME \"$interfaceName\"" .
    357             dumpExtendedAttributes("\n  |                 ", $dataNode->extendedAttributes) . "\n |-\n |\n" unless $beQuiet;
    358     }
    359 }
    360 
    361 # Internal helper
    362 sub DetermineParseMode
    363 {
    364     my $object = shift;  
    365     my $line = shift;
    366 
    367     my $mode = MODE_UNDEF;
    368     if ($_ =~ /module/) {
    369         $mode = MODE_MODULE;
    370     } elsif ($_ =~ /interface/) {
    371         $mode = MODE_INTERFACE;
    372     } elsif ($_ =~ /exception/) {
    373         $mode = MODE_EXCEPTION;
    374     } elsif ($_ =~ /(\A|\b)alias/) {
    375         # The (\A|\b) above is needed so we don't match attributes
    376         # whose names contain the substring "alias".
    377         $mode = MODE_ALIAS;
    378     }
    379 
    380     return $mode;
    381 }
    382 
    383 # Internal helper
    384 sub ProcessSection
    385 {
    386     my $object = shift;
    387   
    388     if ($parseMode eq MODE_MODULE) {
    389         die ("Two modules in one file! Fatal error!\n") if ($document ne 0);
    390         $document = new idlDocument();
    391         $object->ParseModule($document);
    392     } elsif ($parseMode eq MODE_INTERFACE) {
    393         my $node = new domClass();
    394         $object->ParseInterface($node, "interface");
    395     
    396         die ("No module specified! Fatal Error!\n") if ($document eq 0);
    397         my $arrayRef = $document->classes;
    398         push(@$arrayRef, $node);
    399     } elsif($parseMode eq MODE_EXCEPTION) {
    400         my $node = new domClass();
    401         $object->ParseInterface($node, "exception");
    402 
    403         die ("No module specified! Fatal Error!\n") if ($document eq 0);
    404         my $arrayRef = $document->classes;
    405         push(@$arrayRef, $node);
    406     } elsif($parseMode eq MODE_ALIAS) {
    407         print " |- Trying to parse alias...\n" unless $beQuiet;
    408     
    409         my $line = join("", @temporaryContent);
    410         $line =~ /$IDLStructure::aliasSelector/;
    411 
    412         my $interfaceName = (defined($1) ? $1 : die("Parsing error!\nSource:\n$line\n)"));
    413         my $wrapperName = (defined($2) ? $2 : die("Parsing error!\nSource:\n$line\n)"));
    414     
    415         print "  |----> Alias; INTERFACE \"$interfaceName\" WRAPPER \"$wrapperName\"\n |-\n |\n" unless $beQuiet;
    416 
    417         # FIXME: Check if alias is already in aliases
    418         my $aliases = $document->aliases;
    419         $aliases->{$interfaceName} = $wrapperName;
    420     }
    421 
    422     @temporaryContent = "";
    423 }
    424 
    425 1;
    426