⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 preprocessor.java

📁 外国人写的c#语法解析器
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
// 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 + -