From 97f03245d9646b9ade43418dee7dc0d2a6203ce7 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 28 Mar 2012 12:57:34 -0700 Subject: Revived the lisp test. All hail the lisp test! Closes SI-4579. --- test/disabled/run/lisp.check | 26 --- test/disabled/run/lisp.scala | 518 ------------------------------------------- test/files/run/lisp.check | 25 +++ test/files/run/lisp.scala | 518 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 543 insertions(+), 544 deletions(-) delete mode 100644 test/disabled/run/lisp.check delete mode 100644 test/disabled/run/lisp.scala create mode 100644 test/files/run/lisp.check create mode 100644 test/files/run/lisp.scala (limited to 'test') diff --git a/test/disabled/run/lisp.check b/test/disabled/run/lisp.check deleted file mode 100644 index 64053f26d0..0000000000 --- a/test/disabled/run/lisp.check +++ /dev/null @@ -1,26 +0,0 @@ -(lambda (x) (+ (* x x) 1)) -(lambda (x) (+ (* x x) 1)) - -( '(1 2 3)) = (1 2 3) -(car '(1 2 3)) = 1 -(cdr '(1 2 3)) = (2 3) -(null? '(2 3)) = 0 -(null? '()) = 1 - -faculty(10) = 3628800 -faculty(10) = 3628800 -foobar = ("a" "bc" "def" "z") - -List('lambda, List('x), List('+, List('*, 'x, 'x), 1)) -(lambda (x) (+ (* x x) 1)) - -( '(1 2 3)) = (1 2 3) -(car '(1 2 3)) = 1 -(cdr '(1 2 3)) = (2 3) -(null? '(2 3)) = 0 -(null? '()) = 1 - -faculty(10) = 3628800 -faculty(10) = 3628800 -foobar = ("a" "bc" "def" "z") - diff --git a/test/disabled/run/lisp.scala b/test/disabled/run/lisp.scala deleted file mode 100644 index 06e68f508a..0000000000 --- a/test/disabled/run/lisp.scala +++ /dev/null @@ -1,518 +0,0 @@ -//############################################################################ -// Lisp interpreter -//############################################################################ - -//############################################################################ -// Lisp Scanner - -class LispTokenizer(s: String) extends Iterator[String] { - private var i = 0; - private def isDelimiter(ch: Char) = ch <= ' ' || ch == '(' || ch == ')' - def hasNext: Boolean = { - while (i < s.length() && s.charAt(i) <= ' ') i += 1 - i < s.length() - } - def next: String = - if (hasNext) { - val start = i - if (isDelimiter(s charAt i)) i += 1 - else - do i = i + 1 - while (!isDelimiter(s charAt i)) - s.substring(start, i) - } else sys.error("premature end of string") -} - -//############################################################################ -// Lisp Interface - -trait Lisp { - type Data - - def string2lisp(s: String): Data - def lisp2string(s: Data): String - - def evaluate(d: Data): Data - // !!! def evaluate(s: String): Data = evaluate(string2lisp(s)) - def evaluate(s: String): Data -} - -//############################################################################ -// Lisp Implementation Using Case Classes - -object LispCaseClasses extends Lisp { - - import List.range - - trait Data { - def elemsToString(): String = toString(); - } - case class CONS(car: Data, cdr: Data) extends Data { - override def toString() = "(" + elemsToString() + ")"; - override def elemsToString() = car.toString() + (cdr match { - case NIL() => "" - case _ => " " + cdr.elemsToString(); - }) - } - case class NIL() extends Data { // !!! use case object - override def toString() = "()"; - } - case class SYM(name: String) extends Data { - override def toString() = name; - } - case class NUM(x: Int) extends Data { - override def toString() = x.toString(); - } - case class STR(x: String) extends Data { - override def toString() = "\"" + x + "\""; - } - case class FUN(f: List[Data] => Data) extends Data { - override def toString() = ""; - } - - def list(): Data = - NIL(); - def list(x0: Data): Data = - CONS(x0, NIL()); - def list(x0: Data, x1: Data): Data = - CONS(x0, list(x1)); - def list(x0: Data, x1: Data, x2: Data): Data = - CONS(x0, list(x1, x2)); - def list(x0: Data, x1: Data, x2: Data, x3: Data): Data = - CONS(x0, list(x1, x2, x3)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data): Data = - CONS(x0, list(x1, x2, x3, x4)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data, x7: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6, x7)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data, x7: Data, x8: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data, x7: Data, x8: Data, x9: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8, x9)); - - var curexp: Data = null - var trace: Boolean = false - var indent: Int = 0 - - def lispError[a](msg: String): a = - sys.error("error: " + msg + "\n" + curexp); - - trait Environment { - def lookup(n: String): Data; - def extendRec(name: String, expr: Environment => Data) = - new Environment { - def lookup(n: String): Data = - if (n == name) expr(this) else Environment.this.lookup(n); - } - def extend(name: String, v: Data) = extendRec(name, (env1 => v)); - } - val EmptyEnvironment = new Environment { - def lookup(n: String): Data = lispError("undefined: " + n); - } - - def toList(x: Data): List[Data] = x match { - case NIL() => List() - case CONS(y, ys) => y :: toList(ys) - case _ => lispError("malformed list: " + x); - } - - def toBoolean(x: Data) = x match { - case NUM(0) => false - case _ => true - } - - def normalize(x: Data): Data = x match { - case CONS(SYM("def"), - CONS(CONS(SYM(name), args), CONS(body, CONS(expr, NIL())))) => - normalize(list(SYM("def"), - SYM(name), list(SYM("lambda"), args, body), expr)) - case CONS(SYM("cond"), CONS(CONS(SYM("else"), CONS(expr, NIL())),NIL())) => - normalize(expr) - case CONS(SYM("cond"), CONS(CONS(test, CONS(expr, NIL())), rest)) => - normalize(list(SYM("if"), test, expr, CONS(SYM("cond"), rest))) - case CONS(h, t) => CONS(normalize(h), normalize(t)) - case _ => x - } - - def eval(x: Data, env: Environment): Data = { - val prevexp = curexp; - curexp = x; - if (trace) { - for (x <- range(1, indent)) Console.print(" "); - Console.println("===> " + x); - indent = indent + 1; - } - val result = eval1(x, env); - if (trace) { - indent = indent - 1; - for (x <- range(1, indent)) Console.print(" "); - Console.println("<=== " + result); - } - curexp = prevexp; - result - } - - def eval1(x: Data, env: Environment): Data = x match { - case SYM(name) => - env lookup name - case CONS(SYM("def"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => - eval(z, env.extendRec(name, (env1 => eval(y, env1)))) - case CONS(SYM("val"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => - eval(z, env.extend(name, eval(y, env))) - case CONS(SYM("lambda"), CONS(params, CONS(y, NIL()))) => - mkLambda(params, y, env) - case CONS(SYM("if"), CONS(c, CONS(t, CONS(e, NIL())))) => - if (toBoolean(eval(c, env))) eval(t, env) else eval(e, env) - case CONS(SYM("quote"), CONS(x, NIL())) => - x - case CONS(y, xs) => - apply(eval(y, env), toList(xs) map (x => eval(x, env))) - case NUM(_) => x - case STR(_) => x - case FUN(_) => x - case _ => - lispError("illegal term") - } - - def apply(fn: Data, args: List[Data]): Data = fn match { - case FUN(f) => f(args); - case _ => lispError("application of non-function: " + fn); - } - - def mkLambda(params: Data, expr: Data, env: Environment): Data = { - - def extendEnv(env: Environment, - ps: List[String], args: List[Data]): Environment = - Pair(ps, args) match { - case Pair(List(), List()) => - env - case Pair(p :: ps1, arg :: args1) => - extendEnv(env.extend(p, arg), ps1, args1) - case _ => - lispError("wrong number of arguments") - } - - val ps: List[String] = toList(params) map { - case SYM(name) => name - case _ => sys.error("illegal parameter list"); - } - - FUN(args => eval(expr, extendEnv(env, ps, args))) - } - - val globalEnv = EmptyEnvironment - .extend("=", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(if (arg1 == arg2) 1 else 0) - case List(STR(arg1),STR(arg2)) => NUM(if (arg1 == arg2) 1 else 0)})) - .extend("+", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 + arg2) - case List(STR(arg1),STR(arg2)) => STR(arg1 + arg2)})) - .extend("-", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 - arg2)})) - .extend("*", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 * arg2)})) - .extend("/", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 / arg2)})) - .extend("car", FUN({ - case List(CONS(x, xs)) => x})) - .extend("cdr", FUN({ - case List(CONS(x, xs)) => xs})) - .extend("null?", FUN({ - case List(NIL()) => NUM(1) - case _ => NUM(0)})) - .extend("cons", FUN({ - case List(x, y) => CONS(x, y)})); - - def evaluate(x: Data): Data = eval(normalize(x), globalEnv); - def evaluate(s: String): Data = evaluate(string2lisp(s)); - - def string2lisp(s: String): Data = { - val it = new LispTokenizer(s); - def parseExpr(token: String): Data = { - if (token == "(") parseList - else if (token == ")") sys.error("unbalanced parentheses") - else if ('0' <= token.charAt(0) && token.charAt(0) <= '9') - NUM(token.toInt) - else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') - STR(token.substring(1,token.length() - 1)) - else SYM(token) - } - def parseList: Data = { - val token = it.next; - if (token == ")") NIL() else CONS(parseExpr(token), parseList) - } - parseExpr(it.next) - } - - def lisp2string(d: Data): String = d.toString(); -} - -//############################################################################ -// Lisp Implementation Using Any - -object LispAny extends Lisp { - - import List._; - - type Data = Any; - - case class Lambda(f: List[Data] => Data); - - var curexp: Data = null; - var trace: Boolean = false; - var indent: Int = 0; - - def lispError[a](msg: String): a = - sys.error("error: " + msg + "\n" + curexp); - - trait Environment { - def lookup(n: String): Data; - def extendRec(name: String, expr: Environment => Data) = - new Environment { - def lookup(n: String): Data = - if (n == name) expr(this) else Environment.this.lookup(n); - } - def extend(name: String, v: Data) = extendRec(name, (env1 => v)); - } - val EmptyEnvironment = new Environment { - def lookup(n: String): Data = lispError("undefined: " + n); - } - - def asList(x: Data): List[Data] = x match { - case y: List[_] => y - case _ => lispError("malformed list: " + x) - } - - def asInt(x: Data): Int = x match { - case y: Int => y - case _ => lispError("not an integer: " + x) - } - - def asString(x: Data): String = x match { - case y: String => y - case _ => lispError("not a string: " + x) - } - - def asBoolean(x: Data): Boolean = x != 0 - - def normalize(x: Data): Data = x match { - case 'and :: x :: y :: Nil => - normalize('if :: x :: y :: 0 :: Nil) - case 'or :: x :: y :: Nil => - normalize('if :: x :: 1 :: y :: Nil) - case 'def :: (name :: args) :: body :: expr :: Nil => - normalize('def :: name :: ('lambda :: args :: body :: Nil) :: expr :: Nil) - case 'cond :: ('else :: expr :: Nil) :: rest => - normalize(expr); - case 'cond :: (test :: expr :: Nil) :: rest => - normalize('if :: test :: expr :: ('cond :: rest) :: Nil) - case 'cond :: 'else :: expr :: Nil => - normalize(expr) - case h :: t => - normalize(h) :: asList(normalize(t)) - case _ => - x - } - - def eval(x: Data, env: Environment): Data = { - val prevexp = curexp; - curexp = x; - if (trace) { - for (x <- range(1, indent)) Console.print(" "); - Console.println("===> " + x); - indent += 1; - } - val result = eval1(x, env); - if (trace) { - indent -= 1; - for (x <- range(1, indent)) Console.print(" "); - Console.println("<=== " + result); - } - curexp = prevexp; - result - } - - def eval1(x: Data, env: Environment): Data = x match { - case Symbol(name) => - env lookup name - case 'def :: Symbol(name) :: y :: z :: Nil => - eval(z, env.extendRec(name, (env1 => eval(y, env1)))) - case 'val :: Symbol(name) :: y :: z :: Nil => - eval(z, env.extend(name, eval(y, env))) - case 'lambda :: params :: y :: Nil => - mkLambda(params, y, env) - case 'if :: c :: y :: z :: Nil => - if (asBoolean(eval(c, env))) eval(y, env) else eval(z, env) - case 'quote :: y :: Nil => - y - case y :: z => - apply(eval(y, env), z map (x => eval(x, env))) - case Lambda(_) => x - case y: String => x - case y: Int => x - case y => lispError("illegal term") - } - - def lisp2string(x: Data): String = x match { - case Symbol(name) => name - case Nil => "()" - case y :: ys => - def list2string(xs: List[Data]): String = xs match { - case List() => "" - case y :: ys => " " + lisp2string(y) + list2string(ys) - } - "(" + lisp2string(y) + list2string(ys) + ")" - case _ => if (x.isInstanceOf[String]) "\"" + x + "\""; else x.toString() - } - - def apply(fn: Data, args: List[Data]): Data = fn match { - case Lambda(f) => f(args); - case _ => lispError("application of non-function: " + fn + " to " + args); - } - - def mkLambda(params: Data, expr: Data, env: Environment): Data = { - - def extendEnv(env: Environment, - ps: List[String], args: List[Data]): Environment = - Pair(ps, args) match { - case Pair(List(), List()) => - env - case Pair(p :: ps1, arg :: args1) => - extendEnv(env.extend(p, arg), ps1, args1) - case _ => - lispError("wrong number of arguments") - } - - val ps: List[String] = asList(params) map { - case Symbol(name) => name - case _ => sys.error("illegal parameter list"); - } - - Lambda(args => eval(expr, extendEnv(env, ps, args))) - } - - val globalEnv = EmptyEnvironment - .extend("=", Lambda{ - case List(arg1, arg2) => if(arg1 == arg2) 1 else 0}) - .extend("+", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 + arg2 - case List(arg1: String, arg2: String) => arg1 + arg2}) - .extend("-", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 - arg2}) - .extend("*", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 * arg2}) - .extend("/", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 / arg2}) - .extend("nil", Nil) - .extend("cons", Lambda{ - case List(arg1, arg2) => arg1 :: asList(arg2)}) - .extend("car", Lambda{ - case List(x :: xs) => x}) - .extend("cdr", Lambda{ - case List(x :: xs) => xs}) - .extend("null?", Lambda{ - case List(Nil) => 1 - case _ => 0}); - - def evaluate(x: Data): Data = eval(normalize(x), globalEnv); - def evaluate(s: String): Data = evaluate(string2lisp(s)); - - def string2lisp(s: String): Data = { - val it = new LispTokenizer(s); - def parseExpr(token: String): Data = { - if (token == "(") parseList - else if (token == ")") sys.error("unbalanced parentheses") - //else if (Character.isDigit(token.charAt(0))) - else if (token.charAt(0).isDigit) - token.toInt - else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') - token.substring(1,token.length() - 1) - else Symbol(token) - } - def parseList: List[Data] = { - val token = it.next; - if (token == ")") Nil else parseExpr(token) :: parseList - } - parseExpr(it.next) - } -} - -//############################################################################ -// List User - -class LispUser(lisp: Lisp) { - - import lisp._; - - def evaluate(s: String) = lisp2string(lisp.evaluate(s)); - - def run = { - - Console.println(string2lisp("(lambda (x) (+ (* x x) 1))").asInstanceOf[AnyRef]); - Console.println(lisp2string(string2lisp("(lambda (x) (+ (* x x) 1))"))); - Console.println; - - Console.println("( '(1 2 3)) = " + evaluate(" (quote(1 2 3))")); - Console.println("(car '(1 2 3)) = " + evaluate("(car (quote(1 2 3)))")); - Console.println("(cdr '(1 2 3)) = " + evaluate("(cdr (quote(1 2 3)))")); - Console.println("(null? '(2 3)) = " + evaluate("(null? (quote(2 3)))")); - Console.println("(null? '()) = " + evaluate("(null? (quote()))")); - Console.println; - - Console.println("faculty(10) = " + evaluate( - "(def (faculty n) " + - "(if (= n 0) " + - "1 " + - "(* n (faculty (- n 1)))) " + - "(faculty 10))")); - Console.println("faculty(10) = " + evaluate( - "(def (faculty n) " + - "(cond " + - "((= n 0) 1) " + - "(else (* n (faculty (- n 1))))) " + - "(faculty 10))")); - Console.println("foobar = " + evaluate( - "(def (foo n) " + - "(cond " + - "((= n 0) \"a\")" + - "((= n 1) \"b\")" + - "((= (/ n 2) 1) " + - "(cond " + - "((= n 2) \"c\")" + - "(else \"d\")))" + - "(else " + - "(def (bar m) " + - "(cond " + - "((= m 0) \"e\")" + - "((= m 1) \"f\")" + - "(else \"z\"))" + - "(bar (- n 4)))))" + - "(val nil (quote ())" + - "(val v1 (foo 0) " + - "(val v2 (+ (foo 1) (foo 2)) " + - "(val v3 (+ (+ (foo 3) (foo 4)) (foo 5)) " + - "(val v4 (foo 6) " + - "(cons v1 (cons v2 (cons v3 (cons v4 nil))))))))))")); - Console.println; - } -} - -//############################################################################ -// Main - -object Test { - def main(args: Array[String]) { - new LispUser(LispCaseClasses).run; - new LispUser(LispAny).run; - () - } -} - -//############################################################################ diff --git a/test/files/run/lisp.check b/test/files/run/lisp.check new file mode 100644 index 0000000000..38ca7b655d --- /dev/null +++ b/test/files/run/lisp.check @@ -0,0 +1,25 @@ +(lambda (x) (+ (* x x) 1)) +(lambda (x) (+ (* x x) 1)) + +( '(1 2 3)) = (1 2 3) +(car '(1 2 3)) = 1 +(cdr '(1 2 3)) = (2 3) +(null? '(2 3)) = 0 +(null? '()) = 1 + +faculty(10) = 3628800 +faculty(10) = 3628800 +foobar = ("a" "bc" "def" "z") + +List('lambda, List('x), List('+, List('*, 'x, 'x), 1)) +(lambda (x) (+ (* x x) 1)) + +( '(1 2 3)) = (1 2 3) +(car '(1 2 3)) = 1 +(cdr '(1 2 3)) = (2 3) +(null? '(2 3)) = 0 +(null? '()) = 1 + +faculty(10) = 3628800 +faculty(10) = 3628800 +foobar = ("a" "bc" "def" "z") diff --git a/test/files/run/lisp.scala b/test/files/run/lisp.scala new file mode 100644 index 0000000000..07f44e1151 --- /dev/null +++ b/test/files/run/lisp.scala @@ -0,0 +1,518 @@ +//############################################################################ +// Lisp interpreter +//############################################################################ + +//############################################################################ +// Lisp Scanner + +class LispTokenizer(s: String) extends Iterator[String] { + private var i = 0; + private def isDelimiter(ch: Char) = ch <= ' ' || ch == '(' || ch == ')' + def hasNext: Boolean = { + while (i < s.length() && s.charAt(i) <= ' ') i += 1 + i < s.length() + } + def next: String = + if (hasNext) { + val start = i + if (isDelimiter(s charAt i)) i += 1 + else + do i = i + 1 + while (!isDelimiter(s charAt i)) + s.substring(start, i) + } else sys.error("premature end of string") +} + +//############################################################################ +// Lisp Interface + +trait Lisp { + type Data + + def string2lisp(s: String): Data + def lisp2string(s: Data): String + + def evaluate(d: Data): Data + // !!! def evaluate(s: String): Data = evaluate(string2lisp(s)) + def evaluate(s: String): Data +} + +//############################################################################ +// Lisp Implementation Using Case Classes + +object LispCaseClasses extends Lisp { + + import List.range + + trait Data { + def elemsToString(): String = toString(); + } + case class CONS(car: Data, cdr: Data) extends Data { + override def toString() = "(" + elemsToString() + ")"; + override def elemsToString() = car.toString() + (cdr match { + case NIL() => "" + case _ => " " + cdr.elemsToString(); + }) + } + case class NIL() extends Data { // !!! use case object + override def toString() = "()"; + } + case class SYM(name: String) extends Data { + override def toString() = name; + } + case class NUM(x: Int) extends Data { + override def toString() = x.toString(); + } + case class STR(x: String) extends Data { + override def toString() = "\"" + x + "\""; + } + case class FUN(f: List[Data] => Data) extends Data { + override def toString() = ""; + } + + def list(): Data = + NIL(); + def list(x0: Data): Data = + CONS(x0, NIL()); + def list(x0: Data, x1: Data): Data = + CONS(x0, list(x1)); + def list(x0: Data, x1: Data, x2: Data): Data = + CONS(x0, list(x1, x2)); + def list(x0: Data, x1: Data, x2: Data, x3: Data): Data = + CONS(x0, list(x1, x2, x3)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data): Data = + CONS(x0, list(x1, x2, x3, x4)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data, x7: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6, x7)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data, x7: Data, x8: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data, x7: Data, x8: Data, x9: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8, x9)); + + var curexp: Data = null + var trace: Boolean = false + var indent: Int = 0 + + def lispError[a](msg: String): a = + sys.error("error: " + msg + "\n" + curexp); + + trait Environment { + def lookup(n: String): Data; + def extendRec(name: String, expr: Environment => Data) = + new Environment { + def lookup(n: String): Data = + if (n == name) expr(this) else Environment.this.lookup(n); + } + def extend(name: String, v: Data) = extendRec(name, (env1 => v)); + } + val EmptyEnvironment = new Environment { + def lookup(n: String): Data = lispError("undefined: " + n); + } + + def toList(x: Data): List[Data] = x match { + case NIL() => List() + case CONS(y, ys) => y :: toList(ys) + case _ => lispError("malformed list: " + x); + } + + def toBoolean(x: Data) = x match { + case NUM(0) => false + case _ => true + } + + def normalize(x: Data): Data = x match { + case CONS(SYM("def"), + CONS(CONS(SYM(name), args), CONS(body, CONS(expr, NIL())))) => + normalize(list(SYM("def"), + SYM(name), list(SYM("lambda"), args, body), expr)) + case CONS(SYM("cond"), CONS(CONS(SYM("else"), CONS(expr, NIL())),NIL())) => + normalize(expr) + case CONS(SYM("cond"), CONS(CONS(test, CONS(expr, NIL())), rest)) => + normalize(list(SYM("if"), test, expr, CONS(SYM("cond"), rest))) + case CONS(h, t) => CONS(normalize(h), normalize(t)) + case _ => x + } + + def eval(x: Data, env: Environment): Data = { + val prevexp = curexp; + curexp = x; + if (trace) { + for (x <- range(1, indent)) Console.print(" "); + Console.println("===> " + x); + indent = indent + 1; + } + val result = eval1(x, env); + if (trace) { + indent = indent - 1; + for (x <- range(1, indent)) Console.print(" "); + Console.println("<=== " + result); + } + curexp = prevexp; + result + } + + def eval1(x: Data, env: Environment): Data = x match { + case SYM(name) => + env lookup name + case CONS(SYM("def"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => + eval(z, env.extendRec(name, (env1 => eval(y, env1)))) + case CONS(SYM("val"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => + eval(z, env.extend(name, eval(y, env))) + case CONS(SYM("lambda"), CONS(params, CONS(y, NIL()))) => + mkLambda(params, y, env) + case CONS(SYM("if"), CONS(c, CONS(t, CONS(e, NIL())))) => + if (toBoolean(eval(c, env))) eval(t, env) else eval(e, env) + case CONS(SYM("quote"), CONS(x, NIL())) => + x + case CONS(y, xs) => + apply(eval(y, env), toList(xs) map (x => eval(x, env))) + case NUM(_) => x + case STR(_) => x + case FUN(_) => x + case _ => + lispError("illegal term") + } + + def apply(fn: Data, args: List[Data]): Data = fn match { + case FUN(f) => f(args); + case _ => lispError("application of non-function: " + fn); + } + + def mkLambda(params: Data, expr: Data, env: Environment): Data = { + + def extendEnv(env: Environment, + ps: List[String], args: List[Data]): Environment = + Pair(ps, args) match { + case Pair(List(), List()) => + env + case Pair(p :: ps1, arg :: args1) => + extendEnv(env.extend(p, arg), ps1, args1) + case _ => + lispError("wrong number of arguments") + } + + val ps: List[String] = toList(params) map { + case SYM(name) => name + case _ => sys.error("illegal parameter list"); + } + + FUN(args => eval(expr, extendEnv(env, ps, args))) + } + + val globalEnv = EmptyEnvironment + .extend("=", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(if (arg1 == arg2) 1 else 0) + case List(STR(arg1),STR(arg2)) => NUM(if (arg1 == arg2) 1 else 0)})) + .extend("+", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 + arg2) + case List(STR(arg1),STR(arg2)) => STR(arg1 + arg2)})) + .extend("-", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 - arg2)})) + .extend("*", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 * arg2)})) + .extend("/", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 / arg2)})) + .extend("car", FUN({ + case List(CONS(x, xs)) => x})) + .extend("cdr", FUN({ + case List(CONS(x, xs)) => xs})) + .extend("null?", FUN({ + case List(NIL()) => NUM(1) + case _ => NUM(0)})) + .extend("cons", FUN({ + case List(x, y) => CONS(x, y)})); + + def evaluate(x: Data): Data = eval(normalize(x), globalEnv); + def evaluate(s: String): Data = evaluate(string2lisp(s)); + + def string2lisp(s: String): Data = { + val it = new LispTokenizer(s); + def parseExpr(token: String): Data = { + if (token == "(") parseList + else if (token == ")") sys.error("unbalanced parentheses") + else if ('0' <= token.charAt(0) && token.charAt(0) <= '9') + NUM(token.toInt) + else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') + STR(token.substring(1,token.length() - 1)) + else SYM(token) + } + def parseList: Data = { + val token = it.next; + if (token == ")") NIL() else CONS(parseExpr(token), parseList) + } + parseExpr(it.next) + } + + def lisp2string(d: Data): String = d.toString(); +} + +//############################################################################ +// Lisp Implementation Using Any + +object LispAny extends Lisp { + + import List._; + + type Data = Any; + + case class Lambda(f: List[Data] => Data); + + var curexp: Data = null; + var trace: Boolean = false; + var indent: Int = 0; + + def lispError[a](msg: String): a = + sys.error("error: " + msg + "\n" + curexp); + + trait Environment { + def lookup(n: String): Data; + def extendRec(name: String, expr: Environment => Data) = + new Environment { + def lookup(n: String): Data = + if (n == name) expr(this) else Environment.this.lookup(n); + } + def extend(name: String, v: Data) = extendRec(name, (env1 => v)); + } + val EmptyEnvironment = new Environment { + def lookup(n: String): Data = lispError("undefined: " + n); + } + + def asList(x: Data): List[Data] = x match { + case y: List[_] => y + case _ => lispError("malformed list: " + x) + } + + def asInt(x: Data): Int = x match { + case y: Int => y + case _ => lispError("not an integer: " + x) + } + + def asString(x: Data): String = x match { + case y: String => y + case _ => lispError("not a string: " + x) + } + + def asBoolean(x: Data): Boolean = x != 0 + + def normalize(x: Data): Data = x match { + case 'and :: x :: y :: Nil => + normalize('if :: x :: y :: 0 :: Nil) + case 'or :: x :: y :: Nil => + normalize('if :: x :: 1 :: y :: Nil) + case 'def :: (name :: args) :: body :: expr :: Nil => + normalize('def :: name :: ('lambda :: args :: body :: Nil) :: expr :: Nil) + case 'cond :: ('else :: expr :: Nil) :: rest => + normalize(expr); + case 'cond :: (test :: expr :: Nil) :: rest => + normalize('if :: test :: expr :: ('cond :: rest) :: Nil) + case 'cond :: 'else :: expr :: Nil => + normalize(expr) + case h :: t => + normalize(h) :: asList(normalize(t)) + case _ => + x + } + + def eval(x: Data, env: Environment): Data = { + val prevexp = curexp; + curexp = x; + if (trace) { + for (x <- range(1, indent)) Console.print(" "); + Console.println("===> " + x); + indent += 1; + } + val result = eval1(x, env); + if (trace) { + indent -= 1; + for (x <- range(1, indent)) Console.print(" "); + Console.println("<=== " + result); + } + curexp = prevexp; + result + } + + def eval1(x: Data, env: Environment): Data = x match { + case Symbol(name) => + env lookup name + case 'def :: Symbol(name) :: y :: z :: Nil => + eval(z, env.extendRec(name, (env1 => eval(y, env1)))) + case 'val :: Symbol(name) :: y :: z :: Nil => + eval(z, env.extend(name, eval(y, env))) + case 'lambda :: params :: y :: Nil => + mkLambda(params, y, env) + case 'if :: c :: y :: z :: Nil => + if (asBoolean(eval(c, env))) eval(y, env) else eval(z, env) + case 'quote :: y :: Nil => + y + case y :: z => + apply(eval(y, env), z map (x => eval(x, env))) + case Lambda(_) => x + case y: String => x + case y: Int => x + case y => lispError("illegal term") + } + + def lisp2string(x: Data): String = x match { + case Symbol(name) => name + case Nil => "()" + case y :: ys => + def list2string(xs: List[Data]): String = xs match { + case List() => "" + case y :: ys => " " + lisp2string(y) + list2string(ys) + } + "(" + lisp2string(y) + list2string(ys) + ")" + case _ => if (x.isInstanceOf[String]) "\"" + x + "\""; else x.toString() + } + + def apply(fn: Data, args: List[Data]): Data = fn match { + case Lambda(f) => f(args); + case _ => lispError("application of non-function: " + fn + " to " + args); + } + + def mkLambda(params: Data, expr: Data, env: Environment): Data = { + + def extendEnv(env: Environment, + ps: List[String], args: List[Data]): Environment = + Pair(ps, args) match { + case Pair(List(), List()) => + env + case Pair(p :: ps1, arg :: args1) => + extendEnv(env.extend(p, arg), ps1, args1) + case _ => + lispError("wrong number of arguments") + } + + val ps: List[String] = asList(params) map { + case Symbol(name) => name + case _ => sys.error("illegal parameter list"); + } + + Lambda(args => eval(expr, extendEnv(env, ps, args))) + } + + val globalEnv = EmptyEnvironment + .extend("=", Lambda{ + case List(arg1, arg2) => if(arg1 == arg2) 1 else 0}) + .extend("+", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 + arg2 + case List(arg1: String, arg2: String) => arg1 + arg2}) + .extend("-", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 - arg2}) + .extend("*", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 * arg2}) + .extend("/", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 / arg2}) + .extend("nil", Nil) + .extend("cons", Lambda{ + case List(arg1, arg2) => arg1 :: asList(arg2)}) + .extend("car", Lambda{ + case List(x :: xs) => x}) + .extend("cdr", Lambda{ + case List(x :: xs) => xs}) + .extend("null?", Lambda{ + case List(Nil) => 1 + case _ => 0}); + + def evaluate(x: Data): Data = eval(normalize(x), globalEnv); + def evaluate(s: String): Data = evaluate(string2lisp(s)); + + def string2lisp(s: String): Data = { + val it = new LispTokenizer(s); + def parseExpr(token: String): Data = { + if (token == "(") parseList + else if (token == ")") sys.error("unbalanced parentheses") + //else if (Character.isDigit(token.charAt(0))) + else if (token.charAt(0).isDigit) + token.toInt + else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') + token.substring(1,token.length() - 1) + else Symbol(token) + } + def parseList: List[Data] = { + val token = it.next; + if (token == ")") Nil else parseExpr(token) :: parseList + } + parseExpr(it.next) + } +} + +//############################################################################ +// List User + +class LispUser(lisp: Lisp) { + + import lisp._; + + def evaluate(s: String) = lisp2string(lisp.evaluate(s)); + + def run = { + + Console.println(string2lisp("(lambda (x) (+ (* x x) 1))").asInstanceOf[AnyRef]); + Console.println(lisp2string(string2lisp("(lambda (x) (+ (* x x) 1))"))); + Console.println; + + Console.println("( '(1 2 3)) = " + evaluate(" (quote(1 2 3))")); + Console.println("(car '(1 2 3)) = " + evaluate("(car (quote(1 2 3)))")); + Console.println("(cdr '(1 2 3)) = " + evaluate("(cdr (quote(1 2 3)))")); + Console.println("(null? '(2 3)) = " + evaluate("(null? (quote(2 3)))")); + Console.println("(null? '()) = " + evaluate("(null? (quote()))")); + Console.println; + + Console.println("faculty(10) = " + evaluate( + "(def (faculty n) " + + "(if (= n 0) " + + "1 " + + "(* n (faculty (- n 1)))) " + + "(faculty 10))")); + Console.println("faculty(10) = " + evaluate( + "(def (faculty n) " + + "(cond " + + "((= n 0) 1) " + + "(else (* n (faculty (- n 1))))) " + + "(faculty 10))")); + Console.println("foobar = " + evaluate( + "(def (foo n) " + + "(cond " + + "((= n 0) \"a\")" + + "((= n 1) \"b\")" + + "((= (/ n 2) 1) " + + "(cond " + + "((= n 2) \"c\")" + + "(else \"d\")))" + + "(else " + + "(def (bar m) " + + "(cond " + + "((= m 0) \"e\")" + + "((= m 1) \"f\")" + + "(else \"z\"))" + + "(bar (- n 4)))))" + + "(val nil (quote ())" + + "(val v1 (foo 0) " + + "(val v2 (+ (foo 1) (foo 2)) " + + "(val v3 (+ (+ (foo 3) (foo 4)) (foo 5)) " + + "(val v4 (foo 6) " + + "(cons v1 (cons v2 (cons v3 (cons v4 nil))))))))))")); + Console.println; + } +} + +//############################################################################ +// Main + +object Test { + def main(args: Array[String]) { + new LispUser(LispCaseClasses).run; + new LispUser(LispAny).run; + () + } +} + +//############################################################################ -- cgit v1.2.3 From 399bd6240f775583ee9709311bd0b02e8359c15c Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 20 Mar 2012 10:35:10 -0700 Subject: Never write final fields outside of constructors. Closes SI-3569, SI-3770. Also threw in experimental -Yoverride-vars. It's not robust. --- src/compiler/scala/reflect/internal/Flags.scala | 2 +- .../scala/tools/nsc/backend/jvm/GenJVM.scala | 34 ++++++++++++++------ .../scala/tools/nsc/settings/ScalaSettings.scala | 3 +- .../scala/tools/nsc/typechecker/Namers.scala | 5 +-- .../scala/tools/nsc/typechecker/RefChecks.scala | 15 ++++++--- .../tools/nsc/typechecker/SuperAccessors.scala | 3 +- test/files/run/finalvar.check | 6 ++++ test/files/run/finalvar.flags | 1 + test/files/run/finalvar.scala | 37 ++++++++++++++++++++++ 9 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 test/files/run/finalvar.check create mode 100644 test/files/run/finalvar.flags create mode 100644 test/files/run/finalvar.scala (limited to 'test') diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala index 3110d73461..ce1c8d0908 100644 --- a/src/compiler/scala/reflect/internal/Flags.scala +++ b/src/compiler/scala/reflect/internal/Flags.scala @@ -84,7 +84,7 @@ import scala.collection.{ mutable, immutable } */ class ModifierFlags { final val IMPLICIT = 0x00000200 - final val FINAL = 0x00000020 + final val FINAL = 0x00000020 // May not be overridden. Note that java final implies much more than scala final. final val PRIVATE = 0x00000004 final val PROTECTED = 0x00000001 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 8e326c202a..b7b4212b93 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -871,7 +871,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with debuglog("Adding field: " + f.symbol.fullName) val jfield = jclass.addNewField( - javaFlags(f.symbol) | javaFieldFlags(f.symbol), + javaFieldFlags(f.symbol), javaName(f.symbol), javaType(f.symbol.tpe) ) @@ -1915,16 +1915,30 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with val privateFlag = sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) - // This does not check .isFinal (which checks flags for the FINAL flag), - // instead checking rawflags for that flag so as to exclude symbols which - // received lateFINAL. These symbols are eligible for inlining, but to - // avoid breaking proxy software which depends on subclassing, we avoid - // insisting on their finality in the bytecode. + // Final: the only fields which can receive ACC_FINAL are eager vals. + // Neither vars nor lazy vals can, because: + // + // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3 + // "Another problem is that the specification allows aggressive + // optimization of final fields. Within a thread, it is permissible to + // reorder reads of a final field with those modifications of a final + // field that do not take place in the constructor." + // + // A var or lazy val which is marked final still has meaning to the + // scala compiler. The word final is heavily overloaded unfortunately; + // for us it means "not overridable". At present you can't override + // vars regardless; this may change. + // + // The logic does not check .isFinal (which checks flags for the FINAL flag, + // and includes symbols marked lateFINAL) instead inspecting rawflags so + // we can exclude lateFINAL. Such symbols are eligible for inlining, but to + // avoid breaking proxy software which depends on subclassing, we do not + // emit ACC_FINAL. val finalFlag = ( ((sym.rawflags & (Flags.FINAL | Flags.MODULE)) != 0) && !sym.enclClass.isInterface && !sym.isClassConstructor - && !sym.isMutable // fix for SI-3569, it is too broad? + && !sym.isMutable // lazy vals and vars both ) mkFlags( @@ -1939,13 +1953,13 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (sym.hasFlag(Flags.SYNCHRONIZED)) JAVA_ACC_SYNCHRONIZED else 0 ) } - def javaFieldFlags(sym: Symbol) = { - mkFlags( + def javaFieldFlags(sym: Symbol) = ( + javaFlags(sym) | mkFlags( if (sym hasAnnotation TransientAttr) ACC_TRANSIENT else 0, if (sym hasAnnotation VolatileAttr) ACC_VOLATILE else 0, if (sym.isMutable) 0 else ACC_FINAL ) - } + ) def isTopLevelModule(sym: Symbol): Boolean = afterPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index fdde8f9990..14b3bcc8ce 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -31,7 +31,7 @@ trait ScalaSettings extends AbsScalaSettings protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".") /** Enabled under -Xexperimental. */ - protected def experimentalSettings = List[BooleanSetting](YmethodInfer, overrideObjects) + protected def experimentalSettings = List[BooleanSetting](YmethodInfer, overrideObjects, overrideVars) /** Enabled under -Xfuture. */ protected def futureSettings = List[BooleanSetting]() @@ -117,6 +117,7 @@ trait ScalaSettings extends AbsScalaSettings * -Y "Private" settings */ val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.") + val overrideVars = BooleanSetting ("-Yoverride-vars", "Allow vars to be overridden.") val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.") val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 6b27c27652..8604366bf2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -315,9 +315,10 @@ trait Namers extends MethodSynthesis { case DefDef(_, _, _, _, _, _) => owner.newMethod(name.toTermName, pos, flags) case ClassDef(_, _, _, _) => owner.newClassSymbol(name.toTypeName, pos, flags) case ModuleDef(_, _, _) => owner.newModule(name, pos, flags) - case ValDef(_, _, _, _) if isParameter => owner.newValueParameter(name, pos, flags) case PackageDef(pid, _) => createPackageSymbol(pos, pid) - case ValDef(_, _, _, _) => owner.newValue(name, pos, flags) + case ValDef(_, _, _, _) => + if (isParameter) owner.newValueParameter(name, pos, flags) + else owner.newValue(name, pos, flags) } } private def createFieldSymbol(tree: ValDef): TermSymbol = diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 013a74da7e..9177aca656 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -394,9 +394,14 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R overrideError("needs `override' modifier") } else if (other.isAbstractOverride && other.isIncompleteIn(clazz) && !member.isAbstractOverride) { overrideError("needs `abstract override' modifiers") - } else if (member.isAnyOverride && (other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) { - overrideError("cannot override a mutable variable") - } else if (member.isAnyOverride && + } + else if (member.isAnyOverride && (other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) { + // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here. + // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches. + if (!settings.overrideVars.value) + overrideError("cannot override a mutable variable") + } + else if (member.isAnyOverride && !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && !member.isDeferred && !other.isDeferred && intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) { @@ -1248,9 +1253,9 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } List(tree1) } - case Import(_, _) => Nil + case Import(_, _) => Nil case DefDef(mods, _, _, _, _, _) if (mods hasFlag MACRO) => Nil - case _ => List(transform(tree)) + case _ => List(transform(tree)) } /* Check whether argument types conform to bounds of type parameters */ diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 4248b6f024..94733369a8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -259,7 +259,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case sel @ Select(Super(_, mix), name) => if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { - unit.error(tree.pos, "super may be not be used on "+ sym.accessedOrSelf) + if (!settings.overrideVars.value) + unit.error(tree.pos, "super may be not be used on "+ sym.accessedOrSelf) } else if (isDisallowed(sym)) { unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") diff --git a/test/files/run/finalvar.check b/test/files/run/finalvar.check new file mode 100644 index 0000000000..2496293972 --- /dev/null +++ b/test/files/run/finalvar.check @@ -0,0 +1,6 @@ +(2,2,2,2,1) +(2,2,2,2) +(2,2,2,2,1001) +(2,2,2,2) +2 +10 diff --git a/test/files/run/finalvar.flags b/test/files/run/finalvar.flags new file mode 100644 index 0000000000..aee3039bec --- /dev/null +++ b/test/files/run/finalvar.flags @@ -0,0 +1 @@ +-Yoverride-vars -Yinline \ No newline at end of file diff --git a/test/files/run/finalvar.scala b/test/files/run/finalvar.scala new file mode 100644 index 0000000000..010813e520 --- /dev/null +++ b/test/files/run/finalvar.scala @@ -0,0 +1,37 @@ +object Final { + class X(final var x: Int) { } + def f = new X(0).x += 1 +} + +class A { + var x = 1 + def y0 = x + def y1 = this.x + def y2 = (this: A).x +} + +class B extends A { + override def x = 2 + def z = super.x +} + +object Test { + def main(args: Array[String]): Unit = { + Final.f + val a = new B + println((a.x, a.y0, a.y1, a.y2, a.z)) + val a0: A = a + println((a0.x, a0.y0, a0.y1, a0.y2)) + a.x = 1001 + println((a.x, a.y0, a.y1, a.y2, a.z)) + println((a0.x, a0.y0, a0.y1, a0.y2)) + + val d = new D + println(d.w) + d.ten + println(d.w) + } +} + +class C { var w = 1 ; def ten = this.w = 10 } +class D extends C { override var w = 2 } \ No newline at end of file -- cgit v1.2.3 From 622cc9967376d6cef57c7478587e20d6afe9503f Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 28 Mar 2012 17:54:19 -0700 Subject: Revert the lisp test. The lisp test enjoys the suffering of others. --- test/disabled/run/lisp.check | 26 +++ test/disabled/run/lisp.scala | 518 +++++++++++++++++++++++++++++++++++++++++++ test/files/run/lisp.check | 25 --- test/files/run/lisp.scala | 518 ------------------------------------------- 4 files changed, 544 insertions(+), 543 deletions(-) create mode 100644 test/disabled/run/lisp.check create mode 100644 test/disabled/run/lisp.scala delete mode 100644 test/files/run/lisp.check delete mode 100644 test/files/run/lisp.scala (limited to 'test') diff --git a/test/disabled/run/lisp.check b/test/disabled/run/lisp.check new file mode 100644 index 0000000000..64053f26d0 --- /dev/null +++ b/test/disabled/run/lisp.check @@ -0,0 +1,26 @@ +(lambda (x) (+ (* x x) 1)) +(lambda (x) (+ (* x x) 1)) + +( '(1 2 3)) = (1 2 3) +(car '(1 2 3)) = 1 +(cdr '(1 2 3)) = (2 3) +(null? '(2 3)) = 0 +(null? '()) = 1 + +faculty(10) = 3628800 +faculty(10) = 3628800 +foobar = ("a" "bc" "def" "z") + +List('lambda, List('x), List('+, List('*, 'x, 'x), 1)) +(lambda (x) (+ (* x x) 1)) + +( '(1 2 3)) = (1 2 3) +(car '(1 2 3)) = 1 +(cdr '(1 2 3)) = (2 3) +(null? '(2 3)) = 0 +(null? '()) = 1 + +faculty(10) = 3628800 +faculty(10) = 3628800 +foobar = ("a" "bc" "def" "z") + diff --git a/test/disabled/run/lisp.scala b/test/disabled/run/lisp.scala new file mode 100644 index 0000000000..06e68f508a --- /dev/null +++ b/test/disabled/run/lisp.scala @@ -0,0 +1,518 @@ +//############################################################################ +// Lisp interpreter +//############################################################################ + +//############################################################################ +// Lisp Scanner + +class LispTokenizer(s: String) extends Iterator[String] { + private var i = 0; + private def isDelimiter(ch: Char) = ch <= ' ' || ch == '(' || ch == ')' + def hasNext: Boolean = { + while (i < s.length() && s.charAt(i) <= ' ') i += 1 + i < s.length() + } + def next: String = + if (hasNext) { + val start = i + if (isDelimiter(s charAt i)) i += 1 + else + do i = i + 1 + while (!isDelimiter(s charAt i)) + s.substring(start, i) + } else sys.error("premature end of string") +} + +//############################################################################ +// Lisp Interface + +trait Lisp { + type Data + + def string2lisp(s: String): Data + def lisp2string(s: Data): String + + def evaluate(d: Data): Data + // !!! def evaluate(s: String): Data = evaluate(string2lisp(s)) + def evaluate(s: String): Data +} + +//############################################################################ +// Lisp Implementation Using Case Classes + +object LispCaseClasses extends Lisp { + + import List.range + + trait Data { + def elemsToString(): String = toString(); + } + case class CONS(car: Data, cdr: Data) extends Data { + override def toString() = "(" + elemsToString() + ")"; + override def elemsToString() = car.toString() + (cdr match { + case NIL() => "" + case _ => " " + cdr.elemsToString(); + }) + } + case class NIL() extends Data { // !!! use case object + override def toString() = "()"; + } + case class SYM(name: String) extends Data { + override def toString() = name; + } + case class NUM(x: Int) extends Data { + override def toString() = x.toString(); + } + case class STR(x: String) extends Data { + override def toString() = "\"" + x + "\""; + } + case class FUN(f: List[Data] => Data) extends Data { + override def toString() = ""; + } + + def list(): Data = + NIL(); + def list(x0: Data): Data = + CONS(x0, NIL()); + def list(x0: Data, x1: Data): Data = + CONS(x0, list(x1)); + def list(x0: Data, x1: Data, x2: Data): Data = + CONS(x0, list(x1, x2)); + def list(x0: Data, x1: Data, x2: Data, x3: Data): Data = + CONS(x0, list(x1, x2, x3)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data): Data = + CONS(x0, list(x1, x2, x3, x4)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data, x7: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6, x7)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data, x7: Data, x8: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8)); + def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, + x6: Data, x7: Data, x8: Data, x9: Data): Data = + CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8, x9)); + + var curexp: Data = null + var trace: Boolean = false + var indent: Int = 0 + + def lispError[a](msg: String): a = + sys.error("error: " + msg + "\n" + curexp); + + trait Environment { + def lookup(n: String): Data; + def extendRec(name: String, expr: Environment => Data) = + new Environment { + def lookup(n: String): Data = + if (n == name) expr(this) else Environment.this.lookup(n); + } + def extend(name: String, v: Data) = extendRec(name, (env1 => v)); + } + val EmptyEnvironment = new Environment { + def lookup(n: String): Data = lispError("undefined: " + n); + } + + def toList(x: Data): List[Data] = x match { + case NIL() => List() + case CONS(y, ys) => y :: toList(ys) + case _ => lispError("malformed list: " + x); + } + + def toBoolean(x: Data) = x match { + case NUM(0) => false + case _ => true + } + + def normalize(x: Data): Data = x match { + case CONS(SYM("def"), + CONS(CONS(SYM(name), args), CONS(body, CONS(expr, NIL())))) => + normalize(list(SYM("def"), + SYM(name), list(SYM("lambda"), args, body), expr)) + case CONS(SYM("cond"), CONS(CONS(SYM("else"), CONS(expr, NIL())),NIL())) => + normalize(expr) + case CONS(SYM("cond"), CONS(CONS(test, CONS(expr, NIL())), rest)) => + normalize(list(SYM("if"), test, expr, CONS(SYM("cond"), rest))) + case CONS(h, t) => CONS(normalize(h), normalize(t)) + case _ => x + } + + def eval(x: Data, env: Environment): Data = { + val prevexp = curexp; + curexp = x; + if (trace) { + for (x <- range(1, indent)) Console.print(" "); + Console.println("===> " + x); + indent = indent + 1; + } + val result = eval1(x, env); + if (trace) { + indent = indent - 1; + for (x <- range(1, indent)) Console.print(" "); + Console.println("<=== " + result); + } + curexp = prevexp; + result + } + + def eval1(x: Data, env: Environment): Data = x match { + case SYM(name) => + env lookup name + case CONS(SYM("def"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => + eval(z, env.extendRec(name, (env1 => eval(y, env1)))) + case CONS(SYM("val"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => + eval(z, env.extend(name, eval(y, env))) + case CONS(SYM("lambda"), CONS(params, CONS(y, NIL()))) => + mkLambda(params, y, env) + case CONS(SYM("if"), CONS(c, CONS(t, CONS(e, NIL())))) => + if (toBoolean(eval(c, env))) eval(t, env) else eval(e, env) + case CONS(SYM("quote"), CONS(x, NIL())) => + x + case CONS(y, xs) => + apply(eval(y, env), toList(xs) map (x => eval(x, env))) + case NUM(_) => x + case STR(_) => x + case FUN(_) => x + case _ => + lispError("illegal term") + } + + def apply(fn: Data, args: List[Data]): Data = fn match { + case FUN(f) => f(args); + case _ => lispError("application of non-function: " + fn); + } + + def mkLambda(params: Data, expr: Data, env: Environment): Data = { + + def extendEnv(env: Environment, + ps: List[String], args: List[Data]): Environment = + Pair(ps, args) match { + case Pair(List(), List()) => + env + case Pair(p :: ps1, arg :: args1) => + extendEnv(env.extend(p, arg), ps1, args1) + case _ => + lispError("wrong number of arguments") + } + + val ps: List[String] = toList(params) map { + case SYM(name) => name + case _ => sys.error("illegal parameter list"); + } + + FUN(args => eval(expr, extendEnv(env, ps, args))) + } + + val globalEnv = EmptyEnvironment + .extend("=", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(if (arg1 == arg2) 1 else 0) + case List(STR(arg1),STR(arg2)) => NUM(if (arg1 == arg2) 1 else 0)})) + .extend("+", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 + arg2) + case List(STR(arg1),STR(arg2)) => STR(arg1 + arg2)})) + .extend("-", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 - arg2)})) + .extend("*", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 * arg2)})) + .extend("/", FUN({ + case List(NUM(arg1),NUM(arg2)) => NUM(arg1 / arg2)})) + .extend("car", FUN({ + case List(CONS(x, xs)) => x})) + .extend("cdr", FUN({ + case List(CONS(x, xs)) => xs})) + .extend("null?", FUN({ + case List(NIL()) => NUM(1) + case _ => NUM(0)})) + .extend("cons", FUN({ + case List(x, y) => CONS(x, y)})); + + def evaluate(x: Data): Data = eval(normalize(x), globalEnv); + def evaluate(s: String): Data = evaluate(string2lisp(s)); + + def string2lisp(s: String): Data = { + val it = new LispTokenizer(s); + def parseExpr(token: String): Data = { + if (token == "(") parseList + else if (token == ")") sys.error("unbalanced parentheses") + else if ('0' <= token.charAt(0) && token.charAt(0) <= '9') + NUM(token.toInt) + else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') + STR(token.substring(1,token.length() - 1)) + else SYM(token) + } + def parseList: Data = { + val token = it.next; + if (token == ")") NIL() else CONS(parseExpr(token), parseList) + } + parseExpr(it.next) + } + + def lisp2string(d: Data): String = d.toString(); +} + +//############################################################################ +// Lisp Implementation Using Any + +object LispAny extends Lisp { + + import List._; + + type Data = Any; + + case class Lambda(f: List[Data] => Data); + + var curexp: Data = null; + var trace: Boolean = false; + var indent: Int = 0; + + def lispError[a](msg: String): a = + sys.error("error: " + msg + "\n" + curexp); + + trait Environment { + def lookup(n: String): Data; + def extendRec(name: String, expr: Environment => Data) = + new Environment { + def lookup(n: String): Data = + if (n == name) expr(this) else Environment.this.lookup(n); + } + def extend(name: String, v: Data) = extendRec(name, (env1 => v)); + } + val EmptyEnvironment = new Environment { + def lookup(n: String): Data = lispError("undefined: " + n); + } + + def asList(x: Data): List[Data] = x match { + case y: List[_] => y + case _ => lispError("malformed list: " + x) + } + + def asInt(x: Data): Int = x match { + case y: Int => y + case _ => lispError("not an integer: " + x) + } + + def asString(x: Data): String = x match { + case y: String => y + case _ => lispError("not a string: " + x) + } + + def asBoolean(x: Data): Boolean = x != 0 + + def normalize(x: Data): Data = x match { + case 'and :: x :: y :: Nil => + normalize('if :: x :: y :: 0 :: Nil) + case 'or :: x :: y :: Nil => + normalize('if :: x :: 1 :: y :: Nil) + case 'def :: (name :: args) :: body :: expr :: Nil => + normalize('def :: name :: ('lambda :: args :: body :: Nil) :: expr :: Nil) + case 'cond :: ('else :: expr :: Nil) :: rest => + normalize(expr); + case 'cond :: (test :: expr :: Nil) :: rest => + normalize('if :: test :: expr :: ('cond :: rest) :: Nil) + case 'cond :: 'else :: expr :: Nil => + normalize(expr) + case h :: t => + normalize(h) :: asList(normalize(t)) + case _ => + x + } + + def eval(x: Data, env: Environment): Data = { + val prevexp = curexp; + curexp = x; + if (trace) { + for (x <- range(1, indent)) Console.print(" "); + Console.println("===> " + x); + indent += 1; + } + val result = eval1(x, env); + if (trace) { + indent -= 1; + for (x <- range(1, indent)) Console.print(" "); + Console.println("<=== " + result); + } + curexp = prevexp; + result + } + + def eval1(x: Data, env: Environment): Data = x match { + case Symbol(name) => + env lookup name + case 'def :: Symbol(name) :: y :: z :: Nil => + eval(z, env.extendRec(name, (env1 => eval(y, env1)))) + case 'val :: Symbol(name) :: y :: z :: Nil => + eval(z, env.extend(name, eval(y, env))) + case 'lambda :: params :: y :: Nil => + mkLambda(params, y, env) + case 'if :: c :: y :: z :: Nil => + if (asBoolean(eval(c, env))) eval(y, env) else eval(z, env) + case 'quote :: y :: Nil => + y + case y :: z => + apply(eval(y, env), z map (x => eval(x, env))) + case Lambda(_) => x + case y: String => x + case y: Int => x + case y => lispError("illegal term") + } + + def lisp2string(x: Data): String = x match { + case Symbol(name) => name + case Nil => "()" + case y :: ys => + def list2string(xs: List[Data]): String = xs match { + case List() => "" + case y :: ys => " " + lisp2string(y) + list2string(ys) + } + "(" + lisp2string(y) + list2string(ys) + ")" + case _ => if (x.isInstanceOf[String]) "\"" + x + "\""; else x.toString() + } + + def apply(fn: Data, args: List[Data]): Data = fn match { + case Lambda(f) => f(args); + case _ => lispError("application of non-function: " + fn + " to " + args); + } + + def mkLambda(params: Data, expr: Data, env: Environment): Data = { + + def extendEnv(env: Environment, + ps: List[String], args: List[Data]): Environment = + Pair(ps, args) match { + case Pair(List(), List()) => + env + case Pair(p :: ps1, arg :: args1) => + extendEnv(env.extend(p, arg), ps1, args1) + case _ => + lispError("wrong number of arguments") + } + + val ps: List[String] = asList(params) map { + case Symbol(name) => name + case _ => sys.error("illegal parameter list"); + } + + Lambda(args => eval(expr, extendEnv(env, ps, args))) + } + + val globalEnv = EmptyEnvironment + .extend("=", Lambda{ + case List(arg1, arg2) => if(arg1 == arg2) 1 else 0}) + .extend("+", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 + arg2 + case List(arg1: String, arg2: String) => arg1 + arg2}) + .extend("-", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 - arg2}) + .extend("*", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 * arg2}) + .extend("/", Lambda{ + case List(arg1: Int, arg2: Int) => arg1 / arg2}) + .extend("nil", Nil) + .extend("cons", Lambda{ + case List(arg1, arg2) => arg1 :: asList(arg2)}) + .extend("car", Lambda{ + case List(x :: xs) => x}) + .extend("cdr", Lambda{ + case List(x :: xs) => xs}) + .extend("null?", Lambda{ + case List(Nil) => 1 + case _ => 0}); + + def evaluate(x: Data): Data = eval(normalize(x), globalEnv); + def evaluate(s: String): Data = evaluate(string2lisp(s)); + + def string2lisp(s: String): Data = { + val it = new LispTokenizer(s); + def parseExpr(token: String): Data = { + if (token == "(") parseList + else if (token == ")") sys.error("unbalanced parentheses") + //else if (Character.isDigit(token.charAt(0))) + else if (token.charAt(0).isDigit) + token.toInt + else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') + token.substring(1,token.length() - 1) + else Symbol(token) + } + def parseList: List[Data] = { + val token = it.next; + if (token == ")") Nil else parseExpr(token) :: parseList + } + parseExpr(it.next) + } +} + +//############################################################################ +// List User + +class LispUser(lisp: Lisp) { + + import lisp._; + + def evaluate(s: String) = lisp2string(lisp.evaluate(s)); + + def run = { + + Console.println(string2lisp("(lambda (x) (+ (* x x) 1))").asInstanceOf[AnyRef]); + Console.println(lisp2string(string2lisp("(lambda (x) (+ (* x x) 1))"))); + Console.println; + + Console.println("( '(1 2 3)) = " + evaluate(" (quote(1 2 3))")); + Console.println("(car '(1 2 3)) = " + evaluate("(car (quote(1 2 3)))")); + Console.println("(cdr '(1 2 3)) = " + evaluate("(cdr (quote(1 2 3)))")); + Console.println("(null? '(2 3)) = " + evaluate("(null? (quote(2 3)))")); + Console.println("(null? '()) = " + evaluate("(null? (quote()))")); + Console.println; + + Console.println("faculty(10) = " + evaluate( + "(def (faculty n) " + + "(if (= n 0) " + + "1 " + + "(* n (faculty (- n 1)))) " + + "(faculty 10))")); + Console.println("faculty(10) = " + evaluate( + "(def (faculty n) " + + "(cond " + + "((= n 0) 1) " + + "(else (* n (faculty (- n 1))))) " + + "(faculty 10))")); + Console.println("foobar = " + evaluate( + "(def (foo n) " + + "(cond " + + "((= n 0) \"a\")" + + "((= n 1) \"b\")" + + "((= (/ n 2) 1) " + + "(cond " + + "((= n 2) \"c\")" + + "(else \"d\")))" + + "(else " + + "(def (bar m) " + + "(cond " + + "((= m 0) \"e\")" + + "((= m 1) \"f\")" + + "(else \"z\"))" + + "(bar (- n 4)))))" + + "(val nil (quote ())" + + "(val v1 (foo 0) " + + "(val v2 (+ (foo 1) (foo 2)) " + + "(val v3 (+ (+ (foo 3) (foo 4)) (foo 5)) " + + "(val v4 (foo 6) " + + "(cons v1 (cons v2 (cons v3 (cons v4 nil))))))))))")); + Console.println; + } +} + +//############################################################################ +// Main + +object Test { + def main(args: Array[String]) { + new LispUser(LispCaseClasses).run; + new LispUser(LispAny).run; + () + } +} + +//############################################################################ diff --git a/test/files/run/lisp.check b/test/files/run/lisp.check deleted file mode 100644 index 38ca7b655d..0000000000 --- a/test/files/run/lisp.check +++ /dev/null @@ -1,25 +0,0 @@ -(lambda (x) (+ (* x x) 1)) -(lambda (x) (+ (* x x) 1)) - -( '(1 2 3)) = (1 2 3) -(car '(1 2 3)) = 1 -(cdr '(1 2 3)) = (2 3) -(null? '(2 3)) = 0 -(null? '()) = 1 - -faculty(10) = 3628800 -faculty(10) = 3628800 -foobar = ("a" "bc" "def" "z") - -List('lambda, List('x), List('+, List('*, 'x, 'x), 1)) -(lambda (x) (+ (* x x) 1)) - -( '(1 2 3)) = (1 2 3) -(car '(1 2 3)) = 1 -(cdr '(1 2 3)) = (2 3) -(null? '(2 3)) = 0 -(null? '()) = 1 - -faculty(10) = 3628800 -faculty(10) = 3628800 -foobar = ("a" "bc" "def" "z") diff --git a/test/files/run/lisp.scala b/test/files/run/lisp.scala deleted file mode 100644 index 07f44e1151..0000000000 --- a/test/files/run/lisp.scala +++ /dev/null @@ -1,518 +0,0 @@ -//############################################################################ -// Lisp interpreter -//############################################################################ - -//############################################################################ -// Lisp Scanner - -class LispTokenizer(s: String) extends Iterator[String] { - private var i = 0; - private def isDelimiter(ch: Char) = ch <= ' ' || ch == '(' || ch == ')' - def hasNext: Boolean = { - while (i < s.length() && s.charAt(i) <= ' ') i += 1 - i < s.length() - } - def next: String = - if (hasNext) { - val start = i - if (isDelimiter(s charAt i)) i += 1 - else - do i = i + 1 - while (!isDelimiter(s charAt i)) - s.substring(start, i) - } else sys.error("premature end of string") -} - -//############################################################################ -// Lisp Interface - -trait Lisp { - type Data - - def string2lisp(s: String): Data - def lisp2string(s: Data): String - - def evaluate(d: Data): Data - // !!! def evaluate(s: String): Data = evaluate(string2lisp(s)) - def evaluate(s: String): Data -} - -//############################################################################ -// Lisp Implementation Using Case Classes - -object LispCaseClasses extends Lisp { - - import List.range - - trait Data { - def elemsToString(): String = toString(); - } - case class CONS(car: Data, cdr: Data) extends Data { - override def toString() = "(" + elemsToString() + ")"; - override def elemsToString() = car.toString() + (cdr match { - case NIL() => "" - case _ => " " + cdr.elemsToString(); - }) - } - case class NIL() extends Data { // !!! use case object - override def toString() = "()"; - } - case class SYM(name: String) extends Data { - override def toString() = name; - } - case class NUM(x: Int) extends Data { - override def toString() = x.toString(); - } - case class STR(x: String) extends Data { - override def toString() = "\"" + x + "\""; - } - case class FUN(f: List[Data] => Data) extends Data { - override def toString() = ""; - } - - def list(): Data = - NIL(); - def list(x0: Data): Data = - CONS(x0, NIL()); - def list(x0: Data, x1: Data): Data = - CONS(x0, list(x1)); - def list(x0: Data, x1: Data, x2: Data): Data = - CONS(x0, list(x1, x2)); - def list(x0: Data, x1: Data, x2: Data, x3: Data): Data = - CONS(x0, list(x1, x2, x3)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data): Data = - CONS(x0, list(x1, x2, x3, x4)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data, x7: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6, x7)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data, x7: Data, x8: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8)); - def list(x0: Data, x1: Data, x2: Data, x3: Data, x4: Data, x5: Data, - x6: Data, x7: Data, x8: Data, x9: Data): Data = - CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8, x9)); - - var curexp: Data = null - var trace: Boolean = false - var indent: Int = 0 - - def lispError[a](msg: String): a = - sys.error("error: " + msg + "\n" + curexp); - - trait Environment { - def lookup(n: String): Data; - def extendRec(name: String, expr: Environment => Data) = - new Environment { - def lookup(n: String): Data = - if (n == name) expr(this) else Environment.this.lookup(n); - } - def extend(name: String, v: Data) = extendRec(name, (env1 => v)); - } - val EmptyEnvironment = new Environment { - def lookup(n: String): Data = lispError("undefined: " + n); - } - - def toList(x: Data): List[Data] = x match { - case NIL() => List() - case CONS(y, ys) => y :: toList(ys) - case _ => lispError("malformed list: " + x); - } - - def toBoolean(x: Data) = x match { - case NUM(0) => false - case _ => true - } - - def normalize(x: Data): Data = x match { - case CONS(SYM("def"), - CONS(CONS(SYM(name), args), CONS(body, CONS(expr, NIL())))) => - normalize(list(SYM("def"), - SYM(name), list(SYM("lambda"), args, body), expr)) - case CONS(SYM("cond"), CONS(CONS(SYM("else"), CONS(expr, NIL())),NIL())) => - normalize(expr) - case CONS(SYM("cond"), CONS(CONS(test, CONS(expr, NIL())), rest)) => - normalize(list(SYM("if"), test, expr, CONS(SYM("cond"), rest))) - case CONS(h, t) => CONS(normalize(h), normalize(t)) - case _ => x - } - - def eval(x: Data, env: Environment): Data = { - val prevexp = curexp; - curexp = x; - if (trace) { - for (x <- range(1, indent)) Console.print(" "); - Console.println("===> " + x); - indent = indent + 1; - } - val result = eval1(x, env); - if (trace) { - indent = indent - 1; - for (x <- range(1, indent)) Console.print(" "); - Console.println("<=== " + result); - } - curexp = prevexp; - result - } - - def eval1(x: Data, env: Environment): Data = x match { - case SYM(name) => - env lookup name - case CONS(SYM("def"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => - eval(z, env.extendRec(name, (env1 => eval(y, env1)))) - case CONS(SYM("val"), CONS(SYM(name), CONS(y, CONS(z, NIL())))) => - eval(z, env.extend(name, eval(y, env))) - case CONS(SYM("lambda"), CONS(params, CONS(y, NIL()))) => - mkLambda(params, y, env) - case CONS(SYM("if"), CONS(c, CONS(t, CONS(e, NIL())))) => - if (toBoolean(eval(c, env))) eval(t, env) else eval(e, env) - case CONS(SYM("quote"), CONS(x, NIL())) => - x - case CONS(y, xs) => - apply(eval(y, env), toList(xs) map (x => eval(x, env))) - case NUM(_) => x - case STR(_) => x - case FUN(_) => x - case _ => - lispError("illegal term") - } - - def apply(fn: Data, args: List[Data]): Data = fn match { - case FUN(f) => f(args); - case _ => lispError("application of non-function: " + fn); - } - - def mkLambda(params: Data, expr: Data, env: Environment): Data = { - - def extendEnv(env: Environment, - ps: List[String], args: List[Data]): Environment = - Pair(ps, args) match { - case Pair(List(), List()) => - env - case Pair(p :: ps1, arg :: args1) => - extendEnv(env.extend(p, arg), ps1, args1) - case _ => - lispError("wrong number of arguments") - } - - val ps: List[String] = toList(params) map { - case SYM(name) => name - case _ => sys.error("illegal parameter list"); - } - - FUN(args => eval(expr, extendEnv(env, ps, args))) - } - - val globalEnv = EmptyEnvironment - .extend("=", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(if (arg1 == arg2) 1 else 0) - case List(STR(arg1),STR(arg2)) => NUM(if (arg1 == arg2) 1 else 0)})) - .extend("+", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 + arg2) - case List(STR(arg1),STR(arg2)) => STR(arg1 + arg2)})) - .extend("-", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 - arg2)})) - .extend("*", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 * arg2)})) - .extend("/", FUN({ - case List(NUM(arg1),NUM(arg2)) => NUM(arg1 / arg2)})) - .extend("car", FUN({ - case List(CONS(x, xs)) => x})) - .extend("cdr", FUN({ - case List(CONS(x, xs)) => xs})) - .extend("null?", FUN({ - case List(NIL()) => NUM(1) - case _ => NUM(0)})) - .extend("cons", FUN({ - case List(x, y) => CONS(x, y)})); - - def evaluate(x: Data): Data = eval(normalize(x), globalEnv); - def evaluate(s: String): Data = evaluate(string2lisp(s)); - - def string2lisp(s: String): Data = { - val it = new LispTokenizer(s); - def parseExpr(token: String): Data = { - if (token == "(") parseList - else if (token == ")") sys.error("unbalanced parentheses") - else if ('0' <= token.charAt(0) && token.charAt(0) <= '9') - NUM(token.toInt) - else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') - STR(token.substring(1,token.length() - 1)) - else SYM(token) - } - def parseList: Data = { - val token = it.next; - if (token == ")") NIL() else CONS(parseExpr(token), parseList) - } - parseExpr(it.next) - } - - def lisp2string(d: Data): String = d.toString(); -} - -//############################################################################ -// Lisp Implementation Using Any - -object LispAny extends Lisp { - - import List._; - - type Data = Any; - - case class Lambda(f: List[Data] => Data); - - var curexp: Data = null; - var trace: Boolean = false; - var indent: Int = 0; - - def lispError[a](msg: String): a = - sys.error("error: " + msg + "\n" + curexp); - - trait Environment { - def lookup(n: String): Data; - def extendRec(name: String, expr: Environment => Data) = - new Environment { - def lookup(n: String): Data = - if (n == name) expr(this) else Environment.this.lookup(n); - } - def extend(name: String, v: Data) = extendRec(name, (env1 => v)); - } - val EmptyEnvironment = new Environment { - def lookup(n: String): Data = lispError("undefined: " + n); - } - - def asList(x: Data): List[Data] = x match { - case y: List[_] => y - case _ => lispError("malformed list: " + x) - } - - def asInt(x: Data): Int = x match { - case y: Int => y - case _ => lispError("not an integer: " + x) - } - - def asString(x: Data): String = x match { - case y: String => y - case _ => lispError("not a string: " + x) - } - - def asBoolean(x: Data): Boolean = x != 0 - - def normalize(x: Data): Data = x match { - case 'and :: x :: y :: Nil => - normalize('if :: x :: y :: 0 :: Nil) - case 'or :: x :: y :: Nil => - normalize('if :: x :: 1 :: y :: Nil) - case 'def :: (name :: args) :: body :: expr :: Nil => - normalize('def :: name :: ('lambda :: args :: body :: Nil) :: expr :: Nil) - case 'cond :: ('else :: expr :: Nil) :: rest => - normalize(expr); - case 'cond :: (test :: expr :: Nil) :: rest => - normalize('if :: test :: expr :: ('cond :: rest) :: Nil) - case 'cond :: 'else :: expr :: Nil => - normalize(expr) - case h :: t => - normalize(h) :: asList(normalize(t)) - case _ => - x - } - - def eval(x: Data, env: Environment): Data = { - val prevexp = curexp; - curexp = x; - if (trace) { - for (x <- range(1, indent)) Console.print(" "); - Console.println("===> " + x); - indent += 1; - } - val result = eval1(x, env); - if (trace) { - indent -= 1; - for (x <- range(1, indent)) Console.print(" "); - Console.println("<=== " + result); - } - curexp = prevexp; - result - } - - def eval1(x: Data, env: Environment): Data = x match { - case Symbol(name) => - env lookup name - case 'def :: Symbol(name) :: y :: z :: Nil => - eval(z, env.extendRec(name, (env1 => eval(y, env1)))) - case 'val :: Symbol(name) :: y :: z :: Nil => - eval(z, env.extend(name, eval(y, env))) - case 'lambda :: params :: y :: Nil => - mkLambda(params, y, env) - case 'if :: c :: y :: z :: Nil => - if (asBoolean(eval(c, env))) eval(y, env) else eval(z, env) - case 'quote :: y :: Nil => - y - case y :: z => - apply(eval(y, env), z map (x => eval(x, env))) - case Lambda(_) => x - case y: String => x - case y: Int => x - case y => lispError("illegal term") - } - - def lisp2string(x: Data): String = x match { - case Symbol(name) => name - case Nil => "()" - case y :: ys => - def list2string(xs: List[Data]): String = xs match { - case List() => "" - case y :: ys => " " + lisp2string(y) + list2string(ys) - } - "(" + lisp2string(y) + list2string(ys) + ")" - case _ => if (x.isInstanceOf[String]) "\"" + x + "\""; else x.toString() - } - - def apply(fn: Data, args: List[Data]): Data = fn match { - case Lambda(f) => f(args); - case _ => lispError("application of non-function: " + fn + " to " + args); - } - - def mkLambda(params: Data, expr: Data, env: Environment): Data = { - - def extendEnv(env: Environment, - ps: List[String], args: List[Data]): Environment = - Pair(ps, args) match { - case Pair(List(), List()) => - env - case Pair(p :: ps1, arg :: args1) => - extendEnv(env.extend(p, arg), ps1, args1) - case _ => - lispError("wrong number of arguments") - } - - val ps: List[String] = asList(params) map { - case Symbol(name) => name - case _ => sys.error("illegal parameter list"); - } - - Lambda(args => eval(expr, extendEnv(env, ps, args))) - } - - val globalEnv = EmptyEnvironment - .extend("=", Lambda{ - case List(arg1, arg2) => if(arg1 == arg2) 1 else 0}) - .extend("+", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 + arg2 - case List(arg1: String, arg2: String) => arg1 + arg2}) - .extend("-", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 - arg2}) - .extend("*", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 * arg2}) - .extend("/", Lambda{ - case List(arg1: Int, arg2: Int) => arg1 / arg2}) - .extend("nil", Nil) - .extend("cons", Lambda{ - case List(arg1, arg2) => arg1 :: asList(arg2)}) - .extend("car", Lambda{ - case List(x :: xs) => x}) - .extend("cdr", Lambda{ - case List(x :: xs) => xs}) - .extend("null?", Lambda{ - case List(Nil) => 1 - case _ => 0}); - - def evaluate(x: Data): Data = eval(normalize(x), globalEnv); - def evaluate(s: String): Data = evaluate(string2lisp(s)); - - def string2lisp(s: String): Data = { - val it = new LispTokenizer(s); - def parseExpr(token: String): Data = { - if (token == "(") parseList - else if (token == ")") sys.error("unbalanced parentheses") - //else if (Character.isDigit(token.charAt(0))) - else if (token.charAt(0).isDigit) - token.toInt - else if (token.charAt(0) == '\"' && token.charAt(token.length()-1)=='\"') - token.substring(1,token.length() - 1) - else Symbol(token) - } - def parseList: List[Data] = { - val token = it.next; - if (token == ")") Nil else parseExpr(token) :: parseList - } - parseExpr(it.next) - } -} - -//############################################################################ -// List User - -class LispUser(lisp: Lisp) { - - import lisp._; - - def evaluate(s: String) = lisp2string(lisp.evaluate(s)); - - def run = { - - Console.println(string2lisp("(lambda (x) (+ (* x x) 1))").asInstanceOf[AnyRef]); - Console.println(lisp2string(string2lisp("(lambda (x) (+ (* x x) 1))"))); - Console.println; - - Console.println("( '(1 2 3)) = " + evaluate(" (quote(1 2 3))")); - Console.println("(car '(1 2 3)) = " + evaluate("(car (quote(1 2 3)))")); - Console.println("(cdr '(1 2 3)) = " + evaluate("(cdr (quote(1 2 3)))")); - Console.println("(null? '(2 3)) = " + evaluate("(null? (quote(2 3)))")); - Console.println("(null? '()) = " + evaluate("(null? (quote()))")); - Console.println; - - Console.println("faculty(10) = " + evaluate( - "(def (faculty n) " + - "(if (= n 0) " + - "1 " + - "(* n (faculty (- n 1)))) " + - "(faculty 10))")); - Console.println("faculty(10) = " + evaluate( - "(def (faculty n) " + - "(cond " + - "((= n 0) 1) " + - "(else (* n (faculty (- n 1))))) " + - "(faculty 10))")); - Console.println("foobar = " + evaluate( - "(def (foo n) " + - "(cond " + - "((= n 0) \"a\")" + - "((= n 1) \"b\")" + - "((= (/ n 2) 1) " + - "(cond " + - "((= n 2) \"c\")" + - "(else \"d\")))" + - "(else " + - "(def (bar m) " + - "(cond " + - "((= m 0) \"e\")" + - "((= m 1) \"f\")" + - "(else \"z\"))" + - "(bar (- n 4)))))" + - "(val nil (quote ())" + - "(val v1 (foo 0) " + - "(val v2 (+ (foo 1) (foo 2)) " + - "(val v3 (+ (+ (foo 3) (foo 4)) (foo 5)) " + - "(val v4 (foo 6) " + - "(cons v1 (cons v2 (cons v3 (cons v4 nil))))))))))")); - Console.println; - } -} - -//############################################################################ -// Main - -object Test { - def main(args: Array[String]) { - new LispUser(LispCaseClasses).run; - new LispUser(LispAny).run; - () - } -} - -//############################################################################ -- cgit v1.2.3 From 0bc2210f5541bfc70d0316bdb1ae89755f4aa70c Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 28 Mar 2012 09:26:15 -0700 Subject: A couple tests for pending. --- test/pending/pos/t1476.scala | 23 +++++++++++++++++++++++ test/pending/pos/t5626.scala | 12 ++++++++++++ test/pending/run/t5629.check | 2 ++ test/pending/run/t5629.scala | 25 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 test/pending/pos/t1476.scala create mode 100644 test/pending/pos/t5626.scala create mode 100644 test/pending/run/t5629.check create mode 100644 test/pending/run/t5629.scala (limited to 'test') diff --git a/test/pending/pos/t1476.scala b/test/pending/pos/t1476.scala new file mode 100644 index 0000000000..1f8e95c28f --- /dev/null +++ b/test/pending/pos/t1476.scala @@ -0,0 +1,23 @@ +abstract class Module { + def moduleDemands(): List[Module] +} + +object Test { + new Module { owner: Module => + def moduleDemands() = Nil + + val a = new Module { def moduleDemands(): List[Module] = Nil } + val b = new Module { def moduleDemands(): List[Module] = owner :: c :: Nil } + val c = new Module { def moduleDemands(): List[Module] = owner :: a :: Nil } + } +} + +object Test2 { + new Module { owner => + def moduleDemands() = Nil + + val a = new Module { def moduleDemands(): List[Module] = Nil } + val b = new Module { def moduleDemands(): List[Module] = owner :: c :: Nil } + val c = new Module { def moduleDemands(): List[Module] = owner :: a :: Nil } + } +} diff --git a/test/pending/pos/t5626.scala b/test/pending/pos/t5626.scala new file mode 100644 index 0000000000..7ab3881827 --- /dev/null +++ b/test/pending/pos/t5626.scala @@ -0,0 +1,12 @@ +object Test { + val blob0 = new { + case class Foo(i : Int) + } + val foo0 = blob0.Foo(22) + + val blob1 = new { + class Foo(i: Int) + object Foo { def apply(i: Int): Foo = new Foo(i) } + } + val foo1 = blob1.Foo(22) +} diff --git a/test/pending/run/t5629.check b/test/pending/run/t5629.check new file mode 100644 index 0000000000..6a2d630f4e --- /dev/null +++ b/test/pending/run/t5629.check @@ -0,0 +1,2 @@ +int child got: 33 +any child got: 33 diff --git a/test/pending/run/t5629.scala b/test/pending/run/t5629.scala new file mode 100644 index 0000000000..28e74a1c94 --- /dev/null +++ b/test/pending/run/t5629.scala @@ -0,0 +1,25 @@ +import scala.{specialized => spec} + +trait GrandParent[@spec(Int) -A] { + def foo(a:A): Unit + def bar[B <: A](b:B): Unit = println("grandparent got: %s" format b) +} + +trait Parent[@spec(Int) -A] extends GrandParent[A] { + def foo(a:A) = bar(a) +} + +class IntChild extends Parent[Int] { + override def bar[B <: Int](b:B): Unit = println("int child got: %s" format b) +} + +class AnyChild extends Parent[Any] { + override def bar[B <: Any](b:B): Unit = println("any child got: %s" format b) +} + +object Test { + def main(args:Array[String]) { + new IntChild().foo(33) + new AnyChild().foo(33) + } +} -- cgit v1.2.3 From 66b47e1a8c11196d648ed5a98f934a1c65203a65 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Mon, 16 Jan 2012 07:57:09 +0700 Subject: a fast, functional PartialFunction implementation runtime.AbstractPartialFunction provides a default implementation for the new-style partial function. In principle this class is only subclassed by compiler-generated partial functions arising from matches. Either - the apply method (old-style partialfun) or - the applyOrElse method (current scheme) must be overridden, and the isDefinedAt method implemented. The applyOrElse method implementation is provided to ease the transition from the old scheme, since starr still generates old-style PartialFunctions, but locker's library has the new AbstractPartialFunction. Thus, this implementation is intended as a drop-in replacement for the old partial function, and does not require changes to the compiler. (compiler patches, both for old and new-style pattern matching, follow) - runtime.AbstractPartialFunction is based on PartialFunction.WithDefault Original version of FunctionWithDefault by Odersky (http://article.gmane.org/gmane.comp.lang.scala.internals/4032) - better performance for OrElse#applyOrElse, OrElse#lift, PF.cond - new combinator methods: PF#run, PF#runWith, PF.apply authored by @pavelpavlov, refactored by @adriaanm, review by @paulp --- src/actors/scala/actors/Actor.scala | 8 +- src/actors/scala/actors/Future.scala | 4 +- src/actors/scala/actors/Reactor.scala | 4 +- src/compiler/scala/tools/cmd/FromString.scala | 12 +- .../nsc/interpreter/AbstractOrMissingHandler.scala | 4 +- .../tools/nsc/matching/ParallelMatching.scala | 4 +- .../scala/tools/nsc/transform/UnCurry.scala | 1 + src/library/scala/Function.scala | 7 +- src/library/scala/PartialFunction.scala | 134 ++++++++++++++++----- .../scala/runtime/AbstractPartialFunction.scala | 84 +++++++------ src/library/scala/util/control/Exception.scala | 5 +- test/files/run/lift-and-unlift.scala | 6 +- 12 files changed, 177 insertions(+), 96 deletions(-) (limited to 'test') diff --git a/src/actors/scala/actors/Actor.scala b/src/actors/scala/actors/Actor.scala index aab533ae8d..bc9bbc6ef0 100644 --- a/src/actors/scala/actors/Actor.scala +++ b/src/actors/scala/actors/Actor.scala @@ -1,3 +1,5 @@ + + /* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2005-2011, LAMP/EPFL ** @@ -246,8 +248,8 @@ object Actor extends Combinators { rawSelf.react(new RecursiveProxyHandler(rawSelf, f)) private class RecursiveProxyHandler(a: InternalReplyReactor, f: PartialFunction[Any, Unit]) - extends scala.runtime.AbstractPartialFunction[Any, Unit] { - def _isDefinedAt(m: Any): Boolean = + extends PartialFunction[Any, Unit] { + def isDefinedAt(m: Any): Boolean = true // events are immediately removed from the mailbox def apply(m: Any) { if (f.isDefinedAt(m)) f(m) @@ -404,5 +406,5 @@ trait Actor extends InternalActor with ReplyReactor { this } -} + } diff --git a/src/actors/scala/actors/Future.scala b/src/actors/scala/actors/Future.scala index eec43013d3..735c13190b 100644 --- a/src/actors/scala/actors/Future.scala +++ b/src/actors/scala/actors/Future.scala @@ -200,8 +200,8 @@ object Futures { Actor.timer.schedule(timerTask, timeout) def awaitWith(partFuns: Seq[PartialFunction[Any, Pair[Int, Any]]]) { - val reaction: PartialFunction[Any, Unit] = new scala.runtime.AbstractPartialFunction[Any, Unit] { - def _isDefinedAt(msg: Any) = msg match { + val reaction: PartialFunction[Any, Unit] = new PartialFunction[Any, Unit] { + def isDefinedAt(msg: Any) = msg match { case TIMEOUT => true case _ => partFuns exists (_ isDefinedAt msg) } diff --git a/src/actors/scala/actors/Reactor.scala b/src/actors/scala/actors/Reactor.scala index 8fc7578344..206a97d97c 100644 --- a/src/actors/scala/actors/Reactor.scala +++ b/src/actors/scala/actors/Reactor.scala @@ -38,8 +38,8 @@ private[actors] object Reactor { } } - val waitingForNone: PartialFunction[Any, Unit] = new scala.runtime.AbstractPartialFunction[Any, Unit] { - def _isDefinedAt(x: Any) = false + val waitingForNone: PartialFunction[Any, Unit] = new PartialFunction[Any, Unit] { + def isDefinedAt(x: Any) = false def apply(x: Any) {} } } diff --git a/src/compiler/scala/tools/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala index e4504702d4..3792c26c34 100644 --- a/src/compiler/scala/tools/cmd/FromString.scala +++ b/src/compiler/scala/tools/cmd/FromString.scala @@ -14,9 +14,9 @@ import scala.reflect.OptManifest * example instances are in the companion object, but in general * either IntFromString will suffice or you'll want custom transformers. */ -abstract class FromString[+T](implicit m: OptManifest[T]) extends scala.runtime.AbstractPartialFunction[String, T] { +abstract class FromString[+T](implicit m: OptManifest[T]) extends PartialFunction[String, T] { def apply(s: String): T - def _isDefinedAt(s: String): Boolean = true + def isDefinedAt(s: String): Boolean = true def zero: T = apply("") def targetString: String = m.toString @@ -30,20 +30,20 @@ object FromString { /** Path related stringifiers. */ val ExistingFile: FromString[File] = new FromString[File] { - override def _isDefinedAt(s: String) = toFile(s).isFile + override def isDefinedAt(s: String) = toFile(s).isFile def apply(s: String): File = if (isDefinedAt(s)) toFile(s) else cmd.runAndExit(println("'%s' is not an existing file." format s)) } val ExistingDir: FromString[Directory] = new FromString[Directory] { - override def _isDefinedAt(s: String) = toDir(s).isDirectory + override def isDefinedAt(s: String) = toDir(s).isDirectory def apply(s: String): Directory = if (isDefinedAt(s)) toDir(s) else cmd.runAndExit(println("'%s' is not an existing directory." format s)) } def ExistingDirRelativeTo(root: Directory) = new FromString[Directory] { private def resolve(s: String) = toDir(s) toAbsoluteWithRoot root toDirectory - override def _isDefinedAt(s: String) = resolve(s).isDirectory + override def isDefinedAt(s: String) = resolve(s).isDirectory def apply(s: String): Directory = if (isDefinedAt(s)) resolve(s) else cmd.runAndExit(println("'%s' is not an existing directory." format resolve(s))) @@ -65,7 +65,7 @@ object FromString { /** Implicit as the most likely to be useful as-is. */ implicit val IntFromString: FromString[Int] = new FromString[Int] { - override def _isDefinedAt(s: String) = safeToInt(s).isDefined + override def isDefinedAt(s: String) = safeToInt(s).isDefined def apply(s: String) = safeToInt(s).get def safeToInt(s: String): Option[Int] = try Some(java.lang.Integer.parseInt(s)) catch { case _: NumberFormatException => None } } diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala index 33ef4a432d..2f47685757 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala @@ -6,8 +6,8 @@ package scala.tools.nsc package interpreter -class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends scala.runtime.AbstractPartialFunction[Throwable, T] { - def _isDefinedAt(t: Throwable) = t match { +class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends PartialFunction[Throwable, T] { + def isDefinedAt(t: Throwable) = t match { case _: AbstractMethodError => true case _: NoSuchMethodError => true case _: MissingRequirementError => true diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index be5a9907b8..43aad9f591 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -425,7 +425,7 @@ trait ParallelMatching extends ast.TreeDSL // Should the given pattern join the expanded pivot in the success matrix? If so, // this partial function will be defined for the pattern, and the result of the apply // is the expanded sequence of new patterns. - lazy val successMatrixFn = new scala.runtime.AbstractPartialFunction[Pattern, List[Pattern]] { + lazy val successMatrixFn = new PartialFunction[Pattern, List[Pattern]] { private def seqIsDefinedAt(x: SequenceLikePattern) = (hasStar, x.hasStar) match { case (true, true) => true case (true, false) => pivotLen <= x.nonStarLength @@ -433,7 +433,7 @@ trait ParallelMatching extends ast.TreeDSL case (false, false) => pivotLen == x.nonStarLength } - def _isDefinedAt(pat: Pattern) = pat match { + def isDefinedAt(pat: Pattern) = pat match { case x: SequenceLikePattern => seqIsDefinedAt(x) case WildcardPattern() => true case _ => false diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index e54e0289bb..0d39c040f7 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -210,6 +210,7 @@ abstract class UnCurry extends InfoTransform * body = expr match { case P_i if G_i => E_i }_i=1..n * to: * + //TODO: correct code template below * class $anon() extends AbstractPartialFunction[T, R] with Serializable { * def apply(x: T): R = (expr: @unchecked) match { * case P_1 if G_1 => E_1 diff --git a/src/library/scala/Function.scala b/src/library/scala/Function.scala index 4a10b65735..9fa56a332f 100644 --- a/src/library/scala/Function.scala +++ b/src/library/scala/Function.scala @@ -29,6 +29,7 @@ object Function { /** Turns a function `A => Option[B]` into a `PartialFunction[A, B]`. * + * TODO: check if the paragraph below is still correct * '''Important note''': this transformation implies the original function * will be called 2 or more times on each logical invocation, because the * only way to supply an implementation of `isDefinedAt` is to call the @@ -39,11 +40,7 @@ object Function { * f returns `Some(_)` and undefined where `f` returns `None`. * @see [[scala.PartialFunction#lift]] */ - def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = new runtime.AbstractPartialFunction[T, R] { - def apply(x: T): R = f(x).get - def _isDefinedAt(x: T): Boolean = f(x).isDefined - override def lift: T => Option[R] = f - } + def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = PartialFunction.unlifted(f) /** Uncurrying for functions of arity 2. This transforms a unary function * returning another unary function into a function of arity 2. diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala index 3c5d6d0d23..7154b8da34 100644 --- a/src/library/scala/PartialFunction.scala +++ b/src/library/scala/PartialFunction.scala @@ -8,6 +8,7 @@ package scala + /** A partial function of type `PartialFunction[A, B]` is a unary function * where the domain does not necessarily include all values of type `A`. * The function `isDefinedAt` allows to test dynamically if a value is in @@ -43,10 +44,11 @@ package scala * }}} * * - * @author Martin Odersky + * @author Martin Odersky, Pavel Pavlov, Adriaan Moors * @version 1.0, 16/07/2003 */ -trait PartialFunction[-A, +B] extends (A => B) { +trait PartialFunction[-A, +B] extends (A => B) { self => + import PartialFunction._ /** Checks if a value is contained in the function's domain. * @@ -55,10 +57,6 @@ trait PartialFunction[-A, +B] extends (A => B) { */ def isDefinedAt(x: A): Boolean - //protected def missingCase[A1 <: A, B1 >: B]: PartialFunction[A1, B1] = PartialFunction.empty - - protected def missingCase(x: A): B = throw new MatchError(x) - /** Composes this partial function with a fallback partial function which * gets applied where this partial function is not defined. * @@ -70,16 +68,8 @@ trait PartialFunction[-A, +B] extends (A => B) { * takes `x` to `this(x)` where `this` is defined, and to `that(x)` where it is not. */ def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = - new runtime.AbstractPartialFunction[A1, B1] { - def _isDefinedAt(x: A1): Boolean = - PartialFunction.this.isDefinedAt(x) || that.isDefinedAt(x) - def apply(x: A1): B1 = - if (PartialFunction.this.isDefinedAt(x)) PartialFunction.this.apply(x) - else that.apply(x) - } - - def orElseFast[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = - orElse(that) + new OrElse[A1, B1] (this, that) + //TODO: why not overload it with orElse(that: F1): F1? /** Composes this partial function with a transformation function that * gets applied to results of this partial function. @@ -88,9 +78,9 @@ trait PartialFunction[-A, +B] extends (A => B) { * @return a partial function with the same domain as this partial function, which maps * arguments `x` to `k(this(x))`. */ - override def andThen[C](k: B => C) : PartialFunction[A, C] = new runtime.AbstractPartialFunction[A, C] { - def _isDefinedAt(x: A): Boolean = PartialFunction.this.isDefinedAt(x) - def apply(x: A): C = k(PartialFunction.this.apply(x)) + override def andThen[C](k: B => C) : PartialFunction[A, C] = new PartialFunction[A, C] { + def isDefinedAt(x: A): Boolean = self isDefinedAt x + def apply(x: A): C = k(self(x)) } /** Turns this partial function into an plain function returning an `Option` result. @@ -98,9 +88,30 @@ trait PartialFunction[-A, +B] extends (A => B) { * @return a function that takes an argument `x` to `Some(this(x))` if `this` * is defined for `x`, and to `None` otherwise. */ - def lift: A => Option[B] = new (A => Option[B]) { - def apply(x: A): Option[B] = if (isDefinedAt(x)) Some(PartialFunction.this.apply(x)) else None - } + def lift: A => Option[B] = new Lifted(this) + + /** + * TODO: comment + * @since 2.10 + */ + def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + if (isDefinedAt(x)) apply(x) else default(x) + + /** + * TODO: comment + * @since 2.10 + */ + def run[U](x: A)(action: B => U): Boolean = + applyOrElse(x, fallbackToken) match { + case FallbackToken => false + case z => action(z); true + } + + /** + * TODO: comment + * @since 2.10 + */ + def runWith[U](action: B => U): A => Boolean = { x => run(x)(action) } } /** A few handy operations which leverage the extra bit of information @@ -119,14 +130,73 @@ trait PartialFunction[-A, +B] extends (A => B) { * @since 2.8 */ object PartialFunction { - private[this] final val empty_pf: PartialFunction[Any, Nothing] = new runtime.AbstractPartialFunction[Any, Nothing] { - def _isDefinedAt(x: Any) = false - override def isDefinedAt(x: Any) = false - def apply(x: Any): Nothing = throw new MatchError(x) - override def orElse[A1, B1](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = that - override def orElseFast[A1, B1](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = that - override def lift = (x: Any) => None + /** Composite function produced by `PartialFunction#orElse` method + */ + private final class OrElse[-A, +B] (f1: PartialFunction[A, B], f2: PartialFunction[A, B]) extends PartialFunction[A, B] { + def isDefinedAt(x: A) = f1.isDefinedAt(x) || f2.isDefinedAt(x) + + def apply(x: A): B = f1.applyOrElse(x, f2) + + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + f1.applyOrElse(x, fallbackToken) match { + case FallbackToken => f2.applyOrElse(x, default) + case z => z + } + + override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) = + new OrElse[A1, B1] (f1, f2 orElse that) + + override def andThen[C](k: B => C) = + new OrElse[A, C] (f1 andThen k, f2 andThen k) } + + private[scala] lazy val FallbackToken: PartialFunction[Any, PartialFunction[Any, Nothing]] = { case _ => FallbackToken.asInstanceOf[PartialFunction[Any, Nothing]] } + private[scala] final def fallbackToken[B] = FallbackToken.asInstanceOf[PartialFunction[Any, B]] + //TODO: check generated code for PF literal here + + private[scala] final class Lifted[-A, +B] (val pf: PartialFunction[A, B]) + extends runtime.AbstractFunction1[A, Option[B]] { + + def apply(x: A): Option[B] = pf.applyOrElse(x, fallbackToken) match { + case FallbackToken => None + case z => Some(z) + } + } + + private final class Unlifted[A, B] (f: A => Option[B]) extends runtime.AbstractPartialFunction[A, B] { + def isDefinedAt(x: A): Boolean = f(x).isDefined + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + f(x) getOrElse default(x) //TODO: check generated code and inline getOrElse if needed + override def lift = f + } + + private[scala] def unlifted[A, B](f: A => Option[B]): PartialFunction[A, B] = f match { + case lf: Lifted[A, B] => lf.pf + case ff => new Unlifted(ff) + } + + /** Converts ordinary function to partial one + * @since 2.10 + */ + //TODO: check generated code for PF literal here + def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) } + + private[this] final val constFalse: Any => Boolean = { _ => false} + + private[this] final val empty_pf: PartialFunction[Any, Nothing] = new PartialFunction[Any, Nothing] { + def isDefinedAt(x: Any) = false + def apply(x: Any) = throw new MatchError(x) + override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that + override def andThen[C](k: Nothing => C) = this + override val lift = (x: Any) => None + override def run[U](x: Any)(action: Nothing => U) = false + override def runWith[U](action: Nothing => U) = constFalse + } + + /** + * TODO: comment + * @since 2.10 + */ def empty[A, B] : PartialFunction[A, B] = empty_pf /** Creates a Boolean test based on a value and a partial function. @@ -137,8 +207,7 @@ object PartialFunction { * @param pf the partial function * @return true, iff `x` is in the domain of `pf` and `pf(x) == true`. */ - def cond[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean = - (pf isDefinedAt x) && pf(x) + def cond[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean = pf.applyOrElse(x, constFalse) /** Transforms a PartialFunction[T, U] `pf` into Function1[T, Option[U]] `f` * whose result is `Some(x)` if the argument is in `pf`'s domain and `None` @@ -150,6 +219,5 @@ object PartialFunction { * @param pf the PartialFunction[T, U] * @return `Some(pf(x))` if `pf isDefinedAt x`, `None` otherwise. */ - def condOpt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] = - if (pf isDefinedAt x) Some(pf(x)) else None + def condOpt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] = pf.lift(x) } diff --git a/src/library/scala/runtime/AbstractPartialFunction.scala b/src/library/scala/runtime/AbstractPartialFunction.scala index cbe778f09b..2e435d8a7e 100644 --- a/src/library/scala/runtime/AbstractPartialFunction.scala +++ b/src/library/scala/runtime/AbstractPartialFunction.scala @@ -8,45 +8,61 @@ package scala.runtime -import scala.annotation.unchecked.uncheckedVariance - -/** This class provides a default implementation of partial functions - * that is used for all partial function literals. - * It contains an optimized `orElse` method which supports - * chained `orElse` in linear time, and with no slow-down - * if the `orElse` part is not needed. - * The implementation of `orElse` works by cloning the abstract function object - * and modifying a private `fallBack` variable that encodes the `getorElse` part. +/** `AbstractPartialFunction` reformulates all operations of its supertrait `PartialFunction` in terms of `isDefinedAt` and `applyOrElse`. + * + * This allows more efficient implementations in many cases: + * - optimized `orElse` method supports chained `orElse` in linear time, + * and with no slow-down if the `orElse` part is not needed. + * - optimized `lift` method helps to avoid double evaluation of pattern matchers & guards + * of partial function literals. + * + * This trait is used as a basis for implementation of all partial function literals + * with non-exhaustive matchers. + * + * Use of `AbstractPartialFunction` instead of `PartialFunction` as a base trait for + * user-defined partial functions may result in better performance + * and more predictable behavior w.r.t. side effects. + * + * @author Pavel Pavlov + * @since 2.10 */ -abstract class AbstractPartialFunction[-T1, +R] - extends AbstractFunction1[T1, R] - with PartialFunction[T1, R] - with Cloneable { - - private var fallBackField: PartialFunction[T1 @uncheckedVariance, R @uncheckedVariance] = _ +abstract class AbstractPartialFunction[@specialized(scala.Int, scala.Long, scala.Float, scala.Double, scala.AnyRef) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double, scala.AnyRef) +R] extends Function1[T1, R] with PartialFunction[T1, R] { self => + // this method must be overridden for better performance, + // for backwards compatibility, fall back to the one inherited from PartialFunction + // this assumes the old-school partial functions override the apply method, though + // override def applyOrElse[A1 <: T1, B1 >: R](x: A1, default: A1 => B1): B1 = ??? - def fallBack: PartialFunction[T1, R] = synchronized { - if (fallBackField eq null) fallBackField = PartialFunction.empty - fallBackField - } + // probably okay to make final since classes compiled before have overridden against the old version of AbstractPartialFunction + // let's not make it final so as not to confuse anyone + /*final*/ def apply(x: T1): R = applyOrElse(x, PartialFunction.empty) - override protected def missingCase(x: T1): R = fallBack(x) - - // Question: Need to ensure that fallBack is overwritten before any access - // Is the `synchronized` here the right thing to achieve this? - // Is there a cheaper way? - override def orElse[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = { - val result = this.clone.asInstanceOf[AbstractPartialFunction[A1, B1]] - result.synchronized { - result.fallBackField = if (this.fallBackField eq null) that else this.fallBackField orElse that - result + override final def andThen[C](k: R => C) : PartialFunction[T1, C] = + new AbstractPartialFunction[T1, C] { + def isDefinedAt(x: T1): Boolean = self.isDefinedAt(x) + override def applyOrElse[A1 <: T1, C1 >: C](x: A1, default: A1 => C1): C1 = + self.applyOrElse(x, PartialFunction.fallbackToken) match { + case PartialFunction.FallbackToken => default(x) + case z => k(z) + } } - } - - def isDefinedAt(x: T1): scala.Boolean = _isDefinedAt(x) || fallBack.isDefinedAt(x) - def _isDefinedAt(x: T1): scala.Boolean + // TODO: remove + protected def missingCase(x: T1): R = throw new MatchError(x) } - +/** `AbstractTotalFunction` is a partial function whose `isDefinedAt` method always returns `true`. + * + * This class is used as base class for partial function literals with + * certainly exhaustive pattern matchers. + * + * @author Pavel Pavlov + * @since 2.10 + */ +abstract class AbstractTotalFunction[@specialized(scala.Int, scala.Long, scala.Float, scala.Double, scala.AnyRef) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double, scala.AnyRef) +R] extends Function1[T1, R] with PartialFunction[T1, R] { + final def isDefinedAt(x: T1): Boolean = true + override final def applyOrElse[A1 <: T1, B1 >: R](x: A1, default: A1 => B1): B1 = apply(x) + override final def orElse[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = this + //TODO: check generated code for PF literal here + override final def andThen[C](k: R => C): PartialFunction[T1, C] = { case x => k(apply(x)) } +} diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala index 5e3f8b6451..20a179a884 100644 --- a/src/library/scala/util/control/Exception.scala +++ b/src/library/scala/util/control/Exception.scala @@ -230,8 +230,5 @@ object Exception { classes exists (_ isAssignableFrom x.getClass) private def pfFromExceptions(exceptions: Class[_]*): PartialFunction[Throwable, Nothing] = - new scala.runtime.AbstractPartialFunction[Throwable, Nothing] { - def apply(x: Throwable) = throw x - def _isDefinedAt(x: Throwable) = wouldMatch(x, exceptions) - } + { case x if wouldMatch(x, exceptions) => throw x } } diff --git a/test/files/run/lift-and-unlift.scala b/test/files/run/lift-and-unlift.scala index b944c70155..a4a5d9502e 100644 --- a/test/files/run/lift-and-unlift.scala +++ b/test/files/run/lift-and-unlift.scala @@ -2,7 +2,7 @@ import Function.unlift object Test { def evens1(x: Int) = if (x % 2 == 0) Some(x) else None - def evens2: PartialFunction[Int, Int] = { + val evens2: PartialFunction[Int, Int] = { case x if x % 2 == 0 => x } @@ -21,7 +21,7 @@ object Test { }) assert(f1 eq f3.lift) - // Hmm, why is this not true: - // assert(f2 eq f4.lift) + assert(f4 eq unlift(f2)) + assert(f4 eq evens2) } } -- cgit v1.2.3 From 75e584bd0c056a39ea6ef52848e8c2cbe764cb3a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Mar 2012 11:42:40 -0700 Subject: Fix for regression with inference at arity 21+. A classic "off by two" error. Closes SI-4545, SI-5633. --- src/compiler/scala/reflect/internal/Definitions.scala | 4 ++-- test/files/pos/t4545.scala | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/files/pos/t4545.scala (limited to 'test') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index a2dd6fc4c3..09c2228b01 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -601,8 +601,8 @@ trait Definitions extends reflect.api.StandardDefinitions { def isFunctionType(tp: Type): Boolean = tp.normalize match { case TypeRef(_, sym, args) if args.nonEmpty => - val len = args.length - len < MaxFunctionArity && sym == FunctionClass(len - 1) + val arity = args.length - 1 // -1 is the return type + arity <= MaxFunctionArity && sym == FunctionClass(arity) case _ => false } diff --git a/test/files/pos/t4545.scala b/test/files/pos/t4545.scala new file mode 100644 index 0000000000..8c7a3236c4 --- /dev/null +++ b/test/files/pos/t4545.scala @@ -0,0 +1,14 @@ +object Test { + def f[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T](table: Tuple20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T])(fun: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) => Unit) { + } + def g[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U](table: Tuple21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U])(fun: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) => Unit) { + } + + def g20 = f( + ( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) + ) { case ((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)) => () } + + def g21 = g( + (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) + ) { case ((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)) => () } +} -- cgit v1.2.3 From f7535f72903f083b2444fb1d0b73363efa5482e9 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 31 Mar 2012 12:52:54 -0700 Subject: Pushed Symbol/Type creation partitioning further. Yet more funnelling of immutable creation-time known information into the identities of symbols and types. --- .../scala/reflect/internal/Definitions.scala | 66 ++++++++++---- src/compiler/scala/reflect/internal/Symbols.scala | 32 ++++--- src/compiler/scala/reflect/internal/Types.scala | 101 +++++++++++++-------- .../scala/tools/nsc/symtab/BrowsingLoaders.scala | 2 +- .../scala/tools/nsc/symtab/SymbolLoaders.scala | 8 +- .../nsc/symtab/classfile/ClassfileParser.scala | 5 +- .../tools/nsc/typechecker/ContextErrors.scala | 4 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 1 + .../scala/tools/nsc/typechecker/Typers.scala | 9 +- test/files/neg/override-object-flag.check | 2 +- test/files/neg/override-object-no.check | 2 +- test/files/neg/t961.check | 2 +- test/files/presentation/callcc-interpreter.check | 2 +- 13 files changed, 148 insertions(+), 88 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 0188fb3944..8ea3cd511a 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -138,26 +138,47 @@ trait Definitions extends reflect.api.StandardDefinitions { // symbols related to packages var emptypackagescope: Scope = null //debug - // This is the package _root_. The actual root cannot be referenced at - // the source level, but _root_ is essentially a function () => . - lazy val RootPackage: Symbol = { - val rp = ( - NoSymbol.newValue(nme.ROOTPKG, NoPosition, FINAL | MODULE | PACKAGE | JAVA) - setInfo NullaryMethodType(RootClass.tpe) - ) - RootClass.sourceModule = rp - rp + sealed trait WellKnownSymbol extends Symbol { + this initFlags TopLevelCreationFlags + } + // Features common to RootClass and RootPackage, the roots of all + // type and term symbols respectively. + sealed trait RootSymbol extends WellKnownSymbol { + final override def isRootSymbol = true } + // This is the package _root_. The actual root cannot be referenced at + // the source level, but _root_ is essentially a function => . + final object RootPackage extends ModuleSymbol(NoSymbol, NoPosition, nme.ROOTPKG) with RootSymbol { + this setInfo NullaryMethodType(RootClass.tpe) + RootClass.sourceModule = this - // This is the actual root of everything, including the package _root_. - lazy val RootClass: ModuleClassSymbol = ( - NoSymbol.newModuleClassSymbol(tpnme.ROOT, NoPosition, FINAL | MODULE | PACKAGE | JAVA) - setInfo rootLoader - ) + override def isRootPackage = true + } + // This is , the actual root of everything except the package _root_. + // and _root_ (RootPackage and RootClass) should be the only "well known" + // symbols owned by NoSymbol. All owner chains should go through RootClass, + // although it is probable that some symbols are created as direct children + // of NoSymbol to ensure they will not be stumbled upon. (We should designate + // a better encapsulated place for that.) + final object RootClass extends ModuleClassSymbol(NoSymbol, NoPosition, tpnme.ROOT) with RootSymbol { + this setInfo rootLoader + + override def isRoot = true + override def isEffectiveRoot = true + override def isStatic = true + override def isNestedClass = false + override def ownerOfNewSymbols = EmptyPackageClass + } // The empty package, which holds all top level types without given packages. - lazy val EmptyPackage = RootClass.newPackage(nme.EMPTY_PACKAGE_NAME, NoPosition, FINAL) - lazy val EmptyPackageClass = EmptyPackage.moduleClass - + final object EmptyPackage extends ModuleSymbol(RootClass, NoPosition, nme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { + override def isEmptyPackage = true + } + final object EmptyPackageClass extends ModuleClassSymbol(RootClass, NoPosition, tpnme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { + override def isEffectiveRoot = true + override def isEmptyPackageClass = true + } + // It becomes tricky to create dedicated objects for other symbols because + // of initialization order issues. lazy val JavaLangPackage = getModule(sn.JavaLang) lazy val JavaLangPackageClass = JavaLangPackage.moduleClass lazy val ScalaPackage = getModule(nme.scala_) @@ -542,6 +563,12 @@ trait Definitions extends reflect.api.StandardDefinitions { // Checks whether the given type is true for the given condition, // or if it is a specialized subtype of a type for which it is true. + // + // Origins notes: + // An issue was introduced with specialization in that the implementation + // of "isTupleType" in Definitions relied upon sym == TupleClass(elems.length). + // This test is untrue for specialized tuples, causing mysterious behavior + // because only some tuples are specialized. def isPossiblySpecializedType(tp: Type)(cond: Type => Boolean) = { cond(tp) || (tp match { case TypeRef(pre, sym, args) if sym hasFlag SPECIALIZED => @@ -1112,9 +1139,14 @@ trait Definitions extends reflect.api.StandardDefinitions { def init() { if (isInitialized) return + // Still fiddling with whether it's cleaner to do some of this setup here + // or from constructors. The latter approach tends to invite init order issues. EmptyPackageClass setInfo ClassInfoType(Nil, newPackageScope(EmptyPackageClass), EmptyPackageClass) EmptyPackage setInfo EmptyPackageClass.tpe + connectModuleToClass(EmptyPackage, EmptyPackageClass) + connectModuleToClass(RootPackage, RootClass) + RootClass.info.decls enter EmptyPackage RootClass.info.decls enter RootPackage diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 2019b92836..4473d63f5f 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -451,12 +451,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isVarargsMethod = isMethod && hasFlag(VARARGS) /** Package tests */ - final def isEmptyPackage = isPackage && name == nme.EMPTY_PACKAGE_NAME - final def isEmptyPackageClass = isPackageClass && name == tpnme.EMPTY_PACKAGE_NAME final def isPackage = isModule && hasFlag(PACKAGE) final def isPackageClass = isClass && hasFlag(PACKAGE) - final def isRoot = isPackageClass && owner == NoSymbol - final def isRootPackage = isPackage && owner == NoSymbol + + /** Overridden in custom objects in Definitions */ + def isRoot = false + def isRootPackage = false + def isRootSymbol = false // RootPackage and RootClass. TODO: also NoSymbol. + def isEmptyPackage = false + def isEmptyPackageClass = false + + /** Is this symbol an effective root for fullname string? + */ + def isEffectiveRoot = false + + /** For RootClass, EmptyPackageClass. For all other symbols, itself. + */ + def ownerOfNewSymbols = this /** Does this symbol denote a wrapper created by the repl? */ final def isInterpreterWrapper = ( @@ -464,9 +475,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => && owner.isPackageClass && nme.isReplWrapperName(name) ) - /** Is this symbol an effective root for fullname string? - */ - def isEffectiveRoot = isRoot || isEmptyPackageClass /** Term symbols with the exception of static parts of Java classes and packages. */ @@ -652,8 +660,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isModuleVar = hasFlag(MODULEVAR) /** Is this symbol static (i.e. with no outer instance)? */ - final def isStatic: Boolean = - hasFlag(STATIC) || isRoot || owner.isStaticOwner + def isStatic = (this hasFlag STATIC) || owner.isStaticOwner /** Is this symbol a static constructor? */ final def isStaticConstructor: Boolean = @@ -685,8 +692,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isConstant: Boolean = isStable && isConstantType(tpe.resultType) /** Is this class nested in another class or module (not a package)? */ - final def isNestedClass: Boolean = - isClass && !isRoot && !owner.isPackageClass + def isNestedClass = isClass && !owner.isPackageClass /** Is this class locally defined? * A class is local, if @@ -2045,7 +2051,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def fullNameString: String = { def recur(sym: Symbol): String = { - if (sym.isRoot || sym.isRootPackage || sym == NoSymbol) sym.nameString + if (sym.isRootSymbol || sym == NoSymbol) sym.nameString else if (sym.owner.isEffectiveRoot) sym.nameString else recur(sym.effectiveOwner.enclClass) + "." + sym.nameString } @@ -2095,7 +2101,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => case rt => " <: " + rt } ) - else if (isModule) moduleClass.infoString(tp) + else if (isModule) "" // avoid "object X of type X.type" else tp match { case PolyType(tparams, res) => typeParamsString(tp) + infoString(res) case NullaryMethodType(res) => infoString(res) diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index e062e875cd..5afa5343ed 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -904,6 +904,12 @@ trait Types extends api.Types { self: SymbolTable => else str } + /** The string representation of this type when the direct object in a sentence. + * Normally this is no different from the regular representation, but modules + * read better as "object Foo" here and "Foo.type" the rest of the time. + */ + def directObjectString = safeToString + /** A test whether a type contains any unification type variables. */ def isGround: Boolean = this match { case TypeVar(_, constr) => @@ -1224,8 +1230,7 @@ trait Types extends api.Types { self: SymbolTable => else if (sym.isModuleClass) sym.fullNameString + "." else sym.nameString + ".this." override def safeToString: String = - if (sym.isRoot) "" - else if (sym.isEmptyPackageClass) "" + if (sym.isEffectiveRoot) "" + sym.name else super.safeToString override def narrow: Type = this override def kind = "ThisType" @@ -1851,6 +1856,35 @@ trait Types extends api.Types { self: SymbolTable => // advantage to call TypeRef directly. override def typeConstructor = TypeRef(pre, sym, Nil) } + + class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { + require(sym.isModuleClass, sym) + private[this] var narrowedCache: Type = _ + override def isStable = true + override def narrow = { + if (narrowedCache eq null) + narrowedCache = singleType(pre, sym.sourceModule) + + narrowedCache + } + final override def isNotNull = true + override protected def finishPrefix(rest: String) = objectPrefix + rest + override def directObjectString = super.safeToString + override def toLongString = toString + override def safeToString = narrow.toString + } + class PackageTypeRef(pre0: Type, sym0: Symbol) extends ModuleTypeRef(pre0, sym0) { + require(sym.isPackageClass, sym) + override protected def finishPrefix(rest: String) = packagePrefix + rest + } + class RefinementTypeRef(sym0: Symbol) extends NoArgsTypeRef(NoType, sym0) with ClassTypeRef { + require(sym.isRefinementClass, sym) + + // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers + override protected def normalizeImpl: Type = sym.info.normalize + override protected def finishPrefix(rest: String) = "" + thisInfo + } + class NoArgsTypeRef(pre0: Type, sym0: Symbol) extends TypeRef(pre0, sym0, Nil) with UniqueType { // A reference (in a Scala program) to a type that has type parameters, but where the reference // does not include type arguments. Note that it doesn't matter whether the symbol refers @@ -1898,10 +1932,6 @@ trait Types extends api.Types { self: SymbolTable => // !!! There are scaladoc-created symbols arriving which violate this require. // require(sym.isClass, sym) - override protected def normalizeImpl: Type = - if (sym.isRefinementClass) sym.info.normalize // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers - else super.normalizeImpl - override def baseType(clazz: Symbol): Type = if (sym == clazz) this else transform(sym.info.baseType(clazz)) @@ -2147,12 +2177,15 @@ trait Types extends api.Types { self: SymbolTable => } } - private def preString = ( - // ensure that symbol is not a local copy with a name coincidence - if (!settings.debug.value && shorthands(sym.fullName) && sym.ownerChain.forall(_.isClass)) "" - else pre.prefixString + // ensure that symbol is not a local copy with a name coincidence + private def needsPreString = ( + settings.debug.value + || !shorthands(sym.fullName) + || sym.ownerChain.exists(s => !s.isClass) ) + private def preString = if (needsPreString) pre.prefixString else "" private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]") + def refinementString = ( if (sym.isStructuralRefinement) ( decls filter (sym => sym.isPossibleInRefinement && sym.isPublic) @@ -2162,12 +2195,9 @@ trait Types extends api.Types { self: SymbolTable => else "" ) - private def finishPrefix(rest: String) = ( - if (sym.isPackageClass) packagePrefix + rest - else if (sym.isModuleClass) objectPrefix + rest - else if (!sym.isInitialized) rest - else if (sym.isAnonymousClass && !phase.erasedTypes) parentsString(thisInfo.parents) + refinementString - else if (sym.isRefinementClass) "" + thisInfo + protected def finishPrefix(rest: String) = ( + if (sym.isInitialized && sym.isAnonymousClass && !phase.erasedTypes) + parentsString(thisInfo.parents) + refinementString else rest ) private def customToString = this match { @@ -2227,6 +2257,9 @@ trait Types extends api.Types { self: SymbolTable => else { if (sym.isAliasType) new NoArgsTypeRef(pre, sym) with AliasTypeRef else if (sym.isAbstractType) new NoArgsTypeRef(pre, sym) with AbstractTypeRef + else if (sym.isRefinementClass) new RefinementTypeRef(sym) + else if (sym.isPackageClass) new PackageTypeRef(pre, sym) + else if (sym.isModuleClass) new ModuleTypeRef(pre, sym) else new NoArgsTypeRef(pre, sym) with ClassTypeRef } }) @@ -4603,31 +4636,22 @@ trait Types extends api.Types { self: SymbolTable => object adaptToNewRunMap extends TypeMap { private def adaptToNewRun(pre: Type, sym: Symbol): Symbol = { - if (phase.flatClasses) { + if (phase.flatClasses || sym.isRootSymbol || (pre eq NoPrefix) || (pre eq NoType) || sym.isPackageClass) sym - } else if (sym == definitions.RootClass) { - definitions.RootClass - } else if (sym == definitions.RootPackage) { - definitions.RootPackage - } else if (sym.isModuleClass) { + else if (sym.isModuleClass) { val sourceModule1 = adaptToNewRun(pre, sym.sourceModule) - var result = sourceModule1.moduleClass - if (result == NoSymbol) result = sourceModule1.initialize.moduleClass - if (result != NoSymbol) result - else { + + sourceModule1.moduleClass orElse sourceModule1.initialize.moduleClass orElse { val msg = "Cannot adapt module class; sym = %s, sourceModule = %s, sourceModule.moduleClass = %s => sourceModule1 = %s, sourceModule1.moduleClass = %s" debuglog(msg.format(sym, sym.sourceModule, sym.sourceModule.moduleClass, sourceModule1, sourceModule1.moduleClass)) sym } - } else if ((pre eq NoPrefix) || (pre eq NoType) || sym.isPackageClass) { - sym - } else { - var rebind0 = pre.findMember(sym.name, BRIDGE, 0, true) - if (rebind0 == NoSymbol) { + } + else { + var rebind0 = pre.findMember(sym.name, BRIDGE, 0, true) orElse { if (sym.isAliasType) throw missingAliasException debugwarn(pre+"."+sym+" does no longer exist, phase = "+phase) throw new MissingTypeControl // For build manager and presentation compiler purposes - //assert(false, pre+"."+sym+" does no longer exist, phase = "+phase) } /** The two symbols have the same fully qualified name */ def corresponds(sym1: Symbol, sym2: Symbol): Boolean = @@ -4646,12 +4670,10 @@ trait Types extends api.Types { self: SymbolTable => ", rebind = " + rebind0.fullLocationString ) } - val rebind = rebind0.suchThat(sym => sym.isType || sym.isStable) - if (rebind == NoSymbol) { + rebind0.suchThat(sym => sym.isType || sym.isStable) orElse { debuglog("" + phase + " " +phase.flatClasses+sym.owner+sym.name+" "+sym.isType) throw new MalformedType(pre, sym.nameString) } - rebind } } def apply(tp: Type): Type = tp match { @@ -5472,9 +5494,14 @@ trait Types extends api.Types { self: SymbolTable => case _: ClassSymbol => if (isRaw(sym1, tr1.args)) isSubType(rawToExistential(tp1), tp2, depth) - else - sym1.name == tpnme.REFINE_CLASS_NAME && + else if (sym1.isModuleClass) tp2 match { + case SingleType(_, sym2) => sym1 == sym2 + case _ => false + } + else if (sym1.isRefinementClass) isSubType(sym1.info, tp2, depth) + else false + case _: TypeSymbol => if (sym1 hasFlag DEFERRED) { val tp1a = tp1.bounds.hi diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index 59342a36ef..5f7deb87bd 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -105,7 +105,7 @@ abstract class BrowsingLoaders extends SymbolLoaders { */ override def enterToplevelsFromSource(root: Symbol, name: String, src: AbstractFile) { try { - if (root == definitions.RootClass || root == definitions.EmptyPackageClass) + if (root.isEffectiveRoot) // RootClass or EmptyPackageClass super.enterToplevelsFromSource(root, name, src) else browseTopLevel(root, src) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index f9ff147e82..7eb04eaf40 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -30,15 +30,11 @@ abstract class SymbolLoaders { member } - private def realOwner(root: Symbol): Symbol = { - if (root.isRoot) definitions.EmptyPackageClass else root - } - /** Enter class with given `name` into scope of `root` * and give them `completer` as type. */ def enterClass(root: Symbol, name: String, completer: SymbolLoader): Symbol = { - val owner = realOwner(root) + val owner = root.ownerOfNewSymbols val clazz = owner.newClass(newTypeName(name)) clazz setInfo completer enterIfNew(owner, clazz, completer) @@ -48,7 +44,7 @@ abstract class SymbolLoaders { * and give them `completer` as type. */ def enterModule(root: Symbol, name: String, completer: SymbolLoader): Symbol = { - val owner = realOwner(root) + val owner = root.ownerOfNewSymbols val module = owner.newModule(newTermName(name)) module setInfo completer module.moduleClass setInfo moduleClassLoader diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 61668b1a8a..9dee441527 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -433,10 +433,7 @@ abstract class ClassfileParser { sym.info.decl(part.encode) }//.suchThat(module == _.isModule) - sym = ( - if (sym1 ne NoSymbol) sym1 - else sym.info.decl(part.encode.toTypeName) - ) + sym = sym1 orElse sym.info.decl(part.encode.toTypeName) } } sym diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 764823d786..49ddb985dc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -317,7 +317,7 @@ trait ContextErrors { } withAddendum(qual.pos)( if (name == nme.CONSTRUCTOR) target + " does not have a constructor" - else nameString + " is not a member of " + targetKindString + target + addendum + else nameString + " is not a member of " + targetKindString + target.directObjectString + addendum ) } issueNormalTypeError(sel, errMsg) @@ -677,7 +677,7 @@ trait ContextErrors { def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String) = { def errMsg = { - val location = if (sym.isClassConstructor) owner0 else pre.widen + val location = if (sym.isClassConstructor) owner0 else pre.widen.directObjectString underlyingSymbol(sym).fullLocationString + " cannot be accessed in " + location + explanation diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 9177aca656..045614e773 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -267,6 +267,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R sym1.locationString + (if (sym1.isAliasType) ", which equals "+self.memberInfo(sym1) else if (sym1.isAbstractType) " with bounds"+self.memberInfo(sym1) + else if (sym1.isModule) "" else if (sym1.isTerm) " of type "+self.memberInfo(sym1) else "") else "") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ae184d2677..2aff00f6a5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -253,22 +253,23 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { /** Check that `tpt` refers to a class type with a stable prefix. */ def checkStablePrefixClassType(tpt: Tree): Boolean = { val tpe = unwrapToStableClass(tpt.tpe) - def prefixIsStable = { def checkPre = tpe match { case TypeRef(pre, _, _) => pre.isStable || errorNotStable(tpt, pre) - case _ => true + case _ => false } // A type projection like X#Y can get by the stable check if the // prefix is singleton-bounded, so peek at the tree too. def checkTree = tpt match { - case SelectFromTypeTree(qual, _) => isSingleType(qual.tpe) || errorNotStable(tpt, qual.tpe) + case SelectFromTypeTree(qual, _) => isSingleType(qual.tpe) || errorNotClass(tpt, tpe) case _ => true } checkPre && checkTree } - isNonRefinementClassType(tpe) && (isPastTyper || prefixIsStable) + ( (isNonRefinementClassType(tpe) || errorNotClass(tpt, tpe)) + && (isPastTyper || prefixIsStable) + ) } /** Check that type tp is not a subtype of itself. diff --git a/test/files/neg/override-object-flag.check b/test/files/neg/override-object-flag.check index 152d31ff8a..344165138d 100644 --- a/test/files/neg/override-object-flag.check +++ b/test/files/neg/override-object-flag.check @@ -1,4 +1,4 @@ -override-object-flag.scala:3: error: overriding object Foo in trait A of type object B.this.Foo; +override-object-flag.scala:3: error: overriding object Foo in trait A; object Foo cannot override final member trait B extends A { override object Foo } ^ diff --git a/test/files/neg/override-object-no.check b/test/files/neg/override-object-no.check index f9fb37381b..52bad2b937 100644 --- a/test/files/neg/override-object-no.check +++ b/test/files/neg/override-object-no.check @@ -10,7 +10,7 @@ an overriding object must conform to the overridden object's class bound; required: Object{def g: Int} trait Quux2 extends Quux1 { override object Bar { def g = "abc" } } // err ^ -override-object-no.scala:25: error: overriding object Bar in trait Quux3 of type object Quux4.this.Bar; +override-object-no.scala:25: error: overriding object Bar in trait Quux3; object Bar cannot override final member trait Quux4 extends Quux3 { override object Bar } // err ^ diff --git a/test/files/neg/t961.check b/test/files/neg/t961.check index 48273f764d..14d39b0f42 100644 --- a/test/files/neg/t961.check +++ b/test/files/neg/t961.check @@ -1,4 +1,4 @@ -t961.scala:11: error: object Temp.B does not take parameters +t961.scala:11: error: Temp.B.type does not take parameters B() match { ^ one error found diff --git a/test/files/presentation/callcc-interpreter.check b/test/files/presentation/callcc-interpreter.check index 3385ef12b7..c50e171b4e 100644 --- a/test/files/presentation/callcc-interpreter.check +++ b/test/files/presentation/callcc-interpreter.check @@ -52,7 +52,7 @@ retrieved 64 members `method wait(x$1: Long, x$2: Int)Unit` `method x=> callccInterpreter.type` `method →[B](y: B)(callccInterpreter.type, B)` -`object Wrongobject callccInterpreter.Wrong` +`object WrongcallccInterpreter.Wrong.type` `trait TermcallccInterpreter.Term` `trait ValuecallccInterpreter.Value` `type AnswercallccInterpreter.Answer` -- cgit v1.2.3 From 883ca63361a039f27e412eb0cb1bee7e9742a0bb Mon Sep 17 00:00:00 2001 From: Dmitry Nadezhin Date: Mon, 2 Apr 2012 10:29:25 +0400 Subject: SI-5627 BigInt.equals(Number) and BigDecimal.equals(Number) should implement equality in mathematical sense --- src/library/scala/math/BigDecimal.scala | 15 ++++++- src/library/scala/math/BigInt.scala | 38 ++++++++++++++++ test/files/run/is-valid-num.scala | 77 +++++++++++++++++++++++++++++++++ test/files/run/numbereq.scala | 35 ++++++++++++++- 4 files changed, 162 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala index c1f45eccfb..cb42b76b51 100644 --- a/src/library/scala/math/BigDecimal.scala +++ b/src/library/scala/math/BigDecimal.scala @@ -183,7 +183,8 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def equals (that: Any): Boolean = that match { case that: BigDecimal => this equals that case that: BigInt => this.toBigIntExact exists (that equals _) - case _: Float | _: Double => unifiedPrimitiveEquals(that) + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that case _ => isValidLong && unifiedPrimitiveEquals(that) } override def isValidByte = noArithmeticException(toByteExact) @@ -191,6 +192,18 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def isValidChar = isValidInt && toIntExact >= Char.MinValue && toIntExact <= Char.MaxValue override def isValidInt = noArithmeticException(toIntExact) def isValidLong = noArithmeticException(toLongExact) + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat = { + val f = toFloat + !f.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(f)) == 0 + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble = { + val d = toDouble + !d.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(d)) == 0 + } private def noArithmeticException(body: => Unit): Boolean = { try { body ; true } diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index 8a53afaa62..dbec30b2fe 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -20,6 +20,7 @@ object BigInt { private val minCached = -1024 private val maxCached = 1024 private val cache = new Array[BigInt](maxCached - minCached + 1) + private val minusOne = BigInteger.valueOf(-1) @deprecated("Use Long.MinValue", "2.9.0") val MinLong = BigInt(Long.MinValue) @@ -122,6 +123,8 @@ class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericCo override def equals(that: Any): Boolean = that match { case that: BigInt => this equals that case that: BigDecimal => that.toBigIntExact exists (this equals _) + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that case x => isValidLong && unifiedPrimitiveEquals(x) } override def isValidByte = this >= Byte.MinValue && this <= Byte.MaxValue @@ -129,6 +132,41 @@ class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericCo override def isValidChar = this >= Char.MinValue && this <= Char.MaxValue override def isValidInt = this >= Int.MinValue && this <= Int.MaxValue def isValidLong = this >= Long.MinValue && this <= Long.MaxValue + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat = { + val bitLen = bitLength + (bitLen <= 24 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Float.MAX_EXPONENT + 1 && // exclude this < -2^128 && this >= 2^128 + lowest >= bitLen - 24 && + lowest < java.lang.Float.MAX_EXPONENT + 1 // exclude this == -2^128 + } + ) && !bitLengthOverflow + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble = { + val bitLen = bitLength + (bitLen <= 53 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Double.MAX_EXPONENT + 1 && // exclude this < -2^1024 && this >= 2^1024 + lowest >= bitLen - 53 && + lowest < java.lang.Double.MAX_EXPONENT + 1 // exclude this == -2^1024 + } + ) && !bitLengthOverflow + } + /** Some implementations of java.math.BigInteger allow huge values with bit length greater than Int.MaxValue . + * The BigInteger.bitLength method returns truncated bit length in this case . + * This method tests if result of bitLength is valid. + * This method will become unnecessary if BigInt constructors reject huge BigIntegers. + */ + private def bitLengthOverflow = { + val shifted = bigInteger.shiftRight(Int.MaxValue) + (shifted.signum != 0) && !(shifted equals BigInt.minusOne) + } protected[math] def isWhole = true def underlying = bigInteger diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index f919a21dee..9c43e98911 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -1,11 +1,15 @@ object Test { def x = BigInt("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") def y = BigDecimal("" + (Short.MaxValue + 1) + ".0") + def y1 = BigDecimal("0.1") + def y2 = BigDecimal("0.5") def l1 = Int.MaxValue.toLong + 1 def l2 = Int.MinValue.toLong - 1 def main(args: Array[String]): Unit = { + assert(!x.isValidDouble, x) + assert(!x.isValidFloat, x) assert(!x.isValidLong, x) assert(!x.isValidInt, x) assert(!x.isValidChar, x) @@ -13,8 +17,81 @@ object Test { assert(!y.isValidShort, y) assert(y.isValidChar, y) assert(y.isValidInt, y) + assert(y.isValidFloat, y) + assert(y.isValidDouble, y) + assert(!y1.isValidLong, y1) + assert(!y1.isValidFloat, y1) + assert(!y1.isValidDouble, y1) + assert(!y2.isValidLong, y2) + assert(y2.isValidFloat, y2) + assert(y2.isValidDouble, y2) + + testBigIntIsFloat() + testBigIntIsDouble() assert(!l1.isValidInt && (l1 - 1).isValidInt, l1) assert(!l2.isValidInt && (l2 + 1).isValidInt, l2) } + + def biExp2(e: Int) = BigInt(1) << e + + def testBigIntIsFloat() { + val prec = 24 + def checkFloatT(x: BigInt) = { + assert(x.isValidFloat, x) + assert((-x).isValidFloat, -x) + } + def checkFloatF(x: BigInt) = { + assert(!x.isValidFloat, x) + assert(!(-x).isValidFloat, -x) + } + checkFloatT(biExp2(prec) - 1) + checkFloatT(biExp2(prec)) + checkFloatF(biExp2(prec) + 1) + checkFloatT(biExp2(prec) + 2) + checkFloatT(biExp2(prec) - 2) + checkFloatF(biExp2(prec + 1) - 1) + checkFloatT(biExp2(prec + 1)) + checkFloatF(biExp2(prec + 1) + 1) + checkFloatF(biExp2(prec + 1) + 2) + checkFloatF(biExp2(prec + 1) + 3) + checkFloatT(biExp2(prec + 1) + 4) + checkFloatT(biExp2(64)) + checkFloatF(biExp2(64) + biExp2(64 - prec)) + checkFloatT(biExp2(64) + biExp2(64 - prec + 1)) + checkFloatT(biExp2(127)) + checkFloatT(biExp2(128) - biExp2(128 - prec)) + checkFloatF(biExp2(128) - biExp2(128 - prec - 1)) + checkFloatF(biExp2(128)) + } + + def testBigIntIsDouble() { + val prec = 53 + def checkDoubleT(x: BigInt) = { + assert(x.isValidDouble, x) + assert((-x).isValidDouble, -x) + } + def checkDoubleF(x: BigInt) = { + assert(!x.isValidDouble, x) + assert(!(-x).isValidDouble, -x) + } + checkDoubleT(biExp2(prec) - 1) + checkDoubleT(biExp2(prec)) + checkDoubleF(biExp2(prec) + 1) + checkDoubleT(biExp2(prec) + 2) + checkDoubleT(biExp2(prec + 1) - 2) + checkDoubleF(biExp2(prec + 1) - 1) + checkDoubleT(biExp2(prec + 1)) + checkDoubleF(biExp2(prec + 1) + 1) + checkDoubleF(biExp2(prec + 1) + 2) + checkDoubleF(biExp2(prec + 1) + 3) + checkDoubleT(biExp2(prec + 1) + 4) + checkDoubleT(biExp2(64)) + checkDoubleF(biExp2(64) + biExp2(64 - prec)) + checkDoubleT(biExp2(64) + biExp2(64 - prec + 1)) + checkDoubleT(biExp2(1023)) + checkDoubleT(biExp2(1024) - biExp2(1024 - prec)) + checkDoubleF(biExp2(1024) - biExp2(1024 - prec - 1)) + checkDoubleF(biExp2(1024)) + } } diff --git a/test/files/run/numbereq.scala b/test/files/run/numbereq.scala index 77a217df36..a1f11da205 100644 --- a/test/files/run/numbereq.scala +++ b/test/files/run/numbereq.scala @@ -16,7 +16,20 @@ object Test { base ::: extras } - + + def mkNumbers(x: BigInt): List[AnyRef] = { + List( + List(BigDecimal(x, java.math.MathContext.UNLIMITED)), + List(x), + if (x.isValidDouble) List(new java.lang.Double(x.toDouble)) else Nil, + if (x.isValidFloat) List(new java.lang.Float(x.toFloat)) else Nil, + if (x.isValidLong) List(new java.lang.Long(x.toLong)) else Nil, + if (x.isValidInt) List(new java.lang.Integer(x.toInt)) else Nil, + if (x.isValidShort) List(new java.lang.Short(x.toShort)) else Nil, + if (x.isValidByte) List(new java.lang.Byte(x.toByte)) else Nil, + if (x.isValidChar) List(new java.lang.Character(x.toChar)) else Nil + ).flatten + } def main(args: Array[String]): Unit = { val ints = (0 to 15).toList map (Short.MinValue >> _) @@ -37,5 +50,23 @@ object Test { assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) } + + val bigInts = (0 to 1024).toList map (BigInt(-1) << _) + val bigInts2 = bigInts map (x => -x) + val bigInts3 = bigInts map (_ + 1) + val bigInts4 = bigInts2 map (_ - 1) + + val setneg1b = bigInts map mkNumbers + val setneg2b = bigInts3 map mkNumbers + val setpos1b = bigInts2 map mkNumbers + val setpos2b = bigInts4 map mkNumbers + + val sets2 = setneg1 ++ setneg1b ++ setneg2 ++ setneg2b ++ List(zero) ++ setpos1 ++ setpos1b ++ setpos2 ++ setpos2b + + for (set <- sets2 ; x <- set ; y <- set) { +// println("'%s' == '%s' (%s == %s) (%s == %s)".format(x, y, x.hashCode, y.hashCode, x.##, y.##)) + assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) +// assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) Disable until Double.## is fixed (SI-5640) + } } -} \ No newline at end of file +} -- cgit v1.2.3