File Coverage

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