【问题标题】:Semantic predicates for simultaneous string and number parsing用于同时解析字符串和数字的语义谓词
【发布时间】:2017-01-23 16:46:50
【问题描述】:

有了后面的简单语法,我想同时解析字符串和数字:

grammar Simple;

aRule : 'fs' '(' value["textual"] ')' ;
bRule : 'fi' '(' value["numeral"] ')' ;
cRule : 'f' '(' (value["textual"] | value["numeral"]) ')' ;    

value[String k]
  : {$k.equals("any") || $k.equals("textual")}? string 
  | {$k.equals("any") || $k.equals("numeral")}? numeric 
  ;

string
  : STRING_LITERAL
  ;

numeric
  : ('+' | '-')? INTEGER_LITERAL
  ;

STRING_LITERAL
  : '\'' (~('\'' | '\r' | '\n') | '\'' '\'' | NEWLINE)* '\''
  ;

INTEGER_LITERAL
  : '0' | [1-9] [0-9]* 
  ;

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

fragment NEWLINE                   : '\r'? '\n';

现在,我想解析以下表达式:

fs('asdf') // works
fi(512)    // works
f('asdf')  // works
f(512)     // fails

如果我在cRule 中切换textualnumeral,则f('asdf') 失败而f(512) 有效。

有什么想法吗?

更新1

grammar Simple;

rules : aRule | bRule | cRule ;
aRule : 'fs' '(' value["textual"] ')' ;
bRule : 'fi' '(' value["numeral"] ')' ;
cRule : 'f' '(' (tRule | nRule) ')' ;
tRule : value["textual"] ;
nRule : value["numeral"] ;
value[String k]
  : {$k.equals("any") || $k.equals("textual")}? string 
  | {$k.equals("any") || $k.equals("numeral")}? numeric 
  ;
string  : STRING_LITERAL ;
numeric : ('+' | '-')? INTEGER_LITERAL ;

STRING_LITERAL  : '\'' (~('\'' | '\r' | '\n') | '\'' '\'' | NEWLINE)* '\'' ;
INTEGER_LITERAL : '0' | [1-9] [0-9]* ;
SPACES          : [ \t\r\n]+ -> skip ;

fragment NEWLINE : '\r'? '\n';

即使有了这个更新的语法——正如@GRosenberg 所建议的——对于f(512),我仍然得到no viable alternative at input '512'。再次 fs('asdf')fi(512)f('asdf') 工作。

【问题讨论】:

  • "that doesn't work" => 这是没有意义的,告诉我们what如何它不起作用,请看minimal reproducible example。另外,为什么你要这样费解你的语法?为什么不简单地做:either: string | numeric;aRule: A_STR_FUNC '(' string ')'; 等等?
  • 这只是一个更复杂的语法的简单摘录。在完整的语法中,我必须将某些地方的表达式限制为“类型”字符串、整数或日期时间等。因此,为了确保在这些地方不允许 /any/ 表达式,我想遵循这种方法。
  • 看起来像context-dependent predicates don't play too well with prediction。我仍然认为您尝试做的事情是可以在没有谓词的情况下实现的。
  • 感谢您的链接!是的,这当然是可能的,但会更冗长。
  • 另一种选择是在语法级别接受任何数据类型,然后执行验证通过(例如访问者)。毕竟,传递意外的参数类型是语义错误,而不是语法错误。这大大简化了语法,因为您不必跟踪其中的类型,只需接受语法上格式正确的所有内容,然后在代码中处理问题。

标签: java parsing antlr4 semantics predicate


【解决方案1】:

下面列出了相关的生成代码。在其中,请注意谓词约束失败导致异常。这解释了为什么总是跳过第二个 alt —— 第一个 alt 是正确的,或者整个规则都失败了。

生成的代码是否以某种方式“不正确”是一个问题,最好直接转到 ANTLR4 Google 小组或 Github 问题页面。

无论如何,解决方案是使用子规则进行分隔:

cRule   : F LPAREN ( dRule | eRule ) RPAREN EOF ;
dRule   : value["textual"] ;
eRule   : value["numeral"] ;

更新

值规则是需要拆分成子规则的:

cRule   : F LPAREN ( valuet["textual"] | valuen["numeral"] ) RPAREN EOF ;

valuet[String k]
    : {$k.equals("any") || $k.equals("textual")}? string
    ;

valuen[String k]
    : {$k.equals("any") || $k.equals("numeral")}? numeric
    ;

测试/工作。


public final ValueContext value(String k) throws RecognitionException {
    ValueContext _localctx = new ValueContext(_ctx, getState(), k);
    enterRule(_localctx, 2, RULE_value);
    try {
        setState(21);
        _errHandler.sync(this);
        switch (getInterpreter().adaptivePredict(_input, 1, _ctx)) {
            case 1:
                enterOuterAlt(_localctx, 1); {
                setState(17);
                if (!(_localctx.k.equals("any") || _localctx.k.equals("textual")))
                    throw new FailedPredicateException(this, "$k.equals(\"any\") || $k.equals(\"textual\")");
                setState(18);
                string();
            }
                break;
            case 2:
                enterOuterAlt(_localctx, 2); {
                setState(19);
                if (!(_localctx.k.equals("any") || _localctx.k.equals("numeral")))
                    throw new FailedPredicateException(this, "$k.equals(\"any\") || $k.equals(\"numeral\")");
                setState(20);
                numeric();
            }
                break;
        }
    } catch (RecognitionException re) {
        _localctx.exception = re;
        _errHandler.reportError(this, re);
        _errHandler.recover(this, re);
    } finally {
        exitRule();
    }
    return _localctx;
}

【讨论】:

  • 当我试图重现这个(在 C# 中)时,我得到的错误不是来自那里,而是来自adaptivePredict,它是一个NoViableAltException,所以它是 不是谓词失败。
  • 我尝试了您的建议,但不幸的是它不起作用。查看更新的语法。
  • 有趣。 OP 没有指定,所以很可能使用默认的 Java 生成器。它在 alt 上生成谓词 约束,如图所示,它们与实际谓词评估是分开的,也是除实际谓词评估之外的。可能是 C# 实现将其构建到其 adaptivePredict 中。
  • 奇怪的是,cRule : 'f' '(' (tRule) ')' | 'f' '(' (nRule) ')' ; 也不接受 f('asdf')f(512)
  • 更新和测试。
【解决方案2】:

我同意 Lucas 的观点,这是一个完全过于复杂的语法。如果您只想接受一个字符串,那么一定要在语法中只指定字符串。为什么要使用具有所有不同选项的值规则并将其限制为单个选项?这是一个典型的脚部射击。而是这样做:

rules : aRule | bRule | cRule ;
aRule : 'fs' '(' string ')' ;
bRule : 'fi' '(' numberic ')' ;
cRule : 'f' '(' (tRule | nRule) ')' ;
tRule : string ;
nRule : numeric ;

如果你拼出你想要你的语言看出来的东西,而不是试图参数化一些通用规则,它也会更容易阅读。

【讨论】:

  • 谢谢迈克!是的,我会听从卢卡斯的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 2019-02-17
  • 1970-01-01
  • 1970-01-01
  • 2012-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多