Skip to content

Instantly share code, notes, and snippets.

@oakwhiz
Created February 24, 2014 00:08
Show Gist options
  • Save oakwhiz/9179290 to your computer and use it in GitHub Desktop.
Save oakwhiz/9179290 to your computer and use it in GitHub Desktop.
VMF (Valve Map Format) Hammer save file parser written in Scala using parser combinators.
import scala.util.parsing.combinator.RegexParsers
object Main {
val testdata = """versioninfo
{
"editorversion" "400"
"editorbuild" "5704"
"mapversion" "3656"
"formatversion" "100"
"prefab" "0"
}
visgroups
{
visgroup
{
"name" "Global"
"visgroupid" "43"
"color" "215 212 165"
visgroup
{
"name" "Geo"
"visgroupid" "44"
"color" "186 211 144"
visgroup
{
"name" "Main"
"visgroupid" "45"
"color" "173 82 107"
visgroup
{
"name" "railing"
"visgroupid" "29"
"color" "143 204 173"
}
}
}
visgroup
{
"name" "Models"
"visgroupid" "47"
"color" "88 249 190"
visgroup
{
"name" "prop_static"
"visgroupid" "46"
"color" "103 244 101"
}
visgroup
{
"name" "prop_physics"
"visgroupid" "50"
"color" "195 224 113"
}
}
visgroup
{
"name" "Lighting_Effects"
"visgroupid" "57"
"color" "230 159 204"
visgroup
{
"name" "sprites"
"visgroupid" "60"
"color" "141 146 155"
}
visgroup
{
"name" "instances"
"visgroupid" "75"
"color" "162 123 104"
}
visgroup
{
"name" "lights_local"
"visgroupid" "77"
"color" "112 97 118"
}
}
}
visgroup
{
"name" "DNC"
"visgroupid" "42"
"color" "170 227 176"
visgroup
{
"name" "_fixtextures"
"visgroupid" "37"
"color" "203 152 201"
}
visgroup
{
"name" "_oldoutdoorstreetlights"
"visgroupid" "36"
"color" "80 97 150"
}
visgroup
{
"name" "_oldoutdoorbuildinglights"
"visgroupid" "35"
"color" "95 220 125"
}
visgroup
{
"name" "_oldoutdoorlights"
"visgroupid" "34"
"color" "82 139 88"
}
visgroup
{
"name" "ExitGreenLights"
"visgroupid" "21"
"color" "43 209 1"
}
visgroup
{
"name" "_temp"
"visgroupid" "38"
"color" "166 159 220"
}
}
visgroup
{
"name" "-art_removal"
"visgroupid" "51"
"color" "115 112 113"
}
visgroup
{
"name" "junk"
"visgroupid" "10"
"color" "190 215 132"
}
visgroup
{
"name" "shrubbery"
"visgroupid" "11"
"color" "219 184 217"
}
}
viewsettings
{
"bSnapToGrid" "1"
"bShowGrid" "1"
"bShowLogicalGrid" "0"
"nGridSpacing" "64"
"bShow3DGrid" "0"
}"""
def main(args: Array[String]) {
println("Testing the parser:")
println(VMFParser(testdata))
}
}
case class VMFDocument(cl: Seq[VMFClass])
class VMFProperty
case class VMFClass(n: String, m: Seq[VMFProperty]) extends VMFProperty
class VMFTuple extends VMFProperty
case class VMFTupleGeneric(n: String, m: String) extends VMFTuple
object VMFParser extends RegexParsers {
import language.postfixOps
def crlf = """\r?\n""".r //CRLF or LF
def ows = """[^\S\r\n]*""".r //optional whitespace
def owscrlf = ows ~ crlf //optional whitespace followed by CRLF
def cname = """[^\s{]+""".r //at least 1 of anything except whitespace or a {
def value = """[^\"]*""".r //anything except a "
def valuetuple: Parser[VMFTuple] = (ows ~> '"' ~> value <~ '"' <~ ows) ~ ('"' ~> value <~ '"' <~ owscrlf) ^^ {
case n ~ m => VMFTupleGeneric(n,m)
}
def property: Parser[VMFProperty] = (valuetuple | clazz)
def propertylist: Parser[List[VMFProperty]] = property*
//def propertylist: Parser[List[VMFProperty]] = log(repsep(valuetuple | clazz,crlf))("property list")
def clazz: Parser[VMFClass] = (ows ~> cname <~ owscrlf) ~
(ows ~> "{" ~> owscrlf ~>
propertylist <~
ows <~ "}" <~ (owscrlf | "$".r)) ^^ {
case n ~ m => VMFClass(n,m)
}
def document: Parser[VMFDocument] = (clazz*) ^^ {
case x => VMFDocument(x)
}
override def skipWhitespace = false
def apply(input: String): VMFDocument = parseAll(document, input) match {
case Success(result, _) => result
case failure: NoSuccess => scala.sys.error(failure.msg)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment