欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ANTLR3C--调用生成的语法文件(How to build Generated C Code 原文翻译)

程序员文章站 2022-04-13 14:10:14
...

为了运行 lexer/解析器/树解析器组合,您需要一个小函数(或 main)函数来控制事件序列,从读取输入文件或字符串,到调用树解析器和检索结果。有关更多详细信息,请参阅"使用 ANTLR3C C target",但如果您只想尽可能快地执行,请学习以下代码示例。

您可以采取所有措施,但是通常最好为您的项目创建一个include,其中将包含ANTLR3 C运行时头文件和生成的头文件(所有这些文件都可以多次包含)和您自己的项目相关头文件。在编译行上使用<>包含和-I(vs2005现在可以处理,而vs2003现在不可以)。
 
 #include <treeparser.h>
 
  此示例的主要入口点
 
 int ANTLR3_CDECL
 main(int argc,char * argv []{
      现在,我们声明所需的与ANTLR相关的局部变量。
      请注意,除非确信您将永远不需要项目的线程安全版本,否则应始终为每次调用创建诸如实例变量之类的东西。
      -------------------
 
      输入文件的名称。请注意,对于ASCII / 8位字符串,我们始终使用抽象类型pANTLR3_UINT8-运行时库保证在所有平台上都可以使用。这是一条通用规则-始终将ANTLR3提供的typedef用于指针/类型/等。
     
     pANTLR3_UINT8 fName;
 
      ANTLR3字符输入流,该输入流对输入源进行了抽象处理,从而很容易从不同的源(例如文件或存储字符串)中获取输入。
     
      对于8Bit / latin-1 / etc存储字符串,请使用:
          input = antlr3New8BitStringInPlaceStream (stringtouse, (ANTLR3_UINT32) length, NULL);
     
      对于UTF16内存字符串,请使用:
          input = antlr3NewUTF16StringInPlaceStream (stringtouse, (ANTLR3_UINT32) length, NULL);
     
      有关文件的输入,请参见下面的代码
     
      请注意,这实质上是指向包含函数指针的结构的指针。
      您可以创建自己的输入流类型(复制现有类型之一),并在创建标准版本后通过安装自己的指针来覆盖任何单个函数。
     
     pANTLR3_INPUT_STREAM input;
      词法分析器当然是由ANTLR生成的,因此词法分析器类型不是大写的。
      向词法分析器提供pANTLR3_INPUT_STREAM,从那里开始消耗其输入并生成令牌流作为输出。这是词法分析器的ctx(CTX宏)指针。
     
     pLangLexer lxr;
 
      令牌流由ANTLR3生成的词法分析器生成。同样,它是基于结构的API/对象,您可以根据需要自定义和覆盖方法。令牌流将提供给生成的解析器,您可以编写自己的令牌流并将其传递给您。
     
     pANTLR3_COMMON_TOKEN_STREAM tstream;
 
      Lang解析器也由ANTLR生成,并接受令牌流,如上所述。令牌流实际上可以是任何源,只要它实现ANTLR3_TOKEN_SOURCE接口即可。在这种情况下,解析器不返回任何内容,但是它当然可以从调用时调用的规则中指定任何类型的返回类型。这是解析器的ctx(CTX宏)指针。
     
     pLangParser psr;
 
      解析器生成AST,该AST作为起始规则(当然,任何规则都可以首先起始)的返回类型的成员返回。这是根据我们开始的规则生成的类型。
    
     LangParser_decl_return langAST;
 
      树节点由树适配器管理,后者根据请求分配节点。您可以创建自己的树类型和适配器,并覆盖内置版本。有关详细信息,请参见运行时源,最后请参见C target的Wiki条目。
     
     pANTLR3_COMMON_TREE_NODE_STREAM nodes;

      最终,当解析器运行时,它将生成可以由树解析器c.f. LangDumpDecl.g3t遍历的AST。这是树解析器的ctx(CTX宏)指针。
      在此示例中,根据命令行中提供给我们的参数创建输入流,如果没有显式参数,则输入将始终默认为./input。
      
     if (argc < 2 || argv[1] == NULL)
     {
        fName   =(pANTLR3_UINT8)"./input";  注意在VS2005调试中,必须配置工作目录
     }
     else
     {
        fName   = (pANTLR3_UINT8)argv[1];
     }
     
      使用提供的文件名创建输入流(对UTF16输入使用antlr38BitFileStreamNew)。
      
     input  = antlr38BitFileStreamNew(fName);
     
      只要有足够的内存并且文件存在,输入将成功创建
      
     if ( input == NULL )
     {
            ANTLR3_FPRINTF(stderr, "Unable to open file %s due to malloc() failure1\n", (char *)fName);
     }
      现在我们的输入流已打开,所有设置都可以使用,因此我们可以创建词法分析器的新实例,并将词法分析器输入设置为输入流:
      (file | memory | ?) --> inputstream -> lexer --> tokenstream --> parser ( --> treeparser )?

     lxr = LangLexerNew(input); CLexerNew由ANTLR生成
 
      需要检查错误
     
     if(lxr == NULL{
            ANTLR3_FPRINTF(stderr, "Unable to create the lexer due to malloc() failure1\n");
            exit(ANTLR3_ERR_NOMEM);
     }

      我们的词法分析器就位,因此我们可以从中创建令牌流
      注意:除了读取文件以外,什么都没发生。我们只是将所有这些东西连接在一起,当我们调用解析器规则时将调用它们。除非您有非常大的令牌流/输入,否则通常可以将ANTLR3_SIZE_HINT保留为默认值。每个生成的词法分析器都提供一个令牌源接口,这是令牌流创建者的第二个参数。
      请注意,即使您实现自己的令牌结构,该令牌结构中也始终会包含一个标准的通用令牌,这是您传递给其他所有对象的指针。一个通用令牌,作为其中的一个指针,应指向您自己的外部令牌结构。
     
     tstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT,lxr-> pLexer-> tokSource);
 
     if (tstream == NULL)
     {
        ANTLR3_FPRINTF(stderr, "Out of memory trying to allocate token stream\n");
        exit(ANTLR3_ERR_NOMEM);
     }
 
      最后,既然我们已经构造了词法分析器,就可以创建解析器了
     
     psr = LangParserNew(tstream); CParserNew由ANTLR3生成
 
     if (psr == NULL)
     {
        ANTLR3_FPRINTF(stderr, "Out of memory trying to allocate parser\n");
        exit(ANTLR3_ERR_NOMEM);
     }
 
      我们都准备好了。尽管乍一看看起来很复杂,但是我敢肯定,您会发现实际上上面的大多数代码都在处理错误,没有那么多事情要做(在C中不是总是这样吗?)。
     
      因此,我们现在调用解析器。 ANTLR3生成的C组件的所有元素以及ANTLR C运行时库本身都是伪对象。这意味着它们被表示为指向结构的指针,该结构包含所需的任何实例数据,以及一组指向其他接口或“方法”的指针。请注意,通常,我们在此处创建的这几个指针是您将显式使用free()的唯一方法,因为其他所有方法都是通过工厂创建的,它们可以高效分配内存,并在关闭解析器/词法分析器时自动使用free()所使用的所有方法/等等。
     
      请注意,这仅意味着方法总是通过对象指针来调用,而任何方法的第一个参数都是指向结构本身的指针。如果您使用的是VS2005之类的IDE,那么在键入->时,您将看到对象支持的所有方法的列表,这也具有副作用。
     
     langAST = psr-> decl(psr);
 
      如果解析器正确运行,我们将有一棵树要解析。通常,我建议保留自己的标志作为错误陷阱的一部分,但是如果您使用通用错误消息,则可以通过以下方法确定是否有错误
     
    if (psr->pParser->rec->errorCount > 0)
    {
        ANTLR3_FPRINTF(stderr, "The parser returned %d errors, tree walking aborted.\n", psr->pParser->rec->errorCount);
 
    }
    else
    {
        nodes   = antlr3CommonTreeNodeStreamNewTree(langAST.tree, ANTLR3_SIZE_HINT); // sIZE HINT WILL SOON BE DEPRECATED!!
 
        给树解析器一个公共的树节点流(或您的覆盖)
        treePsr = LangDumpDeclNew(nodes);
 
        treePsr->decl(treePsr);
        nodes   ->free  (nodes);        nodes   = NULL;
        treePsr ->free  (treePsr);      treePsr = NULL;
    }
 
     我们没有从此解析器规则返回任何内容,因此可以完成。剩下的只是关闭打开的对象,以相反的顺序创建它们
    
    psr     ->free  (psr);      psr     = NULL;
    tstream ->free  (tstream);  tstream = NULL;
    lxr     ->free  (lxr);      lxr     = NULL;
    input   ->close (input);    input   = NULL;
 
     返回0;
 } 
相关标签: antlr