📄 preprocessor.java
字号:
{
skip = SKIP_NONE;
break;
}
}
break;
}
default:
{
reportError("Unexpected #else preprocessor directive found.");
}
}
break;
}
case ELIF:
{
Integer pendingState = (Integer) pendingStates.pop();
// See what the state was, which opened this conditional branch.
switch (pendingState.intValue())
{
case IFDEF:
case IFNDEF:
case IF:
case ELIF:
{
pendingStates.push(ELIF_DIRECTIVE);
// If the parent condition already was skipping then continue that.
// If we are in a subbranch (elif branch), which is currently skipping then continue that.
// If the previous condition was met then start skipping the entire branch that follows now.
// And finally if the previous condition was not met then check here again for the current condition.
if ((skip != SKIP_PARENT) && (skip != SKIP_SUBTREE))
{
// If currently lines are not skipped then the previous condition was met and
// we have to skip everything from now on until the #endif that belongs to the condition starter.
if (skip == SKIP_NONE)
{
skip = SKIP_SUBTREE;
}
else
{
boolean condition = evaluateBooleanExpression(macroTable.expandMacros(parts[1]));
if (condition)
skip = SKIP_NONE;
}
}
break;
}
default:
{
reportError("Unexpected #elif preprocessor directive found.");
}
}
break;
}
case ENDIF:
{
Integer pendingState = (Integer) pendingStates.pop();
Integer lastSkipMode = (Integer) skipFlags.pop();
skip = lastSkipMode.intValue();
// See what the state was, which opened this conditional branch.
switch (pendingState.intValue())
{
case IF:
case IFDEF:
case IFNDEF:
case ELIF:
case ELSE:
{
// Return to parent mode.
break;
}
default:
{
reportError("Unexpected #endif preprocessor directive found.");
}
}
break;
}
}
return result;
}
//------------------------------------------------------------------------------------------------
/**
* Handles an include directive by loading the given file and creating a new (nested) instance
* of the preprocessor.
*
* @param string Contains the rest of the #include line, which contains the file name.
* @return <b>true</b> if a new preprocessor was set up, otherwise <b>false</b>.
*/
private boolean processInclude(String filename) throws FileNotFoundException, IOException
{
boolean foundError = false;
// Filenames must be given either quoted or enclosed in angles. We'll be tolerant by ignoring
// everything, which comes after the file name.
int quote = filename.indexOf('"');
if (quote > -1)
{
// File name in quotes.
int unquote = filename.indexOf('"', quote + 1);
if (unquote == -1)
{
reportError("Invalid include statement.");
foundError = true;
}
filename = filename.substring(quote + 1, unquote);
}
else
{
int leftAngle = filename.indexOf("<");
if (leftAngle > -1)
{
// File name in angles.
int rightAngle = filename.indexOf('>', leftAngle + 1);
if (rightAngle == -1)
{
reportError("Invalid include statement.");
foundError = true;
}
filename = filename.substring(leftAngle + 1, rightAngle);
}
else
{
reportError("Invalid include statement.");
foundError = true;
}
}
if (!foundError)
return includeFile(filename);
else
return false;
}
//------------------------------------------------------------------------------------------------
/**
* Processes a #pragma directive. It particularly handles #pragma once and #pragma message.
* Everything else ist simply passed through to the output.
*
* @param parts The directive line split into two parts ("pragma" and the rest).
*/
private void processPragma(String[] parts) throws IOException
{
// MSDN states the resource compile does not support the pragma directive except for
// pragma code_page. In header files also pragma once can appear (which is handled here).
// But otherwise no more directives are supposedly supported. I wished the documentation
// would keep up with the reality.
if (parts.length < 2)
reportError("Malformed pragma directive.");
else
{
String pragma = parts[1].trim();
if (pragma.equalsIgnoreCase("once"))
processedIncludes.put(inputState.getFilename(), null);
else
if (pragma.startsWith("message"))
{
int start = pragma.indexOf('"');
int stop = pragma.lastIndexOf('"');
if (start > -1 && stop > -1)
reportInfo(pragma.substring(start + 1, stop));
else
reportError("Invalid pragma message format.");
}
else
if (pragma.startsWith("code_page"))
{
// TODO: Handle code page change.
}
// Ignore any other #pragma directive.
}
}
//------------------------------------------------------------------------------------------------
/**
* Called if an #undef directive was found. Get the identifier and remove it from the symbol table.
*
* @param definition Input containing the identifier.
*/
private void processUndef(String definition)
{
String[] parts = definition.split(WHITESPACES, 2);
if (parts.length == 0)
reportError("Invalid #undef directive found.");
else
{
String symbol = parts[0].trim();
macroTable.undefineMacro(symbol);
}
}
//------------------------------------------------------------------------------------------------
/**
* Scans the given string for comments and removes them. If the line contains a multiline comment
* start but no end then the private field inMulitlineComment is set to true to tell the caller
* how to proceed with following lines.
*
* @param line The input line to process.
* @return The cleaned up string without comments. Whitespaces at both ends are removed as well.
*/
private String removeComments(String line) throws IOException
{
StringBuffer buffer = new StringBuffer(line);
for (int i = 0; i < buffer.length(); i++)
{
switch (buffer.charAt(i))
{
case '/': // Potential comment start.
// There must be at least one more character to have a comment start.
if (i + 1 < buffer.length())
{
switch (buffer.charAt(i + 1))
{
case '/': // Single line comment found. Replace the comment by one space character.
buffer.replace(i, buffer.length(), " ");
break;
case '*': // Multi line comment found. Scan for end.
int commentEnd = buffer.indexOf("*/", i + 2);
if (commentEnd > -1)
buffer.replace(i, commentEnd + 2, " ");
else
{
// The comment does not end on this line. Replace what we have by a space char
// and tell the caller about the open comment.
inMultilineComment = true;
buffer.replace(i, buffer.length(), " ");
}
break;
}
}
break;
case '"': // String start found. Search for its end before scanning further for comments.
int stringEnd = buffer.indexOf("\"", i + 1);
// If we did not find a string end delimiter then the source is syntactically wrong.
if (stringEnd == -1)
{
i = buffer.length();
reportError("Unterminated string found.");
}
else
i = stringEnd;
break;
}
}
return buffer.toString().trim();
}
//------------------------------------------------------------------------------------------------
/**
* Splits the given line into the directive string and the rest.
*
* @param line The line to split.
*/
private String[] splitDirective(String line)
{
// Remove the leading number sign and split the rest into two parts of which the first
// one is the textual form of the directive.
int i = 1;
// Skip white spaces between number sign and directive.
while (i < line.length() && ((line.charAt(i) == ' ') || (line.charAt(i) == '\t')))
i++;
int nonWhitespaceStart = i;
while (i < line.length())
{
// Stop scanning if there is a character not in the ranges 'a'..'z' or 'A'..'Z'.
char letter = line.charAt(i);
if (!((letter >= 'a') && (letter <= 'z') || (letter >= 'A') && (letter <= 'Z')))
break;
i++;
}
String[] values = new String[2];
values[0] = line.substring(nonWhitespaceStart, i);
values[1] = line.substring(i, line.length());
return values;
}
//------------------------------------------------------------------------------------------------
protected void doEvent(int event, String message, boolean addFileInfo)
{
switch (event)
{
case IParseEventListener.PANIC:
case IParseEventListener.ERROR:
{
hadErrors = true;
break;
}
case IParseEventListener.WARNING:
{
hadWarnings = true;
break;
}
}
if (addFileInfo)
{
message = MessageFormat.format(
"{0}: [line {1}] {2}",
new Object[]
{
inputState.getFullFilename(),
new Integer(inputState.getLine()),
message
}
);
}
for (int i = 0; i < listeners.size(); i++)
{
IParseEventListener listener = (IParseEventListener)listeners.get(i);
listener.handleEvent(event, message);
}
}
//------------------------------------------------------------------------------------------------
/**
* This method processes uncoditional input and returns when a non-preprocessor line was found.
*
* @return The line, which caused the abort of unconditional input processing.
*
* @throws IOException
*/
protected String processUnconditionalInput() throws IOException
{
// Note: Comments are handled here because we have to make sure we do not consider outcommented
// preprocessor directives. Single line and multi line comments are converted to one space
// character each, so the output does not contain comments anymore (which somewhat
// simplifies the following parser stage).
String line = null;
boolean canReturn = false;
do
{
line = readLine();
if (line == null)
break;
// Now that the string is cleaned-up we can start processing directives.
if (line.startsWith("#"))
{
canReturn = processDirective(line);
if (canReturn)
line = "";
}
else
{
// No preprocessor directive in this line, so return it to the caller if it is not empty.
if ((skip == SKIP_NONE) && !skipNonPPLines)
{
line = macroTable.expandMacros(line);
if (line.length() > 0)
canReturn = true;
}
}
}
while (!canReturn);
return line;
}
//------------------------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -