diff options
author | Lukas Rytz <lukas.rytz@epfl.ch> | 2009-02-09 16:06:06 +0000 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@epfl.ch> | 2009-02-09 16:06:06 +0000 |
commit | 76009173e08545c346d4a66eb847a75120649ad2 (patch) | |
tree | ad271feec9a4411bd9e4d67a7d93ee35b32d0bca /src/scalap | |
parent | 9191eb8dd882e04bee79907bda69d5018ee5ed99 (diff) | |
download | scala-76009173e08545c346d4a66eb847a75120649ad2.tar.gz scala-76009173e08545c346d4a66eb847a75120649ad2.tar.bz2 scala-76009173e08545c346d4a66eb847a75120649ad2.zip |
added build scripts for scalap
Diffstat (limited to 'src/scalap')
27 files changed, 3293 insertions, 0 deletions
diff --git a/src/scalap/scala/tools/scalap/Arguments.scala b/src/scalap/scala/tools/scalap/Arguments.scala new file mode 100644 index 0000000000..4810adf1af --- /dev/null +++ b/src/scalap/scala/tools/scalap/Arguments.scala @@ -0,0 +1,202 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: Arguments.scala 5837 2006-02-23 17:37:25 +0000 (Thu, 23 Feb 2006) michelou $ + +package scala.tools.scalap + +import scala.collection.mutable._ + + +object Arguments { + + case class Parser(optionPrefix: Char) { + + val options: Set[String] = new HashSet + val prefixes: Set[String] = new HashSet + val optionalArgs: Set[String] = new HashSet + val prefixedBindings: Map[String, Char] = new HashMap + val optionalBindings: Map[String, Char] = new HashMap + + def error(message: String): Unit = Console.println(message) + + def withOption(option: String): Parser = { + options += option + this + } + + def withOptionalArg(option: String): Parser = { + optionalArgs += option + this + } + + def withOptionalBinding(option: String, separator: Char): Parser = { + optionalBindings(option) = separator + this + } + + def withPrefixedArg(prefix: String): Parser = { + prefixes += prefix + this + } + + def withPrefixedBinding(prefix: String, separator: Char): Parser = { + prefixedBindings(prefix) = separator + this + } + + def parseBinding(str: String, separator: Char): Pair[String, String] = { + val eqls = str.indexOf(separator) + if (eqls < 0) { + error("missing '" + separator + "' in binding '" + str + "'") + Pair("", "") + } else + Pair(str.substring(0, eqls).trim(), + str.substring(eqls + 1).trim()) + } + + def parse(args: Array[String]): Arguments = { + val res = new Arguments + parse(args, res) + res + } + + def parse(args: Array[String], res: Arguments): Unit = { + if (args != null) { + var i = 0; + while (i < args.length) + if ((args(i) == null) || (args(i).length() == 0)) + i = i + 1 + else if (args(i).charAt(0) != optionPrefix) { + res.addOther(args(i)) + i = i + 1 + } else if (options contains args(i)) { + res.addOption(args(i)) + i = i + 1 + } else if (optionalArgs contains args(i)) { + if ((i + 1) == args.length) { + error("missing argument for '" + args(i) + "'") + i = i + 1 + } else { + res.addArgument(args(i), args(i + 1)) + i = i + 2 + } + } else if (optionalBindings contains args(i)) { + if ((i + 1) == args.length) { + error("missing argument for '" + args(i) + "'") + i = i + 1 + } else { + res.addBinding(args(i), + parseBinding(args(i + 1), optionalBindings(args(i)))); + i = i + 2 + } + } else { + var iter = prefixes.elements + val j = i + while ((i == j) && iter.hasNext) { + val prefix = iter.next + if (args(i) startsWith prefix) { + res.addPrefixed(prefix, args(i).substring(prefix.length()).trim()); + i = i + 1 + } + } + if (i == j) { + val iter = prefixedBindings.keys; + while ((i == j) && iter.hasNext) { + val prefix = iter.next + if (args(i) startsWith prefix) { + val arg = args(i).substring(prefix.length()).trim() + i = i + 1 + res.addBinding(prefix, + parseBinding(arg, prefixedBindings(prefix))); + } + } + if (i == j) { + error("unknown option '" + args(i) + "'") + i = i + 1 + } + } + } + } + } + } + + def parse(options: String*)(args: Array[String]): Arguments = { + val parser = new Parser('-') + val iter = options.elements + while (iter.hasNext) + parser withOption iter.next + parser.parse(args) + } +} + +class Arguments { + + private val options: Set[String] = new HashSet + private val arguments: Map[String, String] = new HashMap + private val prefixes: Map[String, Set[String]] = new HashMap + private val bindings: Map[String, Map[String, String]] = new HashMap + private val others: Buffer[String] = new ListBuffer + + def addOption(option: String): Unit = options += option + + def addArgument(option: String, arg: String): Unit = arguments(option) = arg + + def addPrefixed(prefix: String, arg: String): Unit = + if (prefixes isDefinedAt prefix) + prefixes(prefix) += arg + else { + prefixes(prefix) = new HashSet + prefixes(prefix) += arg + } + + def addBinding(tag: String, key: String, value: String): Unit = + if (key.length() > 0) { + if (bindings isDefinedAt tag) + bindings(tag)(key) = value + else { + bindings(tag) = new HashMap + bindings(tag)(key) = value + } + } + + def addBinding(tag: String, binding: Pair[String, String]): Unit = + addBinding(tag, binding._1, binding._2) + + def addOther(arg: String): Unit = others += arg + + def contains(option: String): Boolean = options contains option + + def getArgument(option: String): Option[String] = arguments get option + + def getSuffixes(prefix: String): Set[String] = + prefixes.get(prefix) match { + case None => new HashSet + case Some(set) => set + } + + def containsSuffix(prefix: String, suffix: String): Boolean = + prefixes.get(prefix) match { + case None => false + case Some(set) => set contains suffix + } + + def getBindings(tag: String): Map[String, String] = + bindings.get(tag) match { + case None => new HashMap + case Some(map) => map + } + + def getBinding(option: String, key: String): Option[String] = + bindings.get(option) match { + case None => None + case Some(map) => map get key + } + + def getOthers: List[String] = others.toList + +} diff --git a/src/scalap/scala/tools/scalap/ByteArrayReader.scala b/src/scalap/scala/tools/scalap/ByteArrayReader.scala new file mode 100644 index 0000000000..13f62ebfd9 --- /dev/null +++ b/src/scalap/scala/tools/scalap/ByteArrayReader.scala @@ -0,0 +1,157 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: ByteArrayReader.scala 5837 2006-02-23 17:37:25 +0000 (Thu, 23 Feb 2006) michelou $ + +package scala.tools.scalap + + +class ByteArrayReader(content: Array[Byte]) { + import java.io._ + + /** the buffer containing the file + */ + val buf: Array[Byte] = content + + /** the current input pointer + */ + var bp: Int = 0 + + /** return byte at offset 'pos' + */ + def byteAt(pos: Int): Byte = buf(pos) + + /** read a byte + */ + def nextByte: Byte = { + bp = bp + 1 + buf(bp - 1) + } + + /** read some bytes + */ + def nextBytes(len: Int): Array[Byte] = { + val res = new Array[Byte](len) + System.arraycopy(buf, bp, res, 0, len) + bp = bp + len + res + } + + /** read a character + */ + def nextChar: Char = { + bp = bp + 2 + (((buf(bp - 2) & 0xff) << 8) + (buf(bp - 1) & 0xff)).asInstanceOf[Char] + } + + /** read an integer + */ + def nextInt: Int = { + bp = bp + 4 + ((buf(bp - 4) & 0xff) << 24) + + ((buf(bp - 3) & 0xff) << 16) + + ((buf(bp - 2) & 0xff) << 8) + + (buf(bp - 1) & 0xff) + } + + /** read a long + */ + def nextLong: Long = + (nextInt.asInstanceOf[Long] << 32) + (nextInt.asInstanceOf[Long] & 0xffffffffL) + + /** read a float + */ + def nextFloat: Float = java.lang.Float.intBitsToFloat(nextInt) + + /** read a double + */ + def nextDouble: Double = java.lang.Double.longBitsToDouble(nextLong) + + /** read the next integer number + */ + def nextNat: Int = { + var x = 0 + var b: Byte = 0 + do { + b = buf(bp) + bp = bp + 1 + x = (x << 7) + (b & 0x7f) + } while ((b & 0x80) != 0) + x + } + + /** read the next signed number in big endian format + */ + def nextNum(n: Int): Long = { + var x: Long = 0 + var i: Int = 0 + while (i < n) { + x = (x << 8) + (nextByte & 0xff) + i = i + 1 + } + val leading: Int = 64 - (n * 8) + x << leading >> leading + } + + /** read an UTF8 encoded string + */ + def nextUTF8(len: Int): String = { + val cs: Array[Char] = new Array(len) + var i = bp + var j = 0 + bp = bp + len + while (i < bp) { + var b: Int = buf(i) & 0xFF + i = i + 1 + if (b >= 0xE0) { + b = ((b & 0x0F) << 12) | (buf(i) & 0x3F) << 6 + i = i + 1 + b = b | (buf(i) & 0x3F) + i = i + 1 + } else if (b >= 0xC0) { + b = ((b & 0x1F) << 6) | (buf(i) & 0x3F) + i = i + 1 + } + cs(j) = b.asInstanceOf[Char] + j = j + 1 + } + new String(cs, 0, j) + } + + /** extract a character at position bp from buf + */ + def getChar(bp: Int): Char = + (((buf(bp) & 0xff) << 8) + (buf(bp + 1) & 0xff)).asInstanceOf[Char] + + /** extract an integer at position bp from buf + */ + def getInt(bp: Int): Int = + ((buf(bp ) & 0xff) << 24) + + ((buf(bp + 1) & 0xff) << 16) + + ((buf(bp + 2) & 0xff) << 8) + + (buf(bp + 3) & 0xff) + + /** extract a long integer at position bp from buf + */ + def getLong(bp: Int): Long = + (getInt(bp).asInstanceOf[Long] << 32) + (getInt(bp + 4).asInstanceOf[Long] & 0xffffffffL) + + /** extract a float at position bp from buf + */ + def getFloat(bp: Int): Float = java.lang.Float.intBitsToFloat(getInt(bp)) + + /** extract a double at position bp from buf + */ + def getDouble(bp: Int): Double = java.lang.Double.longBitsToDouble(getLong(bp)) + + /** skip next 'n' bytes + */ + def skip(n: Int): Unit = { + bp = bp + n + } + +} diff --git a/src/scalap/scala/tools/scalap/Classfile.scala b/src/scalap/scala/tools/scalap/Classfile.scala new file mode 100644 index 0000000000..795f4ec613 --- /dev/null +++ b/src/scalap/scala/tools/scalap/Classfile.scala @@ -0,0 +1,126 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: Classfile.scala 5837 2006-02-23 17:37:25 +0000 (Thu, 23 Feb 2006) michelou $ + +package scala.tools.scalap + + +class Classfile(in: ByteArrayReader) { + import Classfiles._ + + assert(in.nextInt == JAVA_MAGIC) + val minorVersion = in.nextChar + val majorVersion = in.nextChar + val pool = readPool + val flags = in.nextChar + val classname = in.nextChar + val superclass = in.nextChar + val interfaces = readInterfaces + val fields = readMembers(true) + val methods = readMembers(false) + val attribs = readAttribs + + def readAttribs = { + val n = in.nextChar + var attribs: List[Attribute] = Nil + var i = 0 + while (i < n) { + attribs = Attribute(in.nextChar, in.nextBytes(in.nextInt)) :: attribs + i = i + 1 + } + attribs + } + + def readMembers(field: Boolean) = { + val n = in.nextChar + var members: List[Member] = Nil + var i = 0 + while (i < n) { + members = Member(field, in.nextChar, in.nextChar, in.nextChar, readAttribs) :: members + i = i + 1 + } + members + } + + def readInterfaces = { + val n = in.nextChar + var intfs: List[Int] = Nil + var i = 0 + while (i < n) { + intfs = in.nextChar :: intfs + i = i + 1 + } + intfs + } + + def readPool = { + val pool = new Array[PoolEntry](in.nextChar) + var i = 1 + while (i < pool.length) { + val tag: Int = in.nextByte + tag match { + case CONSTANT_UTF8 => + pool(i) = UTF8(in.nextUTF8(in.nextChar)) + case CONSTANT_UNICODE => + in.skip(in.nextChar) + pool(i) = Empty() + case CONSTANT_CLASS => + pool(i) = ClassRef(in.nextChar) + case CONSTANT_STRING => + pool(i) = StringConst(in.nextChar) + case CONSTANT_FIELDREF => + pool(i) = FieldRef(in.nextChar, in.nextChar) + case CONSTANT_METHODREF => + pool(i) = MethodRef(in.nextChar, in.nextChar) + case CONSTANT_INTFMETHODREF => + pool(i) = IntfMethodRef(in.nextChar, in.nextChar) + case CONSTANT_NAMEANDTYPE => + pool(i) = NameAndType(in.nextChar, in.nextChar) + case CONSTANT_INTEGER => + pool(i) = IntegerConst(in.nextInt) + case CONSTANT_FLOAT => + pool(i) = FloatConst(in.nextFloat) + case CONSTANT_LONG => + pool(i) = LongConst(in.nextLong) + i = i + 1 + pool(i) = Empty() + case CONSTANT_DOUBLE => + pool(i) = DoubleConst(in.nextDouble) + i = i + 1 + pool(i) = Empty() + } + i = i + 1 + } + pool + } + + class PoolEntry + case class UTF8(str: String) extends PoolEntry + case class ClassRef(classId: Int) extends PoolEntry + case class FieldRef(classId: Int, memberId: Int) extends PoolEntry + case class MethodRef(classId: Int, memberId: Int) extends PoolEntry + case class IntfMethodRef(classId: Int, memberId: Int) extends PoolEntry + case class StringConst(strId: Int) extends PoolEntry + case class IntegerConst(x: Int) extends PoolEntry + case class FloatConst(x: Float) extends PoolEntry + case class LongConst(x: Long) extends PoolEntry + case class DoubleConst(x: Double) extends PoolEntry + case class NameAndType(nameId: Int, typeId: Int) extends PoolEntry + case class Empty() extends PoolEntry + + case class Member(field: Boolean, flags: Int, name: Int, tpe: Int, attribs: List[Attribute]) + case class Attribute(name: Int, data: Array[Byte]) { + + override def toString(): String = pool(name) match { + case UTF8(str: String) => str + } + + def reader: ByteArrayReader = new ByteArrayReader(data) + } + +} diff --git a/src/scalap/scala/tools/scalap/Classfiles.scala b/src/scalap/scala/tools/scalap/Classfiles.scala new file mode 100644 index 0000000000..d9e85e9d9e --- /dev/null +++ b/src/scalap/scala/tools/scalap/Classfiles.scala @@ -0,0 +1,56 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: Classfiles.scala 5837 2006-02-23 17:37:25 +0000 (Thu, 23 Feb 2006) michelou $ + +package scala.tools.scalap + + +object Classfiles { + final val JAVA_MAGIC = 0xCAFEBABE + final val JAVA_MAJOR_VERSION = 45 + final val JAVA_MINOR_VERSION = 3 + + final val CONSTANT_UTF8 = 1 + final val CONSTANT_UNICODE = 2 + final val CONSTANT_INTEGER = 3 + final val CONSTANT_FLOAT = 4 + final val CONSTANT_LONG = 5 + final val CONSTANT_DOUBLE = 6 + final val CONSTANT_CLASS = 7 + final val CONSTANT_STRING = 8 + final val CONSTANT_FIELDREF = 9 + final val CONSTANT_METHODREF = 10 + final val CONSTANT_INTFMETHODREF = 11 + final val CONSTANT_NAMEANDTYPE = 12 + + final val BAD_ATTR = 0x00000 + final val SOURCEFILE_ATTR = 0x00001 + final val SYNTHETIC_ATTR = 0x00002 + final val DEPRECATED_ATTR = 0x00004 + final val CODE_ATTR = 0x00008 + final val EXCEPTIONS_ATTR = 0x00010 + final val CONSTANT_VALUE_ATTR = 0x00020 + final val LINE_NUM_TABLE_ATTR = 0x00040 + final val LOCAL_VAR_TABLE_ATTR = 0x00080 + final val INNERCLASSES_ATTR = 0x08000 + final val META_ATTR = 0x10000 + final val SCALA_ATTR = 0x20000 + + final val SOURCEFILE_N = "SourceFile" + final val SYNTHETIC_N = "Synthetic" + final val DEPRECATED_N = "Deprecated" + final val CODE_N = "Code" + final val EXCEPTIONS_N = "Exceptions" + final val CONSTANT_VALUE_N = "ConstantValue" + final val LINE_NUM_TABLE_N = "LineNumberTable" + final val LOCAL_VAR_TABLE_N = "LocalVariableTable" + final val INNERCLASSES_N = "InnerClasses" + final val META_N = "JacoMeta" + final val SCALA_N = "ScalaSignature" + final val CONSTR_N = "<init>" +} diff --git a/src/scalap/scala/tools/scalap/CodeWriter.scala b/src/scalap/scala/tools/scalap/CodeWriter.scala new file mode 100644 index 0000000000..b0ad39bd12 --- /dev/null +++ b/src/scalap/scala/tools/scalap/CodeWriter.scala @@ -0,0 +1,140 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: CodeWriter.scala 5837 2006-02-23 17:37:25 +0000 (Thu, 23 Feb 2006) michelou $ + +package scala.tools.scalap + +import java.io._ + + +class CodeWriter(writer: Writer) { + + private val nl = System.getProperty("line.separator") + private var step = " " + private var level = 0 + private var align = false + private var space = false + private var line = false + + def getWriter = writer + + def getIndentLevel = level + + def setIndentLevel(level: Int): CodeWriter = { + this.level = level + this + } + + def getIndentWidth = if (step == null) -1 else step.length() + + def setIndentWidth(width: Int): CodeWriter = { + val buffer = new StringBuffer(width) + var i = 0 + while (i < width) + buffer.append(' ') + setIndentString(buffer.toString()) + } + + def getIndentString = step; + + def setIndentString(step: String): CodeWriter = { + this.step = step + this + } + + def indent: CodeWriter = { + level = level + 1 + this + } + + def undent: CodeWriter = { + level = level - 1 + this + } + + def newline: CodeWriter = { + if (step == null) + newspace + else if (!line) { + try { + writer.write(nl) + } catch { + case e => error("IO error") + } + line = align + align = true + space = false + this + } else + this + } + + def newspace: CodeWriter = { + space = !align + this + } + + def * : Unit = {} + + def println: CodeWriter = newline + + def println(value: Boolean): CodeWriter = print(value).newline + + def println(value: Byte): CodeWriter = print(value).newline + + def println(value: Short): CodeWriter = print(value).newline + + def println(value: Char): CodeWriter = print(value).newline + + def println(value: Int): CodeWriter = print(value).newline + + def println(value: Long): CodeWriter = print(value).newline + + def println(value: Float): CodeWriter = print(value).newline + + def println(value: Double): CodeWriter = print(value).newline + + def println(value: String): CodeWriter = print(value).newline + + def print(value: Boolean): CodeWriter = print(String.valueOf(value)) + + def print(value: Byte): CodeWriter = print(String.valueOf(value)) + + def print(value: Short): CodeWriter = print(String.valueOf(value)) + + def print(value: Char): CodeWriter = print(String.valueOf(value)) + + def print(value: Int): CodeWriter = print(String.valueOf(value)) + + def print(value: Long): CodeWriter = print(String.valueOf(value)) + + def print(value: Float): CodeWriter = print(String.valueOf(value)) + + def print(value: Double): CodeWriter = print(String.valueOf(value)) + + def print(value: String): CodeWriter = try { + if (align) { + var i = 0 + while (i < level) { + writer.write(step) + i = i + 1 + } + } + if (space) + writer.write(" ") + writer.write(value) + align = false + space = false + line = false + this + } catch { + case e => error("IO error") + } + + override def toString(): String = writer.toString() +} diff --git a/src/scalap/scala/tools/scalap/JavaWriter.scala b/src/scalap/scala/tools/scalap/JavaWriter.scala new file mode 100644 index 0000000000..46d1f0fb56 --- /dev/null +++ b/src/scalap/scala/tools/scalap/JavaWriter.scala @@ -0,0 +1,234 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: JavaWriter.scala 5838 2006-02-23 17:54:21Z michelou $ + +package scala.tools.scalap + +import java.io._ + + +class JavaWriter(classfile: Classfile, writer: Writer) extends CodeWriter(writer) { + + val cf = classfile + + def flagsToStr(clazz: Boolean, flags: Int) = { + val buffer = new StringBuffer() + var x: StringBuffer = buffer + if (((flags & 0x0007) == 0) && + ((flags & 0x0002) != 0)) + x = buffer.append("private ") + if ((flags & 0x0004) != 0) + x = buffer.append("protected ") + if ((flags & 0x0010) != 0) + x = buffer.append("final ") + if ((flags & 0x0400) != 0) + x = if (clazz) buffer.append("abstract ") + else buffer.append("/*deferred*/ ") + buffer.toString() + } + + def nameToClass(str: String) = { + val res = Names.decode(str.replace('/', '.')) + if (res == "java.lang.Object") "scala.Any" else res + } + + def nameToClass0(str: String) = { + val res = Names.decode(str.replace('/', '.')) + if (res == "java.lang.Object") "scala.AnyRef" else res + } + + def nameToSimpleClass(str: String) = + Names.decode(str.substring(str.lastIndexOf('/') + 1)) + + def nameToPackage(str: String) = { + val inx = str.lastIndexOf('/') + val name = if (inx == -1) str else str.substring(0, inx).replace('/', '.') + Names.decode(name) + } + + def sigToType(str: String): String = + sigToType(str, 0)._1 + + def sigToType(str: String, i: Int): Pair[String, Int] = str.charAt(i) match { + case 'B' => Pair("scala.Byte", i + 1) + case 'C' => Pair("scala.Char", i + 1) + case 'D' => Pair("scala.Double", i + 1) + case 'F' => Pair("scala.Float", i + 1) + case 'I' => Pair("scala.Int", i + 1) + case 'J' => Pair("scala.Long", i + 1) + case 'S' => Pair("scala.Short", i + 1) + case 'V' => Pair("scala.Unit", i + 1) + case 'Z' => Pair("scala.Boolean", i + 1) + case 'L' => val j = str.indexOf(';', i); + Pair(nameToClass(str.substring(i + 1, j)), j + 1) + case '[' => val Pair(tpe, j) = sigToType(str, i + 1); + Pair("scala.Array[" + tpe + "]", j) + case '(' => val Pair(tpe, j) = sigToType0(str, i + 1); + Pair("(" + tpe, j) + case ')' => val Pair(tpe, j) = sigToType(str, i + 1); + Pair("): " + tpe, j) + } + + def sigToType0(str: String, i: Int): Pair[String, Int] = + if (str.charAt(i) == ')') + sigToType(str, i) + else { + val Pair(tpe, j) = sigToType(str, i) + if (str.charAt(j) == ')') { + val Pair(rest, k) = sigToType(str, j) + Pair(tpe + rest, k) + } else { + val Pair(rest, k) = sigToType0(str, j) + Pair(tpe + ", " + rest, k) + } + } + + def getName(n: Int): String = cf.pool(n) match { + case cf.UTF8(str) => str + case cf.StringConst(m) => getName(m) + case cf.ClassRef(m) => getName(m) + case x => "<error>" + } + + def getClassName(n: Int): String = nameToClass(getName(n)) + + def getSimpleClassName(n: Int): String = nameToSimpleClass(getName(n)) + + def getPackage(n: Int): String = nameToPackage(getName(n)) + + def getType(n: Int): String = sigToType(getName(n)) + + def isStatic(flags: Int) = (flags & 0x0008) != 0 + + def isInterface(flags: Int) = (flags & 0x0200) != 0 + + def isConstr(name: String) = (name == "<init>") + + def printField(flags: Int, name: Int, tpe: Int, attribs: List[cf.Attribute]) = { + print(flagsToStr(false, flags)) + if ((flags & 0x0010) != 0) + print("val " + Names.decode(getName(name))) + else + print("final var " + Names.decode(getName(name))) + print(": " + getType(tpe) + ";").newline + } + + def printMethod(flags: Int, name: Int, tpe: Int, attribs: List[cf.Attribute]) = { + if (getName(name) == "<init>") + print(flagsToStr(false, flags)) + attribs find { + case cf.Attribute(name, _) => getName(name) == "JacoMeta" + } match { + case Some(cf.Attribute(_, data)) => + val mp = new MetaParser(getName( + ((data(0) & 0xff) << 8) + (data(1) & 0xff)).trim()) + mp.parse match { + case None => + if (getName(name) == "<init>") { + print("def this" + getType(tpe) + ";").newline + } else { + print("def " + Names.decode(getName(name))) + print(getType(tpe) + ";").newline + } + case Some(str) => + if (getName(name) == "<init>") + print("def this" + str + ";").newline + else + print("def " + Names.decode(getName(name)) + str + ";").newline + } + case None => + if (getName(name) == "<init>") { + print("def this" + getType(tpe) + ";").newline + } else { + print("def " + Names.decode(getName(name))) + print(getType(tpe) + ";").newline + } + } + attribs find { + case cf.Attribute(name, _) => getName(name) == "Exceptions" + } match { + case Some(cf.Attribute(_, data)) => + val n = ((data(0) & 0xff) << 8) + (data(1) & 0xff) + indent.print("throws ") + for (val i <- Iterator.range(0, n) map {x => 2 * (x + 1)}) { + val inx = ((data(i) & 0xff) << 8) + (data(i+1) & 0xff) + if (i > 2) print(", ") + print(getClassName(inx).trim()) + } + undent.newline + case None => + } + } + + def printClassHeader = { + if (isInterface(cf.flags)) { + print("trait " + getSimpleClassName(cf.classname)) + } else { + print("class " + getSimpleClassName(cf.classname)) + if (cf.pool(cf.superclass) != null) + print(" extends " + nameToClass0(getName(cf.superclass))) + } + cf.interfaces foreach { + n => print(" with " + getClassName(n)) + } + } + + def printClass = { + val pck = getPackage(cf.classname); + if (pck.length() > 0) + println("package " + pck + ";") + print(flagsToStr(true, cf.flags)) + cf.attribs find { + case cf.Attribute(name, _) => getName(name) == "JacoMeta" + } match { + case None => + printClassHeader; + case Some(cf.Attribute(_, data)) => + val mp = new MetaParser(getName( + ((data(0) & 0xff) << 8) + (data(1) & 0xff)).trim()); + mp.parse match { + case None => printClassHeader; + case Some(str) => + if (isInterface(cf.flags)) + print("trait " + getSimpleClassName(cf.classname) + str); + else + print("class " + getSimpleClassName(cf.classname) + str); + } + } + var statics: List[cf.Member] = Nil + print(" {").indent.newline + cf.fields foreach { + case m@cf.Member(_, flags, name, tpe, attribs) => + if (isStatic(flags)) + statics = m :: statics + else + printField(flags, name, tpe, attribs) + } + cf.methods foreach { + case m@cf.Member(_, flags, name, tpe, attribs) => + if (isStatic(flags)) + statics = m :: statics + else + printMethod(flags, name, tpe, attribs) + } + undent.print("}").newline + if (!statics.isEmpty) { + print("object " + getSimpleClassName(cf.classname) + " {") + indent.newline + statics foreach { + case cf.Member(true, flags, name, tpe, attribs) => + printField(flags, name, tpe, attribs) + case cf.Member(false, flags, name, tpe, attribs) => + if (getName(name) != "<clinit>") + printMethod(flags, name, tpe, attribs) + } + undent.print("}").newline + } + } + +} diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala new file mode 100644 index 0000000000..6e91e00234 --- /dev/null +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -0,0 +1,266 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2009, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ http://scala-lang.org/ +** +*/ + +// $Id: Main.scala 9834 2007-01-31 16:31:25Z michelou $ + +package scala.tools.scalap + + +import java.io.{File, PrintStream, OutputStreamWriter, ByteArrayOutputStream} +import scala.tools.nsc.util.ClassPath +import scalax.rules.scalasig._ + +/**The main object used to execute scalap on the command-line. + * + * @author Matthias Zenger, Stephane Micheloud, Burak Emir, Ilya Sergey + */ +object Main { + val SCALA_SIG = "ScalaSig" + val versionMsg = "Scala classfile decoder " + + Properties.versionString + " -- " + + Properties.copyrightString + + /**Verbose program run? + */ + var verbose = false + + /**Prints usage information for scalap. + */ + def usage: Unit = { + Console.println("usage: scalap {<option>} <name>") + Console.println("where <option> is") + Console.println(" -private print private definitions") + Console.println(" -verbose print out additional information") + Console.println(" -version print out the version number of scalap") + Console.println(" -help display this usage message") + Console.println(" -classpath <path> specify where to find user class files") + Console.println(" -cp <path> specify where to find user class files") + } + + def isScalaFile(bytes: Array[Byte]): Boolean = { + val byteCode = ByteCode(bytes) + val classFile = ClassFileParser.parse(byteCode) + classFile.attribute("ScalaSig") match {case Some(_) => true; case None => false} + } + + /**Processes the given Java class file. + * + * @param clazz the class file to be processed. + */ + def processJavaClassFile(clazz: Classfile): Unit = { + // construct a new output stream writer + val out = new OutputStreamWriter(Console.out) + val writer = new JavaWriter(clazz, out) + // print the class + writer.printClass + out.flush() + } + + def parseScalaSignature(scalaSig: ScalaSig) = { + val baos = new ByteArrayOutputStream + val stream = new PrintStream(baos) + val syms = scalaSig.topLevelClasses ::: scalaSig.topLevelObjects + syms.first.parent match { + //Partial match + case Some(p) if (p.name != "<empty>") => { + stream.print("package "); + stream.print(p.path); + stream.print("\n") + } + case _ => + } + // Print classes + val printer = new ScalaSigPrinter(stream) + for (c <- syms) { + printer.printSymbol(c) + } + baos.toString + } + + + def decompileScala(bytes: Array[Byte]) = { + val byteCode = ByteCode(bytes) + val classFile = ClassFileParser.parse(byteCode) + classFile.attribute(SCALA_SIG).map(_.byteCode).map(ScalaSigAttributeParsers.parse) match { + case Some(scalaSig) => Console.println(parseScalaSignature(scalaSig)) + case None => //Do nothing + } + } + + + /**Executes scalap with the given arguments and classpath for the + * class denoted by <code>classname</code>. + * + * @param args... + * @param path... + * @param classname... + */ + def process(args: Arguments, path: ClassPath#Build)(classname: String): Unit = { + // find the classfile + val filename = Names.encode( + if (classname == "scala.AnyRef") "java.lang.Object" + else classname).replace('.', File.separatorChar) + val cfile = path.lookupPath(filename, /*isDir*/ false) + if (cfile != null) { + if (verbose) { + Console.println(Console.BOLD + "FILENAME" + Console.RESET + " = " + cfile.path) + } + val bytes = cfile.toByteArray + if (isScalaFile(bytes)) { + decompileScala(bytes) + } else { + // construct a reader for the classfile content + val reader = new ByteArrayReader(cfile.toByteArray) + // parse the classfile + val clazz = new Classfile(reader) + processJavaClassFile(clazz) + } + // if the class corresponds to the artificial class scala.All. + // (to be removed after update of the STARR libraries) + } else if (classname == "scala.All") { + Console.println("package scala") + Console.println("/* Deprecated. Use scala.Nothing instead. */") + Console.println("sealed abstract class All") + // if the class corresponds to the artificial class scala.AllRef. + // (to be removed after update of the STARR libraries) + } else if (classname == "scala.AllRef") { + Console.println("package scala") + Console.println("/* Deprecated. Use scala.Null instead. */") + Console.println("sealed abstract class AllRef") + // if the class corresponds to the artificial class scala.Any. + // (see member list in class scala.tool.nsc.symtab.Definitions) + } else if (classname == "scala.Any") { + Console.println("package scala") + Console.println("class Any {") + Console.println(" final def ==(scala.Any): scala.Boolean") + Console.println(" final def !=(scala.Any): Boolean") + Console.println(" def equals(scala.Any): scala.Boolean") + Console.println(" def hashCode(): scala.Int") + Console.println(" def toString(): java.lang.String") + Console.println(" final def isInstanceOf[a]: scala.Boolean") + Console.println(" final def asInstanceOf[a]: a") + Console.println("}") + // if the class corresponds to the artificial class scala.AnyRef. + } else if (classname == "scala.AnyRef") { + Console.println("package scala") + Console.println("class AnyRef extends Any {") + Console.println(" def equals(scala.Any): scala.Boolean") + Console.println(" def hashCode(): scala.Int") + Console.println(" def toString(): java.lang.String") + Console.println("}") + // if the class corresponds to the artificial class scala.AnyVal. + } else if (classname == "scala.AnyVal") { + Console.println("package scala") + Console.println("sealed class AnyVal extends Any") + // if the class corresponds to the artificial class scala.Boolean. + } else if (classname == "scala.Boolean") { + Console.println("package scala") + Console.println("sealed abstract class Boolean extends AnyVal {") + Console.println(" def &&(p: => scala.Boolean): scala.Boolean // boolean and") + Console.println(" def ||(p: => scala.Boolean): scala.Boolean // boolean or") + Console.println(" def & (x: scala.Boolean): scala.Boolean // boolean strict and") + Console.println(" def | (x: scala.Boolean): scala.Boolean // boolean stric or") + Console.println(" def ==(x: scala.Boolean): scala.Boolean // boolean equality") + Console.println(" def !=(x: scala.Boolean): scala.Boolean // boolean inequality") + Console.println(" def !: scala.Boolean // boolean negation") + Console.println("}") + // if the class corresponds to the artificial class scala.Int. + } else if (classname == "scala.Int") { + Console.println("package scala") + Console.println("sealed abstract class Int extends AnyVal {") + Console.println(" def ==(that: scala.Double): scala.Boolean") + Console.println(" def ==(that: scala.Float): scala.Boolean") + Console.println(" def ==(that: scala.Long): scala.Boolean") + Console.println(" def ==(that: scala.Int): scala.Boolean") + Console.println(" def ==(that: scala.Short): scala.Boolean") + Console.println(" def ==(that: scala.Byte): scala.Boolean") + Console.println(" def ==(that: scala.Char): scala.Boolean") + Console.println(" /* analogous for !=, <, >, <=, >= */") + Console.println + Console.println(" def + (that: scala.Double): scala.Double // double addition") + Console.println(" def + (that: scala.Float): scala.Float // float addition") + Console.println(" def + (that: scala.Long): scala.Long // long addition") + Console.println(" def + (that: scala.Int): scala.Int // int addition") + Console.println(" def + (that: scala.Short): scala.Int // int addition") + Console.println(" def + (that: scala.Byte): scala.Int // int addition") + Console.println(" def + (that: scala.Char): scala.Int // int addition") + Console.println(" /* analogous for -, *, /, % */") + Console.println + Console.println(" def & (that: scala.Long): scala.Long // long bitwise and") + Console.println(" def & (that: scala.Int): scala.Int // int bitwise and") + Console.println(" def & (that: scala.Short): scala.Int // int bitwise and") + Console.println(" def & (that: scala.Byte): scala.Int // int bitwise and") + Console.println(" def & (that: scala.Char): scala.Int // int bitwise and") + Console.println(" /* analogous for |, ^ */") + Console.println + Console.println(" def <<(cnt: scala.Int): scala.Int // int left shift") + Console.println(" def <<(cnt: scala.Long): scala.Int // long left shift") + Console.println(" /* analogous for >>, >>> */") + Console.println + Console.println(" def + : scala.Int // int identity") + Console.println(" def - : scala.Int // int negation") + Console.println(" def ~ : scala.Int // int bitwise negation") + Console.println + Console.println(" def toByte: scala.Byte // convert to Byte") + Console.println(" def toShort: scala.Short // convert to Short") + Console.println(" def toChar: scala.Char // convert to Char") + Console.println(" def toInt: scala.Int // convert to Int") + Console.println(" def toLong: scala.Long // convert to Long") + Console.println(" def toFloat: scala.Float // convert to Float") + Console.println(" def toDouble: scala.Double // convert to Double") + Console.println("}") + // if the class corresponds to the artificial class scala.Nothing. + } else if (classname == "scala.Nothing") { + Console.println("package scala") + Console.println("sealed abstract class Nothing") + // if the class corresponds to the artificial class scala.Null. + } else if (classname == "scala.Null") { + Console.println("package scala") + Console.println("sealed abstract class Null") + } else + Console.println("class/object " + classname + " not found.") + } + + /**The main method of this object. + */ + def main(args: Array[String]) { + // print usage information if there is no command-line argument + if (args.length == 0) + usage + // otherwise parse the arguments... + else { + val arguments = Arguments.Parser('-') + .withOption("-private") + .withOption("-verbose") + .withOption("-version") + .withOption("-help") + .withOptionalArg("-classpath") + .withOptionalArg("-cp") + .parse(args); + if (arguments contains "-version") + Console.println(versionMsg) + if (arguments contains "-help") + usage + verbose = arguments contains "-verbose" + // construct a custom class path + val classPath0 = new ClassPath(false) + val path = arguments.getArgument("-classpath") match { + case None => arguments.getArgument("-cp") match { + case None => new classPath0.Build() + case Some(path) => new classPath0.Build(path) + } + case Some(path) => new classPath0.Build(path) + } + // print the classpath if output is verbose + if (verbose) { + Console.println(Console.BOLD + "CLASSPATH" + Console.RESET + " = " + path) + } + // process all given classes + arguments.getOthers.foreach(process(arguments, path)) + } + } +} diff --git a/src/scalap/scala/tools/scalap/MetaParser.scala b/src/scalap/scala/tools/scalap/MetaParser.scala new file mode 100644 index 0000000000..1d3bf419db --- /dev/null +++ b/src/scalap/scala/tools/scalap/MetaParser.scala @@ -0,0 +1,184 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: MetaParser.scala 5838 2006-02-23 17:54:21Z michelou $ + +package scala.tools.scalap + +import java.io._ +import java.util._ + + +/** a parser class for parsing meta type information in classfiles + * generated by pico. + */ +class MetaParser(meta: String) { + val scanner = new StringTokenizer(meta, "()[], \t<;", true) + var token: String = _ + val res = new StringBuffer + + private def nextToken: String = { + do { + token = scanner.nextToken().trim() + } while (token.length() == 0) + token + } + + protected def parseType: Unit = { + if (token.startsWith("?")) + res.append(token.substring(1)) + else + res.append(token) + nextToken + if (token == "[") { + do { + res.append(if (token == ",") ", " else "[") + nextToken + parseType + } while (token == ",") + nextToken + res.append("]") + } + } + + def parse: Option[String] = + if (scanner.hasMoreTokens()) { + nextToken + try { + if (!scanner.hasMoreTokens()) + None + else if (token == "class") + Some(parseMetaClass) + else if (token == "method") + Some(parseMetaMethod) + else if (token == "field") + Some(parseMetaField) + else if (token == "constr") + Some(parseConstrField) + else + None; + } catch { + case _ => None + } + } else + None; + + protected def parseMetaClass: String = { + nextToken + if (token == "[") { + do { + res.append(if (token == "[") "[" else ", ") + nextToken + if (token == "+") { + nextToken + res.append('+') + } else if (token == "-") { + nextToken + res.append('-') + } + res.append(token.substring(1)) + nextToken + if (token == "<") { + nextToken + res.append(" <: ") + parseType + } + } while (token == ",") + nextToken + res.append("]") + } + if (token == "extends") { + do { + if (token == "extends") + res.append(" extends ") + else + res.append(" with ") + nextToken + parseType + } while (token == "with") + } + res.toString(); + } + + protected def parseMetaMethod: String = { + nextToken + if (token == "[") { + nextToken + if (token == "]") { + nextToken + } else { + var loop = true + res.append("[") + while (loop) { + res.append(token.substring(1)); + nextToken; + if (token == "<") { + nextToken; + res.append(" <: ") + parseType + } + if (token == ",") { + nextToken + res.append(", ") + } else + loop = false + } + nextToken + res.append("]") + } + } + if (token == "(") { + do { + if (token == ",") { + nextToken; + if (token != ")") + res.append(", ") + } else { + nextToken; + res.append("(") + } + if (token != ")") { + if (token == "def") { + nextToken; + res.append("def ") + } + parseType + } + } while (token == ",") + nextToken + res.append("): ") + parseType + } else { + res.append(": ") + parseType + } + res.toString() + } + + protected def parseMetaField: String = { + nextToken + res.append(": ") + parseType + res.toString() + } + + protected def parseConstrField: String = { + nextToken + if (token == "(") { + do { + res.append(if (token == "(") "(" else ", ") + nextToken + if (token != ")") + parseType + } while (token == ",") + nextToken + res.append(")") + } else { + } + res.toString() + } +} diff --git a/src/scalap/scala/tools/scalap/Names.scala b/src/scalap/scala/tools/scalap/Names.scala new file mode 100644 index 0000000000..8bbc30a0b8 --- /dev/null +++ b/src/scalap/scala/tools/scalap/Names.scala @@ -0,0 +1,97 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2006, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ +** +*/ + +// $Id: Names.scala 5838 2006-02-23 17:54:21 +0000 (Thu, 23 Feb 2006) michelou $ + +package scala.tools.scalap + + +object Names { + + val operatorName = new Array[String](128) + operatorName('$') = "$" + operatorName('~') = "$tilde" + operatorName('=') = "$eq" + operatorName('<') = "$less" + operatorName('>') = "$greater" + operatorName('!') = "$bang" + operatorName('#') = "$hash" + operatorName('%') = "$percent" + operatorName('^') = "$up" + operatorName('&') = "$amp" + operatorName('|') = "$bar" + operatorName('*') = "$times" + operatorName('/') = "$div" + operatorName('\\') = "$bslash" + operatorName('+') = "$plus" + operatorName('-') = "$minus" + operatorName(':') = "$colon" + + /** Replace operator symbols by corresponding "$op_name" in names. + */ + def encode(name: String): String = { + var i = 0 + val len = name.length() + val res = new StringBuffer() + while (i < len) { + val c = name.charAt(i) + if (c < 128) { + val nop = operatorName(c) + if (nop == null) + res.append(c) + else + res.append(nop) + } else + res.append(c) + i = i + 1 + } + res.toString() + } + + /** Replace "$op_name" by corresponding operator symbols in names. + */ + def decode(name: String): String = { + var i = 0 + val len = name.length() + val res = new StringBuffer() + while (i < len) { + val c = name.charAt(i) + if (c == '$') { + var j = len + while (j > i) { + val prefix = name.substring(i, j) + val c = lookup(prefix) + if (c != null) { + i = j + res.append(c) + } else + j = j - 1 + } + } else { + i = i + 1 + res.append(c) + } + } + res.toString() + } + + /** Looks up the array entry for the operator name. + */ + def lookup(string: String): String = { + var i = 0 + var res: String = null + while (i < 128) { + if (string.equals(operatorName(i))) { + res = String.valueOf(i.asInstanceOf[Char]) + i = 128 + } + i = i + 1 + } + res + } + +} diff --git a/src/scalap/scala/tools/scalap/Properties.scala b/src/scalap/scala/tools/scalap/Properties.scala new file mode 100644 index 0000000000..1c23626aae --- /dev/null +++ b/src/scalap/scala/tools/scalap/Properties.scala @@ -0,0 +1,50 @@ +/* ___ ____ ___ __ ___ ___ +** / _// __// _ | / / / _ | / _ \ Scala classfile decoder +** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2007, LAMP/EPFL +** /____/\___/_/ |_/____/_/ |_/_/ http://scala-lang.org/ +** +*/ + +// $Id: Properties.scala 9834 2007-01-31 16:31:25Z michelou $ + +package scala.tools.scalap + +/** A utility to load the compiler properties from a Java properties file + * included in the jar. + */ +object Properties { + + /** The name of the properties file */ + private val propFilename = "/decoder.properties" + + /** The loaded properties */ + private val props = { + val props = new java.util.Properties + val stream = classOf[Classfile].getResourceAsStream(propFilename) + if (stream != null) + props.load(stream) + props + } + + /** The version number of the jar this was loaded from, or + * "(unknown)" if it cannot be determined. + */ + val versionString: String = { + val defaultString = "(unknown)" + "version " + props.getProperty("version.number") + } + + val copyrightString: String = { + val defaultString = "(c) 2002-2006 LAMP/EPFL" + props.getProperty("copyright.string", defaultString) + } + + val scalaHome: String = + System.getProperty("scala.home") + + val cmdName: String = { + val isWin = System.getProperty("os.name") startsWith "Windows" + if (isWin) "scala.bat" else "scala" + } + +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Arrows.scala b/src/scalap/scala/tools/scalap/scalax/rules/Arrows.scala new file mode 100644 index 0000000000..2bc2cf5380 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Arrows.scala @@ -0,0 +1,35 @@ +package scala.tools.scalap.scalax.rules + +trait Arrows extends UnitFunctors { + type Arr[-A, +B] <: Arrow[A, B] + type M[+B] = Arr[Nothing, B] + + def arrow[A, B](f : A => B) : Arr[A, B] + def diag[A] = arrow[A, (A, A)] { a => (a, a) } + + override def unit[B](b : => B) : M[B] = arrow { any : Any => b } + + trait Arrow[-A, +B] extends Functor[B] { this : Arr[A, B] => + + def map[C](f : B => C) = comp(arrow(f)) + def comp[C](bc : => Arr[B, C]) : Arr[A, C] + def fst[C] : Arr[(A, C), (B, C)] + } +} + +trait ApplicativeArrows extends Arrows { + type Arr[-A, +B] <: ApplicativeArrow[A, B] + + def app[A, B] : Arr[(Arr[A, B], A), B] + + trait ApplicativeArrow[-A, +B] extends Arrow[A, B] { self : Arr[A, B] => + def flatMap[SubA <: A, C](f : B => Arr[SubA, C]) : Arr[SubA, C] = + diag[SubA].comp(map(f).fst[SubA]).comp(app[SubA, C]) + } +} + +trait ArrowMonads extends ApplicativeArrows with Monads { + type Arr[-A, +B] <: ApplicativeArrow[A, B] with Monad[B] + + override def unit[A](a : => A) : M[A] = arrow[Unit, A](Unit => a) +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala b/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala new file mode 100644 index 0000000000..b9de5c71da --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Functors.scala @@ -0,0 +1,79 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +trait Functor[+A] { + type M[+A] <: Functor[A] + def map[B](f : A => B) : M[B] +} + +trait Filter[+A] { + type M[+A] <: Filter[A] + def filter(f : A => Boolean) : M[A] +} + +trait Plus[+A] { + type M[+A] <: Plus[A] + def plus[B >: A](other : => M[B]) : M[B] +} + +trait OrElse[+A] { + type M[+A] <: OrElse[A] + def orElse[B >: A](other : => M[B]) : M[B] +} + +trait Units { + type M[+A] + def unit : M[Unit] + def unit[A](a : => A) : M[A] +} + +trait Zero { + type M[+A] + def zero : M[Nothing] +} + +trait Functors { + type M[+A] <: Functor[A] + + trait Functor[+A] extends rules.Functor[A] { this : M[A] => + type M[+A] = Functors.this.M[A] + } + + trait ZeroFunctor extends Functor[Nothing] { this : M[Nothing] => + override def map[B](f : Nothing => B) : M[B] = this + def filter(f : Nothing => Boolean) : M[Nothing] = this + def plus[B](other : => M[B]) : M[B] = other + def orElse[B](other : => M[B]) : M[B] = other + } +} + +/** One of the 'unit' definitions must be overriden in concrete subclasses */ +trait UnitFunctors extends Units with Functors { + def unit : M[Unit] = unit(()) + def unit[A](a : => A) : M[A] = unit map { Unit => a } +} + + +trait Monoidals extends UnitFunctors { + type M[+A] <: Monoidal[A] + + implicit def app[A, B](fab : M[A => B]) = (fa : M[A]) => fa applyTo fab + implicit def appUnit[A, B](a2b : A => B) = app(unit(a2b)) + + /** One of 'and' and 'applyTo' definitions must be overriden in concrete subclasses */ + trait Monoidal[+A] extends Functor[A] { self : M[A] => + def and[B](fb : => M[B]) : M[(A, B)] = ((a : A) => (b : B) => (a, b))(this)(fb) + def applyTo[B](fab : M[A => B]) : M[B] = fab and this map { case (f, a) => f(a) } + } +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Input.scala b/src/scalap/scala/tools/scalap/scalax/rules/Input.scala new file mode 100644 index 0000000000..af4ac39464 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Input.scala @@ -0,0 +1,66 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +trait Input[+A] extends Iterable[A] { + + def next : Result[Input[A], A, Nothing] + def index : Int + + def elements = new Iterator[A] { + private var input : Input[A] = Input.this + private var result = input.next + + def hasNext = result != Failure + def next = { + val Success(input, value) = result + this.input = input + this.result = input.next + value + } + } +} + + +class ArrayInput[A](val array : Array[A], val index : Int) extends Input[A] { + def this(array : Array[A]) = this(array, 0) + + lazy val next : Result[ArrayInput[A], A, Nothing] = if (index >= array.length) Failure + else Success(new ArrayInput[A](array, index + 1), array(index)) + + override lazy val toString = elements.mkString("\"", "", "\"") +} + + +class IterableInput[A](iterator : Iterator[A], val index : Int) extends Input[A] { + def this(iterable : Iterable[A]) = this(iterable.elements, 0) + + lazy val next : Result[IterableInput[A], A, Nothing] = if (!iterator.hasNext) Failure + else Success(new IterableInput(iterator, index + 1), iterator.next) + + override lazy val toString = elements.mkString("\"", "", "\"") +} + + +/** View one type of input as another based on a transformation rule */ +class View[A, B]( + transform : Input[A] => Result[Input[A], B, Nothing], + val input : Input[A], + val index : Int) + extends Input[B] { + + def next : Result[Input[B], B, Nothing] = transform(input) match { + case Success(context, b) => Success(new View(transform, context, index + 1), b) + case _ => Failure + } +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala b/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala new file mode 100644 index 0000000000..0bcae2bded --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Memoisable.scala @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +import scala.collection.mutable.HashMap + +trait MemoisableRules extends Rules { + def memo[In <: Memoisable, Out, A, X](key : AnyRef)(toRule : => In => Result[Out, A, X]) = { + lazy val rule = toRule + from[In] { in => in.memo(key, rule(in)) } + } + + override def ruleWithName[In, Out, A, X](name : String, f : In => rules.Result[Out, A, X]) = super.ruleWithName(name, (in : In) => in match { + case s : Memoisable => s.memo(name, f(in)) + case _ => f(in) + }) +} + +trait Memoisable { + def memo[A](key : AnyRef, a : => A) : A +} + + +object DefaultMemoisable { + var debug = false +} + +trait DefaultMemoisable extends Memoisable { + protected val map = new HashMap[AnyRef, Any] + + def memo[A](key : AnyRef, a : => A) = { + map.getOrElseUpdate(key, compute(key, a)).asInstanceOf[A] + } + + protected def compute[A](key : AnyRef, a : => A) = a match { + case success : Success[_, _] => onSuccess(key, success); success + case other => + if(DefaultMemoisable.debug) println(key + " -> " + other) + other + } + + protected def onSuccess[S, T](key : AnyRef, result : Success[S, T]) { + val Success(out, t) = result + if(DefaultMemoisable.debug) println(key + " -> " + t + " (" + out + ")") + } +} + + + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Monad.scala b/src/scalap/scala/tools/scalap/scalax/rules/Monad.scala new file mode 100644 index 0000000000..1b03b5d3fa --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Monad.scala @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +trait Monad[+A] extends Functor[A] { + type M[+A] <: Monad[A] + def flatMap[B](f : A => M[B]) : M[B] +} + +trait Monads extends UnitFunctors { + type M[+A] <: Monad[A] + + trait Monad[+A] extends Functor[A] with rules.Monad[A] { this : M[A] => + def map[B](f : A => B) = flatMap { a => unit(f(a)) } + } + + trait ZeroMonad extends Monad[Nothing] with ZeroFunctor { this : M[Nothing] => + def flatMap[B](f : Nothing => M[B]) : M[B] = this + } +} + + +trait StateReader extends Monads { + type S + + def get : M[S] + def read[A](f : S => A) : M[A] + def set(s : => S) : M[S] + def update(f : S => S) : M[S] +} + + + + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Result.scala b/src/scalap/scala/tools/scalap/scalax/rules/Result.scala new file mode 100644 index 0000000000..cb8ec4b290 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Result.scala @@ -0,0 +1,70 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules; + +/** Represents the combined value of two rules applied in sequence. + * + * @see the Scala parser combinator + */ +case class ~[+A, +B](_1 : A, _2 : B) { + override def toString = "(" + _1 + " ~ " + _2 + ")" +} + + +sealed abstract class Result[+Out, +A, +X] { + def out : Out + def value : A + def error : X + + implicit def toOption : Option[A] + + def map[B](f : A => B) : Result[Out, B, X] + def mapOut[Out2](f : Out => Out2) : Result[Out2, A, X] + def map[Out2, B](f : (Out, A) => (Out2, B)) : Result[Out2, B, X] + def flatMap[Out2, B](f : (Out, A) => Result[Out2, B, Nothing]) : Result[Out2, B, X] + def orElse[Out2 >: Out, B >: A](other : => Result[Out2, B, Nothing]) : Result[Out2, B, X] +} + +case class Success[+Out, +A](out : Out, value : A) extends Result[Out, A, Nothing] { + def error = throw new ScalaSigParserError("No error") + + def toOption = Some(value) + + def map[B](f : A => B) = Success(out, f(value)) + def mapOut[Out2](f : Out => Out2) = Success(f(out), value) + def map[Out2, B](f : (Out, A) => (Out2, B)) = f(out, value) match { case (out2, b) => Success(out2, b) } + def flatMap[Out2, B](f : (Out, A) => Result[Out2, B, Nothing]) = f(out, value) + def orElse[Out2 >: Out, B >: A](other : => Result[Out2, B, Nothing]) = this +} + +sealed abstract class NoSuccess[+X] extends Result[Nothing, Nothing, X] { + def out = throw new ScalaSigParserError("No output") + def value = throw new ScalaSigParserError("No value") + + def toOption = None + + def map[B](f : Nothing => B) = this + def mapOut[Out2](f : Nothing => Out2) = this + def map[Out2, B](f : (Nothing, Nothing) => (Out2, B)) = this + def flatMap[Out2, B](f : (Nothing, Nothing) => Result[Out2, B, Nothing]) = this + def orElse[Out2, B](other : => Result[Out2, B, Nothing]) = other +} + +case object Failure extends NoSuccess[Nothing] { + def error = throw new ScalaSigParserError("No error") +} + +case class ScalaSigParserError(msg: String) extends RuntimeException(msg) + +case class Error[+X](error : X) extends NoSuccess[X] { +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala b/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala new file mode 100644 index 0000000000..f540d9ad8d --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Rule.scala @@ -0,0 +1,175 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +/** A Rule is a function from some input to a Result. The result may be: + * <ul> + * <li>Success, with a value of some type and an output that may serve as the input to subsequent rules.</li> + * <li>Failure. A failure may result in some alternative rule being applied.</li> + * <li>Error. No further rules should be attempted.</li> + * </ul> + * + * @author Andrew Foggin + * + * Inspired by the Scala parser combinator. + */ +trait Rule[-In, +Out, +A, +X] extends (In => Result[Out, A, X]) { + val factory : Rules + import factory._ + + def as(name : String) = ruleWithName(name, this) + + def flatMap[Out2, B, X2 >: X](fa2ruleb : A => Out => Result[Out2, B, X2]) = mapResult { + case Success(out, a) => fa2ruleb(a)(out) + case Failure => Failure + case err @ Error(_) => err + } + + def map[B](fa2b : A => B) = flatMap { a => out => Success(out, fa2b(a)) } + + def filter(f : A => Boolean) = flatMap { a => out => if(f(a)) Success(out, a) else Failure } + + def mapResult[Out2, B, Y](f : Result[Out, A, X] => Result[Out2, B, Y]) = rule { + in : In => f(apply(in)) + } + + def orElse[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other : => Rule[In2, Out2, A2, X2]) : Rule[In2, Out2, A2, X2] = new Choice[In2, Out2, A2, X2] { + val factory = Rule.this.factory + lazy val choices = Rule.this :: other :: Nil + } + + def orError[In2 <: In] = this orElse(error[In2]) + + def |[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other : => Rule[In2, Out2, A2, X2]) = orElse(other) + + def ^^[B](fa2b : A => B) = map(fa2b) + + def ^^?[B](pf : PartialFunction[A, B]) = filter (pf.isDefinedAt(_)) ^^ pf + + def ??(pf : PartialFunction[A, Any]) = filter (pf.isDefinedAt(_)) + + def -^[B](b : B) = map { any => b } + + /** Maps an Error */ + def !^[Y](fx2y : X => Y) = mapResult { + case s @ Success(_, _) => s + case Failure => Failure + case Error(x) => Error(fx2y(x)) + } + + def >>[Out2, B, X2 >: X](fa2ruleb : A => Out => Result[Out2, B, X2]) = flatMap(fa2ruleb) + + def >->[Out2, B, X2 >: X](fa2resultb : A => Result[Out2, B, X2]) = flatMap { a => any => fa2resultb(a) } + + def >>?[Out2, B, X2 >: X](pf : PartialFunction[A, Rule[Out, Out2, B, X2]]) = filter(pf isDefinedAt _) flatMap pf + + def >>&[B, X2 >: X](fa2ruleb : A => Out => Result[Any, B, X2]) = flatMap { a => out => fa2ruleb(a)(out) mapOut { any => out } } + + def ~[Out2, B, X2 >: X](next : => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next) yield new ~(a, b) + + def ~-[Out2, B, X2 >: X](next : => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next) yield a + + def -~[Out2, B, X2 >: X](next : => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next) yield b + + def ~++[Out2, B >: A, X2 >: X](next : => Rule[Out, Out2, Seq[B], X2]) = for (a <- this; b <- next) yield a :: b.toList + + /** Apply the result of this rule to the function returned by the next rule */ + def ~>[Out2, B, X2 >: X](next : => Rule[Out, Out2, A => B, X2]) = for (a <- this; fa2b <- next) yield fa2b(a) + + /** Apply the result of this rule to the function returned by the previous rule */ + def <~:[InPrev, B, X2 >: X](prev : => Rule[InPrev, In, A => B, X2]) = for (fa2b <- prev; a <- this) yield fa2b(a) + + def ~![Out2, B, X2 >: X](next : => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next orError) yield new ~(a, b) + + def ~-![Out2, B, X2 >: X](next : => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next orError) yield a + + def -~![Out2, B, X2 >: X](next : => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next orError) yield b + + def -[In2 <: In](exclude : => Rule[In2, Any, Any, Any]) = !exclude -~ this + + /** ^~^(f) is equivalent to ^^ { case b1 ~ b2 => f(b1, b2) } + */ + def ^~^[B1, B2, B >: A <% B1 ~ B2, C](f : (B1, B2) => C) = map { a => + (a : B1 ~ B2) match { case b1 ~ b2 => f(b1, b2) } + } + + /** ^~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 => f(b1, b2, b3) } + */ + def ^~~^[B1, B2, B3, B >: A <% B1 ~ B2 ~ B3, C](f : (B1, B2, B3) => C) = map { a => + (a : B1 ~ B2 ~ B3) match { case b1 ~ b2 ~ b3 => f(b1, b2, b3) } + } + + /** ^~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 => f(b1, b2, b3, b4) } + */ + def ^~~~^[B1, B2, B3, B4, B >: A <% B1 ~ B2 ~ B3 ~ B4, C](f : (B1, B2, B3, B4) => C) = map { a => + (a : B1 ~ B2 ~ B3 ~ B4) match { case b1 ~ b2 ~ b3 ~ b4 => f(b1, b2, b3, b4) } + } + + /** ^~~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 ~ b5 => f(b1, b2, b3, b4, b5) } + */ + def ^~~~~^[B1, B2, B3, B4, B5, B >: A <% B1 ~ B2 ~ B3 ~ B4 ~ B5, C](f : (B1, B2, B3, B4, B5) => C) = map { a => + (a : B1 ~ B2 ~ B3 ~ B4 ~ B5) match { case b1 ~ b2 ~ b3 ~ b4 ~ b5 => f(b1, b2, b3, b4, b5) } + } + + /** ^~~~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 => f(b1, b2, b3, b4, b5, b6) } + */ + def ^~~~~~^[B1, B2, B3, B4, B5, B6, B >: A <% B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6, C](f : (B1, B2, B3, B4, B5, B6) => C) = map { a => + (a : B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6) match { case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 => f(b1, b2, b3, b4, b5, b6) } + } + + /** ^~~~~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 => f(b1, b2, b3, b4, b5, b6) } + */ + def ^~~~~~~^[B1, B2, B3, B4, B5, B6, B7, B >: A <% B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6 ~ B7, C](f : (B1, B2, B3, B4, B5, B6, B7) => C) = map { a => + (a : B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6 ~ B7) match { case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 ~b7 => f(b1, b2, b3, b4, b5, b6, b7) } + } + + /** >~>(f) is equivalent to >> { case b1 ~ b2 => f(b1, b2) } + */ + def >~>[Out2, B1, B2, B >: A <% B1 ~ B2, C, X2 >: X](f : (B1, B2) => Out => Result[Out2, C, X2]) = flatMap { a => + (a : B1 ~ B2) match { case b1 ~ b2 => f(b1, b2) } + } + + /** ^-^(f) is equivalent to ^^ { b2 => b1 => f(b1, b2) } + */ + def ^-^ [B1, B2 >: A, C](f : (B1, B2) => C) = map { b2 : B2 => b1 : B1 => f(b1, b2) } + + /** ^~>~^(f) is equivalent to ^^ { case b2 ~ b3 => b1 => f(b1, b2, b3) } + */ + def ^~>~^ [B1, B2, B3, B >: A <% B2 ~ B3, C](f : (B1, B2, B3) => C) = map { a => + (a : B2 ~ B3) match { case b2 ~ b3 => b1 : B1 => f(b1, b2, b3) } + } +} + + +trait Choice[-In, +Out, +A, +X] extends Rule[In, Out, A, X] { + def choices : List[Rule[In, Out, A, X]] + + def apply(in : In) = { + def oneOf(list : List[Rule[In, Out, A, X]]) : Result[Out, A, X] = list match { + case Nil => Failure + case first :: rest => first(in) match { + case Failure => oneOf(rest) + case result => result + } + } + oneOf(choices) + } + + override def orElse[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other : => Rule[In2, Out2, A2, X2]) : Rule[In2, Out2, A2, X2] = new Choice[In2, Out2, A2, X2] { + val factory = Choice.this.factory + lazy val choices = Choice.this.choices ::: other :: Nil + } +} + + + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala b/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala new file mode 100644 index 0000000000..fa6f959384 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala @@ -0,0 +1,144 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +trait Name { + def name : String + override def toString = name +} + +/** A factory for rules. + * + * @author Andrew Foggin + * + * Inspired by the Scala parser combinator. + */ +trait Rules { + implicit def rule[In, Out, A, X](f : In => Result[Out, A, X]) : Rule[In, Out, A, X] = new DefaultRule(f) + + implicit def inRule[In, Out, A, X](rule : Rule[In, Out, A, X]) : InRule[In, Out, A, X] = new InRule(rule) + implicit def seqRule[In, A, X](rule : Rule[In, In, A, X]) : SeqRule[In, A, X] = new SeqRule(rule) + + def from[In] = new { + def apply[Out, A, X](f : In => Result[Out, A, X]) = rule(f) + } + + def state[s] = new StateRules { + type S = s + val factory = Rules.this + } + + def success[Out, A](out : Out, a : A) = rule { in : Any => Success(out, a) } + + def failure = rule { in : Any => Failure } + + def error[In] = rule { in : In => Error(in) } + def error[X](err : X) = rule { in : Any => Error(err) } + + def oneOf[In, Out, A, X](rules : Rule[In, Out, A, X] *) : Rule[In, Out, A, X] = new Choice[In, Out, A, X] { + val factory = Rules.this + val choices = rules.toList + } + + def ruleWithName[In, Out, A, X](_name : String, f : In => Result[Out, A, X]) : Rule[In, Out, A, X] with Name = + new DefaultRule(f) with Name { + val name = _name + } + + class DefaultRule[In, Out, A, X](f : In => Result[Out, A, X]) extends Rule[In, Out, A, X] { + val factory = Rules.this + def apply(in : In) = f(in) + } + + /** Converts a rule into a function that throws an Exception on failure. */ + def expect[In, Out, A, Any](rule : Rule[In, Out, A, Any]) : In => A = (in) => rule(in) match { + case Success(_, a) => a + case Failure => throw new ScalaSigParserError("Unexpected failure") + case Error(x) => throw new ScalaSigParserError("Unexpected error: " + x) + } +} + +/** A factory for rules that apply to a particular context. + * + * @requires S the context to which rules apply. + * + * @author Andrew Foggin + * + * Inspired by the Scala parser combinator. + */ +trait StateRules { + type S + type Rule[+A, +X] = rules.Rule[S, S, A, X] + + val factory : Rules + import factory._ + + def apply[A, X](f : S => Result[S, A, X]) = rule(f) + + def unit[A](a : => A) = apply { s => Success(s, a) } + def read[A](f : S => A) = apply { s => Success(s, f(s)) } + + def get = apply { s => Success(s, s) } + def set(s : => S) = apply { oldS => Success(s, oldS) } + + def update(f : S => S) = apply { s => Success(s, f(s)) } + + def nil = unit(Nil) + def none = unit(None) + + /** Create a rule that suceeds if f(in) is true. */ + def cond(f : S => Boolean) = get filter f + + /** Create a rule that succeeds if all of the given rules succeed. + @param rules the rules to apply in sequence. + */ + def allOf[A, X](rules : Seq[Rule[A, X]]) = { + def rep(in : S, rules : List[Rule[A, X]], results : List[A]) : Result[S, List[A], X] = { + rules match { + case Nil => Success(in, results.reverse) + case rule::tl => rule(in) match { + case Failure => Failure + case Error(x) => Error(x) + case Success(out, v) => rep(out, tl, v::results) + } + } + } + in : S => rep(in, rules.toList, Nil) + } + + + /** Create a rule that succeeds with a list of all the provided rules that succeed. + @param rules the rules to apply in sequence. + */ + def anyOf[A, X](rules : Seq[Rule[A, X]]) = allOf(rules.map(_ ?)) ^^ { opts => opts.flatMap(x => x) } + + /** Repeatedly apply a rule from initial value until finished condition is met. */ + def repeatUntil[T, X](rule : Rule[T => T, X])(finished : T => Boolean)(initial : T) = apply { + // more compact using HoF but written this way so it's tail-recursive + def rep(in : S, t : T) : Result[S, T, X] = { + if (finished(t)) Success(in, t) + else rule(in) match { + case Success(out, f) => rep(out, f(t)) + case Failure => Failure + case Error(x) => Error(x) + } + } + in => rep(in, initial) + } + + +} + +trait RulesWithState extends Rules with StateRules { + val factory = this +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala b/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala new file mode 100644 index 0000000000..d32300f832 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/SeqRule.scala @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------------- +// +// Scalax - The Scala Community Library +// Copyright (c) 2005-8 The Scalax Project. All rights reserved. +// +// The primary distribution site is http://scalax.scalaforge.org/ +// +// This software is released under the terms of the Revised BSD License. +// There is NO WARRANTY. See the file LICENSE for the full text. +// +// ----------------------------------------------------------------------------- + +package scala.tools.scalap.scalax.rules + +/** + * A workaround for the difficulties of dealing with + * a contravariant 'In' parameter type... + */ +class InRule[In, +Out, +A, +X](rule : Rule[In, Out, A, X]) { + + def mapRule[Out2, B, Y](f : Result[Out, A, X] => In => Result[Out2, B, Y]) : Rule[In, Out2, B, Y] = rule.factory.rule { + in : In => f(rule(in))(in) + } + + /** Creates a rule that suceeds only if the original rule would fail on the given context. */ + def unary_! : Rule[In, In, Unit, Nothing] = mapRule { + case Success(_, _) => in : In => Failure + case _ => in : In => Success(in, ()) + } + + /** Creates a rule that succeeds if the original rule succeeds, but returns the original input. */ + def & : Rule[In, In, A, X] = mapRule { + case Success(_, a) => in : In => Success(in, a) + case Failure => in : In => Failure + case Error(x) => in : In => Error(x) + } +} + +class SeqRule[S, +A, +X](rule : Rule[S, S, A, X]) { + import rule.factory._ + + def ? = rule mapRule { + case Success(out, a) => in : S => Success(out, Some(a)) + case Failure => in : S => Success(in, None) + case Error(x) => in : S => Error(x) + } + + /** Creates a rule that always succeeds with a Boolean value. + * Value is 'true' if this rule succeeds, 'false' otherwise */ + def -? = ? map { _ isDefined } + + def * = from[S] { + // tail-recursive function with reverse list accumulator + def rep(in : S, acc : List[A]) : Result[S, List[A], X] = rule(in) match { + case Success(out, a) => rep(out, a :: acc) + case Failure => Success(in, acc.reverse) + case err : Error[_] => err + } + in => rep(in, Nil) + } + + def + = rule ~++ * + + def ~>?[B >: A, X2 >: X](f : => Rule[S, S, B => B, X2]) = for (a <- rule; fs <- f?) yield fs.foldLeft[B](a) { (b, f) => f(b) } + + def ~>*[B >: A, X2 >: X](f : => Rule[S, S, B => B, X2]) = for (a <- rule; fs <- f*) yield fs.foldLeft[B](a) { (b, f) => f(b) } + + def ~*~[B >: A, X2 >: X](join : => Rule[S, S, (B, B) => B, X2]) = { + this ~>* (for (f <- join; a <- rule) yield f(_ : B, a)) + } + + /** Repeats this rule one or more times with a separator (which is discarded) */ + def +/[X2 >: X](sep : => Rule[S, S, Any, X2]) = rule ~++ (sep -~ rule *) + + /** Repeats this rule zero or more times with a separator (which is discarded) */ + def */[X2 >: X](sep : => Rule[S, S, Any, X2]) = +/(sep) | state[S].nil + + def *~-[Out, X2 >: X](end : => Rule[S, Out, Any, X2]) = (rule - end *) ~- end + def +~-[Out, X2 >: X](end : => Rule[S, Out, Any, X2]) = (rule - end +) ~- end + + /** Repeats this rule num times */ + def times(num : Int) : Rule[S, S, Seq[A], X] = from[S] { + val result = new Array[A](num) + // more compact using HoF but written this way so it's tail-recursive + def rep(i : Int, in : S) : Result[S, Seq[A], X] = { + if (i == num) Success(in, result) + else rule(in) match { + case Success(out, a) => { + result(i) = a + rep(i + 1, out) + } + case Failure => Failure + case err : Error[_] => err + } + } + in => rep(0, in) + } +} + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala new file mode 100644 index 0000000000..525217ed83 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala @@ -0,0 +1,221 @@ +package scala.tools.scalap.scalax.rules.scalasig + + +import java.io.IOException + +import scala._ +import scala.Predef._ + +import scalax.rules.Error + +object ByteCode { + def apply(bytes : Array[Byte]) = new ByteCode(bytes, 0, bytes.length) + + def forClass(clazz : Class[_]) = { + val name = clazz.getName + val subPath = name.substring(name.lastIndexOf('.') + 1) + ".class" + val in = clazz.getResourceAsStream(subPath) + + try { + var rest = in.available() + val bytes = new Array[Byte](rest) + while (rest > 0) { + val res = in.read(bytes, bytes.length - rest, rest) + if (res == -1) throw new IOException("read error") + rest -= res + } + ByteCode(bytes) + + } finally { + in.close() + } + } +} + +/** Represents a chunk of raw bytecode. Used as input for the parsers + */ +class ByteCode(val bytes : Array[Byte], val pos : Int, val length : Int) { + + assert(pos >= 0 && length >= 0 && pos + length <= bytes.length) + + def nextByte = if (length == 0) Failure else Success(drop(1), bytes(pos)) + def next(n : Int) = if (length >= n) Success(drop(n), take(n)) else Failure + + def take(n : Int) = new ByteCode(bytes, pos, n) + def drop(n : Int) = new ByteCode(bytes, pos + n, length - n) + + def fold[X](x : X)(f : (X, Byte) => X) : X = { + var result = x + var i = pos + while (i < pos + length) { + result = f(result, bytes(i)) + i += 1 + } + result + } + + override def toString = length + " bytes" + + def toInt = fold(0) { (x, b) => (x << 8) + (b & 0xFF)} + def toLong = fold(0L) { (x, b) => (x << 8) + (b & 0xFF)} + + // NOTE the UTF8 decoder in the Scala compiler is broken for pos > 0 + // TODO figure out patch and submit + def toUTF8String = { + val sb = new StringBuilder(length) + var i = pos + val end = pos + length + while (i < end) { + var b = bytes(i) & 0xFF + i += 1 + if (b >= 0xE0) { + b = ((b & 0x0F) << 12) | (bytes(i) & 0x3F) << 6 + b = b | (bytes(i+1) & 0x3F) + i += 2 + } else if (b >= 0xC0) { + b = ((b & 0x1F) << 6) | (bytes(i) & 0x3F) + i += 1 + } + sb + b.toChar + } + sb.toString + } + + def byte(i : Int) = bytes(pos) & 0xFF +} + +/** Provides rules for parsing byte-code. +*/ +trait ByteCodeReader extends RulesWithState { + type S = ByteCode + type Parser[A] = Rule[A, String] + + val byte = apply(_ nextByte) + + val u1 = byte ^^ (_ & 0xFF) + val u2 = bytes(2) ^^ (_ toInt) + val u4 = bytes(4) ^^ (_ toInt) // should map to Long?? + + def bytes(n : Int) = apply(_ next n) + + + +} + +object ClassFileParser extends ByteCodeReader { + def parse(byteCode : ByteCode) = expect(classFile)(byteCode) + + val magicNumber = (u4 filter (_ == 0xCAFEBABE)) | error("Not a valid class file") + val version = u2 ~ u2 ^^ { case minor ~ major => (major, minor) } + val constantPool = (u2 ^^ ConstantPool) >> repeatUntil(constantPoolEntry)(_ isFull) + + // NOTE currently most constants just evaluate to a string description + // TODO evaluate to useful values + val utf8String = (u2 >> bytes) ^^ add1 { raw => pool => raw.toUTF8String } + val intConstant = u4 ^^ add1 { x => pool => x } + val floatConstant = bytes(4) ^^ add1 { raw => pool => "Float: TODO" } + val longConstant = bytes(8) ^^ add2 { raw => pool => raw.toLong } + val doubleConstant = bytes(8) ^^ add2 { raw => pool => "Double: TODO" } + val classRef = u2 ^^ add1 { x => pool => "Class: " + pool(x) } + val stringRef = u2 ^^ add1 { x => pool => "String: " + pool(x) } + val fieldRef = memberRef("Field") + val methodRef = memberRef("Method") + val interfaceMethodRef = memberRef("InterfaceMethod") + val nameAndType = u2 ~ u2 ^^ add1 { case name ~ descriptor => pool => "NameAndType: " + pool(name) + ", " + pool(descriptor) } + + val constantPoolEntry = u1 >> { + case 1 => utf8String + case 3 => intConstant + case 4 => floatConstant + case 5 => longConstant + case 6 => doubleConstant + case 7 => classRef + case 8 => stringRef + case 9 => fieldRef + case 10 => methodRef + case 11 => interfaceMethodRef + case 12 => nameAndType + } + + val interfaces = u2 >> u2.times + + val attribute = u2 ~ (u4 >> bytes) ^~^ Attribute + val attributes = u2 >> attribute.times + + val field = u2 ~ u2 ~ u2 ~ attributes ^~~~^ Field + val fields = u2 >> field.times + + val method = u2 ~ u2 ~ u2 ~ attributes ^~~~^ Method + val methods = u2 >> method.times + + val header = magicNumber -~ u2 ~ u2 ~ constantPool ~ u2 ~ u2 ~ u2 ~ interfaces ^~~~~~~^ ClassFileHeader + val classFile = header ~ fields ~ methods ~ attributes ~- !u1 ^~~~^ ClassFile + + // TODO create a useful object, not just a string + def memberRef(description : String) = u2 ~ u2 ^^ add1 { + case classRef ~ nameAndTypeRef => pool => description + ": " + pool(classRef) + ", " + pool(nameAndTypeRef) + } + + def add1[T](f : T => ConstantPool => Any)(raw : T)(pool : ConstantPool) = pool add f(raw) + def add2[T](f : T => ConstantPool => Any)(raw : T)(pool : ConstantPool) = pool add f(raw) add { pool => "<empty>" } +} + +case class ClassFile( + header : ClassFileHeader, + fields : Seq[Field], + methods : Seq[Method], + attributes : Seq[Attribute]) { + + def majorVersion = header.major + def minorVersion = header.minor + + def className = constant(header.classIndex) + def superClass = constant(header.superClassIndex) + def interfaces = header.interfaces.map(constant) + + def constant(index : Int) = header.constants(index) + + def attribute(name : String) = attributes.find { attrib => constant(attrib.nameIndex) == name } +} + +case class Attribute(nameIndex : Int, byteCode : ByteCode) +case class Field(flags : Int, nameIndex : Int, descriptorIndex : Int, attributes : Seq[Attribute]) +case class Method(flags : Int, nameIndex : Int, descriptorIndex : Int, attributes : Seq[Attribute]) + +case class ClassFileHeader( + minor : Int, + major : Int, + constants : ConstantPool, + flags : Int, + classIndex : Int, + superClassIndex : Int, + interfaces : Seq[Int]) { + + def constant(index : Int) = constants(index) +} + +case class ConstantPool(len : Int) { + val size = len - 1 + + private val buffer = new scala.collection.mutable.ArrayBuffer[ConstantPool => Any] + private val values = Array.make[Option[Any]](size, None) + + def isFull = buffer.length >= size + + def apply(index : Int) = { + // Note constant pool indices are 1-based + val i = index - 1 + values(i) getOrElse { + val value = buffer(i)(this) + buffer(i) = null + values(i) = Some(value) + value + } + } + + def add(f : ConstantPool => Any) = { + buffer + f + this + } +} + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Flags.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Flags.scala new file mode 100644 index 0000000000..c567cdf715 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Flags.scala @@ -0,0 +1,66 @@ +package scala.tools.scalap.scalax.rules.scalasig + +trait Flags { + def hasFlag(flag : Long) : Boolean + + def isImplicit = hasFlag(0x00000001) + def isFinal = hasFlag(0x00000002) + def isPrivate = hasFlag(0x00000004) + def isProtected = hasFlag(0x00000008) + + def isSealed = hasFlag(0x00000010) + def isOverride = hasFlag(0x00000020) + def isCase = hasFlag(0x00000040) + def isAbstract = hasFlag(0x00000080) + + def isDeferred = hasFlag(0x00000100) + def isMethod = hasFlag(0x00000200) + def isModule = hasFlag(0x00000400) + def isInterface = hasFlag(0x00000800) + + def isMutable = hasFlag(0x00001000) + def isParam = hasFlag(0x00002000) + def isPackage = hasFlag(0x00004000) + def isDeprecated = hasFlag(0x00008000) + + def isCovariant = hasFlag(0x00010000) + def isCaptured = hasFlag(0x00010000) + + def isByNameParam = hasFlag(0x00010000) + def isContravariant = hasFlag(0x00020000) + def isLabel = hasFlag(0x00020000) // method symbol is a label. Set by TailCall + def isInConstructor = hasFlag(0x00020000) // class symbol is defined in this/superclass constructor + + def isAbstractOverride = hasFlag(0x00040000) + def isLocal = hasFlag(0x00080000) + + def isJava = hasFlag(0x00100000) + def isSynthetic = hasFlag(0x00200000) + def isStable = hasFlag(0x00400000) + def isStatic = hasFlag(0x00800000) + + def isCaseAccessor = hasFlag(0x01000000) + def isTrait = hasFlag(0x02000000) + def isBridge = hasFlag(0x04000000) + def isAccessor = hasFlag(0x08000000) + + def isSuperAccessor = hasFlag(0x10000000) + def isParamAccessor = hasFlag(0x20000000) + + def isModuleVar = hasFlag(0x40000000) // for variables: is the variable caching a module value + def isSyntheticMethod = hasFlag(0x40000000) // for methods: synthetic method, but without SYNTHETIC flag + def isMonomorphic = hasFlag(0x40000000) // for type symbols: does not have type parameters + def isLazy = hasFlag(0x80000000L) // symbol is a lazy val. can't have MUTABLE unless transformed by typer + + def isError = hasFlag(0x100000000L) + def isOverloaded = hasFlag(0x200000000L) + def isLifted = hasFlag(0x400000000L) + + def isMixedIn = hasFlag(0x800000000L) + def isExistential = hasFlag(0x800000000L) + + def isExpandedName = hasFlag(0x1000000000L) + def isImplementationClass = hasFlag(0x2000000000L) + def isPreSuper = hasFlag(0x2000000000L) + +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala new file mode 100644 index 0000000000..c660bdd7c3 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala @@ -0,0 +1,269 @@ +package scala.tools.scalap.scalax.rules.scalasig + +object ScalaSigParser { + + def getScalaSig(clazz : Class[_]) : Option[ByteCode] = { + val byteCode = ByteCode.forClass(clazz) + val classFile = ClassFileParser.parse(byteCode) + + /* + println("ClassFile version: " + classFile.majorVersion + "." + classFile.minorVersion) + println("Class: " + classFile.className) + println("Superclass: " + classFile.superClass) + println("Interfaces: " + classFile.interfaces.mkString(", ")) + println("Constant pool:") + val constantPool = classFile.header.constants + for (i <- 1 to constantPool.size) println(i + "\t" + constantPool(i)) + */ + + classFile.attribute("ScalaSig").map(_.byteCode) + } + + def parse(clazz : Class[_]) : Option[ScalaSig] = { + getScalaSig(clazz).map(ScalaSigAttributeParsers.parse) + } +} + +object ScalaSigAttributeParsers extends ByteCodeReader { + def parse(byteCode : ByteCode) = expect(scalaSig)(byteCode) + + val nat = apply { + def natN(in : ByteCode, x : Int) : Result[ByteCode, Int, Nothing] = in.nextByte match { + case Success(out, b) => { + val y = (x << 7) + (b & 0x7f) + if ((b & 0x80) == 0) Success(out, y) else natN(out, y) + } + case _ => Failure + } + in => natN(in, 0) + } + + val rawBytes = nat >> bytes + val entry = nat ~ rawBytes + val symtab = nat >> entry.times + val scalaSig = nat ~ nat ~ symtab ^~~^ ScalaSig + + val utf8 = read(_ toUTF8String) + val longValue = read(_ toLong) +} + +case class ScalaSig(majorVersion : Int, minorVersion : Int, table : Seq[Int ~ ByteCode]) extends DefaultMemoisable { + + case class Entry(index : Int, entryType : Int, byteCode : ByteCode) extends DefaultMemoisable { + def scalaSig = ScalaSig.this + + def setByteCode(byteCode : ByteCode) = Entry(index, entryType, byteCode) + } + + def hasEntry(index : Int) = table isDefinedAt index + + def getEntry(index : Int) = { + val entryType ~ byteCode = table(index) + Entry(index, entryType, byteCode) + } + + def parseEntry(index : Int) = applyRule(ScalaSigParsers.parseEntry(ScalaSigEntryParsers.entry)(index)) + + implicit def applyRule[A](parser : ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(parser)(this) + + override def toString = "ScalaSig version " + majorVersion + "." + minorVersion + { + for (i <- 0 until table.size) yield i + ":\t" + parseEntry(i) // + "\n\t" + getEntry(i) + }.mkString("\n", "\n", "") + + lazy val symbols : Seq[Symbol] = ScalaSigParsers.symbols + + lazy val topLevelClasses : List[ClassSymbol] = ScalaSigParsers.topLevelClasses + lazy val topLevelObjects : List[ObjectSymbol] = ScalaSigParsers.topLevelObjects +} + +object ScalaSigParsers extends RulesWithState with MemoisableRules { + type S = ScalaSig + type Parser[A] = Rule[A, String] + + val symTab = read(_.table) + val size = symTab ^^ (_.size) + + def entry(index : Int) = memo(("entry", index)) { + cond(_ hasEntry index) -~ read(_ getEntry index) >-> { entry => Success(entry, entry.entryType) } + } + + def parseEntry[A](parser : ScalaSigEntryParsers.EntryParser[A])(index : Int) : Parser[A] = + entry(index) -~ parser >> { a => entry => Success(entry.scalaSig, a) } + + def allEntries[A](f : ScalaSigEntryParsers.EntryParser[A]) = size >> { n => anyOf((0 until n) map parseEntry(f)) } + + lazy val entries = allEntries(ScalaSigEntryParsers.entry) as "entries" + lazy val symbols = allEntries(ScalaSigEntryParsers.symbol) as "symbols" + lazy val methods = allEntries(ScalaSigEntryParsers.methodSymbol) as "methods" + lazy val attributes = allEntries(ScalaSigEntryParsers.attributeInfo) as "attributes" + + lazy val topLevelClasses = allEntries(ScalaSigEntryParsers.topLevelClass) + lazy val topLevelObjects = allEntries(ScalaSigEntryParsers.topLevelObject) +} + +object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { + import ScalaSigAttributeParsers.{nat, utf8, longValue} + + type S = ScalaSig#Entry + type EntryParser[A] = Rule[A, String] + + implicit def byteCodeEntryParser[A](rule : ScalaSigAttributeParsers.Parser[A]) : EntryParser[A] = apply { entry => + rule(entry.byteCode) mapOut (entry setByteCode _) + } + + def toEntry[A](index : Int) = apply { sigEntry => ScalaSigParsers.entry(index)(sigEntry.scalaSig) } + + def parseEntry[A](parser : EntryParser[A])(index : Int) = (toEntry(index) -~ parser) + + implicit def entryType(code : Int) = key filter (_ == code) + + val index = read(_.index) + val key = read(_.entryType) + + lazy val entry : EntryParser[Any] = symbol | typeEntry | literal | name | attributeInfo | annotInfo | children | get + + val ref = byteCodeEntryParser(nat) + + val termName = 1 -~ utf8 + val typeName = 2 -~ utf8 + + val name = termName | typeName as "name" + + def refTo[A](rule : EntryParser[A]) : EntryParser[A] = ref >>& parseEntry(rule) + + lazy val nameRef = refTo(name) + lazy val symbolRef = refTo(symbol) + lazy val typeRef = refTo(typeEntry) + lazy val constantRef = refTo(literal) + + val symbolInfo = nameRef ~ symbolRef ~ nat ~ (symbolRef?) ~ ref ~ get ^~~~~~^ SymbolInfo + + def symbolEntry(key : Int) = (key -~ none | (key + 64) -~ nat) -~ symbolInfo + + val noSymbol = 3 -^ NoSymbol + val typeSymbol = symbolEntry(4) ^^ TypeSymbol as "typeSymbol" + val aliasSymbol = symbolEntry(5) ^^ AliasSymbol as "alias" + val classSymbol = symbolEntry(6) ~ (ref?) ^~^ ClassSymbol as "class" + val objectSymbol = symbolEntry(7) ^^ ObjectSymbol as "object" + val methodSymbol = symbolEntry(8) ~ (ref?) ^~^ MethodSymbol as "method" + val extRef = 9 -~ nameRef ~ (symbolRef?) ~ get ^~~^ ExternalSymbol as "extRef" + val extModClassRef = 10 -~ nameRef ~ (symbolRef?) ~ get ^~~^ ExternalSymbol as "extModClassRef" + + lazy val symbol : EntryParser[Symbol] = oneOf( + noSymbol, + typeSymbol, + aliasSymbol, + classSymbol, + objectSymbol, + methodSymbol, + extRef, + extModClassRef) as "symbol" + + val classSymRef = refTo(classSymbol) + val attribTreeRef = ref + val typeLevel = nat + val typeIndex = nat + + lazy val typeEntry : EntryParser[Type] = oneOf( + 11 -^ NoType, + 12 -^ NoPrefixType, + 13 -~ symbolRef ^^ ThisType, + 14 -~ typeRef ~ symbolRef ^~^ SingleType, + 15 -~ constantRef ^^ ConstantType, + 16 -~ typeRef ~ symbolRef ~ (typeRef*) ^~~^ TypeRefType, + 17 -~ typeRef ~ typeRef ^~^ TypeBoundsType, + 18 -~ classSymRef ~ (typeRef*) ^~^ RefinedType, + 19 -~ symbolRef ~ (typeRef*) ^~^ ClassInfoType, + 20 -~ typeRef ~ (typeRef*) ^~^ MethodType, + 21 -~ typeRef ~ (refTo(typeSymbol)*) ^~^ PolyType, + 22 -~ typeRef ~ (typeRef*) ^~^ ImplicitMethodType, + 42 -~ typeRef ~ (attribTreeRef*) ^~^ AnnotatedType, + 51 -~ typeRef ~ symbolRef ~ (attribTreeRef*) ^~~^ AnnotatedWithSelfType, + 47 -~ typeLevel ~ typeIndex ^~^ DeBruijnIndexType, + 48 -~ typeRef ~ (symbolRef*) ^~^ ExistentialType) as "type" + + lazy val literal = oneOf( + 24 -^ (), + 25 -~ longValue ^^ (_ != 0), + 26 -~ longValue ^^ (_.asInstanceOf[Byte]), + 27 -~ longValue ^^ (_.asInstanceOf[Short]), + 28 -~ longValue ^^ (_.asInstanceOf[Char]), + 29 -~ longValue ^^ (_.asInstanceOf[Int]), + 30 -~ longValue ^^ (_.asInstanceOf[Long]), + 31 -~ longValue ^^ (l => java.lang.Float.intBitsToFloat(l.asInstanceOf[Int])), + 32 -~ longValue ^^ (java.lang.Double.longBitsToDouble), + 33 -~ nameRef, + 34 -^ null, + 35 -~ typeRef) + + lazy val attributeInfo = 40 -~ symbolRef ~ typeRef ~ (constantRef?) ~ (nameRef ~ constantRef *) ^~~~^ AttributeInfo // sym_Ref info_Ref {constant_Ref} {nameRef constantRef} + lazy val children = 41 -~ (nat*) ^^ Children //sym_Ref {sym_Ref} + lazy val annotInfo = 43 -~ (nat*) ^^ AnnotInfo // attarg_Ref {constant_Ref attarg_Ref} + + lazy val topLevelClass = classSymbol filter isTopLevelClass + lazy val topLevelObject = objectSymbol filter isTopLevel + + def isTopLevel(symbol : Symbol) = symbol.parent match { + case Some(ext : ExternalSymbol) => true + case _ => false + } + def isTopLevelClass (symbol : Symbol) = !symbol.isModule && isTopLevel(symbol) +} + + case class AttributeInfo(symbol : Symbol, typeRef : Type, value : Option[Any], values : Seq[String ~ Any]) // sym_Ref info_Ref {constant_Ref} {nameRef constantRef} + case class Children(symbolRefs : Seq[Int]) //sym_Ref {sym_Ref} + + case class AnnotInfo(refs : Seq[Int]) // attarg_Ref {constant_Ref attarg_Ref} + + /* | 49 TREE len_Nat 1 EMPTYtree + * | 49 TREE len_Nat 2 PACKAGEtree type_Ref sym_Ref mods_Ref name_Ref {tree_Ref} + * | 49 TREE len_Nat 3 CLASStree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 4 MODULEtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref + * | 49 TREE len_Nat 5 VALDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 6 DEFDEFtree type_Ref sym_Ref mods_Ref name_Ref numtparams_Nat {tree_Ref} numparamss_Nat {numparams_Nat {tree_Ref}} tree_Ref tree_Ref + * | 49 TREE len_Nat 7 TYPEDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 8 LABELtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 9 IMPORTtree type_Ref sym_Ref tree_Ref {name_Ref name_Ref} + * | 49 TREE len_Nat 10 ANNOTATIONtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 11 DOCDEFtree type_Ref sym_Ref string_Ref tree_Ref + * | 49 TREE len_Nat 12 TEMPLATEtree type_Ref sym_Ref numparents_Nat {tree_Ref} tree_Ref {tree_Ref} + * | 49 TREE len_Nat 13 BLOCKtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 14 CASEtree type_Ref tree_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 15 SEQUENCEtree type_Ref {tree_Ref} + * | 49 TREE len_Nat 16 ALTERNATIVEtree type_Ref {tree_Ref} + * | 49 TREE len_Nat 17 STARtree type_Ref {tree_Ref} + * | 49 TREE len_Nat 18 BINDtree type_Ref sym_Ref name_Ref tree_Ref + * | 49 TREE len_Nat 19 UNAPPLYtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 20 ARRAYVALUEtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 21 FUNCTIONtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 22 ASSIGNtree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 23 IFtree type_Ref tree_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 24 MATCHtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 25 RETURNtree type_Ref sym_Ref tree_Ref + * | 49 TREE len_Nat 26 TREtree type_Ref tree_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 27 THROWtree type_Ref tree_Ref + * | 49 TREE len_Nat 28 NEWtree type_Ref tree_Ref + * | 49 TREE len_Nat 29 TYPEDtree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 30 TYPEAPPLYtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 31 APPLYtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 32 APPLYDYNAMICtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 33 SUPERtree type_Ref sym_Ref tree_Ref name_Ref + * | 49 TREE len_Nat 34 THIStree type_Ref sym_Ref name_Ref + * | 49 TREE len_Nat 35 SELECTtree type_Ref sym_Ref tree_Ref name_Ref + * | 49 TREE len_Nat 36 IDENTtree type_Ref sym_Ref name_Ref + * | 49 TREE len_Nat 37 LITERALtree type_Ref constant_Ref + * | 49 TREE len_Nat 38 TYPEtree type_Ref + * | 49 TREE len_Nat 39 ANNOTATEDtree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 40 SINGLETONTYPEtree type_Ref tree_Ref + * | 49 TREE len_Nat 41 SELECTFROMTYPEtree type_Ref tree_Ref name_Ref + * | 49 TREE len_Nat 42 COMPOUNDTYPEtree type_Ref tree_Ref + * | 49 TREE len_Nat 43 APPLIEDTYPEtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 44 TYPEBOUNDStree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 45 EXISTENTIALTYPEtree type_Ref tree_Ref {tree_Ref} + * | 50 MODIFIERS len_Nat flags_Long privateWithin_Ref {Annotation_Ref} + + * Attarg = Refltree | Constant + * + * len is remaining length after `len'. + */ + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala new file mode 100644 index 0000000000..abdb28e1f7 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala @@ -0,0 +1,322 @@ +package scala.tools.scalap.scalax.rules.scalasig + +import _root_.scala.Symbol +import java.io.{PrintStream, ByteArrayOutputStream} +import util.StringUtil +import java.util.regex.Pattern + +class ScalaSigPrinter(stream: PrintStream) { + import stream._ + + val CONSTRUCTOR_NAME = "<init>" + + case class TypeFlags(printRep: Boolean) + + def printSymbol(symbol: Symbol) {printSymbol(0, symbol)} + + def printSymbol(level: Int, symbol: Symbol) { + if (!symbol.isLocal) { + def indent() {for (i <- 1 to level) print(" ")} + + symbol match { + case o: ObjectSymbol => if (!isCaseClassObject(o)) { + indent + printObject(level, o) + } + case c: ClassSymbol if !refinementClass(c) && !c.isModule => indent; { + printClass(level, c) + } + case m: MethodSymbol => printMethod(level, m, indent) + case a: AliasSymbol => indent; printAlias(level, a) + case t: TypeSymbol => () + case s => {} + } + } + } + + def isCaseClassObject(o: ObjectSymbol): Boolean = { + val TypeRefType(prefix, classSymbol: ClassSymbol, typeArgs) = o.infoType + o.isFinal && (classSymbol.children.find(x => x.isCase && x.isInstanceOf[MethodSymbol]) match { + case Some(_) => true + case None => false + }) + } + + def underCaseClass(m: MethodSymbol) = m.parent match { + case Some(c: ClassSymbol) => c.isCase + case _ => false + } + + + def printChildren(level: Int, symbol: Symbol) { + for (child <- symbol.children) printSymbol(level + 1, child) + } + + def printWithIndent(level: Int, s: String) { + def indent() {for (i <- 1 to level) print(" ")} + indent; + print(s) + } + + def printModifiers(symbol: Symbol) { + if (symbol.isSealed) print("sealed ") + if (symbol.isImplicit) print("implicit ") + if (symbol.isFinal) print("final ") + if (symbol.isPrivate) print("private ") + else if (symbol.isProtected) print("protected ") + if (symbol.isOverride) print("override ") + if (symbol.isAbstract) symbol match { + case c@(_: ClassSymbol | _: ObjectSymbol) if !c.isTrait => print("abstract ") + case _ => () + } + if (symbol.isCase && !symbol.isMethod) print("case ") + } + + private def refinementClass(c: ClassSymbol) = c.name == "<refinement>" + + def printClass(level: Int, c: ClassSymbol) { + printModifiers(c) + val defaultConstructor = if (c.isCase) getPrinterByConstructor(c) else "" + if (c.isTrait) print("trait ") else print("class ") + print(processName(c.name)) + val it = c.infoType + val classType = it match { + case PolyType(typeRef, symbols) => PolyTypeWithCons(typeRef, symbols, defaultConstructor) + case _ => it + } + printType(classType) + print(" {") + //Print class selftype + c.selfType match { + case Some(t: Type) => print("\n"); print(" this : " + toString(t) + " =>") + case None => + } + print("\n") + printChildren(level, c) + printWithIndent(level, "}\n") + } + + def getPrinterByConstructor(c: ClassSymbol) = { + c.children.find{ + case m : MethodSymbol if m.name == CONSTRUCTOR_NAME => true + case _ => false + } match { + case Some(m: MethodSymbol) => { + val baos = new ByteArrayOutputStream + val stream = new PrintStream(baos) + val printer = new ScalaSigPrinter(stream) + printer.printMethodType(m.infoType, false) + baos.toString + } + case None => "" + } + } + + def printObject(level: Int, o: ObjectSymbol) { + printModifiers(o) + print("object ") + print(processName(o.name)) + val TypeRefType(prefix, classSymbol: ClassSymbol, typeArgs) = o.infoType + printType(classSymbol) + print(" {\n") + printChildren(level, classSymbol) + printWithIndent(level, "}\n") + } + + def genParamNames(t: {def paramTypes: Seq[Type]}): List[String] = t.paramTypes.toList.map(x => { + var str = toString(x) + val j = str.indexOf("[") + if (j > 0) str = str.substring(0, j) + str = StringUtil.trimStart(str, "=> ") + var i = str.lastIndexOf(".") + val res = if (i > 0) str.substring(i + 1) else str + if (res.length > 1) StringUtil.decapitalize(res.substring(0, 1)) else res.toLowerCase + }) + + def printMethodType(t: Type, printResult: Boolean): Unit = { + def _pmt(mt: Type {def resultType: Type; def paramTypes: Seq[Type]}) = { + print(genParamNames(mt).zip(mt.paramTypes.toList.map(toString(_)(TypeFlags(true)))).map(p => p._1 + " : " + p._2).mkString( + "(" + (mt match {case ImplicitMethodType(_, _) => "implicit "; case _ => ""}) + , ", ", ")")) + mt.resultType match { + case mt: MethodType => printMethodType(mt, printResult) + case imt: ImplicitMethodType => printMethodType(imt, printResult) + case x => if (printResult) { + print(" : "); + printType(x) + } + } + } + t match { + case mt@MethodType(resType, paramTypes) => _pmt(mt) + case mt@ImplicitMethodType(resType, paramTypes) => _pmt(mt) + case pt@PolyType(mt, typeParams) => { + print(typeParamString(typeParams)) + printMethodType(mt, printResult) + } + //todo consider another method types + case x => print(" : "); printType(x) + } + } + + def printMethod(level: Int, m: MethodSymbol, indent : () => Unit): Unit = { + val n = m.name + if (underCaseClass(m) && n == CONSTRUCTOR_NAME) return + if (m.isAccessor && n.endsWith("_$eq")) return + indent() + printModifiers(m) + if (m.isAccessor) { + val indexOfSetter = m.parent.get.children.findIndexOf(x => x.isInstanceOf[MethodSymbol] && + x.asInstanceOf[MethodSymbol].name == n + "_$eq") + print(if (indexOfSetter > 0) "var " else "val ") + } else { + print("def ") + } + n match { + case CONSTRUCTOR_NAME => { + print("this") + printMethodType(m.infoType, false) + print(" = { /* compiled code */ }") + } + case name => { + val nn = processName(name) + print(nn) + printMethodType(m.infoType, true) + if (!m.isDeferred) { // Print body only for non-abstract metods + print(" = { /* compiled code */ }") + } + } + } + print("\n") + printChildren(level, m) + } + + def printAlias(level: Int, a: AliasSymbol) { + print("type ") + print(processName(a.name)) + printType(a.infoType, " = ") + print("\n") + printChildren(level, a) + } + + def printAttributes(sym: SymbolInfoSymbol) { + for (attrib <- sym.attributes) printAttribute(attrib) + } + + def printAttribute(attrib: AttributeInfo) { + printType(attrib.typeRef, "@") + if (attrib.value.isDefined) { + print("(") + printValue(attrib.value.get) + print(")") + } + if (!attrib.values.isEmpty) { + print(" {") + for (name ~ value <- attrib.values) { + print(" val ") + print(processName(name)) + print(" = ") + printValue(value) + } + printValue(attrib.value) + print(" }") + } + print(" ") + } + + def printValue(value: Any): Unit = value match { + case t: Type => printType(t) + // TODO string, char, float, etc. + case _ => print(value) + } + + implicit object _tf extends TypeFlags(false) + + def printType(sym: SymbolInfoSymbol)(implicit flags: TypeFlags): Unit = printType(sym.infoType)(flags) + + def printType(t: Type)(implicit flags: TypeFlags): Unit = print(toString(t)(flags)) + + def printType(t: Type, sep: String)(implicit flags: TypeFlags): Unit = print(toString(t, sep)(flags)) + + def toString(t: Type)(implicit flags: TypeFlags): String = toString(t, "")(flags) + + def toString(t: Type, sep: String)(implicit flags: TypeFlags): String = t match { + case ThisType(symbol) => sep + symbol.path + ".type" + case SingleType(typeRef, symbol) => sep + symbol.path + ".type" + case ConstantType(constant) => sep + (constant match { + case null => "scala.Null" + case _: Unit => "scala.Unit" + case _: Boolean => "scala.Boolean" + case _: Byte => "scala.Byte" + case _: Char => "scala.Char" + case _: Short => "scala.Short" + case _: Int => "scala.Int" + case _: Long => "scala.Long" + case _: Float => "scala.Float" + case _: Double => "scala.Double" + case _: String => "java.lang.String" + case c: Class[_] => "java.lang.Class[" + c.getComponentType.getCanonicalName.replace("$", ".") + "]" + }) + case TypeRefType(prefix, symbol, typeArgs) => sep + (symbol.path match { + case "scala.<repeated>" => flags match { + case TypeFlags(true) => toString(typeArgs.first) + "*" + case _ => "scala.Seq" + typeArgString(typeArgs) + } + case "scala.<byname>" => "=> " + toString(typeArgs.first) + case _ => StringUtil.trimStart(processName(symbol.path) + typeArgString(typeArgs), "<empty>.") + }) + case TypeBoundsType(lower, upper) => " >: " + toString(lower) + " <: " + toString(upper) + case RefinedType(classSym, typeRefs) => sep + typeRefs.map(toString).mkString("", " with ", "") + case ClassInfoType(symbol, typeRefs) => sep + typeRefs.map(toString).mkString(" extends ", " with ", "") + + case ImplicitMethodType(resultType, _) => toString(resultType, sep) + case MethodType(resultType, _) => toString(resultType, sep) + + case PolyType(typeRef, symbols) => typeParamString(symbols) + toString(typeRef, sep) + case PolyTypeWithCons(typeRef, symbols, cons) => typeParamString(symbols) + cons + toString(typeRef, sep) + case AnnotatedType(typeRef, attribTreeRefs) => toString(typeRef, sep) + case AnnotatedWithSelfType(typeRef, symbol, attribTreeRefs) => toString(typeRef, sep) + //case DeBruijnIndexType(typeLevel, typeIndex) => + case ExistentialType(typeRef, symbols) => { + val refs = symbols.map(toString _).filter(!_.startsWith("_ ")).map("type " + _) + toString(typeRef, sep) + (if (refs.size > 0) refs.mkString(" forSome {", "; ", "}") else "") + } + case _ => sep + t.toString + } + + def getVariance(t: TypeSymbol) = if (t.isCovariant) "+" else if (t.isContravariant) "-" else "" + + def toString(symbol: Symbol): String = symbol match { + case symbol: TypeSymbol => getVariance(symbol) + processName(symbol.name) + toString(symbol.infoType) + case s => symbol.toString + } + + def typeArgString(typeArgs: Seq[Type]): String = + if (typeArgs.isEmpty) "" + else typeArgs.map(toString).map(StringUtil.trimStart(_, "=> ")).mkString("[", ", ", "]") + + def typeParamString(params: Seq[Symbol]): String = + if (params.isEmpty) "" + else params.map(toString).mkString("[", ", ", "]") + + val _syms = Map("\\$bar" -> "|", "\\$tilde" -> "~", + "\\$bang" -> "!", "\\$up" -> "^", "\\$plus" -> "+", + "\\$minus" -> "-", "\\$eq" -> "=", "\\$less" -> "<", + "\\$times" -> "*", "\\$div" -> "/", "\\$bslash" -> "\\\\", + "\\$greater" -> ">", "\\$qmark" -> "?", "\\$percent" -> "%", + "\\$amp" -> "&", "\\$colon" -> ":", "\\$u2192" -> "→") + val pattern = Pattern.compile(_syms.keySet.foldLeft("")((x, y) => if (x == "") y else x + "|" + y)) + val placeholderPattern = "_\\$(\\d)+" + + def processName(name: String) = { + val m = pattern.matcher(name) + var temp = name + while (m.find) { + val key = m.group + val re = "\\" + key + temp = temp.replaceAll(re, _syms(re)) + } + temp.replaceAll(placeholderPattern, "_") + } + +} diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/SourceFileAttributeParser.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/SourceFileAttributeParser.scala new file mode 100644 index 0000000000..f02ca870b0 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/SourceFileAttributeParser.scala @@ -0,0 +1,25 @@ +package scala.tools.scalap.scalax.rules.scalasig + +/** + * @author ilyas + */ + +object SourceFileAttributeParser extends ByteCodeReader { + val sourceFile = u2 ^^ SourceFileInfo + + def parse(byteCode: ByteCode) = expect(sourceFile)(byteCode) +} + +/** + * + * SourceFile_attribute { + u2 attribute_name_index; + u4 attribute_length; + u2 sourcefile_index; + } + * + * Contains only file index in ConstantPool, first two fields are already treated + * by {@link scalax.rules.scalasig.ClassFile.attribute#attribute} + */ +case class SourceFileInfo(sourceFileIndex: Int) + diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Symbol.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Symbol.scala new file mode 100644 index 0000000000..f8c5e88db1 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Symbol.scala @@ -0,0 +1,70 @@ +package scala.tools.scalap.scalax.rules.scalasig + +import ScalaSigEntryParsers._ + +trait Symbol extends Flags { + def name : String + def parent : Option[Symbol] + def children : Seq[Symbol] + + def path : String = parent.map(_.path + ".").getOrElse("") + name +} + +case object NoSymbol extends Symbol { + def name = "<no symbol>" + def parent = None + def hasFlag(flag : Long) = false + def children = Nil +} + +abstract class ScalaSigSymbol extends Symbol { + def applyRule[A](rule : EntryParser[A]) : A = expect(rule)(entry) + def applyScalaSigRule[A](rule : ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(rule)(entry.scalaSig) + + def entry : ScalaSig#Entry + def index = entry.index + + lazy val children : Seq[Symbol] = applyScalaSigRule(ScalaSigParsers.symbols) filter (_.parent == Some(this)) + lazy val attributes : Seq[AttributeInfo] = applyScalaSigRule(ScalaSigParsers.attributes) filter (_.symbol == this) +} + +case class ExternalSymbol(name : String, parent : Option[Symbol], entry : ScalaSig#Entry) extends ScalaSigSymbol { + override def toString = path + def hasFlag(flag : Long) = false +} + +case class SymbolInfo(name : String, owner : Symbol, flags : Int, privateWithin : Option[AnyRef], info : Int, entry : ScalaSig#Entry) { + def symbolString(any : AnyRef) = any match { + case sym : SymbolInfoSymbol => sym.index.toString + case other => other.toString + } + + override def toString = name + ", owner=" + symbolString(owner) + ", flags=" + flags.toHexString + ", info=" + info + (privateWithin match { + case Some(any) => ", privateWithin=" + symbolString(any) + case None => " " + }) +} + +abstract class SymbolInfoSymbol extends ScalaSigSymbol { + def symbolInfo : SymbolInfo + + def entry = symbolInfo.entry + def name = symbolInfo.name + def parent = Some(symbolInfo.owner) + def hasFlag(flag : Long) = (symbolInfo.flags & flag) != 0 + + lazy val infoType = applyRule(parseEntry(typeEntry)(symbolInfo.info)) +} + +case class TypeSymbol(symbolInfo : SymbolInfo) extends SymbolInfoSymbol{ + override def path = name +} + +case class AliasSymbol(symbolInfo : SymbolInfo) extends SymbolInfoSymbol{ + override def path = name +} +case class ClassSymbol(symbolInfo : SymbolInfo, thisTypeRef : Option[Int]) extends SymbolInfoSymbol { + lazy val selfType = thisTypeRef.map{(x: Int) => applyRule(parseEntry(typeEntry)(x))} +} +case class ObjectSymbol(symbolInfo : SymbolInfo) extends SymbolInfoSymbol +case class MethodSymbol(symbolInfo : SymbolInfo, aliasRef : Option[Int]) extends SymbolInfoSymbol diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala new file mode 100644 index 0000000000..c812a94af7 --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/Type.scala @@ -0,0 +1,22 @@ +package scala.tools.scalap.scalax.rules.scalasig + +abstract class Type + +case object NoType extends Type +case object NoPrefixType extends Type + +case class ThisType(symbol : Symbol) extends Type +case class SingleType(typeRef : Type, symbol : Symbol) extends Type +case class ConstantType(constant : Any) extends Type +case class TypeRefType(prefix : Type, symbol : Symbol, typeArgs : Seq[Type]) extends Type +case class TypeBoundsType(lower : Type, upper : Type) extends Type +case class RefinedType(classSym : Symbol, typeRefs : List[Type]) extends Type +case class ClassInfoType(symbol : Symbol, typeRefs : Seq[Type]) extends Type +case class MethodType(resultType : Type, paramTypes : Seq[Type]) extends Type +case class PolyType(typeRef : Type, symbols : Seq[TypeSymbol]) extends Type +case class PolyTypeWithCons(typeRef : Type, symbols : Seq[TypeSymbol], cons: String) extends Type +case class ImplicitMethodType(resultType : Type, paramTypes : Seq[Type]) extends Type +case class AnnotatedType(typeRef : Type, attribTreeRefs : List[Int]) extends Type +case class AnnotatedWithSelfType(typeRef : Type, symbol : Symbol, attribTreeRefs : List[Int]) extends Type +case class DeBruijnIndexType(typeLevel : Int, typeIndex : Int) extends Type +case class ExistentialType(typeRef : Type, symbols : Seq[Symbol]) extends Type
\ No newline at end of file diff --git a/src/scalap/scala/tools/scalap/scalax/util/StringUtil.scala b/src/scalap/scala/tools/scalap/scalax/util/StringUtil.scala new file mode 100644 index 0000000000..d48597fd9a --- /dev/null +++ b/src/scalap/scala/tools/scalap/scalax/util/StringUtil.scala @@ -0,0 +1,15 @@ +package scala.tools.scalap.scalax.util + +import java.beans.Introspector + +/** + * @author ilyas + */ + +object StringUtil { + + def trimStart(s: String, prefix: String) = if (s != null && s.startsWith(prefix)) s.substring(prefix.length) else s + + def decapitalize(s: String) = Introspector.decapitalize(s) + +}
\ No newline at end of file |