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