Preprocessor.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 1999, 2000, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * COMPONENT_NAME: idl.parser
27 *
28 * ORIGINS: 27
29 *
30 * Licensed Materials - Property of IBM
31 * 5639-D57 (C) COPYRIGHT International Business Machines Corp. 1997, 1999
32 * RMI-IIOP v1.0
33 *
34 */
35
36package com.sun.tools.corba.se.idl;
37
38// NOTES:
39// -D57110<daz> Allow ID pragma directive to be applied to modules and update
40//  feature in accordance to CORBA 2.3.
41// -D59165<daz> Enable escaped identifiers when processing pragmas.
42// -f60858.1<daz> Support -corba option, level = 2.2: Accept identifiers that
43//  collide with keywords, in letter but not case, and issue a warning.
44// -d62023 <daz> support -noWarn option; suppress inappropriate warnings when
45//  parsing IBM-specific pragmas (#meta <interface_name> abstract).
46
47import java.io.File;
48import java.io.FileNotFoundException;
49import java.io.IOException;
50import java.math.BigInteger;
51import java.util.Enumeration;
52import java.util.Hashtable;
53import java.util.Stack;
54import java.util.Vector;
55
56import com.sun.tools.corba.se.idl.RepositoryID;
57
58import com.sun.tools.corba.se.idl.constExpr.*;
59
60/**
61 * This class should be extended if new pragmas are desired.  If the
62 * preprocessor encounters a pragma name which it doesn't recognize
63 * (anything other than ID, prefix, or version), it calls the method
64 * otherPragmas.  This is the only method which need be overridden.
65 * The Preprocessor base class has a number of utility-like methods
66 * which can be used by the overridden otherPragmas method.
67 **/
68public class Preprocessor
69{
70  /**
71   * Public zero-argument constructor.
72   **/
73  Preprocessor ()
74  {
75  } // ctor
76
77  /**
78   *
79   **/
80  void init (Parser p)
81  {
82    parser  = p;
83    symbols = p.symbols;
84    macros  = p.macros;
85  } // init
86
87  /**
88   *
89   **/
90  protected Object clone ()
91  {
92    return new Preprocessor ();
93  } // clone
94
95  /**
96   *
97   **/
98  Token process (Token t) throws IOException, ParseException
99  {
100    token   = t;
101    scanner = parser.scanner;
102    // <f46082.40> Deactivate escaped identifier processing in Scanner while
103    // preprocessing.
104    //scanner.underscoreOK = true;
105    scanner.escapedOK = false;
106    try
107    {
108      switch (token.type)
109      {
110        case Token.Include:
111          include ();
112          break;
113        case Token.If:
114          ifClause ();
115          break;
116        case Token.Ifdef:
117          ifdef (false);
118          break;
119        case Token.Ifndef:
120          ifdef (true);
121          break;
122        case Token.Else:
123          if (alreadyProcessedABranch.empty ())
124            throw ParseException.elseNoIf (scanner);
125          else if (((Boolean)alreadyProcessedABranch.peek ()).booleanValue ())
126            skipToEndif ();
127          else
128          {
129            alreadyProcessedABranch.pop ();
130            alreadyProcessedABranch.push (new Boolean (true));
131            token = scanner.getToken ();
132          }
133          break;
134        case Token.Elif:
135          elif ();
136          break;
137        case Token.Endif:
138          if (alreadyProcessedABranch.empty ())
139            throw ParseException.endNoIf (scanner);
140          else
141          {
142            alreadyProcessedABranch.pop ();
143            token = scanner.getToken ();
144            break;
145          }
146        case Token.Define:
147          define ();
148          break;
149        case Token.Undef:
150          undefine ();
151          break;
152        case Token.Pragma:
153          pragma ();
154          break;
155        case Token.Unknown:
156          if (!parser.noWarn)
157            ParseException.warning (scanner, Util.getMessage ("Preprocessor.unknown", token.name));
158        case Token.Error:
159        case Token.Line:
160        case Token.Null:
161          // ignore
162        default:
163          scanner.skipLineComment ();
164          token = scanner.getToken ();
165      }
166    }
167    catch (IOException e)
168    {
169      // <f46082.40> Underscore may now precede any identifier, so underscoreOK
170      // is vestigal.  The Preprocessor must reset escapedOK so that Scanner
171      // will process escaped identifiers according to specification.
172      //scanner.underscoreOK = false;
173      scanner.escapedOK = true;
174      throw e;
175    }
176    catch (ParseException e)
177    {
178      // <f46082.40> See above.
179      //scanner.underscoreOK = false;
180      scanner.escapedOK = true;
181      throw e;
182    }
183    // <f46082.40> See above.
184    //scanner.underscoreOK = false;
185    scanner.escapedOK = true;
186    return token;
187  } // process
188
189  /**
190   *
191   **/
192  private void include () throws IOException, ParseException
193  {
194    match (Token.Include);
195    IncludeEntry include = parser.stFactory.includeEntry (parser.currentModule);
196    include.sourceFile (scanner.fileEntry ());
197    scanner.fileEntry ().addInclude (include);
198    if (token.type == Token.StringLiteral)
199      include2 (include);
200    else if (token.type == Token.LessThan)
201      include3 (include);
202    else
203    {
204      int[] expected = {Token.StringLiteral, Token.LessThan};
205      throw ParseException.syntaxError (scanner, expected, token.type);
206    }
207    if (parser.currentModule instanceof ModuleEntry)
208      ((ModuleEntry)parser.currentModule).addContained (include);
209    else if (parser.currentModule instanceof InterfaceEntry)
210      ((InterfaceEntry)parser.currentModule).addContained (include);
211  } // include
212
213  /**
214   *
215   **/
216  private void include2 (IncludeEntry include) throws IOException, ParseException
217  {
218    include.name ('"' + token.name + '"');
219    include4 (include, token.name);
220    match (Token.StringLiteral);
221  } // include2
222
223  /**
224   *
225   **/
226  private void include3 (IncludeEntry include) throws IOException, ParseException
227  {
228    if (token.type != Token.LessThan)
229      // match will throw an exception
230      match (Token.LessThan);
231    else
232    {
233      try
234      {
235        String includeFile = getUntil ('>');
236        token = scanner.getToken ();
237        include.name ('<' + includeFile + '>');
238        include4 (include, includeFile);
239        match (Token.GreaterThan);
240      }
241      catch (IOException e)
242      {
243        throw ParseException.syntaxError (scanner, ">", "EOF");
244      }
245    }
246  } // include3
247
248  /**
249   *
250   **/
251  private void include4 (IncludeEntry include, String filename) throws IOException, ParseException
252  {
253    try
254    {
255      // If the #include is at the global scope, it is treated as
256      // an import statement.  If it is within some other scope, it
257      // is treated as a normal #include.
258      boolean includeIsImport = parser.currentModule == parser.topLevelModule;
259      //daz
260      include.absFilename (Util.getAbsolutePath (filename, parser.paths));
261      scanner.scanIncludedFile (include, getFilename (filename), includeIsImport);
262    }
263    catch (IOException e)
264    {
265      ParseException.generic (scanner, e.toString ());
266    }
267  } // include4
268
269  /**
270   *
271   **/
272  private void define () throws IOException, ParseException
273  {
274    match (Token.Define);
275    if (token.equals (Token.Identifier))
276    {
277      String symbol = scanner.getStringToEOL ();
278      symbols.put (token.name, symbol.trim ());
279      match (Token.Identifier);
280    }
281    else if (token.equals (Token.MacroIdentifier))
282    {
283      symbols.put (token.name, '(' + scanner.getStringToEOL () . trim ());
284      macros.addElement (token.name);
285      match (Token.MacroIdentifier);
286    }
287    else
288      throw ParseException.syntaxError (scanner, Token.Identifier, token.type);
289  } // define
290
291  /**
292   *
293   **/
294  private void undefine () throws IOException, ParseException
295  {
296    match (Token.Undef);
297    if (token.equals (Token.Identifier))
298    {
299      symbols.remove (token.name);
300      macros.removeElement (token.name);
301      match (Token.Identifier);
302    }
303    else
304      throw ParseException.syntaxError (scanner, Token.Identifier, token.type);
305  } // undefine
306
307  /**
308   *
309   **/
310  private void ifClause () throws IOException, ParseException
311  {
312    match (Token.If);
313    constExpr ();
314  } // ifClause
315
316  /**
317   *
318   **/
319  private void constExpr () throws IOException, ParseException
320  {
321    SymtabEntry dummyEntry = new SymtabEntry (parser.currentModule);
322    dummyEntry.container (parser.currentModule);
323    parser.parsingConditionalExpr = true;
324    Expression boolExpr = booleanConstExpr (dummyEntry);
325    parser.parsingConditionalExpr = false;
326    boolean expr;
327    if (boolExpr.value () instanceof Boolean)
328      expr = ((Boolean)boolExpr.value ()).booleanValue ();
329    else
330      expr = ((Number)boolExpr.value ()).longValue () != 0;
331    alreadyProcessedABranch.push (new Boolean (expr));
332    if (!expr)
333      skipToEndiforElse ();
334  } // constExpr
335
336  /**
337   *
338   **/
339  Expression booleanConstExpr (SymtabEntry entry) throws IOException, ParseException
340  {
341    Expression expr = orExpr (null, entry);
342    try
343    {
344      expr.evaluate ();
345    }
346    catch (EvaluationException e)
347    {
348      ParseException.evaluationError (scanner, e.toString ());
349    }
350    return expr;
351  } // booleanConstExpr
352
353  /**
354   *
355   **/
356  private Expression orExpr (Expression e, SymtabEntry entry) throws IOException, ParseException
357  {
358    if (e == null)
359      e = andExpr (null, entry);
360    else
361    {
362      BinaryExpr b = (BinaryExpr)e;
363      b.right (andExpr (null, entry));
364      e.rep (e.rep () + b.right ().rep ());
365    }
366    if (token.equals (Token.DoubleBar))
367    {
368      match (token.type);
369      BooleanOr or = parser.exprFactory.booleanOr (e, null);
370      or.rep (e.rep () + " || ");
371      return orExpr (or, entry);
372    }
373    else
374      return e;
375  } // orExpr
376
377  /**
378   *
379   **/
380  private Expression andExpr (Expression e, SymtabEntry entry) throws IOException, ParseException
381  {
382    if (e == null)
383      e = notExpr (entry);
384    else
385    {
386      BinaryExpr b = (BinaryExpr)e;
387      b.right (notExpr (entry));
388      e.rep (e.rep () + b.right ().rep ());
389    }
390    if (token.equals (Token.DoubleAmpersand))
391    {
392      match (token.type);
393      BooleanAnd and = parser.exprFactory.booleanAnd (e, null);
394      and.rep (e.rep () + " && ");
395      return andExpr (and, entry);
396    }
397    else
398      return e;
399  } // andExpr
400
401  /**
402   *
403   **/
404  private Expression notExpr (/*boolean alreadySawExclamation, */SymtabEntry entry) throws IOException, ParseException
405  {
406    Expression e;
407    if (token.equals (Token.Exclamation))
408    {
409      match (Token.Exclamation);
410      e = parser.exprFactory.booleanNot (definedExpr (entry));
411      e.rep ("!" + ((BooleanNot)e).operand ().rep ());
412    }
413    else
414      e = definedExpr (entry);
415    return e;
416  } // notExpr
417
418  /**
419   *
420   **/
421  private Expression definedExpr (SymtabEntry entry) throws IOException, ParseException
422  {
423    if (token.equals (Token.Identifier) && token.name.equals ("defined"))
424      match (Token.Identifier);
425    return equalityExpr (null, entry);
426  } // definedExpr
427
428  /**
429   *
430   **/
431  private Expression equalityExpr (Expression e, SymtabEntry entry) throws IOException, ParseException
432  {
433    if (e == null)
434    {
435      parser.token = token; // Since parser to parse, give it this token
436      e = parser.constExp (entry);
437      token = parser.token; // Since parser last parsed, get its token
438    }
439    else
440    {
441      BinaryExpr b = (BinaryExpr)e;
442      parser.token = token; // Since parser to parse, give it this token
443      Expression constExpr = parser.constExp (entry);
444      token = parser.token; // Since parser last parsed, get its token
445      b.right (constExpr);
446      e.rep (e.rep () + b.right ().rep ());
447    }
448    if (token.equals (Token.DoubleEqual))
449    {
450      match (token.type);
451      Equal eq = parser.exprFactory.equal (e, null);
452      eq.rep (e.rep () + " == ");
453      return equalityExpr (eq, entry);
454    }
455    else if (token.equals (Token.NotEqual))
456    {
457      match (token.type);
458      NotEqual n = parser.exprFactory.notEqual (e, null);
459      n.rep (e.rep () + " != ");
460      return equalityExpr (n, entry);
461    }
462    else if (token.equals (Token.GreaterThan))
463    {
464      match (token.type);
465      GreaterThan g = parser.exprFactory.greaterThan (e, null);
466      g.rep (e.rep () + " > ");
467      return equalityExpr (g, entry);
468    }
469    else if (token.equals (Token.GreaterEqual))
470    {
471      match (token.type);
472      GreaterEqual g = parser.exprFactory.greaterEqual (e, null);
473      g.rep (e.rep () + " >= ");
474      return equalityExpr (g, entry);
475    }
476    else if (token.equals (Token.LessThan))
477    {
478      match (token.type);
479      LessThan l = parser.exprFactory.lessThan (e, null);
480      l.rep (e.rep () + " < ");
481      return equalityExpr (l, entry);
482    }
483    else if (token.equals (Token.LessEqual))
484    {
485      match (token.type);
486      LessEqual l = parser.exprFactory.lessEqual (e, null);
487      l.rep (e.rep () + " <= ");
488      return equalityExpr (l, entry);
489    }
490    else
491      return e;
492  } // equalityExpr
493
494  /**
495   *
496   **/
497  Expression primaryExpr (SymtabEntry entry) throws IOException, ParseException
498  {
499    Expression primary = null;
500    switch (token.type)
501    {
502      case Token.Identifier:
503        // If an identifier gets this far, it means that no
504        // preprocessor variable was defined with that name.
505        // Generate a FALSE boolean expr.
506        //daz        primary = parser.exprFactory.terminal ("0", new Long (0));
507        primary = parser.exprFactory.terminal ("0", BigInteger.valueOf (0));
508        token = scanner.getToken ();
509        break;
510      case Token.BooleanLiteral:
511      case Token.CharacterLiteral:
512      case Token.IntegerLiteral:
513      case Token.FloatingPointLiteral:
514      case Token.StringLiteral:
515        //daz        primary = parser.literal ();
516        primary = parser.literal (entry);
517        token = parser.token;
518        break;
519      case Token.LeftParen:
520        match (Token.LeftParen);
521        primary = booleanConstExpr (entry);
522        match (Token.RightParen);
523        primary.rep ('(' + primary.rep () + ')');
524        break;
525      default:
526        int[] expected = {Token.Literal, Token.LeftParen};
527        throw ParseException.syntaxError (scanner, expected, token.type);
528    }
529    return primary;
530  } // primaryExpr
531
532  /**
533   *
534   **/
535  private void ifDefine (boolean inParens, boolean not) throws IOException, ParseException
536  {
537    if (token.equals (Token.Identifier))
538      if ((not && symbols.containsKey (token.name)) || (!not && !symbols.containsKey (token.name)))
539      {
540        alreadyProcessedABranch.push (new Boolean (false));
541        skipToEndiforElse ();
542      }
543      else
544      {
545        alreadyProcessedABranch.push (new Boolean (true));
546        match (Token.Identifier);
547        if (inParens)
548          match (Token.RightParen);
549      }
550    else
551      throw ParseException.syntaxError (scanner, Token.Identifier, token.type);
552  } // ifDefine
553
554  /**
555   *
556   **/
557  private void ifdef (boolean not) throws IOException, ParseException
558  {
559    if (not)
560      match (Token.Ifndef);
561    else
562      match (Token.Ifdef);
563    if (token.equals (Token.Identifier))
564      if ((not && symbols.containsKey (token.name)) || (!not && !symbols.containsKey (token.name)))
565      {
566        alreadyProcessedABranch.push (new Boolean (false));
567        skipToEndiforElse ();
568      }
569      else
570      {
571        alreadyProcessedABranch.push (new Boolean (true));
572        match (Token.Identifier);
573      }
574    else
575      throw ParseException.syntaxError (scanner, Token.Identifier, token.type);
576  } // ifdef
577
578  /**
579   *
580   **/
581  private void elif () throws IOException, ParseException
582  {
583    if (alreadyProcessedABranch.empty ())
584      throw ParseException.elseNoIf (scanner);
585    else if (((Boolean)alreadyProcessedABranch.peek ()).booleanValue ())
586      skipToEndif ();
587    else
588    {
589      match (Token.Elif);
590      constExpr ();
591    }
592  } // elif
593
594  /**
595   *
596   **/
597  private void skipToEndiforElse () throws IOException, ParseException
598  {
599    while (!token.equals (Token.Endif) && !token.equals (Token.Else) && !token.equals (Token.Elif))
600    {
601      if (token.equals (Token.Ifdef) || token.equals (Token.Ifndef))
602      {
603        alreadyProcessedABranch.push (new Boolean (true));
604        skipToEndif ();
605      }
606      else
607        token = scanner.skipUntil ('#');
608    }
609    process (token);
610  } // skipToEndiforElse
611
612  /**
613   *
614   **/
615  private void skipToEndif () throws IOException, ParseException
616  {
617    while (!token.equals (Token.Endif))
618    {
619      token = scanner.skipUntil ('#');
620      if (token.equals (Token.Ifdef) || token.equals (Token.Ifndef))
621      {
622        alreadyProcessedABranch.push (new Boolean (true));
623        skipToEndif ();
624      }
625    }
626    alreadyProcessedABranch.pop ();
627    match (Token.Endif);
628  } // skipToEndif
629
630  ///////////////
631  // For Pragma
632
633  /**
634   *
635   **/
636  private void pragma () throws IOException, ParseException
637  {
638    match (Token.Pragma);
639    String pragmaType = token.name;
640
641    // <d59165> Enable escaped identifiers while processing pragma internals.
642    // Don't enable until scanning pragma name!
643    scanner.escapedOK = true;
644    match (Token.Identifier);
645
646    // Add pragma entry to container
647    PragmaEntry pragmaEntry = parser.stFactory.pragmaEntry (parser.currentModule);
648    pragmaEntry.name (pragmaType);
649    pragmaEntry.sourceFile (scanner.fileEntry ());
650    pragmaEntry.data (scanner.currentLine ());
651    if (parser.currentModule instanceof ModuleEntry)
652      ((ModuleEntry)parser.currentModule).addContained (pragmaEntry);
653    else if (parser.currentModule instanceof InterfaceEntry)
654      ((InterfaceEntry)parser.currentModule).addContained (pragmaEntry);
655
656    // If the token was an identifier, then pragmaType WILL be non-null.
657    if (pragmaType.equals ("ID"))
658      idPragma ();
659    else if (pragmaType.equals ("prefix"))
660      prefixPragma ();
661    else if (pragmaType.equals ("version"))
662      versionPragma ();
663
664    // we are adding extensions to the Sun's idlj compiler to
665    // handle correct code generation for local Objects, where
666    // the OMG is taking a long time to formalize stuff.  Good
667    // example of this is poa.idl.  Two proprietory pragmas
668    // sun_local and sun_localservant are defined.  sun_local
669    // generates only Holder and Helper classes, where read
670    // and write methods throw marshal exceptions.  sun_localservant
671    // is to generate Helper, Holder, and only Skel with _invoke
672    // throwing an exception, since it does not make sense for
673    // local objects.
674
675    else if (pragmaType.equals ("sun_local"))
676      localPragma();
677    else if (pragmaType.equals ("sun_localservant"))
678      localServantPragma();
679    else
680    {
681      otherPragmas (pragmaType, tokenToString ());
682      token = scanner.getToken ();
683    }
684
685    scanner.escapedOK = false; // <d59165> Disable escaped identifiers.
686  } // pragma
687
688  // <d57110> Pragma ID can be appiled to modules and it is an error to
689  // name a type in more than one ID pragma directive.
690
691  private Vector PragmaIDs = new Vector ();
692
693  private void localPragma () throws IOException, ParseException
694  {
695    // Before I can use a parser method, I must make sure it has the current token.
696    parser.token = token;
697    // this makes sense only for interfaces, if specified for modules,
698    // parser should throw an error
699    SymtabEntry anErrorOccurred = new SymtabEntry ();
700    SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred);
701    // Was the indicated type found in the symbol table?
702    if (entry == anErrorOccurred)
703    {
704        System.out.println("Error occured ");
705      // Don't have to generate an error, scopedName already has.
706      scanner.skipLineComment ();
707      token = scanner.getToken ();
708    }
709    else
710    {
711      // by this time we have already parsed the ModuleName and the
712      // pragma type, therefore setInterfaceType
713      if (entry instanceof InterfaceEntry) {
714          InterfaceEntry ent = (InterfaceEntry) entry;
715          ent.setInterfaceType (InterfaceEntry.LOCAL_SIGNATURE_ONLY);
716      }
717      token = parser.token;
718      String string = token.name;
719      match (Token.StringLiteral);
720      // for non-interfaces it doesn't make sense, so just ignore it
721    }
722  } // localPragma
723
724  private void localServantPragma () throws IOException, ParseException
725  {
726    // Before I can use a parser method, I must make sure it has the current token.
727    parser.token = token;
728    // this makes sense only for interfaces, if specified for modules,
729    // parser should throw an error
730    SymtabEntry anErrorOccurred = new SymtabEntry ();
731    SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred);
732
733    // Was the indicated type found in the symbol table?
734    if (entry == anErrorOccurred)
735    {
736      // Don't have to generate an error, scopedName already has.
737      scanner.skipLineComment ();
738      token = scanner.getToken ();
739        System.out.println("Error occured ");
740    }
741    else
742    {
743      // by this time we have already parsed the ModuleName and the
744      // pragma type, therefore setInterfaceType
745      if (entry instanceof InterfaceEntry) {
746          InterfaceEntry ent = (InterfaceEntry) entry;
747          ent.setInterfaceType (InterfaceEntry.LOCALSERVANT);
748      }
749      token = parser.token;
750      String string = token.name;
751      match (Token.StringLiteral);
752      // for non-interfaces it doesn't make sense, so just ignore it
753    }
754  } // localServantPragma
755
756
757  /**
758   *
759   **/
760  private void idPragma () throws IOException, ParseException
761  {
762    // Before I can use a parser method, I must make sure it has the current token.
763    parser.token = token;
764
765    // <d57110> This flag will relax the restriction that the scopedNamed
766    // in this ID pragma directive cannot resolve to a module.
767    parser.isModuleLegalType (true);
768    SymtabEntry anErrorOccurred = new SymtabEntry ();
769    SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred);
770    parser.isModuleLegalType (false);  // <57110>
771
772    // Was the indicated type found in the symbol table?
773    if (entry == anErrorOccurred)
774    {
775      // Don't have to generate an error, scopedName already has.
776      scanner.skipLineComment ();
777      token = scanner.getToken ();
778    }
779    // <d57110>
780    //else if (PragmaIDs.contains (entry))
781    //{
782    //  ParseException.badRepIDAlreadyAssigned (scanner, entry.name ());
783    //  scanner.skipLineComment ();
784    //  token = scanner.getToken ();
785    //}
786    else
787    {
788      token = parser.token;
789      String string = token.name;
790      // Do not match token until after raise exceptions, otherwise
791      // incorrect messages will be emitted!
792      if (PragmaIDs.contains (entry)) // <d57110>
793      {
794        ParseException.badRepIDAlreadyAssigned (scanner, entry.name ());
795      }
796      else if (!RepositoryID.hasValidForm (string)) // <d57110>
797      {
798        ParseException.badRepIDForm (scanner, string);
799      }
800      else
801      {
802        entry.repositoryID (new RepositoryID (string));
803        PragmaIDs.addElement (entry); // <d57110>
804      }
805      match (Token.StringLiteral);
806    }
807  } // idPragma
808
809  /**
810   *
811   **/
812  private void prefixPragma () throws IOException, ParseException
813  {
814    String string = token.name;
815    match (Token.StringLiteral);
816    ((IDLID)parser.repIDStack.peek ()).prefix (string);
817    ((IDLID)parser.repIDStack.peek ()).name ("");
818  } // prefixPragma
819
820  /**
821   *
822   **/
823  private void versionPragma () throws IOException, ParseException
824  {
825    // Before I can use a parser method, I must make sure it has the current token.
826    parser.token = token;
827    // This flag will relax the restriction that the scopedNamed
828    // in this Version pragma directive cannot resolve to a module.
829    parser.isModuleLegalType (true);
830    SymtabEntry anErrorOccurred = new SymtabEntry ();
831    SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred);
832    // reset the flag to original value
833    parser.isModuleLegalType (false);
834    if (entry == anErrorOccurred)
835    {
836      // Don't have to generate an error, scopedName already has.
837      scanner.skipLineComment ();
838      token = scanner.getToken ();
839    }
840    else
841    {
842      token = parser.token;
843      String string = token.name;
844      match (Token.FloatingPointLiteral);
845      if (entry.repositoryID () instanceof IDLID)
846        ((IDLID)entry.repositoryID ()).version (string);
847    }
848  } // versionPragma
849
850  private Vector pragmaHandlers = new Vector ();
851
852  /**
853   *
854   **/
855  void registerPragma (PragmaHandler handler)
856  {
857    pragmaHandlers.addElement (handler);
858  } // registerPragma
859
860  /**
861   *
862   **/
863  private void otherPragmas (String pragmaType, String currentToken) throws IOException
864  {
865    for (int i = pragmaHandlers.size () - 1; i >= 0; --i)
866    {
867      PragmaHandler handler = (PragmaHandler)pragmaHandlers.elementAt (i);
868      if (handler.process (pragmaType, currentToken))
869                break;
870    }
871  } // otherPragmas
872
873  /*
874   * These protected methods are used by extenders, by the code
875   * which implements otherPragma.
876   */
877
878  /**
879   * Get the current token.
880   **/
881  String currentToken ()
882  {
883    return tokenToString ();
884  } // currentToken
885
886  /**
887   * This method, given an entry name, returns the entry with that name.
888   * It can take fully or partially qualified names and returns the
889   * appropriate entry defined within the current scope.  If no entry
890   * exists, null is returned.
891   **/
892  SymtabEntry getEntryForName (String string)
893  {
894    boolean partialScope = false;
895    boolean globalScope  = false;
896
897    // Change all ::'s to /'s
898    if (string.startsWith ("::"))
899    {
900      globalScope = true;
901      string = string.substring (2);
902    }
903    int index = string.indexOf ("::");
904    while (index >= 0)
905    {
906      partialScope = true;
907      string = string.substring (0, index) + '/' + string.substring (index + 2);
908      index = string.indexOf ("::");
909    }
910
911    // Get the entry for that string
912    SymtabEntry entry = null;
913    if (globalScope)
914      entry = parser.recursiveQualifiedEntry (string);
915    else if (partialScope)
916      entry = parser.recursivePQEntry (string, parser.currentModule);
917    else
918      entry = parser.unqualifiedEntryWMod (string, parser.currentModule);
919    return entry;
920  } // getEntryForName
921
922  /**
923   * This method returns a string of all of the characters from the
924   * input file from the current position up to, but not including,
925   * the end-of-line character(s).
926   **/
927  String getStringToEOL () throws IOException
928  {
929    return scanner.getStringToEOL ();
930  } // getStringToEOL
931
932  /**
933   * This method returns a string of all of the characters from the
934   * input file from the current position up to, but not including,
935   * the given character.  It encapsulates parenthesis and quoted strings,
936   * meaning it does not stop if the given character is found within
937   * parentheses or quotes.  For instance, given the input of
938   * `start(inside)end', getUntil ('n') will return "start(inside)e"
939   **/
940  String getUntil (char c) throws IOException
941  {
942    return scanner.getUntil (c);
943  } // getUntil
944
945  private boolean lastWasMacroID = false;
946
947  /**
948   *
949   **/
950  private String tokenToString ()
951  {
952    if (token.equals (Token.MacroIdentifier))
953    {
954      lastWasMacroID = true;
955      return token.name;
956    }
957    else if (token.equals (Token.Identifier))
958      return token.name;
959    else
960      return token.toString ();
961  } // tokenToString
962
963  /**
964   * This method returns the next token String from the input file.
965   **/
966  String nextToken () throws IOException
967  {
968    if (lastWasMacroID)
969    {
970      lastWasMacroID = false;
971      return "(";
972    }
973    else
974    {
975      token = scanner.getToken ();
976      return tokenToString ();
977    }
978  } // nextToken
979
980  /**
981   * This method assumes that the current token marks the beginning
982   * of a scoped name.  It then parses the subsequent identifier and
983   * double colon tokens, builds the scoped name, and finds the symbol
984   * table entry with that name.
985   **/
986  SymtabEntry scopedName () throws IOException
987  {
988    boolean     globalScope  = false;
989    boolean     partialScope = false;
990    String      name         = null;
991    SymtabEntry entry        = null;
992    try
993    {
994      if (token.equals (Token.DoubleColon))
995        globalScope = true;
996      else
997      {
998        if (token.equals (Token.Object))
999        {
1000          name = "Object";
1001          match (Token.Object);
1002        }
1003        else if (token.type == Token.ValueBase)
1004        {
1005          name = "ValueBase";
1006          match (Token.ValueBase);
1007        }
1008        else
1009        {
1010          name = token.name;
1011          match (Token.Identifier);
1012        }
1013      }
1014      while (token.equals (Token.DoubleColon))
1015      {
1016        match (Token.DoubleColon);
1017        partialScope = true;
1018        if (name != null)
1019          name = name + '/' + token.name;
1020        else
1021          name = token.name;
1022        match (Token.Identifier);
1023      }
1024      if (globalScope)
1025        entry = parser.recursiveQualifiedEntry (name);
1026      else if (partialScope)
1027        entry = parser.recursivePQEntry (name, parser.currentModule);
1028      else
1029        entry = parser.unqualifiedEntryWMod (name, parser.currentModule);
1030    }
1031    catch (ParseException e)
1032    {
1033      entry = null;
1034    }
1035    return entry;
1036  } // scopedName
1037
1038  /**
1039   * Skip to the end of the line.
1040   **/
1041  void skipToEOL () throws IOException
1042  {
1043    scanner.skipLineComment ();
1044  } // skipToEOL
1045
1046  /**
1047   * This method skips the data in the input file until the specified
1048   * character is encountered, then it returns the next token.
1049   **/
1050  String skipUntil (char c) throws IOException
1051  {
1052    if (!(lastWasMacroID && c == '('))
1053      token = scanner.skipUntil (c);
1054    return tokenToString ();
1055  } // skipUntil
1056
1057  /**
1058   * This method displays a Parser Exception complete with line number
1059   * and position information with the given message string.
1060   **/
1061  void parseException (String message)
1062  {
1063    // <d62023> Suppress warnings
1064    if (!parser.noWarn)
1065      ParseException.warning (scanner, message);
1066  } // parseException
1067
1068  // For Pragma
1069  ///////////////
1070  // For macro expansion
1071
1072  /**
1073   *
1074   **/
1075  String expandMacro (String macroDef, Token t) throws IOException, ParseException
1076  {
1077    token = t;
1078    // Get the parameter values from the macro 'call'
1079    Vector parmValues = getParmValues ();
1080
1081    // Get the parameter names from the macro definition
1082    // NOTE:  a newline character is appended here so that when
1083    // getStringToEOL is called, it stops scanning at the end
1084    // of this string.
1085    scanner.scanString (macroDef + '\n');
1086    Vector parmNames = new Vector ();
1087    macro (parmNames);
1088
1089    if (parmValues.size () < parmNames.size ())
1090      throw ParseException.syntaxError (scanner, Token.Comma, Token.RightParen);
1091    else if (parmValues.size () > parmNames.size ())
1092      throw ParseException.syntaxError (scanner, Token.RightParen, Token.Comma);
1093
1094    macroDef = scanner.getStringToEOL ();
1095    for (int i = 0; i < parmNames.size (); ++i)
1096      macroDef = replaceAll (macroDef, (String)parmNames.elementAt (i), (String)parmValues.elementAt (i));
1097    return removeDoublePound (macroDef);
1098  } // expandMacro
1099
1100  // This method is only used by the macro expansion methods.
1101  /**
1102   *
1103   **/
1104  private void miniMatch (int type) throws ParseException
1105  {
1106    // A normal production would now execute:
1107    // match (type);
1108    // But match reads the next token.  I don't want to do that now.
1109    // Just make sure the current token is a 'type'.
1110    if (!token.equals (type))
1111      throw ParseException.syntaxError (scanner, type, token.type);
1112  } // miniMatch
1113
1114  /**
1115   *
1116   **/
1117  private Vector getParmValues () throws IOException, ParseException
1118  {
1119    Vector values = new Vector ();
1120    if (token.equals (Token.Identifier))
1121    {
1122      match (Token.Identifier);
1123      miniMatch (Token.LeftParen);
1124    }
1125    else if (!token.equals (Token.MacroIdentifier))
1126      throw ParseException.syntaxError (scanner, Token.Identifier, token.type);
1127
1128    if (!token.equals (Token.RightParen))
1129    {
1130      values.addElement (scanner.getUntil (',', ')').trim ());
1131      token = scanner.getToken ();
1132      macroParmValues (values);
1133    }
1134    return values;
1135  } // getParmValues
1136
1137  /**
1138   *
1139   **/
1140  private void macroParmValues (Vector values) throws IOException, ParseException
1141  {
1142    while (!token.equals (Token.RightParen))
1143    {
1144      miniMatch (Token.Comma);
1145      values.addElement (scanner.getUntil (',', ')').trim ());
1146      token = scanner.getToken ();
1147    }
1148  } // macroParmValues
1149
1150  /**
1151   *
1152   **/
1153  private void macro (Vector parmNames) throws IOException, ParseException
1154  {
1155    match (token.type);
1156    match (Token.LeftParen);
1157    macroParms (parmNames);
1158    miniMatch (Token.RightParen);
1159  } // macro
1160
1161  /**
1162   *
1163   **/
1164  private void macroParms (Vector parmNames) throws IOException, ParseException
1165  {
1166    if (!token.equals (Token.RightParen))
1167    {
1168      parmNames.addElement (token.name);
1169      match (Token.Identifier);
1170      macroParms2 (parmNames);
1171    }
1172  } // macroParms
1173
1174  /**
1175   *
1176   **/
1177  private void macroParms2 (Vector parmNames) throws IOException, ParseException
1178  {
1179    while (!token.equals (Token.RightParen))
1180    {
1181      match (Token.Comma);
1182      parmNames.addElement (token.name);
1183      match (Token.Identifier);
1184    }
1185  } // macroParms2
1186
1187  /**
1188   *
1189   **/
1190  private String replaceAll (String string, String from, String to)
1191  {
1192    int index = 0;
1193    while (index != -1)
1194    {
1195      index = string.indexOf (from, index);
1196      if (index != -1)
1197      {
1198        if (!embedded (string, index, index + from.length ()))
1199          if (index > 0 && string.charAt(index) == '#')
1200            string = string.substring (0, index) + '"' + to + '"' + string.substring (index + from.length ());
1201          else
1202            string = string.substring (0, index) + to + string.substring (index + from.length ());
1203        index += to.length ();
1204      }
1205    }
1206    return string;
1207  } // replaceAll
1208
1209  /**
1210   *
1211   **/
1212  private boolean embedded (String string, int index, int endIndex)
1213  {
1214    // Don't replace if found substring is not an independent id.
1215    // For example, don't replace "thither".indexOf ("it", 0)
1216    boolean ret    = false;
1217    char    preCh  = index == 0 ? ' ' : string.charAt (index - 1);
1218    char    postCh = endIndex >= string.length () - 1 ? ' ' : string.charAt (endIndex);
1219    if ((preCh >= 'a' && preCh <= 'z') || (preCh >= 'A' && preCh <= 'Z'))
1220      ret = true;
1221    else if ((postCh >= 'a' && postCh <= 'z') || (postCh >= 'A' && postCh <= 'Z') || (postCh >= '0' && postCh <= '9') || postCh == '_')
1222      ret = true;
1223    else
1224      ret = inQuotes (string, index);
1225    return ret;
1226  } // embedded
1227
1228  /**
1229   *
1230   **/
1231  private boolean inQuotes (String string, int index)
1232  {
1233    int quoteCount = 0;
1234    for (int i = 0; i < index; ++i)
1235      if (string.charAt (i) == '"') ++quoteCount;
1236    // If there are an odd number of quotes before this region,
1237    // then this region is within quotes
1238    return quoteCount % 2 != 0;
1239  } // inQuotes
1240
1241  /**
1242   * Remove any occurrences of ##.
1243   **/
1244  private String removeDoublePound (String string)
1245  {
1246    int index = 0;
1247    while (index != -1)
1248    {
1249      index = string.indexOf ("##", index);
1250      if (index != -1)
1251      {
1252        int startSkip = index - 1;
1253        int stopSkip  = index + 2;
1254        if (startSkip < 0)
1255          startSkip = 0;
1256        if (stopSkip >= string.length ())
1257          stopSkip = string.length () - 1;
1258        while (startSkip > 0 &&
1259               (string.charAt (startSkip) == ' ' ||
1260               string.charAt (startSkip) == '\t'))
1261          --startSkip;
1262        while (stopSkip < string.length () - 1 &&
1263               (string.charAt (stopSkip) == ' ' ||
1264               string.charAt (stopSkip) == '\t'))
1265          ++stopSkip;
1266        string = string.substring (0, startSkip + 1) + string.substring (stopSkip);
1267      }
1268    }
1269    return string;
1270  } // removeDoublePound
1271
1272  // For macro expansion
1273  ///////////////
1274
1275  /**
1276   *
1277   **/
1278  private String getFilename (String name) throws FileNotFoundException
1279  {
1280    String fullName = null;
1281    File file = new File (name);
1282    if (file.canRead ())
1283      fullName = name;
1284    else
1285    {
1286      Enumeration pathList = parser.paths.elements ();
1287      while (!file.canRead () && pathList.hasMoreElements ())
1288      {
1289        fullName = (String)pathList.nextElement () + File.separatorChar + name;
1290        file = new File (fullName);
1291      }
1292      if (!file.canRead ())
1293        throw new FileNotFoundException (name);
1294    }
1295    return fullName;
1296  } // getFilename
1297
1298  /**
1299   *
1300   **/
1301  private void match (int type) throws IOException, ParseException
1302  {
1303    if (!token.equals (type))
1304      throw ParseException.syntaxError (scanner, type, token.type);
1305    token = scanner.getToken ();
1306
1307    // <d62023> Added for convenience, but commented-out because there is
1308    // no reason to issue warnings for tokens scanned during preprocessing.
1309    // See issueTokenWarnings().
1310    //issueTokenWarnings ();
1311
1312    //System.out.println ("Preprocessor.match token = " + token.type);
1313    //if (token.equals (Token.Identifier) || token.equals (Token.MacroIdentifier))
1314    //  System.out.println ("Preprocessor.match token name = " + token.name);
1315
1316    // If the token is a defined thingy, scan the defined string
1317    // instead of the input stream for a while.
1318    if (token.equals (Token.Identifier) || token.equals (Token.MacroIdentifier))
1319    {
1320      String string = (String)symbols.get (token.name);
1321      if (string != null && !string.equals (""))
1322        // If this is a macro, parse the macro
1323        if (macros.contains (token.name))
1324        {
1325          scanner.scanString (expandMacro (string, token));
1326          token = scanner.getToken ();
1327        }
1328        // else this is just a normal define
1329        else
1330        {
1331          scanner.scanString (string);
1332          token = scanner.getToken ();
1333        }
1334    }
1335  } // match
1336
1337  // <d62023>
1338  /**
1339   * Issue warnings about tokens scanned during preprocessing.
1340   **/
1341  private void issueTokenWarnings ()
1342  {
1343    if (parser.noWarn)
1344      return;
1345
1346    // There are no keywords defined for preprocessing (only directives), so:
1347    //
1348    // 1.) Do not issue warnings for identifiers known to be keywords in
1349    //     another level of IDL.
1350    // 2.) Do not issue warnings for identifiers that collide with keywords
1351    //     in letter, but not case.
1352    // 3.) Do not issue warnings for deprecated keywords.
1353    //
1354    // Should we warn when a macro identifier replaces a keyword?  Hmmm.
1355
1356    // Deprecated directives?  None to date.
1357    //if (token.isDirective () && token.isDeprecated ())
1358    //  ParseException.warning (scanner, Util.getMesage ("Deprecated.directive", token.name));
1359  } // issueTokenWarnings
1360
1361  /**
1362   * This method is called when the parser encounters a left curly brace.
1363   * An extender of PragmaHandler may find scope information useful.
1364   * For example, the prefix pragma takes effect as soon as it is
1365   * encountered and stays in effect until the current scope is closed.
1366   * If a similar pragma extension is desired, then the openScope and
1367   * closeScope methods are available for overriding.
1368   * @param entry the symbol table entry whose scope has just been opened.
1369   *  Be aware that, since the scope has just been entered, this entry is
1370   *  incomplete at this point.
1371   **/
1372  void openScope (SymtabEntry entry)
1373  {
1374    for (int i = pragmaHandlers.size () - 1; i >= 0; --i)
1375    {
1376      PragmaHandler handler = (PragmaHandler)pragmaHandlers.elementAt (i);
1377      handler.openScope (entry);
1378    }
1379  } // openScope
1380
1381  /**
1382   * This method is called when the parser encounters a right curly brace.
1383   * An extender of PragmaHandler may find scope information useful.
1384   * For example, the prefix pragma takes effect as soon as it is
1385   * encountered and stays in effect until the current scope is closed.
1386   * If a similar pragma extension is desired, then the openScope and
1387   * closeScope methods are available for overriding.
1388   * @param entry the symbol table entry whose scope has just been closed.
1389   **/
1390  void closeScope (SymtabEntry entry)
1391  {
1392    for (int i = pragmaHandlers.size () - 1; i >= 0; --i)
1393    {
1394      PragmaHandler handler = (PragmaHandler)pragmaHandlers.elementAt (i);
1395      handler.closeScope (entry);
1396    }
1397  } // closeScope
1398
1399  private Parser    parser;
1400  private Scanner   scanner;
1401  private Hashtable symbols;
1402  private Vector    macros;
1403
1404  // The logic associated with this stack is scattered above.
1405  // A concise map of the logic is:
1406  // case #if false, #ifdef false, #ifndef true
1407  //   push (false);
1408  //   skipToEndifOrElse ();
1409  // case #if true, #ifdef true, #ifndef false
1410  //   push (true);
1411  // case #elif <conditional>
1412  //   if (top == true)
1413  //     skipToEndif ();
1414  //   else if (conditional == true)
1415  //     pop ();
1416  //     push (true);
1417  //   else if (conditional == false)
1418  //     skipToEndifOrElse ();
1419  // case #else
1420  //   if (top == true)
1421  //     skipToEndif ();
1422  //   else
1423  //     pop ();
1424  //     push (true);
1425  // case #endif
1426  //   pop ();
1427  private        Stack  alreadyProcessedABranch = new Stack ();
1428                 Token  token;
1429
1430  private static String indent = "";
1431}
1432