上一篇文章写了Antlr如何从文本中读入数据,实际开发过程中,由于ANTLR生成了抽象语法树,我们只需要遍历树即可实现语法的解释,并加入自己的代码进行处理。实际在执行过程中,经常需要动态获取输入文本的对应行号。比如,我们经常会需要在执行过程中,告诉用户现在执行到第几行语句了;又比如,如果执行过程中发生了错误,我们需要告诉用户是在第几行出的问题。更高级一些的功能,例如编译器,也是需要及时提示用户在什么地方有变量定义错误、语法行文错误等等。
那么我们怎么来获取行号呢? Antlr在设计时其实早就为我们想好并实现了。
Token
在进行词法解析的时候,输入文本被识别为tokens,存储在内存中。antlr设计了一个虚基类Token,其定义在/runtime/src/Token.h中。定义如下:
class ANTLR4CPP_PUBLIC Token
一个token内含丰富信息,拥有文本、类型、行号、列位置、通道、索引等属性。没错,你需要的行号就在这里。使用Token类提供的getLine方法,即可获取到所需行号。
Token从哪来
在antlr自动生成的h和cpp文件中,如果你选择了-listener或-vistor选项,怎会生成监听器或访问器。这些接口都会用到一个叫做上下文的东西作为参数传递,从上下文中,可以获取到需要的token。比如我写了一个监听器类:
void SymbolsListener::enterProc_block(RllanParser::Proc_blockContext* ctx)
{
m_vecBlockName.push_back(m_nowName);
LOG(INFO) << "标识符入栈:" << m_nowName;
//获取hash输入字符
std::string str = "hash:" + m_nowName;
m_nowName = ctx->proc_defnation()->proc_name()->ID_NAME()->toString();
LOG(INFO) << "转换为标识符:" << m_nowName;
str += m_nowName;
HashValue hval = Utility::getInstance().DJBHash(str);
//制造scope对象,用来初始化函数类
Scope scp;
boost::shared_ptr<SymbolBase> ptr(new SymbolFunc(m_nowName, scp));
SymbolTable::getInstance().GetFunctionTable()[hval] = ptr;
//SymbolTable::getInstance().GetFunctionTable().insert(FuncPair<SymbolBase>(hval, ptr));
//token test
antlr4::Token* tk = ctx->getStart();
size_t line = tk->getLine();
return;
}
其中上下文对象输入类型为Proc_blockContext,他是ANTLR自动生成的类,与g4文件中的Proc_block方法对应。
这个类的继承树有一层是ParserRuleContext,包含两份方法。而这两个方法返回了我们需要的Token。
/**
* Get the initial token in this context.
* Note that the range from start to stop is inclusive, so for rules that do not consume anything
* (for example, zero length or error productions) this token may exceed stop.
*/
virtual Token *getStart();
/**
* Get the final token in this context.
* Note that the range from start to stop is inclusive, so for rules that do not consume anything
* (for example, zero length or error productions) this token may precede start.
*/
virtual Token *getStop();
所以在代码里可以调用ctx->getStart();获得Token对象,再调用getLine即可获取到token所在行号。
运行效果
拿一个输入文本做验证。程序需要解析识别函数initia。这个词在文件第81行,使用VS单步调试,函数名和行号都正确识别了。