• 扩展iQuery使其支持多种编程语言(四) – 兼编译器的语法错误处理简介


    扩展iQuery使其支持多种编程语言(四) – 兼编译器的语法错误处理简介

    iQuery是一个开源的自动化测试框架项目,有兴趣的朋友可以在这里下载:https://github.com/vowei/iQuery/downloads

    源码位置:https://github.com/vowei/iQuery

    相关的使用文档,请参看:

    开源类库iQuery Android版使用说明
    类jQuery selector的控件查询iQuery开源类库介绍
    开源手机自动化测试框架iQuery入门教程(一)
    开源手机自动化测试框架iQuery入门教程(二)
    开源手机自动化测试框架iQuery入门教程(三)
    上一篇文章中,简单介绍了iQuery解释器的语义分析部分。

    ANTLR已经自带了一些对词法和语法错误的处理功能,当一行语句出现语法错误时,ANTLR会尽量跳过出错的一行代码,恢复编译和解释功能,通过一个回调函数,我们可以向最终用户显示更细致的错误提示。

    一般来说,好的错误提示应该有以下几个性质:
    1.    需要指明错误的行号和列号,以便用户快速在源代码中定位出错的那一行代码,这个功能ANTLR会在调用我们的回调函数时传入这些信息。
    2.    需要指明错误原因,例如“不匹配的字符”这样的错误信息显然没有“第1行,第25列: 没有关闭的语句,期望']',当前碰到的是''<EOF>''!”这样的信息更明确。
    3.    需要指明导致出错的文字,例如在编程中,针对使用一个未定义的变量的编程错误,当然需要在错误信息里指出这个未定义的变量名。
    4.    最好给出修复错误的建议。

    在ANTLR里,可以在代码里定义一个getErrorMessage函数,以便ANTLR回调。在Java版本中,getErrorMessage函数的声明形式如下(代码实现在:https://github.com/vowei/iQuery/blob/master/java/iquery/iquery-core/src/main/java/cc/iqa/iquery/iQuery.g):

       1:  public String getErrorMessage(RecognitionException e,
       2:                                      String[] tokenNames)

    当ANTLR碰到词法或者语法错误时,会抛出一个基类为RecognitionException的异常,并传递给getErrorMessage函数,而第二个参数tokenNames就是导致词法/语法错误时的源码符号,getErrorMessage函数的返回值就是细化后的错误消息。

    ANTLR针对不同的词/语法错误会生成不同的RecognitionException的继承类,在这些继承类里,分别定义了一些对细化错误消息有帮助的属性。下面代码是一个细化错误消息的例子,其中MismatchedTokenException表示一个未匹配的语法,在MismatchedTokenException的对象里,可以通过expecting字段获取期望的词法符号(原始代码位置是:https://github.com/vowei/iQuery/blob/master/java/iquery/iquery-core/src/main/java/cc/iqa/iquery/ErrorMessageHelper.java )。

       1:  if (e instanceof MismatchedTokenException) {
       2:              MismatchedTokenException mte = (MismatchedTokenException) e;
       3:              String tokenName = "<unknown>";
       4:              if (mte.expecting == Token.EOF) {
       5:                  tokenName = "EOF";
       6:              } else if (tokenNames != null) {
       7:                  tokenName = tokenNames[mte.expecting];
       8:              } else {
       9:                  tokenName = new String(new char[] {(char)mte.expecting});
      10:              }
      11:   
      12:              if ( e.token != null ) {
      13:                  msg = String.format("%1$s: 没有关闭的语句,期望%2$s,当前碰到的是'%3$s'!",
      14:                          hdr, tokenName, recognizer.getTokenErrorDisplay(e.token));
      15:              } else {
      16:                  msg = String.format("%1$s: 没有关闭的语句,期望%2$s!",
      17:                          hdr, tokenName);                
      18:              }
      19:          }

    JavaScript版本里getErrorMessage函数的声明类似,处理方式也类似参考代码:https://github.com/vowei/iQuery/blob/master/iOS/lib/iQuery.ghttps://github.com/vowei/iQuery/blob/master/iOS/lib/error.js ):

       1:  function onMismatchedTokenException(mte, tokenNames, recognizer) {
       2:      debug("onMismatchedTokenException");
       3:   
       4:      var tokenName = "<unknown>";
       5:      if (mte.expecting == org.antlr.runtime.Token.EOF) {
       6:          tokenName = "EOF";
       7:      } else if (tokenNames != null) {
       8:          tokenName = tokenNames[mte.expecting];
       9:      } else {
      10:          debug("[onMismatchedTokenException] - mte.expecting: " + mte.expecting);
      11:          tokenName = mte.expecting;
      12:      }
      13:   
      14:      if (mte.token != null) {
      15:          return "没有关闭的语句,期望" + tokenName + ",当前碰到的是'" + recognizer.getTokenErrorDisplay(mte.token) + "'!";
      16:      } else if (tokenName != undefined) {
      17:          return "没有关闭的语句,期望" + tokenName + "!";
      18:      } else {
      19:          return "没有关闭的语句!";
      20:      }
      21:  }

    由于词法分析器(Lexer)和语法分析器(Parser)是两个类,而且词法和语法分析过程都有可能发生错误,因此需要分别在两个分析器里定义getErrorMessage函数,添加的方式很简单,在antlr的语法定义.g文件里,添加在@lexer::members和@parser::members代码块里即可,例如下面是JavaScript版本的声明方式:

       1:  @lexer::members {
       2:  _errors = [];
       3:  this.getErrorMessage = function(e, tokenNames)
       4:  {
       5:      var error = getErrorsHelper(e, null, tokenNames, this);
       6:   
       7:      if ( _errors != undefined && _errors != null ) {
       8:          _errors.push(error);   
       9:      }
      10:   
      11:      return error;
      12:  }
      13:  }
      14:   
      15:  @parser::members {
      16:  _errors = [];
      17:  this.getErrorMessage = function(e, tokenNames)
      18:  {
      19:      var error = getErrorsHelper(e, this.input, tokenNames, this);
      20:   
      21:      if ( _errors != undefined && _errors != null ) {
      22:          _errors.push(error);   
      23:      }
      24:   
      25:      return error;
      26:  }
      27:  }

  • 相关阅读:
    详解go语言的array和slice 【一】
    node.js 事件循环
    搭建Docker私有仓库--自签名方式
    详解JavaScript闭包
    [个人翻译]Redis 集群教程(下)
    转:CMake 使用方法
    转: Ogre实现无缝地图要改的地方 记下来 用的时候可以看
    转:ogre的编译及安装
    转:Ogre TerrainGroup地形赏析
    转:如何编译delta3d
  • 原文地址:https://www.cnblogs.com/vowei/p/2707451.html
Copyright © 2020-2023  润新知