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