首页 > 解决方案 > 用于简单命令处理器的 Lexer 和 Parser 规则

问题描述

我正在尝试为遗留语言构建一个简单的命令处理器。

我正在尝试使用具有 antlr4 版本“ANTLR”、“4.6.6”的 C#)

我无法在几种情况下取​​得进展。

以下示例显示命令 PKS 的各种示例调用。

PKS
PKS?
PKStext_that_is_a_filename

我无法解决的场景是 PKS 命令后跟文件名。

Command:
PKS
(block (line (expr (command PKS)) (eol \r\n)) <EOF>)
Command:
PKS?
(block (line (expr (command PKS) (query ?)) (eol \r\n)) <EOF>)
Command:
PKSFILENAME
line 1:0 mismatched input 'PKSFILENAME' expecting COMMAND
(block PKSFILENAME \r\n)
Command:

我认为是相关的语法片段:

 block      : line+ EOF;
 line       : (expr eol)+;

 expr       : command file
            | command listOfDouble
            | command query
            | command
            ;

command     : COMMAND
            ;

query       : QUERY;
file        : TEXT ;
eol         : EOL;
listOfDouble: DOUBLE (COMMA DOUBLE)* ;

从词法分析器:

COMMAND     : PKS;
PKS         :'PKS' ;

QUERY       : '?'
            ;

fragment LETTER : [A-Z];
fragment DIGIT  : [0-9];
fragment UNDER  : [_];

TEXT        : (LETTER) (LETTER|DIGIT|UNDER)* ;

标签: antlr4

解决方案


这里的主要问题是您的TEXT规则也匹配PKS应该匹配的内容。并且由于PKStext_that_is_a_filename可以完全与该TEXT规则匹配,因此它优于PKS规则,即使它首先出现在语法中(如果 2 个规则匹配相同的输入,则第一个获胜)。

为了解决该问题,您有 2 个选项:

  1. 在关键字 (PKS) 和表达式的其余部分之间需要空格。
  2. 更改TEXT规则以明确排除“PKS”作为有效输入。

选项 2 肯定是可能的,但是如果您有更多关键字(因为它们都必须被排除),它会变得非常混乱。在关键字和文本之间有一个空格,词法分析器会自动为您执行此操作。

让我给你一个提示来解决这类问题:总是检查词法分析器生成的标记列表,看看它是否生成了你期望的标记。我稍微修改了你的语法,添加了丢失的标记并通过我的 ANTLR4 调试器运行它,它给了我:

Parser error (5, 1): extraneous input 'PKStext_that_is_a_filename' expecting {<EOF>, COMMAND, EOL}

Tokens:
[@0,0:2='PKS',<1>,1:0]
[@1,3:3='\n',<8>,1:3]
[@2,4:4='\n',<8>,2:0]
[@3,5:7='PKS',<1>,3:0]
[@4,8:8='?',<3>,3:3]
[@5,9:9='\n',<8>,3:4]
[@6,10:10='\n',<8>,4:0]
[@7,11:36='PKStext_that_is_a_filename',<7>,5:0]
[@8,37:37='\n',<8>,5:26]
[@9,38:37='<EOF>',<-1>,6:0]

对于此输入:

PKS

PKS?

PKStext_that_is_a_filename

这是我使用的语法:

grammar Example;

start: block;

block: line+ EOF;
line:  expr? eol;

expr: command (file | listOfDouble | query)?;

command: COMMAND;

query:        QUERY;
file:         TEXT;
eol:          EOL;
listOfDouble: DOUBLE (COMMA DOUBLE)*;

COMMAND: PKS;
PKS:     'PKS';

QUERY: '?';

fragment LETTER: [a-zA-Z];
fragment DIGIT:  [0-9];
fragment UNDER:  [_];

COMMA:  ',';
DOUBLE: DIGIT+ (DOT DIGIT*)?;
DOT:    '.';
TEXT:   LETTER (LETTER | DIGIT | UNDER)*;
EOL:    [\n\r];

和生成的视觉解析树:

在此处输入图像描述


推荐阅读