Scala pattern matching multiple combinator parsers results - scala

Having a List[String] and several parsers I want to pattern match each String from the List to parsers. So it'd look like this (Warning, pseudo-code):
myStringList.map{
case MyParser.keyword => keyword match {
case KeywordParser.keyword1 => //it's special keyword1
case KeywordParser.keyword2 => //special treatment for keyword2
case NotSpecial => //it's a usual command
}
case MyParser.stringValue => //etc...
}
Why would I want to do so?
I'm parsing a simple script line, that contains "strings" and $(keywords). Some of the keywords are special and need to be treated separately.
Currently I have only one special keyword, so I'm using chained parseAll and match, but that doesn't feel right.
So, how it can be done?

It's not really supported, but you can do it like this:
val parseKeyword: PartialFunction[String, ... ] {
case KeywordParser.keyword1 => //it's special keyword1
case KeywordParser.keyword2 => //special treatment for keyword2
case NotSpecial => //it's a usual command
}
myStringList.map{
case x#MyParser.keyword if parseKeyword.isDefinedAt(x) => parseKeyword(x)
case MyParser.stringValue => //etc...
}

Bet I had a short-circuit in my brain, because I woke up next morning and realised, that all I need is a couple of parsers, some case classes and a pair of pattern matchers. So the solution to my problem looks like this (pseudo-code):
case class SpecialKeyword1(value: String)
case class SpecialKeyword2(value: String)
...
case class SpecialKeywordN(value: String)
case class SimpleKeyword(value: String)
object MyParser extends JavaTokenParsers{
def expression: Parser[Any] = ...
def keyword1: Parser[SpecialKeyword1] = ...
def keyword2: ...
...
def simplekeyword: Parser[SimpleKeyword] = ...
}
object Main {
def parseScript(inputScript: String): String = {
MyParser.parseAll(MyParser.expression, inputScript) match {
case Success(result, _) => {
case SpecialKeyword1(val) => ...
case SpecialKeyword2(val) => ...
...
case SimpleKeyword(val) => ...
case str: String => ...
}
case Failure(_, _) => "Parsing Failure"
case Error(_, _) => "Parsing Error"
}
}
}

Related

Scala 2: Case class pattern matching to show error

I have a case class like this:
case class AEMError(
authError: Option[AEMAuthError] = None,
unexpectedError: Option[AEMUnexpectedError] = None,
)
I want to do a pattern matching like this:
def throwErrorAndLogMessage(error:AEMError,message:String):Unit={
error match {
case authError: Some[AEMAuthError] => {???}
case unexpectedError: Some[AEMUnexpectedError] => {???}
case _ => {???}
}
}
and add multiple case classes for different types of error but I am totally spinning on the syntax for case match can somebody help me here?
With your definition of AEMError you can't do reasonable pattern matching because it possible to have both error present.
You should change AEMError to
sealed trait AEMError
case class AEMErrorAuth(authError: AEMAuthError) extends AEMError
case class AEMErrorUnexpected(authError: AEMUnexpectedError) extends AEMError
And use it like this:
err match {
case AEMErrorAuth(authError) => {
print(authError)
}
case AEMErrorUnexpected(unexpectedError) => {
print(unexpectedError)
}
}
If you can't change the signature of AEMError like #talex mentioned, you can try something like:
def throwErrorAndLogMessage(error: AEMError, message: String): Unit = {
error match {
case AEMError(Some(authError), Some(unexpectedError)) => ???
case AEMError(Some(authError), None) => ???
case AEMError(None, Some(unexpectedError)) => ???
case _ => ???
}
}

Is it possible to use a implicit function in match case?

I have enum like this.
object SortCountryField extends Enumeration {
type SortCountryField = Value
val countryName = Value("country_name")
val countryStatus = Value("country_status")
}
I'm using this SortCountryField enum in match-case.
Here I need to convert toString every time.
To make it convenient I'm trying to implicit converter to extract String from SortCountryField.{Value}
However, I end up having a compiler error when I use the implicit function in following match case.
'myString' match{
case SortCountryField.countryName.toString => //Some operations
case SortCountryField.countryStatus.toString => //another operation
}
Error Log:-
found : mypackage.ConstantUtils.SortCountryField.Value
[error] required: String
[error] case SortCountryField.countryStatus => //my-operations
I think you'd be better off using the enum in your match like:
SortCountryField withName <your_string> match {
case SortCountryField.countryName => //Some operations
case SortCountryField.countryStatus => //another operation
}
If your string sometimes doesn't match any field then you can easily wrap this in a Try like in the following code:
Try(SortCountryField withName <your_string>) match {
case Success(SortCountryField.countryName) => //Some operations
case Success(SortCountryField.countryStatus) => //another operation
case _ => //another operation
}
You can also do:
'myString' match{
case x if x == SortCountryField.countryName.toString => //Some operations
case x if x == SortCountryField.countryStatus.toString => //another operation
}
How does your implicit converter looks like?
My guess is that you have converter SortCountryField => String, but you need SortCountryField.Value => String converter.
Add below function in your enum:
implicit def toString(value: Value): String = value.toString
Use below in matching:
val countryStaus:String = SortCountryField.countryStatus
'myString' match {
case `countryStatus` => //Some operations
case _ => // Another operation
}

Elegant Handling of Scala Future[Either]]

I have a type whose shape is like this:
val myType: Future[Either[MyError, TypeA]] = // some value
I know that I could pattern match on this and get to the Right or Left type, but the problem is that I would have to nest my pattern matching logic. I'm looking for much more elegant way of handling this? Any suggestions?
If you encode your MyError as an exception, you don't need the Either anymore and can simply patternMatch against the completion, or use a recoverWith to map it to another type:
myType.onComplete {
case Success(t) =>
case Failure(e) =>
}
To map your existing Either types you could do something like this:
case class MyException(e: MyError) extends Exception
def eitherToException[A](f: Future[Either[MyError,A]]): Future[A] = {
f.flatMap {
case Left(e) => Future.failed(MyException(e))
case Right(x) => Future.successful(x)
}
}
val myType2 = eitherToException(myType)
Alternatively, if MyError and TypeA are under your control, you could create a common super type and pattern match against that:
sealed trait MyResult
final case class MyError() extends MyResult
final case class TypeA() extends MyResult
myType.map {
case MyError() => ...
case TypeA() => ...
}
You can create custom extractor objects:
object FullSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Right(x)) => Some(x)
case _ => None
}
}
object PartSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Left(err)) => Some(err)
case _ => None
}
}
And
val myType: Future[Either[MyError, TypeA]] = // some value
myType.onComplete {
case FullSuccess(x) => ... // equivalent to case Success(Right(x))
case PartSuccess(x) => ... // equivalent to case Success(Left(x))
case Failure(e) => ...
}

How to make implicit conversion work during pattern matching

I have an enum for keywords and operators (and some other too), e.g. (all are similar):
object Keywords extends Enumeration {
val AND, ARRAY, BEGIN, ...= Value
case class Keyword(keyword: Value) extends Token[Value] {
def this(keyword: String) = this(Keywords.fromString(keyword))
def value = keyword
}
implicit def valueToKeyword(keyword: Value) = new Keyword(keyword)
}
this implicit conversion allows me to pass enum values where Tokens are expected e.g.
def testFunction[T](t: Token[T]) = ...
testFunction(Keywords.ARRAY) // gets converted
testFunction(Operators.PLUS) // gets converted too
it also seems that the same implicit conversion is not applied during matching i.e.
val token = new Keyword("ARRAY")
token match {
case Keywords.ARRAY => ... // not selected but SHOULD be
case Operators.PLUS => ... // completely different Enum
...
}
Why? How to overcome this?
This doesn't work because:
token match {
case Keywords.ARRAY => println("Array")
case _ => println("Something else")
}
is essentially a PartialFunction with the following type signature: PartialFunction[Keywords.Value, Unit]. Which means an implicit won't be applied, because it's either isDefinedAt or it isn't for that input.
If it isn't defined than case _ => ... will catch everything in my example code. If it's not defined at all and nothing will match it then you will get a MatchError thrown.
In your case token of type Token[Value] isn't defined in the Partial Function that the match will compile to, because only types of Keywords.Value are defined.
Three solutions
If you really want implicits then you could explicitly ask for an implicit (yes, that sentence is funny :))
implicitly[Keywords.Value](token) match {
case Keywords.ARRAY => println("Array")
case _ => println("Something else")
}
Or you can explicitly state the type of token, to invoke the implicit magic:
val token: Keywords.Value = new Keyword("ARRAY")
token match {
case Keywords.ARRAY => println("Array")
case _ => println("Something else")
}
Or the simplest solution if you can live without implicits:
token.value match {
case Keywords.ARRAY => println("Array")
case _ => println("Something else")
}
I know it's not the answer you're looking for, but I hope that you understood what match {...} really means and what Partial Functions are.

How to pattern match on Scala's parser combinator result

We have a multithreaded RPC server that parses input strings. We've run into an issue where Scala's parser combinator library is not multithreaded safe: the var lastNoSuccess in Parsers.scala is used by any parsing. We get a NullPointerException in this line
if (!(lastNoSuccess != null && next.pos < lastNoSuccess.next.pos))
The default way to implement the parser by making an object that extends one of the Parsers, but I want to construct a parser on demand so each has its own internal state, so I'm using a class instead of an object. However, I can't get it to compile since I need to pattern match on the result:
import scala.util.parsing.combinator.RegexParsers
class SqlParserImpl
extends RegexParsers
{
val term: Parser[String] = """(?i)term\b""".r
}
object Test
{
def main(args: Array[String]): Unit =
{
val parser = new SqlParserImpl
parser.parseAll(parser.term, "term") match {
// How do I match?
case SqlParserImpl#Success(result, _) => true
case SqlParserImpl#NoSuccess => false
}
}
}
Fails with
t.scala:16: error: '=>' expected but '#' found.
case SqlParserImpl#Success(result, _) => true
^
t.scala:17: error: '=>' expected but '#' found.
case SqlParserImpl#NoSuccess => false
^
two errors found
Use this:
val parser = new SqlParserImpl
parser.parseAll(parser.term, "term") match {
case parser.Success(result, _) => true
case parser.NoSuccess(_, _) => false
}
The # sign is used to designate a type member. In your case it's using a constructor or an extractor pattern which needs to reference to an object or something that looks like a constructor.
Hmm. I don't have a 2.7 handy. Try this:
parser.parseAll(parser.term, "term") match {
case parser.Success(result, _) => true
case parser.Failure(_, _) => false
case parser.Error(_, _) => false
}
I was able to compile the following:
object Test {
def main(args: Array[String]): Unit = {
val parser = new SqlParserImpl
println(parser.parseAll(parser.term, "term") match {
case x: parser.Success[_] => true
case x: parser.NoSuccess => false
})
}
}
The NoSuccess object (with extractor) was added back in 2009, at a time when no code was being backported to 2.7 anymore It's implementation, however, is pretty simple:
object NoSuccess {
def unapply[T](x: ParseResult[T]) = x match {
case Failure(msg, next) => Some(msg, next)
case Error(msg, next) => Some(msg, next)
case _ => None
}
}
So you can replace the parser.NoSuccess(_, _) match with one parser.Failure(_, _) and one parser.Error(_, _) match. But if you are not interested in what is being returned, then it's simpler to match against the type:
case _: parser.Success[_] => true
case _: parser.NoSuccess => false
Like suggested by Eugene.