如何匹配括号来分析 S 表达式



我正在尝试创建一个执行以下操作的函数:

假设代码输入为"(a 1 2 (b 3 4 5 (c 6) |7) 8 9)"管道在哪里 |符号是光标的位置,

该函数返回:a 字符串"b 3 4 5 (c 6) 7",表示游标范围内的代码

一个 int 8,表示字符串相对于输入的起始索引

一个 int 30,表示字符串相对于输入的结束索引

我已经有完全返回该内容的工作代码。但是,问题在于忽略注释,同时跟踪上下文(例如字符串文字,我自己的文字分隔符等)。

以下是跟踪上下文的代码:

public static void applyContext(Context context, String s, String snext, String sprev) {
        if (s.equals(""")) {
            if (context.context == Context.Contexts.MAIN) {
                context.context = Context.Contexts.STRING;
                context.stringDelimiterIsADoubleQuote = true;
            } else if (context.context == Context.Contexts.STRING && context.stringDelimiterIsADoubleQuote && !sprev.equals("\"))
                context.context = Context.Contexts.MAIN;
        } else if (s.equals("'")) {
            if (context.context == Context.Contexts.MAIN) {
                context.context = Context.Contexts.STRING;
                context.stringDelimiterIsADoubleQuote = false;
            } else if (context.context == Context.Contexts.STRING && !context.stringDelimiterIsADoubleQuote && !sprev.equals("""))
                context.context = Context.Contexts.MAIN;
        } else if (s.equals("/") && snext.equals("/")) {
            if (context.context == Context.Contexts.MAIN)
                context.context = Context.Contexts.COMMENT;
        } else if (s.equals("n")) {
            if(context.context == Context.Contexts.COMMENT)
                context.context = Context.Contexts.MAIN;
        }
        else if (s.equals("\")) {
            if(context.context == Context.Contexts.MAIN)
                context.context = Context.Contexts.PATTERN;
            else if(context.context == Context.Contexts.PATTERN)
                context.context = Context.Contexts.MAIN;
        }
    }

首先,我将像这样使用上面的函数:

String sampleCode = "(a b "cdef" g c4 bb2 eb4 g4v0.75)";
Context c = new Context(Context.Contexts.MAIN);
for(int i = 0; i < sampleCode.length(); i++) {
    String s = String.valueOf(sampleCode.charAt(i));
    String snext = *nullcheck* ? String.valueOf(sampleCode.charAt(i + 1)) : "";
    String sprev = *nullcheck* ? String.valueOf(sampleCode.charAt(i - 1)) : "";
    applyContext(c, s, snext, sprev);
    if(c.context == blahlbah) doBlah();
}

其次,我将同时向前和向后使用它,因为当前执行描述顶部所述的函数的方法(在伪代码中)是这样的:

function returnCodeInScopeOfCursor(theWholeCode::String, cursorIndex::int) {
  var depthOfCodeAtCursorPosition::int = getDepth(theWholeCode, cursorIndex);
  Context c = new Context(getContextAt(theWholeCode, cursorIndex));
  var currDepth::int = depthOfCodeAtCursorPosition;
  var startIndex::int, endIndex::int;
  for(i = cursorIndex; i >= 0; i--) {//going backwards
    s = .....
    snext = ......
    sprev = ......
    applyContext(c, s, snext, sprev);
    if(c.context == Context.MAIN) {
       if s = "(" then currDepth --;
       if s = ")" then currDepth ++;
    }
    when currDepth < depthOfCodeAtCursorPosition
      startIndex = i + 1;
      break;
  }
  currDepth = depthOfCodeAtCursorPosition;//reset
  for(i = cursorIndex; i < theWholeCode.length; i++) {//going forwards
    s = ...
    snex......
    sprev.....
    applyContext(c, s, snext, sprev);
    if(c.context == Context.MAIN) {
      if s = "(" then currDepth ++;
      if s = ")" then currDepth --;
    }
    when currDepth < depthOfCodeAtCursorPosition
      endIndex = i - 1;
      break;
  }
  var returnedStr = theWholeCode->from startIndex->to endIndex
  return new IndexedCode(returnedStr, startIndex, endIndex);

如您所见,此函数可以向前和反向工作。或者至少大部分。唯一的问题是,如果我向后使用此函数,则正确扫描注释(由标准ECMA双斜杠"//"表示)就会混乱。

如果我为反向上下文应用程序创建一个单独的函数并递归检查每一行是否有双斜杠,然后将该"//"之后的所有内容都设为 COMMENT(或按照函数的使用方向,之前的所有内容//),这将花费太多的处理时间,因为我想将其用作音乐的实时编码环境。

此外,在尝试returnCodeInScopeOfCursor方法之前删除注释可能不可行......因为我需要跟踪代码的索引以及什么不是。如果我要删除注释,所有代码位置都会一团糟,并跟踪我在哪里删除了什么以及多少字符等......我正在使用的文本区域输入 GUI (RichTextFX) 不支持行字符跟踪,因此仅使用 char 索引跟踪所有内容,因此问题...

所以。。。我对如何处理我当前的代码感到非常困惑。任何帮助,建议,建议等...将不胜感激。

您能否将注释从// This is a comment<CR>预转换为{ This is a comment}<CR>然后您拥有一种可以前后行走的语言。

在进入时应用此转换,在输出时反转它,一切都应该很好。请注意,我们将//...替换为{...},以便保留所有字符偏移量。

无论如何,在对 OldCurmudgeon 的想法进行了一些实验之后,我想出了一个单独的函数来获取相反方向的代码上下文。

public static void applyContextBackwards(Context context, String entireCode, int caretPos) {
        String s = String.valueOf(entireCode.charAt(caretPos));
        //So far this is not used
        //String snext = caretPos + 1 < entireCode.length() ? String.valueOf(entireCode.charAt(caretPos + 1)) : "";
        String sprev = caretPos - 1 >= 0 ? String.valueOf(entireCode.charAt(caretPos - 1)) : "";
        //Check for all the flags and what not...
        if(context.commentedCharsLeft > 0) {
            context.commentedCharsLeft--;
            if(context.commentedCharsLeft == 0)
                context.context = Context.Contexts.MAIN;//The comment is over
        }
        if(context.expectingEndOfString){
            context.context = Context.Contexts.MAIN;
            context.expectingEndOfString = false;
        }
        if(context.expectingEndOfPattern) {
            context.context = Context.Contexts.MAIN;
            context.expectingEndOfPattern = false;
        }
        //After all the flags are cleared, do this
        if(context.commentedCharsLeft == 0) {
            if (s.equals(""")) {
                if (context.context == Context.Contexts.MAIN) {
                    context.context = Context.Contexts.STRING;
                    context.stringDelimiterIsADoubleQuote = true;
                } else if (context.context == Context.Contexts.STRING && context.stringDelimiterIsADoubleQuote && !sprev.equals("\"))
                    context.expectingEndOfString = true;//Change the next char to a MAIN, cuz this one's still part of the string
            } else if (s.equals("'")) {
                if (context.context == Context.Contexts.MAIN) {
                    context.context = Context.Contexts.STRING;
                    context.stringDelimiterIsADoubleQuote = false;
                } else if (context.context == Context.Contexts.STRING && !context.stringDelimiterIsADoubleQuote && !sprev.equals("""))
                    context.expectingEndOfString = true;//Change the next char to a MAIN, cuz this one's still part of the string
            } else if (s.equals("n")) {
                int earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine = -1;//-1 for no comments
                //Loop until the next n is found. In the process, determine location of comment if any
                for(int i = caretPos; i >= 0; i--) {
                    String curr = String.valueOf(entireCode.charAt(i));
                    String prev = i - 1 >= 0 ? String.valueOf(entireCode.charAt(i - 1)) : "";
                    if(curr.equals("n"))
                        break;//Line has been scanned through
                    if(curr.equals("/") && prev.equals("/"))
                        earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine = caretPos - i;
                }
                //Set the comment context flag
                //If no comments, -1 + 1 will be 0 and will be treated as no comments.
                context.commentedCharsLeft = earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine + 1;
                if(earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine > 0) {
                    context.context = Context.Contexts.COMMENT;
                }
            } else if (s.equals("\")) {
                if (context.context == Context.Contexts.MAIN)
                    context.context = Context.Contexts.PATTERN;
                else if (context.context == Context.Contexts.PATTERN)
                    context.expectingEndOfPattern = true;//Change the next char to a MAIN cuz this one's still part of the Pattern
            }
        }
    }

相关内容

  • 没有找到相关文章

最新更新