Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rewrite string modifications more functional

Tags:

scala

I'm reading lines from a file

for (line <- Source.fromFile("test.txt").getLines) {
  ....
}

I basically want to get a list of paragraphs in the end. If a line is empty, that starts as a new paragraph, and I might want to parse some keyword - value pairs in the future.

The text file contains a list of entries like this (or something similar, like an Ini file)

User=Hans
Project=Blow up the moon
The slugs are going to eat the mustard. // multiline possible!
They are sneaky bastards, those slugs. 

User=....

And I basically want to have a List[Project] where Project looks something like

class Project (val User: String, val Name:String, val Desc: String) {}

And the Description is that big chunk of text that doesn't start with a <keyword>=, but can stretch over any number of lines.

I know how to do this in an iterative style. Just do a list of checks for the keywords, and populate an instance of a class, and add it to a list to return later.

But I think it should be possible to do this in proper functional style, possibly with match case, yield and recursion, resulting in a list of objects that have the fields User, Project and so on. The class used is known, as are all the keywords, and the file format is not set in stone either. I'm mostly trying to learn better functional style.

like image 895
Kajetan Abt Avatar asked May 07 '13 21:05

Kajetan Abt


1 Answers

You're obviously parsing something, so it might be the time to use... a parser!

Since your language seems to treat line breaks as significant, you will need to refer to this question to tell the parser so.

Apart from that, a rather simple implementation would be

import scala.util.parsing.combinator.RegexParsers

case class Project(user: String, name: String, description: String)

object ProjectParser extends RegexParsers {
  override val whiteSpace = """[ \t]+""".r

  def eol : Parser[String] = """\r?\n""".r

  def user: Parser[String] = "User=" ~> """[^\n]*""".r <~ eol
  def name: Parser[String] = "Project=" ~> """[^\n]*""".r <~ eol
  def description: Parser[String] = repsep("""[^\n]+""".r, eol) ^^ { case l => l.mkString("\n") }
  def project: Parser[Project] = user ~ name ~ description ^^ { case a ~ b ~ c => Project(a, b, c) }
  def projects: Parser[List[Project]] = repsep(project,eol ~ eol)
}

And how to use it:

val sample = """User=foo1
Project=bar1
desc1
desc2
desc3

User=foo
Project=bar
desc4 desc5 desc6
desc7 desc8 desc9"""

import scala.util.parsing.input._
val reader = new CharSequenceReader(sample)
val res = ProjectParser.parseAll(ProjectParser.projects, reader)
if(res.successful) {
    print("Found projects: " + res.get)
} else {
    print(res)
}
like image 107
themel Avatar answered Nov 03 '22 05:11

themel