tags:

views:

383

answers:

2

First off, I'm new to Scala.

I'm trying to make a template parser in Scala (similar to Smarty (PHP)). It needs to search through the document, replacing anything inside "{{ }}" tags, with anything provided in the HashMap.

I'm currently stuck here:

import scala.collection.mutable.HashMap
import scala.io.Source

class Template(filename: String, vars: HashMap[Symbol, Any]) {
  def parse() = { 
    var contents = Source.fromFile(filename, "ASCII").mkString
    var rule = """\{\{(.*)\}\}""".r

    //for(rule(v) <- rule findAllIn contents) {
    //  yield v
    //}

    //rule.replaceAllIn(contents, )
  }
}

var t = new Template("FILENAME", new HashMap[Symbol, Any])
println(t.parse)

The part's that I've commented are things that I've thought about doing.

Thanks

A: 

I've come a little further...

import scala.collection.mutable.HashMap
import scala.io.Source
import java.util.regex.Pattern
import java.util.regex.Matcher

class Template(filename: String, vars: HashMap[Symbol, Any]) {

  def findAndReplace(m: Matcher)(callback: String => String):String = {
    val sb = new StringBuffer
    while (m.find) { 
      m.appendReplacement(sb, callback(m.group(1))) 
    }
    m.appendTail(sb)
    sb.toString
  }

  def parse() = { 
    var contents = Source.fromFile(filename, "ASCII").mkString
    val m = Pattern.compile("""\{\{(.*)\}\}""").matcher(contents)

    findAndReplace(m){ x => x }

  }
}

var t = new Template("FILENAME.html", new HashMap[Symbol, Any])
println(t.parse)

At the moment it just currently adds whatever was inside of the tag, back into the document. I'm wondering if there is an easier way of doing a find-and-replace style regexp in Scala?

Sam Nardoni
A: 

I'd do it like this (String as key instead of Symbol):

var s : String = input // line, whatever
val regexp = """pattern""".r

while(regexp findFirstIn s != None) {
  s = regexp replaceFirstIn (s, vars(regexp.findFirstIn(s).get))
}

If you prefer not using var, go recursive instead of using while. And, of course, a stringbuilder would be more efficient. In that case, I might do the following:

val regexp = """^(.*?)(?:{{(pattern)}})?""".r
for(subs <- regexp findAllIn s)
  subs match {
    case regexp(prefix, var) => sb.append(prefix); if (var != null) sb.append("{{"+vars(var)+"}}")
    case _ => error("Shouldn't happen")
  }

That way you keep appending the non-changing part, followed by the next part to be replaced.

Daniel