这个问题的一个解决方案是重写扫描程序的行为,特别是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文件。 我查看了这个链接并尝试了解决方案,但当它遇到预处理器语句以外的东西时,它就无法解决