r19580: Add --includedir option.
[samba.git] / source4 / pidl / idl.yp
1 ########################
2 # IDL Parse::Yapp parser
3 # Copyright (C) Andrew Tridgell <tridge@samba.org>
4 # released under the GNU GPL version 2 or later
5
6
7
8 # the precedence actually doesn't matter at all for this grammar, but
9 # by providing a precedence we reduce the number of conflicts
10 # enormously
11 %left   '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ',' ';'
12
13
14 ################
15 # grammar
16 %%
17 idl: 
18         #empty  { {} }
19         | idl interface { push(@{$_[1]}, $_[2]); $_[1] }
20         | idl coclass { push(@{$_[1]}, $_[2]); $_[1] }
21 ;
22
23 coclass: property_list 'coclass' identifier '{' interface_names '}' optional_semicolon
24           {$_[3] => {
25                "TYPE" => "COCLASS", 
26                "PROPERTIES" => $_[1],
27                "NAME" => $_[3],
28                "DATA" => $_[5],
29                    "FILE" => $_[0]->YYData->{INPUT_FILENAME},
30                    "LINE" => $_[0]->YYData->{LINE},
31           }}
32 ;
33
34 interface_names:
35         #empty { {} }
36         | interface_names 'interface' identifier ';' { push(@{$_[1]}, $_[2]); $_[1] }
37 ;
38
39 interface: property_list 'interface' identifier base_interface '{' definitions '}' optional_semicolon
40           {$_[3] => {
41                "TYPE" => "INTERFACE", 
42                "PROPERTIES" => $_[1],
43                "NAME" => $_[3],
44                "BASE" => $_[4],
45                "DATA" => $_[6],
46                    "FILE" => $_[0]->YYData->{INPUT_FILENAME},
47                    "LINE" => $_[0]->YYData->{LINE},
48           }}
49 ;
50
51 base_interface:
52         #empty
53         | ':' identifier { $_[2] }
54 ;
55
56 definitions: 
57       definition              { [ $_[1] ] }    
58     | definitions definition  { push(@{$_[1]}, $_[2]); $_[1] }
59 ;    
60
61
62 definition: function | const | typedef | declare | typedecl
63 ;
64
65 const: 'const' identifier pointers identifier '=' anytext ';' 
66         {{
67                      "TYPE"  => "CONST", 
68                      "DTYPE"  => $_[2],
69                          "POINTERS" => $_[3],
70                      "NAME"  => $_[4],
71                      "VALUE" => $_[6],
72                      "FILE" => $_[0]->YYData->{INPUT_FILENAME},
73                      "LINE" => $_[0]->YYData->{LINE},
74         }}
75         | 'const' identifier pointers identifier array_len '=' anytext ';' 
76         {{
77                      "TYPE"  => "CONST", 
78                      "DTYPE"  => $_[2],
79                          "POINTERS" => $_[3],
80                      "NAME"  => $_[4],
81                      "ARRAY_LEN" => $_[5],
82                      "VALUE" => $_[7],
83                      "FILE" => $_[0]->YYData->{INPUT_FILENAME},
84                      "LINE" => $_[0]->YYData->{LINE},
85         }}
86 ;
87
88
89 function: property_list type identifier '(' element_list2 ')' ';' 
90          {{
91                 "TYPE" => "FUNCTION",
92                 "NAME" => $_[3],
93                 "RETURN_TYPE" => $_[2],
94                 "PROPERTIES" => $_[1],
95                 "ELEMENTS" => $_[5],
96                 "FILE" => $_[0]->YYData->{INPUT_FILENAME},
97                 "LINE" => $_[0]->YYData->{LINE},
98           }}
99 ;
100
101 declare: 'declare' property_list decl_type identifier';' 
102         {{
103                      "TYPE" => "DECLARE", 
104                      "PROPERTIES" => $_[2],
105                      "NAME" => $_[4],
106                      "DATA" => $_[3],
107                      "FILE" => $_[0]->YYData->{INPUT_FILENAME},
108                      "LINE" => $_[0]->YYData->{LINE},
109         }}
110 ;
111
112 decl_type: decl_enum | decl_bitmap | decl_union
113 ;
114
115 decl_enum: 'enum' 
116         {{
117                      "TYPE" => "ENUM"
118         }}
119 ;
120
121 decl_bitmap: 'bitmap' 
122         {{
123                      "TYPE" => "BITMAP"
124         }}
125 ;
126
127 decl_union: 'union' 
128         {{
129                      "TYPE" => "UNION"
130         }}
131 ;
132
133 typedef: 'typedef' property_list type identifier array_len ';' 
134         {{
135                      "TYPE" => "TYPEDEF", 
136                      "PROPERTIES" => $_[2],
137                      "NAME" => $_[4],
138                      "DATA" => $_[3],
139                      "ARRAY_LEN" => $_[5],
140                      "FILE" => $_[0]->YYData->{INPUT_FILENAME},
141                      "LINE" => $_[0]->YYData->{LINE},
142         }}
143 ;
144
145 usertype: struct | union | enum | bitmap;
146
147 typedecl: usertype ';' { $_[1] };
148
149 sign: 'signed' | 'unsigned';
150
151 existingtype: 
152         | sign identifier { "$_[1] $_[2]" }
153         | identifier 
154 ;
155
156 type: usertype | existingtype | void { "void" } ;
157
158 enum_body: '{' enum_elements '}' { $_[2] };
159 opt_enum_body: | enum_body;
160 enum: 'enum' optional_identifier opt_enum_body
161         {{
162              "TYPE" => "ENUM", 
163                          "NAME" => $_[2],
164                      "ELEMENTS" => $_[3]
165         }}
166 ;
167
168 enum_elements: 
169       enum_element                    { [ $_[1] ] }            
170     | enum_elements ',' enum_element  { push(@{$_[1]}, $_[3]); $_[1] }
171 ;
172
173 enum_element: identifier 
174               | identifier '=' anytext { "$_[1]$_[2]$_[3]" }
175 ;
176
177 bitmap_body: '{' opt_bitmap_elements '}' { $_[2] };
178 opt_bitmap_body: | bitmap_body;
179 bitmap: 'bitmap' optional_identifier opt_bitmap_body
180         {{
181              "TYPE" => "BITMAP", 
182                          "NAME" => $_[2],
183                      "ELEMENTS" => $_[3]
184         }}
185 ;
186
187 bitmap_elements: 
188       bitmap_element                    { [ $_[1] ] }            
189     | bitmap_elements ',' bitmap_element  { push(@{$_[1]}, $_[3]); $_[1] }
190 ;
191
192 opt_bitmap_elements: | bitmap_elements;
193
194 bitmap_element: identifier '=' anytext { "$_[1] ( $_[3] )" }
195 ;
196
197 struct_body: '{' element_list1 '}' { $_[2] };
198 opt_struct_body: | struct_body;
199
200 struct: 'struct' optional_identifier opt_struct_body
201         {{
202              "TYPE" => "STRUCT", 
203                          "NAME" => $_[2],
204                      "ELEMENTS" => $_[3]
205         }}
206 ;
207
208 empty_element: property_list ';'
209         {{
210                  "NAME" => "",
211                  "TYPE" => "EMPTY",
212                  "PROPERTIES" => $_[1],
213                  "POINTERS" => 0,
214                  "ARRAY_LEN" => [],
215                  "FILE" => $_[0]->YYData->{INPUT_FILENAME},
216                  "LINE" => $_[0]->YYData->{LINE},
217          }}
218 ;
219
220 base_or_empty: base_element ';' | empty_element;
221
222 optional_base_element:
223         property_list base_or_empty { $_[2]->{PROPERTIES} = FlattenHash([$_[1],$_[2]->{PROPERTIES}]); $_[2] }
224 ;
225
226 union_elements: 
227     #empty
228     | union_elements optional_base_element { push(@{$_[1]}, $_[2]); $_[1] }
229 ;
230
231 union_body: '{' union_elements '}' { $_[2] };
232 opt_union_body: | union_body;
233
234 union: 'union' optional_identifier opt_union_body
235         {{
236              "TYPE" => "UNION", 
237                      "NAME" => $_[2],
238                      "ELEMENTS" => $_[3]
239         }}
240 ;
241
242 base_element: property_list type pointers identifier array_len
243               {{
244                            "NAME" => $_[4],
245                            "TYPE" => $_[2],
246                            "PROPERTIES" => $_[1],
247                            "POINTERS" => $_[3],
248                            "ARRAY_LEN" => $_[5],
249                        "FILE" => $_[0]->YYData->{INPUT_FILENAME},
250                        "LINE" => $_[0]->YYData->{LINE},
251               }}
252 ;
253
254
255 pointers: 
256   #empty            
257    { 0 }
258     | pointers '*'  { $_[1]+1 }
259 ;
260
261 element_list1: 
262     #empty
263     | element_list1 base_element ';' { push(@{$_[1]}, $_[2]); $_[1] }
264 ;
265
266 element_list2: 
267     #empty
268     | 'void' 
269     | base_element { [ $_[1] ] }
270     | element_list2 ',' base_element { push(@{$_[1]}, $_[3]); $_[1] }
271 ;
272
273 array_len: 
274     #empty                        { [] }
275     | '[' ']' array_len           { push(@{$_[3]}, "*"); $_[3] }
276     | '[' anytext ']' array_len   { push(@{$_[4]}, "$_[2]"); $_[4] }
277 ;
278
279
280 property_list: 
281     #empty
282     | property_list '[' properties ']' { FlattenHash([$_[1],$_[3]]); }
283 ;
284
285 properties: property          { $_[1] }
286     | properties ',' property { FlattenHash([$_[1], $_[3]]); }
287 ;
288
289 property: identifier                   {{ "$_[1]" => "1"     }}
290           | identifier '(' listtext ')' {{ "$_[1]" => "$_[3]" }}
291 ;
292
293 listtext:
294     anytext 
295     | listtext ',' anytext { "$_[1] $_[3]" }
296 ;
297
298 commalisttext:
299     anytext 
300     | commalisttext ',' anytext { "$_[1],$_[3]" }
301 ;
302
303 anytext:  #empty
304     { "" }
305     | identifier | constant | text
306     | anytext '-' anytext  { "$_[1]$_[2]$_[3]" }
307     | anytext '.' anytext  { "$_[1]$_[2]$_[3]" }
308     | anytext '*' anytext  { "$_[1]$_[2]$_[3]" }
309     | anytext '>' anytext  { "$_[1]$_[2]$_[3]" }
310     | anytext '<' anytext  { "$_[1]$_[2]$_[3]" }
311     | anytext '|' anytext  { "$_[1]$_[2]$_[3]" }
312     | anytext '&' anytext  { "$_[1]$_[2]$_[3]" }
313     | anytext '/' anytext  { "$_[1]$_[2]$_[3]" }
314     | anytext '?' anytext  { "$_[1]$_[2]$_[3]" }
315     | anytext ':' anytext  { "$_[1]$_[2]$_[3]" }
316     | anytext '=' anytext  { "$_[1]$_[2]$_[3]" }
317     | anytext '+' anytext  { "$_[1]$_[2]$_[3]" }
318     | anytext '~' anytext  { "$_[1]$_[2]$_[3]" }
319     | anytext '(' commalisttext ')' anytext  { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
320     | anytext '{' commalisttext '}' anytext  { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
321 ;
322
323 identifier: IDENTIFIER
324 ;
325
326 optional_identifier: 
327         IDENTIFIER
328    | #empty { undef }
329 ;
330
331 constant: CONSTANT
332 ;
333
334 text: TEXT { "\"$_[1]\"" }
335 ;
336
337 optional_semicolon: 
338         #empty
339         | ';'
340 ;
341
342
343 #####################################
344 # start code
345 %%
346
347 #####################################################################
348 # flatten an array of hashes into a single hash
349 sub FlattenHash($) 
350
351     my $a = shift;
352     my %b;
353     for my $d (@{$a}) {
354         for my $k (keys %{$d}) {
355             $b{$k} = $d->{$k};
356         }
357     }
358     return \%b;
359 }
360
361
362
363 #####################################################################
364 # traverse a perl data structure removing any empty arrays or
365 # hashes and any hash elements that map to undef
366 sub CleanData($)
367 {
368     sub CleanData($);
369     my($v) = shift;
370         return undef if (not defined($v));
371     if (ref($v) eq "ARRAY") {
372         foreach my $i (0 .. $#{$v}) {
373             CleanData($v->[$i]);
374             if (ref($v->[$i]) eq "ARRAY" && $#{$v->[$i]}==-1) { 
375                     $v->[$i] = undef; 
376                     next; 
377             }
378         }
379         # this removes any undefined elements from the array
380         @{$v} = grep { defined $_ } @{$v};
381     } elsif (ref($v) eq "HASH") {
382         foreach my $x (keys %{$v}) {
383             CleanData($v->{$x});
384             if (!defined $v->{$x}) { delete($v->{$x}); next; }
385             if (ref($v->{$x}) eq "ARRAY" && $#{$v->{$x}}==-1) { delete($v->{$x}); next; }
386         }
387     }
388         return $v;
389 }
390
391 sub _Error {
392     if (exists $_[0]->YYData->{ERRMSG}) {
393                 print $_[0]->YYData->{ERRMSG};
394                 delete $_[0]->YYData->{ERRMSG};
395                 return;
396         };
397         my $line = $_[0]->YYData->{LINE};
398         my $last_token = $_[0]->YYData->{LAST_TOKEN};
399         my $file = $_[0]->YYData->{INPUT_FILENAME};
400         
401         print "$file:$line: Syntax error near '$last_token'\n";
402 }
403
404 sub _Lexer($)
405 {
406         my($parser)=shift;
407
408     $parser->YYData->{INPUT} or return('',undef);
409
410 again:
411         $parser->YYData->{INPUT} =~ s/^[ \t]*//;
412
413         for ($parser->YYData->{INPUT}) {
414                 if (/^\#/) {
415                         if (s/^\# (\d+) \"(.*?)\"( \d+|)//) {
416                                 $parser->YYData->{LINE} = $1-1;
417                                 $parser->YYData->{INPUT_FILENAME} = $2;
418                                 goto again;
419                         }
420                         if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) {
421                                 $parser->YYData->{LINE} = $1-1;
422                                 $parser->YYData->{INPUT_FILENAME} = $2;
423                                 goto again;
424                         }
425                         if (s/^(\#.*)$//m) {
426                                 goto again;
427                         }
428                 }
429                 if (s/^(\n)//) {
430                         $parser->YYData->{LINE}++;
431                         goto again;
432                 }
433                 if (s/^\"(.*?)\"//) {
434                         $parser->YYData->{LAST_TOKEN} = $1;
435                         return('TEXT',$1); 
436                 }
437                 if (s/^(\d+)(\W|$)/$2/) {
438                         $parser->YYData->{LAST_TOKEN} = $1;
439                         return('CONSTANT',$1); 
440                 }
441                 if (s/^([\w_]+)//) {
442                         $parser->YYData->{LAST_TOKEN} = $1;
443                         if ($1 =~ 
444                             /^(coclass|interface|const|typedef|declare|union
445                               |struct|enum|bitmap|void|unsigned|signed)$/x) {
446                                 return $1;
447                         }
448                         return('IDENTIFIER',$1);
449                 }
450                 if (s/^(.)//s) {
451                         $parser->YYData->{LAST_TOKEN} = $1;
452                         return($1,$1);
453                 }
454         }
455 }
456
457 sub parse_string
458 {
459         my ($data,$filename) = @_;
460
461         my $self = new Parse::Pidl::IDL;
462
463     $self->YYData->{INPUT_FILENAME} = $filename;
464     $self->YYData->{INPUT} = $data;
465     $self->YYData->{LINE} = 0;
466     $self->YYData->{LAST_TOKEN} = "NONE";
467
468         my $idl = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error );
469
470         return CleanData($idl);
471 }
472
473 sub parse_file($$)
474 {
475         my ($filename,$incdirs) = @_;
476
477         my $saved_delim = $/;
478         undef $/;
479         my $cpp = $ENV{CPP};
480         if (! defined $cpp) {
481                 $cpp = "cpp";
482         }
483         my $includes = map { " -I$_" } @$incdirs;
484         my $data = `$cpp -D__PIDL__$includes -xc $filename`;
485         $/ = $saved_delim;
486
487         return parse_string($data, $filename);
488 }