📄 preprocessor.java
字号:
// Created on 02.05.2004
package net.softgems.resourceparser.preprocessor;
import java.io.*;
import java.text.MessageFormat;
import java.util.*;
import net.softgems.resourceparser.expressions.*;
import net.softgems.resourceparser.main.IParseEventListener;
import antlr.*;
import antlr.collections.AST;
/**
* The preprocess class is responsible for handling every preprocessor directive in the input. It
* reads input from the InputConverter stored in the input state and feeds the lexer with its output.
*
* @author Mike Lischke
*/
public class Preprocessor extends Reader implements IParseEventListener
{
private static final int DEFINE = 0;
private static final Integer DEFINE_DIRECTIVE = new Integer(DEFINE);
/** Some static data for quick lookups. Also the state stack needs objects. */
private static final HashMap directives = new HashMap(10);
private static final int ELIF = 1;
private static final Integer ELIF_DIRECTIVE = new Integer(ELIF);
private static final int ELSE = 2;
private static final Integer ELSE_DIRECTIVE = new Integer(ELSE);
private static final int ENDIF = 3;
private static final Integer ENDIF_DIRECTIVE = new Integer(ENDIF);
private static final int ERROR = 4;
private static final Integer ERROR_DIRECTIVE = new Integer(ERROR);
private static final int IF = 5;
private static final Integer IF_DIRECTIVE = new Integer(IF);
private static final int IFDEF = 6;
private static final Integer IFDEF_DIRECTIVE = new Integer(IFDEF);
private static final int IFNDEF = 7;
private static final Integer IFNDEF_DIRECTIVE = new Integer(IFNDEF);
private static final int INCLUDE = 8;
private static final Integer INCLUDE_DIRECTIVE = new Integer(INCLUDE);
private static final int PRAGMA = 9;
private static final Integer PRAGMA_DIRECTIVE = new Integer(PRAGMA);
private static final int UNDEF = 10;
private static final Integer UNDEF_DIRECTIVE = new Integer(UNDEF);
/** Skip modes used in the state machine. */
private static final int SKIP_CONDITIONAL = 0;
private static final int SKIP_NONE = 1;
private static final int SKIP_PARENT = 2;
private static final int SKIP_SUBTREE = 3;
/** Regular expression containing allowed white spaces to split parts on a line. */
private static final String WHITESPACES = " |\t";
/**
* This charset is assumed to be the initial encoding for the input files. Though this might not
* always be the case and character sets can change within the file.
*/
public static final String DEFAULT_CHARSET = "ISO-8859-1";
//------------------------------------------------------------------------------------------------
static
{
directives.put("define", DEFINE_DIRECTIVE);
directives.put("undef", UNDEF_DIRECTIVE);
directives.put("if", IF_DIRECTIVE);
directives.put("ifdef", IFDEF_DIRECTIVE);
directives.put("ifndef", IFNDEF_DIRECTIVE);
directives.put("else", ELSE_DIRECTIVE);
directives.put("elif", ELIF_DIRECTIVE);
directives.put("endif", ENDIF_DIRECTIVE);
directives.put("include", INCLUDE_DIRECTIVE);
directives.put("error", ERROR_DIRECTIVE);
directives.put("pragma", PRAGMA_DIRECTIVE);
}
/** The current input line that is used to feed the lexer. */
private StringBuffer currentLine = new StringBuffer();
/** The current read position in the input buffer. */
private int currentPosition;
private boolean hadErrors;
private boolean hadWarnings;
private ArrayList includePaths;
/** Flag, which indicates pending comment lines. */
private boolean inMultilineComment;
/** The class that feeds the preprocessor. */
private InputConverter input;
/**
* The input state itself has a reference to this preprocess but the processor also needs
* a back reference in order to restore the previous input state once its input is exhausted.
*/
private PreprocessorInputState inputState;
/** List of event listeners who want to get notified about a preprocessor event. */
private ArrayList listeners = new ArrayList();
/** Contains a a mapping of identifiers to macro definitions. */
private MacroTable macroTable;
/** A stack of pending states, which must be correctly finished for a valid file. */
private Stack pendingStates = new Stack();
/** A list of already processed include files, which are marked with the #pragma once directive. */
private HashMap processedIncludes;
/** Indicates if the preprocessor is currently skipping lines. */
private int skip;
/** A stack of skip flags for the state machine. */
private Stack skipFlags = new Stack();
/** This field is <b>true</b> when the current input file is either a *.c or a *.h file. */
private boolean skipNonPPLines;
/** Only for debugging. If <b>true</b> then input and output are printed to console. */
private boolean traceProcessing;
//------------------------------------------------------------------------------------------------
public Preprocessor(InputConverter input, Preprocessor parentProcessor, PreprocessorInputState inputState,
boolean traceProcessing)
{
this.inputState = inputState;
this.traceProcessing = traceProcessing;
this.input = input;
// Initial skip states.
skip = SKIP_NONE;
skipFlags.push(new Integer(skip));
// If the parent processor is set then this is an instance called for an include file.
// Share certain structures with the parent processor instead creating own ones.
if (parentProcessor != null)
{
macroTable = parentProcessor.macroTable;
includePaths = parentProcessor.includePaths;
processedIncludes = parentProcessor.processedIncludes;
}
else
{
macroTable = new MacroTable();
macroTable.addMacroEventListener(
new IParseEventListener()
{
public void handleEvent(int event, String message)
{
doEvent(event, message, true);
};
}
);
includePaths = new ArrayList();
processedIncludes = new HashMap();
}
}
//------------------------------------------------------------------------------------------------
/**
* Evaluates the given expression by setting up a local parser and evaluator.
*
* @param expression The expression to evaluate.
* @return <b>true</b> if the expression turns out to be true, otherwise <b>false</b>.
*/
private boolean evaluateBooleanExpression(String expression)
{
// This variable holds the abstract syntax tree for the parsed expression.
AST expressionTree = null;
try
{
expressionTree = ExpressionParser.parse(expression, inputState.getFilename(), inputState.getLine(),
inputState.getColumn(), this);
}
catch (RecognitionException e)
{
reportError(e.getMessage());
}
catch (TokenStreamException e)
{
reportError(e.getMessage());
}
boolean result = false;
try
{
result = Evaluator.evaluateToBoolean(expressionTree, null, true);
}
catch (EvaluationException e)
{
reportError(e.getMessage());
}
return result;
}
//------------------------------------------------------------------------------------------------
/**
* Converts the first identifer on the given line (which must start with a number sign) to an
* internal ID for quicker processing.
*
* @param line The current input line, including the number sign.
* @return An integer value for the found directive or -1 if unknown.
*/
private int getDirective(String line)
{
String[] parts = splitDirective(line);
Integer value = (Integer)directives.get(parts[0]);
if (value == null)
return -1;
else
return value.intValue();
}
//------------------------------------------------------------------------------------------------
/**
* Called when a #define directive is found. Get the identifier and the associated expression
* and put it into our symbol list.
*
* @param definition Input containing the identifier and the associated expression (if any).
*/
private void processDefine(String definition)
{
macroTable.defineMacro(definition);
}
//------------------------------------------------------------------------------------------------
/**
* Checks for any known preprocessor directive and triggers individual processing.
*
* @param input The directive with the number sign.
* @return <b>true</b> if the calling method should stop its reader loop.
*/
private boolean processDirective(String input) throws FileNotFoundException,
IOException
{
boolean result = false;
int directive = getDirective(input);
// Remove the leading number sign and split the rest into two parts of which the first
// one is the directive and the second one the expression to parse.
String[] parts = splitDirective(input);
String directiveText = parts[1].trim();
switch (directive)
{
case INCLUDE:
{
if (skip == SKIP_NONE)
result = processInclude(macroTable.expandMacros(directiveText));
break;
}
case DEFINE:
{
if (skip == SKIP_NONE)
processDefine(parts[1]);
break;
}
case UNDEF:
{
if (skip == SKIP_NONE)
processUndef(parts[1]);
break;
}
case PRAGMA:
{
if (skip == SKIP_NONE)
processPragma(parts);
break;
}
case ERROR:
{
if (skip == SKIP_NONE)
reportError(parts[1]);
break;
}
case IFDEF:
{
pendingStates.push(IF_DIRECTIVE);
skipFlags.push(new Integer(skip));
switch (skip)
{
case SKIP_NONE:
{
boolean condition = macroTable.isDefined(directiveText);
if (!condition)
skip = SKIP_CONDITIONAL;
break;
}
case SKIP_CONDITIONAL:
{
// If there was already a previous condition that failed then we are
// entering a sub condition right now. Change the skip mode to parent skip to reflect that.
skip = SKIP_PARENT;
break;
}
}
break;
}
case IFNDEF:
{
pendingStates.push(IF_DIRECTIVE);
skipFlags.push(new Integer(skip));
switch (skip)
{
case SKIP_NONE:
{
boolean condition = !macroTable.isDefined(directiveText);
if (!condition)
skip = SKIP_CONDITIONAL;
break;
}
case SKIP_CONDITIONAL:
{
// If there was already a previous condition that failed then we are
// entering a sub condition right now. Change the skip mode to parent skip to reflect that.
skip = SKIP_PARENT;
break;
}
}
break;
}
case IF:
{
pendingStates.push(IF_DIRECTIVE);
skipFlags.push(new Integer(skip));
switch (skip)
{
case SKIP_NONE:
{
boolean condition = evaluateBooleanExpression(macroTable.expandMacros(directiveText));
if (!condition)
skip = SKIP_CONDITIONAL;
break;
}
case SKIP_CONDITIONAL:
{
// If there was already a previous condition that failed then we are
// entering a sub condition right now. Change the skip mode to parent skip to reflect that.
skip = SKIP_PARENT;
break;
}
}
break;
}
case ELSE:
{
Integer pendingState = (Integer) pendingStates.pop();
// See what the state was, which opened this conditional branch.
switch (pendingState.intValue())
{
case IF:
case IFDEF:
case IFNDEF:
case ELIF:
{
pendingStates.push(ELSE_DIRECTIVE);
// If the superordinated conditional branch was already skipping then continue that,
// otherwise reveverse the skip mode for this branch.
switch (skip)
{
case SKIP_NONE:
{
skip = SKIP_CONDITIONAL;
break;
}
case SKIP_CONDITIONAL:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -