With-input-from-string

I have what might be a motivating reason to abandon the simple tail recursive approach and embrace the parser approach.

When I did some timing analysis, I discovered that for a very large benchmark example, my simple parser takes about 16 seconds to run, with most of the time being spent in converting the content of the file to a List[Char]. Thereafter the parseListContent function works very fast. I then re-wrote the function to operate on a BufferedIterator[Char], and the time drops to 2.5 seconds. Unfortunately can’t use the came code to parse a List[Char] and BufferedIterator[Char] because BufferedIterator neither supports pattern matching, nor dropWhile and friends.

So here is what the buffered version of the parser looks like now. Considerably uglier than before. Worse is that I have two versions of the function, one for BufferedIterator[Char] and one for List[Char], the latter being used in several test cases which call parseListContent(string.toList). I could of course eliminate the latter and test using parseBufferedContent(string.toIterator.buffered), but perhaps biting the bullet and massaging the code from @sangamon, to fit is quickly become a more elegant alternative.

def parseBufferedContent(it:BufferedIterator[Char], clause: Clause, clauses: CNF): CNF = {
    def dropWhile(p: Char => Boolean): BufferedIterator[Char] = {
      while (it.hasNext && p(it.head)) {
        it.next()
      }
      it
    }

    def takeWhile(p: Char => Boolean): List[Char] = {
      def loop(buf: List[Char]):List[Char] = {
        if (it.hasNext && p(it.head))
          loop(it.next() :: buf)
        else
          buf.reverse
      }
      loop(Nil)
    }

    if (it.hasNext)
      it.head match {
        case 'p' |
             'c' =>
          parseBufferedContent(dropWhile(!endOfLine.contains(_)), clause, clauses)
        case c if digits.contains(c) =>
          val num = takeWhile(digits.contains)
          val number = num.mkString.toInt
          if (number == 0)
            parseBufferedContent(it, Nil, clause :: clauses)
          else
            parseBufferedContent(it, number :: clause, clauses)
        case c if whiteSpace.contains(c) =>
          parseBufferedContent(dropWhile(whiteSpace.contains), clause, clauses)
      }
    else if (clause.nonEmpty)
      parseBufferedContent(it, Nil, clause :: clauses)
    else
      clauses
  }