File Coverage

blib/lib/CORBA/IDLtree.pm
Criterion Covered Total %
statement 1109 2462 45.0
branch 566 1416 39.9
condition 119 435 27.3
subroutine 59 95 62.1
pod 21 81 25.9
total 1874 4489 41.7


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