ANTLR4(九) 修改错误信息
写在之前
先来回顾一下ANTLR4是如何处理并分析输入流的。
语法文件
一个简单的类分析语法文件。
grammar Simple;prog: classDef+ ; // match one or more class definitionsclassDef: 'class' ID '{' member+ '}' // a class has one or more members{System.out.println("class "+$ID.text);};member: 'int' ID ';' // field definition{System.out.println("var "+$ID.text);}| 'int' f=ID '(' ID ')' '{' stat '}' // method definition{System.out.println("method: "+$f.text);};stat: expr ';'{System.out.println("found expr: "+$ctx.getText());}| ID '=' expr ';'{System.out.println("found assign: "+$ctx.getText());};expr: INT | ID '(' INT ')';INT : [0-9]+ ;
ID : [a-zA-Z]+ ;
WS : [ \t\r\n]+ -> skip ;
输入流的处理分析过程
我们从常见的main程序中截取一段:
ANTLRInputStream input = new ANTLRInputStream(System.in);SimpleLexer lexer = new SimpleLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);SimpleParser parser = new SimpleParser(tokens);parser.prog();
首先我们创建一个ANTLRInputStream,并从System.in获取到
输入流
://System.in class T XYZ {int ; }
创建一个Simple语法下的词法分析器lexer,并传入input。我们这一步将
input
对照着Simple.tokens
,在lexer通过词法分析,得出了映射关系
。//Simple.tokens T__0=1 T__1=2 T__2=3 T__3=4 T__4=5 T__5=6 T__6=7 T__7=8 INT=9 ID=10 WS=11 'class'=1 '{'=2 '}'=3 'int'=4 ';'=5 '('=6 ')'=7 '='=8
创建CommonTokenStream,我们将词法分析过后的tokens从lexer中取出,根据顺序排列起来成为
tokens的流
。(其中可能丢弃了一些空白符号)最后我们将tokens放入Simple语法的Parser中,进行语法分析,从Prog文法规则开始解析。
修改错误信息
ANTLR4本身自带了ConsoleErrorListener
,但有时候我们需要自定义错误信息,这时可以从BaseErrorListener
那继承来,并重写syntaxError
。
我们即将介绍两种自定义错误信息:
- 通过栈显示当前错误位置经过了哪几次语法判定。
- 显示当前错误行,并在出错的token下标记。
错误时显示规则栈调用
syntaxError的几个参数我们等会对照着运行结果细说。
注意recognizer可以强转成Parser、CommonTokenStream等多个类型。
//TestE_Listener.java
public static class VerboseListener extends BaseErrorListener {@Overridepublic void syntaxError(Recognizer<?, ?> recognizer,Object offendingSymbol,int line, int charPositionInLine,String msg,RecognitionException e){List<String> stack = ((Parser)recognizer).getRuleInvocationStack();Collections.reverse(stack);System.err.println("rule stack: "+stack);System.err.println("line "+line+":"+charPositionInLine+" at "+offendingSymbol+": "+msg);}}
需要注意parser需要去掉原有的ConsoleErrorListener,再实例化自定义的。
parser.removeErrorListeners(); // remove ConsoleErrorListenerparser.addErrorListener(new VerboseListener()); // add ours
运行结果:
可以看到rule stack里,从prog->classDef规则匹配时发现出现了问题。
接下来我们一一解析各个参数。
- line:当前出错行。
- charPositionInLine:出错token首字符在当前行的位置。
- offendingSymbol:我们以
[@2,8:10='XYZ',< 10 >,1:8]
为例。@2
指的是XYZ这个token在整个tokens流
里的位置,从0开始算,也就是第3个。8:10
是指该token对应的源字符串从头到尾的字符位置。
< 10 >
指的是词法符号的index
,可以回顾上面的Simple.tokens
。最后的1:8和开头的1:8含义相同。 - msg则是经过判断后的出错信息显示。
标记出错的token
offendingSymbol
我们将它强转成Token
类型,为了获取它的开始与结束的字符位置。- 如何从CommonTokenStream提取出最初的System.in的输入流呢。
我们先从CommonTokenStream
中提取出TokenSource
,也就是lexer里token与Input的映射
;
再提取出它原有的CharStream
,也就是系统输入的System.in
,它还没有经过lexer处理。
ANTLR4 API - 通过换行符我们将input切割,然后将出错的那行先打印出来。
- 在charPositionInLine之前的第二行输入空格。
- 然后通过从offendingSymbol转来的token,获取start,stop的Index,并给上标记。
- 特别需要注意在java中,
print
和println
差了个回车。
public static class UnderLineListener extends BaseErrorListener {@Overridepublic void syntaxError(Recognizer<?, ?> recognizer,Object offendingSymbol,int line, int charPositionInLine,String msg,RecognitionException e){System.err.println("line "+line+":"+charPositionInLine+" "+msg);UnderLineError(recognizer, (Token)offendingSymbol, line, charPositionInLine);}protected void UnderLineError(Recognizer recognizer,Token offendingToken,int line,int charPositionInLine){CommonTokenStream tokens=(CommonTokenStream)recognizer.getInputStream(); String input=tokens.getTokenSource().getInputStream().toString();String lines[]=input.split("\n");String errorLine=lines[line-1];System.out.println(errorLine);for(int i=0;i<charPositionInLine;++i) System.err.print(" ");int start=offendingToken.getStartIndex();int stop=offendingToken.getStopIndex();if(start>=0&&stop>=0) for(int i=start;i<=stop;++i) System.err.print("^");System.err.println();}}
运行结果:
修改错误处理手段
ANTLR4在遇到错误时,往往会通过一些自动恢复
的机制,使得匹配从错误中恢复过来。
但有时我们需要让它及时地停止并抛出异常
,比如对于bash的处理。
词法异常跳出
因为对于输入流的匹配是从词法开始的,比如我们在Simple的语法顶一下,输入:
# class T { int i; }
我们需要让它在词法分析
#的时候,就抛出异常。
于是我们自定义一个SimpleLexer,将它的自动恢复函数
重写一遍,抛出异常。
public static class BailSimpleLexer extends SimpleLexer{public BailSimpleLexer(CharStream input) { super(input); }public void recover(LexerNoViableAltException e) {throw new RuntimeException(e); // Bail out}}
在编写main函数的时候注意,将SimpleLexer
改成BailSimpleLexer
。
运行结果:
语法异常跳出
更常见的是语法匹配时,我们不需要自动恢复,而是抛出异常。
比如输入:
class {}
我们需要自定义DefaultErrorStrategy
,这是针对Parser
的。
一共有三个需要重写:句内恢复、子规则同步、其它恢复。
import org.antlr.v4.runtime.*;
public class BailErrorStrategy extends DefaultErrorStrategy{@Override public Token recoverInline(Parser recognizer) throws RecognitionException{throw new RuntimeException(new InputMismatchException(recognizer));}@Override public void sync(Parser recognizer){}@Override public void recover(Parser recognizer,RecognitionException e){throw new RuntimeException(e);}
}
运行结果:
相当是句内恢复的异常抛出。
ANTLR4(九) 修改错误信息相关推荐
- 错误信息: 集合已修改;可能无法执行枚举操作。
错误信息: 集合已修改:可能无法执行枚举操作. 调用堆栈: 在 System.ThrowHelper.ThrowInvalidOperationException(ExceptionResou ...
- 修改html文件500错误信息,打开网页提示 HTTP500错误的终极解决方法
http 500内部服务器错误说明IIS服务器无法解析ASP代码,访问一个静态页面试试是否也出现这个问题,如果访问静态页面没问题,那就要分以下几种 情况来分析了: ① 你是否改变过计算机名称. ② 站 ...
- 电脑出现错误信息的故障排除
1.出现TASKMON caused an invalid page fault in module KERNEL32.DLL (1)适用范围:Windows Me.Windows 98 (2)症状: ...
- 电脑各种中英文信息对照及错误信息总汇 系统出错信息及解决方案
一.BIOS中的提示信息 提示信息 说明 Drive A error 驱动器A错误 System halt 系统挂起 Keyboard controller ...
- win10你的电脑遇到问题,需要重新启动,我们只收集某些错误信息(volmgr win10 161) ---(解决方法:调整内存对应的页面文件)
目录 前言 ■解决(step3,6有效,其他感觉没有什么效果.) ■解决step1.设置电脑启动为快速启动(没有什么效果) ■解决Step2.1 完全内存转储 (不太好用) ■解决Step2.2 ...
- Partition Magic错误信息与解决方法
Partition Magic错误信息与解决方法<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:of ...
- php mysql 500错误日志_服务器出现500错误的时候,让PHP显示错误信息
用PHP开发程序时,如果服务器出现500的时候,如果无法知道究竟是什么原因,就无法进行调试. 要让PHP显示错误信息,可以从PHP配置文件 (php.ini)或PHP程序文件入手,另外,如果与IIS整 ...
- 复杂个人信息输出程序python_练习题-修改个人信息程序
1 #!/usr/bin/env python 2 #-*- coding=utf-8 -*- 3 """ 4 @author:Wllen5 @file:staff_ma ...
- nginx下启动php-fpm相关错误信息集锦(长期补充)
一.背景 今天的服务器好几次都报502错误,可能是由于写的脚本太多了,加上使用的第三方插件,响应超时等等一系列问题.这边碰到很多php-fpm方面的错误.记录一下. 二.报错信息 1. value i ...
最新文章
- 远程办公是巨头游戏?十倍扩容,他们如何做到百万级并发流量
- Android进阶知识:事件分发与滑动冲突(一)
- linux配置临时IP和永久IP
- PHP调用扩展的三种方式:dl() .so ZendEngine
- java chat_使用 Java 创建聊天客户端-1
- 你鼓舞了我是世界杯主题曲吗_选择方法和鼓舞人心的网站列表
- liigo:爱可视70平板电脑使用感受,遗憾与满足并存
- python统计行号_用Python实现两个文件的不同行的编号
- 以太坊核心开发者正在制定ETH2.0合并最低技术规范
- 「极点日历」小程序插件
- UE4 C++头文件
- 新手入门学Python一定要知道的编程开发工具
- cms三次标记浮动垃圾是如何产生的
- python列表get方法_Python json.get方法代码示例
- [UOJ311]积劳成疾
- Yau 近代几何讲座
- 五款最出色的数据恢复工具
- 微信运动的刷步思路+云部署
- 谈一谈Java中的“静态”
- STM32F105标准库读写USB写入csv文件