alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (SymbolTrackers.scala)

This example Scala source code file (SymbolTrackers.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

change, list, long, map, map, node, node, ordering, set, set, string, symbol, symbol, symboltracker

The Scala SymbolTrackers.scala source code

/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools.nsc
package symtab

import scala.collection.{ mutable, immutable }

/** Printing the symbol graph (for those symbols attached to an AST node)
 *  after each phase.
 */
trait SymbolTrackers {
  val global: Global
  import global._
  
  private implicit lazy val TreeOrdering: Ordering[Tree] =
    Ordering by (x => (x.shortClass, x.symbol))
  
  private implicit lazy val SymbolOrdering: Ordering[Symbol] =
    Ordering by (x => (x.kindString, x.name.toString))
    
  private implicit def toList[T: Ordering](xs: Set[T]): List[T] = xs.toList.sorted
  
  /** Reversing the direction of Symbol's owner arrow. */
  trait Hierarchy {
    def root: Symbol
    def children: List[Hierarchy]
    def flatten: Set[Symbol]
    def indentString(indent: String): String
    def symString(sym: Symbol): String

    override def toString() = indentString("")
  }
  case class Change(
    added: Set[Symbol],
    removed: Set[Symbol],
    trees: Map[Symbol, Set[Tree]],  // symbol -> trees which proudly display it
    owners: Map[Symbol, Symbol],    // symbol -> previous owner
    flags: Map[Symbol, Long]        // symbol -> previous flags
  )

  object SymbolTracker {
    def containsSymbol(t: Tree) = t.symbol != null && t.symbol != NoSymbol

    // This is noise reduction only.
    def dropSymbol(sym: Symbol) = sym.ownerChain exists (_ hasFlag Flags.SPECIALIZED)
    
    def symbolSnapshot(unit: CompilationUnit): Map[Symbol, Set[Tree]] = {
      if (unit.body == null) Map()
      else unit.body filter containsSymbol groupBy (_.symbol) mapValues (_.toSet) toMap
    }
    def apply(unit: CompilationUnit) = new SymbolTracker(
      () => symbolSnapshot(unit) filterNot { case (k, _) => dropSymbol(k) }
    )
  }
  
  class SymbolTracker(snapshotFn: () => Map[Symbol, Set[Tree]]) {
    def flagsMask: Long = Flags.PrintableFlags
    
    private var currentMap = Map[Symbol, Set[Tree]]()
    private var prevMap    = Map[Symbol, Set[Tree]]()
    private def current    = currentMap.keySet
    private def prev       = prevMap.keySet
    
    private var history    = List[Change](Change(Set(), Set(), Map(), Map(), Map()))
    private var prevFlags  = Map[Symbol, Long]()
    private var prevOwners = Map[Symbol, Symbol]()

    private def changed                    = history.head
    private def isAdded(sym: Symbol)       = changed added sym
    private def isOwnerChange(sym: Symbol) = changed.owners contains sym
    private def isFlagsChange(sym: Symbol) = changed.flags contains sym

    private implicit def NodeOrdering: Ordering[Node] = Ordering by (_.root)    
    private def ownersString(sym: Symbol, num: Int) = sym.ownerChain drop 1 take num mkString " -> "
    
    object Node {
      def nodes(syms: Set[Symbol]): List[Node] = {
        def descendents(s: Symbol) = (syms - s) filter (_ hasTransOwner s)
        def rooted(root: Symbol)   = new Node(root, nodes(descendents(root)))
        
        val roots    = syms filterNot (_.ownerChain drop 1 exists syms)
        val deep     = roots map rooted
        val deepSyms = deep flatMap (_.flatten)
        
        deep ++ (syms filterNot deepSyms map (x => Node(x)))
      }

      def apply(sym: Symbol): Node = new Node(sym, Nil)
      def apply(syms: Set[Symbol]): Node = nodes(syms) match {
        case List(x)  => x
        case xs       => new Node(NoSymbol, xs)
      }
    }
    class Node(val root: Symbol, val children: List[Hierarchy]) extends Hierarchy {
      def masked = root.flags & flagsMask
      def indicatorString =
        if (isAdded(root)) "* "
        else List(
          if (isFlagsChange(root)) "F" else "",
          if (isOwnerChange(root)) "O" else "",
          "  "
        ).mkString take 2
      
      def changedOwnerString = changed.owners get root match {
        case Some(prev) => " [Owner was " + prev + ", now " + root.owner + "]"
        case _          => ""
      }
      def flagSummaryString = changed.flags get root match {
        case Some(oldFlags) =>
          val added   = masked & ~oldFlags
          val removed = oldFlags & ~masked
          val steady  = masked & ~(added | removed)
          val all     = masked | oldFlags
          val strs    = 0 to 63 map { bit =>
            val flag = 1L << bit
            val prefix = (
              if ((added & flag) != 0L) "+"
              else if ((removed & flag) != 0L) "-"
              else ""
            )
            if ((all & flag) == 0L) ""
            else prefix + Flags.flagToString(flag)
          }
          
          " " + strs.filterNot(_ == "").mkString("[", " ", "]")
        case _ =>
          if (masked == 0L) ""
          else " (" + Flags.flagsToString(masked) + ")"
      }
      def symString(sym: Symbol) = (
        if (settings.debug.value && sym.hasRawInfo && sym.rawInfo.isComplete) {
          val s = sym.defString take 240
          if (s.length == 240) s + "..." else s
        }
        else sym + changedOwnerString + flagSummaryString
      )

      def flatten = children.foldLeft(Set(root))(_ ++ _.flatten)
      def indentString(indent: String): String = {
        if (root == NoSymbol)
          children map (c => c.indentString(indent)) mkString "\n"
        else {
          indicatorString + indent + symString(root) + (
            if (children.isEmpty) ""
            else children map (c => c.indentString(indent + "    ")) mkString ("\n", "\n", "")
          )
        }
      }
    }

    def snapshot(): Unit = {
      currentMap = snapshotFn()

      val added   = current filterNot prev
      val removed = prev filterNot current
      val steady  = prev intersect current
      
      def changedOwner(sym: Symbol) = prevOwners get sym filter (_ != sym.owner)
      def changedFlags(sym: Symbol) = prevFlags get sym filter (_ != (sym.flags & flagsMask))

      val owners = ({
        for (sym <- steady; old <- changedOwner(sym)) yield
          (sym, old)
      }).toMap
      val flags = ({
        for (sym <- steady; old <- changedFlags(sym)) yield
          (sym, old)
      }).toMap

      val change = Change(added, removed, prevMap, owners, flags)

      prevMap    = currentMap
      prevOwners = current map (s => (s, s.owner)) toMap;
      prevFlags  = current map (s => (s, (s.flags & flagsMask))) toMap;
      history    = change :: history
    }
    def show(label: String): String = {
      val hierarchy = Node(current)
      val Change(added, removed, symMap, owners, flags) = history.head
      def detailString(sym: Symbol) = {
        val ownerString = sym.ownerChain splitAt 3 match {
          case (front, back) =>
            val xs = if (back.isEmpty) front else front :+ "..."
            xs mkString " -> "
        }
        val treeStrings = symMap(sym) map { t =>
          "%10s: %s".format(t.shortClass, t)
        }
        
        ownerString :: treeStrings mkString "\n"
      }
      def removedString = (removed: List[Symbol]).zipWithIndex map {
        case (t, i) => "(%2s) ".format(i + 1) + detailString(t)
      } mkString "\n"
        
      "" + hierarchy + (
        if (removed.isEmpty) ""
        else "\n\n!!! " + label + ", " + removed.size + " symbols vanished:\n" + removedString
      )
    }
  }
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala SymbolTrackers.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.