ANTLR输出源

时间:2011-11-06 22:21:05

标签: antlr grammar lexer

我正在尝试为JavaScript之类的Code Contracts功能实现类似于我的某个课程的作业。

我遇到的问题是,我似乎无法找到一种方法直接将源文件输出到控制台,而无需修改整个语法。

有人知道实现这个目标的方法吗?

提前致谢。

以下是我正在尝试做的一个例子:

function DoClear(num, arr, text){ 
  Contract.Requires<RangeError>(num > 0); 
  Contract.Requires(num < 1000); 
  Contract.Requires<TypeError>(arr instanceOf Array); 
  Contract.Requires<RangeError>(arr.length > 0 && arr.length <= 9); 
  Contract.Requires<ReferenceError>(text != null); 
  Contract.Ensures<RangeError>(text.length === 0); 

  // method body
  [...] 

  return text; 
}  

function DoClear(num, arr, text){ 
   if (!(num > 0)) 
     throw RangeError; 
   if (!(num < 1000)) 
     throw Error; 
   if (!(arr instanceOf Array)) 
     throw TypeError; 
   if (!(arr.length > 0 && arr.length <= 9)) 
     throw RangeError; 
   if (!(text != null)) 
     throw ReferenceError 

   // method body
   [...] 

   if (!(text.length === 0)) 
     throw RangeError 
   else 
     return text; 
} 

1 个答案:

答案 0 :(得分:0)

您需要考虑一些(次要)事项:

  • 忽略可能包含特殊合同语法的字符串文字;
  • 忽略可能包含特殊Contract语法的多行和单行注释;
  • 忽略这样的代码:var Requires = "Contract.Requires<RangeError>";(即常规JavaScript代码“看起来像”你的合同语法);

将上述各点考虑在内并简单地为整个合同行创建单个令牌是非常简单的。将以下内容标记为4个不同的标记Contract.Requires<RangeError>(num > 0)

时,您将努力工作
  • Contract
  • Requires
  • <RangeError>
  • (num > 0)

因此最简单的方法是从中创建一个令牌,并在解析阶段,将令牌分成".""<"">",最多包含4个令牌(离开表达式)包含".""<"">"。)

我上面描述的内容的快速演示可能如下所示:

grammar CCJS;

parse
  :  atom+ EOF
  ;

atom
  :  code_contract
  |  (Comment | String | Any) {System.out.print($text);}
  ;

code_contract
  :  Contract 
     {
       String[] tokens = $text.split("[.<>]", 4);
       System.out.print("if (!" + tokens[3] + ") throw " + tokens[2]);
     }
  ;

Contract
@init{
  boolean hasType = false;
}
@after{
  if(!hasType) {
    // inject a generic Error if this contract has no type
    setText(getText().replaceFirst("\\(", "<Error>("));
  }
}
  :  'Contract.' ('Requires' | 'Ensures') ('<' ('a'..'z' | 'A'..'Z')+ '>' {hasType=true;})? '(' ~';'+
  ;

Comment
  :  '//' ~('\r' | '\n')*
  |  '/*' .* '*/'
  ;

String
  :  '"' (~('\\' | '"' | '\r' | '\n') | '\\' . )* '"'
  ;

Any
  :  .
  ;

您可以使用以下类进行测试:

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String src = 
        "/*                                                                  \n" +
        "   Contract.Requires to be ignored                                  \n" +
        "*/                                                                  \n" +
        "function DoClear(num, arr, text){                                   \n" +
        "  Contract.Requires<RangeError>(num > 0);                           \n" +
        "  Contract.Requires(num < 1000);                                    \n" +
        "  Contract.Requires<TypeError>(arr instanceOf Array);               \n" +
        "  Contract.Requires<RangeError>(arr.length > 0 && arr.length <= 9); \n" +
        "  Contract.Requires<ReferenceError>(text != null);                  \n" +
        "  Contract.Ensures<RangeError>(text.length === 0);                  \n" +
        "                                                                    \n" +
        "  // method body                                                    \n" +
        "  // and ignore single line comments, Contract.Ensures              \n" +
        "  var s = \"Contract.Requires\"; // also ignore strings             \n" +
        "                                                                    \n" +
        "  return text;                                                      \n" +
        "}                                                                   \n";

    CCJSLexer lexer = new CCJSLexer(new ANTLRStringStream(src));
    CCJSParser parser = new CCJSParser(new CommonTokenStream(lexer));
    parser.parse();
  }
}

如果您运行上面的Main课程,则会在控制台上打印以下内容:

/*
   Contract.Requires to be ignored
*/
function DoClear(num, arr, text){
  if (!(num > 0)) throw RangeError;
  if (!(num < 1000)) throw Error;
  if (!(arr instanceOf Array)) throw TypeError;
  if (!(arr.length > 0 && arr.length <= 9)) throw RangeError;
  if (!(text != null)) throw ReferenceError;
  if (!(text.length === 0)) throw RangeError;

  // method body
  // and ignore single line comments, Contract.Ensures
  var s = "Contract.Requires"; // also ignore strings

  return text;
}

但是......

...我意识到这不是你正在寻找的东西:RangeError 不是放在你的功能结束时。这将是艰难的:一个函数可能有多个return,并且可能有多个代码块{ ... },这使得很难知道}在哪里结束{ {1}}。所以你不知道究竟要注入这个function - 检查。至少,不是我所展示的天真的方法。

实现这种方法的唯一可靠方法是获得一个不错的JavaScript语法,为它添加自己的契约规则,重写解析器生成的AST,最后以友好格式的方式发出新的AST:not not至少可以说是一项微不足道的任务!

ANTLR Wiki上有各种ECMA / JS语法,但小心谨慎:它们是用户提交的语法,可能包含错误(在这种情况下可能会 RangeError !)。

如果您选择将[1]放在应该重写的位置,请执行以下操作:

RangeError

会导致:

function DoClear(num, arr, text){ 
  Contract.Requires<RangeError>(num > 0); 
  ...

  // method body
  ...

  Contract.Ensures<RangeError>(text.length === 0);

  return text; 
}  

然后你不需要解析整个方法体,你可能会像我提出的那样躲开黑客攻击。

祝你好运!

function DoClear(num, arr, text){ if (!(num > 0)) throw RangeError; ... // method body ... if (!(text.length === 0)) throw RangeError return text; } 我最后一次检查这些ECMA / JS脚本语法时,没有一个正确地处理正则表达式文字,[1],这让我觉得可疑。