PIDL: fix parsing linemarkers in preprocessor output
[amitay/samba.git] / pidl / idl.yp
1 ########################
2 # IDL Parse::Yapp parser
3 # Copyright (C) Andrew Tridgell <tridge@samba.org>
4 # released under the GNU GPL version 3 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         |
20         idl interface { push(@{$_[1]}, $_[2]); $_[1] }
21         |
22         idl coclass   { push(@{$_[1]}, $_[2]); $_[1] }
23         |
24         idl import    { push(@{$_[1]}, $_[2]); $_[1] }
25         |
26         idl include   { push(@{$_[1]}, $_[2]); $_[1] }
27         |
28         idl importlib { push(@{$_[1]}, $_[2]); $_[1] }
29         |
30         idl cpp_quote { push(@{$_[1]}, $_[2]); $_[1] }
31 ;
32
33 import:
34         'import' commalist ';'
35         {{
36                 "TYPE" => "IMPORT",
37                 "PATHS" => $_[2],
38                 "FILE" => $_[0]->YYData->{FILE},
39                 "LINE" => $_[0]->YYData->{LINE},
40         }}
41 ;
42
43 include:
44         'include' commalist ';'
45         {{
46                 "TYPE" => "INCLUDE",
47                 "PATHS" => $_[2],
48                 "FILE" => $_[0]->YYData->{FILE},
49                 "LINE" => $_[0]->YYData->{LINE},
50         }}
51 ;
52
53 importlib:
54         'importlib' commalist ';'
55         {{
56                 "TYPE" => "IMPORTLIB",
57                 "PATHS" => $_[2],
58                 "FILE" => $_[0]->YYData->{FILE},
59                 "LINE" => $_[0]->YYData->{LINE},
60         }}
61 ;
62
63 commalist:
64         text { [ $_[1] ] }
65         |
66         commalist ',' text { push(@{$_[1]}, $_[3]); $_[1] }
67 ;
68
69 coclass:
70         property_list 'coclass' identifier '{' interface_names '}' optional_semicolon
71         {{
72                 "TYPE" => "COCLASS",
73                 "PROPERTIES" => $_[1],
74                 "NAME" => $_[3],
75                 "DATA" => $_[5],
76                 "FILE" => $_[0]->YYData->{FILE},
77                 "LINE" => $_[0]->YYData->{LINE},
78         }}
79 ;
80
81 interface_names:
82         #empty { {} }
83         |
84         interface_names 'interface' identifier ';' { push(@{$_[1]}, $_[2]); $_[1] }
85 ;
86
87 interface:
88         property_list 'interface' identifier base_interface '{' definitions '}' optional_semicolon
89         {{
90                 "TYPE" => "INTERFACE",
91                 "PROPERTIES" => $_[1],
92                 "NAME" => $_[3],
93                 "BASE" => $_[4],
94                 "DATA" => $_[6],
95                 "FILE" => $_[0]->YYData->{FILE},
96                 "LINE" => $_[0]->YYData->{LINE},
97         }}
98 ;
99
100 base_interface:
101         #empty
102         |
103         ':' identifier { $_[2] }
104 ;
105
106
107 cpp_quote:
108         'cpp_quote' '(' text ')'
109         {{
110                  "TYPE" => "CPP_QUOTE",
111                  "DATA" => $_[3],
112                  "FILE" => $_[0]->YYData->{FILE},
113                  "LINE" => $_[0]->YYData->{LINE},
114         }}
115 ;
116
117 definitions:
118         definition              { [ $_[1] ] }
119         |
120         definitions definition  { push(@{$_[1]}, $_[2]); $_[1] }
121 ;
122
123 definition:
124         function
125         |
126         const
127         |
128         typedef
129         |
130         typedecl
131 ;
132
133 const:
134         'const' identifier pointers identifier '=' anytext ';'
135         {{
136                 "TYPE"  => "CONST",
137                 "DTYPE"  => $_[2],
138                 "POINTERS" => $_[3],
139                 "NAME"  => $_[4],
140                 "VALUE" => $_[6],
141                 "FILE" => $_[0]->YYData->{FILE},
142                 "LINE" => $_[0]->YYData->{LINE},
143         }}
144         |
145         'const' identifier pointers identifier array_len '=' anytext ';'
146         {{
147                 "TYPE"  => "CONST",
148                 "DTYPE"  => $_[2],
149                 "POINTERS" => $_[3],
150                 "NAME"  => $_[4],
151                 "ARRAY_LEN" => $_[5],
152                 "VALUE" => $_[7],
153                 "FILE" => $_[0]->YYData->{FILE},
154                 "LINE" => $_[0]->YYData->{LINE},
155         }}
156 ;
157
158 function:
159         property_list type identifier '(' element_list2 ')' ';'
160         {{
161                 "TYPE" => "FUNCTION",
162                 "NAME" => $_[3],
163                 "RETURN_TYPE" => $_[2],
164                 "PROPERTIES" => $_[1],
165                 "ELEMENTS" => $_[5],
166                 "FILE" => $_[0]->YYData->{FILE},
167                 "LINE" => $_[0]->YYData->{LINE},
168         }}
169 ;
170
171 typedef:
172         property_list 'typedef' type pointers identifier array_len ';'
173         {{
174                 "TYPE" => "TYPEDEF",
175                 "PROPERTIES" => $_[1],
176                 "NAME" => $_[5],
177                 "DATA" => $_[3],
178                 "POINTERS" => $_[4],
179                 "ARRAY_LEN" => $_[6],
180                 "FILE" => $_[0]->YYData->{FILE},
181                 "LINE" => $_[0]->YYData->{LINE},
182         }}
183 ;
184
185 usertype:
186         struct
187         |
188         union
189         |
190         enum
191         |
192         bitmap
193         |
194         pipe
195 ;
196
197 typedecl:
198         usertype ';' { $_[1] }
199 ;
200
201 sign:
202         'signed'
203         |
204         'unsigned'
205 ;
206
207 existingtype:
208         sign identifier { ($_[1]?$_[1]:"signed") ." $_[2]" }
209         |
210         identifier
211 ;
212
213 type:
214         usertype
215         |
216         existingtype
217         |
218         void { "void" }
219 ;
220
221 enum_body:
222         '{' enum_elements '}' { $_[2] }
223 ;
224
225 opt_enum_body:
226         #empty
227         |
228         enum_body
229 ;
230
231 enum:
232         property_list 'enum' optional_identifier opt_enum_body
233         {{
234                 "TYPE" => "ENUM",
235                 "PROPERTIES" => $_[1],
236                 "NAME" => $_[3],
237                 "ELEMENTS" => $_[4],
238                 "FILE" => $_[0]->YYData->{FILE},
239                 "LINE" => $_[0]->YYData->{LINE},
240         }}
241 ;
242
243 enum_elements:
244         enum_element                    { [ $_[1] ] }
245         |
246         enum_elements ',' enum_element  { push(@{$_[1]}, $_[3]); $_[1] }
247 ;
248
249 enum_element:
250         identifier
251         |
252         identifier '=' anytext { "$_[1]$_[2]$_[3]" }
253 ;
254
255 bitmap_body:
256         '{' opt_bitmap_elements '}' { $_[2] }
257 ;
258
259 opt_bitmap_body:
260         #empty
261         |
262         bitmap_body
263 ;
264
265 bitmap:
266         property_list 'bitmap' optional_identifier opt_bitmap_body
267         {{
268                 "TYPE" => "BITMAP",
269                 "PROPERTIES" => $_[1],
270                 "NAME" => $_[3],
271                 "ELEMENTS" => $_[4],
272                 "FILE" => $_[0]->YYData->{FILE},
273                 "LINE" => $_[0]->YYData->{LINE},
274         }}
275 ;
276
277 bitmap_elements:
278         bitmap_element                      { [ $_[1] ] }
279         |
280         bitmap_elements ',' bitmap_element  { push(@{$_[1]}, $_[3]); $_[1] }
281 ;
282
283 opt_bitmap_elements:
284         #empty
285         |
286         bitmap_elements
287 ;
288
289 bitmap_element:
290         identifier '=' anytext { "$_[1] ( $_[3] )" }
291 ;
292
293 struct_body:
294         '{' element_list1 '}' { $_[2] }
295 ;
296
297 opt_struct_body:
298         #empty
299         |
300         struct_body
301 ;
302
303 struct:
304         property_list 'struct' optional_identifier opt_struct_body
305         {{
306                 "TYPE" => "STRUCT",
307                 "PROPERTIES" => $_[1],
308                 "NAME" => $_[3],
309                 "ELEMENTS" => $_[4],
310                 "FILE" => $_[0]->YYData->{FILE},
311                 "LINE" => $_[0]->YYData->{LINE},
312         }}
313 ;
314
315 empty_element:
316         property_list ';'
317         {{
318                 "NAME" => "",
319                 "TYPE" => "EMPTY",
320                 "PROPERTIES" => $_[1],
321                 "POINTERS" => 0,
322                 "ARRAY_LEN" => [],
323                 "FILE" => $_[0]->YYData->{FILE},
324                 "LINE" => $_[0]->YYData->{LINE},
325         }}
326 ;
327
328 base_or_empty:
329         base_element ';'
330         |
331         empty_element;
332
333 optional_base_element:
334         property_list base_or_empty { $_[2]->{PROPERTIES} = FlattenHash([$_[1],$_[2]->{PROPERTIES}]); $_[2] }
335 ;
336
337 union_elements:
338         #empty
339         |
340         union_elements optional_base_element { push(@{$_[1]}, $_[2]); $_[1] }
341 ;
342
343 union_body:
344         '{' union_elements '}' { $_[2] }
345 ;
346
347 opt_union_body:
348         #empty
349         |
350         union_body
351 ;
352
353 union:
354         property_list 'union' optional_identifier opt_union_body
355         {{
356                 "TYPE" => "UNION",
357                 "PROPERTIES" => $_[1],
358                 "NAME" => $_[3],
359                 "ELEMENTS" => $_[4],
360                 "FILE" => $_[0]->YYData->{FILE},
361                 "LINE" => $_[0]->YYData->{LINE},
362         }}
363 ;
364
365 base_element:
366         property_list type pointers identifier array_len
367         {{
368                 "NAME" => $_[4],
369                 "TYPE" => $_[2],
370                 "PROPERTIES" => $_[1],
371                 "POINTERS" => $_[3],
372                 "ARRAY_LEN" => $_[5],
373                 "FILE" => $_[0]->YYData->{FILE},
374                 "LINE" => $_[0]->YYData->{LINE},
375         }}
376 ;
377
378 pointers:
379         #empty
380         { 0 }
381         |
382         pointers '*'  { $_[1]+1 }
383 ;
384
385 pipe:
386         property_list 'pipe' type
387         {{
388                 "TYPE" => "PIPE",
389                 "PROPERTIES" => $_[1],
390                 "NAME" => undef,
391                 "DATA" => {
392                         "TYPE" => "STRUCT",
393                         "PROPERTIES" => $_[1],
394                         "NAME" => undef,
395                         "ELEMENTS" => [{
396                                 "NAME" => "count",
397                                 "PROPERTIES" => $_[1],
398                                 "POINTERS" => 0,
399                                 "ARRAY_LEN" => [],
400                                 "TYPE" => "uint3264",
401                                 "FILE" => $_[0]->YYData->{FILE},
402                                 "LINE" => $_[0]->YYData->{LINE},
403                         },{
404                                 "NAME" => "array",
405                                 "PROPERTIES" => $_[1],
406                                 "POINTERS" => 0,
407                                 "ARRAY_LEN" => [ "count" ],
408                                 "TYPE" => $_[3],
409                                 "FILE" => $_[0]->YYData->{FILE},
410                                 "LINE" => $_[0]->YYData->{LINE},
411                         }],
412                         "FILE" => $_[0]->YYData->{FILE},
413                         "LINE" => $_[0]->YYData->{LINE},
414                 },
415                 "FILE" => $_[0]->YYData->{FILE},
416                 "LINE" => $_[0]->YYData->{LINE},
417         }}
418 ;
419
420 element_list1:
421         #empty
422         { [] }
423         |
424         element_list1 base_element ';' { push(@{$_[1]}, $_[2]); $_[1] }
425 ;
426
427 optional_const:
428         #empty
429         |
430         'const'
431 ;
432
433 element_list2:
434         #empty
435         |
436         'void'
437         |
438         optional_const base_element { [ $_[2] ] }
439         |
440         element_list2 ',' optional_const base_element { push(@{$_[1]}, $_[4]); $_[1] }
441 ;
442
443 array_len:
444         #empty { [] }
445         |
446         '[' ']' array_len           { push(@{$_[3]}, "*"); $_[3] }
447         |
448         '[' anytext ']' array_len   { push(@{$_[4]}, "$_[2]"); $_[4] }
449 ;
450
451 property_list:
452         #empty
453         |
454         property_list '[' properties ']' { FlattenHash([$_[1],$_[3]]); }
455 ;
456
457 properties:
458         property                { $_[1] }
459         |
460         properties ',' property { FlattenHash([$_[1], $_[3]]); }
461 ;
462
463 property:
464         identifier                       {{ "$_[1]" => "1"     }}
465         |
466         identifier '(' commalisttext ')' {{ "$_[1]" => "$_[3]" }}
467 ;
468
469 commalisttext:
470         anytext
471         |
472         commalisttext ',' anytext { "$_[1],$_[3]" }
473 ;
474
475 anytext:
476         #empty
477         { "" }
478         |
479         identifier
480         |
481         constant
482         |
483         text
484         |
485         anytext '-' anytext  { "$_[1]$_[2]$_[3]" }
486         |
487         anytext '.' anytext  { "$_[1]$_[2]$_[3]" }
488         |
489         anytext '*' anytext  { "$_[1]$_[2]$_[3]" }
490         |
491         anytext '>' anytext  { "$_[1]$_[2]$_[3]" }
492         |
493         anytext '<' anytext  { "$_[1]$_[2]$_[3]" }
494         |
495         anytext '|' anytext  { "$_[1]$_[2]$_[3]" }
496         |
497         anytext '&' anytext  { "$_[1]$_[2]$_[3]" }
498         |
499         anytext '/' anytext  { "$_[1]$_[2]$_[3]" }
500         |
501         anytext '?' anytext  { "$_[1]$_[2]$_[3]" }
502         |
503         anytext ':' anytext  { "$_[1]$_[2]$_[3]" }
504         |
505         anytext '=' anytext  { "$_[1]$_[2]$_[3]" }
506         |
507         anytext '+' anytext  { "$_[1]$_[2]$_[3]" }
508         |
509         anytext '~' anytext  { "$_[1]$_[2]$_[3]" }
510         |
511         anytext '(' commalisttext ')' anytext  { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
512         |
513         anytext '{' commalisttext '}' anytext  { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
514 ;
515
516 identifier:
517         IDENTIFIER
518 ;
519
520 optional_identifier:
521         #empty { undef }
522         |
523         IDENTIFIER
524 ;
525
526 constant:
527         CONSTANT
528 ;
529
530 text:
531         TEXT { "\"$_[1]\"" }
532 ;
533
534 optional_semicolon:
535         #empty
536         |
537         ';'
538 ;
539
540
541 #####################################
542 # start code
543 %%
544
545 use Parse::Pidl qw(error);
546
547 #####################################################################
548 # flatten an array of hashes into a single hash
549 sub FlattenHash($)
550 {
551         my $a = shift;
552         my %b;
553         for my $d (@{$a}) {
554                 for my $k (keys %{$d}) {
555                 $b{$k} = $d->{$k};
556                 }
557         }
558         return \%b;
559 }
560
561 #####################################################################
562 # traverse a perl data structure removing any empty arrays or
563 # hashes and any hash elements that map to undef
564 sub CleanData($)
565 {
566         sub CleanData($);
567         my($v) = shift;
568
569         return undef if (not defined($v));
570
571         if (ref($v) eq "ARRAY") {
572                 foreach my $i (0 .. $#{$v}) {
573                         CleanData($v->[$i]);
574                 }
575                 # this removes any undefined elements from the array
576                 @{$v} = grep { defined $_ } @{$v};
577         } elsif (ref($v) eq "HASH") {
578                 foreach my $x (keys %{$v}) {
579                         CleanData($v->{$x});
580                         if (!defined $v->{$x}) {
581                                 delete($v->{$x});
582                                 next;
583                         }
584                 }
585         }
586
587         return $v;
588 }
589
590 sub _Error {
591         if (exists $_[0]->YYData->{ERRMSG}) {
592                 error($_[0]->YYData, $_[0]->YYData->{ERRMSG});
593                 delete $_[0]->YYData->{ERRMSG};
594                 return;
595         }
596
597         my $last_token = $_[0]->YYData->{LAST_TOKEN};
598
599         error($_[0]->YYData, "Syntax error near '$last_token'");
600 }
601
602 sub _Lexer($)
603 {
604         my($parser)=shift;
605
606         $parser->YYData->{INPUT} or return('',undef);
607
608 again:
609         $parser->YYData->{INPUT} =~ s/^[ \t]*//;
610
611         for ($parser->YYData->{INPUT}) {
612                 if (/^\#/) {
613                         # Linemarker format is described at
614                         # http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
615                         if (s/^\# (\d+) \"(.*?)\"(( \d+){1,4}|)//) {
616                                 $parser->YYData->{LINE} = $1-1;
617                                 $parser->YYData->{FILE} = $2;
618                                 goto again;
619                         }
620                         if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) {
621                                 $parser->YYData->{LINE} = $1-1;
622                                 $parser->YYData->{FILE} = $2;
623                                 goto again;
624                         }
625                         if (s/^(\#.*)$//m) {
626                                 goto again;
627                         }
628                 }
629                 if (s/^(\n)//) {
630                         $parser->YYData->{LINE}++;
631                         goto again;
632                 }
633                 if (s/^\"(.*?)\"//) {
634                         $parser->YYData->{LAST_TOKEN} = $1;
635                         return('TEXT',$1);
636                 }
637                 if (s/^(\d+)(\W|$)/$2/) {
638                         $parser->YYData->{LAST_TOKEN} = $1;
639                         return('CONSTANT',$1);
640                 }
641                 if (s/^([\w_]+)//) {
642                         $parser->YYData->{LAST_TOKEN} = $1;
643                         if ($1 =~
644                             /^(coclass|interface|import|importlib
645                               |include|cpp_quote|typedef
646                               |union|struct|enum|bitmap|pipe
647                               |void|const|unsigned|signed)$/x) {
648                                 return $1;
649                         }
650                         return('IDENTIFIER',$1);
651                 }
652                 if (s/^(.)//s) {
653                         $parser->YYData->{LAST_TOKEN} = $1;
654                         return($1,$1);
655                 }
656         }
657 }
658
659 sub parse_string
660 {
661         my ($data,$filename) = @_;
662
663         my $self = new Parse::Pidl::IDL;
664
665         $self->YYData->{FILE} = $filename;
666         $self->YYData->{INPUT} = $data;
667         $self->YYData->{LINE} = 0;
668         $self->YYData->{LAST_TOKEN} = "NONE";
669
670         my $idl = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error );
671
672         return CleanData($idl);
673 }
674
675 sub parse_file($$)
676 {
677         my ($filename,$incdirs) = @_;
678
679         my $saved_delim = $/;
680         undef $/;
681         my $cpp = $ENV{CPP};
682         my $options = "";
683         if (! defined $cpp) {
684                 if (defined $ENV{CC}) {
685                         $cpp = "$ENV{CC}";
686                         $options = "-E";
687                 } else {
688                         $cpp = "cpp";
689                 }
690         }
691         my $includes = join('',map { " -I$_" } @$incdirs);
692         my $data = `$cpp $options -D__PIDL__$includes -xc "$filename"`;
693         $/ = $saved_delim;
694
695         return parse_string($data, $filename);
696 }