From 9b3852f26201aee7761637f89979dab2a71294a9 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 1 Apr 2011 05:02:30 +0000 Subject: A less ad hoc infrastructure for generating Any... A less ad hoc infrastructure for generating AnyVal sources. A few more comments on said sources. No review. --- src/compiler/scala/tools/cmd/gen/AnyVals.scala | 430 +++++++++++++------------ src/compiler/scala/tools/cmd/gen/Codegen.scala | 8 +- 2 files changed, 217 insertions(+), 221 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala index db07229ce9..a1280eec8e 100644 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala @@ -6,20 +6,138 @@ package scala.tools.cmd package gen -trait AnyValTemplates { - def indent(s: String) = if (s == "") "" else " " + s - def indentN(s: String) = s.lines map indent mkString "\n" - -def classDocTemplate = (""" -/** `@name@` is a member of the value classes, those whose instances are - * not represented as objects by the underlying host system. - * - * There is an implicit conversion from [[scala.@name@]] => [[scala.runtime.Rich@name@]] - * which provides useful non-primitive operations. +/** Code generation of the AnyVal types and their companions. */ -""".trim + "\n") +trait AnyValReps { + self: AnyVals => + + sealed abstract class AnyValNum(name: String) extends AnyValRep(name) { + def isCardinal: Boolean = isIntegerType(this) + def unaryOps = if (isCardinal) List("+", "-", "~") else List("+", "-") + def bitwiseOps = if (isCardinal) List("|", "&", "^") else Nil + def shiftOps = if (isCardinal) List("<<", ">>>", ">>") else Nil + def comparisonOps = List("==", "!=", "<", "<=", ">", ">=") + def otherOps = List("+", "-" ,"*", "/", "%") + + // Given two numeric value types S and T , the operation type of S and T is defined as follows: + // If both S and T are subrange types then the operation type of S and T is Int. + // Otherwise the operation type of S and T is the larger of the two types wrt ranking. + // Given two numeric values v and w the operation type of v and w is the operation type + // of their run-time types. + def opType(that: AnyValNum): AnyValNum = { + val rank = IndexedSeq(I, L, F, D) + (rank indexOf this, rank indexOf that) match { + case (-1, -1) => I + case (r1, r2) => rank apply (r1 max r2) + } + } - def timestampString = "" + def mkCoercions = numeric map (x => "def to%s: %s".format(x, x)) + def mkUnaryOps = unaryOps map (x => "def unary_%s : %s".format(x, this opType I)) + def mkStringOps = List("def +(x: String): String") + def mkShiftOps = ( + for (op <- shiftOps ; arg <- List(I, L)) yield + "def %s(x: %s): %s".format(op, arg, this opType I) + ) + + def clumps: List[List[String]] = { + val xs1 = List(mkCoercions, mkUnaryOps, mkStringOps, mkShiftOps) map (xs => if (xs.isEmpty) xs else xs :+ "") + val xs2 = List( + mkBinOpsGroup(comparisonOps, numeric, _ => Z), + mkBinOpsGroup(bitwiseOps, cardinal, this opType _), + mkBinOpsGroup(otherOps, numeric, this opType _) + ) + xs1 ++ xs2 + } + def classLines = clumps.foldLeft(List[String]()) { + case (res, Nil) => res + case (res, lines) => + val xs = lines map { + case "" => "" + case s => interpolate(s) + " = " + stub + } + res ++ xs + } + def objectLines = { + val comp = if (isCardinal) cardinalCompanion else floatingCompanion + (comp + allCompanions).trim.lines map interpolate toList + } + + /** Makes a set of binary operations based on the given set of ops, args, and resultFn. + * + * @param ops list of function names e.g. List(">>", "%") + * @param args list of types which should appear as arguments + * @param resultFn function which calculates return type based on arg type + * @return list of function definitions + */ + def mkBinOpsGroup(ops: List[String], args: List[AnyValNum], resultFn: AnyValNum => AnyValRep): List[String] = ( + ops flatMap (op => + args.map(arg => "def %s(x: %s): %s".format(op, arg, resultFn(arg))) :+ "" + ) + ).toList + } + + sealed abstract class AnyValRep(val name: String) { + def classLines: List[String] + def objectLines: List[String] + + def lcname = name.toLowerCase + def boxedName = this match { + case U => "scala.runtime.BoxedUnit" + case C => "java.lang.Character" + case I => "java.lang.Integer" + case _ => "java.lang." + name + } + def zeroRep = this match { + case L => "0L" + case F => "0.0f" + case D => "0.0d" + case _ => "0" + } + + def indent(s: String) = if (s == "") "" else " " + s + def indentN(s: String) = s.lines map indent mkString "\n" + + def boxUnboxImpls = Map( + "@boxImpl@" -> "%s.valueOf(x)".format(boxedName), + "@unboxImpl@" -> "x.asInstanceOf[%s].%sValue()".format(boxedName, lcname), + "@unboxDoc@" -> "the %s resulting from calling %sValue() on `x`".format(name, lcname) + ) + def interpolations = Map( + "@name@" -> name, + "@boxed@" -> boxedName, + "@lcname@" -> lcname, + "@zero@" -> zeroRep + ) ++ boxUnboxImpls + + def interpolate(s: String): String = interpolations.foldLeft(s) { + case (str, (key, value)) => str.replaceAll(key, value) + } + def classDoc = interpolate(classDocTemplate) + def objectDoc = "" + def mkImports = "" + def mkClass = assemble("final class", "AnyVal", classLines) + "\n" + def mkObject = assemble("object", "AnyValCompanion", objectLines) + "\n" + def make() = List[String]( + headerTemplate, + mkImports, + classDoc, + mkClass, + objectDoc, + mkObject + ) mkString "" + + def assemble(what: String, parent: String, lines: List[String]): String = { + val decl = "%s %s extends %s ".format(what, name, parent) + val body = if (lines.isEmpty) "{ }\n\n" else lines map indent mkString ("{\n", "\n", "\n}\n") + + decl + body + } + override def toString = name + } +} + +trait AnyValTemplates { def headerTemplate = (""" /* __ *\ ** ________ ___ / / ___ Scala API ** @@ -34,80 +152,59 @@ package scala """.trim.format(timestampString) + "\n\n") - def nonUnitCompanion = """ + def classDocTemplate = (""" +/** `@name@` is a member of the value classes, those whose instances are + * not represented as objects by the underlying host system. + * + * There is an implicit conversion from [[scala.@name@]] => [[scala.runtime.Rich@name@]] + * which provides useful non-primitive operations. + */ +""".trim + "\n") + + def timestampString = "// DO NOT EDIT, CHANGES WILL BE LOST.\n" + def stub = """sys.error("stub")""" + + def allCompanions = """ /** Transform a value type into a boxed reference type. * * @param x the @name@ to be boxed - * @return a @type@ offering `x` as its underlying value. + * @return a @boxed@ offering `x` as its underlying value. */ -def box(x: @name@): @type@ = @type@.valueOf(x) +def box(x: @name@): @boxed@ = @boxImpl@ /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw - * an exception if the argument is not a @type@. + * an exception if the argument is not a @boxed@. * - * @param x the @name@ to be unboxed. - * @throws ClassCastException if the argument is not a @type@ - * @return the @name@ resulting from calling @lcname@Value() on `x` + * @param x the @boxed@ to be unboxed. + * @throws ClassCastException if the argument is not a @boxed@ + * @return @unboxDoc@ */ -def unbox(x: java.lang.Object): @name@ = x.asInstanceOf[@type@].@lcname@Value() +def unbox(x: java.lang.Object): @name@ = @unboxImpl@ /** The String representation of the scala.@name@ companion object. */ override def toString = "object scala.@name@" - """ - - def booleanBody = (""" -final class Boolean extends AnyVal { - def unary_! : Boolean = sys.error("stub") - - def ==(x: Boolean): Boolean = sys.error("stub") - def !=(x: Boolean): Boolean = sys.error("stub") - def ||(x: Boolean): Boolean = sys.error("stub") - def &&(x: Boolean): Boolean = sys.error("stub") - // Compiler won't build with these seemingly more accurate signatures - // def ||(x: => Boolean): Boolean = sys.error("stub") - // def &&(x: => Boolean): Boolean = sys.error("stub") - def |(x: Boolean): Boolean = sys.error("stub") - def &(x: Boolean): Boolean = sys.error("stub") - def ^(x: Boolean): Boolean = sys.error("stub") -} - -object Boolean extends AnyValCompanion { -""".trim + indentN(nonUnitCompanion) + "\n}") - - def unitBody = """ -import runtime.BoxedUnit - -/** Unit is a member of the value classes, those whose instances are - * not represented as objects by the underlying host system. There is - * only one value of type Unit: `()`. - */ -final class Unit extends AnyVal { } +""" -object Unit extends AnyValCompanion { - override def toString = "object scala.Unit" - def box(x: Unit): BoxedUnit = BoxedUnit.UNIT - def unbox(x: java.lang.Object): Unit = () -} - """.trim - - def cardinalCompanion = (""" + def cardinalCompanion = """ /** The smallest value representable as a @name@. */ -final val MinValue = @type@.MIN_VALUE +final val MinValue = @boxed@.MIN_VALUE /** The largest value representable as a @name@. */ -final val MaxValue = @type@.MAX_VALUE -""" + nonUnitCompanion).trim.lines +final val MaxValue = @boxed@.MAX_VALUE +""" - def floatingCompanion = (""" -/** The smallest positive value greater than @zero@.*/ -final val MinPositiveValue = @type@.MIN_VALUE -final val NaN = @type@.NaN -final val PositiveInfinity = @type@.POSITIVE_INFINITY -final val NegativeInfinity = @type@.NEGATIVE_INFINITY + def floatingCompanion = """ +/** The smallest positive value greater than @zero@ which is + * representable as a @name@. + */ +final val MinPositiveValue = @boxed@.MIN_VALUE +final val NaN = @boxed@.NaN +final val PositiveInfinity = @boxed@.POSITIVE_INFINITY +final val NegativeInfinity = @boxed@.NEGATIVE_INFINITY @deprecated("use @name@.MinPositiveValue instead") final val Epsilon = MinPositiveValue @@ -117,162 +214,67 @@ final val Epsilon = MinPositiveValue * is the smallest positive value representable by a @name@. In Scala that number * is called @name@.MinPositiveValue. */ -final val MinValue = -@type@.MAX_VALUE +final val MinValue = -@boxed@.MAX_VALUE /** The largest finite positive number representable as a @name@. */ -final val MaxValue = @type@.MAX_VALUE -""" + nonUnitCompanion).trim.lines +final val MaxValue = @boxed@.MAX_VALUE +""" } -class AnyVals extends AnyValTemplates { - trait AnyValInterpolation { - def name: String - def isCardinal: Boolean - def restype: String - def tpe: String - def zero: String - - lazy val interpolations = Map( - "@restype@" -> restype, - "@name@" -> name, - "@type@" -> tpe, - "@lcname@" -> name.toLowerCase, - "@zero@" -> zero - ) - def interpolate(s: String): String = interpolations.foldLeft(s) { - case (str, (key, value)) => str.replaceAll(key, value) - } - - def classDoc = interpolate(classDocTemplate) - def make(): String +class AnyVals extends AnyValReps with AnyValTemplates { + object B extends AnyValNum("Byte") + object S extends AnyValNum("Short") + object C extends AnyValNum("Char") + object I extends AnyValNum("Int") + object L extends AnyValNum("Long") + object F extends AnyValNum("Float") + object D extends AnyValNum("Double") + object Z extends AnyValRep("Boolean") { + def classLines = """ +def unary_! : Boolean = sys.error("stub") + +def ==(x: Boolean): Boolean = sys.error("stub") +def !=(x: Boolean): Boolean = sys.error("stub") +def ||(x: Boolean): Boolean = sys.error("stub") +def &&(x: Boolean): Boolean = sys.error("stub") +// Compiler won't build with these seemingly more accurate signatures +// def ||(x: => Boolean): Boolean = sys.error("stub") +// def &&(x: => Boolean): Boolean = sys.error("stub") +def |(x: Boolean): Boolean = sys.error("stub") +def &(x: Boolean): Boolean = sys.error("stub") +def ^(x: Boolean): Boolean = sys.error("stub") + """.trim.lines.toList + + def objectLines = interpolate(allCompanions).lines.toList } - - val B = "Byte" - val S = "Short" - val C = "Char" - val I = "Int" - val L = "Long" - val F = "Float" - val D = "Double" - - lazy val cardinal = List(B, S, C, I, L) - lazy val floating = List(F, D) - lazy val numeric = cardinal ++ floating - - def javaType(primType: String) = "java.lang." + (primType match { - case C => "Character" - case I => "Integer" - case t => t - }) - - def make() = { - val nums = numeric map (name => new AnyValOps(name).make()) - (numeric zip nums) :+ ("Boolean", makeBoolean()) :+ ("Unit", makeUnit()) - } - - def makeBoolean() = (new AnyValInterpolation { - def name = "Boolean" - def isCardinal = false - def restype = "Nothing" - def tpe = "java.lang.Boolean" - def zero = "false" - - def make() = headerTemplate + classDoc + interpolate(booleanBody) - }).make() - - def makeUnit() = (new AnyValInterpolation { - def name = "Unit" - def isCardinal = false - def restype = "Nothing" - def tpe = "scala.runtime.BoxedUnit" - def zero = "()" - - def make() = headerTemplate + unitBody - }).make() - - class AnyValOps(val name: String) extends AnyValInterpolation { - val isCardinal = cardinal contains name - val restype = if ("LFD" contains name.head) name else I - val tpe = javaType(name) - val zero = name.head match { - case 'L' => "0L" - case 'F' => "0.0f" - case 'D' => "0.0d" - case _ => "0" - } - def mkCoercions = numeric map (x => "def to%s: %s".format(x, x)) - def mkUnaryOps = unaryops map (op => "def unary_%s : @restype@".format(op)) - def mkCommon = List( - "def +(x: String): String" - ) - def mkShiftOps = ( - for (op <- shiftops ; tpe <- List(I, L)) yield - "def %s(x: %s): @restype@".format(op, tpe) - ) - - def clumps: List[List[String]] = { - val xs1 = List(mkCoercions, mkUnaryOps, mkCommon, mkShiftOps) map (xs => if (xs.isEmpty) xs else xs :+ "") - val xs2 = List( - mkBinOpsGroup(boolBinops, numeric, _ => "Boolean"), - mkBinOpsGroup(bitwiseops, cardinal, resultTypeForArg), - mkBinOpsGroup(otherBinops, numeric, resultTypeForArg) - ) - xs1 ++ xs2 - } - - def defImplementation = "sys.error(\"stub\")" - def mkClass = { - val lines = clumps.foldLeft(List[String]()) { - case (res, Nil) => res - case (res, lines) => - val xs = lines map { - case "" => "" - case s => interpolate(s) + " = " + defImplementation - } - res ++ xs - } - classDoc + assemble("final class", "AnyVal", lines) - } - def mkObject = assemble("object", "AnyValCompanion", companionBody map interpolate toList) - - def assemble(what: String, parent: String, lines: List[String]): String = ( - List(what, name, "extends", parent, "{").mkString(" ") +: - (lines map indent) :+ - "}" - ).mkString("\n", "\n", "\n") - - def make() = headerTemplate + mkClass + "\n" + mkObject - - /** Makes a set of binary operations based on the given set of ops, args, and resultFn. - * - * @param ops list of function names e.g. List(">>", "%") - * @param args list of types which should appear as arguments - * @param resultFn function which calculates return type based on arg type - * @return list of function definitions - */ - def mkBinOpsGroup(ops: List[String], args: List[String], resultFn: String => String): List[String] = ( - ops flatMap { op => - args.map(arg => "def %s(x: %s): ".format(op, arg) + resultFn(arg)) :+ "" - } + object U extends AnyValRep("Unit") { + override def classDoc = """ +/** Unit is a member of the value classes, those whose instances are + * not represented as objects by the underlying host system. There is + * only one value of type Unit: `()`. + */ +""" + def classLines = Nil + def objectLines = interpolate(allCompanions).lines.toList + + override def boxUnboxImpls = Map( + "@boxImpl@" -> "scala.runtime.BoxedUnit.UNIT", + "@unboxImpl@" -> "()", + "@unboxDoc@" -> "the Unit value ()" ) + } - def resultTypeForArg(arg: String): String = arg match { - case L if isCardinal => L - case F => if (name == D) D else F - case D => D - case _ => restype - } + def isSubrangeType = Set(B, S, C) + def isIntegerType = Set(B, S, C, I, L) + def isFloatingType = Set(F, D) + def isWideType = Set(L, D) - def unaryops = if (isCardinal) List("+", "-", "~") else List("+", "-") - def bitwiseops = if (isCardinal) List("|", "&", "^") else Nil - def shiftops = if (isCardinal) List("<<", ">>>", ">>") else Nil - def boolBinops = List("==", "!=", "<", "<=", ">", ">=") - def otherBinops = List("+", "-" ,"*", "/", "%") + def cardinal = numeric filter isIntegerType + def numeric = List(B, S, C, I, L, F, D) + def values = List(U, Z) ++ numeric - def companionBody = - if (isCardinal) cardinalCompanion - else floatingCompanion - } + def make() = values map (x => (x.name, x.make())) } -object AnyVals extends AnyVals { } \ No newline at end of file +object AnyVals extends AnyVals { } + diff --git a/src/compiler/scala/tools/cmd/gen/Codegen.scala b/src/compiler/scala/tools/cmd/gen/Codegen.scala index d7801f2c08..1bb820455a 100644 --- a/src/compiler/scala/tools/cmd/gen/Codegen.scala +++ b/src/compiler/scala/tools/cmd/gen/Codegen.scala @@ -26,13 +26,7 @@ object Codegen { echo("Generating sources into " + out) if (anyvals || all) { - val av = new AnyVals { - override def timestampString = ( - "// DO NOT EDIT, CHANGES WILL BE LOST.\n" + ( - if (stamp) "// Generated by tools/codegen on " + new java.util.Date + "\n" else "" - ) - ) - } + val av = new AnyVals { } av.make() foreach { case (name, code ) => val file = out / (name + ".scala") toFile; -- cgit v1.2.3