4

私は Scala コンパイラ プラグインを書いており、「apply(unit:CompilationUnit」メソッドを作成するところまで来ました。ただし、そのメソッド内の構文は理解できません。次のコードはhttp://www.scala-langからのものです。 .org/ノード/140

    for ( tree @ Apply(Select(rcvr, nme.DIV), List(Literal(Constant(0)))) <- unit.body;
         if rcvr.tpe <:< definitions.IntClass.tpe) {
        unit.error(tree.pos, "definitely division by zero")
      }

この式は、ゼロによるすべての除算を検出します。すべての実行可能なステートメント (TermTrees??) を検索するために同様のことを行う方法がわかりません。

4

1 に答える 1

7

わかりました、以下のファイルを想定しますTwoStatements.scala

class TwoStatements {
  def f {
    println("first statement")
    println("second statement")
  }
}

次のコマンドを試してください。

scalac -Yshow-trees -Xprint:typer TwoStatements.scala 
scalac -Yshow-trees-compact -Xprint:typer TwoStatements.scala 
scalac -Yshow-trees-stringified -Xprint:typer TwoStatements.scala 
scalac -Ybrowse:typer TwoStatements.scala 

出力が生成されるtyperのフェーズであることに注意してください。そのため、独自のプラグインが実行されるフェーズに適したフェーズを選択してください。

これ-Yshow-trees-compactは、独自のコードで使用するものとまったく同じ (またはかなり近い) 出力を生成するものですが、非常に読みにくいものです。

他のものは読みやすいですが、独自のコードに変換すると混乱する可能性があります。は、最も多くの情報を表示するため、-Yshow-trees-stringifiedおそらく よりも便利です。-Yshow-trees一方、-Ybrowse:typerはインタラクティブで、選択したツリー ノードのコードを表示します。これは、特に大きなプログラムを見る場合に役立ちます。

リンクされたブログの例を試す-Yshow-trees-compactと、次のスニペットが表示されます。

 Apply(
   Select(
     Select(This(newTypeName("Test")), newTermName("five")), // assigned to rcvr
     newTermName("$div")                                     // compared to nme.DIV
   ), 
   List(Literal(Constant(0))))                               // as is
 )

したがって、私が提案しているのは、プラグインが動作している段階で処理したいコードがどのように変換されるかを確認し、スニペットを取得して、興味のない部分があれば変数に置き換えることです。

DefDefeveryには body が 6 番目の要素として含まれていることに注意してください。それはBlockList複数のステートメントの場合もあれば、メソッド呼び出し ( Apply)、ゲッター ( Select)、代入 ( Assign)、if ステートメント ( If) などの場合もあります。などの他の種類の宣言にValDefも、コードが関連付けられています。

のようなものを探していた場合tree @ Statement(...)、存在しません。TermTreeコードを表すものを識別するために (または、さらに良い方法として) を使用することもできisTermますが、式の一部または完全なブロックからステートメントを区別することはできません。

実際のコードの AST を見て、それがどのように機能するかを学びます。

編集

プレゼンテーションを見ていて、scala/reflecti/api/Trees.scalaファイルの最後にある次のコメントにたどり着きました。

// A standard pattern match
  case EmptyTree =>
  case PackageDef(pid, stats) =>
     // package pid { stats }
  case ClassDef(mods, name, tparams, impl) =>
     // mods class name [tparams] impl   where impl = extends parents { defs }
  case ModuleDef(mods, name, impl) =>                             (eliminated by refcheck)
     // mods object name impl  where impl = extends parents { defs }
  case ValDef(mods, name, tpt, rhs) =>
     // mods val name: tpt = rhs
     // note missing type information is expressed by tpt = TypeTree()
  case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
     // mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs
     // note missing type information is expressed by tpt = TypeTree()
  case TypeDef(mods, name, tparams, rhs) =>                       (eliminated by erasure)
     // mods type name[tparams] = rhs
     // mods type name[tparams] >: lo <: hi,  where lo, hi are in a TypeBoundsTree,
                                              and DEFERRED is set in mods
  case LabelDef(name, params, rhs) =>
     // used for tailcalls and like
     // while/do are desugared to label defs as follows:
     // while (cond) body ==> LabelDef($L, List(), if (cond) { body; L$() } else ())
     // do body while (cond) ==> LabelDef($L, List(), body; if (cond) L$() else ())
  case Import(expr, selectors) =>                                 (eliminated by typecheck)
     // import expr.{selectors}
     // Selectors are a list of pairs of names (from, to).
     // The last (and maybe only name) may be a nme.WILDCARD
     // for instance
     //   import qual.{x, y => z, _}  would be represented as
     //   Import(qual, List(("x", "x"), ("y", "z"), (WILDCARD, null)))
  case Template(parents, self, body) =>
     // extends parents { self => body }
     // if self is missing it is represented as emptyValDef
  case Block(stats, expr) =>
     // { stats; expr }
  case CaseDef(pat, guard, body) =>                               (eliminated by transmatch/explicitouter)
    // case pat if guard => body
  case Alternative(trees) =>                                      (eliminated by transmatch/explicitouter)
    // pat1 | ... | patn
  case Star(elem) =>                                              (eliminated by transmatch/explicitouter)
    // pat*
  case Bind(name, body) =>                                        (eliminated by transmatch/explicitouter)
    // name @ pat
  case UnApply(fun: Tree, args)                                   (introduced by typer, eliminated by transmatch/explicitouter)
    // used for unapply's
  case ArrayValue(elemtpt, trees) =>                              (introduced by uncurry)
    // used to pass arguments to vararg arguments
    // for instance, printf("%s%d", foo, 42) is translated to after uncurry to:
    // Apply(
    //   Ident("printf"),
    //   Literal("%s%d"),
    //   ArrayValue(<Any>, List(Ident("foo"), Literal(42))))
  case Function(vparams, body) =>                                 (eliminated by lambdaLift)
    // vparams => body  where vparams:List[ValDef]
  case Assign(lhs, rhs) =>
    // lhs = rhs
  case AssignOrNamedArg(lhs, rhs) =>                              (eliminated by typer, resurrected by reifier)
    // @annotation(lhs = rhs)
  case If(cond, thenp, elsep) =>
    // if (cond) thenp else elsep
  case Match(selector, cases) =>
    // selector match { cases }
  case Return(expr) =>
    // return expr
  case Try(block, catches, finalizer) =>
    // try block catch { catches } finally finalizer where catches: List[CaseDef]
  case Throw(expr) =>
    // throw expr
  case New(tpt) =>
    // new tpt   always in the context: (new tpt).<init>[targs](args)
  case Typed(expr, tpt) =>                                        (eliminated by erasure)
    // expr: tpt
  case TypeApply(fun, args) =>
    // fun[args]
  case Apply(fun, args) =>
    // fun(args)
    // for instance fun[targs](args)  is expressed as  Apply(TypeApply(fun, targs), args)
  case ApplyDynamic(qual, args)                                   (introduced by erasure, eliminated by cleanup)
    // fun(args)
  case Super(qual, mix) =>
    // qual.super[mix]     qual is always This(something), if mix is empty, it is tpnme.EMPTY
  case This(qual) =>
    // qual.this
  case Select(qualifier, selector) =>
    // qualifier.selector
  case Ident(name) =>
    // name
    // note: type checker converts idents that refer to enclosing fields or methods
    // to selects; name ==> this.name
  case ReferenceToBoxed(ident) =>                                 (created by typer, eliminated by lambdalift)
    // synthetic node emitted by macros to reference capture vars directly without going through ``elem''
    // var x = ...; fun { x } will emit Ident(x), which gets transformed to Select(Ident(x), "elem")
    // if ReferenceToBoxed were used instead of Ident, no transformation would be performed
  case Literal(value) =>
    // value
  case TypeTree() =>                                              (introduced by refcheck)
    // a type that's not written out, but given in the tpe attribute
  case Annotated(annot, arg) =>                                   (eliminated by typer)
    // arg @annot  for types,  arg: @annot for exprs
  case SingletonTypeTree(ref) =>                                  (eliminated by uncurry)
    // ref.type
  case SelectFromTypeTree(qualifier, selector) =>                 (eliminated by uncurry)
    // qualifier # selector, a path-dependent type p.T is expressed as p.type # T
  case CompoundTypeTree(templ: Template) =>                       (eliminated by uncurry)
    // parent1 with ... with parentN { refinement }
  case AppliedTypeTree(tpt, args) =>                              (eliminated by uncurry)
    // tpt[args]
  case TypeBoundsTree(lo, hi) =>                                  (eliminated by uncurry)
    // >: lo <: hi
  case ExistentialTypeTree(tpt, whereClauses) =>                  (eliminated by uncurry)
    // tpt forSome { whereClauses }
于 2012-05-02T19:28:09.610 に答える