From 715262fcfcefbb75788f287444c4791180e7eb67 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Jan 2011 16:20:51 +0000 Subject: Replay now enabled for presentation compiler. --- .../scala/tools/nsc/interactive/Global.scala | 30 +- .../scala/tools/nsc/interactive/Picklers.scala | 17 +- .../scala/tools/nsc/interactive/REPL.scala | 1 + src/compiler/scala/tools/nsc/io/JSON.scala | 512 --------------------- src/compiler/scala/tools/nsc/io/Lexer.scala | 203 ++++++++ src/compiler/scala/tools/nsc/io/Pickler.scala | 320 +++++++++++++ src/compiler/scala/tools/nsc/io/PrettyWriter.scala | 41 ++ src/compiler/scala/tools/nsc/io/Replay.scala | 74 +++ src/compiler/scala/tools/nsc/util/Pickler.scala | 316 ------------- src/compiler/scala/tools/nsc/util/Replayer.scala | 59 --- 10 files changed, 666 insertions(+), 907 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/io/JSON.scala create mode 100644 src/compiler/scala/tools/nsc/io/Lexer.scala create mode 100644 src/compiler/scala/tools/nsc/io/Pickler.scala create mode 100644 src/compiler/scala/tools/nsc/io/PrettyWriter.scala create mode 100644 src/compiler/scala/tools/nsc/io/Replay.scala delete mode 100644 src/compiler/scala/tools/nsc/util/Pickler.scala delete mode 100644 src/compiler/scala/tools/nsc/util/Replayer.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 48ddf7d5f6..b3a56278c4 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -7,13 +7,12 @@ import scala.collection.mutable import mutable.{LinkedHashMap, SynchronizedMap,LinkedHashSet, SynchronizedSet} import scala.concurrent.SyncVar import scala.util.control.ControlThrowable -import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.util.{SourceFile, Position, RangePosition, NoPosition, WorkScheduler, LogReplay, Logger, Replayer, NullLogger} +import scala.tools.nsc.io.{AbstractFile, LogReplay, Logger, NullLogger, Replayer} +import scala.tools.nsc.util.{SourceFile, Position, RangePosition, NoPosition, WorkScheduler} import scala.tools.nsc.reporters._ import scala.tools.nsc.symtab._ import scala.tools.nsc.ast._ -import scala.tools.nsc.io.JSON._ -import scala.tools.nsc.util.Pickler._ +import scala.tools.nsc.io.Pickler._ /** The main class of the presentation compiler in an interactive environment such as an IDE */ @@ -34,14 +33,13 @@ self => private def replayName = settings.YpresentationReplay.value private def logName = settings.YpresentationLog.value - lazy val log = + val log = if (replayName != "") new Replayer(new FileReader(replayName)) else if (logName != "") new Logger(new FileWriter(logName)) else NullLogger -// import log.logreplay + import log.logreplay - def logreplay[T](label: String, x: T): T = x /** Print msg only when debugIDE is true. */ @inline final def debugLog(msg: => String) = @@ -120,6 +118,7 @@ self => } catch { case ex : Throwable => if (context.unit != null) integrateNew() + log.flush() throw ex } if (typerRun == currentTyperRun) @@ -201,12 +200,13 @@ self => } def nodeWithWork(): Option[Int] = { - nodesSeen += 1 if (scheduler.moreWork || pendingResponse.isCancelled) Some(nodesSeen) else None } + nodesSeen += 1 logreplay("atnode", nodeWithWork()) match { case Some(id) => + debugLog("some work at node "+id+" current = "+nodesSeen) assert(id >= nodesSeen) moreWorkAtNode = id case None => @@ -224,7 +224,7 @@ self => minRunId = currentRunId if (outOfDate) throw ex else outOfDate = true - case Some(ex: Throwable) => throw ex + case Some(ex: Throwable) => log.flush(); throw ex case _ => } logreplay("workitem", scheduler.nextWorkItem()) match { @@ -237,6 +237,7 @@ self => debugLog("quitting work item: "+action) } case None => + debugLog("no work found") } } } @@ -288,10 +289,12 @@ self => */ def newRunnerThread: Thread = new Thread("Scala Presentation Compiler V"+threadId) { override def run() { + debugLog("starting new runner thread") try { while (true) { - scheduler.waitForMoreWork() + logreplay("wait for more work", { scheduler.waitForMoreWork(); true }) pollForWork() + debugLog("got more work") while (outOfDate) { try { backgroundCompile() @@ -299,12 +302,15 @@ self => } catch { case FreshRunReq => } + log.flush() } } } catch { - case ShutdownReq => - ; + case ex @ ShutdownReq => + debugLog("exiting presentation compiler") + log.close() case ex => + log.flush() outOfDate = false compileRunner = newRunnerThread compileRunner.start() diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala index 79b1921642..45134fca6d 100644 --- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala +++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala @@ -4,16 +4,17 @@ package interactive import util.{SourceFile, BatchSourceFile} import io.{AbstractFile, PlainFile} -import util.{Pickler, CondPickler, Position, RangePosition, NoPosition, OffsetPosition, TransparentPosition} -import util.Pickler._ +import util.{Position, RangePosition, NoPosition, OffsetPosition, TransparentPosition} +import io.{Pickler, CondPickler} +import io.Pickler._ import collection.mutable trait Picklers { self: Global => - lazy val freshRunReq = obj(FreshRunReq) - lazy val shutdownReq = obj(ShutdownReq) + lazy val freshRunReq = singletonPickler(FreshRunReq) + lazy val shutdownReq = singletonPickler(ShutdownReq) - def defaultThrowable[T <: Throwable]: CondPickler[T] = anyJavaInstance[T] cond { _ => true } + def defaultThrowable[T <: Throwable]: CondPickler[T] = javaInstancePickler[T] cond { _ => true } implicit lazy val throwable: Pickler[Throwable] = freshRunReq | shutdownReq | defaultThrowable @@ -27,7 +28,7 @@ trait Picklers { self: Global => override def default(key: AbstractFile) = Array() } - type Diff = (Int /*start*/, Int /*end*/, Array[Char] /*replacement*/) + type Diff = (Int /*start*/, Int /*end*/, String /*replacement*/) def delta(f: AbstractFile, cs: Array[Char]): Diff = { val bs = sourceFilesSeen(f) @@ -37,7 +38,7 @@ trait Picklers { self: Global => var end2 = cs.length while (end > start && end2 > start && bs(end - 1) == cs(end2 - 1)) { end -= 1; end2 -= 1 } sourceFilesSeen(f) = cs - (start, end, cs.slice(start, end2)) + (start, end, cs.slice(start, end2).mkString("")) } def patch(f: AbstractFile, d: Diff): Array[Char] = { @@ -69,7 +70,7 @@ trait Picklers { self: Global => .wrapped { case source ~ start ~ point ~ end => new TransparentPosition(source, start, point, end) } { p => p.source ~ p.start ~ p.point ~ p.end } .asClass (classOf[TransparentPosition]) - lazy val noPosition = obj(NoPosition) + lazy val noPosition = singletonPickler(NoPosition) implicit lazy val position: Pickler[Position] = transparentPosition | rangePosition | offsetPosition | noPosition diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala index 834747c547..47807ccdac 100644 --- a/src/compiler/scala/tools/nsc/interactive/REPL.scala +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -124,6 +124,7 @@ object REPL { case List("complete", file, off1) => doComplete(makePos(file, off1, off1)) case List("quit") => + comp.askShutdown() system.exit(1) case _ => println("unrecongized command") diff --git a/src/compiler/scala/tools/nsc/io/JSON.scala b/src/compiler/scala/tools/nsc/io/JSON.scala deleted file mode 100644 index c4584f762a..0000000000 --- a/src/compiler/scala/tools/nsc/io/JSON.scala +++ /dev/null @@ -1,512 +0,0 @@ -package scala.tools.nsc.io - -import java.io.{Reader, Writer, StringReader, StringWriter} -import scala.collection.mutable.{Buffer, ArrayBuffer} -import scala.math.BigInt - -object JSON { - - class MalformedInput(val rdr: JReader, val msg: String) extends Exception("Malformed JSON input at "+rdr.tokenPos+": "+msg) - - class PrettyWriter(wr: Writer) extends Writer { - protected val indentStep = " " - private var indent = 0 - private def newLine() { - wr.write('\n') - wr.write(indentStep * indent) - } - def close() = wr.close() - def flush() = wr.flush() - def write(str: Array[Char], off: Int, len: Int): Unit = { - if (off < str.length && off < len) { - str(off) match { - case '{' | '[' => - indent += 1 - wr.write(str(off)) - newLine() - wr.write(str, off + 1, len - 1) - case '}' | ']' => - wr.write(str, off, len) - indent -= 1 - case ',' => - wr.write(',') - newLine() - wr.write(str, off + 1, len - 1) - case ':' => - wr.write(':') - wr.write(' ') - wr.write(str, off + 1, len - 1) - case _ => - wr.write(str, off, len) - } - } else { - wr.write(str, off, len) - } - } - override def toString = wr.toString - } - - object JReader { - - type Token = Int - - val STRING = 1 - val INTEGER = 2 - val FLOAT = 3 - val LBRACE = 4 - val RBRACE = 5 - val LBRACKET = 6 - val RBRACKET = 7 - val COMMA = 8 - val COLON = 9 - val TRUE = 10 - val FALSE = 11 - val NULL = 12 - val EOF = 13 - - def show(token: Token) = token match { - case STRING => "string literal" - case INTEGER => "integer literal" - case FLOAT => "floating point literal" - case LBRACE => "'{'" - case RBRACE => "'}'" - case LBRACKET => "'['" - case RBRACKET => "']'" - case COMMA => "','" - case COLON => "':'" - case TRUE => "'true'" - case FALSE => "'false'" - case NULL => "'null'" - case EOF => "" - } - - private def toUDigit(ch: Int): Char = { - val d = ch & 0xF - (if (d < 10) d + '0' else d - 10 + 'A').toChar - } - - private def addToStr(buf: StringBuilder, ch: Char) { - ch match { - case '"' => buf ++= "\\\"" - case '\b' => buf ++= "\\b" - case '\f' => buf ++= "\\f" - case '\n' => buf ++= "\\n" - case '\r' => buf ++= "\\r" - case '\t' => buf ++= "\\t" - case '\\' => buf ++= "\\\\" - case _ => - if (' ' <= ch && ch < 128) buf += ch - else buf ++= "\\u" += toUDigit(ch >>> 12) += toUDigit(ch >>> 8) += toUDigit(ch >>> 4) += toUDigit(ch) - } - } - - def quoted(str: String): String = { - val buf = new StringBuilder += '\"' - str foreach (addToStr(buf, _)) - buf += '\"' - buf.toString - } - - private val BUF_SIZE = 2 << 16 - } - - import JReader._ - - abstract class JIterator[T] extends BufferedIterator[T] { - def reader: Option[JReader] - } - - class JReader(rd: Reader) { - - var ch: Char = 0 - var pos: Long = 0 - var token: Token = 0 - var tokenPos: Long = 0 - private var atEOF: Boolean = false - - private val buf = new Array[Char](BUF_SIZE) - private var nread: Int = 0 - private var bp = 0 - - def nextChar() { - if (!atEOF) { - if (bp == nread) { - nread = rd.read(buf) - bp = 0 - if (nread <= 0) { ch = 0; atEOF = true; return } - } - ch = buf(bp) - bp += 1 - pos += 1 - } - } - - def acceptChar(c: Char) = if (ch == c) nextChar() else error("'"+c+"' expected") - - val sb = new StringBuilder - - def putChar() { - sb += ch; nextChar() - } - - def putAcceptString(str: String) { - str foreach acceptChar - sb ++= str - } - - def nextToken() { - sb.clear() - while (!atEOF && ch <= ' ') nextChar() - tokenPos = pos - 1 - if (atEOF) token = EOF - else ch match { - case '{' => putChar(); token = LBRACE - case '}' => putChar(); token = RBRACE - case '[' => putChar(); token = LBRACKET - case ']' => putChar(); token = RBRACKET - case ',' => putChar(); token = COMMA - case ':' => putChar(); token = COLON - case 't' => putAcceptString("true"); token = TRUE - case 'f' => putAcceptString("false"); token = FALSE - case 'n' => putAcceptString("null"); token = NULL - case '"' => getString() - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => getNumber() - case _ => error("illegal string literal character: '"+ch+"'") - } - println("["+showCurrent+"]") - } - - def getString() { - def udigit() = { - nextChar() - if ('0' <= ch && ch <= '9') ch - '9' - else if ('A' <= ch && ch <= 'F') ch - 'A' + 10 - else if ('a' <= ch && ch <= 'f') ch - 'a' + 10 - else error("illegal unicode escape character: '"+ch+"'") - } - val delim = ch - nextChar() - while (ch != delim && ch >= ' ') { - if (ch == '\\') { - nextChar() - ch match { - case '\'' => sb += '\'' - case '"' => sb += '"' - case '\\' => sb += '\\' - case '/' => sb += '/' - case 'b' => sb += '\b' - case 'f' => sb += '\f' - case 'n' => sb += '\n' - case 'r' => sb += '\r' - case 't' => sb += '\t' - case 'u' => sb += (udigit() << 12 | udigit() << 8 | udigit() << 4 | udigit()).toChar - case _ => error("illegal escape character: '"+ch+"'") - } - nextChar() - } else { - putChar() - } - } - acceptChar(delim) - token = STRING - } - - def getNumber() { - def digit() = - if ('0' <= ch && ch <= '9') putChar() - else error(" expected") - def digits() = - do { digit() } while ('0' <= ch && ch <= '9') - token = INTEGER - if (ch == '-') putChar() - if (ch == '0') digit() - else digits() - if (ch == '.') { - token = FLOAT - putChar() - digits() - } - if (ch == 'e' || ch == 'E') { - token = FLOAT - putChar() - if (ch == '+' || ch == '-') putChar() - digits() - } - } - - def accept(t: Token) = - if (token == t) nextToken() - else error(show(t)+" expected, but "+showCurrent+" found") - - def headValue(): JValue = token match { - case STRING => JString(tokenStr) - case INTEGER => - try { - JLong(tokenStr.toLong) - } catch { - case ex: NumberFormatException => JInteger(BigInt(tokenStr)) - } - case FLOAT => JDouble(tokenStr.toDouble) - case LBRACE => new JObject(this) - case LBRACKET => new JArray(this) - case TRUE => JTrue - case FALSE => JFalse - case NULL => JNull - case t => error("unexpected: "+showCurrent) - } - - def nextValue(): JValue = { - val result = headValue() - nextToken() - result - } - - def iterator: JIterator[JValue] = - new JTopLevel(this).inputIterator - - def tokenStr = sb.toString - - def showCurrent() = token match { - case STRING => "string literal "+quoted(tokenStr) - case INTEGER => "integer literal "+tokenStr - case FLOAT => "floating point literal "+tokenStr - case _ => show(token) - } - - def error(msg: String) = throw new MalformedInput(this, msg) - - nextChar() - nextToken() - } - - abstract class JElement { - def isComplete: Boolean - def doneReading() - def readFully(): this.type - def write(wr: Writer) - } - - case class J_: (label: String, value: JValue) extends JElement { - def isComplete = value.isComplete - def doneReading() = value.doneReading() - def readFully() = { value.readFully(); this } - def write(wr: Writer) { wr.write(quoted(label)); wr.write(":"); value.write(wr) } - override def toString = label+" J_: "+value - } - - abstract class JValue extends JElement { - def J_: (label: String) = new J_: (label, this) - } - - abstract class JContainer[T <: JElement](closingToken: Token, - elems: T*) extends JValue { - protected var input: Option[JReader] = None - private var prevInput = input - - val contents: Buffer[T] = new ArrayBuffer[T] ++= elems - - protected def readElement(rdr: JReader): T - - val inputIterator = new JIterator[T] { - var last: Option[T] = None - var lastIsNext: Boolean = false - def hasNext: Boolean = - lastIsNext || { - input match { - case None => false - case Some(rdr) => - closeLast() - rdr.token != closingToken - } - } - def head: T = - if (lastIsNext) last.get - else input match { - case None => Iterator.empty.next - case Some(rdr) => - closeLast() - if (last.isDefined) rdr.accept(COMMA) - val x = readElement(rdr) - last = Some(x) - lastIsNext = true - x - } - def next(): T = { - val result = head - lastIsNext = false - println("next: "+result) - result - } - def closeLast(): Unit = last match { - case Some(elem) => elem.doneReading() - case _ => - } - def reader = input orElse prevInput - } - - def isComplete: Boolean = !inputIterator.hasNext - - def doneReading() { - require(isComplete) - input match { - case None => // already closed - case Some(rdr) => - inputIterator.closeLast() - println("done reading: "+rdr.showCurrent) - rdr.accept(closingToken) - prevInput = input - input = None - } - } - - def readFully() = { - contents ++= inputIterator map (_.readFully()) - doneReading() - this - } - - def skip() { - inputIterator foreach { _ => () } - doneReading() - } - - protected def writeContents(wr: Writer) { - var first = true - for (elem <- contents) { - if (first) first = false else wr.write(",") - elem.write(wr) - } - } - - } - - class JObject(elems: J_: *) extends JContainer(RBRACE, elems: _*) { - def this(in: JReader) = { this(); input = Some(in) } - protected def readElement(rdr: JReader): J_: = { - if (rdr.token == STRING) { - val label = rdr.tokenStr - rdr.nextToken(); rdr.accept(COLON) - label J_: rdr.nextValue() - } else { - rdr.error("string literal expected, but "+rdr.tokenStr+" found") - } - } - - def write(wr: Writer) { - wr.write("{") - writeContents(wr) - wr.write("}") - } - - override def toString = contents.mkString("{", ",", "}") - override def hashCode = contents.hashCode + 17 - override def equals(other: Any) = other match { - case that: JObject => this.contents == that.contents - case _ => false - } - } - - class JArray(elems: JValue*) extends JContainer(RBRACKET, elems: _*) { - def this(in: JReader) = { this(); input = Some(in) } - protected val closingToken = RBRACKET - protected def readElement(rdr: JReader): JValue = rdr.nextValue() - - def write(wr: Writer) { - wr.write("[") - writeContents(wr) - wr.write("]") - } - - override def toString = contents.mkString("[", ",", "]") - override def hashCode = contents.hashCode + 19 - override def equals(other: Any) = other match { - case that: JArray => this.contents == that.contents - case _ => false - } - } - - class JTopLevel(elems: JValue*) extends JContainer(EOF, elems: _*) { - def this(in: JReader) = { this(); input = Some(in) } - protected val closingToken = EOF - protected def readElement(rdr: JReader): JValue = rdr.nextValue() - - def write(wr: Writer) { - writeContents(wr) - } - - override def toString = contents.mkString(",") - override def hashCode = contents.hashCode + 23 - override def equals(other: Any) = other match { - case that: JTopLevel => this.contents == that.contents - case _ => false - } - } - - object JObject { - def apply(elems: J_: *) = new JObject(elems: _*) - def unapplySeq(x: JObject): Some[Seq[J_:]] = Some(x.contents) - } - - object JArray { - def apply(elems: JValue*) = new JArray(elems: _*) - def unapplySeq(x: JArray): Some[Seq[JValue]] = Some(x.contents) - } - - abstract class JAtom extends JValue { - def isComplete = true - def doneReading() {} - def readFully() = this - } - - case class JInteger(value: BigInt) extends JAtom { - def write(wr: Writer) { wr.write(value.toString) } - } - - case class JLong(value: Long) extends JAtom { - def write(wr: Writer) { wr.write(value.toString) } - } - - case class JDouble(value: Double) extends JAtom { - def write(wr: Writer) { wr.write(value.toString) } - } - - case class JString(value: String) extends JAtom { - def write(wr: Writer) { wr.write(quoted(value)) } - } - - case object JTrue extends JAtom { - def write(wr: Writer) { wr.write("true") } - } - - case object JFalse extends JAtom { - def write(wr: Writer) { wr.write("false") } - } - - case object JNull extends JAtom { - def write(wr: Writer) { wr.write("null") } - } -} - -object Test extends Application { - import JSON._ - val obj = JObject( - "a" J_: JArray(JLong(1), JDouble(2.0), JInteger(BigInt("12345678901234567890"))), - "b" J_: JString("I am a \t String \n on two lines"), - "c" J_: JObject("t" J_: JTrue, "f" J_: JFalse), - "d" J_: JNull, - "e" J_: JArray(), - "f" J_: JObject() - ) - val sw = new PrettyWriter(new StringWriter()) - obj.write(sw) - sw.close() - val s = sw.toString - println("written: "+s) - var sr = new JReader(new StringReader(s)) - val it = sr.iterator - val r = it.head.readFully() - it.next() - println("read: "+r) - assert(!it.hasNext, it.next()+"/"+sr.showCurrent+"/"+r) - assert(obj == r) -} diff --git a/src/compiler/scala/tools/nsc/io/Lexer.scala b/src/compiler/scala/tools/nsc/io/Lexer.scala new file mode 100644 index 0000000000..dd0e826351 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/Lexer.scala @@ -0,0 +1,203 @@ +package scala.tools.nsc.io + +import java.io.{Reader, Writer, StringReader, StringWriter} +import scala.collection.mutable.{Buffer, ArrayBuffer} +import scala.math.BigInt + +object Lexer { + + class MalformedInput(val rdr: Lexer, val msg: String) extends Exception("Malformed JSON input at "+rdr.tokenPos+": "+msg) + + class Token(val str: String) { + override def toString = str + } + + case class Delim(char: Char) extends Token("'"+char.toString+"'") + case class IntLit(override val str: String) extends Token(str) + case class FloatLit(override val str: String) extends Token(str) + case class StringLit(override val str: String) extends Token(str) { + override def toString = quoted(str) + } + + val TrueLit = new Token("true") + val FalseLit = new Token("false") + val NullLit = new Token("null") + val LParen = new Delim('(') + val RParen = new Delim(')') + val LBrace = new Delim('{') + val RBrace = new Delim('}') + val LBracket = new Delim('[') + val RBracket = new Delim(']') + val Comma = new Delim(',') + val Colon = new Delim(':') + val EOF = new Token("") + + private def toUDigit(ch: Int): Char = { + val d = ch & 0xF + (if (d < 10) d + '0' else d - 10 + 'A').toChar + } + + private def addToStr(buf: StringBuilder, ch: Char) { + ch match { + case '"' => buf ++= "\\\"" + case '\b' => buf ++= "\\b" + case '\f' => buf ++= "\\f" + case '\n' => buf ++= "\\n" + case '\r' => buf ++= "\\r" + case '\t' => buf ++= "\\t" + case '\\' => buf ++= "\\\\" + case _ => + if (' ' <= ch && ch < 128) buf += ch + else buf ++= "\\u" += toUDigit(ch >>> 12) += toUDigit(ch >>> 8) += toUDigit(ch >>> 4) += toUDigit(ch) + } + } + + def quoted(str: String): String = { + val buf = new StringBuilder += '\"' + str foreach (addToStr(buf, _)) + buf += '\"' + buf.toString + } + + private val BUF_SIZE = 2 << 16 +} + +import Lexer._ + +class Lexer(rd: Reader) { + + var ch: Char = 0 + var pos: Long = 0 + var token: Token = _ + var tokenPos: Long = 0 + + private var atEOF: Boolean = false + private val buf = new Array[Char](BUF_SIZE) + private var nread: Int = 0 + private var bp = 0 + + def nextChar() { + assert(!atEOF) + if (bp == nread) { + nread = rd.read(buf) + bp = 0 + if (nread <= 0) { ch = 0; atEOF = true; return } + } + ch = buf(bp) + bp += 1 + pos += 1 + } + + def acceptChar(c: Char) = if (ch == c) nextChar() else error("'"+c+"' expected") + + val sb = new StringBuilder + + def putChar() { + sb += ch; nextChar() + } + + def putAcceptString(str: String) { + str foreach acceptChar + sb ++= str + } + + def nextToken() { + sb.clear() + while (!atEOF && ch <= ' ') nextChar() + tokenPos = pos - 1 + if (atEOF) token = EOF + else ch match { + case '(' => putChar(); token = LParen + case ')' => putChar(); token = RParen + case '{' => putChar(); token = LBrace + case '}' => putChar(); token = RBrace + case '[' => putChar(); token = LBracket + case ']' => putChar(); token = RBracket + case ',' => putChar(); token = Comma + case ':' => putChar(); token = Colon + case 't' => putAcceptString("true"); token = TrueLit + case 'f' => putAcceptString("false"); token = FalseLit + case 'n' => putAcceptString("null"); token = NullLit + case '"' => getString() + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => getNumber() + case _ => error("illegal string literal character: '"+ch+"'") + } + println("["+token+"]") + } + + def getString() { + def udigit() = { + nextChar() + if ('0' <= ch && ch <= '9') ch - '9' + else if ('A' <= ch && ch <= 'F') ch - 'A' + 10 + else if ('a' <= ch && ch <= 'f') ch - 'a' + 10 + else error("illegal unicode escape character: '"+ch+"'") + } + val delim = ch + nextChar() + while (ch != delim && ch >= ' ') { + if (ch == '\\') { + nextChar() + ch match { + case '\'' => sb += '\'' + case '"' => sb += '"' + case '\\' => sb += '\\' + case '/' => sb += '/' + case 'b' => sb += '\b' + case 'f' => sb += '\f' + case 'n' => sb += '\n' + case 'r' => sb += '\r' + case 't' => sb += '\t' + case 'u' => sb += (udigit() << 12 | udigit() << 8 | udigit() << 4 | udigit()).toChar + case _ => error("illegal escape character: '"+ch+"'") + } + nextChar() + } else { + putChar() + } + } + acceptChar(delim) + token = StringLit(sb.toString) + } + + def getNumber() { + def digit() = + if ('0' <= ch && ch <= '9') putChar() + else error(" expected") + def digits() = + do { digit() } while ('0' <= ch && ch <= '9') + var isFloating = false + if (ch == '-') putChar() + if (ch == '0') digit() + else digits() + if (ch == '.') { + isFloating = true + putChar() + digits() + } + if (ch == 'e' || ch == 'E') { + isFloating = true + putChar() + if (ch == '+' || ch == '-') putChar() + digits() + } + token = if (isFloating) FloatLit(sb.toString) else IntLit(sb.toString) + } + + def accept(t: Token) { + if (token == t) nextToken() + else error(t+" expected, but "+token+" found") + } + + def accept(ch: Char) { + token match { + case Delim(`ch`) => nextToken() + case _ => accept(Delim(ch)) + } + } + + def error(msg: String) = throw new MalformedInput(this, msg) + + nextChar() + nextToken() +} diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala new file mode 100644 index 0000000000..e57e513132 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/Pickler.scala @@ -0,0 +1,320 @@ +package scala.tools.nsc.io + +import annotation.unchecked +import Lexer._ +import java.io.Writer + +abstract class Pickler[T] { + + import Pickler._ + + def pickle(wr: Writer, x: T) + def unpickle(rd: Lexer): Unpickled[T] + + def ~ [U] (that: => Pickler[U]): Pickler[T ~ U] = seqPickler(this, that) + + def labelled(label: String): Pickler[T] = labelledPickler(label, this) + def wrapped [U] (in: T => U)(out: U => T): Pickler[U] = wrappedPickler(this)(in)(out) + def orNull(implicit fromNull: Null <:< T): Pickler[T] = nullablePickler(this) + def cond(p: Any => Boolean): CondPickler[T] = conditionalPickler(this, p) + def asClass[U <: T](c: Class[U]): CondPickler[T] = this.labelled(c.getName).cond(c isInstance _) +} + +object Pickler { + + var picklerDebugMode = false + + abstract class Unpickled[+T] { + def map[U](f: T => U): Unpickled[U] = this match { + case UnpickleSuccess(x) => UnpickleSuccess(f(x)) + case f: UnpickleFailure => f + } + def flatMap[U](f: T => Unpickled[U]): Unpickled[U] = this match { + case UnpickleSuccess(x) => f(x) + case f: UnpickleFailure => f + } + def orElse[U >: T](alt: => Unpickled[U]): Unpickled[U] = this match { + case UnpickleSuccess(x) => this + case f: UnpickleFailure => alt + } + def requireSuccess: UnpickleSuccess[T] = this match { + case s @ UnpickleSuccess(x) => s + case f: UnpickleFailure => throw new Error("Unrecoverable unpickle failure:\n"+f) + } + } + + case class UnpickleSuccess[+T](result: T) extends Unpickled[T] + + class UnpickleFailure(msg: => String, rd: Lexer) extends Unpickled[Nothing] { + override def toString = "Failure at "+rd.tokenPos+":\n"+msg + } + + private def errorExpected(rd: Lexer, msg: => String) = + new UnpickleFailure("expected: "+msg+"\n" + + "found : "+rd.token, + rd) + + private def nextSuccess[T](rd: Lexer, result: T) = { + rd.nextToken() + UnpickleSuccess(result) + } + + def pkl[T: Pickler] = implicitly[Pickler[T]] + + case class ~[S, T](fst: S, snd: T) + + class TildeDecorator[S](x: S) { + def ~ [T](y: T): S ~ T = new ~ (x, y) + } + + implicit def tildeDecorator[S](x: S): TildeDecorator[S] = new TildeDecorator(x) + + implicit def fromTilde[T1, T2, R](f: (T1, T2) => R): T1 ~ T2 => R = { case x1 ~ x2 => f(x1, x2) } + + implicit def toTilde[T1, T2, S](f: S => Option[(T1, T2)]): S => T1 ~ T2 = { x => (f(x): @unchecked) match { case Some((x1, x2)) => x1 ~ x2 } } + + def labelledPickler[T](label: String, p: Pickler[T]): Pickler[T] = new Pickler[T] { + def pickle(wr: Writer, x: T) = { + wr.write(quoted(label)); + wr.write("(") + p.pickle(wr, x) + wr.write(")") + } + def unpickle(rd: Lexer): Unpickled[T] = + rd.token match { + case StringLit(`label`) => + rd.nextToken() + rd.accept('(') + val result = p.unpickle(rd).requireSuccess + rd.accept(')') + result + case _ => + errorExpected(rd, quoted(label)+"(...)") + } + } + + def wrappedPickler[S, T](p: Pickler[S])(in: S => T)(out: T => S) = new Pickler[T] { + def pickle(wr: Writer, x: T) = p.pickle(wr, out(x)) + def unpickle(rd: Lexer) = p.unpickle(rd) map in + } + + def conditionalPickler[T](p: Pickler[T], cond: Any => Boolean) = new CondPickler[T](cond) { + def pickle(wr: Writer, x: T) = p.pickle(wr, x) + def unpickle(rd: Lexer) = p.unpickle(rd) + } + + def seqPickler[T, U](p: Pickler[T], q: => Pickler[U]) = new Pickler[T ~ U] { + lazy val qq = q + def pickle(wr: Writer, x: T ~ U) = { + p.pickle(wr, x.fst) + wr.write(',') + q.pickle(wr, x.snd) + } + def unpickle(rd: Lexer) = + for (x <- p.unpickle(rd); y <- { rd.accept(','); qq.unpickle(rd).requireSuccess }) + yield x ~ y + } + + def eitherPickler[T, U <: T, V <: T](p: CondPickler[U], q: => CondPickler[V]) = + new CondPickler[T](x => p.canPickle(x) || q.canPickle(x)) { + lazy val qq = q + override def tryPickle(wr: Writer, x: Any): Boolean = + p.tryPickle(wr, x) || qq.tryPickle(wr, x) + def pickle(wr: Writer, x: T) = + require(tryPickle(wr, x), + "no pickler found for "+x+" of class "+x.asInstanceOf[AnyRef].getClass.getName) + def unpickle(rd: Lexer) = p.unpickle(rd) orElse qq.unpickle(rd) + } + + def singletonPickler[T <: AnyRef](x: T): CondPickler[T] = + unitPickler + .wrapped { _ => x } { x => () } + .labelled (x.getClass.getName) + .cond (x eq _.asInstanceOf[AnyRef]) + + def nullablePickler[T](p: Pickler[T])(implicit fromNull: Null <:< T): Pickler[T] = new Pickler[T] { + def pickle(wr: Writer, x: T) = + if (x == null) wr.write("null") else p.pickle(wr, x) + def unpickle(rd: Lexer): Unpickled[T] = + if (rd.token == NullLit) nextSuccess(rd, fromNull(null)) + else p.unpickle(rd) + } + + def javaInstancePickler[T <: AnyRef]: Pickler[T] = + (stringPickler labelled "$new") + .wrapped { name => Class.forName(name).newInstance().asInstanceOf[T] } { _.getClass.getName } + + implicit def iterPickler[T: Pickler]: Pickler[Iterator[T]] = new Pickler[Iterator[T]] { + lazy val p = pkl[T] + def pickle(wr: Writer, xs: Iterator[T]) { + var first = true + for (x <- xs) { + if (first) first = false else wr.write(',') + p.pickle(wr, x) + } + } + def unpickle(rd: Lexer): Unpickled[Iterator[T]] = UnpickleSuccess(new Iterator[T] { + var first = true + def hasNext = { + val t = rd.token + t != EOF && t != RParen && t != RBrace && t != RBracket + } + def next(): T = { + if (first) first = false else rd.accept(',') + p.unpickle(rd).requireSuccess.result + } + }) + } + + private def tokenPickler[T](kind: String)(matcher: PartialFunction[Token, T]) = new Pickler[T] { + def pickle(wr: Writer, x: T) = wr.write(x.toString) + def unpickle(rd: Lexer) = + if (matcher isDefinedAt rd.token) nextSuccess(rd, matcher(rd.token)) + else errorExpected(rd, kind) + } + + implicit val longPickler: Pickler[Long] = + tokenPickler("integer literal") { case IntLit(s) => s.toLong } + implicit val doublePickler: Pickler[Double] = + tokenPickler("floating point literal") { case FloatLit(s) => s.toDouble } + + implicit val bytePickler: Pickler[Byte] = longPickler.wrapped { _.toByte } { _.toLong } + implicit val shortPickler: Pickler[Short] = longPickler.wrapped { _.toShort } { _.toLong } + implicit val intPickler: Pickler[Int] = longPickler.wrapped { _.toInt } { _.toLong } + implicit val floatPickler: Pickler[Float] = doublePickler.wrapped { _.toFloat } { _.toLong } + + private val truePickler = + tokenPickler("boolean literal") { case TrueLit => true } cond { _ == true } + private val falsePickler = + tokenPickler("boolean literal") { case FalseLit => false } cond { _ == false } + + implicit def booleanPickler: Pickler[Boolean] = truePickler | falsePickler + + implicit val unitPickler: Pickler[Unit] = new Pickler[Unit] { + def pickle(wr: Writer, x: Unit) {} + def unpickle(rd: Lexer): Unpickled[Unit] = UnpickleSuccess(()) + } + + implicit val stringPickler: Pickler[String] = new Pickler[String] { + def pickle(wr: Writer, x: String) = wr.write(if (x == null) "null" else quoted(x)) + def unpickle(rd: Lexer) = rd.token match { + case StringLit(s) => nextSuccess(rd, s) + case NullLit => nextSuccess(rd, null) + case _ => errorExpected(rd, "string literal") + } + } + + implicit val charPickler: Pickler[Char] = + stringPickler + .wrapped { s => require(s.length == 1, "single character string literal expected, but "+quoted(s)+" found"); s(0) } { _.toString } + + implicit def tuple2Pickler[T1: Pickler, T2: Pickler]: Pickler[(T1, T2)] = + (pkl[T1] ~ pkl[T2]) + .wrapped { case x1 ~ x2 => (x1, x2) } { case (x1, x2) => x1 ~ x2 } + .labelled ("tuple2") + + implicit def tuple3Pickler[T1, T2, T3](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3]): Pickler[(T1, T2, T3)] = + (p1 ~ p2 ~ p3) + .wrapped { case x1 ~ x2 ~ x3 => (x1, x2, x3) } { case (x1, x2, x3) => x1 ~ x2 ~ x3 } + .labelled ("tuple3") + + implicit def tuple4Pickler[T1, T2, T3, T4](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3], p4: Pickler[T4]): Pickler[(T1, T2, T3, T4)] = + (p1 ~ p2 ~ p3 ~ p4) + .wrapped { case x1 ~ x2 ~ x3 ~ x4 => (x1, x2, x3, x4) } { case (x1, x2, x3, x4) => x1 ~ x2 ~ x3 ~ x4 } + .labelled ("tuple4") + + implicit val nonePickler = singletonPickler(None) + + implicit def somePickler[T: Pickler]: CondPickler[Some[T]] = + pkl[T] + .wrapped { Some(_) } { _.get } + .asClass (classOf[Some[T]]) + + implicit def optionPickler[T: Pickler]: Pickler[Option[T]] = nonePickler | somePickler[T] + + implicit def listPickler[T: Pickler]: Pickler[List[T]] = + iterPickler[T] .wrapped { _.toList } { _.iterator } .labelled ("scala.List") + + implicit def vectorPickler[T: Pickler]: Pickler[Vector[T]] = + iterPickler[T] .wrapped { Vector() ++ _ } { _.iterator } .labelled ("scala.Vector") + + implicit def array[T : ClassManifest : Pickler]: Pickler[Array[T]] = + iterPickler[T] .wrapped { _.toArray} { _.iterator } .labelled ("scala.Array") +} + +abstract class CondPickler[T](val canPickle: Any => Boolean) extends Pickler[T] { + import Pickler._ + def tryPickle(wr: Writer, x: Any): Boolean = { + val result = canPickle(x) + if (result) pickle(wr, x.asInstanceOf[T]) + result + } + def | [V >: T, U <: V] (that: => CondPickler[U]): CondPickler[V] = + eitherPickler[V, T, U](this, that) +} + +object Test extends Application { + import Pickler._ + import java.io.{StringReader, StringWriter} + + case class Foo(x: Int, y: String) + + implicit val fooPickler: Pickler[Foo] = + (pkl[Int] ~ pkl[String]) + .wrapped { Foo.apply } { toTilde(Foo.unapply) } + .asClass (classOf[Foo]) + + case class Rec(x: Int, r: Rec) + + implicit val recPickler: Pickler[Rec] = + (pkl[Int] ~ pkl[Rec]) + .wrapped { Rec.apply } { toTilde(Rec.unapply) } + .asClass (classOf[Rec]) + .orNull + + abstract class L[+T] + + case class Cons[T](head: T, tail: L[T]) extends L[T] + case object NIL extends L[Nothing] + + implicit def consPickler[T: Pickler] = + (pkl[T] ~ pkl[L[T]]) + .wrapped { case x ~ y => Cons(x, y) } { toTilde(Cons.unapply) } + .asClass (classOf[Cons[T]]) + + implicit lazy val nilPickler = singletonPickler(NIL) + + implicit def lPickler[T: Pickler]: Pickler[L[T]] = consPickler[T] | nilPickler + + implicit lazy val testPickler = singletonPickler(Test) + + implicit def anyThrowableInstance[T <: Throwable]: CondPickler[T] = javaInstancePickler[T] cond { _ => true } + + def testRelaxed[T: Pickler](x: T): T = { + val sw = new PrettyWriter(new StringWriter()) + val pickler = pkl[T] + val pickled = pickler.pickle(sw, x) + sw.close() + val s = sw.toString + println("pickled: "+s) + val sr = new Lexer(new StringReader(s)) + val r = pickler.unpickle(sr) + println("unpickled: "+r) + val UnpickleSuccess(y) = r + y + } + + def test[T: Pickler](x: T) { + assert(testRelaxed(x) == x) + } + + test(Foo(123, "abc")) + test(List(1, 2, 3)) + test(Test) + test(Foo(1, null)) + test(Rec(1, Rec(2, Rec(3, null)))) + test(Cons(1, Cons(2, Cons(3, NIL)))) + testRelaxed(new java.io.IOException) + +} + diff --git a/src/compiler/scala/tools/nsc/io/PrettyWriter.scala b/src/compiler/scala/tools/nsc/io/PrettyWriter.scala new file mode 100644 index 0000000000..acd4847469 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/PrettyWriter.scala @@ -0,0 +1,41 @@ +package scala.tools.nsc.io + +import java.io.Writer + +class PrettyWriter(wr: Writer) extends Writer { + protected val indentStep = " " + private var indent = 0 + private def newLine() { + wr.write('\n') + wr.write(indentStep * indent) + } + def close() = wr.close() + def flush() = wr.flush() + def write(str: Array[Char], off: Int, len: Int): Unit = { + if (off < str.length && off < len) { + str(off) match { + case '{' | '[' | '(' => + indent += 1 + wr.write(str(off)) + newLine() + wr.write(str, off + 1, len - 1) + case '}' | ']' | ')' => + wr.write(str, off, len) + indent -= 1 + case ',' => + wr.write(',') + newLine() + wr.write(str, off + 1, len - 1) + case ':' => + wr.write(':') + wr.write(' ') + wr.write(str, off + 1, len - 1) + case _ => + wr.write(str, off, len) + } + } else { + wr.write(str, off, len) + } + } + override def toString = wr.toString +} diff --git a/src/compiler/scala/tools/nsc/io/Replay.scala b/src/compiler/scala/tools/nsc/io/Replay.scala new file mode 100644 index 0000000000..5cb61b6cb1 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/Replay.scala @@ -0,0 +1,74 @@ +package scala.tools.nsc.io + +import java.io.{Reader, Writer} + +import Pickler._ +import Lexer.{Token, EOF} + +abstract class LogReplay { + def logreplay(event: String, x: => Boolean): Boolean + def logreplay[T: Pickler](event: String, x: => Option[T]): Option[T] + def close() + def flush() +} + +class Logger(wr0: Writer) extends LogReplay { + val wr = new PrettyWriter(wr0) + private var first = true + private def insertComma() = if (first) first = false else wr.write(",") + + def logreplay(event: String, x: => Boolean) = { + val xx = x + if (xx) { insertComma(); pkl[Unit].labelled(event).pickle(wr, ()) } + xx + } + def logreplay[T: Pickler](event: String, x: => Option[T]) = { + val xx = x + xx match { + case Some(y) => insertComma(); pkl[T].labelled(event).pickle(wr, y) + case None => + } + xx + } + def close() { wr.close() } + def flush() { wr.flush() } +} + +object NullLogger extends LogReplay { + def logreplay(event: String, x: => Boolean) = x + def logreplay[T: Pickler](event: String, x: => Option[T]) = x + def close() {} + def flush() {} +} + +class Replayer(raw: Reader) extends LogReplay { + private val rd = new Lexer(raw) + private var nextComma = false + + private def eatComma() = + if (nextComma) { rd.accept(','); nextComma = false } + + def logreplay(event: String, x: => Boolean) = + if (rd.token == EOF) NullLogger.logreplay(event, x) + else { + eatComma() + pkl[Unit].labelled(event).unpickle(rd) match { + case UnpickleSuccess(_) => nextComma = true; true + case _ => false + } + } + + def logreplay[T: Pickler](event: String, x: => Option[T]) = + if (rd.token == EOF) NullLogger.logreplay(event, x) + else { + eatComma() + pkl[T].labelled(event).unpickle(rd) match { + case UnpickleSuccess(y) => nextComma = true; Some(y) + case _ => None + } + } + + def close() { raw.close() } + def flush() {} +} + diff --git a/src/compiler/scala/tools/nsc/util/Pickler.scala b/src/compiler/scala/tools/nsc/util/Pickler.scala deleted file mode 100644 index ef9fcb4c92..0000000000 --- a/src/compiler/scala/tools/nsc/util/Pickler.scala +++ /dev/null @@ -1,316 +0,0 @@ -package scala.tools.nsc.util - -import scala.tools.nsc.io.JSON._ -import annotation.unchecked - -abstract class Pickler[T] { - - import Pickler._ - - def pickle(x: T): Pickled - def unpickle(it: PickleIterator): Result[T] - - def ~ [U] (that: => Pickler[U]): Pickler[T ~ U] = seq(this, that) - - def labelled(label: String): Pickler[T] = Pickler.labelled(label, this) - def wrapped [U] (in: T => U)(out: U => T): Pickler[U] = wrap(this)(in)(out) - def cond(p: Any => Boolean): CondPickler[T] = conditional(this, p) - def asClass[U <: T](c: Class[U]): CondPickler[T] = this labelled c.getName cond (c isInstance _) -} - -object Pickler { - - var debugMode = false - - type Pickled = JValue - type PickleIterator = JIterator[Pickled] - - abstract class Result[+T] { - def map[U](f: T => U): Result[U] = this match { - case Success(x) => Success(f(x)) - case f: Failure => f - } - def flatMap[U](f: T => Result[U]): Result[U] = this match { - case Success(x) => f(x) - case f: Failure => f - } - def orElse[U >: T](alt: => Result[U]): Result[U] = this match { - case Success(x) => this - case f: Failure => alt - } - } - - case class Success[T](result: T) extends Result[T] - - class Failure(msg: => String, it: PickleIterator) extends Result[Nothing] { - override def toString = it.reader match { - case Some(rdr) => "Failure at "+rdr.tokenPos+": "+msg - case None => "Failure: "+msg - } - } - - def errorExpected(msg: String, it: PickleIterator) = - new Failure("expected: "+msg+"\n" + - "found : "+(if (it.hasNext) it.head.toString else ""), - it) - - def valueIterator(x: JValue): PickleIterator = { - val xArray = x match { - case arr: JArray => arr - case _ => new JArray(x) - } - xArray.inputIterator - } - - def unpickleLabelled[T](it: PickleIterator, label: String) - (unpickleBinding: (PickleIterator, String) => Result[T]): Result[T] = { - def err(it: PickleIterator) = errorExpected("{"+label+": ?}", it) - if (it.hasNext) - it.head match { - case jo: JObject => - val it2 = jo.inputIterator - if (it2.hasNext) - it2.head match { - case name J_: rhs if (label == "?" || label == name) => - it2.next() - val result = unpickleBinding(valueIterator(rhs), name) - it.next() - result - case _ => - err(it) - } - else err(it) - case _ => - err(it) - } - else - err(it) - } - - def labelled[T](label: String, p: Pickler[T]): Pickler[T] = new Pickler[T] { - def pickle(x: T): Pickled = - JObject(label J_: p.pickle(x)) - def unpickle(it: PickleIterator): Result[T] = - unpickleLabelled(it, label) { (rit, name) => p unpickle rit } - } - - def wrap[S, T](p: Pickler[S])(in: S => T)(out: T => S) = new Pickler[T] { - def pickle(x: T) = p.pickle(out(x)) - def unpickle(it: PickleIterator) = p.unpickle(it) map in - } - - def conditional[T](p: Pickler[T], cond: Any => Boolean) = new CondPickler[T](cond) { - def pickle(x: T) = p.pickle(x) - def unpickle(it: PickleIterator) = p.unpickle(it) - } - - def seq[T, U](p: Pickler[T], q: => Pickler[U]) = new Pickler[T ~ U] { - private def toSeq(x: JValue): Seq[JValue] = x match { - case JArray(elems @ _*) => elems - case _ => Vector(x) - } - lazy val qq = q - def pickle(x: T ~ U) = - JArray(toSeq(p pickle x.fst) ++ toSeq(qq pickle x.snd): _*) - def unpickle(it: PickleIterator) = - for (x <- p.unpickle(it); y <- qq.unpickle(it)) yield x ~ y - } - - def rep[T](implicit p: Pickler[T]): Pickler[Seq[T]] = new Pickler[Seq[T]] { - def pickle(xs: Seq[T]): Pickled = JArray(xs map p.pickle: _*) - def unpickle(it: PickleIterator): Result[Seq[T]] = - if (it.hasNext) - for { - first <- p.unpickle(it) - rest <- rep(p).unpickle(it) - } yield first +: rest - else Success(Seq()) - } - - def either[T, U <: T, V <: T](p: CondPickler[U], q: CondPickler[V]) = - new CondPickler[T](x => p.canPickle(x) || q.canPickle(x)) { - override def tryPickle(x: Any) = p.tryPickle(x) orElse q.tryPickle(x) - def pickle(x: T): Pickled = tryPickle(x) getOrElse { - throw new Error("no pickler found for a "+x.asInstanceOf[AnyRef].getClass.getName) - } - def unpickle(it: PickleIterator) = p.unpickle(it) orElse q.unpickle(it) - } - - def obj[T <: AnyRef](x: T): CondPickler[T] = - unit.wrapped { _ => x } { x => () } labelled x.getClass.getName cond (x eq _.asInstanceOf[AnyRef]) - - def pkl[T: Pickler] = implicitly[Pickler[T]] - - def anyJavaInstance[T <: AnyRef]: Pickler[T] = new Pickler[T] { - def pickle(x: T) = JObject(x.getClass.getName J_: JArray()) - def unpickle(it: PickleIterator): Result[T] = - unpickleLabelled(it, "?") { (rit, name) => Success(Class.forName(name).newInstance().asInstanceOf[T]) } - } - - def nullable[T >: Null](p: Pickler[T]): Pickler[T] = new Pickler[T] { - def pickle(x: T) = if (x == null) JNull else p.pickle(x) - def unpickle(it: PickleIterator): Result[T] = - if (it.hasNext && it.head == JNull) Success(null) - else p.unpickle(it) - } - - case class ~[S, T](fst: S, snd: T) - - class TildeDecorator[S](x: S) { - def ~ [T](y: T): S ~ T = new ~ (x, y) - } - - implicit def tildeDecorator[S](x: S): TildeDecorator[S] = new TildeDecorator(x) - - implicit def fromTilde[T1, T2, R](f: (T1, T2) => R): T1 ~ T2 => R = { case x1 ~ x2 => f(x1, x2) } - - implicit def toTilde[T1, T2, S](f: S => Option[(T1, T2)]): S => T1 ~ T2 = { x => (f(x): @unchecked) match { case Some((x1, x2)) => x1 ~ x2 } } - - private def jsonValue[T](it: PickleIterator, kind: String) - (matcher: PartialFunction[JValue, T]): Result[T] = - if (it.hasNext && matcher.isDefinedAt(it.head)) Success(matcher(it.next())) - else errorExpected(kind+" value", it) - - implicit def bigint: Pickler[BigInt] = new Pickler[BigInt] { - def pickle(x: BigInt): Pickled = JInteger(x) - def unpickle(it: PickleIterator): Result[BigInt] = jsonValue(it, "integer") { case JInteger(x) => x } - } - - implicit def long: Pickler[Long] = new Pickler[Long] { - def pickle(x: Long): Pickled = JLong(x) - def unpickle(it: PickleIterator): Result[Long] = jsonValue(it, "long") { case JLong(x) => x } - } - - implicit def double: Pickler[Double] = new Pickler[Double] { - def pickle(x: Double): Pickled = JDouble(x) - def unpickle(it: PickleIterator): Result[Double] = jsonValue(it, "double") { case JDouble(x) => x } - } - - implicit def boolean: Pickler[Boolean] = new Pickler[Boolean] { - def pickle(x: Boolean): Pickled = if (x) JTrue else JFalse - def unpickle(it: PickleIterator): Result[Boolean] = jsonValue(it, "boolean") { - case JTrue => true - case JFalse => false - } - } - - implicit def unit: Pickler[Unit] = new Pickler[Unit] { - def pickle(x: Unit): Pickled = JArray() - def unpickle(it: PickleIterator): Result[Unit] = Success(()) - } - - implicit def string: Pickler[String] = nullable { - new Pickler[String] { - def pickle(x: String): Pickled = JString(x) - def unpickle(it: PickleIterator): Result[String] = jsonValue(it, "string") { case JString(x) => x } - } - } - - implicit def byte: Pickler[Byte] = long.wrapped { _.toByte } { _.toLong } - implicit def short: Pickler[Short] = long.wrapped { _.toShort } { _.toLong } - implicit def char: Pickler[Char] = long.wrapped { _.toChar } { _.toLong } - implicit def int: Pickler[Int] = long.wrapped { _.toInt } { _.toLong } - implicit def float: Pickler[Float] = double.wrapped { _.toFloat } { _.toLong } - - implicit def tuple2[T1, T2](implicit p1: Pickler[T1], p2: Pickler[T2]): Pickler[(T1, T2)] = - (p1 ~ p2) - .wrapped { case x1 ~ x2 => (x1, x2) } { case (x1, x2) => x1 ~ x2 } - .labelled ("tuple2") - - implicit def tuple3[T1, T2, T3](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3]): Pickler[(T1, T2, T3)] = - (p1 ~ p2 ~ p3) - .wrapped { case x1 ~ x2 ~ x3 => (x1, x2, x3) } { case (x1, x2, x3) => x1 ~ x2 ~ x3 } - .labelled ("tuple3") - - implicit def tuple4[T1, T2, T3, T4](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3], p4: Pickler[T4]): Pickler[(T1, T2, T3, T4)] = - (p1 ~ p2 ~ p3 ~ p4) - .wrapped { case x1 ~ x2 ~ x3 ~ x4 => (x1, x2, x3, x4) } { case (x1, x2, x3, x4) => x1 ~ x2 ~ x3 ~ x4 } - .labelled ("tuple3") - - implicit def none = obj(None) - - implicit def some[T](implicit pt: Pickler[T]): CondPickler[Some[T]] = - pt.wrapped { Some(_) } { _.get } - .asClass (classOf[Some[T]]) - - implicit def option[T](implicit pt: Pickler[T]): Pickler[Option[T]] = none | some[T] - - implicit def list[T](implicit pt: Pickler[T]): Pickler[List[T]] = - rep[T](pt).wrapped { _.toList } { x => x } .labelled ("scala.List") - - implicit def vector[T](implicit pt: Pickler[T]): Pickler[Vector[T]] = - rep[T](pt).wrapped { Vector() ++ _ } { x => x } .labelled ("scala.Vector") - - implicit def array[T](implicit ev: ClassManifest[T], pt: Pickler[T]): Pickler[Array[T]] = - rep[T](pt).wrapped { _.toArray} { _.toSeq } .labelled ("scala.Array") -} - -abstract class CondPickler[T](val canPickle: Any => Boolean) extends Pickler[T] { - import Pickler._ - def tryPickle(x: Any): Option[Pickled] = - if (canPickle(x)) Some(pickle(x.asInstanceOf[T])) else None - def | [V >: T, U <: V] (that: CondPickler[U]): CondPickler[V] = - either[V, T, U](this, that) -} - -object Test extends Application { - import Pickler._ - import scala.tools.nsc.io.JSON._ - import java.io.{StringReader, StringWriter} - - case class Foo(x: Int, y: String) - - implicit lazy val foo: Pickler[Foo] = - nullable((int ~ string).wrapped { Foo.apply } { toTilde(Foo.unapply) } asClass classOf[Foo]) - - case class Rec(x: Int, r: Rec) - - implicit lazy val rec: Pickler[Rec] = - nullable((int ~ rec).wrapped { Rec.apply } { toTilde(Rec.unapply) } asClass classOf[Rec]) - - abstract class L[+T] - - case class Cons[T](head: T, tail: L[T]) extends L[T] - case object NIL extends L[Nothing] - - implicit def cons[T: Pickler] = - (pkl[T] ~ pkl[L[T]]) - .wrapped { case x ~ y => Cons(x, y) } { toTilde(Cons.unapply) } asClass classOf[Cons[T]] - - implicit lazy val nil = obj(NIL) - - implicit def l[T: Pickler]: Pickler[L[T]] = cons[T] | nil - - implicit lazy val test = obj(Test) - - implicit def anyThrowableInstance[T <: Throwable]: CondPickler[T] = anyJavaInstance[T] cond { _ => true } - - def testRelaxed[T: Pickler](x: T): T = { - val sw = new PrettyWriter(new StringWriter()) - val pickler = pkl[T] - val pickled = pickler pickle x - pickled.write(sw) - sw.close() - val s = sw.toString - println("pickled: "+s) - val sr = new JReader(new StringReader(s)) - val r = pickler unpickle sr.iterator - println("unpickled: "+r) - val Success(y) = r - y - } - - def test[T: Pickler](x: T) { - assert(testRelaxed(x) == x) - } - - test(Foo(123, "abc")) - test(List(1, 2, 3)) - test(Test) - test(Foo(1, null)) - test(Rec(1, Rec(2, Rec(3, null)))) - test(Cons(1, Cons(2, Cons(3, NIL)))) - testRelaxed(new java.io.IOException) - -} - diff --git a/src/compiler/scala/tools/nsc/util/Replayer.scala b/src/compiler/scala/tools/nsc/util/Replayer.scala deleted file mode 100644 index 57e8ac0e78..0000000000 --- a/src/compiler/scala/tools/nsc/util/Replayer.scala +++ /dev/null @@ -1,59 +0,0 @@ -package scala.tools.nsc.util - -import Pickler.{PickleIterator, valueIterator, Result, Success} -import scala.tools.nsc.io.JSON._ - -abstract class LogReplay { - def logreplay(event: String, x: => Boolean): Boolean - def logreplay[T](event: String, x: => Option[T])(implicit pt: Pickler[T]): Option[T] -} - -class Logger(wr: java.io.Writer) extends LogReplay { - wr.write('{') - def logreplay(event: String, x: => Boolean) = { - if (x) (event J_: JNull) write wr - x - } - def logreplay[T](event: String, x: => Option[T])(implicit pt: Pickler[T]) = { - x match { - case Some(y) => (event J_: (pt pickle y)) write wr - case None => - } - x - } - def close() { - wr.write('}') - wr.close() - } -} - -object NullLogger extends LogReplay { - def logreplay(event: String, x: => Boolean) = x - def logreplay[T](event: String, x: => Option[T])(implicit pt: Pickler[T]) = x - def close() {} -} - -class Replayer(log: java.io.Reader) extends LogReplay { - var rdr = new JReader(log) - val it: BufferedIterator[J_:] = rdr.nextValue() match { - case jo: JObject => - jo.inputIterator - case _ => - throw new MalformedInput(rdr, "no top-level object found") - } - def logreplay(event: String, x: => Boolean) = it.head match { - case `event` J_: _ => it.next(); true - case _ => false - } - def logreplay[T](event: String, x: => Option[T])(implicit pt: Pickler[T]) = it.head match { - case `event` J_: rhs => - it.next() - val Success(result) = pt.unpickle(valueIterator(rhs)) - Some(result) - case _ => None - } - def close() { - log.close() - } -} - -- cgit v1.2.3