首页 > 解决方案 > 尝试处理引号的简单 ANTLR 语法打嗝

问题描述

我想解析“ Prolog atom”列表,即在某些情况下必须将其放入单引号中的简单字符串,以免它们与变量名或整数混淆,以允许包含空格以及允许包含单引号本身.

应处理包含数字后跟 olon 后跟 Prolog 原子列表的输入文件。

例如,输入文件可能是:

1: [ foo, bar ]
2: [ alpha, 'THIS IS VARIABLE NAMES WITH BLANKS THUS QUOTES' , '_also_a_variable_name' ]
3: [ empty_atom_follows, '' ]
4: []
5: [ 'the above is an empty list' ]
6: [ 'single quotes \' are possible' ]

我正在 IntellijIDEA 中尝试这个,它有一个很好的插件,您可以在其中试验语法并获得关于词法分析/解析是否成功的即时反馈(非常宝贵!)。

到目前为止的语法如下。

请注意,我不能丢弃空白,因为它们可能出现在引用的原子内,它们很重要。

grammar simple;

file : line ( EOL line )* EOL? ;

line : BLANK* uinteger BLANK* COLON BLANK* list_of_atom BLANK* ;

empty_list   : '[' BLANK* ']' ;

list_of_atom : empty_list
             | '[' BLANK* atom (BLANK* ',' BLANK* atom)* BLANK* ']'
             ;

uinteger : DIGIT+ ;

atom : fused_atoms
     | quoted_atom
     | unquoted_atom
     | empty_atom
     ;

empty_atom    : '\'\'';  // literally: '', no blanks
unquoted_atom : LOWER atom_char_not_needing_quotation* ;
quoted_atom   : '\'' atom_char_any+ '\'' ;
fused_atoms   : quoted_atom ( quoted_atom )+ ;

atom_char_not_needing_quotation : LOWER | UPPER | DIGIT | USCORE ;
atom_char_any                   : LOWER | UPPER | DIGIT | USCORE | DASH | BLANK | quote_escape ;
quote_escape                    : ('\\\'') ;

LOWER  : [a-z] ;
UPPER  : [A-Z] ;
DIGIT  : [0-9] ;
USCORE : '_' ;
DASH   : '-' ;
DOT   : '.' ;
SIGN  : '+' | '-' ;
COLON : ':' ;
EOL    : [\r\n];
BLANK  : [\t ];

我正在为fused_atoms.

在 Prolog 中,(至少)有两种方法可以将单引号插入到原子中:

用退格键将其转义:

?- X = 'a\'b'.
X = 'a\'b'.

用单引号转义它:

?- X = 'a''b'.
X = 'a\'b'.

为了获得相同的效果,我想保留将原子视为“几个融合原子”(实际上是连接引用的原子)的可能性,即

'a''b'

应被解析为两个并排引用的原子,组合成一个“融合原子”。我的想法是,通过后处理,我将在两个字符串之间加上一个单引号。

显然 ANTLR 不喜欢我的规则。

如果我给它

1 : [ 'a''b' ,  'a\'\'\'b'   , '\' ', ' ']

它变得困惑:

line 1:8 no viable alternative at input ''a'''

解析树表示失败,因为它试图一次抓住两个单引号:

解析树详细信息

怎么了?

标签: antlr4

解决方案


通常,您希望将字符串 (ATOM) 解析为单个标记,而不是在解析器规则中汇集的一堆单独标记:

词法分析器规则:

ATOM: '\'' ('\\\'' | '\'\'' | ~'\'')* '\'';

将处理嵌入引号的“'”(斜线引号)和“''”(引号)表示。快速替换所有标记文本将负责将这些后处理为单个嵌入的 ' 引用。

在您的评论中,您提到了未引用ATOM的 s。您可以像这样修改您的 ATOM Lexer 规则:(基于您问题中的规则)

ATOM:
    '\'' ('\\\'' | '\'\'' | ~'\'')* '\''
    | LOWER (LOWER | UPPER | DIGIT | USCORE)*;

现在你有一个ATOM词法分析器规则,它会给你一个ATOM在你的规则中使用的标记。

这将导致更简单的解析器规则,但也许更重要的是,当您开始处理解析树时,会产生更简单的 ParseTree 和 *Context 类。

修改后的语法将是:

grammar Simple;

file: line ( EOL line)* EOL?;

line: U_INTEGER COLON list_of_atom;

empty_list: '[' ']';

list_of_atom: empty_list | '[' ATOM (',' ATOM)* ']';

U_INTEGER: DIGIT+;

DASH: '-';
DOT: '.';
SIGN: '+' | '-';
COLON: ':';
EOL: [\r\n];
BLANK: [\t ] -> skip;
ATOM:
    '\'' ('\\\'' | '\'\'' | ~'\'')* '\''
    | LOWER (LOWER | UPPER | DIGIT | USCORE)*;

fragment LOWER: [a-z];
fragment UPPER: [A-Z];
fragment DIGIT: [0-9];
fragment USCORE: '_';

传递这个输入:

1 : [ 'a''b' ,  'a\'\'\'b'   , '\' ', ' ', 'atom with embedded spaces', ab_c]

给出以下 TokenStream:

[@0,0:0='1',<U_INTEGER>,1:0]
[@1,2:2=':',<':'>,1:2]
[@2,4:4='[',<'['>,1:4]
[@3,6:11=''a''b'',<ATOM>,1:6]
[@4,13:13=',',<','>,1:13]
[@5,16:25=''a\'\'\'b'',<ATOM>,1:16]
[@6,29:29=',',<','>,1:29]
[@7,31:35=''\' '',<ATOM>,1:31]
[@8,36:36=',',<','>,1:36]
[@9,38:40='' '',<ATOM>,1:38]
[@10,41:41=',',<','>,1:41]
[@11,43:69=''atom with embedded spaces'',<ATOM>,1:43]
[@12,70:70=',',<','>,1:70]
[@13,72:75='ab_c',<ATOM>,1:72]
[@14,76:76=']',<']'>,1:76]
[@15,77:76='<EOF>',<EOF>,1:77]

还有这个 ParseTree: 在此处输入图像描述


推荐阅读