Antlr4 在解析器中重新定义令牌类型

Antlr4 在解析器中重新定义令牌类型,antlr4,Antlr4,需要为COS aka实现语法突出显示 对于语言的一种可能的形式设计 new (new,set,kill) set kill=new 其中:“new”和“set”是命令,也是变量 grammar cos; Command_KILL :( ('k'|'K') | ( ('k'|'K')('i'|'I')('l'|'L')('l'|'L') ) ); Command_NEW :( ('n'|'N') | ( ('n'|'N')('e'|'E')('w

需要为COS aka实现语法突出显示
对于语言的一种可能的形式设计

new (new,set,kill)
set kill=new
其中:“new”和“set”是命令,也是变量

grammar cos;

Command_KILL            :( ('k'|'K') | ( ('k'|'K')('i'|'I')('l'|'L')('l'|'L') ) ); 
Command_NEW             :( ('n'|'N') | ( ('n'|'N')('e'|'E')('w'|'W') ) ); 
Command_SET             :( ('s'|'S') | ( ('s'|'S')('e'|'E')('t'|'T') ) );


INT : [0-9]+;
ID : [a-zA-Z][a-zA-Z0-9]*;
Space: ' ';
Equal: '=';

newCommand
    :   Command_NEW Space ID
    ;
setCommand
    :   Command_SET Space ID Space*  Equal Space* INT
    ; 

我有一个问题,当我想把名字作为命令时(新建,设置e.t.c.)

根据维基百科页面,腮腺炎没有保留词:

保留字:无。因为腮腺炎是根据上下文解释源代码的,所以不需要保留字。您可以使用语言命令的名称作为变量

类似于
Command\u KILL
的Lexer规则的功能与保留字完全相同:它们被设计为确保在遇到输入
“KILL”
时不会生成其他令牌。因此,令牌类型
命令\u KILL
将始终在
的“KILL”
上生成,即使它是一个标识符。如果需要,您可以保留命令lexer规则,但是您也必须将它们视为id,因为您不知道仅基于令牌的
“kill”
指的是什么

在ANTLR中实现腮腺炎意味着关注令牌使用和上下文,而不是令牌类型。考虑这一语法:

grammar Example;


document    : (expr (EOL|EOF))+;
expr        : command=ID Space+ value (Space* COMMA Space* value)*  #CallExpr
            | command=ID Space+ name=ID Space* Equal Space* value   #SetExpr
            ;     

value       : ID | INT;

INT         : [0-9]+;
ID          : [a-zA-Z][a-zA-Z0-9]*;
Space       : ' ';
Equal       : '=';
EOL         : [\r\n]+;
COMMA       : ',';
解析器规则
expr
知道
ID
标记何时是基于整行布局的命令

  • 如果输入标记是
    ID ID
    ,则输入是
    CallExpr
    :第一个
    ID
    是命令名,第二个
    ID
    是常规标识符
  • 如果输入标记是
    ID-ID-Equal-ID
    ,则输入是
    SetExpr
    :第一个
    ID
    将是一个命令(或者
    “set”
    或者类似的东西),第二个
    ID
    是目标标识符,第三个
    ID
    是源标识符
下面是一个Java测试应用程序,后面是一个类似于您问题中提到的测试用例

import java.util.List;
导入org.antlr.v4.runtime.antlInputStream;
导入org.antlr.v4.runtime.CommonTokenStream;
公共类示例测试{
公共静态void main(字符串[]args){
AntlInputStream输入=新的AntlInputStream(
“新建、设置、终止\n设置终止=新建”);
ExampleLexer lexer=新的ExampleLexer(输入);
ExampleParser=newexampleparser(newcommontokenstream(lexer));
addParseListener(新示例BaseListener(){
@凌驾
public void exitCallExpr(ExampleParser.CallExprContext ctx){
System.out.println(“调用:”);
System.out.printf(“\t命令=%s%n”,ctx.command.getText());
列表值=ctx.value();
如果(值!=null){
对于(int i=0,count=values.size();i
输入 输出 由调用代码确定命令在给定上下文中是否有效。解析器无法合理地处理这一问题,因为腮腺炎对命令和标识符的处理过于松散。但这并不像听起来那么糟糕:您将知道哪些命令的功能类似于调用,哪些命令的功能类似于集合,因此您将能够测试ANTLR生成的
侦听器的输入。例如,在上面的代码中,很容易测试“set”是否是传递给
exitsetxpr
的命令


一些腮腺炎语法可能比这更难处理,但一般的方法是一样的:让lexer处理命令和标识符,如
ID
s,并使用解析器规则根据整行的上下文确定
ID
是指命令还是指标识符。

稍微偏离主题,由于ANTLR 4支持字符类表示法,
[…]
,因此不敏感地匹配
k
大小写将更容易,就像
[kK]
而不是
('k'|'k')
new new, set, kill
set kill = new
Call:
    command = new
    arg[0]  = new
    arg[1]  = set
    arg[2]  = kill
Set:
    command = set
    name    = kill
    value   = new