Search code examples
scalagenericscastingparser-combinators

Constructing a type check parser with parser combinators


Currently, I'm trying to parse a Reader[Token] in Scala. Therefore, I'd like to check during the parsing steps if a Token is element of a specific class (e.g. AToken). I can do that easily with the following code:

def aToken = acceptIf(_.isInstanceOf[AToken])("Token " + _ + " is not of type AToken")
  ^^ { _.asInstanceOf[AToken] }  

This works perfectly fine. But I have several types to check. So for every type, I need to write that whole thing above again. So I'd like some abstract acceptIfInstanceOf[T] method that automatically (magically?) creates one for type T.

My current solution is still a two-step thing:

def acceptIfInstanceOf[T](implicit m: Manifest[T]) : Parser[Elem] =
  acceptIf(_.getClass == m.runtimeClass)("" + _ + " is not of type " + m)
def aToken = acceptIfInstanceOf[AToken] ^^ { _.asInstanceOf[AToken] }

This works too, but I'd like to get rid of the function application in aToken and include it directly in acceptIfInstanceOf. Sadly, this doesn't work:

def acceptIfInstanceOf[T](implicit m: Manifest[T]) : Parser[T] =
  acceptIf(_.getClass == m.runtimeClass)("" + _ + " is not of type " + m)
  ^^ { m.runtimeClass.cast(_) }

I get the following error message from the Scala compiler:

scala: type mismatch;
found   : _$1 where type _$1
required: T
def acceptIfInstanceOf[T](implicit m: Manifest[T]): Parser[T] =
  acceptIf(_.getClass == m.runtimeClass)("" + _ + " is not of type " + m)
  ^^ { m.runtimeClass.cast(_) }
                          ^

Does someone know if and how it is possible to construct such a thing? Thanks!


Solution

  • Using _.asInstaceOf[T] instead m.runtimeClass.cast(_) as stated by huynhjl solves the initial problem:

    def acceptIfInstanceOf[T](implicit m: Manifest[T]) : Parser[T] =
      acceptIf(_.getClass == m.runtimeClass)("" + _ + " is not of type " + m)
      ^^ { _.asInstanceOf[T] }
    

    However, the check _.getClass == m.runtimeClass obviously ignores all subtype semantics that _.isInstanceOf contains - simply because we check equality of two Class objects.

    If I want to maintain the semantics of isInstanceOf, I have to use the following test:

    reflect.ClassManifest.singleType(_) <:< m
    

    But as this is deprecated in 2.10, I replaced Manifest by TypeTag and use now the following:

    def acceptIfInstanceOf[T](implicit tag: TypeTag[T]): Parser[T] =
      acceptIf(currentMirror.reflect(_).symbol.toType <:< typeOf[T])
      ("" + _ + " is not of type " + tag.tpe)
      ^^ { _.asInstanceOf[T] }
    

    I got the above solution from another question on Stack Overflow.