试图处理引号的简单 ANTLR 语法打嗝

Simple ANTLR grammar hiccup trying to deal with quotes

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

应处理包含数字后跟 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 中尝试这个,它有一个很好的插件,您可以在其中试验语法并获得关于 lexing/parsing 是否成功的即时反馈(非常宝贵!)。

目前语法如下。

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

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'''

并且解析树指示失败,因为它试图同时获取两个单引号:

怎么了?

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

一个 Lexer 规则:

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

将处理嵌入引号的“'”(斜线引号)和“''”(引号)表示。快速替换令牌文本上的所有内容将处理 post- 将它们处理为单个嵌入的 ' 引号。

在您的评论中,您提到了未加引号的 ATOM。您可以像这样修改您的 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: