Aladdin - Scala Bugtracking
[#928] project: compiler priority: low category: missing feature
submitter assigned to status date submitted
Burak _ _ 2007-01-31 17:19:36.0
subject [contrib #304] replacement of def into val breaks code
code
====== Test.scala ======
import scala.util.parsing._

object Test {
  def main(args: Array[String]) {
    val args = "-7+-5+1-6+2+3"
    System.out.println(args)
    val ps = new ParseString(args) with ListParsers
    ps.expr(ps.input) match {
      case Some({list, _}) => {
        System.out.println("parsed: " + list)
        val ps2 = new ParseTokenSeq(list: Seq[Token]) with MathExp.Parsers
        ps2.expr(ps2.input) match {
          case Some({exp, _}) => System.out.println("=> parsed: " + exp)
          case None => System.out.println("=> nothing parsed")
        }
      }
      case None => System.out.println("nothing parsed")
    }
  }
}

====== ParserBase.scala ======
import scala.util.parsing._

trait CharParsers extends Parsers {
  val any: Parser[char]
  def chr(ch: char): Parser[char] =
    for (val c <- any; c == ch) yield c
  def chrSuchThat(p: char => boolean): Parser[char] =
    for (val c <- any; p(c)) yield c
}

sealed abstract class Token
case class Symbol(sym: char) extends Token
case class Number(num: int) extends Token

trait TokenParsers extends Parsers {
  val any: Parser[Token]
  def symbol(sym: char): Parser[Symbol] =
    for (val t <- any; t.isInstanceOf[Symbol];
         t.asInstanceOf[Symbol].sym == sym)
    yield t.asInstanceOf[Symbol]
  val number: Parser[Number] =
    for (val t <- any; t.isInstanceOf[Number])
    yield t.asInstanceOf[Number]
}

trait ListParsers extends CharParsers {

  val space: Parser[unit] =
    for {
      val _   <- rep(chrSuchThat(Character.isWhitespace))
    } yield ()

  val number: Parser[Token] =
    for {
      val d   <- chrSuchThat(Character.isDigit)
      val ds  <- rep(chrSuchThat(Character.isDigit))
    } yield Number(((d - '0') /: ds) ((x, digit) => x * 10 + digit - '0'))

  val symbol: Parser[Token] =
    for {
      val sym <- any
    } yield Symbol(sym)

  val expr: Parser[List[Token]] =
    for {
      val _   <- space
      val res <- number ||| symbol
      val rest <- expr ||| succeed(List())
    } yield res :: rest
}

class ParseString(s: String) extends Parsers {
  type inputType = int
  val input = 0 
  val any = new Parser[char] {
    def apply(in: inputType): Parser[char]#Result =
      if (in < s.length()) Some({s charAt in, in+1}) else None
  }
}

class ParseTokenSeq(l: Seq[Token]) extends Parsers {
  type inputType = int
  val input = 0 
  val any = new Parser[Token] {
    def apply(in: inputType): Parser[Token]#Result =
      if (in < l.length) Some({l(in), in+1}) else None
  }
}

====== MathExp.scala ======
import scala.util.parsing._

object MathExp {
  sealed abstract class t
  case class Const(num: int) extends t
  case class Add(exp1: t, exp2: t) extends t
  case class Sub(exp1: t, exp2: t) extends t
  case class Neg(exp: t) extends t

  trait Parsers extends TokenParsers {

    // --- level 1 ----------------------------------------

    val add: Parser[t] =
      for {
        val exp1 <- lvl2
        val _    <- symbol('+')
        val exp2 <- lvl1 ||| lvl2
      } yield Add(exp1, exp2)

    val sub: Parser[t] =
      for {
        val exp1 <- lvl2
        val _    <- symbol('-')
        val exp2 <- lvl1 ||| lvl2
      } yield Sub(exp1, exp2)

    val lvl1: Parser[t] =
      add ||| sub
    // --- level 2 ----------------------------------------

    val const: Parser[t] =
      for {
        val num  <- number
      } yield Const(num.num)

    val brackets: Parser[t] =
      for {
        val _    <- symbol('(')
        val exp  <- expr
        val _    <- symbol(')')
      } yield exp

    val neg: Parser[t] =
      for {
        val _    <- symbol('-')
        val exp  <- lvl2
      } yield Neg(exp)

    val lvl2: Parser[t] =
      const ||| brackets ||| neg

    // --- dispatch ---------------------------------------

    val expr: Parser[t] =
      lvl1 ||| lvl2
  }
}

====== MathExp2.scala ======
import scala.util.parsing._

object MathExp {
  sealed abstract class t
  case class Const(num: int) extends t
  case class Add(exp1: t, exp2: t) extends t
  case class Sub(exp1: t, exp2: t) extends t
  case class Neg(exp: t) extends t

  trait Parsers extends TokenParsers {

    // --- level 1 ----------------------------------------

    def add: Parser[t] =
      for {
        val exp1 <- lvl2
        val _    <- symbol('+')
        val exp2 <- lvl1 ||| lvl2
      } yield Add(exp1, exp2)

    def sub: Parser[t] =
      for {
        val exp1 <- lvl2
        val _    <- symbol('-')
        val exp2 <- lvl1 ||| lvl2
      } yield Sub(exp1, exp2)

    def lvl1: Parser[t] =
      add ||| sub

    // --- level 2 ----------------------------------------

    def const: Parser[t] =
      for {
        val num  <- number
      } yield Const(num.num)

    def brackets: Parser[t] =
      for {
        val _    <- symbol('(')
        val exp  <- expr
        val _    <- symbol(')')
      } yield exp

    def neg: Parser[t] =
      for {
        val _    <- symbol('-')
        val exp  <- lvl2
      } yield Neg(exp)

    def lvl2: Parser[t] =
      const ||| brackets ||| neg

    // --- dispatch ---------------------------------------

    def expr: Parser[t] =
      lvl1 ||| lvl2
  }
}

===
P.S. Rather long code, sorry about that.
what happened
The difference between MathExp.scala & MathExp2.scala is that all redundant defs was replaced with vals (in \
MathExp.scala).

Following shell session should explain:
[asbeta@home bug2]$ scalac Test.scala ParserBase.scala MathExp.scala
[asbeta@home bug2]$ java -cp /opt/scala-2.3.3/share/scala/lib/scala-library.jar:. Test
-7+-5+1-6+2+3
parsed: List(Symbol(-),Number(7),Symbol(+),Symbol(-),Number(5),Symbol(+),Number(1),Symbol(-),Number(6),Symbol(+)\
,Number(2),Symbol(+),Number(3))
Exception in thread "main" java.lang.NullPointerException
        at MathExp$Parsers$class.$init$(MathExp.scala:16)
        at Test$$anon$1.<init>(Test.scala:11)
        at Test$.main(Test.scala:11)
        at Test.main(Test.scala)
[asbeta@home bug2]$ scalac Test.scala ParserBase.scala MathExp2.scala
[asbeta@home bug2]$ java -cp /opt/scala-2.3.3/share/scala/lib/scala-library.jar:. Test
-7+-5+1-6+2+3
parsed: List(Symbol(-),Number(7),Symbol(+),Symbol(-),Number(5),Symbol(+),Number(1),Symbol(-),Number(6),Symbol(+)\
,Number(2),Symbol(+),Number(3))
=> parsed: Add(Neg(Const(7)),Add(Neg(Const(5)),Sub(Const(1),Add(Const(6),Add(Const(2),Const(3))))))
what expected Equivalence of output from MathExp.scala & MathExp2.scala.
[back to overview]
Changes of this bug report
Burak  edited on  2007-01-31 17:20:36.0
this is a feature request in disguise: since val's are not lazily initialized, the above fails with nullpointer exceptions. I noticed the same in compiler construction exercises, just added here so doesn't get lost.