【问题标题】:Why won't my JavaCC lexer/parser accept this input?为什么我的 JavaCC 词法分析器/解析器不接受这个输入?
【发布时间】:2016-08-07 20:42:06
【问题描述】:

我正在创建一个词法分析器/解析器,它应该接受属于无限语言集的字符串。
一个这样的字符串是"a <2L>AA <2U>a <2L>AA <2U>a</2U></2L></2U></2L>"

语言集定义如下:

基础语言,L0

  • 来自 L0 的字符串由几个由空格字符分隔的块组成。 必须至少存在一个块。
  • 块是奇数长度的小写字母 (a-z) 序列。
  • 第一个块之前或最后一个块之后不允许有空格。
  • 块之间的空格数必须是奇数。

属于L0的字符串示例:

zyx abcba m xyzvv

zyxabcba之间有1个空格字符,有3个空格 在abcbam 之间,并且在mxyzvv 之间只有一个。字符串中没有其他空格字符。


语言 L1

  • 来自 L1 的字符串由几个由空格字符分隔的块组成。 必须至少存在一个块。
  • 有两种块。第一类块必须是 大写字母 (A-Z) 的偶数长度序列。一块 第二种必须有<2U>. . .</2U>的形状,. . .代表 对于 L0 中的任何字符串。
  • 第一个块之前或最后一个块之后不允许有空格。
  • 块之间的空格数必须是奇数。

属于L1的字符串示例:

YZ <2U>abc zzz</2U> ABBA <2U>kkkkk</2U> KM

请注意,YZ<2U>abc zzz</2U> 有五个空格分开,abczzz 分开三个空格。否则使用单个空格作为分隔符。 YZ 前面没有空格,KM 后面也没有空格。


语言 L2

  • 来自 L2 的字符串由多个由空格字符分隔的块组成。 必须至少存在一个块。
  • 有两种块。第一类块必须是 一个奇数长度的小写字母序列 (a-z)。一块 第二种必须具有<2L>. . .</2L> 的形状,其中. . . 代表 对于 L1 中的任何字符串。
  • 第一个块之前或最后一个块之后不允许有空格。
  • 块之间的空格数必须是奇数。

属于L2的字符串示例:

abc <2L>AA ZZ <2U>a bcd</2U></2L> z <2L><2U>abcde</2U></2L>

在上面给出的句子中使用单个空格作为分隔符,但任何其他奇数个空格也会导致有效的 L2 句子。


语言 L{2k + 1}, k > 0

  • 来自 L{2k + 1} 的字符串由多个由空格字符分隔的块组成。必须至少存在一个块。
  • 有两种块。第一类块必须是 大写字母 (A-Z) 的偶数长度序列。一块 第二种必须有<2U>. . .</2U>的形状,. . .代表 对于来自 L{2k} 的任何字符串。
  • 第一个块之前或最后一个块之后不允许有空格。
  • 块之间的空格数必须是奇数。

语言 L{2k + 2}, k > 0

  • 来自 L{2k + 2} 的字符串由几个用空格分隔的块组成 人物。必须至少存在一个块。
  • 有两种块。第一类块必须是 一个奇数长度的小写字母序列 (a-z)。一块 第二种必须有<2L>. . .</2L>的形状,. . .代表 对于来自 L{2k + 1} 的任何字符串。
  • 第一个块之前或最后一个块之后不允许有空格。
  • 块之间的空格数必须是奇数。

我的词法分析器/解析器的代码如下:

PARSER_BEGIN(Assignment)

  /** A parser which determines if user's input belongs to any one of the set of acceptable languages. */
  public class Assignment {
    public static void main(String[] args) { 
      try {
        Assignment parser = new Assignment(System.in);
        parser.Start();
        System.out.println("YES"); // If the user's input belongs to any of the set of acceptable languages, then print YES.
      } catch (ParseException e) {
        System.out.println("NO"); // If the user's input does not belong to any of the set of acceptable languages, then print NO.      
      }
    }
  }

PARSER_END(Assignment)

//** A token which matches any lowercase letter from the English alphabet. */
TOKEN :
{
  < #L_CASE_LETTER: ["a"-"z"] >
}

//* A token which matches any uppercase letter from the English alphabet. */
TOKEN:
{
  < #U_CASE_LETTER: ["A"-"Z"] >
}

//** A token which matches an odd number of lowercase letters from the English alphabet. */
TOKEN:
{
  < ODD_L_CASE_LETTER: <L_CASE_LETTER>(<L_CASE_LETTER><L_CASE_LETTER>)* >
}

//** A token which matches an even number of uppercase letters from the English alphabet. */
TOKEN:
{
  < EVEN_U_CASE_LETTERS: (<U_CASE_LETTER><U_CASE_LETTER>)+ >
}

//* A token which matches the string "<2U>" . */
TOKEN:
{
  < OPEN_UPPER: "<2U>" >
}

//* A token which matches the string "</2U>". */
TOKEN:
{
  < CLOSE_UPPER: "</2U>" >
}

//* A token which matches the string "<2L>". */
TOKEN:
{
  < OPEN_LOWER: "<2L>" >
}

//* A token which matches the string "</2L>". */
TOKEN:
{
  < CLOSE_LOWER: "</2L>" >
}

//* A token which matches an odd number of white spaces. */
TOKEN : 
{
  < ODD_WHITE_SPACE: " "(" "" ")* >
}

//* A token which matches an EOL character. */
TOKEN:
{
 < EOL: "\n" | "\r" | "\r\n" >
}

/** This production matches strings which belong to the base language L^0. */
void Start() :
{}
{
  LOOKAHEAD(3)
  <ODD_L_CASE_LETTER> (<ODD_WHITE_SPACE> <ODD_L_CASE_LETTER>)* <EOL> <EOF>

  |

  NextLanguage()

  |

  LOOKAHEAD(3)
  NextLanguageTwo()

  |

  EvenLanguage()
}

/** This production matches strings which belong to language L^1. */
void NextLanguage():
{}
{
  (<OPEN_UPPER> (PseudoStart()) <CLOSE_UPPER>)+ (<ODD_WHITE_SPACE> UpperOrPseudoStart())* <EOL> <EOF>

  |

  (<EVEN_U_CASE_LETTERS>)+ (<ODD_WHITE_SPACE> UpperOrPseudoStart())* <EOL> <EOF>
}

/** This production matches either an even number of uppercase letters, or a string from L^0, encased within the tags <2U> and </2U>. */
void UpperOrPseudoStart() :
{}
{
  <EVEN_U_CASE_LETTERS>

  |

  <OPEN_UPPER> (PseudoStart()) <CLOSE_UPPER>
}

/** This production matches strings from L^0, in a similar way to Start(); however, the strings that it matches do not have EOL or EOF characters after them. */
void PseudoStart() :
{}
{
  <ODD_L_CASE_LETTER> (<ODD_WHITE_SPACE> <ODD_L_CASE_LETTER>)*
}

/** This production matches strings which belong to language L^2. */
void NextLanguageTwo() :
{}
{
  (<ODD_L_CASE_LETTER>)+ (<ODD_WHITE_SPACE> LowerOrPseudoNextLanguage())* <EOL> <EOF>

  |

  (<OPEN_LOWER> PseudoNextLanguage() <CLOSE_LOWER>)+ (<ODD_WHITE_SPACE> LowerOrPseudoNextLanguage())* <EOL> <EOF>
}

/** This production matches either an odd number of lowercase letters, or a string from L^1, encased within the tags <2L> and </2L>. */
void LowerOrPseudoNextLanguage() :
{}
{
  <ODD_L_CASE_LETTER> 

  |

  <OPEN_LOWER> PseudoNextLanguage() <CLOSE_LOWER>
}

/** This production matches strings from L^1, in a similar way to NextLanguage(); however, the strings that it matches do not have EOL or EOF characters after them. */
void PseudoNextLanguage() :
{}
{
  (<OPEN_UPPER> (PseudoStart()) <CLOSE_UPPER>)+ (<ODD_WHITE_SPACE> UpperOrPseudoStart())*

  |

  (<EVEN_U_CASE_LETTERS>)+ (<ODD_WHITE_SPACE> UpperOrPseudoStart())*
}

/** This production matches strings which belong to any of the languages L^{2k + 2}, where k > 0 (the infinite set of even languages). */ 
void EvenLanguage() :
{}
{
  (<ODD_L_CASE_LETTER>)+ (<ODD_WHITE_SPACE> EvenLanguageAuxiliary())* <EOL> <EOF>

  |

  (CommonPattern())+ (<ODD_WHITE_SPACE> EvenLanguageAuxiliary())* <EOL> <EOF>
}

/** This production is an auxiliary production that helps when parsing strings from any of the even set of languages. */
void EvenLanguageAuxiliary() :
{}
{
  CommonPattern()

  |

  <ODD_L_CASE_LETTER>
}


void CommonPattern() :
{}
{
  <OPEN_LOWER> <EVEN_U_CASE_LETTERS> <ODD_WHITE_SPACE> <OPEN_UPPER> <ODD_L_CASE_LETTER> (<ODD_WHITE_SPACE> CommonPattern())+ <CLOSE_UPPER> <CLOSE_LOWER>
}

现在有好几次,我都输入了字符串"a &lt;2L&gt;AA &lt;2U&gt;a &lt;2L&gt;AA &lt;2U&gt;a&lt;/2U&gt;&lt;/2L&gt;&lt;/2U&gt;&lt;/2L&gt;"
但是,每次都会在终端上打印出NO
我仔细查看了我的代码几次,检查了我认为应该解析输入字符串的顺序;但是,我无法在我的逻辑中找到任何错误或字符串未被接受的原因。

请给我一些关于为什么它不被接受的建议吗?

【问题讨论】:

  • 如果您不准备描述该语言,至少要为其提供语法,那么很难看到任何人可以帮助您。
  • @EJP:对不起。没想到会造成这么大的困难。我会马上修改我的问题。
  • 您尝试过 JavaCC 的任何调试选项吗?我建议你这样做,看看你的期望和现实在哪里分歧。
  • 解析这种语言的动机是什么?这只是一个毫无意义的家庭作业还是有一个应用程序?
  • @TheodoreNorvell 这是一个有意义的家庭作业。

标签: parsing compiler-construction lexer javacc


【解决方案1】:

您的输入是否被接受?我已将您的代码复制到我的计算机上,并发现任何正确的输入(据我从您的语言定义中可以看出),它总是输出“NO”。

【讨论】:

  • 是的。例如,字符串"YZ &lt;2U&gt;abc zzz&lt;/2U&gt; ABBA &lt;2U&gt;kkkkk&lt;/2U&gt; KM" 被接受。这是一个位于 L1 中的字符串,它在我的终端上被接受。
  • 很奇怪,即使这个字符串使用你的确切程序在我的终端上输出'NO'。
  • 我要再试一次。我不确定发生了什么。
  • 它仍在工作,这次我复制了本文中使用的准确词法分析器/解析器代码。我将处理@TheodoreNorvell 上面的评论。
【解决方案2】:

以下步骤有助于解决问题。

  1. 运行以下代码:
    javacc -debug_parser Assignment.jj
    javac Assignment*.java
  2. 然后,运行词法分析器/解析器(通过键入java Assignment),然后输入字符串:
    "a &lt;2L&gt;AA &lt;2U&gt;a &lt;2L&gt;AA &lt;2U&gt;a&lt;/2U&gt;&lt;/2L&gt;&lt;/2U&gt;&lt;/2L&gt;"
  3. 解析器操作的结果跟踪显示在此字符串上调用了产生式NextLangaugeTwo(),而不是所需的EvenLanguage() 产生式。
  4. 跟踪NextLangaugeTwo() 表明它匹配输入字符串中的前八个标记。
  5. 因此,使用 9 的前瞻虽然效率低下,但会导致输入字符串被接受。也就是说,通过将第二个前瞻值(就在对 NextLanguageTwo() 的调用上方)从 3 更改为 9 来修改 Start() 产生式。

【讨论】:

  • 我真的不喜欢将前瞻从 3 更改为 9 的建议,除非有分析表明没有需要 10 的字符串。另一方面,使用的建议“debug_parser”就在眼前;我希望每个有这种问题的人在发帖前至少做一下基本的调查。
  • @TheodoreNorvell 我还没有找到解决这个问题的另一种方法。此外,这是我第一次了解到 debug_parser 能够做到这一点。如果给您带来不便,请见谅。
  • NextLanguageTwoEvenLanguage 都可以以任何正数的&lt;ODD_L_CASE_LETTER&gt; 标记开始,因此很明显,前瞻的任何有限界限都是不够的。您可以尝试语法前瞻。
  • 或者您可以尝试分解出公共前缀。这可能就是我会做的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 1970-01-01
相关资源
最近更新 更多