Created
August 31, 2013 06:35
-
-
Save tstone/6396579 to your computer and use it in GitHub Desktop.
A little evening project to test a proof of concept idea about turning SMACSS into it's own language.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.util.parsing.combinator.JavaTokenParsers | |
object Parser extends JavaTokenParsers { | |
object Renderer { | |
def render(ms: List[Module]): String = { | |
// Assemble a map | |
val names = ms.map(_.name) | |
val modules = Map(names.zip(ms).toArray: _*) | |
// Reduce to CSS | |
modules.foldLeft("") { case (acc, (_, m)) => | |
m.lineage = resolveLineage(m, modules) | |
acc + "\n" + render(m) | |
} | |
} | |
private def resolveLineage(module: Module, ms: Map[String, Module]): String = { | |
if (module.parentName.isEmpty) module.name | |
else resolveLineage(ms.get(module.parentName.get).get, ms) + "-" + module.name | |
} | |
private def render(m: Module): String = { | |
"/* === module [ " + m.name + " ] ============================ */\n" + | |
m.rules.map(render(_, "." + m.lineage)).mkString("\n") | |
} | |
private def render(rule: Rule, prefix: String): String = | |
prefix + " " + rule.selector + " {\n" + | |
rule.properties.map(render(_)).mkString("\n") + | |
"\n}\n" | |
private def render(prop: Property): String = " " + prop.name + ": " + prop.value + ";" | |
} | |
class Module(val name: String, val parentName: Option[String], val rules: List[Rule]) { | |
var lineage: String = name | |
} | |
class Rule(val selector: String, val properties: List[Property]) | |
class Property(val name: String, var value: String) | |
def toString(a: Any): String = a match { | |
case None => "" | |
case Some(a) => toString(a) | |
case s: String => s | |
case list: List[Any] => list.foldLeft("") { _ + toString(_) } | |
case ~(a, b) => toString(a) + toString(b) | |
} | |
def toList[T](a: Any): List[T] = a match { | |
case list: List[Any] => list.asInstanceOf[List[T]] | |
} | |
def toModule(a: Any): Module = a match { | |
case ~(~(name: String, parent: Option[Any]), block: List[Any]) => | |
new Module(name, parent.asInstanceOf[Option[String]], block.asInstanceOf[List[Rule]]) | |
} | |
def toRule(a: Any): Rule = a match { | |
case ~(selector: String, properties: List[Any]) => new Rule(selector, properties.asInstanceOf[List[Property]]) | |
} | |
def toProperty(a: Any): Property = a match { | |
case ~(name: String, value: String) => new Property(name, value) | |
} | |
def file = rep(module) ^^ toList[Module] | |
def module = "@module" ~> identifier ~ opt("::" ~> identifier) ~ moduleBlock ^^ toModule | |
def moduleBlock = "{" ~> rep(rule) <~ "}" ^^ toList[Rule] | |
def rule = identifier ~ ruleBlock ^^ toRule | |
def ruleBlock = "{" ~> rep(property) <~ "}" ^^ toList[Property] | |
def property = identifier ~ propertyValue <~ opt(termination) ^^ toProperty | |
def propertyValue = ":" ~> regex("[^;]+"r) ^^ toString | |
def valueChar = regex("[^;]+"r) | |
def identifier = letter ~ rep(letter | wholeNumber | symbol) ^^ toString | |
def letter = regex("[A-Za-z]"r) | |
def symbol = "_" | "-" | |
def termination = "\n" | ";" | |
def main(args: Array[String]) { | |
val code = """ | |
@module third :: second { | |
div { font-size: 15px; } | |
ul { | |
list-style-type: none; | |
display: inline-block; | |
} | |
} | |
@module first{div{color:#333;}} | |
@module fourth :: third { aside { display: inline; } } | |
@module second :: first { | |
li {color: steelblue;} | |
} | |
""" | |
val m = parseAll(file, code).get | |
println(Renderer.render(m)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output from the above is: