用于同时解析字符串和数字的语义谓词

Posted

技术标签:

【中文标题】用于同时解析字符串和数字的语义谓词【英文标题】: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。我仍然认为您尝试做的事情是可以在没有谓词的情况下实现的。 感谢您的链接!是的,这当然是可能的,但会更冗长。 另一种选择是在语法级别接受任何数据类型,然后执行验证通过(例如访问者)。毕竟,传递意外的参数类型是语义错误,而不是语法错误。这大大简化了语法,因为您不必跟踪其中的类型,只需接受语法上格式正确的所有内容,然后在代码中处理问题。 【参考方案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 ;

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

【讨论】:

谢谢迈克!是的,我会听从卢卡斯的建议。

以上是关于用于同时解析字符串和数字的语义谓词的主要内容,如果未能解决你的问题,请参考以下文章

核心数据谓词根据关系获取数据,错误为无法解析格式字符串

是否有用于将谓词定义为类似 SQL 的字符串的 Java 库?

NSPredicate 在包含数字和字母的字符串中包含搜索

解析PFQuery谓词指针对象

用于以任何出现顺序匹配具有多个单词的列中的字符串的 Coredata 谓词

NSPredicate 谓词