C#编写第一个解析器,早期stackoverflow检测

问题描述

我刚刚开始用 C# 编写一个简单的解析器,您可以在这里找到它:https://github.com/JohnnyErnest/LexerParser/blob/main/Program.cs#L1078

主要功能是我希望能够为我想要分析的某些语法编写 JSON 配置,在运行时加载配置并通过词法分析器/解析器评估一些字符串输入并生成包含有关信息的树每个节点都用于语法突出显示和从解析文本的部分提取变量,而不是使用第三方工具(如 yacc/bison clone)来创建要从语法编译的代码,对于某些语言,如 sql/CSS/HTML。

简单的问题是,在 StackOverflowException 之前,还有哪些其他好的方法可以从格式错误的输入中尽早检测递归问题?详情如下。

我还没有为各种语言完全构建语法,到目前为止只是对一些基本语法的简单测试,但是在尝试实现 EBNF 时遇到了一个问题,例如:https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form如何像这样评估语法语句的右侧:

rhs = identifier
     | terminal
     | "[",rhs,"]"
     | "{","}"
     | "(",")"
     | rhs,"|",rhs
     | rhs,",rhs ;

所以在我的 JSON 配置中,它的设置如下:https://github.com/JohnnyErnest/LexerParser/blob/main/Configuration/ParserEBNF.json#L178 也考虑空格。

"rhsBlock1": {
  "sequence": [
    {
      "tokenList": "whitespaces","isOptional": "true"
    },{ "tokenList": "bracketopen" },{
      "tokenList": "whitespaces",{ "sequenceList": "rhs" },{ "tokenList": "bracketClose" },"isOptional": "true"
    }
  ]
},"rhsBlock2": {
  "sequence": [
    {
      "tokenList": "whitespaces",{ "tokenList": "braceOpen" },{ "tokenList": "braceClose" },"rhsBlock3": {
  "sequence": [
    {
      "tokenList": "whitespaces",{ "tokenList": "parenthesisOpen" },{ "tokenList": "parenthesisClose" },"rhsBlockPipe": {
  "sequence": [
    {
      "tokenList": "whitespaces",{ "tokenList": "pipe" },"rhsBlockComma": {
  "sequence": [
    {
      "tokenList": "whitespaces",{ "tokenList": "comma" },"rhs": {
  "sequence": [
    { "sequenceList": "identifier,ebnfTerminal,rhsBlock1,rhsBlock2,rhsBlock3,rhsBlockPipe,rhsBlockComma" }
  ]
},

鉴于该定义,以下内容有效并评估为真:

        string inputEBNF = "a=\"5\";";
        var ebnfParserResult = ebnfParser.Parse(inputEBNF,"grammar");

但是,如果将“5”更改为文字,这是无效的 EBNF,则不会出现错误,而是会无限递归。

        string inputEBNF = "a=5;";
        var ebnfParserResult = ebnfParser.Parse(inputEBNF,"grammar");
        var exit = ebnfParser.CancelAllParsing;

exit 的值为真,因为解析器将在递归中上升到 MaxLevel,我在那时将其关闭,否则它会继续运行,直到出现 StackOverflowException。

当 JSON 配置中较早的块(如 rhsBlockComma调用后来的块 rhs 时,问题就开始了。

在我的代码中,您会看到 Parser.Parse 调用一个名为 Check方法https://github.com/JohnnyErnest/LexerParser/blob/main/Program.cs#L1017

Check 然后将调用 CheckSequence: https://github.com/JohnnyErnest/LexerParser/blob/main/Program.cs#L954 它将检查序列的每个部分,调用 CheckSequenceSection {{3} }

CheckSequenceSection 首先查看 JSON 部分的 tokenList 中的每个词法分析器标记,然后查看该部分的 sequenceList 中的每个序列,并调用CheckSequence 在每个序列上进行一次递归。

如果输入有效,通常可以正常工作。如果不是,则跟踪变量 level,如果它超过 MaxLevel,则发生中止。我还需要在 CheckSequenceSectionCheckSequence 之前添加一个抢占式返回,否则它只会继续递归直到 StackOverflowException

还有哪些其他方法可以从格式错误的输入中早期检测递归问题?

解决方法

这里对答案进行了更好的解释:https://stackoverflow.com/a/20179940/5556724

维基百科上的EBNF版本是这样写的:

rhs = identifier
     | terminal
     | "[",rhs,"]"
     | "{","}"
     | "(",")"
     | rhs,"|",rhs
     | rhs,",rhs ;

但应该这样写:

primary = identifier
        | terminal
        | "[","]"
        | "{","}"
        | "(",")"
        ;
factor  = primary,{ "|",primary } ;
rhs     = factor,{ ",factor } ;

否则会出现 FIRST/FIRST 冲突。