File Coverage

blib/lib/CORBA/IDLtree.pm
Criterion Covered Total %
statement 1100 2410 45.6
branch 567 1414 40.1
condition 122 441 27.6
subroutine 59 95 62.1
pod 20 81 24.6
total 1868 4441 42.0


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