如何将C实现的词法分析器Token输入ANTLR4解析器语法测试?
问题描述
我已使用ANTLR4编写了如下IFJ23解析器语法:
parser grammar IFJ23; tokens { Identifier, Type, LeftBracket, RightBracket, LeftCurlyBracket, RightCurlyBracket, Semicolon, Colon, Arrow, Comma, /** Keywords */ FunctionKeyword } swiftFile : functionDeclaration swiftFile ; /** Statements */ statement : declaration Semicolon? ; statements : statement statements? ; codeBlock : LeftCurlyBracket statements? RightCurlyBracket ; /** Declarations */ declaration : functionDeclaration // | variableDeclaration ; /** Function declarations */ functionDeclaration : FunctionKeyword Identifier functionSignature functionBody ; functionSignature : parameterClause functionResult? ; functionBody : codeBlock ; parameterClause : LeftBracket RightBracket | LeftBracket parameterList RightBracket ; functionResult : Arrow Type ; parameterList : parameter | parameter Comma parameterList ; parameter : externalParameterName? localParameterName typeAnnotation ; externalParameterName : Identifier ; localParameterName : Identifier ; typeAnnotation : Colon Type ;
目前我有一个用C语言实现的可用词法分析器,能为文件生成Token列表。想知道有没有办法让ANTLR4接收这个Token列表并生成语法树,同时需要匹配tokens{}列表中的Token与我词法分析器生成的对应Token类型。
解决方法
1. 对齐Token类型编号
ANTLR4解析器依赖Token的类型编号匹配语法规则,所以第一步必须让你的C词法分析器生成的Token类型编号,和ANTLR语法里tokens{}定义的完全对应:
- 生成ANTLR解析器代码后,查看自动生成的头文件(比如
IFJ23Parser.h),里面会给tokens{}里的每个Token分配唯一整数编号,比如IFJ23Parser_Identifier、IFJ23Parser_FunctionKeyword这类。 - 在你的C词法分析器中,把你自己定义的Token枚举(比如你写的
IDENTIFIER)对应到这些ANTLR生成的编号上。
2. 转换自定义Token为ANTLR兼容格式
ANTLR解析器只认实现了它自身Token接口的对象(C目标中是antlr4c_Token)。你需要把C词法分析器输出的Token转换成ANTLR能识别的实例:
- 每个转换后的Token需要包含:类型编号、文本内容、行号、列号这些核心信息。
- 直接使用ANTLR C目标提供的
antlr4c_CommonToken结构体即可,填充对应的字段即可完成转换。
3. 构造Token流供解析器读取
ANTLR解析器需要通过TokenStream来读取Token序列,你可以借助antlr4c_ListTokenSource将你的Token列表转换为Token源,再用antlr4c_CommonTokenStream包装这个源:
// 假设你的C词法分析器生成了token_list数组,每个元素包含type、text、line、column字段 antlr4c_ListTokenSource *token_source = antlr4c_ListTokenSource_new(); for (int i = 0; i < token_count; i++) { YourToken *your_token = &token_list[i]; // 构造ANTLR兼容的CommonToken antlr4c_CommonToken *antlr_token = antlr4c_CommonToken_new( your_token->type, // 这里必须是ANTLR对应的类型编号 your_token->text ); antlr4c_Token_setLine((antlr4c_Token*)antlr_token, your_token->line); antlr4c_Token_setCharPositionInLine((antlr4c_Token*)antlr_token, your_token->column); antlr4c_ListTokenSource_add(token_source, (antlr4c_Token*)antlr_token); } antlr4c_CommonTokenStream *token_stream = antlr4c_CommonTokenStream_new((antlr4c_TokenSource*)token_source);
4. 启动解析生成语法树
有了TokenStream后,就可以实例化解析器并调用语法的起始规则生成语法树:
antlr4c_IFJ23Parser *parser = antlr4c_IFJ23Parser_new((antlr4c_TokenStream*)token_stream); antlr4c_ParseTree *parse_tree = antlr4c_IFJ23Parser_swiftFile(parser);
5. 自定义错误处理(可选)
如果需要捕获并处理解析错误,可以给解析器添加自定义错误监听器:
typedef struct { antlr4c_BaseErrorListener base; } CustomErrorListener; void custom_syntax_error(antlr4c_Recognizer *recognizer, antlr4c_Token *offending_symbol, size_t line, size_t char_pos, const char *msg, void *ctx) { fprintf(stderr, "语法错误:第%zu行,第%zu列:%s\n", line, char_pos, msg); } CustomErrorListener* custom_error_listener_new() { CustomErrorListener *listener = malloc(sizeof(CustomErrorListener)); antlr4c_BaseErrorListener_init(&listener->base); listener->base.syntaxError = custom_syntax_error; return listener; } // 替换默认错误监听器,使用自定义逻辑 antlr4c_Parser_removeErrorListeners((antlr4c_Parser*)parser); antlr4c_Parser_addErrorListener((antlr4c_Parser*)parser, (antlr4c_ErrorListener*)custom_error_listener_new());
内容的提问来源于stack exchange,提问作者Dj Sushi
相关产品推荐
相关产品推荐

