File Coverage

blib/lib/CORBA/IDLtree.pm
Criterion Covered Total %
statement 1079 2362 45.6
branch 558 1386 40.2
condition 122 435 28.0
subroutine 56 92 60.8
pod 20 79 25.3
total 1835 4354 42.1


line stmt bran cond sub pod time code
1             # CORBA/IDLtree.pm IDL to symbol tree translator
2             # This module is distributed under the same terms as Perl itself.
3             # Copyright (C) 1998-2019, O. Kellogg
4             # Main Authors: Oliver Kellogg, Heiko Schroeder
5             #
6             # -----------------------------------------------------------------------------
7             # Ver. | Date | Recent changes (for complete history see file Changes)
8             # -----+----------+------------------------------------------------------------
9             # 2.03 2019/04/27 * Fixed a bug related to Dump_Symbols whereby when using
10             # a string array ref as the optional argument, repeated
11             # calls to the sub would accumulate the text.
12             # * In sub parse_members, optional argument $comment fixes
13             # processing of trailing comment at members of struct,
14             # exception, and valuetype.
15             # 2.02 2018/08/15 * Fixed a few typos in documentation.
16             # * Added support for IDL4 struct inheritance defined by the
17             # Building Block Extended Data-Types:
18             # In case of STRUCT, the first SUBORDINATES element of may
19             # be a reference to a further STRUCT node instead of the
20             # reference to quintuplet. In this case, the first element
21             # indicates the IDL4 parent struct type of the current
22             # struct. The function isnode() can be used for detecting
23             # this case. The support for IDL4 struct inheritance is
24             # implemented in sub Parse_File_i case $kw eq 'struct'.
25             # * In sub is_elementary_type return early on undefined
26             # $tdesc.
27             # * In sub info check for valid $currfile and @infilename
28             # before accessing $infilename[$currfile].
29             # * In sub error avoid code duplication by reusing the
30             # implementation of sub info.
31             # * In sub dump_symbols_internal handling of METHOD, pop
32             # @arg only if @arg is non empty and $arg[-1] contains
33             # the exception list. We need these extra tests because
34             # METHODs in VALUETYPEs do not have an exception list as
35             # the last element of the SUBORDINATES.
36             # * In sub dump_symbols_internal handling of REMARK nodes,
37             # on calling sub dump_comment swap elements of anonymous
38             # constructed array: $name comes first, then $subord.
39             # (COMMENT nodes use the same layout.)
40             # 2.01 2018/01/23 * Fixed parsing of named argument values in sub
41             # parse_annotation_app: At case
42             # @$argref && $argref->[0] eq '('
43             # while-loop over @$argref case
44             # $val =~ /^[a-z]/i and $argref->[0] eq '='
45             # for-loop of $ai case
46             # $adef[$ai]->[1] eq $parname,
47             # after assigning $param_index execute `last' instead of
48             # `return'.
49             # * Declared globals %annoEnum and @annoDefs as `our' to
50             # make them accessible from outside.
51             # * Added 'port' to global %keywords.
52             # * Fixed calls to sub annotation so that more than one
53             # annotation may accumulate on a given IDL item.
54             # * Fixed changelog entry for v. 1.6 modification of REMARK
55             # NAME/SUBORDINATES.
56             # 2.00 2018/01/05 * Fixed parsing of parameterless annotation with empty
57             # @$argref in sub parse_annotation_app.
58             # * Changed version numbering to conform to CPAN format.
59             # * Based distro on skeleton generated by module-starter.
60             # * Started converting inline documentation to POD format.
61             # 1.6 2018/01/01 * Fixed parsing of inheritance from an absolute qualified
62             # superclass such as e.g.
63             # valuetype vt : ::absolute::qualified::superclass {...};
64             # * Added variable $global_idlfile, a copy of the file name
65             # passed into the most recent call to Parse_File.
66             # * Simplified the REMARK node as follows:
67             # - Its NAME contains the starting line number of the
68             # comment lines.
69             # - Its SUBORDINATES points to a simple array of lines.
70             # The file name and line number elements are no longer
71             # part of the lines array.
72             # * The COMMENT element now points to a tuple of (starting)
73             # line number and reference to simple array of lines.
74             # I.e. the file name and line number elements are no
75             # longer part of the lines array.
76             # * Added support for IDL4 standard annotations and user
77             # defined @annotation. See below for documentation on
78             # the new node element ANNOTATIONS.
79             # IDL4 annotations are currently supported in the
80             # following locations:
81             # - Type declarations
82             # - Member declarations of structured types
83             # - Enum literal value declarations
84             # Modified the node structure of these constructs
85             # accordingly.
86             # * New sub enum_literals returns the net literals of an
87             # ENUM. It is intended to shield against the node
88             # structure change at enum literals. Direct usages of
89             # enum SUBORDINATES should be replaced by calls to this
90             # sub when possible.
91             # * Removed support for non standard enum value repre-
92             # sentation as in: enum MyEnum { zero=0, one=1 };
93             # This is superseded by the @value annotation.
94             # 1.5 2017/07/23 The SCOPEREF of a MODULE now points to the previous
95             # opening of the module.
96             # Changed the COMMENT node element and the NAME element of
97             # the REMARK node as follows: Each element in the comment
98             # array is a ref to an array that contains the name of the
99             # file, the line number, and the comment text in that order.
100              
101             package CORBA::IDLtree;
102              
103             # require Exporter;
104             require Carp;
105              
106 2     2   73532 use 5.006;
  2         14  
107 2     2   10 use strict 'vars';
  2         4  
  2         53  
108 2     2   10 use warnings;
  2         3  
  2         70  
109 2     2   11 use Exporter qw(import);
  2         4  
  2         71  
110 2     2   2283 use Math::BigInt;
  2         57904  
  2         11  
111 2     2   46280 use Config;
  2         4  
  2         130  
112              
113             # @EXPORT = ();
114             # @EXPORT_OK = (); # &Parse_File, &Dump_Symbols, and all the constants subs
115              
116 2         54574 use vars qw(@include_path %defines $cache_trees $global_idlfile
117             $n_errors $enable_comments $struct2vt $vt2struct
118             $cache_statistics $string_bound $permissive
119             $long_double_supported $union_default_null_allowed
120 2     2   13 $leading_underscore_allowed);
  2         4  
121              
122             =head1 NAME
123              
124             CORBA::IDLtree - OMG IDL to symbol tree translator
125              
126             =head1 VERSION
127              
128             Version 2.03
129              
130             =cut
131              
132             our $VERSION = '2.03';
133              
134             =head1 SYNOPSIS
135              
136             Subroutine Parse_File is the universal entry point (to be called by the
137             main program.)
138             It takes an IDL file name as the input parameter and parses that file,
139             constructing one or more symbol trees for the outermost declarations
140             encountered. It returns a reference to an array containing references
141             to those trees.
142             In case of errors during parsing, Parse_File returns 0.
143              
144             Usage:
145              
146             use CORBA::IDLtree;
147              
148             my $ref_to_array_of_outermost_declarations = CORBA::IDLtree::Parse_File("myfile.idl");
149              
150             $ref_to_array_of_outermost_declarations or die "File had syntax errors\n";
151             foreach my $node (@$ref_to_array_of_outermost_declarations) {
152             # Query $node->[TYPE] to find out what each node is;
153             # use $node->[SUBORDINATES] according to the $node->[TYPE].
154             # For example:
155             if ($node->[CORBA::IDLtree::TYPE] == CORBA::IDLtree::MODULE) {
156             foreach my $subnode @{$node->[CORBA::IDLtree::SUBORDINATES]}) {
157             # Assuming your "sub process" codes your business logic:
158             &process($subnode);
159             }
160             } elsif ($node->[CORBA::IDLtree::TYPE] == CORBA::IDLtree::...) {
161             # And so on, decode and process all the types you need ...
162             # For further details see the demo application in subdir demoapp.
163             }
164             }
165              
166             =head1 STRUCTURE OF THE SYMBOL TREE
167              
168             A "thing" in the symbol tree can be either a reference to a node, or a
169             reference to an array of references to nodes.
170              
171             Each node is a six element array with the elements
172              
173             [0] => TYPE (MODULE|INTERFACE|STRUCT|UNION|ENUM|TYPEDEF|CHAR|...)
174             [1] => NAME
175             [2] => SUBORDINATES
176             [3] => ANNOTATIONS
177             [4] => COMMENT
178             [5] => SCOPEREF
179              
180             The C element, instead of holding a type ID number (see the following
181             list under C), can also be a reference to the node defining the
182             type. When the C element can contain either a type ID or a reference to
183             the defining node, we will call it a I.
184             Which of the two alternatives is in effect can be determined via the
185             C function.
186              
187             The C element, unless specified otherwise, simply holds the name string
188             of the respective IDL syntactic item.
189              
190             The C element depends on the type ID:
191              
192             =over
193              
194             =item MODULE or INTERFACE
195              
196             Reference to an array of nodes (symbols) which are defined
197             within the module or interface. In the case of C,
198             element [0] in this array will contain a reference to a
199             further array which in turn contains references to the
200             parent interfaceZ<>(s) if inheritance is used, or the null
201             value if the current interface is not derived by
202             inheritance. Element [1] is the "local/abstract" flag
203             which is C for abstract interfaces, or C for
204             interfaces declared local.
205              
206             =item INTERFACE_FWD
207              
208             Reference to the node of the full interface declaration.
209              
210             =item STRUCT or EXCEPTION
211              
212             Reference to an array of node references representing the
213             member components of the struct or exception.
214             Each member representative node is a quintuplet consisting
215             of (C, C, , C, C).
216             The is a reference to a list of dimension numbers,
217             or is 0 if no dimensions were given.
218             In case of STRUCT, the first element may be a reference to a
219             further STRUCT node instead of the reference to quintuplet.
220             In this case, the first element indicates the IDL4 parent
221             struct type of the current struct. The function isnode() can
222             be used for detecting this case.
223              
224             =item UNION
225              
226             Similar to C/C, reference to an array of
227             nodes. For union members, the member node has the same
228             structure as for STRUCT/EXCEPTION.
229             However, the first node contains a type descriptor for
230             the discriminant type. The switch node does not follow the
231             usual quadruplet structure of members; it is a single item.
232             The C of a member node may also be C or C.
233             When the TYPE is CASE or DEFAULT, this means that the
234             following member node will be the union branch controlled
235             by the CASE or DEFAULT.
236             For C, the C is unused, and the C contains
237             a reference to a list of the case values for the following
238             member node.
239             For C, both the C and the C are unused.
240              
241             =item ENUM
242              
243             Reference to an array describing the enum value literals.
244             Each element in the array is a reference to a triplet
245             (three element array): The first element in the triplet is
246             the enum literal value. The second element is a reference
247             to an array of annotations as described in the C
248             documentation (see below). The third element is a
249             reference to the trailing comment list.
250              
251             =item TYPEDEF
252              
253             Reference to a two-element array: element 0 contains a
254             reference to the type descriptor of the original type;
255             element 1 contains a reference to an array of dimension
256             expressions, or the null value if no dimensions are given.
257             When given, the dimension expressions are plain strings.
258              
259             =item SEQUENCE
260              
261             As a special case, the C element of a C node
262             does not contain a name (as sequences are anonymous
263             types), but instead is used to hold the bound number.
264             If the bound number is 0 then it is an unbounded
265             sequence. The C element contains the type
266             descriptor of the base type of the sequence. This
267             descriptor could itself be a reference to a C
268             defining node (that is, a nested sequence definition.)
269              
270             =item BOUNDED_STRING
271              
272             Bounded strings are treated as a special case of sequence.
273             They are represented as references to a node that has
274             C or C as the type ID, the bound
275             number in the C, and the C element is unused.
276              
277             =item CONST
278              
279             Reference to a two-element array. Element 0 is a type
280             descriptor of the const's type; element 1 is a reference
281             to an array containing the RHS expression symbols.
282              
283             =item FIXED
284              
285             Reference to a two-element array. Element 0 contains the
286             digit number and element 1 contains the scale factor.
287             The C component in a C node is unused.
288              
289             =item VALUETYPE
290              
291             Uses the following structure:
292              
293             [0] => $is_abstract (boolean)
294             [1] => reference to a tuple (two-element list) containing
295             inheritance related information:
296             [0] => $is_truncatable (boolean)
297             [1] => \@ancestors (reference to array containing
298             references to ancestor nodes)
299             [2] => \@members: reference to array containing references
300             to tuples (two-element lists) of the form:
301             [0] => 0|PRIVATE|PUBLIC
302             A zero for this value means the element [1]
303             contains a reference to a declaration, such
304             as a METHOD or ATTRIBUTE.
305             In case of METHOD, the first element in the
306             method node subordinates (i.e., the return
307             type) may be FACTORY.
308             However, unlike interface methods, the last
309             element is _not_ a reference to the 'raises'
310             list. Support for 'raises' of valuetype
311             methods may be added in a future version.
312             [1] => reference to the defining node.
313             In case of PRIVATE or PUBLIC state member,
314             the SUBORDINATES of the defining node
315             contains a dimref (reference to dimensions
316             list, see STRUCT.)
317              
318             =item VALUETYPE_BOX
319              
320             Reference to the defining type node.
321              
322             =item VALUETYPE_FWD
323              
324             Reference to the node of the full valuetype declaration.
325              
326             =item NATIVE
327              
328             Subordinates unused.
329              
330             =item ATTRIBUTE
331              
332             Reference to a two-element array; element 0 is the read-
333             only flag (0 for read/write attributes), element 1 is a
334             type descriptor of the attribute's type.
335              
336             =item METHOD
337              
338             Reference to a variable length array; element 0 is a type
339             descriptor for the return type. Elements 1 and following
340             are references to parameter descriptor nodes with the
341             following structure:
342              
343             elem. 0 => parameter type descriptor
344             elem. 1 => parameter name
345             elem. 2 => parameter mode (IN, OUT, or INOUT)
346              
347             The last element in the variable-length array is a
348             reference to the "raises" list. This list contains
349             references to the declaration nodes of exceptions raised,
350             or is empty if there is no "raises" clause.
351              
352             =item INCFILE
353              
354             Reference to an array of nodes (symbols) which are defined
355             within the include file. The Name element of this node
356             contains the include file name.
357              
358             =item PRAGMA_PREFIX
359              
360             Subordinates unused.
361              
362             =item PRAGMA_VERSION
363              
364             Version string.
365              
366             =item PRAGMA_ID
367              
368             ID string.
369              
370             =item PRAGMA
371              
372             This is for the general case of pragmas that are none
373             of the above, i.e. pragmas unknown to IDLtree.
374             The C holds the pragma name, and C
375             holds all further text appearing after the pragma name.
376              
377             =item REMARK
378              
379             The C of the node contains the starting line number
380             of the comment text.
381             The C component contains a reference to a list
382             of comment lines. The comment lines are not newline
383             terminated.
384             The source line number of each comment line can be
385             computed by adding the starting line number and the
386             array index of the comment line.
387             By default, C nodes will not be generated;
388             generation of C nodes can be enabled by setting the
389             $enable_comments global variable to non zero.
390              
391             =back
392              
393             The C element holds the reference to an array of annotation nodes
394             if IDL4 style annotations are present (if no annotations are present then
395             the ANNOTATIONS element holds 0).
396             Each entry in this array is an array reference. The first element in the
397             array referenced is a reference to an entry in @annoDefs (see comments at
398             declaration of @annoDefs). The following elements contain the concrete
399             values for the parameters, in the order as defined by the entry in
400             @annoDefs. If the user omitted the value of the parameter then the
401             default as specified by the entry in @annoDefs is filled in.
402              
403             The C element holds the comment text that follows the IDL declaration
404             on the same line. Usually this is just a single line. However, if a multi-
405             line comment is started on the same line after a declaration, the multi-line
406             comment may extend to further lines - therefore we use a list of lines.
407             The lines in this list are not newline terminated. The C field is a
408             reference to a tuple of starting line number and reference to the line list,
409             or contains 0 if no trailing comment is present at the IDL item.
410              
411             The C element is a reference back to the node of the module or
412             interface enclosing the current node. If the current node is already
413             at the global scope level then the C is 0.
414             Special case: For a reopened module, the C points to the previous
415             opening of the same module. In case of multiple reopenings, each reopening
416             points to the previous opening. The C of the initial module finally
417             points to the enclosing scope.
418             All nodes have this element except for the parameter nodes of methods and
419             the component nodes of structs/unions/exceptions.
420              
421             =head1 CLASS VARIABLES
422              
423             =head2 Variables that can be set by client code
424              
425             =over
426              
427             =item @CORBA::IDLtree::include_path
428              
429             Paths where to look for included IDL files.
430              
431             =item %CORBA::IDLtree::defines
432              
433             Symbol definitions for preprocessor.
434              
435             =item $CORBA::IDLtree::cache_trees
436              
437             Values 0 or 1, default 0.
438             By default, do not cache trees of C<#include>d files.
439              
440             =item $CORBA::IDLtree::enable_comments
441              
442             Values 0 or 1, default 0.
443             By default, do not generate C nodes.
444              
445             =item $CORBA::IDLtree::struct2vt
446              
447             Values 0 or 1, default 0.
448             Change struct into equivalent valuetype
449              
450             =item $CORBA::IDLtree::vt2struct
451              
452             Values 0 or 1, default 0.
453             Change valuetype into equivalent struct
454              
455             =item $CORBA::IDLtree::cache_statistics
456              
457             Values 0 or 1, default 0.
458             Print cache statistics
459              
460             =item $CORBA::IDLtree::long_double_supported
461              
462             Values 0 or 1, default 0.
463             Switch on support for IDL C.
464              
465             =item $CORBA::IDLtree::union_default_null_allowed
466              
467             Values 0 or 1, default 1.
468             Switch off permission that a C's C branch may be empty.
469              
470             =item $CORBA::IDLtree::leading_underscore_allowed
471              
472             Value 1 will remove the leading underscore.
473             Value 2 will preserve the leading underscore.
474              
475             =item $CORBA::IDLtree::permissive
476              
477             Values 0 or 1, default 0.
478             By default, misuse of IDL keywords as identifiers is a hard error.
479              
480             =back
481              
482             =head2 Variables written by CORBA::IDLtree
483              
484             These are to be considered read-only from outside:
485              
486             =over
487              
488             =item $CORBA::IDLtree::n_errors
489              
490             Cumulative number of errors for a C call.
491              
492             =item $CORBA::IDLtree::global_idlfile
493              
494             Copy of filename passed into most recent call of sub Parse_File
495              
496             =back
497              
498             =cut
499              
500             # User definable auxiliary data for Parse_File:
501             @include_path = (); # Paths where to look for included IDL files
502             %defines = (); # Symbol definitions for preprocessor
503             $cache_trees = 0; # By default, do not cache trees of #included files
504             $enable_comments = 0; # By default, do not generate REMARK nodes.
505             $struct2vt = 0; # change struct into equivalent valuetype
506             $vt2struct = 0; # change valuetype into equivalent struct
507             $cache_statistics = 0; # print cache statistics
508              
509             $long_double_supported = 0;
510             $union_default_null_allowed = 1;
511             $leading_underscore_allowed = 0; # value 1 will remove the leading underscore
512             # value 2 will preserve the leading underscore
513             $permissive = 0; # By default, misuse of IDL keywords is a hard error
514              
515             # Variables written by CORBA::IDLtree (to be considered read-only from outside)
516              
517             $n_errors = 0; # Cumulative number of errors for a Parse_File call.
518             $global_idlfile = ""; # Copy of filename passed into most recent call of
519             # sub Parse_File
520              
521             # Internal variables (should not be visible)
522              
523             my $is64bit = $Config{ivsize} >= 8;
524              
525             our $verbose = 0; # report progress to stdout, set via sub set_verbose
526              
527             my $comment_directives = undef; # may be set to an IDLtree::Comment_Directives
528             # object or derivative via set_directive_object
529              
530             my %active_defines = (); # used by #ifdef / #ifndef / #define / #undef processing
531              
532             =head1 CONSTANTS
533              
534             =head2 Constants for accessing the elements of a node
535              
536             =over 2
537              
538             =item Constants for indexing the elements of a node
539              
540             As explained in STRUCTURE OF THE SYMBOL TREE, each node is represented as a
541             six element array. These constants are intended for indexing the array:
542              
543             sub TYPE () { 0 }
544             sub NAME () { 1 }
545             sub SUBORDINATES () { 2 }
546             sub MODE () { 2 }
547             sub ANNOTATIONS () { 3 }
548             sub COMMENT () { 4 }
549             sub SCOPEREF () { 5 }
550              
551             The constant C is an alias of C for method parameter nodes.
552              
553             =cut
554              
555             sub TYPE () { 0 }
556             sub NAME () { 1 }
557             sub SUBORDINATES () { 2 }
558             sub MODE () { 2 } # alias of SUBORDINATES (for method parameter nodes)
559             sub ANNOTATIONS () { 3 }
560             sub COMMENT () { 4 }
561             sub SCOPEREF () { 5 }
562              
563             =item Method parameter modes
564              
565             sub IN () { 1 }
566             sub OUT () { 2 }
567             sub INOUT () { 3 }
568              
569             =cut
570              
571             sub IN () { 1 }
572             sub OUT () { 2 }
573             sub INOUT () { 3 }
574              
575             =item Meanings of the TYPE entry in the symbol node
576              
577             sub NONE () { 0 } # error/illegality value
578             sub BOOLEAN () { 1 }
579             sub OCTET () { 2 }
580             sub CHAR () { 3 }
581             sub WCHAR () { 4 }
582             sub SHORT () { 5 }
583             sub LONG () { 6 }
584             sub LONGLONG () { 7 }
585             sub USHORT () { 8 }
586             sub ULONG () { 9 }
587             sub ULONGLONG () { 10 }
588             sub FLOAT () { 11 }
589             sub DOUBLE () { 12 }
590             sub LONGDOUBLE () { 13 }
591             sub STRING () { 14 }
592             sub WSTRING () { 15 }
593             sub OBJECT () { 16 }
594             sub TYPECODE () { 17 }
595             sub ANY () { 18 }
596             sub FIXED () { 19 } # node
597             sub BOUNDED_STRING () { 20 } # node
598             sub BOUNDED_WSTRING () { 21 } # node
599             sub SEQUENCE () { 22 } # node
600             sub ENUM () { 23 } # node
601             sub TYPEDEF () { 24 } # node
602             sub NATIVE () { 25 } # node
603             sub STRUCT () { 26 } # node
604             sub UNION () { 27 } # node
605             sub CASE () { 28 }
606             sub DEFAULT () { 29 }
607             sub EXCEPTION () { 30 } # node
608             sub CONST () { 31 } # node
609             sub MODULE () { 32 } # node
610             sub INTERFACE () { 33 } # node
611             sub INTERFACE_FWD () { 34 } # node
612             sub VALUETYPE () { 35 } # node
613             sub VALUETYPE_FWD () { 36 } # node
614             sub VALUETYPE_BOX () { 37 } # node
615             sub ATTRIBUTE () { 38 } # node
616             sub ONEWAY () { 39 } # implies "void" as the return type
617             sub VOID () { 40 }
618             sub FACTORY () { 41 }
619             sub METHOD () { 42 } # node
620             sub INCFILE () { 43 } # node
621             sub PRAGMA_PREFIX () { 44 } # node
622             sub PRAGMA_VERSION () { 45 } # node
623             sub PRAGMA_ID () { 46 } # node
624             sub PRAGMA () { 47 } # node
625             sub REMARK () { 48 } # node
626             sub NUMBER_OF_TYPES () { 49 }
627              
628             The constant C can only occur as the return type of a method in a valuetype.
629              
630             =cut
631              
632             # If these codes are changed then @predef_types must be changed accordingly.
633             sub NONE () { 0 } # error/illegality value
634             sub BOOLEAN () { 1 }
635             sub OCTET () { 2 }
636             sub CHAR () { 3 }
637             sub WCHAR () { 4 }
638             sub SHORT () { 5 }
639             sub LONG () { 6 }
640             sub LONGLONG () { 7 }
641             sub USHORT () { 8 }
642             sub ULONG () { 9 }
643             sub ULONGLONG () { 10 }
644             sub FLOAT () { 11 }
645             sub DOUBLE () { 12 }
646             sub LONGDOUBLE () { 13 }
647             sub STRING () { 14 }
648             sub WSTRING () { 15 }
649             sub OBJECT () { 16 }
650             sub TYPECODE () { 17 }
651             sub ANY () { 18 }
652             sub FIXED () { 19 } # node
653             sub BOUNDED_STRING () { 20 } # node
654             sub BOUNDED_WSTRING () { 21 } # node
655             sub SEQUENCE () { 22 } # node
656             sub ENUM () { 23 } # node
657             sub TYPEDEF () { 24 } # node
658             sub NATIVE () { 25 } # node
659             sub STRUCT () { 26 } # node
660             sub UNION () { 27 } # node
661             sub CASE () { 28 }
662             sub DEFAULT () { 29 }
663             sub EXCEPTION () { 30 } # node
664             sub CONST () { 31 } # node
665             sub MODULE () { 32 } # node
666             sub INTERFACE () { 33 } # node
667             sub INTERFACE_FWD () { 34 } # node
668             sub VALUETYPE () { 35 } # node
669             sub VALUETYPE_FWD () { 36 } # node
670             sub VALUETYPE_BOX () { 37 } # node
671             sub ATTRIBUTE () { 38 } # node
672             sub ONEWAY () { 39 } # implies "void" as the return type
673             sub VOID () { 40 }
674             sub FACTORY () { 41 } # treated as return type of METHOD;
675             # can only occur inside valuetype
676             sub METHOD () { 42 } # node
677             sub INCFILE () { 43 } # node
678             sub PRAGMA_PREFIX () { 44 } # node
679             sub PRAGMA_VERSION () { 45 } # node
680             sub PRAGMA_ID () { 46 } # node
681             sub PRAGMA () { 47 } # node
682             sub REMARK () { 48 } # node
683             sub NUMBER_OF_TYPES () { 49 }
684              
685             # special type code used for filling @typestack
686 59     59 0 252 sub ANNOTATION { &NUMBER_OF_TYPES }
687              
688             =item Interface/valuetype flag values
689              
690             sub ABSTRACT { 1 }
691             sub LOCAL { 2 }
692             sub TRUNCATABLE { 2 }
693             sub CUSTOM { 3 }
694              
695             =cut
696              
697 16     16 0 51 sub ABSTRACT { 1 }
698 16     16 0 45 sub LOCAL { 2 }
699 0     0 0 0 sub TRUNCATABLE { 2 }
700 0     0 0 0 sub CUSTOM { 3 }
701              
702             =item Valuetype member flags
703              
704             sub PRIVATE { 1 }
705             sub PUBLIC { 2 }
706              
707             =back
708              
709             =cut
710              
711 0     0 0 0 sub PRIVATE { 1 }
712 0     0 0 0 sub PUBLIC { 2 }
713              
714             =head1 SUBROUTINES
715              
716             =head2 Parse_File
717              
718             Parses the file name given as argument.
719             Returns reference to array of nodes representing the top level (global)
720             declarations in the file.
721             Returns 0 if the file had syntax errors.
722             C writes the error messages to C.
723              
724             =cut
725              
726             sub Parse_File;
727              
728             sub set_directive_object {
729 0     0 0 0 $comment_directives = shift;
730             }
731              
732             =head2 Dump_Symbols
733              
734             Symbol tree dumper (for debugging etc.) reconstructs the IDL source notation
735             from the parsed symbol tree.
736             Parameters:
737              
738             =over
739              
740             =item 1.
741              
742             Reference to a symbol array (return value from a previous call to Parse_File).
743              
744             =item 2.
745              
746             Optional parameter controlling the output:
747              
748             =over
749              
750             =item *
751              
752             If given as string then it is the name of a file into which to dump the IDL source.
753              
754             =item *
755              
756             If given as array reference then the IDL source will be placed in the
757             referenced array, one line per element, where each line is not newline
758             terminated.
759              
760             =item *
761              
762             If the optional parameter is not given or is given as C then the IDL
763             source will be dumped to C.
764              
765             =back
766              
767             =back
768              
769             =cut
770              
771             sub Dump_Symbols;
772              
773             sub Version ()
774             {
775 0     0 0 0 for ('$Revision$') { #'){
776 0 0       0 /: *(\S+)/ and return $VERSION . "_" . $1;
777             }
778 0         0 return $VERSION;
779             }
780              
781             =head2 is_elementary_type
782              
783             Given a node reference, returns the type constant if the node prepresents
784             an elementary type. Returns 0 if the type is not elementary.
785              
786             =head2 predef_type
787              
788             Given a type name (as string), returns the type constant if the type name
789             is that of an elementary type. Returns 0 if the type is not elementary.
790              
791             =head2 isnode
792              
793             Given a "thing", returns 1 if it is a reference to a node, 0 otherwise.
794              
795             =head2 is_scope
796              
797             Given a "thing", returns 1 if it's a ref to a C, C, or
798             C node.
799              
800             =head2 find_node
801              
802             Looks up a name in the symbol treeZ<>(s) constructed so far.
803             Returns the node ref if found, else 0.
804              
805             =head2 typeof
806              
807             Given a type descriptor, returns the type as a string in IDL syntax.
808              
809             =head2 set_verbose
810              
811             Call this to make the parser tell us what it's doing.
812              
813             =head2 is_a
814              
815             Determine if typeid is of given type, recursing through Cs.
816              
817             =head2 root_type
818              
819             Get the original type of a C, i.e. recurse through all non array
820             Cs until the original type is reached.
821              
822             =head2 is_pragma
823              
824             Return 1 if the given type constant or node is a pragma.
825              
826             =head2 files_included
827              
828             Returns an array with the names of files #included.
829              
830             =head2 get_scalar_default
831              
832             Get default value for type.
833             Uses comment directives object if available.
834              
835             =head2 idlsplit
836              
837             Splits a given IDL expression into its individual
838             tokens. Returns the tokens as a list.
839             Example: The call
840              
841             idlsplit("(m_a::myconst+1.0) / scale")
842              
843             returns the list
844              
845             "(", "m_a::myconst", "+", "1.0", ")", "/", "scale"
846              
847             =head2 is_valid_identifier
848              
849             Returns 1 if the argument is a valid IDL identifier.
850              
851             =head2 scoped_name
852              
853             Expects a symbol node as the input argument and
854             returns its fully qualified name in IDL syntax.
855              
856             =head2 collect_includes
857              
858             Utility for collecting C<#include>d files.
859             Parameters:
860              
861             =over
862              
863             =item 1.
864              
865             Reference to node list to analyze.
866              
867             =item 2.
868              
869             Reference to hash in which to add the includefile names encountered.
870             The includefile names are added as key fields of the hash.
871             The value fields are not used.
872              
873             =back
874              
875             =head2 get_numeric
876              
877             Computes numeric value of expression.
878              
879             =head2 enum_literals
880              
881             The C of C contains more than just the actual enum literal
882             values (the additional data are: annotations, trailing comments).
883             This is a convenience subroutine which returns the net literals of the given
884             C<$enumnode[SUBORDINATES]>.
885              
886             =cut
887              
888             sub is_elementary_type;
889             sub predef_type;
890             sub isnode;
891             sub is_scope;
892             sub find_node;
893             sub typeof;
894             sub set_verbose;
895             sub is_a;
896             sub root_type;
897             sub is_pragma;
898             sub files_included;
899             sub get_scalar_default;
900             sub idlsplit;
901             sub is_valid_identifier;
902             sub scoped_name;
903             sub collect_includes;
904             sub get_numeric;
905             sub enum_literals;
906              
907             # Internal subroutines (should not be visible)
908              
909             sub use_system_preprocessor; # Attempt to use the system preprocessor if
910             # one is found.
911             # Takes no arguments.
912             # NOTE: Due to variations in preprocessor
913             # options and behavior, this might not work
914             # on your system.
915             # If use_system_preprocessor is not called
916             # then the IDLtree parser attempts to do the
917             # preprocessing itself.
918             sub in_annotation_def; # Returns true while parsing an @annotation.
919             sub get_items;
920             sub unget_items;
921             sub check_name;
922             sub curr_scope;
923             sub scope_names;
924             sub find_node_i;
925             sub parse_sequence;
926             sub parse_type;
927             sub parse_members;
928             sub error;
929             sub info;
930             sub abort;
931             sub require_end_of_stmt;
932             sub get_files_included;
933             sub dump_symbols_internal;
934              
935             # Start of implementation
936              
937             # Auxiliary (non-visible) global stuff ########################################
938              
939             # Annotation enumeration types (auxiliary to declaring @annoDefs).
940             # User defined annotation enumeration types are added here when they arise.
941             our %annoEnum = (
942             "AutoidKind" => [ "SEQUENTIAL", "HASH" ],
943             "ExtensibilityKind" => [ "FINAL", "APPENDABLE", "MUTABLE" ],
944             "PlacementKind" => [ "BEGIN_FILE",
945             "BEFORE_DECLARATION",
946             "BEGIN_DECLARATION",
947             "END_DECLARATION",
948             "AFTER_DECLARATION",
949             "END_FILE" ]
950             );
951              
952             # Predefined annotation definitions.
953             # User defined annotation definitions are appended here when they arise.
954             # Each element in @annoDefs is a reference to an array:
955             # The first element in the array is the annotation name.
956             # The following elements in the array represent the parameters of the
957             # annotation (if the annotation has no parameters then there will be no
958             # further elements). Each parameter is represented as a reference to a
959             # triplet (three element array). The first element in the triplet is
960             # either a type number (one of the values BOOLEAN, OCTET, CHAR, SHORT,
961             # LONG, LONGLONG, USHORT, ULONG, ULONGLONG, FLOAT, STRING, ANY) or a
962             # reference to an entry in %annoEnum. The second element in the triplet
963             # is the parameter name. The third element in the triplet is the default
964             # value if a default is given, or is undef if no default is given.
965              
966             our @annoDefs = (
967             [ "id", [ ULONG, "value", undef ] ],
968             [ "autoid", [ "AutoidKind", "value", "HASH" ] ],
969             [ "optional", [ BOOLEAN, "value", "TRUE" ] ],
970             [ "position", [ USHORT, "value", undef ] ],
971             [ "value", [ ANY, "value", undef ] ],
972             [ "extensibility", [ "ExtensibilityKind", "value", undef ] ],
973             [ "final" ],
974             [ "appendable" ],
975             [ "mutable" ],
976             [ "key", [ BOOLEAN, "value", "TRUE" ] ],
977             [ "must_understand", [ BOOLEAN, "value", "TRUE" ] ],
978             [ "default_literal" ],
979             [ "default", [ ANY, "value", undef ] ],
980             [ "range", [ ANY, "min", undef ], [ ANY, "max", undef ] ],
981             [ "min", [ ANY, "value", undef ] ],
982             [ "max", [ ANY, "value", undef ] ],
983             [ "unit", [ STRING, "value", undef ] ],
984             [ "bit_bound", [ USHORT, "value", undef ] ],
985             [ "external", [ BOOLEAN, "value", "TRUE" ] ],
986             [ "nested", [ BOOLEAN, "value", "TRUE" ] ],
987             [ "verbatim", [ STRING, "language", "*" ],
988             [ "PlacementKind", "placement", "BEFORE_DECLARATION" ],
989             [ STRING, "text", undef ] ],
990             [ "service", [ STRING, "platform", "*" ] ],
991             [ "oneway", [ BOOLEAN, "value", "TRUE" ] ],
992             [ "ami", [ BOOLEAN, "value", "TRUE" ] ]
993             );
994              
995             # Temporary store collecting annotations will be flushed when the construct
996             # to annotate is seen.
997             # The structure of @annotations is that of the ANNOTATIONS element in tree
998             # nodes (see documentation at beginning of file):
999             # Each entry in this array is an array reference. The first element in the
1000             # array referenced is a reference to an entry in @annoDefs. The following
1001             # elements contain the concrete values for the parameters, in the order as
1002             # defined by the entry in @annoDefs. If the user omitted the value of the
1003             # parameter then the default as specified by the entry in @annoDefs is
1004             # filled in.
1005             my @annotations = ();
1006              
1007             {
1008             # general symbol cache class, used for include file cache and
1009             # node cache
1010             package CORBA::IDLtree::Cache;
1011              
1012             sub new {
1013 4     4   7 my $class = shift;
1014 4   33     22 $class = ref($class) || $class;
1015              
1016 4         10 my $this = bless {}, $class;
1017 4         10 $this->clear();
1018 4         6 return $this;
1019             }
1020              
1021             sub clear {
1022 8     8   14 my $this = shift;
1023              
1024 8         13 %{$this->{_cache}} = ();
  8         58  
1025 8         15 $this->{_hits} = 0;
1026 8         17 $this->{_queries} = 0;
1027 8         23 return $this;
1028             }
1029              
1030             # if $value is true add under the name $name
1031             # to the cache
1032             sub add {
1033 43     43   65 my $this = shift;
1034 43         93 my ($name, $value) = @_;
1035              
1036 43 50       98 if ($value) {
1037 43 100       116 if (exists $this->{_cache}{$name}) {
1038 1         3 my $existing = $this->{_cache}{$name};
1039 1 50       4 if ($existing != $value) {
1040             # This happens when adding the reopening of a known module.
1041             # The cache only holds the last reopening.
1042 1 50       4 warn("CORBA::IDLtree::Cache::add($name): replacing "
1043             . "$existing (" . CORBA::IDLtree::typeof($existing)
1044             . ") by $value (" . CORBA::IDLtree::typeof($value) . ")\n")
1045             if $CORBA::IDLtree::verbose;
1046             }
1047             }
1048 43         127 $this->{_cache}{$name} = $value;
1049             }
1050 43         65 return $this;
1051             }
1052              
1053             # get entry for $name or undef if $name is not known
1054             sub get {
1055 377     377   491 my $this = shift;
1056 377         605 my ($name) = @_;
1057              
1058 377         537 $this->{_queries}++;
1059 377 100       771 if (exists $this->{_cache}{$name}) {
1060 66         95 $this->{_hits}++;
1061 66         148 return $this->{_cache}{$name};
1062             }
1063 311         457 return undef;
1064             }
1065              
1066             # return hits / queries ratio
1067             sub ratio {
1068 0     0   0 my $this = shift;
1069 0         0 return $this->{_hits}." / ".$this->{_queries};
1070             }
1071              
1072             # return known names
1073             sub symbols {
1074 0     0   0 my $this = shift;
1075 0         0 return keys %{$this->{_cache}};
  0         0  
1076             }
1077             }
1078              
1079             # The @predef_types array must have the types in the same order as
1080             # the numeric order of type identifying constants defined above.
1081             my @predef_types = qw/ none boolean octet char wchar short long long_long
1082             unsigned_short unsigned_long unsigned_long_long
1083             float double long_double string wstring Object
1084             TypeCode any fixed bounded_string bounded_wstring
1085             sequence enum typedef native struct union case default
1086             exception const module interface interface_fwd
1087             valuetype valuetype_fwd valuetype_box
1088             attribute oneway void factory method
1089             include pragma_prefix pragma_version pragma_id pragma /;
1090              
1091             # list of all IDL keywords (as of CORBA 3.0) in lower case
1092             # used to check for name conflicts
1093             my %keywords = map { $_ => undef } qw/
1094             abstract any attribute boolean case char component const
1095             consumes context custom default double emits enum exception
1096             eventtype factory false finder fixed float getraises home
1097             import in inout interface local long module multiple native
1098             object octet oneway out port primarykey private provides public
1099             publishes raises readonly setraises sequence short string
1100             struct supports switch true truncatable typedef typeid
1101             typeprefix unsigned union uses valuebase valuetype void
1102             wchar wstring/;
1103              
1104             my @infilename = (); # infilename and line_number move in parallel.
1105             my @line_number = ();
1106             my @remark = (); # Auxiliary to comment processing
1107             my @post_comment = (); # Auxiliary to comment processing
1108             my @global_items = (); # Auxiliary to sub unget_items
1109             my $findnode_cache = new CORBA::IDLtree::Cache();
1110             # Auxiliary to find_node_i(): cache for lookups
1111             my $abstract = 0; # can also contain LOCAL (for interfaces)
1112             my $currfile = -1;
1113             my $starting_line_number_of_remark = 0; # 0 = there is no pre comment
1114             my $line_number_of_post_comment = 0; # 0 = there is no post comment
1115             my $emucpp = 1; # use C preprocessor emulation
1116             my $locale_was_determined = 0;
1117             my $locale = undef;
1118              
1119             sub locate_executable {
1120             # FIXME: this is probably another reinvention of the wheel.
1121             # Should look for builtin Perl solution or CPAN module that does this.
1122 0     0 0 0 my $executable = shift;
1123             # my $pathsep = $Config{'path_sep'};
1124 0         0 my $pathsep = ':';
1125 0         0 my $fully_qualified_name = "";
1126 0         0 my @dirs = split(/$pathsep/, $ENV{'PATH'});
1127 0         0 foreach (@dirs) {
1128 0         0 my $fqn = "$_/$executable";
1129 0 0       0 if (-e $fqn) {
1130 0         0 $fully_qualified_name = $fqn;
1131 0         0 last;
1132             }
1133             }
1134 0         0 $fully_qualified_name;
1135             }
1136              
1137              
1138             sub idlsplit {
1139 224     224 1 324 my $str = shift;
1140 224         394 my $in_preprocessor = $str =~ /^\s*#/;
1141 224         297 my $in_string = 0;
1142 224         296 my $in_lit = 0;
1143 224         286 my $in_space = 0;
1144 224         304 my $i;
1145 224         330 my @out = ();
1146 224         282 my $ondx = -1;
1147 224         484 for ($i = 0; $i < length($str); $i++) {
1148 6134         9118 my $ch = substr($str, $i, 1);
1149 6134 100       16781 if ($in_string) {
    100          
    100          
    100          
    100          
    100          
1150 34         57 $out[$ondx] .= $ch;
1151 34 100 66     103 if ($ch eq '"' and substr($str, $i-1, 1) ne "\\") {
1152 3         11 $in_string = 0;
1153             }
1154             } elsif ($ch eq '"') {
1155 3         7 $in_string = 1;
1156 3         11 $out[++$ondx] = $ch;
1157             } elsif ($ch eq "'") {
1158 6         20 my $endx = index $str, "'", $i + 2;
1159 6 50       27 if ($endx < $i + 2) {
1160 0         0 error "cannot find closing apostrophe of char literal";
1161 0         0 return @out;
1162             }
1163 6         16 $out[++$ondx] = substr($str, $i, $endx - $i + 1);
1164             # print "idlsplit: $out[$ondx]\n";
1165 6         14 $i = $endx;
1166             } elsif ($ch =~ /[a-z_0-9\.]/i) {
1167 4921 100       7679 if (! $in_lit) {
1168 694         896 $in_lit = 1;
1169 694         843 $ondx++;
1170             }
1171 4921         9704 $out[$ondx] .= $ch;
1172             } elsif ($in_lit) {
1173 694         896 $in_lit = 0;
1174             # do preprocessor substitution
1175 694 50       1240 if (exists $active_defines{$out[$ondx]}) {
1176 0         0 my $value = $active_defines{$out[$ondx]};
1177 0 0       0 if ("$value" ne "") {
1178 0         0 my @addl = idlsplit($value);
1179 0         0 pop @out; # remove original symbol
1180 0         0 push @out, @addl; # add replacement text
1181 0         0 $ondx = $#out;
1182             }
1183             }
1184 694 100       1744 if ($ch !~ /\s/) {
1185 236         563 $out[++$ondx] = $ch;
1186             }
1187             } elsif ($ch !~ /\s/) {
1188 247         587 $out[++$ondx] = $ch;
1189             }
1190             }
1191 224 50       419 if ($in_lit) {
1192             # do preprocessor substitution
1193 0 0       0 if (exists $active_defines{$out[$ondx]}) {
1194 0         0 my $value = $active_defines{$out[$ondx]};
1195 0 0       0 if ("$value" ne "") {
1196 0         0 my @addl = idlsplit($value);
1197 0         0 pop @out; # remove original symbol
1198 0         0 push @out, @addl; # add replacement text
1199 0         0 $ondx = $#out;
1200             }
1201             }
1202             }
1203             # For simplification of further processing:
1204             # 1. Turn extra-long and unsigned types into single keyword
1205             # long double => long_double
1206             # unsigned short => unsigned_short
1207             # 2. Put scoped names back together, e.g. 'A' ':' ':' 'B' => 'A::B'
1208             # Also, discard global-scope designators. (leading ::)
1209             # 3. Put the sign and value of negative numbers back together
1210             # 4. Put bounded string type (string) into one element
1211 224         504 for ($i = 0; $i < $#out; $i++) {
1212 951 100 66     3221 if ($out[$i] eq 'long') {
    100 33        
    50          
    50          
1213 30 50 66     95 if ($out[$i+1] eq 'double' && !$long_double_supported) {
1214 0         0 error("ERROR: long double not supported");
1215             }
1216 30 100 100     106 if ($out[$i+1] eq 'long' or $out[$i+1] eq 'double') {
1217 10         27 $out[$i] .= '_' . $out[$i + 1];
1218 10         20 splice @out, $i + 1, 1;
1219             }
1220             } elsif ($out[$i] eq 'unsigned') {
1221 1 50 33     8 if ($out[$i+1] eq 'short' or $out[$i+1] eq 'long') {
1222 1         4 $out[$i] .= '_' . $out[$i + 1];
1223 1         4 splice @out, $i + 1, 1;
1224 1 50       6 if ($out[$i+1] eq 'long') {
1225 0         0 $out[$i] .= '_long';
1226 0         0 splice @out, $i + 1, 1;
1227             }
1228             }
1229             } elsif ($out[$i] eq ':' and $out[$i+1] eq ':') {
1230             # remove "::"
1231             # except when inheriting from an absolute qualified superclass
1232             # such as: valuetype vt : ::absolute::qualified::superclass {
1233             # ...
1234             # };
1235             # here, we need to preserve the first ':' as inheritance intro
1236 0 0 0     0 unless ($i < $#out - 1 && $out[$i+2] eq ':') {
1237 0         0 splice @out, $i, 2;
1238 0 0       0 if ($i > 0) {
1239 0         0 my $prev = $out[$i - 1];
1240 0 0 0     0 if ($prev =~ /\w$/ and !exists($keywords{$prev})) {
1241 0 0       0 if ($out[$i - 1] eq 'CORBA') {
1242 0         0 $out[$i - 1] = $out[$i]; # discard CORBA namespace
1243             } else {
1244 0         0 $out[$i - 1] .= '::' . $out[$i];
1245             }
1246 0         0 splice @out, $i--, 1;
1247             }
1248             }
1249             }
1250             # } elsif ($out[$i] eq '@' and $out[$i+1] =~ /^\w/) {
1251             # # Put annotation '@' together with its identifier
1252             # $out[$i] .= $out[$i + 1];
1253             # splice @out, $i + 1, 1;
1254             } elsif ($out[$i] eq '-' and $out[$i+1] =~ /^\d/) {
1255 0 0 0     0 if ($i == 0 || $out[$i-1] eq '(' || $out[$i-1] eq '='
      0        
      0        
1256             || $in_preprocessor) {
1257 0         0 $out[$i] .= $out[$i + 1];
1258 0         0 splice @out, $i + 1, 1;
1259             }
1260             }
1261             # Restore floating point scientific notation (e.g. 10.0e-3)
1262 951 0 0     2262 if ($out[$i] =~ /^[\-\d][\d\.]*e$/i and
      33        
1263             $out[$i+1] eq '+' || $out[$i+1] eq '-') {
1264 0         0 $out[$i] .= $out[$i + 1] . $out[$i + 2];
1265 0         0 splice @out, $i + 1, 2;
1266             }
1267             }
1268             # Bounded strings are special-cased:
1269             # compress the notation "string" into one element
1270 224         512 for ($i = 0; $i < $#out - 1; $i++) {
1271 727 50 33     1686 if ($out[$i] =~ /^w?string$/
      66        
1272             and $out[$i+1] eq '<' && $out[$i+3] eq '>') {
1273 0         0 my $bound = $out[$i+2];
1274 0         0 $out[$i] .= '<' . $bound . '>';
1275 0         0 splice @out, $i + 1, 3;
1276             }
1277             }
1278 224         947 @out;
1279             }
1280              
1281              
1282             sub is_elementary_type {
1283             # Returns the type index of an elementary type,
1284             # or 0 if the type is not elementary.
1285 17     17 1 38 my $tdesc = shift; # argument: a type descriptor
1286 17 50       36 unless (defined $tdesc) {
1287 0         0 error("CORBA::IDLtree::is_elementary_type called on undefined tdesc"
1288             . Carp::longmess());
1289 0         0 return 0;
1290             }
1291 17         22 my $recurse_into_typedef = 0; # optional argument
1292 17 50       35 if (@_) {
1293 0         0 $recurse_into_typedef = shift;
1294             }
1295 17         26 my $rv = 0;
1296 17 50 33     63 if ($tdesc >= BOOLEAN && $tdesc <= ANY) {
    0 0        
      0        
1297             # For our purposes, sequences, bounded strings, enums, structs, and
1298             # unions do not count as elementary types. They are represented as a
1299             # further node, i.e. the argument to is_elementary_type is not a
1300             # numeric constant but instead contains a reference to the defining
1301             # node.
1302 17         29 $rv = $tdesc;
1303             } elsif ($recurse_into_typedef && isnode($tdesc) &&
1304             $$tdesc[TYPE] == TYPEDEF) {
1305 0         0 my @origtype_and_dim = @{$$tdesc[SUBORDINATES]};
  0         0  
1306 0         0 my $dimref = $origtype_and_dim[1];
1307 0 0 0     0 unless ($dimref && @{$dimref}) {
  0         0  
1308 0         0 $rv = is_elementary_type($origtype_and_dim[0], 1);
1309             }
1310             }
1311 17         43 $rv;
1312             }
1313              
1314              
1315             sub predef_type {
1316 221     221 1 300 my $idltype = shift;
1317 221         288 my $i;
1318 221         450 for ($i = 1; $i <= $#predef_types; $i++) {
1319 6515 100       12720 if ($idltype eq $predef_types[$i]) {
1320 111 50 33     237 if ($string_bound and $idltype =~ /^w?string$/) {
1321 0         0 info("bounding $idltype to $string_bound");
1322 0         0 $idltype .= "<$string_bound>";
1323             } else {
1324 111         219 return $i;
1325             }
1326             }
1327             }
1328 110 50       259 if ($idltype =~ /^(w?string)\s*<(\d+)\s*>/) {
1329 0         0 my $type;
1330 0 0       0 $type = ($1 eq "wstring" ? BOUNDED_WSTRING : BOUNDED_STRING);
1331 0         0 my $bound = $2;
1332 0         0 return [ $type, $bound, 0, 0, 0, curr_scope ];
1333             }
1334 110         184 0;
1335             }
1336              
1337              
1338             sub is_valid_identifier {
1339 185     185 1 245 my $name = shift;
1340 185 50 33     840 if ($name !~ /^[a-z_:]/i || ($name =~ /^_/ && !$leading_underscore_allowed)) {
      33        
1341 0         0 return 0; # illegal first character
1342             }
1343 185         545 $name !~ /[^a-z0-9_:\.]/i
1344             }
1345              
1346             sub check_name {
1347 169     169 0 250 my $name = shift;
1348 169         229 my $msg = "name";
1349 169 100       342 if (@_) {
1350 81         126 $msg = shift;
1351             }
1352 169 50       308 unless (is_valid_identifier($name)) {
1353 0 0       0 unless ($name =~ /^w?string<.*>$/) {
1354 0         0 error "illegal $msg: $name";
1355             }
1356             }
1357 169 50       489 if (exists $keywords{lc($name)}) {
1358 0 0       0 if ($permissive) {
1359 0         0 info "WARNING: illegal $msg: '$name' is an IDL keyword";
1360             } else {
1361 0         0 error "illegal $msg: '$name' is an IDL keyword";
1362             }
1363             }
1364             # according to spec, a leading underscore disables keyword check but
1365             # is not part of the identifier
1366 169 50       321 unless ($leading_underscore_allowed > 1) {
1367 169         287 $name =~ s/^_//;
1368             }
1369 169         307 return $name;
1370             }
1371              
1372             sub check_typename {
1373 13     13 0 36 my $name = shift;
1374 13         20 my $msg = "name";
1375 13 50       29 if (@_) {
1376 13         28 $msg = shift;
1377             }
1378 13 50       56 unless (is_valid_identifier($name)) {
1379 0 0       0 unless ($name =~ /^w?string<.*>$/) {
1380 0         0 error "illegal $msg: $name";
1381             }
1382             }
1383 13         34 my $pt = predef_type($name);
1384 13 50       44 if ((ref($pt) ? $pt->[0] : $pt) < TYPEDEF) {
    50          
1385             # elementary type => OK
1386 13         28 return $name;
1387             }
1388 0 0       0 if (exists $keywords{lc($name)}) {
1389 0 0       0 if ($permissive) {
1390 0         0 info "WARNING: illegal $msg: '$name' is an IDL keyword";
1391             } else {
1392 0         0 error "illegal $msg: '$name' is an IDL keyword";
1393             }
1394             }
1395             # according to spec, a leading underscore disables keyword check but
1396             # is not part of the identifier
1397 0         0 $name =~ s/^_//;
1398 0         0 return $name;
1399             }
1400              
1401             my @scopestack = ();
1402             # The scope stack. Elements in this stack are references to
1403             # MODULE or INTERFACE nodes.
1404              
1405             sub curr_scope {
1406 145 100   145 0 489 ($#scopestack < 0 ? 0 : $scopestack[$#scopestack]);
1407             }
1408              
1409             sub annotation {
1410 183     183 0 256 my $retval = 0;
1411 183 50       351 if (@annotations) {
1412 0         0 $retval = [ @annotations ];
1413 0         0 @annotations = ();
1414             }
1415 183         417 return $retval;
1416             }
1417              
1418             sub comment {
1419 236     236 0 300 my $cmnt = 0;
1420 236 50       439 if (@post_comment) {
1421 0         0 $cmnt = [ $line_number_of_post_comment, [ @post_comment ] ];
1422 0         0 @post_comment = ();
1423 0         0 $line_number_of_post_comment = 0;
1424             }
1425 236         388 return $cmnt;
1426             }
1427              
1428              
1429             sub parse_sequence {
1430 8     8 0 15 my ($argref, $symroot) = @_;
1431 8 50       12 if (shift @{$argref} ne '<') {
  8         20  
1432 0         0 error "expecting '<'";
1433 0         0 return 0;
1434             }
1435 8         14 my $nxtarg = shift @{$argref};
  8         13  
1436 8         15 my $type = predef_type $nxtarg;
1437 8 100       32 if (! $type) {
    50          
1438 3         9 $type = find_node_i($nxtarg, $symroot);
1439 3 50       10 if (! $type) {
1440 0         0 error "unknown sequence type";
1441 0         0 return 0;
1442             }
1443             } elsif ($type == SEQUENCE) {
1444 0         0 $type = parse_sequence($argref, $symroot);
1445             }
1446 8         13 my $bound = 0;
1447 8         12 $nxtarg = shift @{$argref};
  8         15  
1448 8 100       19 if ($nxtarg eq ',') {
1449 2         4 $bound = shift @{$argref};
  2         5  
1450 2         3 $nxtarg = shift @{$argref};
  2         3  
1451             }
1452 8 50       20 if ($nxtarg ne '>') {
1453 0         0 error "expecting '<'";
1454 0         0 return 0;
1455             }
1456 8         19 return [SEQUENCE, $bound, $type, annotation, comment, curr_scope];
1457             }
1458              
1459              
1460             sub parse_type {
1461 88     88 0 178 my ($typename, $argref, $symtreeref) = @_;
1462 88         142 my $type;
1463 88 50       262 if ($typename eq 'fixed') {
    50          
    100          
1464 0 0       0 if (shift @{$argref} ne '<') {
  0         0  
1465 0         0 error "expecting '<' after 'fixed'";
1466 0         0 return 0;
1467             }
1468 0         0 my $digits = shift @{$argref};
  0         0  
1469 0 0       0 if ($digits =~ /\D/) {
1470 0         0 error "digit number in 'fixed' must be constant";
1471 0         0 return 0;
1472             }
1473 0 0       0 if (shift @{$argref} ne ',') {
  0         0  
1474 0         0 error "expecting comma in 'fixed'";
1475 0         0 return 0;
1476             }
1477 0         0 my $scale = shift @{$argref};
  0         0  
1478 0 0       0 if ($scale =~ /\D/) {
1479 0         0 error "scale number in 'fixed' must be constant";
1480 0         0 return 0;
1481             }
1482 0 0       0 if (shift @{$argref} ne '>') {
  0         0  
1483 0         0 error "expecting '>' at end of 'fixed'";
1484 0         0 return 0;
1485             }
1486 0         0 my @digits_and_scale = ($digits, $scale);
1487 0         0 $type = [ FIXED, "", \@digits_and_scale, annotation, comment, curr_scope ];
1488             } elsif ($typename =~ /^(w?string)<([\w:]+)>$/) { # bounded string
1489 0         0 my $t;
1490 0 0       0 $t = ($1 eq "wstring" ? BOUNDED_WSTRING : BOUNDED_STRING);
1491 0         0 my $bound = $2;
1492 0 0       0 if ($bound !~ /^\d/) {
1493 0         0 my $boundtype = find_node_i($bound, $symtreeref);
1494 0 0       0 if (isnode $boundtype) {
1495 0         0 my @node = @{$boundtype};
  0         0  
1496 0 0       0 if ($node[TYPE] == CONST) {
1497 0         0 my($basetype, $expr_ref) = @{$node[SUBORDINATES]};
  0         0  
1498 0         0 my @expr = @{$expr_ref};
  0         0  
1499 0 0 0     0 if (scalar(@expr) > 1 or $expr[0] !~ /^\d/) {
1500 0         0 error("string bound expressions"
1501             . " are not yet implemented");
1502             }
1503 0         0 $bound = $expr[0];
1504             } else {
1505 0         0 error "illegal type for string bound";
1506             }
1507             } else {
1508 0         0 error "Cannot resolve string bound";
1509             }
1510             }
1511 0         0 $type = [ $t, $bound, 0, annotation, comment, curr_scope ];
1512             } elsif ($typename eq 'sequence') {
1513 8         16 $type = parse_sequence($argref, $symtreeref);
1514             } else {
1515 80         164 $type = find_node_i($typename, $symtreeref);
1516             }
1517 88         208 $type;
1518             }
1519              
1520              
1521             sub parse_members {
1522             # params: \@symbols, \@arg, $structref_or_vt_access
1523             # If the structref_or_vt_access is a reference then we
1524             # assume to be parsing a struct and the member data are stored
1525             # in the list referenced by $structref.
1526             # If the structref_or_vt_access is not a reference then we
1527             # assume to be parsing a valuetype state member. In that case
1528             # $structref_or_vt_access contains the value &PRIVATE or
1529             # &PUBLIC indicating the access of the state member.
1530             # The valuetype member is directly added to @$symtreeref.
1531             # returns: -1 for error;
1532             # 0 for success with enclosing scope still open;
1533             # 1 for success with enclosing scope closed (i.e. seen '};')
1534 30     30 0 60 my($symtreeref, $argref, $structref_or_vt_access, $comment) = @_;
1535 30         55 my @arg = @{$argref};
  30         67  
1536 30         52 my $structref = 0;
1537 30         44 my $value_member_flag = 0;
1538 30 50       64 if (ref $structref_or_vt_access) {
1539 30         43 $structref = $structref_or_vt_access;
1540             } else {
1541 0         0 $value_member_flag = $structref_or_vt_access;
1542             }
1543 30         58 while (@arg) { # We're up here for a TYPE name
1544 30         50 my $first_thing = shift @arg; # but it could also be '}'
1545 30 50       60 if ($first_thing eq '}') {
1546 0         0 return 1; # return value signals closing of scope.
1547             }
1548 30         64 my $component_type = parse_type($first_thing, \@arg, $symtreeref);
1549 30 50       72 if (! $component_type) {
1550 0         0 error "unknown type $first_thing";
1551 0         0 return -1; # return value signals error.
1552             }
1553 30 50       57 if (in_annotation_def()) {
1554 0         0 my $component_name = shift @arg;
1555 0         0 my $default;
1556 0 0       0 if (@arg) {
1557 0         0 my $next = shift @arg;
1558 0 0       0 if ($next eq 'default') {
1559 0         0 $default = shift @arg;
1560             }
1561             }
1562 0         0 push @{$structref}, [ $component_type, $component_name, $default ];
  0         0  
1563 0 0       0 if (@arg) {
1564 0         0 my $next = shift @arg;
1565 0 0       0 unless ($next eq ';') {
1566 0         0 error("parse_members($first_thing) : found '$next' (expecting ';')");
1567 0         0 return -1;
1568             }
1569             }
1570 0         0 next;
1571             }
1572 30 50       79 if (! is_type($component_type)) {
1573 0         0 error "$first_thing is not a type";
1574 0         0 return -1;
1575             }
1576 30         68 while (@arg) { # We're here for VARIABLE name(s)
1577 30         51 my $component_name = shift @arg;
1578 30 50       62 last if ($component_name eq '}');
1579 30         59 $component_name = check_name($component_name);
1580 30         55 my @dimensions = ();
1581 30         49 my $nxtarg = "";
1582 30         68 while (@arg) { # We're here for a variable's DIMENSIONS
1583 13         21 $nxtarg = shift @arg;
1584 13 50 33     65 if ($nxtarg eq '[') {
    50          
1585 0         0 my $dim = shift @arg;
1586 0 0       0 if (shift @arg ne ']') {
1587 0         0 error "expecting ']'";
1588 0         0 return -1;
1589             }
1590 0         0 push @dimensions, $dim;
1591             } elsif ($nxtarg eq ',' || $nxtarg eq ';') {
1592 13         21 last;
1593             } else {
1594 0         0 error "component declaration syntax error";
1595 0         0 return -1;
1596             }
1597             }
1598 30         44 my $dimref = 0;
1599 30 50       58 if (@dimensions) {
1600 0         0 $dimref = [ @dimensions ];
1601 0 0       0 unless ($permissive) {
1602 0         0 info "$component_name : array members are DEPRECATED";
1603             }
1604             }
1605             # check for duplicate component names
1606 30         54 my $name_found = "";
1607 30 50       52 if ($value_member_flag) {
1608 0         0 for (@$symtreeref) {
1609 0         0 my $type = $_->[TYPE];
1610 0 0 0     0 if (isnode($type) && $type->[NAME] eq $component_name) {
1611 0         0 $name_found = $component_name;
1612 0         0 last;
1613             }
1614             }
1615             } else {
1616 30         61 for (@$structref) {
1617 113 100       197 next unless ref $_;
1618 98 100 100     252 next if $_->[TYPE] == CASE || $_->[TYPE] == DEFAULT;
1619 45 50 33     152 if ($_->[TYPE] != REMARK && $_->[NAME] eq $component_name) {
1620 0         0 $name_found = $component_name;
1621 0         0 last;
1622             }
1623             }
1624             }
1625 30 50       55 if ($name_found) {
1626 0         0 error "duplicate component name $name_found";
1627 0         0 return -1;
1628             }
1629 30 100       56 unless (defined $comment) {
1630 15         29 $comment = comment();
1631             }
1632 30         64 my $member_node = [ $component_type, $component_name, $dimref,
1633             annotation(), $comment ];
1634 30 50       59 if ($value_member_flag) {
1635 0         0 push @{$symtreeref}, [ $value_member_flag, $member_node ];
  0         0  
1636             } else {
1637 30         37 push @{$structref}, $member_node;
  30         51  
1638             }
1639 30 100       112 last if ($nxtarg eq ';');
1640             }
1641             }
1642             0 # return value signals success with scope still open.
1643 30         97 }
1644              
1645              
1646             my @prev_symroots = ();
1647             # Stack of the roots of previously constructed symtrees.
1648             # Used by find_node_i() for identifying symbols.
1649             # Elements are added to/removed from the front of this,
1650             # i.e. using unshift/shift (as opposed to push/pop.)
1651              
1652             my @fh = qw/ IN0 IN1 IN2 IN3 IN4 IN5 IN6 IN7 IN8 IN9/;
1653             # Input file handles (constants)
1654              
1655             # Cache of previously parsed includefiles
1656             my $includecache = new CORBA::IDLtree::Cache();
1657             my $did_emucppmsg = 0; # auxiliary to sub emucppmsg
1658              
1659             my @struct = (); # Temporary storage for struct/union/exception/
1660             # @annotation definition members.
1661             my @typestack = (); # For struct/union/exception, typestack, namestack, and
1662             my @namestack = (); # cmntstack move in parallel.
1663             # For valuetypes, only typestack is used.
1664             # For annotation definitions, @struct is flushed to
1665             # @annoDefs.
1666              
1667             my @annostack = (); # The annotation stack stores the concrete annotations
1668             # given on a struct/union/exception/valuetype declaration, e.g.
1669             # @external
1670             # struct mystruct {
1671             # ...
1672             # };
1673             # It is needed because the node is not constructed until the end of the
1674             # structure declaration, and members may have own annotations which would
1675             # overwrite the annotations of the type.
1676              
1677             my @cmntstack = (); # The comment stack stores a trailing comment on the
1678             # struct/union/exception declaration line, e.g.
1679             # struct mystruct { // This comment is stored in @cmntstack.
1680             # ...
1681             # };
1682             # It is needed because the node is not constructed until the end of the
1683             # structure declaration, and members may have trailing comments which
1684             # would overwrite the single post_comment buffer.
1685              
1686             sub in_annotation_def() {
1687 166   66 166 0 427 return (@typestack && $typestack[$#typestack] == ANNOTATION);
1688             }
1689              
1690             sub set_verbose {
1691 0 0   0 1 0 if (@_) {
1692 0         0 $verbose = shift;
1693             } else {
1694 0         0 $verbose = 1;
1695             }
1696             }
1697              
1698             sub emucppmsg {
1699 1 50 33 1 0 10 if (! $did_emucppmsg && $verbose) {
1700 0         0 print "// using preprocessor emulation\n";
1701 0         0 $did_emucppmsg = 1;
1702             }
1703             }
1704              
1705             sub use_system_preprocessor {
1706 0     0 0 0 $emucpp = 0;
1707             }
1708              
1709             sub eval_preproc_expr {
1710 0     0 0 0 my @arg = @_;
1711 0         0 my $symbol = shift @arg;
1712 0 0       0 if ($symbol eq 'defined') {
    0          
    0          
1713 0         0 shift @arg; # discard open-paren
1714 0         0 $symbol = shift @arg;
1715 0 0       0 if ($#arg > 0) { # there's more than the closing-paren
1716 0         0 error "warning: #if not yet fully implemented\n";
1717             }
1718 0         0 return ($symbol =~ /^\d/);
1719             } elsif ($symbol =~ /^[A-z]/) {
1720             # NB: sub idlsplit has already done symbol substitution
1721 0         0 error "built-in preprocessor does not know how to interpret $symbol";
1722 0         0 return 0;
1723             } elsif ($symbol !~ /^\d+$/) {
1724 0         0 error "warning: #if expressions not yet implemented\n";
1725             }
1726             $symbol
1727 0         0 }
1728              
1729             sub skip_input {
1730 0     0 0 0 my $count = 0;
1731 0         0 my $in = $fh[$#infilename];
1732 0         0 while (<$in>) {
1733 0 0       0 next unless (/^\s*#/);
1734 0         0 my @arg = idlsplit($_);
1735 0         0 my $kw = shift @arg;
1736             # print (join ('|', @arg) . "\n");
1737 0         0 my $directive = shift @arg;
1738 0 0       0 if ($count == 0) {
1739 0 0 0     0 if ($directive eq 'else' || $directive eq 'endif') {
1740 0         0 return;
1741             }
1742 0 0       0 if ($directive eq 'elif') {
1743 0 0       0 if (eval_preproc_expr @arg) {
1744 0         0 return;
1745             }
1746 0         0 next;
1747             }
1748             }
1749 0 0 0     0 if ($directive eq 'if' ||
    0 0        
1750             $directive eq 'ifdef' ||
1751             $directive eq 'ifndef') {
1752 0         0 $count++;
1753             } elsif ($directive eq 'endif') {
1754 0         0 $count--;
1755 0 0       0 if ($count <= 0) {
1756 0         0 return;
1757             }
1758             }
1759             # For #elif, the count remains the same.
1760             }
1761 0         0 error "skip_input: fell off end of file";
1762             }
1763              
1764              
1765             sub get_items { # returns empty list for end-of-file or fatal error
1766 229     229 0 357 my $in = shift;
1767 229         362 my @items = ();
1768 229 100       468 if (@global_items) {
1769 2         5 @items = @global_items;
1770 2         5 @global_items = ();
1771 2         8 return @items;
1772             }
1773 227         312 my $first = 1;
1774 227         307 my $in_comment = 0;
1775 227         293 my $seen_token = 0;
1776 227         303 my $line = "";
1777 227         307 $starting_line_number_of_remark = 0;
1778 227         330 $line_number_of_post_comment = 0;
1779 227         270 my $l;
1780 227         308 @remark = ();
1781 227         308 @post_comment = ();
1782             line:
1783 227         1270 while (($l = <$in>)) {
1784 303         537 $line_number[$currfile]++;
1785 303         448 chomp $l;
1786 303         546 $l =~ s/\r//g; # zap DOS line ending
1787 303 50       586 unless ($locale) {
1788 303         1234 $l =~ s/[^[:print:]]/ /g;
1789             }
1790 303 100       1010 if ($l =~ /^\s*$/) { # empty
1791 43 50       85 if ($in_comment) {
1792 0 0       0 if ($seen_token) {
1793 0         0 push @post_comment, "";
1794             } else {
1795 0         0 push @remark, "";
1796             }
1797             }
1798 43         404 next;
1799             }
1800 260 100       493 if ($in_comment) {
1801 21 50       40 if ($l =~ /\/\*/) {
1802 0         0 info "warning: nested comments not supported!";
1803             }
1804 21 100       37 if ($l =~ /\*\//) {
1805 1         9 my $cpos = index($l, "*/");
1806 1         2 my $cmnt = substr($l, 0, $cpos);
1807 1         5 $cmnt =~ s/\s*$//;
1808 1         2 $l = substr($l, $cpos+2);
1809             #my $cmnt = $l;
1810             #$cmnt =~ s/\s*\*\/.*$//;
1811 1 50       11 if ($seen_token) {
1812 0         0 push @post_comment, $cmnt;
1813             } else {
1814 1         4 push @remark, $cmnt;
1815             }
1816 1         2 $in_comment = 0; # end of multi-line comment
1817             #$l =~ s/^.*\*\///;
1818 1 50       14 if ($seen_token) {
1819 0 0       0 if ($l !~ /^\s*$/) {
1820 0         0 error "unsupported comment/token combination";
1821             }
1822 0         0 last;
1823             }
1824 1 50       19 next if ($l =~ /^\s*$/);
1825             } else {
1826 20 50       31 if ($seen_token) {
1827 0         0 push @post_comment, $l;
1828             } else {
1829 20         38 push @remark, $l;
1830             }
1831 20         53 next;
1832             }
1833             }
1834 239 100       522 if ($l =~ /^\s*\/\/(.*)/) { # single-line comment by itself
1835 10         39 my $cmnt = $1;
1836 10 100       28 unless (@remark) {
1837 6         15 $starting_line_number_of_remark = $line_number[$currfile];
1838             }
1839 10         22 push @remark, $cmnt;
1840 10         95 next;
1841             }
1842 229         502 while ($l =~ /\/\*/) { # start of multi-line comment
1843 1         5 my $cpos = index($l, "/*");
1844 1         4 my $cend = index($l, "*/", $cpos+2);
1845 1         2 my $cmnt = "";
1846 1 50       4 if ($cend > 0) {
1847             # start and end on the same line
1848             # extract comment block
1849 0         0 $cmnt = substr($l, $cpos+2, $cend-$cpos-2);
1850 0         0 substr($l, $cpos, $cend-$cpos+2) = "";
1851             } else {
1852             # only start found, extract comment part
1853 1         12 $cmnt = substr($l, $cpos+2);
1854 1         5 $l = substr($l, 0, $cpos);
1855             # comment continues on next line
1856 1         2 $in_comment = 1;
1857             }
1858 1         4 $cmnt =~ s/\s*$//;
1859             #my $cmnt = $l;
1860             #$cmnt =~ s/^.*\/\*//; # remove comment start and stuff before
1861             #$cmnt =~ s/\*\/.*$//; # remove comment end and stuff after (if any)
1862             #if ($l =~ /\*\//) {
1863             # # remove comment
1864             # $l =~ s/\/\*.*\*\///;
1865             #} else {
1866             # $in_comment = 1;
1867             # # remove start of comment
1868             # $l =~ s/\/\*.*$//;
1869             #}
1870 1 50       5 unless (defined($line_number[$currfile])) {
1871 0         0 die("CORBA::IDLtree::get_items line_number of $currfile undefined (1)\n");
1872             }
1873 1 50       31 if ($l =~ /^\s*$/) { # If there is nothing else on the line
1874 1         3 push @remark, $cmnt; # then declare it a prefixed comment;
1875 1         3 $starting_line_number_of_remark = $line_number[$currfile];
1876 1         35 next line;
1877             } else {
1878 0         0 push @post_comment, $cmnt; # else declare it a trailing comment.
1879 0         0 $line_number_of_post_comment = $line_number[$currfile];
1880             }
1881             }
1882 228 50       492 if ($l =~ /\/\/(.*)$/) {
1883 0         0 my $cmnt = $1;
1884 0 0       0 unless ($cmnt =~ /^\s*$/) {
1885 0 0       0 unless (defined($line_number[$currfile])) {
1886 0         0 die("CORBA::IDLtree::get_items line_number of $currfile undefined (2)\n");
1887             }
1888 0         0 $line_number_of_post_comment = $line_number[$currfile];
1889 0         0 push @post_comment, $cmnt;
1890             }
1891 0         0 $l =~ s/\/\/.*$//; # discard trailing comment
1892             }
1893 228         634 $l =~ s/^\s+//; # discard leading whitespace
1894 228         579 $l =~ s/\s+$//; # discard trailing whitespace
1895 228 100       400 if ($first) {
1896 224         316 $first = 0;
1897             } else {
1898 4         10 $l = " $l";
1899             }
1900 228         449 $line .= $l;
1901 228 100 33     1449 if (($line =~ /^#/) # preprocessor directive
      66        
      66        
1902             or ($line =~ /\@/ and $line !~ /\@annotation\b/) # annotation
1903             or ($line =~ /[;,":{]$/)) { #" characters declared to denote eol.
1904 224         338 $seen_token = 1;
1905 224 50       455 last unless $in_comment;
1906             }
1907             }
1908 227 50       363 if ($in_comment) {
1909 0         0 error "end of file reached while comment still open";
1910 0         0 $in_comment = 0;
1911             }
1912 227 100       395 if (! $line) {
1913 3         16 return ();
1914             }
1915             # sub idlsplit also does preprocessor symbol substitution.
1916 224         457 my @arg = idlsplit($line);
1917 224         551 my @tmp = @arg;
1918 224 100       477 if ($tmp[0] ne '#') {
1919 222         1499 return @arg;
1920             }
1921 2         4 shift @tmp; # discard '#'
1922 2         12 my $directive = shift @tmp;
1923 2 50 33     47 if ($directive eq 'if' || $directive eq 'elif') {
    50          
    50          
    50          
    50          
    50          
    50          
1924 0         0 emucppmsg;
1925 0 0       0 skip_input unless (eval_preproc_expr @tmp);
1926 0         0 @arg = get_items($in);
1927             } elsif ($directive eq 'ifdef') {
1928 0         0 my $symbol = shift @tmp;
1929 0         0 emucppmsg;
1930 0 0       0 skip_input unless ($symbol =~ /^\d/);
1931 0         0 @arg = get_items($in);
1932             } elsif ($directive eq 'ifndef') {
1933 0         0 my $symbol = shift @tmp;
1934 0         0 emucppmsg;
1935 0 0       0 skip_input if ($symbol =~ /^\d/);
1936 0         0 @arg = get_items($in);
1937             } elsif ($directive eq 'define') {
1938 0         0 my $symbol = shift @tmp;
1939 0         0 my $value = 1;
1940 0         0 emucppmsg;
1941 0 0       0 if (@tmp) {
1942 0         0 $value = join(' ', @tmp);
1943 0 0       0 print("// defining $symbol as $value\n") if ($verbose);
1944             }
1945 0 0 0     0 if (exists $active_defines{$symbol} and
1946             $value ne $active_defines{$symbol}) {
1947 0 0       0 if ($cache_trees) {
1948 0         0 error("Redefinition of $symbol may lead to " .
1949             "erroneous trees when cache_trees is used");
1950             } else {
1951 0         0 info "info: redefining $symbol";
1952             }
1953             }
1954 0         0 $active_defines{$symbol} = $value;
1955 0         0 @arg = get_items($in);
1956             } elsif ($directive eq 'undef') {
1957 0         0 my $symbol = shift @tmp;
1958 0         0 emucppmsg;
1959 0 0       0 if (exists $active_defines{$symbol}) {
1960 0 0       0 if ($cache_trees) {
1961 0         0 error("#undef of $symbol may lead to " .
1962             "erroneous trees when cache_trees is used");
1963             }
1964 0         0 delete $active_defines{$symbol};
1965             }
1966 0         0 @arg = get_items($in);
1967             } elsif ($directive eq 'else') {
1968             # We only get to see the #else here if we were not skipping
1969             # the preceding #if or #elif.
1970 0         0 skip_input;
1971 0         0 @arg = get_items($in);
1972             } elsif ($directive eq 'endif') {
1973 0         0 @arg = get_items($in);
1974             }
1975 2         13 @arg;
1976             }
1977              
1978             sub unget_items {
1979 2     2 0 6 @global_items = @_;
1980             }
1981              
1982              
1983             sub isname {
1984 0     0 0 0 my $txt = shift;
1985 0         0 $txt =~ /^[A-Za-z]/
1986             }
1987              
1988             # check if the path given by the strings in @parts leads to
1989             # the given $scope starting at the referring scope $refscope.
1990             # return the absolute path if the path is OK, undef otherwise
1991             sub check_scope {
1992 2     2 0 5 my ($scope, $refscope, @parts) = @_;
1993 2         8 my $p = join("::", get_scope($scope));
1994              
1995 2 50       8 if (@parts == 0) {
1996             # special case: both elements are in top-level scope
1997 2 50 33     9 if (! $refscope && $p eq "") {
1998 0         0 return $p;
1999             }
2000             # no scope given, must be in the referring scope or
2001             # its ancestors
2002 2         7 for (my $s = $refscope; $s; $s = $s->[SCOPEREF]) {
2003 2 50       8 return $p if $scope == $s;
2004             }
2005 0         0 return undef;
2006             }
2007              
2008             # the specified parts must either be an absolute
2009             # path to $scope or start at the referring scope
2010             # or one of its ancestors
2011             # (a path starting with "::" is always absolute,
2012             # though the parser can't handle this right now!)
2013 0         0 my $is_abs = $parts[0] eq "";
2014 0 0       0 shift @parts if $is_abs;
2015              
2016             # check absolute path first
2017 0 0       0 return $p if join("::", @parts) eq $p;
2018              
2019 0 0       0 unless ($is_abs) {
2020             # try possible "relative" paths
2021 0         0 for (my @anc = get_scope($refscope); @anc; pop @anc) {
2022 0 0       0 return $p if join("::", (@anc, @parts)) eq $p;
2023             }
2024             }
2025              
2026             # wrong scope given
2027 0         0 return undef;
2028             }
2029              
2030             # In the SUBORDINATES of ENUM there may be remark nodes or trailing comment
2031             # nodes. Function enum_literals returns the net literals stripped of any
2032             # remark nodes or trailing comment info.
2033             # It expects the SUBORDINATES of the enum node as the argument and
2034             # returns the extracted list.
2035             sub enum_literals {
2036 6     6 1 19 my ($enum_subordinate) = @_;
2037 6 50       30 unless (ref $enum_subordinate) {
2038             # Possible misuse - generate warning?
2039 0         0 return ();
2040             }
2041 6         19 my @values = ();
2042 6         10 foreach my $elem (@{$enum_subordinate}) {
  6         17  
2043 21 50       52 unless (ref($elem) eq "ARRAY") {
2044 0         0 Carp::cluck("enum_literals: IDLtree internal error" .
2045             "- enum subordinates should be ARRAY\n");
2046 0         0 last;
2047             }
2048 21 50       60 $elem->[0] =~ /^\d/ and next; # remark node
2049 21         42 push @values, $elem->[0];
2050             }
2051 6         20 return @values;
2052             }
2053              
2054             # check if the given literal correctly identifies
2055             # an enumeration member of the enumeration type $type
2056             # as referenced from the referring scope $refscope
2057             # if $refscope is not specified, curr_scope() is used.
2058             sub check_enum_literal {
2059 2     2 0 7 my ($type, $literal, $refscope) = @_;
2060              
2061 2 50       9 $refscope = curr_scope() unless defined $refscope;
2062              
2063 2         5 my $found = 0;
2064 2         6 my @p = (split "::", $literal);
2065 2         5 my $e = pop @p;
2066 2         8 my $s = check_scope($type->[SCOPEREF], $refscope, @p);
2067 2 50       6 if (defined $s) {
2068 2         16 foreach (enum_literals($type->[SUBORDINATES])) {
2069 4 100       11 $found = 1, last if $_ eq $e;
2070             }
2071             }
2072 2         8 return $found;
2073             }
2074              
2075             sub check_union_case {
2076 17     17 0 28 my ($symroot, $known_cases, $case) = @_;
2077              
2078 17         26 my $i = 0;
2079 17 100       39 if ($case->[TYPE] == DEFAULT) {
2080 1         3 foreach (@$known_cases) {
2081 3 100       8 next if $i++ == 0;
2082 2 50       7 if ($_->[TYPE] == DEFAULT) {
2083 0         0 error "duplicate default label";
2084 0         0 return undef;
2085             }
2086             }
2087             } else {
2088 16         35 my $type = root_type($known_cases->[TYPE]);
2089 16         24 my $c;
2090 16 100       33 if (is_a($type, ENUM)) {
    100          
    100          
2091             # check if value is part of enumeration
2092 2         3 foreach $c (@{$case->[SUBORDINATES]}) {
  2         6  
2093 2 50       7 unless (check_enum_literal($type, $c)) {
2094 0         0 error "invalid case value $c";
2095 0         0 return undef;
2096             }
2097             }
2098             } elsif (is_a($type, BOOLEAN)) {
2099 2         3 foreach $c (@{$case->[SUBORDINATES]}) {
  2         7  
2100 2 50 66     10 unless ($c eq "TRUE" || $c eq "FALSE") {
2101 0         0 error "invalid case value $c";
2102 0         0 return undef;
2103             }
2104             }
2105             } elsif (is_a($type, CHAR)) {
2106 4         5 foreach $c (@{$case->[SUBORDINATES]}) {
  4         10  
2107 5 50 33     30 unless ($c =~ /^'.*'$/ || $c =~ /^\d+$/) {
2108 0         0 error "invalid case value $c";
2109 0         0 return undef;
2110             }
2111             }
2112             } else {
2113             # must be integer
2114 8         12 foreach $c (@{$case->[SUBORDINATES]}) {
  8         19  
2115 9 50       78 unless ($c =~ /^[-+]?\d+$/) {
2116 0         0 my $resolved_const = get_numeric($symroot, $c, curr_scope);
2117 0 0       0 unless ($resolved_const =~ /^[-+]?\d+$/) {
2118 0         0 error "invalid case value $c";
2119 0         0 return undef;
2120             }
2121             }
2122             }
2123             }
2124 16         44 foreach (@$known_cases) {
2125 86 100       155 next if $i++ == 0;
2126 70 100       128 next unless $_->[TYPE] == CASE;
2127 29         34 foreach (@{$_->[SUBORDINATES]}) {
  29         50  
2128 34         41 foreach $c (@{$case->[SUBORDINATES]}) {
  34         52  
2129 38 50       82 if ($c eq $_) {
2130 0         0 error "duplicate case label $c";
2131 0         0 return undef;
2132             }
2133             }
2134             }
2135             }
2136             }
2137 17         28 return 1;
2138             }
2139              
2140              
2141             sub Parse_File {
2142 2     2 1 685 my $filename = shift;
2143 2 50       10 if ($cache_trees) {
2144 0         0 my $incfile_contents_ref = $includecache->get($filename);
2145 0 0       0 if ($incfile_contents_ref) {
2146 0         0 bless($incfile_contents_ref, "CORBA::IDLtree");
2147 0         0 return $incfile_contents_ref;
2148             }
2149             } else {
2150 2         13 $includecache->clear(); # Roots of previously parsed includefiles
2151 2         8 $findnode_cache->clear(); # Flush the find_node_i() cache
2152             }
2153 2         11 $global_idlfile = $filename;
2154 2         7 @infilename = (); # infilename and line_number move in parallel.
2155 2         4 @line_number = ();
2156 2         5 $n_errors = 0; # auxiliary to sub error
2157 2         5 @remark = (); # Auxiliary to comment processing
2158 2         5 @post_comment = (); # Auxiliary to comment processing
2159 2         5 $abstract = 0;
2160 2         5 $currfile = -1;
2161 2         5 $did_emucppmsg = 0; # auxiliary to sub emucppmsg
2162 2         7 @scopestack = ();
2163 2         6 @prev_symroots = ();
2164 2         8 %active_defines = %defines;
2165 2 100       7 unless ($locale_was_determined) {
2166 1         4 foreach my $env ('LANG', 'LOCALE', 'LC_ALL') {
2167 3 50       8 if (exists $ENV{$env}) {
2168 0         0 my $lang = $ENV{$env};
2169 0 0 0     0 if ($lang && $lang ne "C") {
2170 0         0 $locale = $lang;
2171 0         0 last;
2172             }
2173             }
2174             }
2175 1         5 $locale_was_determined = 1;
2176             }
2177 2         9 my $res = Parse_File_i($filename);
2178 2 50       8 if ($cache_statistics) {
2179 0         0 print "Node cache: ".$findnode_cache->ratio()."\n";
2180 0         0 print "Include cache: ".$includecache->ratio()."\n";
2181             }
2182 2 50 33     34 if ($res && !@$res) {
    50          
2183 0         0 warn "Warning: CORBA::IDLtree::Parse_File: $filename is empty\n";
2184 0         0 $res = 0;
2185             } elsif ($cache_trees) {
2186             # Put the main unit in the include cache, too
2187             # (it may be #included by a subsequent main file.)
2188 0         0 $includecache->add($filename, $res);
2189             }
2190 2         33 return $res;
2191             }
2192              
2193             # the function changes the passed in struct node
2194             # into an "equivalent" valuetype
2195             sub convert_to_valuetype {
2196 0     0 0 0 my ($node) = @_;
2197              
2198             # just in case...
2199 0 0       0 return unless $node->[TYPE] == STRUCT;
2200              
2201             # first, convert the members to public state members
2202 0         0 foreach (@{$node->[SUBORDINATES]}) {
  0         0  
2203 0         0 my $membertype = $_->[TYPE];
2204 0 0       0 if ($membertype == REMARK) {
2205 0         0 $_ = [ 0, $_ ];
2206             } else {
2207 0 0 0     0 if (isnode($membertype) &&
      0        
2208             ($membertype->[TYPE] == CORBA::IDLtree::BOUNDED_STRING ||
2209             $membertype->[TYPE] == CORBA::IDLtree::BOUNDED_WSTRING)) {
2210             # Ad hoc member type declaration shall have its own
2211             # enclosing valuetype as the SCOPEREF
2212 0         0 $membertype->[SCOPEREF] = $node;
2213             }
2214 0         0 $_ = [ PUBLIC, $_ ];
2215             }
2216             }
2217             # now, change the subordinates:
2218 0         0 $node->[SUBORDINATES] = [
2219             0, # abstract
2220             [ 0, # is_truncatable
2221             0 # ancestors
2222             ],
2223             $node->[SUBORDINATES], # members
2224             ];
2225             # change the type into VALUETYPE
2226 0         0 $node->[TYPE] = VALUETYPE;
2227             }
2228              
2229             # Parses an annotation application.
2230             # Parsing of an @annotation definition is not done here.
2231             # Expects the annotation name as the first parameter and possible
2232             # annotation arguments by an array reference in the second parameter.
2233             # Is expected to be called not too long after get_items (the sub may find
2234             # that too many args were returned by get_items and may therefore call
2235             # unget_items).
2236             # Returns 1 on success, 0 on error.
2237             sub parse_annotation_app {
2238 0     0 0 0 my ($ann, $argref) = @_;
2239 0         0 my ($index) = grep { $annoDefs[$_]->[0] eq $ann } 0..$#annoDefs;
  0         0  
2240 0 0       0 unless (defined $index) {
2241 0         0 error "Unknown annotation \@$ann";
2242 0         0 return 0;
2243             }
2244 0         0 my @adef = @{$annoDefs[$index]};
  0         0  
2245 0         0 shift @adef; # discard name
2246 0         0 my @anode = ($index);
2247 0         0 my @anargs;
2248 0 0       0 if (@adef) {
2249 0         0 @anargs = map { undef } @adef;
  0         0  
2250 0 0 0     0 unless ($argref && @$argref) {
2251 0         0 error("parse_annotation_app: internal error"
2252             . " (get_items returned insufficient args)\n"
2253             . Carp::longmess());
2254 0         0 return 0;
2255             }
2256             }
2257 0 0 0     0 if (@$argref && $argref->[0] eq '(') {
2258 0 0       0 unless (@adef) {
2259 0         0 error "Annotation \@$ann does not require arguments";
2260 0         0 return 0;
2261             }
2262 0         0 shift @$argref;
2263 0         0 my $closing_parenth_seen = 0;
2264 0         0 my $upcounter = 0;
2265 0         0 while (@$argref) {
2266 0         0 my $val = shift @$argref;
2267 0 0       0 if ($val eq ')') {
2268 0         0 $closing_parenth_seen = 1;
2269 0         0 last;
2270             }
2271 0 0       0 $val eq ',' and next;
2272 0 0 0     0 if ($val =~ /^[a-z]/i and $argref->[0] eq '=') {
2273 0         0 my $parname = $val;
2274 0         0 shift @$argref;
2275 0 0       0 unless (@$argref) {
2276 0         0 error "Annotation \@$ann no value given for $parname";
2277 0         0 return 0;
2278             }
2279 0         0 my $param_index = undef;
2280 0         0 for (my $ai = 0; $ai < scalar(@adef); ++$ai) {
2281 0 0       0 if ($adef[$ai]->[1] eq $parname) {
2282 0         0 $param_index = $ai;
2283 0         0 last;
2284             }
2285             }
2286 0 0       0 unless (defined $param_index) {
2287 0         0 error "Annotation \@$ann unknown parameter given: $parname";
2288 0         0 return 0;
2289             }
2290 0         0 $val = shift @$argref;
2291 0         0 my $type = $adef[$param_index]->[0];
2292 0 0       0 if (exists $annoEnum{$type}) {
2293 0         0 my $enumvalues = $annoEnum{$type};
2294 0 0       0 unless (grep { $_ eq $val } @{$enumvalues}) {
  0         0  
  0         0  
2295 0         0 error "Annotation \@$ann parameter $parname illegal value: $val";
2296 0         0 return 0;
2297             }
2298             }
2299 0         0 $anargs[$param_index] = $val;
2300             } else {
2301 0         0 my $type = $adef[$upcounter]->[0];
2302 0 0       0 if (exists $annoEnum{$type}) {
2303 0         0 my $enumvalues = $annoEnum{$type};
2304 0 0       0 unless (grep { $_ eq $val } @{$enumvalues}) {
  0         0  
  0         0  
2305 0         0 error("Annotation \@$ann parameter " . $adef[$upcounter]->[1]
2306             . " illegal value: $val");
2307 0         0 return 0;
2308             }
2309             }
2310 0         0 $anargs[$upcounter] = $val;
2311 0         0 ++$upcounter;
2312             }
2313             }
2314 0 0       0 unless ($closing_parenth_seen) {
2315 0         0 error "Annotation \@$ann syntax error: require closing parenthesis";
2316 0         0 return 0;
2317             }
2318             }
2319 0 0       0 if (@adef) {
2320 0         0 for (my $i = 0; $i < scalar(@adef); ++$i) {
2321 0 0       0 unless (defined $anargs[$i]) {
2322 0         0 my $parname = $adef[$i]->[1];
2323 0         0 my $default = $adef[$i]->[2];
2324 0 0       0 if (defined $default) {
2325 0         0 $anargs[$i] = $default;
2326 0         0 info("Annotation \@$ann using default value for parameter $parname");
2327             } else {
2328 0         0 error("Annotation \@$ann no value given for parameter $parname");
2329 0         0 return 0;
2330             }
2331             }
2332             }
2333 0         0 push @anode, @anargs;
2334             }
2335 0         0 push @annotations, [ @anode ];
2336 0 0       0 if (@$argref) {
2337 0         0 unget_items(@$argref);
2338             }
2339             }
2340              
2341             # Push subordinate - just like perl push() but hides
2342             # different structure of valuetype subordinates.
2343             sub pushsub {
2344 97     97 0 174 my($symbols, $noderef, $in_valuetype) = @_;
2345 97 50 33     225 if ($in_valuetype && !$vt2struct) {
2346 0         0 push @$symbols, [ 0, $noderef ];
2347             } else {
2348 97         349 push @$symbols, $noderef;
2349             }
2350             }
2351              
2352             sub Parse_File_i {
2353 22     22 0 67 my ($file, $input_filehandle, $symb, $in_valuetype) = @_;
2354              
2355             # my $file = shift;
2356             # my $input_filehandle = "";
2357             # if (@_) {
2358             # $input_filehandle = shift; # internal use only
2359             # }
2360              
2361 22         65 my @vt_inheritance = (0, 0);
2362 22         33 my $in;
2363 22         41 my $custom = 0;
2364 22         42 $abstract = 0;
2365 22 100       82 if ($file) { # Process a new file (or includefile if cpp emulated)
    50          
2366 3 50       85 -e "$file" or abort("Cannot find file $file");
2367             # remove "//" from filename to ensure correct filename match
2368 3         42 $file =~ s:/+:/:g;
2369 3         24 push @infilename, $file;
2370 3         7 push @line_number, 0;
2371 3         7 $currfile = $#infilename;
2372 3         10 $in = $fh[$currfile];
2373 3         7 my $cppcmd = "";
2374 3 50       10 unless ($emucpp) {
2375             # Try to find and run the C preprocessor.
2376             # Use `cpp' in preference of `cc -E' if the former can be found.
2377             # If no preprocessor can be found, we will try to emulate it.
2378 0 0       0 if (locate_executable 'cpp') {
    0          
2379 0         0 $cppcmd = 'cpp';
2380             } elsif (locate_executable 'gcc') {
2381 0         0 $cppcmd = 'gcc -E -x c++';
2382             } else {
2383 0         0 $emucpp = 1;
2384             }
2385             }
2386 3 50       11 if ($emucpp) {
2387 3 50       139 open($in, $file) or abort("Cannot open file $file");
2388             } else {
2389 0         0 my $cpp_args = "";
2390 0         0 foreach (keys %defines) {
2391 0         0 $cpp_args .= " -D$_=" . $defines{$_};
2392             }
2393 0         0 foreach (@include_path) {
2394 0         0 $cpp_args .= " -I$_";
2395             }
2396 0 0       0 open($in, "$cppcmd $cpp_args $file |")
2397             or abort("Cannot open file $file");
2398             }
2399 3 50       18 print("// processing: $file\n") if ($verbose);
2400             } elsif ("$input_filehandle") {
2401 19         33 $in = $input_filehandle; # Process a module or interface within file.
2402             }
2403              
2404             # symbol tree that will be constructed here
2405 22         40 my $symbols;
2406 22 100       46 if ($symb) {
2407 20         38 $symbols = $symb;
2408             } else {
2409 2         5 $symbols = [ ];
2410             }
2411             # @struct, @typestack, @namestack, @cmntstack used to be my() vars here.
2412             # They were moved to the global scope in order to support #include
2413             # statements at arbitrary locations.
2414 22         30 my @arg;
2415 22         54 while ((@arg = get_items($in))) {
2416 201 50       451 if ($verbose > 1) {
2417 0         0 my $line = join(' ', @arg);
2418 0         0 print "IDLtree: parsing $line\n"; # "super verbose mode"
2419             }
2420 201 50 33     421 if ($enable_comments && @remark) {
2421 0         0 my $remnode_ref = [ REMARK, $starting_line_number_of_remark, [ @remark ], 0, 0, curr_scope ];
2422 0 0       0 if (@typestack) {
2423 0         0 push @struct, $remnode_ref;
2424             } else {
2425 0         0 pushsub($symbols, $remnode_ref, $in_valuetype);
2426             }
2427 0         0 @remark = ();
2428 0         0 $starting_line_number_of_remark = 0;
2429             }
2430 201         375 my $cmnt = comment;
2431 201         419 KEYWORD:
2432             my $kw = shift @arg;
2433 201 100 33     1852 if ($kw eq '#') {
    50 100        
    100 100        
    100          
    100          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
2434 2         5 my $directive = shift @arg;
2435 2 100 0     11 if ($directive eq 'pragma') {
    50 0        
    0 0        
    0 0        
      0        
      0        
      0        
2436 1         2 my @pragma_node;
2437 1         2 $directive = shift @arg;
2438 1 50       5 if ($directive eq 'prefix') {
    0          
    0          
2439 1         2 my $prefix = shift @arg;
2440 1 50       4 if (substr($prefix, 0, 1) ne '"') {
2441 0         0 error "prefix should be given in double quotes";
2442             } else {
2443 1         3 $prefix = substr($prefix, 1);
2444 1 50       5 if (substr($prefix, length($prefix) - 1) ne '"') {
2445 0         0 error "missing closing quote";
2446             } else {
2447 1         3 $prefix = substr($prefix, 0, length($prefix) - 1);
2448             }
2449             }
2450 1         5 @pragma_node = (PRAGMA_PREFIX, $prefix, 0, 0, $cmnt,
2451             curr_scope);
2452             } elsif ($directive eq 'version') {
2453 0         0 my $unitname = shift @arg;
2454 0         0 my $vstring = shift @arg;
2455 0         0 @pragma_node = (PRAGMA_VERSION, $unitname, $vstring, 0, $cmnt,
2456             curr_scope);
2457             } elsif (uc($directive) eq 'ID') {
2458 0         0 my $unitname = shift @arg;
2459 0         0 my $idstring = shift @arg;
2460 0         0 @pragma_node = (PRAGMA_ID, $unitname, $idstring, 0, $cmnt,
2461             curr_scope);
2462             } else {
2463 0         0 my $rest_of_line = join ' ', @arg;
2464 0         0 @pragma_node = (PRAGMA, $directive, $rest_of_line, 0, $cmnt,
2465             curr_scope);
2466             }
2467 1         10 push @$symbols, \@pragma_node;
2468             } elsif ($directive eq 'include') {
2469 1         3 my $filename = shift @arg;
2470 1         7 emucppmsg;
2471 1 50       5 if ($filename eq '<') {
2472             # try to convert filename in '<...>' to "normal" string
2473 0         0 $filename = '"';
2474 0         0 my $t;
2475 0         0 while (@arg) {
2476 0         0 $t = shift @arg;
2477 0 0       0 if ($t eq '>') {
2478 0         0 $filename .= '"';
2479 0         0 last;
2480             }
2481 0         0 $filename .= $t;
2482             }
2483             }
2484 1 50       5 if (substr($filename, 0, 1) ne '"') {
2485 0         0 error "include file name should be given in double quotes or < >";
2486             } else {
2487 1         3 $filename = substr($filename, 1);
2488 1 50       5 if (substr($filename, length($filename) - 1) ne '"') {
2489 0         0 error "missing closing quote";
2490             } else {
2491 1         4 $filename = substr($filename, 0, length($filename) - 1);
2492             }
2493             }
2494 1         3 $filename =~ s/\\/\//g; # convert DOS path to Unix
2495 1         2 my $found = 1;
2496 1 50       47 if (not -e "$filename") {
2497 1         4 $found = 0;
2498 1         5 foreach (@include_path) {
2499 1 50       19 if (-e "$_/$filename") {
2500 1         6 $filename = "$_/$filename";
2501 1         11 $found = 1;
2502 1         5 last;
2503             }
2504             }
2505             }
2506 1 50       3 $found or abort ("Cannot find file $filename");
2507 1         3 my $in_global_scope = 1;
2508 1 50 33     24 if (@typestack || @scopestack) {
2509 0         0 $in_global_scope = 0;
2510             }
2511 1         8 my $include_node = [ INCFILE, $filename, 0, 0, $cmnt, curr_scope ];
2512 1         7 my $incfile_contents_ref = $includecache->get($filename);
2513 1 50       4 if ($incfile_contents_ref) {
2514 0         0 $include_node->[SUBORDINATES] = $incfile_contents_ref;
2515 0         0 push @$symbols, $include_node;
2516             } else {
2517 1         3 unshift @prev_symroots, $symbols;
2518 1         2 $incfile_contents_ref = [];
2519 1         4 $include_node->[SUBORDINATES] = $incfile_contents_ref;
2520             # add include file node early so that find_node_i
2521             # can use it
2522             # @todo THIS CANNOT WORK - $symbols is local in this sub
2523             # and is not passed into the Parse_file_i call.
2524 1         3 push @$symbols, $include_node;
2525 1 50       6 Parse_File_i($filename, undef, $incfile_contents_ref)
2526             or abort("can't go on, sorry");
2527 1         6 $includecache->add($filename, $incfile_contents_ref);
2528 1         2 shift @prev_symroots;
2529 1 50       4 pop @scopestack if $in_global_scope;
2530             }
2531 1 50       4 unless ($in_global_scope) {
2532             # Quick fix for Ada code generator:
2533             # replace the INCFILE node by the symbols if not
2534             # in global scope
2535 0         0 pop @$symbols;
2536 0         0 foreach (@$incfile_contents_ref) {
2537 0         0 push @$symbols, $_
2538             }
2539             }
2540             } elsif ($directive =~ /^\d/) {
2541             # It's an output from the C preprocessor generated for
2542             # a "#include"
2543 0         0 my $linenum = $directive;
2544 0         0 $linenum =~ s/^(\d+)/$1/;
2545 0         0 my $filename = shift @arg;
2546 0         0 $filename = substr($filename, 1, length($filename) - 2);
2547 0         0 $filename =~ s@^./@@;
2548 0         0 $filename =~ s:/+:/:g;
2549 0 0       0 if ($filename eq $infilename[$currfile]) {
2550 0         0 $line_number[$currfile] = $linenum;
2551 0         0 next;
2552             }
2553 0         0 my $seen_file = 0;
2554 0         0 my $i;
2555 0         0 for ($i = 0; $i <= $#infilename; $i++) {
2556 0 0       0 if ($filename eq $infilename[$i]) {
2557 0         0 $currfile = $i;
2558 0         0 $line_number[$currfile] = $linenum;
2559 0         0 $seen_file = 1;
2560 0         0 last;
2561             }
2562             }
2563 0 0       0 last if ($seen_file);
2564 0         0 push @infilename, $filename;
2565 0         0 $currfile = $#infilename;
2566 0         0 $line_number[$currfile] = $linenum;
2567 0         0 unshift @prev_symroots, $symbols;
2568 0         0 my $incfile_contents_ref = Parse_File_i("", $in, []);
2569 0 0       0 $incfile_contents_ref or abort("can't go on, sorry");
2570 0         0 shift @prev_symroots;
2571 0         0 my @include_node = (INCFILE, $filename,
2572             $incfile_contents_ref, 0, $cmnt, curr_scope);
2573 0         0 push @$symbols, \@include_node;
2574             } elsif ($directive eq 'if' ||
2575             $directive eq 'ifdef' ||
2576             $directive eq 'ifndef' ||
2577             $directive eq 'elif' ||
2578             $directive eq 'else' ||
2579             $directive eq 'endif' ||
2580             $directive eq 'define' ||
2581             $directive eq 'undef') {
2582             # Sanity check only -
2583             # preprocessor conditions and definitions were already handled
2584             # in sub get_items and do not appear here.
2585 0         0 error "internal error - seen #$directive in Parse_File_i\n";
2586             } else {
2587 0         0 info "ignoring preprocessor directive \#$directive\n";
2588             }
2589 2         8 next;
2590              
2591             } elsif ($kw eq '@') {
2592 0         0 my $ann = shift @arg;
2593 0 0       0 if ($ann eq "annotation") {
2594 0         0 my $name = check_name(shift @arg);
2595 0         0 push @typestack, ANNOTATION;
2596 0         0 push @namestack, $name;
2597 0 0       0 if (shift @arg ne '{') {
2598 0         0 error "expecting '{'";
2599 0         0 next;
2600             }
2601 0         0 @struct = ();
2602 0 0       0 if (@arg) {
2603 0 0 0     0 if ($arg[0] eq '}' or
2604             parse_members($symbols, \@arg, \@struct) == 1) {
2605             # end of type declaration was encountered
2606 0         0 push @annoDefs, [ $name, @struct ];
2607 0         0 pop @namestack;
2608 0         0 pop @typestack;
2609 0         0 @struct = ();
2610             }
2611             }
2612             } else {
2613 0         0 parse_annotation_app($ann, \@arg);
2614             }
2615 0         0 next;
2616              
2617             } elsif ($kw eq '}') {
2618 34 50       102 if (shift @arg ne ';') {
2619 0         0 error "missing ';'";
2620             }
2621 34 100       78 unless (@typestack) { # must be closing of module, interface, or valuetype
2622 19 50       47 if (@scopestack) {
2623 19         28 pop @scopestack;
2624             } else {
2625 0         0 error('unexpected };');
2626             }
2627 19         72 return $symbols;
2628             }
2629 15 50       33 if ($in_valuetype) {
2630 0         0 error "Parse_File_i internal: in_valuetype true on non empty typestack";
2631 0         0 return $symbols;
2632             }
2633 15 50       29 if (in_annotation_def()) {
2634 0         0 pop @typestack;
2635 0         0 my $anno = pop @namestack;
2636 0         0 push @annoDefs, [ $anno, @struct ];
2637 0         0 @struct = ();
2638 0         0 next;
2639             }
2640 15         31 my $type = pop @typestack;
2641 15         36 my $name = pop @namestack;
2642 15         26 my $anno = pop @annostack;
2643 15         26 my $cmnt = pop @cmntstack;
2644 15 100 100     48 if ($type == UNION && is_a($struct[0], ENUM)) {
2645             # For the case of ENUM, check that all enum values
2646             # are covered by CASEs.
2647 1         4 my $maybe_dflt = $struct[$#struct - 1];
2648             # No check possible if DEFAULT given.
2649 1 50       6 unless ($$maybe_dflt[TYPE] == DEFAULT) {
2650 1         3 my $enumtype = root_type($struct[0]);
2651 1         4 my %lits_given = ();
2652 1         2 my $umember;
2653 1         4 foreach $umember (@struct) {
2654 5 100       32 if ($$umember[TYPE] == CASE) {
2655 2         3 foreach (@{$$umember[SUBORDINATES]}) {
  2         5  
2656 2         3 my $stripped_lit = $_;
2657 2         7 $stripped_lit =~ s/^.*:://;
2658 2         8 $lits_given{$stripped_lit} = 1;
2659             }
2660             }
2661             }
2662 1         4 foreach (enum_literals($enumtype->[SUBORDINATES])) {
2663 3 50       12 my $lit = ref($_) ? $_->[0] : $_;
2664 3 100 66     19 if (defined($lit) && !defined($lits_given{$lit})) {
2665 1         24 info("$name info: no case for enum value "
2666             . $lit . " given");
2667             }
2668             }
2669             }
2670             }
2671 15         55 my @structnode = ($type, $name, [ @struct ], $anno, $cmnt, curr_scope);
2672 15 50 33     41 if ($struct2vt && $type == STRUCT) {
2673 0         0 convert_to_valuetype(\@structnode);
2674             }
2675 15         53 pushsub($symbols, [ @structnode ]);
2676 15         34 @struct = ();
2677 15         38 next;
2678              
2679             } elsif ($kw eq 'module') {
2680 3         10 my $name = check_name(shift @arg);
2681 3 50       19 error("expecting '{'") if (shift(@arg) ne '{');
2682 3         7 my $subord;
2683 3         9 my $fullname = join('::', scope_names(), $name);
2684 3         8 my $scope = curr_scope();
2685             # See if the full name is in the findnode_cache already.
2686             # This can happen in the case of reopened modules.
2687             # The findnode_cache always contains the most recently seen
2688             # reopening of a module.
2689 3         10 my $module = $findnode_cache->get($fullname);
2690 3 100       7 if ($module) {
2691             # If this is a reopening then our SCOPEREF shall point to
2692             # the previous opening of the module.
2693 1         2 $scope = $module;
2694             }
2695 3         7 $subord = [ ];
2696 3         9 $module = [ MODULE, $name, $subord, annotation, $cmnt, $scope ];
2697 3         15 $findnode_cache->add($fullname, $module);
2698 3         8 push @$symbols, $module;
2699 3         21 unshift @prev_symroots, $symbols;
2700 3         7 push @scopestack, $module;
2701 3 50       84 Parse_File_i("", $in, $subord) or abort("can't go on, sorry");
2702 3 50       9 unless ($module) {
2703 0         0 shift @prev_symroots;
2704             }
2705 3         9 next;
2706              
2707             } elsif ($kw eq 'interface') {
2708 30         78 my $name = check_name(shift @arg);
2709 30         64 my $symnode = [ INTERFACE, $name, undef, annotation, $cmnt, curr_scope ];
2710 30         57 my $lasttok = pop(@arg);
2711 30 100       98 if ($lasttok eq ';') {
    50          
2712 14         23 $symnode->[TYPE] = INTERFACE_FWD;
2713 14         22 push @$symbols, $symnode;
2714 14         29 next;
2715             } elsif ($lasttok ne '{') {
2716 0         0 push @arg, $lasttok;
2717 0 0       0 if (! require_end_of_stmt(\@arg, $in, '{')) {
2718 0         0 error "expecting '{'";
2719 0         0 next;
2720             }
2721             }
2722 16         40 my $fwd = find_node_i($name, $symbols);
2723 16 100       46 if ($fwd) {
2724 14 50       46 if ($$fwd[TYPE] != INTERFACE_FWD) {
2725 0         0 error "type of interface fwd decl is not INTERFACE_FWD";
2726 0         0 next;
2727             }
2728 14         27 $$fwd[SUBORDINATES] = $symnode;
2729             }
2730 16         27 my @ancestor = ();
2731 16 100       38 if (@arg) { # we have ancestors
2732 5 50       32 if (shift @arg ne ':') {
    50          
2733 0         0 error "syntax error";
2734 0         0 next;
2735             } elsif (! @arg) {
2736 0         0 error "expecting ancestor(s)";
2737 0         0 next;
2738             }
2739 5         12 my $i; # "use strict" wants it.
2740 5         19 for ($i = 0; $i < @arg; $i++) {
2741 6         20 my $name = check_name($arg[$i], "ancestor name");
2742 6         19 my $ancestor_node = find_node_i($name, $symbols);
2743 6 50       16 if (! $ancestor_node) {
2744 0         0 error "could not find ancestor $name";
2745 0         0 next;
2746             }
2747 6         15 push @ancestor, $ancestor_node;
2748 6 100       22 if ($i < $#arg) {
2749 1 50       39 if ($arg[++$i] ne ',') {
2750 0         0 error "expecting comma separated list of ancestors";
2751 0         0 last;
2752             }
2753             }
2754             }
2755             }
2756 16         42 my $subord = [ \@ancestor, $abstract ];
2757 16         33 $symnode->[SUBORDINATES] = $subord;
2758 16         26 push @$symbols, $symnode;
2759 16         35 unshift @prev_symroots, $symbols;
2760 16         24 push @scopestack, $symnode;
2761 16 50       106 Parse_File_i("", $in, $subord)
2762             or abort("can't go on, sorry");
2763 16         45 shift @prev_symroots;
2764 16         25 $abstract = 0;
2765 16         61 next;
2766              
2767             } elsif ($kw eq 'local') {
2768 0         0 $abstract = LOCAL;
2769 0         0 goto KEYWORD;
2770              
2771             } elsif ($kw eq 'abstract') {
2772 0         0 $abstract = ABSTRACT;
2773 0         0 goto KEYWORD;
2774              
2775             } elsif ($kw eq 'custom') {
2776 0         0 $custom = 1;
2777 0         0 goto KEYWORD;
2778              
2779             } elsif ($kw eq 'valuetype') {
2780 0         0 my $name = check_name(shift @arg);
2781 0         0 my $anno = annotation;
2782 0         0 my $symnode = [ VALUETYPE, $name, 0, $anno, $cmnt, curr_scope ];
2783 0 0       0 if ($vt2struct) {
2784 0         0 push @typestack, STRUCT;
2785 0         0 push @namestack, $name;
2786 0         0 push @annostack, $anno;
2787 0         0 push @cmntstack, $cmnt;
2788 0         0 @struct = ();
2789             } else {
2790 0         0 push @$symbols, $symnode;
2791             }
2792 0         0 my $nxttok = shift @arg;
2793 0 0       0 if ($nxttok eq ';') {
2794 0         0 $symnode->[TYPE] = VALUETYPE_FWD;
2795             # Aliased to $symbols[$#symbols]
2796 0         0 next;
2797             }
2798 0         0 my @ancestors = (); # do the inheritance jive
2799 0         0 my $seen_ancestors = 0;
2800 0 0       0 if ($nxttok eq ':') {
2801 0 0       0 if (($nxttok = shift @arg) eq 'truncatable') {
2802 0         0 $vt_inheritance[0] = 1;
2803 0         0 $nxttok = shift @arg;
2804             }
2805 0   0     0 while (isname($nxttok) and $nxttok ne 'supports') {
2806 0         0 my $anc_type = find_node_i($nxttok, $symbols);
2807 0 0 0     0 if (! isnode($anc_type)
      0        
      0        
2808             || ($$anc_type[TYPE] != VALUETYPE &&
2809             $$anc_type[TYPE] != VALUETYPE_BOX &&
2810             $$anc_type[TYPE] != VALUETYPE_FWD)) {
2811 0         0 error "ancestor $nxttok must be valuetype";
2812             } else {
2813 0         0 push @ancestors, $anc_type;
2814             }
2815 0 0       0 last unless (($nxttok = shift @arg) eq ',');
2816 0         0 $nxttok = shift @arg;
2817             }
2818 0         0 $seen_ancestors = 1;
2819             }
2820 0 0       0 if ($nxttok eq 'supports') {
2821 0         0 while (isname($nxttok = shift @arg)) {
2822 0         0 my $anc_type = find_node_i($nxttok, $symbols);
2823 0 0 0     0 if (! $anc_type) {
    0 0        
2824 0         0 error "unknown ancestor $nxttok";
2825             } elsif (! isnode($anc_type)
2826             || $$anc_type[TYPE] != INTERFACE
2827             || $$anc_type[TYPE] != INTERFACE_FWD) {
2828 0         0 error "ancestor $nxttok must be interface";
2829             } else {
2830 0         0 push @ancestors, $anc_type;
2831             }
2832 0 0       0 last unless (($nxttok = shift @arg) eq ',');
2833             }
2834 0         0 $seen_ancestors = 1;
2835             }
2836 0 0       0 if ($seen_ancestors) {
    0          
    0          
2837 0 0       0 if ($nxttok ne '{') {
2838 0         0 error "expecting '{' at valuetype declaration";
2839             }
2840 0         0 $vt_inheritance[1] = [ @ancestors ];
2841             } elsif (isname $nxttok) {
2842             # suspect a value box
2843 0         0 my $type = parse_type($nxttok, \@arg, $symbols);
2844 0 0       0 if ($type) {
2845 0         0 $symnode->[TYPE] = VALUETYPE_BOX;
2846 0         0 $symnode->[SUBORDINATES] = $type;
2847             # Aliased to $symbols[$#symbols]
2848             } else {
2849 0         0 error "value box: unknown type $nxttok";
2850             }
2851 0         0 next;
2852             } elsif ($nxttok ne '{') {
2853 0         0 error "expecting '{' at valuetype declaration";
2854             }
2855 0         0 my $fwd = find_node_i($name, $symbols);
2856 0 0 0     0 if (ref($fwd) && $$fwd[TYPE] == VALUETYPE_FWD) {
2857 0         0 $$fwd[SUBORDINATES] = $symnode;
2858             }
2859              
2860 0 0       0 unless ($vt2struct) {
2861 0         0 my $declarations = [ ];
2862 0         0 my $obvsub = [ $abstract, [ @vt_inheritance ], $declarations ];
2863 0         0 $symnode->[SUBORDINATES] = $obvsub;
2864 0         0 unshift @prev_symroots, $symbols;
2865 0         0 push @scopestack, $symnode;
2866 0 0       0 Parse_File_i("", $in, $declarations, 1) or abort("can't go on, sorry");
2867             # The closing "};" was seen in Parse_File_i and @scopestack was popped there.
2868 0         0 shift @prev_symroots;
2869             }
2870 0         0 $abstract = 0;
2871 0         0 @vt_inheritance = (0, 0);
2872 0         0 next;
2873              
2874             } elsif ($kw eq 'public' or $kw eq 'private') {
2875 0 0       0 unless ($in_valuetype) {
2876 0         0 error "'$kw' is only permitted in valuetypes";
2877 0         0 next;
2878             }
2879 0 0       0 if ($abstract) {
2880 0         0 error "state members not permitted in abstract valuetype";
2881 0         0 next;
2882             }
2883 0 0       0 if ($vt2struct) {
2884 0 0       0 if (parse_members($symbols, \@arg, \@struct) == 1) {
2885             # end of type declaration was encountered
2886 0         0 my $type = pop @typestack;
2887 0         0 my $name = pop @namestack;
2888 0         0 my $initial_cmnt = pop @cmntstack;
2889 0         0 my @node = ($type, $name, [ @struct ], 0, $initial_cmnt, curr_scope);
2890 0         0 push @$symbols, [ @node ];
2891 0         0 @struct = ();
2892             }
2893             } else {
2894 0         0 my $vt_access;
2895 0 0       0 if ($kw eq 'public') {
2896 0         0 $vt_access = PUBLIC;
2897             } else {
2898 0         0 $vt_access = PRIVATE;
2899             }
2900 0 0       0 if (parse_members($symbols, \@arg, $vt_access, $cmnt) == 1) {
2901             # end of type declaration was encountered
2902 0 0       0 if (@scopestack) {
2903 0         0 pop @scopestack;
2904             } else {
2905 0         0 error "internal error - scopestack is empty";
2906             }
2907 0         0 return $symbols;
2908             }
2909             }
2910 0         0 next;
2911              
2912             } elsif ($kw eq 'struct' or $kw eq 'exception') {
2913 10         23 my $type;
2914 10 100       31 $type = ($kw eq 'struct' ? STRUCT : EXCEPTION);
2915 10         22 my $name = check_name(shift @arg);
2916 10         23 my $anno = annotation;
2917 10         20 push @typestack, $type;
2918 10         15 push @namestack, $name;
2919 10         16 push @annostack, $anno;
2920 10         15 push @cmntstack, $cmnt;
2921 10         15 @struct = ();
2922 10         18 my $nxt = shift @arg;
2923 10 50 66     44 if ($type == STRUCT && $nxt eq ':') {
2924 0         0 my $parent_type = shift @arg;
2925 0         0 my $parent = parse_type($parent_type, \@arg, $symbols);
2926 0 0 0     0 if (isnode($parent) && $parent->[TYPE] == STRUCT) {
2927 0         0 push @struct, $parent;
2928             } else {
2929 0         0 error "expecting a struct type as parent of $name";
2930             }
2931 0         0 $nxt = shift @arg;
2932             }
2933 10 50       25 if ($nxt ne '{') {
2934 0         0 error "expecting '{'";
2935 0         0 next;
2936             }
2937 10 50       22 if (@arg) {
2938 0 0 0     0 if ($arg[0] eq '}' or
2939             parse_members($symbols, \@arg, \@struct, $cmnt) == 1) {
2940             # end of type declaration was encountered
2941 0         0 my $node = [ $type, $name, [ @struct ], $anno, $cmnt, curr_scope ];
2942 0 0 0     0 if ($struct2vt && $type == STRUCT) {
2943 0         0 convert_to_valuetype($node);
2944             }
2945 0         0 push @$symbols, $node;
2946 0         0 pop @cmntstack;
2947 0         0 pop @annostack;
2948 0         0 pop @namestack;
2949 0         0 pop @typestack;
2950 0         0 @struct = ();
2951             }
2952             }
2953 10         23 next;
2954              
2955             } elsif ($kw eq 'union') {
2956 5         15 my $name = check_name(shift @arg, "type name");
2957 5         13 my $anno = annotation;
2958 5         11 push @typestack, UNION;
2959 5         8 push @namestack, $name;
2960 5         7 push @annostack, $anno;
2961 5         14 push @cmntstack, $cmnt;
2962 5 50       12 if (shift(@arg) ne 'switch') {
2963 0         0 error "union: expecting keyword 'switch'";
2964 0         0 next;
2965             }
2966 5 50       12 if (shift @arg ne '(') {
2967 0         0 error "expecting '('";
2968 0         0 next;
2969             }
2970 5         9 my $switchtypename = shift @arg;
2971 5         15 my $switchtype = find_node_i($switchtypename, $symbols);
2972 5 50 33     18 if (! $switchtype) {
    100          
    50          
2973 0         0 error "unknown type of switch variable";
2974 0         0 next;
2975             } elsif (isnode $switchtype) {
2976 1         3 my $typ = ${$switchtype}[TYPE];
  1         4  
2977 1 50 33     19 if ($typ < BOOLEAN ||
      33        
      33        
2978             ($typ > ULONG && $typ != ENUM && $typ != TYPEDEF)) {
2979 0         0 error "illegal switch variable type (node; $typ)";
2980 0         0 next;
2981             }
2982             } elsif ($switchtype < BOOLEAN || $switchtype > ULONGLONG) {
2983 0         0 error "illegal switch variable type ($switchtype)";
2984 0         0 next;
2985             }
2986 5 50       19 error("expecting ')'") if (shift @arg ne ')');
2987 5 50       16 error("expecting '{'") if (shift @arg ne '{');
2988 5 50       20 error("ignoring excess characters") if (@arg);
2989 5         13 @struct = ($switchtype);
2990 5         15 next;
2991              
2992             } elsif ($kw eq 'case' or $kw eq 'default') {
2993 17         26 my @node;
2994 17         24 my @casevals = ();
2995 17 100       33 if ($kw eq 'case') {
2996 16         34 while (@arg) {
2997 16         34 push @casevals, shift @arg;
2998 16 50       34 if (shift @arg ne ':') {
2999 0         0 error "expecting ':'";
3000 0         0 last;
3001             }
3002 16 100       33 last unless (@arg);
3003 12 50       29 last unless ($arg[0] eq 'case');
3004 0         0 shift @arg;
3005             }
3006 16 100       34 if (! @arg) {
3007             # Peek ahead at following lines. If they contain further
3008             # CASEs then append them to @casevals.
3009 4         11 while ((@arg = get_items($in))) {
3010 4         10 $kw = shift @arg;
3011 4 100       20 unless ($kw eq 'case') {
3012 2         5 unshift @arg, $kw;
3013 2         10 unget_items(@arg);
3014 2         5 @arg = ();
3015 2         3 last;
3016             }
3017 2 50       7 if ($arg[$#arg] eq ';') {
3018 2         5 pop @arg;
3019             }
3020 2         7 while (@arg) {
3021 2         4 push @casevals, shift @arg;
3022 2 50       7 if (shift @arg ne ':') {
3023 0         0 error "expecting ':'";
3024 0         0 last;
3025             }
3026 2 50       7 last unless (@arg);
3027 2 50       9 last unless ($arg[0] eq 'case');
3028 0         0 shift @arg;
3029             }
3030 2 50       6 last if (@arg);
3031             }
3032             }
3033 16         42 @node = (CASE, "", \@casevals);
3034             } else {
3035 1 50       47 if (shift @arg ne ':') {
3036 0         0 error "expecting ':'";
3037 0         0 next;
3038             }
3039 1         5 @node = (DEFAULT, "", 0);
3040             }
3041 17         49 check_union_case($symbols, \@struct, \@node);
3042 17         35 push @struct, \@node;
3043 17 100       40 if (@arg) {
3044 15 50       37 if (parse_members($symbols, \@arg, \@struct) == 1) {
3045             # end of type declaration was encountered
3046 0 0       0 if ($#typestack < 0) {
3047 0         0 error "internal error 1";
3048 0         0 next;
3049             }
3050 0         0 my $type = pop @typestack;
3051 0         0 my $name = pop @namestack;
3052 0         0 my $anno = pop @annostack;
3053 0         0 my $initial_cmnt = pop @cmntstack;
3054 0 0       0 if ($initial_cmnt) {
3055 0 0 0     0 if ($cmnt && $cmnt != $initial_cmnt) {
3056 0         0 push @{$initial_cmnt->[1]}, @{$cmnt->[1]};
  0         0  
  0         0  
3057             }
3058 0         0 $cmnt = $initial_cmnt;
3059             }
3060 0 0       0 if ($type != UNION) {
3061 0         0 error "internal error 2";
3062 0         0 next;
3063             }
3064 0         0 my @unionnode = ($type, $name, [ @struct ], $anno, $cmnt,
3065             curr_scope);
3066 0         0 push @$symbols, [ @unionnode ];
3067 0         0 @struct = ();
3068             }
3069             }
3070 17         50 next;
3071              
3072             } elsif ($kw eq 'enum') {
3073 3         11 my $typename = check_name(shift @arg, "type name");
3074 3 50       16 if (shift @arg ne '{') {
3075 0         0 error("expecting '{'");
3076 0         0 next;
3077             }
3078 3         8 my $anno = annotation;
3079 3         7 my @values = ();
3080 3 100       28 @arg = get_items($in) unless @arg;
3081 3         16 while (@arg) {
3082 12         20 my $lit = shift @arg;
3083 12 50       26 if (in_annotation_def()) {
3084 0 0       0 unless ($lit =~ /^\w+$/) {
3085 0         0 error("illegal enum value at $lit");
3086 0         0 $lit = check_name($lit);
3087             }
3088 0         0 push @values, $lit;
3089             } else {
3090 12 50 33     29 if ($enable_comments && @remark) {
3091 0         0 push @values, [ $starting_line_number_of_remark, [ @remark ]];
3092 0         0 $starting_line_number_of_remark = 0;
3093 0         0 @remark = ();
3094             }
3095 12 50       22 if ($lit eq '@') {
3096 0         0 my $annoName = shift @arg;
3097 0         0 parse_annotation_app($annoName, \@arg);
3098             } else {
3099 12         22 $lit = check_name($lit); # must be a literal
3100 12 50       42 unless ($lit =~ /^\w+$/) {
3101 0         0 last; # error message was already produced by sub check_name
3102             }
3103 12         27 push @values, [ $lit, annotation, comment ];
3104             }
3105             }
3106 12 50       25 if (@arg) {
3107 12         18 my $nxt = shift @arg;
3108 12 100       32 $nxt eq '}' and last;
3109 9 50       18 unless ($nxt eq ',') {
3110 0         0 error "syntax error at $nxt (expecting ',')";
3111 0         0 last;
3112             }
3113             }
3114             } continue {
3115 9 100       24 @arg = get_items($in) unless @arg;
3116             }
3117 3 50       13 if (in_annotation_def()) {
3118 0         0 $annoEnum{$typename} = [ @values ];
3119             } else {
3120 3         14 my $node = [ ENUM, $typename, [ @values ], $anno, $cmnt, curr_scope ];
3121 3         11 push @$symbols, $node;
3122             }
3123 3         10 next;
3124             }
3125              
3126 97 50       228 if (! require_end_of_stmt(\@arg, $in)) {
3127 0         0 error "statement not terminated";
3128 0         0 next;
3129             }
3130              
3131 97 50 100     700 if ($kw eq 'native') {
    100          
    100          
    100          
    100          
3132 0         0 my $name = check_name(shift @arg, "type name");
3133 0         0 my $node = [ NATIVE, $name, 0, annotation, $cmnt, curr_scope ];
3134 0         0 pushsub($symbols, $node, $in_valuetype);
3135              
3136             } elsif ($kw eq 'const') {
3137 12         24 my $type = shift @arg;
3138 12         16 my $name = shift @arg;
3139 12 50       33 if (shift(@arg) ne '=') {
3140 0         0 error "expecting '='";
3141 0         0 next;
3142             }
3143 12         28 my $typething = find_node_i($type, $symbols);
3144 12 50       33 unless ($typething) {
3145 0         0 error "unknown const type $type";
3146 0         0 next;
3147             }
3148             # Check basic validity of the RHS expression.
3149 12         24 foreach (@arg) {
3150 12 50 66     63 next if (/^\d/ or /^\.\d/ or /^-\d/); # numeric constant
      66        
3151 5 100 100     28 next if (/^'.*'$/ or /^".*"$/); # character or string
3152 3 50       9 next if is_valid_identifier $_; # identifier
3153             # Check against predefined operands.
3154 0         0 my $arg = $_;
3155 0         0 my @operands = ( '+', '-', '*', '/', '%', '<<', '>>', '~',
3156             '^', '|', '&', '!', '||', '&&', '==', '!=',
3157             '<', '>', '<=', '>=' );
3158 0         0 my $is_operand = 0;
3159 0         0 foreach (@operands) {
3160 0 0       0 if ($arg eq $_) {
3161 0         0 $is_operand = 1;
3162 0         0 last;
3163             }
3164             }
3165 0 0       0 next if $is_operand;
3166 0         0 error "unknown token in CONST: $arg";
3167             }
3168 12         39 my @tuple = ($typething, [ @arg ]);
3169 12 100       34 if (isnode $typething) {
3170 2         3 my $id = ${$typething}[TYPE];
  2         4  
3171 2 50 33     13 if ($id < ENUM || $id > TYPEDEF) {
3172 0         0 error "expecting type";
3173 0         0 next;
3174             }
3175             }
3176 12         26 my $node = [ CONST, $name, \@tuple, annotation, $cmnt, curr_scope ];
3177 12         27 pushsub($symbols, $node, $in_valuetype);
3178              
3179             } elsif ($kw eq 'typedef') {
3180 13         35 my $oldtype = check_typename(shift @arg, "name of original type");
3181             # TO BE DONE: oldtype is STRUCT or UNION
3182 13         31 my $existing_typenode = parse_type($oldtype, \@arg, $symbols);
3183 13 50       31 if (! $existing_typenode) {
3184 0         0 error "typedef: unknown type $oldtype";
3185 0         0 next;
3186             }
3187 13         29 my $newtype = check_name(shift @arg, "name of newly defined type");
3188 13         28 my @dimensions = ();
3189 13         30 while (@arg) {
3190 7 50       20 if (shift(@arg) ne '[') {
3191 0         0 error "expecting '['";
3192 0         0 last;
3193             }
3194 7         11 my $dim;
3195             my $token;
3196 7         17 while (@arg) {
3197 14         24 $token = shift(@arg);
3198 14 100       29 last if ($token eq ']');
3199 7 50       32 if ($dim) {
3200 0         0 $dim .= ' ';
3201             }
3202 7         18 $dim .= $token;
3203             }
3204 7 50       15 unless ($dim) {
3205 0         0 error "expecting dimension";
3206 0         0 last;
3207             }
3208 7 50       16 unless ($token eq ']') {
3209 0         0 error "expecting ']'";
3210 0         0 last;
3211             }
3212 7         16 push @dimensions, $dim;
3213             }
3214 13         37 my @subord = ($existing_typenode, [ @dimensions ]);
3215 13         35 my $node = [ TYPEDEF, $newtype, \@subord, annotation, $cmnt, curr_scope ];
3216 13         31 pushsub($symbols, $node, $in_valuetype);
3217              
3218             } elsif ($kw eq 'readonly' or $kw eq 'attribute') {
3219 3         8 my $readonly = 0;
3220 3 100       10 if ($kw eq 'readonly') {
3221 1 50       5 if (shift(@arg) ne 'attribute') {
3222 0         0 error "expecting keyword 'attribute'";
3223 0         0 next;
3224             }
3225 1         3 $readonly = 1;
3226             }
3227 3         6 my $typename = shift @arg;
3228 3         9 my $type = parse_type($typename, \@arg, $symbols);
3229 3 50       10 if (! $type) {
3230 0         0 error "unknown type $typename";
3231 0         0 next;
3232             }
3233 3         9 my @subord = ($readonly, $type);
3234 3         22 my $name = check_name(shift @arg);
3235 3         8 my $node = [ ATTRIBUTE, $name, \@subord, annotation, $cmnt, curr_scope ];
3236 3         10 pushsub($symbols, $node, $in_valuetype);
3237              
3238             } elsif (grep /\(/, @arg) { # Method declaration
3239 54         98 my $rettype;
3240             my @subord;
3241 54 100 33     171 if ($kw eq 'oneway') {
    100          
    50          
3242 5 50       20 if (shift(@arg) ne 'void') {
3243 0         0 error "expecting keyword 'void' after oneway";
3244 0         0 next;
3245             }
3246 5         10 $rettype = ONEWAY;
3247             } elsif ($kw eq 'void') {
3248 7         11 $rettype = VOID;
3249             } elsif ($in_valuetype and $kw eq 'factory') {
3250 0         0 $rettype = FACTORY;
3251             } else {
3252 42         92 $rettype = parse_type($kw, \@arg, $symbols);
3253 42 50       104 if (! $rettype) {
3254 0         0 error "unknown return type $kw";
3255 0         0 next;
3256             }
3257             }
3258 54         93 @subord = ($rettype);
3259 54         145 my $name = check_name(shift @arg, "method name");
3260 54 50       183 if (shift(@arg) ne '(') {
    50          
3261 0         0 error "expecting opening parenthesis";
3262 0         0 next;
3263             } elsif (pop(@arg) ne ')') {
3264 0         0 error "expecting closing parenthesis";
3265 0         0 next;
3266             }
3267 54         100 my @exception_list = ();
3268 54         75 my $expecting_exception_list = 0;
3269 54         114 while (@arg) {
3270 78         126 my $m = shift @arg;
3271 78         114 my $typename = shift @arg;
3272 78         124 my $pname = shift @arg;
3273 78 100       151 if ($m eq ')') {
3274 1 50       6 if ($typename ne 'raises') {
    50          
3275 0         0 error "expecting keyword 'raises'";
3276             } elsif ($pname ne '(') {
3277 0         0 error "expecting '(' after 'raises'";
3278             } else {
3279 1         2 $expecting_exception_list = 1;
3280             }
3281 1         2 last;
3282             }
3283 77 50       279 my $pmode = ($m eq 'in' ? &IN :
    100          
    100          
3284             $m eq 'out' ? &OUT :
3285             $m eq 'inout' ? &INOUT : 0);
3286 77 50       131 unless ($pmode) {
3287 0         0 error("$name parameter $pname : bad mode $m (expecting 'in', 'out', or 'inout')");
3288 0         0 last;
3289             }
3290 77 50 33     170 if ($rettype == FACTORY && $pmode != IN) {
3291 0         0 error("$name: FACTORY parameter $pname must have mode 'in'");
3292 0         0 last;
3293             }
3294 77         151 my $ptype = find_node_i($typename, $symbols);
3295 77 50       151 if (! $ptype) {
3296 0         0 error "unknown type of parameter $pname";
3297 0         0 last;
3298             }
3299 77         147 my @param_node = ($ptype, $pname);
3300 77         133 push @param_node, $pmode;
3301 77         160 push @subord, [ @param_node ];
3302 77 100 66     264 if (@arg and $arg[0] eq ',') {
3303 46         125 shift @arg;
3304             }
3305             }
3306 54         144 my @node = (METHOD, $name, [ @subord ], annotation, $cmnt, curr_scope);
3307 54 100       111 if ($expecting_exception_list) {
3308 1         4 while (@arg) {
3309 1         4 my $exc_name = shift @arg;
3310 1         4 my $exc_type = find_node_i($exc_name, $symbols);
3311 1 50       6 if (! $exc_type) {
    50          
3312 0         0 error "unknown exception $exc_name";
3313 0         0 last;
3314 1         5 } elsif (${$exc_type}[TYPE] != EXCEPTION) {
3315 0         0 error "cannot raise $exc_name (not an exception)";
3316 0         0 last;
3317             }
3318 1         2 push @exception_list, $exc_type;
3319 1 50 33     16 if (@arg and shift @arg ne ',') {
3320 0         0 error "expecting ',' in exception list";
3321 0         0 last;
3322             }
3323             }
3324             }
3325 54 50       149 if ($in_valuetype) {
3326 0 0       0 if (@exception_list) {
3327 0         0 error "'raises' not yet supported in valuetype methods";
3328             }
3329             } else {
3330 54         68 push @{$node[SUBORDINATES]}, [ @exception_list ];
  54         202  
3331             }
3332 54         159 pushsub($symbols, [ @node ], $in_valuetype);
3333              
3334             } else { # Data
3335 15         35 unshift @arg, $kw; # put type back into @arg
3336 15 50       39 if ($#typestack < 0) {
3337 0         0 error "unexpected declaration";
3338 0         0 next;
3339             }
3340 15 100       35 if ($typestack[-1] == UNION) {
3341             # a union case may be followed by only one declaration,
3342             # i.e. each declaration must come after CASE or DEFAULT
3343 2         3 my $i = $#struct;
3344 2         7 while ($i > 0) {
3345 2 50       8 last unless $struct[$i]->[TYPE] == REMARK;
3346 0         0 --$i;
3347             }
3348 2 50 33     10 if ($i < 0 || $struct[$i]->[TYPE] != CASE && $struct[$i]->[TYPE] != DEFAULT) {
      33        
3349 0         0 error "unexpected declaration, case missing?";
3350 0         0 next;
3351             }
3352             }
3353 15 50       42 if (parse_members($symbols, \@arg, \@struct, $cmnt) == 1) {
3354             # end of type declaration was encountered
3355 0         0 my $type = pop @typestack;
3356 0         0 my $name = pop @namestack;
3357 0 0       0 if ($type == ANNOTATION) {
3358 0         0 push @annoDefs, [ $name, @struct ];
3359             } else {
3360 0         0 my $anno = pop @annostack;
3361 0         0 my $initial_cmnt = pop @cmntstack;
3362 0 0       0 if ($initial_cmnt) {
3363 0 0 0     0 if ($cmnt && $cmnt != $initial_cmnt) {
3364 0         0 push @{$initial_cmnt->[1]}, @{$cmnt->[1]};
  0         0  
  0         0  
3365             }
3366 0         0 $cmnt = $initial_cmnt;
3367             }
3368 0         0 my @node = ($type, $name, [ @struct ], $anno, $cmnt, curr_scope);
3369 0         0 push @$symbols, [ @node ];
3370             }
3371 0         0 @struct = ();
3372             }
3373             }
3374             }
3375 3 50       9 if ($verbose) {
3376 0         0 print "IDLtree: done with parsing $file\n";
3377             }
3378 3 50       11 if ($file) {
3379 3         48 close $in;
3380 3         9 pop @infilename;
3381 3         8 pop @line_number;
3382 3         8 $currfile--;
3383             }
3384 3 50       10 if ($n_errors) {
3385 0         0 return 0;
3386             }
3387 3 100       20 bless($symbols, "CORBA::IDLtree") unless $symb;
3388 3         13 return $symbols;
3389             }
3390              
3391             # If @{$argref} ends with ';' right off the bat then pop @{$argref} and
3392             # return success.
3393             # Otherwise read items from file and push them onto @{$argref} until ';'
3394             # is seen.
3395             # If end of file is encountered before seeing a ';' then return error,
3396             # else pop the ';' off end of @{$argref} and return success.
3397             sub require_end_of_stmt {
3398 97     97 0 125 my $argref = shift;
3399 97         146 my $file = shift;
3400 97         136 my $stmt_terminator = ';';
3401 97 50       200 if (@_) {
3402 0         0 $stmt_terminator = shift;
3403             }
3404 97 100       213 if ($argref->[$#$argref] eq $stmt_terminator) {
3405 88         113 pop @{$argref};
  88         131  
3406 88         249 return 1;
3407             }
3408 9         20 my @new_items;
3409 9         48 while ($argref->[$#$argref] ne $stmt_terminator) {
3410 16 50       40 last if (! (@new_items = get_items($file)));
3411 16         25 push @{$argref}, @new_items;
  16         57  
3412             }
3413 9 50       29 if ($argref->[$#$argref] eq $stmt_terminator) {
3414 9         16 pop @{$argref};
  9         11  
3415 9         32 return 1;
3416             }
3417 0         0 0;
3418             }
3419              
3420              
3421             sub isnode {
3422 7632     7632 1 9291 my $node_ref = shift;
3423              
3424 7632 100       13399 ref($node_ref) or return 0;
3425 7337 100 100     20481 ref($node_ref) eq "ARRAY" && defined($node_ref->[TYPE]) or return 0;
3426 7182 100 66     19910 if ($node_ref->[TYPE] >= BOOLEAN
3427             && $node_ref->[TYPE] < NUMBER_OF_TYPES) {
3428 6834 0 33     11306 if (scalar(@$node_ref) == 5 && $verbose) {
3429             # We give a warning here because element count 5 could indicate
3430             # that isnode() is called on a structured member (may indicate
3431             # misuse or latent bug).
3432 0         0 warn("isnode(" . $node_ref->[NAME] . ") : element count is 5\n"
3433             . Carp::longmess() . "\n");
3434             }
3435 6834         14785 return (scalar(@$node_ref) == 6);
3436             }
3437             # NB: The (@$node_ref == 6) means that component descriptors of
3438             # structs/unions/exceptions and parameter descriptors of methods
3439             # do not qualify as nodes.
3440 348         730 return 0;
3441             }
3442              
3443              
3444             sub is_scope {
3445 0     0 1 0 my $thing = shift;
3446 0         0 my $rv = 0;
3447 0 0       0 if (isnode $thing) {
3448 0         0 my $type = $$thing[TYPE];
3449 0   0     0 $rv = ($type == MODULE || $type == INTERFACE || $type == VALUETYPE ||
3450             $type == INCFILE);
3451             }
3452 0         0 $rv;
3453             }
3454              
3455              
3456             sub is_type {
3457 30     30 0 48 my $thing = shift;
3458 30 100       49 if (isnode($thing)) {
3459 13         23 my $type = $thing->[TYPE];
3460 13   0     245 return $type == FIXED
3461             || $type == BOUNDED_STRING
3462             || $type == BOUNDED_WSTRING
3463             || $type == SEQUENCE
3464             || $type == ENUM
3465             || $type == TYPEDEF
3466             || $type == NATIVE
3467             || $type == STRUCT
3468             || $type == UNION
3469             || $type == INTERFACE
3470             || $type == INTERFACE_FWD
3471             || $type == VALUETYPE
3472             || $type == VALUETYPE_FWD
3473             || $type == VALUETYPE_BOX;
3474             } else {
3475 17         42 return is_elementary_type($thing);
3476             }
3477             }
3478              
3479             # Return the names of the nodes in @scopestack as a list.
3480             sub scope_names {
3481 243     243 0 342 my @names = ();
3482 243         379 foreach my $noderef (@scopestack) {
3483 432 50       772 unless ($$noderef[TYPE] == INCFILE) {
3484 432         772 push @names, $$noderef[NAME];
3485             }
3486             }
3487 243         660 @names;
3488             }
3489              
3490              
3491             # Only push those elements which are not already in targetlist.
3492             sub push_uniq {
3493 247     247 0 464 my ($targetlistref, @elements) = @_;
3494 247         320 my $element;
3495 247         419 foreach $element (@elements) {
3496 247 100       539 unless (grep { $_ eq $element } @$targetlistref) {
  51         278  
3497 197         590 push @$targetlistref, $element;
3498             }
3499             }
3500             }
3501              
3502             # Auxiliary to find_node_i:
3503             # Find symbol named by @parts in (or below) scope $root
3504             # Return list of matching node refs (empty list if no match)
3505             # Does not check enclosing scopes!
3506             sub find_node_i_sc {
3507 479     479 0 891 my ($root, @parts) = @_;
3508              
3509 479         620 my ($decls, $start, $end);
3510 479         612 my $anc = undef;
3511 479         643 my $type = 0;
3512 479         680 $start = 0;
3513 479 100       760 if (isnode($root)) {
3514 170         236 $decls = $root->[SUBORDINATES];
3515 170         236 $type = $root->[TYPE];
3516 170 100       424 if ($type == INTERFACE) {
    50          
    100          
3517 64         110 $anc = $decls->[0];
3518 64         88 $start = 2;
3519             } elsif ($type == VALUETYPE) {
3520 0         0 $decls = $decls->[2];
3521             } elsif ($type == INTERFACE_FWD) {
3522 14         19 my $full_interface = $decls;
3523 14 50       24 unless (isnode($full_interface)) {
3524             # Return the INTERFACE_FWD node only if the full interface
3525             # is not known.
3526 0         0 my @r = ();
3527 0         0 my $first = $parts[0];
3528 0 0 0     0 if (defined($root->[NAME]) && $root->[NAME] eq $first) {
3529 0         0 info("find_node_i_sc($first) : Unresolved INTERFACE_FWD");
3530 0         0 @r = ($root);
3531             }
3532 0         0 return @r;
3533             }
3534 14         24 $decls = $full_interface->[SUBORDINATES];
3535 14         33 $anc = $decls->[0];
3536 14         35 $start = 2;
3537             }
3538             } else {
3539 309         455 $decls = $root;
3540             }
3541 479         754 $end = $#$decls;
3542 479         703 my $first = shift @parts;
3543 479         661 my @result = ();
3544 479         572 my $i;
3545 479         864 for ($i = $start; $i <= $end; $i++) {
3546 6738         8886 my $node = $decls->[$i];
3547             # !isnode($node) on the first 2 elements of INTERFACE subordinates
3548 6738 100       9850 next unless (isnode $node);
3549 6354 50       11579 if ($type == VALUETYPE) {
3550 0 0       0 next if $node->[0]; # ignore state members
3551 0         0 $node = $node->[1];
3552             }
3553 6354         8367 my $nt = $node->[TYPE];
3554 6354 50       9993 unless (defined $nt) {
3555 0         0 error("Undefined TYPE on node: " . join(',', @$node) . Carp::longmess());
3556 0         0 next;
3557             }
3558 6354 100 66     22608 next if ($nt == REMARK || $nt == METHOD || $nt == ATTRIBUTE);
      100        
3559 5344         6538 my @r;
3560 5344 100 66     15836 if ($nt == INCFILE) {
    100          
3561 1         6 @r = find_node_i_sc($node->[SUBORDINATES], $first, @parts);
3562             } elsif (defined($node->[NAME]) && $node->[NAME] eq $first) {
3563 246 100       424 if (@parts == 0) {
3564 182         330 @r = ($node);
3565 182 100       313 if ($nt == INTERFACE_FWD) {
3566             # Return the full interface if it is already known.
3567 64         91 my $full_interface = $node->[SUBORDINATES];
3568 64 100       104 if (isnode($full_interface)) {
3569 50         107 @r = ($full_interface);
3570             }
3571             }
3572             } else {
3573 64         151 @r = find_node_i_sc($node, @parts);
3574             }
3575             }
3576 5344 100       12667 if (@r) {
3577 247         510 push_uniq(\@result, @r);
3578             }
3579             }
3580              
3581             # interfaces may inherit symbols from their ancestors
3582 479 100 66     1034 if (defined($anc) && @parts == 0) {
3583 78         121 my @r;
3584 78         151 foreach (@$anc) {
3585 14         33 @r = find_node_i_sc($_, $first);
3586 14 50       33 if (@r) {
3587 0         0 push_uniq(\@result, @r);
3588             }
3589             }
3590             }
3591 479         967 return @result;
3592             }
3593              
3594             sub find_node_i {
3595             # Returns a reference to the defining node, or a type id value
3596             # if the name given is a CORBA predefined type name.
3597             # Returns 0 if the name could not be identified.
3598 200     200 0 284 my $name = shift;
3599 200 50       400 if ("$name" eq "") {
3600 0         0 Carp::cluck("IDLtree::find_node_i() called on empty name\n");
3601 0         0 return 0;
3602             }
3603 200         264 my $current_symtree_ref = shift;
3604 200         250 my $is_abs = 0;
3605 200 50       401 if ($name =~ /^::/) {
3606 0         0 $name =~ s/^:://;
3607 0         0 $is_abs = 1;
3608             }
3609 200 50 33     668 if ($name =~ /^CORBA::/ || $name !~ /::/) {
3610 200         330 my $n = $name;
3611             # this is not absolutely correct: according to the CORBA
3612             # specification IDL predefined names must not be scoped
3613 200         382 $n =~ s/^CORBA:://;
3614 200         339 my $predef_type_id = predef_type($n);
3615 200 100       380 if ($predef_type_id) {
3616 94         179 return $predef_type_id;
3617             }
3618             }
3619              
3620 106 50       238 if (in_annotation_def()) {
3621 0 0       0 if (exists $annoEnum{$name}) {
3622 0         0 return $name;
3623             }
3624 0         0 error("\@annotation " . $namestack[$#namestack] . ": unknown type $name");
3625 0         0 return 0;
3626             }
3627              
3628 106         186 my $res = undef;
3629 106         315 my @namecomponents = split(/::/, $name);
3630              
3631 106 50       215 unless ($is_abs) {
3632             # check "local" scope first
3633 106         222 my $scn = join("::", scope_names(), @namecomponents);
3634 106         271 $res = $findnode_cache->get($scn);
3635 106 100       236 return $res if defined $res;
3636 80         153 my @r = find_node_i_sc($current_symtree_ref, @namecomponents);
3637 80 100       177 if (@r == 1) {
3638 13         28 $res = $r[0];
3639 13         46 $findnode_cache->add($scn, $res);
3640 13         34 return $res;
3641             }
3642             }
3643              
3644 67         129 my @roots = ($current_symtree_ref);
3645 67 50 33     243 if (@prev_symroots && $prev_symroots[-1] != $current_symtree_ref) {
3646 67         100 push @roots, $prev_symroots[-1];
3647             }
3648 67         108 my $root;
3649 67         123 foreach $root (@roots) {
3650 134 50       224 unless ($is_abs) {
3651 134         234 my @scopes = scope_names;
3652 134         284 while (@scopes) {
3653 198         389 my $scn = join("::", @scopes);
3654             # try the node cache for the full name first
3655 198         400 my $n = join("::", $scn, $name);
3656 198         439 $res = $findnode_cache->get($n);
3657 198 100       362 if ($res) {
3658 39         114 return $res;
3659             }
3660             # find the scope
3661 159         259 my @sc = find_node_i_sc($root, @scopes);
3662 159 100       354 last unless @sc;
3663 92         163 foreach (@sc) {
3664 92         141 my $s = $_;
3665 92         171 my @r = find_node_i_sc($s, @namecomponents);
3666 92 100       239 if (@r) {
3667 26 50       71 if (scalar(@r) > 1) {
3668             # remove pragmas from node list for now
3669 0         0 @r = grep(!is_pragma($_), @r);
3670 0 0       0 if (@r > 1) {
3671 0         0 warn("find_node_i: find_node_i_sc(" . typeof($s)
3672             . ", $name) returns multiple matches:\n");
3673 0         0 foreach (@r) {
3674 0         0 warn "\t$_\n";
3675             }
3676 0         0 Carp::cluck();
3677             } else {
3678 0         0 warn("find_node_i($name): pragmas ignored\n");
3679             }
3680             }
3681            
3682 26         35 $res = $r[0];
3683 26         103 $findnode_cache->add($n, $res);
3684 26         91 return $res;
3685             }
3686             }
3687 66         149 pop @scopes;
3688             }
3689             }
3690             # check global scope
3691             #info "find_node_i($name): checking global scope...\n";
3692 69         150 $res = $findnode_cache->get($name);
3693 69 50       114 last if defined $res;
3694 69         120 my @r = find_node_i_sc($root, @namecomponents);
3695 69 50       170 if (@r) {
3696 0 0       0 if (scalar(@r) > 1) {
3697             # remove pragmas from node list for now
3698 0         0 @r = grep(!is_pragma($_), @r);
3699 0 0       0 if (@r > 1) {
3700 0         0 warn("find_node_i: global find_node_i_sc("
3701             . $name . ") returns multiple matches:\n");
3702 0         0 foreach (@r) {
3703 0         0 warn("\t" . typeof($_) . "\n");
3704             }
3705 0         0 Carp::cluck();
3706             } else {
3707 0         0 warn("find_node_i($name): pragmas ignored\n");
3708             }
3709             }
3710 0         0 $res = $r[0];
3711 0         0 my $n = typeof($res, 1);
3712 0         0 $findnode_cache->add($n, $res);
3713 0         0 last;
3714             }
3715             }
3716 2         7 return $res;
3717             }
3718              
3719              
3720             sub info {
3721 1     1 0 4 my $message = shift;
3722 1 50 33     15 if ($currfile >= 0 && $currfile < scalar(@infilename)) {
3723 1         214 warn ($infilename[$currfile] . " line " . $line_number[$currfile]
3724             . ": $message\n");
3725             } else {
3726 0         0 warn($message . "\n");
3727             }
3728             }
3729              
3730             sub error {
3731 0     0 0 0 my $message = shift;
3732 0         0 info($message);
3733 0         0 $n_errors++;
3734             }
3735              
3736             sub abort {
3737 0     0 0 0 my $message = shift;
3738 0         0 my $f = "";
3739 0 0       0 if ($currfile >= 0) {
3740 0         0 $f = $infilename[$currfile] . " line " . $line_number[$currfile]
3741             . ": ";
3742             }
3743 0         0 die ($f . $message . "\n");
3744             }
3745              
3746              
3747             # From here on, it's only Useful User Utilities
3748             # (not required for IDLtree internal purposes)
3749              
3750             sub typeof { # Returns the string of a "type descriptor" in IDL syntax
3751 189     189 1 262 my $type = shift;
3752 189         235 my $gen_scope = 0; # generate scope-qualified name
3753 189 50       363 if (@_) {
3754 189         235 $gen_scope = shift;
3755             }
3756 189         269 my $rv = "";
3757 189 100 33     677 if (!ref($type) && ($type >= BOOLEAN && $type < NUMBER_OF_TYPES)) {
    50 66        
3758 98         173 $rv = $predef_types[$type];
3759 98 50       164 if ($type <= ANY) {
3760 98         233 $rv =~ s/_/ /g;
3761             }
3762 98         287 return $rv;
3763             } elsif (! isnode($type)) {
3764 0         0 Carp::cluck("CORBA::IDLtree::typeof error: parameter is not a node ($type)\n");
3765 0         0 return "";
3766             }
3767 91         142 my @node = @{$type};
  91         235  
3768 91         132 my $name = $node[NAME];
3769 91         126 my $prefix = "";
3770 91 50       169 if ($gen_scope) {
3771 91         164 my @tmpnode = @node;
3772 91         128 my @scope;
3773 91         121 while ((@scope = @{$tmpnode[SCOPEREF]})) {
  182         539  
3774 91 50       181 last if ($scope[TYPE] == INCFILE);
3775 91         165 my $new_prefix = $scope[NAME] . "::";
3776 91 50       363 unless ($prefix =~ /\b$new_prefix/) {
3777 91         167 $prefix = $new_prefix . $prefix;
3778             }
3779 91         224 @tmpnode = @scope;
3780             }
3781 91 50       195 if (ref $gen_scope) {
3782             # @gen_scope contains the scope strings.
3783             # Now we can decide whether the scope prefix is needed.
3784 91         156 my $curr_scope = join("::", @{$gen_scope});
  91         217  
3785 91 100       222 if ($prefix eq "${curr_scope}::") {
3786 28         48 $prefix = "";
3787             }
3788             }
3789             }
3790 91         174 $rv = "$prefix$name";
3791 91 50 33     376 if ($node[TYPE] == FIXED) {
    50          
    100          
3792 0         0 my @digits_and_scale = @{$node[SUBORDINATES]};
  0         0  
3793 0         0 my $digits = $digits_and_scale[0];
3794 0         0 my $scale = $digits_and_scale[1];
3795 0         0 $rv = "fixed<$digits,$scale>";
3796             } elsif ($node[TYPE] == BOUNDED_STRING ||
3797             $node[TYPE] == BOUNDED_WSTRING) {
3798 0         0 my $wide = "";
3799 0 0       0 if ($node[TYPE] == BOUNDED_WSTRING) {
3800 0         0 $wide = "w";
3801             }
3802 0         0 $rv = "${wide}string<" . $name . ">";
3803             } elsif ($node[TYPE] == SEQUENCE) {
3804 8         15 my $bound = $name; # NAME holds the bound
3805 8         24 my $eltype = typeof($node[SUBORDINATES], $gen_scope);
3806 8         16 $rv = 'sequence<' . $eltype;
3807 8 100       18 if ($bound) {
3808 2         5 $rv .= ", $bound";
3809             }
3810 8         11 $rv .= '>';
3811             }
3812 91         252 $rv;
3813             }
3814              
3815              
3816             sub is_a {
3817             # Determines whether node is of given type. Recurses through TYPEDEFs.
3818 47     47 1 78 my ($type, $typeid) = @_;
3819              
3820 47 50       77 unless ($type) {
3821 0         0 Carp::cluck("CORBA::IDLtree::is_a: invalid input (comparing to "
3822             . typeof($typeid) . ")\n");
3823 0         0 return 0;
3824             }
3825 47 100       73 if (! isnode $type) {
3826 44 50       68 if ($typeid > 0) {
3827 44         122 return $type == $typeid;
3828             } else {
3829 0         0 return typeof($type) eq $typeid;
3830             }
3831             }
3832              
3833             # check the node
3834 3 50       9 if ($typeid > 0) {
3835 3 50       13 return 1 if $type->[TYPE] == $typeid;
3836             } else {
3837 0 0       0 return 1 if scoped_name($type) eq $typeid;
3838             }
3839 0 0       0 return 0 unless $type->[TYPE] == TYPEDEF;
3840              
3841             # we have a typedef
3842              
3843 0         0 my $origtype_and_dim = $type->[SUBORDINATES];
3844              
3845             # array ?
3846 0         0 my $dimref = $$origtype_and_dim[1];
3847 0 0 0     0 return 0 if $dimref && @{$dimref};
  0         0  
3848              
3849             # no, recursively check basetype
3850 0         0 return is_a($$origtype_and_dim[0], $typeid);
3851             }
3852              
3853             sub root_type {
3854             # Returns the original type of a TYPEDEF, i.e. recurses through
3855             # all non-array TYPEDEFs until the original type is reached.
3856 17     17 1 28 my $type = shift;
3857 17 50 66     29 if (isnode $type and $$type[TYPE] == TYPEDEF) {
3858 0         0 my($origtype, $dimref) = @{$$type[SUBORDINATES]};
  0         0  
3859 0 0 0     0 unless ($dimref && @{$dimref}) {
  0         0  
3860 0         0 return root_type($origtype);
3861             }
3862             }
3863             $type
3864 17         30 }
3865              
3866             sub root_elem_type {
3867             # Returns the original type of a TYPEDEF, i.e. recurses through
3868             # all TYPEDEFs until the original type is reached.
3869             # Also recurses through array types taking the element type of
3870             # an array type.
3871 0     0 0 0 my $type = shift;
3872 0 0 0     0 if (isnode $type and $$type[TYPE] == TYPEDEF) {
3873 0         0 return root_elem_type($type->[SUBORDINATES][0]);
3874             }
3875 0         0 return $type;
3876             }
3877              
3878              
3879             sub is_pragma {
3880 0     0 1 0 my $type = shift;
3881 0 0       0 if (isnode $type) {
3882 0         0 $type = $type->[TYPE];
3883             }
3884 0   0     0 return ($type == PRAGMA_PREFIX ||
3885             $type == PRAGMA_VERSION ||
3886             $type == PRAGMA_ID ||
3887             $type == PRAGMA);
3888             }
3889              
3890             sub files_included {
3891 0     0 1 0 return $includecache->symbols()
3892             }
3893              
3894             sub collect_includes {
3895 0     0 1 0 my($symroot, $dependency_hash_ref) = @_;
3896 0         0 my $myname = "CORBA::IDLtree::collect_includes";
3897              
3898 0 0       0 if (! $symroot) {
    0          
    0          
3899 0         0 warn "\n$myname: encountered empty elem (returning)\n";
3900 0         0 return;
3901             } elsif (not ref $symroot) {
3902 0         0 warn "\n$myname: incoming symroot is $symroot (returning)\n";
3903 0         0 return;
3904             } elsif (isnode $symroot) {
3905 0         0 warn "\n$myname: usage error: invoked on node (returning)\n";
3906 0         0 return;
3907             }
3908 0         0 foreach my $noderef (@{$symroot}) {
  0         0  
3909 0         0 my @node = @{$noderef};
  0         0  
3910 0         0 my $type = $node[TYPE];
3911 0         0 my $name = $node[NAME];
3912 0 0       0 if ($type == INCFILE) {
3913 0         0 $dependency_hash_ref->{$name} = 1;
3914 0         0 collect_includes($noderef->[SUBORDINATES], $dependency_hash_ref);
3915             }
3916             }
3917             }
3918              
3919             # For floating point notation, FORTRAN and C inspired languages support
3920             # omitting the trailing dot-zero but Ada does not.
3921             sub append_dot_zero {
3922 0     0 0 0 my $res = shift;
3923 0         0 my $epos = index($res, 'e');
3924 0 0       0 if ($epos < 0) {
3925 0         0 $epos = index($res, 'E');
3926             }
3927 0 0       0 if ($epos > 0) {
3928 0         0 $res = substr($res, 0, $epos) . ".0" . substr($res, $epos);
3929             } else {
3930 0         0 $res .= ".0";
3931             }
3932 0         0 return $res;
3933             }
3934              
3935             sub get_numeric {
3936 0     0 1 0 my $tree = shift;
3937 0         0 my ($value, $scoperef, $wantfloat) = @_;
3938              
3939 0 0       0 if ($value =~ /^[-+]?(?:0x)?[0-9a-f]*$/i) {
3940             # integer literal, convert to decimal
3941 0 0       0 if ($is64bit) {
3942 0         0 my $res = eval($value);
3943 0 0       0 if ($wantfloat) {
3944 0         0 $res = append_dot_zero($res);
3945             }
3946 0         0 return $res;
3947             } else {
3948             # use BigInt so that Perl won't switch to
3949             # floating point for large values
3950 0         0 my $v;
3951 0 0       0 if ($value =~ /^[-+]?0[0-7]/) {
3952             # Math::BigInt->new won't convert octal numbers
3953             # (and from_oct produces NaN for '0')...
3954 0 0       0 if (Math::BigInt->can('from_oct')) {
3955 0         0 $v = Math::BigInt->from_oct($value);
3956             } else {
3957             # older Math::BigInt versions don't have from_oct
3958 0         0 my @dg = (split //, $value);
3959 0         0 my $sg = '';
3960 0 0 0     0 if ($dg[0] eq '-' || $dg[0] eq '+' || $dg[0] eq '0') {
      0        
3961 0         0 my $c = shift @dg;
3962 0 0       0 $sg = $c if $c eq '-';
3963             }
3964 0         0 $v = Math::BigInt->new(shift @dg);
3965 0         0 while (@dg > 0) {
3966 0         0 my $c = shift(@dg);
3967 0 0 0     0 if ($c lt '0' || $c gt '7') {
3968 0         0 $v->bnan();
3969 0         0 last;
3970             }
3971 0         0 $v = $v * 8 + $c;
3972             }
3973 0 0       0 $v->bneg() if $sg eq '-';
3974             }
3975 0 0       0 if ($v->is_nan()) {
3976 0         0 return undef;
3977             }
3978             } else {
3979 0         0 $v = Math::BigInt->new($value);
3980 0 0       0 if ($v->is_nan()) {
3981 0         0 return undef;
3982             }
3983 0 0 0     0 if ($wantfloat && $v !~ /\./) {
3984 0         0 $v = append_dot_zero($v);
3985             }
3986             }
3987 0         0 return $v;
3988             }
3989             }
3990 0 0       0 if ($value =~ /^[-+]?(?:\d+.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/) {
3991             # floating point literal
3992 0         0 my $res = eval($value);
3993 0 0 0     0 if ($wantfloat && $res !~ /\./) {
3994 0         0 $res = append_dot_zero($res);
3995             }
3996 0         0 return $res;
3997             }
3998              
3999 0 0       0 if (isnode($value)) {
4000             # only const node allowed here
4001 0 0       0 return undef unless $value->[TYPE] == CONST;
4002             # constants may contain an expression which
4003             # max contain other constants
4004 0         0 my $t = root_type($value->[SUBORDINATES][0]);
4005 0   0     0 $wantfloat = ($t >= FLOAT && $t <= LONGDOUBLE);
4006 0         0 my $rhs_ref = $value->[SUBORDINATES][1];
4007              
4008 0         0 my $expr = "";
4009 0         0 foreach my $token (@$rhs_ref) {
4010 0 0       0 if ($token =~ /^[a-z]/i) {
4011             # hex value or constant
4012 0         0 my $v = get_numeric($tree, $token, $value->[SCOPEREF], $wantfloat);
4013 0 0       0 if (defined $v) {
4014 0         0 $expr .= $v;
4015             } else {
4016 0         0 $expr .= " $token";
4017             }
4018             } else {
4019 0         0 $expr .= " $token";
4020             }
4021             }
4022 0         0 my $res = eval($expr);
4023 0 0 0     0 if ($wantfloat && $res !~ /\./) {
4024 0         0 $res = append_dot_zero($res);
4025             }
4026 0         0 return $res;
4027             }
4028              
4029 0         0 my @expr = idlsplit($value);
4030 0 0       0 if (@expr > 1) {
    0          
    0          
4031             # expression, construct a "pseudo const node" from it
4032 0 0       0 my $t = ($wantfloat ? &FLOAT : &LONG);
4033             # &LONG in the above is probably wrong - but we get away with it.
4034             # We just need to distinguish float from non float type here.
4035 0         0 return get_numeric($tree, [ CONST, "expr", [$t, [ @expr ] ], 0, "", $scoperef ], $wantfloat);
4036             } elsif ($expr[0] eq 'FALSE') {
4037 0         0 info "CORBA::IDLtree::get_numeric returns 'false' for boolean FALSE";
4038 0         0 return 'false';
4039             } elsif ($expr[0] eq 'TRUE') {
4040 0         0 info "CORBA::IDLtree::get_numeric returns 'true' for boolean TRUE";
4041 0         0 return 'true';
4042             }
4043 0         0 my $node = find_node($tree, $value, $scoperef);
4044 0 0       0 if (isnode($node)) {
4045 0         0 return get_numeric($tree, $node, $wantfloat);
4046             }
4047 0         0 warn ("unknown symbol in expression: $value\n");
4048 0         0 return undef;
4049             }
4050              
4051              
4052             # Subs for finding stuff
4053              
4054             sub find_in_current_scope { # Auxiliary to find_scope() / find_node().
4055 0     0 0 0 my $name = shift;
4056 0         0 my $scoperef = shift; # Expects node (of MODULE or INTERFACE)
4057 0         0 my $must_be_scope_node = 0;
4058 0 0       0 if (@_) {
4059 0         0 $must_be_scope_node = shift;
4060             }
4061 0 0       0 return undef unless defined $scoperef->[SUBORDINATES];
4062              
4063 0         0 my $decls = $scoperef->[SUBORDINATES];
4064 0         0 my $start = 0;
4065 0         0 my $scopetype = $scoperef->[TYPE];
4066 0 0       0 if ($scopetype == INTERFACE) {
    0          
4067 0         0 $start = 2;
4068             } elsif ($scopetype == VALUETYPE) {
4069 0         0 $decls = $decls->[2];
4070             }
4071 0         0 my $end = $#$decls;
4072 0         0 for (my $i = $start; $i <= $end; $i++) {
4073 0         0 my $node = $decls->[$i];
4074 0 0       0 if ($scopetype == VALUETYPE) {
4075 0 0       0 next if $node->[0]; # ignore state members
4076 0         0 $node = $node->[1];
4077             }
4078 0 0 0     0 if (@$node > 1 && $node->[NAME] eq $name) {
4079 0 0 0     0 if ($must_be_scope_node and not is_scope $node) {
4080 0         0 warn("warning: $name also used in " .
4081             scoped_name($node) . "\n");
4082             } else {
4083 0         0 return $node;
4084             }
4085             }
4086             }
4087 0         0 undef;
4088             }
4089              
4090             sub find_scope_i; # Auxiliary to find_scope().
4091              
4092             sub find_scope_i {
4093 0     0 0 0 my ($scopelist_ref, $currscope, $global_symroot) = @_;
4094 0         0 my @scopes = @{$scopelist_ref};
  0         0  
4095             # $currscope sometimes is 0 instead of undef...
4096              
4097 0 0       0 $currscope = undef unless $currscope;
4098 0 0       0 unless (defined $currscope) {
4099 0 0       0 return undef unless defined $global_symroot;
4100              
4101             # Try find it somewhere in $global_symroot.
4102             GLOBAL_SCOPES:
4103 0         0 foreach my $node (@$global_symroot) {
4104 0 0 0     0 if ($node->[TYPE] == INCFILE) {
    0          
4105 0         0 my $subord = $node->[SUBORDINATES];
4106 0         0 $currscope = find_scope_i(\@scopes, undef, $subord);
4107 0 0       0 last GLOBAL_SCOPES if $currscope;
4108             } elsif (is_scope($node) && $scopes[0] eq $node->[NAME]) {
4109             # It's in this scope.
4110 0         0 $currscope = $node;
4111 0 0       0 if (scalar(@scopes) > 1) {
4112             # See if the further scopes match, too.
4113 0         0 my $subord = $node->[SUBORDINATES];
4114 0         0 my @sc = @scopes;
4115 0         0 shift @sc;
4116 0         0 $currscope = find_scope_i(\@sc, undef, $subord);
4117 0 0       0 if ($currscope) {
4118 0         0 return $currscope;
4119             }
4120             } else {
4121 0         0 last;
4122             }
4123             }
4124             }
4125 0 0       0 return undef unless defined $currscope;
4126             }
4127              
4128 0 0       0 if ($scopes[0] eq $$currscope[NAME]) {
4129             # It's in the current scope.
4130 0         0 shift @scopes;
4131 0         0 while (@scopes) {
4132 0         0 my $sought_name = shift @scopes;
4133 0         0 $currscope = find_in_current_scope($sought_name, $currscope, 1);
4134 0 0       0 last unless $currscope;
4135             }
4136 0         0 return $currscope;
4137             }
4138             # Not a direct match with current scope.
4139             # Try the scopes nested in the current scope.
4140 0         0 my $scope = find_in_current_scope($scopes[0], $currscope, 1);
4141 0 0       0 if ($scope) {
4142 0         0 shift @scopes;
4143 0         0 while (@scopes) {
4144 0         0 my $sought_name = shift @scopes;
4145 0         0 $scope = find_in_current_scope($sought_name, $scope, 1);
4146 0 0       0 last unless $scope;
4147             }
4148 0         0 return $scope;
4149             }
4150             # Still no match. Step outside and try again.
4151 0         0 find_scope_i($scopelist_ref, $$currscope[SCOPEREF], $global_symroot);
4152             }
4153              
4154             sub find_scope {
4155 0     0 0 0 my $global_symroot = shift;
4156 0         0 my ($scopelist_ref, $currscope) = @_;
4157              
4158 0         0 my $scoperef = undef;
4159 0 0       0 $scoperef = find_scope_i($scopelist_ref, $currscope)
4160             if defined $currscope;
4161              
4162             # undef as the second arg to find_scope_i means
4163             # try to find it anywhere in $global_symroot.
4164 0 0       0 $scoperef = find_scope_i($scopelist_ref, undef, $global_symroot)
4165             unless defined $scoperef;
4166              
4167 0         0 $scoperef;
4168             }
4169              
4170             # Auxiliary to get_scope()
4171             sub get_scope_1;
4172              
4173             sub get_scope_1 {
4174 4     4 0 5 my ($scoperef) = @_;
4175 4 100       14 return () unless ref($scoperef);
4176 2 50       7 return () if ($scoperef->[TYPE] == INCFILE);
4177 2         7 return (get_scope_1($scoperef->[SCOPEREF]), $scoperef->[NAME]);
4178             }
4179              
4180             # return a list of scope names leading to the given scope
4181             # (including the scope itself)
4182             sub get_scope {
4183 2     2 0 4 my $scoperef = shift;
4184 2         7 my @scopes = get_scope_1($scoperef);
4185             # Remove multiple consecutive mentions of the same scope.
4186             # This happens for reopened modules (the SCOPEREF of a reopening
4187             # points to the previous opening of the same module.)
4188 2         4 my $i;
4189 2         9 for ($i = 1; $i < scalar(@scopes); $i++) {
4190 0 0       0 if ($scopes[$i] eq $scopes[$i - 1]) {
4191 0         0 splice(@scopes, $i, 1);
4192 0         0 $i--;
4193             }
4194             }
4195 2         7 return @scopes;
4196             }
4197              
4198             sub find_node {
4199 0     0 1 0 my $global_symroot = shift;
4200 0         0 my ($name, $scoperef, $recurse) = @_;
4201            
4202             #Carp::cluck("find_node: scoperef == 0") if $scoperef==0;
4203            
4204             # $scoperef is expected to be a MODULE or INTERFACE node reference
4205              
4206 0         0 my @components = split(/::/, $name);
4207 0 0       0 shift @components if $components[0] eq "";
4208 0         0 my $noderef = undef;
4209 0 0 0     0 if (scalar(@components) > 1) {
    0          
4210 0         0 $name = pop @components;
4211 0         0 $scoperef = $global_symroot->find_scope(\@components, $scoperef);
4212 0 0       0 if (defined $scoperef) {
4213 0         0 $noderef = find_in_current_scope($name, $scoperef);
4214             }
4215             } elsif (defined($scoperef) && $scoperef != 0) {
4216 0         0 my $scope = $scoperef;
4217 0         0 while ($scope) {
4218 0         0 $noderef = find_in_current_scope($name, $scope);
4219 0 0       0 last if $noderef;
4220 0         0 $scope = $$scope[SCOPEREF];
4221             }
4222 0 0 0     0 if ($recurse && !$noderef) {
4223 0         0 my $nodetype = $scoperef->[TYPE];
4224 0         0 my $innernodes = $scoperef->[SUBORDINATES];
4225 0 0       0 if ($nodetype == VALUETYPE) {
4226 0         0 $innernodes = $innernodes->[2];
4227             }
4228 0         0 foreach (@{$innernodes}) {
  0         0  
4229 0         0 my $n = $_;
4230 0 0       0 if ($nodetype == VALUETYPE) {
4231 0 0       0 next if $n->[0]; # ignore state members
4232 0         0 $n = $n->[1];
4233             }
4234 0         0 my $nt = $n->[TYPE];
4235 0 0 0     0 next unless ($nt == INCFILE || $nt == MODULE ||
      0        
      0        
4236             $nt == INTERFACE || $nt == VALUETYPE);
4237 0         0 $noderef = $global_symroot->find_node($name, $n, 1);
4238 0 0       0 last if $noderef;
4239             }
4240             }
4241             } else {
4242 0         0 foreach (@$global_symroot) {
4243 0 0       0 next if $_->[TYPE] == REMARK;
4244 0 0       0 if ($_->[NAME] eq $name) {
4245 0 0       0 if ($_->[TYPE] == INTERFACE_FWD) {
4246 0         0 my $full_interface = $_->[SUBORDINATES];
4247             # Return the INTERFACE_FWD node only if the full interface
4248             # is not known.
4249 0 0 0     0 next if (defined($full_interface) && @{$full_interface});
  0         0  
4250             }
4251 0         0 return $_;
4252             }
4253             }
4254             # FIXME: This is not really correct:
4255             # If no scope is given, search in all scopes, recursively
4256 0         0 foreach (@$global_symroot) {
4257 0         0 my $nt = $_->[TYPE];
4258 0 0 0     0 if ($nt == INCFILE || $nt == MODULE ||
      0        
      0        
4259             $nt == INTERFACE || $nt == VALUETYPE) {
4260 0         0 $noderef = $global_symroot->find_node($name, $_, 1);
4261 0 0       0 last if $noderef;
4262             }
4263             }
4264             }
4265 0         0 return $noderef;
4266             }
4267              
4268             sub scoped_name {
4269 0     0 1 0 my $node = shift;
4270 0         0 my $scope_sep = "::";
4271 0 0       0 if (@_) {
4272 0         0 $scope_sep = shift;
4273             }
4274              
4275 0 0       0 unless (isnode($node)) {
4276 0         0 return "";
4277             }
4278 0         0 my $sc = $node->[SCOPEREF];
4279 0         0 my @scopes = ($node->[NAME]);
4280 0         0 while ($sc) {
4281 0 0 0     0 unshift @scopes, $sc->[NAME]
4282             unless ($sc->[TYPE] == INCFILE || $sc->[NAME] eq $scopes[0]);
4283 0         0 $sc = $sc->[SCOPEREF];
4284             }
4285 0         0 return join($scope_sep, @scopes);
4286             }
4287              
4288              
4289             # Dump_Symbols and auxiliary subroutines
4290              
4291             # Meaning of $dsoptarg:
4292             # undef => print to stdout
4293             # not ref => print to file
4294             # ref => print to $dstext
4295             my $dsoptarg = undef; # by default, print to stdout
4296             my $dstext;
4297             my $dsindentlevel = 0;
4298              
4299             sub dsemit {
4300 944     944 0 1479 my $str = shift;
4301 944 50       1665 if (defined $dsoptarg) {
4302 0 0       0 if (ref $dsoptarg) {
4303 0         0 $dstext .= $str;
4304             } else {
4305 0         0 print DS $str;
4306             }
4307             } else {
4308 944         33563 print $str;
4309             }
4310             }
4311              
4312             sub dsdent {
4313 282     282 0 848 dsemit(' ' x ($dsindentlevel * 3));
4314 282 100       1147 if (@_) {
4315 191         418 dsemit shift;
4316             }
4317             }
4318              
4319             sub dump_comment {
4320 46     46 0 77 my $cmnt_ref = shift;
4321 46 50       96 $cmnt_ref or return;
4322 0         0 my @cmnt = @{$cmnt_ref->[1]};
  0         0  
4323 0 0       0 @cmnt or return;
4324 0 0       0 if (scalar(@cmnt) == 1) {
4325 0         0 my $comment = $cmnt[0];
4326 0         0 dsdent "// $comment\n";
4327 0         0 return;
4328             }
4329             # multi line comment
4330 0         0 dsdent "/*\n";
4331 0         0 foreach (@cmnt) {
4332 0         0 dsdent " $_\n";
4333             }
4334 0         0 dsdent " */\n";
4335             }
4336              
4337             my @dscopes; # List of scope strings; auxiliary to sub dstypeof
4338              
4339             sub dstypeof {
4340 181     181 0 442 typeof(shift, \@dscopes);
4341             }
4342              
4343             sub dump_symbols_internal {
4344 135     135 0 231 my $sym_array_ref = shift;
4345 135 50       275 if (! $sym_array_ref) {
4346 0         0 warn "dump_symbols_internal: empty elem (returning)\n";
4347 0         0 return 0;
4348             }
4349 135         189 my $status = 1;
4350 135 100       273 if (not isnode $sym_array_ref) {
4351 2         5 foreach (@{$sym_array_ref}) {
  2         6  
4352 4 50       38 unless (dump_symbols_internal $_) {
4353 0         0 $status = 0;
4354             }
4355             }
4356 2         19 return $status;
4357             }
4358 133         227 my @node = @{$sym_array_ref};
  133         366  
4359 133         205 my $type = $node[TYPE];
4360 133         177 my $name = $node[NAME];
4361 133         181 my $subord = $node[SUBORDINATES];
4362 133         164 my @arg = @{$subord};
  133         333  
4363 133         193 my $i;
4364 133 100 100     392 if ($type == INCFILE || $type == PRAGMA_PREFIX) {
4365 2 100       16 if ($type == INCFILE) {
4366 1         3 dsemit "\#include ";
4367 1         9 $name =~ s@^.*/@@;
4368             } else {
4369 1         7 dsemit "\#pragma prefix ";
4370             }
4371 2         12 dsemit "\"$name\"\n\n";
4372 2         16 return $status;
4373             }
4374 131 100 100     613 if ($type == ATTRIBUTE) {
    100 100        
    50 100        
    100          
    100          
    100          
    100          
    100          
    50          
    0          
4375 3         11 dsdent;
4376 3 100       16 dsemit("readonly ") if ($arg[0]);
4377 3         13 dsemit("attribute " . dstypeof($arg[1]) . " $name");
4378             } elsif ($type == METHOD) {
4379 54         80 my $t = shift @arg;
4380 54         88 my $rettype;
4381 54 100       117 if ($t == ONEWAY) {
    100          
4382 5         10 $rettype = 'oneway void';
4383             } elsif ($t == VOID) {
4384 7         11 $rettype = 'void';
4385             } else {
4386 42         74 $rettype = dstypeof($t);
4387             }
4388 54         84 my @exc_list;
4389 54 50       98 if (@arg) {
4390 54         94 my $lastarg = $arg[$#arg];
4391 54 50       116 unless (ref($lastarg) eq "ARRAY") {
4392 0         0 die("CORBA::IDLtree::dump_symbols_internal error at METHOD "
4393             . $name . " last arg ($global_idlfile)\n");
4394             }
4395 54         69 my @last = @{$lastarg};
  54         85  
4396 54 50 33     121 if (scalar(@last) != 3 || ref($last[NAME])) {
4397 54         68 @exc_list = @{pop @arg};
  54         122  
4398             }
4399             }
4400 54         193 dsdent($rettype . " $name (");
4401 54 100       215 if (@arg) {
4402 31 100       65 unless ($#arg == 0) {
4403 24         59 dsemit "\n";
4404 24         69 $dsindentlevel += 5;
4405             }
4406 31         102 for ($i = 0; $i <= $#arg; $i++) {
4407 77         189 my $pnode = $arg[$i];
4408 77         171 my $ptype = dstypeof($$pnode[TYPE]);
4409 77         152 my $pname = $$pnode[NAME];
4410 77         118 my $m = $$pnode[SUBORDINATES];
4411 77 100       250 my $pmode = ($m == &IN ? 'in' : $m == &OUT ? 'out' : 'inout');
    100          
4412 77 100       229 dsdent unless ($#arg == 0);
4413 77         311 dsemit "$pmode $ptype $pname";
4414 77 100       365 dsemit(",\n") if ($i < $#arg);
4415             }
4416 31 100       65 unless ($#arg == 0) {
4417 24         42 $dsindentlevel -= 5;
4418             }
4419             }
4420 54         139 dsemit ")";
4421 54 100       197 if (@exc_list) {
4422 1         6 dsemit "\n";
4423 1         4 $dsindentlevel++;
4424 1         6 dsdent " raises (";
4425 1         10 for ($i = 0; $i <= $#exc_list; $i++) {
4426 1         4 dsemit(${$exc_list[$i]}[NAME]);
  1         18  
4427 1 50       9 dsemit(", ") if ($i < $#exc_list);
4428             }
4429 1         5 dsemit ")";
4430 1         5 $dsindentlevel--;
4431             }
4432             } elsif ($type == VALUETYPE) {
4433 0         0 dsdent;
4434 0 0       0 if ($arg[0]) { # `abstract' flag
4435 0         0 dsemit "abstract ";
4436             }
4437 0         0 dsemit "valuetype $name ";
4438 0 0       0 if ($arg[1]) { # ancestor info
4439 0         0 my($truncatable, $ancestors_ref) = @{$arg[1]};
  0         0  
4440 0 0       0 if ($truncatable) {
4441 0         0 dsemit "truncatable ";
4442             }
4443 0 0       0 if (@{$ancestors_ref}) {
  0         0  
4444 0         0 dsemit ": ";
4445 0         0 my $first = 1;
4446 0         0 foreach (@{$ancestors_ref}) {
  0         0  
4447 0 0       0 if ($first) {
4448 0         0 $first = 0;
4449             } else {
4450 0         0 dsemit ", ";
4451             }
4452 0         0 dsemit(dstypeof $_);
4453             }
4454 0         0 dsemit ' ';
4455             }
4456             }
4457 0         0 dsemit "{\n";
4458 0         0 $dsindentlevel++;
4459 0         0 foreach (@{$arg[2]}) {
  0         0  
4460 0         0 my ($memberkind, $member) = @$_;
4461 0 0       0 if ($memberkind) {
4462 0         0 my $mtype = dstypeof($member->[TYPE]);
4463 0         0 my $mname = $member->[NAME];
4464 0         0 dump_comment $member->[COMMENT];
4465 0 0       0 dsdent($memberkind == &PUBLIC ? "public" : "private");
4466 0         0 dsemit " $mtype $mname;\n";
4467             } else {
4468 0 0       0 unless (dump_symbols_internal $member) {
4469 0         0 $status = 0;
4470             }
4471             }
4472             }
4473 0         0 $dsindentlevel--;
4474 0         0 dsdent "}";
4475             } elsif ($type == MODULE || $type == INTERFACE) {
4476 18         31 push @dscopes, $name;
4477 18         46 dsdent;
4478 18 100       143 if ($type == INTERFACE) {
4479 16 50       43 if ($arg[1] == ABSTRACT) {
    50          
4480 0         0 dsemit "abstract ";
4481             } elsif ($arg[1] == LOCAL) {
4482 0         0 dsemit "local ";
4483             }
4484             }
4485 18         75 dsemit($predef_types[$type] . " ");
4486 18         92 dsemit "$name ";
4487 18 100       135 if ($type == INTERFACE) {
4488 16         33 my $ancref = shift @arg;
4489 16         23 my @ancestors = @{$ancref};
  16         36  
4490 16         28 shift @arg; # discard the "abstract" flag
4491 16 100       43 if (@ancestors) {
4492 5         17 dsemit ": ";
4493 5         24 for ($i = 0; $i <= $#ancestors; $i++) {
4494 6         10 my @ancnode = @{$ancestors[$i]};
  6         22  
4495 6         16 dsemit $ancnode[NAME];
4496 6 100       37 dsemit(", ") if ($i < $#ancestors);
4497             }
4498             }
4499             }
4500 18         51 dsemit " {\n\n";
4501 18         57 $dsindentlevel++;
4502 18         42 foreach (@arg) {
4503 129 50       313 unless (dump_symbols_internal $_) {
4504 0         0 $status = 0;
4505             }
4506             }
4507 18         32 $dsindentlevel--;
4508 18         48 dsdent "}";
4509 18         51 pop @dscopes;
4510             } elsif ($type == TYPEDEF) {
4511 13         23 my $origtype = $arg[0];
4512 13         18 my $dimref = $arg[1];
4513 13         27 dsdent("typedef " . dstypeof($origtype) . " $name");
4514 13 100 66     63 if ($dimref and @{$dimref}) {
  13         49  
4515 5         9 foreach (@{$dimref}) {
  5         14  
4516 7         30 dsemit "[$_]";
4517             }
4518             }
4519             } elsif ($type == CONST) {
4520 12         30 dsdent("const " . dstypeof($arg[0]) . " $name = ");
4521 12         37 dsemit join(' ', @{$arg[1]});
  12         47  
4522             } elsif ($type == ENUM) {
4523 3         14 dsdent "enum $name { ";
4524 3         19 @arg = enum_literals($subord);
4525 3 50       12 if ($#arg > 4) {
4526 0         0 $dsindentlevel += 5;
4527 0         0 dsemit "\n";
4528             }
4529 3         16 for ($i = 0; $i <= $#arg; $i++) {
4530 12 50       43 dsdent if ($#arg > 4);
4531 12         31 dsemit $arg[$i];
4532 12 100       54 if ($i < $#arg) {
4533 9         23 dsemit(", ");
4534 9 50       44 dsemit("\n") if ($#arg > 4);
4535             }
4536             }
4537 3 50       15 if ($#arg > 4) {
4538 0         0 $dsindentlevel -= 5;
4539 0         0 dsemit "\n";
4540 0         0 dsdent "}";
4541             } else {
4542 3         9 dsemit " }";
4543             }
4544             } elsif ($type == STRUCT || $type == UNION || $type == EXCEPTION) {
4545 14         58 dsdent($predef_types[$type] . " $name");
4546 14 100       52 if ($type == UNION) {
4547 5         18 dsemit(" switch (" . dstypeof(shift @arg) . ")");
4548             }
4549 14         50 dsemit " {\n";
4550 14         42 $dsindentlevel++;
4551 14         25 my $had_case = 0;
4552 14         40 while (@arg) {
4553 46         90 my $node = shift @arg;
4554 46         101 my $type = $$node[TYPE];
4555 46         116 my $name = $$node[NAME];
4556 46         66 my $suboref = $$node[SUBORDINATES];
4557 46         139 dump_comment $$node[COMMENT];
4558 46 100 100     178 if ($type == CASE || $type == DEFAULT) {
    50          
4559 17 100       34 if ($had_case) {
4560 12         14 $dsindentlevel--;
4561             } else {
4562 5         8 $had_case = 1;
4563             }
4564 17 100       34 if ($type == CASE) {
4565 16         19 foreach (@{$suboref}) {
  16         54  
4566 18         49 dsdent "case $_:\n";
4567             }
4568             } else {
4569 1         6 dsdent "default:\n";
4570             }
4571 17         59 $dsindentlevel++;
4572             } elsif ($type == REMARK) {
4573 0         0 dump_comment [ $name, $suboref ];
4574             } else {
4575 29         38 foreach (@{$suboref}) {
  29         102  
4576 0         0 $name .= '[' . $_ . ']';
4577             }
4578 29         68 dsdent(dstypeof($type) . " $name;\n");
4579             }
4580             }
4581 14         45 $dsindentlevel -= $had_case + 1;
4582 14         34 dsdent "}";
4583             } elsif ($type == INTERFACE_FWD) {
4584 14         34 dsdent "interface $name";
4585             } elsif ($type == REMARK) {
4586 0         0 dump_comment [ $name, $subord ];
4587 0         0 return $status;
4588             } else {
4589 0         0 my $ttext;
4590 0 0       0 if (ref $type) {
4591 0         0 $ttext = dstypeof($type);
4592             } else {
4593 0         0 $ttext = $type;
4594             }
4595 0         0 warn("Dump_Symbols: unimplemented type $ttext\n");
4596 0         0 $status = 0;
4597             }
4598 131 50       338 if ($status) {
4599 131         237 dsemit ";\n\n";
4600             } else {
4601 0         0 dsemit "\n"; # just to get a clean line ending on error
4602             }
4603 131         734 $status
4604             }
4605              
4606              
4607             sub Dump_Symbols {
4608 2     2 1 7 my $sym_array_ref = shift;
4609 2         6 my $output_file_name;
4610 2 50       8 if (@_) {
4611             # Meaning of optional argument:
4612             # when string => filename to open and write to
4613             # when array reference => dump into dereferenced array
4614 0         0 $dsoptarg = shift;
4615 0 0       0 unless (ref $dsoptarg) {
4616 0         0 $output_file_name = $dsoptarg;
4617 0 0       0 unless (open(DS, ">$output_file_name")) {
4618 0         0 warn "CORBA::IDLtree::Dump_Symbols: cannot create $output_file_name\n";
4619 0         0 $dsoptarg = undef;
4620 0         0 return undef;
4621             }
4622 0         0 my $hfence = $output_file_name;
4623 0         0 $hfence =~ s/\W+/_/g;
4624 0         0 $hfence = "_" . uc($hfence) . "_";
4625 0         0 dsemit "#ifndef $hfence\n";
4626 0         0 dsemit "#define $hfence\n\n";
4627             }
4628             } else {
4629 2         5 $dsoptarg = undef;
4630             }
4631 2         6 $dstext = "";
4632 2         26 my $res = dump_symbols_internal($sym_array_ref);
4633 2 50       11 if ($output_file_name) {
    50          
4634 0         0 dsemit "#endif\n";
4635 0         0 close DS;
4636             } elsif ($dsoptarg) {
4637 0         0 @{$dsoptarg} = split(/\n/, $dstext);
  0         0  
4638             }
4639 2         18 return $res;
4640             }
4641              
4642             # End of Dump_Symbols stuff.
4643              
4644              
4645             # traverse_tree stuff.
4646              
4647             my $user_sub_ref = 0;
4648             my $traverse_includefiles = 0;
4649              
4650             sub traverse;
4651              
4652             sub traverse {
4653 0     0 0   my ($symroot, $scope, $inside_includefile) = @_;
4654 0 0         if (! $symroot) {
    0          
    0          
4655 0           warn "\nCORBA::IDLtree::traverse: encountered empty elem (returning)\n";
4656 0           return;
4657             } elsif (is_elementary_type $symroot) {
4658 0           &{$user_sub_ref}($symroot, $scope, $inside_includefile);
  0            
4659 0           return;
4660             } elsif (not isnode $symroot) {
4661 0           foreach (@{$symroot}) {
  0            
4662 0           traverse($_, $scope, $inside_includefile);
4663             }
4664 0           return;
4665             }
4666 0           &{$user_sub_ref}($symroot, $scope, $inside_includefile);
  0            
4667 0           my @node = @{$symroot};
  0            
4668 0           my $type = $node[TYPE];
4669 0           my $name = $node[NAME];
4670 0           my $subord = $node[SUBORDINATES];
4671 0           my @arg = @{$subord};
  0            
4672 0 0         if ($type == &INCFILE) {
    0          
    0          
4673 0 0         traverse($subord, $scope, 1) if ($traverse_includefiles);
4674             } elsif ($type == MODULE) {
4675 0           foreach (@arg) {
4676 0           traverse($_, scoped_name($symroot), $inside_includefile);
4677             }
4678             } elsif ($type == INTERFACE) {
4679             # my @ancestors = @{$arg[0]};
4680             # if (@ancestors) {
4681             # foreach $elder (@ancestors) {
4682             # &{$user_sub_ref}($elder, $scope, $inside_includefile);
4683             # }
4684             # }
4685 0           shift @arg; # discard ancestors
4686 0           shift @arg; # discard abstract flag
4687 0           foreach (@arg) {
4688 0           traverse($_, scoped_name($symroot), $inside_includefile);
4689             }
4690             }
4691             }
4692              
4693             sub traverse_tree {
4694 0     0 0   my $sym_array_ref = shift;
4695 0           $user_sub_ref = shift;
4696 0           $traverse_includefiles = 0;
4697 0 0         if (@_) {
4698 0           $traverse_includefiles = shift;
4699             }
4700 0           traverse($sym_array_ref, "", 0);
4701             }
4702              
4703             # End of traverse_tree stuff.
4704              
4705             sub get_scalar_default {
4706 0     0 1   my ($node, $scoped) = @_;
4707              
4708 0 0         if (defined($comment_directives)) {
4709 0           return $comment_directives->get_default($node, $scoped);
4710             } else {
4711 0           my $t = root_type($node);
4712 0 0         if ($t == BOOLEAN) {
    0          
    0          
4713 0           return "FALSE";
4714             } elsif (is_elementary_type($t)) {
4715 0           return 0;
4716             } elsif ($t->[TYPE] == ENUM) {
4717 0           my @literals = enum_literals($t->[SUBORDINATES]);
4718 0           my $v = $literals[0];
4719 0 0         if ($scoped) {
4720 0           my @sc = get_scope($t);
4721 0           pop @sc;
4722 0           $v = join("::", @sc, $v);
4723             }
4724 0           return $v;
4725             } else {
4726 0           return undef;
4727             }
4728             }
4729             }
4730              
4731             sub is_integer {
4732 0     0 0   my ($type) = @_;
4733 0           my $e = is_elementary_type($type, 1);
4734 0   0       return $e == OCTET
4735             || $e == SHORT
4736             || $e == LONG
4737             || $e == LONGLONG
4738             || $e == USHORT
4739             || $e == ULONG
4740             || $e == ULONGLONG;
4741             }
4742              
4743             sub find_union_case {
4744 0     0 0   my ($tree, $node, $caseval) = @_;
4745              
4746 0           my $case = $caseval;
4747 0 0         if ($caseval =~ /::/) {
4748 0           $caseval =~ s/^.*:://;
4749             }
4750 0 0         return undef unless $node->[TYPE] == UNION;
4751 0           my $int = is_integer($node->[SUBORDINATES][0]);
4752 0           my $found = 0;
4753 0           my $thecase = undef;
4754 0           my $thecase_memb = undef;
4755 0           for (my $n = 1; $n <= $#{$node->[SUBORDINATES]}; ++$n) {
  0            
4756 0           my $memb = $node->[SUBORDINATES][$n];
4757 0 0         next if $memb->[TYPE] == REMARK;
4758 0 0         if ($memb->[TYPE] == CASE) {
    0          
    0          
4759 0           for my $c (@{$memb->[SUBORDINATES]}) {
  0            
4760 0 0         my $cv = $int ? $tree->get_numeric($c) : $c;
4761 0 0         if ($cv =~ /::/) { $cv =~ s/^.*:://; }
  0            
4762 0 0         if ($cv eq $caseval) {
4763 0           $found = 1;
4764 0           $thecase = $c;
4765 0           last;
4766             }
4767             }
4768 0           $thecase_memb = $memb;
4769             } elsif ($memb->[TYPE] == DEFAULT) {
4770             # note: this assumes "default" is always the last branch
4771 0           $found = 1;
4772 0           $thecase = $case;
4773 0           $thecase_memb = $memb;
4774             } elsif ($found) {
4775 0           return ($thecase, $memb, $thecase_memb);
4776             }
4777             }
4778 0           return ($case, undef, undef);
4779             }
4780              
4781             sub get_union_default {
4782 0     0 0   my ($tree, $node) = @_;
4783              
4784 0 0         return undef unless $node->[TYPE] == UNION;
4785              
4786 0           my $switcht = $node->[SUBORDINATES][0];
4787              
4788             # first try: default of discriminant type
4789 0           my $case = get_scalar_default($switcht, 1);
4790              
4791 0           my ($memb, $casememb);
4792 0           ($case, $memb, $casememb) = find_union_case($tree, $node, $case);
4793 0 0 0       if (defined($memb) || $union_default_null_allowed) {
4794 0           return ($case, $memb, $casememb);
4795             }
4796             # else...
4797 0           my $st = root_type($switcht);
4798 0 0 0       if (isnode($st) && $st->[TYPE] == ENUM) {
4799             # try each enum label until a match is found
4800 0           for my $e (enum_literals($st->[SUBORDINATES])) {
4801 0 0         my $el = ref($e) ? $e->[0] : $e;
4802 0           ($el, $memb, $casememb) = find_union_case($tree, $node, $el);
4803 0 0         if (defined $memb) {
4804 0 0         unless ($el =~ /::/) {
4805 0           my @sc = CORBA::IDLtree::get_scope($st);
4806 0           pop @sc;
4807 0           $el = join("::", @sc, $el);
4808             }
4809 0           return ($el, $memb, $casememb);
4810             }
4811             }
4812             }
4813             # use the first case as fallback
4814 0           $case = undef;
4815 0           $casememb = undef;
4816 0           for (my $n = 1; $n <= $#{$node->[SUBORDINATES]}; ++$n) {
  0            
4817 0           my $memb = $node->[SUBORDINATES][$n];
4818 0 0         next if $memb->[TYPE] == REMARK;
4819 0 0         if ($memb->[TYPE] == CASE) {
    0          
    0          
4820 0           $case = $memb->[SUBORDINATES][0];
4821 0 0         $case = $tree->get_numeric($case) if is_integer($switcht);
4822 0           $casememb = $memb;
4823             } elsif ($memb->[TYPE] == DEFAULT) {
4824 0           $case = undef;
4825 0           $casememb = undef;
4826 0           next;
4827             } elsif (defined $case) {
4828 0           return ($case, $memb, $casememb);
4829             }
4830             }
4831 0           return undef;
4832             }
4833              
4834             =head1 AUTHOR
4835              
4836             Oliver M. Kellogg, C<< >>
4837              
4838             =head1 BUGS
4839              
4840             Please report any bugs or feature requests to C,
4841             or through the web interface at
4842             L.
4843             I will be notified, and then you'll automatically be notified of progress on your
4844             bug as I make changes.
4845              
4846              
4847             =head1 SUPPORT
4848              
4849             You can find documentation for this module with the perldoc command.
4850              
4851             perldoc CORBA::IDLtree
4852              
4853              
4854             You can also look for information at:
4855              
4856             =over 4
4857              
4858             =item * RT: CPAN's request tracker (report bugs here)
4859              
4860             L
4861              
4862             =item * AnnoCPAN: Annotated CPAN documentation
4863              
4864             L
4865              
4866             =item * CPAN Ratings
4867              
4868             L
4869              
4870             =item * Search CPAN
4871              
4872             L
4873              
4874             =back
4875              
4876              
4877             =head1 ACKNOWLEDGEMENTS
4878              
4879             Thanks to Heiko Schroeder for contributing.
4880              
4881             =head1 LICENSE AND COPYRIGHT
4882              
4883             Copyright (C) 1998-2019, Oliver M. Kellogg
4884              
4885             This program is free software; you can redistribute it and/or modify it
4886             under the same terms as Perl itself.
4887              
4888             =cut
4889              
4890             1;
4891              
4892             # Local Variables:
4893             # cperl-indent-level: 4
4894             # indent-tabs-mode: nil
4895             # End: