Reputation: 819
I do not know how to capture regex subgroups in Scala.
Say potions are either basic or made of other potions and they are named as <adjective> <noun>
. There are recipe formulas as follows:
val recipe = "white phial potion is 3 fortify health potions, 1 vigorous stamina potion, 5 plentiful magicka potions."
I want to parse the formulas into the following domain:
case class Potion(name: String)
case class Formula(potion: Potion, ingredients: Option[List[(Potion, Int)]])
So far I have come up with the pattern
val recipePattern: Regex = """(\w+ \w+) potion is( (\d+) (\w+ \w+) (?:potion|potions)[,.])+""".r
but I have no idea how to proceed further and grab the iterative subgroups.
Upvotes: 0
Views: 70
Reputation: 14224
It is not possible to capture repeated groups with regular expressions. The last match of a specific group will overwrite all the previous matches of the same group.
I recommend using a proper parsing library to parse that kind of strings. For example, using fastparse:
import scala.util.{Try, Success, Failure}
case class Potion(name: String)
object Potion {
def apply(adjective: String, noun: String): Potion = Potion(s"$adjective $noun")
}
case class Formula(potion: Potion, ingredients: List[(Potion, Int)])
object Formula {
import fastparse._, SingleLineWhitespace._
object Parser {
def number[_: P] = P(CharsWhileIn("0-9").!).map(_.toInt)
def word[_: P] = P(CharsWhileIn("a-z").!)
def ingredient[_: P] = P(number ~ word ~ word ~ "potion"~~"s".?).map {
case (count, adj, noun) => (Potion(adj, noun), count)
}
def formula[_: P] = P(word ~ word ~ "potion" ~ "is" ~ ingredient.rep(sep = ",") ~ "." ~ End).map {
case (adj, noun, ingredients) => Formula(Potion(adj, noun), ingredients.toList)
}
}
def parse(formula: String): Try[Formula] = {
fastparse.parse(formula, Parser.formula(_)) match {
case Parsed.Success(result, _) => Success(result)
case failure: Parsed.Failure => Failure(new RuntimeException(failure.trace().longMsg))
}
}
}
The result is:
scala> Formula.parse("white phial potion is 3 fortify health potions, 1 vigorous stamina potion, 5 plentiful magicka potions.")
res0: scala.util.Try[core.Test.Formula] = Success(Formula(Potion(white phial),List((Potion(fortify health),3), (Potion(vigorous stamina),1), (Potion(plentiful magicka),5))))
Upvotes: 2