用于同时解析字符串和数字的语义谓词
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
中切换textual
和numeral
,则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 库?