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