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