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



需要实现cos aka mumps
的语法突出显示对于形式的可能设计的语言

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
    ; 

我有一个问题,当ID类似于命令(new,set e.t.c。)

根据wikipedia页面,mumps没有保留的单词:

保留的单词:无。由于mumps通过上下文解释源代码,因此无需保留单词。您可以将语言命令的名称用作变量。

诸如Command_KILL之类的Lexer规则与保留的单词完全一样:它们旨在确保遇到输入"kill"时不会生成其他令牌。因此,即使是打算作为标识符,也将始终在"kill"上产生代币类型的Command_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         : [rn]+;
COMMA       : ',';

解析器规则expr知道ID令牌何时是基于整个行的布局的命令。

  • 如果输入令牌是ID ID,则输入为CallExpr:第一个ID是命令名称,第二个ID是常规标识符。
  • 如果输入令牌为ID ID Equal ID,则输入为SetExpr:第一个ID将是命令("set"或类似的东西),第二个ID是目标标识符,而第三个ID是源标识符。

这是一个Java测试申请,然后是与您的问题中提到的测试案例相似的测试案例。

import java.util.List;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
public class ExampleTest {
    public static void main(String[] args) {
        ANTLRInputStream input = new ANTLRInputStream(
                "new new, set, killnset kill = new");
        ExampleLexer lexer = new ExampleLexer(input);
        ExampleParser parser = new ExampleParser(new CommonTokenStream(lexer));
        parser.addParseListener(new ExampleBaseListener() {
            @Override
            public void exitCallExpr(ExampleParser.CallExprContext ctx) {
                System.out.println("Call:");
                System.out.printf("tcommand = %s%n", ctx.command.getText());
                List<ExampleParser.ValueContext> values = ctx.value();
                if (values != null) {
                    for (int i = 0, count = values.size(); i < count; ++i) {
                        ExampleParser.ValueContext value = values.get(i);
                        System.out.printf("targ[%d]  = %s%n", i,
                                value.getText());
                    }
                }
            }
            @Override
            public void exitSetExpr(ExampleParser.SetExprContext ctx) {
                System.out.println("Set:");
                System.out.printf("tcommand = %s%n", ctx.command.getText());
                System.out.printf("tname    = %s%n", ctx.name.getText());
                System.out.printf("tvalue   = %s%n", ctx.value().getText());
            }
        });
        parser.document();
    }
}

输入

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

由调用代码确定命令在给定上下文中是否有效。解析器无法合理地处理这一点,因为腮腺炎对命令和标识符的宽松方法。但这并不像听起来那样糟糕:您会知道哪些命令像呼叫一样函数,哪些命令像集合一样,因此您可以测试ANTLR产生的Listener的输入。例如,在上面的代码中,很容易测试"设置"是否是传递给exitSetExpr的命令。

某些腮腺炎语法可能比这更难处理,但是一般方法将是相同的:让lexer处理命令和标识符(如 ID s),并使用解析器规则来确定ID是指命令还是指基于整个行的上下文的标识符。

相关内容

  • 没有找到相关文章

最新更新