为什么我的 ANTLR4 语法不能解析这段文字?

Why can my ANTLR4 grammar not parse this text?

我希望能够使用 ANTLR4 解析以下文本:

six-buffers() {
    evil-window-split();
    evil-window-vsplit();
    evil-window-vsplit();
    evil-window-down(1);
    evil-window-vsplit();
    evil-window-vsplit();
};
six-buffers();

首先我定义一个函数,然后调用它。

为此,我定义了以下 grammar

grammar Deplorable;

script: statement*;
statement: (methodCall | functionDeclaration) ';' (WHITESPACE|NEW_LINE);

// General stuff
deplorableString: '"' DEPLORABLE_STRING* '"';
deplorableInteger: DEPLORABLE_NUMBER;

// Method call definition
methodCall: methodName LPAREN (methodArgument COMMA?)* RPAREN;

methodName: DEPLORABLE_IDENTIFIER;
methodArgument: (deplorableString | deplorableInteger);

// Function Declaration
functionStatement: methodCall ';' (WHITESPACE|NEW_LINE);
functionDeclaration: methodName LPAREN RPAREN functionBody;
functionBody: CURLY_BRACE_LEFT functionStatement* CURLY_BRACE_RIGHT;

// Lexer stuff
LPAREN: '(';
RPAREN: ')';
DEPLORABLE_IDENTIFIER: (LOWERCASE_LATIN_LETTER | UPPERCASE_LATIN_LETTER | UNDERSCORE | DASH)+;
DEPLORABLE_STRING: (LOWERCASE_LATIN_LETTER | UPPERCASE_LATIN_LETTER | UNDERSCORE | WHITESPACE | EXCLAMATION_POINT)+;

CURLY_BRACE_LEFT: '{';
CURLY_BRACE_RIGHT: '}';

NEW_LINE: ('\r\n'|'\n'|'\r');

DEPLORABLE_NUMBER: DIGIT+;

fragment COMMA: ',';

fragment DASH: '-';
fragment LOWERCASE_LATIN_LETTER: 'a'..'z';
fragment UPPERCASE_LATIN_LETTER: 'A'..'Z';
fragment UNDERSCORE: '_';
fragment WHITESPACE: ' ';
fragment EXCLAMATION_POINT: '!';
fragment DIGIT: '0'..'9';

我使用 mvn clean antlr4:antlr4 install(禁用测试)编译此语法。 Here 是我的 pom.xml 文件。

但是,当我尝试在 test 中解析上述文本时,出现错误

line 1:13 no viable alternative at input 'six-buffers() '

我试图在函数声明前添加 void 以便解析器可以区分函数声明和函数调用,但这没有帮助。

我该如何解决这个错误,我。 e.确保解析器正确识别函数声明并且不会将其误认为是函数调用?

更新 1: 这个版本的语法(受 Mike Cargal 启发)目前似乎可以使用:

grammar Deplorable;

script: statement*;
statement: (methodCall | functionDeclaration) ';';

// General stuff

// Method call definition
methodCall: methodName LPAREN (methodArgument COMMA?)* RPAREN;

methodName: DEPLORABLE_IDENTIFIER;
methodArgument: (DEPLORABLE_STRING | DEPLORABLE_NUMBER);

// Function Declaration
functionStatement: methodCall ';';
functionDeclaration: methodName LPAREN RPAREN functionBody;
functionBody: CURLY_BRACE_LEFT functionStatement* CURLY_BRACE_RIGHT;

// Lexer stuff
LPAREN: '(';
RPAREN: ')';
DEPLORABLE_IDENTIFIER: (
        LOWERCASE_LATIN_LETTER
        | UPPERCASE_LATIN_LETTER
        | UNDERSCORE
        | DASH
    )+;
DEPLORABLE_STRING: '"' (
        LOWERCASE_LATIN_LETTER
        | UPPERCASE_LATIN_LETTER
        | UNDERSCORE
        | WHITESPACE
        | EXCLAMATION_POINT
    )+ '"';

CURLY_BRACE_LEFT: '{';
CURLY_BRACE_RIGHT: '}';

NEW_LINE: (
    '\r' '\n'?
    | '\n'
) -> skip;

DEPLORABLE_NUMBER: DIGIT+;

fragment COMMA: ',';

fragment DASH: '-';
fragment LOWERCASE_LATIN_LETTER: 'a'..'z';
fragment UPPERCASE_LATIN_LETTER: 'A'..'Z';
fragment UNDERSCORE: '_';
WHITESPACE: [ \t]+ -> skip;
fragment EXCLAMATION_POINT: '!';
fragment DIGIT: '0'..'9';

@sepp2k 为您指明了正确的方向。

您的 Lexer 规则(特别是 DEPLORABLE_STRING)正在引起您的痛苦。更具体地说,这看起来像是很多人在早期使用 ANTLR 时的误解,即解析器规则与标记化有任何关系。

在 ANTLR 管道中,您的输入首先使用词法分析器规则标记化为标记流。因此,转储您的令牌流通常非常有帮助。

在您的情况下,流如下所示:

[@0,0:10='six-buffers',<DEPLORABLE_IDENTIFIER>,1:0]
[@1,11:11='(',<'('>,1:11]
[@2,12:12=')',<')'>,1:12]
[@3,13:13=' ',<DEPLORABLE_STRING>,1:13]
[@4,14:14='{',<'{'>,1:14]
[@5,15:15='\n',<NEW_LINE>,1:15]
[@6,16:23='    evil',<DEPLORABLE_STRING>,2:0]
[@7,24:36='-window-split',<DEPLORABLE_IDENTIFIER>,2:8]
[@8,37:37='(',<'('>,2:21]
[@9,38:38=')',<')'>,2:22]
[@10,39:39=';',<';'>,2:23]
[@11,40:40='\n',<NEW_LINE>,2:24]
[@12,41:48='    evil',<DEPLORABLE_STRING>,3:0]
[@13,49:62='-window-vsplit',<DEPLORABLE_IDENTIFIER>,3:8]
[@14,63:63='(',<'('>,3:22]
[@15,64:64=')',<')'>,3:23]
[@16,65:65=';',<';'>,3:24]
[@17,66:66='\n',<NEW_LINE>,3:25]
[@18,67:74='    evil',<DEPLORABLE_STRING>,4:0]
[@19,75:88='-window-vsplit',<DEPLORABLE_IDENTIFIER>,4:8]
[@20,89:89='(',<'('>,4:22]
[@21,90:90=')',<')'>,4:23]
[@22,91:91=';',<';'>,4:24]
[@23,92:92='\n',<NEW_LINE>,4:25]
[@24,93:100='    evil',<DEPLORABLE_STRING>,5:0]
[@25,101:112='-window-down',<DEPLORABLE_IDENTIFIER>,5:8]
[@26,113:113='(',<'('>,5:20]
[@27,114:114='1',<DEPLORABLE_NUMBER>,5:21]
[@28,115:115=')',<')'>,5:22]
[@29,116:116=';',<';'>,5:23]
[@30,117:117='\n',<NEW_LINE>,5:24]
[@31,118:125='    evil',<DEPLORABLE_STRING>,6:0]
[@32,126:139='-window-vsplit',<DEPLORABLE_IDENTIFIER>,6:8]
[@33,140:140='(',<'('>,6:22]
[@34,141:141=')',<')'>,6:23]
[@35,142:142=';',<';'>,6:24]
[@36,143:143='\n',<NEW_LINE>,6:25]
[@37,144:151='    evil',<DEPLORABLE_STRING>,7:0]
[@38,152:165='-window-vsplit',<DEPLORABLE_IDENTIFIER>,7:8]
[@39,166:166='(',<'('>,7:22]
[@40,167:167=')',<')'>,7:23]
[@41,168:168=';',<';'>,7:24]
[@42,169:169='\n',<NEW_LINE>,7:25]
[@43,170:170='}',<'}'>,8:0]
[@44,171:171=';',<';'>,8:1]
[@45,172:172='\n',<NEW_LINE>,8:2]
[@46,173:183='six-buffers',<DEPLORABLE_IDENTIFIER>,9:0]
[@47,184:184='(',<'('>,9:11]
[@48,185:185=')',<')'>,9:12]
[@49,186:186=';',<';'>,9:13]
[@50,187:186='<EOF>',<EOF>,9:14]

您会注意到 @3,13 单个 ' ' 被标记为 DEPLORABLE_STRING。

您需要将引号合并到您的 DEPLORABLE_STRING 规则中。

(还建议您跳过 WHITESPACE(并且可能 NEW_LINE(大多数语法将 NEW_LINE 视为 WHITESPACE)

这样的事情应该会让你“解脱”

grammar Deplorable;

script: statement*;
statement: (methodCall | functionDeclaration) ';' (
        WHITESPACE
        | NEW_LINE
    );

// General stuff deplorableString: '"' DEPLORABLE_STRING* '"'; deplorableInteger: DEPLORABLE_NUMBER;

// Method call definition
methodCall: methodName LPAREN (methodArgument COMMA?)* RPAREN;

methodName: DEPLORABLE_IDENTIFIER;
methodArgument: (DEPLORABLE_STRING | DEPLORABLE_NUMBER);

// Function Declaration
functionStatement: methodCall ';' (WHITESPACE | NEW_LINE);
functionDeclaration: methodName LPAREN RPAREN functionBody;
functionBody:
    CURLY_BRACE_LEFT functionStatement* CURLY_BRACE_RIGHT;

// Lexer stuff
LPAREN: '(';
RPAREN: ')';
DEPLORABLE_IDENTIFIER: (
        LOWERCASE_LATIN_LETTER
        | UPPERCASE_LATIN_LETTER
        | UNDERSCORE
        | DASH
    )+;
DEPLORABLE_STRING:
    '"' (
        LOWERCASE_LATIN_LETTER
        | UPPERCASE_LATIN_LETTER
        | UNDERSCORE
        | WHITESPACE
        | EXCLAMATION_POINT
    )+ '"';

CURLY_BRACE_LEFT: '{';
CURLY_BRACE_RIGHT: '}';

NEW_LINE: ('\r\n' | '\n' | '\r');

DEPLORABLE_NUMBER: DIGIT+;

fragment COMMA: ',';

fragment DASH: '-';
fragment LOWERCASE_LATIN_LETTER: 'a' ..'z';
fragment UPPERCASE_LATIN_LETTER: 'A' ..'Z';
fragment UNDERSCORE: '_';
fragment WHITESPACE: ' ' -> skip;
fragment EXCLAMATION_POINT: '!';
fragment DIGIT: '0' ..'9';

这仍然是一个无关的 \n 绊倒(因此我的评论是:WS 和 NL 处理)。不确定你的意图,但看看其他语法如何处理它。 skip 它们通常比在解析器规则中可能出现它们的任何地方都更容易解释。

最重要的是...让您的思维模型正确理解 ANTLR 将字符流处理成标记流(使用 Lexer 规则)然后使用解析器规则处理标记流的过程。在你明白这一点之前,你会经历很多痛苦。