解析键/值子参数

Parsing key / value subparameters

我对如何解析(或多或少)“自由形式”参数列表有点无能为力,假设语法允许

PARM=(VAL1, 'VAL2', VAL3, KEY4=VAL4, KEY5=VAL5(XYZ), PARM=ABC, SOMETHING=ELSE)

我已经设法基本上解析了位置参数和 key/value 参数的组合,但是一旦我遇到像 PARM= 这样的词法分析器标记,解析器就会用“不匹配的输入”退出,我不能特别允许或期望任何东西,因为传递给函数的这些参数是完全任意的。

所以我想我需要切换到特定的词法分析器模式,但现在我看不到如何正确切换回“正常”模式,分隔符是 PARM=( 在左边和右侧的结束 ),但由于“数据”本身可以包含(成对的)括号,我如何识别正确的结束括号,这样我就不会过早地结束词法分析器模式?

TIA - 亚历克斯

编辑 1:

显示关键字在不应该使用的地方出现问题的最小语法,因为这是复杂语法的一部分,我无法更改标记的顺序以将 ID 放在其他所有内容的前面,例如,作为它会抓住太多。所以我看不出如果不进入不同的词法分析器模式,它是如何工作的。

lexer grammar ParmLexer;

  SPACE     :   [ \t\r\n]+    -> channel(HIDDEN) ;
  COMMA     :   ',' ;
  EQUALS    :   '=' ;
  LPAREN    :   '(' ;
  RPAREN    :   ')' ;

  PARM      :   'PARM=' ;

  ID        :   ID_LITERAL ;
  fragment ID_LITERAL   :   [A-Za-z]+ ;

.

parser grammar ParmParser;

options { tokenVocab=ParmLexer; }

parms :     PARM LPAREN parm+ RPAREN    ;
parm :      (pkey=ID EQUALS)? pval=ID COMMA?    ;

输入:

PARM=( TEST, KEY=VAL, PARM=X)

结果

line 1:22 extraneous input 'PARM=' expecting {')', ID}

您的 parm 规则中没有可识别 PARM 标记的规则(替代)。

Bart 使用 Lexer 模式提供了答案(并假设 LPARENRPAREN 始终控制这些模式),但您也可以只设置一个匹配所有关键字的解析器规则:

lexer grammar ParmLexer
    ;

SPACE:  [ \t\r\n]+ -> channel(HIDDEN);
COMMA:  ',';
EQUALS: '=';
LPAREN: '(';
RPAREN: ')';

PARM: 'PARM';
KW1:  'KW1';
KW2:  'KW2';

ID:                  ID_LITERAL;
fragment ID_LITERAL: [A-Za-z]+;
parser grammar ParmParser
    ;

options {
    tokenVocab = ParmLexer;
}

parms: PARM EQUALS LPAREN parm (COMMA parm)* RPAREN;
parm:  ((pkey = ID | kwid = kw) EQUALS)? pval = ID;
kw:    PARM | KW1 | KW2;

输入

"PARM=( TEST, KEY=VAL, KW2=v2, PARM=X)"

产量:

(parms PARM = ( (parm TEST) , (parm KEY = VAL) , (parm (kw KW2) = v) , (parm (kw PARM) = X) ))

So I'd think I'll need to switch to a specific lexer mode but right now I can't see how I would properly switch back to "normal" mode

而不是切换到模式(使用 -> mode(...)),您可以将“特殊”模式压入堆栈(使用 -> pushMode(...)),然后在遇到 ) 时弹出来自堆栈的模式。这样,您可以有多个嵌套列表 (..(..(..).)..)。快速演示:

lexer grammar ParmLexer;

SPACE  : [ \t\r\n]+ -> channel(HIDDEN);
EQUALS : '=' ;
LPAREN : '(' -> pushMode(InList);
PARM   : 'PARM';
ID     : [A-Za-z] [A-Za-z0-9]*;

mode InList;

 LST_LPAREN : '(' -> type(LPAREN), pushMode(InList);
 RPAREN     : ')' -> popMode;
 COMMA      : ',';
 LST_EQUALS : '=' -> type(EQUALS);
 STRING     : '\'' ~['\r\n]* '\'';
 LST_ID     : [A-Za-z] [A-Za-z0-9]* -> type(ID);
 LST_SPACE  : [ \t\r\n]+ -> channel(HIDDEN);

和:

parser grammar ParmParser;

options { tokenVocab=ParmLexer; }

parse
 : PARM EQUALS list EOF
 ;

list
 : LPAREN ( value ( COMMA value )* )? RPAREN
 ;

value
 : ID
 | STRING
 | key_value
 | ID list
 ;

key_value
 : ID EQUALS value
 ;

它将像这样解析您的示例输入 PARM=(VAL1, 'VAL2', VAL3, KEY4=VAL4, KEY5=VAL5(XYZ), PARM=ABC, SOMETHING=ELSE)