在C#中解析脚本循环

时间:2013-03-20 10:33:02

标签: algorithm c#-3.0 text-parsing

我正在编写一个应用程序,它将使用自定义语言解析脚本(基于稍微的C语法和Allman style格式化)并且我正在寻找更好的(读取:更快)将脚本代码块解析为字符串数组的方式比我当前正在进行的方式(当前方法可以做到,但是调试比其他任何方法更多)。

脚本内容当前从文件读取到字符串数组中并传递给方法。

这是一个脚本块模板:

loop [/* some conditional */ ]
{
  /* a whole bunch of commands that are to be read into
   * a List<string>, then converted to a string[] and
   * passed to the next step for execution */

   /* some command that has a bracket delimited set of
    * properties or attributes */
   {
     /* some more commands to be acted on */
   }
}

基本上,花括号块可以嵌套(就像在任何其他基于C语言中一样),我正在寻找找到这样的单个块的最佳方法。

大括号分隔的块将始终格式化为这样 - 括号的内容将从开放括号后面的行开始,最后一个属性后面的行后面会有一个括号/命令/评论/不管。

一个例子可能是:

loop [ someVar <= 10 ]
{
  informUser "Get ready to do something"
  readValue
  {
    valueToLookFor = 0x54
    timeout = 10 /* in seconds */
  }
}

这会告诉应用程序循环,而someVar小于10(抱歉吸蛋评论)。每次回合,我们都会向用户传递一条消息,并从某处查找特定值(超时为10秒)。

这就是我现在正在做的事情(注意:调用它的方法将包含当前脚本的整个字符串[]传递给它,并带有要读取的索引):

private string[] findEntireBlock(string[] scriptContents, int indexToReadFrom,
                                out int newIndex)
{
    newIndex = 0;
    int openBraceCount = 0;     // for '{' char count
    int closeBraceCount = 0;    // for '}' char count
    int openSquareCount = 0;    // for '[' char count
    int closeSquareCount = 0;   // for ']' char count

    List<string> fullblock = new List<string>();

    for (int i = indexToReadFrom; i < scriptContents.Length; i++)
    {
        if (scriptContents[i].Contains('}'))
        {
            if (scriptContents[i].Contains("[") && fullblock.Count > 0)
            {
                //throw new exception, as we shouldn't expect to
                //to find a line which starts with [ when we've already
            }
            else
            {
                if (scriptContents[i].Contains('{')) openBraceCount++;
                if (scriptContents[i].Contains('}')) closeBraceCount++;
                if (scriptContents[i].Contains('[')) openSquareCount++;
                if (scriptContents[i].Contains(']')) closeBraceCount++;
                newIndex = i;
                fullblock.Add(scriptContents[i]);
                break;
            }
        }
        else
        {
            if (scriptContents[i].Contains("[") && fullblock.Count > 0)
            {
                //throw new exception, as we shouldn't expect to
                //to find a line which starts with [ when we've already
            }
            else
            {
                if (scriptContents[i].Contains('{')) openBraceCount++;
                if (scriptContents[i].Contains('}')) closeBraceCount++;
                if (scriptContents[i].Contains('[')) openSquareCount++;
                if (scriptContents[i].Contains(']')) closeBraceCount++;
                fullblock.Add(scriptContents[i]);
            }
        }
    }
    if (openBraceCount == closeBraceCount &&
        openSquareCount == closeSquareCount)
          return fullblock.ToArray();
    else
        //throw new exception, the number of open brackets doesn't match
        //the number of close brackets
}

我同意这可能是一个稍微迟钝和缓慢的方法,这就是为什么我要求有关如何重新实现这一点的速度和清晰度的任何想法(如果可以达到平衡,那就是)。

我希望远离RegEx,因为我无法使用它来维持括号数,而且我不确定你是否可以编写一个RegEx语句(这是正确的术语吗?)递归。我想从内向外工作,但我确信这会很慢。

正在寻找有人为我重新编写它,但我可以使用的算法或技术/库的一般概念可以改善我的方法。

作为一个附带问题,编译器如何处理源代码中的多个嵌套括号?

1 个答案:

答案 0 :(得分:3)

杰克·克伦肖(Jack Crenshaw)的

Let's Build a Compiler是一本非常棒的,易于阅读的关于构建基本编译器的介绍。所讨论的技术应该有助于你在这里尝试做什么。