【问题标题】:ANTLR4 Accepting additional tokens as valid?ANTLR4 接受额外的令牌是有效的吗?
【发布时间】:2014-04-13 20:40:42
【问题描述】:

我正在构建一种小型规则语言来测试并习惯 ANTLR。我正在使用 ANTLR V4,我的语法拆分如下:

Lexer.g4

lexer grammar Lexer;

/*------------------------------------------------------------------
 * LEXER RULES - GENERIC KEYWORDS
 *------------------------------------------------------------------*/
NOT
    : 'not'
    ;

NULL
    : 'null'
    ;

AND
    : 'and'
    | '&'
    ;

/*------------------------------------------------------------------
 * LEXER RULES - PATTERN MATCHING
 *------------------------------------------------------------------*/
DELIM
    : [\|\\/:,&@+><^]
    ;

WS 
    : [ \t\r\n]+ -> skip 
    ;

VALUE 
    : SQUOTE TEXT SQUOTE
    ;

fragment SQUOTE
    : '\'' 
    ;

fragment TEXT 
    : ( 'a'..'z' 
      | 'A'..'Z'
      | '0'..'9'
      | '-'
      )+ ;

Attribute.g4

grammar Attribute;

/*------------------------------------------------------------------
 * Semantic Predicate
 *
 * Attributes are capitalised words that may have spaces.  They're 
 * loaded from the database and and set in the glue code so that
 * they can be cross checked here.  If the grammar passed in sees
 * an attribute it will pass so long as the attribute is in the 
 * database, otherwise the grammar will fail to parse.
 *------------------------------------------------------------------*/  
attr
    : a=ATTR {attributes.contains($a.text)}?
    ;

ATTR
    : ([A-Z][a-zA-Z0-9/]+([ ][A-Z][a-zA-Z0-9/]+)?)
    ;

ReplaceInWith.g4

grammar ReplaceInWith;

/*------------------------------------------------------------------
 * REPLACE IN WITH PARSER RULES
 *------------------------------------------------------------------*/
replace_in_with
    : rep in with {row.put($in.value    , $in.value.replace($rep.value, $with.value));}
    | repAtt with {row.put($repAtt.value, $with.value);}
    ;

rep returns[String value]
    : REPLACE v=VALUE {$value = trimQuotes($v.text);}
    ;

repAtt returns[String value]
    : REPLACE a=attr  {$value = $a.text;}
    ;

in returns[String value]
    : IN a=attr {$value = $a.text;}
    ;

with returns[String value]
    : WITH v=VALUE {$value = trimQuotes($v.text);}
    ;

/*------------------------------------------------------------------
 * LEXER RULES - KEYWORDS
 *------------------------------------------------------------------*/
REPLACE
    : 'rep'
    | 'replace'
    ;

IN
    : 'in'
    ;

WITH
    : 'with'
    ;

Parser.g4

grammar Parser;

/*------------------------------------------------------------------
 * IMPORTED RULES
 *------------------------------------------------------------------*/
 import //Essential imports
    Attribute,
    GlueCode,
    Lexer,

    //Actual Rules
    ReplaceInWith,

/*------------------------------------------------------------------
 * PARSER RULES
 * MUST ADD EACH TOP LEVEL RULE HERE FOR IT TO BE CALLABLE
 *------------------------------------------------------------------*/
eval
    : replace_in_with
    ;

GlueCode.g4

Java to supply static calling functionality to the grammar and to set the attributes up from the database.

ParserErrorListener.java

public class ParserErrorListener extends ParserBaseListener 
{
    /**
     * After every rule check to see if an exception was thrown, if so exit with a runtime exception to indicate a 
     * parser problem.<p>
     */
    @Override 
    public void exitEveryRule(@NotNull ParserRuleContext ctx) 
    { 
        super.exitEveryRule(ctx);

        if (ctx.exception != null)
        {
            throw new ParserRuntimeException(String.format("Error evaluating expression(s) '%s'", ctx.exception));
        } //if
    } //exitEveryRule
} //class

当我向语法提供以下内容时,它会按预期通过:

"replace 'Acme' in Name with 'acme'",
"rep 'Acme' in Name with 'acme'",
"replace 'Acme' in Name with 'ACME'",
"rep 'Acme' in Name with 'ACME'",
"replace 'e' in Name with 'i'",
"rep 'e' in Name with 'i'",

"replace '-' in Number with ' '",
"rep '-' in Number with ' '",
"replace '555' in Number with '00555'",
"rep '555' in Number with '00555'"

其中 NAME 和 NUMBER 被设置为语义谓词的属性。

但是,当我传入以下语句时,语法仍然通过,但我不确定它为什么匹配:

"replace any 'Acme' in Name with 'acme'",
"replaceany 'Acme' in Name with 'acme'",

再次将 NAME 作为属性传递给语义谓词匹配,这部分语法在我的测试中有效。失败的部分是“任何”部分。语法匹配替换,然后获取它认为是“Acme”的下一个标记,忽略上面两个示例中的“任何”部分。我在这里期望的是语法失败,并且在退出规则的侦听器中添加了一个检查,该检查应该抛出一个运行时异常,该异常被 GlueCode 捕获以指示失败。

有什么想法可以让我的语法在发生这种情况时抛出错误?

【问题讨论】:

    标签: java parsing antlr4


    【解决方案1】:
    1. 首先,词法分析器规则在 ANTLR 中始终是全局的。您输入中的每个标记都将被分配一种,并且只有一种标记类型。如果您将词法分析器规则分成多个文件,那么确定令牌不明确的情况将成为维护的噩梦。一般规则是:

      避免import用于包含标记有fragment修饰符的规则的词法分析器语法。

    2. 无论attr 规则中的谓词是否成功,都会将ATTR 标记分配给与ATTR 匹配的输入。这将防止匹配 ATTR 规则的输入被视为另一种令牌类型。您应该将语义谓词从attr 规则移动到ATTR 规则,以防止词法分析器为不在预定义属性集中的输入创建ATTR 标记。

    3. 如果出现语法错误,不保证设置 ParserRuleContext.exception 字段。唯一确定没有出现语法错误的方法是解析后调用Parser.getNumberOfSyntaxErrors(),或者添加自己的ANTLRErrorListener

    4. 您的最后一个词法分析器规则应类似于以下内容。否则,不匹配词法分析器规则的输入序列将被静默删除。此规则将这些输入传递给解析器以进行处理/报告。

      ErrorChar : . ;
      
    5. 对于复杂的语法,避免使用组合语法。相反,创建lexer grammarparser grammar 语法,其中解析器语法使用tokenVocab 选项来导入标记。组合语法允许您通过在解析器规则中编写字符串文字来隐式声明词法分析器规则,这会降低大型语法的可维护性。

    6. ReplaceInWith.g4 包含许多带有嵌入式操作的规则。这些操作应移至解析完成后运行的单独侦听器,并且应删除这些规则中的 returns 子句。这提高了语法的可移植性和可重用性。在these commits 中可以看到如何执行此操作的示例,它是显示conversion of an application using ANTLR 3 to ANTLR 4 的更大拉取请求的一部分。

    【讨论】:

    • 感谢您提供如此好的答案。我已经按照您的建议添加了语义部分和 ErrorChar 部分。我正在分离词法分析器和语法规则 atm,然后我将按照您的建议将代码分离到侦听器中。
    猜你喜欢
    • 2015-09-18
    • 2020-06-10
    • 2022-08-17
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 2023-02-05
    • 2022-10-15
    • 2011-05-08
    相关资源
    最近更新 更多