异常类
JavaCC 支持两种异常类型:
异常类型 描述
TokenMgrError 每当令牌管理器检测到问题时,它都会抛出异常TokenMgrError。
ParseException 每当解析器检测到问题时,它都会抛出异常ParseException。
TokenMgrError是 的子类ErrorwhileParseException是 的子类Exception。
这里的原因是令牌管理器永远不会抛出异常 - 您必须小心定义令牌规范,以便涵盖所有情况。因此后缀Error在TokenMgrError. 你不必担心这个异常 - 如果你设计的令牌很好,它永远不会被抛出。
相反,通常尝试从Parser错误中恢复。如果您仍想从令牌管理器错误中恢复,您可以这样做——只是您没有被迫捕获它们。
JavaCC 语法规范包括一种语法,用于指定可能由对应于非终端的方法抛出的其他异常。此语法与 Javathrows …语法相同。
以下是如何使用它的示例:
void VariableDeclaration() throws SymbolTableException, IOException :
{…}
{
…
}
VariableDeclaration被定义为抛出异常SymbolTableException,IOException除了ParseException.
错误报告
错误报告的方案很简单——只需ParseException.java根据您的目的修改文件。通常,您应该修改该getMessage()方法以执行您自己的自定义错误报告。有关这些方法的所有信息都可以从生成的文件ParseException.java和TokenMgrError.java. 它还有助于理解该类的标准 Java 功能Throwable。
生成的解析器中有一个方法叫做generateParseException(). 您可以随时调用此方法来生成 类型的对象ParseException。该对象将包含自上次成功使用令牌以来解析器尝试过的所有选择。
错误恢复
JavaCC 提供了两种错误恢复——浅层恢复和深度恢复。
如果当前的选择都没有成功被选中,那么浅恢复将恢复,而深度恢复是选择选择的时候,但是在此选择的解析期间某个时候发生错误。
浅层错误恢复
考虑以下示例:
void Stm() :
{}
{
IfStm()
|
WhileStm()
}
让我们假设IfStmstarts with the reserved wordif并WhileStm以 reserved word 开头while。IfStm假设当norWhileStm都不能与下一个输入标记匹配时(假设是LOOKAHEAD),您希望通过一直跳到下一个分号来恢复1。也就是说,下一个标记既不是if也不是while。
您可以指定以下内容:
void Stm() :
{}
{
IfStm()
|
WhileStm()
|
error_skipto(SEMICOLON)
}
但是你必须先定义error_skipto。就 JavaCC 而言,error_skipto就像任何其他非终端一样。
下面是一种定义方式error_skipto(这里我们使用标准JAVACODE制作):
JAVACODE
void error_skipto(int kind) {
ParseException e = generateParseException(); // generate the exception object
System.out.println(e.toString()); // print the error message
Token t;
// consume tokens all the way up to a token of “kind” - use a do-while loop
// rather than a while because the current token is the one immediately before
// the erroneous token (in our case the token immediately before what should
// have been “if”/“while”.
do {
t = getNextToken();
}
while (t.kind != kind);
}
这就是浅层错误恢复。在 JavaCC 的未来版本中,我们将支持语法的模块化组合。发生这种情况时,可以将所有这些错误恢复例程放入一个单独的模块中,该模块可以导入到主语法模块中。我们打算在实现此功能时提供一个有用例程库(用于错误恢复和其他)。
深度错误恢复
使用与浅层恢复相同的示例:
void Stm() :
{}
{
IfStm()
|
WhileStm()
}
在这种情况下,我们希望以相同的方式恢复,但是我们希望即使在解析更深层次上存在错误时也能恢复。
例如,假设下一个记号是while- 因此做出了选择WhileStm。但是假设在解析过程中WhileStm遇到了一些错误——比如说我们while (foo { stm; }已经错过了右括号。浅恢复不适用于这种情况,我们需要深度恢复来实现这一点。为此,我们在 JavaCC 中提供了一个新的语法实体——try-catch-finally块。
首先,让我们重构上面的深度错误恢复示例,然后try-catch-finally更详细地解释该块:
void Stm() :
{}
{
try {
(
IfStm()
|
WhileStm()
)
}
catch (ParseException e) {
error_skipto(SEMICOLON);
}
}
而已。IfStm如果在or的解析过程中有任何未恢复的错误WhileStm,则 catch 块接管。您可以有任意数量的catch块和可选的finally块(就像 Java 错误一样)。进入catch块的是Java 代码,而不是 JavaCC 扩展。
例如,上面的例子可以改写为:
void Stm() :
{}
{
try {
(
IfStm()
|
WhileStm()
)
}
catch (ParseException e) {
System.out.println(e.toString());
Token t;
do {
t = getNextToken();
} while (t.kind != SEMICOLON);
}
}
catch最好避免在和块中放置过多的 Java 代码,finally因为它会使语法读者不知所措 - 最好定义您可以从catch块中获取的方法。
请注意,在示例的第二个版本中,我们基本上将代码从error_skipto. 但是我们省略了第一条语句——调用generateParseException(). 在这种情况下,该catch块已经为我们提供了异常。即使你确实调用了这个方法,你也会得到一个相同的对象。