diff options
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala')
-rw-r--r-- | examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala new file mode 100644 index 0000000..264c548 --- /dev/null +++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala @@ -0,0 +1,420 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js tools ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ + + +package scala.scalajs.tools.javascript + +import scala.annotation.switch + +import scala.util.control.Breaks + +import java.io.Writer +import java.net.URI + +import scala.scalajs.ir +import ir.Position +import ir.Position.NoPosition +import ir.Printers.IndentationManager +import ir.Utils.escapeJS + +import Trees._ + +import scala.scalajs.tools.sourcemap.SourceMapWriter + +object Printers { + + class JSTreePrinter(protected val out: Writer) extends IndentationManager { + def printTopLevelTree(tree: Tree) { + tree match { + case Skip() => + // do not print anything + case Block(stats) => + for (stat <- stats) + printTopLevelTree(stat) + case _ => + printStat(tree) + if (shouldPrintSepAfterTree(tree)) + print(";") + println() + } + } + + protected def shouldPrintSepAfterTree(tree: Tree): Boolean = + !tree.isInstanceOf[DocComment] + + protected def printBlock(tree: Tree): Unit = { + val trees = tree match { + case Block(trees) => trees + case _ => List(tree) + } + print("{"); indent(); println() + printSeq(trees) { x => + printStat(x) + } { x => + if (shouldPrintSepAfterTree(x)) + print(";") + println() + } + undent(); println(); print("}") + } + + protected def printSig(args: List[ParamDef]): Unit = { + printRow(args, "(", ", ", ")") + print(" ") + } + + protected def printArgs(args: List[Tree]): Unit = { + printRow(args, "(", ", ", ")") + } + + def printStat(tree: Tree): Unit = + printTree(tree, isStat = true) + + def printTree(tree: Tree, isStat: Boolean): Unit = { + tree match { + case EmptyTree => + print("<empty>") + + // Comments + + case DocComment(text) => + val lines = text.split("\n").toList + if (lines.tail.isEmpty) { + print("/** ", lines.head, " */") + } else { + print("/** ", lines.head); println() + for (line <- lines.tail) { + print(" * ", line); println() + } + print(" */") + } + + // Definitions + + case VarDef(ident, mutable, rhs) => + print("var ", ident) + if (rhs != EmptyTree) + print(" = ", rhs) + + case ParamDef(ident, mutable) => + print(ident) + + // Control flow constructs + + case Skip() => + print("/*<skip>*/") + + case tree @ Block(trees) => + if (isStat) + printBlock(tree) + else + printRow(trees, "(", ", ", ")") + + case Labeled(label, body) => + print(label, ": ") + printBlock(body) + + case Assign(lhs, rhs) => + print(lhs, " = ", rhs) + + case Return(expr) => + print("return ", expr) + + case If(cond, thenp, elsep) => + if (isStat) { + print("if (", cond, ") ") + printBlock(thenp) + elsep match { + case Skip() => () + case If(_, _, _) => + print(" else ") + printTree(elsep, isStat) + case _ => + print(" else ") + printBlock(elsep) + } + } else { + print("(", cond, " ? ", thenp, " : ", elsep, ")") + } + + case While(cond, body, label) => + if (label.isDefined) + print(label.get, ": ") + print("while (", cond, ") ") + printBlock(body) + + case DoWhile(body, cond, label) => + if (label.isDefined) + print(label.get, ": ") + print("do ") + printBlock(body) + print(" while (", cond, ")") + + case Try(block, errVar, handler, finalizer) => + print("try ") + printBlock(block) + if (handler != EmptyTree) { + print(" catch (", errVar, ") ") + printBlock(handler) + } + if (finalizer != EmptyTree) { + print(" finally ") + printBlock(finalizer) + } + + case Throw(expr) => + print("throw ", expr) + + case Break(label) => + if (label.isEmpty) print("break") + else print("break ", label.get) + + case Continue(label) => + if (label.isEmpty) print("continue") + else print("continue ", label.get) + + case Switch(selector, cases, default) => + print("switch (", selector, ") ") + print("{"); indent + for ((value, body) <- cases) { + println() + print("case ", value, ":"); indent; println() + printStat(body) + print(";") + undent + } + if (default != EmptyTree) { + println() + print("default:"); indent; println() + printStat(default) + print(";") + undent + } + undent; println(); print("}") + + case Debugger() => + print("debugger") + + // Expressions + + case New(ctor, args) => + def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match { + case DotSelect(qual, _) => containsOnlySelectsFromAtom(qual) + case BracketSelect(qual, _) => containsOnlySelectsFromAtom(qual) + case VarRef(_, _) => true + case This() => true + case _ => false // in particular, Apply + } + if (containsOnlySelectsFromAtom(ctor)) + print("new ", ctor) + else + print("new (", ctor, ")") + printArgs(args) + + case DotSelect(qualifier, item) => + print(qualifier, ".", item) + + case BracketSelect(qualifier, item) => + print(qualifier, "[", item, "]") + + case Apply(fun, args) => + print(fun) + printArgs(args) + + case Delete(prop) => + print("delete ", prop) + + case UnaryOp("typeof", lhs) => + print("typeof(", lhs, ")") + + case UnaryOp(op, lhs) => + print("(", op, lhs, ")") + + case BinaryOp(op, lhs, rhs) => + print("(", lhs, " ", op, " ", rhs, ")") + + case ArrayConstr(items) => + printRow(items, "[", ", ", "]") + + case ObjectConstr(Nil) => + print("{}") + + case ObjectConstr(fields) => + print("{"); indent; println() + printSeq(fields) { + case (name, value) => print(name, ": ", value) + } { _ => + print(",") + println() + } + undent; println(); print("}") + + // Literals + + case Undefined() => + print("(void 0)") + + case Null() => + print("null") + + case BooleanLiteral(value) => + print(if (value) "true" else "false") + + case IntLiteral(value) => + if (value >= 0) + print(value) + else + print("(", value, ")") + + case DoubleLiteral(value) => + if (value == 0 && 1 / value < 0) + print("(-0)") + else if (value >= 0) + print(value) + else + print("(", value, ")") + + case StringLiteral(value) => + print("\"", escapeJS(value), "\"") + + // Atomic expressions + + case VarRef(ident, _) => + print(ident) + + case This() => + print("this") + + case Function(args, body) => + print("(function") + printSig(args) + printBlock(body) + print(")") + + case _ => + print(s"<error, elem of class ${tree.getClass()}>") + } + } + + protected def printIdent(ident: Ident): Unit = + printString(escapeJS(ident.name)) + + def printOne(arg: Any): Unit = arg match { + case tree: Tree => + printTree(tree, isStat = false) + case ident: Ident => + printIdent(ident) + case arg => + printString(if (arg == null) "null" else arg.toString) + } + + protected def printString(s: String): Unit = { + out.write(s) + } + + // Make it public + override def println(): Unit = super.println() + + def complete(): Unit = () + } + + class JSTreePrinterWithSourceMap(_out: Writer, + sourceMap: SourceMapWriter) extends JSTreePrinter(_out) { + + private var column = 0 + + override def printTree(tree: Tree, isStat: Boolean): Unit = { + val pos = tree.pos + if (pos.isDefined) + sourceMap.startNode(column, pos) + + super.printTree(tree, isStat) + + if (pos.isDefined) + sourceMap.endNode(column) + } + + override protected def printIdent(ident: Ident): Unit = { + if (ident.pos.isDefined) + sourceMap.startNode(column, ident.pos, ident.originalName) + super.printIdent(ident) + if (ident.pos.isDefined) + sourceMap.endNode(column) + } + + override def println(): Unit = { + super.println() + sourceMap.nextLine() + column = this.indentMargin + } + + override protected def printString(s: String): Unit = { + // assume no EOL char in s, and assume s only has ASCII characters + super.printString(s) + column += s.length() + } + + override def complete(): Unit = { + sourceMap.complete() + super.complete() + } + } + + /** Prints a tree to find original locations based on line numbers. + * @param untilLine last 0-based line the positions should be recorded for + */ + class ReverseSourceMapPrinter(untilLine: Int) + extends JSTreePrinter(ReverseSourceMapPrinter.NullWriter) { + + private val positions = Array.fill(untilLine+1)(NoPosition) + private var curLine = 0 + + private val doneBreak = new Breaks + + def apply(x: Int): Position = positions(x) + + def reverseSourceMap(tree: Tree): Unit = doneBreak.breakable { + printTopLevelTree(tree) + } + + override def printTree(tree: Tree, isStat: Boolean): Unit = { + if (positions(curLine).isEmpty) + positions(curLine) = tree.pos + + super.printTree(tree, isStat) + } + + override protected def printIdent(ident: Ident): Unit = { + if (positions(curLine).isEmpty) + positions(curLine) = ident.pos + + super.printIdent(ident) + } + + override def println(): Unit = { + super.println() + curLine += 1 + if (curLine > untilLine) + doneBreak.break() + } + + override protected def printString(s: String): Unit = { + // assume no EOL char in s, and assume s only has ASCII characters + // therefore, we fully ignore the string + } + } + + object ReverseSourceMapPrinter { + private object NullWriter extends Writer { + def close(): Unit = () + def flush(): Unit = () + def write(buf: Array[Char], off: Int, len: Int): Unit = () + } + } + +} |