当前位置: 首页 > 知识库问答 >
问题:

C#和ANTLR4:解析文件时处理“include”指令

许法
2023-03-14
    null

共有1个答案

壤驷高旻
2023-03-14

这个问题的一个解决方案是重写扫描程序的行为,特别是NextToken()方法。这是necassary,因为EOF令牌不能由ANTLR lexer语法处理(据我所知),并且任何附加到识别EOF的lexer规则的操作都被忽略(如下面的代码所示)。因此,有必要将此行为直接实现到扫描器方法中。

所以假设我们有一个解析器语法

parser grammar INCParserGrammar;

@parser::members {
        public static Stack<ICharStream> m_nestedfiles = new Stack<ICharStream>();
}

options { tokenVocab = INCLexerGrammar; }

/*
 * Parser Rules
 */

compileUnit
    :   (include_directives | ANY )+ ENDOFFILE
    ;

include_directives : INCLUDEPREFIX FILE DQUOTE
                     ;

应该在语法的成员中引入静态公共堆栈 (即MySpecialFileStack)。该堆栈将用于存储与参与解析的文件相关联的字符流。当使用include语句遇到新文件时,字符流被推送到这个堆栈

   lexer grammar INCLexerGrammar;

   @lexer::header {
    using System;
    using System.IO;
   }

   @lexer::members { 
    string file;
    ICharStream current;
    
   }


/*
 * Lexer Rules
 */
INCLUDEPREFIX : '#include'[ \t]+'"' {                                                 
                                      Mode(INCLexerGrammar.FILEMODE);
                                    };

// The following ruls has always less length matched string that the the rule above
ANY : ~[#]+ ;


ENDOFFILE : EOF { // Any actions in the this rule are ignored by the ANTLR lexer };


////////////////////////////////////////////////////////////////////////////////////////////////////////

mode FILEMODE;
FILE : [a-zA-Z][a-zA-Z0-9_]*'.'[a-zA-Z0-9_]+ {  file= Text;
                                                StreamReader s = new StreamReader(file);
                                                INCParserGrammar.m_nestedfiles.Push(_input);                                                
                                                current =new AntlrInputStream(s);           
                                            
                                             };
DQUOTE: '"'  {  
                this._input = current;
                Mode(INCLexerGrammar.DefaultMode);  };
if (this._input.La(1) == -1)
{
    if ( mySpecialFileStack.Count == 0 )
        this._hitEOF = true;
    else
        this._input = mySpecialFileStack.Pop();
}

NextToken()方法重写的全部内容是

public override IToken NextToken() {
            int marker = this._input != null ? this._input.Mark() : throw new InvalidOperationException("nextToken requires a non-null input stream.");
            label_3:
            try {
                while (!this._hitEOF) {
                    this._token = (IToken)null;
                    this._channel = 0;
                    this._tokenStartCharIndex = this._input.Index;
                    this._tokenStartCharPositionInLine = this.Interpreter.Column;
                    this._tokenStartLine = this.Interpreter.Line;
                    this._text = (string)null;
                    do {
                        this._type = 0;
                        int num;
                        try {
                            num = this.Interpreter.Match(this._input, this._mode);
                        } catch (LexerNoViableAltException ex) {
                            this.NotifyListeners(ex);
                            this.Recover(ex);
                            num = -3;
                        }

                        if (this._input.La(1) == -1) {
                            if (INCParserGrammar.m_nestedfiles.Count == 0 ) {
                                this._hitEOF = true;
                            }
                            else
                            {
                                this._input = INCParserGrammar.m_nestedfiles.Pop();
                            }
                        }

                        if (this._type == 0)
                            this._type = num;
                        if (this._type == -3)
                            goto label_3;
                    }
                    while (this._type == -2);
                    if (this._token == null)
                        this.Emit();
                    return this._token;
                }
                this.EmitEOF();
                return this._token;
            } finally {
                this._input.Release(marker);
            }
        }

现在,当您在代码中识别出应该解析的文件时,只需添加以下操作

FILE
    : [a-zA-Z][a-zA-Z0-9_]*'.'[a-zA-Z0-9_]+ {
        StreamReader s = new StreamReader(Text);
        mySpecialFileStack.Push(_input);                                                
        _input = new AntlrInputStream(s);                                               
    };
    
DQUOTE: '"'  {  this._input = current;
            Mode(INCLexerGrammar.DefaultMode);  };
//***Warning:***
// Be careful when your file inclusion is enclosed inside quotes or other symbols, or if  
// the filename-to-be-included is not the last token that defines an inclusion: `_input`  
// should only be switched AFTER the inclusion detection is completely found (i.e. after  
// the closing quote has been recognized).  

最后,下面给出了主程序,很明显根文件首先添加到ICharStream堆栈中

 static void Main(string[] args) {
            var a = new StreamReader("./root.txt");
            var antlrInput = new AntlrInputStream(a);
            INCParserGrammar.m_nestedfiles.Push(antlrInput);
            var lexer = new INCLexerGrammar(antlrInput);
            var tokens = new BufferedTokenStream(lexer);
            var parser = new INCParserGrammar(tokens);
            parser.compileUnit();
            
        }
 类似资料:
  • 本文向大家介绍php include类文件超时问题处理,包括了php include类文件超时问题处理的使用技巧和注意事项,需要的朋友参考一下 最近发现,php运行自动加载类函数时总是超时,跟踪php慢查询日志,发现程序卡在了include某个类文件时竟然超时。     初步定位是io响应超时,硬盘读取有问题。     通过几个命令来定位:     iostat -d -x -k 1 10 //

  • 本文向大家介绍c#预处理指令分析,包括了c#预处理指令分析的使用技巧和注意事项,需要的朋友参考一下 预处理指令 这些指令/命令不会转换为可执行代码,但会影响编译过程的各个方面;列如,可以让编译器不编译某一部分代码等。 C#中主要的预处理指令 #define和#undef #define指令定义: 它告诉编译器存在DEBUG这个符号;这个符号不是实际代码的一部分,而只是在编译器编译代码时候可能会根据

  • 本文向大家介绍C / C ++中的#include <文件名>和#include“文件名”之间的区别?,包括了C / C ++中的#include <文件名>和#include“文件名”之间的区别?的使用技巧和注意事项,需要的朋友参考一下 两种形式之间的区别在于预处理器搜索要包含的文件的位置。 #include <文件名> 预处理器以实现相关的方式进行搜索,它搜索由编译器预先指定的目录。此方法通常

  • 本文向大家介绍解析C++中的字符串处理函数和指针,包括了解析C++中的字符串处理函数和指针的使用技巧和注意事项,需要的朋友参考一下 C++字符串处理函数 字符串连接函数 strcat 其函数原型为 strcat是string catenate(字符串连接)的缩写。该函数有两个字符数组的参数,函数的作用是:将第二个字符数组中的字符串连接到前面字符数组的字符串的后面。第二个字符数组被指定为const,

  • 本文向大家介绍常用C/C++预处理指令详解,包括了常用C/C++预处理指令详解的使用技巧和注意事项,需要的朋友参考一下   预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。预处理命令以符号“#”开头。   常用的预处理指令包括: 宏定义:#define 文件包含:#include 条件编译:#if、#elif、#ifndef、#ifdef、#endif、#undef

  • 我是ANTLR4的新人。我正在使用Antlr4和Antlr4适配器来解析C文件并生成PSI树。 我知道C预处理器应该处理#include和#define部分,并将结果传递给C lexer和C parser。但是我需要为C.G4解析#include和#define,这样我的插件就可以在没有预处理器的情况下处理C文件。 我查看了这个链接并尝试了解决方案,但当它遇到预处理器语句以外的东西时,它就无法解决