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

📄 macrotable.java

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