更换+副作用

Replacement + Side effect

在访问编译单元时——并给定特定条件——我想应用一个转换(使用 => 运算符)并计算对给定编译单元应用相同转换的次数。

我能够使用一种 "global module variable" 来执行此操作,但我非常确定可以在单个访问表达式中组合替换和操作。那可能吗?

module MultiCatch

import lang::java::\syntax::Java18;
import ParseTree; 
import IO;
import Map;
import Type; 
import List;

// sure, I don't like global variables. 
//
// However I could not find a way to perform both 
// a replacement and count the number of times 
// it was applied in the same compilation unit. 
int numberOfOccurences = 0; 

/**
 * Refactor a try-catch statement to use the 
 * MultiCatch construct of Java 7. 
 */
public tuple[int, CompilationUnit]  refactorMultiCatch(CompilationUnit unit) { 
  numberOfOccurences = 0;  
  CompilationUnit cu =  visit(unit) {
   case (TryStatement)`try <Block b1> <Catches c1>` => (TryStatement)`try <Block b1> <Catches mc>`
     when mc := computeMultiCatches(c1)
  };
  return <numberOfOccurences, cu>;
}

/*
 * Based on a simple notion of similarity, 
 * this function calculates the possible 
 * occurences of MultiCatch. 
 */ 
private Catches computeMultiCatches(cs){
   map [Block, tuple[list[CatchType], VariableDeclaratorId, Block] ] mCatches =();
   visit(cs){
      case(CatchClause)`catch (<CatchType t> <VariableDeclaratorId vId>) <Block b>` :{
         if (b in mCatches){
            <ts, vId, blk> = mCatches[b];
            ts += t;
            mCatches[b] = <ts, vId, blk>;
            numberOfOccurences += 1;
         }
         else{
            mCatches[b] = <[t], vId, b>;
         }
      }
   }
   return generateMultiCatches([mCatches[b] | b <- mCatches]); 
}

/*
 * Creates a syntactic catch clause (either a simple one or 
 * a multicatch). 
 * 
 * This is a recursive definition. The base case expects only 
 * one tuple, and than it returns a single catch clause. In the 
 * recursive definition, at least two tuples must be passed as 
 * arguments, and thus it returns at least two catches clauses 
 * (actually, one catch clause for each element in the list)
 */
private Catches generateMultiCatches([<ts, vId, b>]) = {
  types = parse(#CatchType, intercalate("| ", ts));
  return (Catches)`catch(<CatchType types>  <VariableDeclaratorId vId>) <Block b>`; 
};
private Catches generateMultiCatches([<ts, vId, b>, C*]) = {
  catches = generateMultiCatches(C);
  types = parse(#CatchType, intercalate("| ", ts));
  return (Catches)`catch(<CatchType types> <VariableDeclaratorId vId>) <Block b> <CatchClause+ catches>`;
};

一种方法是使用 local 变量和带有 insert:

的块
module MultiCatch

import lang::java::\syntax::Java18;
import ParseTree; 
import IO;
import Map;
import Type; 
import List;

/**
 * Refactor a try-catch statement to use the 
 * MultiCatch construct of Java 7. 
 */
public tuple[int, CompilationUnit]  refactorMultiCatch(CompilationUnit unit) { 
  int numberOfOccurences = 0;  /* the type is superfluous */
  CompilationUnit cu =  visit(unit) {
   case (TryStatement)`try <Block b1> <Catches c1>` : {
      numberOfOccurences += 1;
      mc = computeMultiCatches(c1)
      insert (TryStatement)`try <Block b1> <Catches mc>`;
   }
  };

  return <numberOfOccurences, cu>;
}
  • {...}块允许匹配后执行多个语句;
  • 局部变量现在只存在于refactorMultiCatch的框架中;
  • insert 语句与前面的 => 箭头具有相同的效果;
  • 由于匹配 := 总是成功,我将 when 子句更改为简单的赋值

在 Rascal 中还有其他更复杂的方式来共享状态,但我个人更喜欢让状态不脱离函数的词法范围。