treebrowsers.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 694 行 · 第 1/2 页
SCALA
694 行
/* NSC -- new Scala compiler * Copyright 2005-2007 LAMP/EPFL * @author Martin Odersky */// $Id: TreeBrowsers.scala 14477 2008-04-02 10:07:21Z rytz $package scala.tools.nsc.astimport java.awt.{List => awtList, _}import java.awt.event._import java.io.StringWriterimport javax.swing._import javax.swing.event.TreeModelListenerimport javax.swing.tree._import scala.concurrent.Lockimport scala.text._import symtab.Flags._import symtab.SymbolTable/** * Tree browsers can show the AST in a graphical and interactive * way, useful for debugging and understanding. * * @author Iulian Dragos * @version 1.0 */abstract class TreeBrowsers { val global: Global import global._ import nme.EMPTY def create(): SwingBrowser = new SwingBrowser(); /** Pseudo tree class, so that all JTree nodes are treated uniformly */ case class ProgramTree(units: List[UnitTree]) extends Tree { override def toString(): String = "Program" } /** Pseudo tree class, so that all JTree nodes are treated uniformly */ case class UnitTree(unit: CompilationUnit) extends Tree { override def toString(): String = unit.toString() } /** * Java Swing pretty printer for Scala abstract syntax trees. */ class SwingBrowser { def browse(t: Tree): Unit = { val tm = new ASTTreeModel(t) val frame = new BrowserFrame() frame.setTreeModel(tm) val lock = new Lock() frame.createFrame(lock) // wait for the frame to be closed lock.acquire } def browse(units: Iterator[CompilationUnit]): Unit = browse(units.toList) /** print the whole program */ def browse(units: List[CompilationUnit]): Unit = { var unitList: List[UnitTree] = Nil for (i <- units) unitList = UnitTree(i) :: unitList val tm = new ASTTreeModel(ProgramTree(unitList)) val frame = new BrowserFrame() frame.setTreeModel(tm) val lock = new Lock() frame.createFrame(lock) // wait for the frame to be closed lock.acquire } } /** Tree model for abstract syntax trees */ class ASTTreeModel(val program: Tree) extends TreeModel { var listeners: List[TreeModelListener] = Nil /** Add a listener to this tree */ def addTreeModelListener(l: TreeModelListener): Unit = listeners = l :: listeners /** Return the index'th child of parent */ def getChild(parent: Any, index: Int): AnyRef = packChildren(parent).drop(index).head /** Return the number of children this 'parent' has */ def getChildCount(parent: Any): Int = packChildren(parent).length /** Return the index of the given child */ def getIndexOfChild(parent: Any, child: Any): Int = packChildren(parent).dropWhile(c => c != child).length /** Return the root node */ def getRoot(): AnyRef = program /** Test whether the given node is a leaf */ def isLeaf(node: Any): Boolean = packChildren(node).length == 0 def removeTreeModelListener(l: TreeModelListener): Unit = listeners remove (x => x == l) /** we ignore this message for now */ def valueForPathChanged(path: TreePath, newValue: Any) = () /** * Return a list of children for the given node. */ def packChildren(t: Any): List[AnyRef] = TreeInfo.children(t.asInstanceOf[Tree]) } /** * A window that can host the Tree widget and provide methods for * displaying information * * @author Iulian Dragos * @version 1.0 */ class BrowserFrame { val frame = new JFrame("Scala AST") val topLeftPane = new JPanel(new BorderLayout()) val topRightPane = new JPanel(new BorderLayout()) val bottomPane = new JPanel(new BorderLayout()) var splitPane: JSplitPane = _ var treeModel: TreeModel = _ val textArea: JTextArea = new JTextArea(20, 50) val infoPanel = new TextInfoPanel() /** Create a frame that displays the AST. * * @param lock The lock is used in order to stop the compilation thread * until the user is done with the tree inspection. Swing creates its * own threads when the frame is packed, and therefore execution * would continue. However, this is not what we want, as the tree and * especially symbols/types would change while the window is visible. */ def createFrame(lock: Lock): Unit = { lock.acquire // keep the lock until the user closes the window frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE) frame.addWindowListener(new WindowAdapter() { /** Release the lock, so compilation may resume after the window is closed. */ override def windowClosed(e: WindowEvent): Unit = lock.release }); val tree = new JTree(treeModel) { /** Return the string for a tree node. */ override def convertValueToText(value: Any, sel: Boolean, exp: Boolean, leaf: Boolean, row: Int, hasFocus: Boolean) = { val (cls, name) = TreeInfo.treeName(value.asInstanceOf[Tree]) if (name != EMPTY) cls + "[" + name.toString() + "]" else cls } } tree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() { def valueChanged(e: javax.swing.event.TreeSelectionEvent): Unit = { textArea.setText(e.getPath().getLastPathComponent().toString()) infoPanel.update(e.getPath().getLastPathComponent()) } }) val topSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, topLeftPane, topRightPane) topSplitPane.setResizeWeight(0.5) topLeftPane.add(new JScrollPane(tree), BorderLayout.CENTER) topRightPane.add(new JScrollPane(infoPanel), BorderLayout.CENTER) bottomPane.add(new JScrollPane(textArea), BorderLayout.CENTER) textArea.setFont(new Font("monospaced", Font.PLAIN, 14)) textArea.setEditable(false) splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topSplitPane, bottomPane) frame.getContentPane().add(splitPane) frame.pack() frame.setVisible(true) } def setTreeModel(tm: TreeModel): Unit = treeModel = tm } /** * Present detailed information about the selected tree node. */ class TextInfoPanel extends JTextArea(30, 40) { setFont(new Font("monospaced", Font.PLAIN, 12)) def update(v: AnyRef): Unit = { val t: Tree = v.asInstanceOf[Tree] val str = new StringBuilder() var buf = new StringWriter() t match { case ProgramTree(_) => () case UnitTree(_) => () case _ => str.append("tree.pos: ").append(t.pos) str.append("\nSymbol: ").append(TreeInfo.symbolText(t)) str.append("\nSymbol info: \n") TreeInfo.symbolTypeDoc(t).format(getWidth() / getColumnWidth(), buf) str.append(buf.toString()) str.append("\nSymbol tpe: ") if (t.symbol ne null) { str.append(t.symbol.tpe).append("\n") buf = new StringWriter() TypePrinter.toDocument(t.symbol.tpe).format(getWidth() / getColumnWidth(), buf) str.append(buf.toString()) } str.append("\nSymbol Attributes: \n").append(TreeInfo.symbolAttributes(t)) str.append("\ntree.tpe: ") if (t.tpe ne null) { str.append(t.tpe.toString()).append("\n") buf = new StringWriter() TypePrinter.toDocument(t.tpe).format(getWidth() / getColumnWidth(), buf) str.append(buf.toString()) } } setText(str.toString()) } } /** Computes different information about a tree node. It * is used as central place to do all pattern matching against * Tree. */ object TreeInfo { /** Return the case class name and the Name, if the node defines one */ def treeName(t: Tree): (String, Name) = t match { case ProgramTree(units) => ("Program", EMPTY) case UnitTree(unit) => ("CompilationUnit", unit.toString()) case DocDef(comment, definition) => ("DocDef", EMPTY) case ClassDef(mods, name, tparams, impl) => ("ClassDef", name) case PackageDef(packaged, impl) => ("PackageDef", EMPTY) case ModuleDef(mods, name, impl) => ("ModuleDef", name) case ValDef(mods, name, tpe, rhs) => ("ValDef", name) case DefDef(mods, name, tparams, vparams, tpe, rhs) => ("DefDef", name) case TypeDef(mods, name, tparams, rhs) => ("TypeDef", name) case Import(expr, selectors) => ("Import", EMPTY) case CaseDef(pat, guard, body) => ("CaseDef", EMPTY) case Template(parents, self, body) => ("Template", EMPTY) case LabelDef(name, params, rhs) => ("LabelDef", name) case Block(stats, expr) => ("Block", EMPTY) case Sequence(trees) => ("Sequence", EMPTY) case Alternative(trees) => ("Alternative", EMPTY) case Bind(name, rhs) => ("Bind", name) case UnApply(fun, args) => ("UnApply", EMPTY) case Match(selector, cases) => ("Visitor", EMPTY) case Function(vparams, body) => ("Function", EMPTY) case Assign(lhs, rhs) => ("Assign", EMPTY) case If(cond, thenp, elsep) => ("If", EMPTY) case Return(expr) => ("Return", EMPTY) case Throw(expr) => ("Throw", EMPTY) case New(init) => ("New", EMPTY) case Typed(expr, tpe) => ("Typed", EMPTY) case TypeApply(fun, args) => ("TypeApply", EMPTY) case Apply(fun, args) => ("Apply", EMPTY) case ApplyDynamic(qual, args) => ("Apply", EMPTY) case Super(qualif, mix) => ("Super", qualif.toString() + ", mix: " + mix.toString()) case This(qualifier) => ("This", qualifier) case Select(qualifier, selector) => ("Select", selector) case Ident(name) =>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?