?? macrotable.java
字號:
/**
* Soft Gems Resource parser. Created by Mike Lischke.
*
* The source code in this file can freely be used for any purpose provided this notice remains
* unchanged in the file.
*
* Copyright 2004 by Mike Lischke, www.soft-gems.net, public@soft-gems.net. All rights reserved.
*/
package net.softgems.resourceparser.preprocessor;
import java.io.*;
import java.text.MessageFormat;
import java.util.*;
import net.softgems.resourceparser.main.IParseEventListener;
/**
* This class provides support for macro substitution and symbol lookup. The implementation is
* based on the C/C++ preprocessor language description in MSDN:
* Visual Studio -> Visual C++ -> Visual C++ Reference -> C/C++ Languages ->
* C/C++ Preprocessor Reference -> The Preprocessor -> Macros.
*/
public class MacroTable
{
/** A list of macros, which are currently being expanded (to avoid endless recursions). */
protected ArrayList evaluationList = new ArrayList();
/** List of event listeners who want to get notified about a preprocessor event. */
private ArrayList listeners = new ArrayList();
/** A list of Macro classes, sorted by the name of the macros. */
private HashMap macros = new HashMap();
//------------------------------------------------------------------------------------------------
/**
* This private class is the actual representation of a macro in the macro table.
*/
private class Macro
{
protected int formalParameterCount;
protected String[] formalParameters;
protected String name;
protected String substitution;
//----------------------------------------------------------------------------------------------
/**
* Constructor of the Macro class.
*
* @param theName The name (identification) of the macro.
* @param theParameters The formal parameters of the macro or <b>null</b> if there aren't any.
* @param theSubstitution The string to use instead of the macro identification, when calling
* {@see getSubstitution}.
*/
public Macro(String theName, String[] theParameters, String theSubstition)
{
name = theName;
formalParameters = theParameters;
if (formalParameters == null)
formalParameterCount = 0;
else
formalParameterCount = formalParameters.length;
substitution = theSubstition;
}
//----------------------------------------------------------------------------------------------
/**
* Looks through the list of formal parameters to find the given symbol and returns the
* actual parameter from the <b>parameters</b> list if there is one.
*
* @param parameters The list of actual parameters.
* @return The actual parameter, which corresponds to the given symbol.
*/
private String getActualParameter(ArrayList parameters, String symbol)
{
// Look whether this symbol is a parameter and get its index in the parameter list if so.
int index = -1;
for (int i = 0; i < formalParameters.length; i++)
if (formalParameters[i].equals(symbol))
{
index = i;
break;
}
// Replace the formal parameter by its actual equivalent if there is one.
if (index > -1 && index < parameters.size())
return (String) parameters.get(index);
else
return symbol;
}
//----------------------------------------------------------------------------------------------
/**
* This method handles stringizing or charizing of parameters.
*
* @param parameters The list of actual parameters.
* @param tokenizer The currently used tokenizer.
*/
private String handleNumberSign(ArrayList parameters, MacroTokenizer tokenizer)
{
StringBuffer result = new StringBuffer();
int lookAhead = tokenizer.nextToken();
if (lookAhead == '@')
{
// Skip leading whitespaces.
do
{
lookAhead = tokenizer.nextToken();
}
while (lookAhead == ' ' || lookAhead == '\t');
// Charizing is requested.
result.append('\'');
// The next token must be a single character.
if (lookAhead == StreamTokenizer.TT_WORD)
{
String actualParameter = getActualParameter(parameters, tokenizer.getStringValue());
if (actualParameter.length() == 1)
result.append(expandMacros(actualParameter));
else
reportError("Invalid charizing sequence in macro \"" + name + "\".");
}
else
reportError("Invalid charizing sequence in macro \"" + name + "\".");
result.append('\'');
}
else
{
// Skip leading whitespaces.
while (lookAhead == ' ' || lookAhead == '\t')
{
lookAhead = tokenizer.nextToken();
}
if (lookAhead == StreamTokenizer.TT_WORD)
{
result.append("\"");
// Expand any macro before making it a string literal.
String actualParameter = getActualParameter(parameters, tokenizer.getStringValue());
{
StringBuffer expandedValue = new StringBuffer(expandMacros(actualParameter));
// Mask any character, which would make the string literal invalid.
for (int i = expandedValue.length() - 1; i >= 0; i--)
{
switch (expandedValue.charAt(i))
{
case '\\':
case '"':
{
expandedValue.insert(i, '\\');
break;
}
}
}
result.append(expandedValue.toString());
}
result.append("\"");
}
else
reportError("Invalid macro operator in \"" + name + "\"");
}
return result.toString();
}
//----------------------------------------------------------------------------------------------
/**
* Replaces the formal parameters in the macro definition by the given actual parameters and
* returns the result.
*/
public String getSubstition(String[] actualParameters)
{
StringBuffer result;
if (substitution == null)
result = null;
else
{
result = new StringBuffer();
// Convert the actual parameters into a list of non-empty entries.
// Note: MSVC allows for strange constructs here like
// macro(1,,,,,,,,,3)
// where any empty entry is skipped. So the 3 would actually be used as second parameter.
ArrayList parameters = new ArrayList();
if (actualParameters != null)
{
for (int i = 0; i < actualParameters.length; i++)
if ((actualParameters[i] != null) && (actualParameters[i].length() > 0))
parameters.add(actualParameters[i].trim());
}
if (parameters.size() > formalParameterCount)
reportWarning("Macro \"" + name + "\": too many actual parameters in macro call.");
if (parameters.size() < formalParameterCount)
reportWarning("Macro \"" + name + "\": too few actual parameters in macro call.");
boolean pastingPending = false;
if (formalParameterCount == 0)
// Shortcut if there is nothing to substitution.
result.append(substitution);
else
{
// Scan the list of formal parameters and replace any occurance in the substitution by the
// matching actual parameter.
MacroTokenizer tokenizer = new MacroTokenizer(substitution, true);
// Copy everything from the substition to the output until the opening parenthesis is found.
int token = 0;
if (substitution.indexOf('(') > -1)
{
boolean stopPreLoop = false;
do
{
token = tokenizer.nextToken();
switch (token)
{
case StreamTokenizer.TT_EOF:
case '(':
{
if (token == '(')
result.append((char) token);
stopPreLoop = true;
break;
}
case StreamTokenizer.TT_WORD:
{
result.append(tokenizer.getStringValue());
break;
}
case '"':
{
result.append('"');
result.append(tokenizer.getStringValue());
result.append('"');
break;
}
case '\'':
{
result.append('\'');
result.append(tokenizer.getStringValue());
result.append('\'');
break;
}
default:
result.append((char) token);
}
}
while (!stopPreLoop);
}
do
{
// Collect leading whitespace but do not write them to the result yet.
boolean whiteSpacePending = false;
do
{
token = tokenizer.nextToken();
if (token == ' ' || token == '\t')
whiteSpacePending = true;
else
break;
}
while (true);
if (token == StreamTokenizer.TT_EOF)
break;
switch (token)
{
case StreamTokenizer.TT_WORD:
{
if (whiteSpacePending && !pastingPending)
result.append(" ");
String actualParameter = getActualParameter(parameters, tokenizer.getStringValue());
result.append(expandMacros(actualParameter));
pastingPending = false;
break;
}
case '#': // Macro operator.
{
token = tokenizer.nextToken();
switch (token)
{
case '#':
{
// Token-pasting operator. Leave out any pending white space and keep a flag to
// indicate that he next token is written directly to the previous token.
// Check that this operator is not the first token in the substitution.
if (result.length() == 0)
reportError("\'##\' cannot occur at the beginning of a macro definition");
pastingPending = true;
break;
}
case '@':
{
// Charizing operator.
if (whiteSpacePending)
result.append(" ");
tokenizer.pushBack();
result.append(handleNumberSign(parameters, tokenizer));
break;
}
default:
{
// Stringizing operator.
if (whiteSpacePending)
result.append(" ");
tokenizer.pushBack();
result.append(handleNumberSign(parameters, tokenizer));
}
}
break;
}
case '"':
{
if (whiteSpacePending)
result.append(" ");
result.append('"');
result.append(tokenizer.getStringValue());
result.append('"');
break;
}
case '\'':
{
if (whiteSpacePending)
result.append(" ");
result.append('\'');
result.append(tokenizer.getStringValue());
result.append('\'');
break;
}
default:
{
if (whiteSpacePending)
result.append(" ");
result.append((char)token);
}
}
}
while (true);
}
if (pastingPending)
reportError("\'##\' cannot occur at the end of a macro definition");
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -