C# 我的ANTLR解析器(而不是lexer)如何触发一个词法“;包括「;(不是AST拼接)?

C# 我的ANTLR解析器(而不是lexer)如何触发一个词法“;包括「;(不是AST拼接)?,c#,include,antlr,antlr3,C#,Include,Antlr,Antlr3,ANTLR网站描述了如何实施“包括”指令。第一种方法是识别lexer中的指令并以词汇形式包含文件(通过将CharStream推到堆栈上并用读取新文件的堆栈替换);第二种方法是识别解析器中的指令,启动子解析器来解析新文件,然后拼接子解析器生成的AST。这两个都不是我所需要的 在我正在分析的语言中,识别lexer中的指令是不切实际的,原因如下: 没有始终表示“这是一个include指令”的自包含字符模式。例如,包括“foo”是一个include指令,但在数组条-->include“foo”中或常量包

ANTLR网站描述了如何实施“包括”指令。第一种方法是识别lexer中的指令并以词汇形式包含文件(通过将CharStream推到堆栈上并用读取新文件的堆栈替换);第二种方法是识别解析器中的指令,启动子解析器来解析新文件,然后拼接子解析器生成的AST。这两个都不是我所需要的

在我正在分析的语言中,识别lexer中的指令是不切实际的,原因如下:

  • 没有始终表示“这是一个include指令”的自包含字符模式。例如,
    包括“foo”是一个include指令,但在
    数组条-->include“foo”中
    常量包括“foo”单词
    包含
    是一个标识符
  • 要包含的文件名可以作为字符串或常量标识符给出,并且可以使用任意复杂的表达式定义此类常量
  • 所以我想从解析器中触发包含。但要执行包含,我不能启动子解析器并将AST拼接在一起;我得把代币拼接起来。块在主文件中以
    {
    开头,在包含的文件中以
    }
    结尾是合法的。函数中包含的文件甚至可以关闭函数定义并启动新的函数定义

    看起来我需要类似于第一种方法的东西,但是在令牌流而不是CharStreams级别。这是一个可行的方法吗?我需要在堆栈上保持多少状态,如何使解析器切换回原始令牌流,而不是在到达EOF时终止?还是有更好的方法来处理这个问题

    ==========

    下面是该语言的一个示例,演示了在主文件中打开的块可以在包含的文件中关闭(反之亦然)。请注意,如果指令位于函数内部,但在函数外部是可选的,则需要在
    Include
    之前添加
    #

    main.inf:

    [ Main; print "This is Main!"; if (0) { #include "other.h"; print "This is OtherFunction!"; ]; [主要; 打印“这是主要的!”; 如果(0){ #包括“其他.h”; 打印“这是其他功能!”; ]; 其他.h:

    } ! end if ]; ! end Main [ OtherFunction; } ! 如果结束 ]; ! 端干管 [其他职能;
    每个
    Include
    语句都可以让解析器创建一个lexer的新实例,并在解析器当前所在的索引处插入lexer创建的这些新标记(请参阅解析器的
    @members
    块中的
    insertTokens(…)
    方法)

    下面是一个快速演示:

    Inform6.g 其他 主类 要运行演示,请在命令行上执行以下操作:

    java -cp antlr-3.3.jar org.antlr.Tool Inform6.g javac -cp antlr-3.3.jar *.java java -cp .:antlr-3.3.jar Main java-cp antlr-3.3.jar org.antlr.Tool Inform6.g javac-cp antlr-3.3.jar*.java java-cp.:antlr-3.3.jar Main 您将看到的输出对应于以下AST:


    这是专为受虐狂设计的语言吗?嘿。这是Inform,一种用于编写文本冒险游戏的语言。英语像Inform 7()是当您的设计不受传统上下文无关解析工具约束时,您可以做的优雅而令人敬畏的事情的一个例子。不幸的是,我正在解析Inform 6,这是当您的设计不受传统上下文无关解析工具约束时,您可以做的难以形容的可怕事情的一个例子。@BartKiers幸运的是,不是:filename只能是一个带引号的字符串或一个在前面用
    常量
    定义的标识符。给定给
    常量
    的定义必须是编译时常量,因此没有函数调用。该语言也没有文本运算符,因此没有连接,但它可以复制另一个常量:
    常量FOO“file.h”;Constant BAR FOO;Include BAR;
    但是
    Constant
    指令通常可以有任意复杂的表达式,因为它通常与数字一起使用:
    Constant FOO(BAR+5*BAZ);
    等,因此在lexer中处理
    Constant
    是不切实际的。
    Constant IMPORT "other.h";
    
    [ Main;
      print "This is Main!";
      if (0) {    
    
      Include IMPORT;
    
      print "This is OtherFunction!";
    ];
    
      } ! end if
    ];  ! end Main
    
    [ OtherFunction;
    
    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        // create lexer & parser
        Inform6Lexer lexer = new Inform6Lexer(new ANTLRFileStream("main.inf"));
        Inform6Parser parser = new Inform6Parser(new CommonTokenStream(lexer));
    
        // print the AST
        DOTTreeGenerator gen = new DOTTreeGenerator();
        StringTemplate st = gen.toDOT((CommonTree)parser.parse().getTree());
        System.out.println(st);
      }
    }
    
    java -cp antlr-3.3.jar org.antlr.Tool Inform6.g javac -cp antlr-3.3.jar *.java java -cp .:antlr-3.3.jar Main