• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# KDOM IDL parser
3#
4# Copyright (C) 2005 Nikolas Zimmermann <wildfox@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
22package IDLParser;
23
24use IPC::Open2;
25use IDLStructure;
26
27use constant MODE_UNDEF    => 0; # Default mode.
28
29use constant MODE_MODULE  => 10; # 'module' section
30use constant MODE_INTERFACE  => 11; # 'interface' section
31use constant MODE_EXCEPTION  => 12; # 'exception' section
32use constant MODE_ALIAS    => 13; # 'alias' section
33
34# Helper variables
35my @temporaryContent = "";
36
37my $parseMode = MODE_UNDEF;
38my $preservedParseMode = MODE_UNDEF;
39
40my $beQuiet; # Should not display anything on STDOUT?
41my $document = 0; # Will hold the resulting 'idlDocument'
42my $parentsOnly = 0; # If 1, parse only enough to populate parents list
43
44# Default Constructor
45sub 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'
58sub 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
115sub 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
131sub 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
148sub 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
167sub 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
362sub 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
384sub 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
4251;
426