java - ANTLR4 接受附加 token 有效吗?

标签 java parsing antlr4

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

词法分析器.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'
      | '-'
      )+ ;

属性.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'
    ;

解析器.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”的下一个标记,忽略上面两个示例中的“any”部分。我在这里期望的是语法失败,并且在退出规则的监听器中我添加了一个检查,该检查应该抛出运行时异常,该异常由 GlueCode 捕获以指示失败。

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

最佳答案

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

    Avoid using import for lexer grammars which contain rules that are not marked with the fragment modifier.

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

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

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

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

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

关于java - ANTLR4 接受附加 token 有效吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22305340/

相关文章:

java - 我的 Mac 没有采用我的堆大小配置

Java:子数组和二进制搜索算法

php - PHP解析/语法错误;以及如何解决它们

java - JSONException:没有值(value)(解析 JSON 图像)

java - 如何创建根据类的某些属性排序的优先级队列?

java - SimpleDateFormat 解析错误的时间

c# - 语法识别 unlimited '{' expr '}' next to each-other

c# - '´' 完全出乎我的意料

javascript - 如何检测源/调试 Lexer 的空值

java - 在 Java 中设置 url header 的最佳方法是什么