All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sireum.scalac.SireumPlugin.scala Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 Copyright (c) 2017, Robby, Kansas State University
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 1. Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.sireum.scalac

import scala.tools.nsc.Global
import scala.tools.nsc.Phase
import scala.tools.nsc.plugins.Plugin
import scala.tools.nsc.plugins.PluginComponent
import scala.tools.nsc.transform.TypingTransformers

class SireumPlugin(override val global: Global) extends Plugin {
  override val name = "sireum"
  override val description = "Compiler plugin for the Sireum Scala subset."
  override val components: List[PluginComponent] = List(new SireumComponent(global))
}

final class SireumComponent(val global: Global) extends PluginComponent with TypingTransformers {

  implicit class CopyPos(val t: Any) {
    def copyPos(tree: Any): global.Tree = {
      val t2 = t.asInstanceOf[global.Tree]
      t2.pos = tree.asInstanceOf[global.Tree].pos
      t2
    }
  }

  import global._

  override val phaseName = "sireum"
  override val runsRightAfter = Some("parser")
  override val runsAfter: List[String] = runsRightAfter.toList
  override val runsBefore: List[String] = List[String]("namer")

  def isDollar(t: Any): Boolean = t match {
    case q"$$" => true
    case _ => false
  }

  val unitCons = Literal(Constant(()))

  final class Transformer(unit: CompilationUnit,
                          var inTrait: Boolean) extends TypingTransformer(unit) {
    def sup(tree: global.Tree): global.Tree = super.transform(tree)

    def trans(tree: Any): global.Tree = transform(tree.asInstanceOf[global.Tree])

    def assign(tree: Any): global.Tree = tree match {
      case _: Literal => trans(tree.asInstanceOf[global.Tree])
      case _: Function => trans(tree.asInstanceOf[global.Tree])
      case _ => q"_assign(${trans(tree)})"
    }

    def pos(tree: Any): global.Position = tree.asInstanceOf[global.Tree].pos

    override def transform(tree: global.Tree): global.Tree = tree match {
      case Apply(Select(Apply(Ident(TermName("StringContext")), _), TermName("s")), _) => tree
      case tree@Apply(Select(Ident(TermName(f)), TermName("update")), l) if f == "up" || f == "pat" =>
        tree.copy(args = l.dropRight(1) ++ l.takeRight(1).map(transform)).copyPos(tree)
      case q"$_ trait $_[..$_] extends { ..$_ } with ..$_ { $_ => ..$_ }" =>
        val oldInTrait = inTrait
        inTrait = true
        val tree2 = sup(tree)
        inTrait = oldInTrait
        tree2.copyPos(tree)
      case tree: DefDef =>
        tree.rhs match {
          case rhs@Apply(sc@Select(Apply(Ident(TermName("StringContext")), _), TermName("l")), _) =>
            if (inTrait) tree.copy(rhs = EmptyTree).copyPos(tree)
            else tree.copy(rhs = rhs.copy(fun = sc.copy(name = TermName("lDef")).copyPos(sc)).copyPos(rhs)).copyPos(tree)
          case _ => sup(tree)
        }
      case tree@Select(_, TermName("hash")) =>
        Apply(Ident(TermName("_Z")).copyPos(tree), List(transform(tree))).copyPos(tree)
      case tree@Apply(sc@Select(Apply(Ident(TermName("StringContext")), _), TermName("l")), _) =>
        tree.copy(fun = sc.copy(name = TermName("lUnit")).copyPos(sc)).copyPos(tree)
      case Literal(Constant(n: Int)) => q"StringContext(${n.toString}).z()".copyPos(tree)
      case Literal(Constant(n: Long)) => q"StringContext(${n.toString}).z()".copyPos(tree)
      case tree: CaseDef =>
        val g = transform(tree.guard)
        val b = transform(tree.body)
        if ((g ne tree.guard) || (b ne tree.body))
          tree.copy(guard = transform(tree.guard), body = transform(tree.body)).copyPos(tree)
        else tree
      case tree: Apply if tree.args.nonEmpty =>
        var changed = false
        val r = tree.copy(fun = { val r = transform(tree.fun); changed ||= r ne tree.fun; r },
          args = for (arg <- tree.args) yield arg match {
            case arg: Literal => val r = transform(arg); changed ||= r ne arg; r
            case arg: Function => val r = transform(arg); changed ||= r ne arg; r
            case arg: AssignOrNamedArg =>
              changed = true
              arg.copy(rhs = assign(arg.rhs).copyPos(arg.rhs)).copyPos(arg)
            case _ => changed = true; assign(arg).copyPos(arg)
          })
        if (changed) r.copyPos(tree) else tree
      case tree: Assign =>
        tree.copy(rhs = assign(tree.rhs).copyPos(tree.rhs)).copyPos(tree)
      case q"$expr1(..$exprs2) = $expr" =>
        q"${trans(expr1)}(..${exprs2.map(trans)}) = ${assign(expr).copyPos(expr)}".copyPos(tree)
      case q"(..$exprs)" if exprs.size > 1 =>
        q"(..${exprs.map(e => assign(e).copyPos(e))})".copyPos(tree)
      case q"$mods val $pat: $tpt = $expr" =>
        if (!(expr == EmptyTree || isDollar(expr))) q"$mods var $pat: $tpt = ${assign(expr).copyPos(expr)}"
        else tree
      case q"$mods var $pat: $tpt = $expr" =>
        if (!(expr == EmptyTree || isDollar(expr))) q"$mods var $pat: $tpt = ${assign(expr).copyPos(expr)}"
        else tree
      case _ =>
        val tree2 = super.transform(tree)
//        if (tree2 ne tree) {
//          println(s"${tree2.pos.source.file.canonicalPath} [${tree2.pos.line}, ${tree2.pos.column}]: ${showCode(tree2)}")
//        }
        tree2
    }
  }

  def newPhase(prev: Phase): StdPhase = new StdPhase(prev) {
    def apply(unit: CompilationUnit): Unit = {
      var isSireum = unit.source.file.hasExtension("logika") || (unit.source.file.hasExtension("sc") &&
        (unit.body.children.headOption match {
          case Some(x) if x == q"import org.sireum._" || x == q"import org.sireum.logika._" => true
          case _ => false
        }))
      if (!isSireum) {
        val cs = unit.source.content
        val i = cs.indexOf('\n')
        val sb = new StringBuilder
        if (i >= 0) {
          for (j <- 0 until i) cs(j) match {
            case '\t' | '\r' | ' ' =>
            case c => sb.append(c)
          }
        }
        val firstLine = sb.toString
        isSireum = firstLine.contains("#Sireum")
      }
      if (isSireum) unit.body = new Transformer(unit, inTrait = false).transform(unit.body)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy