| /* |
| * Copyright (c) 1999, 2000, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| /* |
| * COMPONENT_NAME: idl.parser |
| * |
| * ORIGINS: 27 |
| * |
| * Licensed Materials - Property of IBM |
| * 5639-D57 (C) COPYRIGHT International Business Machines Corp. 1997, 1999 |
| * RMI-IIOP v1.0 |
| * |
| */ |
| |
| package com.sun.tools.corba.se.idl; |
| |
| // NOTES: |
| // -D57110<daz> Allow ID pragma directive to be applied to modules and update |
| // feature in accordance to CORBA 2.3. |
| // -D59165<daz> Enable escaped identifiers when processing pragmas. |
| // -f60858.1<daz> Support -corba option, level = 2.2: Accept identifiers that |
| // collide with keywords, in letter but not case, and issue a warning. |
| // -d62023 <daz> support -noWarn option; suppress inappropriate warnings when |
| // parsing IBM-specific pragmas (#meta <interface_name> abstract). |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Stack; |
| import java.util.Vector; |
| |
| import com.sun.tools.corba.se.idl.RepositoryID; |
| |
| import com.sun.tools.corba.se.idl.constExpr.*; |
| |
| /** |
| * This class should be extended if new pragmas are desired. If the |
| * preprocessor encounters a pragma name which it doesn't recognize |
| * (anything other than ID, prefix, or version), it calls the method |
| * otherPragmas. This is the only method which need be overridden. |
| * The Preprocessor base class has a number of utility-like methods |
| * which can be used by the overridden otherPragmas method. |
| **/ |
| public class Preprocessor |
| { |
| /** |
| * Public zero-argument constructor. |
| **/ |
| Preprocessor () |
| { |
| } // ctor |
| |
| /** |
| * |
| **/ |
| void init (Parser p) |
| { |
| parser = p; |
| symbols = p.symbols; |
| macros = p.macros; |
| } // init |
| |
| /** |
| * |
| **/ |
| protected Object clone () |
| { |
| return new Preprocessor (); |
| } // clone |
| |
| /** |
| * |
| **/ |
| Token process (Token t) throws IOException, ParseException |
| { |
| token = t; |
| scanner = parser.scanner; |
| // <f46082.40> Deactivate escaped identifier processing in Scanner while |
| // preprocessing. |
| //scanner.underscoreOK = true; |
| scanner.escapedOK = false; |
| try |
| { |
| switch (token.type) |
| { |
| case Token.Include: |
| include (); |
| break; |
| case Token.If: |
| ifClause (); |
| break; |
| case Token.Ifdef: |
| ifdef (false); |
| break; |
| case Token.Ifndef: |
| ifdef (true); |
| break; |
| case Token.Else: |
| if (alreadyProcessedABranch.empty ()) |
| throw ParseException.elseNoIf (scanner); |
| else if (((Boolean)alreadyProcessedABranch.peek ()).booleanValue ()) |
| skipToEndif (); |
| else |
| { |
| alreadyProcessedABranch.pop (); |
| alreadyProcessedABranch.push (new Boolean (true)); |
| token = scanner.getToken (); |
| } |
| break; |
| case Token.Elif: |
| elif (); |
| break; |
| case Token.Endif: |
| if (alreadyProcessedABranch.empty ()) |
| throw ParseException.endNoIf (scanner); |
| else |
| { |
| alreadyProcessedABranch.pop (); |
| token = scanner.getToken (); |
| break; |
| } |
| case Token.Define: |
| define (); |
| break; |
| case Token.Undef: |
| undefine (); |
| break; |
| case Token.Pragma: |
| pragma (); |
| break; |
| case Token.Unknown: |
| if (!parser.noWarn) |
| ParseException.warning (scanner, Util.getMessage ("Preprocessor.unknown", token.name)); |
| case Token.Error: |
| case Token.Line: |
| case Token.Null: |
| // ignore |
| default: |
| scanner.skipLineComment (); |
| token = scanner.getToken (); |
| } |
| } |
| catch (IOException e) |
| { |
| // <f46082.40> Underscore may now precede any identifier, so underscoreOK |
| // is vestigal. The Preprocessor must reset escapedOK so that Scanner |
| // will process escaped identifiers according to specification. |
| //scanner.underscoreOK = false; |
| scanner.escapedOK = true; |
| throw e; |
| } |
| catch (ParseException e) |
| { |
| // <f46082.40> See above. |
| //scanner.underscoreOK = false; |
| scanner.escapedOK = true; |
| throw e; |
| } |
| // <f46082.40> See above. |
| //scanner.underscoreOK = false; |
| scanner.escapedOK = true; |
| return token; |
| } // process |
| |
| /** |
| * |
| **/ |
| private void include () throws IOException, ParseException |
| { |
| match (Token.Include); |
| IncludeEntry include = parser.stFactory.includeEntry (parser.currentModule); |
| include.sourceFile (scanner.fileEntry ()); |
| scanner.fileEntry ().addInclude (include); |
| if (token.type == Token.StringLiteral) |
| include2 (include); |
| else if (token.type == Token.LessThan) |
| include3 (include); |
| else |
| { |
| int[] expected = {Token.StringLiteral, Token.LessThan}; |
| throw ParseException.syntaxError (scanner, expected, token.type); |
| } |
| if (parser.currentModule instanceof ModuleEntry) |
| ((ModuleEntry)parser.currentModule).addContained (include); |
| else if (parser.currentModule instanceof InterfaceEntry) |
| ((InterfaceEntry)parser.currentModule).addContained (include); |
| } // include |
| |
| /** |
| * |
| **/ |
| private void include2 (IncludeEntry include) throws IOException, ParseException |
| { |
| include.name ('"' + token.name + '"'); |
| include4 (include, token.name); |
| match (Token.StringLiteral); |
| } // include2 |
| |
| /** |
| * |
| **/ |
| private void include3 (IncludeEntry include) throws IOException, ParseException |
| { |
| if (token.type != Token.LessThan) |
| // match will throw an exception |
| match (Token.LessThan); |
| else |
| { |
| try |
| { |
| String includeFile = getUntil ('>'); |
| token = scanner.getToken (); |
| include.name ('<' + includeFile + '>'); |
| include4 (include, includeFile); |
| match (Token.GreaterThan); |
| } |
| catch (IOException e) |
| { |
| throw ParseException.syntaxError (scanner, ">", "EOF"); |
| } |
| } |
| } // include3 |
| |
| /** |
| * |
| **/ |
| private void include4 (IncludeEntry include, String filename) throws IOException, ParseException |
| { |
| try |
| { |
| // If the #include is at the global scope, it is treated as |
| // an import statement. If it is within some other scope, it |
| // is treated as a normal #include. |
| boolean includeIsImport = parser.currentModule == parser.topLevelModule; |
| //daz |
| include.absFilename (Util.getAbsolutePath (filename, parser.paths)); |
| scanner.scanIncludedFile (include, getFilename (filename), includeIsImport); |
| } |
| catch (IOException e) |
| { |
| ParseException.generic (scanner, e.toString ()); |
| } |
| } // include4 |
| |
| /** |
| * |
| **/ |
| private void define () throws IOException, ParseException |
| { |
| match (Token.Define); |
| if (token.equals (Token.Identifier)) |
| { |
| String symbol = scanner.getStringToEOL (); |
| symbols.put (token.name, symbol.trim ()); |
| match (Token.Identifier); |
| } |
| else if (token.equals (Token.MacroIdentifier)) |
| { |
| symbols.put (token.name, '(' + scanner.getStringToEOL () . trim ()); |
| macros.addElement (token.name); |
| match (Token.MacroIdentifier); |
| } |
| else |
| throw ParseException.syntaxError (scanner, Token.Identifier, token.type); |
| } // define |
| |
| /** |
| * |
| **/ |
| private void undefine () throws IOException, ParseException |
| { |
| match (Token.Undef); |
| if (token.equals (Token.Identifier)) |
| { |
| symbols.remove (token.name); |
| macros.removeElement (token.name); |
| match (Token.Identifier); |
| } |
| else |
| throw ParseException.syntaxError (scanner, Token.Identifier, token.type); |
| } // undefine |
| |
| /** |
| * |
| **/ |
| private void ifClause () throws IOException, ParseException |
| { |
| match (Token.If); |
| constExpr (); |
| } // ifClause |
| |
| /** |
| * |
| **/ |
| private void constExpr () throws IOException, ParseException |
| { |
| SymtabEntry dummyEntry = new SymtabEntry (parser.currentModule); |
| dummyEntry.container (parser.currentModule); |
| parser.parsingConditionalExpr = true; |
| Expression boolExpr = booleanConstExpr (dummyEntry); |
| parser.parsingConditionalExpr = false; |
| boolean expr; |
| if (boolExpr.value () instanceof Boolean) |
| expr = ((Boolean)boolExpr.value ()).booleanValue (); |
| else |
| expr = ((Number)boolExpr.value ()).longValue () != 0; |
| alreadyProcessedABranch.push (new Boolean (expr)); |
| if (!expr) |
| skipToEndiforElse (); |
| } // constExpr |
| |
| /** |
| * |
| **/ |
| Expression booleanConstExpr (SymtabEntry entry) throws IOException, ParseException |
| { |
| Expression expr = orExpr (null, entry); |
| try |
| { |
| expr.evaluate (); |
| } |
| catch (EvaluationException e) |
| { |
| ParseException.evaluationError (scanner, e.toString ()); |
| } |
| return expr; |
| } // booleanConstExpr |
| |
| /** |
| * |
| **/ |
| private Expression orExpr (Expression e, SymtabEntry entry) throws IOException, ParseException |
| { |
| if (e == null) |
| e = andExpr (null, entry); |
| else |
| { |
| BinaryExpr b = (BinaryExpr)e; |
| b.right (andExpr (null, entry)); |
| e.rep (e.rep () + b.right ().rep ()); |
| } |
| if (token.equals (Token.DoubleBar)) |
| { |
| match (token.type); |
| BooleanOr or = parser.exprFactory.booleanOr (e, null); |
| or.rep (e.rep () + " || "); |
| return orExpr (or, entry); |
| } |
| else |
| return e; |
| } // orExpr |
| |
| /** |
| * |
| **/ |
| private Expression andExpr (Expression e, SymtabEntry entry) throws IOException, ParseException |
| { |
| if (e == null) |
| e = notExpr (entry); |
| else |
| { |
| BinaryExpr b = (BinaryExpr)e; |
| b.right (notExpr (entry)); |
| e.rep (e.rep () + b.right ().rep ()); |
| } |
| if (token.equals (Token.DoubleAmpersand)) |
| { |
| match (token.type); |
| BooleanAnd and = parser.exprFactory.booleanAnd (e, null); |
| and.rep (e.rep () + " && "); |
| return andExpr (and, entry); |
| } |
| else |
| return e; |
| } // andExpr |
| |
| /** |
| * |
| **/ |
| private Expression notExpr (/*boolean alreadySawExclamation, */SymtabEntry entry) throws IOException, ParseException |
| { |
| Expression e; |
| if (token.equals (Token.Exclamation)) |
| { |
| match (Token.Exclamation); |
| e = parser.exprFactory.booleanNot (definedExpr (entry)); |
| e.rep ("!" + ((BooleanNot)e).operand ().rep ()); |
| } |
| else |
| e = definedExpr (entry); |
| return e; |
| } // notExpr |
| |
| /** |
| * |
| **/ |
| private Expression definedExpr (SymtabEntry entry) throws IOException, ParseException |
| { |
| if (token.equals (Token.Identifier) && token.name.equals ("defined")) |
| match (Token.Identifier); |
| return equalityExpr (null, entry); |
| } // definedExpr |
| |
| /** |
| * |
| **/ |
| private Expression equalityExpr (Expression e, SymtabEntry entry) throws IOException, ParseException |
| { |
| if (e == null) |
| { |
| parser.token = token; // Since parser to parse, give it this token |
| e = parser.constExp (entry); |
| token = parser.token; // Since parser last parsed, get its token |
| } |
| else |
| { |
| BinaryExpr b = (BinaryExpr)e; |
| parser.token = token; // Since parser to parse, give it this token |
| Expression constExpr = parser.constExp (entry); |
| token = parser.token; // Since parser last parsed, get its token |
| b.right (constExpr); |
| e.rep (e.rep () + b.right ().rep ()); |
| } |
| if (token.equals (Token.DoubleEqual)) |
| { |
| match (token.type); |
| Equal eq = parser.exprFactory.equal (e, null); |
| eq.rep (e.rep () + " == "); |
| return equalityExpr (eq, entry); |
| } |
| else if (token.equals (Token.NotEqual)) |
| { |
| match (token.type); |
| NotEqual n = parser.exprFactory.notEqual (e, null); |
| n.rep (e.rep () + " != "); |
| return equalityExpr (n, entry); |
| } |
| else if (token.equals (Token.GreaterThan)) |
| { |
| match (token.type); |
| GreaterThan g = parser.exprFactory.greaterThan (e, null); |
| g.rep (e.rep () + " > "); |
| return equalityExpr (g, entry); |
| } |
| else if (token.equals (Token.GreaterEqual)) |
| { |
| match (token.type); |
| GreaterEqual g = parser.exprFactory.greaterEqual (e, null); |
| g.rep (e.rep () + " >= "); |
| return equalityExpr (g, entry); |
| } |
| else if (token.equals (Token.LessThan)) |
| { |
| match (token.type); |
| LessThan l = parser.exprFactory.lessThan (e, null); |
| l.rep (e.rep () + " < "); |
| return equalityExpr (l, entry); |
| } |
| else if (token.equals (Token.LessEqual)) |
| { |
| match (token.type); |
| LessEqual l = parser.exprFactory.lessEqual (e, null); |
| l.rep (e.rep () + " <= "); |
| return equalityExpr (l, entry); |
| } |
| else |
| return e; |
| } // equalityExpr |
| |
| /** |
| * |
| **/ |
| Expression primaryExpr (SymtabEntry entry) throws IOException, ParseException |
| { |
| Expression primary = null; |
| switch (token.type) |
| { |
| case Token.Identifier: |
| // If an identifier gets this far, it means that no |
| // preprocessor variable was defined with that name. |
| // Generate a FALSE boolean expr. |
| //daz primary = parser.exprFactory.terminal ("0", new Long (0)); |
| primary = parser.exprFactory.terminal ("0", BigInteger.valueOf (0)); |
| token = scanner.getToken (); |
| break; |
| case Token.BooleanLiteral: |
| case Token.CharacterLiteral: |
| case Token.IntegerLiteral: |
| case Token.FloatingPointLiteral: |
| case Token.StringLiteral: |
| //daz primary = parser.literal (); |
| primary = parser.literal (entry); |
| token = parser.token; |
| break; |
| case Token.LeftParen: |
| match (Token.LeftParen); |
| primary = booleanConstExpr (entry); |
| match (Token.RightParen); |
| primary.rep ('(' + primary.rep () + ')'); |
| break; |
| default: |
| int[] expected = {Token.Literal, Token.LeftParen}; |
| throw ParseException.syntaxError (scanner, expected, token.type); |
| } |
| return primary; |
| } // primaryExpr |
| |
| /** |
| * |
| **/ |
| private void ifDefine (boolean inParens, boolean not) throws IOException, ParseException |
| { |
| if (token.equals (Token.Identifier)) |
| if ((not && symbols.containsKey (token.name)) || (!not && !symbols.containsKey (token.name))) |
| { |
| alreadyProcessedABranch.push (new Boolean (false)); |
| skipToEndiforElse (); |
| } |
| else |
| { |
| alreadyProcessedABranch.push (new Boolean (true)); |
| match (Token.Identifier); |
| if (inParens) |
| match (Token.RightParen); |
| } |
| else |
| throw ParseException.syntaxError (scanner, Token.Identifier, token.type); |
| } // ifDefine |
| |
| /** |
| * |
| **/ |
| private void ifdef (boolean not) throws IOException, ParseException |
| { |
| if (not) |
| match (Token.Ifndef); |
| else |
| match (Token.Ifdef); |
| if (token.equals (Token.Identifier)) |
| if ((not && symbols.containsKey (token.name)) || (!not && !symbols.containsKey (token.name))) |
| { |
| alreadyProcessedABranch.push (new Boolean (false)); |
| skipToEndiforElse (); |
| } |
| else |
| { |
| alreadyProcessedABranch.push (new Boolean (true)); |
| match (Token.Identifier); |
| } |
| else |
| throw ParseException.syntaxError (scanner, Token.Identifier, token.type); |
| } // ifdef |
| |
| /** |
| * |
| **/ |
| private void elif () throws IOException, ParseException |
| { |
| if (alreadyProcessedABranch.empty ()) |
| throw ParseException.elseNoIf (scanner); |
| else if (((Boolean)alreadyProcessedABranch.peek ()).booleanValue ()) |
| skipToEndif (); |
| else |
| { |
| match (Token.Elif); |
| constExpr (); |
| } |
| } // elif |
| |
| /** |
| * |
| **/ |
| private void skipToEndiforElse () throws IOException, ParseException |
| { |
| while (!token.equals (Token.Endif) && !token.equals (Token.Else) && !token.equals (Token.Elif)) |
| { |
| if (token.equals (Token.Ifdef) || token.equals (Token.Ifndef)) |
| { |
| alreadyProcessedABranch.push (new Boolean (true)); |
| skipToEndif (); |
| } |
| else |
| token = scanner.skipUntil ('#'); |
| } |
| process (token); |
| } // skipToEndiforElse |
| |
| /** |
| * |
| **/ |
| private void skipToEndif () throws IOException, ParseException |
| { |
| while (!token.equals (Token.Endif)) |
| { |
| token = scanner.skipUntil ('#'); |
| if (token.equals (Token.Ifdef) || token.equals (Token.Ifndef)) |
| { |
| alreadyProcessedABranch.push (new Boolean (true)); |
| skipToEndif (); |
| } |
| } |
| alreadyProcessedABranch.pop (); |
| match (Token.Endif); |
| } // skipToEndif |
| |
| /////////////// |
| // For Pragma |
| |
| /** |
| * |
| **/ |
| private void pragma () throws IOException, ParseException |
| { |
| match (Token.Pragma); |
| String pragmaType = token.name; |
| |
| // <d59165> Enable escaped identifiers while processing pragma internals. |
| // Don't enable until scanning pragma name! |
| scanner.escapedOK = true; |
| match (Token.Identifier); |
| |
| // Add pragma entry to container |
| PragmaEntry pragmaEntry = parser.stFactory.pragmaEntry (parser.currentModule); |
| pragmaEntry.name (pragmaType); |
| pragmaEntry.sourceFile (scanner.fileEntry ()); |
| pragmaEntry.data (scanner.currentLine ()); |
| if (parser.currentModule instanceof ModuleEntry) |
| ((ModuleEntry)parser.currentModule).addContained (pragmaEntry); |
| else if (parser.currentModule instanceof InterfaceEntry) |
| ((InterfaceEntry)parser.currentModule).addContained (pragmaEntry); |
| |
| // If the token was an identifier, then pragmaType WILL be non-null. |
| if (pragmaType.equals ("ID")) |
| idPragma (); |
| else if (pragmaType.equals ("prefix")) |
| prefixPragma (); |
| else if (pragmaType.equals ("version")) |
| versionPragma (); |
| |
| // we are adding extensions to the Sun's idlj compiler to |
| // handle correct code generation for local Objects, where |
| // the OMG is taking a long time to formalize stuff. Good |
| // example of this is poa.idl. Two proprietory pragmas |
| // sun_local and sun_localservant are defined. sun_local |
| // generates only Holder and Helper classes, where read |
| // and write methods throw marshal exceptions. sun_localservant |
| // is to generate Helper, Holder, and only Skel with _invoke |
| // throwing an exception, since it does not make sense for |
| // local objects. |
| |
| else if (pragmaType.equals ("sun_local")) |
| localPragma(); |
| else if (pragmaType.equals ("sun_localservant")) |
| localServantPragma(); |
| else |
| { |
| otherPragmas (pragmaType, tokenToString ()); |
| token = scanner.getToken (); |
| } |
| |
| scanner.escapedOK = false; // <d59165> Disable escaped identifiers. |
| } // pragma |
| |
| // <d57110> Pragma ID can be appiled to modules and it is an error to |
| // name a type in more than one ID pragma directive. |
| |
| private Vector PragmaIDs = new Vector (); |
| |
| private void localPragma () throws IOException, ParseException |
| { |
| // Before I can use a parser method, I must make sure it has the current token. |
| parser.token = token; |
| // this makes sense only for interfaces, if specified for modules, |
| // parser should throw an error |
| SymtabEntry anErrorOccurred = new SymtabEntry (); |
| SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred); |
| // Was the indicated type found in the symbol table? |
| if (entry == anErrorOccurred) |
| { |
| System.out.println("Error occured "); |
| // Don't have to generate an error, scopedName already has. |
| scanner.skipLineComment (); |
| token = scanner.getToken (); |
| } |
| else |
| { |
| // by this time we have already parsed the ModuleName and the |
| // pragma type, therefore setInterfaceType |
| if (entry instanceof InterfaceEntry) { |
| InterfaceEntry ent = (InterfaceEntry) entry; |
| ent.setInterfaceType (InterfaceEntry.LOCAL_SIGNATURE_ONLY); |
| } |
| token = parser.token; |
| String string = token.name; |
| match (Token.StringLiteral); |
| // for non-interfaces it doesn't make sense, so just ignore it |
| } |
| } // localPragma |
| |
| private void localServantPragma () throws IOException, ParseException |
| { |
| // Before I can use a parser method, I must make sure it has the current token. |
| parser.token = token; |
| // this makes sense only for interfaces, if specified for modules, |
| // parser should throw an error |
| SymtabEntry anErrorOccurred = new SymtabEntry (); |
| SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred); |
| |
| // Was the indicated type found in the symbol table? |
| if (entry == anErrorOccurred) |
| { |
| // Don't have to generate an error, scopedName already has. |
| scanner.skipLineComment (); |
| token = scanner.getToken (); |
| System.out.println("Error occured "); |
| } |
| else |
| { |
| // by this time we have already parsed the ModuleName and the |
| // pragma type, therefore setInterfaceType |
| if (entry instanceof InterfaceEntry) { |
| InterfaceEntry ent = (InterfaceEntry) entry; |
| ent.setInterfaceType (InterfaceEntry.LOCALSERVANT); |
| } |
| token = parser.token; |
| String string = token.name; |
| match (Token.StringLiteral); |
| // for non-interfaces it doesn't make sense, so just ignore it |
| } |
| } // localServantPragma |
| |
| |
| /** |
| * |
| **/ |
| private void idPragma () throws IOException, ParseException |
| { |
| // Before I can use a parser method, I must make sure it has the current token. |
| parser.token = token; |
| |
| // <d57110> This flag will relax the restriction that the scopedNamed |
| // in this ID pragma directive cannot resolve to a module. |
| parser.isModuleLegalType (true); |
| SymtabEntry anErrorOccurred = new SymtabEntry (); |
| SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred); |
| parser.isModuleLegalType (false); // <57110> |
| |
| // Was the indicated type found in the symbol table? |
| if (entry == anErrorOccurred) |
| { |
| // Don't have to generate an error, scopedName already has. |
| scanner.skipLineComment (); |
| token = scanner.getToken (); |
| } |
| // <d57110> |
| //else if (PragmaIDs.contains (entry)) |
| //{ |
| // ParseException.badRepIDAlreadyAssigned (scanner, entry.name ()); |
| // scanner.skipLineComment (); |
| // token = scanner.getToken (); |
| //} |
| else |
| { |
| token = parser.token; |
| String string = token.name; |
| // Do not match token until after raise exceptions, otherwise |
| // incorrect messages will be emitted! |
| if (PragmaIDs.contains (entry)) // <d57110> |
| { |
| ParseException.badRepIDAlreadyAssigned (scanner, entry.name ()); |
| } |
| else if (!RepositoryID.hasValidForm (string)) // <d57110> |
| { |
| ParseException.badRepIDForm (scanner, string); |
| } |
| else |
| { |
| entry.repositoryID (new RepositoryID (string)); |
| PragmaIDs.addElement (entry); // <d57110> |
| } |
| match (Token.StringLiteral); |
| } |
| } // idPragma |
| |
| /** |
| * |
| **/ |
| private void prefixPragma () throws IOException, ParseException |
| { |
| String string = token.name; |
| match (Token.StringLiteral); |
| ((IDLID)parser.repIDStack.peek ()).prefix (string); |
| ((IDLID)parser.repIDStack.peek ()).name (""); |
| } // prefixPragma |
| |
| /** |
| * |
| **/ |
| private void versionPragma () throws IOException, ParseException |
| { |
| // Before I can use a parser method, I must make sure it has the current token. |
| parser.token = token; |
| // This flag will relax the restriction that the scopedNamed |
| // in this Version pragma directive cannot resolve to a module. |
| parser.isModuleLegalType (true); |
| SymtabEntry anErrorOccurred = new SymtabEntry (); |
| SymtabEntry entry = parser.scopedName (parser.currentModule, anErrorOccurred); |
| // reset the flag to original value |
| parser.isModuleLegalType (false); |
| if (entry == anErrorOccurred) |
| { |
| // Don't have to generate an error, scopedName already has. |
| scanner.skipLineComment (); |
| token = scanner.getToken (); |
| } |
| else |
| { |
| token = parser.token; |
| String string = token.name; |
| match (Token.FloatingPointLiteral); |
| if (entry.repositoryID () instanceof IDLID) |
| ((IDLID)entry.repositoryID ()).version (string); |
| } |
| } // versionPragma |
| |
| private Vector pragmaHandlers = new Vector (); |
| |
| /** |
| * |
| **/ |
| void registerPragma (PragmaHandler handler) |
| { |
| pragmaHandlers.addElement (handler); |
| } // registerPragma |
| |
| /** |
| * |
| **/ |
| private void otherPragmas (String pragmaType, String currentToken) throws IOException |
| { |
| for (int i = pragmaHandlers.size () - 1; i >= 0; --i) |
| { |
| PragmaHandler handler = (PragmaHandler)pragmaHandlers.elementAt (i); |
| if (handler.process (pragmaType, currentToken)) |
| break; |
| } |
| } // otherPragmas |
| |
| /* |
| * These protected methods are used by extenders, by the code |
| * which implements otherPragma. |
| */ |
| |
| /** |
| * Get the current token. |
| **/ |
| String currentToken () |
| { |
| return tokenToString (); |
| } // currentToken |
| |
| /** |
| * This method, given an entry name, returns the entry with that name. |
| * It can take fully or partially qualified names and returns the |
| * appropriate entry defined within the current scope. If no entry |
| * exists, null is returned. |
| **/ |
| SymtabEntry getEntryForName (String string) |
| { |
| boolean partialScope = false; |
| boolean globalScope = false; |
| |
| // Change all ::'s to /'s |
| if (string.startsWith ("::")) |
| { |
| globalScope = true; |
| string = string.substring (2); |
| } |
| int index = string.indexOf ("::"); |
| while (index >= 0) |
| { |
| partialScope = true; |
| string = string.substring (0, index) + '/' + string.substring (index + 2); |
| index = string.indexOf ("::"); |
| } |
| |
| // Get the entry for that string |
| SymtabEntry entry = null; |
| if (globalScope) |
| entry = parser.recursiveQualifiedEntry (string); |
| else if (partialScope) |
| entry = parser.recursivePQEntry (string, parser.currentModule); |
| else |
| entry = parser.unqualifiedEntryWMod (string, parser.currentModule); |
| return entry; |
| } // getEntryForName |
| |
| /** |
| * This method returns a string of all of the characters from the |
| * input file from the current position up to, but not including, |
| * the end-of-line character(s). |
| **/ |
| String getStringToEOL () throws IOException |
| { |
| return scanner.getStringToEOL (); |
| } // getStringToEOL |
| |
| /** |
| * This method returns a string of all of the characters from the |
| * input file from the current position up to, but not including, |
| * the given character. It encapsulates parenthesis and quoted strings, |
| * meaning it does not stop if the given character is found within |
| * parentheses or quotes. For instance, given the input of |
| * `start(inside)end', getUntil ('n') will return "start(inside)e" |
| **/ |
| String getUntil (char c) throws IOException |
| { |
| return scanner.getUntil (c); |
| } // getUntil |
| |
| private boolean lastWasMacroID = false; |
| |
| /** |
| * |
| **/ |
| private String tokenToString () |
| { |
| if (token.equals (Token.MacroIdentifier)) |
| { |
| lastWasMacroID = true; |
| return token.name; |
| } |
| else if (token.equals (Token.Identifier)) |
| return token.name; |
| else |
| return token.toString (); |
| } // tokenToString |
| |
| /** |
| * This method returns the next token String from the input file. |
| **/ |
| String nextToken () throws IOException |
| { |
| if (lastWasMacroID) |
| { |
| lastWasMacroID = false; |
| return "("; |
| } |
| else |
| { |
| token = scanner.getToken (); |
| return tokenToString (); |
| } |
| } // nextToken |
| |
| /** |
| * This method assumes that the current token marks the beginning |
| * of a scoped name. It then parses the subsequent identifier and |
| * double colon tokens, builds the scoped name, and finds the symbol |
| * table entry with that name. |
| **/ |
| SymtabEntry scopedName () throws IOException |
| { |
| boolean globalScope = false; |
| boolean partialScope = false; |
| String name = null; |
| SymtabEntry entry = null; |
| try |
| { |
| if (token.equals (Token.DoubleColon)) |
| globalScope = true; |
| else |
| { |
| if (token.equals (Token.Object)) |
| { |
| name = "Object"; |
| match (Token.Object); |
| } |
| else if (token.type == Token.ValueBase) |
| { |
| name = "ValueBase"; |
| match (Token.ValueBase); |
| } |
| else |
| { |
| name = token.name; |
| match (Token.Identifier); |
| } |
| } |
| while (token.equals (Token.DoubleColon)) |
| { |
| match (Token.DoubleColon); |
| partialScope = true; |
| if (name != null) |
| name = name + '/' + token.name; |
| else |
| name = token.name; |
| match (Token.Identifier); |
| } |
| if (globalScope) |
| entry = parser.recursiveQualifiedEntry (name); |
| else if (partialScope) |
| entry = parser.recursivePQEntry (name, parser.currentModule); |
| else |
| entry = parser.unqualifiedEntryWMod (name, parser.currentModule); |
| } |
| catch (ParseException e) |
| { |
| entry = null; |
| } |
| return entry; |
| } // scopedName |
| |
| /** |
| * Skip to the end of the line. |
| **/ |
| void skipToEOL () throws IOException |
| { |
| scanner.skipLineComment (); |
| } // skipToEOL |
| |
| /** |
| * This method skips the data in the input file until the specified |
| * character is encountered, then it returns the next token. |
| **/ |
| String skipUntil (char c) throws IOException |
| { |
| if (!(lastWasMacroID && c == '(')) |
| token = scanner.skipUntil (c); |
| return tokenToString (); |
| } // skipUntil |
| |
| /** |
| * This method displays a Parser Exception complete with line number |
| * and position information with the given message string. |
| **/ |
| void parseException (String message) |
| { |
| // <d62023> Suppress warnings |
| if (!parser.noWarn) |
| ParseException.warning (scanner, message); |
| } // parseException |
| |
| // For Pragma |
| /////////////// |
| // For macro expansion |
| |
| /** |
| * |
| **/ |
| String expandMacro (String macroDef, Token t) throws IOException, ParseException |
| { |
| token = t; |
| // Get the parameter values from the macro 'call' |
| Vector parmValues = getParmValues (); |
| |
| // Get the parameter names from the macro definition |
| // NOTE: a newline character is appended here so that when |
| // getStringToEOL is called, it stops scanning at the end |
| // of this string. |
| scanner.scanString (macroDef + '\n'); |
| Vector parmNames = new Vector (); |
| macro (parmNames); |
| |
| if (parmValues.size () < parmNames.size ()) |
| throw ParseException.syntaxError (scanner, Token.Comma, Token.RightParen); |
| else if (parmValues.size () > parmNames.size ()) |
| throw ParseException.syntaxError (scanner, Token.RightParen, Token.Comma); |
| |
| macroDef = scanner.getStringToEOL (); |
| for (int i = 0; i < parmNames.size (); ++i) |
| macroDef = replaceAll (macroDef, (String)parmNames.elementAt (i), (String)parmValues.elementAt (i)); |
| return removeDoublePound (macroDef); |
| } // expandMacro |
| |
| // This method is only used by the macro expansion methods. |
| /** |
| * |
| **/ |
| private void miniMatch (int type) throws ParseException |
| { |
| // A normal production would now execute: |
| // match (type); |
| // But match reads the next token. I don't want to do that now. |
| // Just make sure the current token is a 'type'. |
| if (!token.equals (type)) |
| throw ParseException.syntaxError (scanner, type, token.type); |
| } // miniMatch |
| |
| /** |
| * |
| **/ |
| private Vector getParmValues () throws IOException, ParseException |
| { |
| Vector values = new Vector (); |
| if (token.equals (Token.Identifier)) |
| { |
| match (Token.Identifier); |
| miniMatch (Token.LeftParen); |
| } |
| else if (!token.equals (Token.MacroIdentifier)) |
| throw ParseException.syntaxError (scanner, Token.Identifier, token.type); |
| |
| if (!token.equals (Token.RightParen)) |
| { |
| values.addElement (scanner.getUntil (',', ')').trim ()); |
| token = scanner.getToken (); |
| macroParmValues (values); |
| } |
| return values; |
| } // getParmValues |
| |
| /** |
| * |
| **/ |
| private void macroParmValues (Vector values) throws IOException, ParseException |
| { |
| while (!token.equals (Token.RightParen)) |
| { |
| miniMatch (Token.Comma); |
| values.addElement (scanner.getUntil (',', ')').trim ()); |
| token = scanner.getToken (); |
| } |
| } // macroParmValues |
| |
| /** |
| * |
| **/ |
| private void macro (Vector parmNames) throws IOException, ParseException |
| { |
| match (token.type); |
| match (Token.LeftParen); |
| macroParms (parmNames); |
| miniMatch (Token.RightParen); |
| } // macro |
| |
| /** |
| * |
| **/ |
| private void macroParms (Vector parmNames) throws IOException, ParseException |
| { |
| if (!token.equals (Token.RightParen)) |
| { |
| parmNames.addElement (token.name); |
| match (Token.Identifier); |
| macroParms2 (parmNames); |
| } |
| } // macroParms |
| |
| /** |
| * |
| **/ |
| private void macroParms2 (Vector parmNames) throws IOException, ParseException |
| { |
| while (!token.equals (Token.RightParen)) |
| { |
| match (Token.Comma); |
| parmNames.addElement (token.name); |
| match (Token.Identifier); |
| } |
| } // macroParms2 |
| |
| /** |
| * |
| **/ |
| private String replaceAll (String string, String from, String to) |
| { |
| int index = 0; |
| while (index != -1) |
| { |
| index = string.indexOf (from, index); |
| if (index != -1) |
| { |
| if (!embedded (string, index, index + from.length ())) |
| if (index > 0 && string.charAt(index) == '#') |
| string = string.substring (0, index) + '"' + to + '"' + string.substring (index + from.length ()); |
| else |
| string = string.substring (0, index) + to + string.substring (index + from.length ()); |
| index += to.length (); |
| } |
| } |
| return string; |
| } // replaceAll |
| |
| /** |
| * |
| **/ |
| private boolean embedded (String string, int index, int endIndex) |
| { |
| // Don't replace if found substring is not an independent id. |
| // For example, don't replace "thither".indexOf ("it", 0) |
| boolean ret = false; |
| char preCh = index == 0 ? ' ' : string.charAt (index - 1); |
| char postCh = endIndex >= string.length () - 1 ? ' ' : string.charAt (endIndex); |
| if ((preCh >= 'a' && preCh <= 'z') || (preCh >= 'A' && preCh <= 'Z')) |
| ret = true; |
| else if ((postCh >= 'a' && postCh <= 'z') || (postCh >= 'A' && postCh <= 'Z') || (postCh >= '0' && postCh <= '9') || postCh == '_') |
| ret = true; |
| else |
| ret = inQuotes (string, index); |
| return ret; |
| } // embedded |
| |
| /** |
| * |
| **/ |
| private boolean inQuotes (String string, int index) |
| { |
| int quoteCount = 0; |
| for (int i = 0; i < index; ++i) |
| if (string.charAt (i) == '"') ++quoteCount; |
| // If there are an odd number of quotes before this region, |
| // then this region is within quotes |
| return quoteCount % 2 != 0; |
| } // inQuotes |
| |
| /** |
| * Remove any occurrences of ##. |
| **/ |
| private String removeDoublePound (String string) |
| { |
| int index = 0; |
| while (index != -1) |
| { |
| index = string.indexOf ("##", index); |
| if (index != -1) |
| { |
| int startSkip = index - 1; |
| int stopSkip = index + 2; |
| if (startSkip < 0) |
| startSkip = 0; |
| if (stopSkip >= string.length ()) |
| stopSkip = string.length () - 1; |
| while (startSkip > 0 && |
| (string.charAt (startSkip) == ' ' || |
| string.charAt (startSkip) == '\t')) |
| --startSkip; |
| while (stopSkip < string.length () - 1 && |
| (string.charAt (stopSkip) == ' ' || |
| string.charAt (stopSkip) == '\t')) |
| ++stopSkip; |
| string = string.substring (0, startSkip + 1) + string.substring (stopSkip); |
| } |
| } |
| return string; |
| } // removeDoublePound |
| |
| // For macro expansion |
| /////////////// |
| |
| /** |
| * |
| **/ |
| private String getFilename (String name) throws FileNotFoundException |
| { |
| String fullName = null; |
| File file = new File (name); |
| if (file.canRead ()) |
| fullName = name; |
| else |
| { |
| Enumeration pathList = parser.paths.elements (); |
| while (!file.canRead () && pathList.hasMoreElements ()) |
| { |
| fullName = (String)pathList.nextElement () + File.separatorChar + name; |
| file = new File (fullName); |
| } |
| if (!file.canRead ()) |
| throw new FileNotFoundException (name); |
| } |
| return fullName; |
| } // getFilename |
| |
| /** |
| * |
| **/ |
| private void match (int type) throws IOException, ParseException |
| { |
| if (!token.equals (type)) |
| throw ParseException.syntaxError (scanner, type, token.type); |
| token = scanner.getToken (); |
| |
| // <d62023> Added for convenience, but commented-out because there is |
| // no reason to issue warnings for tokens scanned during preprocessing. |
| // See issueTokenWarnings(). |
| //issueTokenWarnings (); |
| |
| //System.out.println ("Preprocessor.match token = " + token.type); |
| //if (token.equals (Token.Identifier) || token.equals (Token.MacroIdentifier)) |
| // System.out.println ("Preprocessor.match token name = " + token.name); |
| |
| // If the token is a defined thingy, scan the defined string |
| // instead of the input stream for a while. |
| if (token.equals (Token.Identifier) || token.equals (Token.MacroIdentifier)) |
| { |
| String string = (String)symbols.get (token.name); |
| if (string != null && !string.equals ("")) |
| // If this is a macro, parse the macro |
| if (macros.contains (token.name)) |
| { |
| scanner.scanString (expandMacro (string, token)); |
| token = scanner.getToken (); |
| } |
| // else this is just a normal define |
| else |
| { |
| scanner.scanString (string); |
| token = scanner.getToken (); |
| } |
| } |
| } // match |
| |
| // <d62023> |
| /** |
| * Issue warnings about tokens scanned during preprocessing. |
| **/ |
| private void issueTokenWarnings () |
| { |
| if (parser.noWarn) |
| return; |
| |
| // There are no keywords defined for preprocessing (only directives), so: |
| // |
| // 1.) Do not issue warnings for identifiers known to be keywords in |
| // another level of IDL. |
| // 2.) Do not issue warnings for identifiers that collide with keywords |
| // in letter, but not case. |
| // 3.) Do not issue warnings for deprecated keywords. |
| // |
| // Should we warn when a macro identifier replaces a keyword? Hmmm. |
| |
| // Deprecated directives? None to date. |
| //if (token.isDirective () && token.isDeprecated ()) |
| // ParseException.warning (scanner, Util.getMesage ("Deprecated.directive", token.name)); |
| } // issueTokenWarnings |
| |
| /** |
| * This method is called when the parser encounters a left curly brace. |
| * An extender of PragmaHandler may find scope information useful. |
| * For example, the prefix pragma takes effect as soon as it is |
| * encountered and stays in effect until the current scope is closed. |
| * If a similar pragma extension is desired, then the openScope and |
| * closeScope methods are available for overriding. |
| * @param entry the symbol table entry whose scope has just been opened. |
| * Be aware that, since the scope has just been entered, this entry is |
| * incomplete at this point. |
| **/ |
| void openScope (SymtabEntry entry) |
| { |
| for (int i = pragmaHandlers.size () - 1; i >= 0; --i) |
| { |
| PragmaHandler handler = (PragmaHandler)pragmaHandlers.elementAt (i); |
| handler.openScope (entry); |
| } |
| } // openScope |
| |
| /** |
| * This method is called when the parser encounters a right curly brace. |
| * An extender of PragmaHandler may find scope information useful. |
| * For example, the prefix pragma takes effect as soon as it is |
| * encountered and stays in effect until the current scope is closed. |
| * If a similar pragma extension is desired, then the openScope and |
| * closeScope methods are available for overriding. |
| * @param entry the symbol table entry whose scope has just been closed. |
| **/ |
| void closeScope (SymtabEntry entry) |
| { |
| for (int i = pragmaHandlers.size () - 1; i >= 0; --i) |
| { |
| PragmaHandler handler = (PragmaHandler)pragmaHandlers.elementAt (i); |
| handler.closeScope (entry); |
| } |
| } // closeScope |
| |
| private Parser parser; |
| private Scanner scanner; |
| private Hashtable symbols; |
| private Vector macros; |
| |
| // The logic associated with this stack is scattered above. |
| // A concise map of the logic is: |
| // case #if false, #ifdef false, #ifndef true |
| // push (false); |
| // skipToEndifOrElse (); |
| // case #if true, #ifdef true, #ifndef false |
| // push (true); |
| // case #elif <conditional> |
| // if (top == true) |
| // skipToEndif (); |
| // else if (conditional == true) |
| // pop (); |
| // push (true); |
| // else if (conditional == false) |
| // skipToEndifOrElse (); |
| // case #else |
| // if (top == true) |
| // skipToEndif (); |
| // else |
| // pop (); |
| // push (true); |
| // case #endif |
| // pop (); |
| private Stack alreadyProcessedABranch = new Stack (); |
| Token token; |
| |
| private static String indent = ""; |
| } |