More arsing around with Scala. This time, trying to reimplement lesscss. This is about two hours worth of hacking:
object LessParser extends RegexParsers {
def parse(spec: String) : Sequence = {
parseAll(sequence, spec) match {
case Success(l, _) => l
case Failure(m, _) => throw new IllegalArgumentException(m)
case Error(m, _) => throw new IllegalArgumentException(m)
}
}
def sequence = rep(namedNested | atom) ^^ {
Sequence(_)
}
def namedNested = nestedName~"{"~rep(atom)~"}" ^^ {
case name~"{"~contents~"}" => NamedNested(name, contents)
}
def nestedName = """[\.\#][a-zA-Z]+""".r
def atom = variableDef | variableUse | text
def text = "[^@{}]+".r ^^ { Text(_) }
def variableDef = "@"~variableName~":"~variableValue~";" ^^ {
case "@"~name~":"~value~";" => VariableDef(name, value)
}
def variableUse = "@"~variableName ^^ {
case "@"~name => VariableUse(name)
}
def variableName = "[a-z-]+".r
def variableValue = "[^;]+".r
}
abstract class Tree {
def eval(defs: Map[String, String], out: StringBuffer) : Map[String, String]
}
case class Sequence(parts: List[Tree]) extends Tree {
def eval(defs: Map[String, String], out: StringBuffer) : Map[String, String] = {
parts.foldLeft (defs) { (accumDefs : Map[String, String], part : Tree) =>
part.eval(accumDefs, out)
}
}
}
case class NamedNested(name: String, contents: List[Atom]) extends Tree {
def eval(defs: Map[String, String], out: StringBuffer) : Map[String, String] = {
out.append(name)
out.append(" {")
contents.foldLeft (defs) { (accumDefs : Map[String, String], part : Tree) =>
part.eval(accumDefs, out)
}
out.append("}")
defs
}
}
abstract case class Atom() extends Tree
case class VariableDef(name: String, value: String) extends Atom {
def eval(defs: Map[String, String], out: StringBuffer) : Map[String, String] = {
defs + (name -> value)
}
}
case class VariableUse(name: String) extends Atom {
def eval(defs: Map[String, String], out: StringBuffer) : Map[String, String] = {
out.append(defs(name))
defs
}
}
case class Text(value: String) extends Atom {
def eval(defs: Map[String, String], out: StringBuffer) : Map[String, String] = {
out.append(value)
defs
}
}
object Less {
def main(args : Array[String]) : Unit = {
println(LessParser.parse("@nice-blue: #5B83AD;"))
println(LessParser.parse("@nice-blue"))
println(LessParser.parse("@nice-blue: #5B83AD; @nice-blue"))
println(LessParser.parse("@nice-blue: #5B83AD; #header { background-color: @nice-blue; }"))
var fullExample = """
@nice-blue: #5B83AD;
#header { background-color: @nice-blue; }
"""
var buf = new StringBuffer
LessParser.parse(fullExample).eval(Map(), buf)
println(buf)
}
}
Output:
Sequence(List(VariableDef(nice-blue,#5B83AD)))
Sequence(List(VariableUse(nice-blue)))
Sequence(List(VariableDef(nice-blue,#5B83AD), VariableUse(nice-blue)))
Sequence(List(VariableDef(nice-blue,#5B83AD), NamedNested(#header,List(Text(background-color: ),
VariableUse(nice-blue), Text(; )))))
#header {background-color: #5B83AD; }
It does variables and the beginnings of mixins. It's actually quite lame e.g. it throws away whitespace. Hohum, time to learn more about parsers in Scala...