写在之前

先来回顾一下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();
  1. 首先我们创建一个ANTLRInputStream,并从System.in获取到输入流

    //System.in
    class T XYZ {int ;
    }
    
  2. 创建一个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
  3. 创建CommonTokenStream,我们将词法分析过后的tokens从lexer中取出,根据顺序排列起来成为tokens的流。(其中可能丢弃了一些空白符号)

  4. 最后我们将tokens放入Simple语法的Parser中,进行语法分析,从Prog文法规则开始解析。

修改错误信息

ANTLR4本身自带了ConsoleErrorListener,但有时候我们需要自定义错误信息,这时可以从BaseErrorListener那继承来,并重写syntaxError

我们即将介绍两种自定义错误信息:

  1. 通过栈显示当前错误位置经过了哪几次语法判定。
  2. 显示当前错误行,并在出错的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规则匹配时发现出现了问题。

接下来我们一一解析各个参数。

  1. line:当前出错行。
  2. charPositionInLine:出错token首字符在当前行的位置。
  3. 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含义相同。
  4. msg则是经过判断后的出错信息显示。

标记出错的token

  1. offendingSymbol我们将它强转成Token类型,为了获取它的开始与结束的字符位置。
  2. 如何从CommonTokenStream提取出最初的System.in的输入流呢。
    我们先从CommonTokenStream中提取出TokenSource,也就是lexer里token与Input的映射
    再提取出它原有的CharStream,也就是系统输入的System.in,它还没有经过lexer处理。
    ANTLR4 API
  3. 通过换行符我们将input切割,然后将出错的那行先打印出来。
  4. 在charPositionInLine之前的第二行输入空格。
  5. 然后通过从offendingSymbol转来的token,获取start,stop的Index,并给上标记。
  6. 特别需要注意在java中,printprintln差了个回车。
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(九) 修改错误信息相关推荐

  1. 错误信息: 集合已修改;可能无法执行枚举操作。

    错误信息:     集合已修改:可能无法执行枚举操作. 调用堆栈: 在 System.ThrowHelper.ThrowInvalidOperationException(ExceptionResou ...

  2. 修改html文件500错误信息,打开网页提示 HTTP500错误的终极解决方法

    http 500内部服务器错误说明IIS服务器无法解析ASP代码,访问一个静态页面试试是否也出现这个问题,如果访问静态页面没问题,那就要分以下几种 情况来分析了: ① 你是否改变过计算机名称. ② 站 ...

  3. 电脑出现错误信息的故障排除

    1.出现TASKMON caused an invalid page fault in module KERNEL32.DLL (1)适用范围:Windows Me.Windows 98 (2)症状: ...

  4. 电脑各种中英文信息对照及错误信息总汇 系统出错信息及解决方案

    一.BIOS中的提示信息 提示信息       说明    Drive A error   驱动器A错误    System halt     系统挂起    Keyboard controller ...

  5. win10你的电脑遇到问题,需要重新启动,我们只收集某些错误信息(volmgr win10 161) ---(解决方法:调整内存对应的页面文件)

    目录 前言 ■解决(step3,6有效,其他感觉没有什么效果.) ■解决step1.设置电脑启动为快速启动(没有什么效果) ■解决Step2.1  完全内存转储 (不太好用) ■解决Step2.2  ...

  6. Partition Magic错误信息与解决方法

    Partition Magic错误信息与解决方法<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:of ...

  7. php mysql 500错误日志_服务器出现500错误的时候,让PHP显示错误信息

    用PHP开发程序时,如果服务器出现500的时候,如果无法知道究竟是什么原因,就无法进行调试. 要让PHP显示错误信息,可以从PHP配置文件 (php.ini)或PHP程序文件入手,另外,如果与IIS整 ...

  8. 复杂个人信息输出程序python_练习题-修改个人信息程序

    1 #!/usr/bin/env python 2 #-*- coding=utf-8 -*- 3 """ 4 @author:Wllen5 @file:staff_ma ...

  9. nginx下启动php-fpm相关错误信息集锦(长期补充)

    一.背景 今天的服务器好几次都报502错误,可能是由于写的脚本太多了,加上使用的第三方插件,响应超时等等一系列问题.这边碰到很多php-fpm方面的错误.记录一下. 二.报错信息 1. value i ...

最新文章

  1. 远程办公是巨头游戏?十倍扩容,他们如何做到百万级并发流量
  2. Android进阶知识:事件分发与滑动冲突(一)
  3. linux配置临时IP和永久IP
  4. PHP调用扩展的三种方式:dl() .so ZendEngine
  5. java chat_使用 Java 创建聊天客户端-1
  6. 你鼓舞了我是世界杯主题曲吗_选择方法和鼓舞人心的网站列表
  7. liigo:爱可视70平板电脑使用感受,遗憾与满足并存
  8. python统计行号_用Python实现两个文件的不同行的编号
  9. 以太坊核心开发者正在制定ETH2.0合并最低技术规范
  10. 「极点日历」小程序插件
  11. UE4 C++头文件
  12. 新手入门学Python一定要知道的编程开发工具
  13. cms三次标记浮动垃圾是如何产生的
  14. python列表get方法_Python json.get方法代码示例
  15. [UOJ311]积劳成疾
  16. Yau 近代几何讲座
  17. 五款最出色的数据恢复工具
  18. 微信运动的刷步思路+云部署
  19. 谈一谈Java中的“静态”
  20. STM32F105标准库读写USB写入csv文件

热门文章

  1. YUV和RGB的相互转换实验
  2. 类方法和对象方法的区别
  3. 计算机专业爱玩游戏的笔记本,如果你是个爱玩游戏的设计师,这款游戏电脑笔记本是你的首选...
  4. iView UI Select 选择器 下拉列表跟随滚动条移动
  5. Tkinter 实现弹出子窗口并冻结主窗口
  6. 万众瞩目!YOLOv8诞生,要包揽目标检测、实例分割新SOTA!
  7. 三星C系列手机疑似要升级到安卓7.0,还另加手机数据恢复功能
  8. 美国的华人码农,正在成为IT届的吠舍?
  9. java重写需要注意的
  10. mysql数据迁移数据一致性检校验